当前位置:   article > 正文

微信支付:springboot企业付款到个人银行卡_springboot 对公账户给微信用户转账

springboot 对公账户给微信用户转账

第一次做这个功能,坑有点多(官方坑也有),最终完全了付款到银行卡功能,一一为大家踩坑。文章只讲解关于转账至银行卡的业务逻辑,需要用到的工具类,还有具体做法都会贴,适合第一次开发此功能的人看。

官方文档:

【微信支付】付款开发者文档

一、准备

1.证书

按照文档操作,得到apiclient_cert.p12文件,将他放到项目内的resources/static内,如果要部署jar到云服务器的,可以把证书放在项目同文件。后面访问时需要证书路径。

2.rsa公钥文件

看文档内获取rsa公钥的文档,准备好请求参数,直接用postman请求,参数中随机值(自己生产也好,随便敲也好,跟示例参数一样多位的随机值就行),sign参数(用签名工具类把几个参数签下名)

签名方式和支付功能一样,可以直接用之前开发支付用的工具类,啥?之前没做支付,没有工具类?

那我贴一下吧:

  1. import org.slf4j.Logger;
  2. import org.slf4j.LoggerFactory;
  3. import org.w3c.dom.Node;
  4. import org.w3c.dom.NodeList;
  5. import org.w3c.dom.Document;
  6. import org.w3c.dom.Element;
  7. import javax.xml.parsers.DocumentBuilder;
  8. import javax.xml.transform.OutputKeys;
  9. import javax.xml.transform.Transformer;
  10. import javax.xml.transform.TransformerFactory;
  11. import javax.xml.transform.dom.DOMSource;
  12. import javax.xml.transform.stream.StreamResult;
  13. import java.io.ByteArrayInputStream;
  14. import java.io.InputStream;
  15. import java.io.StringWriter;
  16. import java.util.*;
  17. /**
  18. * 微信支付要用到的:xml解析工具类
  19. */
  20. public class WXPayUtils {
  21. /**
  22. * XML格式字符串转换为Map
  23. *
  24. * @param strXML XML字符串
  25. * @return XML数据转换后的Map
  26. * @throws Exception
  27. */
  28. public static Map<String, String> xmlToMap(String strXML) throws Exception {
  29. try {
  30. Map<String, String> data = new HashMap<String, String>();
  31. DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
  32. InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
  33. org.w3c.dom.Document doc = documentBuilder.parse(stream);
  34. doc.getDocumentElement().normalize();
  35. NodeList nodeList = doc.getDocumentElement().getChildNodes();
  36. for (int idx = 0; idx < nodeList.getLength(); ++idx) {
  37. Node node = nodeList.item(idx);
  38. if (node.getNodeType() == Node.ELEMENT_NODE) {
  39. org.w3c.dom.Element element = (org.w3c.dom.Element) node;
  40. data.put(element.getNodeName(), element.getTextContent());
  41. }
  42. }
  43. try {
  44. stream.close();
  45. } catch (Exception ex) {
  46. // do nothing
  47. }
  48. return data;
  49. } catch (Exception ex) {
  50. WXPayUtils.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
  51. throw ex;
  52. }
  53. }
  54. /**
  55. * 将Map转换为XML格式的字符串
  56. *
  57. * @param data Map类型数据
  58. * @return XML格式的字符串
  59. * @throws Exception
  60. */
  61. public static String mapToXml(Map<String, String> data) throws Exception {
  62. Document document = WXPayXmlUtil.newDocument();
  63. Element root = document.createElement("xml");
  64. document.appendChild(root);
  65. for (String key: data.keySet()) {
  66. String value = data.get(key);
  67. if (value == null) {
  68. value = "";
  69. }
  70. value = value.trim();
  71. Element filed = document.createElement(key);
  72. filed.appendChild(document.createTextNode(value));
  73. root.appendChild(filed);
  74. }
  75. TransformerFactory tf = TransformerFactory.newInstance();
  76. Transformer transformer = tf.newTransformer();
  77. DOMSource source = new DOMSource(document);
  78. transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
  79. transformer.setOutputProperty(OutputKeys.INDENT, "yes");
  80. StringWriter writer = new StringWriter();
  81. StreamResult result = new StreamResult(writer);
  82. transformer.transform(source, result);
  83. String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
  84. try {
  85. writer.close();
  86. }
  87. catch (Exception ex) {
  88. }
  89. return output;
  90. }
  91. /**
  92. * 生成微信支付sign
  93. */
  94. public static String createSign(SortedMap<String, String> params, String key){
  95. StringBuilder sb = new StringBuilder();
  96. Set<Map.Entry<String, String>> es = params.entrySet();
  97. Iterator<Map.Entry<String, String>> it = es.iterator();
  98. while(it.hasNext()){
  99. Map.Entry<String, String> entry = it.next();
  100. String k = entry.getKey();
  101. String v = entry.getValue();
  102. if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){
  103. sb.append(k + "=" + v + "&");
  104. }
  105. }
  106. sb.append("key=").append(key);
  107. String sign = CommonUtils.MD5(sb.toString()).toUpperCase();
  108. return sign;
  109. }
  110. /**
  111. * 校验签名
  112. * @param params
  113. * @param key
  114. * @return
  115. */
  116. public static Boolean isCorrectSign(SortedMap<String, String> params, String key){
  117. String sign = createSign(params, key);
  118. String wxPaySign = params.get("sign").toUpperCase();
  119. return wxPaySign.equals(sign);
  120. }
  121. /**
  122. * 获取有序map
  123. * @param map
  124. */
  125. public static SortedMap<String, String> getSortedMap(Map<String, String> map){
  126. SortedMap<String, String> sortedMap = new TreeMap<>();
  127. Iterator<String> it = map.keySet().iterator();
  128. while(it.hasNext()){
  129. String key = it.next();
  130. String value = map.get(key);
  131. String temp = "";
  132. if(null != value){
  133. temp = value.trim();
  134. }
  135. sortedMap.put(key, value);
  136. }
  137. return sortedMap;
  138. }
  139. /**
  140. * 日志
  141. * @return
  142. */
  143. public static Logger getLogger() {
  144. Logger logger = LoggerFactory.getLogger("wxpay java sdk");
  145. return logger;
  146. }
  147. /**
  148. * 获取当前时间戳,单位秒
  149. * @return
  150. */
  151. public static long getCurrentTimestamp() {
  152. return System.currentTimeMillis()/1000;
  153. }
  154. /**
  155. * 获取当前时间戳,单位毫秒
  156. * @return
  157. */
  158. public static long getCurrentTimestampMs() {
  159. return System.currentTimeMillis();
  160. }
  161. /**
  162. * 生成UUID(用来表示一笔订单)
  163. * @return
  164. */
  165. public static String generateUUID(){
  166. String uuid = UUID.randomUUID().toString()
  167. .replaceAll("-","")
  168. .substring(0,32);
  169. return uuid;
  170. }
  171. }

