微信公众号支付
微信公众号支付的总体其实很简单,大致就分为三步。第一步需要获取用户授权;第二步调用统一下单接口获取预支付id;第三步H5调起微信支付的内置的js。下面介绍具体每一步的开发流程。
一 首先要明确微信公众号支付属于网页版支付,所以相较于app的直接调取微信支付要多一步微信授权。也就是需要获取用户的openid。微信公众号使用的交易类型是JSAPI,所以统一下单接口的文档明确的写到
因此我们必须去获取openid,同时也可以处理一些我们需要的逻辑。获取用户授权有两种方式:1.scope=snsapi_base;2.scope=snsapi_userinfo.我使用的是snsapi_base
Scope为snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=http%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
Scope为snsapi_userinfo
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
微信的官方文档也有对各个参数的详细说明,我就关键的参数仔细的说明一下。首先appid就不多说了就是你微信公众号的appid固定写死的,redirect_uri这个参数是最重要的,这个地址是访问你处理的接口地址。你可以在这个链接上拼接上你所需要的参数,一般你是要把订单的金额传到这个接口里的,访问这个链接的时候微信会给你code你需要用它去获取openid,记得要对其进行urlencode处理。state参数可以理解为扩展字段,其他的参数都是固定写法就不在多做介绍了。下面是获取openid的代码片段。
1
2
3
4
5
6
7
8
9
10
|
//获取openId
HttpClientUtil util = HttpClientUtil.getInstance();
Map
new
HashMap
map.put(
"appid"
, WxPayConfig.APPID);
map.put(
"secret"
, WxPayConfig.APPSECRET);
map.put(
"code"
, code);
map.put(
"grant_type"
, WxPayConfig.GRANT_TYPE);
String returnStr = util.doPostRetString(
"https://api.weixin.qq.com/sns/oauth2/access_token"
,
null
,map);
logger.info(
"returnStr:["
+ returnStr +
"]"
);
AccessToken at = JSON.parseObject(returnStr, AccessToken.
class
);
|
AccessToken.java
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
|
public
class
AccessToken {
private
String access_token;
private
String expires_in;
private
String refresh_token;
private
String openid;
private
String scope;
private
String unionid;
public
String getAccess_token() {
return
access_token;
}
public
void
setAccess_token(String access_token) {
this
.access_token = access_token;
}
public
String getExpires_in() {
return
expires_in;
}
public
void
setExpires_in(String expires_in) {
this
.expires_in = expires_in;
}
public
String getRefresh_token() {
return
refresh_token;
}
public
void
setRefresh_token(String refresh_token) {
this
.refresh_token = refresh_token;
}
public
String getOpenid() {
return
openid;
}
public
void
setOpenid(String openid) {
this
.openid = openid;
}
public
String getScope() {
return
scope;
}
public
void
setScope(String scope) {
this
.scope = scope;
}
public
String getUnionid() {
return
unionid;
}
public
void
setUnionid(String unionid) {
this
.unionid = unionid;
}
@Override
public
String toString() {
return
"AccessToken [access_token="
+ access_token +
", expires_in="
+ expires_in +
", refresh_token="
+ refresh_token +
", openid="
+ openid +
", scope="
+ scope +
", unionid="
+ unionid +
"]"
;
}
}
|
二 我们获取了openid后,就可以进行下一步的统一下单的开发了。微信上统一下单接口的文档写的比较详细了,具体的参数含义我就不多做介绍了。下面直接贴最直观的代码,特别提醒的是一定要注意签名的正确。签名所使用的key并不是AppSecret而是你申请时自己定义的商户key。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//统一下单
WxPaySendData data =
new
WxPaySendData();
data.setAppid(WxPayConfig.APPID);
data.setAttach(
"微信支付"
);
data.setBody(
"微信公众号支付"
);
data.setMch_id(WxPayConfig.MCHID);
data.setNonce_str(nonceStr);
data.setNotify_url(WxPayConfig.NOTIFY_URL);
data.setOut_trade_no(tradeNo);
data.setTotal_fee((
int
)(fee*
100
));
//单位:分
data.setTrade_type(
"JSAPI"
);
data.setSpbill_create_ip(ip);
data.setOpenid(at.getOpenid());
String returnXml = UnifiedorderService.unifiedOrder(data,WxPayConfig.KEY);
WxPayReturnData reData =
new
WxPayReturnData();
XStream xs1 =
new
XStream(
new
DomDriver());
xs1.alias(
"xml"
, WxPayReturnData.
class
);
reData = (WxPayReturnData) xs1.fromXML(returnXml);
|
UnifiedorderService.java
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
|
public
class
UnifiedorderService {
private
final
static
Logger logger = LoggerFactory.getLogger(UnifiedorderService.
class
);
public
static
String unifiedOrder(WxPaySendData data,String key){
//统一下单支付
String returnXml =
null
;
try
{
//生成sign签名
SortedMap
new
TreeMap
parameters.put(
"appid"
, data.getAppid());
parameters.put(
"attach"
, data.getAttach());
parameters.put(
"body"
, data.getBody());
parameters.put(
"mch_id"
, data.getMch_id());
parameters.put(
"nonce_str"
, data.getNonce_str());
parameters.put(
"notify_url"
, data.getNotify_url());
parameters.put(
"out_trade_no"
, data.getOut_trade_no());
parameters.put(
"total_fee"
, data.getTotal_fee());
parameters.put(
"trade_type"
, data.getTrade_type());
parameters.put(
"spbill_create_ip"
, data.getSpbill_create_ip());
parameters.put(
"openid"
, data.getOpenid());
parameters.put(
"device_info"
, data.getDevice_info());
logger.info(
"SIGN:"
+WxSign.createSign(parameters,key));
data.setSign(WxSign.createSign(parameters,key));
XStream xs =
new
XStream(
new
DomDriver(
"UTF-8"
,
new
XmlFriendlyNameCoder(
"-_"
,
"_"
)));
xs.alias(
"xml"
, WxPaySendData.
class
);
String xml = xs.toXML(data);
logger.info(
"统一下单xml为:\n"
+ xml);
HttpClientUtil util = HttpClientUtil.getInstance();
returnXml = util.doPostForString(
"https://api.mch.weixin.qq.com/pay/unifiedorder"
,
null
, xml);
logger.info(
"返回结果:"
+ returnXml);
}
catch
(Exception e) {
e.printStackTrace();
}
return
returnXml;
}
}
|
WxSign
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
|
public
class
WxSign {
private
static
String characterEncoding =
"UTF-8"
;
@SuppressWarnings
(
"rawtypes"
)
public
static
String createSign(SortedMap
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="
+ key);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return
sign;
}
public
static
String getNonceStr() {
Random random =
new
Random();
return
MD5Util.MD5Encode(String.valueOf(random.nextInt(
10000
)),
"UTF-8"
);
}
public
static
String getTimeStamp() {
return
String.valueOf(System.currentTimeMillis() /
1000
);
}
}
|
最后要提一下的是NOTIFY_URL回调地址,接收微信支付异步通知回调地址。
三 通过上面的操作我们获得了预支付交易会话标识prepay_id,这样我们就可以进行最后一步的操作了。使用H5调起支付api。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//H5调起支付
attr.addAttribute(
"appId"
, reData.getAppid());
attr.addAttribute(
"timeStamp"
, WxSign.getTimeStamp());
attr.addAttribute(
"nonceStr"
, reData.getNonce_str());
attr.addAttribute(
"package"
,
"prepay_id="
+reData.getPrepay_id());
attr.addAttribute(
"signType"
,
"MD5"
);
SortedMap
new
TreeMap
signMap.put(
"appId"
, reData.getAppid());
signMap.put(
"timeStamp"
, WxSign.getTimeStamp());
signMap.put(
"nonceStr"
, reData.getNonce_str());
signMap.put(
"package"
,
"prepay_id="
+reData.getPrepay_id());
signMap.put(
"signType"
,
"MD5"
);
logger.info(
"PaySIGN:"
+WxSign.createSign(signMap,WxPayConfig.KEY));
attr.addAttribute(
"paySign"
, WxSign.createSign(signMap,WxPayConfig.KEY));
|
将需要的参数传给页面后,使用微信提供方法调起支付。
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
|
function
getUrlParam(name) {
//构造一个含有目标参数的正则表达式对象
var
reg =
new
RegExp(
"(^|&)"
+ name +
"=([^&]*)(&|$)"
);
//匹配目标参数
var
r = window.location.search.substr(1).match(reg);
//返回参数值
if
(r !=
null
)
return
unescape(r[2]);
return
null
;
}
function
onBridgeReady() {
var
appId = getUrlParam(
'appId'
);
var
timeStamp = getUrlParam(
'timeStamp'
);
var
nonceStr = getUrlParam(
'nonceStr'
);
var
Package = getUrlParam(
'package'
);
var
signType = getUrlParam(
'signType'
);
var
paySign = getUrlParam(
'paySign'
);
WeixinJSBridge.invoke(
'getBrandWCPayRequest'
, {
"appId"
: appId,
//"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp"
: timeStamp,
//"1395712654", //时间戳,自1970年以来的秒数
"nonceStr"
: nonceStr,
//"e61463f8efa94090b1f366cccfbbb444", //随机串
"package"
: Package,
//"prepay_id=u802345jgfjsdfgsdg888",
"signType"
: signType,
//"MD5", //微信签名方式:
"paySign"
: paySign,
//"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function
(res) {
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
//alert(res.err_msg);
if
(res.err_msg ==
"get_brand_wcpay_request:ok"
) {
alert(
"支付成功"
);
}
if
(res.err_msg ==
"get_brand_wcpay_request:cancel"
) {
alert(
"交易取消"
);
}
if
(res.err_msg ==
"get_brand_wcpay_request:fail"
) {
alert(
"支付失败"
);
}
});
}
function
callPay() {
if
(
typeof
WeixinJSBridge ==
"undefined"
) {
if
(document.addEventListener) {
document.addEventListener(
'WeixinJSBridgeReady'
, onBridgeReady,
false
);
}
else
if
(document.attachEvent) {
document.attachEvent(
'WeixinJSBridgeReady'
, onBridgeReady);
document.attachEvent(
'onWeixinJSBridgeReady'
, onBridgeReady);
}
}
else
{
onBridgeReady();
}
}
|
注:在返回结果的地方可以自定义一些自己的返回页面。
觉得本文对您有帮助,,,,,,关注一下呗。