1. JSAPI支付申请和配置
申请商户号和公众号 ,获取四大支付要素: merchant_id、key、appid、appSecret
公众号: appid和appSecret以及配置授权回调地址(否则会报:redirect_uri与后台配置不一致的错误)
商户号:mch_id和key以及授权目录(否则再拉起支付的时候报当前URL未注册)
2. 支付流程梳理
- 根据appid获取code
(2)跳转授权地址redirect_uri后(一般是一个方法的地址),就能获取到code
String code = request.getParameter(“code”);
//授权回调获取code
1----public String getAuthorizeInfo(String authorizeInfo) throws UnsupportedEncodingException {
String redirectUri = wechatConfig.getWechatRedirectUri();
logger.info("微信授权地址为:" + redirectUri);
logger.info("------authorizeInfo:" + authorizeInfo);
//如果参数不为空,拼接参数
if (StringUtils.isNotEmpty(authorizeInfo)) {
authorizeInfo += "&payNotify=" + wechatConfig.getPayNotify() + "&cancelNotify=" + wechatConfig.getCancelNotify() + "&findNotifyUrl="
+ wechatConfig.getFindNotifyUrl();
redirectUri = redirectUri + "?" + authorizeInfo;
} else {
authorizeInfo += "payNotify=" + wechatConfig.getPayNotify() + "&cancelNotify=" + wechatConfig.getCancelNotify() + "&findNotifyUrl="
+ wechatConfig.getFindNotifyUrl();
redirectUri = redirectUri + "?" + authorizeInfo;
}
logger.info("-----微信授权地址2为:" + redirectUri);
//地址进行encode处理
redirectUri = URLEncoder.encode(redirectUri, "utf-8");
//封装授权地址
StringBuffer sb = new StringBuffer();
sb.append(WechatPaymentConstants.AUTHORIZE_URL);
sb.append("?appid=").append(wechatConfig.getWechatAppid());
sb.append("&redirect_uri=").append(redirectUri);
sb.append("&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect");
return sb.toString();
2---- response.sendRedirect(sb.toString());
}
- 根据code获取openid
(1)地址以及示例:https://api.weixin.qq.com/sns/oauth2/access_token?appid=wxfb5b168fe46a40d8&secret=17ce5aa1ded73c1dc5b8d3f702191180&code=021dQUqz0RD7ie1YwGsz0HzTqz0dQUqh&grant_type=authorization_code
(2) 返回参数格式:{“access_token”:“17_1PPobPecvywAZqmYJKMBghfH9kjqimxt7kveqPtVMhH3ydC_jNsGuG6atRaY9gWZNzfDFs68lfhbiTbw7kiW_3iDaZDMqHCFXy5pmAf-KBc”,“expires_in”:7200,“refresh_token”:“17_EqKT6GrLU6bX0j9sre1EI9MXU5Lsnsl-nvPwHW0WrizvlHSWhhNE9LC2Qq0j-wwiZ9EyjyDSeNCeT8tagwAYqjNvHdKajK7fcC_M1JjvYOo”,“openid”:“oak-OvzNbhO7J6v7dKpX4gpJUGes”,“scope”:“snsapi_base”}
public String getOpenidByCode(String code) throws Exception {
//封装请请求地址
StringBuffer sb = new StringBuffer();
sb.append(WechatPaymentConstants.TOKEN_URL);
sb.append("?appid=").append(wechatConfig.getWechatAppid());
sb.append("&secret=").append(wechatConfig.getWechatAppSecret());
sb.append("&code=").append(code);
sb.append("&grant_type=authorization_code");
logger.info("获取openid请求地址:" + sb.toString());
// 定义日志对象
//RequestInfoDTO tReportLogDTO = new RequestInfoDTO();
String accessInfo = HttpClientUtil.originalGetData(sb.toString());
logger.info("getOpenidByCode请求:" + accessInfo);
return accessInfo;
}
- 调用统一下单接口
(1).准备统一下单报文:
示例:<xml>
<appid>wx7ad792621769e980</appid>
<mch_id>1449823102</mch_id>
<device_info>WEB</device_info>
<nonce_str>PlTZGZL60eMed8ZAlocJfes1XsQeDNmD</nonce_str>
<sign>E4E92C23296BE4B84541E92F4039DF6B</sign>
<body>1分钱测试方案</body>
<out_trade_no>P00000326611</out_trade_no>
<fee_type>CNY</fee_type>
<total_fee>1</total_fee>
<spbill_create_ip>192.168.122.1</spbill_create_ip>
<notify_url>http://weixindev.fosun-uhi.com/sinopay-web-weixinpay/sinopay/wechatNotify/wechatNotify.do</notify_url>
<trade_type>JSAPI</trade_type>
<openid>oak-OvzNbhO7J6v7dKpX4gpJUGes</openid>
<limit_pay>no_credit</limit_pay>
</xml>
(2).下单返回报文:
<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[wx7ad792621769e980]]></appid><mch_id><![CDATA[1449823102]]></mch_id><device_info><![CDATA[WEB]]></device_info><nonce_str><![CDATA[kGBKwYesPbc1h73z]]></nonce_str><sign><![CDATA[882C7F27DAFD5341CF9A8848513CEC54]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[wx272122146318383044375e1f3027269817]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type></xml>
(3).根据返回的参数,组装拉起支付的参数
其中参数有签名参数,是对这几个字段签名:appId、nonceStr 随机字符串(RandomStringUtil.getRandomString32())、package(值为 prepay_id)、signType、timeStamp
(4).准备参数-签名:
WechatSignUtil.getSign();
getSign获取签名,加入到请求支付请求报文中
用WXPayUtil中的publicstatic String generateSignature(final Map<String, String> data, Stringkey)方法,key是
&nonceStr="", 这样的格式拼接参数
public WechatPaymentResultDTO wechatPrePayment(String payNo, String openid) throws SinoBaseCheckException, Exception {
// TODO Auto-generated method stub
logger.info("统一下单支付开始......");
PaymentInfoDetailDTO wPaymentInfoDetailDTO = getPaymentTradeForPayNo(payNo);
//准备统一下单参数
WechatPrePaymentRequestDTO wWechatPrePaymentRequestDTO = new WechatPrePaymentRequestDTO();
wWechatPrePaymentRequestDTO.setAppid(wechatConfig.getWechatAppid());//公众账号ID
wWechatPrePaymentRequestDTO.setMch_id(wechatConfig.getWechatMchid());//商户号
wWechatPrePaymentRequestDTO.setDevice_info("WEB");//设备号
wWechatPrePaymentRequestDTO.setNonce_str(RandomStringUtil.getRandomString32());//随机字符串
wWechatPrePaymentRequestDTO.setBody(wPaymentInfoDetailDTO.getTradeDesc());//商品描述
wWechatPrePaymentRequestDTO.setOut_trade_no(payNo);//商户订单号
wWechatPrePaymentRequestDTO.setFee_type("CNY");//货币类型
wWechatPrePaymentRequestDTO.setLimit_pay("no_credit");//限制信用卡支付
/*if (isLimitPay(wPaymentInfoDetailDTO)) {
wWechatPrePaymentRequestDTO.setLimit_pay("no_credit");//限制信用卡支付
}*/
String tPayAmount = amountTransUtil.transAmount(wPaymentInfoDetailDTO.getPayAmount());
wWechatPrePaymentRequestDTO.setTotal_fee(Integer.valueOf(tPayAmount));//总金额
wWechatPrePaymentRequestDTO.setSpbill_create_ip(IpUtil.getLocalIP());//终端IP
wWechatPrePaymentRequestDTO.setNotify_url(wechatConfig.getWechatNotifyUrl());//通知地址
wWechatPrePaymentRequestDTO.setTrade_type("JSAPI");//交易类型
wWechatPrePaymentRequestDTO.setOpenid(openid);//用户标识
String sign = WechatSignUtil.getSign(wWechatPrePaymentRequestDTO.toMap(), wechatConfig.getWechatKey());
wWechatPrePaymentRequestDTO.setSign(sign);//签名
wWechatPrePaymentRequestDTO.createXml();
//传输报文
String requestXML = wWechatPrePaymentRequestDTO.getXml();
logger.info("统一下单请求报文:" + requestXML);
//统一下单请求
String responseXML = HttpClientUtil.postJsonData(WechatPaymentConstants.ORDER_URL, requestXML, "utf-8");
logger.info("统一下单返回报文:" + responseXML);
//解析返回,并返回订单参数
XStreamComponent xStreamComponent = XStreamComponent.newInstance();
xStreamComponent.processAnnotations(WechatPrePaymentResultDTO.class);
WechatPrePaymentResultDTO tWechatPrePaymentResultDTO = (WechatPrePaymentResultDTO) xStreamComponent.fromXML(responseXML);
WechatPaymentResultDTO tWechatPaymentResultDTO = new WechatPaymentResultDTO();
if ("SUCCESS".equals(tWechatPrePaymentResultDTO.getResult_code()) && "SUCCESS".equals(tWechatPrePaymentResultDTO.getReturn_code())) {
String timeStamp = Long.toString(System.currentTimeMillis() / 1000);
String nonceStr = tWechatPrePaymentResultDTO.getNonce_str();
tWechatPaymentResultDTO.setAppId(tWechatPrePaymentResultDTO.getAppid());
tWechatPaymentResultDTO.setTimeStamp(timeStamp);
tWechatPaymentResultDTO.setNonceStr(nonceStr);
tWechatPaymentResultDTO.setSignType("MD5");
tWechatPaymentResultDTO.setPackages("prepay_id=" + tWechatPrePaymentResultDTO.getPrepay_id());
StringBuffer sb = new StringBuffer();
sb.append("appId=").append(tWechatPaymentResultDTO.getAppId());
sb.append("&nonceStr=").append(tWechatPaymentResultDTO.getNonceStr());
sb.append("&package=").append(tWechatPaymentResultDTO.getPackages());
sb.append("&signType=").append(tWechatPaymentResultDTO.getSignType());
sb.append("&timeStamp=").append(tWechatPaymentResultDTO.getTimeStamp());
String paySign = sb.toString() + "&key=" + wechatConfig.getWechatKey();
paySign = SecurityUtil.Md5(paySign).toUpperCase();
tWechatPaymentResultDTO.setPaySign(paySign);
tWechatPaymentResultDTO.setPayNo(payNo);
tWechatPaymentResultDTO.setReturnCode("000000");//
tWechatPaymentResultDTO.setPayAmount(wPaymentInfoDetailDTO.getPayAmount());
String signature = Signature(nonceStr, payNo, timeStamp);
tWechatPaymentResultDTO.setSignature(signature);
} else {
tWechatPaymentResultDTO.setReturnCode("111111");
tWechatPaymentResultDTO.setReturnDesc(tWechatPrePaymentResultDTO.getReturn_msg());
}
return tWechatPaymentResultDTO;
}
- 微信支付页面支付
支付需要的才能参数:
引入微信js:<script type="text/javascript" charset="UTF-8" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
function onBridgeReady() {
var appId = $('#appId').val();
var timeStamp = $('#timeStamp').val();
var nonceStr = $('#nonceStr').val();
var pk = $('#pk').val();
var paySign = $('#paySign').val();
var signature = $('#signature').val();
var payNo = $('#payNo').val();
var payAmount = $('#payAmount').val();
var payNotify = $('#payNotify').val();
var cancelNotify = $('#cancelNotify').val();
var findNotifyUrl = $('#findNotifyUrl').val();
var signType = $('#signType').val();
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId" : appId, //公众号名称,由商户传入
"timeStamp" : timeStamp, //时间戳,自1970年以来的秒数
"nonceStr" : nonceStr, //随机串
"package" : 'prepay_id=' + pk,
"signType" : signType, //微信签名方式:
"paySign" : paySign
//微信签名
}, function(res) {
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
if (res.err_msg == "get_brand_wcpay_request:ok") {
//微信返回的状态 成功:
var data = {
'returnCode' : 'SUCCESS',
'payNo' : payNo,
'payAmount' : payAmount
};
var notifyData = JSON.stringify(data);
$.ajax({
type : "POST",
url : findNotifyUrl,
dataType : "json",
data : {
"payNo" : payNo,
"notifyData" : notifyData
},
success : function(data) {
var notifyUrl = data.notifyUrl;
//alert(notifyUrl+"?data="+notifyData+"+++++++++++++ss++++++++++++++++++++++=");
notifyUrl = notifyUrl + "?data=" + notifyData;
var isEncode = data.isEncode;
if (isEncode == 'Y') {
notifyUrl = encodeURI(notifyUrl);
}
//alert(notifyUrl);
window.location.href = notifyUrl;
},
error : function() {
alert(error);
}
})
}
//支付失败
if (res.err_msg == "get_brand_wcpay_request:fail") {
var data = {
'returnCode' : 'FAIL',
'payNo' : payNo,
'payAmount' : payAmount
};
var notifyData = JSON.stringify(data);
//ajax请求后台,查询商户对应回调地址
$.ajax({
type : "POST",
url : findNotifyUrl,
dataType : "json",
data : {
"payNo" : payNo,
"notifyData" : notifyData
},
success : function(data) {
var notifyUrl = data.notifyUrl;
//alert(notifyUrl+"?data="+notifyData+"+++++++++++++ss++++++++++++++++++++++=");
notifyUrl = notifyUrl + "?data=" + notifyData;
var isEncode = data.isEncode;
if (isEncode == 'Y') {
notifyUrl = encodeURI(notifyUrl);
}
window.location.href = notifyUrl;
},
error : function() {
alert(error);
}
})
}
//支付取消
if (res.err_msg == "get_brand_wcpay_request:cancel") {
//alert("微信支付页面取消");
var data = {
'returnCode' : 'CANCEL',
'payNo' : payNo,
'payAmount' : payAmount
};
var notifyData = JSON.stringify(data);
$.ajax({
type : "POST",
url : cancelNotify,
dataType : "json",
data : {
"payNo" : payNo,
"notifyData" : notifyData
},
success : function(data) {
var notifyUrl = data.notifyUrl;
//alert(notifyUrl+"?data="+notifyData+"+++++++++++++ss++++++++++++++++++++++=");
notifyUrl = notifyUrl + "?data=" + notifyData;
var isEncode = data.isEncode;
if (isEncode == 'Y') {
notifyUrl = encodeURI(notifyUrl);
}
//alert("支付地址是:"+notifyUrl);
window.location.href = notifyUrl;
},
error : function() {
}
})
}
});
}
function toPay() {
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();
}
}
</script>
3. 业务流程图