前段时间公司要求开发微信公众号支付功能,在和XX大学合作的过程中需要帮助推广其精品课程,故开发了对其课程进行打赏的功能。此公众号为服务号。开发过程中遇到了多多少少的问题,虽然不是大问题但是总是浪费时间去排查,所以写了此文作为梳理。 --2016.10
一、微信支付
1.微信支付简介微信支付:
是集成在微信客户端的支付功能,用户可以通过手机完成快速的支付流程。微信支付以绑定银行卡的快捷支付为基础,向用户提供安全、快捷、高效的支付服务。
2.条件:
申请微信公众号支付有2个条件:
一个是已微信认证的服务号
一个是已微信认证的政府、媒体订阅号
3.微信支付模式
刷卡支付: 刷卡支付是用户展示微信钱包内的“刷卡条码/二维码”给商户系统扫描后直接完成支付的模式。主要应用线下面对面收银的场景。
扫码支付: 扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
公众号支付: 公众号支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。应用场景有:用户在微信公众账号内进入商家公众号,打开某个主页面,完成支付用户的好友在朋友圈、聊天窗口等分享商家页面连接,用户点击链接打开商家页面,完成支付将商户页面转换成二维码,用户扫描二维码后在微信浏览器中打开页面后完成支付
APP支付: APP支付又称移动端支付,是商户通过在移动端应用APP中集成开放SDK调起微信支付模块完成支付的模式。
二、开发流程
1.开发思路
1.1 配置
图1:申请支付功能
图2: 填写授权回调域名(安全起见,创造一个安全的支付环境)
图3: 填写授权目录
图4: 设置密钥(也是为了支付安全)
1.2 授权(此处针对课程进行打赏,并不要求用户关注公众号,因此使用静默授权)
2. 静默授权
关于网页授权的两种scope的区别说明
1)、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2)、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
https ://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
3.统一下单
商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
字段在return_code 和result_code都为SUCCESS的时候有返回prepay_id,微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时。
微信统一下单接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder
4.调起支付API
在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。
注意:WeixinJSBridge内置对象在其他浏览器中无效。
列表中参数名区分大小,大小写错误签名验证会失败。
三、遇到的问题
1.未添加测试者进白名单:需要在公众号里配置
2.body中文编码方式:uft-8
3. 支付完成回调(同步和异步)
/** * 提交打赏信息 */ function subRewardPrice(){ //获取打赏金额 var price = $("#rewardPrice").val(); //获取课程ID var courseId = $("#courseId").val(); if(null==price||price==""||parseFloat(price)<1){ $.msgbox({msg:"金额不得小于1元!",icon:"error",time:2000}); return; } // 支付方式: 1支付宝,2微信 var statement = $("#statement").val(); var openid = $("#openid").val(); $.ajax({ type:'post', url:'shippingBuyCourseMobile/rewardCourseMobile.htm', data:{ courseId:courseId, payAmount:price, statement:statement, openid:openid }, dataType:'json', success:function(data){ if(statement == "1"){ // 处理支付宝支付下单成功 $(".warp").after(data); var queryParam = ''; Array.prototype.slice.call(document.querySelectorAll('input[type=hidden]')).forEach(function (ele) { queryParam += ele.name + '=' + encodeURIComponent(ele.value) + '&'; }); var gotoUrl = document.querySelector('#alipay_form').getAttribute('action') + queryParam; _AP.pay(gotoUrl); }else if(statement == "2"){ // 处理微信支付下单成功 var returnCode = data.returnCode; var resultCode = data.resultCode; // 在return_code 和result_code都为SUCCESS的时候才返回prepay_id if(returnCode == "SUCCESS" && resultCode == "SUCCESS"){ if (typeof WeixinJSBridge == "undefined"){ if(document.addEventListener){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady(data), false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady(data)); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady(data)); } }else{ onBridgeReady(data); } } } } }); } //放重复提交变量 var submitFlag = 0; //打赏 微信支付 function onBridgeReady(data) { if(!submitFlag){ submitFlag++; }else { return; } var orderNo = data.orderNo; var courseId = $("#courseId").val(); WeixinJSBridge.invoke('getBrandWCPayRequest', { "appId" : data.appId, // 公众号名称,由商户传入 "timeStamp" : data.timeStamp, // 时间戳,自1970年以来的秒数 "nonceStr" : data.nonceStr, // 随机串 "package" : "prepay_id=" + data.prepayId, // 随机串 "signType" : "MD5", // 微信签名方式 "paySign" : data.paySign // 微信签名 }, function(res) { submitFlag = 0; // 把对象转字符串 // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 if (res.err_msg == "get_brand_wcpay_request:ok") { $.ajax({ type : 'post', url : 'shippingBuyCourseMobile/updateOrderStatus.htm', data : { orderNo : orderNo }, dataType : 'text', success : function(data) { window.location.href = "shippingCourseMobileSearch/shippingCourseMobileDetailIndex.htm?courseId="+courseId; } }); } else { alert("打赏失败!"); } }); }