前言: 最近公司做产品,有这样几个需求

  • 微信退款
  • 微信企业给用户转账到零钱
  • 通过微信给用户转账到银行卡
  • 支付宝退款
  • 支付宝转账到余额

根据上述需求,本人呢,做了几天的研究与学习,在此期间不免会出现摇头抓脑壳的现象,但是最终还是收获了一些经验教训,不过在几天的研究中,得出一个结论,微信开发文档真的有够坑的,让初次接触的我是狠狠的抓了好几次脑壳,为了避免在后续的开发中再次遇到这种情况,我决定记录下这段时间的研究成果,俗话说的好,好记忆不如烂笔头,当然在本页不会介绍关于支付宝方面的开发,因为个人觉得支付宝的开发文档很人性化,相当节省时间,简单到直接拷贝源码,就可以直接使用,可能这样会减少你在研究技术中获得的提升快感,但是在公司那么急促的开始时间来说,不得不给支付宝点一万个赞,虽然微信开发文档相对支付宝来说差一些,但是你却能在研究中得到一定的提升,所以,综合来说各有各的好吧

准备工作

第一、开通微信转账功能,设置API可访问IP地址(必须)

注意:如果是本地测试IP地址,请使用你本地路由的公网地址,否则会提示IP地址无权访问
  • 开发企业转账时需要的前提条件 微信企业转账官方开发文档点此前往
  • 设置可被允许访问的接口IP地址




第二、下载官方的开发SDK(注:当前介绍官方的v3.0.9版本,但该版本存在BUG,将会影响支付功能,稍后会介绍下如何解决)

  • 下载官方SDK(按照自己的开发习惯下载相应的SDK,本文主要介绍JAVA版本的)
  • 下载地址 微信官网SDK下载地址

第三、下载API安全证书(微信转账,退款都是需要证书的)

第四、证书使用方法

  • 微信官网证书使用说明
  • 本文通过JAVA代码从外部文件中读取证书来实现开发功能
  • 证书存在地址
  • 加载证书
   /*** 证书地址:resource下*/public static final String CERT_PATH = "/cert/apiclient_cert.p12";/*** 获取证书*/public InputStream getCertStream() {ClassPathResource cl = new ClassPathResource(CERT_PATH);try {return cl.getInputStream();} catch (IOException e) {e.printStackTrace();}return null;}

第五、编写WeChatPayConfig.java(需要继承SDK的WXPayConfig.java)

package com.casom.pay.config;import java.io.IOException;
import java.io.InputStream;import org.springframework.core.io.ClassPathResource;import com.github.wxpay.sdk.IWXPayDomain;
import com.github.wxpay.sdk.WXPayConfig;public class WeChatPayConfig extends WXPayConfig {/*** 微信支付回调地址*/public static final String NOTITY_URL = "自己设置的回调地址";/*** 证书地址:resource下*/public static final String CERT_PATH = "/cert/apiclient_cert.p12";/*** pkcs8公钥*/public String RSA_PKCS8 = "/rsa/pkcs8.pem";/*** 填充算法*/public String FILLING_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING";/*** SECRETKEY*/private String key = "公司的SECRETKEY";/*** 申请商户号的appid或商户号绑定的appid*/public String getAppID() {return "公司的APPID";}/*** 商户号*/public String getMchID() {return "公司商户号";}/*** SECRETKEY*/public String getKey() {return this.key;}/*** 获取证书*/public InputStream getCertStream() {ClassPathResource cl = new ClassPathResource(CERT_PATH);try {return cl.getInputStream();} catch (IOException e) {e.printStackTrace();}return null;}/*** 接口域名*/@Overridepublic IWXPayDomain getWXPayDomain() { // 这个方法需要这样实现, 否则无法正常初始化WXPayIWXPayDomain iwxPayDomain = new IWXPayDomain() {@Overridepublic void report(String domain, long elapsedTimeMillis, Exception ex) {}@Overridepublic DomainInfo getDomain(WXPayConfig config) {return new IWXPayDomain.DomainInfo(doam_api, true);}};return iwxPayDomain;}
}

