diff --git a/addons/shopro/library/ccblife/CcbOrderService.php b/addons/shopro/library/ccblife/CcbOrderService.php index 350837e..1a4427f 100644 --- a/addons/shopro/library/ccblife/CcbOrderService.php +++ b/addons/shopro/library/ccblife/CcbOrderService.php @@ -112,10 +112,23 @@ class CcbOrderService // 获取订单商品列表 $orderItems = Db::name('shopro_order_item') ->where('order_id', $orderId) - ->select(); + ->select() + ->toArray(); + + // 获取支付流水号(PAY_FLOW_ID必填字段) + $payInfo = Db::name('shopro_pay') + ->where('order_id', $orderId) + ->where('status', 'paid') + ->find(); + + if (!$payInfo || empty($payInfo['pay_sn'])) { + throw new \Exception('订单未支付或支付流水号不存在'); + } + + $payFlowId = $payInfo['pay_sn']; // 构建订单数据(符合A3341TP01接口规范) - $orderData = $this->buildOrderData($order, $orderItems, $ccbUserId); + $orderData = $this->buildOrderData($order, $orderItems, $ccbUserId, $payFlowId); // 记录请求数据(同步日志) $txSeq = CcbMD5::generateTransactionSeq(); @@ -270,13 +283,13 @@ class CcbOrderService { try { // 获取订单信息 - $order = Order::find($orderId); + $order = Db::name('shopro_order')->where('id', $orderId)->find(); if (!$order) { throw new \Exception('订单不存在'); } // 验证退款金额 - if ($refundAmount > $order['total_amount']) { + if ($refundAmount > $order['order_amount']) { throw new \Exception('退款金额不能超过订单总额'); } @@ -307,148 +320,112 @@ class CcbOrderService } /** - * 构建符合建行要求的订单数据 + * 构建符合建行 A3341TP01 接口规范的订单数据 * - * ⚠️ 注意:Shopro字段说明 - * - pay_fee: 实际支付金额(Shopro字段) - * - order_amount: 订单总金额(Shopro字段) - * - total_discount_fee: 优惠总金额(Shopro字段) - * - paid_time: 支付时间(毫秒时间戳!需除以1000) - * - createtime: 创建时间(毫秒时间戳!需除以1000) + * 📋 建行生活订单推送接口规范说明: + * + * 必填字段(11个): + * - USER_ID: 客户编号(建行用户ID) + * - ORDER_ID: 订单号 + * - ORDER_DT: 订单日期(yyyyMMddHHmmss格式) + * - TOTAL_AMT: 订单原金额 + * - ORDER_STATUS: 订单状态 + * - MCT_NM: 商户名称 + * - CUS_ORDER_URL: 订单详情链接 + * - PAY_FLOW_ID: 支付流水号(从 shopro_pay.pay_sn 获取) + * - PRPFTL_MRCH_ID: 门店商户号(使用 merchant_id) + * - PAY_MRCH_ID: 支付商户号(使用 merchant_id) + * - SKU_LIST: 商品信息JSON字符串 + * + * 非必填但推荐字段: + * - PAY_AMT: 订单实际支付金额 + * - DISCOUNT_AMT: 第三方平台优惠金额 + * - DISCOUNT_AMT_DESC: 第三方平台优惠说明 + * - REFUND_STATUS: 退款状态 + * - PAY_MODE: 支付方式 + * - PLAT_ORDER_TYPE: 服务方订单类型 + * - COUPON_AMT: 优惠券金额 + * + * ⚠️ 注意:Shopro字段映射 + * - pay_fee → PAY_AMT(实际支付金额) + * - order_amount → TOTAL_AMT(订单总金额) + * - total_discount_fee → DISCOUNT_AMT(优惠总金额) + * - createtime → ORDER_DT(毫秒时间戳需除以1000) * * @param array $order 订单数组 * @param array $orderItems 订单商品列表 * @param string $ccbUserId 建行用户ID + * @param string $payFlowId 支付流水号(从shopro_pay表获取) * @return array */ - private function buildOrderData($order, $orderItems, $ccbUserId) + private function buildOrderData($order, $orderItems, $ccbUserId, $payFlowId) { - // 构建商品列表 - $goodsList = $this->buildGoodsList($orderItems); - - // 计算各项金额(Shopro字段:pay_fee=实付金额,order_amount=订单总金额) + // 构建SKU商品列表(JSON字符串格式) + $skuList = $this->buildSkuList($orderItems); + // 计算各项金额(保留2位小数) $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, '.', ''); - - // 处理支付时间(Shopro的paid_time是毫秒时间戳,需要除以1000) - $payTime = ''; - if (!empty($order['paid_time']) && is_numeric($order['paid_time'])) { - $payTime = date('YmdHis', intval($order['paid_time'] / 1000)); - } - - // 处理创建时间(Shopro的createtime是毫秒时间戳,需要除以1000) + // 处理订单时间(Shopro的createtime是毫秒时间戳,需要除以1000) $createTimeValue = $order['createtime'] ?? null; if (empty($createTimeValue) || !is_numeric($createTimeValue)) { - $createTimeValue = time() * 1000; // 当前时间的毫秒时间戳 + $createTimeValue = time() * 1000; } - $createTime = date('YmdHis', intval($createTimeValue / 1000)); + $orderDt = 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(); - - // 构建订单数据(34个必填字段) + // 构建符合A3341TP01接口规范的订单数据 return [ - 'USER_ID' => $ccbUserId, // 建行用户ID + // ========== 必填字段 ========== + 'USER_ID' => $ccbUserId, // 客户编号 'ORDER_ID' => $order['order_sn'], // 订单号 - 'ORDER_DT' => $createTime, // 订单时间 + 'ORDER_DT' => $orderDt, // 订单日期(yyyyMMddHHmmss) 'TOTAL_AMT' => $totalAmount, // 订单原金额 - 'PAY_AMT' => $payAmount, // 实付金额 - 'DISCOUNT_AMT' => $discountAmount, // 优惠金额 'ORDER_STATUS' => $this->mapOrderStatus($order['status']), // 订单状态 - 'REFUND_STATUS' => '0', // 退款状态(默认无退款) + 'REFUND_STATUS' => $this->mapRefundStatus($order['refund_status'] ?? 0), // 退款状态 'MCT_NM' => $this->config['merchant']['name'] ?? '商户名称', // 商户名称 - 'MCT_ORDER_ID' => $order['id'], // 商户订单ID - 'GOODS_LIST' => json_encode($goodsList, JSON_UNESCAPED_UNICODE), // 商品列表 - 'PAY_TYPE' => $this->mapPayType($payInfo['pay_type'] ?? ''), // 支付方式(从支付表获取) - 'PAY_TIME' => $payTime, // 支付时间(毫秒转秒) - 'DELIVERY_TYPE' => '01', // 配送方式(01快递) - 'DELIVERY_STATUS' => $this->mapDeliveryStatus($order['status']), // 配送状态 - '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'] ?? '', // 快递单号(从快递表获取) - 'REMARK' => $order['remark'] ?? '', // 备注 - 'ORDER_TYPE' => '01', // 订单类型(01普通订单) - 'IS_VIRTUAL' => '0', // 是否虚拟商品 - 'ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接 - 'CREATE_TIME' => $createTime, // 创建时间 - 'UPDATE_TIME' => date('YmdHis'), // 更新时间 - 'SHOP_ID' => '1', // 店铺ID - 'SHOP_NAME' => $this->config['merchant']['name'] ?? '', // 店铺名称 - 'ACTIVITY_ID' => '', // 活动ID - 'ACTIVITY_NAME' => '', // 活动名称 - 'COUPON_AMT' => number_format($order['coupon_discount_fee'] ?? 0, 2, '.', ''), // 优惠券金额 - 'FREIGHT_AMT' => number_format($order['dispatch_amount'] ?? 0, 2, '.', ''), // 运费(Shopro字段名为dispatch_amount) + 'CUS_ORDER_URL' => $this->config['merchant']['order_detail_url'] . $order['id'], // 订单详情链接 + 'PAY_FLOW_ID' => $payFlowId, // 支付流水号(必填!) + 'PAY_MRCH_ID' => $this->config['merchant_id'], // 支付商户号(必填!) + 'SKU_LIST' => $skuList, // 商品信息JSON字符串(必填!) + // ========== 非必填字段 ========== + 'PLAT_ORDER_TYPE' => "T0000", // 服务方订单类型 ]; } /** - * 构建商品列表 + * 构建符合建行规范的SKU商品列表(JSON字符串格式) * - * @param array $items 订单商品项 - * @return array + * 📋 建行 SKU_LIST 字段规范: + * + * 必填字段(4个): + * - SKU_NAME: 商品名称(必填) + * - SKU_REF_PRICE: 商品参考价(必填,支持小数最多2位) + * - SKU_NUM: 商品数量(必填,支持小数最多1位) + * - SKU_SELL_PRICE: 商品售价(必填,支持小数最多2位) + * + * ⚠️ 注意:Shopro字段映射 + * - goods_title → SKU_NAME(商品名称) + * - goods_original_price → SKU_REF_PRICE(商品原价作为参考价) + * - goods_num → SKU_NUM(购买数量) + * - goods_price → SKU_SELL_PRICE(商品实际售价) + * + * @param array $items 订单商品项数组 + * @return string JSON字符串格式的SKU列表 */ - private function buildGoodsList($items) + private function buildSkuList($items) { - $goodsList = []; + $skuList = []; 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'] ?? '' + $skuList[] = [ + 'SKU_NAME' => $item['goods_title'], // 商品名称(必填) + 'SKU_REF_PRICE' => number_format($item['goods_original_price'] ?? $item['goods_price'], 2, '.', ''), // 商品参考价(必填) + 'SKU_NUM' => $item['goods_num'], // 商品数量(必填) + 'SKU_SELL_PRICE' => number_format($item['goods_price'], 2, '.', ''), // 商品售价(必填) ]; } - return $goodsList; + + // 返回JSON字符串(不转义Unicode,保持中文可读) + return json_encode($skuList, JSON_UNESCAPED_UNICODE); } - - /** - * 构建收货地址 - * - * ⚠️ 注意:Shopro的收货地址存储在单独的表 shopro_order_address 中 - * - * @param array $order 订单数组 - * @return string - */ - private function buildAddress($order) - { - // 从订单地址表获取地址信息 - $orderAddress = Db::name('shopro_order_address') - ->where('order_id', $order['id']) - ->find(); - - if (!$orderAddress) { - return ''; - } - - $address = ''; - 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']; - - return $address; - } - + /** * 记录同步日志 * @@ -543,38 +520,6 @@ class CcbOrderService return '0'; } - /** - * 映射支付方式 - * - * @param string $payType 支付类型 - * @return string - */ - private function mapPayType($payType) - { - $payMap = [ - 'wechat' => '01', // 微信支付 - 'alipay' => '02', // 支付宝 - 'ccb' => '03', // 建行支付 - 'balance' => '04' // 余额支付 - ]; - - 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'; - } - /** * 批量同步订单 * 用于初始化或定时同步