OrderService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. /**
  3. * 1688分销严选采购解决方案 - 订单服务类
  4. *
  5. * 提供预下单、下单、订单查询等功能
  6. *
  7. * @author: yourname
  8. * @day: 2026/04/28
  9. */
  10. namespace app\services\ThirdParty\AlibabaAgent;
  11. use think\facade\Db;
  12. use think\facade\Log;
  13. class OrderService extends AlibabaAgentBaseService
  14. {
  15. /**
  16. * API命名空间
  17. */
  18. const NAMESPACE = 'com.alibaba.trade';
  19. /**
  20. * 预下单
  21. *
  22. * API: com.alibaba.trade:alibaba.createOrder.preview-1
  23. *
  24. * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.createOrder.preview-1
  25. *
  26. * 请求参数格式:
  27. * - flow: 流程标识(saleproxy=分销采购, general=普通下单)
  28. * - productList: 商品列表JSON数组,每个商品包含 offerId, quantity, specId
  29. * - addressParam: 地址参数JSON对象,包含 fullName, mobile, provinceText, cityText, areaText, address
  30. * - totalAmount: 总金额(单位:分,可选)
  31. * - message: 买家留言(可选)
  32. *
  33. * @param array $params 预下单参数
  34. * @return array|false
  35. */
  36. public function previewOrder(array $params = [])
  37. {
  38. $businessParams = [];
  39. // --- productList(商品列表,JSON字符串) ---
  40. // 直接传入已格式化的数组,由方法转为JSON
  41. if (!empty($params['cargoParamList'])) {
  42. $businessParams['cargoParamList'] = is_array($params['cargoParamList'])
  43. ? json_encode($params['cargoParamList'], JSON_UNESCAPED_UNICODE)
  44. : $params['cargoParamList'];
  45. }
  46. // --- addressParam(地址参数,JSON字符串) ---
  47. // 直接传入已格式化的数组,由方法转为JSON
  48. if (!empty($params['addressParam'])) {
  49. $businessParams['addressParam'] = is_array($params['addressParam'])
  50. ? json_encode($params['addressParam'], JSON_UNESCAPED_UNICODE)
  51. : $params['addressParam'];
  52. }
  53. // --- 调用API ---
  54. $result = $this->executeParam2(
  55. self::NAMESPACE,
  56. 'alibaba.createOrder.preview',
  57. 1,
  58. $businessParams
  59. );
  60. if ($result === false) {
  61. Log::channel('alibaba')->error('[预下单] API请求失败', $businessParams);
  62. return false;
  63. }
  64. Log::channel('alibaba')->info('[预下单] API请求成功', ['result' => $result]);
  65. return $this->formatPreviewResult($result);
  66. }
  67. /**
  68. * 格式化预下单返回结果
  69. *
  70. * 实际返回结构:
  71. * {
  72. * "orderPreviewResuslt": [{
  73. * "tradeModeNameList": ["assureTrade"],
  74. * "status": true,
  75. * "sumPayment": 370, // 总支付金额(单位:分)
  76. * "sumCarriage": 300, // 总运费(单位:分)
  77. * "sumPaymentNoCarriage": 70, // 商品总金额(不含运费,单位:分)
  78. * "flowFlag": "fenxiaonew",
  79. * "cargoList": [{ // 商品列表
  80. * "amount": 0.7,
  81. * "finalUnitPrice": 0.7,
  82. * "specId": "...",
  83. * "skuId": 6148579878582,
  84. * "offerId": 678279922176,
  85. * "openOfferId": "...",
  86. * "cargoPromotionList": []
  87. * }],
  88. * "tradeModelList": [{"name":"担保交易","tradeType":"assureTrade","opSupport":true}],
  89. * "payChannelInfos": [{"name":"alipay"}],
  90. * "orderGroup": "79d4f82d...",
  91. * "canUseOfficialSolution": false
  92. * }],
  93. * "success": true,
  94. * "unsupportedCrossBorderPayOfferList": []
  95. * }
  96. *
  97. * @param array $result API原始返回数据
  98. * @return array
  99. */
  100. protected function formatPreviewResult(array $result): array
  101. {
  102. $success = $result['success'] ?? false;
  103. $previewList = $result['orderPreviewResuslt'] ?? [];
  104. if (empty($previewList) || !is_array($previewList)) {
  105. return [
  106. 'success' => $success,
  107. 'preview' => null,
  108. 'list' => [],
  109. ];
  110. }
  111. $formattedList = [];
  112. foreach ($previewList as $item) {
  113. $formattedList[] = [
  114. 'sumPayment' => $item['sumPayment'] ?? 0,
  115. 'sumCarriage' => $item['sumCarriage'] ?? 0,
  116. 'sumPaymentNoCarriage' => $item['sumPaymentNoCarriage'] ?? 0,
  117. 'flowFlag' => $item['flowFlag'] ?? '',
  118. // 'orderGroup' => $item['orderGroup'] ?? '',
  119. // 'status' => $item['status'] ?? false,
  120. // 'cargoList' => $item['cargoList'] ?? [],
  121. // 'tradeModeNameList' => $item['tradeModeNameList'] ?? [],
  122. // 'tradeModelList' => $item['tradeModelList'] ?? [],
  123. // 'payChannelInfos' => $item['payChannelInfos'] ?? [],
  124. // 'shopPromotionList' => $item['shopPromotionList'] ?? [],
  125. // 'tradeServiceList' => $item['tradeServiceList'] ?? [],
  126. // 'taoSampleSinglePromotion' => $item['taoSampleSinglePromotion'] ?? false,
  127. // 'canUseOfficialSolution' => $item['canUseOfficialSolution'] ?? false,
  128. ];
  129. }
  130. return [
  131. // 'success' => $success,
  132. 'preview' => $formattedList[0] ?? null,
  133. // 'list' => $formattedList,
  134. // 'unsupportedCrossBorderPayOfferList' => $result['unsupportedCrossBorderPayOfferList'] ?? [],
  135. ];
  136. }
  137. /**
  138. * 快速创建订单
  139. *
  140. * API: com.alibaba.trade:alibaba.trade.fastCreateOrder-1
  141. *
  142. * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.trade.fastCreateOrder-1
  143. *
  144. * 请求参数:
  145. * - flow: 流程标识(saleproxy=分销采购, general=普通下单)
  146. * - cargoParamList: 商品列表JSON数组(alibaba.trade.fast.cargo[]),每个商品包含 offerId, quantity, specId
  147. * - addressParam: 地址参数JSON对象,包含 fullName, mobile, provinceText, cityText, areaText, address
  148. * - totalAmount: 总金额(单位:分,可选)
  149. * - message: 买家留言(可选)
  150. * - orderGroup: 订单分组标识(从预下单返回的 orderGroup 字段获取,可选)
  151. *
  152. * @param array $params 创建订单参数
  153. * @return array|false
  154. */
  155. public function fastCreateOrder(array $params = [])
  156. {
  157. $businessParams = [];
  158. // --- flow(流程标识) ---
  159. $businessParams['flow'] = $params['flow'] ?? 'general';
  160. // --- cargoParamList(商品列表,JSON字符串) ---
  161. if (!empty($params['cargoParamList'])) {
  162. $businessParams['cargoParamList'] = is_array($params['cargoParamList'])
  163. ? json_encode($params['cargoParamList'], JSON_UNESCAPED_UNICODE)
  164. : $params['cargoParamList'];
  165. }
  166. // --- addressParam(地址参数,JSON字符串) ---
  167. if (!empty($params['addressParam'])) {
  168. $businessParams['addressParam'] = is_array($params['addressParam'])
  169. ? json_encode($params['addressParam'], JSON_UNESCAPED_UNICODE)
  170. : $params['addressParam'];
  171. }
  172. // --- 调用API ---
  173. $result = $this->executeParam2(
  174. self::NAMESPACE,
  175. 'alibaba.trade.fastCreateOrder',
  176. 1,
  177. $businessParams
  178. );
  179. if ($result === false) {
  180. Log::channel('alibaba')->error('[创建订单] API请求失败', $businessParams);
  181. return false;
  182. }
  183. Log::channel('alibaba')->info('[创建订单] API请求成功', ['result' => $result]);
  184. return $this->formatCreateOrderResult($result);
  185. }
  186. /**
  187. * 免密支付准备
  188. *
  189. * API: com.alibaba.trade:alibaba.trade.pay.protocolPay.preparePay-1
  190. *
  191. * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.trade.pay.protocolPay.preparePay-1
  192. *
  193. * 请求参数说明(接口文档定义):
  194. * - tradeWithholdPreparePayParam: 必填,类型 message:alibaba.trade.pay.withhold.preparePayParam
  195. * preparePayParam 结构:
  196. * - orderId: 订单ID(Long,必填)
  197. *
  198. * 调用示例:
  199. * $orderService->prepareProtocolPay([
  200. * 'orderId' => 123456789,
  201. * ]);
  202. *
  203. * @param array $params 免密支付参数(orderId)
  204. * @return array|false
  205. */
  206. public function prepareProtocolPay(array $params = [])
  207. {
  208. // --- 参数校验 ---
  209. if (empty($params['orderId'])) {
  210. Log::channel('alibaba')->error('[免密支付] orderId不能为空');
  211. return false;
  212. }
  213. // --- 构建 tradeWithholdPreparePayParam(message类型需序列化为JSON字符串) ---
  214. $businessParams = [
  215. 'tradeWithholdPreparePayParam' => json_encode([
  216. 'orderId' => (int)$params['orderId'],
  217. ], JSON_UNESCAPED_UNICODE),
  218. ];
  219. // --- 调用API ---
  220. $result = $this->executeParam2(
  221. self::NAMESPACE,
  222. 'alibaba.trade.pay.protocolPay.preparePay',
  223. 1,
  224. $businessParams
  225. );
  226. if ($result === false) {
  227. Log::channel('alibaba')->error('[免密支付] API请求失败', $businessParams);
  228. return false;
  229. }
  230. Log::channel('alibaba')->info('[免密支付] API请求成功', ['result' => $result]);
  231. return $this->formatProtocolPayResult($result);
  232. }
  233. /**
  234. * 格式化免密支付准备返回结果
  235. *
  236. * @param array $result API原始返回数据
  237. * @return array
  238. */
  239. protected function formatProtocolPayResult(array $result): array
  240. {
  241. // 返回结构: { "result": { ... }, "success": true }
  242. $data = $result['result'] ?? $result;
  243. $success = $result['success'] ?? false;
  244. Log::channel('alibaba')->info('[免密支付] 返回结果', [
  245. 'success' => $success,
  246. 'data' => $data,
  247. ]);
  248. return [
  249. 'success' => $success,
  250. 'data' => $data,
  251. 'rawData' => $result,
  252. ];
  253. }
  254. /**
  255. * 格式化创建订单返回结果
  256. *
  257. * @param array $result API原始返回数据
  258. * @return array
  259. */
  260. protected function formatCreateOrderResult(array $result): array
  261. {
  262. // 创建订单成功返回: { "result": { "orderId": "123456", "totalAmount": 370 }, "success": true }
  263. $data = $result['result'] ?? $result;
  264. $success = $result['success'] ?? false;
  265. return [
  266. 'success' => $success,
  267. 'orderId' => $data['orderId'] ?? 0,
  268. 'totalAmount' => $data['totalAmount'] ?? 0,
  269. 'rawData' => $data,
  270. ];
  271. }
  272. /**
  273. * 构建商品列表参数(便捷方法)
  274. *
  275. * @param array $items 商品列表 [['offerId' => 123, 'quantity' => 2, 'skuId' => 456], ...]
  276. * @return array
  277. */
  278. public function buildProductList(array $items): array
  279. {
  280. $productList = [];
  281. foreach ($items as $item) {
  282. $product = [
  283. 'offerId' => (int)($item['offerId'] ?? $item['productId'] ?? 0),
  284. 'quantity' => (int)($item['quantity'] ?? 1),
  285. ];
  286. if (!empty($item['skuId'])) {
  287. $product['skuId'] = (int)$item['skuId'];
  288. }
  289. if (!empty($item['price'])) {
  290. $product['price'] = (int)$item['price']; // 单位:分
  291. }
  292. if (!empty($item['productUnit'])) {
  293. $product['productUnit'] = $item['productUnit'];
  294. }
  295. $productList[] = $product;
  296. }
  297. return $productList;
  298. }
  299. /**
  300. * 构建地址参数(便捷方法)
  301. *
  302. * @param int $addressId 地址ID
  303. * @return array
  304. */
  305. public function buildAddressParam(int $addressId): array
  306. {
  307. return [
  308. 'addressId' => $addressId,
  309. ];
  310. }
  311. /**
  312. * 构建完整地址参数(使用系统地址数据格式)
  313. *
  314. * 适配系统 user_address 表字段:
  315. * address_id, real_name, phone, province, city, district, detail, post_code, code_list
  316. *
  317. * @param array $address 地址信息(系统user_address表数据)
  318. * @return array
  319. */
  320. public function buildFullAddressParam(array $address): array
  321. {
  322. // 解析地区编码列表: "1,67449,35,493"
  323. $codeList = $address['code_list'] ?? '';
  324. $codes = explode(',', $codeList);
  325. $districtCode = $codes[1] ?? $codes[0] ?? '';
  326. return [
  327. 'fullName' => $address['real_name'] ?? $address['fullName'] ?? '',
  328. 'mobile' => $address['phone'] ?? $address['mobile'] ?? '',
  329. 'phone' => $address['phone'] ?? $address['phone'] ?? '',
  330. 'postCode' => $address['post_code'] ?? $address['postCode'] ?? '',
  331. 'cityText' => $address['city'] ?? $address['cityText'] ?? '',
  332. 'provinceText' => $address['province'] ?? $address['provinceText'] ?? '',
  333. 'areaText' => $address['district'] ?? $address['areaText'] ?? '',
  334. 'townText' => $address['village'] ?? $address['townText'] ?? '',
  335. 'address' => $address['detail'] ?? $address['address'] ?? '',
  336. 'districtCode' => $districtCode,
  337. ];
  338. }
  339. }