J'ai récemment eu du mal avec le paiement WeChat et les certificats sont assez ennuyeux, il est donc nécessaire de partager quelques expériences. pour réduire votre temps de développement. Pièges lors du paiement avec WeChat. À l'heure actuelle, l'API de paiement WeChat a été développée vers la version V3, adoptant le style populaire Restful.
Aujourd'hui, je vais partager les difficultés du paiement WeChat - Signature Bien qu'il existe de nombreux SDK utiles, si vous souhaitez comprendre le paiement WeChat en profondeur, vous devez encore le comprendre pendant un instant.
Afin d'assurer la sécurité des données financières sensibles et de garantir que les transactions financières de notre entreprise sont infaillibles. Actuellement, WeChat Pay utilise la clé privée fournie dans le certificat CA faisant autorité (certificat API) émis par un tiers pour signer. Vous pouvez configurer et obtenir des certificats API via la plateforme marchand.
N'oubliez pas que vous serez invité à télécharger lorsque vous le configurerez pour la première fois. Le téléchargement ne sera plus fourni plus tard. Veuillez vous référer aux instructions pour plus de détails.
Après le réglage, recherchez le package compressé zip
et décompressez-le. Il contient de nombreux fichiers. Pour le développement JAVA, il vous suffit de faire attention au fichier de certificat, qui contient apiclient_cert.p12
, nous devons le mettre côté serveur et utiliser Java pour analyser le fichier 公私钥
afin d'obtenir les clés publiques et privées. .p12
Assurez-vous d'assurer la sécurité du certificat côté serveur, ce qui implique une sécurité financière.
L'étape suivante consiste à analyser le certificat. Il existe de nombreuses façons d'analyser le certificat sur Internet. Ici, j'utilise une méthode plus "formelle" pour analyser, en utilisant. le package de sécurité JDKjava.security.KeyStore
à analyser.
Le certificat de l'API de paiement WeChat utilise l'algorithme PKCS12
Nous utilisons KeyStore
pour obtenir le support de la paire de clés publique et privée KeyPair
et le numéro de série du certificat serialNumber
. class :
import org.springframework.core.io.ClassPathResource;import java.security.KeyPair;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.cert.X509Certificate;/** * KeyPairFactory * * @author dax * @since 13:41 **/public class KeyPairFactory { private KeyStore store; private final Object lock = new Object(); /** * 获取公私钥. * * @param keyPath the key path * @param keyAlias the key alias * @param keyPass password * @return the key pair */ public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) { ClassPathResource resource = new ClassPathResource(keyPath); char[] pem = keyPass.toCharArray(); try { synchronized (lock) { if (store == null) { synchronized (lock) { store = KeyStore.getInstance("PKCS12"); store.load(resource.getInputStream(), pem); } } } X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias); certificate.checkValidity(); // 证书的序列号 也有用 String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); // 证书的 公钥 PublicKey publicKey = certificate.getPublicKey(); // 证书的私钥 PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem); return new KeyPair(publicKey, storeKey); } catch (Exception e) { throw new IllegalStateException("Cannot load keys from store: " + resource, e); } } }复制代码
Si cela semble familier, on peut voir qu'il s'agit d'une version modifiée de la méthode d'extraction de clés publiques et privées utilisée par JWT dans le didacticiel Spring Security. Vous pouvez comparer les différences.
Il y a trois paramètres dans cette méthode, qui doivent être expliqués ici :
keyPath
Le chemin apiclient_cert.p12
du certificat API classpath
, généralement nous le ferons mettez-le dans le chemin resources
, vous pouvez bien sûr modifier la manière d'obtenir le flux d'entrée du certificat. keyAlias
L'alias du certificat n'est pas disponible dans ce document WeChat. Brother Fat l'a obtenu par DEBUG lors du chargement du certificat et a constaté que la valeur est fixée à Tenpay Certificate
. keyPass
Mot de passe du certificat, c'est le numéro du commerçant par défaut, il est également nécessaire dans d'autres configurations mchid
, c'est-à-dire que vous utilisez le super administrateur pour vous connecter au WeChat plateforme marchande dans votre profil personnel une chaîne de chiffres. La signature de la version WeChat Pay V3 est que nous portons une signature spécifique dans l'en-tête de la requête HTTP lors de l'appel du WeChat Pay spécifique API La chaîne de codage est utilisée par le serveur de paiement WeChat pour vérifier la source de la demande afin de garantir que la demande est authentique et digne de confiance.
Le format spécifique de la chaîne de signature, pas moins de cinq lignes au total, chaque ligne se termine par un caractère de nouvelle ligne n
.
HTTP请求方法\n URL\n 请求时间戳\n 请求随机串\n 请求报文主体\n复制代码
POST
. https://api.mch.weixin.qq.com/v3/pay/transactions/app
, supprimez la partie nom de domaine pour obtenir l'URL de la signature participante. S'il y a des paramètres de requête dans la requête, « ? » et la chaîne de requête correspondante doivent être ajoutées à la fin de l'URL. C'est /v3/pay/transactions/app
. System.currentTimeMillis() / 1000
pour l'obtenir. 593BEC0C930BF1AFEB40B4A08C8FB242
. ""
lorsque la méthode de requête est POST
ou PUT
, veuillez utiliser pour envoyer réellement le message de JSON
. Pour l'API de téléchargement d'images, veuillez utiliser le message meta
correspondant à JSON
. Ensuite, nous utilisons la clé privée du commerçant pour exécuter SHA256 avec la signature RSA sur la chaîne à signer dans le format ci-dessus, et exécutons Encodage Base64Obtenir la valeur de la signature. Le code Java de base correspondant est :
/** * V3 SHA256withRSA 签名. * * @param method 请求方法 GET POST PUT DELETE 等 * @param canonicalUrl 例如 https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1 * @param timestamp 当前时间戳 因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致 * @param nonceStr 随机字符串 要和TOKEN中的保持一致 * @param body 请求体 GET 为 "" POST 为JSON * @param keyPair 商户API 证书解析的密钥对 实际使用的是其中的私钥 * @return the string */@SneakyThrowsString sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) { String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body) .collect(Collectors.joining("\n", "", "\n")); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(keyPair.getPrivate()); sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); }复制代码
et placez-la dans l'en-tête de requête Token
de la requête HTTP correspondante. Le format est : Authorization
Authorization: WECHATPAY2-SHA256-RSA2048 {Token}复制代码
se compose des cinq parties suivantes : Token
mchid
, utilisé pour déclarer le certificat utiliséserial_no
nonce_str
timestamp
signature
Code principal généré :Token
/** * 生成Token. * * @param mchId 商户号 * @param nonceStr 随机字符串 * @param timestamp 时间戳 * @param serialNo 证书序列号 * @param signature 签名 * @return the string */String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) { final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""; // 生成token return String.format(TOKEN_PATTERN, wechatPayProperties.getMchId(), nonceStr, timestamp, serialNo, signature); }复制代码
généré dans l'en-tête de la requête selon le format ci-dessus pour terminer l'utilisation de la signature. Token
Recommandations d'apprentissage gratuites associées : Introduction à l'article connexe :Comment implémenter la fonction de paiement du mini-programme
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!