微信转账之商家付款

    近期公司app要接入余额提现的功能,接入支付宝和微信的提现功能。忍不住想吐槽微信,文档做的很简单,接口调用也比较复杂。下面就把我的接入过程记录一点。

1.微信公众号,商户平台都是公司原有的,其他项目组在用。这里申请过程就不说了。

2.前期准备:

     1):去微信商户平台--产品中心--api安全下载证书。(可能需要管理员授权安装操作证书,需要管理员手机验证码)

     2):如果是正在用的商户平台需要向公司前辈要密钥,如果是没在使用的商户平台可以自己生成一个32位密钥,然后在微信商户平台--产品中心--api安全这个下面设置密钥,记住,设置完成一定要保存好密钥;

     3):商户平台下微信商户平台--产品中心--api安全这里的商户转账的ip限制和转账次数设置一下。

3.参考微信api文档进行开发,文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2

4.把下载下来的证书放到项目目录中,请求时候会用到。

5.相关代码:

   1).WeXinPay.java

package com.qlwb.business.payment;


import java.util.HashMap;
import java.util.Map;


import org.apache.commons.lang3.StringUtils;


import com.aspire.boc.util.ResourceManager;
import com.github.wxpay.sdk.WXPayUtil;
import com.qlwb.business.payment.util.WxpayUtil;
import com.qlwb.business.test.util.WeChatConfig;
import com.qlwb.business.util.StringUtil;


/**
 * 微信相关的业务
 * @author 朱玉猛
 * @since
 *
 */
public class WeXinPay {

//关联配置文件
    private static ResourceManager rm = ResourceManager.getInstance();
    //商家付款的url
    private static String post_transfer_url=rm.getValue("post_transfer_url");
    //默认字符集
    public static String charSet="UTF-8";
    //p12证书的路径
    private static String ca_path=StringUtil.getRootFileStr("/weixinCert/apiclient_cert.p12");
    //API_KEY
    private static String api_key=rm.getValue("api_key");
    //商户号
    private static String mch_id=rm.getValue("mch_id");
    //商户app_id
    private static String mch_app_id=rm.getValue("mch_app_id");
    //check_name
    private static String check_name=rm.getValue("check_name");
    //desc
    private static String desc=rm.getValue("desc");
    
    private static WeXinPay instance=new WeXinPay();
    
    private WeXinPay(){
   
    }
    public static WeXinPay  getInstance(){
    return instance;
    }
    /**
     * 银行向个人微信钱包转账,用于提现
     * @param re_openid openid
     * @param trade_no  订单编号,转账id
     * @param amount    转账金额,元为单位两位小数
     * @return success 转账成功与否的标志  returnCode 请求发送成功标志  resultCode转账成功标志  err_code_des 结果描述
     */
    public static Map<String,String> payToUser(String re_openid,String trade_no,double amount) {
    Map<String,String> resMap=new HashMap<String,String>();
Map<String, String> map = new HashMap<String, String>();
map.put("nonce_str", WxpayUtil.getNonceStr());
map.put("mchid", mch_id);
map.put("mch_appid", mch_app_id);
        map.put("partner_trade_no", WxpayUtil.getNonceStr());
// 用户的openid
map.put("openid", re_openid);
//付款金额,单位分
map.put("amount", Integer.toString((int)(amount*100)));
//校验姓名:不校验
map.put("check_name", check_name);
map.put("desc",desc);
map.put("spbill_create_ip", "58.56.96.154");
String xmlResult="";
try {
String sign = WxpayUtil.getSgin(map, api_key);
map.put("sign", sign);
String strParams=WxpayUtil.map2xml(map);
    xmlResult=WxpayUtil.post(strParams, mch_id, post_transfer_url,ca_path);
    Map<String, String> result = WxpayUtil.xml2map(xmlResult);
System.out.println(result);
// 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
String return_code = result.get("return_code");
// 业务结果
String result_code = result.get("result_code");
String err_code_des=result.get("err_code_des");
//请求不成功,建议重试
if (StringUtils.isBlank(return_code) || !"SUCCESS".equals(return_code)) {
resMap.put("succes", "false");
resMap.put("returnCode", "fail");
resMap.put("resultCode", "fail");
resMap.put("desc", "请求失败,建议重试");
}else if("SUCCESS".equals(return_code)&&"FAIL".equals(result_code)){//请求成功,但是支付失败
resMap.put("succes", "false");
resMap.put("returnCode", "success");
resMap.put("resultCode", "fail");
resMap.put("desc", err_code_des);
}else if("SUCCESS".equals(return_code)&&"SUCCESS".equals(result_code)){//支付成功
resMap.put("succes", "true");
resMap.put("returnCode", "success");
resMap.put("resultCode", "success");
resMap.put("desc", "支付成功");
}else{
resMap.put("succes", "false");
resMap.put("returnCode", return_code);
resMap.put("resultCode", result_code);
resMap.put("desc", err_code_des);
}
} catch (Exception e) {
resMap.put("succes", "false");
resMap.put("returnCode", "fail");
resMap.put("resultCode", "fail");
resMap.put("desc", "转账失败,请联系系统管理员");
e.printStackTrace();
}

return resMap;
}

}

