tp5.1 微信支付、支付宝、招商支付(Payment)

插件合集

点击跳转

源地址

https://github.com/helei112g/payment

安装

当前 Payment 项目仅支持 PHP version > 7.0 的版本,并且仅支持通过 composer 进行安装。

需要 PHP 安装以下扩展:

- ext-curl
- ext-mbstring
- ext-bcmath
- package-Guzzle

Composer安装

composer require "riverslei/payment:*"
composer update

直接上代码

<?php
/**
 * Created by PhpStorm.
 * User: Zhangyongfeng
 * Date: 2020/11/24
 * Time: 11:27
 *
 * ━━━━━━━━━神兽出没━━━━━━━━━
 *
 *        ┏┓   ┏┓+ +
 *       ┏┛┻━━━┛┻┓ + +
 *       ┃       ┃  
 *       ┃   ━   ┃ ++ + + +
 *       ████━████ ┃+
 *       ┃       ┃ +
 *       ┃   ┻   ┃
 *       ┃       ┃ + +
 *       ┗━┓   ┏━┛
 *         ┃   ┃           
 *         ┃   ┃ + + + +
 *         ┃   ┃    Code is far away from bug with the animal protecting       
 *         ┃   ┃ +     神兽保佑,代码无bug  
 *         ┃   ┃
 *         ┃   ┃  +         
 *         ┃    ┗━━━┓ + +
 *         ┃        ┣┓
 *         ┃        ┏┛
 *         ┗┓┓┏━┳┓┏┛ + + + +
 *          ┃┫┫ ┃┫┫
 *          ┗┻┛ ┗┻┛+ + + +
 *
 * ━━━━━━━━━感觉萌萌哒━━━━━━━━━
 */

namespace zyf;

// 博客地址 https://www.cnblogs.com/zyfeng/p/14034536.html

class Pay
{
    protected $wxConfig = [];
    protected $aliConfig = [];

    public static function returnCode($code = '', $msg = '', $data = [])
    {
        return \app\common\controller\Base::returnData($code, $msg, $data);
    }

    /**
     * 构造函数
     * @access protected
     * @param array $options 参数
     * @author zyf <1322816443@qq.com>
     */
    public function __construct($options = [])
    {

        foreach ($options as $name => $item) {
            if (property_exists($this, $name)) {
                $this->$name = $item;
            }
        }

        $this->wxConfig = [
            'debug' => config("pay.wechat.debug"),
            'app_id' => config("pay.wechat.app_id"),
            'mch_id' => config("pay.wechat.mch_id"),
            'md5_key' => config("pay.wechat.md5_key"),
            'app_cert_pem' => dirname(__FILE__). DIRECTORY_SEPARATOR .
                'pem' . DIRECTORY_SEPARATOR . 'apiclient_cert.pem',
            'app_key_pem' => dirname(__FILE__). DIRECTORY_SEPARATOR .
                'pem' . DIRECTORY_SEPARATOR . 'apiclient_key.pem',
            'sign_type' => 'MD5',   // MD5  HMAC-SHA256
            'limit_pay' => [],      // 指定不能使用信用卡支付   不传入,则均可使用
            'fee_type' => 'CNY',    // 货币类型  当前仅支持该字段
            'notify_url' => $_SERVER['REQUEST_SCHEME'] . "://". $_SERVER['SERVER_NAME'] ."/common/Pay/wxNotify",
            'redirect_url' => 'https://dayutalk.cn/'
        ];

        $this->aliConfig = [
            'use_sandbox' => config("pay.alipay.debug"),
            'app_id' => config("pay.alipay.app_id"),
            'sign_type' => 'RSA2',  // RSA  RSA2
            'ali_public_key' => config("pay.alipay.public_key"),
            'rsa_private_key' => config("pay.alipay.private_key"),
            'limit_pay' => config("pay.alipay.limit_pay"),
            'notify_url' => $_SERVER['REQUEST_SCHEME'] . "://". $_SERVER['SERVER_NAME'] ."/common/Pay/aliNotify",
            'return_url' => $_SERVER['REQUEST_SCHEME'] . "://". $_SERVER['SERVER_NAME'] ."/common/Pay/aliNotify",
            'fee_type' => 'CNY',    // 货币类型  当前仅支持该字段
        ];
    }

