支付SDK
大约 3 分钟
简介
在我们学习开发个人项目的过程中,但涉及到支付业务的一般都需要微信支付或者支付宝支付,这两种是比较常见的。但对于我们个人开发者而言,没有企业资质或者是个体工商营业执照是无法申请到的,而且申请的过程比较繁琐,还要花上几百个大洋。
这时候,我们还可以选择个人就可以申请到的蓝兔支付。
官方防止。
申请蓝图支付
只需要:
- 一张正常激活的银行卡。
- 一个可以正常访问的网站(域名访问)。
- 50块钱签约费
我选择直接使用我的博客地址申请。
关键信息:
- 商户号
- 密钥
支付SDK开发
官方API 文档地址:https://www.ltzf.cn/doc
下面我们就需要按照官方提供的文档,对接支付接口,并封装成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.
结果截图
- 支付
- 退款
- 微信也有通知