JAVA微信支付——企业付款(企业向微信用户个人付款、转账)

2022-10-15,,,,

本地开发环境支付回调调试方法可以参考:https://www.cnblogs.com/pxblog/p/11623053.html

需要自行引入相关依赖

官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2

用于企业向微信用户个人付款,目前支持向指定微信用户的openid付款。

官方提示

ClientCustomSSL.java

package com.weixinpay;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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 javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore; /**
* This example demonstrates how to create secure connections with a custom SSL
* context.
*/
public class ClientCustomSSL { public static String getInSsl(String url,File pkcFile,String storeId,
String params,String contentType)
throws Exception {
String text = "";
// 指定读取证书格式为PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 读取本机存放的PKCS12证书文件
FileInputStream instream = new FileInputStream(pkcFile);
try {
// 指定PKCS12的密码(商户ID)
keyStore.load(instream, storeId.toCharArray());
} finally {
instream.close();
} // Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, storeId.toCharArray()).build();
// Allow TLSv1 protocol only
// 指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
// 设置httpclient的SSLSocketFactory
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost post = new HttpPost(url);
StringEntity s = new StringEntity(params,"utf-8");
if(StringUtils.isBlank(contentType)){
s.setContentType("application/xml");
}
s.setContentType(contentType);
post.setEntity(s);
HttpResponse res = httpclient.execute(post);
HttpEntity entity = res.getEntity();
text= EntityUtils.toString(entity, "utf-8");
} finally {
httpclient.close();
}
return text;
} }

Num62.java

package com.weixinpay;

/**
* 62进制数字
*/
public class Num62 {
/**
* 62个字母和数字,含大小写
*/
public static final char[] N62_CHARS = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', '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'}; }

PaymentConfig.java

package com.weixinpay;

public class PaymentConfig {
/*******微信支付参数*********/ //公众账号ID
public static final String appid="wxd3e"; //密钥 key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
public static final String appKey="abcde"; //商户号
public static final String mch_id="15849"; //转账请求接口地址
public static final String pay_url="https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; }

PayUtil.java

package com.weixinpay;

import org.apache.commons.lang.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.*; public class PayUtil { /**
* UTF-8编码
*/
public static final String UTF8 = "UTF-8"; /**
* 将需要传递给微信的参数转成xml格式
* @param parameters
* @return
*/
public static String assembParamToXml(Map<String,String> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set<String> es = parameters.keySet();
List<Object> list=new ArrayList<Object>(es);
Object[] ary =list.toArray();
Arrays.sort(ary);
list=Arrays.asList(ary);
Iterator<Object> it = list.iterator();
while(it.hasNext()) {
String key = (String) it.next();
String val=(String) parameters.get(key);
if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) {
sb.append("<"+key+">"+"<![CDATA["+val+"]]></"+key+">");
}else {
sb.append("<"+key+">"+val+"</"+key+">");
}
}
sb.append("</xml>");
return sb.toString();
} /**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map parseXMLToMap(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\""+UTF8+"\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes(UTF8));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v =PayUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
} /**
* 获取子结点的xml
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
} /**
* 微信支付签名sign
* @param param
* @param key
* @return
*/
public static String createSign(Map<String, String> param,String key){
//签名步骤一:按字典排序参数
List list=new ArrayList(param.keySet());
Object[] ary =list.toArray();
Arrays.sort(ary);
list=Arrays.asList(ary);
String str="";
for(int i=0;i<list.size();i++){
str+=list.get(i)+"="+param.get(list.get(i)+"")+"&";
}
//签名步骤二:加上key
str+="key="+key;
//步骤三:加密并大写
str=PayUtil.MD5Encode(str,"utf-8").toUpperCase();
return str;
} public static String MD5Encode(String origin,String charsetName){
String resultString=null;
try{
resultString=new String(origin);
MessageDigest md=MessageDigest.getInstance("MD5");
if(StringUtils.isBlank(charsetName)){
resultString=byteArrayToHexString(md.digest(resultString.getBytes()));
}else{
resultString=byteArrayToHexString(md.digest(resultString.getBytes(charsetName)));
}
}catch(Exception e){ }
return resultString;
} public static String byteArrayToHexString(byte b[]){
StringBuffer resultSb=new StringBuffer();
for(int i=0;i<b.length;i++){
resultSb.append(PayUtil.byteToHexString(b[i]));
}
return resultSb.toString();
} public static String byteToHexString(byte b){
int n=b;
if(n<0){
n+=256;
}
int d1=n/16;
int d2=n%16;
return PayUtil.hexDigits[d1]+PayUtil.hexDigits[d2];
} public static final String hexDigits[]={ "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; /**
* 元转换为分
* @param amount
*/
public static String changeY2F(Double amount){
String currency = amount.toString();
int index = currency.indexOf(".");
int length = currency.length();
Long amLong = 0l;
if(index == -1){
amLong = Long.valueOf(currency+"00");
}else if(length - index >= 3){
amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));
}else if(length - index == 2){
amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);
}else{
amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");
}
return amLong.toString();
} }