    /**
     * [order_num 生成订单号]
     * @param string $prefix 订单前缀(如:JD-)
     * @return string 输出订单号字符串
     */
    public function order_num($prefix = '')
    {
        $micro = substr(microtime(), 2, 3);
        return $prefix . date("YmdHis") . $micro . rand(100000, 999999);
    }

    /**
     * 微信支付
     * @param string $type 'app' =>     APP
     * @param string $type 'pub' =>     公众号
     * @param string $type 'qr' =>      扫码支付(可以使用app的帐号,也可以用公众的帐号完成)
     * @param string $type 'single' =>  单次分账
     * @param string $type 'bar' =>     刷卡支付,与支付宝的条码支付对应
     * @param string $type 'lite' =>    小程序
     * @param string $type 'wap' =>     wap支付,针对特定用户
     * @param string $type 'multi' =>   多次分账
     * @param array $data['body']           商品描述
     * @param array $data['subject']        商品的标题
     * @param array $data['trade_no']       商户网站唯一订单号
     * @param array $data['time_expire']    该笔订单允许的最晚付款时间,逾期将关闭交易
     * @param array $data['amount']         订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
     * @param array $data['return_param']   公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。
     * @param array $data['client_ip']      必须传正确的用户端IP,支持ipv4、ipv6格式,获取方式详见
     * @param array $data['openid']         $type=pub此参数必传,用户在商户appid下的唯一标识
     * @param array $data['product_id']     $type=qr此参数必传,此id为二维码中包含的商品ID,商户自行定义
     * @return string|array
     * @author zyf <1322816443@qq.com>
     * */
    public function wxPay($type = "app", $data = [])
    {
        // 请求参数,完整参数见具体表格
        $payData = [
            'body' => 'test body',
            'subject' => 'test subject',
            'trade_no' => '',               // 自己实现生成
            'time_expire' => time() + 600,  // 表示必须 600s 内付款
            'amount' => '5.20',             // 微信沙箱模式,需要金额固定为3.01
            'return_param' => '123',
            'client_ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1', // 客户地址
            'openid'       => '',
            'product_id'   => '',
            'attach' => '自定义数据'
        ];
        if ($type == 'pub') {
            if (empty($data['openid'])) {
                return self::returnCode(999, '公众号支付请传openid');
            }
        }
        if ($type == 'qr') {
            if (empty($data['product_id'])) {
                return self::returnCode(999, '扫码支付请传product_id');
            }
        }
        if ($data) {
            foreach ($data as $k => $v) {
                if (isset($payData[$k])) {
                    $payData[$k] = $v;
                }
            }
        }
        // 使用
        try {
            $client = new \Payment\Client(\Payment\Client::WECHAT, $this->wxConfig);
            switch ($type) {
                case "app":
                    $res = $client->pay(\Payment\Client::WX_CHANNEL_APP, $payData);
                    break;
                case "pub":
                    $res = $client->pay(\Payment\Client::WX_CHANNEL_PUB, $payData);
                    break;
                case "qr":
                    $res = $client->pay(\Payment\Client::WX_CHANNEL_QR, $payData);
                    break;
                case "single":
                    $res = $client->pay(\Payment\Client::WX_SETTLE_SINGLE, $payData);
                    break;
                case "bar":
                    $res = $client->pay(\Payment\Client::WX_CHANNEL_BAR, $payData);
                    break;
                case "lite":
                    $res = $client->pay(\Payment\Client::WX_CHANNEL_LITE, $payData);
                    break;
                case "wap":
                    $res = $client->pay(\Payment\Client::WX_CHANNEL_WAP, $payData);
                    break;
                case "multi":
                    $res = $client->pay(\Payment\Client::WX_SETTLE_MULTI, $payData);
                    break;
            }
            return $res;
        } catch (\InvalidArgumentException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\GatewayException $e) {
            echo $e->getMessage();
            var_dump($e->getRaw());
            exit;
        } catch (\Payment\Exceptions\ClassNotFoundException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Exception $e) {
            echo $e->getMessage();
            exit;
        }
    }