第六、开发企业微信转账到用户零钱

  • 请求接口参数可在微信官网查看,这里不做介绍
  • 微信官网企业转账开发文档(转账至微信零钱)
  • 在下载的SDK包的WXPayConstants类中加入下列代码(转账到微信零钱和银行都需要用到此类)
 /*** RSA获取路径*/public static final String DOMAIN_APIRSA = "fraud.mch.weixin.qq.com";/*** 转账到零钱*/public static final String TRANSFER_TO_WECHAT_WALLET_URL_SUFFIX = "/mmpaymkttransfers/promotion/transfers";/*** 转账到银行卡*/public static final String TRANSFER_TO_BANK_URL_SUFFIX = "/mmpaysptrans/pay_bank";/*** 获取公钥*/public static final String RSA_GET_PUBLIC_KEY = "/risk/getpublickey";
  • 在下载的SDK包的WXPay类中加入下列方法
   /*** 企业转账* @param reqData* @param isTransBank(true:转账到银行卡,false:转账到微信零钱)* @return* @throws Exception*/public Map<String,String> trans(Map<String, String> reqData,boolean isTransBank) throws Exception{return this.trans(reqData, isTransBank, this.config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());}/*** 企业转账* @param reqData* @param isTransBank(true:转账到银行卡,false:转账到微信零钱)* @param connectTimeoutMs* @param readTimeoutMs* @return* @throws Exception*/public Map<String, String> trans(Map<String, String> reqData,boolean isTransBank, int connectTimeoutMs, int readTimeoutMs) throws Exception {String url;if (isTransBank) {url = WXPayConstants.TRANSFER_TO_BANK_URL_SUFFIX;}else {url = WXPayConstants.TRANSFER_TO_WECHAT_WALLET_URL_SUFFIX;}//返回结果转化为Map格式return WXPayUtil.xmlToMap(this.requestWithCert(url, reqData, connectTimeoutMs, readTimeoutMs));}
  • 在自己的Controller或者Service中添加下列代码(下列代码都是以Map对象接收,自己可以创建对象来接收请求参数和返回结果)
   /*** 企业转账到零钱* @param partner_trade_no 商户订单号(唯一)* @param amount 转账金额* @param desc 转账备注* @param card_code (如果转账到银行卡:则表示银行卡号;如果转账到微信零钱:则表示微信用户的openid)* @param real_name 真实姓名* @param spbill_create_ip 服务端或者客户端操作IP(与在商户号中设置的没有任何关系)* @return*/public JSONObject transfer(String partner_trade_no, BigDecimal amount, String desc, String card_code, String real_name, String spbill_create_ip) {try {WeChatPayConfig config = new WeChatPayConfig();WXPay wxpay = new WXPay(config);Map<String, String> data = setTransWalletParameter(config, partner_trade_no, card_code, real_name, amount, desc, other);Map<String, String> rmap = wxpay.trans(data, false);String rjson = JSON.toJSONString(rmap);log.info("resp:{}", rjson);return JSONObject.parseObject(rjson);} catch (Exception e) {e.printStackTrace();}return null;}/*** 设置转账至微信零钱的请求参数* * @param config* @param partner_trade_no* @param openid* @param re_user_name* @param amount* @param desc* @param spbill_create_ip* @return*/private Map<String, String> setTransWalletParameter(WeChatPayConfig config, String partner_trade_no, String openid, String re_user_name, BigDecimal amount, String desc, String spbill_create_ip) throws Exception {Map<String, String> data = new HashMap<String, String>();// APPIDdata.put("mch_appid", config.getAppID());// 商户号data.put("mchid", config.getMchID());// 随机字符串data.put("nonce_str", WXPayUtil.generateNonceStr());// 商户订单号data.put("partner_trade_no", partner_trade_no);// 用户openiddata.put("openid", openid);/** NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名*/data.put("check_name", "NO_CHECK");// 真实姓名data.put("re_user_name", re_user_name);// 转账金额data.put("amount", String.valueOf(amount.multiply(new BigDecimal(100)).intValue()));// 备注data.put("desc", desc);// 操作IPdata.put("spbill_create_ip", spbill_create_ip);// 签名data.put("sign", WXPayUtil.generateSignature(data, config.getKey()));return data;}

第七、企业微信转账银行卡

  • 请求接口参数可在微信官网查看,这里不做介绍
  • 微信官网企业转账开发文档(转账至银行卡)
  • 银行卡转账稍微麻烦点
    1、需要拿到微信端提供的rsa publicKey(pkcs1格式)
    2、需要在Linux系统通过命令将pkcs1格式的key转化为pkcs8(也可以在线转或者下载工具转化)
    3、命令openssl rsa -RSAPublicKey_in -in pkcs1.pem -pubout > pkcs8.pem
    4、key存在位置如下
  • 在你的Controller或者Service的java文件中加入下列方法
   /*** 企业转账到银行卡* @param partner_trade_no 商户订单号(唯一)* @param amount 转账金额* @param desc 转账备注* @param card_code (如果转账到银行卡:则表示银行卡号;如果转账到微信零钱:则表示微信用户的openid)* @param real_name 真实姓名* @param bank_name 开户行名称* @return*/public JSONObject transfer(String partner_trade_no, BigDecimal amount, String desc, String card_code, String real_name, String bank_name) {try {WeChatPayConfig config = new WeChatPayConfig();WXPay wxpay = new WXPay(config);Map<String, String> data = setTransWalletParameter(config, partner_trade_no, card_code, real_name, amount, desc, bank_name);Map<String, String> rmap = wxpay.trans(data, true);String rjson = JSON.toJSONString(rmap);log.info("resp:{}", rjson);return JSONObject.parseObject(rjson);} catch (Exception e) {e.printStackTrace();}return null;}/*** 设置转到值银行卡的请求参数* * @param config* @param partner_trade_no* @param enc_bank_no* @param enc_true_name* @param bank_name* @param amount* @param desc* @return* @throws Exception*/private Map<String, String> setTransBankParameter(WeChatPayConfig config, WXPay wxpay, String partner_trade_no, String enc_bank_no, String enc_true_name, String bank_name, BigDecimal amount, String desc) throws Exception {Map<String, String> data = new HashMap<String, String>();// 商户号data.put("mch_id", config.getMchID());// 随机字符串data.put("nonce_str", WXPayUtil.generateNonceStr());// 商户订单号data.put("partner_trade_no", partner_trade_no);// 公钥PublicKey pub = WxpayRSAEncrypt.getPubKey(this.getClass().getResource(config.RSA_PKCS8).getPath(), "RSA");// 银行卡号byte[] b_card_number = WxpayRSAEncrypt.encrypt(enc_bank_no.getBytes(), pub, 2048, 11, config.FILLING_ALGORITHM); // 对银行账号进行加密String s_card_number = WxPayBase64.encode(b_card_number);// 并转为base64格式// 用户银行卡账户data.put("enc_bank_no", s_card_number);// 持卡人真实姓名byte[] b_real_name = WxpayRSAEncrypt.encrypt(enc_true_name.getBytes(), pub, 2048, 11, config.FILLING_ALGORITHM); // 对银行账号进行加密String s_real_name = WxPayBase64.encode(b_real_name);// 并转为base64格式// 用户姓名data.put("enc_true_name", s_real_name);// 开户行编号data.put("bank_code", WxpayBank.getBankCode(bank_name));// 转账金额data.put("amount", String.valueOf(amount.multiply(new BigDecimal(100)).intValue()));// 备注data.put("desc", desc);// 签名data.put("sign", WXPayUtil.generateSignature(data, config.getKey()));return data;}
  • rsa加密工具类WxpayRSAEncrypt (网上获取)
package com.github.wxpay.sdk;import javax.crypto.Cipher;
import java.io.*;
import java.lang.reflect.Method;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;public class WxpayRSAEncrypt {public static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey, int keyLength, int reserveSize,String cipherAlgorithm) throws Exception {int keyByteSize = keyLength / 8;int decryptBlockSize = keyByteSize - reserveSize;int nBlock = encryptedBytes.length / keyByteSize;ByteArrayOutputStream outbuf = null;try {Cipher cipher = Cipher.getInstance(cipherAlgorithm);cipher.init(Cipher.DECRYPT_MODE, privateKey);outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize);for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) {int inputLen = encryptedBytes.length - offset;if (inputLen > keyByteSize) {inputLen = keyByteSize;}byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen);outbuf.write(decryptedBlock);}outbuf.flush();return outbuf.toByteArray();} catch (Exception e) {throw new Exception("DEENCRYPT ERROR:", e);} finally {try {if (outbuf != null) {outbuf.close();}} catch (Exception e) {outbuf = null;throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);}}}public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize,String cipherAlgorithm) throws Exception {int keyByteSize = keyLength / 8;int encryptBlockSize = keyByteSize - reserveSize;int nBlock = plainBytes.length / encryptBlockSize;if ((plainBytes.length % encryptBlockSize) != 0) {nBlock += 1;}ByteArrayOutputStream outbuf = null;try {Cipher cipher = Cipher.getInstance(cipherAlgorithm);cipher.init(Cipher.ENCRYPT_MODE, publicKey);outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {int inputLen = plainBytes.length - offset;if (inputLen > encryptBlockSize) {inputLen = encryptBlockSize;}byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);outbuf.write(encryptedBlock);}outbuf.flush();return outbuf.toByteArray();} catch (Exception e) {throw new Exception("ENCRYPT ERROR:", e);} finally {try {if (outbuf != null) {outbuf.close();}} catch (Exception e) {outbuf = null;throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);}}}public static PrivateKey getPriKey(String privateKeyPath, String keyAlgorithm) {PrivateKey privateKey = null;InputStream inputStream = null;try {if (inputStream == null) {System.out.println("hahhah1!");}inputStream = new FileInputStream(privateKeyPath);privateKey = getPrivateKey(inputStream, keyAlgorithm);} catch (Exception e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (Exception e) {e.printStackTrace();}}}return privateKey;}public static PublicKey getPubKey(String publicKeyPath, String keyAlgorithm) {PublicKey publicKey = null;InputStream inputStream = null;try {inputStream = new FileInputStream(publicKeyPath);publicKey = getPublicKey(inputStream, keyAlgorithm);} catch (Exception e) {e.printStackTrace();// EAD PUBLIC KEY ERROR} finally {if (inputStream != null) {try {inputStream.close();} catch (Exception e) {e.printStackTrace();}}}return publicKey;}public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws Exception {try {BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));StringBuilder sb = new StringBuilder();String readLine = null;while ((readLine = br.readLine()) != null) {if (readLine.charAt(0) == '-') {continue;} else {sb.append(readLine);sb.append('\r');}}X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(decodeBase64(sb.toString()));KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);// 下行出错 java.security.spec.InvalidKeySpecException:// java.security.InvalidKeyException: IOException:// DerInputStream.getLength(): lengthTag=127, too big.PublicKey publicKey = keyFactory.generatePublic(pubX509);return publicKey;} catch (Exception e) {e.printStackTrace();throw new Exception("READ PUBLIC KEY ERROR:", e);} finally {try {if (inputStream != null) {inputStream.close();}} catch (IOException e) {inputStream = null;throw new Exception("INPUT STREAM CLOSE ERROR:", e);}}}public static PrivateKey getPrivateKey(InputStream inputStream, String keyAlgorithm) throws Exception {try {BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));StringBuilder sb = new StringBuilder();String readLine = null;while ((readLine = br.readLine()) != null) {if (readLine.charAt(0) == '-') {continue;} else {sb.append(readLine);sb.append('\r');}}PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decodeBase64(sb.toString()));KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);PrivateKey privateKey = keyFactory.generatePrivate(priPKCS8);return privateKey;} catch (Exception e) {throw new Exception("READ PRIVATE KEY ERROR:", e);} finally {try {if (inputStream != null) {inputStream.close();}} catch (IOException e) {inputStream = null;throw new Exception("INPUT STREAM CLOSE ERROR:", e);}}}// 一下面是base64的编码和解码public static String encodeBase64(byte[] input) throws Exception {@SuppressWarnings("rawtypes")Class clazz = Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");@SuppressWarnings("unchecked")Method mainMethod = clazz.getMethod("encode", byte[].class);mainMethod.setAccessible(true);Object retObj = mainMethod.invoke(null, new Object[] { input });return (String) retObj;}/**** decode by Base64*/public static byte[] decodeBase64(String input) throws Exception {@SuppressWarnings("rawtypes")Class clazz = Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");@SuppressWarnings("unchecked")Method mainMethod = clazz.getMethod("decode", String.class);mainMethod.setAccessible(true);Object retObj = mainMethod.invoke(null, input);return (byte[]) retObj;}
}
  • base64加密工具WxPayBase64(网上获取)
package com.github.wxpay.sdk;public class WxPayBase64 {static private final int BASELENGTH = 128;static private final int LOOKUPLENGTH = 64;static private final int TWENTYFOURBITGROUP = 24;static private final int EIGHTBIT = 8;static private final int SIXTEENBIT = 16;static private final int FOURBYTE = 4;static private final int SIGN = -128;static private final char PAD = '=';static private final boolean fDebug = false;static final private byte[] base64Alphabet = new byte[BASELENGTH];static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];static {for (int i = 0; i < BASELENGTH; ++i) {base64Alphabet[i] = -1;}for (int i = 'Z'; i >= 'A'; i--) {base64Alphabet[i] = (byte) (i - 'A');}for (int i = 'z'; i >= 'a'; i--) {base64Alphabet[i] = (byte) (i - 'a' + 26);}for (int i = '9'; i >= '0'; i--) {base64Alphabet[i] = (byte) (i - '0' + 52);}base64Alphabet['+'] = 62;base64Alphabet['/'] = 63;for (int i = 0; i <= 25; i++) {lookUpBase64Alphabet[i] = (char) ('A' + i);}for (int i = 26, j = 0; i <= 51; i++, j++) {lookUpBase64Alphabet[i] = (char) ('a' + j);}for (int i = 52, j = 0; i <= 61; i++, j++) {lookUpBase64Alphabet[i] = (char) ('0' + j);}lookUpBase64Alphabet[62] = (char) '+';lookUpBase64Alphabet[63] = (char) '/';}private static boolean isWhiteSpace(char octect) {return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);}private static boolean isPad(char octect) {return (octect == PAD);}private static boolean isData(char octect) {return (octect < BASELENGTH && base64Alphabet[octect] != -1);}/*** Encodes hex octects into Base64** @param binaryData*            Array containing binaryData* @return Encoded Base64 array*/public static String encode(byte[] binaryData) {if (binaryData == null) {return null;}int lengthDataBits = binaryData.length * EIGHTBIT;if (lengthDataBits == 0) {return "";}int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;char encodedData[] = null;encodedData = new char[numberQuartet * 4];byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;int encodedIndex = 0;int dataIndex = 0;if (fDebug) {System.out.println("number of triplets = " + numberTriplets);}for (int i = 0; i < numberTriplets; i++) {b1 = binaryData[dataIndex++];b2 = binaryData[dataIndex++];b3 = binaryData[dataIndex++];if (fDebug) {System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3);}l = (byte) (b2 & 0x0f);k = (byte) (b1 & 0x03);byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);if (fDebug) {System.out.println("val2 = " + val2);System.out.println("k4   = " + (k << 4));System.out.println("vak  = " + (val2 | (k << 4)));}encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];}// form integral number of 6-bit groupsif (fewerThan24bits == EIGHTBIT) {b1 = binaryData[dataIndex];k = (byte) (b1 & 0x03);if (fDebug) {System.out.println("b1=" + b1);System.out.println("b1<<2 = " + (b1 >> 2));}byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];encodedData[encodedIndex++] = PAD;encodedData[encodedIndex++] = PAD;} else if (fewerThan24bits == SIXTEENBIT) {b1 = binaryData[dataIndex];b2 = binaryData[dataIndex + 1];l = (byte) (b2 & 0x0f);k = (byte) (b1 & 0x03);byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];encodedData[encodedIndex++] = PAD;}return new String(encodedData);}/*** Decodes Base64 data into octects** @param encoded*            string containing Base64 data* @return Array containind decoded data.*/public static byte[] decode(String encoded) {if (encoded == null) {return null;}char[] base64Data = encoded.toCharArray();// remove white spacesint len = removeWhiteSpace(base64Data);if (len % FOURBYTE != 0) {return null;// should be divisible by four}int numberQuadruple = (len / FOURBYTE);if (numberQuadruple == 0) {return new byte[0];}byte decodedData[] = null;byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;char d1 = 0, d2 = 0, d3 = 0, d4 = 0;int i = 0;int encodedIndex = 0;int dataIndex = 0;decodedData = new byte[(numberQuadruple) * 3];for (; i < numberQuadruple - 1; i++) {if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {return null;} // if found "no data" just return nullb1 = base64Alphabet[d1];b2 = base64Alphabet[d2];b3 = base64Alphabet[d3];b4 = base64Alphabet[d4];decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);}if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {return null;// if found "no data" just return null}b1 = base64Alphabet[d1];b2 = base64Alphabet[d2];d3 = base64Data[dataIndex++];d4 = base64Data[dataIndex++];if (!isData((d3)) || !isData((d4))) {// Check if they are PAD charactersif (isPad(d3) && isPad(d4)) {if ((b2 & 0xf) != 0)// last 4 bits should be zero{return null;}byte[] tmp = new byte[i * 3 + 1];System.arraycopy(decodedData, 0, tmp, 0, i * 3);tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);return tmp;} else if (!isPad(d3) && isPad(d4)) {b3 = base64Alphabet[d3];if ((b3 & 0x3) != 0)// last 2 bits should be zero{return null;}byte[] tmp = new byte[i * 3 + 2];System.arraycopy(decodedData, 0, tmp, 0, i * 3);tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));return tmp;} else {return null;}} else { // No PAD e.g 3cQlb3 = base64Alphabet[d3];b4 = base64Alphabet[d4];decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);}return decodedData;}/*** remove WhiteSpace from MIME containing encoded Base64 data.** @param data*            the byte array of base64 data (with WS)* @return the new length*/private static int removeWhiteSpace(char[] data) {if (data == null) {return 0;}// count characters that's not whitespaceint newSize = 0;int len = data.length;for (int i = 0; i < len; i++) {if (!isWhiteSpace(data[i])) {data[newSize++] = data[i];}}return newSize;}
}
  • 微信目前支持的银行清单(前往微信官网查看支持的所有银行)
  • 添加自定义的枚举类做为编码的获取WxpayBank
