zoukankan      html  css  js  c++  java
  • 微信小程序 支付功能 服务器端(TP5.1)实现

    首先下载微信支付SDK ,将整个目录的文件放在 /application/extend/WxPay 目录下

    在使用SDK之前我们需要对 WxPay.Config.php 进行配置

    <?php
    
    namespace appapiservice;
    
    use appapimodelOrder as OrderModel;
    use applibexceptionOrderException;
    use applibexceptionTokenException;
    use thinkException;
    use thinkLoader;
    use thinkLog;
    
    require_once '/extend/WxPay/WxPay.Api.php';
    
    class Pay
    {
        private $orderNo;
        private $orderID;
    
        //实例化时传入订单ID 此ID由第三方服务器自己定义
        function __construct($orderID)
        {
            if (!$orderID)
            {
                throw new Exception('订单号不允许为NULL');
            }
            $this->orderID = $orderID;
        }
    
        public function pay()
        {
            // 根据订单ID 查到订单下对应商品
            // 对商品库存检测等操作
            // Todo ... 
            
            return $this->makeWxPreOrder($status['orderPrice']);
        }
    
        // 构建微信支付订单信息
        private function makeWxPreOrder($totalPrice)
        {
            //获得当前用户 openid
            $openid = Token::getCurrentTokenVar('openid');
            if (!$openid)
            {
                throw new TokenException();
            }
            //创建订单信息
            $wxOrderData = new WxPayUnifiedOrder();        //需要引入微信提供的SDK
            $wxOrderData->SetOut_trade_no($this->orderNo);      //订单编号,第三方自定义
            $wxOrderData->SetTrade_type('JSAPI');               //交易类型,一般是JSAPI
            $wxOrderData->SetTotal_fee($totalPrice * 100);      //设置总金额,单位为0.01元
            $wxOrderData->SetBody('零食商贩');                  //设置展示信息
            $wxOrderData->SetOpenid($openid);                   //openid
            $wxOrderData->SetNotify_url(config('wx.pay_back_url')); //回调地址
    
            return $this->getPaySignature($wxOrderData);
        }
    
        /**
         * 向微信请求订单号并生成签名
         */
        private function getPaySignature($wxOrderData)
        {
    
            $wxOrder = WxPayApi::unifiedOrder($wxOrderData);
            //返回结果中包含 prepay_id ,此ID作为用户拉起支付时凭证,
            //同时此ID作为将来服务器向客户端推送消息的标识,因此需要保存在数据库订单表中
            // 失败时不会返回result_code
            if($wxOrder['return_code'] != 'SUCCESS' || $wxOrder['result_code'] !='SUCCESS'){
                Log::record($wxOrder,'error');
                Log::record('获取预支付订单失败','error');
            }
            //
            $this->recordPreOrder($wxOrder);
            $signature = $this->sign($wxOrder);
            return $signature;
        }
    
        private function recordPreOrder($wxOrder){
            // 将 prepay_id 保存在数据库中
            OrderModel::where('id', '=', $this->orderID)
                ->update(['prepay_id' => $wxOrder['prepay_id']]);
        }
    
        /**
         * 签名
         * @return [array]   [返回数组中要包含小程序发起支付请求的所有参数  包含:小程序ID、时间戳、随机串、数据包(prepay_id)、签名方式、签名 6个参数]
         * 
         */
        private function sign($wxOrder)
        {
            //调用SDK 生成签名
            $jsApiPayData = new WxPayJsApiPay();
            //Appid
            $jsApiPayData->SetAppid(config('wx.app_id'));
            //timeStamp
            $jsApiPayData->SetTimeStamp((string)time());
            //nonceStr
            $rand = md5(time() . mt_rand(0, 1000));
            $jsApiPayData->SetNonceStr($rand);
            //package
            $jsApiPayData->SetPackage('prepay_id=' . $wxOrder['prepay_id']);
            //signType
            $jsApiPayData->SetSignType('md5');
            //生成签名
            $sign = $jsApiPayData->MakeSign();
            //获取数组
            $rawValues = $jsApiPayData->GetValues();
            $rawValues['paySign'] = $sign;
            return $rawValues;
        }
    }

    接着在控制器中调用该类下的 pay 方法 ,并创建回调函数

    当用户支付完成后,微信服务器会以POST请求到指定回调地址,改地址微信服务器会默认屏蔽掉?后面字符串部分,数据通过XML形式放在body中,格式:

    <xml><appid><![CDATA[wxaaf1c852597e365b]]></appid>
    <bank_type><![CDATA[CFT]]></bank_type>
    <cash_fee><![CDATA[1]]></cash_fee>
    <fee_type><![CDATA[CNY]]></fee_type>
    <is_subscribe><![CDATA[N]]></is_subscribe>
    <mch_id><![CDATA[1392378802]]></mch_id>
    <nonce_str><![CDATA[k66j676kzd3tqq2sr3023ogeqrg4np9z]]></nonce_str>
    <openid><![CDATA[ojID50G-cjUsFMJ0PjgDXt9iqoOo]]></openid>
    <out_trade_no><![CDATA[A301089188132321]]></out_trade_no>  //这个是我们服务器向微信服务器发送的订单号,该订单号由我们自己生成,根据该订单号可以做相应的业务
    <result_code><![CDATA[SUCCESS]]></result_code>        //支付结果
    <return_code><![CDATA[SUCCESS]]></return_code>
    <sign><![CDATA[944E2F9AF80204201177B91CEADD5AEC]]></sign>
    <time_end><![CDATA[20170301030852]]></time_end>
    <total_fee>1</total_fee>
    <trade_type><![CDATA[JSAPI]]></trade_type>
    <transaction_id><![CDATA[4004312001201703011727741547]]></transaction_id>
    </xml>
    • 第一种方法我们可以自己解析这个XML 然后获得需要的数据
    • 第二种方法我们可以通过重写微信SDK中 WxPay.Notify.php 中 NotifyProcess 方法来处理这个数据 

    该方法两个参数,第一个参数就是服务器返回的XML数组化后的值()

    我们可以重写该类

    require_once '/application/extend/WxPay/WxPay.Notify.php';
    class WxNotify extends WxPayNotify
    {
    
        public function NotifyProcess($data, &$msg)
        {
            if ($data['result_code'] == 'SUCCESS') {
                $orderNo = $data['out_trade_no'];
                //开启事务,避免因服务器阻塞,微信多个请求同时到达,出现重复执行业务代码
                Db::startTrans();
                try {
                    $order = Order::where('order_no', '=', $orderNo)->lock(true)->find();
                    //如果订单处于未支付状态下才执行里面的业务代码
                    if ($order->status == 1) {
                        //查库存
                        //改支付状态
                        //减库存
                    }
                    Db::commit();
                } catch (Exception $ex) {
                    Db::rollback();
                    Log::error($ex);
                    // 如果出现异常,向微信返回false,请求重新发送通知
                    return false;
                }
            }
            //如果处理成功,需要向微信服务器发送 TRUE, 告诉微信停止请求回调地址
            return true;
        }
    }

    在控制器的回调函数中调用该子类

    注意:不能直接调用 NotifyProcess 方法,因为我们无法传递该方法的参数,该参数是由父类中方法生成,需要调用父类中 handle 方法来执行 NotifyProcess 中代码

        public function receiveNotify()
        {
            $notify = new WxNotify();
            $notify->handle();
        }
  • 相关阅读:
    Fitness
    【数据分析师 Level 1 】10.数据采集方法
    【数据分析师 Level 1 】9.MySQL简介
    【数据分析师 Level 1 】8.数据库简介
    【数据分析师 Level 1 】7.机器学习的基本概念
    【数据分析师 Level 1 】6.一元线性回归
    【数据分析师 Level 1 】5.方差分析
    【数据分析师 Level 1 】4.假设检验
    【数据分析师 Level 1 】3.抽样分布及参数估计
    【数据分析师 Level 1】2.描述性统计分析
  • 原文地址:https://www.cnblogs.com/xiaoliwang/p/9381672.html
Copyright © 2011-2022 走看看