微信支付V3版本weixin-java-pay集成调用含二次加签
准备工作(准备如下信息)
#微信公众号或者小程序等的appid
appId: xxxxxxxxxxxxx
#微信支付商户号
mchId: xxxxxxxxxxxxx
# 证书
apiclient_key.pem: xxxxxxxxx
# 证书
apiclient_cert.pem: xxxxxxxxx
# apiV3key
apiV3key: xxxxxxxxxxxxxxxxxx
代码集成
pom坐标
<!-- 微信支付 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.3.9.B</version>
</dependency>
配置文件编写
wx:
pay:
#微信公众号或者小程序等的appid
appId: xxxxxxxxxxxxxxxxxxxxx
#微信支付商户号
mchId: xxxxxxxxxxxxxxxxxxx
# 证书相对路径 (以classpath:开头)
privateKeyPath: classpath:/dev/apiclient_key.pem
# 证书相对路径 (以classpath:开头)商户私钥
privateCertPath: classpath:/dev/apiclient_cert.pem
#支付回调通知地址
returnUrl: http://xxxxxxxxxx/wxPay/callBack
# 退款回调地址
refundUrl: http://xxxxxxxxxx/wxPay/refund
# apiV3key
apiV3key: xxxxxxxxxxxxxxxxxxxxxxxx
配置类(微信配置实体)
@Data
@Configuration
public class WxPayProperties {
/**
* 设置微信公众号或者小程序等的appid
*/
@Value("${wx.pay.appId}")
private String appId;
/**
* 微信支付商户号
*/
@Value("${wx.pay.mchId}")
private String mchId;
/**
* 证书相对路径
*/
@Value("${wx.pay.privateKeyPath}")
private String privateKeyPath;
/**
* 证书相对路径
*/
@Value("${wx.pay.privateCertPath}")
private String privateCertPath;
/**
* 下单回调地址
*/
@Value("${wx.pay.returnUrl}")
private String returnUrl;
/**
* 退款回调地址
*/
@Value("${wx.pay.refundUrl}")
private String refundUrl;
/**
* apiV3key
*/
@Value("${wx.pay.apiV3key}")
private String apiV3key;
}
配置类(微信PayService-Bean注入)
@Configuration
@ConditionalOnClass(WxPayService.class)
public class WxPayConfiguration {
@Autowired
private WxPayProperties properties;
@Autowired
public WxPayConfiguration(WxPayProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(wxPayConfig());
return wxPayService;
}
@Bean
public WxPayConfig wxPayConfig() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setPrivateKeyPath(this.properties.getPrivateKeyPath());
payConfig.setPrivateCertPath(this.properties.getPrivateCertPath());
payConfig.setApiV3Key(this.properties.getApiV3key());
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
return payConfig;
}
}
微信支付工具类编写
@Component
@Slf4j
public class WxPayUtil {
@Autowired
private WxPayService wxPayService;
@Autowired
private WxPayProperties wxPayProperties;
/**
* 统一支付下单接口
*
* @param outTradeNo 商户订单号
* @param totalFee 下单金额(单位:分)
* @param productContent 商品描述
*/
public Map<String, String> createOrder(String outTradeNo, Integer totalFee, String productContent) {
WxPayUnifiedOrderV3Request wxPayUnifiedOrderV3Request = new WxPayUnifiedOrderV3Request();
wxPayUnifiedOrderV3Request.setDescription(productContent);
WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
amount.setTotal(totalFee);
wxPayUnifiedOrderV3Request.setAmount(amount);
wxPayUnifiedOrderV3Request.setOutTradeNo(outTradeNo);
wxPayUnifiedOrderV3Request.setNotifyUrl(wxPayProperties.getReturnUrl());
try {
WxPayUnifiedOrderV3Result.AppResult appResult = wxPayService.createOrderV3(TradeTypeEnum.APP, wxPayUnifiedOrderV3Request);
return this.startWXPay(appResult);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 商户订单号查询订单 微信官方文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_2.shtml
*
* @param outTradeNo 商户订单号
* @return WxPayOrderQueryV3Result 返回对象
*/
public WxPayOrderQueryV3Result selectOrder(String outTradeNo) {
WxPayOrderQueryV3Request wxPayOrderQueryV3Request = new WxPayOrderQueryV3Request();
wxPayOrderQueryV3Request.setOutTradeNo(outTradeNo);
try {
return wxPayService.queryOrderV3(wxPayOrderQueryV3Request);
} catch (WxPayException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 退款调用
*
* @param outTradeNo 商户订单号
* @param outRefundNo 商户退款单号
* @param reason 退款原因
* @param refund 退款金额(单位:分)
* @param total 原订单金额(单位:分)
* @return WxPayRefundV3Result 返回值参考: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_9.shtml
*/
public WxPayRefundV3Result refundOrder(String outTradeNo, String outRefundNo, String reason,Integer refund,Integer total) {
WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
wxPayRefundV3Request.setOutTradeNo(outTradeNo);
wxPayRefundV3Request.setOutRefundNo(outRefundNo);
wxPayRefundV3Request.setReason(reason);
wxPayRefundV3Request.setNotifyUrl(wxPayProperties.getRefundUrl());
WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
amount.setRefund(refund);
amount.setTotal(total);
amount.setCurrency("CNY");
wxPayRefundV3Request.setAmount(amount);
try {
return wxPayService.refundV3(wxPayRefundV3Request);
} catch (WxPayException e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 封装二次加签数据
*
* @param appResult 预下单数据
* @return Map 返回值
*/
public Map<String, String> startWXPay(WxPayUnifiedOrderV3Result.AppResult appResult) {
Map<String, String> map = new HashMap<>();
//时间戳
long timestamp = System.currentTimeMillis() / 1000;
String token = getSign(appResult.getNoncestr(), appResult.getAppid(), appResult.getPrepayId(), timestamp);
map.put("appid", appResult.getAppid());
map.put("partnerid", appResult.getPartnerId());
map.put("prepayid", appResult.getPrepayId());
map.put("package", appResult.getPackageValue());
map.put("noncestr", appResult.getNoncestr());
map.put("timestamp", String.valueOf(timestamp));
map.put("sign", token);
return map;
}
/**
* 获取签名
*
* @param nonceStr 随机数
* @param appId 微信公众号或者小程序等的appid
* @param prepay_id 预支付交易会话ID
* @param timestamp 时间戳 10位
* @return String 新签名
*/
String getSign(String nonceStr, String appId, String prepay_id, long timestamp) {
//从下往上依次生成
String message = buildMessage(appId, timestamp, nonceStr, prepay_id);
//签名
try {
return sign(message.getBytes("utf-8"));
} catch (IOException e) {
throw new RuntimeException("签名异常,请检查参数或商户私钥");
}
}
String sign(byte[] message) {
try {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
sign.initSign(wxPayService.getConfig().getPrivateKey());
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (Exception e) {
throw new RuntimeException("签名异常,请检查参数或商户私钥");
}
}
/**
* 按照前端签名文档规范进行排序,\n是换行
*
* @param nonceStr 随机数
* @param appId 微信公众号或者小程序等的appid
* @param prepay_id 预支付交易会话ID
* @param timestamp 时间戳 10位
* @return String 新签名
*/
String buildMessage(String appId, long timestamp, String nonceStr, String prepay_id) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ prepay_id + "\n";
}
}