2).WxpayUtil :

package com.qlwb.business.payment.util;


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;


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;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;


import com.qlwb.business.payment.WeXinPay;
import com.tenpay.util.MD5Util;




/**
 * 微信转账相关工具类
 * @author Administrator
 *
 */
public class WxpayUtil {

private static String characterEncoding = "UTF-8";


/**
* post提交到微信服务器

* @param requestXML
* @param instream
* @return
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws IOException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws KeyStoreException
* @throws java.security.cert.CertificateException
*/
public static String post(String requestXML, String mch_id,String url,String ca_path)
throws NoSuchAlgorithmException, Exception, IOException,
KeyManagementException, UnrecoverableKeyException,
KeyStoreException, java.security.cert.CertificateException {


//加载微信提供给商户的证书 
FileInputStream instream = new FileInputStream(new File(ca_path));

KeyStore keyStore = KeyStore.getInstance("PKCS12");
try {
keyStore.load(instream, mch_id.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf).build();
String result = "";
try {
HttpPost httpPost = new HttpPost(url);
StringEntity reqEntity = new StringEntity(requestXML, characterEncoding); // 如果此处编码不对,可能导致客户端签名跟微信的签名不一致
reqEntity.setContentType("application/x-www-form-urlencoded");
reqEntity.setContentEncoding("UTF-8");
httpPost.setEntity(reqEntity);
CloseableHttpResponse response = httpclient.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(entity.getContent(), characterEncoding));
String text;
while ((text = bufferedReader.readLine()) != null) {
result += text;
}
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return result;
}


public static String getSgin(Map<String, String> params, String wxkey) {
String qr = "";
String[] p = new String[params.size()];
int x = 0;
for (String key : params.keySet()) {
p[x] = key + "=" + params.get(key);
x++;
}
// 参数排序
Arrays.sort(p);
// 拼出查询参数
for (String param : p) {
String[] value = param.split("=");
qr += value[0] + "=" + params.get(value[0]) + "&";
}
return MD5Util.MD5Encode(qr + "key=" + wxkey,characterEncoding).toUpperCase();
}
    
  public static String getNonceStr() {
    Random random = new Random();
    return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), characterEncoding);
  }
  
  public static String getTimeStamp() {
    return String.valueOf(System.currentTimeMillis() / 1000);
  }


/**
* 生成提交给微信服务器的xml格式参数

* @param params
* @return
*/
public static String map2xml(Map<String, String> params) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = params.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
}
sb.append("</xml>");
return sb.toString();
}


public static Map<String, String> xml2map(String responseXML) {
Document document = null;
InputStream in = null;
Map<String, String> resultMap = new TreeMap<String, String>();
try {
in = new ByteArrayInputStream(responseXML.getBytes(characterEncoding));
// 读取输入流
SAXReader reader = new SAXReader();
document = reader.read(in);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();

// 遍历所有子节点
for (Element e : elementList) {
resultMap.put(e.getName(), e.getText());
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return resultMap;
}

}

3).由于我的md5调用的公司内部的,加密过程如下:

package com.tenpay.util;


import java.security.MessageDigest;


public class MD5Util
{
  private static final String[] hexDigits = { "0", "1", "2", "3", "4", "5", 
    "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };


  private static String byteArrayToHexString(byte[] b)
  {
    StringBuffer resultSb = new StringBuffer();
    for (int i = 0; i < b.length; i++) {
      resultSb.append(byteToHexString(b[i]));
    }
    return resultSb.toString();
  }


  private static String byteToHexString(byte b) {
    int n = b;
    if (n < 0)
      n += 256;
    int d1 = n / 16;
    int d2 = n % 16;
    return hexDigits[d1] + hexDigits[d2];
  }


  public static String MD5Encode(String origin, String charsetname) {
    String resultString = null;
    try {
      resultString = new String(origin);
      MessageDigest md = MessageDigest.getInstance("MD5");
      if ((charsetname == null) || ("".equals(charsetname)))
        resultString = byteArrayToHexString(md.digest(
          resultString.getBytes()));
      else
        resultString = byteArrayToHexString(md.digest(
          resultString.getBytes(charsetname)));
    } catch (Exception localException) {
    }
    return resultString;
  }

}


6.恩,调用过程就是这样,正常情况下会成功的。但是,微信支付比较恶心,接口证书错误,参数少了,余额不足的情况下会提示,但是有一个错误【签名错误】比较难找。原因如下:

   1).商户平台的密钥不对

   2).参数传多了!!(这个问题很难发现,我找了一下午)

   3).发送请求的时候字符集错误

基本就是这样。我是调通了,其间遇到很多坑。这个地方没必要多研究,有用到的拿去用,少走一些弯路。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值