package com.github.wxpay.sdk;import java.io.Serializable;/*** 开户行编号(企业转账到卡必须传递)* @author rf**/
public enum WXPayBank implements Serializable {B1("工商银行","1002"),B2("农业银行","1005"),B5("交通银行","1020"),B6("招商银行","1001"),B17("宁波银行","1056"),B3("建设银行","1003"),B8("民生银行","1006"),B12("兴业银行","1009"),B9("平安银行","1010"),B11("中信银行","1021"),B13("光大银行","1022"),B14("广发银行","1027"),B16("北京银行","1032"),B16I("北京银行","4836"),B10("浦发银行","1004"),B10I("浦东发展银行","1004"),B7("邮储银行","1066"),B7I("邮政银行","1066"),B7II("邮政储蓄银行","1066"),B15("华夏银行","1025"),B19("上海银行","1024"),B20("南京银行","1054"),B21("长子县融汇村镇银行","4755"),B22("长沙银行","4216"),B23("浙江泰隆商业银行","4051"),B24("中原银行","4753"),B25("企业银行(中国)","4761"),B26("顺德农商银行","4036"),B27("衡水银行","4752"),B28("长治银行","4756"),B29("大同银行","4767"),B30("河南省农村信用社","4115"),B31("宁夏黄河农村商业银行","4150"),B32("山西省农村信用社","4156"),B33("安徽省农村信用社","4166"),B34("甘肃省农村信用社","4157"),B35("天津农村商业银行","4153"),B36("广西壮族自治区农村信用社","4113"),B37("陕西省农村信用社","4108"),B38("深圳农村商业银行","4076"),B39("宁波鄞州农村商业银行","4052"),B40("浙江省农村信用社联合社","4764"),B41("江苏省农村信用社联合社","4217"),B42("江苏紫金农村商业银行股份有限公司","4072"),B43("北京中关村银行股份有限公司","4769"),B44("星展银行(中国)有限公司","4778"),B45("枣庄银行股份有限公司","4766"),B46("海口联合农村商业银行股份有限公司","4758"),B47("南洋商业银行(中国)有限公司","4763"),B4("中国银行","1026");/*** 银行名称*/private String bankName;/*** 获取银行名称* @return*/private String getBankName() {return bankName;}/*** 银行编号*/private String bankCode;/*** 检查提交的银行信息中是否存在下列的键,如果存在,匹配指定银行,获取银行编码*/private static String[] KEYS = {"村镇银行","农商银行","农村信用社联合社","农村信用","农村商业银行股份有限公司","农村商业银行","银行(中国)有限公司"};/*** 构造函数* @param bankName* @param bankCode*/private WXPayBank(String bankName,String bankCode){this.bankName = bankName;this.bankCode = bankCode;}/*** 获取银行编号* @param bankName* @return*/public static String getBankCode(String bankName){if(bankName == null || "".equals(bankName)) return null;if("中国银行".equals(bankName)) return WXPayBank.B4.bankCode;bankName = bankName.replace("中国","");for (WXPayBank bank : WXPayBank.values()) {  if (bank.getBankName().equals(bankName)) {  return bank.bankCode;  }  else {for(String key : KEYS) {if(bankName.contains(key)) {bankName = bankName.replace(key,"");if(bank.getBankName().contains(bankName)) {return bank.bankCode;}}}bankName = bankName.substring(0,2);if(bank.getBankName().contains(bankName)) {return bank.bankCode;}}}return null;}
}
  • 到此微信转账的所有功能就结束了,前边提到该版本的SDK将会影响微信支付,在下边做一个分析与改造

