yii2嵌入微信公衆號支付

序言javascript

隨着微信被愈來愈多的人使用,微信商城成爲現在的熱門。每個商城都須要有本身的支付方式,微信商城也不例外。微信公衆號支付就是微信商城的一種支付方式,微信支付隨着微信的推廣使用也被普遍應用。今天我主要講的是yii2嵌入微信公衆號支付,微信官網的微信支付文檔比較簡潔,接下來跟着我來看一下yii2如何嵌入微信公衆號支付,以及須要注意的事項吧!php

前期準備工做html

首先必須有一個微信公衆號,而且須要在微信公衆平臺申請微信支付。微信公衆平臺的地址是:https://mp.weixin.qq.com/cgi-bin/loginpage。申請的過程我這邊就不寫了,錄公衆平臺之後就能夠看到。
其次須要先配置一個域名,必須是80端口,而後在申請完微信支付之後配置上支付受權目錄。
準備工做完成之後咱們能夠開始開發了。java

代碼分析git

一、咱們須要獲取到關注微信公衆號的人的openid。獲取openid,我這邊是經過網頁受權獲取用戶基本信息接口獲取的。其實在github上已經有封裝好的關於微信的開發的接口,咱們能夠直接從上面下載,之後基於yii2的開發微信的其餘的功能能夠使用。網址是:https://github.com/callmez/yii2-wechat-sdk 你們能夠下載而後安裝。個人openid的獲取也是在此基礎上更改的。安裝完成之後的目錄結構以下圖:
圖片描述github

我這邊使用的是MpWechat.php,在MpWechat.php中我增長了一個方法獲取openid。web

public function getOpenid($turl)
    {
        if (!isset($_GET['code'])){
            //觸發微信返回code碼
            
             $url=$this->getOauth2AuthorizeUrl($turl, $this->state, 'snsapi_userinfo');
            Header("Location: $url");
            exit();
        } else {
            //獲取code碼,以獲取openid
            $code = $_GET['code'];
            $access_info = $this->getOauth2AccessToken($code);
            return $access_info;
        }
        
    }

$turl是獲取到code之後須要跳轉的地址。
在控制其中獲取openid我是這樣寫的:ajax

public function actionView($id)
    {
        
        $openid=isset($_COOKIE['openid'])?$_COOKIE['openid']:'';
            
            if(empty($openid))
            {
                $t_url=Url::to(['@web/clue/view?id='.$id],true);//這個是獲取到code之後須要跳轉的url。Url這個是在頭部須要增長:use yii\helpers\Url
                $info=$this->wechat->getOpenid($t_url);//注:$this->wechat安裝完成yii2-wechat-sdk之後配置之後能夠使用
                if($info){
            setcookie('access_token',$info['access_token'],time()+86400*365);
                  setcookie('openid',$info['openid'],time()+86400*365);    
                  
                }
                
            }
        
    }
將獲取到的openid放入的cookie中儲存。

二、openid獲取之後能夠進行開發微信支付,我這邊將微信支付封裝成了一個類,能夠配置訪問。json

第一步、咱們能夠在common/config/main.php中配置api

return [
    'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
    'language' => 'zh-CN', // 啓用國際化支持
    'sourceLanguage' => 'zh-CN', // 源代碼採用中文
    'timeZone' => 'Asia/Shanghai', // 設置時區
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        'user' => [            
            'identityClass' => 'login\models\User',
            'enableAutoLogin' => false,  //開啓authTimeout失效
            'identityCookie' => ['name' => '_identity', 'httpOnly' => true,'domain' => '.' . DOMAIN],
            // 'returnUrl'=>'//' . DOMAIN_HOME,
            'authTimeout' => 24*3600*30, // 30 days 
        ],        

        'session' => [
            'timeout' => 24*3600*30, // 30 days
            'useCookies' => true,
            'cookieParams' => [
                'domain' => '.' . DOMAIN,
                'lifetime' => 24*3600*30,
            ],
        ],
        //這個是支付的
        'payment' => [
            'class'=>'common\widgets\payment\Instance',
                
                'weixinjspi_config' => [
                    'code'      => 2,
                    'appid'     => 'wx88ee3ca8c06be5c6',//微信的appid
                    'secret'    => '48180f87c2693d50b29d822d019999',//appsecret,在申請完公衆號之後能夠看到
                    'mch_id'    => '13260614455',//商戶號
                    'key'       => '16ceshi',//key須要設置
                    'cert_path' => '',//能夠不用填寫
                    'key_path'  => '',//能夠不用填寫
                ],
               
        ],
    ],

