准备工作
需要获取的信息
商户号 mch_id
公众号 appId
服务器地址 notify_url
api证书
密钥
通知地址
开发指引
微信支付
统一下单Demo下载
- 依赖
- 代码阅读(避免踩坑)
WXPayConfig
import com.github.wxpay.sdk.WXPayConfig;
import java.io.*;
public class MyConfig implements WXPayConfig{
private byte[] certData;
public MyConfig() throws Exception {
// 证书文件
String certPath = "/path/to/apiclient_cert.p12";
File file = new File(certPath);
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int) file.length()];
certStream.read(this.certData);
certStream.close();
}
public String getAppID() { // 公众号appId
return "wx8888888888888888";
}
public String getMchID() { // 商户号id
return "12888888";
}
public String getKey() { // 密钥
return "88888888888888888888888888888888";
}
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
public int getHttpConnectTimeoutMs() {
return 8000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
}
在微信支付官网下载的文档demo中 自己继承 WXPayConfig()类,.md文件中明显有漏洞。缺少一个需要从写的抽象方法
/**
*@description 获取WXPayDomain, 用于多域名容灾自动切换
*/
@Override
IWXPayDomain getWXPayDomain() {
// 这个方法需要这样实现, 否则无法正常初始化WXPay
IWXPayDomain iwxPayDomain = new IWXPayDomain() {
@Override
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
@Override
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
}
};
return iwxPayDomain;
}
WXPay
JSAPI文档中说默认的加密方式为MD5,实则是HMAC-SHA256
统一下单
Demo这里也存在一个问题,他这里和前端JSAPI需要的参数就不统一,前端要求对统一下单之后的参数再次进行签名计算,按照demo走下去会验证签名失败
二次签名计算,参考如下:(通知地址一定要设置,异步通知微信支付结果通知,成功、失败需要更新订单信息)
public static Map<String, String> createPrepayment(Map<String, String> data, WXPayConfiguration wxPayConfiguration) throws Exception {
MyConfig config= new MyConfig(wxPayConfiguration);
WXPay wxpay = new WXPay(config);
// 商品简单描述
data.put("body", "changsha pengyuyuan");
// 商户系统内部订单号,要求32个字符内(最少6个字符),只能是数字、大小写字母_-|*且在同一个商户号下唯一。
/*String out_trade_no = UUIDUtil.generateUUID();
data.put("out_trade_no", out_trade_no);*/
data.put("device_info", "WEB");
// 默认人民币:CNY
data.put("fee_type", "CNY");
// 金额 单位为分establishListInfo
data.put("total_fee", new BigDecimal(String.valueOf(data.get("total_fee"))).multiply(new BigDecimal("100")) + "");
// data.put("total_fee", "1");
// 终端IP
data.put("spbill_create_ip", wxPayConfiguration.getDomain());
// 通知地址
data.put("notify_url", wxPayConfiguration.getNotifyDomain());
// 此处指定为扫码支付
data.put("trade_type", "JSAPI");
try {
Map<String, String> resp = wxpay.unifiedOrder(data);
log.info("第一次验签:{}",resp);
// 参数需要重新进行签名计算
if ("SUCCESS".equals(resp.get("return_code"))) {
// 返回给前端的参数,前端再调起支付接口
Map<String,String> repData = new HashMap<>();
// 注意参数要区分大小写
repData.put("appId", wxPayConfiguration.getAppid());
// 这个参数巨恶心 务必这样写 返回给前端 否则前端一直报错
String pkg = "prepay_id="+ resp.get("prepay_id");
repData.put("package",pkg);
// 要添加签名方式
repData.put("signType","HMAC-SHA256");
repData.put("nonceStr", WXPayUtil.generateNonceStr());
repData.put("timeStamp",String.valueOf(System.currentTimeMillis()/1000));
//签名
String sign = WXPayUtil.generateSignature(repData, wxPayConfiguration.getApiV3Key(), WXPayConstants.SignType.HMACSHA256);
repData.put("prepayId",resp.get("prepay_id"));
repData.put("mchId", wxPayConfiguration.getMchId());
repData.put("paySign",sign);
log.info("第二次验签:{}",repData);
return repData;
}
return resp;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
JSAPI调起支付
JSAPI文档中说默认的加密方式为MD5,实则是HMAC-SHA256,前端copy过来记得改一下
最佳实践
前端出现的其他问题参考这位大佬的博客,总结的很到位:
https://blog.csdn.net/qq_38371367/article/details/87195489