用北哥三個火槍手(yii2+houjs+yii2-wx)實現微信禮物打賞功能 --- 上部

有段時間沒有寫實戰類的文章了,今天分享一篇,使用yii2+houjs+yii2-wx實現微信送禮物功能。

先來個效果圖javascript

圖片描述

簡單點說就是點擊「送禮物」按鈕後出現一個彈出框,裏面有不少禮物,點擊某個禮物後彈出框刷新並出現一個二維碼,微信掃碼支付。php

固然這個錢會進入到會員的我的帳號內,而後提現。html

爲什麼要作這樣一個功能那? 說內心話我真心沒想過經過這個獲得多少,更多算一種激勵吧,若是你在咱們學習社羣分享了有價值的文章,你很是有可能受到個人禮物。java

好了,仍是說功能吧,功能有幾個git

  • 創建數據表結構(禮物以及送禮物日誌)
  • 使用houjs完成前臺彈出框
  • 使用yii2-wx實現支付二維碼
  • 爲用戶增長帳戶功能
  • 使用yii2-wx的企業付款到零錢和yii2的控制檯模式現實爲用戶打款功能(大於等於2元就打款)。

庫網址列表github

  1. houjs https://github.com/abei2017/y...
  2. yii2-wx https://github.com/abei2017/h...

我勒個去,乾貨滿滿呀。開始。json

數據表結構

既然是送禮品,那天然包含禮品表,還有送禮物的日誌表,我規劃以下。瀏覽器

禮物表gift服務器

圖片描述

禮物日誌表gift_log微信

圖片描述

對於 gift_log 表能夠不用lang_id,這裏爲了統計方便添加了這個字段。

總體思路

用戶送禮物的總體邏輯以下

  1. 點擊「送禮物」和後臺進行交互獲取此社羣禮物列表。
  2. 獲取數據後使用jsmart引擎渲染出具體禮物的html代碼。
  3. 使用modal將禮物列表放到彈出框彈出。
  4. 點擊禮物和後臺交互,後臺生成二維碼返回。
  5. 用戶掃碼付款。
  6. 付款成功。

獲取禮物列表

接下來咱們使用houjs來構建前臺功能,關於houjs的使用能夠去社羣查看 傳送門,咱們主要使用其modal彈出框助手和jsmart模板引擎。

首先指定一個按鈕

<button class="ui green button" id="giftBtn" data-url="<?= Url::to(['/gift/list','id'=>$lang->id]);?>">
    <i class="share icon"></i>送禮物
</button>

data-url表明用於獲取禮物列表的路由,爲按鈕作一個click事件處理

requirejs(['mods/modal','jSmart'],function(modal,jSmart){
    $('#giftBtn').click(function(){
        var url = $(this).attr('data-url');
        $.getJSON(url,{},function(d){
            if(d.result === 'ok'){
                // d.data
            }else{
                modal.msg(d.message);
            }
        });
    });
})

發起了一個請求用來獲取禮物列表,若是失敗則提示錯誤信息。

接下來咱們新建GiftController.php並定義list動做。

public function actionList($id){
    Yii::$app->response->format = 'json';
    try {
        $data = Gift::find()->where(['lang_id'=>$id])->asArray()->all();

        return ['result'=>'ok','data'=>$data];
    }catch(Exception $e){
        return ['result'=>'fail','message'=>$e->getMessage()];
    }
}

從這裏咱們知道如今點擊按鈕後得到的數據裏d.data就是此社羣的禮物列表。

當前臺獲得了禮物列表後,使用jsmart將數據轉換成html代碼,爲此咱們須要先作一個jsmart的模板,在送禮物按鈕頁面增長此模板。

<script id="giftTpl" type="text/x-jsmart-tmpl">
    <div class="gifts-box">
        <div class="gifts">
        {foreach $data as $key=>$gift}
            <a href="">
                <div class="gift-icon"><img src='{$gift.icon}'/></div>
                <div class="gift-name">{$gift.name}</div>
            </a>
        {/foreach}
        </div>
    </div>
</script>

這個模板的大致意思就是講過來的d.data中的數據進行循環,每一個禮物放到標籤a中,而後咱們向後臺獲取禮物列表的js代碼進行補充,以下。