我上面的appid、key等都是錯誤信息須要大家根據本身申請的微信支付進行填寫
第二步、在commonwidgets中加入payment文件夾,在文件夾中增長Instance.php

<?php

namespace common\widgets\payment;

use yii\base\Component;

class Instance extends Component{
    private $weixin_config;
    private $weixins_config;
    private $weixinjspi_config;
    private $alipay_config;
    private $balance_config;
    private $_weixin = null;
    private $_weixins = null;
    private $_alipay = null;
    private $_balance = null;
    private $_weixinjspi = null;

    public function setWeixin_config($config)
    {
        $this->weixin_config = $config;
    }
    public function setWeixins_config($config)
    {
        $this->weixins_config = $config;
    }
    public function setWeixinjspi_config($config)
    {
        $this->weixinjspi_config = $config;
    }
    public function setAlipay_config($config)
    {
        $this->alipay_config = $config;
    }
    public function setBalance_config($config)
    {
        $this->balance_config = $config;
    }

    /**
     * 組合使用餘額支付和其餘支付
     * @param $order
     * @param $payment
     * @param $balance
     */
    public function unionPay($order,$payment,$balance,$notify)
    {
        $res = [];
        $total_fee = $order['total_fee'];
        if($balance > 0)
        {
            $pay = $this->getBalance();
            if($balance >= $total_fee)
            {
                $res['balance'] = $pay->pay($order);
                return $res;
            }
            else
            {
                $order['total_fee'] = $balance;
                $res['balance'] = $pay->pay($order);
                $total_fee -= $balance;
            }
        }
        $order['total_fee'] = $total_fee;
        $pay = $this->$payment;
        $pay->setNotifyUrl($notify);
        $res[$payment] = $pay->prepay($order);
        //$res[$payment]['balance'] = $balance;
        return $res;
    }

    /**
     * 得到支付寶支付
     * @param null $notify_url
     * @return mixed|object
     */
    public function getAlipay($notify_url = null)
    {
        $this->alipay_config = array_merge(['class'=>Alipay::className()],$this->alipay_config);
        //'var_dump($this->alipay_config);exit;
        return $this->_getPayment('_alipay',$notify_url);
    }

    /**
     * 得到微信app c端支付
     * @param null $notify_url
     * @return mixed|object
     */
    public function getWeixin($notify_url = null)
    {
        $this->weixin_config = array_merge(['class'=>Weixin::className()],$this->weixin_config);
        return $this->_getPayment('_weixin',$notify_url);
    }
    /**
     * 得到手機微信端支付
     * @param null $notify_url
     * @return mixed|object
     */
    public function getWeixinjspi($notify_url = null)
    {
        $this->weixinjspi_config = array_merge(['class'=>Weixinjspi::className()],$this->weixinjspi_config);
        return $this->_getPayment('_weixinjspi',$notify_url);
    }

    /**
     * 得到微信app s端支付
     * @param null $notify_url
     * @return mixed|object
     */
    public function getWeixins($notify_url = null)
    {
        $this->weixins_config = array_merge(['class'=>Weixin::className()],$this->weixins_config);
        return $this->_getPayment('_weixins',$notify_url);
    }

    /**
     * 得到餘額支付
     * @return mixed|object
     */
    public function getBalance()
    {
        $this->balance_config = array_merge(['class'=>Balance::className()],$this->balance_config);
        return $this->_getPayment('_balance',null);
    }

    /**
     * @param $name
     * @param $notify_url
     * @return mixed|object
     * @throws \yii\base\InvalidConfigException
     */
    private function _getPayment($name, $notify_url)
    {
        $config = substr($name.'_config',1);
        if(is_null($this->{$name}))
        {
            $this->{$name} = \Yii::createObject($this->{$config});
        }
        if(!is_null($notify_url))
        {
            $this->{$name}->setNotifyUrl($notify_url);
        }
        return $this->{$name};
    }
}

這個類實際上是結合了其餘的支付方式,其中如今最主要的是getWeixinjspi($notify_url = null)這個方法。
除了這個還須要增長Weixinjspi.php

<?php
namespace common\widgets\payment;