第八、解决微信SDKv3.0.9支付问题

  • 将SDK中WXPay中的下列代码
 /*** 作用:统一下单<br>* 场景:公共号支付、扫码支付、APP支付* @param reqData 向wxpay post的请求数据* @param connectTimeoutMs 连接超时时间,单位是毫秒* @param readTimeoutMs 读超时时间,单位是毫秒* @return API返回数据* @throws Exception*/public Map<String, String> unifiedOrder(Map<String, String> reqData,  int connectTimeoutMs, int readTimeoutMs) throws Exception {String url;if (this.useSandbox) {url = WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX;}else {url = WXPayConstants.UNIFIEDORDER_URL_SUFFIX;}if(this.notifyUrl != null) {reqData.put("notify_url", this.notifyUrl);}String respXml = this.requestWithoutCert(url, this.fillRequestData(reqData), connectTimeoutMs, readTimeoutMs);return this.processResponseXml(respXml);}
  • 改造为下列代码
 /*** 作用:统一下单<br>* 场景:公共号支付、扫码支付、APP支付* @param reqData 向wxpay post的请求数据* @param connectTimeoutMs 连接超时时间,单位是毫秒* @param readTimeoutMs 读超时时间,单位是毫秒* @return API返回数据* @throws Exception*/public Map<String, String> unifiedOrder(Map<String, String> reqData,  int connectTimeoutMs, int readTimeoutMs) throws Exception {String url;if (this.useSandbox) {url = WXPayConstants.SANDBOX_UNIFIEDORDER_URL_SUFFIX;}else {url = WXPayConstants.UNIFIEDORDER_URL_SUFFIX;}String respXml = this.requestWithoutCert(url, reqData, connectTimeoutMs, readTimeoutMs);return WXPayUtil.xmlToMap(respXml);}
  • 微信支付返回前端的方法如下
