3、支付宝接口公用函数类:AlipayCore.java 5
4、支付宝通知处理类:AlipayNotify.java 9
5、支付宝各接口请求提交类:AlipaySubmit.java 12
7.HttpClient方式访问:HttpProtocolHandler.java 17
8、Http请求对象的封装:HttpRequest.java 23
9、 Http返回对象的封装:HttpResponse.java 27
10、表示Http返回的结果字符方式:HttpResultType.java 29
1、 跳转页面 :alipay.jsp和alipaywap.jsp 37
Java版支付宝支付功能整理文档
作者:Lock-玄清
时间:2017-03-15
注:此文档只支持付款,没有退款的功能
一、引用文件
1、基础配置类:AlipayConfig.java
package com.alipay.config;
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*版本:3.4
*修改日期:2016-03-08
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static Stringpartner ="";
// 收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号
public static Stringseller_id =partner;
// MD5密钥,安全检验码,由数字和字母组成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static Stringkey ="";
//----------------pc端支付宝返回路径---------------------
// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static Stringnotify_url ="";
// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static Stringreturn_url ="";
// 签名方式
public static Stringsign_type ="MD5";
// 调试用,创建TXT日志文件夹路径,见AlipayCore.java类中的logResult(String sWord)打印方法。
public static Stringlog_path ="C:\\";
// 字符编码格式 目前支持 gbk 或utf-8
public static Stringinput_charset ="utf-8";
// 支付类型 ,无需修改
public static Stringpayment_type ="1";
//----------------pc端支付宝调用接口---------------------
// 调用的接口名,无需修改
public static Stringservice ="create_direct_pay_by_user";
//----------------手机端支付宝调用接口---------------------
public static Stringwap_service ="alipay.wap.create.direct.pay.by.user";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//↓↓↓↓↓↓↓↓↓↓ 请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 防钓鱼时间戳 若要使用请调用类文件submit中的query_timestamp函数
public static Stringanti_phishing_key ="";
// 客户端的IP地址 非局域网的外网IP地址,如:221.0.0.1
public static Stringexter_invoke_ip ="";
//↑↑↑↑↑↑↑↑↑↑请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
2、支付宝MD5签名处理核心文件:MD5.java
package com.alipay.sign;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import org.apache.commons.codec.digest.DigestUtils;
/**
* 功能:支付宝MD5签名处理核心文件,不需要修改
* 版本:3.3
* 修改日期:2012-08-17
* 说明:
* 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
* 该代码仅供学习和研究支付宝接口使用,只是提供一个
* */
public class MD5 {
/**
* 签名字符串
* @param text 需要签名的字符串
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static String sign(Stringtext, Stringkey, Stringinput_charset) {
text =text + key;
return DigestUtils.md5Hex(getContentBytes(text,input_charset));
}
/**
* 签名字符串
* @param text 需要签名的字符串
* @param sign 签名结果
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static boolean verify(Stringtext, Stringsign, Stringkey, Stringinput_charset) {
text =text + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text,input_charset));
if(mysign.equals(sign)) {
return true;
}
else {
return false;
}
}
/**
* @param content
* @param charset
* @return
* @throws SignatureException
* @throws UnsupportedEncodingException
*/
private static byte[] getContentBytes(String content, Stringcharset) {
if (charset ==null ||"".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingExceptione) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" +charset);
}
}
}
3、支付宝接口公用函数类:AlipayCore.java
package com.alipay.util;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import com.alipay.config.AlipayConfig;
/* *
*类名:AlipayFunction
*功能:支付宝接口公用函数类
*详细:该类是请求、通知返回两个文件所调用的公用函数核心处理文件,不需要修改
*版本:3.3
*日期:2012-08-14
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipayCore {
/**
* 除去数组中的空值和签名参数
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result =new HashMap<String, String>();
if (sArray ==null ||sArray.size() <= 0) {
return result;
}
for (Stringkey : sArray.keySet()) {
String value =sArray.get(key);
if (value ==null ||value.equals("") ||key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key,value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys =new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr ="";
for (int i = 0; i < keys.size(); i++) {
String key =keys.get(i);
String value =params.get(key);
if (i ==keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr =prestr + key + "=" +value;
} else {
prestr =prestr + key + "=" +value + "&";
}
}
return prestr;
}
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param sWord 要写入日志里的文本内容
*/
public static void logResult(StringsWord) {
FileWriter writer =null;
try {
writer =new FileWriter(AlipayConfig.log_path +"alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exceptione) {
e.printStackTrace();
} finally {
if (writer !=null) {
try {
writer.close();
} catch (IOExceptione) {
e.printStackTrace();
}
}
}
}
/**
* 生成文件摘要
* @param strFilePath 文件路径
* @param file_digest_type 摘要算法
* @return 文件摘要结果
*/
public static String getAbstract(StringstrFilePath, Stringfile_digest_type)throws IOException {
PartSource file =new FilePartSource(new File(strFilePath));
if(file_digest_type.equals("MD5")){
return DigestUtils.md5Hex(file.createInputStream());
}
else if(file_digest_type.equals("SHA")) {
return DigestUtils.sha256Hex(file.createInputStream());
}
else {
return "";
}
}
}
4、支付宝通知处理类:AlipayNotify.java
package com.alipay.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;
/* *
*类名:AlipayNotify
*功能:支付宝通知处理类
*详细:处理支付宝各接口通知返回
*版本:3.3
*日期:2012-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考
*************************注意*************************
*调试通知返回时,可查看或改写log日志的写入TXT里的数据,来检查通知返回是否正常
*/
public class AlipayNotify {
/**
* 支付宝消息验证地址
*/
private static final StringHTTPS_VERIFY_URL ="https://mapi.alipay.com/gateway.do?service=notify_verify&";
/**
* 验证消息是否是支付宝发出的合法消息
* @param params 通知返回来的参数数组
* @return 验证结果
*/
public static boolean verify(Map<String, String> params) {
//判断responsetTxt是否为true,isSign是否为true
//responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
//isSign不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
String responseTxt = "false";
if(params.get("notify_id") !=null) {
String notify_id =params.get("notify_id");
responseTxt = verifyResponse(notify_id);
}
String sign ="";
if(params.get("sign") != null) {sign =params.get("sign");}
boolean isSign =getSignVeryfy(params,sign);
//写日志记录(若要调试,请取消下面两行注释)
//String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数:" + AlipayCore.createLinkString(params);
//AlipayCore.logResult(sWord);
if (isSign &&responseTxt.equals("true")) {
return true;
} else {
return false;
}
}
/**
* 根据反馈回来的信息,生成签名结果
* @param Params 通知返回来的参数数组
* @param sign 比对的签名结果
* @return 生成的签名结果
*/
private static boolean getSignVeryfy(Map<String, String> Params, Stringsign) {
//过滤空值、sign与sign_type参数
Map<String, String>sParaNew = AlipayCore.paraFilter(Params);
//获取待签名字符串
String preSignStr = AlipayCore.createLinkString(sParaNew);
//获得签名验证结果
boolean isSign =false;
if(AlipayConfig.sign_type.equals("MD5") ) {
isSign = MD5.verify(preSignStr,sign, AlipayConfig.key, AlipayConfig.input_charset);
}
return isSign;
}
/**
* 获取远程服务器ATN结果,验证返回URL
* @param notify_id 通知校验ID
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String verifyResponse(Stringnotify_id) {
//获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求
String partner = AlipayConfig.partner;
String veryfy_url =HTTPS_VERIFY_URL +"partner=" +partner + "¬ify_id=" + notify_id;
return checkUrl(veryfy_url);
}
/**
* 获取远程服务器ATN结果
* @param urlvalue 指定URL路径地址
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String checkUrl(Stringurlvalue) {
String inputLine ="";
try {
URL url =new URL(urlvalue);
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
BufferedReader in =new BufferedReader(new InputStreamReader(urlConnection
.getInputStream()));
inputLine =in.readLine().toString();
} catch (Exceptione) {
e.printStackTrace();
inputLine ="";
}
return inputLine;
}
}
5、支付宝各接口请求提交类:AlipaySubmit.java
package com.alipay.util;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;
/* *
*类名:AlipaySubmit
*功能:支付宝各接口请求提交类
*详细:构造支付宝各接口表单HTML文本,获取远程HTTP数据
*版本:3.3
*日期:2012-08-13
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class AlipaySubmit {
/**
* 支付宝提供给商户的服务接入网关URL(新)
*/
private static final String ALIPAY_GATEWAY_NEW ="https://mapi.alipay.com/gateway.do?";
/**
* 生成签名结果
* @param sPara 要签名的数组
* @return 签名结果字符串
*/
public static String buildRequestMysign(Map<String, String>sPara) {
String prestr = AlipayCore.createLinkString(sPara);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String mysign ="";
if(AlipayConfig.sign_type.equals("MD5") ) {
mysign = MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
}
return mysign;
}
/**
* 生成要请求给支付宝的参数数组
* @param sParaTemp 请求前的参数数组
* @return 要请求的参数数组
*/
private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
//除去数组中的空值和签名参数
Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
//生成签名结果
String mysign =buildRequestMysign(sPara);
//签名结果与签名方式加入请求提交参数组中
sPara.put("sign",mysign);
sPara.put("sign_type", AlipayConfig.sign_type);
return sPara;
}
/**
* 建立请求,以表单HTML形式构造(默认)
* @param sParaTemp 请求参数数组
* @param strMethod 提交方式。两个值可选:post、get
* @param strButtonName 确认按钮显示文字
* @return 提交表单HTML文本
*/
public static String buildRequest(Map<String, String>sParaTemp, StringstrMethod, StringstrButtonName) {
//待请求参数数组
Map<String, String> sPara =buildRequestPara(sParaTemp);
List<String> keys =new ArrayList<String>(sPara.keySet());
StringBuffer sbHtml =new StringBuffer();
sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" +ALIPAY_GATEWAY_NEW
+ "_input_charset=" + AlipayConfig.input_charset +"\" method=\"" +strMethod
+ "\">");
for (int i = 0; i < keys.size(); i++) {
String name = (String)keys.get(i);
String value = (String)sPara.get(name);
sbHtml.append("<input type=\"hidden\" name=\"" +name + "\" value=\"" + value + "\"/>");
}
//submit按钮控件请不要含有name属性
sbHtml.append("<input type=\"submit\" value=\"" +strButtonName +"\" style=\"display:none;\"></form>");
sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");
return sbHtml.toString();
}
/**
* 用于防钓鱼,调用接口query_timestamp来获取时间戳的处理函数
* 注意:远程解析XML出错,与服务器是否支持SSL等配置有关
* @return 时间戳字符串
* @throws IOException
* @throws DocumentException
* @throws MalformedURLException
*/
public static String query_timestamp()throws MalformedURLException,
DocumentException, IOException {
//构造访问query_timestamp接口的URL串
String strUrl =ALIPAY_GATEWAY_NEW +"service=query_timestamp&partner=" + AlipayConfig.partner +"&_input_charset" +AlipayConfig.input_charset;
StringBuffer result =new StringBuffer();
SAXReader reader =new SAXReader();
Document doc =reader.read(new URL(strUrl).openStream());
List<Node> nodeList =doc.selectNodes("//alipay/*");
for (Nodenode : nodeList) {
// 截取部分不需要解析的信息
if (node.getName().equals("is_success") && node.getText().equals("T")) {
// 判断是否有成功标示
List<Node> nodeList1 =doc.selectNodes("//response/timestamp/*");
for (Nodenode1 : nodeList1) {
result.append(node1.getText());
}
}
}
return result.toString();
}
}
6.自定义订单类:UtilDate.java
package com.alipay.util;
import java.util.Date;
import java.util.Random;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
/* *
*类名:UtilDate
*功能:自定义订单类
*详细:工具类,可以用作获取系统日期、订单编号等
*版本:3.3
*日期:2012-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class UtilDate {
/** 年月日时分秒(无下划线) yyyyMMddHHmmss */
public static final StringdtLong ="yyyyMMddHHmmss";
/** 完整时间 yyyy-MM-dd HH:mm:ss */
public static final Stringsimple ="yyyy-MM-dd HH:mm:ss";
/** 年月日(无下划线) yyyyMMdd */
public static final StringdtShort ="yyyyMMdd";
/**
* 返回系统当前时间(精确到毫秒),作为一个唯一的订单编号
* @return
* 以yyyyMMddHHmmss为格式的当前系统时间
*/
public static String getOrderNum(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(dtLong);
return df.format(date);
}
/**
* 获取系统当前日期(精确到毫秒),格式:yyyy-MM-dd HH:mm:ss
* @return
*/
public static String getDateFormatter(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(simple);
return df.format(date);
}
/**
* 获取系统当期年月日(精确到天),格式:yyyyMMdd
* @return
*/
public static String getDate(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(dtShort);
return df.format(date);
}
/**
* 产生随机的三位数
* @return
*/
public static String getThree(){
Random rad=new Random();
return rad.nextInt(1000)+"";
}
}
7.HttpClient方式访问:HttpProtocolHandler.java
package com.alipay.util.httpClient;
import org.apache.commons.httpclient.HttpException;
import java.io.IOException;
import java.net.UnknownHostException;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.util.IdleConnectionTimeoutThread;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/* *
*类名:HttpProtocolHandler
*功能:HttpClient方式访问
*详细:获取远程HTTP数据
*版本:3.3
*日期:2012-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class HttpProtocolHandler {
private static String DEFAULT_CHARSET ="GBK";
/** 连接超时时间,由bean factory设置,缺省为8秒钟 */
private int defaultConnectionTimeout = 8000;
/** 回应超时时间, 由bean factory设置,缺省为30秒钟 */
private int defaultSoTimeout = 30000;
/** 闲置连接超时时间, 由bean factory设置,缺省为60秒钟 */
private int defaultIdleConnTimeout = 60000;
private int defaultMaxConnPerHost = 30;
private int defaultMaxTotalConn = 80;
/** 默认等待HttpConnectionManager返回连接超时(只有在达到最大连接数时起作用):1秒*/
private static final long defaultHttpConnectionManagerTimeout = 3 * 1000;
/**
* HTTP连接管理器,该连接管理器必须是线程安全的.
*/
private HttpConnectionManager connectionManager;
private static HttpProtocolHandlerhttpProtocolHandler =new HttpProtocolHandler();
/**
* 工厂方法
*
* @return
*/
public static HttpProtocolHandler getInstance() {
return httpProtocolHandler;
}
/**
* 私有的构造方法
*/
private HttpProtocolHandler() {
// 创建一个线程安全的HTTP连接池
connectionManager =new MultiThreadedHttpConnectionManager();
connectionManager.getParams().setDefaultMaxConnectionsPerHost(defaultMaxConnPerHost);
connectionManager.getParams().setMaxTotalConnections(defaultMaxTotalConn);
IdleConnectionTimeoutThread ict =new IdleConnectionTimeoutThread();
ict.addConnectionManager(connectionManager);
ict.setConnectionTimeout(defaultIdleConnTimeout);
ict.start();
}
/**
* 执行Http请求
*
* @param request 请求数据
* @param strParaFileName 文件类型的参数名
* @param strFilePath 文件路径
* @return
* @throws HttpException, IOException
*/
public HttpResponse execute(HttpRequestrequest, StringstrParaFileName, StringstrFilePath)throws HttpException, IOException {
HttpClient httpclient =new HttpClient(connectionManager);
// 设置连接超时
int connectionTimeout =defaultConnectionTimeout;
if (request.getConnectionTimeout() > 0) {
connectionTimeout =request.getConnectionTimeout();
}
httpclient.getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
// 设置回应超时
int soTimeout =defaultSoTimeout;
if (request.getTimeout() > 0) {
soTimeout =request.getTimeout();
}
httpclient.getHttpConnectionManager().getParams().setSoTimeout(soTimeout);
// 设置等待ConnectionManager释放connection的时间
httpclient.getParams().setConnectionManagerTimeout(defaultHttpConnectionManagerTimeout);
String charset =request.getCharset();
charset =charset == null ?DEFAULT_CHARSET :charset;
HttpMethod method =null;
//get模式且不带上传文件
if (request.getMethod().equals(HttpRequest.METHOD_GET)) {
method =new GetMethod(request.getUrl());
method.getParams().setCredentialCharset(charset);
// parseNotifyConfig会保证使用GET方法时,request一定使用QueryString
method.setQueryString(request.getQueryString());
} else if(strParaFileName.equals("") && strFilePath.equals("")) {
//post模式且不带上传文件
method =new PostMethod(request.getUrl());
((PostMethod) method).addParameters(request.getParameters());
method.addRequestHeader("Content-Type","application/x-www-form-urlencoded; text/html; charset=" +charset);
}
else {
//post模式且带上传文件
method =new PostMethod(request.getUrl());
List<Part> parts =new ArrayList<Part>();
for (int i = 0; i < request.getParameters().length;i++) {
parts.add(new StringPart(request.getParameters()[i].getName(),request.getParameters()[i].getValue(),charset));
}
//增加文件参数,strParaFileName是参数名,使用本地文件
parts.add(new FilePart(strParaFileName,new FilePartSource(new File(strFilePath))));
// 设置请求体
((PostMethod) method).setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[0]),new HttpMethodParams()));
}
// 设置Http Header中的User-Agent属性
method.addRequestHeader("User-Agent","Mozilla/4.0");
HttpResponse response =new HttpResponse();
try {
httpclient.executeMethod(method);
if (request.getResultType().equals(HttpResultType.STRING)) {
response.setStringResult(method.getResponseBodyAsString());
} else if (request.getResultType().equals(HttpResultType.BYTES)) {
response.setByteResult(method.getResponseBody());
}
response.setResponseHeaders(method.getResponseHeaders());
} catch (UnknownHostExceptionex) {
return null;
} catch (IOExceptionex) {
return null;
} catch (Exceptionex) {
return null;
} finally {
method.releaseConnection();
}
return response;
}
/**
* 将NameValuePairs数组转变为字符串
*
* @param nameValues
* @return
*/
protected String toString(NameValuePair[]nameValues) {
if (nameValues ==null ||nameValues.length == 0) {
return "null";
}
StringBuffer buffer =new StringBuffer();
for (int i = 0; i < nameValues.length;i++) {
NameValuePair nameValue =nameValues[i];
if (i == 0) {
buffer.append(nameValue.getName() +"=" + nameValue.getValue());
} else {
buffer.append("&" +nameValue.getName() +"=" + nameValue.getValue());
}
}
return buffer.toString();
}
}
8、Http请求对象的封装:HttpRequest.java
package com.alipay.util.httpClient;
import org.apache.commons.httpclient.NameValuePair;
/* *
*类名:HttpRequest
*功能:Http请求对象的封装
*详细:封装Http请求
*版本:3.3
*日期:2011-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class HttpRequest {
/** HTTP GET method */
public static final StringMETHOD_GET ="GET";
/** HTTP POST method */
public static final StringMETHOD_POST ="POST";
/**
* 待请求的url
*/
private String url =null;
/**
* 默认的请求方式
*/
private Stringmethod =METHOD_POST;
private int timeout = 0;
private int connectionTimeout = 0;
/**
* Post方式请求时组装好的参数值对
*/
private NameValuePair[]parameters = null;
/**
* Get方式请求时对应的参数
*/
private StringqueryString =null;
/**
* 默认的请求编码方式
*/
private Stringcharset = "GBK";
/**
* 请求发起方的ip地址
*/
private StringclientIp;
/**
* 请求返回的方式
*/
private HttpResultTyperesultType = HttpResultType.BYTES;
public HttpRequest(HttpResultTyperesultType) {
super();
this.resultType =resultType;
}
/**
* @return Returns the clientIp.
*/
public String getClientIp() {
return clientIp;
}
/**
* @param clientIp The clientIp to set.
*/
public void setClientIp(StringclientIp) {
this.clientIp =clientIp;
}
public NameValuePair[] getParameters() {
return parameters;
}
public void setParameters(NameValuePair[]parameters) {
this.parameters =parameters;
}
public String getQueryString() {
return queryString;
}
public void setQueryString(StringqueryString) {
this.queryString =queryString;
}
public String getUrl() {
return url;
}
public void setUrl(Stringurl) {
this.url =url;
}
public String getMethod() {
return method;
}
public void setMethod(Stringmethod) {
this.method =method;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout =connectionTimeout;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout =timeout;
}
/**
* @return Returns thecharset.
*/
public String getCharset() {
return charset;
}
/**
* @param charset Thecharset to set.
*/
public void setCharset(Stringcharset) {
this.charset =charset;
}
public HttpResultType getResultType() {
return resultType;
}
public void setResultType(HttpResultTyperesultType) {
this.resultType =resultType;
}
}
9、Http返回对象的封装:HttpResponse.java
package com.alipay.util.httpClient;
import com.alipay.config.AlipayConfig;
import org.apache.commons.httpclient.Header;
import java.io.UnsupportedEncodingException;
/* *
*类名:HttpResponse
*功能:Http返回对象的封装
*详细:封装Http返回信息
*版本:3.3
*日期:2011-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class HttpResponse {
/**
* 返回中的Header信息
*/
private Header[]responseHeaders;
/**
* String类型的result
*/
private String stringResult;
/**
* btye类型的result
*/
private byte[] byteResult;
public Header[] getResponseHeaders() {
return responseHeaders;
}
public void setResponseHeaders(Header[]responseHeaders) {
this.responseHeaders =responseHeaders;
}
public byte[] getByteResult() {
if (byteResult !=null) {
return byteResult;
}
if (stringResult !=null) {
return stringResult.getBytes();
}
return null;
}
public void setByteResult(byte[]byteResult) {
this.byteResult =byteResult;
}
public String getStringResult()throws UnsupportedEncodingException {
if (stringResult !=null) {
return stringResult;
}
if (byteResult !=null) {
return new String(byteResult, AlipayConfig.input_charset);
}
return null;
}
public void setStringResult(StringstringResult) {
this.stringResult =stringResult;
}
}
10、表示Http返回的结果字符方式:HttpResultType.java
/*
* Alipay.com Inc.
* Copyright (c) 2004-2005 All Rights Reserved.
*/
package com.alipay.util.httpClient;
/* *
*类名:HttpResultType
*功能:表示Http返回的结果字符方式
*详细:表示Http返回的结果字符方式
*版本:3.3
*日期:2012-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public enum HttpResultType {
/**
* 字符串方式
*/
STRING,
/**
* 字节数组方式
*/
BYTES
}
二、Controller处理方法
1、支付宝PC端支付(即时到账)
(1、)支付方法
/**
* 支付宝付款 Lock-玄清
* @throws UnsupportedEncodingException
*/
@RequestMapping("{orderid}/web/alipay.html")
public ModelAndView zhifu(HttpSessionsession, HttpServletRequestrequest,@PathVariable("orderid")long orderid)throws UnsupportedEncodingException{
//判断用户登录状态
Userinfo user = getUserLogin(request);
if(user !=null){
ModelAndView mv =new ModelAndView("web/alipay");
// 订单查询
Orders orders =orderService.findBydetail(orderid);
// 此方法根据自己的实际情况而定↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// 商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = String.valueOf(orders.getOrderid());
// 订单名称,必填
String subject =new String(String.valueOf(orders.getOrderid()));
// 付款金额,必填
String total_fee =new String(String.format("%.2f",orders.getPrice()).getBytes("ISO-8859-1"),"UTF-8");
System.err.println(total_fee);
// 商品描述,可空
String body =new String(orders.getRemark().getBytes("ISO-8859-1"),"UTF-8");
// 把请求参数打包成数组
Map<String, String> sParaTemp =new HashMap<>();
sParaTemp.put("service", AlipayConfig.service);
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_id", AlipayConfig.seller_id);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", AlipayConfig.payment_type);
sParaTemp.put("notify_url", AlipayConfig.notify_url);
sParaTemp.put("return_url", AlipayConfig.return_url);
sParaTemp.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
sParaTemp.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
sParaTemp.put("out_trade_no",out_trade_no);
sParaTemp.put("subject",subject);
sParaTemp.put("total_fee",total_fee);
sParaTemp.put("body",body);
// 其他业务参数根据在线开发文档,添加参数.文档地址:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.O9yorI&treeId=62&articleId=103740&docType=1
// 如sParaTemp.put("参数名","参数值");
// 建立请求
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get", "确认");
mv.addObject("sHtmlText",sHtmlText);
System.err.println(sHtmlText);
return mv;
}else{
return new ModelAndView("redirect:../../web/login");
}
}
(2、)支付成功后的同步调用
/**
* 同步调用 Lock-玄清
* @param request
* @param orderId
* @throws IOException
*/
@SuppressWarnings("rawtypes")
@RequestMapping("web/return_url.html")
public String returnorder(HttpServletRequestrequest, HttpServletResponseresponse) throws IOException {
logger.info("into 同步调用");
//获取支付宝GET过来反馈信息
Map<String,String> params =new HashMap<String,String>();
Map requestParams =request.getParameterMap();
for (Iteratoriter = requestParams.keySet().iterator();iter.hasNext();) {
String name = (String)iter.next();
String[] values = (String[])requestParams.get(name);
String valueStr ="";
for (int i = 0;i < values.length;i++) {
valueStr = (i ==values.length - 1) ?valueStr + values[i]
: valueStr +values[i] +",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
valueStr = new String(valueStr.getBytes("ISO-8859-1"),"utf-8");
params.put(name,valueStr);
}
//商户订单号
String out_trade_no =new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
//支付宝交易号
@SuppressWarnings("unused")
String trade_no =new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
//交易状态
String trade_status =new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
//计算得出通知验证结果
boolean verify_result = AlipayNotify.verify(params);
//验证
if(verify_result){//验证成功
//业务逻辑,支付成功后的处理
if(trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")){
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
logger.info("order pay state success!");
//支付成功后的处理
userandorder(out_trade_no,request);
// 根据自己的需要写处理订单方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//返回商户页面
return "web/return_url";
// 注意:
// 退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
}
logger.info("验证成功");
}else{
//该页面可做页面美工编辑
logger.info("验证失败");
return "web/return_url";
}
return null;
}
(4、)支付成功后的异步调用
/**
* 异步调用 Lock-玄清
*
* @param request
* @param orderId
* @throws UnsupportedEncodingException
*/
@SuppressWarnings("rawtypes")
@RequestMapping("web/notify_url.html")
public void alrestlu(HttpServletRequestrequest, HttpServletResponseresponse) throws UnsupportedEncodingException {
logger.info("into 异步调用");
Map<String, String> params =new HashMap<String, String>();
Map requestParams =request.getParameterMap();
for (Iteratoriter = requestParams.keySet().iterator();iter.hasNext();) {
String name = (String)iter.next();
String[] values = (String[])requestParams.get(name);
String valueStr ="";
for (int i = 0;i < values.length;i++) {
valueStr = (i ==values.length - 1) ?valueStr + values[i] :valueStr + values[i] +",";
}
// 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name,valueStr);
}
// 商户订单号
String out_trade_no =new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
// 支付宝交易号
@SuppressWarnings("unused")
String trade_no =new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
// 交易状态
String trade_status =new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");
if (AlipayNotify.verify(params)) {// 验证成功
// 请在这里加上商户的业务逻辑程序代码
logger.info("Md5通过");
// 订单查询
Orders orders =orderService.findBydetail(Long.valueOf(out_trade_no).longValue());
// 判断订单状态是否已经做过更改
if (orders.getStatus() != 1) {
if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
logger.info("异步验证成功");
//支付成功后的处理
userandorder(out_trade_no,request);
// 根据自己的需要写处理订单方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// 注意:
// 退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
}
}
} else {// 验证失败
logger.info("异步验证失败");
}
}
2、支付宝手机网页支付
(1、)支付方法
/**
* 支付宝手机网站支付 Lock-玄清
* @param request
* @param orderid
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping("web/{orderid}/alipaywap.html")
public ModelAndView wapalipay(HttpServletRequestrequest,@PathVariable("orderid")long orderid)throws UnsupportedEncodingException{
logger.info("支付宝手机网站支付 发起支付");
//判断用户登录状态
Userinfo user = getUserLogin(request);
if(user !=null){
ModelAndView mv =new ModelAndView("web/alipaywap");
// 订单查询
Orders orders =orderService.findBydetail(orderid);
//商户订单号,商户网站订单系统中唯一订单号,必填
String out_trade_no = String.valueOf(orders.getOrderid());
//订单名称,必填
String subject =new String(String.valueOf(orders.getOrderid()));
//付款金额,必填
String total_fee =new String(String.format("%.2f",orders.getPrice()).getBytes("ISO-8859-1"),"UTF-8");
/* //收银台页面上,商品展示的超链接,必填
String show_url = new String(request.getParameter("").getBytes("ISO-8859-1"),"UTF-8");*/
logger.info("支付宝手机网站支付 发送请求参数!");
//
//把请求参数打包成数组
Map<String, String> sParaTemp =new HashMap<String, String>();
//此参数为wap的参数
sParaTemp.put("service", AlipayConfig.wap_service);
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_id", AlipayConfig.seller_id);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", AlipayConfig.payment_type);
sParaTemp.put("notify_url", AlipayConfig.notify_url);
sParaTemp.put("return_url", AlipayConfig.return_url);
sParaTemp.put("out_trade_no",out_trade_no);
sParaTemp.put("subject",subject);
sParaTemp.put("total_fee",total_fee);
/*sParaTemp.put("show_url", show_url);*/
sParaTemp.put("app_pay","Y");//启用此参数可唤起钱包APP支付。
sParaTemp.put("body","支付宝手机网站支付");
//其他业务参数根据在线开发文档,添加参数.文档地址:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.2Z6TSk&treeId=60&articleId=103693&docType=1
//如sParaTemp.put("参数名","参数值");
//建立请求
String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认");
mv.addObject("sHtmlText",sHtmlText);
return mv;
}else{
return new ModelAndView("redirect:../../web/login");
}
}
(2、)支付成功后的同步调用
同上
(4、)支付成功后的异步调用
同上
三、页面代码
说明:pc端的跳转页面和手机网页支付的跳转页面一致,同步和异步调用的页面一致。故:页面我在这里就只放一个的就可以。
1、跳转页面:alipay.jsp和alipaywap.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head lang="zh-cmn-Hans">
<meta charset="UTF-8">
<title>支付宝手机网站支付</title>
</head>
<body>
${sHtmlText}
</body>
</html>
2、同步调用页面:return_url.jsp
根据自己的需要画一个支付成功的提示页面
3、异步调用页面:notify_url.jsp
根据自己的需要画一个支付成功的提示页面(此页面并没有什么吊用)
备注:此文档是我在项目中运用的,仅为个人观点,还有一部分的不完善,请在使用时,根据自己的项目的实际情况运用。如有疑问,请参考支付宝提供的完整示例或是官方文档。
注:文档中没有引入任何jar包,在使用过程注意引入各种jar包。
作者:Lock-玄清
时间:2017-03-15