第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > 微信jsapi支付结果回调错误Tag mismatch

微信jsapi支付结果回调错误Tag mismatch

时间:2023-10-30 11:24:59

相关推荐

微信jsapi支付结果回调错误Tag mismatch

文章目录

现象错误信息微信官方说明官网说明解决过程疯狂之路走上正轨发现问题代码-测试正常解密controllerserviceserviceImpl相关工具和实体

现象

使用Java解密时,抛出异常AEADBadTagException: Tag mismatch!

错误信息

Tag mismatch

微信官方说明

官网说明

解决过程

疯狂之路

微信社区,参考了多种方案 微信社区-问题反馈参考度娘搜索,getBytes时传入编码“UTF-8”检查 apiv3秘钥在平台设置的和程序中使用的是否一致检查是否Base64解码,其实使用微信官方的程序即可,官方程序-java版-解密

走上正轨

微信客服沟通(重要解决手段,也是最终查出问题所在的关键)客服说回调数据中,普通支付,associated_data参数一定会传过来,但是在官网对于v3支付结果回调说明中,提示associated_data不是必传的,而且我也没有找到相关说明在什么情况下传,什么情况不传,有找到的伙伴可以留言做个说明第6条中的问题,让我相信微信发送和我接收的数据没有问题😢 ,其实,最主要的就是缺少了associated_data数据,造成解密时出的问题

发现问题

接口使用的@RequestBody接收微信发送过来的数据,但是,但是,但是,接收的数据丢失了一部分,😱😱😱,这个不清楚是springboot框架的问题,还是微信推送消息机制的问题,最终使用HttpServletRequest读取stream的方式解决

代码-测试正常解密

controller

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;/*** @author panda* @date /4/27 14:06* <p>* description 回调接口*/@RestController@RequestMapping("/pay")public class PortalWechatController {@Autowiredprivate PortalWechatService portalWechatService;@PostMapping("/wx/callback")public String wxCallback(HttpServletRequest request){return portalWechatService.callBack(request);}}

service

import javax.servlet.http.HttpServletRequest;/*** @author panda* @date /4/27 14:10* <p>* description service*/public interface PortalWechatService {/*** 微信支付回调* @param request 请求* @return 结果*/String callBack(HttpServletRequest request);}

serviceImpl