——签好名就请求获取rsa公钥接口,拿到了RSA公钥字符串。

——不要高兴得太早,请把这个字符串复制进txt,名字随意英文,然后改后缀名.pem,因为你是Java你需要转pkcs#8

文档内有互转的方法:

openssl rsa -RSAPublicKey_in -in <filename> -pubout

——如果没有openssl,你需要安装并配置openssl,这个可以百度。安装好后配置环境变量,然后在bin路径输入cmd,再跑上面的代码,成功之后,转换结果会直接显示在命令行,请复制这些密钥字符串。

——接下来,删除首尾:-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----

——然后将剩下的字符串放进txt,该后缀名pem,再放进项目的resources/static内。

——截止目前,我们便得到了rsa公钥文件:rsa.pem

3.银行卡开户行代号(付款至银行卡需要微信自己的参数)

这个我觉得不用说那么详细了,我自己用的方式都很业余。我是从百度找到了一个阿里的开发性api,用于获取银行卡开户行,然后再把微信的代号填到对照表里。再整理成工具类,把银行卡号做参数就可以得出代号(有几个小银行没有在对照表里),从优化的角度来看,这种方式需要跨域请求,最好是用一种可以直接识别银行卡号开户行的工具类,然后把微信开户行代号表和工具类的银行名称对齐一下。这里就默认你已经获取了银行卡开户行代号。

二、入参准备

——商户号:自行获取

——商户订单号:在请求这个接口前,你需要先新增一个你目前系统的订单,就是在订单表里插入一条订单,这个订单号就是商户订单号。

——随机值:直接调用我上方的工具类,有个generateUUID()函数直接获取。

——银行卡号:这个就需要用rsa公钥加密了。你已经有了公钥文件,但需要转成PublicKey类才能使用我的加密工具类。下面分步叙述流程(结果将得到PublicKey文件):

1.获取公钥文件的字节输入流

InputStream rsaStream = getClass().getClassLoader().getResourceAsStream(RSA公钥文件的路径字符串);