    /**
     * 查看订单支付状态 判断trade_state==SUCCESS 代表支付成功
     * @param number trade_no 业务生成的订单号
     * @param number transaction_id 交易编号
     * @return array
     * @author zyf <1322816443@qq.com>
     * */
    public function wxTradeQuery($data)
    {
        // 使用
        try {
            $client = new \Payment\Client(\Payment\Client::WECHAT, $this->wxConfig);
            $res    = $client->tradeQuery($data);
            return $res;
        } catch (InvalidArgumentException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\GatewayException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\ClassNotFoundException $e) {
            echo $e->getMessage();
            exit;
        }
    }

    /**
     * 微信签名 jsapi页面用到
     * @param string appid 用户appid
     * @param string nonceStr 支付生成的随机串
     * @param string package 'prepay_id='.$data['PREPAY_ID'] 支付生成的标识编码
     * @param string signType 加密方式 线上:MD5 沙箱。。。
     * @param string timeStamp 时间戳
     * @return string
     * @author zyf <1322816443@qq.com>
     */
    public function wxMakeSign($data)
    {
        $stringA = '';
        foreach ($data as $key => $value) {
            if (!$value) {
                continue;
            }
            if ($stringA) {
                $stringA .= '&'.$key."=".$value;
            } else {
                $stringA = $key."=".$value;
            }
        }
        $wx_key = $this->wxConfig['md5_key'];
        $stringSignTemp = $stringA.'&key='.$wx_key;
        return strtoupper(md5($stringSignTemp));
    }

    /*
     * 微信支付回调
     * */
    public function wxNotify()
    {
        // 实例化继承了接口的类
        $callback = new PayNotify();
        try {
            $client = new \Payment\Client(\Payment\Client::WECHAT, $this->wxConfig);
            $xml = $client->notify($callback);
            //写业务逻辑

        } catch (\InvalidArgumentException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\GatewayException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\ClassNotFoundException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Exception $e) {
            echo $e->getMessage();
            exit;
        }
    }

    /**
     * 支付宝支付
     * @param string $type 'app' => 手机app
     * @param string $type 'wap' => 手机网页
     * @param string $type 'web' => PC网页
     * @param string $type 'qr' =>  扫码支付
     * @param string $type 'bar' => 条码支付
     * @param array $data['body']           商品描述
     * @param array $data['subject']        商品的标题
     * @param array $data['trade_no']       商户网站唯一订单号
     * @param array $data['time_expire']    该笔订单允许的最晚付款时间,逾期将关闭交易
     * @param array $data['amount']         订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000]
     * @param array $data['return_param']   公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。
     * @param array $data['goods_type']     商品主类型 :0-虚拟类商品,1-实物类商品
     * @param array $data['store_id']       商户门店编号
     * @return string|array
     * @author zyf <1322816443@qq.com>
     * */
    public function aliPay($type = "app", $data = [])
    {
        $payData = [
            'body'         => 'ali qr pay',
            'subject'      => '测试支付宝APP支付',
            'trade_no'     => time() . rand(1000, 9999),
            'time_expire'  => time() + 600, // 表示必须 600s 内付款
            'amount'       => '0.01', // 单位为元 ,最小为0.01
            'return_param' => '{"code":"111","type":"'. $type .'"}',
            'goods_type' => '1', // 0—虚拟类商品,1—实物类商品
            'store_id'   => '',
        ];
        if ($data) {
            foreach ($data as $k => $v) {
                if ($payData[$k]) {
                    $payData[$k] = $v;
                }
            }
        }
        // 使用
        try {
            $client = new \Payment\Client(\Payment\Client::ALIPAY, $this->aliConfig);
            switch ($type) {
                case "web":
                    $res    = $client->pay(\Payment\Client::ALI_CHANNEL_WEB, $payData);
                    break;
                case "app":
                    $res    = $client->pay(\Payment\Client::ALI_CHANNEL_APP, $payData);
                    break;
                case "wap":
                    $res    = $client->pay(\Payment\Client::ALI_CHANNEL_WAP, $payData);
                    break;
                case "qr":
                    $res    = $client->pay(\Payment\Client::ALI_CHANNEL_QR, $payData);
                    break;
                case "bar":
                    $res    = $client->pay(\Payment\Client::ALI_CHANNEL_BAR, $payData);
                    break;
            }
            return $res;
        } catch (\InvalidArgumentException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\GatewayException $e) {
            echo $e->getMessage();
            var_dump($e->getRaw());
            exit;
        } catch (\Payment\Exceptions\ClassNotFoundException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Exception $e) {
            echo $e->getMessage();
            exit;
        }
    }