use Yii;
use yii\base\InvalidConfigException;
use yii\base\InvalidValueException;

class Weixinjspi extends Payment
{
    public $order_url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
    public $search_order_url = 'https://api.mch.weixin.qq.com/pay/orderquery';
    public $close_order_url = 'https://api.mch.weixin.qq.com/pay/closeorder';
    public $refund_url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
    public $notify_url;
    public $search_refund_url = 'https://api.mch.weixin.qq.com/pay/refundquery';
    public $order_pre = 'Weixin';

    private $appid;
    private $mch_id;
    private $key;
    private $key_path;
    private $secret;
    private $cert_path;
    private $notify_data;
    private $curl_proxy_host;
    private $curl_proxy_port;
    
    public function setAppid($appid)
    {
        $this->appid = $appid;
    }

    public function setKey($key)
    {
        $this->key = $key;
    }

    public function setSecret($secret)
    {
        $this->secret = $secret;
    }

    public function setKey_path($key_path)
    {
        $this->key_path = Yii::getAlias($key_path);
    }
    public function setCert_path($cert_path)
    {
        $this->cert_path = Yii::getAlias($cert_path);
    }

    public function setMch_id($mch_id)
    {
        $this->mch_id = $mch_id;
    }

    public function init()
    {
        parent::init();
        $needs = array('appid','mch_id','key','secret');
        foreach($needs as $need)
        {
            if(empty($this->{$need}))
            {
                throw new InvalidConfigException(get_class($this) . " must define weixin's params {$need}.");
            }
        }
    }

    private function _checkRefund()
    {
        $needs = array('key_path','cert_path');
        foreach($needs as $need)
        {
            if(empty($this->{$need}))
            {
                throw new InvalidConfigException(get_class($this) . " must define weixin's params {$need}.");
            }
        }
    }

    /**
     * jsapi支付接口
     * @param $order
     * @return mixed
     */
    public function pay($order)
    {
        $paras = [
            'body'              =>$order['goods_desc'],//設置商品或支付單簡要描述
            'attach'            =>'PC',//設置附加數據,在查詢API和支付通知中原樣返回,該字段主要用於商戶攜帶訂單的自定義數據
            'out_trade_no'      =>$this->order_pre.$order['order_sn'],//訂單號
            'total_fee'         =>(int)($order['total_fee'] * 100),//訂單總金額
            'detail'            =>$order['body'],
            'time_start'        =>empty($order['time_start'])? '':$order['time_start'],
            'time_expire'       =>empty($order['time_expire'])? '':$order['time_expire'],
            'goods_tag'         =>empty($order['goods_tag'])? '':$order['goods_tag'],//設置商品標記,代金券或立減優惠功能的參數,說明詳見代金券或立減優惠
            'notify_url'        =>empty($order['notify_url'])? '':$this->setNotifyUrl($order['notify_url']),//回調地址
            'trade_type'        =>'JSAPI',
            'product_id'        =>empty($order['order_sn'])? '':$order['order_sn'],
            'openid'            =>empty($order['openid'])? '':$order['openid'],
            'nonce_str'         =>$this->randomStr(),
           
        ];
        ///var_dump($paras);exit;
        $timeout = empty($order['timeout'])? 6 :$order['timeout'];
        if($order['total_fee'] <= 0)
        {
            throw new InvalidValueException(get_class($this) . " 支付金額必須大於0");
        }
        $trade = $this->createOrder($paras,$timeout);
        
         if(isset($trade['return_code']) && $trade['return_code'] == 'SUCCESS')
        {
            if(isset($trade['result_code']) && $trade['result_code'] == 'SUCCESS')
            {
                
                $data=[
                    'appId'    =>$trade['appid'],
                    'timeStamp'=>"".time()."",
                    'nonceStr' =>$this->randomStr(),
                    'signType'=>'MD5',
                    'package'  =>'prepay_id='.$trade['prepay_id'],    
                ];
                $data['paySign']=$this->sign($data);
                //$data['signature']=$order['signature'];
                $parameters = json_encode($data);
               return $parameters;//二維碼的信息
            }
            else
            {
                
                return false;
            }
        }
        else
        {
            return false;
        }
    }
 