2.用下面这个工具类转成字符串

  1. import java.io.ByteArrayOutputStream;
  2. import java.io.InputStream;
  3. /*IO流工具类*/
  4. public class StreamUtil {
  5. /**
  6. * 读取 InputStream 到 String字符串中
  7. */
  8. public static String readStream(InputStream in) {
  9. try {
  10. //<1>创建字节数组输出流,用来输出读取到的内容
  11. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  12. //<2>创建缓存大小
  13. byte[] buffer = new byte[1024]; // 1KB
  14. //每次读取到内容的长度
  15. int len = -1;
  16. //<3>开始读取输入流中的内容
  17. while ((len = in.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了
  18. baos.write(buffer, 0, len); //把读取到的内容写到输出流中
  19. }
  20. //<4> 把字节数组转换为字符串
  21. String content = baos.toString();
  22. //<5>关闭输入流和输出流
  23. in.close();
  24. baos.close();
  25. //<6>返回字符串结果
  26. return content;
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. return e.getMessage();
  30. }
  31. }
  32. }

3.再用下面这个工具类getPublicKey(String key)函数将公钥字符串转为PublicKey类(该工具类包含RSA加密方法)

  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.lang.reflect.Method;
  8. import java.security.KeyFactory;
  9. import java.security.PrivateKey;
  10. import java.security.PublicKey;
  11. import java.security.spec.PKCS8EncodedKeySpec;
  12. import java.security.spec.X509EncodedKeySpec;
  13. import javax.crypto.Cipher;
  14. import sun.misc.BASE64Decoder;
  15. public class RSAwxUtil {
  16. public static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey, int keyLength, int reserveSize, String cipherAlgorithm) throws Exception {
  17. int keyByteSize = keyLength / 8;
  18. int decryptBlockSize = keyByteSize - reserveSize;
  19. int nBlock = encryptedBytes.length / keyByteSize;
  20. ByteArrayOutputStream outbuf = null;
  21. try {
  22. Cipher cipher = Cipher.getInstance(cipherAlgorithm);
  23. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  24. outbuf = new ByteArrayOutputStream(nBlock * decryptBlockSize);
  25. for (int offset = 0; offset < encryptedBytes.length; offset += keyByteSize) {
  26. int inputLen = encryptedBytes.length - offset;
  27. if (inputLen > keyByteSize) {
  28. inputLen = keyByteSize;
  29. }
  30. byte[] decryptedBlock = cipher.doFinal(encryptedBytes, offset, inputLen);
  31. outbuf.write(decryptedBlock);
  32. }
  33. outbuf.flush();
  34. return outbuf.toByteArray();
  35. } catch (Exception e) {
  36. throw new Exception("DEENCRYPT ERROR:", e);
  37. } finally {
  38. try{
  39. if(outbuf != null){
  40. outbuf.close();
  41. }
  42. }catch (Exception e){
  43. outbuf = null;
  44. throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
  45. }
  46. }
  47. }
  48. public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws Exception {
  49. int keyByteSize = keyLength / 8;
  50. int encryptBlockSize = keyByteSize - reserveSize;
  51. int nBlock = plainBytes.length / encryptBlockSize;
  52. if ((plainBytes.length % encryptBlockSize) != 0) {
  53. nBlock += 1;
  54. }
  55. ByteArrayOutputStream outbuf = null;
  56. try {
  57. Cipher cipher = Cipher.getInstance(cipherAlgorithm);
  58. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  59. outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);
  60. for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
  61. int inputLen = plainBytes.length - offset;
  62. if (inputLen > encryptBlockSize) {
  63. inputLen = encryptBlockSize;
  64. }
  65. byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen);
  66. outbuf.write(encryptedBlock);
  67. }
  68. outbuf.flush();
  69. return outbuf.toByteArray();
  70. } catch (Exception e) {
  71. throw new Exception("ENCRYPT ERROR:", e);
  72. } finally {
  73. try{
  74. if(outbuf != null){
  75. outbuf.close();
  76. }
  77. }catch (Exception e){
  78. outbuf = null;
  79. throw new Exception("CLOSE ByteArrayOutputStream ERROR:", e);
  80. }
  81. }
  82. }
  83. public static PrivateKey getPriKey(String privateKeyPath,String keyAlgorithm){
  84. PrivateKey privateKey = null;
  85. InputStream inputStream = null;
  86. try {
  87. if(inputStream==null){
  88. System.out.println("hahhah1!");
  89. }
  90. inputStream = new FileInputStream(privateKeyPath);
  91. System.out.println("hahhah2!");
  92. privateKey = getPrivateKey(inputStream,keyAlgorithm);
  93. System.out.println("hahhah3!");
  94. } catch (Exception e) {
  95. System.out.println("加载私钥出错!");
  96. } finally {
  97. if (inputStream != null){
  98. try {
  99. inputStream.close();
  100. }catch (Exception e){
  101. System.out.println("加载私钥,关闭流时出错!");
  102. }
  103. }
  104. }
  105. return privateKey;
  106. }
  107. public static PublicKey getPubKey(String publicKeyPath,String keyAlgorithm){
  108. PublicKey publicKey = null;
  109. InputStream inputStream = null;
  110. try
  111. {
  112. System.out.println("getPubkey 1......");
  113. inputStream = new FileInputStream(publicKeyPath);
  114. System.out.println("getPubkey 2......");
  115. publicKey = getPublicKey(inputStream,keyAlgorithm);
  116. System.out.println("getPubkey 3......");
  117. } catch (Exception e) {
  118. e.printStackTrace();//EAD PUBLIC KEY ERROR
  119. System.out.println("加载公钥出错!");
  120. } finally {
  121. if (inputStream != null){
  122. try {
  123. inputStream.close();
  124. }catch (Exception e){
  125. System.out.println("加载公钥,关闭流时出错!");
  126. }
  127. }
  128. }
  129. return publicKey;
  130. }
  131. public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws Exception {
  132. try
  133. {
  134. System.out.println("b1.........");
  135. BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
  136. System.out.println("b2.........");
  137. StringBuilder sb = new StringBuilder();
  138. String readLine = null;
  139. System.out.println("b3.........");
  140. while ((readLine = br.readLine()) != null) {
  141. if (readLine.charAt(0) == '-') {
  142. continue;
  143. } else {
  144. sb.append(readLine);
  145. sb.append('\r');
  146. }
  147. }
  148. System.out.println("b4.........");
  149. X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(decodeBase64(sb.toString()));
  150. System.out.println("b5.........");
  151. KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
  152. System.out.println("b6.........");
  153. //下行出错 java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=127, too big.
  154. PublicKey publicKey = keyFactory.generatePublic(pubX509);
  155. System.out.println("b7.........");
  156. return publicKey;
  157. } catch (Exception e) {
  158. e.printStackTrace();
  159. System.out.println("b8.........");
  160. throw new Exception("1这里报异常了:"+e.getMessage(), e);
  161. } finally {
  162. try {
  163. if (inputStream != null) {
  164. inputStream.close();
  165. }
  166. } catch (IOException e) {
  167. inputStream = null;
  168. throw new Exception("INPUT STREAM CLOSE ERROR:", e);
  169. }
  170. }
  171. }
  172. public static PrivateKey getPrivateKey(InputStream inputStream, String keyAlgorithm) throws Exception {
  173. try {
  174. BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
  175. StringBuilder sb = new StringBuilder();
  176. String readLine = null;
  177. while ((readLine = br.readLine()) != null) {
  178. if (readLine.charAt(0) == '-') {
  179. continue;
  180. } else {
  181. sb.append(readLine);
  182. sb.append('\r');
  183. }
  184. }
  185. System.out.println("hahhah4!"+decodeBase64(sb.toString()));
  186. PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decodeBase64(sb.toString()));
  187. System.out.println("hahhah5!");
  188. KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
  189. System.out.println("hahhah6!");
  190. PrivateKey privateKey = keyFactory.generatePrivate(priPKCS8);
  191. System.out.println("hahhah7!");
  192. return privateKey;
  193. } catch (Exception e) {
  194. throw new Exception("READ PRIVATE KEY ERROR:" ,e);
  195. } finally {
  196. try {
  197. if (inputStream != null) {
  198. inputStream.close();
  199. }
  200. } catch (IOException e) {
  201. inputStream = null;
  202. throw new Exception("INPUT STREAM CLOSE ERROR:", e);
  203. }
  204. }
  205. }
  206. //一下面是base64的编码和解码
  207. public static String encodeBase64(byte[]input) throws Exception{
  208. Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
  209. Method mainMethod= clazz.getMethod("encode", byte[].class);
  210. mainMethod.setAccessible(true);
  211. Object retObj=mainMethod.invoke(null, new Object[]{input});
  212. return (String)retObj;
  213. }
  214. /***
  215. * decode by Base64
  216. */
  217. public static byte[] decodeBase64(String input) throws Exception{
  218. Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
  219. Method mainMethod= clazz.getMethod("decode", String.class);
  220. mainMethod.setAccessible(true);
  221. Object retObj=mainMethod.invoke(null, input);
  222. return (byte[])retObj;
  223. }
  224. public static PublicKey getPublicKey(String key) throws Exception {
  225. byte[] keyBytes;
  226. keyBytes = (new BASE64Decoder()).decodeBuffer(key);
  227. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
  228. KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  229. PublicKey publicKey = keyFactory.generatePublic(keySpec);
  230. return publicKey;
  231. }
  232. public static PrivateKey getPrivateKey(String key) throws Exception {
  233. byte[] keyBytes;
  234. keyBytes = (new BASE64Decoder()).decodeBuffer(key);
  235. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
  236. KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  237. PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
  238. return privateKey;
  239. }
  240. }

