一、简介
1、关于API v3
相较于之前的微信支付API,主要区别是:
- 遵循统一的REST的设计风格
- 使用JSON作为数据交互的格式,不再使用XML
- 使用基于非对称密钥的SHA256-RSA的数字签名算法,不再使用MD5或HMAC-SHA256
- 不再要求携带HTTPS客户端证书(仅需携带证书序列号)
- 使用AES-256-GCM,对回调中的关键信息进行加密保护
2、SDK接入
微信支付API v3官方SDK(目前包含Java、PHP、GO三种语言版本)。此外,微信支付也提供API v3的Postman调试工具、微信支付平台证书下载工具,可以通过微信支付的GitHub获取。
3、微信支付商户平台
微信支付商户平台及开发文档中心
1.在【API安全】里需要申请API证书、设置APIv3密钥。
2.在【开发配置】里需要配置合法域名
3.在【AppID账号管理】里需要绑定小程序的AppID
二、代码
1、小程序调起信支付
timeStamp:时间戳(只需要到秒,如果获取的是毫秒级需要除以1000)
nonceStr:随机字符串(不长于32位)
package:订单详情扩展字符串(prepay_id=……)
signType:签名方式(仅支持RSA)
paySign:签名(使用字段appId、timeStamp、nonceStr、package计算得出的签名值)
具体详解请查看微信支付api文章来源:https://www.toymoban.com/news/detail-511725.html
wx.requestPayment({
timeStamp: '',
nonceStr: '',
package: '',
signType: '',
paySign: '',
success: (result) => {},
fail: () => {},
complete: () => {}
});
2、Java服务端
1.导入maven依赖
<!-- 导入微信支付v3工具包 -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
<!--Hutool工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.4</version>
</dependency>
2.微信支付Utils支付工具类
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.xmlbeans.impl.xb.xsdschema.Public;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.PushBuilder;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class V3WXPayUtil {
public static Map<String, Object> createOrder(String buildCode, String openId, String description, Integer total) throws Exception {
// PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
// new ByteArrayInputStream(WXPayConstants.PRIVATE_KEY.getBytes("utf-8")));
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(WXPayConstants.PRIVATE_KEY);
writeText("C:/log.txt", "获取私钥:=========>" + merchantPrivateKey.toString());
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
writeText("C:/log.txt", "获取证书管理器实例:=========>" + certificatesManager.toString() + "\n");
// 向证书管理器增加需要自动更新平台证书的商户信息
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(WXPayConstants.MCH_ID,
new PrivateKeySigner(WXPayConstants.MCH_SERIAL_NO, merchantPrivateKey));
writeText("C:/log.txt", "获取管理器增加需要自动更新平台证书的商户信息前:=========>" + wechatPay2Credentials.toString() + "\n");
byte[] api_v3KEYBytes = WXPayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8);
writeText("C:/log.txt", "获取api_v3KEYBytes:=========>" + api_v3KEYBytes + "\n");
certificatesManager.putMerchant(WXPayConstants.MCH_ID, wechatPay2Credentials, api_v3KEYBytes);
writeText("C:/log.txt", "证书管理器实例:=========>" + certificatesManager.toString());
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(WXPayConstants.MCH_ID);
writeText("C:/log.txt", "从证书管理器中获取verifier:=========>" + verifier);
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(WXPayConstants.MCH_ID, WXPayConstants.MCH_SERIAL_NO, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
writeText("C:/log.txt", "从证书管理器中获取verifier后:=========>" + "\n");
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid", WXPayConstants.MCH_ID)
.put("appid", WXPayConstants.APP_ID)
.put("notify_url", WXPayConstants.NOTIFY_URL)
.put("description", description)
.put("out_trade_no", buildCode);
rootNode.putObject("amount")
.put("total", total) // 支付总金额,分为单位 需前端传入
.put("currency", "CNY");
rootNode.putObject("payer")
.put("openid", openId);
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
log.info("成功获取微信预支付订单号{}", bodyAsString);
System.out.println("成功获取微信预支付订单号==========>" + bodyAsString);
// 时间戳
String currentTimeMillis = System.currentTimeMillis() / 1000 + "";
// 随机字符串 hutool工具类
String randomString = RandomUtil.randomString(32);
StringBuilder builder = new StringBuilder();
// 应用ID
builder.append(WXPayConstants.APP_ID).append("\n");
// 时间戳
builder.append(currentTimeMillis).append("\n");
// 随机字符串
builder.append(randomString).append("\n");
JsonNode node = objectMapper.readTree(bodyAsString);
writeText("C:/log.txt", "从获取node后:=========>" + node + "\n");
// 订单详情扩展字符串
builder.append("prepay_id=").append(node.get("prepay_id").toString().replace("\"", "")).append("\n");
String paySign = sign(builder.toString().getBytes());
Map<String, Object> map = new HashMap();
map.put("package", "prepay_id=" + node.get("prepay_id").toString().replace("\"", ""));
map.put("prepayId", node.get("prepay_id").toString().replace("\"", ""));
map.put("nonceStr", randomString);
map.put("signType", "RSA");
map.put("paySign", paySign);
map.put("timeStamp", currentTimeMillis);
map.put("appId", WXPayConstants.APP_ID);
return map;
}catch (Exception e){
writeText("C:/log.txt", "异常:" + getStackTraceMessage(e));
throw e;
}
}
public static String getStackTraceMessage(Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
e.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}
public static String sign(byte[] message) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey());
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
* @return 私钥对象
* @throws IOException
*/
public static PrivateKey getPrivateKey() throws IOException {
String content = WXPayConstants.PRIVATE_KEY;
// String content = new String(Files.readAllBytes(new ClassPathResource(filename).getFile().toPath()), "UTF-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
public static boolean signVerify(String serial, String message, String signature) {
// 从证书管理器中获取verifier
Verifier verifier = null;
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(WXPayConstants.PRIVATE_KEY);
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(WXPayConstants.MCH_ID, new WechatPay2Credentials(WXPayConstants.MCH_ID,
new PrivateKeySigner(WXPayConstants.MCH_SERIAL_NO, merchantPrivateKey)), WXPayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
verifier = certificatesManager.getVerifier(WXPayConstants.MCH_ID);
return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
} catch (IOException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (HttpCodeException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
}
return false;
}
public static String decryptOrder(String body) {
AesUtil util = new AesUtil(WXPayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
ObjectMapper objectMapper = new ObjectMapper();
JsonNode node = null;
try {
node = objectMapper.readTree(body);
JsonNode resource = node.get("resource");
String ciphertext = resource.get("ciphertext").textValue();
String associatedData = resource.get("associated_data").textValue();
String nonce = resource.get("nonce").textValue();
return util.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public static void writeText(String fileName, String text) {
FileOutputStream fop = null;
File file;
try {
file = new File(fileName);
fop = new FileOutputStream(file,true);
if (!file.exists()) {
file.createNewFile();
}
fop.write(text.getBytes());
fop.flush();
fop.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fop != null) {
fop.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.V3支付常量配置类
public class WXPayConstants {
/*appid*/
public static final String APP_ID = "";
/*小程序唯一凭证密钥*/
public static final String SECRET = "";
/*商户号*/
public static final String MCH_ID = "";
/* 商户证书序列号 */
public static final String MCH_SERIAL_NO = "";
/*APIv3密钥*/
public static final String API_V3KEY = "";
/*支付成功回调地址*/
public static final String NOTIFY_URL = "https://*.com/*/wXPay/wxPayNotify";
/*退款回调接口*/
public static final String PACKAGE = "Sign=WXPay";
/*序列号*/
public static final String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
"证书内容" +
"-----END PRIVATE KEY-----\n";
}
5.异步通知【支付回调】
前端付款成功后微信会主动访问你的服务器WXPayConstants给的NOTIFY_URL回调地址给你一个通知文章来源地址https://www.toymoban.com/news/detail-511725.html
@ApiOperation(value = "微信回调", notes = "微信回调")
@RequestMapping(value = "/wxPayNotify", method = RequestMethod.POST)
public Map<String, Object> wxPayNotify(HttpServletRequest request) {
// JSONObject params = (JSONObject) this.requestBody();
// return busOrderService.productOrder(params);
System.out.println("Wechatpay-Timestamp:" + request.getHeader("Wechatpay-Timestamp"));
this.writeText("C:/log.txt", "Wechatpay-Timestamp: {}:=========>" + request.getHeader("Wechatpay-Timestamp") + "\n");
System.out.println("Wechatpay-Nonce:" + request.getHeader("Wechatpay-Nonce"));
this.writeText("C:/log.txt", "Wechatpay-Nonce: {}:=========>" + request.getHeader("Wechatpay-Nonce") + "\n");
System.out.println("Wechatpay-Signature:" + request.getHeader("Wechatpay-Signature"));
this.writeText("C:/log.txt", "Wechatpay-Signature: {}:=========>" + request.getHeader("Wechatpay-Signature") + "\n");
System.out.println("Wechatpay-Serial:" + request.getHeader("Wechatpay-Serial"));
this.writeText("C:/log.txt", "Wechatpay-Serial: {}:=========>" + request.getHeader("Wechatpay-Serial") + "\n");
Map result = new HashMap();
result.put("code", "FAIL");
BufferedReader reader = null;
try {
StringBuilder signStr = new StringBuilder();
// 请求时间戳\n
signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
// 请求随机串\n
signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
// 请求报文主体\n
reader = request.getReader();
this.writeText("C:/log.txt", "reader:=========>" + reader.toString() + "\n");
String str = null;
StringBuilder builder = new StringBuilder();
while ((str = reader.readLine()) != null) {
builder.append(str);
}
System.out.println(builder);
log.info("请求报文主体: {}", builder);
this.writeText("C:/log.txt", "请求报文主体:=========>" + builder + "\n");
signStr.append(builder.toString()).append("\n");
// 1.验签
if (!V3WXPayUtil.signVerify(request.getHeader("Wechatpay-Serial"), signStr.toString(), request.getHeader("Wechatpay-Signature"))) {
result.put("message", "sign error");
return result;
}
// 2.解密
String decryptOrder = V3WXPayUtil.decryptOrder(builder.toString());
log.info("验签解密: {}", decryptOrder);
this.writeText("C:/log.txt", "验签解密:=========>" + decryptOrder + "\n");
System.out.println(decryptOrder);
String payResult = busOrderService.verifySuccessfulPayment(decryptOrder);
if (StringUtils.isEmpty(payResult)) {
result.put("message", "verification error");
return result;
} else {
result.put("code", payResult);
}
return result;
} catch (IOException e) {
e.printStackTrace();
result.put("code", "FAIL");
result.put("message", e.getMessage());
return result;
}
}
到了这里,关于小程序微信支付V3版本Java集成的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!