Ver código fonte

实现企业付款的接口,暂时未调通,因缺少证书 for issue #50

master
Binary Wang 8 anos atrás
pai
commit
dabafc2a9e
5 arquivos alterados com 553 adições e 14 exclusões
  1. +16
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java
  2. +79
    -14
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java
  3. +253
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxEntPayRequest.java
  4. +190
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxEntPayResult.java
  5. +15
    -0
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java

+ 16
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java Ver arquivo

@@ -1,8 +1,11 @@
package me.chanjar.weixin.mp.api;

import java.io.File;
import java.util.Map;

import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.bean.pay.WxEntPayRequest;
import me.chanjar.weixin.mp.bean.pay.WxEntPayResult;
import me.chanjar.weixin.mp.bean.pay.WxMpPayCallback;
import me.chanjar.weixin.mp.bean.pay.WxMpPayRefundResult;
import me.chanjar.weixin.mp.bean.pay.WxMpPayResult;
@@ -85,4 +88,17 @@ public interface WxMpPayService {
*/
WxRedpackResult sendRedpack(WxSendRedpackRequest request) throws WxErrorException;

/**
* <pre>
* 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。
* 比如目前的保险行业向客户退保、给付、理赔。
* 企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”https://pay.weixin.qq.com/进行操作。
* 注意:与商户微信支付收款资金并非同一账户,需要单独充值。
* 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
* 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
* @param keyFile 证书文件对象
* </pre>
*/
WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException;

}

+ 79
- 14
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java Ver arquivo

@@ -1,6 +1,9 @@
package me.chanjar.weixin.mp.api.impl;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -8,8 +11,19 @@ import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.joor.Reflect;

import com.google.common.collect.Lists;
@@ -23,6 +37,8 @@ import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.api.WxMpPayService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.pay.WxEntPayRequest;
import me.chanjar.weixin.mp.bean.pay.WxEntPayResult;
import me.chanjar.weixin.mp.bean.pay.WxMpPayCallback;
import me.chanjar.weixin.mp.bean.pay.WxMpPayRefundResult;
import me.chanjar.weixin.mp.bean.pay.WxMpPayResult;
@@ -263,6 +279,23 @@ public class WxMpPayServiceImpl implements WxMpPayService {

private void checkParameters(WxUnifiedOrderRequest request) {

checkNotNullParams(request);

if (!TRADE_TYPES.contains(request.getTradeType())) {
throw new IllegalArgumentException("trade_type目前必须为" + TRADE_TYPES + "其中之一");

}

if ("JSAPI".equals(request.getTradeType()) && request.getOpenid() == null) {
throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid");
}

if ("NATIVE".equals(request.getTradeType()) && request.getProductId() == null) {
throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id");
}
}

private void checkNotNullParams(Object request) {
List<String> nullFields = Lists.newArrayList();
for (Entry<String, Reflect> entry : Reflect.on(request).fields()
.entrySet()) {
@@ -281,20 +314,6 @@ public class WxMpPayServiceImpl implements WxMpPayService {
if (!nullFields.isEmpty()) {
throw new IllegalArgumentException("必填字段[" + nullFields + "]必须提供值");
}

if (!TRADE_TYPES.contains(request.getTradeType())) {
throw new IllegalArgumentException("trade_type目前必须为" + TRADE_TYPES + "其中之一");

}

if ("JSAPI".equals(request.getTradeType()) && request.getOpenid() == null) {
throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid");
}

if ("NATIVE".equals(request.getTradeType())
&& request.getProductId() == null) {
throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id");
}
}

@Override
@@ -332,4 +351,50 @@ public class WxMpPayServiceImpl implements WxMpPayService {
return payInfo;
}

@Override
public WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException {
checkNotNullParams(request);

XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxEntPayRequest.class);
xstream.processAnnotations(WxEntPayResult.class);

request.setMchAppid(this.wxMpService.getWxMpConfigStorage().getAppId());
request.setMchId(this.wxMpService.getWxMpConfigStorage().getPartnerId());
request.setNonceStr(System.currentTimeMillis() + "");

String sign = this.createSign(xmlBean2Map(request), this.wxMpService.getWxMpConfigStorage().getPartnerKey());
request.setSign(sign);

String url = PAY_BASE_URL + "/mmpaymkttransfers/promotion/transfers";

try (FileInputStream instream = new FileInputStream(keyFile)) {
String mchId = request.getMchId();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(instream, mchId.toCharArray());

SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
new DefaultHostnameVerifier());

try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) {
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(new String(xstream.toXML(request).getBytes("UTF-8"), "ISO-8859-1")));

try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
String responseContent = EntityUtils.toString(response.getEntity());
WxEntPayResult result = (WxEntPayResult) xstream.fromXML(responseContent);
if ("FAIL".equals(result.getResultCode())) {
throw new WxErrorException(
WxError.newBuilder().setErrorMsg(result.getErrCode() + ":" + result.getErrCodeDes()).build());
}

return result;
}
}
} catch (Exception e) {
throw new WxErrorException(WxError.newBuilder().setErrorMsg(e.getMessage()).build(), e);
}
}

}