该步骤总代码:

  1. PublicKey pub;
  2. try {
  3. pub = RSAwxUtil.getPublicKey(readStream(wxPayAppConfig.getRsaPublicKeyStream()));
  4. } catch (Exception e) {
  5. e.printStackTrace();
  6. baseEntity.setStatus(2);
  7. baseEntity.setMessage("读取RSA公钥时异常:"+e.getMessage());
  8. return baseEntity;
  9. }

加密流程(以卡号为例):

1.直接复制(文档要求)

String rsa ="RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING";

2.调用encrypt()函数,直接贴我的逻辑代码:

  1. byte[] enc_bank_no_byte;
  2. try {
  3. enc_bank_no_byte = RSAwxUtil.encrypt(bank_card.getBytes(),pub,2048,11,rsa);
  4. } catch (Exception e) {
  5. e.printStackTrace();
  6. baseEntity.setStatus(2);
  7. baseEntity.setMessage("对银行卡号进行加密时异常,异常原因:"+e.getMessage());
  8. return baseEntity;
  9. }
  10. String enc_bank_no;//最终银行卡号参数
  11. try {
  12. enc_bank_no = BASE64.encode(enc_bank_no_byte);
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. baseEntity.setStatus(2);
  16. baseEntity.setMessage("将银行卡号byte转为字符串时异常,异常原因:"+e.getMessage());
  17. return baseEntity;
  18. }

