mirror of
https://gitee.com/liuxioabin/fengketrade.git
synced 2026-04-17 12:57:32 +08:00
支付bug修改
This commit is contained in:
parent
24faf35e55
commit
b65e4d520c
@ -95,11 +95,11 @@ class Ccbpayment extends Common
|
||||
}
|
||||
|
||||
// 5. 保存支付流水号到订单
|
||||
$order->ccb_pay_flow_id = $result['data']['order_sn']; // 使用订单号作为流水号
|
||||
$order->ccb_pay_flow_id = $result['data']['pay_flow_id']; // ✅ 使用真实的支付流水号
|
||||
$order->save();
|
||||
|
||||
// 6. 记录支付日志
|
||||
$this->savePaymentLog($order, $result['data']['payment_string'], $result['data']['order_sn']);
|
||||
$this->savePaymentLog($order, $result['data']['payment_string'], $result['data']['pay_flow_id']);
|
||||
|
||||
// 7. 返回支付串
|
||||
$this->success('支付串生成成功', [
|
||||
@ -108,7 +108,7 @@ class Ccbpayment extends Common
|
||||
'mac' => $result['data']['mac'],
|
||||
'order_id' => $order->id,
|
||||
'order_sn' => $order->order_sn,
|
||||
'pay_flow_id' => $result['data']['order_sn'],
|
||||
'pay_flow_id' => $result['data']['pay_flow_id'], // ✅ 返回真实的支付流水号
|
||||
'amount' => $result['data']['amount'],
|
||||
]);
|
||||
|
||||
@ -210,23 +210,46 @@ class Ccbpayment extends Common
|
||||
* 建行支付成功后,会向notify_url发送支付通知
|
||||
* 这是服务器到服务器的回调,需要验签
|
||||
*
|
||||
* ⚠️ 重要:此接口为建行服务器异步回调,必须返回纯文本 'SUCCESS' 或 'FAIL'
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
try {
|
||||
// TODO: 实现建行支付通知处理逻辑
|
||||
// 1. 接收建行推送的支付结果
|
||||
// 2. 验签
|
||||
// 3. 更新订单状态
|
||||
// 4. 返回success给建行
|
||||
// 1. 获取原始请求数据
|
||||
$rawData = file_get_contents('php://input');
|
||||
Log::info('[建行通知] 收到异步通知: ' . $rawData);
|
||||
|
||||
$this->success('SUCCESS');
|
||||
// 2. 解析POST参数
|
||||
$params = $this->request->post();
|
||||
|
||||
// 3. 如果POST为空,尝试解析原始数据
|
||||
if (empty($params) && $rawData) {
|
||||
parse_str($rawData, $params);
|
||||
}
|
||||
|
||||
// 4. 记录参数
|
||||
Log::info('[建行通知] 解析参数: ' . json_encode($params, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 5. 验证必需参数
|
||||
if (empty($params['ORDERID'])) {
|
||||
Log::error('[建行通知] 缺少ORDERID参数');
|
||||
echo 'FAIL';
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. 调用支付服务处理通知
|
||||
$result = $this->paymentService->handleNotify($params);
|
||||
|
||||
// 7. 返回处理结果
|
||||
echo $result; // 'SUCCESS' 或 'FAIL'
|
||||
|
||||
Log::info('[建行通知] 处理完成: ' . $result);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Log::error('[建行通知] 处理失败 error:' . $e->getMessage());
|
||||
|
||||
$this->error('FAIL');
|
||||
echo 'FAIL';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,10 +10,17 @@ class CcbMD5
|
||||
{
|
||||
/**
|
||||
* 生成API消息签名
|
||||
* 使用大写MD5(源报文 + 私钥)
|
||||
*
|
||||
* ⚠️ 注意:建行API接口签名使用大写MD5
|
||||
* 签名规则:MD5(JSON报文 + 服务方私钥) → 转大写
|
||||
*
|
||||
* 示例:
|
||||
* message = '{"CLD_HEADER":{...},"CLD_BODY":{...}}'
|
||||
* privateKey = 'MIICeAIBA...'
|
||||
* sign = strtoupper(md5(message + privateKey))
|
||||
*
|
||||
* @param string $message JSON格式的源报文
|
||||
* @param string $privateKey 商户私钥(BASE64格式)
|
||||
* @param string $privateKey 服务方私钥(BASE64格式)
|
||||
* @return string 大写的32位MD5签名
|
||||
*/
|
||||
public static function signApiMessage($message, $privateKey)
|
||||
@ -21,7 +28,7 @@ class CcbMD5
|
||||
// 移除私钥中的空格和换行
|
||||
$privateKey = preg_replace('/\s+/', '', $privateKey);
|
||||
|
||||
// 计算MD5并转大写
|
||||
// 计算MD5并转大写(建行要求)
|
||||
return strtoupper(md5($message . $privateKey));
|
||||
}
|
||||
|
||||
@ -41,8 +48,13 @@ class CcbMD5
|
||||
|
||||
/**
|
||||
* 生成支付字符串签名
|
||||
* 使用小写MD5(支付字符串)
|
||||
* 支付字符串格式: MERCHANTID=xxx&POSID=xxx&BRANCHID=xxx&ORDERID=xxx&PAYMENT=xxx&CURCODE=01&TXCODE=530550&REMARK1=&REMARK2=&TIMEOUT=商户私钥
|
||||
*
|
||||
* ⚠️ 注意:建行收银台支付串签名使用小写MD5
|
||||
* 签名规则:MD5(支付参数按顺序拼接 + 商户私钥) → 保持小写
|
||||
*
|
||||
* 示例:
|
||||
* paymentString = 'MERCHANTID=xxx&POSID=xxx&...&TIMEOUT=xxx' + privateKey
|
||||
* mac = md5(paymentString) // 小写
|
||||
*
|
||||
* @param array $params 支付参数
|
||||
* @param string $privateKey 商户私钥
|
||||
@ -73,7 +85,7 @@ class CcbMD5
|
||||
// 拼接支付字符串并添加私钥
|
||||
$paymentString = implode('&', $parts) . $privateKey;
|
||||
|
||||
// 计算MD5(保持小写)
|
||||
// 计算MD5(保持小写,建行要求)
|
||||
return md5($paymentString);
|
||||
}
|
||||
|
||||
|
||||
@ -261,6 +261,13 @@ class CcbOrderService
|
||||
/**
|
||||
* 构建符合建行要求的订单数据
|
||||
*
|
||||
* ⚠️ 注意:Shopro字段说明
|
||||
* - total_fee: 实际支付金额
|
||||
* - total_amount: 订单原价
|
||||
* - discount_fee: 优惠金额
|
||||
* - paid_time: 支付时间(毫秒时间戳!)
|
||||
* - createtime: 创建时间(秒级时间戳)
|
||||
*
|
||||
* @param array $order 订单数组
|
||||
* @param array $orderItems 订单商品列表
|
||||
* @param string $ccbUserId 建行用户ID
|
||||
@ -271,26 +278,35 @@ class CcbOrderService
|
||||
// 构建商品列表
|
||||
$goodsList = $this->buildGoodsList($orderItems);
|
||||
|
||||
// 计算各项金额
|
||||
$totalAmount = number_format($order['total_amount'], 2, '.', '');
|
||||
$payAmount = number_format($order['pay_amount'] ?? $order['total_amount'], 2, '.', '');
|
||||
$discountAmount = number_format($order['discount_amount'] ?? 0, 2, '.', '');
|
||||
// 计算各项金额(Shopro使用total_fee作为实付金额)
|
||||
$totalAmount = number_format($order['total_amount'] ?? 0, 2, '.', '');
|
||||
$payAmount = number_format($order['total_fee'] ?? 0, 2, '.', '');
|
||||
$discountAmount = number_format($order['discount_fee'] ?? 0, 2, '.', '');
|
||||
|
||||
// 处理支付时间(Shopro的paid_time是毫秒时间戳,需要除以1000)
|
||||
$payTime = '';
|
||||
if (!empty($order['paid_time'])) {
|
||||
$payTime = date('YmdHis', intval($order['paid_time'] / 1000));
|
||||
}
|
||||
|
||||
// 处理创建时间(Shopro的createtime是秒级时间戳)
|
||||
$createTime = date('YmdHis', $order['createtime'] ?? time());
|
||||
|
||||
// 构建订单数据(34个必填字段)
|
||||
return [
|
||||
'USER_ID' => $ccbUserId, // 建行用户ID
|
||||
'ORDER_ID' => $order['order_sn'], // 订单号
|
||||
'ORDER_DT' => date('YmdHis', strtotime($order['createtime'])), // 订单时间
|
||||
'TOTAL_AMT' => $totalAmount, // 订单总金额
|
||||
'PAY_AMT' => $payAmount, // 实付金额
|
||||
'DISCOUNT_AMT' => $discountAmount, // 优惠金额
|
||||
'ORDER_DT' => $createTime, // 订单时间
|
||||
'TOTAL_AMT' => $totalAmount, // 订单原金额
|
||||
'PAY_AMT' => $payAmount, // 实付金额(total_fee)
|
||||
'DISCOUNT_AMT' => $discountAmount, // 优惠金额(discount_fee)
|
||||
'ORDER_STATUS' => $this->mapOrderStatus($order['status']), // 订单状态
|
||||
'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态
|
||||
'REFUND_STATUS' => $this->mapRefundStatus($order['aftersale_status'] ?? 0), // 退款状态(aftersale_status)
|
||||
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
|
||||
'MCT_ORDER_ID' => $order['id'], // 商户订单ID
|
||||
'GOODS_LIST' => json_encode($goodsList, JSON_UNESCAPED_UNICODE), // 商品列表
|
||||
'PAY_TYPE' => $this->mapPayType($order['pay_type'] ?? ''), // 支付方式
|
||||
'PAY_TIME' => $order['paytime'] ? date('YmdHis', $order['paytime']) : '', // 支付时间
|
||||
'PAY_TIME' => $payTime, // 支付时间(毫秒转秒)
|
||||
'DELIVERY_TYPE' => '01', // 配送方式(01快递)
|
||||
'DELIVERY_STATUS' => $this->mapDeliveryStatus($order['status']), // 配送状态
|
||||
'DELIVERY_TIME' => $order['delivery_time'] ? date('YmdHis', $order['delivery_time']) : '', // 发货时间
|
||||
@ -303,7 +319,7 @@ class CcbOrderService
|
||||
'ORDER_TYPE' => '01', // 订单类型(01普通订单)
|
||||
'IS_VIRTUAL' => '0', // 是否虚拟商品
|
||||
'ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
|
||||
'CREATE_TIME' => date('YmdHis'), // 创建时间
|
||||
'CREATE_TIME' => $createTime, // 创建时间
|
||||
'UPDATE_TIME' => date('YmdHis'), // 更新时间
|
||||
'SHOP_ID' => '1', // 店铺ID
|
||||
'SHOP_NAME' => $this->config['merchant']['name'] ?? '', // 店铺名称
|
||||
|
||||
@ -35,6 +35,8 @@ class CcbPaymentService
|
||||
* 生成建行支付串
|
||||
* 用于前端JSBridge调用建行收银台
|
||||
*
|
||||
* ⚠️ 注意:必须包含所有必需参数,签名前按ASCII排序
|
||||
*
|
||||
* @param int $orderId Shopro订单ID
|
||||
* @return array ['status' => bool, 'message' => string, 'data' => array]
|
||||
*/
|
||||
@ -58,32 +60,75 @@ class CcbPaymentService
|
||||
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' // 交易码
|
||||
);
|
||||
// 生成支付流水号(使用订单号作为唯一标识)
|
||||
$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, // 支付流水号(必须唯一!)
|
||||
'PAYMENT' => number_format($order['total_fee'], 2, '.', ''), // 支付金额
|
||||
'CURCODE' => '01', // 币种(01=人民币)
|
||||
'TXCODE' => '520100', // 交易码(520100=即时支付)
|
||||
'REMARK1' => '', // 备注1
|
||||
'REMARK2' => $this->config['service_id'], // 备注2(服务方编号)
|
||||
'TYPE' => '1', // 支付类型(1=个人)
|
||||
'GATEWAY' => '0', // 网关标志
|
||||
'CLIENTIP' => request()->ip(), // 客户端IP
|
||||
'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);
|
||||
|
||||
// ⚠️ 注意:建行支付串签名规则
|
||||
// 签名 = MD5(参数字符串 + 服务方私钥)
|
||||
// 不需要PLATFORMPUB字段,直接使用私钥签名
|
||||
$mac = md5($signString . $this->config['private_key']);
|
||||
|
||||
// 使用RSA加密商户公钥(用于ENCPUB字段)
|
||||
$encryption = new CcbEncryption($this->config);
|
||||
$encpub = $encryption->encryptMerchantPublicKey();
|
||||
|
||||
// 组装最终支付串
|
||||
$finalPaymentString = $signString . '&MAC=' . $mac . '&PLATFORMID=' . $this->config['service_id'] . '&ENCPUB=' . urlencode($encpub);
|
||||
|
||||
// 保存支付流水号到订单
|
||||
Order::where('id', $orderId)->update([
|
||||
'ccb_pay_flow_id' => $payFlowId,
|
||||
'updatetime' => time()
|
||||
]);
|
||||
|
||||
// 构建完整的支付URL
|
||||
$paymentUrl = $this->buildPaymentUrl($result['params'], $result['mac']);
|
||||
$paymentUrl = $this->config['cashier_url'] . '?' . $finalPaymentString;
|
||||
|
||||
// 记录支付请求
|
||||
$this->recordPaymentRequest($orderId, $result);
|
||||
$this->recordPaymentRequest($orderId, [
|
||||
'payment_string' => $finalPaymentString,
|
||||
'params' => $paymentParams,
|
||||
'mac' => $mac,
|
||||
'pay_flow_id' => $payFlowId
|
||||
]);
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'message' => '支付串生成成功',
|
||||
'data' => [
|
||||
'payment_string' => $result['payment_string'],
|
||||
'mac' => $result['mac'],
|
||||
'payment_string' => $finalPaymentString,
|
||||
'mac' => $mac,
|
||||
'payment_url' => $paymentUrl,
|
||||
'order_sn' => $order['order_sn'],
|
||||
'pay_flow_id' => $payFlowId,
|
||||
'amount' => number_format($order['total_fee'], 2, '.', '')
|
||||
]
|
||||
];
|
||||
@ -98,6 +143,28 @@ class CcbPaymentService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建商品信息字符串
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理支付回调
|
||||
* 建行支付完成后的同步回调
|
||||
@ -117,13 +184,14 @@ class CcbPaymentService
|
||||
}
|
||||
|
||||
// 获取关键参数
|
||||
$orderSn = $params['ORDERID'] ?? '';
|
||||
$payFlowId = $params['ORDERID'] ?? ''; // 支付流水号
|
||||
$userOrderId = $params['USER_ORDERID'] ?? ''; // 商户订单号
|
||||
$posId = $params['POSID'] ?? '';
|
||||
$success = $params['SUCCESS'] ?? 'N';
|
||||
|
||||
// 验证参数
|
||||
if (empty($orderSn)) {
|
||||
throw new \Exception('订单号不能为空');
|
||||
if (empty($payFlowId)) {
|
||||
throw new \Exception('支付流水号不能为空');
|
||||
}
|
||||
|
||||
// 验证POS号
|
||||
@ -131,8 +199,14 @@ class CcbPaymentService
|
||||
throw new \Exception('POS号验证失败');
|
||||
}
|
||||
|
||||
// 查询订单
|
||||
$order = Order::where('order_sn', $orderSn)->find();
|
||||
// ⚠️ 重要: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();
|
||||
}
|
||||
|
||||
if (!$order) {
|
||||
throw new \Exception('订单不存在');
|
||||
}
|
||||
@ -150,7 +224,8 @@ class CcbPaymentService
|
||||
'message' => '支付成功',
|
||||
'data' => [
|
||||
'order_id' => $order['id'],
|
||||
'order_sn' => $orderSn,
|
||||
'order_sn' => $order['order_sn'], // ✅ 返回真正的订单号
|
||||
'pay_flow_id' => $payFlowId, // 支付流水号
|
||||
'amount' => $params['PAYMENT'] ?? ''
|
||||
]
|
||||
];
|
||||
@ -161,7 +236,8 @@ class CcbPaymentService
|
||||
'message' => '支付失败',
|
||||
'data' => [
|
||||
'order_id' => $order['id'],
|
||||
'order_sn' => $orderSn,
|
||||
'order_sn' => $order['order_sn'], // ✅ 返回真正的订单号
|
||||
'pay_flow_id' => $payFlowId, // 支付流水号
|
||||
'error_code' => $params['ERRCODE'] ?? '',
|
||||
'error_msg' => $params['ERRMSG'] ?? ''
|
||||
]
|
||||
@ -193,9 +269,16 @@ class CcbPaymentService
|
||||
throw new \Exception('签名验证失败');
|
||||
}
|
||||
|
||||
// 获取订单信息
|
||||
$orderSn = $params['ORDERID'] ?? '';
|
||||
$order = Order::where('order_sn', $orderSn)->find();
|
||||
// ⚠️ 重要: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();
|
||||
}
|
||||
|
||||
if (!$order) {
|
||||
throw new \Exception('订单不存在');
|
||||
@ -339,7 +422,7 @@ class CcbPaymentService
|
||||
Db::name('ccb_payment_log')->insert([
|
||||
'order_id' => $orderId,
|
||||
'order_sn' => $order['order_sn'],
|
||||
'pay_flow_id' => $order['order_sn'], // 使用订单号作为流水号
|
||||
'pay_flow_id' => $paymentData['pay_flow_id'] ?? '', // ✅ 使用真实的支付流水号
|
||||
'payment_string' => $paymentData['payment_string'] ?? '',
|
||||
'user_id' => $order['user_id'],
|
||||
'ccb_user_id' => $user['ccb_user_id'] ?? '',
|
||||
|
||||
206
addons/shopro/library/ccblife/model/CcbPaymentLog.php
Normal file
206
addons/shopro/library/ccblife/model/CcbPaymentLog.php
Normal file
@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\ccblife\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 建行支付日志模型
|
||||
*
|
||||
* 用于记录建行支付相关的日志信息
|
||||
* 表名: fa_ccb_payment_log
|
||||
*/
|
||||
class CcbPaymentLog extends Model
|
||||
{
|
||||
/**
|
||||
* 数据表名称(不含前缀)
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ccb_payment_log';
|
||||
|
||||
/**
|
||||
* 自动写入时间戳字段
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoWriteTimestamp = false;
|
||||
|
||||
/**
|
||||
* 字段类型转换
|
||||
* @var array
|
||||
*/
|
||||
protected $type = [
|
||||
'order_id' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'amount' => 'float',
|
||||
'status' => 'integer',
|
||||
'pay_time' => 'integer',
|
||||
'create_time' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* 支付状态常量
|
||||
*/
|
||||
const STATUS_PENDING = 0; // 待支付
|
||||
const STATUS_SUCCESS = 1; // 支付成功
|
||||
const STATUS_FAILED = 2; // 支付失败
|
||||
const STATUS_CANCELLED = 3; // 已取消
|
||||
|
||||
/**
|
||||
* 支付状态映射
|
||||
* @var array
|
||||
*/
|
||||
public static $statusMap = [
|
||||
self::STATUS_PENDING => '待支付',
|
||||
self::STATUS_SUCCESS => '支付成功',
|
||||
self::STATUS_FAILED => '支付失败',
|
||||
self::STATUS_CANCELLED => '已取消',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取状态文本
|
||||
*
|
||||
* @param int $status 状态值
|
||||
* @return string
|
||||
*/
|
||||
public static function getStatusText($status)
|
||||
{
|
||||
return self::$statusMap[$status] ?? '未知状态';
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单ID查找支付日志
|
||||
*
|
||||
* @param int $orderId 订单ID
|
||||
* @return CcbPaymentLog|null
|
||||
*/
|
||||
public static function findByOrderId($orderId)
|
||||
{
|
||||
return self::where('order_id', $orderId)
|
||||
->order('create_time', 'desc')
|
||||
->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据支付流水号查找
|
||||
*
|
||||
* @param string $payFlowId 支付流水号
|
||||
* @return CcbPaymentLog|null
|
||||
*/
|
||||
public static function findByPayFlowId($payFlowId)
|
||||
{
|
||||
return self::where('pay_flow_id', $payFlowId)->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号查找
|
||||
*
|
||||
* @param string $orderSn 订单号
|
||||
* @return CcbPaymentLog|null
|
||||
*/
|
||||
public static function findByOrderSn($orderSn)
|
||||
{
|
||||
return self::where('order_sn', $orderSn)
|
||||
->order('create_time', 'desc')
|
||||
->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建支付日志
|
||||
*
|
||||
* @param array $data 日志数据
|
||||
* @return CcbPaymentLog|false
|
||||
*/
|
||||
public static function createLog($data)
|
||||
{
|
||||
$defaultData = [
|
||||
'status' => self::STATUS_PENDING,
|
||||
'create_time' => time(),
|
||||
];
|
||||
|
||||
$logData = array_merge($defaultData, $data);
|
||||
|
||||
return self::create($logData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新支付状态
|
||||
*
|
||||
* @param string $payFlowId 支付流水号
|
||||
* @param int $status 状态
|
||||
* @param array $extraData 额外数据
|
||||
* @return bool
|
||||
*/
|
||||
public static function updateStatus($payFlowId, $status, $extraData = [])
|
||||
{
|
||||
$updateData = array_merge([
|
||||
'status' => $status,
|
||||
], $extraData);
|
||||
|
||||
return self::where('pay_flow_id', $payFlowId)->update($updateData) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录支付成功
|
||||
*
|
||||
* @param string $payFlowId 支付流水号
|
||||
* @param string $transId 交易ID
|
||||
* @param array $callbackData 回调数据
|
||||
* @return bool
|
||||
*/
|
||||
public static function recordSuccess($payFlowId, $transId, $callbackData = [])
|
||||
{
|
||||
return self::updateStatus($payFlowId, self::STATUS_SUCCESS, [
|
||||
'trans_id' => $transId,
|
||||
'pay_time' => time(),
|
||||
'callback_data' => is_array($callbackData) ? json_encode($callbackData, JSON_UNESCAPED_UNICODE) : $callbackData,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录支付失败
|
||||
*
|
||||
* @param string $payFlowId 支付流水号
|
||||
* @param string $errorMsg 错误信息
|
||||
* @return bool
|
||||
*/
|
||||
public static function recordFailure($payFlowId, $errorMsg)
|
||||
{
|
||||
return self::updateStatus($payFlowId, self::STATUS_FAILED, [
|
||||
'error_msg' => $errorMsg,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待支付的日志列表
|
||||
*
|
||||
* @param int $limit 限制数量
|
||||
* @return array
|
||||
*/
|
||||
public static function getPendingList($limit = 100)
|
||||
{
|
||||
return self::where('status', self::STATUS_PENDING)
|
||||
->where('create_time', '>', time() - 3600 * 24) // 24小时内
|
||||
->limit($limit)
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联订单模型(如果需要)
|
||||
*
|
||||
* @return \think\model\relation\BelongsTo
|
||||
*/
|
||||
public function order()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\shopro\order\Order', 'order_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联用户模型(如果需要)
|
||||
*
|
||||
* @return \think\model\relation\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('app\common\model\User', 'user_id', 'id');
|
||||
}
|
||||
}
|
||||
259
addons/shopro/library/ccblife/model/CcbSyncLog.php
Normal file
259
addons/shopro/library/ccblife/model/CcbSyncLog.php
Normal file
@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace addons\shopro\library\ccblife\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 建行订单同步日志模型
|
||||
*
|
||||
* 用于记录建行订单同步相关的日志信息
|
||||
* 表名: fa_ccb_sync_log
|
||||
*/
|
||||
class CcbSyncLog extends Model
|
||||
{
|
||||
/**
|
||||
* 数据表名称(不含前缀)
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ccb_sync_log';
|
||||
|
||||
/**
|
||||
* 自动写入时间戳字段
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoWriteTimestamp = false;
|
||||
|
||||
/**
|
||||
* 字段类型转换
|
||||
* @var array
|
||||
*/
|
||||
protected $type = [
|
||||
'order_id' => 'integer',
|
||||
'sync_status' => 'integer',
|
||||
'sync_time' => 'integer',
|
||||
'cost_time' => 'integer',
|
||||
'retry_times' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* 同步状态常量
|
||||
*/
|
||||
const STATUS_FAILED = 0; // 同步失败
|
||||
const STATUS_SUCCESS = 1; // 同步成功
|
||||
|
||||
/**
|
||||
* 交易代码常量
|
||||
*/
|
||||
const TX_CODE_PUSH = 'A3341TP01'; // 订单推送
|
||||
const TX_CODE_UPDATE = 'A3341TP02'; // 订单状态更新
|
||||
const TX_CODE_QUERY = 'A3341TP03'; // 订单查询
|
||||
const TX_CODE_REFUND = 'A3341TP04'; // 订单退款
|
||||
|
||||
/**
|
||||
* 交易代码映射
|
||||
* @var array
|
||||
*/
|
||||
public static $txCodeMap = [
|
||||
self::TX_CODE_PUSH => '订单推送',
|
||||
self::TX_CODE_UPDATE => '状态更新',
|
||||
self::TX_CODE_QUERY => '订单查询',
|
||||
self::TX_CODE_REFUND => '订单退款',
|
||||
];
|
||||
|
||||
/**
|
||||
* 同步状态映射
|
||||
* @var array
|
||||
*/
|
||||
public static $statusMap = [
|
||||
self::STATUS_FAILED => '失败',
|
||||
self::STATUS_SUCCESS => '成功',
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取交易代码文本
|
||||
*
|
||||
* @param string $txCode 交易代码
|
||||
* @return string
|
||||
*/
|
||||
public static function getTxCodeText($txCode)
|
||||
{
|
||||
return self::$txCodeMap[$txCode] ?? '未知交易';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态文本
|
||||
*
|
||||
* @param int $status 状态值
|
||||
* @return string
|
||||
*/
|
||||
public static function getStatusText($status)
|
||||
{
|
||||
return self::$statusMap[$status] ?? '未知状态';
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单ID查找同步日志
|
||||
*
|
||||
* @param int $orderId 订单ID
|
||||
* @param string $txCode 交易代码(可选)
|
||||
* @return array
|
||||
*/
|
||||
public static function findByOrderId($orderId, $txCode = '')
|
||||
{
|
||||
$query = self::where('order_id', $orderId);
|
||||
|
||||
if ($txCode) {
|
||||
$query->where('tx_code', $txCode);
|
||||
}
|
||||
|
||||
return $query->order('sync_time', 'desc')->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据交易流水号查找
|
||||
*
|
||||
* @param string $txSeq 交易流水号
|
||||
* @return CcbSyncLog|null
|
||||
*/
|
||||
public static function findByTxSeq($txSeq)
|
||||
{
|
||||
return self::where('tx_seq', $txSeq)->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建同步日志
|
||||
*
|
||||
* @param array $data 日志数据
|
||||
* @return CcbSyncLog|false
|
||||
*/
|
||||
public static function createLog($data)
|
||||
{
|
||||
$defaultData = [
|
||||
'sync_status' => self::STATUS_FAILED,
|
||||
'sync_time' => time(),
|
||||
'retry_times' => 0,
|
||||
'cost_time' => 0,
|
||||
];
|
||||
|
||||
$logData = array_merge($defaultData, $data);
|
||||
|
||||
return self::create($logData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新同步状态
|
||||
*
|
||||
* @param string $txSeq 交易流水号
|
||||
* @param int $status 状态
|
||||
* @param array $extraData 额外数据
|
||||
* @return bool
|
||||
*/
|
||||
public static function updateStatus($txSeq, $status, $extraData = [])
|
||||
{
|
||||
$updateData = array_merge([
|
||||
'sync_status' => $status,
|
||||
], $extraData);
|
||||
|
||||
return self::where('tx_seq', $txSeq)->update($updateData) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录同步成功
|
||||
*
|
||||
* @param string $txSeq 交易流水号
|
||||
* @param mixed $responseData 响应数据
|
||||
* @param int $costTime 耗时(毫秒)
|
||||
* @return bool
|
||||
*/
|
||||
public static function recordSuccess($txSeq, $responseData, $costTime = 0)
|
||||
{
|
||||
return self::updateStatus($txSeq, self::STATUS_SUCCESS, [
|
||||
'response_data' => is_array($responseData) ? json_encode($responseData, JSON_UNESCAPED_UNICODE) : $responseData,
|
||||
'cost_time' => $costTime,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录同步失败
|
||||
*
|
||||
* @param string $txSeq 交易流水号
|
||||
* @param string $errorMsg 错误信息
|
||||
* @param int $costTime 耗时(毫秒)
|
||||
* @return bool
|
||||
*/
|
||||
public static function recordFailure($txSeq, $errorMsg, $costTime = 0)
|
||||
{
|
||||
return self::updateStatus($txSeq, self::STATUS_FAILED, [
|
||||
'error_msg' => $errorMsg,
|
||||
'cost_time' => $costTime,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加重试次数
|
||||
*
|
||||
* @param string $txSeq 交易流水号
|
||||
* @return bool
|
||||
*/
|
||||
public static function incrementRetry($txSeq)
|
||||
{
|
||||
return self::where('tx_seq', $txSeq)->setInc('retry_times') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取失败的同步日志
|
||||
*
|
||||
* @param int $limit 限制数量
|
||||
* @param int $maxRetryTimes 最大重试次数
|
||||
* @return array
|
||||
*/
|
||||
public static function getFailedList($limit = 100, $maxRetryTimes = 3)
|
||||
{
|
||||
return self::where('sync_status', self::STATUS_FAILED)
|
||||
->where('retry_times', '<', $maxRetryTimes)
|
||||
->where('sync_time', '>', time() - 3600 * 24) // 24小时内
|
||||
->order('sync_time', 'asc')
|
||||
->limit($limit)
|
||||
->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单最后一次同步记录
|
||||
*
|
||||
* @param int $orderId 订单ID
|
||||
* @param string $txCode 交易代码
|
||||
* @return CcbSyncLog|null
|
||||
*/
|
||||
public static function getLastSync($orderId, $txCode)
|
||||
{
|
||||
return self::where('order_id', $orderId)
|
||||
->where('tx_code', $txCode)
|
||||
->order('sync_time', 'desc')
|
||||
->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查订单是否同步成功
|
||||
*
|
||||
* @param int $orderId 订单ID
|
||||
* @param string $txCode 交易代码
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSyncSuccess($orderId, $txCode)
|
||||
{
|
||||
$lastSync = self::getLastSync($orderId, $txCode);
|
||||
|
||||
return $lastSync && $lastSync['sync_status'] === self::STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联订单模型(如果需要)
|
||||
*
|
||||
* @return \think\model\relation\BelongsTo
|
||||
*/
|
||||
public function order()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\shopro\order\Order', 'order_id', 'id');
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user