pom.xml
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.1</version>
</dependency>
controller.java
package
import io.swagger.annotations.Api;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springblade.core.tool.api.R;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import org.springblade.core.boot.ctrl.BladeController;
import java.security.*;
import java.security.cert.X509Certificate;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
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.util.PemUtil;
import org.apache.http.client.methods.HttpPost;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
统一下单
private static final String certificate = "-----BEGIN CERTIFICATE-----\n"
+ "-----END CERTIFICATE-----";
private static final String privateKey = "-----BEGIN PRIVATE KEY-----\n"
+ "-----END PRIVATE KEY-----";
// 用户私钥
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
// 这里是微信支付平台公钥,不是商户公钥
X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
new ByteArrayInputStream(certificate.getBytes(StandardCharsets.UTF_8)));
ArrayList<X509Certificate> listCertificates = new ArrayList<>();
listCertificates.add(wechatPayCertificate);
// 初始化微信支付sdk - 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant("1500582841", "6B5651A2477BDCB482A6F884125FEBB486B6A8E2", merchantPrivateKey)
.withWechatPay(listCertificates)
.build();
// 设置请求头
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");
// json 请求体
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid","1500582841")
.put("appid", "wx8b4f8fcb55c0852e")
.put("description", "Image形象店-深圳腾大-QQ公仔")
.put("notify_url", "https://www.weixin.qq.com/wxpay/pay.php")
.put("out_trade_no", "1217752501201407033233368018");
rootNode.putObject("amount")
.put("total", 1);
rootNode.putObject("payer")
.put("openid", "oBZTL5UWjOVqIQv2HY0rmkwySI-Y");
objectMapper.writeValue(bos, rootNode);
// 发起 post 请求
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println(bodyAsString);
二次加密
// 二次签名
String signatureStr = Stream.of(
"wx8b4f8fcb55c0852e",
"1643173840",
"5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
"prepay_id=wx261323254028265fa112becd5da8ad0000")
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
String b = Base64Utils.encodeToString(sign.sign());
服务端回调解密
private String v3key = "JABxxb123456789JABxxb12345678123"; // 商户 apiv3key
public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
throws GeneralSecurityException, IOException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(v3key.getBytes(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(128, 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);
}
}
// 调用
String associatedData = "transaction"; // 微信回调 associated_data
String nonce = "GgtB8o2swh8o"; // 微信回调 nonce
String ciphertext = "z/09JOy0U0Sx0KIJx90xi/YDJEqWNTFrKDxrvDq+bIKIdVSxt1BVdybiDwKYnKEFmBmdyc
loOHNKGcgSPgsh/LUoCAMsRi1Kn+g36HU2HU+k95QMYWPjbR4EJmZo4ofR7NARBfKpyR1nANkoj+Tqeffj9eJGixGNr
nzoo89Rppt4BwBShh56WZvijmq9kl2x/JfRr4BBw9dGGdxp0Wrmr0fTTdpV8lzP9nk2sdZFq80SJi9sEn/zBeuYBX4p
ClbRky1jlR+VPFVJhIFfa9Cur+HSVDA+ByJuJMcnWyJBQuU/8ATO3i46LWKS+m1C4U55vPGIvpfBUCIE2yLoPceMtyb
J534Av2AHiXLgcBzAlyYmXph0ZYT1S2oVlHF1nOXI/ih/fg9A/55k1cHhJFU9ayZmSSo2lvRgyzi+2FfiEK+L68fc5G
pFwjy+IPNSNlegB3TNNKbLJHnXy9JxFd5Cz9mB4V8qNPgxrBPsiju9Wt7BjfKI2/GkJAZTMOOS0NiYbRpuDocCaPMIz
Jvp2pqbWCvgFvQxEfTB0MxDpYLa2MRcSbEclTp7qUmVkCZdg2XgEi22aCbGcCkqM4l6OvYR"; // 微信回调 ciphertext
String aa = decryptToString(associatedData.getBytes(), nonce.getBytes(), ciphertext);