requirejs(['mods/modal','jSmart'],function(modal,jSmart){
    $('#giftBtn').click(function(){
        var url = $(this).attr('data-url');
        $.getJSON(url,{},function(d){
            if(d.result === 'ok'){
                var tplText = $('#giftTpl').html();
                var compiledTemplate = new jSmart(tplText);
                var output = compiledTemplate.fetch(d);
                modal.alert(output,{
                    inPage:false,
                    title:'送禮物',
                    size:'tiny'
                });
            }else{
                modal.msg(d.message);
            }
        });
    });
})

進行模板渲染,到了咱們看效果的時候了。

圖片描述

我很喜歡,使用yii2和houjs的modal&jsmart,完成了禮物列表的功能。接下來咱們要作一個重要的事情,和後臺交互而且獲得支付二維碼。

獲得支付二維碼

在本章咱們使用yii2-wx擴展實現微信支付功能,其思路點擊禮物後獲取支付二維碼。

在進行以前咱們對上一步的js方法進行優化,將代碼放到一個單獨的js模塊中,在houjs中對於業務上的js代碼推薦放到houjs/js/modules中,以下

define(function(require,exports,modules){
    var modal = require('mods/modal');
    var jSmart = require('jSmart');

    exports.list = function(){
        $('#giftBtn').click(function(){
            var url = $(this).attr('data-url');
            $.getJSON(url,{},function(d){
                if(d.result === 'ok'){
                    var tplText = $('#giftTpl').html();
                    var compiledTemplate = new jSmart(tplText);
                    var output = compiledTemplate.fetch(d);
                    modal.alert(output,{
                        inPage:false,
                        title:'送禮物給做者',
                        size:'tiny'
                    });
                }else{
                    modal.msg(d.message);
                }
            });
        });
    };
});

所以獲取禮物列表的js代碼調用就變的簡單了,以下

requirejs(['modules/gift'],function(gift){
    gift.list();
})

之後關於gift的js代碼均可以放到houjs/js/modules/gift.js中。

好,仍是說本部分話題。如何獲取支付二維碼?

個人思路以下:用戶點擊每一個禮品後發起一次get請求到服務器,本次請求包含了禮物的ID,後臺收到後生成送禮物日誌並和微信服務器通信獲得支付二維碼,返回給瀏覽器,前臺渲染此二維碼。

說幹就幹。

首先補充禮物列表,每一個禮物的a連接,以下

<script id="giftTpl" type="text/x-jsmart-tmpl">
    <div class="gifts-box">
        <div class="gifts">
        {foreach $data as $key=>$gift}
            <a class="_get_qrcode" href="javascript:;" data-url="<?= Url::to(['/gift/qrcode']);?>?id={$gift.id}">
                <div class="gift-icon"><img src='{$gift.icon}'/></div>
                <div class="gift-name">{$gift.name}</div>
            </a>
        {/foreach}
        </div>
    </div>
</script>

咱們爲每一個禮物的a連接設置了3個屬性

  • class="_get_qrcode" 一個類,這個類並不起到樣式做用,主要是爲js監聽此標籤使用。
  • href="javascript:;" 防止點擊跳轉
  • data-url 點擊鏈接後,js函數將根據data-url提供的地址發起請求

接下來咱們作一個js方法實現a連接點擊的監聽,以下

// houjs/js/modules/gift.js
define(function(require,exports,modules){
    var modal = require('mods/modal');
    var jSmart = require('jSmart');
    
    .....

    /**
     * 獲取某一個禮物的支付二維碼
     */
    exports.qrcode = function(){
        $('._get_qrcode').click(function(){
            var url = $(this).attr('data-url');
            $.getJSON(url,{},function(d){
                if(d.result === 'ok'){
                    $('#payQrcode')
                        .html("<img width='120' src='"+d.qrcode+"'/>");
                }else{
                    modal.msg(d.message);
                }
            });
        });
    };
});

有一點要說明,所以禮物列表是在頁面dom渲染後加入的html代碼,所以若是想讓禮物列表的a連接被監聽,在獲取禮物列表成功後須要調用exports.qrcode()函數進行監聽,以下

