背景
目前大多数APP收款方式都依赖于第三方支付,其中微信得受众是最广泛得,本文讲讲如何简单得调用微信支付;
微信支付官方API
接入准备
配置相关信息
配置一下证书和V3key,简简单单,证书生成工具跟着微信指引走就行证书申请指引
后端拉起微信支付
可以看到API中得 APP下单 需要得一些参数 然后我们写个demo
先简单得写个微信支付的model
package com.meet.utils.wechat.model;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author Awei
* @Date 2023 02 16 16 10
* @Desc 微信APP支付model
**/
@Data
@NoArgsConstructor
public class WechatPayModel {
protected String appid;
protected String mchid;
protected String description;
protected String out_trade_no;
protected String notify_url;
protected WechatAmount amount;
/**
* @param appid 由微信生成的应用ID,全局唯一。请求基础下单接口时请注意APPID的应用属性,应为公众号的APPID
* @param out_trade_no 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
* @param mchid 直连商户的商户号,由微信支付生成并下发。
* @param description 商品描述
* @param notify_url 通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。
*/
public WechatPayModel(String appid, String out_trade_no, String mchid, String description, String notify_url,
Long total) {
this.appid = appid;
this.out_trade_no = out_trade_no;
this.mchid = mchid;
this.description = description;
this.notify_url = notify_url;
this.amount = new WechatAmount(total, "CNY");
}
}
然后简单的写个配置类
package com.meet.utils.wechat;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* @Author Awei
* @Date 2023 02 16 15 48
**/
@Slf4j
@Data
@Configuration
public class WechatConfig {
/**
* APP 的 appId
*/
private String appId;
/**
* 商户API证书的证书序列号
*/
String merchantSerialNumber;
/**
* 商户API私钥
*
*/
String merchantPrivateKey;
/**
* 商户v3Key
*/
String v3Key;
/**
* 商户号
*/
String merchantId;
/**
* 回调地址
*/
String callBackUrl;
}
当然不能忘了依赖
<!-- 微信授权登录相关 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.2.4.B</version>
</dependency>
<!-- 微信支付相关 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.2.4.B</version>
</dependency>
然后开始进行调用
public String unifyAppPay(UnifyPayParams payParams) throws Exception {
String appUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
// 请求body参数 当然这里需要构建一些请求参数 订单号 商品描述 金额等
WechatPayModel wechatPayModel = new WechatPayModel(config.getAppId(), payParams.getOrderNo(), config.getMerchantId(), payParams.getOrderSubject(),
config.getCallBackUrl(), payParams.getAmount());
//然后调用VX方法
CloseableHttpResponse response = getCloseableHttpResponse(appUrl, JSON.toJSONString(wechatPayModel));
JSONObject jsonString = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
//最后获取到一个prepay_id预支付交易会话标识
return jsonString.getString("prepay_id");
}
private CloseableHttpResponse getCloseableHttpResponse(String h5Url, String s) throws Exception {
String reqdata = s;
HttpPost httpPost = new HttpPost(h5Url);
StringEntity entity = new StringEntity(reqdata, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//这里就需要一些 商户号 证书序列号 私钥 通过选择器去选择证书
CloseableHttpClient closeableHttpClient = getWechatHttpClient(config.getMerchantId(), config.getMerchantSerialNumber(), config.getMerchantPrivateKey());
return closeableHttpClient.execute(httpPost);
}
//通过相关参数进行选择证书实例
private CloseableHttpClient getWechatHttpClient(String merchantId, String merchantSerialNumber, String merchantPrivateKey) throws Exception {
PrivateKey privateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(merchantPrivateKey.getBytes("utf-8")));
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
new PrivateKeySigner(merchantSerialNumber, privateKey)), config.getV3Key().getBytes(StandardCharsets.UTF_8));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(merchantId);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(merchantId, merchantSerialNumber, privateKey)
.withValidator(new WechatPay2Validator(verifier));
return builder.build();
}
然后构建前端SDK需要的请求参数
**
* @Author Awei
* @Date 2023 02 16 16 16
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChargeResponse {
@ApiModelProperty(value = "订单ID")
Long orderId;
@ApiModelProperty(value = "订单唯一标识码")
String orderNo;
@ApiModelProperty(value = "微信,开放平台审核通过的移动应用appid")
String appid;
@ApiModelProperty(value = "微信,package,java里是关键字不能用作属性所以加了")
String packageString;
@ApiModelProperty(value = "微信,随机字符串")
String noncestr;
@ApiModelProperty(value = "微信,时间戳,标准北京时间,时区为东八区 需要转换成秒(10位数字)")
String timestamp;
@ApiModelProperty(value = "微信,RSA")
String signType;
@ApiModelProperty(value = "微信,预支付交易会话标识")
String sign;
@ApiModelProperty(value = "微信,原始id")
String originId;
@ApiModelProperty(value = "支付宝,支付字符串")
String aliPayString;
@ApiModelProperty(value = "APP 支付 partnerid:商户号ID ")
String partnerid;
@ApiModelProperty(value = "APP 支付 prepayid:预支付订单号 ")
String prepayid;
/**
* 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用,实际情况下只有支付完成状态才会返回该字段。
*/
String attach;
public void buildPayInfo(JSONObject jsonObject,String orderNo) {
this.appid = jsonObject.get("appId").toString();
this.packageString = jsonObject.get("package").toString();
this.sign = jsonObject.get("paySign").toString();
this.timestamp = jsonObject.get("timeStamp").toString();
this.signType = jsonObject.get("signType").toString();
this.noncestr = jsonObject.get("nonceStr").toString();
}
}
然后封装一下返回给前端
public Map<String, Object> appPay(UnifyPayParams payParams) {
Map<String, Object> result = new HashMap<>(4);
//这里就是上边的方法获取到的预支付交易会话标识
String prepayId = this.unifyAppPay(payParams);
// 返回前端支付参数
ChargeResponse chargeResponse = this.unifyInitAppResponse(prepayId);
chargeResponse.setOrderNo(payParams.getOrderNo());
result.put("data", chargeResponse);
result.put("payId", this.config.getMchInfoId());
result.put("appId", this.config.getAppId());
result.put("machId", this.config.getMerchantId());
result.put("payServerFlag", payParams.getPayServerFlag().code);
result.put("orderNo", payParams.getOrderNo());
return result;
}
//封装的方法 主要是设置签名sign
public ChargeResponse unifyInitAppResponse(String prepayId) throws Exception {
ChargeResponse chargeResponse = new ChargeResponse();
long timeTmp = System.currentTimeMillis() / 1000;
String randomString = RandomStringUtils.random(16);
chargeResponse.setTimestamp(String.valueOf(timeTmp));
chargeResponse.setSignType("RSA");
chargeResponse.setAppid(config.getAppId());
chargeResponse.setPartnerid(config.getMerchantId());
chargeResponse.setPrepayid(prepayId);
chargeResponse.setNoncestr(randomString);
chargeResponse.setPackageString("Sign=WXPay");
chargeResponse.setSign(getAppSign(randomString, timeTmp, prepayId));
chargeResponse.setOriginId(config.getWxOriginId());
return chargeResponse;
}
//先排序
public String getAppSign(String nonceStr, long timestamp, String prepayId) throws Exception {
String message = config.getAppId() + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ prepayId + "\n";
return sign(message.getBytes("utf-8"));
}
//然后加签
String sign(byte[] message) throws Exception {
PrivateKey privateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(config.getMerchantPrivateKey().getBytes("utf-8")));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
前端就能拉起微信支付啦
后续就是回调处理逻辑了