基于Vue+SpringCloudAlibaba微服务电商项目实战-聚合支付平台-018:基于策略模式重构设计聚合支付平台

1 简单回顾聚合支付整体架构流程

今日课程任务

  1. 如何保证参数安全传递到聚合支付平台
  2. 基于预提交支付参数形式保证接口安全性
  3. 如何设计提高聚合平台接口的扩展性
  4. 基于策略模式重构设计聚合支付平台

在这里插入图片描述

2 分析预支付提交token参数原理分析

参考腾讯课堂报名点击“去付款”,网站由ke.qq.com(腾讯课堂主站)跳转到pay.qq.com(腾讯内部聚合支付平台),跳转网址带token_id参数,采用token的形式隐藏参数真实性。

RPC接口如何保证支付接口参数安全传输

  1. 使用非对称加密 可以但是效率极低;
  2. 使用MD5对参数实现验证签名 采用post请求提交表单
  3. 直接采用token的形式(推荐)

用户下单的时候,调用订单服务接口,在订单表中插入一条订单的记录,同时调用支付服务接口产生一条预支付的记录。
预支付:提前把参数采用内部rpc传递给了支付系统,在支付表插入一条待支付状态订单的记录,返回令牌传递。生成的令牌称作为支付令牌,对应的value为支付的id。

3 支付接口提供生成预支付令牌接口

分析支付服务需要提供的接口:

  1. 提供给订单服务预提交参数,返回支付令牌。
  2. 根据支付令牌查询支付的参数、金额、订单提交form表单给支付宝。

一个大型项目如腾讯课堂、腾讯游戏、腾讯音乐,每个模块有自己独立的订单表,对接聚合支付平台如果只传递订单号不传金额,要额外查询以上所有模块的订单表不合理,因此预支付处理要直接传递金额。

创建模块mt-shop-service-api-pay、mt-shop-service-pay
引入依赖

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>3.0.0</version>
</dependency>

核心代码

public interface PayTokenService {
    /**
     *  预支付提交支付的参数
     * @param payOrderTokenDto
     * @return
     */
    @PostMapping("/toPayToken")
    BaseResponse<String> toPayOrderToken(@RequestBody PayOrderTokenDto payOrderTokenDto);
}
@RestController
public class PayTokenServiceImpl extends BaseApiService implements PayTokenService {
    @Autowired
    private PaymentTransactionMapper paymentTransactionMapper;
    @Autowired
    private TokenUtil tokenUtil;

    @Override
    public BaseResponse<String> toPayOrderToken(PayOrderTokenDto payOrderTokenDto) {
        // 1.验证参数
        String orderId = payOrderTokenDto.getOrderId();
        if (StringUtils.isEmpty(orderId)) {
            return setResultError("订单号码不能为空!");
        }
        Long payAmount = payOrderTokenDto.getPayAmount();
        if (payAmount == null) {
            return setResultError("金额不能为空!");
        }
        Long userId = payOrderTokenDto.getUserId();
        if (userId == null) {
            return setResultError("userId不能为空!");
        }
        String orderName = payOrderTokenDto.getOrderName();
        if (orderName == null) {
            return setResultError("orderName不能为空!");
        }
        // 2.生成支付的全局的id
        PaymentTransactionEntity paymentTransactionEntity =
                dtoToDo(payOrderTokenDto, PaymentTransactionEntity.class);
        // 3.向支付详细表插入一条记录
        paymentTransactionEntity.setPaymentId(SnowflakeIdUtils.nextId() + "");
        int result = paymentTransactionMapper.insertPaymentTransaction(paymentTransactionEntity);
        if (result <= 0) {
            return setResultError("系统错误");
        }
        Long payId = paymentTransactionEntity.getId();
        if (payId == null) {
            return setResultError("系统错误");
        }
        // 4.生成token令牌
        String payToken = tokenUtil.createToken("mayikt:pay", payId + "");
        if (StringUtils.isEmpty(payToken)) {
            return setResultError("生成支付令牌失败");
        }
        return setResultSuccess(payToken);
    }
}

测试效果:
在这里插入图片描述

4 基于策略模式分析聚合支付模块设计

支付渠道接口统一采用数据库的渠道表实现管理,对接支付宝、微信支付、其他支付接口。
基本上大多数支付接口设计思想都是一样,每个支付的渠道设计思想都是一样,唯一不同的就是对接支付代码生成html表单不一样。
策略模式可以解决多重if判断的问题。

5 代码实现定义策略类实现聚合支付

策略模式实现聚合支付
在这里插入图片描述
核心代码

