2025-10-17 16:32:16 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace addons\shopro\library\ccblife;
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
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 CcbOrderService
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* HTTP客户端实例
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
|
|
|
|
|
private $httpClient;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 配置信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
private $config;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
*/
|
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格式的密钥
|
|
|
|
|
|
$this->config = $this->processPemKeys($this->config);
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
$this->httpClient = new CcbHttpClient($this->config);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-18 15:47:25 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 处理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-----";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (empty($config['merchant_public_key'])) {
|
|
|
|
|
|
$config['merchant_public_key'] = $config['public_key'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理平台公钥
|
|
|
|
|
|
if (!empty($config['platform_public_key'])) {
|
|
|
|
|
|
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
|
|
|
|
* 推送订单到建行生活平台
|
|
|
|
|
|
* 当用户下单后调用此方法同步订单信息
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @param int $orderId Shopro订单ID
|
|
|
|
|
|
* @return array ['status' => bool, 'message' => string, 'data' => array]
|
|
|
|
|
|
* @throws \Exception
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
2025-10-17 17:18:15 +08:00
|
|
|
|
public function pushOrder($orderId)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
{
|
2025-10-17 17:18:15 +08:00
|
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
|
2025-10-17 16:32:16 +08:00
|
|
|
|
try {
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 获取订单信息
|
|
|
|
|
|
$order = Db::name('shopro_order')
|
|
|
|
|
|
->alias('o')
|
|
|
|
|
|
->join('user u', 'o.user_id = u.id', 'LEFT')
|
|
|
|
|
|
->where('o.id', $orderId)
|
|
|
|
|
|
->field('o.*, u.ccb_user_id')
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$order) {
|
|
|
|
|
|
throw new \Exception('订单不存在');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取建行用户ID
|
|
|
|
|
|
$ccbUserId = $order['ccb_user_id'];
|
|
|
|
|
|
if (!$ccbUserId) {
|
|
|
|
|
|
throw new \Exception('用户未绑定建行生活账号');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取订单商品列表
|
|
|
|
|
|
$orderItems = Db::name('shopro_order_item')
|
|
|
|
|
|
->where('order_id', $orderId)
|
|
|
|
|
|
->select();
|
|
|
|
|
|
|
|
|
|
|
|
// 构建订单数据(符合A3341TP01接口规范)
|
|
|
|
|
|
$orderData = $this->buildOrderData($order, $orderItems, $ccbUserId);
|
|
|
|
|
|
|
|
|
|
|
|
// 记录请求数据(同步日志)
|
|
|
|
|
|
$txSeq = CcbMD5::generateTransactionSeq();
|
|
|
|
|
|
$this->recordSyncLog($orderId, 'A3341TP01', $txSeq, $orderData, 'request');
|
|
|
|
|
|
|
|
|
|
|
|
// 调用建行API推送订单
|
|
|
|
|
|
$response = $this->httpClient->pushOrder($orderData);
|
|
|
|
|
|
|
|
|
|
|
|
// 记录响应数据和耗时
|
|
|
|
|
|
$costTime = round((microtime(true) - $startTime) * 1000, 2);
|
|
|
|
|
|
$this->recordSyncLog($orderId, 'A3341TP01', $txSeq, $response, 'response', true, $costTime);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 更新订单同步状态
|
|
|
|
|
|
$this->updateOrderSyncStatus($orderId, 1);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'status' => true,
|
|
|
|
|
|
'message' => '订单推送成功',
|
|
|
|
|
|
'data' => $response
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
// 记录错误
|
|
|
|
|
|
$costTime = round((microtime(true) - $startTime) * 1000, 2);
|
|
|
|
|
|
$this->recordSyncLog($orderId, 'A3341TP01', '', null, 'error', false, $costTime, $e->getMessage());
|
|
|
|
|
|
|
|
|
|
|
|
// 更新同步状态为失败
|
|
|
|
|
|
$this->updateOrderSyncStatus($orderId, 2);
|
|
|
|
|
|
|
|
|
|
|
|
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-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 string $status 订单状态
|
|
|
|
|
|
* @param string $refundStatus 退款状态
|
|
|
|
|
|
* @return array
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
2025-10-17 17:18:15 +08:00
|
|
|
|
public function updateOrderStatus($orderId, $status = null, $refundStatus = null)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
{
|
2025-10-17 17:18:15 +08:00
|
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
$txSeq = CcbMD5::generateTransactionSeq();
|
|
|
|
|
|
|
2025-10-17 16:32:16 +08:00
|
|
|
|
try {
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 获取订单信息
|
|
|
|
|
|
$order = Db::name('shopro_order')
|
|
|
|
|
|
->alias('o')
|
|
|
|
|
|
->join('user u', 'o.user_id = u.id', 'LEFT')
|
|
|
|
|
|
->where('o.id', $orderId)
|
|
|
|
|
|
->field('o.*, u.ccb_user_id')
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$order) {
|
|
|
|
|
|
throw new \Exception('订单不存在');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取建行用户ID
|
|
|
|
|
|
$ccbUserId = $order['ccb_user_id'];
|
|
|
|
|
|
if (!$ccbUserId) {
|
|
|
|
|
|
throw new \Exception('用户未绑定建行生活账号');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 映射订单状态
|
|
|
|
|
|
$orderStatus = $status ?: $this->mapOrderStatus($order['status']);
|
|
|
|
|
|
$refundStatus = $refundStatus ?: $this->mapRefundStatus($order['refund_status'] ?? 0);
|
|
|
|
|
|
|
|
|
|
|
|
// 记录请求
|
|
|
|
|
|
$requestData = [
|
|
|
|
|
|
'ccb_user_id' => $ccbUserId,
|
|
|
|
|
|
'order_sn' => $order['order_sn'],
|
|
|
|
|
|
'order_status' => $orderStatus,
|
|
|
|
|
|
'refund_status' => $refundStatus
|
|
|
|
|
|
];
|
|
|
|
|
|
$this->recordSyncLog($orderId, 'A3341TP02', $txSeq, $requestData, 'request');
|
|
|
|
|
|
|
|
|
|
|
|
// 调用建行API更新状态
|
|
|
|
|
|
$response = $this->httpClient->updateOrderStatus(
|
|
|
|
|
|
$ccbUserId,
|
|
|
|
|
|
$order['order_sn'],
|
|
|
|
|
|
$orderStatus,
|
|
|
|
|
|
$refundStatus
|
|
|
|
|
|
);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 记录响应
|
|
|
|
|
|
$costTime = round((microtime(true) - $startTime) * 1000, 2);
|
|
|
|
|
|
$this->recordSyncLog($orderId, 'A3341TP02', $txSeq, $response, 'response', true, $costTime);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'status' => true,
|
|
|
|
|
|
'message' => '订单状态更新成功',
|
|
|
|
|
|
'data' => $response
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
$costTime = round((microtime(true) - $startTime) * 1000, 2);
|
|
|
|
|
|
$this->recordSyncLog($orderId, 'A3341TP02', $txSeq, null, 'error', false, $costTime, $e->getMessage());
|
|
|
|
|
|
|
|
|
|
|
|
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-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 array
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
2025-10-17 17:18:15 +08:00
|
|
|
|
public function queryOrder($orderSn)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
{
|
|
|
|
|
|
try {
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 调用建行API查询订单
|
|
|
|
|
|
$response = $this->httpClient->queryOrder($orderSn);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'status' => true,
|
|
|
|
|
|
'message' => '订单查询成功',
|
|
|
|
|
|
'data' => $response
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
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-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
|
2025-10-17 16:32:16 +08:00
|
|
|
|
* @param float $refundAmount 退款金额
|
|
|
|
|
|
* @param string $refundReason 退款原因
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @return array
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public function refundOrder($orderId, $refundAmount, $refundReason = '')
|
|
|
|
|
|
{
|
|
|
|
|
|
try {
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 获取订单信息
|
|
|
|
|
|
$order = Order::find($orderId);
|
|
|
|
|
|
if (!$order) {
|
|
|
|
|
|
throw new \Exception('订单不存在');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证退款金额
|
|
|
|
|
|
if ($refundAmount > $order['total_amount']) {
|
|
|
|
|
|
throw new \Exception('退款金额不能超过订单总额');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调用建行API发起退款
|
|
|
|
|
|
$response = $this->httpClient->refund(
|
|
|
|
|
|
$order['order_sn'],
|
|
|
|
|
|
number_format($refundAmount, 2, '.', ''),
|
|
|
|
|
|
$refundReason
|
|
|
|
|
|
);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 更新订单退款状态
|
|
|
|
|
|
$this->updateOrderStatus($orderId, null, '2');
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'status' => true,
|
|
|
|
|
|
'message' => '退款申请成功',
|
|
|
|
|
|
'data' => $response
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
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-17 17:18:15 +08:00
|
|
|
|
* 构建符合建行要求的订单数据
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*
|
2025-10-18 01:16:25 +08:00
|
|
|
|
* ⚠️ 注意:Shopro字段说明
|
2025-10-18 15:47:25 +08:00
|
|
|
|
* - pay_fee: 实际支付金额(Shopro字段)
|
|
|
|
|
|
* - order_amount: 订单总金额(Shopro字段)
|
|
|
|
|
|
* - total_discount_fee: 优惠总金额(Shopro字段)
|
|
|
|
|
|
* - paid_time: 支付时间(毫秒时间戳!需除以1000)
|
|
|
|
|
|
* - createtime: 创建时间(毫秒时间戳!需除以1000)
|
2025-10-18 01:16:25 +08:00
|
|
|
|
*
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @param array $order 订单数组
|
|
|
|
|
|
* @param array $orderItems 订单商品列表
|
|
|
|
|
|
* @param string $ccbUserId 建行用户ID
|
|
|
|
|
|
* @return array
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
2025-10-17 17:18:15 +08:00
|
|
|
|
private function buildOrderData($order, $orderItems, $ccbUserId)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
{
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 构建商品列表
|
|
|
|
|
|
$goodsList = $this->buildGoodsList($orderItems);
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
2025-10-18 15:47:25 +08:00
|
|
|
|
// 计算各项金额(Shopro字段:pay_fee=实付金额,order_amount=订单总金额)
|
|
|
|
|
|
$totalAmount = number_format($order['order_amount'] ?? 0, 2, '.', '');
|
|
|
|
|
|
$payAmount = number_format($order['pay_fee'] ?? 0, 2, '.', '');
|
|
|
|
|
|
$discountAmount = number_format($order['total_discount_fee'] ?? 0, 2, '.', '');
|
2025-10-18 01:16:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理支付时间(Shopro的paid_time是毫秒时间戳,需要除以1000)
|
|
|
|
|
|
$payTime = '';
|
2025-10-18 15:47:25 +08:00
|
|
|
|
if (!empty($order['paid_time']) && is_numeric($order['paid_time'])) {
|
2025-10-18 01:16:25 +08:00
|
|
|
|
$payTime = date('YmdHis', intval($order['paid_time'] / 1000));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-18 15:47:25 +08:00
|
|
|
|
// 处理创建时间(Shopro的createtime是毫秒时间戳,需要除以1000)
|
|
|
|
|
|
$createTimeValue = $order['createtime'] ?? null;
|
|
|
|
|
|
if (empty($createTimeValue) || !is_numeric($createTimeValue)) {
|
|
|
|
|
|
$createTimeValue = time() * 1000; // 当前时间的毫秒时间戳
|
|
|
|
|
|
}
|
|
|
|
|
|
$createTime = date('YmdHis', intval($createTimeValue / 1000));
|
|
|
|
|
|
|
|
|
|
|
|
// 获取订单地址信息(Shopro将地址存储在单独的表中)
|
|
|
|
|
|
$orderAddress = Db::name('shopro_order_address')
|
|
|
|
|
|
->where('order_id', $order['id'])
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取支付方式(Shopro将支付信息存储在单独的表中)
|
|
|
|
|
|
$payInfo = Db::name('shopro_pay')
|
|
|
|
|
|
->where('order_id', $order['id'])
|
|
|
|
|
|
->where('status', 'paid')
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取快递信息(Shopro将快递信息存储在单独的表中)
|
|
|
|
|
|
$expressInfo = Db::name('shopro_order_express')
|
|
|
|
|
|
->where('order_id', $order['id'])
|
|
|
|
|
|
->find();
|
2025-10-17 16:32:16 +08:00
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
// 构建订单数据(34个必填字段)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
return [
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'USER_ID' => $ccbUserId, // 建行用户ID
|
|
|
|
|
|
'ORDER_ID' => $order['order_sn'], // 订单号
|
2025-10-18 01:16:25 +08:00
|
|
|
|
'ORDER_DT' => $createTime, // 订单时间
|
|
|
|
|
|
'TOTAL_AMT' => $totalAmount, // 订单原金额
|
2025-10-18 15:47:25 +08:00
|
|
|
|
'PAY_AMT' => $payAmount, // 实付金额
|
|
|
|
|
|
'DISCOUNT_AMT' => $discountAmount, // 优惠金额
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'ORDER_STATUS' => $this->mapOrderStatus($order['status']), // 订单状态
|
2025-10-18 15:47:25 +08:00
|
|
|
|
'REFUND_STATUS' => '0', // 退款状态(默认无退款)
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称
|
|
|
|
|
|
'MCT_ORDER_ID' => $order['id'], // 商户订单ID
|
|
|
|
|
|
'GOODS_LIST' => json_encode($goodsList, JSON_UNESCAPED_UNICODE), // 商品列表
|
2025-10-18 15:47:25 +08:00
|
|
|
|
'PAY_TYPE' => $this->mapPayType($payInfo['pay_type'] ?? ''), // 支付方式(从支付表获取)
|
2025-10-18 01:16:25 +08:00
|
|
|
|
'PAY_TIME' => $payTime, // 支付时间(毫秒转秒)
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'DELIVERY_TYPE' => '01', // 配送方式(01快递)
|
|
|
|
|
|
'DELIVERY_STATUS' => $this->mapDeliveryStatus($order['status']), // 配送状态
|
2025-10-18 15:47:25 +08:00
|
|
|
|
'DELIVERY_TIME' => !empty($expressInfo['createtime']) ? date('YmdHis', intval($expressInfo['createtime'] / 1000)) : '', // 发货时间
|
|
|
|
|
|
'RECEIVE_NAME' => $orderAddress['consignee'] ?? '', // 收货人姓名(从地址表获取)
|
|
|
|
|
|
'RECEIVE_PHONE' => $orderAddress['mobile'] ?? '', // 收货人电话(从地址表获取)
|
|
|
|
|
|
'RECEIVE_ADDRESS' => $this->buildAddress($order), // 收货地址(从地址表获取)
|
|
|
|
|
|
'EXPRESS_COMPANY' => $expressInfo['express_name'] ?? '', // 快递公司(从快递表获取)
|
|
|
|
|
|
'EXPRESS_NO' => $expressInfo['express_no'] ?? '', // 快递单号(从快递表获取)
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'REMARK' => $order['remark'] ?? '', // 备注
|
|
|
|
|
|
'ORDER_TYPE' => '01', // 订单类型(01普通订单)
|
|
|
|
|
|
'IS_VIRTUAL' => '0', // 是否虚拟商品
|
|
|
|
|
|
'ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接
|
2025-10-18 01:16:25 +08:00
|
|
|
|
'CREATE_TIME' => $createTime, // 创建时间
|
2025-10-17 17:18:15 +08:00
|
|
|
|
'UPDATE_TIME' => date('YmdHis'), // 更新时间
|
|
|
|
|
|
'SHOP_ID' => '1', // 店铺ID
|
|
|
|
|
|
'SHOP_NAME' => $this->config['merchant']['name'] ?? '', // 店铺名称
|
|
|
|
|
|
'ACTIVITY_ID' => '', // 活动ID
|
|
|
|
|
|
'ACTIVITY_NAME' => '', // 活动名称
|
2025-10-18 15:47:25 +08:00
|
|
|
|
'COUPON_AMT' => number_format($order['coupon_discount_fee'] ?? 0, 2, '.', ''), // 优惠券金额
|
|
|
|
|
|
'FREIGHT_AMT' => number_format($order['dispatch_amount'] ?? 0, 2, '.', ''), // 运费(Shopro字段名为dispatch_amount)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* 构建商品列表
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param array $items 订单商品项
|
|
|
|
|
|
* @return array
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function buildGoodsList($items)
|
|
|
|
|
|
{
|
|
|
|
|
|
$goodsList = [];
|
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
|
$goodsList[] = [
|
|
|
|
|
|
'goods_id' => $item['goods_id'],
|
|
|
|
|
|
'goods_name' => $item['goods_title'],
|
|
|
|
|
|
'goods_price' => number_format($item['goods_price'], 2, '.', ''),
|
|
|
|
|
|
'goods_num' => $item['goods_num'],
|
|
|
|
|
|
'goods_amount' => number_format($item['goods_amount'], 2, '.', ''),
|
|
|
|
|
|
'goods_image' => $item['goods_image'] ?? '',
|
|
|
|
|
|
'goods_sku' => $item['goods_sku_text'] ?? ''
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
return $goodsList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构建收货地址
|
|
|
|
|
|
*
|
2025-10-18 15:47:25 +08:00
|
|
|
|
* ⚠️ 注意:Shopro的收货地址存储在单独的表 shopro_order_address 中
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param array $order 订单数组
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function buildAddress($order)
|
|
|
|
|
|
{
|
2025-10-18 15:47:25 +08:00
|
|
|
|
// 从订单地址表获取地址信息
|
|
|
|
|
|
$orderAddress = Db::name('shopro_order_address')
|
|
|
|
|
|
->where('order_id', $order['id'])
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$orderAddress) {
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
$address = '';
|
2025-10-18 15:47:25 +08:00
|
|
|
|
if (!empty($orderAddress['province_name'])) $address .= $orderAddress['province_name'];
|
|
|
|
|
|
if (!empty($orderAddress['city_name'])) $address .= $orderAddress['city_name'];
|
|
|
|
|
|
if (!empty($orderAddress['district_name'])) $address .= $orderAddress['district_name'];
|
|
|
|
|
|
if (!empty($orderAddress['address'])) $address .= $orderAddress['address'];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
return $address;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 记录同步日志
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $orderId 订单ID
|
|
|
|
|
|
* @param string $txCode 交易代码
|
|
|
|
|
|
* @param string $txSeq 交易流水号
|
|
|
|
|
|
* @param mixed $data 数据
|
|
|
|
|
|
* @param string $type 类型:request/response/error
|
|
|
|
|
|
* @param bool $success 是否成功
|
|
|
|
|
|
* @param float $costTime 耗时(毫秒)
|
|
|
|
|
|
* @param string $errorMsg 错误信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function recordSyncLog($orderId, $txCode, $txSeq, $data, $type = 'request', $success = true, $costTime = 0, $errorMsg = '')
|
|
|
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取订单号
|
|
|
|
|
|
$orderSn = Db::name('shopro_order')->where('id', $orderId)->value('order_sn');
|
|
|
|
|
|
|
|
|
|
|
|
$logData = [
|
|
|
|
|
|
'order_id' => $orderId,
|
|
|
|
|
|
'order_sn' => $orderSn ?: '',
|
|
|
|
|
|
'tx_code' => $txCode,
|
|
|
|
|
|
'tx_seq' => $txSeq,
|
|
|
|
|
|
'sync_status' => $success ? 1 : 0,
|
|
|
|
|
|
'sync_time' => time(),
|
|
|
|
|
|
'cost_time' => intval($costTime),
|
|
|
|
|
|
'retry_times' => 0
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
if ($type == 'request') {
|
|
|
|
|
|
$logData['request_data'] = is_array($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;
|
|
|
|
|
|
} elseif ($type == 'response') {
|
|
|
|
|
|
$logData['response_data'] = is_array($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;
|
|
|
|
|
|
} elseif ($type == 'error') {
|
|
|
|
|
|
$logData['error_msg'] = $errorMsg;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Db::name('ccb_sync_log')->insert($logData);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
Log::error('记录同步日志失败: ' . $e->getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新订单同步状态
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @param int $orderId 订单ID
|
|
|
|
|
|
* @param int $status 同步状态:0-未同步 1-已同步 2-同步失败
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function updateOrderSyncStatus($orderId, $status)
|
|
|
|
|
|
{
|
|
|
|
|
|
Db::name('shopro_order')->where('id', $orderId)->update([
|
|
|
|
|
|
'ccb_sync_status' => $status,
|
|
|
|
|
|
'ccb_sync_time' => time(),
|
|
|
|
|
|
'updatetime' => time()
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 映射订单状态
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*
|
|
|
|
|
|
* @param string $status Shopro订单状态
|
|
|
|
|
|
* @return string 建行订单状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function mapOrderStatus($status)
|
|
|
|
|
|
{
|
2025-10-17 17:18:15 +08:00
|
|
|
|
$statusMap = [
|
|
|
|
|
|
'unpaid' => '0', // 待支付
|
|
|
|
|
|
'paid' => '1', // 已支付
|
|
|
|
|
|
'shipped' => '2', // 已发货
|
|
|
|
|
|
'received' => '3', // 已收货
|
|
|
|
|
|
'completed' => '4', // 已完成
|
|
|
|
|
|
'cancelled' => '5', // 已取消
|
|
|
|
|
|
'refunded' => '6' // 已退款
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
return $statusMap[$status] ?? '0';
|
2025-10-17 16:32:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 映射退款状态
|
|
|
|
|
|
*
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @param int $refundStatus 退款状态
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function mapRefundStatus($refundStatus)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ($refundStatus == 0) return '0'; // 无退款
|
|
|
|
|
|
if ($refundStatus == 1) return '1'; // 退款中
|
|
|
|
|
|
if ($refundStatus == 2) return '2'; // 已退款
|
|
|
|
|
|
return '0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 映射支付方式
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*
|
2025-10-17 17:18:15 +08:00
|
|
|
|
* @param string $payType 支付类型
|
|
|
|
|
|
* @return string
|
2025-10-17 16:32:16 +08:00
|
|
|
|
*/
|
2025-10-17 17:18:15 +08:00
|
|
|
|
private function mapPayType($payType)
|
2025-10-17 16:32:16 +08:00
|
|
|
|
{
|
2025-10-17 17:18:15 +08:00
|
|
|
|
$payMap = [
|
|
|
|
|
|
'wechat' => '01', // 微信支付
|
|
|
|
|
|
'alipay' => '02', // 支付宝
|
|
|
|
|
|
'ccb' => '03', // 建行支付
|
|
|
|
|
|
'balance' => '04' // 余额支付
|
2025-10-17 16:32:16 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-17 17:18:15 +08:00
|
|
|
|
return $payMap[$payType] ?? '00';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 映射配送状态
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param string $status 订单状态
|
|
|
|
|
|
* @return string
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function mapDeliveryStatus($status)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (in_array($status, ['unpaid', 'paid'])) return '0'; // 未发货
|
|
|
|
|
|
if ($status == 'shipped') return '1'; // 已发货
|
|
|
|
|
|
if (in_array($status, ['received', 'completed'])) return '2'; // 已收货
|
|
|
|
|
|
return '0';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量同步订单
|
|
|
|
|
|
* 用于初始化或定时同步
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param array $conditions 查询条件
|
|
|
|
|
|
* @param int $limit 批量数量
|
|
|
|
|
|
* @return array
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function batchSync($conditions = [], $limit = 100)
|
|
|
|
|
|
{
|
|
|
|
|
|
$successCount = 0;
|
|
|
|
|
|
$failCount = 0;
|
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 构建查询
|
|
|
|
|
|
$query = Db::name('shopro_order');
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($conditions)) {
|
|
|
|
|
|
$query->where($conditions);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查询需要同步的订单
|
|
|
|
|
|
$orders = $query->where('status', '<>', 'cancelled')
|
|
|
|
|
|
->where('ccb_sync_status', 'in', [0, 2]) // 未同步或同步失败的
|
|
|
|
|
|
->limit($limit)
|
|
|
|
|
|
->select();
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($orders as $order) {
|
|
|
|
|
|
$result = $this->pushOrder($order['id']);
|
|
|
|
|
|
if ($result['status']) {
|
|
|
|
|
|
$successCount++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$failCount++;
|
|
|
|
|
|
$errors[] = "订单{$order['order_sn']}: {$result['message']}";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'status' => true,
|
|
|
|
|
|
'message' => "批量同步完成",
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'total' => count($orders),
|
|
|
|
|
|
'success' => $successCount,
|
|
|
|
|
|
'fail' => $failCount,
|
|
|
|
|
|
'errors' => $errors
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'status' => false,
|
|
|
|
|
|
'message' => '批量同步失败: ' . $e->getMessage(),
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
2025-10-17 16:32:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|