1、应用场景
直连商户分账主要用于商户将交易成功的资金,按照一定的周期,分账给其他方,可以是合作伙伴、员工、用户或者其他分润方。
单次分账请求按照传入的分账接收方账号和资金进行分账,同时会将订单剩余的待分账金额解冻给本商户。故操作成功后,订单不能再进行分账,也不能进行分账完结。
2、接口说明
微信开发文档地址:
https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
3、实现代码
PayCommonUtil
public class PayCommonUtil {
private static Logger log = LoggerFactory.getLogger(PayCommonUtil.class);
public static String hashHmac(SortedMap<Object,Object> parameters,String apiKey){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + apiKey);
String sign = sha256_HMAC(sb.toString(),apiKey).toUpperCase();
log.info("createSign方法sign签名=====:"+sign);
return sign;
}
/**
* sha256_HMAC加密
* @param message 消息
* @param secret 秘钥
* @return 加密后字符串
*/
public static String sha256_HMAC(String message, String secret) {
String hash = "";
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
hash = byteArrayToHexString(bytes);
} catch (Exception e) {
System.out.println("Error HmacSHA256 ===========" + e.getMessage());
}
return hash;
}
/**
*
* @Title: getRequestXml
* @Description:将请求参数转换为xml格式的string
* @param parameters 请求参数
* @return String 返回类型
* @throws
*/
public static String getRequestXml(SortedMap<Object,Object> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.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();
if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}else {
sb.append("<"+k+">"+v+"</"+k+">");
}
}
sb.append("</xml>");
return sb.toString();
}
}
SubAccount
public static Logger logger = LoggerFactory.getLogger(SubAccount.class);
/**
* 请求单次分账
* https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_1&index=1
* @return
*/
public static SortedMap<Object, Object> profitSharing() throws Exception {
SortedMap<Object, Object> params = new TreeMap<>();
//微信支付分配的商户号
String mchId = "商户号";
try {
// 我们发送给微信服务器的参数是xml格式的string,微信返回来的信息也是xml格式的string
// 获取package包
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
//微信分配的公众账号ID
parameters.put("appid", "appid");
//微信支付分配的商户号
parameters.put("mch_id", "商户号");
//随机字符串,不长于32位
parameters.put("nonce_str", "随机字符串");
//微信支付订单号
parameters.put("transaction_id", "4200000418201910287201948196");// 4200000418201910296095353357
//商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
parameters.put("out_order_no", System.currentTimeMillis() + "");
//分账接收方列表,不超过50个json对象,不能设置分账方作为分账接受方
List<Map<String, Object>> receivers = new ArrayList<>();
Map<String, Object> receiver1 = new HashMap<>();
//MERCHANT_ID:商户ID,PERSONAL_WECHATID:个人微信,号PERSONAL_OPENID:个人openid
receiver1.put("type", "MERCHANT_ID");
//类型是MERCHANT_ID时,是商户ID,类型是PERSONAL_WECHATID时,是个人微信号,类型是PERSONAL_OPENID时,是个人openid
receiver1.put("account", "商户号");
//分账金额,单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额
receiver1.put("amount", 10);
//分账的原因描述,分账账单中需要体现
receiver1.put("description", "分账给商户");
receivers.add(receiver1);
Map<String, Object> receiver2 = new HashMap<>();
receiver2.put("type", "PERSONAL_WECHATID");
receiver2.put("account", "微信号");
receiver2.put("amount", 10);
receiver2.put("description", "分账给个人");
receivers.add(receiver2);
Map<String, Object> receiver3 = new HashMap<>();
receiver3.put("type", "PERSONAL_WECHATID");
receiver3.put("account", "微信号");
receiver3.put("amount", 1);
receiver3.put("description", "分账给个人");
receivers.add(receiver3);
String receiversJson = JSONObject.toJSONString(receivers);
parameters.put("receivers", receiversJson);
System.out.println(receiversJson);
parameters.put("sign_type", "HMAC-SHA256");
String sign = PayCommonUtil.hashHmac(parameters, "支付签名密钥(api_key)");// 进行签名
parameters.put("sign", sign);
// 将请求的参数转为字符串
String requestXML = PayCommonUtil.getRequestXml(parameters);
logger.info("post请求微信支付参数============:" + requestXML);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//安全证书P12文件目录
FileInputStream instream = new FileInputStream(new File("/Users/mac/Desktop/mchid.p12"));
try {
keyStore.load(instream, mchId.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
// 设置响应头信息
HttpPost httpost = new HttpPost(WxUrl.SHARING);
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(requestXML, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
Map<String, String> result = XMLUtil.doXMLParse(jsonStr);
net.sf.json.JSONObject jsonObject = net.sf.json.JSONObject.fromObject(result);
logger.info("请求单次分账返回结果==={}", jsonObject);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return params;
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//============结束==================
return params;
}
}
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海