想要调用微信支付第一就是获取openid:
获取openid的方法:
首先你需要在前台获取到code,
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
scope两种参数的说明:
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
在公众号的自定义菜单中设置的url为
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=URL&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect
其中APPID为公众号ID,URL是你要访问的网址,注意网页要进行URLEncoder编码。
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
code说明 :
code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
这样在前台就可以直接获取到url当中的code值,
var code = getQueryString("code");
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null)
return unescape(r[2]);
return null;
}
获取到了code的值后,就可以去请求openid了。
尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。
获取code后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
正确时返回的JSON数据包如下:
{
“access_token”:”ACCESS_TOKEN”,
“expires_in”:7200,
“refresh_token”:”REFRESH_TOKEN”,
“openid”:”OPENID”,
“scope”:”SCOPE”
}
这样就获取到了access_token和openid了。
有了openid,我们就可以去获取到pre_id。
获取pre_id的接口地址为:https://api.mch.weixin.qq.com/pay/unifiedorder
请求参数详见微信支付官方文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
其中注意attach参数,这个是附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。这个字段可以传我们的业务数据,用来后续的业务处理。
notify_url参数,这个回调地址一定要写外网能直接访问的地址(可以直接把网址放到浏览器上,看看是不是能直接访问),如果写内网地址是收不到回调信息的哦。
spbill_create_ip参数,这个参数笔者也不清楚应该怎么传,笔者就传的192.168.1.1,如果你们传这个不好使我要找我。
sign参数,详情见微信支付签名生成规则:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
其实微信给的demo中就有现成的sign生成方法
首先先把所有参数放入一个map中
public Map<String, Object> toMap()
{
Map<String, Object> map = new HashMap<String, Object>();
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields)
{
Object obj;
try
{
obj = field.get(this);
if (obj != null)
{
map.put(field.getName(), obj);
}
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
return map;
}
String sign = Signature.getSign(toMap());
public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();//自己保管的私钥
//Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result).toUpperCase();
//Util.log("Sign Result:" + result);
return result;
}
然后把这个sign放入参数中就可以啦。
请求成功后,微信会返回一段xml数据信息,例如:
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
<mch_id><![CDATA[10000100]]></mch_id>
<nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
<openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>
这里提供一个把XML转换成Map的方法:
public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {
//这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = Util.getStringStream(xmlString);
Document document = builder.parse(is);
//获取到document里面的全部结点
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
Map<String, Object> map = new HashMap<String, Object>();
int i=0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if(node instanceof Element){
map.put(node.getNodeName(),node.getTextContent());
}
i++;
}
return map;
}
到这里我们拿到了prepay_id,这里顺便说一下,如果是扫码支付前面的trade_type参数为NATIVE,再返回的时候会有一个code_url,在前台直接用这个url生成二维码图片就可以了。
下面继续说公众号支付。
前台H5调用微信支付的参数说明见:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
这里什么时间戳,随机字符串最后在服务端生成再传到前台来。
String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成签名的时间戳
String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串
注意package参数为prepay_id=PRERAY_ID一定要带上prepay_id=。
同时生成签名的时候也注意要带着prepay_id=,这块算个坑。
具体前台调起微信支付的代码直接复制微信文档
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"1395712654", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
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();
}
这里注意支付成功后那里的判断res.err_msg == “get_brand_wcpay_request:ok”,坑爹的是那里的:是中文的分号,我说回调咋一直都不进呢!!
还有补充,要注意在商户后台配置支付授权目录,否则支付页面是出不来的,我当时没配是一闪而过。
至此能够成功调起微信支付,支付成功后会向notify_url发送支付信息。例如:
<xml>
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
<attach><![CDATA[支付测试]]></attach>
<bank_type><![CDATA[CFT]]></bank_type>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[Y]]></is_subscribe>
<mch_id><![CDATA[10000100]]></mch_id>
<nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
<openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
<out_trade_no><![CDATA[1409811653]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
<sub_mch_id><![CDATA[10000100]]></sub_mch_id>
<time_end><![CDATA[20140903131540]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>
具体参数说明见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
其中attach参数就是传输的业务数据,这样就可以进行业务处理了。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13978.html