'refundWaitSellerSend', // 售中等待买家收货 'waitbuyerreceive'=>'refundWaitBuyerReceive', // // 售中已收货(未确认完成交易) // 'BuyerReceived'=>'refundBuyerReceived', // // 售后未收货 // 'saleBuyerNotReceived'=>'aftersaleBuyerNotReceived', // // 售后已收到货 // 'saleBuyerReceived'=>'aftersaleBuyerReceived', ]; /** * 预下单 * * API: com.alibaba.trade:alibaba.createOrder.preview-1 * * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.createOrder.preview-1 * * 请求参数格式: * - flow: 流程标识(saleproxy=分销采购, general=普通下单) * - productList: 商品列表JSON数组,每个商品包含 offerId, quantity, specId * - addressParam: 地址参数JSON对象,包含 fullName, mobile, provinceText, cityText, areaText, address * - totalAmount: 总金额(单位:分,可选) * - message: 买家留言(可选) * * @param array $params 预下单参数 * @return array|false */ public function previewOrder(array $params = []) { $businessParams = []; // --- productList(商品列表,JSON字符串) --- // 直接传入已格式化的数组,由方法转为JSON if (!empty($params['cargoParamList'])) { $businessParams['cargoParamList'] = is_array($params['cargoParamList']) ? json_encode($params['cargoParamList'], JSON_UNESCAPED_UNICODE) : $params['cargoParamList']; } // --- addressParam(地址参数,JSON字符串) --- // 直接传入已格式化的数组,由方法转为JSON if (!empty($params['addressParam'])) { $businessParams['addressParam'] = is_array($params['addressParam']) ? json_encode($params['addressParam'], JSON_UNESCAPED_UNICODE) : $params['addressParam']; } // --- 调用API --- $result = $this->executeParam2( self::NAMESPACE, 'alibaba.createOrder.preview', 1, $businessParams ); if ($result === false) { Log::channel('alibaba')->error('[预下单] API请求失败', $businessParams); return false; } Log::channel('alibaba')->info('[预下单] API请求成功', ['result' => json_encode($result)]); return $this->formatPreviewResult($result); } /** * 格式化预下单返回结果 * * 实际返回结构: * { * "orderPreviewResuslt": [{ * "tradeModeNameList": ["assureTrade"], * "status": true, * "sumPayment": 370, // 总支付金额(单位:分) * "sumCarriage": 300, // 总运费(单位:分) * "sumPaymentNoCarriage": 70, // 商品总金额(不含运费,单位:分) * "flowFlag": "fenxiaonew", * "cargoList": [{ // 商品列表 * "amount": 0.7, * "finalUnitPrice": 0.7, * "specId": "...", * "skuId": 6148579878582, * "offerId": 678279922176, * "openOfferId": "...", * "cargoPromotionList": [] * }], * "tradeModelList": [{"name":"担保交易","tradeType":"assureTrade","opSupport":true}], * "payChannelInfos": [{"name":"alipay"}], * "orderGroup": "79d4f82d...", * "canUseOfficialSolution": false * }], * "success": true, * "unsupportedCrossBorderPayOfferList": [] * } * * @param array $result API原始返回数据 * @return array */ protected function formatPreviewResult(array $result): array { $success = $result['success'] ?? false; $previewList = $result['orderPreviewResuslt'] ?? []; if (empty($previewList) || !is_array($previewList)) { return [ 'success' => $success, 'preview' => null, 'list' => [], ]; } $formattedList = []; foreach ($previewList as $item) { $formattedList[] = [ 'sumPayment' => $item['sumPayment'] ?? 0, 'sumCarriage' => $item['sumCarriage'] ?? 0, 'sumPaymentNoCarriage' => $item['sumPaymentNoCarriage'] ?? 0, 'flowFlag' => $item['flowFlag'] ?? '', 'msg' => $item['message'] ?? '', // 'status' => $item['status'] ?? false, // 'cargoList' => $item['cargoList'] ?? [], // 'tradeModeNameList' => $item['tradeModeNameList'] ?? [], // 'tradeModelList' => $item['tradeModelList'] ?? [], // 'payChannelInfos' => $item['payChannelInfos'] ?? [], // 'shopPromotionList' => $item['shopPromotionList'] ?? [], // 'tradeServiceList' => $item['tradeServiceList'] ?? [], // 'taoSampleSinglePromotion' => $item['taoSampleSinglePromotion'] ?? false, // 'canUseOfficialSolution' => $item['canUseOfficialSolution'] ?? false, ]; } return [ 'success' => $success, 'preview' => $formattedList[0] ?? null, // 'list' => $formattedList, // 'unsupportedCrossBorderPayOfferList' => $result['unsupportedCrossBorderPayOfferList'] ?? [], ]; } /** * 快速创建订单 * * API: com.alibaba.trade:alibaba.trade.fastCreateOrder-1 * * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.trade.fastCreateOrder-1 * * 请求参数: * - flow: 流程标识(saleproxy=分销采购, general=普通下单) * - cargoParamList: 商品列表JSON数组(alibaba.trade.fast.cargo[]),每个商品包含 offerId, quantity, specId * - addressParam: 地址参数JSON对象,包含 fullName, mobile, provinceText, cityText, areaText, address * - totalAmount: 总金额(单位:分,可选) * - message: 买家留言(可选) * - orderGroup: 订单分组标识(从预下单返回的 orderGroup 字段获取,可选) * * @param array $params 创建订单参数 * @return array|false */ public function fastCreateOrder(array $params = []) { $businessParams = []; // --- flow(流程标识) --- $businessParams['flow'] = $params['flow'] ?? 'general'; // --- cargoParamList(商品列表,JSON字符串) --- if (!empty($params['cargoParamList'])) { $businessParams['cargoParamList'] = is_array($params['cargoParamList']) ? json_encode($params['cargoParamList'], JSON_UNESCAPED_UNICODE) : $params['cargoParamList']; } // --- addressParam(地址参数,JSON字符串) --- if (!empty($params['addressParam'])) { $businessParams['addressParam'] = is_array($params['addressParam']) ? json_encode($params['addressParam'], JSON_UNESCAPED_UNICODE) : $params['addressParam']; } // --- 调用API --- $result = $this->executeParam2( self::NAMESPACE, 'alibaba.trade.fastCreateOrder', 1, $businessParams ); if ($result === false) { Log::channel('alibaba')->error('[创建订单] API请求失败', $businessParams); return false; } Log::channel('alibaba')->info('[创建订单] API请求成功', ['result' => json_encode($result)]); return $this->formatCreateOrderResult($result); } /** * @param StoreOrderEntity[] $storeOrderEntityList * @return bool * @author 史晨 * @date 2026/4/30 09:44 * 支付成功通知1688 */ public function paySuccess(array $storeOrderEntityList): bool { foreach ($storeOrderEntityList as $storeOrderEntity) { $alibabaOrderId = $storeOrderEntity->getAlibabaOrderId(); if (empty($alibabaOrderId)) { Log::channel('alibaba')->error($storeOrderEntity->getOrderSn() . '不是1688订单,不需要调取1688支付成功接口'); continue; } $res = $this->prepareProtocolPay(['orderId' => $alibabaOrderId]); if ($res['success'] === false) { return false; } } return true; } /** * 免密支付准备 * * API: com.alibaba.trade:alibaba.trade.pay.protocolPay.preparePay-1 * * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.trade.pay.protocolPay.preparePay-1 * * 请求参数说明(接口文档定义): * - tradeWithholdPreparePayParam: 必填,类型 message:alibaba.trade.pay.withhold.preparePayParam * preparePayParam 结构: * - orderId: 订单ID(Long,必填) * * 调用示例: * $orderService->prepareProtocolPay([ * 'orderId' => 123456789, * ]); * * @param array $params 免密支付参数(orderId) * @return array|false */ public function prepareProtocolPay(array $params = []) { // --- 参数校验 --- if (empty($params['orderId'])) { Log::channel('alibaba')->error('[免密支付] orderId不能为空'); return false; } // --- 构建 tradeWithholdPreparePayParam(message类型需序列化为JSON字符串) --- $businessParams = [ 'tradeWithholdPreparePayParam' => json_encode([ 'orderId' => (int)$params['orderId'], ], JSON_UNESCAPED_UNICODE), ]; // --- 调用API --- $result = $this->executeParam2( self::NAMESPACE, 'alibaba.trade.pay.protocolPay.preparePay', 1, $businessParams ); if ($result === false) { Log::channel('alibaba')->error('[免密支付] API请求失败', $businessParams); return false; } Log::channel('alibaba')->info('[免密支付] API请求成功', ['result' => json_encode($result)]); return $this->formatProtocolPayResult($result); } /** * 格式化免密支付准备返回结果 * * @param array $result API原始返回数据 * @return array */ protected function formatProtocolPayResult(array $result): array { // 返回结构: { "result": { ... }, "success": true } $data = $result['result'] ?? $result; $success = $result['success'] ?? false; Log::channel('alibaba')->info('[免密支付] 返回结果', [ 'success' => $success, 'data' => $data, ]); return [ 'success' => $success, 'data' => $data, 'rawData' => $result, ]; } /** * 格式化创建订单返回结果 * * @param array $result API原始返回数据 * @return array */ protected function formatCreateOrderResult(array $result): array { // 创建订单成功返回: { "result": { "orderId": "123456", "totalAmount": 370 }, "success": true } $data = $result['result'] ?? $result; $success = $result['success'] ?? false; return [ 'success' => $success, 'orderId' => $data['orderId'] ?? 0, 'totalAmount' => $data['totalAmount'] ?? 0, 'rawData' => $data, ]; } /** * 构建商品列表参数(便捷方法) * * @param array $items 商品列表 [['offerId' => 123, 'quantity' => 2, 'skuId' => 456], ...] * @return array */ public function buildProductList(array $items): array { $productList = []; foreach ($items as $item) { $product = [ 'offerId' => (int)($item['offerId'] ?? $item['productId'] ?? 0), 'quantity' => (int)($item['quantity'] ?? 1), ]; if (!empty($item['skuId'])) { $product['skuId'] = (int)$item['skuId']; } if (!empty($item['price'])) { $product['price'] = (int)$item['price']; // 单位:分 } if (!empty($item['productUnit'])) { $product['productUnit'] = $item['productUnit']; } $productList[] = $product; } return $productList; } /** * 构建地址参数(便捷方法) * * @param int $addressId 地址ID * @return array */ public function buildAddressParam(int $addressId): array { return [ 'addressId' => $addressId, ]; } /** * 构建完整地址参数(使用系统地址数据格式) * * 适配系统 user_address 表字段: * address_id, real_name, phone, province, city, district, detail, post_code, code_list * * @param array $address 地址信息(系统user_address表数据) * @return array */ public function buildFullAddressParam(array $address): array { // 解析地区编码列表: "1,67449,35,493" $codeList = $address['code_list'] ?? ''; $codes = explode(',', $codeList); $districtCode = $codes[1] ?? $codes[0] ?? ''; return [ 'fullName' => $address['real_name'] ?? $address['fullName'] ?? '', 'mobile' => $address['phone'] ?? $address['mobile'] ?? '', 'phone' => $address['phone'] ?? $address['phone'] ?? '', 'postCode' => $address['post_code'] ?? $address['postCode'] ?? '', 'cityText' => $address['city'] ?? $address['cityText'] ?? '', 'provinceText' => $address['province'] ?? $address['provinceText'] ?? '', 'areaText' => $address['district'] ?? $address['areaText'] ?? '', 'townText' => $address['village'] ?? $address['townText'] ?? '', 'address' => $address['detail'] ?? $address['address'] ?? '', 'districtCode' => $districtCode, ]; } /** * 获取交易订单的物流信息(买家视角) * * API: com.alibaba.trade:alibaba.trade.get.logisticsInfos.buyerView-1 * * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.trade.get.logisticsInfos.buyerView-1 * * 请求参数: * - orderId: 1688订单ID(必填) * - webSite: 1688站点(可选,默认"1688") * * 返回示例: * { * "result": { * "logisticsItems": [{ * "logisticsId": "LP001", * "logisticsCode": "SF1234567890", // 物流单号 * "logisticsName": "顺丰速运", // 物流公司名称 * "logisticsCompany": "shunfeng", // 物流公司编码 * "status": "accept", // 物流状态 * "gmtCreate": "2018-05-30 19:34:27" * }] * }, * "success": true * } * * @param array $params 请求参数(orderId 必填) * @return array|false */ public function getLogisticsInfosBuyerView(array $params = []) { // --- 参数校验 --- if (empty($params['orderId'])) { Log::channel('alibaba')->error('[获取物流信息] orderId不能为空'); return false; } $businessParams = [ 'orderId' => (string)$params['orderId'], 'webSite' => $params['webSite'] ?? '1688', ]; // --- 调用API --- $result = $this->executeParam2( self::LOGISTICS_NAMESPACE, 'alibaba.trade.getLogisticsInfos.buyerView', 1, $businessParams ); if ($result === false) { Log::channel('alibaba')->error('[获取物流信息] API请求失败, params:' . json_encode($businessParams, JSON_UNESCAPED_UNICODE)); return false; } Log::channel('alibaba')->info('[获取物流信息] API请求成功, result:' . json_encode($result, JSON_UNESCAPED_UNICODE)); return $this->formatLogisticsInfosResult($result); } /** * 格式化物流信息返回结果 * * 实际返回数据结构(result为数组): * { * "result": [{ * "logisticsId": "LP00815917141701", * "logisticsBillNo": "YT7619307479079", // 物流单号 * "logisticsCompanyName": "圆通速递(YTO)", // 物流公司名称 * "logisticsCompanyNo": "YTO", // 物流公司编码 * "status": "SIGN", * ... * }], * "success": true * } * * @param array $result API原始返回数据 * @return array */ protected function formatLogisticsInfosResult(array $result): array { $success = $result['success'] ?? false; $logisticsItems = []; // result 是数组,每个元素是一条物流信息 if (isset($result['result']) && is_array($result['result'])) { foreach ($result['result'] as $item) { $logisticsItems[] = [ 'logisticsId' => $item['logisticsId'] ?? '', 'logisticsBillNo' => $item['logisticsBillNo'] ?? '', // 物流单号 'logisticsName' => $item['logisticsCompanyName'] ?? '', // 物流公司名称 'logisticsCompany' => $item['logisticsCompanyNo'] ?? '', // 物流公司编码 'status' => $item['status'] ?? '', 'orderEntryIds' => $item['orderEntryIds'] ?? '', ]; } } return [ 'success' => $success, 'logisticsItems' => $logisticsItems, 'rawData' => $result, ]; } /** * 获取订单详情(买家视角) * * API: com.alibaba.trade:alibaba.trade.get.buyerView-1 * * @param string $orderId 1688订单号 * @return array|false */ public function getOrderDetailBuyerView(string $orderId) { if (empty($orderId)) { Log::channel('alibaba')->error('[获取订单详情] orderId不能为空'); return false; } $businessParams = [ 'orderId' => $orderId, 'webSite' => 1688, ]; $result = $this->executeParam2( self::NAMESPACE, 'alibaba.trade.get.buyerView', 1, $businessParams ); if ($result === false) { Log::channel('alibaba')->error('[获取订单详情] API请求失败, orderId:' . $orderId); return false; } Log::channel('alibaba')->info('[获取订单详情] API请求成功, orderId:' . $orderId . ', result:' . json_encode($result, JSON_UNESCAPED_UNICODE)); return $result; } /** * 查询退款退货原因列表 * * 流程: * 1. 先调用 alibaba.trade.get.buyerView 获取订单详情 * 2. 从订单详情中提取子订单号(orderEntryIds)、货物状态、可退款金额、可退运费 * 3. 再调用 alibaba.trade.getRefundReasonList 获取退款原因 * * API: com.alibaba.trade:alibaba.trade.getRefundReasonList-1 * * @param array $params 请求参数(orderId 必填) * @return array|false */ public function getRefundReasonList(array $params = []) { // --- 参数校验 --- if (empty($params['orderId'])) { Log::channel('alibaba')->error('[获取退款原因] orderId不能为空'); return false; } $orderId = (string)$params['orderId']; // 1. 先获取订单详情,提取子订单号和货物状态 $orderDetail = $this->getOrderDetailBuyerView($orderId); if ($orderDetail === false) { Log::channel('alibaba')->error('[获取退款原因] 获取订单详情失败, orderId:' . $orderId); return false; } // 从订单详情中提取子订单ID列表 $orderEntryIds = $orderDetail['result']['productItems'][0]['subItemID'] ?: ''; $goodsStatus = self::REFUND_GOOD_STATUS[$orderDetail['result']['productItems'][0]['status']] ?: ''; if (!$goodsStatus) { Log::channel('alibaba')->error('[获取退款原因] 获取货物状态失败, orderId:' . $orderId); return false; } // 提取可退款金额和可退运费(从订单详情中) // totalSuccessAmount: 实付款(分),carriage: 运费(分) $applyPayment = (string)$orderDetail['result']['baseInfo']['sumProductPayment'] ?? 0; $applyCarriage = (string)$orderDetail['result']['baseInfo']['shippingFee'] ?? 0; // 2. 调用退款原因查询API $businessParams = [ 'orderId' => $orderId, 'orderEntryIds' => '[' . $orderEntryIds . ']', 'goodsStatus' => $goodsStatus, ]; Log::channel('alibaba')->info('[获取退款原因] 准备请求, orderId:' . $orderId . ', orderEntryIds:' . json_encode($orderEntryIds) . ', goodsStatus:' . $goodsStatus); // --- 调用API --- $result = $this->executeParam2( self::NAMESPACE, 'alibaba.trade.getRefundReasonList', 1, $businessParams ); if ($result === false) { Log::channel('alibaba')->error('[获取退款原因] API请求失败, params:' . json_encode($businessParams, JSON_UNESCAPED_UNICODE)); return false; } Log::channel('alibaba')->info('[获取退款原因] API请求成功, result:' . json_encode($result, JSON_UNESCAPED_UNICODE)); return $this->formatRefundReasonListResult($result, $orderId, $orderEntryIds, $goodsStatus, $applyPayment, $applyCarriage); } /** * 格式化退款退货原因列表返回结果 * * 实际返回数据结构: * { * "result": { * "result": { * "reasons": [ * {"id": 20006, "name": "不想买了", "needVoucher": false, "noRefundCarriage": false, "tip": ""} * ] * }, * "success": true * } * } * * @param array $result API原始返回数据 * @param string $orderId 1688订单号 * @param string $orderEntryIds 子订单ID列表 * @param string $goodsStatus 货物状态 * @param int $applyPayment 可退款金额(分) * @param int $applyCarriage 可退运费(分) * @return array */ protected function formatRefundReasonListResult(array $result, string $orderId = '', string $orderEntryIds = '', string $goodsStatus = '', string $applyPayment = '0', string $applyCarriage = '0'): array { $reasons = []; if (isset($result['result']['result']['reasons']) && is_array($result['result']['result']['reasons'])) { foreach ($result['result']['result']['reasons'] as $reason) { $reasons[] = [ 'id' => $reason['id'] ?? '', 'name' => $reason['name'] ?? '', ]; } } return [ 'orderId' => $orderId, 'orderEntryIds' => $orderEntryIds, 'goodsStatus' => $goodsStatus, 'applyPayment' => $applyPayment, 'applyCarriage' => $applyCarriage, 'reasons' => $reasons, ]; } /** * 提交售后申请 * * API: com.alibaba.trade:alibaba.trade.createRefund-1 * * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.trade.createRefund-1 * * 请求参数: * - orderId: 主订单ID(必填) * - orderEntryIds: 子订单ID列表,格式 "[123,456]"(必填) * - disputeReasonId: 退款原因ID(必填,从 getRefundReasonList 获取) * - description: 退款说明(必填) * - goodsStatus: 货物状态(必填) * - applyPayment: 退款金额,单位分(必填) * - applyCarriage: 退运费,单位分(可选,默认0) * - voucher: 凭证图片列表,格式 "[{url:'http://...'}]"(可选) * - applyReason: 退款原因描述(可选) * * 返回示例: * { * "result": { * "result": { * "refundId": "RF123456", * "status": "refundWaitSellerSend" * }, * "success": true * } * } * * @param array $params 请求参数 * @return array|false */ public function createRefund(array $params = []) { // --- 参数校验 --- $requiredFields = ['orderId', 'orderEntryIds', 'disputeReasonId', 'description', 'goodsStatus', 'applyPayment', 'applyCarriage', 'applyReasonId']; foreach ($requiredFields as $field) { if (!isset($params[$field])) { Log::channel('alibaba')->error('[提交售后申请] 缺少必填参数: ' . $field); return [ 'success' => false, 'refundId' => '', 'status' => 'param_missing', 'rawData' => ['error' => '缺少必填参数: ' . $field], ]; } } $businessParams = [ 'orderId' => (string)$params['orderId'], 'orderEntryIds' => '[' . $params['orderEntryIds'] . ']', 'disputeRequest' => (string)$params['disputeReasonId'], 'applyPayment' => (string)$params['applyPayment'], 'applyCarriage' => (string)$params['applyCarriage'], 'applyReasonId' => (string)$params['applyReasonId'], 'description' => (string)$params['description'], 'goodsStatus' => (string)$params['goodsStatus'], ]; // 可选参数 if (!empty($params['voucher'])) { $businessParams['voucher'] = $params['voucher']; } Log::channel('alibaba')->info('[提交售后申请] 准备请求, params:' . json_encode($businessParams, JSON_UNESCAPED_UNICODE)); // --- 调用API --- $result = $this->executeParam2( self::NAMESPACE, 'alibaba.trade.createRefund', 1, $businessParams ); // 不管API调用是否成功,都返回格式化的结果(包含原始数据) if ($result === false) { Log::channel('alibaba')->error('[提交售后申请] API请求失败, params:' . json_encode($businessParams, JSON_UNESCAPED_UNICODE)); return [ 'success' => false, 'refundId' => '', 'status' => 'api_failed', 'rawData' => ['error' => 'API请求失败'], ]; } Log::channel('alibaba')->info('[提交售后申请] API请求成功, result:' . json_encode($result, JSON_UNESCAPED_UNICODE)); return $this->formatCreateRefundResult($result); } /** * 格式化提交售后申请返回结果 * * 成功返回数据结构: * { * "result": { * "result": { * "refundId": "RF123456", * "status": "refundWaitSellerSend" * }, * "success": true * } * } * * 失败返回数据结构: * { * "result": { * "code": "5000", * "message": "错误描述", * "success": false * } * } * * @param array $result API原始返回数据 * @return array */ protected function formatCreateRefundResult(array $result): array { // 检查 result 层是否存在 $resultData = $result['result'] ?? $result; $success = $resultData['success'] ?? false; if ($success) { // 成功:result.result 中包含 refundId 和 status $refundResult = $resultData['result'] ?? []; return [ 'success' => true, 'refundId' => $refundResult['refundId'] ?? '', 'status' => $refundResult['status'] ?? '', 'rawData' => $result, ]; } // 失败:result 中包含 code 和 message return [ 'success' => false, 'refundId' => '', 'status' => $resultData['message'] ?? ($resultData['code'] ?? 'api_error'), 'rawData' => $result, ]; } }