1、获取appid、秘钥、申请证书
https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml
wechat:
miniapp:
configs:
- appid: wx68fd8d5ac14cc768
secret: 64fcfbd73508e7e10e03b72770fe166c
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
pay:
appId: wx68***cc768
mchId: 16***4010
keyPath: ./WechatPayMerchantPrivateKey.pem
certPath: ./WechatPayMerchantCert.pem
platformCertPath: ./WechatPayPlatformCert.pem
apiKey3: mETKpE***3TB8yaLa
domain: https:***.cn
serialNo: 3C5B355***EA8755E
notifyURL: https://***/order/wechatpay/notify/
@ConfigurationProperties(prefix = "wechat.pay")
public class WechatPayV3Properties {
private String appId;
private String keyPath;
private String certPath;
private String platformCertPath;
private String mchId;
private String apiKey3;
private String domain;
private String serialNo;
private String notifyURL;
}
2、服务类,主要用于获取预支付id和签名,微信支付前需要拿到预支付id(prepayId),想要成功拉起支付要对预支付id进行签名才能支付。
@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(WechatPayV3Properties.class)
public class WechatPayJsApiServiceClient {
private final WechatPayV3Properties wechatPayV3Properties;
/**
* 获取小程序支付服务
*/
private JsapiService getAppService() {
// 初始化商户配置
RSAConfig config =
new RSAConfig.Builder()
.merchantId(wechatPayV3Properties.getMchId())
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
.privateKeyFromPath(wechatPayV3Properties.getKeyPath())
.merchantSerialNumber(wechatPayV3Properties.getSerialNo())
.wechatPayCertificatesFromPath(wechatPayV3Properties.getCertPath(), wechatPayV3Properties.getPlatformCertPath())
.build();
// 初始化服务
return new JsapiService.Builder().config(config).build();
}
/**
* 小程序支付下单
*
* @param desc 商品描述
* @param outTradeNo 交易订单号
* @param openId 支付者微信OpenId
* @param totalAmount 商品总金额(单位分)
*/
public PrepayResponse prepay(String desc, String outTradeNo, String openId, Integer totalAmount) {
PrepayRequest request = new PrepayRequest();
request.setAppid(wechatPayV3Properties.getAppId());
request.setMchid(wechatPayV3Properties.getMchId());
request.setDescription(desc);
request.setOutTradeNo(outTradeNo);
request.setNotifyUrl(wechatPayV3Properties.getNotifyURL());
Amount amount = new Amount();
amount.setTotal(totalAmount);
request.setAmount(amount);
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
// request.setTimeExpire(); 订单失效时间
// request.setSupportFapiao(); 传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效。
// 调用接口
return getAppService().prepay(request);
}
/**
* 微信支付订单号查询订单
*/
public Transaction queryOrderById() {
QueryOrderByIdRequest request = new QueryOrderByIdRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
// 调用接口
return getAppService().queryOrderById(request);
}
/**
* 商户订单号查询订单
*/
public Transaction queryOrderByOutTradeNo() {
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
// 调用接口
return getAppService().queryOrderByOutTradeNo(request);
}
/**
* 生成微信调起支付的签名
* @param wechatPayPerPayId 微信预支付交易会话标识
*/
public Map<String, Object> sign(String wechatPayPerPayId) throws IOException {
String privateKeyStr = IOUtil.loadStringFromPath(wechatPayV3Properties.getKeyPath());
PrivateKey privateKey = PemUtil.loadPrivateKeyFromString(privateKeyStr);
RSASigner rsaSigner = new RSASigner(wechatPayV3Properties.getSerialNo(), privateKey);
Long timestamp = new Date().getTime() / 1000;
//随机数最长32,这个随机数没有其他规定,随便给一个就可以
String nonceStr = CommonUtil.generate_random_str(32);
String encryptStr = String.format("%s\n%s\n%s\nprepay_id=%s\n", wechatPayV3Properties.getAppId(), timestamp, nonceStr, wechatPayPerPayId);
//v3支付固定前面类型RSA
SignatureResult signatureResult = rsaSigner.sign(encryptStr);
Map<String, Object> data = new HashMap<>();
data.put("appid", wechatPayV3Properties.getAppId());
data.put("wechatPayMerchantId", wechatPayV3Properties.getMchId());
data.put("timestamp",timestamp);
data.put("nonceStr", nonceStr);
data.put("sign", signatureResult.getSign());
data.put("wechatPayPerPayId", wechatPayPerPayId);
return data;
}
}
3、对应订单传入预支付接口获取预支付id,传入签名接口获取签名,返回给前端
@Transactional(rollbackFor = Exception.class)
@Override
public Map<String, Object> wechatPay(WechatPayDTO wechatPayDTO) throws CallBackException {
String wechatPayPrePayId = null;
Map<String, Object> signData = null;
try {
// 创建订单(传什么看需求)
//reseller.getName()"测试经销商"、orderDesc“充值订单”
String goodsDesc = String.format("%s-%s", reseller.getName(), orderDesc);
//order.getPayOrderNo()随机生成的订单号,reseller.getOpenId()小程序唯一标识,order.getTotalRealAmount()实付金额
PrepayResponse prepayResponse = wechatPayJsApiServiceClient.prepay(goodsDesc, order.getPayOrderNo(), reseller.getOpenId(), order.getTotalRealAmount());
wechatPayPrePayId = prepayResponse.getPrepayId();
// 生成签名
signData = wechatPayJsApiServiceClient.sign(wechatPayPrePayId);
} catch (Exception e) {
log.error(e.toString());
throw new CallBackException(BusinessResultCode.ERROR_WECHAT_ORDER_GENERATE_FAIL);
}
// 如果任一事务失败,触发回滚
if (updateOrderResult == 0 | !updateBatchResult | !createChargeLogResult) {
throw new CallBackException(ResultCode.FAILURE.getMessage());
}
map.put("data", signData);
map.put("result", "success");
return map;
}
4、前端拉起微信支付
function submitWeChatPay(orderID,payAmount,callback) {
if (typeof WeixinJSBridge != "undefined") {
var param = {
orderID:orderID,
payAmount:payAmount
}
httpPost(JSON.stringify(param),"http://xxxx/wechatpay/prepay",function (data, status) {
var param = {
"appId": data.appId,
"timeStamp": data.timeStamp,
"nonceStr": data.nonceStr,
"package": data.packageVal,
"signType": data.signType,
"paySign": data.paySign
}
WeixinJSBridge.invoke(
'getBrandWCPayRequest', param,callback);
})
} else {
alert("非微信环境")
}
}
5、微信回调接口,接口地址就是notifyURL
/**
* 微信支付回调
*/
@PostMapping("/wechatpay/notify")
@ApiOperationSupport(order = 7)
@ApiOperation(value = "微信支付回调", notes = "")
public R wechatPayNotify(@RequestHeader("Wechatpay-Serial") String serialNo,
@RequestHeader("Wechatpay-Signature") String signature,
@RequestHeader("Wechatpay-Timestamp") String timestamp,
@RequestHeader("Wechatpay-Nonce") String nonce,
@RequestBody String requestBody) {
log.info("==Serial:{}", serialNo);
log.info("==Signature:{}", signature);
log.info("==Timestamp:{}", timestamp);
log.info("==Nonce:{}", nonce);
log.info("==支付回调:{}", requestBody);
RequestParam requestParam = new RequestParam.Builder()
.signature(signature)
.body(requestBody)
.serialNumber(serialNo)
.timestamp(timestamp)
.nonce(nonce)
.build();
// 初始化 NotificationParser
RSANotificationConfig rsaNotificationConfig = new RSANotificationConfig.Builder()
.certificatesFromPath(wechatPayV3Properties.getCertPath())
.apiV3Key(wechatPayV3Properties.getApiKey3())
.build();
NotificationParser notificationParser = new NotificationParser(rsaNotificationConfig);
// 验签并解密报文
JSONObject decryptObject = notificationParser.parse(requestParam, JSONObject.class);
log.info("==>wechat pay callBack :{}", decryptObject.toJSONString());
return null;
}