+ 253
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxEntPayRequest.java Ver arquivo

@@ -0,0 +1,253 @@
package me.chanjar.weixin.mp.bean.pay;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* 企业付款请求对象
* 注释中各行含义如下:
* 字段名、 变量名、 是否必填、 示例值、 类型、 描述
* Created by Binary Wang on 2016/10/02.
* @author binarywang (https://github.com/binarywang)
*/
@XStreamAlias("xml")
public class WxEntPayRequest {
/**
* 公众账号appid
* mch_appid
* 是
* wx8888888888888888
* String
* 微信分配的公众账号ID(企业号corpid即为此appId)
*/
@XStreamAlias("mch_appid")
private String mchAppid;

/**
* 商户号
* mchid
* 是
* 1900000109
* String(32)
* 微信支付分配的商户号
*/
@XStreamAlias("mchid")
private String mchId;

/**
* 设备号
* device_info
* 否
* 13467007045764
* String(32)
*微信支付分配的终端设备号
*/
@XStreamAlias("device_info")
private String deviceInfo;

/**
* 随机字符串
* nonce_str
*是
*5K8264ILTKCH16CQ2502SI8ZNMTM67VS
*String(32)
*随机字符串,不长于32位
*/
@XStreamAlias("nonce_str")
private String nonceStr;

/**
* 签名
* sign
* 是
* C380BEC2BFD727A4B6845133519F3AD6
* String(32)
*签名,详见签名算法
*/
@XStreamAlias("sign")
private String sign;

/**
* 商户订单号
* partner_trade_no
* 是
* 10000098201411111234567890
* String
* 商户订单号,
*/
@XStreamAlias("partner_trade_no")
private String partnerTradeNo;

/**
* 需保持唯一性 用户openid
* openid
* 是
* oxTWIuGaIt6gTKsQRLau2M0yL16E
* String
* 商户appid下,某用户的openid
*/
@XStreamAlias("openid")
private String openid;

/**
* 校验用户姓名选项
* check_name
* 是
* OPTION_CHECK
* String
* NO_CHECK:不校验真实姓名 
* FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
* OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
*/
@XStreamAlias("check_name")
private String checkName;

/**
* 收款用户姓名
* re_user_name
* 可选
* 马花花
* String
* 收款用户真实姓名。
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名
*/
@XStreamAlias("re_user_name")
private String reUserName;

/**
* 金额
* amount
* 是
* 10099
* int
* 企业付款金额, 单位为分
*/
@XStreamAlias("amount")
private Integer amount;

/**
* 企业付款描述信息
* desc
* 是
* 理赔
* String
* 企业付款操作说明信息。必填。
*/
@XStreamAlias("desc")
private String description;

/**
* Ip地址
* spbill_create_ip
* 是
* 192.168.0.1
* String(32)
* 调用接口的机器Ip地址
*/
@XStreamAlias("spbill_create_ip")
private String spbillCreateIp;

public String getMchAppid() {
return mchAppid;
}

public void setMchAppid(String mchAppid) {
this.mchAppid = mchAppid;
}

public String getMchId() {
return mchId;
}

public void setMchId(String mchId) {
this.mchId = mchId;
}

public String getDeviceInfo() {
return deviceInfo;
}

public void setDeviceInfo(String deviceInfo) {
this.deviceInfo = deviceInfo;
}

public String getNonceStr() {
return nonceStr;
}

public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}

public String getSign() {
return sign;
}

public void setSign(String sign) {
this.sign = sign;
}

public String getPartnerTradeNo() {
return partnerTradeNo;
}

public void setPartnerTradeNo(String partnerTradeNo) {
this.partnerTradeNo = partnerTradeNo;
}

public String getOpenid() {
return openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}

public String getCheckName() {
return checkName;
}

public void setCheckName(String checkName) {
this.checkName = checkName;
}

public String getReUserName() {
return reUserName;
}

public void setReUserName(String reUserName) {
this.reUserName = reUserName;
}

public Integer getAmount() {
return amount;
}

public void setAmount(Integer amount) {
this.amount = amount;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getSpbillCreateIp() {
return spbillCreateIp;
}

public void setSpbillCreateIp(String spbillCreateIp) {
this.spbillCreateIp = spbillCreateIp;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}

}

+ 190
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxEntPayResult.java Ver arquivo

@@ -0,0 +1,190 @@
package me.chanjar.weixin.mp.bean.pay;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* 企业付款返回结果
* Created by Binary Wang on 2016/10/02.
* @author binarywang (https://github.com/binarywang)
*/
@XStreamAlias("xml")
public class WxEntPayResult {

/**
* 返回状态码
*/
@XStreamAlias("return_code")
private String returnCode;

/**
* 返回信息
*/
@XStreamAlias("return_msg")
private String returnMsg;

//############以下字段在return_code为SUCCESS的时候有返回

/**
* 商户appid
*/
@XStreamAlias("mch_appid")
private String mchAppid;

/**
* 商户号
*/
@XStreamAlias("mchid")
private String mchId;

/**
* 设备号
*/
@XStreamAlias("device_info")
private String deviceInfo;

/**
* 随机字符串
*/
@XStreamAlias("nonce_str")
private String nonceStr;

/**
* 业务结果
*/
@XStreamAlias("result_code")
private String resultCode;
/**
* 错误代码
*/
@XStreamAlias("err_code")
private String errCode;

/**
* 错误代码描述
*/
@XStreamAlias("err_code_des")
private String errCodeDes;

//############以下字段在return_code 和result_code都为SUCCESS的时候有返回##############
/**
* 商户订单号
*/
@XStreamAlias("partner_trade_no")
private String partnerTradeNo;

/**
* 微信订单号
*/
@XStreamAlias("payment_no")
private String paymentNo;

/**
* 微信支付成功时间
*/
@XStreamAlias("payment_time")
private String paymentTime;

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}

public String getReturnCode() {
return returnCode;
}

public void setReturnCode(String returnCode) {
this.returnCode = returnCode;
}

public String getReturnMsg() {
return returnMsg;
}

public void setReturnMsg(String returnMsg) {
this.returnMsg = returnMsg;
}

public String getMchAppid() {
return mchAppid;
}

public void setMchAppid(String mchAppid) {
this.mchAppid = mchAppid;
}

public String getMchId() {
return mchId;
}

public void setMchId(String mchId) {
this.mchId = mchId;
}

public String getDeviceInfo() {
return deviceInfo;
}

public void setDeviceInfo(String deviceInfo) {
this.deviceInfo = deviceInfo;
}

public String getNonceStr() {
return nonceStr;
}

public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}

public String getResultCode() {
return resultCode;
}

public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}

