thinkphp5实现根据渠道号不同实现安卓和IOS的APP支付和H5支付

<?php
namespace app\api\controller;
use think\Controller;
use app\common\model\ShopInfo as ShopInfoModel;
use app\common\model\UserOrderInfo as UserOrderInfoModel;
use app\common\model\User as UserModel;
use app\common\model\FromInfo  as FromInfoModel;

class Pay extends Controller
{
    public function _empty()
    {
        dump(input('get.'));die;
        return $this->fetch('/404');
    }

    //下单
    public function unifiedorder() {
        $playerid   = input('playerid/d');   //玩家id
        $shopid   = input('shopid/d');   //商品id
        $fromid   = input('fromid/d'); //渠道号
        $orderid = date('ymdHis', time()) . mt_rand(1000, 9999); //商户唯一订单号
        $shopinfo = ShopInfoModel::getOne('shopid, shopname, gamecoins, rmb', ['shopid' => $shopid]);
        if(empty($shopinfo)) {
            return json_return(-1, '商品不存在');
        }
        if(!$playerid) {
            return json_return(-1, '缺少玩家id');
        }
        
        //订单信息
        $total_fee = (config('environment') == 'test') ? 1 : $shopinfo->rmb * 100;//; //订单金额
        $order = [
            'appid'     => config('system.appid'),
            'body'      => $shopinfo->shopname,
            'mch_id'    => config('system.mch_id'),
            'nonce_str' => uniqid(),
            'notify_url'  => config('system.site_url').url('pay/notify'),//接受微信异步通知地址
            'out_trade_no' => $orderid,//商户唯一订单号,可包含字母序
            'spbill_create_ip'=>request()->ip(),//产生订单号的服务器IP
            'total_fee'   => $total_fee,//订单金额,单位/分
        ];
        
        $frominfo = FromInfoModel::getOne('fromid, paytype', ['fromid' => $fromid]);
        if(empty($frominfo)) {
            return json_return(-1, '渠道号不存在');
        }
        $pay_type = $frominfo->paytype;
       
        if($fromid < 10000) {//ios
            switch ($pay_type) {
                case 1:
                    $order['trade_type'] = 'APP';
                    $order['appid'] = config('system.ios_appid');
                    break;
                case 2:
                    $order['trade_type'] = 'MWEB';
                    $order['scene_info'] = '{"h5_info": {"type":"IOS","app_name": "'.config('system.ios_appname').'","bundle_id": "'.config('system.ios_packagename').'"}}';
                    break;
                case 3:
                    $order['trade_type'] = 'ApplePay';
                    $orderRes = $this->createOrderInfo($fromid, $orderid, $playerid, $shopinfo, 3);
                    if($orderRes) {
                        $return_data =  [
                            'trade_type' => 'ApplePay', 
                            'orderid'    => $orderid 
                        ];
                        return json_return(0, 'success', $return_data);
                    }else{
                        return json_return(-1, '保存订单信息失败');
                    }
                    break;
                default:
                    return json_return(-1, '交易类型错误');
                    break;
            }
        }else if($fromid > 10000 && $fromid < 100000) {  //安卓
            switch ($pay_type) {
                case 1:
                    $order['trade_type'] = 'APP';
                    $order['appid'] = config('system.android_appid');
                    break;
                case 2:
                    $order['trade_type'] = 'MWEB';
                    $order['scene_info'] = '{"h5_info": {"type":"Android","app_name": "'.config('system.android_appname').'","package_name": "'.config('system.android_packagename').'"}}';
                    break;
                default:
                    return json_return(-1, '交易类型错误');
                    break;
            }
        }else if($fromid >= 100000) { //other
            $order['trade_type'] = 'MWEB';
            $order['scene_info'] = '{"h5_info": {"type":"IOS","app_name": "'.config('system.ios_appname').'","bundle_id": "'.config('system.ios_packagename').'"}}';
        }else{
            return json_return(-1, '当前设备无法支付');
        }

        $response = $this->getWxUnifiedorderResponse($order);
        //若预下单成功,return_code 和result_code为SUCCESS。
        if ( $response['return_code'] ==='SUCCESS' && $response['result_code'] ==='SUCCESS') {
            //保存订单信息
            $res = $this->createOrderInfo($fromid, $orderid, $playerid, $shopinfo);
            if($res) {
                //返回trade_type和prepay_id供前端调用
                $trade_type = $response['trade_type'];
                $return_data =  [
                    'trade_type' => $trade_type, 
                    'orderid'    => $orderid 
                ];
                switch ($trade_type) {
                    case 'APP':
                        $return_data['prepay_order'] = $this->getPrepayOrder($order['appid'], $response['prepay_id']);
                        break;
                    case 'MWEB':
                        $redirect_url = config('system.site_url').'/mweb.php' . '?mweb_url=xxx://xxx';  //后面的mweb_url为浏览器拉起应用的链接
                        $mweb_url = urlencode($response['mweb_url'].'&redirect_url='.$redirect_url);
                        $return_data['mweb_url'] = config('system.site_url').'/mweb.php' . '?mweb_url=' .$mweb_url;
                        //mweb_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
                        break;
                    default:
                        # code...
                        break;
                }
                return json_return(0, 'success', $return_data);
            }else{
                return json_return(-1, '保存订单信息失败');
            }
        }else{
            $errmsg = ($response['return_code'] == 'FAIL') ? $response['return_msg'] : $this->error_code($response['err_code']);
            return json_return(-1, $errmsg);
        }

    }
    
