前段时间一直在做微信相关的业务,虽说不是什么新技术,但之前一直没有机会接触到,然后踩了些坑,抽空整理记录下。
微信支付一共分为7种,分为是:付款码支付、JSAPI支付、Native支付、APP支付、H5支付、小程序支付、人脸支付。
此次业务中使用到的是微信JSAPI支付:用户通过微信扫码、关注公众号等方式进入商家H5页面,并在微信内调用 JSSDK完成支付
文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
SDK:[https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=11_1
](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=11_1)JSAPI支付需要在微信中的浏览器打开才能唤起微信支付,效果如下图

附上界面代码
代码中使用了模板引擎
html:
{$competition\['username'\]}
JavaScript:
// 投票 var offset = {$activity[‘offset’]}; $(’#tips’).html(‘正在给{$competition[‘code’]}号赠送{$prices[0]}钻=’+({$prices[0]}*offset)+‘票’); $(’#vote’).click(function(){ $.post(’/index/index/detailed.html?cid={$cid}&aid={$aid}’,{ formhash :’{FORMHASH}’, submit:‘1’, type:1, openid:’{$_G[‘member’][‘openid’]}’ },function(res){ alert(res.msg); if (res.code == 0) { $(’.box-1 span’).text(res.data.all); $(’.box-2 span’).text(res.data.rank); $(’.box-3 span’).text(res.data.up + ‘票’); } }); }) /* jQuery对象级别插件扩展 */ $.fn.extend({ /* 单选框 */ hlRadio:function () { var radioEl=$(this); radioEl.click(function () { var price = 0; price = $(‘input:radio:checked’).val(); $(’#price’).val(’’); $(’#tips’).html(‘正在给{$competition[‘code’]}号赠送’+price+‘钻=’+(price*offset)+‘票’); radioEl.siblings(“div”).removeClass(“active”); $(this).siblings(“div”).addClass(“active”); }); }, }); $(“input[name=‘price’]”).hlRadio(); $(’#price’).bind(‘input propertychange’, function(){ var price = 0; price = $(’#price’).val(); $(’#tips’).html(‘正在给{$competition[‘code’]}号赠送’+price+‘钻=’+(price*offset)+‘票’); })
CSS部分
.ul_box { margin:0 auto; padding:0; list-style:none; width: 344px; } .ul_box>li { padding:10px 10px 0 10px; overflow:hidden; border-bottom:#e5e5e5 solid 1px; } .ul_box>li:last-child { border-bottom:none; } .ul_box>li>div { float:left; } .ul_box>li>div:nth-child(1) { width:100px; } .ul_box>li>div:nth-child(2) { width:480px; overflow:hidden; } .label_box>label { display:block; float:left; margin:0 10px 10px 0; position:relative; overflow:hidden; } .label_box>label>input { position:absolute; top:0; left:-20px; } .label_box>label>div { width:100px; text-align:center; border:#dddddd solid 1px; height:40px; line-height:40px; color:#666666; user-select:none; overflow:hidden; position:relative; height: 75px; } .label_box>label>div.active{ border:#d51917 solid 1px; background-color: #fff9f8; color:#d51917; } .label_box>label>div.active:after { content:’’; display:block; width:20px; height:20px; background-color:#d51917; transform:skewY(-45deg); position:absolute; bottom:-10px; right:0; z-index:1; } .label_box>label>div.active:before { content:’’; display:block; width:3px; height:8px; border-right:#ffffff solid 2px; border-bottom:#ffffff solid 2px; transform:rotate(35deg); position:absolute; bottom:2px; right:4px; z-index:2; } .input{ height: 75px!important; border: 1px solid #DDD!important; position: initial!important; width: 100px!important; text-align: center!important; padding: 5px!important; font-size: 19px!important; } .am-modal-bd{ border-bottom: none; margin-top: 20px; } .am-modal-bd img{ width: 50px; border-radius: 50px; } .am-icon-diamond{ font-size: 20px; display: block; text-align: center; margin-bottom: -7px; }
唤起微信支付,此处的js代码仅在微信手机浏览器中生效
//调用微信JS api 支付 function jsApiCall(appId,timeStamp,nonceStr,package,signType,paySign) { WeixinJSBridge.invoke( ‘getBrandWCPayRequest’,{ “appId”:appId, //公众号名称,由商户传入 “timeStamp”:timeStamp, //时间戳,自1970年以来的秒数 “nonceStr”:nonceStr, //随机串 “package”:package, “signType”:signType, //微信签名方式: “paySign”:paySign //微信签名 }, function(res){ if(res.err_msg == “get_brand_wcpay_request:ok” ){ WeixinJSBridge.log(res.err_msg); alert(‘投票成功’); // window.history.go(-1); var url = ‘/index/index/detailed.html?cid={$cid}&aid={$aid}’; window.location.href=url; // location.reload(); return false; } alert(‘投票失败’); } ); } // 支付 function callpay() { var price = ‘’; if ($(’#price’).val()) { price = $(’#price’).val(); }else if($(‘input:radio:checked’).val()){ price = $(‘input:radio:checked’).val(); }else{ alert(‘请输入购买数量’); } if (Number(price) <= 0) { alert(‘请输入一个大于0的正数’); $(’#price’).val(’’); return false } $.post(’/index/index/weixin.html?cid={$cid}&aid={$aid}’,{ formhash :’{FORMHASH}’, submit:‘1’, type:1, pay:1, price: price, openid:’{$_G[‘member’][‘openid’]}’ },function(res){ if (res.code ==0) { jsApiCall( res.data.appId, res.data.timeStamp, res.data.nonceStr, res.data.package, res.data.signType, res.data.paySign, ); }else{ alert(res.msg); } }); // if (typeof WeixinJSBridge == “undefined”){ // if( document.addEventListener ){ // document.addEventListener(‘WeixinJSBridgeReady’, jsApiCall, false); // }else if (document.attachEvent){ // document.attachEvent(‘WeixinJSBridgeReady’, jsApiCall); // document.attachEvent(‘onWeixinJSBridgeReady’, jsApiCall); // } // }else{ // } }
界面基于妹子UI,效果如下图

PHP后端,创建本地订单
//①、获取用户openid $tools = new JsApiPay(); $openid = $tools->Getopenid(); //如果已经有了,可以直接赋值 //②、统一下单 $input = new WxPayUnifiedOrder(); $input->SetBody(“购买钻石票”); $input->SetAttach(json_encode([ //附加参数 ‘order’ => $order_id, ‘cid’ => $cid, ‘uid’ => $_G[‘uid’], ‘aid’ => $aid, ‘md5’ => $md5 ])); $input->SetOut_trade_no(WxPayConfig::MCHID.date(“YmdHis”)); $input->SetTotal_fee($price); //价格,单位分,“元记得*100” $input->SetTime_start(date(“YmdHis”)); $input->SetTime_expire(date(“YmdHis”, time() + 600)); $input->SetGoods_tag(“test”); $input->SetNotify_url(WX_NOTIFY_URL); $input->SetTrade_type(“JSAPI”); $input->Setopenid($openid); $order = WxPayApi::unifiedOrder($input); $jsApiParameters = $tools->GetJsApiParameters($order); //保存本地订单 DB::insert(‘vote_order’, [ ‘order’ => $order_id, ’time’ => time(), ‘price’ => intval($_GET[‘price’]), ‘uid’ => $_G[‘uid’], ‘openid’ => $openid, ‘ip’ => $ip ]); Json([‘code’ => 0, ‘msg’ => ‘成功’,‘data’ => json_decode($jsApiParameters,true)]); //获取共享收货地址js函数参数 // $editAddress = $tools->GetEditAddressParameters(); // debug($jsApiParameters); //③、在支持成功回调通知中处理成功之后的事宜,见 notify.php /** * 注意: * 1、当你的回调地址不可访问的时候,回调通知会失败,可以通过查询订单来确认支付是否成功 * 2、jsapi支付时需要填入用户openid,WxPay.JsApiPay.php中有获取openid流程 (文档可以参考微信公众平台“网页授权接口”, * 参考http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html) */
SetReturn\_code("FAIL"); $this->SetReturn\_msg($msg); $this->ReplyNotify(false); return; } else { //该分支在成功回调到NotifyCallBack方法,处理完成之后流程 $attach = json\_decode($msg\['attach'\], true); // file\_put\_contents('aaaa.txt',print\_r($attach,true)); $cid = $attach\['cid'\]; $aid = $attach\['aid'\]; $uid = $attach\['uid'\]; $order = $attach\['order'\]; $md5 = $attach\['md5'\]; $vote\_order = DB::fetch\_first("SELECT \* FROM " . DB::table('vote\_order') . " WHERE \`order\`='{$order}' and status = 0"); if (!$vote\_order) { $this->SetReturn\_code("SUCCESS"); $this->SetReturn\_msg("OK"); } $sign\_md5 = md5($vote\_order\['openid'\] . $vote\_order\['order'\] . $vote\_order\['uid'\] ); if ($sign\_md5 == $md5) { // 所有活动信息 $activity = DB::fetch\_first("SELECT \* FROM " . DB::table('vote\_activity') . " WHERE aid={$aid} "); $ip = $vote\_order\['ip'\]; $jewel\_vote = $vote\_order\['price'\] \* $activity\['offset'\]; // 投票 DB::query("UPDATE \`pre\_vote\_competition\` SET \`jewel\`=jewel+{$vote\_order\['price'\]},jewel\_vote=jewel\_vote+{$jewel\_vote} WHERE (\`cid\`='{$cid}')"); // 增加投票记录 DB::insert('vote\_record',\[ 'time' => TIMESTAMP, 'openid' => $vote\_order\['openid'\], 'type' => 1, 'aid' => $aid, 'cid' => $cid, 'ip' => $ip, 'price' => $vote\_order\['price'\], 'jewel\_vote' => $jewel\_vote, 'uid' => $uid, 'order' => $vote\_order\['order'\] \]); // 更新订单状态 DB::query("UPDATE \`pre\_vote\_order\` SET \`status\` = 1 WHERE \`order\`={$order}"); // 更新活动总收益 DB::query("UPDATE \`pre\_vote\_activity\` SET \`profit\`=profit+{$vote\_order\['price'\]} WHERE \`aid\`={$aid}"); } $this->SetReturn\_code("SUCCESS"); $this->SetReturn\_msg("OK"); } $this->ReplyNotify($needSign); } /\*\* \* \* 回调方法入口,子类可重写该方法 \* 注意: \* 1、微信回调超时时间为2s,建议用户使用异步处理流程,确认成功之后立刻回复微信服务器 \* 2、微信服务器在调用失败或者接到回包为非确认包的时候,会发起重试,需确保你的回调是可以重入 \* @param array $data 回调解释出的参数 \* @param string $msg 如果回调处理失败,可以将错误信息输出到该方法 \* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调 \*/ public function NotifyProcess($data, &$msg) { //TODO 用户基础该类之后需要重写该方法,成功的时候返回true,失败返回false return true; } /\*\* \* \* notify回调方法,该方法中需要赋值需要输出的参数,不可重写 \* @param array $data \* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调 \*/ final public function NotifyCallBack($data) { $msg = "OK"; $result = $this->NotifyProcess($data, $msg); if ($result == true) { $this->SetReturn\_code("SUCCESS"); $this->SetReturn\_msg("OK"); } else { $this->SetReturn\_code("FAIL"); $this->SetReturn\_msg($msg); } return $result; } /\*\* \* \* 回复通知 \* @param bool $needSign 是否需要签名输出 \*/ final private function ReplyNotify($needSign = true) { //如果需要签名 if ($needSign == true && $this->GetReturn\_code($return\_code) == "SUCCESS") { $this->SetSign(); } WxpayApi::replyNotify($this->ToXml()); } } 回调类调用 Handle(false); 注意:微信支付需要在后台配置一个微信支付授权目录,其中的APPID等参数在微信支付后台可获取到。 微信公众号信息配置 APPID:绑定支付的APPID(必须配置,开户邮件中可查看) MCHID:商户号(必须配置,开户邮件中可查看) KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置) 设置地址:https://pay.weixin.qq.com/index.php/account/api\_cert APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置), 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh\_CN 