微信支付:springboot企业付款到个人银行卡

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

官方文档:

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

一、准备

1.证书

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

2.rsa公钥文件

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

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

那我贴一下吧:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.*;

/**
 * 微信支付要用到的:xml解析工具类
 */
public class WXPayUtils {
    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            WXPayUtils.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        Document document = WXPayXmlUtil.newDocument();
        Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }

    /**
     * 生成微信支付sign
     */
    public static String createSign(SortedMap<String, String> params, String key){
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> es = params.entrySet();
        Iterator<Map.Entry<String, String>> it = es.iterator();
        while(it.hasNext()){
            Map.Entry<String, String> entry = it.next();
            String k = entry.getKey();
            String v = entry.getValue();
            if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=").append(key);
        String sign = CommonUtils.MD5(sb.toString()).toUpperCase();

        return sign;
    }

    /**
     * 校验签名
     * @param params
     * @param key
     * @return
     */
    public static Boolean isCorrectSign(SortedMap<String, String> params, String key){
        String sign = createSign(params, key);
        String wxPaySign = params.get("sign").toUpperCase();

        return  wxPaySign.equals(sign);
    }
    /**
     * 获取有序map
     * @param map
     */
    public static SortedMap<String, String> getSortedMap(Map<String, String> map){
        SortedMap<String, String> sortedMap = new TreeMap<>();
        Iterator<String> it = map.keySet().iterator();
        while(it.hasNext()){
            String key = it.next();
            String value = map.get(key);
            String temp = "";
            if(null != value){
                temp = value.trim();
            }
            sortedMap.put(key, value);
        }
        return sortedMap;
    }

    /**
     * 日志
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }

    /**
     * 获取当前时间戳,单位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

    /**
     * 获取当前时间戳,单位毫秒
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }
    /**
     * 生成UUID(用来表示一笔订单)
     * @return
     */
    public static String generateUUID(){
        String uuid = UUID.randomUUID().toString()
                .replaceAll("-","")
                .substring(0,32);
        return uuid;
    }
}

——签好名就请求获取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.用下面这个工具类转成字符串

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/*IO流工具类*/
public class StreamUtil {
    /**
     * 读取 InputStream 到 String字符串中
     */
    public static String readStream(InputStream in) {
        try {
            //<1>创建字节数组输出流,用来输出读取到的内容
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //<2>创建缓存大小
            byte[] buffer = new byte[1024]; // 1KB
            //每次读取到内容的长度
            int len = -1;
            //<3>开始读取输入流中的内容
            while ((len = in.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了
                baos.write(buffer, 0, len);   //把读取到的内容写到输出流中
            }
            //<4> 把字节数组转换为字符串
            String content = baos.toString();
            //<5>关闭输入流和输出流
            in.close();
            baos.close();
            //<6>返回字符串结果
            return content;
        } catch (Exception e) {
            e.printStackTrace();
            return  e.getMessage();
        }
    }
}

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

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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;

import javax.crypto.Cipher;
import sun.misc.BASE64Decoder;

public class RSAwxUtil {

    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);
            System.out.println("hahhah2!");
            privateKey = getPrivateKey(inputStream,keyAlgorithm);
            System.out.println("hahhah3!");
        } catch (Exception e) {
            System.out.println("加载私钥出错!");
        } finally {
            if (inputStream != null){
                try {
                    inputStream.close();
                }catch (Exception e){
                    System.out.println("加载私钥,关闭流时出错!");
                }
            }
        }
        return privateKey;
    }
    public static PublicKey getPubKey(String publicKeyPath,String keyAlgorithm){
        PublicKey publicKey = null;
        InputStream inputStream = null;
        try
        {
            System.out.println("getPubkey 1......");

            inputStream = new FileInputStream(publicKeyPath);
            System.out.println("getPubkey 2......");

            publicKey = getPublicKey(inputStream,keyAlgorithm);
            System.out.println("getPubkey 3......");

        } catch (Exception e) {

            e.printStackTrace();//EAD PUBLIC KEY ERROR
            System.out.println("加载公钥出错!");
        } finally {
            if (inputStream != null){
                try {
                    inputStream.close();
                }catch (Exception e){
                    System.out.println("加载公钥,关闭流时出错!");
                }
            }
        }
        return publicKey;
    }
    public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws Exception {
        try
        {
            System.out.println("b1.........");
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            System.out.println("b2.........");
            StringBuilder sb = new StringBuilder();
            String readLine = null;
            System.out.println("b3.........");
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            System.out.println("b4.........");
            X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(decodeBase64(sb.toString()));
            System.out.println("b5.........");
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            System.out.println("b6.........");
            //下行出错  java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=127, too big.
            PublicKey publicKey = keyFactory.generatePublic(pubX509);
            System.out.println("b7.........");
            return publicKey;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("b8.........");
            throw new Exception("1这里报异常了:"+e.getMessage(), 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');
                }
            }
            System.out.println("hahhah4!"+decodeBase64(sb.toString()));
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decodeBase64(sb.toString()));
            System.out.println("hahhah5!");
            KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
            System.out.println("hahhah6!");
            PrivateKey privateKey = keyFactory.generatePrivate(priPKCS8);
            System.out.println("hahhah7!");
            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{
        Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
        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{
        Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
        Method mainMethod= clazz.getMethod("decode", String.class);
        mainMethod.setAccessible(true);
        Object retObj=mainMethod.invoke(null, input);
        return (byte[])retObj;
    }

    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

}

该步骤总代码:

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

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

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

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

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

byte[] enc_bank_no_byte;
try {
    enc_bank_no_byte = RSAwxUtil.encrypt(bank_card.getBytes(),pub,2048,11,rsa);
} catch (Exception e) {
    e.printStackTrace();
    baseEntity.setStatus(2);
    baseEntity.setMessage("对银行卡号进行加密时异常,异常原因:"+e.getMessage());
    return baseEntity;
}

String enc_bank_no;//最终银行卡号参数
try {
    enc_bank_no = BASE64.encode(enc_bank_no_byte);
} catch (Exception e) {
    e.printStackTrace();
    baseEntity.setStatus(2);
    baseEntity.setMessage("将银行卡号byte转为字符串时异常,异常原因:"+e.getMessage());
    return baseEntity;
}

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

——银行卡代号:不赘述

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

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

最终请求代码:

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

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

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

到此,入参封装好了。

三、请求转账接口

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

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

下面是请求类:

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * This example demonstrates how to create secure connections with a custom SSL
 * context.
 */
public class ClientCustomSSL {

    @SuppressWarnings("deprecation")
    public static String doRefund(String url, String data, String p12_path, String p12_key) throws Exception {
        

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        

        FileInputStream instream = new FileInputStream(new File(p12_path));//P12文件目录

        try {
            /**
             * 下载证书时的密码、默认密码是你的MCHID mch_id
             * */
            keyStore.load(instream, p12_key.toCharArray());
        } finally {
            instream.close();
        }

        // Trust own CA and all self-signed certs
        /**
         * 下载证书时的密码、默认密码是你的MCHID mch_id
         * */
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, p12_key.toCharArray())//这里也是写密码的
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
                CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {
            HttpPost httpost = new HttpPost(url); 
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();

                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
}

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

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

 最后需要将xml转为map:

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

至此,付款请求完成。

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

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

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

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

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

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值