    /**
     * [createOrderInfo 创建订单信息]
     * @param  [integer]  $orderid  [订单号]
     * @param  [integer]  $playerid [玩家id]
     * @param  [obj]  $shopinfo [商品信息]
     * @param  integer $paytype  [支付类型]
     * @return [type]            [obj]
     */
    private function createOrderInfo($fromid, $orderid, $playerid, $shopinfo, $paytype = 1) {
        $userorderinfo = array(
            'orderid'   => $orderid,
            'shopname'  => $shopinfo->shopname,
            'gamecoins' => $shopinfo->gamecoins,
            'rmb'       => $shopinfo->rmb,
            'orderstatus' => 0,
            'fromid'   => $fromid,
            'paytype'  => $paytype, //支付方式1-微信,2-支付宝,3-Apple Pay
            'playerid' => $playerid,
            'shopid'   => $shopinfo->shopid,
            'orderdate'    => date('Y-m-d H:i:s'),
        );
        return UserOrderInfoModel::create($userorderinfo);
    }

    /**
     * [getWxUnifiedorderResponse 获得微信下单结果]
     * @param  [array]  $orderid  [订单信息]
     * @return [array]      
     */
    private function getWxUnifiedorderResponse($order) {
        $sign = $this->getSign($order);
        $order['sign'] = $sign;
        //转换成一维XML格式
        $xml = '<xml>';
        foreach($order as $k=>$v){
            $xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>';
        }
        $xml.='</xml>';

        //CURL会话
        $response = curlHtml('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml);
        //将xml格式的$response 转成数组
        $response = json_decode( json_encode( simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA) ), true );
        return $response;
    }

