回调函数:
1.首先获取请求头的时间戳和随机数
2.微信回调时,会把回调的参数body传递给该接口
3. 获取平台对应的证书来核实签名
4.进行回调的报文解密
5.获取微信支付返回的信息
6.如果支付成功,保存用户订单明细
/**
* 支付回调
* @param body
* @param request
* @return
*/
@PostMapping("/api/pay/callback")
public Map orderPayCallback(@RequestBody Map body, HttpServletRequest request)
{
log.info("1----------->微信支付回调开始");
Map<String, Object> result = new HashMap();
//1:获取微信支付回调的获取签名信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
//获取随机数
String nonce = request.getHeader("Wechatpay-Nonce");
ObjectMapper objectMapper = new ObjectMapper();
try {
// 2: 开始解析报文体
String data = objectMapper.writeValueAsString(body);
String message = timestamp + "\n" + nonce + "\n" + data + "\n";
//3:获取应答签名
String sign = request.getHeader("Wechatpay-Signature");
//4:获取平台对应的证书
String serialNo = request.getHeader("Wechatpay-Serial");
//如果yaml的证书公钥不包含微信传递过来的证书公钥,则获取平台证书
if (!KsdStaticParameter.certificateMap.containsKey(serialNo))
{
KsdStaticParameter.certificateMap = WechatPayUtils.refreshCertificate();
}
X509Certificate x509Certificate = KsdStaticParameter.certificateMap.get(serialNo);
//核实签名是否正确
if (!WechatPayUtils.verify(x509Certificate, message.getBytes(), sign))
{
throw new IllegalArgumentException("微信支付签名验证失败:" + message);
}
log.info("签名验证成功");
Map<String, String> resource = (Map) body.get("resource");
// 5:回调报文解密
AesUtil aesUtil = new AesUtil(KsdStaticParameter.v3Key.getBytes());
//解密后json字符串
String decryptToString = aesUtil.decryptToString(
resource.get("associated_data").getBytes(),
resource.get("nonce").getBytes(),
resource.get("ciphertext"));
log.info("2------------->decryptToString====>{}", decryptToString);
//6:获取微信支付返回的信息
Map<String, Object> jsonData = objectMapper.readValue(decryptToString, Map.class);
//7: 支付状态的判断 如果是success就代表支付成功
if ("SUCCESS".equals(jsonData.get("trade_state")))
{
// 8:获取支付的交易单号,流水号,和附属参数
String out_trade_no = jsonData.get("out_trade_no").toString();
String transaction_id = jsonData.get("transaction_id").toString();
// 附属参数
String attach = jsonData.get("attach").toString();
//TODO 根据订单号查询支付状态,如果未支付,更新支付状态 为已支付
log.info("3----------->微信支付成功,支付流水号是:{},附属参数是:{}", out_trade_no, attach);
log.info("4----------->微信支付成功,支付流水号是:{},{}", transaction_id);
// 把附属参数转换成map
HashMap<String,Object> hashMap = JsonUtil.string2Obj(attach, HashMap.class);
// 保存用户订单明细
KssUserPay userPay = new KssUserPay();
userPay.setUserid(String.valueOf(hashMap.get("userid")));
userPay.setNickname(String.valueOf(hashMap.get("nickname")));
userPay.setCourseid(String.valueOf(hashMap.get("courseid")));
userPay.setPrice(String.valueOf(hashMap.get("price")));
userPay.setTradeno(transaction_id);
userPay.setCreateTime(new Date());
userPayService.saveOrUpdate(userPay);
}
result.put("code", "SUCCESS");
result.put("message", "成功");
} catch (Exception e) {
result.put("code", "fail");
result.put("message", "系统错误");
e.printStackTrace();
}
return result;
}
验证签名:
/**
* 验证签名
* @param certificate
* @param message
* @param signature
* @return
*/
public static boolean verify(X509Certificate certificate, byte[] message, String signature) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initVerify(certificate);
sign.update(message);
return sign.verify(Base64.getDecoder().decode(signature));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
throw new RuntimeException("签名验证过程发生了错误", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("无效的证书", e);
}
}
微信回调成功生成的明文信息:
package com.suqi.payment.utils;
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);
}
}
}