使用APIv3 Java SDK接入微信小程序支付
这里使用的是apiv3
的0.2.10
版本。
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.10</version>
</dependency>
在实现之前看文档让人头疼,各种版本的文档东一块西一块的,来回跳转,还有一些名词也让我感到头昏。
但是实际上手后又感觉蛮简单的,可能是我初次接触的原因吧。
这里分成四个部分,分别是:配置部分、controller、service和util。
- 配置部分内容是 appid、secret、apiV3key、MCHID、NotifyUrl等。
- service用来处理支付和退款的逻辑。
- controller 用来处理支付和退款的回调。
- util用来处理回调参数和加载私钥文件。
小程序方面只需要根据后端返回的参数调起支付就好了。
配置部分
在resources目录下创建配置文件config.properties
,如果你使用了git等版本管理工具,记得将其添加到 .gitignore 中。
miniapp.appid=x
miniapp.secret=x
miniapp.salt=x
miniapp.mchid=x
miniapp.serialNo=x
miniapp.apiV3key=x
miniapp.payNotifyUrl=https://xxx.com/notify/pay
miniapp.refundNotifyUrl=https://xxx.com/notify/refund
添加WxPayConfig.java
@Configuration
@PropertySource("classpath:config.properties") //读取配置文件
@ConfigurationProperties(prefix="miniapp")
public class WxPayConfig {
private String appid;
// 省略其他属性
public String getAppid(){
return appid;
}
public String setAppid(String appid) {
this.appid = appid;
}
}
实现service
添加WechatPayService
。
@Service
public class WechatPayService {
public static JsapiServiceExtension jsapiServiceExtension;
public static RefundService refundService;
private final WxPayConfig wxPayConfig;
public static final String CNY = "CNY";
@Autowired
public WechatPayService(WxPayConfig wxPayConfig) throws Exception {
this.wxPayConfig = wxPayConfig;
this.init();
}
@PostConstruct
public void init() throws Exception {
String privateKey = WxPayUtil.loadKeyByResource("wxpay/apiclient_key.pem");
// 初始化商户配置
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(wxPayConfig.getMCHID())
.privateKey(privateKey)
.merchantSerialNumber(wxPayConfig.getSERIAL_NO())
.apiV3Key(wxPayConfig.getApiV3key())
.build();
// 初始化服务
jsapiServiceExtension =
new JsapiServiceExtension.Builder()
.config(config)
.signType("RSA") // 不填默认为RSA
.build();
refundService = new RefundService.Builder().config(config).build();
}
/**
* JSAPI支付下单,并返回JSAPI调起支付数据
* @param details 订单描述
* @param outTradeNo id
* @param money 金额
* @param openId 用户openid
* @param type 购买商品 goods、充值 charge
* @return PrepayWithRequestPaymentResponse 支付信息
*/
public PrepayWithRequestPaymentResponse prepayWithRequestPayment(String details, String outTradeNo,
BigDecimal money, String openId, OrderType type) {
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(decimalToInt(money));
amount.setCurrency(CNY);
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
request.setAmount(amount);
request.setAttach(type.getName());
request.setAppid(wxPayConfig.getAPPID());
request.setMchid(wxPayConfig.getMCHID());
request.setOutTradeNo(outTradeNo);
request.setDescription(details);
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
// 调用接口
return jsapiServiceExtension.prepayWithRequestPayment(request);
}
/**
* 退款申请
**/
public Refund createRefund(String outTradeNo, BigDecimal finalAmount) {
CreateRequest request = new CreateRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo("REFUND_" + outTradeNo);
AmountReq amount = new AmountReq();
amount.setTotal(decimalToLong(finalAmount));
amount.setRefund(decimalToLong(finalAmount));
amount.setCurrency(CNY);
request.setAmount(amount);
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
// 调用接口
return refundService.create(request);
}
private static int decimalToInt(BigDecimal money) {
return money.multiply(BigDecimal.valueOf(100)).intValue();
}
private static long decimalToLong(BigDecimal money) {
return money.multiply(BigDecimal.valueOf(100)).longValue();
}
实现util
添加WechatPayUtil.java
@Service
public class WxPayUtil {
private final WxPayConfig wxPayConfig;
@Autowired
public WxPayUtil(WxPayConfig wxPayConfig) {
this.wxPayConfig = wxPayConfig;
}
public <T> T getNotificationParser(HttpServletRequest request, Map<String, Object> body, Class<T> clazz) throws Exception {
String privateKey = loadKeyByResource("wxpay/apiclient_key.pem");
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(request.getHeader("Wechatpay-Serial"))
.nonce(request.getHeader("Wechatpay-Nonce"))
.signature(request.getHeader("Wechatpay-Signature"))
.timestamp(request.getHeader("Wechatpay-Timestamp"))
.signType(request.getHeader("Wechatpay-Signature-Type"))
.body(JSON.toJSONString(body))
.build();
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxPayConfig.getMCHID())
.privateKey(privateKey)
.merchantSerialNumber(wxPayConfig.getSERIAL_NO())
.apiV3Key(wxPayConfig.getApiV3key())
.build();
NotificationParser parser = new NotificationParser(config);
return parser.parse(requestParam, clazz);
}
/**
* 通过文件路径获取文件内容
* ClassPathResource可以在jar包中运行,但不能使用其中getFile().getPath()
* @param path 文件路径
* @return 文件内容
* @throws Exception 报错信息
*/
public static String loadKeyByResource(String path) throws Exception {
ClassPathResource resource = new ClassPathResource(path);
byte[] byteArray = FileCopyUtils.copyToByteArray(resource.getInputStream());
return new String(byteArray, StandardCharsets.UTF_8);
}
}
实现回调
添加WeChatPayCallbackController.java
private final WechatPayUtil wechatPayUtil;
@Autowired
public WeChatPayCallbackController(WxPayUtil WechatPayUtil) {
this.WechatPayUtil = WechatPayUtil;
}
@RestController
@RequestMapping("/notify")
public class WeChatPayCallbackController {
@PostMapping(value = "/pay")
public HashMap<String, String> callBackWeiXinPay(HttpServletRequest request, HttpServletResponse response, @RequestBody Map<String, Object> body) {
Transaction payTransaction = wxPayUtil.getNotificationParser(request, body, Transaction.class);
String outTradeNo = payTransaction.getOutTradeNo();
// 根据payTransaction中的信息编写一些自己的实现逻辑
}
@PostMapping("/refund")
public HashMap<String, String> callback(HttpServletRequest request, HttpServletResponse response,
@RequestBody Map<String, Object> body) {
RefundNotification refundNotification = wxPayUtil.getNotificationParser(request, body, RefundNotification.class);
String orderTradeNo = refundNotification.getOutTradeNo();
// 根据refundNotification中的信息编写一些自己的实现逻辑
}
public HashMap<String, String> generateResponseToWxPayCallback(String code, String message) {
HashMap<String, String> jsonResponse = new HashMap<>();
jsonResponse.put("code", code);
jsonResponse.put("message", message);
return jsonResponse;
}
}
这是微信支付通知的文档退款通知,支付通知,可以根据这些信息编写支付和退款的逻辑,比如扣减库存、设置订单状态。
小程序部分
这里比较简单,前端把后端需要的订单数据传给后端,后端将从微信得到的PrepayWithRequestPaymentResponse
返回给前端,
注意将其中的packageVal
改为package
。
const doPay = (res) => {
const params = res;
params['package'] = res.packageVal;
delete params.packageVal;
uni.requestPayment({
...params,
success: res => {
// 做一些处理
},
fail: f => {
if (f.errMsg.includes('cancel')) {
uni.showToast({
title: '已取消',
icon: 'none'
});
}
}
});
}
const getPaymentInfo = async () => {
const res = await createOrder({})
if(res.xxx) {
doPay(res)
}
}