    //执行第二次签名,才能返回给客户端使用
    private function getPrepayOrder($appid, $prepayid){
        $data["appid"] = $appid;
        $data["noncestr"] = uniqid();
        $data["package"] = "Sign=WXPay";
        $data["partnerid"] = config('system.mch_id');
        $data["prepayid"] = $prepayid; //微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
        $data["timestamp"] = time();
        $data["sign"] = $this->getSign($data);

        return $data;
    }
    /**
     * 格式化参数格式化成url参数
     */
    private function ToUrlParams($data)
    {
        $buff = "";
        foreach ($data as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        
        $buff = trim($buff, "&");
        return $buff;
    }
    
    /**
     * 生成签名
     * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
     */
    public function getSign($data)
    {
        //签名步骤一:按字典序排序参数
        if(is_array($data)){
            ksort($data);
        } else{
            $data = [];
        }
        $string = $this->ToUrlParams($data);
        //签名步骤二:在string后加入KEY
        $string = $string . "&key=".config('system.apikey');
        //签名步骤三:MD5加密
        $string = md5($string);
        //签名步骤四:所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    
//订单查询
    public function order_query() {
        $orderid = input('orderid');
        require_once "./pay/wxpay/lib/WxPay.Api.php";
        $input = new \WxPayOrderQuery();
        $input->SetOut_trade_no($orderid);
        $result = \WxPayApi::orderQuery($input);
        if ($result['err_code_des'] == "order not exist") {
                // 订单不存在
            $trade_state = '订单不存在';
        } else {
            if ($result['trade_state'] == "SUCCESS") {
                $trade_state = '支付成功'; //支付成功
                return json_return(0, $trade_state, $result);
            } else if ($result['trade_state'] == "REFUND") {
                $trade_state = '已退款'; //已退款
            } else if ($result['trade_state'] == "NOTPAY") {
                $trade_state = '未支付'; //用户还没支付
            } else if ($result['trade_state'] == "CLOSED") {
                $trade_state = '订单关闭'; //订单关闭
            } else if ($result['trade_state'] == "REVOKED") {
                $trade_state = '已撤销'; //已撤销(刷卡支付)
            } else if ($result['trade_state'] == "USERPAYING") {
                $trade_state = '支付中'; //用户支付中
            } else if ($result['trade_state'] == "PAYERROR") {
                $trade_state = '支付失败'; //支付失败(其他原因,例如银行返回失败)
            }
        }
        return json_return(-1, $trade_state);
    }

    //支付完成后的回调
    public function notify()
    {
        $xml = file_get_contents('php://input');
        $result = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        //为了防止假数据,验证签名是否和返回的一样。
        $sign = $this->getSign($result);
        if ( $sign === $result['sign']) {
            //  校验返回的订单金额是否与商户侧的订单金额一致。修改订单表中的支付状态。
            if ($result['result_code'] == "SUCCESS" && $result['return_code'] == "SUCCESS") {
                //获取订单信息
                $orderinfo = UserOrderInfoModel::getOne('orderid, rmb, playerid,shopid,orderstatus', ['orderid' => $result['out_trade_no']]);
                $total_fee = (config('environment') == 'test') ? 1 : $orderinfo->rmb * 100;//; //订单金额
                if (($total_fee == $result['total_fee']) && ($orderinfo['orderstatus'] == 0)) {
                    $this->payHandler($orderinfo);
                }
            }
        }
        $return = ['return_code'=>'SUCCESS','return_msg'=>'OK'];
        $xml = '<xml>';
        foreach($return as $k=>$v){
            $xml.='<'.$k.'><![CDATA['.$v.']]></'.$k.'>';
        }
        $xml.='</xml>';

        echo $xml;
    }

    //支付成功后的处理
    private function payHandler($orderinfo) {
        UserOrderInfoModel::transaction(function() use($orderinfo){ //开启事务
            //更新订单等操作
        });
    }

    /**
     * 错误代码
     * @param  $code       服务器输出的错误代码
     * return string
     */
    private function error_code($code)
    {
        $errList = array(
            'NOAUTH'                => '商户未开通此接口权限',
            'NOTENOUGH'             => '用户帐号余额不足',
            'ORDERNOTEXIST'         => '订单号不存在',
            'ORDERPAID'             => '商户订单已支付,无需重复操作',
            'ORDERCLOSED'           => '当前订单已关闭,无法支付',
            'SYSTEMERROR'           => '系统错误!系统超时',
            'APPID_NOT_EXIST'       => '参数中缺少APPID',
            'MCHID_NOT_EXIST'       => '参数中缺少MCHID',
            'APPID_MCHID_NOT_MATCH' => 'appid和mch_id不匹配',
            'LACK_PARAMS'           => '缺少必要的请求参数',
            'OUT_TRADE_NO_USED'     => '同一笔交易不能多次提交',
            'SIGNERROR'             => '参数签名结果不正确',
            'XML_FORMAT_ERROR'      => 'XML格式错误',
            'REQUIRE_POST_METHOD'   => '未使用post传递参数 ',
            'POST_DATA_EMPTY'       => 'post数据不能为空',
            'NOT_UTF8'              => '未使用指定编码格式',
        );
        if (array_key_exists($code, $errList)) {
            return $errList[$code];
        }else{
            return $code;
        }
    }

}

 

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