前言
本文记录了微信扫码支付(API V3版本native支付 Java版)实操过程。客户需求:业务系统内根据商品价格自动生成支付二维码,用户扫码后调启微信支付。查阅了微信支付官方文档,Navite支付产品符合需求预期。这里直接使用的微信支付提供的SDK,免去了自己加签验签的烦恼。
一、native支付是什么?
Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。详见微信官网介绍
https://pay.weixin.qq.com/docs/merchant/products/native-payment/introduction.html
二、使用步骤
1.接入前准备
- merchantId 商户id
- apiV3Key V3版API密钥key
- appId 微信应用标识,公众号/小程序/APP
- 支付证书
支付证书 - merchantSerialNumber 商户证书序列号
2.引入依赖
```
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.14</version>
</dependency>
```
3.证书配置
本文将微信证书放入到项目的resources文件夹下
4.代码设置
新建一个名为PayController.java的Controller类,代码如下
//微信商户号和密钥
private String merchantId = "16xxxxxxxx";
private String apiV3Key = "D5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
private String appId = "wxxxxxxxxxxxxx";
//支付回调地址
private final static String notifyUrl = "https://xxxxx/api/pay/v1/payCallBack";
private String privateKeyName = "wechat/apiclient_key.pem";
private String privateKeyString = "";
private PrivateKey privateKey;
/**
* 商户证书序列号
*/
private String merchantSerialNumber = "407xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Config config;
public PayController() {
try {
ClassPathResource classPathResource = new ClassPathResource(privateKeyName);
privateKeyString = IOUtils.toString(classPathResource.getInputStream(), "utf8");
config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKeyString)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
privateKey = getPrivateKey(privateKeyName);
} catch (Exception e) {
e.printStackTrace();
}
}
public ResultData createUserOrder(@RequestBody PayOrderDTO payOrderDTO) {
try {
// 构建service
NativePayService service = new NativePayService.Builder().config(config).build();
//构建支付请求参数
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
//微信支付单位为分,将元转为分
amount.setTotal(金额 * 100));
request.setAmount(amount);
request.setAppid(appId);
request.setMchid(merchantId);
request.setDescription(productDO.getName());
request.setNotifyUrl(notifyUrl);
Date date = new Date();
request.setOutTradeNo("hxt" + DateUtil.formatDate(date, "yyyyMMddHHmmssSSS") + RandomUtil.randomNumbers(6));
// 调用下单方法,得到应答
PrepayResponse response = service.prepay(request);
// 使用微信扫描 code_url 对应的二维码,即可体验Native支付
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("order_id", request.getOutTradeNo());
hashMap.put("qrcode_url", response.getCodeUrl());
return new ResultData(true, hashMap);
} catch (Exception e) {
LOGGER.error("createUserOrder error: {}", e.getMessage());
return new ResultData(false, e.getMessage(), "");
}
}
调用微信接口成功后,正常返回
{
“code_url” : “weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00”
}
将此code_url的内容在前端转换成二维码,通过微信扫码即可调启微信支付,如下图
5.前端调用
前端调用接口生成二维码展示即可,这不是本文的重点。
6.支付回调
上文中提到的支付回调地址payCallBack代码如下
@PostMapping(value = "/v1/payCallBack")
public ResultData payCallBack(@RequestBody String body) {
try {
LOGGER.error("payCallBack body: ", body);
JSONObject jsonObject = JSONObject.parseObject(body);
if (jsonObject.size() > 0) {
NotifationDO notifationDO = jsonObject.toJavaObject(NotifationDO.class);
String ciphertext = jsonObject.getJSONObject("resource").getString("ciphertext");
String associated_data = jsonObject.getJSONObject("resource").getString("associated_data");
String nonce = jsonObject.getJSONObject("resource").getString("nonce");
String decryText = decryptText(ciphertext, associated_data, nonce);
JSONObject decryJsonObject = JSONObject.parseObject(decryText);
//业务代码处理
......
}
} catch (Exception e) {
LOGGER.error("payCallBack error: {}", e.getMessage());
}
return new ResultData(ResultStatusEnum.SUCCESS.getCode(), "");
}
为了保证安全性,微信支付在回调通知,对关键信息进行了AES-256-GCM加密。需要对返回的数据进行解密后,做相应的业务处理
private String decryptText(String ciphertext, String associated_data, String nonce) {
try {
String decryptData = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
return decryptData;
} catch (Exception e) {
return e.getMessage();
}
}
总结
以上就是对微信扫码支付介绍,实际项目中不能完全依赖支付回调,应结合查询订单接口主动查询订单状态,完成下一步的业务逻辑。
查询订单可通过微信支付订单号和商户订单号两种方式查询。