对于现在日新月异的信息化社会,我们的日常生活也有了翻天覆地的变化;最最有力的证明就是我们的支付方式;不管我们身处那个城市,只要我们有一部手机,就可以轻松的进行各种支付,根本不用担心自己的现金是否足够;因此在这的基础上,任何一个盈利非盈利的系统,都会有自己的支付功能;所以,不管是从成本,还是从使用范围考虑,都会首先想到支付宝和微信;
今天就来谈谈支付宝APP支付的接口整理:
1; 准备工作我就不多说了,直接看官方网站:https://docs.open.alipay.com/204/105297/
2; 下载支付宝SDK ,里面有支付宝官方封装的各种签名算法: https://docs.open.alipay.com/54/106370/
网页下面还有支付宝官方的示例,很有用:
好了,看了这么多,都有些烦了,我们就直接上代码了:
1:首先将下载的JAVA版SDK复制到项目:
2:支付宝关键数据配置文件(以下数据都是测试的,不一定可以测试成功,大家可自己在官网申请):
package com.xt.shop.web.alipay;
/**说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*商户配置文件:
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
/**
*合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
*/
public static final String SELLER_ID = "2088621208345438";
/**
*商户的私钥,需要PKCS8格式,RSA公私钥生成:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.nBDxfy&treeId=58&articleId=103242&docType=1
*/
public static final String PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4H+xjbNc1L3g8RNbaJ0v2XmzoH3VjBZ/gdsE50Gn1sAn2IEsSXj3c+j714+7bzc8EqbSFmTuIuFpgAwgsRIfMaSqjTGg62NKg2KKvb9BIDqks4pVd2ClIC656bUYVDev6zwx7MNeeJHf8DAQ85juoW1nnLzz5pKG5hmlnTeMGB2/XssuhZNhiApSoDdlrWVzx+ftOLBsQa7xIJDMlTH4nl5yOFqeW7Qt8ZJBkEg3QeVKRz2iX94PagK/gHPLdwx9U1me7aVgQGxP0QUXOTmBMpMYQiTs1OQFSgR0ndvqbcTSObF0aVcUv6RKrYoxFf7+w7NAdXBfu3Sj7c0J5CXkfAgMBAAECggEALowjfp8taMyvYM0mY9A7a/VDIwuQ6Y+qc1ySTMNbhjkSy54uXF5m1U6OTCtrXzYQd+VPNdcfLdktP8iEcGyCyDJuDQpr7zSRSwh5WFC0HwNV0XsWt7rF9Oi/G0Q0y23Rkn51cU9PszMVAisnhx3NRyqQ99kBWTs9h2FrlCokkokwPoZK5yMCzvHVwfT2K9uNxrtEMfXSEHlbvqdFZxEGb5tAagZmzMOtc2/7F1ZtcXELtAVtgS4t0pMCD/xq9PTk6pBFuDKsE9ha5Nx3StjeTo+mtQiI0h4JTMmVEF3CvAxYAQhnI0p78E255PhZ6REpkgX1YBKqHYrtBNd8fPzWgQKBgQD0EDXgxoXIjbz5JUy0McdsKjVS1rfUQMe0EhupVfSCoUaXwVa0x0NXiZCDF9OE4HlqY9Wg36UtNiP31wIaJqB5pg1K5j4EJfOd4/7ZHhmQBj2jGzEkm4br0XZvZqnHUEGWtDARggORyybW1NQ0m3VHce2lmXoEbwmS15VoIaYCyQKBgQDBIUCyR844d51sO4MXHtfhK4G9JxNNgzIe1POV03J5WgmSIVGM5/nkiqnMqM5YXC7sbX6jpZluPYEc20S0mXiHSFrYtyAi/MBDy+l7x5ECmASpATj4pTWN27ptEvWydlc7vsmUt1KVX+mCIa1i5BdI2oH05egZbfLSGGy6QbxopwKBgF7rLwkMLFujRCthZkNuWLCoebSN7VCPIRvXLwrccwuazWmXt7D3DimtRpa1XX4ADzhhO2QllofO1o49AIRLIX4uJl1KzjOuzuBJG4Q1QJvS0YXtvV8PXZm5DOamdsEdZWTHDyq/9cNtzt0eSKltDF+M32/YEKxIwuBpF6I38+iBAoGBAK1tUe6GYp38c+X0TxSdAtjVu8yC4WeSr5hWPGLHnR1yB/QsbVHuIk3jYvVAhQN9zDlNXhJQK8Z9/opOMJVI154MEGBZmccxVunKm+EmZYaBwnWNfn5xxPSdYvx9sRrQACfLfLNKKCOxHDxaR5vtHSez6E1lgxYK0Klve3uKekkzAoGBANLdkt/krL1JTaXPpPQzS5+IzximFKK29wBVYvRAavZNA+zswUZHpp47IyaYkZt0ab9M6CHL0RmfaYUvoF0538mdCzDMM3AYUuATH1ShNGmtnFAsshNhEBJIyuYlHDgN3KVrL3LwnJlvMKaC9F0jQheFpj+L2mnh2tk9TqygwUJr";
/**
*支付宝的公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
*/
public static final String ALIPAY_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkrIvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsraprwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUrCmZYI/FCEa3/cNMW0QIDAQAB";
/**
* 签名方式
*/
public static final String SIGN_TYPE = "RSA2";
/**
* 调试用,创建TXT日志文件夹路径,见AlipayCore.java类中的logResult(String sWord)打印方法
*/
public static final String LOG_PATH = "C://";
/**
* 字符编码格式 目前支持 gbk 或 utf-8
*/
public static final String CHARSET = "utf-8";
/**
* 接收通知的接口名(回调地址)
*/
public static final String NOTIFY_URL = "/alipayNotify";
/**
* APPID
*/
public static final String APP_ID = "2017033006478366";
//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
3: 现在就来进行封装工具了;看代码:
package com.xt.shop.web.alipay;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
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.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
/**
*<p>类 说 明: TODO
*<p>创建时间: 2017年11月7日 上午9:38:02
*<p>创 建 人: geYang
**/
public class AlipayUtil {
/**
* 支付宝消息验证地址
*/
private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";
/**
*<p>方法说明: TODO 签名验证
*<p>参数说明: @param params 通知返回来的参数数组
*<p>参数说明: @param sign 比对的签名结果
*<p>参数说明: @throws AlipayApiException
*<p>返回说明: boolean 签名验证结果
*<p>创建时间: 2017年11月1日 下午2:19:18
*<p>创 建 人: geYang
**/
public static boolean rsaCheck(Map<String, String> params) throws AlipayApiException {
return AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
}
/**
*<p>方法说明: TODO 进行签名
*<p>参数说明: @param params 需要签名的参数
*<p>参数说明: @throws AlipayApiException
*<p>返回说明: String 签名字符串
*<p>创建时间: 2017年11月1日 下午2:29:01
*<p>创 建 人: geYang
**/
public static String rsaSign (Map<String, String> params) throws AlipayApiException{
String content = AlipaySignature.getSignCheckContentV2(params);
return AlipaySignature.rsaSign(content, AlipayConfig.PRIVATE_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
}
/**
*<p>方法说明: TODO 解密
*<p>参数说明: @param params 密文参数
*<p>参数说明: @throws AlipayApiException
*<p>返回说明: String 解密字符串
*<p>创建时间: 2017年11月1日 下午3:34:02
*<p>创 建 人: geYang
**/
public static String rsaDecrypt(Map<String, String> params) throws AlipayApiException{
return AlipaySignature.checkSignAndDecrypt(params, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.PRIVATE_KEY, true, true, AlipayConfig.SIGN_TYPE);
}
/**
* 获取远程服务器ATN结果,验证返回URL
*
* @param notifyId 通知校验ID
* @return 服务器ATN结果 验证结果集: invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 true
* 返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
public static String verifyResponse(String notifyId) {
// 获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求
String urlValue = HTTPS_VERIFY_URL + "partner=" + AlipayConfig.SELLER_ID + "¬ify_id=" + notifyId;
return checkUrl(urlValue);
}
/**
* 获取远程服务器ATN结果
*
* @param urlValue 指定URL路径地址
* @return 服务器ATN结果 验证结果集: invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空 true
* 返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String checkUrl(String urlValue) {
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 (Exception e) {
e.printStackTrace();
}
return inputLine;
}
/**
*<p>方法说明: TODO 获取支付宝回调地址
*<p>创建时间: 2017年11月2日 下午1:21:05
*<p>创 建 人: geYang
**/
public static String getNotifyUrl(HttpServletRequest request){
StringBuffer requestURL = request.getRequestURL();
return requestURL.substring(0,requestURL.indexOf("/front/"))+AlipayConfig.NOTIFY_URL;
}
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param word 要写入日志里的文本内容
*/
public static void logResult(String word) {
FileWriter writer = null;
try {
writer = new FileWriter(AlipayConfig.LOG_PATH + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(word);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 生成文件摘要
* @param strFilePath 文件路径
* @param fileDigestType 摘要算法
* @return 文件摘要结果
*/
public static String getAbstract(String strFilePath, String fileDigestType) throws IOException {
PartSource file = new FilePartSource(new File(strFilePath));
if("MD5".equals(fileDigestType)){
return DigestUtils.md5Hex(file.createInputStream());
}
else if("SHA".equals(fileDigestType)) {
return DigestUtils.sha256Hex(file.createInputStream());
}
else {
return "";
}
}
}
4: 使用,接口接入:
package com.xt.shop.web.alipay;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.xt.shop.base.exception.MyException;
import com.xt.shop.base.service.inter.ProductOrderInter;
import com.xt.shop.base.service.inter.RepairOrderInter;
import com.xt.shop.until.IsNull;
import com.xt.shop.until.often.MyResult;
import com.xt.shop.until.time.DateUtils;
import net.sf.json.JSONObject;
/**
*<p>类 说 明: TODO
*<p>创建时间: 2017年11月7日 上午9:40:54
*<p>创 建 人: geYang
**/
@RestController
public class AlipayController {
/**
* 商品订单
*/
@Resource
private ProductOrderInter productOrderInter;
/**
* 维修订单
*/
@Resource
private RepairOrderInter repairOrderInter;
/**
*<p>方法说明: TODO 支付通知(支付宝后台的回调通知)
*<p>返回说明: String SUCCESS/FAIL
*<p>创建时间: 2017年10月31日 下午3:18:04
*<p>创 建 人: geYang
* @throws Exception
**/
@RequestMapping(value="/alipayNotify")
String getPayNotify(HttpServletRequest request) throws Exception{
System.out.println("========== 支付宝支付回调 ==========");
//获取支付宝POST过来反馈信息
Map<String,String> receiveMap = getReceiveMap(request);
System.out.println("支付宝支付回调参数=="+receiveMap);
// 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)
// 1,判断是否有异步通知
if(IsNull.isNull(receiveMap.get("notify_id"))){
System.out.println("支付宝支付回调没有异步通知");
return "no notify message";
}
//2,判断是否是支付宝发来的异步通知
if(!AlipayUtil.verifyResponse(receiveMap.get("notify_id")).equals("true")){
System.out.println("支付宝支付回调通知错误");
return ("response failure");
}
//3,判断是否是支付宝发来的异步通知
if(!AlipayUtil.rsaCheck(receiveMap)){
System.out.println("支付宝支付回调验证签名失败");
return "sign failure";
}
//4,判断交易状态
String tradeStatus = receiveMap.get("trade_status");
if ("TRADE_FINISHED".equals(tradeStatus) || "TRADE_SUCCESS".equals(tradeStatus)){
System.out.println("支付宝支付回调签名解码=="+URLDecoder.decode(receiveMap.get("sign"),"UTF-8"));
String orderNo = receiveMap.get("out_trade_no"); // 商户订单号
System.out.println("支付宝支付回调商户订单号=="+orderNo);
//订单业务操作
if(orderNo.startsWith("G")){ //商品订单
try {
return productOrderInter.paymentProductOrder(orderNo, 1);
} catch (MyException e) {
e.printStackTrace();
System.out.println(e.getMessage());
return "failure";
}
} else if(orderNo.startsWith("R")){ //维修订单
try {
return repairOrderInter.paymentRepairOrder(orderNo, 1);
} catch (MyException e) {
e.printStackTrace();
System.out.println(e.getMessage());
return "failure";
}
}
return "SUCCESS";
}
System.out.println("支付宝支付回调失败");
return "failure";
}
/**
*<p>方法说明: TODO 获取请求参数
*<p>返回说明: Map<String,String> receiveMap
*<p>创建时间: 2017年11月3日 下午2:25:02
*<p>创 建 人: geYang
**/
private static Map<String,String> getReceiveMap(HttpServletRequest request){
Map<String,String> receiveMap = new HashMap<String,String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "UTF-8");
receiveMap.put(name, valueStr);
}
return receiveMap;
}
/**
*<p>方法说明: TODO 订单支付(返回支付宝订单给客户端)
*<p>参数说明: String orderNo 订单号
*<p>参数说明: 支付状态
*<p>返回说明:
*<p>创建时间: 2017年10月31日 下午3:38:37
*<p>创 建 人: geYang
* @throws Exception
**/
@RequestMapping(value="/front/aliPay",method=RequestMethod.POST)
Object alipay(String orderNo,HttpServletRequest request) {
//订单判断,拿到订单信息
if(IsNull.isNull(orderNo)){
return MyResult.errMap("请指定订单号");
}
StringBuffer totalAmount = new StringBuffer(); //订单总价值
StringBuffer body = new StringBuffer(); //对一笔交易的具体描述信息
StringBuffer subject = new StringBuffer(); //商品的标题/交易标题/订单标题/订单关键字等
if(orderNo.startsWith("G")){ //商品订单
Map<String, String> order;
try {
order = productOrderInter.isPayment(orderNo);
totalAmount.append(order.get("totalAmount"));
body.append(order.get("productName"));
subject.append("木木公司商品");
} catch (MyException e) {
e.printStackTrace();
return MyResult.errMap(e.getMessage());
}
} else if(orderNo.startsWith("R")){ //维修订单
Map<String, String> order;
try {
order = repairOrderInter.isPayment(orderNo);
totalAmount.append(order.get("totalAmount"));
body.append(order.get("repairItem"));
subject.append("木木公司项目");
} catch (MyException e) {
e.printStackTrace();
return MyResult.errMap(e.getMessage());
}
} else {
return MyResult.errMap("订单号错误");
}
String notifyUrl = AlipayUtil.getNotifyUrl(request);
System.out.println("支付宝回调地址=="+notifyUrl);
try {
return payment(notifyUrl,orderNo,totalAmount.toString(),body.toString(),subject.toString());
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
/**
*<p>方法说明: TODO 订单支付处理(生成支付宝订单)
*<p>参数说明: String notifyUrl 回调地址
*<p>参数说明: String orderNo 订单号
*<p>参数说明: String total_amount 订单总价
*<p>返回说明:
*<p>创建时间: 2017年10月31日 下午4:04:12
*<p>创 建 人: geYang
* @throws AlipayApiException
* @throws UnsupportedEncodingException
**/
private static Map<String,Object> payment(String notifyUrl,String orderNo,String totalAmount,String body,String subject) throws Exception{
String nowTime = DateUtils.getDate(); //当前时间
//公共参数:
Map<String, String> publicParam = new HashMap<String, String>();
publicParam.put("app_id", AlipayConfig.APP_ID); //支付宝分配给开发者的应用ID
publicParam.put("method", "alipay.trade.app.pay"); // 接口名称
publicParam.put("format", "json");
publicParam.put("charset", AlipayConfig.CHARSET);
publicParam.put("sign_type", AlipayConfig.SIGN_TYPE); //商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
publicParam.put("timestamp", nowTime);
publicParam.put("version", "1.0"); //调用的接口版本,固定为:1.0
publicParam.put("notify_url", notifyUrl);//支付宝服务器主动通知商户服务器里指定的页面http/https路径。建议商户使用https
//业务参数:
Map<String, String> payParam = new HashMap<String, String>();
payParam.put("body", body); //对一笔交易的具体描述信息
payParam.put("subject", subject); //商品的标题/交易标题/订单标题/订单关键字等。
payParam.put("out_trade_no", orderNo); //商户网站唯一订单号
payParam.put("timeout_express", "30m"); //该笔订单允许的最晚付款时间,逾期将关闭交易
payParam.put("total_amount",totalAmount );//订单总金额,单位为元,精确到小数点后两位
// payParam.put("seller_id", AlipayConfig.SELLER_ID); //收款支付宝用户ID。 如果该值为空,则默认为商户签约账号对应的支付宝用户ID
payParam.put("product_code", "QUICK_MSECURITY_PAY");//销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
JSONObject bizcontentJson= JSONObject.fromObject(payParam);
System.out.println("支付宝业务参数=="+bizcontentJson);
//业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档
publicParam.put("biz_content", bizcontentJson.toString());
System.out.println("支付宝请求参数=="+publicParam);
//RSA签名
String rsaSign = AlipayUtil.rsaSign(publicParam);
System.out.println("支付宝签名参数=="+rsaSign);
Map<String, String> codeParam = new HashMap<String, String>();
codeParam.put("app_id", AlipayConfig.APP_ID);
codeParam.put("method", "alipay.trade.app.pay");
codeParam.put("format", "json");
codeParam.put("charset", AlipayConfig.CHARSET);
codeParam.put("sign_type", AlipayConfig.SIGN_TYPE);
codeParam.put("timestamp", URLEncoder.encode(nowTime,"UTF-8"));
codeParam.put("version", "1.0");
codeParam.put("notify_url", URLEncoder.encode(AlipayConfig.NOTIFY_URL,"UTF-8")); //通知接口
//最后对请求字符串的所有一级value(biz_content作为一个value)进行encode,编码格式按请求串中的charset为准,没传charset按UTF-8处理
codeParam.put("biz_content", URLEncoder.encode(bizcontentJson.toString(), "UTF-8"));
String data = AlipaySignature.getSignContent(codeParam); //拼接后的字符串
data = data + "&sign=" + URLEncoder.encode(rsaSign, "UTF-8");
System.out.println("支付宝支付参数=="+data);
Map<String, String> dataMap = new HashMap<>();
dataMap.put("data", data);
return MyResult.succMap(dataMap); //返回给前端处理
}
/**
*<p>方法说明: TODO 支付同步验证结果
*<p>参数说明: String data 成功为9000
*<p>参数说明: @return 支付状态 成功为1
*<p>创建时间: 2017年11月1日 上午9:13:41
**/
@RequestMapping(value="/front/alipayVerification",method=RequestMethod.POST)
Object alipayVerification(String data) {
if ("9000".equals(data)) {
return MyResult.succMap(1, "支付成功");
} else if ("8000".equals(data)) {
return MyResult.succMap(2, "正在处理中");
} else if ("4000".equals(data)) {
return MyResult.succMap(3, "订单支付失败");
} else if ("5000".equals(data)) {
return MyResult.succMap(4, "重复请求");
} else if ("6001".equals(data)) {
return MyResult.succMap(5, "用户中途取消");
} else if ("6002".equals(data)) {
return MyResult.succMap(6, "网络连接出错");
} else if ("6004".equals(data)) {
return MyResult.succMap(7, "支付结果未知");
} else {
return MyResult.succMap(8, "其他未知错误");
}
}
}
就是这样,完成统一下单功能,然后返回给用户端APP,APP拿到参数后请求支付宝APP进行支付,支付后,支付宝会异步通知后台,我们就接收到通知,对订单进行相应的处理就行,其实就是这样;还有其他的的支付,都大同小异,可以根据官方文档进行整合;基本上都是这种支付模式;我的封装也没有做到最好,有需要的朋友可以根据自己的想法进行再次封装;不太清楚的朋友可以与官方文档进行对照;也可以进行留言;希望能为大家带来一点帮助;
最后如果代码有什么错误,BUG,还请各位大牛帮忙指正; 真诚感谢............
来源:oschina
链接:https://my.oschina.net/u/3681868/blog/1563628