    public function getJs($parameters,$order_sn)
    {
        
        $str="<script type=\"text/javascript\">
    //調用微信JS api 支付
    function jsApiCall()
    {
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest',
            ".$parameters.",
            function(res){
                
                 if(res.err_msg == \"get_brand_wcpay_request:ok\")
                 {
                     $.ajax({
                            type:\"get\",
                            url:\"/clue/pay_status?order_sn=".$order_sn."\",
                            datatype:\"json\",
                            data:{},
                            success: function(data_)
                            {
                               window.location.reload();
                   
                            }
                        })
                     alert('支付成功!');
                    
                 }else if(res.err_msg == \"get_brand_wcpay_request:cancel\")
                 {
                     
                     alert('支付過程當中用戶取消!');
                     
                 }else{
                     
                    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();
        }
    }
    </script>
        ";
        return $str;
    }
    /**
     * 預支付接口,在APP上發起支付
     * @param $order
     * @return mixed
     */
    public function prepay($order)
    {
        $needs = array('title','order_sn','body','total_fee');
        foreach($needs as $need)
        {
            if(!isset($order[$need]))
            {
                throw new InvalidConfigException(get_class($this) . " \$order 中必須包含鍵 {$need}.");
            }
        }
        $paras = [
            'trade_type'        =>'APP',
            'time_start'        =>empty($order['time_start'])? '':$order['time_start'],
            'time_expire'       =>empty($order['time_expire'])? '':$order['time_expire'],
            'goods_tag'         =>empty($order['goods_tag'])? '':$order['goods_tag'],
            'device_info'       =>empty($order['device_info'])? '':$order['device_info'],
            'out_trade_no'      =>$this->order_pre.$order['order_sn'],
            'detail'            =>$order['body'],
            'total_fee'         =>(int)($order['total_fee'] * 100),
            'body'              =>$order['title'],
            'fee_type'          =>empty($order['fee_type'])? '':$order['fee_type'],
            'product_id'        =>empty($order['product_id'])? '':$order['product_id'],
            'openid'            =>empty($order['openid'])? '':$order['openid'],
            'attach'            =>empty($order['attach'])? '':$order['attach'],
            'notify_url'        =>empty($order['notify_url'])? '':$this->setNotifyUrl($order['notify_url']),//回調地址
        ];
        $timeout = empty($order['timeout'])? 6 :$order['timeout'];
        if($order['total_fee'] <= 0)
        {
            throw new InvalidValueException(get_class($this) . " 支付金額必須大於0");
        }
        $trade = $this->createOrder($paras,$timeout);
        if(isset($trade['return_code']) && $trade['return_code'] == 'SUCCESS')
        {
            if(isset($trade['result_code']) && $trade['result_code'] == 'SUCCESS')
            {
                $trade['total_fee'] = $order['total_fee'];
                return $this->getSign($trade);
            }
            else
            {
                throw new InvalidValueException(get_class($this) . $trade['err_code_des']);
            }
        }
        else
        {
            throw new InvalidValueException(get_class($this) . $trade['return_msg']);
        }
    }

    public function getSign($order)
    {
        $total_fee = $order['total_fee'];
        $keys = ['appid','partnerid','prepayid','package','noncestr','timestamp','sign'];
        $order['partnerid'] = $order['mch_id'];
        $order['prepayid'] = $order['prepay_id'];
        $order = array_intersect_key($order,array_fill_keys($keys,''));
        $order['package'] = 'Sign=WXPay';
        $order['timestamp'] = time();
        $order['noncestr'] = $this->randomStr(30);
        $order['sign'] = $this->sign($order);
        $order['total_fee'] = $total_fee;
        return $order;
    }

    public function notify()
    {
        //獲取通知的數據
        $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
        //若是返回成功則驗證簽名
        //try {
            $result = $this->fromXml($xml);
            if($result['return_code'] == 'SUCCESS')
            {
                $sign = $this->sign($result);
                if($sign == $result['sign'])
                {
                    $result['trade_no']=$result['transaction_id'];
                    $result['out_trade_no']=substr($result['out_trade_no'],strlen($this->order_pre));
                    $result['trade_status']='TRADE_SUCCESS';
                    return $result;
                }
                else
                {
                    return false;
                }
            }
            else
            {
               return false;
            }
       // } catch (\Exception $e){
       //     throw new InvalidValueException(get_class($this) . $e->errorMessage());
       // }
    }

    /**
     * 退款接口
     * @param $order
     * @return mixed
     */
    public function refund($order)
    {
        $this->_checkRefund();
        $needs = array('order_sn','total_fee');
        foreach($needs as $need)
        {
            if(!isset($order[$need]))
            {
                throw new InvalidConfigException(get_class($this) . " \$order 中必須包含鍵 {$need}.");
            }
        }
        $order['out_trade_no'] = $this->order_pre.$order['order_sn'];
        $order['total_fee'] = round($order['total_fee'],2) * 100;
        $order['refund_fee'] = $order['total_fee'];
        $order['op_user_id'] = $this->mch_id;

        $need = array('out_trade_no','out_refund_no','total_fee','refund_fee','op_user_id');
        $keys = ['device_info','refund_fee_type','transaction_id'];
        foreach($need as $key)
        {
            if(empty($order[$key])) {
                throw new InvalidConfigException("缺乏退款申請接口必填參數{$key}!");
            }
        }

        $order = array_intersect_key($order,array_fill_keys(array_merge($need, $keys),''));
        $order['appid'] = $this->appid;
        $order['mch_id'] = $this->mch_id;
        $order['nonce_str'] = $this->randomStr();
        $order['sign'] = $this->sign($order);
        $xml = $this->toXml($order);
        $response = $this->postXmlCurl($xml, $this->refund_url);
        $result = $this->convertResponse($response);

        return $result;
    }

    /**
     * Notify處理完成接口
     * @return mixed
     */
    public function finish()
    {
        $arr = ['return_code'=>'SUCCESS'];
        $xml = $this->toXml($arr);
        return $xml;
    }

    /**
     * 設置Notify回調接口
     * @return mixed
     */
    public function setNotifyUrl($url)
    {
        $this->notify_url = $url;
    }

    /**
     * 得到Notify返回的支付金額
     * @return mixed
     */
    public function getTotalFee($total_fee = null)
    {
        if($total_fee)
        {
            return round($total_fee/100,2,PHP_ROUND_HALF_DOWN);
        }
        if(isset($this->notify_data['total_fee']))
        {
            return round($this->notify_data['total_fee']/100,2,PHP_ROUND_HALF_DOWN);
        }
        return false;
    }

    /**
     * 得到Notify返回的交易號
     * @return mixed
     */
    public function getSerialNo($arr = null)
    {
        if(isset($arr['transaction_id']))
        {
            return $arr['transaction_id'];
        }
        if(isset($this->notify_data['transaction_id']))
        {
            return $this->notify_data['transaction_id'];
        }
        return false;
    }

    /**
     * 得到Notify返回的原始數據
     * @return mixed
     */
    public function getNotifyRaw()
    {
        return $GLOBALS['HTTP_RAW_POST_DATA'];
    }

    public function randomStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }

    public function getIp()
    {
        if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $strIp = $arr[0];
        } elseif (isset($_SERVER["HTTP_CLIENT_IP"])) {
            $strIp = $_SERVER["HTTP_CLIENT_IP"];
        } elseif (isset($_SERVER["REMOTE_ADDR"])) {
            $strIp = $_SERVER["REMOTE_ADDR"];
        } else {
            $strIp = "0.0.0.0";
        }
        return $strIp;
    }

    private function toXml($values)
    {
        if(!is_array($values) || count($values) <= 0)
        {
            throw new InvalidValueException("數組數據異常!");
        }
        //var_dump($values);exit;
        $xml = "<xml>";
        foreach ($values as $key=>$val)
        {
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
                
            }
        }
        $xml.="</xml>";
        //echo $xml;exit;
        return $xml;
    }

