fengketrade/addons/shopro/library/ccblife/CcbPaymentService.php

681 lines
25 KiB
PHP
Raw Normal View History

2025-10-17 16:32:16 +08:00
<?php
namespace addons\shopro\library\ccblife;
2025-10-17 17:18:15 +08:00
use app\admin\model\shopro\order\Order;
use think\Db;
use think\Log;
2025-10-17 16:32:16 +08:00
/**
2025-10-17 17:18:15 +08:00
* 建行生活支付服务类
* 处理支付串生成、支付回调、支付验证等业务
2025-10-17 16:32:16 +08:00
*/
class CcbPaymentService
{
/**
* 配置信息
*/
private $config;
/**
2025-10-17 17:18:15 +08:00
* 订单服务实例
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
private $orderService;
2025-10-17 16:32:16 +08:00
/**
* 构造函数
*/
2025-10-17 17:18:15 +08:00
public function __construct()
2025-10-17 16:32:16 +08:00
{
2025-10-18 15:47:25 +08:00
// 加载插件配置文件
$configFile = __DIR__ . '/../../config/ccblife.php';
if (file_exists($configFile)) {
$this->config = include $configFile;
} else {
throw new \Exception('建行生活配置文件不存在');
}
// 处理BASE64格式的密钥添加PEM包装
$this->config = $this->processPemKeys($this->config);
2025-10-17 17:18:15 +08:00
$this->orderService = new CcbOrderService();
2025-10-17 16:32:16 +08:00
}
2025-10-18 15:47:25 +08:00
/**
* 处理PEM格式密钥
* 如果密钥是BASE64格式不含-----BEGIN-----则添加PEM包装
*
* @param array $config 配置数组
* @return array
*/
private function processPemKeys($config)
{
// 处理私钥
if (!empty($config['private_key']) && strpos($config['private_key'], '-----BEGIN') === false) {
$config['private_key'] = "-----BEGIN PRIVATE KEY-----\n"
. chunk_split($config['private_key'], 64, "\n")
. "-----END PRIVATE KEY-----";
}
// 处理公钥
if (!empty($config['public_key']) && strpos($config['public_key'], '-----BEGIN') === false) {
$config['public_key'] = "-----BEGIN PUBLIC KEY-----\n"
. chunk_split($config['public_key'], 64, "\n")
. "-----END PUBLIC KEY-----";
}
// 兼容merchant_public_key字段
if (empty($config['merchant_public_key'])) {
$config['merchant_public_key'] = $config['public_key'];
}
// 处理平台公钥
if (!empty($config['platform_public_key'])) {
// 如果有配置平台公钥且是BASE64格式添加PEM包装
if (strpos($config['platform_public_key'], '-----BEGIN') === false) {
$config['platform_public_key'] = "-----BEGIN PUBLIC KEY-----\n"
. chunk_split($config['platform_public_key'], 64, "\n")
. "-----END PUBLIC KEY-----";
}
} else {
// 如果没有配置平台公钥,使用商户公钥作为默认值
$config['platform_public_key'] = $config['public_key'];
}
return $config;
}
2025-10-17 16:32:16 +08:00
/**
2025-10-17 17:18:15 +08:00
* 生成建行支付串
* 用于前端JSBridge调用建行收银台
2025-10-17 16:32:16 +08:00
*
2025-10-18 01:16:25 +08:00
* ⚠️ 注意必须包含所有必需参数签名前按ASCII排序
*
2025-10-17 17:18:15 +08:00
* @param int $orderId Shopro订单ID
* @return array ['status' => bool, 'message' => string, 'data' => array]
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
public function generatePaymentString($orderId)
2025-10-17 16:32:16 +08:00
{
2025-10-20 15:51:06 +08:00
// ⚠️ 开启事务保护,确保数据一致性
Db::startTrans();
2025-10-17 16:32:16 +08:00
try {
2025-10-17 17:18:15 +08:00
// 获取订单信息
$order = Order::find($orderId);
if (!$order) {
throw new \Exception('订单不存在');
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 检查订单状态
if ($order['status'] != 'unpaid') {
throw new \Exception('订单状态不正确');
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 获取用户建行ID
$user = Db::name('user')->where('id', $order['user_id'])->field('ccb_user_id')->find();
if (empty($user['ccb_user_id'])) {
throw new \Exception('用户未绑定建行账号');
}
2025-10-17 16:32:16 +08:00
2025-10-18 01:16:25 +08:00
// 生成支付流水号(使用订单号作为唯一标识)
$payFlowId = 'PAY' . date('YmdHis') . mt_rand(100000, 999999);
// 构建完整的支付参数34个参数
$paymentParams = [
'MERCHANTID' => $this->config['merchant_id'], // 商户代码
'POSID' => $this->config['pos_id'], // 柜台代码
'BRANCHID' => $this->config['branch_id'], // 分行代码
'ORDERID' => $payFlowId, // 支付流水号(必须唯一!)
2025-10-18 15:47:25 +08:00
'PAYMENT' => number_format($order['pay_fee'], 2, '.', ''), // 支付金额Shopro使用pay_fee
2025-10-18 01:16:25 +08:00
'CURCODE' => '01', // 币种01=人民币)
'TXCODE' => '520100', // 交易码520100=即时支付)
'REMARK1' => '', // 备注1
'REMARK2' => $this->config['service_id'], // 备注2服务方编号
'TYPE' => '1', // 支付类型1=个人)
'GATEWAY' => '0', // 网关标志
2025-10-18 15:47:25 +08:00
'CLIENTIP' => $this->getClientIp(), // 客户端IP
2025-10-18 01:16:25 +08:00
'REGINFO' => '', // 注册信息
'PROINFO' => $this->buildProductInfo($order), // 商品信息
'REFERER' => '', // 来源页面
'THIRDAPPINFO' => 'comccbpay1234567890cloudmerchant', // 第三方应用信息(固定值)
'USER_ORDERID' => $order['order_sn'], // 商户订单号
'TIMEOUT' => date('YmdHis', strtotime('+30 minutes')) // 超时时间
];
// 按ASCII排序
ksort($paymentParams);
// 生成签名字符串
$signString = http_build_query($paymentParams);
2025-10-20 15:29:15 +08:00
// ⚠️ 建行支付串签名规则(v2.2版本):
// 1. PLATFORMPUB字段仅参与MD5计算,不作为HTTP参数传递
// 2. 签名 = MD5(参数字符串 + &PLATFORMPUB= + 服务方公钥内容)
// 3. 生成32位大写MD5字符串(对照MD5Util.java第30行)
$platformPubKey = $this->config['public_key']; // 服务方公钥
$mac = strtoupper(md5($signString . '&PLATFORMPUB=' . $platformPubKey));
// 使用RSA加密商户公钥后30位(用于ENCPUB字段)
2025-10-18 01:16:25 +08:00
$encryption = new CcbEncryption($this->config);
2025-10-20 15:29:15 +08:00
$encpub = $encryption->encryptMerchantPublicKeyLast30();
2025-10-18 01:16:25 +08:00
// 组装最终支付串
$finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub);
// 保存支付流水号到订单
Order::where('id', $orderId)->update([
'ccb_pay_flow_id' => $payFlowId,
'updatetime' => time()
]);
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 构建完整的支付URL
2025-10-18 01:16:25 +08:00
$paymentUrl = $this->config['cashier_url'] . '?' . $finalPaymentString;
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 记录支付请求
2025-10-18 01:16:25 +08:00
$this->recordPaymentRequest($orderId, [
'payment_string' => $finalPaymentString,
'params' => $paymentParams,
'mac' => $mac,
'pay_flow_id' => $payFlowId
]);
2025-10-17 16:32:16 +08:00
2025-10-20 15:51:06 +08:00
// ✅ 提交事务
Db::commit();
2025-10-17 16:32:16 +08:00
return [
2025-10-17 17:18:15 +08:00
'status' => true,
'message' => '支付串生成成功',
'data' => [
2025-10-18 01:16:25 +08:00
'payment_string' => $finalPaymentString,
'mac' => $mac,
2025-10-17 17:18:15 +08:00
'payment_url' => $paymentUrl,
'order_sn' => $order['order_sn'],
2025-10-18 01:16:25 +08:00
'pay_flow_id' => $payFlowId,
2025-10-20 15:51:06 +08:00
'amount' => number_format($order['pay_fee'], 2, '.', ''),
'order_id' => $orderId // 补充order_id字段
2025-10-17 17:18:15 +08:00
]
2025-10-17 16:32:16 +08:00
];
2025-10-17 17:18:15 +08:00
} catch (\Exception $e) {
2025-10-20 15:51:06 +08:00
// ❌ 回滚事务
Db::rollback();
2025-10-17 17:18:15 +08:00
Log::error('建行支付串生成失败: ' . $e->getMessage());
2025-10-17 16:32:16 +08:00
return [
2025-10-17 17:18:15 +08:00
'status' => false,
'message' => $e->getMessage(),
'data' => null
2025-10-17 16:32:16 +08:00
];
}
}
2025-10-18 15:47:25 +08:00
/**
* 获取客户端IP
*
* @return string
*/
private function getClientIp()
{
try {
$ip = request()->ip();
return $ip ?: '127.0.0.1';
} catch (\Exception $e) {
// CLI模式或其他异常情况使用默认值
return '127.0.0.1';
}
}
2025-10-18 01:16:25 +08:00
/**
* 构建商品信息字符串
*
* @param object $order 订单对象
* @return string
*/
private function buildProductInfo($order)
{
// 获取订单商品
$orderItems = Db::name('shopro_order_item')
->where('order_id', $order['id'])
->limit(3) // 最多取3个商品
->column('goods_title');
if (empty($orderItems)) {
return '商城订单';
}
// 拼接商品名称
return implode(',', $orderItems);
}
2025-10-17 16:32:16 +08:00
/**
* 处理支付回调
2025-10-17 17:18:15 +08:00
* 建行支付完成后的同步回调
*
* @param array $params URL参数
* @return array
*/
public function handleCallback($params)
{
try {
2025-10-20 09:23:30 +08:00
// 解密ccbParamSJ参数使用服务方私钥
2025-10-17 17:18:15 +08:00
if (isset($params['ccbParamSJ'])) {
2025-10-20 09:23:30 +08:00
$decryptedParams = CcbUrlDecrypt::decrypt($params['ccbParamSJ'], $this->config['private_key']);
2025-10-17 17:18:15 +08:00
if ($decryptedParams) {
$params = array_merge($params, $decryptedParams);
}
}
// 获取关键参数
2025-10-18 01:16:25 +08:00
$payFlowId = $params['ORDERID'] ?? ''; // 支付流水号
$userOrderId = $params['USER_ORDERID'] ?? ''; // 商户订单号
2025-10-17 17:18:15 +08:00
$posId = $params['POSID'] ?? '';
$success = $params['SUCCESS'] ?? 'N';
// 验证参数
2025-10-18 01:16:25 +08:00
if (empty($payFlowId)) {
throw new \Exception('支付流水号不能为空');
2025-10-17 17:18:15 +08:00
}
// 验证POS号
if ($posId != $this->config['pos_id']) {
throw new \Exception('POS号验证失败');
}
2025-10-18 01:16:25 +08:00
// ⚠️ 重要ORDERID是支付流水号不是订单号
// 优先使用USER_ORDERID查询如果没有则用ccb_pay_flow_id查询
if (!empty($userOrderId)) {
$order = Order::where('order_sn', $userOrderId)->find();
} else {
$order = Order::where('ccb_pay_flow_id', $payFlowId)->find();
}
2025-10-17 17:18:15 +08:00
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'],
2025-10-18 01:16:25 +08:00
'order_sn' => $order['order_sn'], // ✅ 返回真正的订单号
'pay_flow_id' => $payFlowId, // 支付流水号
2025-10-17 17:18:15 +08:00
'amount' => $params['PAYMENT'] ?? ''
]
];
} else {
// 支付失败
return [
'status' => false,
'message' => '支付失败',
'data' => [
'order_id' => $order['id'],
2025-10-18 01:16:25 +08:00
'order_sn' => $order['order_sn'], // ✅ 返回真正的订单号
'pay_flow_id' => $payFlowId, // 支付流水号
2025-10-17 17:18:15 +08:00
'error_code' => $params['ERRCODE'] ?? '',
'error_msg' => $params['ERRMSG'] ?? ''
]
];
}
} catch (\Exception $e) {
Log::error('建行支付回调处理失败: ' . $e->getMessage());
return [
'status' => false,
'message' => $e->getMessage(),
'data' => null
];
}
}
/**
* 处理异步通知
* 建行支付异步通知处理
2025-10-17 16:32:16 +08:00
*
2025-10-20 16:36:06 +08:00
* ⚠️ 注意:这是唯一可信的支付确认来源!
* 返回订单ID供控制器调用pushOrderToCcb()推送到外联系统
*
2025-10-17 17:18:15 +08:00
* @param array $params 通知参数
2025-10-20 16:36:06 +08:00
* @return array ['status' => 'success'|'fail', 'order_id' => int, 'order_sn' => string]
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
public function handleNotify($params)
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
try {
// 验证签名
if (!$this->verifyNotifySignature($params)) {
throw new \Exception('签名验证失败');
}
2025-10-18 01:16:25 +08:00
// ⚠️ 重要ORDERID是支付流水号不是订单号
// 优先使用USER_ORDERID查询如果没有则用ccb_pay_flow_id查询
$payFlowId = $params['ORDERID'] ?? ''; // 支付流水号
$userOrderId = $params['USER_ORDERID'] ?? ''; // 商户订单号
if (!empty($userOrderId)) {
$order = Order::where('order_sn', $userOrderId)->find();
} else {
$order = Order::where('ccb_pay_flow_id', $payFlowId)->find();
}
2025-10-17 17:18:15 +08:00
if (!$order) {
throw new \Exception('订单不存在');
}
2025-10-20 16:36:06 +08:00
// 如果订单已支付,直接返回成功(幂等性)
2025-10-17 17:18:15 +08:00
if ($order['status'] == 'paid') {
2025-10-20 16:36:06 +08:00
Log::info('[建行通知] 订单已支付,跳过处理 order_id:' . $order->id);
return [
'status' => 'success',
'order_id' => $order->id,
'order_sn' => $order->order_sn,
'already_paid' => true, // 标记为已支付
];
2025-10-17 17:18:15 +08:00
}
// 更新订单状态
$this->updateOrderPaymentStatus($order, $params);
2025-10-20 16:36:06 +08:00
Log::info('[建行通知] 订单状态更新成功 order_id:' . $order->id . ' order_sn:' . $order->order_sn);
2025-10-17 17:18:15 +08:00
2025-10-20 16:36:06 +08:00
// ✅ 返回订单ID供控制器推送到外联系统
return [
'status' => 'success',
'order_id' => $order->id,
'order_sn' => $order->order_sn,
'already_paid' => false, // 新支付
];
2025-10-17 17:18:15 +08:00
} catch (\Exception $e) {
2025-10-20 16:36:06 +08:00
Log::error('[建行通知] 处理失败: ' . $e->getMessage());
return [
'status' => 'fail',
'message' => $e->getMessage(),
];
2025-10-17 17:18:15 +08:00
}
2025-10-17 16:32:16 +08:00
}
/**
* 验证支付结果
2025-10-17 17:18:15 +08:00
* 主动查询订单支付状态
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param string $orderSn 订单号
* @return bool
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
public function verifyPayment($orderSn)
2025-10-17 16:32:16 +08:00
{
2025-10-17 17:18:15 +08:00
try {
// 查询建行订单状态
$result = $this->orderService->queryOrder($orderSn);
if ($result['status']) {
$data = $result['data']['CLD_BODY'] ?? [];
$txnStatus = $data['TXN_STATUS'] ?? '';
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
// 00=交易成功
return $txnStatus == '00';
}
return false;
} catch (\Exception $e) {
Log::error('建行支付验证失败: ' . $e->getMessage());
return false;
2025-10-17 16:32:16 +08:00
}
2025-10-17 17:18:15 +08:00
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
/**
* 构建支付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;
2025-10-17 16:32:16 +08:00
}
/**
2025-10-17 17:18:15 +08:00
* 更新订单支付状态
2025-10-17 16:32:16 +08:00
*
2025-10-17 17:18:15 +08:00
* @param object $order 订单对象
* @param array $params 支付参数
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
private function updateOrderPaymentStatus($order, $params)
2025-10-17 16:32:16 +08:00
{
2025-10-18 15:47:25 +08:00
// ⚠️ 重要字段说明:
// 1. paid_time: Shopro使用毫秒时间戳time() * 1000
// 2. pay_type: 建行支付暂用'offline'(建行线下银行支付),后续可扩展枚举
// 3. transaction_id: 存储建行支付流水号ORDERID
2025-10-17 17:18:15 +08:00
Order::where('id', $order['id'])->update([
'status' => 'paid',
2025-10-18 15:47:25 +08:00
'pay_type' => 'offline', // 建行支付归类为线下银行支付
'paid_time' => time() * 1000, // 毫秒时间戳
'transaction_id' => $params['ORDERID'] ?? '', // 建行支付流水号
2025-10-17 17:18:15 +08:00
'updatetime' => time()
]);
// 记录支付日志
$this->recordPaymentLog($order['id'], 'payment_success', $params);
2025-10-17 16:32:16 +08:00
}
/**
2025-10-17 17:18:15 +08:00
* 验证异步通知签名
2025-10-17 16:32:16 +08:00
*
2025-10-20 15:29:15 +08:00
* ⚠️ 建行异步通知签名规则:
* 1. SIGN字段为256字符十六进制字符串(2048位RSA签名)
* 2. NT_TYPE=YS时,使用"建行生活分配的服务商支付验签公钥"
* 3. 签名算法: RSA-SHA256或SHA1(需建行技术支持确认)
*
* 📌 配置说明:
* - 如果配置了ccb_payment_verify_public_key: 使用RSA验签
* - 如果未配置: 降级为POSID验证(临时方案)
*
2025-10-17 17:18:15 +08:00
* @param array $params 通知参数
* @return bool
2025-10-17 16:32:16 +08:00
*/
2025-10-17 17:18:15 +08:00
private function verifyNotifySignature($params)
2025-10-17 16:32:16 +08:00
{
2025-10-20 15:29:15 +08:00
try {
// 1. 提取SIGN字段
$sign = $params['SIGN'] ?? '';
if (empty($sign)) {
Log::error('[建行验签] SIGN字段为空');
return false;
}
// 验证SIGN长度(256个十六进制字符 = 2048位RSA签名)
if (strlen($sign) !== 256) {
Log::error('[建行验签] SIGN长度错误: ' . strlen($sign) . ', 应为256');
return false;
}
2025-10-17 16:32:16 +08:00
2025-10-20 15:29:15 +08:00
// 2. 检查是否配置了建行支付验签公钥
$ccbVerifyPublicKey = $this->config['ccb_payment_verify_public_key'] ?? '';
2025-10-17 17:18:15 +08:00
2025-10-20 15:29:15 +08:00
if (empty($ccbVerifyPublicKey)) {
// 降级方案: 未配置验签公钥时,使用POSID验证
Log::warning('[建行验签] 未配置ccb_payment_verify_public_key,使用降级验证方案');
// 验证POSID是否匹配
if (($params['POSID'] ?? '') !== $this->config['pos_id']) {
Log::error('[建行验签] POSID不匹配,预期: ' . $this->config['pos_id'] . ', 实际: ' . ($params['POSID'] ?? ''));
return false;
}
// 验证订单号是否存在
$orderSn = $params['USER_ORDERID'] ?? $params['ORDERID'] ?? '';
if (empty($orderSn)) {
Log::error('[建行验签] 订单号为空');
return false;
}
Log::warning('[建行验签] 降级验证通过,建议联系建行技术支持获取验签公钥');
return true;
2025-10-17 17:18:15 +08:00
}
2025-10-20 15:29:15 +08:00
// 3. 移除SIGN字段,构建签名原串
$verifyParams = $params;
unset($verifyParams['SIGN']);
// 4. 按key排序
ksort($verifyParams);
2025-10-17 17:18:15 +08:00
2025-10-20 15:29:15 +08:00
// 5. 构建签名原串(非空参数)
$signStr = '';
foreach ($verifyParams as $key => $value) {
// 跳过空值参数
if ($value !== '' && $value !== null) {
$signStr .= $key . '=' . $value . '&';
}
}
$signStr = rtrim($signStr, '&');
Log::info('[建行验签] 签名原串: ' . $signStr);
Log::info('[建行验签] 收到SIGN前32位: ' . substr($sign, 0, 32) . '...');
// 6. 将十六进制SIGN转为二进制
$signBinary = hex2bin($sign);
if ($signBinary === false) {
Log::error('[建行验签] SIGN十六进制转换失败');
return false;
}
// 7. 加载建行支付验签公钥
$pubKey = openssl_pkey_get_public($ccbVerifyPublicKey);
if (!$pubKey) {
Log::error('[建行验签] 验签公钥加载失败: ' . openssl_error_string());
return false;
}
// 8. 先尝试SHA256验签
$verifyResult = openssl_verify($signStr, $signBinary, $pubKey, OPENSSL_ALGO_SHA256);
if ($verifyResult !== 1) {
// SHA256失败,尝试SHA1
Log::info('[建行验签] SHA256验签失败,尝试SHA1');
$verifyResult = openssl_verify($signStr, $signBinary, $pubKey, OPENSSL_ALGO_SHA1);
}
// PHP 8+ 资源自动释放
if (PHP_VERSION_ID < 80000) {
openssl_free_key($pubKey);
}
if ($verifyResult === 1) {
Log::info('[建行验签] RSA验签成功');
return true;
} elseif ($verifyResult === 0) {
Log::error('[建行验签] RSA验签失败,签名不匹配');
return false;
} else {
Log::error('[建行验签] RSA验签过程发生错误: ' . openssl_error_string());
return false;
}
} catch (\Exception $e) {
Log::error('[建行验签] 验签异常: ' . $e->getMessage());
return false;
}
2025-10-17 17:18:15 +08:00
}
2025-10-17 16:32:16 +08:00
2025-10-17 17:18:15 +08:00
/**
* 记录支付请求
*
* @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'],
2025-10-18 01:16:25 +08:00
'pay_flow_id' => $paymentData['pay_flow_id'] ?? '', // ✅ 使用真实的支付流水号
2025-10-17 17:18:15 +08:00
'payment_string' => $paymentData['payment_string'] ?? '',
'user_id' => $order['user_id'],
'ccb_user_id' => $user['ccb_user_id'] ?? '',
2025-10-18 15:47:25 +08:00
'amount' => $order['pay_fee'], // 使用Shopro的pay_fee字段
2025-10-17 17:18:15 +08:00
'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
];
}
2025-10-17 16:32:16 +08:00
}
}