使用Yii2+ajax實現無跳轉“公衆號微信支付”解決方案 - EasyWechat版

爲了閱讀不累,我們仍然以故事的形式展開,本次我們服務的客戶是一個手機充值店老闆,他有一個公衆服務號,我們要爲其實現微信瀏覽器內支付功能。

客戶給了我們一個原型圖,是下面的樣子。

alt

需求並不複雜

  • 點擊支付按鈕直接彈出如圖二的支付頁面,注意:不進行頁面跳轉。
  • 輸入密碼支付成功。跳轉到圖三的成功支付頁面。

準確的說就兩個頁面。

關鍵點普及

首先我對微信支付是有大概瞭解的(官方文檔),爲了將每個場景統一,微信支付提出了一個叫做 預支付交易單 的概念。

商戶系統先調用接口在微信支付服務後臺生成預支付交易單,返回正確的預支付交易回話標識後再按掃碼、JSAPI、APP等不同場景生成交易串調起支付。

而客戶要求的通過微信瀏覽器這種場景發起的支付則屬於JSAPI支付模式,如果你對各種場景下對應的支付模式不清楚,可以看下下面的表格,這將對我們以後開發各種微信支付很有好處。

JSAPI--公衆號支付、NATIVE--原生掃碼支付、APP--APP支付
使用場景支付模式
PC網站NATIVE
微信瀏覽器JSAPI
門店掃碼NATIVE / JSAPI
手機應用APPAPP

一些配置工作

想讓JSAPI支付類型的場景生效,我們需要對公衆號進行一些設置。

首先進入公衆號的微信支付頁面設置公衆號授權目錄

alt

注意:發起支付請求的鏈接地址,都必須在支付授權目錄之下。

其次我們需要4個配置參數(AppID、AppSecret、merchant_id、key),merchant_id是你的商戶號、key是你的支付key(在支付平臺可以找到)。

我準備好了這些,現在開始編碼~

開始啦

我設計了一個控制器 ChargeController

namespace app\modules\wechat\controllers;

use Yii;
use yii\web\Controller;

class ChargeController extends Controller {

    /**
     * 第一個頁面
     */
    public function actionIndex(){
        return $this->render('index');
    }
}

對應視圖

// http://abc.com/wechat/charge-index.html
<h1>¥49.95</h1>
<div>
    <button>點擊支付</button>   
</div>

現在要實現點擊button後調出微信支付,對此我不懼怕,因爲微信官方已經提供了相應文檔 - 微信內H5調起支付,說白了就是一組能被微信瀏覽器識別的js代碼,我們將含有AppId及預支付交易回話標識等傳進去後微信支付就蹦出來了。

而這個過程一般是在頁面加載過程中這些特殊的js代碼就跟着添加了,這顯然和當前需求有出入,我們要做的是點擊支付後才調用js代碼。

好,那就用ajax來實現它。

看來我要做3步事情,點擊按鈕後

  • 通過ajax在服務器上生成一個類似充值訂單的記錄(狀態爲未支付),同時將一組已經擁有了正確參數的js代碼返給瀏覽器。
  • ajax接收了js代碼,並且加載,支付彈出等等等等。
  • 服務器端要有一個actionNotify方法接收微信服務器的異步通知,將上面的訂單設置爲已支付。

我們說做就做,開始重寫視圖

// charge/index.php
<h1>¥49.95</h1>
<div id="wxJs"></div>
<div>
    <button id="payBtn">點擊支付</button>   
</div>

<script type="text/javascript">
    $('#payBtn').click(function(){
        var url = "/index.php?wechat/charge/pay";
        $.getJSON(url,{},function(d){
            if(d.done == true){
                $('#wxJs').html(d.data);
            }else{

            }
        });
    });
</script>

wxJs就是用來存放服務器返回的那個可以調起微信支付的js代碼,如果你還看不懂,那麼阿北給你一個看圖說話版再。

alt

至關重要的pay

通過上面的編寫我們實現了不跳轉頁面彈出微信支付佈局,現在我們來編寫這個至關重要的 actionPay 函數。

// ChargeController
use EasyWeChat\Foundation\Application;
use EasyWeChat\Payment\Order;
...
/**
 * 該函數被前臺的button觸發
 **/