    /*
     * 支付宝支付回调
     * {
     *  "channel":"Alipay", 通知的渠道,如:支付宝、微信、招商
     *  "type":"pay",       通知的类型,如:支付、退款
     *  "way":"sync",       通知的方式,如:异步 async,同步 sync
     *  "data":{
     *      "charset":"utf-8",  编码
     *      "out_trade_no":"16069883507794",    数据库中的订单号
     *      "method":"alipay.trade.page.pay.return",
     *      "total_amount":"0.01",  金额
     *      "trade_no":"2020120322001440350504855181",  支付宝生成的订单号
     *      "auth_app_id":"2016073100130857",   用户appid
     *      "version":"1.0",
     *      "app_id":"2016073100130857",
     *      "seller_id":"2088102169252684",
     *      "timestamp":"2020-12-03 17:39:42"   时间
     *   }
     * }
     * @author zyf <1322816443@qq.com>
     * */
    public function aliNotify()
    {
        // 实例化继承了接口的类
        $callback = new PayNotify();
        $file = $_SERVER['DOCUMENT_ROOT'].'/callback.text';
        try {
            $client = new \Payment\Client(\Payment\Client::ALIPAY, $this->aliConfig);

            $xml = $client->notify($callback);
            // 写业务逻辑
            $request = json_decode((string)$xml, true);

            $fp = fopen($file, 'a');
            fwrite($fp, json_encode($xml));
            fclose($fp);
        } catch (\InvalidArgumentException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\GatewayException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Payment\Exceptions\ClassNotFoundException $e) {
            echo $e->getMessage();
            exit;
        } catch (\Exception $e) {
            echo $e->getMessage();
            exit;
        }
    }



}

 

微信JSAPI

页面

{include file="public/header" /}
</head>
<body>
<font color="#9ACD32">
    <b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span></b>
</font><br/><br/>
<div align="center">
    <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid;
    cursor: pointer;  color:white;  font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
</div>

</body>
</html>

<script>

    //调用微信JS api 支付
    function jsApiCall()
    {
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest',
            {
                "appId": "{$data.appid}",
                "nonceStr": "{$data.nonce_str}",
                "package": "{$data.prepay_id}",
                "paySign": "{$data.sign}",
                "signType": "MD5",
                "timeStamp": "{$data.timeStamp}",
            },
            function(res){
                console.log(res)
                if(res.err_msg=='get_brand_wcpay_request:ok'){
                    window.location.href="{:url('resulte')}";
                }else if(res.err_msg=='get_brand_wcpay_request:cancel'){
                    alert('未支付');
                }else if(res.err_msg=='get_brand_wcpay_request:fail'){
                    alert('支付失败');
                }else{
                    console.log(res)
                }
            }
        );
    }

    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>

后台

    /**
     * 微信jsapi支付 公众号
     * @param string amount 金额
     * @param string trade_no 生成的唯一订单号
     * */
    public function wxJsapi()
    {
        $payMod = new Pay();
        $payData = [
            'trade_no' => '',
            'amount' => ''
        ];
        $res = $payMod->wxPay('pub', $payData);
        $data['appid'] = '';
        $data['nonceStr'] = $res['nonce_str'];
        $data['package'] = 'prepay_id='. $res['prepay_id'];
        $data['singType'] = 'MD5';
        $data['timeStamp'] = time();
        $data['sign'] = $payMod->wxMakeSign($data);
        $this->render('', $data);
    }

