微信官方提供了微信企业账户付款到微信个人零钱接口,提供企业向用户付款的功能,支持企业通过api接口付款,或通过微信支付商户平台网页功能操作付款。该接口并不是直接所有的商户都拥有,企业要开启必须满足以下两个条件:
1、商户号已入驻90日
2、商户号有30天连续正常交易
满足以上条件就可登录微信支付商户平台-产品中心,开通企业付款。
调用的链接地址:接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
微信官方接口文档提供的调用微信企业付款的参数:
参数中最重要是获取用户openid和调用接口ip,获取openid可以通过公众号获取,app端可以直接获取。具体的代码实现如下:
封装请求微信企业付款的实体类transfers:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
public class transfers implements serializable{
private static final long serialversionuid = 1l;
/** 商户账号appid*/
public string mch_appid;
/** 微信支付商户号*/
public string mchid;
/** 随机串*/
public string nonce_str;
/** 签名*/
public string sign;
/** 商户订单号*/
public string partner_trade_no;
/** 用户id*/
public string openid;
/** 是否校验用户姓名 no_check:不校验真实姓名 force_check:强校验真实姓名*/
public string check_name;
/** 金额 单位:分*/
public integer amount;
/** 企业付款描述信息*/
public string desc;
/** ip地址*/
public string spbill_create_ip;
public string getmch_appid() {
return mch_appid;
}
public void setmch_appid(string mch_appid) {
this .mch_appid = mch_appid;
}
public string getmchid() {
return mchid;
}
public void setmchid(string mchid) {
this .mchid = mchid;
}
public string getnonce_str() {
return nonce_str;
}
public void setnonce_str(string nonce_str) {
this .nonce_str = nonce_str;
}
public string getsign() {
return sign;
}
public void setsign(string sign) {
this .sign = sign;
}
public string getpartner_trade_no() {
return partner_trade_no;
}
public void setpartner_trade_no(string partner_trade_no) {
this .partner_trade_no = partner_trade_no;
}
public string getopenid() {
return openid;
}
public void setopenid(string openid) {
this .openid = openid;
}
public string getcheck_name() {
return check_name;
}
public void setcheck_name(string check_name) {
this .check_name = check_name;
}
public integer getamount() {
return amount;
}
public void setamount(integer amount) {
this .amount = amount;
}
public string getdesc() {
return desc;
}
public void setdesc(string desc) {
this .desc = desc;
}
public string getspbill_create_ip() {
return spbill_create_ip;
}
public void setspbill_create_ip(string spbill_create_ip) {
this .spbill_create_ip = spbill_create_ip;
}
}
|
接口部分代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
private transfers transfers = new transfers();
// 构造签名的map
private sortedmap<object, object> parameters = new treemap<object, object>();
// 微信的参数
private weixinconfigutils config = new weixinconfigutils();
/**
* 微信提现(企业付款)
*/
@action ( "weixinwithdraw" )
public string weixinwithdraw(){
string openid = request.getparameter( "openid" );
string ip = request.getparameter( "ip" );
string money = request.getparameter( "money" );
string doctorid = request.getparameter( "doctorid" );
if (stringutils.isnotblank(money) && stringutils.isnotblank(ip) && stringutils.isnotblank(openid) && stringutils.isnotblank(doctorid)) {
// 参数组
string appid = config.appid;
string mch_id = config.mch_id;
string nonce_str = randcharsutils.getrandomstring( 16 );
//是否校验用户姓名 no_check:不校验真实姓名 force_check:强校验真实姓名
string checkname = "no_check" ;
//等待确认转账金额,ip,openid的来源
integer amount = integer.valueof(money);
string spbill_create_ip = ip;
string partner_trade_no = uuidutils.getuuid();
//描述
string desc = "健康由我医师助手提现" +amount/ 100 + "元" ;
// 参数:开始生成第一次签名
parameters.put( "appid" , appid);
parameters.put( "mch_id" , mch_id);
parameters.put( "partner_trade_no" , partner_trade_no);
parameters.put( "nonce_str" , nonce_str);
parameters.put( "openid" , openid);
parameters.put( "checkname" , checkname);
parameters.put( "amount" , amount);
parameters.put( "spbill_create_ip" , spbill_create_ip);
parameters.put( "desc" , desc);
string sign = wxsignutils.createsign( "utf-8" , parameters);
transfers.setamount(amount);
transfers.setcheck_name(checkname);
transfers.setdesc(desc);
transfers.setmch_appid(appid);
transfers.setmchid(mch_id);
transfers.setnonce_str(nonce_str);
transfers.setopenid(openid);
transfers.setpartner_trade_no(partner_trade_no);
transfers.setsign(sign);
transfers.setspbill_create_ip(spbill_create_ip);
string xmlinfo = httpxmlutils.transferxml(transfers);
try {
closeablehttpresponse response = httputil.post(weixinconstant.withdraw_url, xmlinfo, true );
string transfersxml = entityutils.tostring(response.getentity(), "utf-8" );
map<string, string> transfermap = httpxmlutils.parserefundxml(transfersxml);
if (transfermap.size()> 0 ) {
if (transfermap.get( "result_code" ).equals( "success" ) && transfermap.get( "return_code" ).equals( "success" )) {
//成功需要进行的逻辑操作,
}
}
system.out.println( "成功" );
} catch (exception e) {
log.error(e.getmessage());
throw new basicruntimeexception( this , "企业付款异常" + e.getmessage());
}
} else {
system.out.println( "失败" );
}
return none;
}
|
产生随机串部分代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class randcharsutils {
private static simpledateformat df = new simpledateformat( "yyyymmddhhmmss" );
public static string getrandomstring( int length) { //length表示生成字符串的长度
string base = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" ;
random random = new random();
stringbuffer sb = new stringbuffer();
int number = 0 ;
for ( int i = 0 ; i < length; i++) {
number = random.nextint(base.length());
sb.append(base.charat(number));
}
return sb.tostring();
}
}
|
生成签名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public class wxsignutils {
/**
* 微信支付签名算法sign
* @param characterencoding
* @param parameters
* @return
*/
@suppresswarnings ( "rawtypes" )
public static string createsign(string characterencoding,sortedmap<object,object> parameters){
stringbuffer sb = new stringbuffer();
set es = parameters.entryset(); //所有参与传参的参数按照accsii排序(升序)
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=" + weixinconstant.key);
string sign = md5util.md5encode(sb.tostring(), characterencoding).touppercase();
return sign;
}
}
|
md5部分代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
import java.security.messagedigest;
public class md5util {
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 exception) {
}
return resultstring;
}
private static final string hexdigits[] = { "0" , "1" , "2" , "3" , "4" , "5" ,
"6" , "7" , "8" , "9" , "a" , "b" , "c" , "d" , "e" , "f" };
}
|
构造xml:
1
2
3
4
5
6
7
8
9
10
|
/**
* 构造企业付款xml参数
* @param xml
* @return
*/
public static string transferxml(transfers transfers){
xstream.autodetectannotations( true );
xstream.alias( "xml" , transfers. class );
return xstream.toxml(transfers);
}
|
向微信发送xml请求(验证证书)部分代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class httputil {
/**
* 发送post请求
*
* @param url
* 请求地址
* @param outputentity
* 发送内容
* @param isloadcert
* 是否加载证书
*/
public static closeablehttpresponse post(string url, string outputentity, boolean isloadcert) throws exception {
httppost httppost = new httppost(url);
// 得指明使用utf-8编码,否则到api服务器xml的中文不能被成功识别
httppost.addheader( "content-type" , "text/xml" );
httppost.setentity( new stringentity(outputentity, "utf-8" ));
if (isloadcert) {
// 加载含有证书的http请求
return httpclients.custom().setsslsocketfactory(certutil.initcert()).build().execute(httppost);
} else {
return httpclients.custom().build().execute(httppost);
}
}
}
|
加载证书部分代码:(加载证书需要注意证书放置位置在项目下的webapp中建文件夹,linux单独防止只要地址配置正确即可。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
import java.io.file;
import java.io.fileinputstream;
import java.security.keystore;
import javax.net.ssl.sslcontext;
import org.apache.http.conn.ssl.sslconnectionsocketfactory;
import org.apache.http.ssl.sslcontexts;
/**
* 加载证书的类
* @author
* @since 2017/08/16
*/
@suppresswarnings ( "deprecation" )
public class certutil {
private static weixinconfigutils config = new weixinconfigutils();
/**
* 加载证书
*/
public static sslconnectionsocketfactory initcert() throws exception {
fileinputstream instream = null ;
keystore keystore = keystore.getinstance( "pkcs12" );
instream = new fileinputstream( new file(weixinconstant.path));
keystore.load(instream, config.mch_id.tochararray());
if ( null != instream) {
instream.close();
}
sslcontext sslcontext = sslcontexts.custom().loadkeymaterial(keystore,config.mch_id.tochararray()).build();
sslconnectionsocketfactory sslsf = new sslconnectionsocketfactory(sslcontext, new string[]{ "tlsv1" }, null , sslconnectionsocketfactory.browser_compatible_hostname_verifier);
return sslsf;
}
}
|
加载配置文件部分代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@suppresswarnings ( "unused" )
public class weixinconfigutils {
private static final log log = logfactory.getlog(weixinconfigutils. class );
public static string appid;
public static string mch_id;
public static string notify_url;
public static string order_notify_url;
public static string doctor_notify_url;
static {
try {
inputstream is = weixinconfigutils. class .getresourceasstream( "/weixin.properties" );
properties properties = new properties();
properties.load(is);
appid = properties.getproperty( "weixin.appid" );
mch_id = properties.getproperty( "weixin.mch_id" );
notify_url = properties.getproperty( "weixin.notify_url" );
order_notify_url = properties.getproperty( "weixin.order_notify_url" );
doctor_notify_url = properties.getproperty( "weixin.doctor_notify_url" );
} catch (exception ex){
log.debug( "加载配置文件:" +ex.getmessage());
}
}
}
|
获取返回的xml参数并解析为map:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/**
* 解析申请退款之后微信返回的值并进行存库操作
* @throws ioexception
* @throws jdomexception
*/
public static map<string, string> parserefundxml(string refundxml) throws jdomexception, ioexception{
parsexmlutils.jdomparsexml(refundxml);
stringreader read = new stringreader(refundxml);
// 创建新的输入源sax 解析器将使用 inputsource 对象来确定如何读取 xml 输入
inputsource source = new inputsource(read);
// 创建一个新的saxbuilder
saxbuilder sb = new saxbuilder();
// 通过输入源构造一个document
org.jdom.document doc;
doc = (org.jdom.document) sb.build(source);
org.jdom.element root = doc.getrootelement(); // 指向根节点
list<org.jdom.element> list = root.getchildren();
map<string, string> refundordermap = new hashmap<string, string>();
if (list!= null &&list.size()> 0 ){
for (org.jdom.element element : list) {
refundordermap.put(element.getname(), element.gettext());
}
return refundordermap;
}
return null ;
}
|
调用时候主要是获取openid和调起接口的ip(ip十分重要,微信在收到xml后会校验传过去的ip和微信获取的调起接口ip是否一致)
在调用时候当返回错误码为“systemerror”时,一定要使用原单号重试,否则可能造成重复支付等资金风险。
微信官方文档提供有相关的 参数错误码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/github_38924695/article/details/78850704