    private function fromXml($xml)
    {
        if(!$xml){
            throw new InvalidValueException("xml數據異常!");
        }
        try
        {
            $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        }
        catch(\Exception $e)
        {
            throw new InvalidValueException("xml數據異常!");
        }
        return $values;
    }

    public function sign($values)
    {
        ksort($values);
        $string = "";
        foreach ($values as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $string .= $k . "=" . $v . "&";
            }
        }

        $string = trim($string, "&");
        $string = $string . "&key=".$this->key;
        $string = md5($string);
        return strtoupper($string);
    }

    public function checkSign($values)
    {
        if($this->sign($values) == $values['sign']){
            return true;
        }
        throw new InvalidValueException("驗證簽名錯誤!");
    }

    private function convertResponse($xml)
    {
        $result = $this->fromXml($xml);
        if($result['return_code'] != 'SUCCESS')
        {
            throw new InvalidValueException($result['return_msg']);
        }
        if($this->checkSign($result) === true){
            return $result;
        }else{
            return false;
        }
    }

    public function searchOrder($out_trade_no, $transaction_id = '',$timeOut = 6)
    {
        if(empty($out_trade_no) && empty($transaction_id)) {
            throw new InvalidValueException("缺乏訂單查詢接口必填參數out_trade_no或transaction_id!");
        }
        $order = ['out_trade_no'=>$out_trade_no,'transaction_id'=>$transaction_id];
        $order['appid'] = $this->appid;
        $order['mch_id'] = $this->mch_id;
        $order['nonce_str'] = $this->randomStr();
        $order['sign'] = $this->sign($order);

        $xml = $this->toXml($order);
        $response = $this->postXmlCurl($xml, $this->search_order_url, false, $timeOut);
        $result = $this->convertResponse($response);

        return $result;
    }

    public function closeOrder($out_trade_no, $timeOut = 6)
    {

        if(empty($out_trade_no)) {
            throw new InvalidValueException("缺乏訂單查詢接口必填參數out_trade_no!");
        }
        $order = ['out_trade_no'=>$out_trade_no];
        $order['appid'] = $this->appid;
        $order['mch_id'] = $this->mch_id;
        $order['nonce_str'] = $this->randomStr();
        $order['sign'] = $this->sign($order);

        $xml = $this->toXml($order);
        $response = $this->postXmlCurl($xml, $this->close_order_url, false, $timeOut);
        $result = $this->convertResponse($response);

        return $result;
    }

    public function createOrder(array $order, $timeOut = 6)
    {
        //檢測必填參數
        $need = array('out_trade_no','body','total_fee','trade_type');
        $keys = array('appid','mch_id','device_info','nonce_str','sign','detail','attach','fee_type',
            'spbill_create_ip','time_start','time_expire','goods_tag','notify_url','product_id','openid');
        $keys = array_merge($need,$keys);
        foreach($need as $key)
        {
            if(empty($order[$key])) {
                throw new InvalidValueException("缺乏統一下單接口必填參數{$key}!");
            }
        }

        //關聯參數
        if($order['trade_type'] == "JSAPI" && empty($order['openid'])){
            throw new InvalidValueException("統一支付接口中,缺乏必填參數openid!trade_type爲JSAPI時,openid爲必填參數!");
        }
        if($order['trade_type'] == "NATIVE" && empty($order['product_id'])){
            throw new InvalidValueException("統一支付接口中,缺乏必填參數product_id!trade_type爲JSAPI時,product_id爲必填參數!");
        }

        $order = array_intersect_key($order,array_fill_keys($keys,''));
        $order['appid'] = $this->appid;
        $order['mch_id'] = $this->mch_id;
        $order['notify_url'] = $this->notify_url;
        $order['spbill_create_ip'] = $this->getIp();
        $order['nonce_str'] = $this->randomStr();
        $order['sign'] = $this->sign($order);
        $xml = $this->toXml($order);
    //    var_dump( $order['notify_url']  );exit;
        $response = $this->postXmlCurl($xml, $this->order_url, false, $timeOut);
        //var_dump($response);exit;
        $result = $this->convertResponse($response);
        return $result;
    }

    public function searchRefund($order, $timeOut = 6)
    {
        $keys = ['out_refund_no','out_trade_no','transaction_id','refund_id'];
        if(empty($order['out_trade_no']) && empty($order['transaction_id']) && empty($order['out_refund_no']) && empty($order['refund_id'])) {
            throw new InvalidValueException("退款查詢接口中,out_refund_no、out_trade_no、transaction_id、refund_id四個參數必填一個!");
        }
        $order = array_intersect_key($order,array_fill_keys($keys,''));
        $order['appid'] = $this->appid;
        $order['mch_id'] = $this->mch_id;
        $order['nonce_str'] = $this->randomStr();
        $order['sign'] = $this->sign($order);
        $xml = $this->toXml($order);
        $response = $this->postXmlCurl($xml, $this->search_refund_url, true, $timeOut);
        $result = $this->convertResponse($response);

        return $result;
    }

    private function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
        $ch = curl_init();
        //設置超時
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        //若是有配置代理這裏就設置代理
        if($this->curl_proxy_host != "0.0.0.0" && $this->curl_proxy_port != 0){
            curl_setopt($ch,CURLOPT_PROXY, $this->curl_proxy_host);
            curl_setopt($ch,CURLOPT_PROXYPORT, $this->curl_proxy_port);
        }
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //設置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求結果爲字符串且輸出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if($useCert == true && !empty($this->cert_path) && !empty($this->key_path)){
            //設置證書
            //使用證書:cert 與 key 分別屬於兩個.pem文件
            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLCERT, $this->cert_path);
            curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
            curl_setopt($ch,CURLOPT_SSLKEY, $this->key_path);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //運行curl
        $data = curl_exec($ch);
        //返回結果
        if($data){
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            throw new InvalidValueException("curl出錯,錯誤碼:{$error}");
        }
    }
}