——真实姓名:加密过程一致,不赘述。

——银行卡代号:不赘述

——金额:这里要注意,没做过支付的朋友,这个单位是分不是元,用订单金额*100即可。

——签名:上面已经贴了签名工具类,下面会贴我的请求代码。

最终请求代码:

  1. SortedMap<String, String> params = new TreeMap<>();
  2. try{
  3. params.put("mch_id",wxPayAppConfig.getMchID());
  4. params.put("partner_trade_no",orderNo); // 商户订单号
  5. params.put("nonce_str",WXPayUtils.generateUUID());//随机值
  6. params.put("enc_bank_no",enc_bank_no);//加密后的银行卡号
  7. params.put("enc_true_name",enc_true_name);//加密后的真实姓名
  8. params.put("bank_code",bank_code);//银行卡代号
  9. params.put("amount",amount);// 标价金额(单位为分)
  10. params.put("desc", "提现");
  11. //sign签名
  12. String sign = WXPayUtils.createSign(params, wxPayAppConfig.getKey());
  13. params.put("sign",sign);
  14. } catch (Exception e){
  15.     e.printStackTrace();
  16. baseEntity.setStatus(2);
  17. baseEntity.setMessage("请求前,封装入参时异常,异常原因:"+e.getMessage());
  18. return baseEntity;
  19. }

因为入参格式要求xml,请用工具类(上面找)转换:

  1. String payXml;
  2. try{
  3. payXml = WXPayUtils.mapToXml(params);
  4. } catch (Exception e){
  5. e.printStackTrace();
  6. baseEntity.setStatus(2);
  7. baseEntity.setMessage("请求接口前,map转xml异常,异常原因:"+e.getMessage());
  8. return baseEntity;
  9. }

到此,入参封装好了。

三、请求转账接口

 直接调用请求类请求,下面会发,在请求之前,说一说证书路径。如果要将jar部署到云服务器上,可以将你的证书放在云服务器里和项目同一个目录,然后用以下方法直接获取路径:

String path = System.getProperty("user.dir")+"/apiclient_cert.p12";