public String getErrCode() {
return errCode;
}

public void setErrCode(String errCode) {
this.errCode = errCode;
}

public String getErrCodeDes() {
return errCodeDes;
}

public void setErrCodeDes(String errCodeDes) {
this.errCodeDes = errCodeDes;
}

public String getPartnerTradeNo() {
return partnerTradeNo;
}

public void setPartnerTradeNo(String partnerTradeNo) {
this.partnerTradeNo = partnerTradeNo;
}

public String getPaymentNo() {
return paymentNo;
}

public void setPaymentNo(String paymentNo) {
this.paymentNo = paymentNo;
}

public String getPaymentTime() {
return paymentTime;
}

public void setPaymentTime(String paymentTime) {
this.paymentTime = paymentTime;
}
}

+ 15
- 0
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java Ver arquivo

@@ -1,5 +1,7 @@
package me.chanjar.weixin.mp.api.impl;

import java.io.File;

import org.testng.annotations.Guice;
import org.testng.annotations.Test;

@@ -8,6 +10,7 @@ import com.google.inject.Inject;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.ApiTestModule;
import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.bean.pay.WxEntPayRequest;
import me.chanjar.weixin.mp.bean.pay.WxRedpackResult;
import me.chanjar.weixin.mp.bean.pay.WxSendRedpackRequest;
import me.chanjar.weixin.mp.bean.pay.WxUnifiedOrderRequest;
@@ -74,4 +77,16 @@ public class WxMpPayServiceImplTest {
.tradeType("JSAPI").openid("122").outTradeNo("111111").build());
System.err.println(result);
}

/**
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#entPay(WxEntPayRequest, File)}.
* @throws WxErrorException
*/
@Test
public final void testEntPay() throws WxErrorException {
File keyFile = new File("E:\\dlt.p12");
WxEntPayRequest request = new WxEntPayRequest();
System.err.println(this.wxService.getPayService().entPay(request, keyFile));
}

}

Carregando…
Cancelar
Salvar