淘东电商项目(62) -聚合支付(基于模板方法设计模式管理支付回调-支付宝)

简介: 淘东电商项目(62) -聚合支付(基于模板方法设计模式管理支付回调-支付宝)

引言

本文代码已提交至Github(版本号:65e4ac8468af8af00546db391e158c8e611d9ab9),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop

阅读本文前,有兴趣的同学可以参考我之前写的聚合支付的文章:

目前「淘东电商项目」的聚合支付模块,已经完成了银联支付以及支付宝的集成,而且实现了基于"模板设计模式"的方式来管理银联支付结果回调接口。上一篇博客讲的只是“银联支付”的回调管理,本文主要讲的是“支付宝”支付结果回调接口管理。由于代码跟上一篇博客类似,所以直接上源码并测试。

本文目录结构:

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):


支付成功:

可以看到订单状态为已支付:

可以看到同步回调也是成功的:

本文完!

目录
相关文章
|
1月前
|
设计模式 算法 Java
模板方法--设计模式
模板方法--设计模式
25 0
|
1月前
|
设计模式 算法 Java
模板方法设计模式(TemplateMethod)
模板方法设计模式(TemplateMethod)
|
1月前
|
设计模式
二十三种设计模式全面解析-组合模式与享元模式的结合应用:实现对象的共享和高效管理
二十三种设计模式全面解析-组合模式与享元模式的结合应用:实现对象的共享和高效管理
|
1月前
|
设计模式 算法 Java
Java一分钟之-设计模式:策略模式与模板方法
【5月更文挑战第17天】本文介绍了策略模式和模板方法模式,两种行为设计模式用于处理算法变化和代码复用。策略模式封装不同算法,允许客户独立于具体策略进行选择,但需注意选择复杂度和过度设计。模板方法模式定义算法骨架,延迟部分步骤给子类实现,但过度抽象或滥用继承可能导致问题。代码示例展示了两种模式的应用。根据场景选择合适模式,以保持代码清晰和可维护。
31 1
|
1月前
|
设计模式 Java API
【设计模式】JAVA Design Patterns——Aggregator Microservices(聚合器微服务模式)
【设计模式】JAVA Design Patterns——Aggregator Microservices(聚合器微服务模式)
|
3天前
|
设计模式 算法
模板方法-大话设计模式
模板方法-大话设计模式
6 0
|
21天前
|
设计模式 存储 Java
JavaSE——面向对象高级二(2/4)-final关键字、常量、抽象类(认识抽象类、抽象类的好处、应用场景-模板方法设计模式)
JavaSE——面向对象高级二(2/4)-final关键字、常量、抽象类(认识抽象类、抽象类的好处、应用场景-模板方法设计模式)
14 0
|
1月前
|
设计模式 Java
【设计模式】文件目录管理是组合模式吗?
【设计模式】文件目录管理是组合模式吗?
21 0
|
1月前
|
设计模式 算法 Java
【设计模式】springboot3项目整合模板方法深入理解设计模式之模板方法(Template Method)
【设计模式】springboot3项目整合模板方法深入理解设计模式之模板方法(Template Method)
|
1月前
|
设计模式 存储 安全
C++多线程管理的艺术:从基础到设计模式
C++多线程管理的艺术:从基础到设计模式
78 0

热门文章

最新文章