引言
本文代码已提交至Github(版本号:
65e4ac8468af8af00546db391e158c8e611d9ab9
),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop
阅读本文前,有兴趣的同学可以参考我之前写的聚合支付的文章:
- 《淘东电商项目(52) -聚合支付开篇》
- 《淘东电商项目(53) -银联支付案例源码分析》
- 《淘东电商项目(54) -银联支付案例(同步与异步)》
- 《淘东电商项目(55) -支付系统核心表设计》
- 《淘东电商项目(56) -支付系统分布式事务的解决方案》
- 《淘东电商项目(57) -聚合支付(支付令牌接口)》
- 《淘东电商项目(58) -聚合支付(基于设计模式自动跳转支付接口)》
- 《淘东电商项目(59) -聚合支付(集成银联支付)》
- 《淘东电商项目(60) -聚合支付(集成支付宝)》
- 《淘东电商项目(61) -聚合支付(基于模板方法设计模式管理支付回调)》
目前「淘东电商项目」的聚合支付模块,已经完成了银联支付以及支付宝的集成,而且实现了基于"模板设计模式"的方式来管理银联支付
结果回调接口。上一篇博客讲的只是“银联支付”的回调管理,本文主要讲的是“支付宝”支付结果回调接口管理。由于代码跟上一篇博客类似,所以直接上源码并测试。
本文目录结构:
l____引言
l____ 1. 支付宝支付结果核心源码
l____ 2. 测试
1. 支付宝支付结果核心源码
①模板实现源码:
/**
* description: 阿里支付回调模版实现
* create by: YangLinWei
* create time: 2020/5/18 10:41 上午
*/
@Component
public class AliPayCallbackTemplate extends AbstractPayCallbackTemplate {
@Autowired
private PaymentTransactionMapper paymentTransactionMapper;
@Override
public Map<String, String> verifySignature(HttpServletRequest request, HttpServletResponse resp) throws UnsupportedEncodingException, AlipayApiException {
Map<String, String> reqParam = new HashMap<>();
//获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = 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] + ",";
}
//乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
/* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
if (signVerified) {//验证成功
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付宝交易号
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");
reqParam.put("out_trade_no", out_trade_no);
reqParam.put("trade_no", trade_no);
reqParam.put("trade_status", trade_status);
reqParam.put(PayConstant.RESULT_NAME, PayConstant.RESULT_PAYCODE_200);
} else {//验证失败
reqParam.put(PayConstant.RESULT_NAME, PayConstant.RESULT_PAYCODE_201);
//调试用,写文本函数记录程序运行情况是否正常
//String sWord = AlipaySignature.getSignCheckContentV1(params);
//AlipayConfig.logResult(sWord);
}
return reqParam;
}
@Override
public String syncService(HttpServletRequest request, HttpServletResponse resp) throws UnsupportedEncodingException, AlipayApiException {
//获取支付宝GET过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (Iterator<String> iter = 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] + ",";
}
//乱码解决,这段代码在出现乱码时使用
valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
//——请在这里编写您的程序(以下代码仅作参考)——
if (signVerified) {
//商户订单号
String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
//支付宝交易号
String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
//付款金额
String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");
return "trade_no:" + trade_no + "<br/>out_trade_no:" + out_trade_no + "<br/>total_amount:" + total_amount;
}
return "验签失败";
}
@Override
public String asyncService(Map<String, String> verifySignature) {
String out_trade_no = verifySignature.get("out_trade_no");
String trade_no = verifySignature.get("trade_no");
String trade_status = verifySignature.get("trade_status");
if (trade_status.equals("TRADE_FINISHED")) {
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
return failResult();
} else if (trade_status.equals("TRADE_SUCCESS")) {
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//如果有做过处理,不执行商户的业务程序
//注意:
//付款完成后,支付宝系统发送该交易状态通知
// 根据日志 手动补偿 使用支付id调用第三方支付接口查询
PaymentTransactionEntity paymentTransaction = paymentTransactionMapper.selectByPaymentId(out_trade_no);
if (paymentTransaction.getPaymentStatus().equals(PayConstant.PAY_STATUS_SUCCESS)) {
// 网络重试中,之前已经支付过
return successResult();
}
// 2.将状态改为已经支付成功
paymentTransactionMapper.updatePaymentStatus(PayConstant.PAY_STATUS_SUCCESS + "", out_trade_no);
// 3.调用积分服务接口增加积分(处理幂等性问题)
return successResult();
}
return failResult();
}
@Override
public String failResult() {
return PayConstant.ALI_RESULT_SUCCESS;
}
@Override
public String successResult() {
return PayConstant.ALI_RESULT_FAIL;
}
}
②请求接口:
/**
* description: 阿里同步回调接口执行代码
* create by: YangLinWei
* create time: 2020/5/18 10:08 上午
*/
@RequestMapping("/aliPaySynCallback")
public String aliPaySynCallback(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException, AlipayApiException {
AbstractPayCallbackTemplate abstractPayCallbackTemplate = TemplateFactory
.getPayCallbackTemplate(ALIPAYCALLBACK_TEMPLATE);
return abstractPayCallbackTemplate.syncService(req, resp);
}
/**
* description: 阿里异步回调接口执行代码
* create by: YangLinWei
* create time: 2020/5/18 10:08 上午
*/
@RequestMapping("/aliPayAsynCallback")
public String aliPayAsynCallback(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException, AlipayApiException {
AbstractPayCallbackTemplate abstractPayCallbackTemplate = TemplateFactory
.getPayCallbackTemplate(ALIPAYCALLBACK_TEMPLATE);
return abstractPayCallbackTemplate.asyncCallBack(req, resp);
}
2. 测试
首先启动项目(Eureka注册中心、单点服务中心、会员服务、支付服务、支付门户):
①配置银联支付的同步和异步回调地址,其SQL语句如下:
INSERT INTO `taodong-pay`.`payment_channel`(`ID`, `CHANNEL_NAME`, `CHANNEL_ID`, `MERCHANT_ID`, `SYNC_URL`, `ASYN_URL`, `PUBLIC_KEY`, `PRIVATE_KEY`, `CHANNEL_STATE`, `REVISION`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `CLASS_ADDRES`) VALUES (2, '支付宝', 'ali_pay', '777666655522521', 'http://localhost:8600/aliPaySynCallback', 'http://7z4nwp.natappfree.cc/aliPayAsynCallback', NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 'com.ylw.service.pay.strategy.impl.AliPayStrategy');
②浏览器模拟提交订单:请求地址http://localhost:8600/cratePayToken?payAmount=99999&orderId=20200513141452&userId=27&productName=广东米酒
数据库订单状态为未支付:
③模拟使用银联支付,浏览器请求:http://localhost:8079/pay?payToken=pay_4366baebae434bc0af4e440fb8c89b64
④.点击支付宝支付,按照提示一步一步支付至完成(使用测试账号支付,账号:oibawh1821@sandbox.com
,密码和支付密码均为:111111
):
支付成功:
可以看到订单状态为已支付:
可以看到同步回调也是成功的:
本文完!