使用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为我节省了大量时间,强烈推荐。

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