public String unifiedOrder(String body, String outTradeNo,  String totalFee, String ip) {log.info("微信支付 - 统一下单");try {WeChatPayConfig config = new WeChatPayConfig();WXPay wxpay = new WXPay(config);Map<String, String> data = new HashMap<String, String>();data.put("appid", config.getAppID());data.put("mch_id", config.getMchID());data.put("body", body);data.put("nonce_str", WXPayUtil.generateNonceStr());data.put("out_trade_no", outTradeNo);data.put("total_fee", String.valueOf(new BigDecimal(totalFee).multiply(new BigDecimal(100)).intValue()));data.put("spbill_create_ip", ip);data.put("notify_url", WeChatPayConfig.NOTITY_URL);data.put("trade_type", "APP");
//          data.put("trade_type", "NATIVE"); data.put("sign", WXPayUtil.generateSignature(data, config.getKey()));//返回预支付结果集Map<String, String> resp = wxpay.unifiedOrder(data);log.info(JSONObject.toJSONString(resp));//返回给APP吊起支付Map<String, String> rm = new HashMap<String, String>();rm.put("appid", resp.get("appid"));rm.put("partnerid", resp.get("mch_id"));rm.put("prepayid", resp.get("prepay_id"));rm.put("noncestr", WXPayUtil.generateNonceStr());rm.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));rm.put("package", "Sign=WXPay");//签名rm.put("sign", WXPayUtil.generateSignature(rm, config.getKey()));log.info("resp:{}", JSONObject.toJSONString(rm));return JSONObject.toJSONString(rm);} catch (Exception e) {e.printStackTrace();}return null;}
本文介绍的内容到此就结束了,仅供学习与参考
如果有朋友需要源码(源码不包含前端代码)可以点击源码下载

微信企业转账JAVA版(包括:1,转账个人零钱;2,转账个人银行卡;3,微信官网的SDK3.0.9存在的支付问题)相关推荐

  1. 空白世界地图打印版_考研准考证打印什么时候_中国研究生招生信息网官网

    考研准考证打印什么时候_中国研究生招生信息网官网由广东研究生考试网考试快讯栏目由提供,更多关于考研准考证打印入口,广东研究生考试快讯的内容,请关注广东研究生考试频道/广东人事考试网! 2021考研人注 ...

  2. 微信小程序java版Spring Cloud+Spring Boot+mybatis+security+uniapp+Redis+MQ+VR全景

    @源码地址来源: https://minglisoft.cn/honghu2/business.html 微信小程序登录代码: /*** Copyright © 2012-2017 <a hre ...

  3. 微信企业应用java开发_移动办公-创建自定义企业微信应用

    关键词:移动办公,O2OA微信办公,企业微信办公,微信办公,手机办公 O2OA平台拥有配套的原生开发的安卓和IOS移动APP,可以以自建应用的方式集成到企业微信,同步企业微信的企业通讯录作为本地组织人 ...

  4. java配置微信网页授权_玩玩微信公众号Java版之六:微信网页授权

    配置好了,就可以进行开发了,首先来看一下具体的流程: 其实很多功能点,前面已经实现过,只用改一下调用地址和参数即可. 首先,调用的定义链接:https://open.weixin.qq.com/con ...

  5. 微信jssdk开发java版_微信jssdk

    class JSSDK { private $appId; private $appSecret; public function __construct($appId, $appSecret) { ...

  6. mt管理器java版下载_MT管理器2.7app下载-MT管理器2.7下载v2.7.0 稳定清爽版-西西软件下载...

    为大家带来MT管理器2.7.0最新版下载!这是一款非常强大的安卓文件管理器软件,新版本增加了可以让用户在线安装和更新插件的插件中心,增加了安装包提取功能,同时还优化文件名排序算法并修复云备份遗漏了文本 ...

  7. (八)java版spring cloud+spring boot+redis多租户社交电子商务平台 -SSO单点登录之OAuth2.0登录认证(2)...

    电子商务平台源码请加企鹅求求:一零三八七七四六二六.上一篇是站在巨人的肩膀上去研究OAuth2.0,也是为了快速帮助大家认识OAuth2.0,闲话少说,我根据框架中OAuth2.0的使用总结,画了一个 ...

  8. 【Java程序员来写一个简单的HTML前端——映纷创意官网】

    官网页面:INFINI | 映纷创意 (infinistudio.cn) 布局分析: 映纷创意.css *{margin: 0;padding: 0; } body{background-color: ...

  9. 第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装...

    微信服务器和第三方服务器之间究竟是通过什么方式进行对话的? 下面,我们先看下图: 其实我们可以简单的理解: (1)首先,用户向微信服务器发送消息: (2)微信服务器接收到用户的消息处理之后,通过开发者 ...

最新文章

  1. java多线程查询_利用Java函数式接口处理多线程查询
  2. lufylegend基础知识1
  3. 在命令行下对ntfs分区文件夹权限的设置
  4. python学习之文件读写
  5. 你所不知道的mybatis居然也有拦截器
  6. 个人经验分享 | 在面试官眼中:PDF版简历和个人主页的区别
  7. 寻路优化(一)——二维地图上A*启发函数的设计探索
  8. 1081 检查密码 (15 分)—PAT (Basic Level) Practice (中文)
  9. Hexo报错Usage: hexo command处理及图片显示问题
  10. 2013年中国区Skyline软件价格体系
  11. 测试环境由谁搭建?第三方软件测试环境搭建步骤流程
  12. matlab梯形法数值积分,数值积分(梯形法)
  13. 不堪忍受医美行业潜规则,百万年薪院长想辞职
  14. 魔高一丈道高一尺,开放接口安全性设计
  15. 英语口语练习四十二之12种“安静”的表达
  16. AVD Android虚拟设备root教程
  17. vue-cli · Failed to download repo vuejs-templates/webpack-simple: tunneling socket could not be esta
  18. Java SE 基础部分经典100道笔试题
  19. 【通信协议】一文搞懂SPI
  20. android 错误记录Attempt to invoke virtual method ‘void android.view.View.setVisibility(int)‘

热门文章

  1. MaxCompute分区和列操作
  2. 新手小白入门必看!如何批量注册Twitter账号?
  3. 嵌入式硬盘录像机和PC电脑+视频卡录像比较
  4. (二)QCustomPlot生成热力图/矩阵颜色图
  5. PYTHON 黑帽资料
  6. 通过游戏编程学Python(番外篇)— 单词小测验
  7. 面对突发事故,APP如何做好崩溃分析与性能监控?
  8. HTTP的请求头标签 If-Modified-Since与Last-Modified
  9. 服务报错:Required request body is missing
  10. Oracle基本操作查询总结(其一)