還須要增長一個Notifyurl.php

<?php

namespace common\widgets\payment;
use Yii;
use common\widgets\payment\Weixinjspi;

class Notifyurl
{
   
    public function notify($code)
   {
       
       $funame='get'.$code;
     
        $respmodel=Yii::$app->payment->$funame();
        $result=$respmodel->notify();
        if($result){
            //商戶訂單號

            $out_trade_no = $result['out_trade_no'];

            //交易號

            $trade_no = $result['trade_no'];

            //交易狀態
            $trade_status = $result['trade_status'];


            if ($trade_status == 'TRADE_SUCCESS')
            {
                $this->update_status($out_trade_no,$trade_no,1);
                echo "success";
            }else{
                
                 echo "fail";
            }
            
        }else{
            echo "fail";
         
        }
       
       
       
   }
   //支付狀態更改
   public  function update_status($out_trade_no,$trade_no,$pay_status)
   {
      
   }
   
}

?>

第三步、咱們如何使用呢,
在咱們需支付的controller中,
//微信支付

public function actionWeixinpay()
    {
        
            $openid=isset($_COOKIE['openid'])?$_COOKIE['openid']:'';
        
           
            if($openid)
            {
                
                  $order_=[
                              'goods_desc'=>'測試',
                              'order_sn' =>'2222',
                              'total_fee' =>12,
                              'body'=>'測試',
                              'time_start' =>date("YmdHis"),
                              'time_expire'=>date("YmdHis", time() + 86400*300),
                              'goods_tag' =>'',
                              'notify_url'=>Url::to(['@web/respond/updatepay'],true),//這是回調地址,微信通知的地址
                              'openid'=>$openid,
                           
                  
                           ];
                  $paymodel=Yii::$app->payment->getWeixinjspi();
        
                   $result=$paymodel->pay($order_);//生成預付單
                 if($result)
                 {
                     $jsstr=$paymodel->getJs($result,$order_['order_sn']);
                    //根據預付單信息生成js,詳細的能夠看上面的類的方法。
                
                 }
                  
                
           }
              
                
            
           
        echo $jsstr;
        
        
    }