// houjs/js/modules/gift.js
define(function(require,exports,modules){
    var modal = require('mods/modal');
    var jSmart = require('jSmart');
    
    exports.list = function(){
        $('#giftBtn').click(function(){
            var url = $(this).attr('data-url');
            $.getJSON(url,{},function(d){
                if(d.result === 'ok'){
                    ....

                    exports.qrcode();
                }else{
                    modal.msg(d.message);
                }
            });
        });
    };

    /**
     * 獲取某一個禮物的支付二維碼
     */
    exports.qrcode = function(){
        ....
    };
});

此刻用戶點擊了禮物的a連接,gift.qrcode()方法開始運做,請求達到了yii2的gift/qrcode動做,我寫了以下代碼。

// yii2 GiftController/actionQrcode
<?php
namespace app\controllers;

use app\models\Gift;
use app\models\GiftLog;
use yii\helpers\Url;
use abei2017\wx\Application;
use Da\QrCode\QrCode;
use Yii;
use yii\base\Exception;

class GiftController extends NBase {

    ....
        
    public function actionQrcode($id){
        Yii::$app->response->format = 'json';
        try {
            $model = Gift::findOne($id);

            // order
            $order = new GiftLog();
            $order->gift_id = $id;
            $order->user_id = Yii::$app->user->id;
            $order->created_at = time();
            $order->number = 1;
            $order->money = $order->number*$model->price;
            $order->status = 'unpay';
            $order->lang_id = $model->lang_id;
            if($order->save() == false){
                throw new Exception(implode(',',$order->getFirstErrors()));
            }

            $out_trade_no = "gift-{$order->id}-".rand(1000,9999);

            $totalFee = $order->money*100;

            $conf = Yii::$app->params['wx'];
            $app = new Application(['conf'=>$conf['mp']]);
            $pay = $app->driver("mp.pay");

            $attributes = [
                'body'=>"送禮物",
                'detail'=>"{$model->name}",
                'out_trade_no'=>$out_trade_no,
                'total_fee'=>$totalFee,
                'notify_url'=>Yii::$app->urlManager->createAbsoluteUrl(['/gift/notify']),
                'product_id'=>'gift-'.$id
            ];

            $native = $pay->native($attributes);

            $qrCode = (new QrCode($native['code_url']))->setSize(250)->setMargin(20);

            return ['result'=>'ok','qrcode'=>$qrCode->writeDataUri()];
        }catch(Exception $e){
            return ['result'=>'fail','message'=>$e->getMessage()];
        }
    }

}

首先要說明的是上述代碼沒有問題,但若是上線仍是要處理細節的。

在actionQrcode方法中咱們作了3件事情

  1. 生成送禮物日誌
  2. 調用yii2-wx生成支付二維碼
  3. 使用QrCode生成二維碼並傳給瀏覽器

這裏使用的是yii2-wx提供的生成二維碼方法native,剩下的事情就是如何顯示這個二維碼。

爲了讓用戶能夠在支付前從新選擇禮物,本次並無選擇彈出二維碼,而是使用了禮物頁面替換的方法,以下圖

圖片描述

在禮物的右側我增長了一個div來存放二維碼,沒有選擇的時候用一些幫助來填充。這個二維碼的存放工做由gift.qrcode()方法實現

$('#payQrcode').html("<img width='120' src='"+d.qrcode+"'/>");

對應的禮物列表模板也增長了支付區域

<script id="giftTpl" type="text/x-jsmart-tmpl">
    <div class="gifts-box">
        <div class="gifts">
        {foreach $data as $key=>$gift}
            <a class="_get_qrcode" href="javascript:;" data-url="<?= Url::to(['/gift/qrcode']);?>?id={$gift.id}">
                <div class="gift-icon"><img src='{$gift.icon}'/></div>
                <div class="gift-name">{$gift.name}</div>
            </a>
        {/foreach}
        </div>
        <div id="payQrcode">
            <h1>使用小提示</h1>
            <p>
                點擊左側的小禮物後會出現支付二維碼,掃碼即送。
            </p>
        </div>
        <div class="clear"></div>
    </div>
</script>

好,看下效果。

圖片描述

