config = config('ccblife'); $this->orderService = new CcbOrderService(); } /** * 生成建行支付串 * 用于前端JSBridge调用建行收银台 * * @param int $orderId Shopro订单ID * @return array ['status' => bool, 'message' => string, 'data' => array] */ public function generatePaymentString($orderId) { try { // 获取订单信息 $order = Order::find($orderId); if (!$order) { throw new \Exception('订单不存在'); } // 检查订单状态 if ($order['status'] != 'unpaid') { throw new \Exception('订单状态不正确'); } // 获取用户建行ID $user = Db::name('user')->where('id', $order['user_id'])->field('ccb_user_id')->find(); if (empty($user['ccb_user_id'])) { throw new \Exception('用户未绑定建行账号'); } // 生成支付串签名(Shopro 使用 total_fee 作为支付金额字段) $result = CcbMD5::generatePaymentSignature( $this->config['merchant_id'], $this->config['pos_id'], $this->config['branch_id'], $order['order_sn'], number_format($order['total_fee'], 2, '.', ''), // 使用 total_fee $this->config['private_key'], '01', // 币种:人民币 '530550' // 交易码 ); // 构建完整的支付URL $paymentUrl = $this->buildPaymentUrl($result['params'], $result['mac']); // 记录支付请求 $this->recordPaymentRequest($orderId, $result); return [ 'status' => true, 'message' => '支付串生成成功', 'data' => [ 'payment_string' => $result['payment_string'], 'mac' => $result['mac'], 'payment_url' => $paymentUrl, 'order_sn' => $order['order_sn'], 'amount' => number_format($order['total_fee'], 2, '.', '') ] ]; } catch (\Exception $e) { Log::error('建行支付串生成失败: ' . $e->getMessage()); return [ 'status' => false, 'message' => $e->getMessage(), 'data' => null ]; } } /** * 处理支付回调 * 建行支付完成后的同步回调 * * @param array $params URL参数 * @return array */ public function handleCallback($params) { try { // 解密ccbParamSJ参数 if (isset($params['ccbParamSJ'])) { $decryptedParams = CcbUrlDecrypt::decrypt($params['ccbParamSJ'], $this->config['service_id']); if ($decryptedParams) { $params = array_merge($params, $decryptedParams); } } // 获取关键参数 $orderSn = $params['ORDERID'] ?? ''; $posId = $params['POSID'] ?? ''; $success = $params['SUCCESS'] ?? 'N'; // 验证参数 if (empty($orderSn)) { throw new \Exception('订单号不能为空'); } // 验证POS号 if ($posId != $this->config['pos_id']) { throw new \Exception('POS号验证失败'); } // 查询订单 $order = Order::where('order_sn', $orderSn)->find(); if (!$order) { throw new \Exception('订单不存在'); } // 处理支付结果 if ($success == 'Y') { // 支付成功,更新订单状态 $this->updateOrderPaymentStatus($order, $params); // 同步订单到建行 $this->orderService->pushOrder($order['id']); return [ 'status' => true, 'message' => '支付成功', 'data' => [ 'order_id' => $order['id'], 'order_sn' => $orderSn, 'amount' => $params['PAYMENT'] ?? '' ] ]; } else { // 支付失败 return [ 'status' => false, 'message' => '支付失败', 'data' => [ 'order_id' => $order['id'], 'order_sn' => $orderSn, 'error_code' => $params['ERRCODE'] ?? '', 'error_msg' => $params['ERRMSG'] ?? '' ] ]; } } catch (\Exception $e) { Log::error('建行支付回调处理失败: ' . $e->getMessage()); return [ 'status' => false, 'message' => $e->getMessage(), 'data' => null ]; } } /** * 处理异步通知 * 建行支付异步通知处理 * * @param array $params 通知参数 * @return string 'success' 或 'fail' */ public function handleNotify($params) { try { // 验证签名 if (!$this->verifyNotifySignature($params)) { throw new \Exception('签名验证失败'); } // 获取订单信息 $orderSn = $params['ORDERID'] ?? ''; $order = Order::where('order_sn', $orderSn)->find(); if (!$order) { throw new \Exception('订单不存在'); } // 如果订单已支付,直接返回成功 if ($order['status'] == 'paid') { return 'success'; } // 更新订单状态 $this->updateOrderPaymentStatus($order, $params); // 同步到建行 $this->orderService->pushOrder($order['id']); return 'success'; } catch (\Exception $e) { Log::error('建行支付异步通知处理失败: ' . $e->getMessage()); return 'fail'; } } /** * 验证支付结果 * 主动查询订单支付状态 * * @param string $orderSn 订单号 * @return bool */ public function verifyPayment($orderSn) { try { // 查询建行订单状态 $result = $this->orderService->queryOrder($orderSn); if ($result['status']) { $data = $result['data']['CLD_BODY'] ?? []; $txnStatus = $data['TXN_STATUS'] ?? ''; // 00=交易成功 return $txnStatus == '00'; } return false; } catch (\Exception $e) { Log::error('建行支付验证失败: ' . $e->getMessage()); return false; } } /** * 构建支付URL * * @param array $params 支付参数 * @param string $mac 签名 * @return string */ private function buildPaymentUrl($params, $mac) { // 添加必要参数 $params['MAC'] = $mac; $params['REMARK2'] = $this->config['service_id']; // 服务方编号 // 生成查询字符串 $queryString = http_build_query($params); // 返回完整URL(实际使用时通过JSBridge调用,不直接访问) return $this->config['cashier_url'] . '?' . $queryString; } /** * 更新订单支付状态 * * @param object $order 订单对象 * @param array $params 支付参数 */ private function updateOrderPaymentStatus($order, $params) { // 更新订单状态为已支付 Order::where('id', $order['id'])->update([ 'status' => 'paid', 'pay_type' => 'ccb', 'paytime' => time(), 'transaction_id' => $params['ORDERID'] ?? '', 'updatetime' => time() ]); // 记录支付日志 $this->recordPaymentLog($order['id'], 'payment_success', $params); } /** * 验证异步通知签名 * * @param array $params 通知参数 * @return bool */ private function verifyNotifySignature($params) { // 获取签名 $signature = $params['SIGN'] ?? ''; if (empty($signature)) { return false; } // 移除签名字段 unset($params['SIGN']); // 按照建行要求的方式构建签名字符串 ksort($params); $signStr = ''; foreach ($params as $key => $value) { if ($value !== '') { $signStr .= $key . '=' . $value . '&'; } } $signStr = rtrim($signStr, '&'); // 使用私钥计算签名 $expectedSign = md5($signStr . $this->config['private_key']); return strtolower($signature) === strtolower($expectedSign); } /** * 记录支付请求 * * @param int $orderId 订单ID * @param array $paymentData 支付数据 */ private function recordPaymentRequest($orderId, $paymentData) { // 获取订单信息 $order = Order::find($orderId); $user = Db::name('user')->where('id', $order['user_id'])->field('ccb_user_id')->find(); // 记录到建行支付日志表 Db::name('ccb_payment_log')->insert([ 'order_id' => $orderId, 'order_sn' => $order['order_sn'], 'pay_flow_id' => $order['order_sn'], // 使用订单号作为流水号 'payment_string' => $paymentData['payment_string'] ?? '', 'user_id' => $order['user_id'], 'ccb_user_id' => $user['ccb_user_id'] ?? '', 'amount' => $order['total_fee'], 'status' => 0, // 待支付 'create_time' => time() ]); } /** * 记录支付日志 * * @param int $orderId 订单ID * @param string $type 日志类型 * @param array $data 数据 */ private function recordPaymentLog($orderId, $type, $data) { // 更新建行支付日志 if ($type == 'payment_success') { Db::name('ccb_payment_log') ->where('order_id', $orderId) ->update([ 'status' => 1, // 支付成功 'pay_time' => time(), 'trans_id' => $data['ORDERID'] ?? '', 'callback_data' => json_encode($data, JSON_UNESCAPED_UNICODE) ]); } } /** * 生成退款申请 * * @param int $orderId 订单ID * @param float $refundAmount 退款金额 * @param string $refundReason 退款原因 * @return array */ public function refund($orderId, $refundAmount, $refundReason = '') { try { // 调用订单服务处理退款 $result = $this->orderService->refundOrder($orderId, $refundAmount, $refundReason); if ($result['status']) { // 记录退款日志 $this->recordPaymentLog($orderId, 'refund_request', [ 'amount' => $refundAmount, 'reason' => $refundReason ]); } return $result; } catch (\Exception $e) { Log::error('建行退款申请失败: ' . $e->getMessage()); return [ 'status' => false, 'message' => $e->getMessage(), 'data' => null ]; } } }