第四步、在view層咱們能夠這樣寫:

<button type="button" class="btn red-sunglo" onclick="pay_(2)" >
<div id="js_">
        

        </div>
<script>
//支付
        function pay_(id)
        {
            
             $.ajax({
                    type:"get",
                    url:"/ceshi/weixinpay?id="+id,//這是訪問的地址
                    datatype:"json",
                    data:{},
                    success: function(data)
                    {
                       if(data!='')
                       {
                           
                           $("#js_").html(data);
                           callpay();
                          
                           
                       }
            //alert(data);
                    }
             })
        }
</script>

我這邊是採用的ajax的形式進行支付的,能夠根據本身的狀況進行調整
第五步、支付最重要的是須要回調,yii2增長了Csrf驗證,禁止了外部的訪問,咱們在控制其中寫回調方法的時候必須增長:

public function beforeAction($action) {
        $this->enableCsrfValidation = false;
       return parent::beforeAction($action);
   }

否則微信將訪問不到。
回調的方法使用:

use common\widgets\payment\Weixinjspi;
use common\widgets\payment\Notifyurl;
 public function actionUpdatepay()
    {
        $model=new Notifyurl();
        $model->notify('Weixinjspi');
        
    }

以上就是個人微信支付。第一次寫文章,有不完善的地方請多包涵,有不對的地方請指出我這邊再完善

相關文章
相關標籤/搜索