跳至主要內容

支付SDK

科哒大约 3 分钟

简介

在我们学习开发个人项目的过程中,但涉及到支付业务的一般都需要微信支付或者支付宝支付,这两种是比较常见的。但对于我们个人开发者而言,没有企业资质或者是个体工商营业执照是无法申请到的,而且申请的过程比较繁琐,还要花上几百个大洋。

这时候,我们还可以选择个人就可以申请到的蓝兔支付。

官方防止。

申请蓝图支付

只需要:

  1. 一张正常激活的银行卡。
  2. 一个可以正常访问的网站(域名访问)。
  3. 50块钱签约费

我选择直接使用我的博客地址申请。

关键信息:

  • 商户号
  • 密钥

支付SDK开发

官方API 文档地址:https://www.ltzf.cn/docopen in new window

下面我们就需要按照官方提供的文档,对接支付接口,并封装成SDK

分析和验证支付接口

蓝图支付调用流程分析:

  • 参数加签
  • 发起请求等待响应
  • 参数是否有配置回调地址,发送回调通知。

工程搭建

签名工具类

发起请求的参数需要进行参数加签,按照官方提供的加签方式加签,保证请求参数的安全。

根据官方文档可以快速的生成工具类:

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.TreeMap;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @description 签名工具类
 */
public class SignUtils {

    public static String createSign(Map<String, String> params, String partnerKey) {
        // 生成签名前先去除sign
        params.remove("sign");
        String stringA = packageSign(params, false);
        String stringSignTemp = stringA + "&key=" + partnerKey;
        return DigestUtils.md5Hex(stringSignTemp).toUpperCase();
    }

    private static String packageSign(Map<String, String> params, boolean urlEncoder) {
        // 先将参数以其参数名的字典序升序进行排序
        TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
        // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> param : sortedParams.entrySet()) {
            String value = param.getValue();
            if (StringUtils.isBlank(value)) {
                continue;
            }
            if (first) {
                first = false;
            } else {
                sb.append("&");
            }
            sb.append(param.getKey()).append("=");
            if (urlEncoder) {
                try {
                    value = urlEncode(value);
                } catch (UnsupportedEncodingException e) {
                }
            }
            sb.append(value);
        }
        return sb.toString();
    }

    private static String urlEncode(String src) throws UnsupportedEncodingException {
        return URLEncoder.encode(src, UTF_8.name()).replace("+", "%20");
    }

}

基于retrofit封装请求

以微信扫码支付为例

说明:

  • 微信扫码支付nativepay
  • 所有的请求对象,响应结果接收对象单独实体接受,不做成公共的,遵循设计原则。
  • PayFactory工厂模式,获取单例,保持结构清晰,代码职责划分清楚。

测试微信支付用例子

import com.ke.ltzf.factory.Configuration;
import com.ke.ltzf.factory.PayFactory;
import com.ke.ltzf.factory.defaults.DefaultPayFactory;
import com.ke.ltzf.payments.nativepay.NativePayService;
import com.ke.ltzf.payments.nativepay.model.*;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before;
import org.junit.Test;

@Slf4j
public class NativePayServiceTest {

    private NativePayService nativePayService;

    @Before
    public void init() {
        Configuration configuration = new Configuration(
                "", "1683930880", "190b0008bcd3cf226e6d08b4aff1d396"
        );

        PayFactory payFactory = new DefaultPayFactory(configuration);
        this.nativePayService = payFactory.nativePayService();
    }

    @Test
    public void test_prepay() throws Exception {
        // 1. 请求参数
        PrepayRequest request = new PrepayRequest();
        request.setMchid("1683930880");
        request.setOutTradeNo(RandomStringUtils.randomNumeric(8));
        request.setTotalFee("0.01");
        request.setBody("QQ公仔");
        request.setNotifyUrl("https://www.kedayyds.top/");

        // 2. 创建支付订单
        PrepayResponse response = nativePayService.prepay(request);

        log.info("请求参数:{}", JSON.toJSONString(request));
        log.info("应答结果:{}", JSON.toJSONString(response));

    }
    @Test
    public void test_queryOrderByOutTradeNo() throws Exception {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid("1683930880");
        request.setOutTradeNo("17545189");
        QueryOrderByOutTradeNoResponse response = nativePayService.queryOrderByOutTradeNo(request);
        log.info("请求参数: {}", JSON.toJSONString(request));
        log.info("应答结果: {}", JSON.toJSONString(response));
    }

    @Test
    public void test_refundOrder() throws Exception {
        RefundOrderRequest request = new RefundOrderRequest();
        request.setMchid("1683930880");
        request.setOutTradeNo("17545189");
        request.setOutRefundNo(RandomStringUtils.randomNumeric(8));
        request.setRefundFee("0.01");
        request.setRefundDesc("测试退款");
        request.setNotifyUrl("https://www.kedayyds.top/api");

        Object response = nativePayService.refundOrder(request);
        log.info("请求参数: {}", JSON.toJSONString(request));
        log.info("应答结果: {}", JSON.toJSONString(response));
    }

    @Test
    public void test_getRefundOrder() throws Exception {
        GetRefundOrderRequest request = new GetRefundOrderRequest();
        request.setMchid("1683930880");
        request.setOutRefundNo("32848640");

        GetRefundOrderResponse response = nativePayService.getRefundOrder(request);
        log.info("请求参数: {}", JSON.toJSONString(request));
        log.info("应答结果: {}", JSON.toJSONString(response));
    }

}
  • 将商户号,密钥填写到配置类中。
  • 依次执行测试用例,验证结果,支付0.01 和退款0.01.

结果截图

  • 支付
  • 退款
  • 微信也有通知

项目地址

https://gitee.com/keyyds/lantu-pay-sdkopen in new window

上次编辑于:
贡献者: 黄科铭