1.商户退款单号(out_refund_no)必须唯一,如果不唯一微信端返回的错误提示是“支付单号校验不一致”,需要注意。2.退款解密算法注意操作密钥。在此记录下解密代码。(1)对加密串A做base64解码,得到加密串B(2)对商户key做md5,得到32位小写key(key设置路径:微信商户平台(pay.weixin.qq.com)–账户设置–API安全–密钥设置)3)用key对加密串B做AES-256-ECB解密(PKCS7Padding)
Base64Utils.java
publicclassBase64Utils{
privatestaticchar[]base64EncodeChars=newchar[]{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',};
privatestaticbyte[]base64DecodeChars=newbyte[]{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1};
privateBase64Utils(){}
/将字节数组编码为字符串@paramdata/publicstaticStringencode(byte[]data){StringBuffersb=newStringBuffer();intlen=data.length;inti=0;intb1,b2,b3;
while(ilen){b1=data[i++]&0xff;if(i==len){sb.append(base64EncodeChars[b12]);sb.append(base64EncodeChars[(b1&0x3)4]);sb.append("==");break;}b2=data[i++]&0xff;if(i==len){sb.append(base64EncodeChars[b12]);sb.append(base64EncodeChars[((b1&0x03)4)|((b2&0xf0)4)]);sb.append(base64EncodeChars[(b2&0x0f)2]);sb.append("=");break;}b3=data[i++]&0xff;sb.append(base64EncodeChars[b12]);sb.append(base64EncodeChars[((b1&0x03)4)|((b2&0xf0)4)]);sb.append(base64EncodeChars[((b2&0x0f)2)|((b3&0xc0)6)]);sb.append(base64EncodeChars[b3&0x3f]);}returnsb.toString();}
/解密@paramstr/publicstaticbyte[]decode(Stringstr)throwsException{byte[]data=str.getBytes("GBK");intlen=data.length;ByteArrayOutputStreambuf=newByteArrayOutputStream(len);inti=0;intb1,b2,b3,b4;
while(ilen){
/b1/do{b1=base64DecodeChars[data[i++]];}while(ilen&&b1==-1);if(b1==-1){break;}
/b2/do{b2=base64DecodeChars[data[i++]];}while(ilen&&b2==-1);if(b2==-1){break;}buf.write((b12)|((b2&0x30)4));
/b3/do{b3=data[i++];if(b3==61){returnbuf.toByteArray();}b3=base64DecodeChars[b3];}while(ilen&&b3==-1);if(b3==-1){break;}buf.write(((b2&0x0f)4)|((b3&0x3c)2));
/b4/do{b4=data[i++];if(b4==61){returnbuf.toByteArray();}b4=base64DecodeChars[b4];}while(ilen&&b4==-1);if(b4==-1){break;}buf.write(((b3&0x03)6)|b4);}returnbuf.toByteArray();}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113Md5Util.java
publicclassMd5Util{/MD5加密字符串@paramsource要加密的字符串@return/publicstaticStringgetMD5(Stringsource){Strings=null;charhexDigits[]={//用来将字节转换成16进制表示的字符'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};try{java.security.MessageDigestmd=java.security.MessageDigest.getInstance("MD5");if(source==null||"".equals(source)){returnnull;}md.update(source.getBytes());bytetmp[]=md.digest();//MD5的计算结果是一个128位的长整数,//用字节表示就是16个字节charstr[]=newchar[162];//每个字节用16进制表示的话,使用两个字符,//所以表示成16进制需要32个字符intk=0;//表示转换结果中对应的字符位置for(inti=0;i16;i++){//从第一个字节开始,对MD5的每一个字节//转换成16进制字符的转换bytebyte0=tmp[i];//取第i个字节str[k++]=hexDigits[byte04&0xf];//取字节中高4位的数字转换,////为逻辑右移,将符号位一起右移str[k++]=hexDigits[byte0&0xf];//取字节中低4位的数字转换}s=newString(str);//换后的结果转换为字符串
}catch(Exceptione){e.printStackTrace();}returns;}
/MD5加密byte数据@paramsource要加密字符串的byte数据@return/publicstaticStringgetMD5(byte[]source){Strings=null;charhexDigits[]={//用来将字节转换成16进制表示的字符'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};try{java.security.MessageDigestmd=java.security.MessageDigest.getInstance("MD5");md.update(source);bytetmp[]=md.digest();//MD5的计算结果是一个128位的长整数,//用字节表示就是16个字节charstr[]=newchar[162];//每个字节用16进制表示的话,使用两个字符,//所以表示成16进制需要32个字符intk=0;//表示转换结果中对应的字符位置for(inti=0;i16;i++){//从第一个字节开始,对MD5的每一个字节//转换成16进制字符的转换bytebyte0=tmp[i];//取第i个字节str[k++]=hexDigits[byte04&0xf];//取字节中高4位的数字转换,////为逻辑右移,将符号位一起右移str[k++]=hexDigits[byte0&0xf];//取字节中低4位的数字转换}s=newString(str);//换后的结果转换为字符串
}catch(Exceptione){e.printStackTrace();}returns;}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778Encrypt.java
publicclassEncrypt{publicstaticbooleaninitialized=false;
publicstaticfinalStringALGORITHM="AES/ECB/PKCS7Padding";
/@paramStringstr要被加密的字符串@parambyte[]key加/解密要用的长度为32的字节数组(256位)密钥@returnbyte[]加密后的字节数组/publicstaticbyte[]Aes256Encode(Stringstr,byte[]key){initialize();byte[]result=null;try{Ciphercipher=Cipher.getInstance(ALGORITHM,"BC");SecretKeySpeckeySpec=newSecretKeySpec(key,"AES");//生成加密解密需要的Keycipher.init(Cipher.ENCRYPT_MODE,keySpec);result=cipher.doFinal(str.getBytes("UTF-8"));}catch(Exceptione){e.printStackTrace();}returnresult;}
/@parambyte[]bytes要被解密的字节数组@parambyte[]key加/解密要用的长度为32的字节数组(256位)密钥@returnString解密后的字符串/publicstaticStringAes256Decode(byte[]bytes,byte[]key){initialize();Stringresult=null;try{Ciphercipher=Cipher.getInstance(ALGORITHM,"BC");SecretKeySpeckeySpec=newSecretKeySpec(key,"AES");//生成加密解密需要的Keycipher.init(Cipher.DECRYPT_MODE,keySpec);byte[]decoded=cipher.doFinal(bytes);result=newString(decoded,"UTF-8");}catch(Exceptione){e.printStackTrace();}returnresult;}
publicstaticvoidinitialize(){if(initialized)return;//Security.addProvider(newBouncyCastleProvider());Security.addProvider(neworg.bouncycastle.jce.provider.BouncyCastleProvider());initialized=true;}}---------------------作者:FunHours2016来源:CSDN原文:https://blog.csdn.net/a250787181/article/details/79005227版权声明:本文为博主原创文章,转载请附上博文链接!
最新资讯
-

小程序制作平台选型踩坑记录:2026年五大主流方案横向对比
2026 年微信小程序月活达 10.7 亿、覆盖 108 个行业,本次横向对比即速应用、乔拓云、凡科、有赞、微盟五大主流平台,分三阶段给出选型结论,核心聚焦成本、扩展性、运营能力三大维度。 -

即速应用,赋能企业玩转微信小程序智慧经营
作为国内领军的智慧商业经营服务商,即速应用始终秉承“让每个企业都拥有自己的智慧店铺”的愿景,持续赋能更多企业玩转智慧经营。即速应用旗下拥有“小程序搭建工具-即速应用”、“私域流量专家-即客云”等产品,帮助商家打通互联网全生态营销闭环。 -

即客云2.0重磅更新,让微信小程序运营更简单!
即客云作为一款基于企业微信的第三方工具,现从多维度提供超过30种功能,自上线以来,已服务多家企业,受到一致好评。近期,我们根据客户反馈和市场调研正式推出升级版 即客云2.0!更新了私域运营SOP,群日历功能,批量拓客,客户雷达,消息推送,个人欢迎语,帮助企业更好运用企业微信;同时提升了社群运营工作标准化,提升运营效率,帮助企业实现客户增长,玩转私域流量。