@Component
public interface PayStrategy {

    /**
     * 聚合支付 共同抽象行为
     * @return
     */
    String toPayHtml();
}
@Component
public class AliPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml() {
        return "调用支付宝接口";
    }
}
@Component
public class UnionPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml() {
        return "调用银联的支付接口";
    }
}
@RestController
@Slf4j
public class PaymentAggregateServiceImpl extends BaseApiService implements PaymentAggregateService {

    @Autowired
    private PaymentChannelMapper paymentChannelMapper;

    @Override
    public BaseResponse<String> toPayHtml(String payToken, String channelId) {
        // 1.验证参数
        if (StringUtils.isEmpty(payToken)) {
            return setResultError("payToken不能为空!");
        }
        if (StringUtils.isEmpty(channelId)) {
            return setResultError("channelId不能为空!");
        }
        // 2.根据渠道id查询到详细渠道的信息
        PaymentChannelEntity pce = paymentChannelMapper.selectBychannelId(channelId);
        if (pce == null) {
            return setResultError("该渠道已关闭或不存在,请联系管理员");
        }

        // 4.根据beanid 从Spring容器中找到策略类执行
        String payBeanId = pce.getPayBeanId();
        if (StringUtils.isEmpty(payBeanId)) {
            return setResultError("没有配置payBeanId");
        }
        String data = payStrategy.toPayHtml();
        log.info("result:" + data);
        return setResultSuccess(data);
    }
}

测试效果:
在这里插入图片描述

6 聚合支付整体代码联调测试

数据库支付渠道表中填入阿里提供沙箱环境公钥、私钥和APPID等信息,完善AliPayStrategy代码生成form表单,测试整个预付款流程。

@RestController
@Slf4j
public class PaymentAggregateServiceImpl extends BaseApiService implements PaymentAggregateService {

    @Autowired
    private PaymentChannelMapper paymentChannelMapper;
    @Autowired
    private TokenUtil tokenUtil;
    @Autowired
    private PaymentTransactionMapper paymentTransactionMapper;

    @Override
    public BaseResponse<String> toPayHtml(String payToken, String channelId) {
        // 1.验证参数
        if (StringUtils.isEmpty(payToken)) {
            return setResultError("payToken不能为空!");
        }
        if (StringUtils.isEmpty(channelId)) {
            return setResultError("channelId不能为空!");
        }
        // 2.根据渠道id查询到详细渠道的信息
        PaymentChannelEntity pce = paymentChannelMapper.selectBychannelId(channelId);
        if (pce == null) {
            return setResultError("该渠道已关闭或不存在,请联系管理员");
        }

        // 4.根据beanid 从Spring容器中找到策略类执行
        String payBeanId = pce.getPayBeanId();
        if (StringUtils.isEmpty(payBeanId)) {
            return setResultError("没有配置payBeanId");
        }
        // 5.根据payToken查询到支付参数内容
        String tokenValue = tokenUtil.getTokenValue(payToken);
        if (StringUtils.isEmpty(tokenValue)) {
            return setResultError("支付已经超时");
        }
        // 6.获取我们的预支付的参数的信息
        Long payId = Long.parseLong(tokenValue);
        PaymentTransactionEntity ptc = paymentTransactionMapper.selectById(payId);
        if (ptc == null) {
            return setResultError("请不要随意攻击我们支付平台");
        }
        PayStrategy payStrategy = SpringContextUtils.getBean(payBeanId, PayStrategy.class);
        // 7.统一返回html表单给客户端
        String data = payStrategy.toPayHtml(pce, ptc);
        log.info("result:" + data);
        return setResultSuccess(data);
    }
}
@Component
@Slf4j
public class AliPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml(PaymentChannelEntity pce, PaymentTransactionEntity pte) {
        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(pce.getRequestAddress(),
                pce.getMerchantId(), pce.getPrivateKey(), "json",
                AlipayConfig.CHARSET, pce.getPublicKey(), AlipayConfig.SIGN_TYPE);
        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(pce.getSyncUrl());
        alipayRequest.setNotifyUrl(pce.getAsynUrl());
        String orderId = pte.getOrderId();
        alipayRequest.setBizContent("{\"out_trade_no\":\"" + pte.getPaymentId() + "\","
                + "\"total_amount\":\"" + pte.getPayAmount() / 100 + "\","
                + "\"subject\":\"" + pte.getOrderName() + "\","
                + "\"body\":\"" + pte.getOrderBody() + "\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        try {
            String result = alipayClient.pageExecute(alipayRequest).getBody();

            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

测试效果:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值