引言
本文代码已提交至Github(版本号:
65e4ac8468af8af00546db391e158c8e611d9ab9
),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop
阅读本文前,有兴趣的同学可以参考我之前写的聚合支付的文章:
- 《淘东电商项目(52) -聚合支付开篇》
- 《淘东电商项目(53) -银联支付案例源码分析》
- 《淘东电商项目(54) -银联支付案例(同步与异步)》
- 《淘东电商项目(55) -支付系统核心表设计》
- 《淘东电商项目(56) -支付系统分布式事务的解决方案》
- 《淘东电商项目(57) -聚合支付(支付令牌接口)》
- 《淘东电商项目(58) -聚合支付(基于设计模式自动跳转支付接口)》
- 《淘东电商项目(59) -聚合支付(集成银联支付)》
- 《淘东电商项目(60) -聚合支付(集成支付宝)》
- 《淘东电商项目(61) -聚合支付(基于模板方法设计模式管理支付回调)》
目前「淘东电商项目」的聚合支付模块,已经完成了银联支付以及支付宝的集成,而且实现了基于"模板设计模式"的方式来管理银联支付
结果回调接口。上一篇博客讲的只是“银联支付”的回调管理,本文主要讲的是“支付宝”支付结果回调接口管理。由于代码跟上一篇博客类似,所以直接上源码并测试。
本文目录结构:
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
):
支付成功:
可以看到订单状态为已支付:
可以看到同步回调也是成功的:
本文完!