package com.lunhan.water.common.wechat; import com.lunhan.water.common.ExecutedResult; import com.lunhan.water.common.enums.EHttpContentType; import com.lunhan.water.common.util.HttpUtil; import com.lunhan.water.common.util.SerializeUtil; import com.lunhan.water.common.util.StringUtil; import com.lunhan.water.common.wechat.req.ReqCreateWechatPay; import com.lunhan.water.common.wechat.req.ReqCreateWeiXinRefund; import com.lunhan.water.common.wechat.res.ResWechatPrePay; import com.lunhan.water.common.wechat.res.ResWeiXinRefund; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.Objects; public class WechatPayV3Util { private static final String BASIC_API = "https://api.mch.weixin.qq.com"; private static final int KEY_LENGTH_BYTE = 32; private static final int TAG_LENGTH_BIT = 128; /** * 解密 * @param associatedData 附加数据 * @param nonce 随机串 * @param cipherText 数据密文 * @param aesKey apiv3秘钥 * @return 结果 * @throws Exception 异常 */ public static String decryptToString(byte[] associatedData, byte[] nonce, String cipherText,byte[] aesKey) throws Exception { try { if (aesKey.length != KEY_LENGTH_BYTE) { throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); } 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); if(associatedData != null && associatedData.length > 0) { cipher.updateAAD(associatedData); }else { cipher.updateAAD("".getBytes(StandardCharsets.UTF_8)); } return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), StandardCharsets.UTF_8); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw new IllegalStateException(e); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw new IllegalArgumentException(e); } } public static ExecutedResult createPay(ReqCreateWechatPay request, WechatAuthorization auth) { String url = "/v3/pay/transactions/jsapi"; String body = SerializeUtil.toJson(request); Map headers = new HashMap<>(); headers.put("Authorization", auth.getAuthorization("POST", url, body)); headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"); headers.put("Accept", "text/html,application/xhtml+xml,application/xml,application/json;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"); String response = null; try { response = HttpUtil.doPost(BASIC_API + url, body, EHttpContentType.JSON, headers); } catch (Exception e) { return ExecutedResult.failed("微信支付发起支付失败: " + e.getMessage()); } ResWechatPrePay result = SerializeUtil.toObject(response, ResWechatPrePay.class); if (Objects.isNull(result) || StringUtil.isNullOrEmpty(result.getPrepayId())) { return ExecutedResult.failed("微信支付返回报文无法解析: " + response); } return ExecutedResult.success(result.getPrepayId()); } public static ExecutedResult createRefund(ReqCreateWeiXinRefund request, WechatAuthorization auth) { String url = "/v3/refund/domestic/refunds"; String body = SerializeUtil.toJson(request); Map headers = new HashMap<>(); headers.put("Authorization", auth.getAuthorization("POST", url, body)); headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36"); headers.put("Accept", "text/html,application/xhtml+xml,application/xml,application/json;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"); String response = null; try { response = HttpUtil.doPost(BASIC_API + url, body, EHttpContentType.JSON, headers); } catch (Exception e) { return ExecutedResult.failed("微信支付发起退款失败: " + e.getMessage()); } ResWeiXinRefund result = SerializeUtil.toObject(response, ResWeiXinRefund.class); if (Objects.isNull(result) || StringUtil.isNullOrEmpty(result.getRefund_id())) { return ExecutedResult.failed("微信支付返回报文无法解析: " + response); } return ExecutedResult.success(result); } }