下面是请求类:

  1. import java.io.File;
  2. import java.io.FileInputStream;
  3. import java.security.KeyStore;
  4. import javax.net.ssl.SSLContext;
  5. import org.apache.http.HttpEntity;
  6. import org.apache.http.client.methods.CloseableHttpResponse;
  7. import org.apache.http.client.methods.HttpPost;
  8. import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
  9. import org.apache.http.conn.ssl.SSLContexts;
  10. import org.apache.http.entity.StringEntity;
  11. import org.apache.http.impl.client.CloseableHttpClient;
  12. import org.apache.http.impl.client.HttpClients;
  13. import org.apache.http.util.EntityUtils;
  14. /**
  15. * This example demonstrates how to create secure connections with a custom SSL
  16. * context.
  17. */
  18. public class ClientCustomSSL {
  19. @SuppressWarnings("deprecation")
  20. public static String doRefund(String url, String data, String p12_path, String p12_key) throws Exception {
  21. KeyStore keyStore = KeyStore.getInstance("PKCS12");
  22. FileInputStream instream = new FileInputStream(new File(p12_path));//P12文件目录
  23. try {
  24. /**
  25. * 下载证书时的密码、默认密码是你的MCHID mch_id
  26. * */
  27. keyStore.load(instream, p12_key.toCharArray());
  28. } finally {
  29. instream.close();
  30. }
  31. // Trust own CA and all self-signed certs
  32. /**
  33. * 下载证书时的密码、默认密码是你的MCHID mch_id
  34. * */
  35. SSLContext sslcontext = SSLContexts.custom()
  36. .loadKeyMaterial(keyStore, p12_key.toCharArray())//这里也是写密码的
  37. .build();
  38. // Allow TLSv1 protocol only
  39. SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
  40. sslcontext,
  41. SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
  42. CloseableHttpClient httpclient = HttpClients.custom()
  43. .setSSLSocketFactory(sslsf)
  44. .build();
  45. try {
  46. HttpPost httpost = new HttpPost(url);
  47. httpost.addHeader("Connection", "keep-alive");
  48. httpost.addHeader("Accept", "*/*");
  49. httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
  50. httpost.addHeader("Host", "api.mch.weixin.qq.com");
  51. httpost.addHeader("X-Requested-With", "XMLHttpRequest");
  52. httpost.addHeader("Cache-Control", "max-age=0");
  53. httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
  54. httpost.setEntity(new StringEntity(data, "UTF-8"));
  55. CloseableHttpResponse response = httpclient.execute(httpost);
  56. try {
  57. HttpEntity entity = response.getEntity();
  58. String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
  59. EntityUtils.consume(entity);
  60. return jsonStr;
  61. } finally {
  62. response.close();
  63. }
  64. } finally {
  65. httpclient.close();
  66. }
  67. }
  68. }

 我的请求代码:(请求类四个参数分别是:付款url,入参xml,证书路径,商户号)

  1. String result;
  2. try{
  3. result = ClientCustomSSL.doRefund(url, payXml,path,wxPayAppConfig.getMchID());
  4. } catch (Exception e){
  5. e.printStackTrace();
  6. baseEntity.setStatus(2);
  7. baseEntity.setMessage("请求转账接口时异常,异常原因:"+e.getMessage());
  8. return baseEntity;
  9. }

 最后需要将xml转为map:

  1. Map<String, String> payBankMap;
  2. try{
  3. payBankMap = WXPayUtils.xmlToMap(s);
  4. } catch (Exception e){
  5. e.printStackTrace();
  6. baseEntity.setStatus(2);
  7. baseEntity.setMessage("请求结果xml数据转map时异常,异常原因:"+e.getMessage());
  8. return baseEntity;
  9. }

至此,付款请求完成。

根据payBankMap.get("result_code")参数可以知道付款成功或失败。

最后不要忘记更新相关数据库表的业务逻辑,如:付款后,订单改为已付款之类的。

还有,请保证微信支付运营账户余额大于1.5元,因为每一笔打款订单,微信起步手续费1元。也就是说,就算你打款0.01元,将被扣1.01元。

因为文档没有体现手续费起步1元,我浪费了大半天时间百度,就连技术客服都不知道这个规则,还帮我看后台日志,最后问他同事才知道。这种坑实在是不应该,还有请求时突然不支持的协议(该文档已经屏蔽),从开发社区里看到,两年前的文档漏洞,今天还没改...

另外需要注意的是,每个商户最低打款额不同,有些商户是0.01元,也有看到有很多商户是0.03元。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/201107
推荐阅读
相关标签
  

闽ICP备14008679号