public function actionPay(){
    $charge = new Charge();
    // 刷刷刷一堆代碼,就生成了未付款訂單。

    // 通過EasyWechat來調用
    $config = Yii::$app->params['WECHAT'];

    $wxApp = new Application($config);
    $payment = $wxApp->payment;

    $notifyUrl = Yii::$app->request->getHostInfo() . Url::to(['/wechat/charge/notify']);
    $attributes = [
        'trade_type'=>Order::JSAPI,
        'body'=>"商品描述",
        'detail'=>"商品詳情",
        'out_trade_no'=>$charge->number,
        'total_fee'=>$charge->money*100,
        'notify_url'=>$notifyUrl,
        'openid'=>$this->wxLogin->open_id 
    ];

    $order = new Order($attributes);
    $result = $payment->prepare($order);
    if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS'){
        $prepayId = $result->prepay_id;
        $json = $payment->configForPayment($prepayId);

        $html = $this->renderPartial('_wxpay',[
            'json'=>$json,
            'charge'=>$charge
        ]);

        echo Json::encode(['done'=>true,'data'=>$html]);

    }
}
...

EasyWechat對微信支付進行了很好的封裝,我對代碼裏關鍵點進行說明

  • $payment 是EasyWechat對微信支付的封裝對象。
  • $attributes 是我們要傳遞給微信服務器的訂單信息,用來獲取 預支付交易會話標識prepayId的。
  • $payment->prepare是具體和微信服務器獲取prepayId的功能實現
  • $json 是$payment->configForPayment接收$prepayId後生成的json字符串,這個字符串傳給特殊js代碼就能調其微信支付。

關於EasyWechat對微信支付更多封裝信息情況文檔 https://easywechat.org/zh-cn/docs/payment.html

我想你也看到了下面的代碼

$html = $this->renderPartial('_wxpay',[
    'json'=>$json,
    'charge'=>$charge
]);

_wxpay視圖就是那種特殊js代碼模板,我使用renderPartial函數得到傳遞完$json的js代碼,而renderPartial的作用是不加載任何佈局文件並且將其返回給變量 $html

再看一眼_wxpay的實現

use yii\helpers\Url;
<script type="text/javascript">
    function jsApiCall() {
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest',
            <?= $json ?>,
            function(res){
                if(res.err_msg === 'get_brand_wcpay_request:ok'){
                    window.location.href = "<?= Url::to(['/wechat/charge/result','id'=>$charge->id]);?>";
                }else if(res.err_msg === 'get_brand_wcpay_request:cancel'){
                    weui.alert('支付被取消');
                }else if(res.err_msg === 'get_brand_wcpay_request:fail'){
                    weui.alert('支付失敗');
                }
            }
        );
    }

    function callpay() {

        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{

            jsApiCall();
        }
    }

    callpay();

</script>

請對比官方文檔 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 中的js代碼,其實他們是一樣的。

好的,現在$html代碼傳遞給了微信瀏覽器,如圖所示,微信支付框出來了。

alt

支付後的事情

既然支付都彈出來了,那麼我就輸入了支付密碼,成功後跳轉到結果頁面,看下_wxpay視圖中的這段代碼

alt

支付完跳轉到 ?r=wechat/charge/result,actionResult實現很簡單

public function actionResult($id){
    $model = Charge::findOne($id);
    return $this->render('result',[
        'model'=>$model
    ]);
}

這個action實現支付結果,那麼我們的服務器如何知道用戶已經支付成功了那?還是說中途放棄。

還記得前面代碼中我們設置的 $notifyUrl 麼?這貨就是幹這個的。

// $notifyUrl = Yii::$app->request->getHostInfo() . Url::to(['/wechat/charge/notify']);
public function actionNotify(){
    $config = Yii::$app->params['WECHAT'];
    $wxApp = new Application($config);
    $payment = $wxApp->payment;
    $response = $payment->handleNotify(function ($notify, $successful){
        if ($successful) {
            $order_arr = json_decode($notify, true);
            $transactionId = $order_arr['transaction_id'];
            // $order_arr就是微信異步通知給服務器的信息

            //todo 我們的邏輯,將charge變爲已支付
        }
    });
    $response->send();
}

有一點要注意,微信服務器的異步通知是POST請求,而Yii2對POST默認使用了csrf驗證,爲了能接收到信息,請在ChargeController控制器裏設置如下代碼

public $enableCsrfValidation = false;

否則收不到通知哦,這點一定要記得。

到此爲止我就完成了客戶的需求,使用EasyWechat爲我節省了大量時間,強烈推薦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章