WeixinPay.java

package com.weixinpay;

import org.apache.commons.lang.RandomStringUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Map; public class WeixinPay { /**
* 企业付款接口 用于企业向微信用户个人付款
* 目前支持向指定微信用户的openid付款。
* @param pkcFile 商户证书文件
* @param orderNo 订单编号
* @param weixinOpenId 要付款的用户openid
* @param realname 收款用户姓名
* @param payAmount 转账金额
* @param desc 企业付款备注
* @param ip ip地址
* @return
*/
public static Object[] payToUser(File pkcFile, String orderNo, String weixinOpenId, String realname
, Double payAmount, String desc, String ip) {
Map<String, String> paramMap = new HashMap<String, String>();
// 公众账号appid[必填]
paramMap.put("mch_appid", PaymentConfig.appid);
// 微信支付分配的商户号 [必填]
paramMap.put("mchid", PaymentConfig.mch_id);
// 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" [非必填]
paramMap.put("device_info", "WEB");
// 随机字符串,不长于32位。 [必填]
paramMap.put("nonce_str", RandomStringUtils.random(16, Num62.N62_CHARS)); // 商户订单号,需保持唯一性[必填]
paramMap.put("partner_trade_no", orderNo); // 商户appid下,某用户的openid[必填]
paramMap.put("openid", weixinOpenId); //校验用户姓名选项 NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
paramMap.put("check_name", "OPTION_CHECK"); //收款用户姓名,如果check_name设置为FORCE_CHECK,则必填用户真实姓名
paramMap.put("re_user_name", realname);
// 企业付款金额,金额必须为整数 单位为分 [必填]
paramMap.put("amount", PayUtil.changeY2F(payAmount));
// 企业付款描述信息 [必填]
paramMap.put("desc", desc);
// 调用接口的机器Ip地址[必填]
paramMap.put("spbill_create_ip", ip);
// 根据微信签名规则,生成签名
paramMap.put("sign",
PayUtil.createSign(paramMap, PaymentConfig.appKey));
// 把参数转换成XML数据格式
String xmlWeChat = PayUtil.assembParamToXml(paramMap);
String resXml = "";
boolean postError = false;
try {
resXml = ClientCustomSSL.getInSsl(PaymentConfig.pay_url, pkcFile, PaymentConfig.mch_id
, xmlWeChat, "application/xml");
} catch (Exception e1) {
postError = true;
e1.printStackTrace();
}
Object[] result = new Object[2];
result[0] = postError;
result[1] = resXml;
return result;
} }

商户证书说明:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=4_3

控制器调用类

PayContoller.java

package com.weixinpay;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import java.io.File;
import java.util.HashMap;
import java.util.Map; @Controller
public class PayContoller { /**
* 企业付款到用户个人微信
* 简单demo 需要自行完善逻辑
* @return
*/
@RequestMapping(value = "/transfers")
public String transfers() { File pkcFile = new File("商户证书路径"); String orderNo = "订单编号 可以参考:https://www.cnblogs.com/pxblog/p/12818067.html"; String weixinOpenId = "用户的openid 可以参考:https://www.cnblogs.com/pxblog/p/10542698.html"; String realname = "用户的真实姓名"; //转账的金额 金额格式转换可以参考 https://www.cnblogs.com/pxblog/p/13186037.html
Double payAmount = 0.01; String desc = "企业付款备注"; String ip = "ip地址 可以参考:https://www.cnblogs.com/pxblog/p/13360768.html"; Object result[] = WeixinPay.payToUser(pkcFile, orderNo, weixinOpenId, realname, payAmount, desc, ip);
String resXml = (String) result[1];
boolean postError = (Boolean) result[0];
if (!postError) {
Map<String, String> map = new HashMap<String, String>();
try {
map = PayUtil.parseXMLToMap(resXml);
} catch (Exception e) {
e.printStackTrace();
}
String returnCode = map.get("return_code");
if (returnCode.equalsIgnoreCase("FAIL")) {
//支付失败
return map.get("return_msg");
} else if (returnCode.equalsIgnoreCase("SUCCESS")) {
if (map.get("err_code") != null) {
//支付失败
return map.get("err_code_des");
} else if (map.get("result_code").equalsIgnoreCase(
"SUCCESS")) {
//支付成功 paymentNo:微信付款单号 payment_time:付款成功时间
String paymentNo = map.get("payment_no");
String payment_time = map.get("payment_time");
try { //如果是体现操作,在这里处理体现订单的状态,把状态转为提现成功 } catch (Exception e) {
e.printStackTrace();
}
return "返回到成功的页面";
}
}
} return "返回通信失败的错误页面";
}
}

JAVA微信支付——企业付款(企业向微信用户个人付款、转账)的相关教程结束。

《JAVA微信支付——企业付款(企业向微信用户个人付款、转账).doc》

下载本文的Word格式文档,以方便收藏与打印。