panda 1 年之前
父節點
當前提交
4270f6d8ea

+ 352 - 0
application/api/controller/JingXiuPay.php

@@ -0,0 +1,352 @@
+<?php
+
+namespace app\api\controller;
+
+use addons\epay\library\Service;
+use app\common\controller\Api;
+use app\utils\CurlUtil;
+use app\utils\Easywechat\MiniAppService;
+use app\utils\JingXiu\JingXiuPayUtil;
+use app\utils\LogUtil;
+use app\utils\PayUtil;
+use think\Cache;
+use think\Db;
+use think\Exception;
+use think\Request;
+
+/**
+ * 通行证
+ */
+class JingXiuPay extends Api
+{
+    // 日志模块名称
+    const LOG_MODULE = 'JingXiuPay';
+    protected $noNeedLogin = ['vip_recharge', 'gold_recharge', 'pay', 'pay_notify'];
+    protected $noNeedRight = '*';
+
+    public function _initialize()
+    {
+        parent::_initialize();
+    }
+
+    public function __construct(Request $request = null)
+    {
+        parent::__construct($request);
+        //日志统一写入
+        register_shutdown_function([new LogUtil, 'close']);
+        LogUtil::getInstance('Api/'); //设置日志存入通道
+    }
+
+
+    /**
+     * 会员充值 (跳转小程序充值)
+     * @return void
+     */
+    public function vip_recharge()
+    {
+        Db::startTrans();
+        try {
+            $rc_id = input('rc_id', 0);
+            $pay_type = input('pay_type', 'wechat');
+            $uid = $this->auth->id;
+
+            if ($pay_type != 'wechat') {
+                throw new Exception('支付类型有误');
+            }
+
+            if (!$rc_id) {
+                throw new Exception('请选择会员套餐');
+            }
+
+            //赋值money
+            $recharge_config = Db::name('payvip_config')->where('id', $rc_id)->find();
+            $money = $recharge_config['money'];
+
+            if ($money <= 0) {
+                throw new Exception('支付金额必须大于0');
+            }
+            if ($money > 10000) {
+                throw new Exception('支付金额太大');
+            }
+
+            //会员等级冲突
+            //当前是会员,但是却要向下级续费,直接提示报错
+            //修改等级,向上立刻改,向下不允许
+            $wallet_info = model('wallet')->getWallet($this->auth->id);
+            if ($wallet_info['vip_endtime'] > time() && $recharge_config['vip_level'] < $wallet_info['vip_level']) {
+                throw new Exception('当前会员没有过期,不能续费');
+            }
+
+            //创建订单
+            $data = [];
+            $data['status'] = 0;
+            $pay_no = createUniqueNo('V', $uid);
+            $data['pay_no'] = $pay_no;
+            $data['money'] = $money;
+            $data['payment_class'] = $pay_type;
+            $data['user_id'] = $uid;
+            $data['ext_info'] = json_encode(['subject' => '充值vip支付']);
+            $data['memo'] = '充值会员支付';
+            $data['createtime'] = time();
+            //$data['payment'] = 'miniapp';
+            $data['payment'] = 'app';
+            $pay_order = Db::name('pay_order')->insertGetId($data);
+
+            //创建回调
+            $even_data = [];
+            $even_data['event'] = 'success';
+            $even_data['class'] = 'app\common\model\Recharge';
+            $even_data['method'] = 'vippaysucc';
+            $even_data['args'] = json_encode(['user_id' => $uid, 'days' => $recharge_config['days'], 'vip_level' => $recharge_config['vip_level'], 'gold_num' => $recharge_config['gold_num'], 'money' => $money]);
+            $even_data['pay_no'] = $pay_no;
+            $pay_event = Db::name('pay_event')->insertGetId($even_data);
+
+            if (!$pay_order || !$pay_event) {
+                throw new Exception('下单失败');
+            }
+
+            Db::commit();
+
+            $pay = new JingXiuPayUtil();
+            if (!$pay->wxWapPay($pay_no,$money,'充值会员','/api/jing_xiu_pay/pay_notify')){
+                $this->error($pay->getMessage());
+            }
+            $data = $pay->getData();
+            $this->success('success', $data);
+        } catch (Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+    }
+
+    /**
+     * 金币充值
+     * @return void
+     */
+    public function gold_recharge()
+    {
+        Db::startTrans();
+        try {
+            $rc_id = input_post('rc_id', 0);
+            $pay_type = 'wechat';
+            $freemoney = input_post('freemoney', 0, 'intval');
+            $uid = $this->auth->id;
+
+            if (!$rc_id && !$freemoney) {
+                throw new Exception('请选择或填写充值金额');
+            }
+            if (!in_array($pay_type, ['wechat', 'alipay'])) {
+                throw new Exception('错误的支付类型');
+            }
+            //赋值money
+            if ($rc_id) {
+                $recharge_config = Db::name('paygold_config')->where('id', $rc_id)->find();
+                $money = $recharge_config ? $recharge_config['money'] : 0;
+                $gold = $recharge_config ? $recharge_config['gold'] : 0;
+                $first_gold = $recharge_config ? $recharge_config['first_gold'] : 0;
+                $first_vipdays = $recharge_config ? $recharge_config['first_vipdays'] : 0;
+                $vip_gold = $recharge_config ? $recharge_config['vip_gold'] : 0;
+            }
+
+            //自由输入覆盖
+            if (!empty($freemoney)) {
+                $rc_id = 0;
+                $money = floatval($freemoney);
+                $bili = config('site.money_to_gold') ?: 10;
+                $gold = bcmul($money, $bili, 0);
+                $first_gold = 0;
+                $first_vipdays = 0;
+                $vip_gold = 0;
+            }
+            if ($money <= 0) {
+                throw new Exception('支付金额必须大于0');
+            }
+            if ($money > 10000) {
+                throw new Exception('支付金额太大');
+            }
+            //查询是不是会员,若不是则不赠送金币
+            $vip_endtime = Db::name('user_wallet')->where('user_id', $this->auth->id)->value('vip_endtime');
+            if ($vip_endtime < time()) {
+                $vip_gold = 0;
+            }
+
+            //创建订单
+            $data = [];
+            $data['status'] = 0;
+            $pay_no = createUniqueNo('P', $uid);
+            $data['pay_no'] = $pay_no;
+            $data['money'] = $money;
+            $data['payment_class'] = $pay_type;
+            $data['user_id'] = $uid;
+            $data['ext_info'] = json_encode(['subject' => '充值金币支付']);
+            $data['memo'] = '充值金币支付';
+            $data['createtime'] = time();
+            $data['payment'] = 'app';
+            $pay_order = Db::name('pay_order')->insertGetId($data);
+
+            //创建回调
+            $even_data = [];
+            $even_data['event'] = 'success';
+            $even_data['class'] = 'app\common\model\Recharge';
+            $even_data['method'] = 'goldpaysucc';
+            $even_data['args'] = json_encode(['user_id' => $uid, 'gold' => $gold, 'money' => $money, 'pg_id' => $rc_id, 'first_gold' => $first_gold, 'first_vipdays' => $first_vipdays, 'intro_uid' => $this->auth->intro_uid, 'vip_gold' => $vip_gold]);
+            $even_data['pay_no'] = $pay_no;
+            $pay_event = Db::name('pay_event')->insertGetId($even_data);
+
+
+            if (!$pay_order || !$pay_event) {
+                throw new Exception('下单失败');
+            }
+
+            Db::commit();
+            $pay = new JingXiuPayUtil();
+            if (!$pay->wxWapPay($pay_no,$money,'充值会员','/api/jing_xiu_pay/pay_notify')){
+                $this->error($pay->getMessage());
+            }
+            $data = $pay->getData();
+            $this->success('success', $data);
+        } catch (Exception $e) {
+            Db::rollback();
+            $this->error($e->getMessage());
+        }
+    }
+
+    /**
+     * 支付回调
+     *
+     * @param Request $request
+     * @return string
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public function pay_notify(Request $request)
+    {
+        $params = $request->param();
+
+        // 消息主体信息
+        $resp_data_json = htmlspecialchars_decode($params['resp_data'] ?? '');
+        $resp_data = json_decode(stripslashes($resp_data_json), true);
+        unset($params['resp_data']);
+
+        LogUtil::info('支付回调参数', self::LOG_MODULE, __FUNCTION__, [
+            'params' => $params,
+            'resp_data2' => base64_encode($resp_data_json),
+            'resp_data' => $resp_data,
+        ]);
+
+        // 校验回调信息
+        if (empty($params['resp_code']) || $params['resp_code'] != '00000000' || empty($resp_data['req_seq_id'])) {
+            LogUtil::info('回调信息有误', self::LOG_MODULE, __FUNCTION__, "resp_code error");
+            return self::response201('支付信息有误');
+        }
+
+        // 校验签名
+        $huiPay = new PayUtil();
+        if (!$huiPay->checkSign($params['sign'],$resp_data_json)){
+            LogUtil::info('签名校验失败', self::LOG_MODULE, __FUNCTION__);
+            return self::response201('签名校验失败');
+        }
+
+        if ($resp_data['resp_code'] != '00000000'){
+            LogUtil::info('订单交易失败', self::LOG_MODULE, __FUNCTION__,$resp_data['resp_desc'] ?? '交易失败');
+            return self::response201('支付信息有误');
+        }
+
+        // 开始处理业务逻辑
+        $pay_no = $resp_data['req_seq_id'];
+        LogUtil::info('订单号', self::LOG_MODULE, __FUNCTION__, $pay_no);
+        //查询订单信息
+        $order_info = Db::name('pay_order')->where('pay_no', $pay_no)->find();
+        if (!$order_info) {
+            LogUtil::info('订单信息不存在', self::LOG_MODULE, __FUNCTION__);
+            return self::response201('订单信息不存在');
+        }
+        if ($order_info['status'] == 3){
+            LogUtil::info('订单信息_已删除', self::LOG_MODULE, __FUNCTION__, [
+                'status' => $order_info['status']
+            ]);
+            return self::response201('充值入账失败');
+        }
+        if ($order_info['status'] == 1 || $order_info['status'] == 2) {
+            LogUtil::info('充值入账更新余额失败_status已更新过', self::LOG_MODULE, __FUNCTION__, [
+                'status' => $order_info['status']
+            ]);
+            return self::response201('充值入账失败');
+        }
+
+        $extendType = '';
+        //区分vip 还是 冲金币
+        $is_g = stripos($pay_no, 'P');
+        if ($is_g !== false) {
+            $extendType = 'gold';
+        } else {
+            $is_v = stripos($pay_no, 'V');
+            if ($is_v !== false) {
+                $extendType = 'vip';
+            }
+        }
+
+        //status已更新过
+        $result = Db::name('pay_order')->where('pay_no', $pay_no)->setField(['status' => 2]);
+
+        if (!$result) {
+            LogUtil::info('充值入账更新余额失败_status更新状态2不成功', self::LOG_MODULE, __FUNCTION__, [
+                'status' => $order_info['status']
+            ]);
+            return self::response201('充值入账失败');
+        }
+
+        //你可以在此编写订单逻辑
+        $payEvent = Db::name('pay_event')->where('pay_no', $pay_no)->find();
+        $args = json_decode($payEvent['args'] ?? '', true);
+        $rechargeM = new \app\common\model\Recharge();
+        if ($extendType == 'gold') {
+            $payRes = $rechargeM->goldpaysucc($pay_no, $args);
+            $payTypeStr = '充值';
+        } elseif ($extendType == 'vip') {
+            $payRes = $rechargeM->vippaysucc($pay_no, $args);
+            $payTypeStr = 'vip';
+        } else {
+            $payRes = false;
+            $payTypeStr = '未知支付类型';
+        }
+        if (!$payRes) {
+            LogUtil::info('更新失败', self::LOG_MODULE, __FUNCTION__, $payTypeStr);
+            return self::response201('更新失败');
+        }
+
+        LogUtil::info('订单处理成功', self::LOG_MODULE, __FUNCTION__, $payTypeStr);
+        return self::response200('success');
+    }
+
+    /**
+     * 成功返回
+     * @param string $message
+     * @param $result
+     * @return string
+     */
+    private static function response200(string $message = 'success', $result = null)
+    {
+        return json_encode([
+            'code' => 200,
+            'message' => $message,
+            'result' => $result,
+        ], JSON_UNESCAPED_UNICODE);
+    }
+
+    /**
+     * 失败返回201
+     * @param string $message
+     * @param $result
+     * @return string json
+     */
+    private static function response201(string $message = 'error', $result = null)
+    {
+        return json_encode([
+            'code' => 201,
+            'message' => $message,
+            'result' => $result
+        ], JSON_UNESCAPED_UNICODE);
+    }
+}

+ 78 - 0
application/utils/JingXiu/JingXiuPayUtil.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace app\utils\JingXiu;
+
+use app\utils\CurlUtil;
+use think\Request;
+
+class JingXiuPayUtil extends Module
+{
+    private $host                 = 'https://gateway.jxpays.com/';
+    private $mch_id               = '880843903216443';
+    private $merchant_private_key = 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCysh3XnRELOwLnj0AyiU8RGNLkCOgYpZsGbXLL00U0p+Y2duXTj8LWqxXHQPMbtrKSryzNJ/qbxV9zxwYB8bJP1HAf1HnXlGGN7y3I2uxaq5Y6DkY1mZPiYk3WEhc41HJ1z9Z81j0gV594Z4/XvXTlZTZnFDDdbLr5tuQr9GttA3g62qiQZkvG9Ea93ltLjXKIQBOr9HqeaZLe1fbzdsSsX7HVFsh4OLu6UxVhfIxseYE5eh4dOa68wB1HgkcR34fMGZv7UdpF+V2lTYDpWG3QmlwAc6K+BDlxjreXX38tux1Etu9sQXBwUSjy2b5wgHyldOajVftSQiYF930dfTqFAgMBAAECggEAJZYzkru17w/Nj5lhHoAlQmpfSZnZ//0te4+f+uGIiAfc843Uz8hIalD4Nr4hpK2sKBByswB6xTMh3b96MwR8xi/gGcHekwrh7jHmBn6OApBe+/0upW2VXqPgzWtmuQxuuvspmX6Swu/LWk8mQXHXWb0NUWc81AZf2dCvc9NomTgsnR5w4cx7uwarDbOIF3XRyyb6o1csb1gQ/ufGIoQCbjoNCJYSWRSBsTQ7Uj/rB1ex58SEV0fg9g9Grp8q8yG+OnkpvizHycbzWezuC60r74oIhRIGNiyDlhmK5+kqay9wQ9EbYpBtczHrJr0uPZFu9kgZvuuz5uxsm1UzJKueLQKBgQDaouGSvBgtb3/qd3guztzUty/ECeFba9PRbq1GzIGkUg/DEyJV/YHNQY6z2gWpfsxTw71q49OD+zC6P0dnBArpZTi78cl2rhJKguMmBrW918TDq7kmN56gHVhNsoGEne7yXU9QZfCimCAW6VCuxs+yEDJ2k8ncKMKjN6dEWPqsvwKBgQDRO+D67ybjDca9Udi0VVu+IWycxU6lU1804BvRVUCLOVcFm0nM4TXLLH9vnZPx2sbDgRg77a5wxQKIAjv/Vyd5uGW++YiTT2zbcUypIcEFWQ0CcXdUKq4kMNnkrSvu7gdrRBXUxmXmfr9RNAm9SXZR/STfUZPB9i48HGha50K1uwKBgQCDdGOEQiuXRe73HpXYLvB/xRSnHfmtaguVZKIya0uXbO+lLp/6a83bh4sxf+jfnClmQ85jsk++jw0hLmLUAY49/mo6NooXHVpKCqBggLBNUKoxq9JKtC5fvsUVEjrlbb/5YTkK8f/ZAvZoUCf+rfK68RomSX2ZLbi5QgEECyvNKQKBgQCeDM5AnyBPyRtD7Sx15Did4Z+quHxU9CppxZQdx3ku8cxjZs8TnVdGN0G4LV8RBYK5Ma39rufjNV5ZIN7zXYFrN7hbzyC0rf17shUHeYDVqlPicZWDFY5ftisRyyHxpQojo9Wd2ZQlzb5094sX2gMvoboVmwizLmP0cjz91c0VewKBgQDDqQW90PVHNpU/LLJG/bk5aAWImIgU6ytNshgJAr8LRiamQ+pC/uBoAOvx0XP4jdiEz+i0+Bpmq9BfWLnXORuWbCo6FsdsycGvGr7JdgJvR7zhPgeAm99RP0q1SJxhbbYBgvqII+woQkYAof/OqZE4+gMiAsy4pnMHwoaZgNX6kg==';
+    private $platform_public_key  = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAigLUoKx2TmHwgI4beRwgp07q7QncR+BQcAs/j4dtYH0W9Gq3pKwhmEerHUozEXjWtGuT119AzGlIY8UnXA+NRX7h04OEm6tpLAl6yVo3gLeSYrKIyjXPIa4S3mCzApXrjx4/KWNcvX0xK9ly9iV3R0nCoYb9ZR/HttS03zt+9JQ3MVTXm9kBUDnC6A13lnDXKO54tBoRIXKANTxoVi7uSwuim5qFofh6AowKEwyBDEvVnburdDo7ZL7FL0ntglA2NfUF2Viyby0HNMmcvbl6fbm+z5jXLcK++RNJQZoiTtvej6S19eH38ihll0cdUqtibO5oi9xzLyjEJq/bTexdQQIDAQAB';
+    private $order_no             = '';
+    private $money                = 0.01;
+    private $remark               = '';
+    private $notify_url           = '/api/jx_pay/pay_notify';
+
+    /**
+     * 微信手机支付
+     * @param $order_no
+     * @param $money
+     * @param $remark
+     * @param $notify_url
+     * @return bool
+     */
+    public function wxWapPay($order_no, $money, $remark = '', $notify_url = '')
+    {
+        $this->order_no   = $order_no;
+        $this->money      = $money;
+        $this->remark     = $remark;
+        $this->notify_url = $notify_url;
+        try {
+            $result   = $this->addOrder('wechatLiteH5');
+            $code_url = $result['payurl'];
+        } catch (\Exception $e) {
+            return $this->error('微信支付下单失败!' . $e->getMessage());
+        }
+        return $this->success('微信支付下单成功!', [
+            'type' => 'qrcode', 'page' => 'wxpay_h5', 'url' => $code_url
+        ]);
+    }
+
+    /**
+     * 统一下单
+     * @param string $trade_type
+     * @param $sub_appid
+     * @param $sub_openid
+     * @return mixed
+     */
+    private function addOrder(string $trade_type, $sub_appid = null, $sub_openid = null)
+    {
+        // 客户端IP 防风控
+        $client_ip = Request::instance()->ip();
+
+        $client = new PasspayClient($this->host, $this->mch_id, $this->merchant_private_key, $this->platform_public_key);
+
+        $notify_url = CurlUtil::getHttp($this->notify_url, false);
+        $param      = [
+            'trade_type'   => $trade_type,
+            'out_trade_no' => $this->order_no,
+            'total_amount' => $this->money,
+            "subject"      => $this->remark,
+            'notify_url'   => $notify_url,
+            'return_url'   => '',//支付完成跳转地址,仅支持非原生支付,快捷支付,网银等支持同步跳转的支付类型
+            'client_ip'    => $client_ip,
+        ];
+        if ($sub_appid && $sub_openid) {
+            $param += [
+                'sub_appid'     => $sub_appid,
+                'user_id'       => $sub_openid,
+                'channe_expend' => json_encode(['is_raw' => 1])
+            ];
+        }
+
+        return $client->execute('pay.order/create', $param);
+    }
+}

+ 35 - 0
application/utils/JingXiu/Module.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace app\utils\JingXiu;
+
+use app\utils\CurlUtil;
+
+class Module
+{
+    protected $message = '';
+    protected $data = [];
+
+    protected function success($message = '', $data = [])
+    {
+        $this->message = $message;
+        $this->data = $data;
+        return true;
+    }
+
+    protected function error($message = '', $data = [])
+    {
+        $this->message = $message;
+        $this->data = $data;
+        return false;
+    }
+
+    public function getMessage()
+    {
+        return $this->message;
+    }
+
+    public function getData()
+    {
+        return $this->data;
+    }
+}

+ 114 - 0
application/utils/JingXiu/PasspayClient.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace app\utils\JingXiu;
+use app\utils\CurlUtil;
+
+class PasspayClient
+{
+    //接口地址
+    private $apiurl;
+
+    //商户号
+    private $mchid;
+
+    //商户私钥
+    private $merchant_private_key;
+
+    //平台公钥
+    private $platform_public_key;
+
+    private $charset   = 'utf-8';
+    private $sign_type = 'RSA2';
+    private $version   = '1.0';
+
+    public function __construct($apiurl, $mchid, $merchant_private_key, $platform_public_key)
+    {
+        $this->apiurl               = $apiurl;
+        $this->mchid                = $mchid;
+        $this->merchant_private_key = $merchant_private_key;
+        $this->platform_public_key  = $platform_public_key;
+    }
+
+    //请求API接口并解析返回数据
+    public function execute($method, $bizContent)
+    {
+        $requrl         = $this->apiurl . $method;
+        $params         = [
+            'mchid'       => $this->mchid,
+            'method'      => $method,
+            'charset'     => $this->charset,
+            'sign_type'   => $this->sign_type,
+            'timestamp'   => time(),
+            'version'     => $this->version,
+            'biz_content' => json_encode($bizContent)
+        ];
+        $params['sign'] = $this->generateSign($params);
+        $response = CurlUtil::postJson($requrl,$params);dd($response);
+        if (isset($response['code']) && $response['code'] == 1) {
+            return $response['data'];
+        } elseif (isset($response['msg'])) {
+            throw new \Exception($response['msg']);
+        } else {
+            throw new \Exception('返回数据解析失败');
+        }
+    }
+
+    //获取待签名字符串
+    private function getSignContent($param)
+    {
+        ksort($param);
+        $signstr = '';
+
+        foreach ($param as $k => $v) {
+            if ($k != "sign" && $v !== '' && $v !== null) {
+                $signstr .= $k . '=' . $v . '&';
+            }
+        }
+        $signstr = substr($signstr, 0, -1);
+        return $signstr;
+    }
+
+    //请求参数签名
+    private function generateSign($param)
+    {
+        return $this->rsaPrivateSign($this->getSignContent($param));
+    }
+
+    //验签方法
+    public function verifySign($param)
+    {
+        if (empty($param['sign'])) return false;
+        return $this->rsaPubilcSign($this->getSignContent($param), $param['sign']);
+    }
+
+    //商户私钥签名
+    private function rsaPrivateSign($data)
+    {
+        $priKey = $this->merchant_private_key;
+        $res    = "-----BEGIN RSA PRIVATE KEY-----\n" .
+            wordwrap($priKey, 64, "\n", true) .
+            "\n-----END RSA PRIVATE KEY-----";
+        $pkeyid = openssl_pkey_get_private($res);
+        if (!$pkeyid) {
+            throw new Exception('签名失败,商户私钥不正确');
+        }
+        openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_SHA256);
+        $signature = base64_encode($signature);
+        return $signature;
+    }
+
+    //平台公钥验签
+    private function rsaPubilcSign($data, $signature)
+    {
+        $pubKey   = $this->platform_public_key;
+        $res      = "-----BEGIN PUBLIC KEY-----\n" .
+            wordwrap($pubKey, 64, "\n", true) .
+            "\n-----END PUBLIC KEY-----";
+        $pubkeyid = openssl_pkey_get_public($res);
+        if (!$pubkeyid) {
+            throw new Exception('验签失败,平台公钥不正确');
+        }
+        $result = openssl_verify($data, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_SHA256);
+        return $result === 1;
+    }
+}

+ 0 - 5
public/.htaccess

@@ -1,5 +0,0 @@
-location / {
-	if (!-e $request_filename){
-		rewrite  ^(.*)$  /index.php?s=$1  last;   break;
-	}
-}