Tips:當你看到這個提示的時候,說明當前的文章是由原emlog部落格系統搬遷至此的,文章釋出時間已過於久遠,編排和內容不一定完整,還請諒解`
微信JSAPI支付
日期:2019-3-30 阿珏 折騰程式碼 瀏覽:1883次 評論:6條
前段時間一直在做微信相關的業務,雖說不是什麼新技術,但之前一直沒有機會接觸到,然後踩了些坑,抽空整理記錄下。微信支付一共分為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
JSAPI支付需要在微信中的瀏覽器開啟才能喚起微信支付,效果如下圖
附上介面程式碼
程式碼中使用了模板引擎
html:
<div class="am-modal-bd"> <img src="{$competition['avatar']}" alt=""><br> <span style="font-size: 13px;color: #72c6ef">{$competition['username']}</span><br> <span id="tips" style="font-size: 13px"></span> <ul class="ul_box"> <li> <div></div> <div class="label_box"> <label> <input type="radio" name="price" value="{$prices[0]}" checked=""> <div class="active"><span class="am-icon-diamond"></span> {$prices[0]}鑽</div> </label> <label> <input type="radio" name="price" value="{$prices[1]}"> <div><span class="am-icon-diamond"></span> {$prices[1]}鑽</div> </label> <label> <input type="radio" name="price" value="{$prices[2]}"> <div><span class="am-icon-diamond"></span> {$prices[2]}鑽</div> </label> </div> </li> <li> <div></div> <div class="label_box"> <label> <input type="radio" name="price" value="{$prices[3]}"> <div><span class="am-icon-diamond"></span> {$prices[3]}鑽</div> </label> <label> <input type="radio" name="price" value="{$prices[4]}"> <div><span class="am-icon-diamond"></span> {$prices[4]}鑽</div> </label> <label> <input type="number" name="price" id="price" class="input" placeholder="自定義"> </label> </div> </li> </ul> <span style="font-size: 13px">注:1鑽=1元,1鑽={$activity['offset']}票</span><br> <button type="button" class="am-btn am-btn-primary am-radius" onclick="callpay()" style="margin-top: 5px;">立即微信支付</button> </div>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) */
<?php /** * * 回撥基礎類 * @author widyhu * */ class WxPayNotify extends WxPayNotifyReply { /** * * 回撥入口 * @param bool $needSign 是否需要簽名輸出 */ final public function Handle($needSign = true) { $msg = "OK"; //當返回false的時候,表示notify中呼叫NotifyCallBack回撥失敗獲取簽名校驗失敗,此時直接回復失敗 $result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg); // 這是一個除錯技巧 // file_put_contents('a.txt',print_r($msg,true)); if ($result == false) { $this->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()); } }回撥類呼叫
<?php if(!defined('IN_DISCUZ')) { exit('Access Denied'); } require_once "./source/plugin/vote/class/WxpayAPI_php_v3/lib/WxPay.Api.php"; require_once "./source/plugin/vote/class/WxpayAPI_php_v3/lib/WxPay.Notify.php"; require_once "./source/plugin/vote/class/WxpayAPI_php_v3/example/WxPay.JsApiPay.php"; require_once './source/plugin/vote/class/WxpayAPI_php_v3/example/log.php'; //初始化日誌 // $logHandler= new CLogFileHandler("../logs/".date('Y-m-d').'.log'); // $log = Log::Init($logHandler, 15); // Log::DEBUG("begin notify"); $notify = new WxPayNotify(); $notify->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
網友評論:
蘇寶寶 2年前 (2019-04-06)
這要怎麼複製程式碼呀 我想實戰操作下 求博主給個程式碼的百度雲連結可否阿珏 2年前 (2019-04-07)
@蘇寶寶:微信官方給的SDK中有呼叫示例,文章中只是基本的html程式碼,複製去你也是沒辦法直接使用的,僅供參考
譯丶淺夏 2年前 (2019-04-03)
表情包怎麼整合的,能不能出個教程大佬阿珏 2年前 (2019-04-03)
@譯丶淺夏:這個有現成的外掛,可以去搜一下,但是由於每個部落格的模板都不一樣,得花點時間自己整上去
Ricky 2年前 (2019-03-31)
前提是要有個可以開通支付的微信公眾號
楊小杰部落格 2年前 (2019-03-30)
朕已閱