用戶拿手機支付

當用戶獲得支付二維碼後必然是掃碼支付,接下來有兩個事情要作

  • yii2要處理微信支付結果通知,將此禮物日誌設置爲已經支付。
  • 瀏覽器上次禮物列表二維碼消失,提示支付成功。

先來處理結果通知,這個使用yii2-wx很是好實現。在GiftController中增長一個notify動做。

// GiftController.php
<?php
namespace app\controllers;

use app\models\Gift;
use app\models\GiftLog;
use yii\data\ActiveDataProvider;
use yii\helpers\Url;
use abei2017\wx\Application;
use Da\QrCode\QrCode;
use Yii;
use yii\base\Exception;

class GiftController extends NBase {

    public $enableCsrfValidation = false;

    ......

    public function actionNotify(){
        $conf = Yii::$app->params['wx'];
        $app = new Application(['conf'=>$conf['mp']]);
        $pay = $app->driver("mp.pay");

        $response = $pay->handleNotify(function($notify,$isSuccess){
            if($isSuccess){
                @list($_,$id,$_) = explode('-',$notify['out_trade_no']);

                $model = GiftLog::findOne($id);
                if($model->status == 'pay'){
                    return true;
                }

                $model->status = 'pay';
                $model->paid_at = time();
                $model->transaction_id = $notify['transaction_id'];
                $model->update();

                return true;
            }
        });

        return $response;
    }

}

對上面的邏輯有幾點要注意,這也是咱們用yii2-wx的時候要注意的。

  • 關閉csrf驗證 主要是防止yii2將微信給咱們的結果通知請求屏蔽掉。
  • 在設置禮物日誌已付款前要判斷下,若是已經付款則返回true,這樣微信就不會再發請求。

如今咱們搞定了回調,看下效果。

圖片描述

不錯不錯

離成功愈來愈近了!接下來咱們要解決一個問題,就是當用戶支付後在瀏覽器上禮物列表的變化,我但願二維碼消失同時出現一個支付成功的頁面。

我須要一個輪詢,那麼開始吧,爲此我在gift.js中增長一個輪詢功能,這個功能在渲染出二維碼後被觸發。

//gift.js
exports.askIsPay = function(id){
    var url = '/gift/is-pay.html';
    $.getJSON(url,{id:id},function(d){
        if(d.result === 'ok'){
            $('#payQrcode').empty()
                .html("<h1>支付成功</h1><p>感謝您對做者的支持,他會知道您的名字以及打款。</p>");
        }else{
            setTimeout(function(){
                exports.askIsPay(id)
            },3000);
        }
    });
}

每3秒詢問一次服務器上gift/is-pay動做是否此送禮物日誌已經付款,固然要告訴是哪一個訂單,若是已經付款則改變div#payQrcode的內容,不然繼續調用exports.askIsPay(id)再一次詢問。一點注意的是咱們在生成二維碼的時候須要服務器將此日誌的id返回(這須要服務器的gift/qrcode動做返回此送禮物日誌的ID),當exports.askIsPay觸發時export.qrcode將其傳入。

...
if(d.result === 'ok'){
    $('#payQrcode').empty()
        .html("<img width='120' src='"+d.qrcode+"'/>");
    exports.askIsPay(d.oId);
}else{
    modal.msg(d.message);
}
...

固然咱們還要在服務器上新建一個控制器的動做。

// GiftController.php
public function actionIsPay($id){
    Yii::$app->response->format = 'json';
    try {
        $model = GiftLog::findOne($id);
        if($model->status == 'unpay'){
            throw new Exception('尚未支付');
        }

        return ['result'=>'ok'];
    }catch(Exception $e){
        return ['result'=>'fail','message'=>$e->getMessage()];
    }
}

大功告成,看看效果。

圖片描述

小結

到此咱們就完成了永不打賞禮物的全過程,算上部吧,下部咱們將實現具體的打款到用戶帳號以及使用yii2-wx調用微信企業付款到零錢包接口實現錢到微信功能。

教程中的內容未實現識別哪一個帖子或文章收到的打款,固然個人站點已經實現,你能夠試試哈。

更多Yii原創文章
相關文章
相關標籤/搜索