1. Préparation avant accès, préparez-vous selon ce document
Vous pouvez l'obtenir lorsque vous êtes prêt ( paramètres requis au deuxième point ) :
paramètre 1 ID marchand : xxxxxx (tous les chiffres)
Paramètre 2 Merchant APIV3 key apiV3key : xxxxxxx (32 chaînes alphanumériques majuscules et minuscules, préparées par le développeur lui-même)
Paramètre 3 Numéro de série du certificat du commerçant marchandSerialNumber : xxxxx
Paramètre 4 Chemin de la clé privée de l'API Merchant privateKeyPath : chemin du fichier apiclient_key.pem
--------------------- Ordre de paiement natif (code QR), les quatre premiers suffisent, 5 est pour le paiement Jsapi ------ ---- ------
Paramètre 5 Chemin du certificat secret national de la plateforme de paiement WeChat (fichier X509) wechatPayCertificatePath : wechatpay_xxxx.pem()
Je cherchais ce fichier depuis longtemps et j'ai finalement essayé l'outil ci-dessous pour le télécharger à l'aide de GitHub - wechatpay-apiv3/CertificateDownloader : outil de téléchargement de la ligne de commande du certificat de la plate-forme APIv3 de paiement Java WeChat
2. Accès au code de paiement
Documentation du site officiel de paiement : https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7
Wechat a fait un paquet maven pour apiv3, et ça y ressemblait finalement.En conséquence, j'ai trouvé que bien que l'exemple de paiement natif soit complet, je ne sais pas comment donner les 1-5 paramètres dans le premier point. Les paramètres jsapi sont les mêmes, je suis Le patchwork de choses s'est réuni. documentation du paquet maven
Postez un exemple d'appel de jsapi, il suffit d'invoquer le paiement côté applet, ceci est basé sur l'exemple officiel :
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class JsapiDemoService {
public static String merchantId = "1xxxx";
// public static String privateKeyPath = "";
public static String merchantSerialNumber = "xxxx";
public static String wechatPayCertificatePath = "";
public static com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension service;
public static PrepayWithRequestPaymentResponse createWxJsapOrder(String certificatePath, String keyPath, JsapiReq cliReq) {
String method = Thread.currentThread().getStackTrace()[1].getMethodName();
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
Config config =
new RSAConfig.Builder()
.merchantId(merchantId)
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
.privateKeyFromPath(keyPath)
.merchantSerialNumber(merchantSerialNumber)
.wechatPayCertificatesFromPath(certificatePath)
.build();
// 构建service
JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
Payer payer = new Payer();
payer.setOpenid(cliReq.getOpenid());
request.setPayer(payer);
Amount amount = new Amount();
amount.setTotal(10);//订单总金额,单位为分
request.setAmount(amount);
request.setAppid("wxddddxxxxxx");
request.setMchid("1xxxx");
request.setDescription("ms");
request.setNotifyUrl("https://xxx.net/callBackr");
//request.setOutTradeNo("out_trade_no_1");
request.setOutTradeNo(cliReq.getOutTradeNo());
// 调用下单方法,得到应答
try {
// ... 调用接口
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
// 使用微信扫描 code_url 对应的二维码,即可体验Native支付
System.out.println(JSONObject.toJSON(response));
return response;
} catch (HttpException e) { // 发送HTTP请求失败
// 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义
log.error(method, e);
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
// 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义
log.error(method, e);
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
log.error(method, e);
} catch (Exception e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
log.error(method, e);
}
return new PrepayWithRequestPaymentResponse();
}
/** 关闭订单 */
public static void closeOrder() {
CloseOrderRequest request = new CloseOrderRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
// 调用接口
service.closeOrder(request);
}
/** JSAPI支付下单,并返回JSAPI调起支付数据 */
public static PrepayWithRequestPaymentResponse prepayWithRequestPayment() {
PrepayRequest request = new PrepayRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
// 调用接口
return service.prepayWithRequestPayment(request);
}
/** 微信支付订单号查询订单 */
public static Transaction queryOrderById() {
QueryOrderByIdRequest request = new QueryOrderByIdRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
// 调用接口
return service.queryOrderById(request);
}
/** 商户订单号查询订单 */
public static Transaction queryOrderByOutTradeNo() {
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
// 调用接口
return service.queryOrderByOutTradeNo(request);
}
}
Remplissez la définition de JsapiReq
public class JsapiReq implements Serializable {
private static final long serialVersionUID = 1L;
private String openid;
private String outTradeNo;
}
Rappelez-vous également comment lire le chemin du fichier de ressources dans java/spring/srpingboot
String userDir = System.getProperty("user.dir");
String certificatePath = userDir + "/src/main/resources/cert/wechatpay_xxx.pem";
String keyPath = userDir + "/src/main/resources/cert/apiclient_xxxxkey.pem";
3. Rappel de paiement
Paiement Callback-Documentation du site Web officiel : WeChat Payment-Developer Documentation
le code
@PostMapping(value = "/callBack")
public Map<String, String> callBack(@RequestBody JSONObject jsonObject) {
String method = Thread.currentThread().getStackTrace()[1].getMethodName();
try {
String key = WxNativePayProxy.getWxV3Key();
String json = jsonObject.toString();
String associated_data = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.associated_data");
String ciphertext = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.ciphertext");
String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");
String decryptData = new AesUtil(key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
//验签成功
JSONObject decryptDataObj = JSONObject.parseObject(decryptData, JSONObject.class);
//decryptDataObj 为解码后的obj,其内容如下。之后便是验签成功后的业务处理
//{
// "sp_appid": "wx8888888888888888",
// "sp_mchid": "1230000109",
// "sub_appid": "wxd678efh567hg6999",
// "sub_mchid": "1900000109",
// "out_trade_no": "1217752501201407033233368018",
// "trade_state_desc": "支付成功",
// "trade_type": "MICROPAY",
// "attach": "自定义数据",
// "transaction_id": "1217752501201407033233368018",
// "trade_state": "SUCCESS",
// "bank_type": "CMC",
// "success_time": "2018-06-08T10:34:56+08:00",
// ...
// "payer": {
// "openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
// },
// "scene_info": {
// "device_id": "013467007045764"
// }
//}
}catch (Exception e){
log.info("{} ,parms{}, 异常:", method, jsonObject.toJSONString(), e);
}
Map<String, String> res = new HashMap<>();
res.put("code", "SUCCESS");
res.put("message", "成功");
return res;
}
Parmi eux, la classe d'outil de décodage WeChat est utile : AesUtil
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AesUtil{
static final int KEY_LENGTH_BYTE = 32;
static final int TAG_LENGTH_BIT = 128;
private final byte[] aesKey;
public AesUtil(byte[] key) {
if (key.length != KEY_LENGTH_BYTE) {
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
}
this.aesKey = key;
}
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
throws GeneralSecurityException, IOException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData);
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(e);
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e);
}
}
}