$v) { if ($v !== "" && $k != 'sign' && $k != 'sign_type' && !is_array($v)) { $str .= "{$k}={$v}&"; } } $str = rtrim($str, "&"); // 拼接PEM格式 $privateKey = "-----BEGIN PRIVATE KEY-----\n" . chunk_split($this->merchantPrivateKey, 64, "\n") . "-----END PRIVATE KEY-----\n"; $res = openssl_get_privatekey($privateKey); if (!$res) { throw new \Exception('私钥格式错误或无法加载'); } openssl_sign($str, $sign, $res, OPENSSL_ALGO_SHA256); return base64_encode($sign); } // 生成 MD5 签名 private function makeMd5Sign($data) { $md5_key = $this->key; // 商户密钥 ksort($data); // 按照 ASCII 码排序 $str = ''; foreach ($data as $k => $v) { if ($v !== "" && $k != 'sign' && $k != 'sign_type') { // 排除空值、sign 和 sign_type $str .= "{$k}={$v}&"; } } $str = rtrim($str, "&"); // 去掉最后一个 & $str .= $md5_key; // 按文档要求,直接拼接 KEY return md5($str); // 小写 } // 发起Http请求 private function HttpRequest($url, $postData) { // 将数组编码为 x-www-form-urlencoded 格式的查询字符串 $postData1 = http_build_query($postData); // 初始化 cURL 会话 $ch = curl_init($url); // 设置 cURL 选项 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 将响应作为字符串返回 curl_setopt($ch, CURLOPT_POST, true); // 使用 POST 方法 curl_setopt($ch, CURLOPT_POSTFIELDS, $postData1); // 设置 POST 数据 curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/x-www-form-urlencoded' // 设置 Content-Type 头 )); // 执行请求并获取响应 $response = curl_exec($ch); // 关闭 cURL 会话 curl_close($ch); // 检查请求是否成功 if ($response === false) { return "cURL Error: " . curl_error($ch); } else { // 处理响应 return $response; } } // JSON清理 function fixEscapedJson($jsonString) { // 移除多余的反斜杠 $fixed = stripslashes($jsonString); // 替换HTML实体 $fixed = str_replace( ['"', '&', '<', '>'], ['"', '&', '<', '>'], $fixed ); return $fixed; } //拉起订单 public function order() { $goodsName = $this->request->post('goodsName'); // 商品名称 $amount = $this->request->post('amount'); // 商品金额 $type = $this->request->post('type'); // 支付方式 $isVip = $this->request->post('isVip') ?? 0; // 是否为购买VIP $user = $this->getUser(); if (!$goodsName || !$type || !is_numeric($amount)) { $this->fail(500, '参数校验错误'); } if (!$user) { $this->fail(500, '用户未登录'); } // 参数构建 $data['pid'] = $this->merchantId; $data['method'] = 'jump'; // 接口类型 $data['device'] = 'pc'; // 设备类型 $data['type'] = $type; // 支付方式 $data['out_trade_no'] = date('YmdHis') . strval(mt_rand(100000, 999999)); // 订单号 $data['notify_url'] = 'https://api.danjiwanjia.com/pay/payCallback'; // 服务器异步通知地址 $data['return_url'] = 'https://www.danjiwanjia.com/#/userCenter'; // 页面跳转通知地址 $data['name'] = $goodsName; // 商品名称 $data['money'] = $amount; // 商品金额 $data['clientip'] = $this->request->ip(); // 用户ip地址 $data['timestamp'] = time(); // 当前时间戳 $data['param'] = json_encode(['isVip' => $isVip]); // 业务拓展参数 通过此参数判断用户是否通过VIP页面调起支付 $data['sign_type'] = 'RSA'; // 当前时间戳 $data['sign'] = $this->makeRsaSign($data); // 签名生成 // 插入数据表 Db::table('tb_user_order') ->insert([ 'user_id' => $user->user_id, 'balance' => $amount, 'state' => 0, 'created_at' => time(), 'order_no' => $data['out_trade_no'], ]); // 发起请求 $url = $this->baseUrl . '/api/pay/create'; $result = $this->HttpRequest($url, $data); // header('Content-Type: application/json'); // echo $result; // exit(); $result = json_decode($result, true); if($result['code'] == 0) { $this->success('success', $result['pay_info']); } else { $this->fail(500, $result['msg']); } } //获取我的订单 public function getMyOrder() { $page = $this->request->post('page'); //页码 $pageNum = $this->request->post('pageNum'); //每页显示的数据条数 if (!is_numeric($pageNum) || !is_numeric($page)) { $this->fail(500, 'page或pageNum参数校验错误'); } $data = Db::table('tb_user_order') ->where(['user_id' => $this->getUser()->user_id]) ->order('created_at DESC') ->paginate([ 'page' => $page, 'list_rows' => $pageNum, ]); $this->success('success', $data); } /** * 支付回调 */ public function payCallback() { $data = $this->request->param(); error_log('payCallback: '.json_encode($data)); if(!$data) { exit('无法获取到传参'); } $order_sn = $data['out_trade_no']; $amount = $data['money']; $isVip = json_decode($this->fixEscapedJson($data['param']), true)['isVip']; $order = Db::name('tb_user_order')->where('order_no', $order_sn)->find(); if (!$order) { exit('订单不存在'); } if ($order['state'] == 1) { // 订单已支付 不走后面的逻辑但是要返回success exit('success'); } // 订单校验通过 更新订单状态 Db::name('tb_user_order') ->where('order_no', $order_sn) ->update([ 'state' => 1, 'pay_at' => time(), ]); // 业务判断 if($isVip == 1) { // 增加用户VIP时长 // 查找对应价格VIP等级 $vipInfo = Db::name('tb_user_vip_price') ->where('user_id', $order['user_id']) ->where('price', $amount) ->find(); // 首先判断当前用户是否已经为VIP $userVip = Db::name('tb_user_vip')->where('user_id', $order['user_id'])->find(); if($userVip) { // 如果用户已经为VIP 且为同类型VIP则增加VIP时长 if($userVip['type'] == $vipInfo['type']) { Db::name('tb_user_vip') ->where('user_id', $order['user_id']) ->setInc('expired_at', strtotime($vipInfo['duration'] . 'month')); } else { // 如果用户已经为VIP 且为不同类型VIP则删除原有VIP记录并新增VIP记录 Db::name('tb_user_vip') ->where('user_id', $order['user_id']) ->delete(); Db::name('tb_user_vip')->insert([ 'user_id' => $order['user_id'], 'type' => $vipInfo['type'], 'expired_at' => strtotime($vipInfo['duration'] . 'month') ]); } } else { // 如果用户不是VIP 则新增VIP记录 Db::name('tb_user_vip')->insert([ 'user_id' => $order['user_id'], 'type' => $vipInfo['type'], 'expired_at' => strtotime($vipInfo['duration'] . 'month') ]); } } else { // 增加用户余额 Db::name('tb_user') ->where('id', $order['user_id']) ->inc('balance', $amount * 2) // 限时活动 实际到账金额为金额的2倍 ->update(); } exit('success'); } }