PayController.php 9.6 KB
<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/3/26
 * Time: 8:26
 */

namespace api\wxapp\controller;

use think\Db;
use think\Log;

/**
 * @title 小程序支付提现
 * @description 小程序支付提现
 */
class PayController
{
    /**
     * 统一下单
     * @param Request $request
     * @return mixed
     * @throws \think\Exception\DbException
     */
    public function wxPay(){
        $appid = config('wxpay.appId');
        $body  = $request->param('body'); // 商品描述
        $CNY   = (double)$request->param('price'); // 金额(0.00)
        $id    = (double)$request->param('id'); // 学生||非定向||活动 id
        $type  = $request->param('type'); // 1->定向支付,2->非定向捐助,3->活动捐款
        if (!isset($request->uid) || empty($request->uid)){
            return outputs(6);
        }
        $order_db = new Order;
        if ($CNY == 0){
            return outputs(10000, '金额不能为0');
        }
        $arr = [
            'appid'            => $appid,
            'mch_id'           => config('wxpay.mchId'),
            'nonce_str'        => md5(rand(100000, 999999)),
            'sign_type'        => 'MD5',
            'body'             => $body,
            'out_trade_no'     => date('Ymdhis', time()) . mt_rand(10000, 99999),
            'total_fee'        => $CNY * 100, // 转换为分
            'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
            'notify_url'       => config('wxpay.notifyUrl'),
            'trade_type'       => 'JSAPI',
            'openid'           => $request->user['openid']
        ];
//        预创建订单
        $order_data = [
            'type'        => $type,
            'numbering'   => $arr['out_trade_no'],
            'recipient'   => $id,
            'uid'         => $request->uid,
            'price'       => $CNY,
            'state'      => - 1,
            'school_year' => $type == 1 ? $student->school_year : null,
            'create_time' => date('Y-m-d H:i:s', time()),
        ];
        $status     = $order_db->addOrder($order_data);
        if (!$status){
            return outputs(9);
        }
        $sign        = $this->getSign($arr);
        $arr['sign'] = $sign;
        $data        = $this->array2xml($arr);
        $return      = $this->curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', $data);
        $return      = $this->xml2array($return);
        if ($return['return_code'] == 'SUCCESS'){
            $back            = [
                'appId'     => $appid,
                'timeStamp' => (string)time(),
                'nonceStr'  => md5(rand(100000, 999999)),
                'package'   => 'prepay_id=' . $return['prepay_id'],
                'signType'  => 'MD5',
            ];
            $paySign         = $this->getSign($back);
            $back['paySign'] = $paySign;
        } else {
            return outputs(1, $return);
        }
        return outputs(1, ['htmlResult' => $back, 'order_sn' => $arr['out_trade_no']]);
    }

    /**
     * 充值回调
     * @return array|bool|string
     */
    public function rechargeCallBack(){
        $xml = file_get_contents("php://input");
        $arr = $this->xml2array($xml);
        if ($arr['return_code'] == 'SUCCESS'){
            Db::startTrans();
            $res = Db::name('recharge_log')->where(['order_sn' => $arr['out_trade_no']])->field('id,money,num,user_id')->find();
            if (!$res){
                $return = [
                    'return_code' => 'FAIL',
                    'return_msg'  => 'ERROR'
                ];
                return $return;
            }
//            更新订单状态
            $status = Db::name('recharge_log')->where(['order_sn' => $arr['out_trade_no']])->update(['state' => 1, 'pay_time' => date('Y-m-d H:i:s')]);
            $is_first = Db::name('user')->where(['id' => $res['user_id']])->value('is_first');
            if ($is_first == 1) {//首设
                $arr = [
                    'money' => ['exp','money + '.$res['money']],
                    'num' => $res['num'],
                    'is_first' =>0
                ];
            } else {
                $arr = [
                    'money' => ['exp','money + '.$res['money']],
                    'num' => $res['num']
                ];
            }
            $userInfo = Db::name('user')->where(['id' => $res['user_id']])->update($arr);
            if ($status === false || $userInfo === false){
                Db::rollback();
                $return = [
                    'return_code' => 'FAIL',
                    'return_msg'  => 'ERROR',
                ];
                return $return;
            }
            Db::commit();
            $return = [
                'return_code' => 'SUCCESS',
                'return_msg'  => 'OK'
            ];
            $return = $this->array2xml($return);
            exit($return);
        }
    }

    /**
     * 提现回调
     * @return array|bool|string
     */
    public function withDrawCallBack(){
        $xml = file_get_contents("php://input");
        $arr = $this->xml2array($xml);
        if ($arr['return_code'] == 'SUCCESS'){
            $res = Db::name('withdraw_log')->where(['order_sn' => $arr['out_trade_no']])->field('id,money,user_id,card_id')->find();
            if (!$res){
                Log::error('订单更新失败(订单不存在)');
                $return = [
                    'return_code' => 'FAIL',
                    'return_msg'  => 'ERROR',
                ];
                trace($return, 'error');
                return $return;
            }
//            更新订单状态
            $status = Db::name('withdraw_log')->where(['order_sn' => $arr['out_trade_no']])->update(['state' => 1, 'pay_time' => date('Y-m-d H:i:s')]);
            $array2 = [
                'money' => ['exp','money -'.$data['money']]
            ];
            $condition = [
                'card_id' => $data['id'],
                'user_id' => $this->userId
            ];
            Db::name('money_log')->where($condition)->update($array2);
            if (!$status){
                Log::error('订单更新失败(操作失败)');
                $return = [
                    'return_code' => 'FAIL',
                    'return_msg'  => 'ERROR',
                ];
                trace($return, 'error');
                return $return;
            }
            $return = [
                'return_code' => 'SUCCESS',
                'return_msg'  => 'OK',
            ];
            $return = $this->array2xml($return);
            return $return;
        }
    }

    /**
     * 生成支付签名
     * @param $arr
     * @return string
     */
    private function getSign($arr){
        //去除数组中的空值
        $arr = array_filter($arr);
        //如果数组中有签名删除签名
        if (isset($arr['sing'])){
            unset($arr['sing']);
        }
        //按照键名字典排序
        ksort($arr);
        //生成URL格式的字符串
        $str = http_build_query($arr) . "&key=" . config('wxpay.payKey');
        $str = $this->arrToUrl($str);
        return strtoupper(md5($str));
    }

    /**
     * URL解码为中文
     * @param $str
     * @return string
     */
    private function arrToUrl($str){
        return urldecode($str);
    }

    private function array2xml($arr){
        if (!is_array($arr) || count($arr) <= 0){
            return false;
        }

        $xml = "<xml>";
        foreach ($arr as $key => $val){
            if (is_numeric($val)){
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }

    private function xml2array($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $values;
    }
    function curl_post_ssl($url, $xmldata, $second = 30, $aHeader = array()){
        $isdir = $_SERVER['DOCUMENT_ROOT']."/commonAssociation/cert/yfls/";//证书位置;绝对路径
        $ch = curl_init();//初始化curl

        curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 终止从服务端进行验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
        curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
        curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
        curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
        curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
        }
        curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xmldata);//全部数据使用HTTP协议中的"POST"操作来发送


        $data = curl_exec($ch);//执行回话

        if ($data) {
            curl_close($ch);
            return xmlToArray($data);
        } else {
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n";
            curl_close($ch);
            return false;
        }
    }

}