import cn.hutool.core.util.StrUtil;import com.alibaba.fastjson.JSONObject;import lombok.extern.slf4j.Slf4j;import mons.codec.binary.Base64;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;/*** @author panda* @date /4/27 14:11* <p>* description serviceImpl*/@Slf4j@Servicepublic class PortalWechatServiceimpl implements PortalWechatService {/*** 微信支付回调** @param request 请求* @return 结果*/@Overridepublic String callBack(HttpServletRequest request) {// TODO 必须使用request获取body(readLine读取),微信推送的消息使用@RequestBody可能一次性无法读完,造成解密失败String resCode = "SUCCESS";String resMessage = "成功";String streamReadString = getRequestBody(request);WxPayCallbackModel model = JSONObject.parseObject(streamReadString, WxPayCallbackModel.class);log.info("pay callback: request read={}", streamReadString);try {WxPayCallbackResourceModel resource = model.getResource();String associatedData = resource.getAssociated_data();String nonce = resource.getNonce();String ciphertext = resource.getCiphertext();byte[] aesKey = WeChatPayCostant.API_V3_SECRET.trim().toLowerCase().getBytes("UTF-8");byte[] associatedDataBytes = associatedData.getBytes("UTF-8");byte[] nonceBytes = nonce.getBytes("UTF-8");byte[] ciphertextBytes = Base64.decodeBase64(ciphertext);// 开始解密AesUtil aesUtil = new AesUtil(aesKey);String decryptedString = aesUtil.decryptToString(associatedDataBytes, nonceBytes, ciphertextBytes);log.info("微信支付回调 - 解密: {}", decryptedString);// 解密得到的json结果JSONObject decryptedJsonObj = JSONObject.parseObject(decryptedString);String code = decryptedJsonObj.getString("out_trade_no");// 1. 数据库通过订单号查询到的的订单// 2. 修改订单中支付状态,微信通知状态,支付相关其它信息} catch (Exception e) {log.error("支付回调失败: {}", e.getMessage());// e.printStackTrace();resCode = "FAIL";resMessage = "支付失败";}JSONObject returnJson = new JSONObject();returnJson.put("code", resCode);returnJson.put("message", resMessage);return returnJson.toJSONString();}// *****************************************************业务支撑*****************************************************/*** 获取请求体* @param request 请求*/private String getRequestBody(HttpServletRequest request) {ServletInputStream stream = null;BufferedReader reader = null;StringBuilder sb = new StringBuilder();try {stream = request.getInputStream();// 获取响应reader = new BufferedReader(new InputStreamReader(stream));String line;while ((line = reader.readLine()) != null) {sb.append(line);}} catch (IOException e) {e.printStackTrace();} finally {assert stream != null;try {stream.close();} catch (IOException e) {e.printStackTrace();}assert reader != null;try {reader.close();} catch (IOException e) {e.printStackTrace();}}return sb.toString();}}

相关工具和实体

import lombok.Data;/*** @author panda* @date /5/8 14:12* <p>* description 微信支付回调 model*/@Datapublic class WxPayCallbackModel {/*** 通知的唯一ID* 示例值:EV-022511223320873*/private String id = "";/*** 通知创建的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:-05-20T13:29:35+08:00表示北京时间05月20日13点29分35秒。* 示例值:-05-20T13:29:35+08:00*/private String create_time = "";/*** 通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS* 示例值:TRANSACTION.SUCCESS*/private String event_type = "";/*** 通知的资源数据类型,支付成功通知为encrypt-resource* 示例值:encrypt-resource*/private String resource_type = "";/*** 回调摘要* 示例值:支付成功*/private String summary = "";/*** 通知资源数据* json格式,见示例*/private WxPayCallbackResourceModel resource;@Overridepublic String toString() {return "WxPayCallbackModel{" +"id='" + id + '\'' +", create_time='" + create_time + '\'' +", event_type='" + event_type + '\'' +", resource_type='" + resource_type + '\'' +", summary='" + summary + '\'' +", resource=" + resource.toString() +'}';}}import lombok.Data;/*** @author panda* @date /5/8 14:12* <p>* description 微信支付回调通知数据 model*/@Datapublic class WxPayCallbackResourceModel {/*** 加密算法类型* 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM* 示例值:AEAD_AES_256_GCM*/private String algorithm = "";/*** 数据密文* Base64编码后的开启/停用结果数据密文* 示例值:sadsadsadsad*/private String ciphertext = "";/*** 附加数据* 附加数据* 示例值:fdasfwqewlkja484w*/private String associated_data = "";/*** 原始类型* 原始回调类型,为transaction* 示例值:transaction*/private String original_type = "";/*** 随机串* 加密使用的随机串* 示例值:fdasflkja484w*/private String nonce = "";@Overridepublic String toString() {return "WxPayCallbackResourceModel{" +"algorithm='" + algorithm + '\'' +", ciphertext='" + ciphertext + '\'' +", associated_data='" + associated_data + '\'' +", original_type='" + original_type + '\'' +", nonce='" + nonce + '\'' +'}';}}/*** @author panda* @date /4/27 11:21* <p>* description 微信支付常量*/public interface WeChatPayCostant {/*** 应用id*/String APP_ID = "";/*** 直连商户号*/String MCH_ID = "";/*** 通知地址*/String NOTIFY_URL = "https://a./xxx";/*** 货币类型*/String CURRENCY = "CNY";/*** 微信支付url*/String PAY_URL = "https://api.mch./v3/pay/transactions/jsapi";/*** APIv3密钥 32位(数字 + 字母_小写),可以直接使用Hutool的IdUtil.simpleUuid()生成*/String API_V3_SECRET = "";/*** 商户API证书序列号*/String SERIAL_NO = "";}import javax.crypto.Cipher;import javax.crypto.NoSuchPaddingException;import javax.crypto.spec.GCMParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.io.IOException;import java.security.GeneralSecurityException;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.util.Base64;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 != 32) {throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");} else {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(this.aesKey, "AES");GCMParameterSpec spec = new GCMParameterSpec(128, nonce);cipher.init(2, key, spec);cipher.updateAAD(associatedData);return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "UTF-8");} catch (NoSuchPaddingException | NoSuchAlgorithmException var7) {throw new IllegalStateException(var7);} catch (InvalidAlgorithmParameterException | InvalidKeyException var8) {throw new IllegalArgumentException(var8);}}public String decryptToString(byte[] associatedData, byte[] nonce, byte[] ciphertext) throws GeneralSecurityException, IOException {try {Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec key = new SecretKeySpec(this.aesKey, "AES");GCMParameterSpec spec = new GCMParameterSpec(128, nonce);cipher.init(2, key, spec);cipher.updateAAD(associatedData);return new String(cipher.doFinal(ciphertext), "UTF-8");} catch (NoSuchPaddingException | NoSuchAlgorithmException var7) {throw new IllegalStateException(var7);} catch (InvalidAlgorithmParameterException | InvalidKeyException var8) {throw new IllegalArgumentException(var8);}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。