微信H5支付

    /**
     * 微信H5支付 wap
     * @param string amount 金额
     * @param string trade_no 生成的唯一订单号
     * @param string scene_info 场景值
     * */
    public function wxWap()
    {
        $payMod = new Pay();
        $payData = [
            'amount' => '',
            'trade_no' => '',
            'scene_info' => [
                'type'     => 'Wap', // IOS  Android  Wap  腾讯建议 IOS  ANDROID 采用app支付
                'wap_url'  => 'http://', // 自己的 wap 地址
                'wap_name' => '网站名称',
            ]
        ];
        // 跳转下方返回链接即可
        return $payMod->wxPay('wap', $payData)['mweb_url'];
    }

微信扫码支付

需要用到二维码生成,可使用本人博客:

https://www.cnblogs.com/zyfeng/p/14143957.html

后台

    /**
     * 微信native支付 扫码支付
     * @param string amount 金额
     * @param string product_id 产品id
     * @param string trade_no 生成的唯一订单号
     * @return mixed
     */
    public function wxNative()
    {
        $payMod = new Pay();
        $qrCode = new QRcode();
        $payData = [
            'amount' => '',
            'product_id' => '',
            'trade_no' => '',
        ];
        $data = $payMod->wxPay('qr', $payData);
        $qrCode->png($data['code_url']);
        return $this->render('', [
            'img' => $qrCode->show()
        ]);
    }

    /**
     * 微信支付确认订单状态
     * @param string trade_no 生成的唯一订单号
     * */
    public function isOrderType()
    {
        $payMod = new Pay();
        $payData = [
            'trade_no' => '',
        ];
        $res = $payMod->wxTradeQuery($payData);
        if ($res['trade_state'] == 'SUCCESS') {
            // 修改订单状态
        }
        // 写业务逻辑
    }

 页面

<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script src="/static/index/js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
    <title>微信支付</title>
</head>
<body>

<div style="text-align: center;">
    <div style="margin-left: 10px;color:#556B2F;font-size:30px;font-weight: bolder;">扫描支付</div><br/>
    {$img | raw}
    <!--    <div style="color:#ff0000"><b>微信支付样例程序,仅做参考</b></div>-->
    <div id="myDiv"></div>
    <!--    <div id="timer">0</div>-->
</div>
<!--<img alt="模式二扫码支付" src="" style="width:150px;height:150px;"/>-->
<script>
    //设置每隔1000毫秒执行一次load() 方法
    var myIntval = setInterval(function(){load()},1000);
    function load(){
        // document.getElementById("timer").innerHTML=parseInt(document.getElementById("timer").innerHTML)+1;
        var xmlhttp;
        if (window.XMLHttpRequest){
            // code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp = new XMLHttpRequest();
        }else{
            // code for IE6, IE5
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        xmlhttp.onreadystatechange = function(){
            if (xmlhttp.readyState == 4 && xmlhttp.status==200){
                trade_state=xmlhttp.responseText;
                if(trade_state == 'SUCCESS'){
                    document.getElementById("myDiv").innerHTML = '支付成功';
                    //alert(transaction_id);
                    //延迟3000毫秒执行tz() 方法
                    clearInterval(myIntval);

                }else if(trade_state == 'REFUND'){
                    document.getElementById("myDiv").innerHTML = '转入退款';
                    clearInterval(myIntval);

                }else if(trade_state == 'NOTPAY'){
                    document.getElementById("myDiv").innerHTML = '请扫码支付';

                }else if(trade_state == 'CLOSED'){
                    document.getElementById("myDiv").innerHTML = '已关闭';
                    clearInterval(myIntval);

                }else if(trade_state == 'REVOKED'){
                    document.getElementById("myDiv").innerHTML = '已撤销';
                    clearInterval(myIntval);

                }else if(trade_state == 'USERPAYING'){
                    document.getElementById("myDiv").innerHTML = '用户支付中';

                }else if(trade_state == 'PAYERROR'){
                    document.getElementById("myDiv").innerHTML = '支付失败';
                    clearInterval(myIntval);
                }
            }
        }
        //orderquery.php 文件返回订单状态,通过订单状态确定支付状态
        xmlhttp.open("POST", "{:url('isOrderType')}", false);
        //下面这句话必须有
        //把标签/值对添加到要发送的头文件。
        xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        xmlhttp.send("out_trade_no=1");
    }
</script>

</body>
</html>

 

posted @ 2020-12-02 17:19  张永峰z  阅读(1106)  评论(11编辑  收藏  举报