taiwan-logistics-skill 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,536 +1,536 @@
1
- # ECPay Logistics API Reference
2
-
3
- 綠界科技 (ECPay) 物流 API 完整參考文件。
4
-
5
- ---
6
-
7
- ## 目錄
8
-
9
- 1. [API 端點總覽](#api-端點總覽)
10
- 2. [測試環境](#測試環境)
11
- 3. [物流類型](#物流類型)
12
- 4. [CheckMacValue 計算](#checkmacvalue-計算)
13
- 5. [建立物流訂單](#建立物流訂單)
14
- 6. [超商電子地圖](#超商電子地圖)
15
- 7. [列印託運單](#列印託運單)
16
- 8. [物流狀態查詢](#物流狀態查詢)
17
- 9. [物流狀態通知](#物流狀態通知)
18
- 10. [錯誤碼對照表](#錯誤碼對照表)
19
-
20
- ---
21
-
22
- ## API 端點總覽
23
-
24
- ### 基礎 API 路徑
25
-
26
- | 環境 | 基礎路徑 |
27
- |------|----------|
28
- | **測試環境** | `https://logistics-stage.ecpay.com.tw` |
29
- | **正式環境** | `https://logistics.ecpay.com.tw` |
30
-
31
- ### 物流相關端點
32
-
33
- | 功能 | 測試環境 | 正式環境 |
34
- |------|----------|----------|
35
- | 建立物流訂單 | `/Express/Create` | `/Express/Create` |
36
- | 超商電子地圖 | `/Express/map` | `/Express/map` |
37
- | 列印託運單 | `/helper/printTradeDocument` | `/helper/printTradeDocument` |
38
- | 查詢訂單 | `/Helper/QueryLogisticsTradeInfo/V2` | `/Helper/QueryLogisticsTradeInfo/V2` |
39
-
40
- ---
41
-
42
- ## 測試環境
43
-
44
- ### 測試帳號
45
-
46
- ```
47
- 測試網址: https://logistics-stage.ecpay.com.tw
48
- 商店代號: 2000132
49
- HashKey: 5294y06JbISpM5x9
50
- HashIV: v77hoKGq4kWxNNIS
51
- ```
52
-
53
- ### 測試用超商門市
54
-
55
- | 超商 | 測試門市代號 |
56
- |------|-------------|
57
- | 7-11 | `131386` |
58
- | 全家 | `006598` |
59
- | 萊爾富 | `2001` |
60
- | OK | `1328` |
61
-
62
- ---
63
-
64
- ## 物流類型
65
-
66
- ### 超商取貨類型
67
-
68
- | 類型 | 代碼 | 說明 |
69
- |------|------|------|
70
- | 7-11 超商取貨 | `UNIMART` | 統一超商 B2C |
71
- | 7-11 交貨便 | `UNIMARTC2C` | C2C 店到店 |
72
- | 全家超商取貨 | `FAMI` | 全家便利商店 B2C |
73
- | 全家店到店 | `FAMIC2C` | C2C 店到店 |
74
- | 萊爾富超商取貨 | `HILIFE` | 萊爾富 B2C |
75
- | 萊爾富店到店 | `HILIFEC2C` | C2C 店到店 |
76
- | OK 超商取貨 | `OKMART` | OK 便利商店 B2C |
77
- | OK 店到店 | `OKMARTC2C` | C2C 店到店 |
78
-
79
- ### 宅配類型
80
-
81
- | 類型 | 代碼 | 說明 |
82
- |------|------|------|
83
- | 黑貓宅急便 | `TCAT` | 宅配到府 |
84
- | 宅配通 | `ECAN` | 常溫宅配 |
85
-
86
- ### LogisticsType 對照
87
-
88
- | 值 | 說明 |
89
- |------|------|
90
- | `CVS` | 超商取貨 |
91
- | `Home` | 宅配 |
92
-
93
- ---
94
-
95
- ## CheckMacValue 計算
96
-
97
- ### 計算步驟
98
-
99
- 1. 將參數依照 Key 排序 (A-Z, 不分大小寫)
100
- 2. 組合成 `key=value&key=value` 格式
101
- 3. 前後加上 `HashKey={HashKey}&` 和 `&HashIV={HashIV}`
102
- 4. URL Encode (RFC 1866)
103
- 5. 轉小寫
104
- 6. 計算 MD5
105
- 7. 轉大寫
106
-
107
- ### PHP 範例
108
-
109
- ```php
110
- <?php
111
-
112
- function generateCheckMacValue(array $params, string $hashKey, string $hashIV): string
113
- {
114
- // 1. 排序參數 (不分大小寫)
115
- uksort($params, 'strcasecmp');
116
-
117
- // 2. 組合字串
118
- $paramStr = urldecode(http_build_query($params));
119
-
120
- // 3. 加上 HashKey 和 HashIV
121
- $raw = "HashKey={$hashKey}&{$paramStr}&HashIV={$hashIV}";
122
-
123
- // 4. URL Encode
124
- $encoded = urlencode($raw);
125
-
126
- // 5. 轉小寫
127
- $lower = strtolower($encoded);
128
-
129
- // 6. MD5
130
- $md5 = md5($lower);
131
-
132
- // 7. 轉大寫
133
- return strtoupper($md5);
134
- }
135
- ```
136
-
137
- ### Python 範例
138
-
139
- ```python
140
- import hashlib
141
- import urllib.parse
142
-
143
- def generate_check_mac_value(params: dict, hash_key: str, hash_iv: str) -> str:
144
- # 1. 排序參數
145
- sorted_params = sorted(params.items(), key=lambda x: x[0].lower())
146
-
147
- # 2. 組合字串
148
- param_str = '&'.join(f'{k}={v}' for k, v in sorted_params)
149
-
150
- # 3. 加上 HashKey 和 HashIV
151
- raw = f'HashKey={hash_key}&{param_str}&HashIV={hash_iv}'
152
-
153
- # 4. URL Encode
154
- encoded = urllib.parse.quote_plus(raw)
155
-
156
- # 5. 轉小寫
157
- lower = encoded.lower()
158
-
159
- # 6. MD5
160
- md5 = hashlib.md5(lower.encode('utf-8')).hexdigest()
161
-
162
- # 7. 轉大寫
163
- return md5.upper()
164
- ```
165
-
166
- ---
167
-
168
- ## 建立物流訂單
169
-
170
- ### 端點
171
-
172
- ```
173
- POST /Express/Create
174
- ```
175
-
176
- ### 通用參數
177
-
178
- | 參數 | 類型 | 必填 | 說明 |
179
- |------|------|------|------|
180
- | `MerchantID` | String(10) | | 商店代號 |
181
- | `MerchantTradeNo` | String(20) | | 訂單編號 (唯一) |
182
- | `MerchantTradeDate` | String(20) | | 訂單日期 `yyyy/MM/dd HH:mm:ss` |
183
- | `LogisticsType` | String(20) | | 物流類型 `CVS`/`Home` |
184
- | `LogisticsSubType` | String(20) | | 物流子類型 |
185
- | `GoodsAmount` | Integer | | 商品金額 |
186
- | `GoodsName` | String(50) | | 商品名稱 |
187
- | `SenderName` | String(10) | | 寄件人姓名 |
188
- | `SenderPhone` | String(20) | | 寄件人電話 |
189
- | `SenderCellPhone` | String(20) | 否 | 寄件人手機 |
190
- | `ReceiverName` | String(10) | | 收件人姓名 |
191
- | `ReceiverPhone` | String(20) | | 收件人電話 |
192
- | `ReceiverCellPhone` | String(20) | 否 | 收件人手機 |
193
- | `ServerReplyURL` | String(200) | | 物流狀態通知網址 |
194
- | `CheckMacValue` | String | | 檢查碼 |
195
-
196
- ### 超商取貨專用參數
197
-
198
- | 參數 | 類型 | 必填 | 說明 |
199
- |------|------|------|------|
200
- | `ReceiverStoreID` | String(6) | | 收件門市代號 |
201
- | `ReturnStoreID` | String(6) | 否 | 退貨門市代號 |
202
- | `IsCollection` | String(1) | 否 | 是否代收貨款 `Y`/`N` |
203
- | `CollectionAmount` | Integer | 否 | 代收金額 |
204
-
205
- ### 宅配專用參數
206
-
207
- | 參數 | 類型 | 必填 | 說明 |
208
- |------|------|------|------|
209
- | `SenderZipCode` | String(5) | | 寄件人郵遞區號 |
210
- | `SenderAddress` | String(200) | | 寄件人地址 |
211
- | `ReceiverZipCode` | String(5) | | 收件人郵遞區號 |
212
- | `ReceiverAddress` | String(200) | | 收件人地址 |
213
- | `Temperature` | String(4) | 否 | 溫層 `0001`常溫 `0002`冷藏 `0003`冷凍 |
214
- | `Distance` | String(2) | 否 | 距離 `00`同縣市 `01`外縣市 `02`離島 |
215
- | `Specification` | String(4) | 否 | 規格 (見下表) |
216
- | `ScheduledDeliveryTime` | String(1) | 否 | 預定送達時段 |
217
- | `ScheduledDeliveryDate` | String(10) | 否 | 預定送達日期 |
218
-
219
- ### Specification 規格代碼
220
-
221
- | 代碼 | 尺寸 |
222
- |------|------|
223
- | `0001` | 60cm |
224
- | `0002` | 90cm |
225
- | `0003` | 120cm |
226
- | `0004` | 150cm |
227
-
228
- ### ScheduledDeliveryTime 時段代碼
229
-
230
- | 代碼 | 時段 |
231
- |------|------|
232
- | `1` | 13:00 前 |
233
- | `2` | 14:00-18:00 |
234
- | `3` | 不限時 |
235
- | `4` | 任何時間 (黑貓夜配) |
236
-
237
- ### PHP 範例 - 超商取貨
238
-
239
- ```php
240
- <?php
241
-
242
- $params = [
243
- 'MerchantID' => '2000132',
244
- 'MerchantTradeNo' => 'LOG' . time(),
245
- 'MerchantTradeDate' => date('Y/m/d H:i:s'),
246
- 'LogisticsType' => 'CVS',
247
- 'LogisticsSubType' => 'UNIMART',
248
- 'GoodsAmount' => 500,
249
- 'GoodsName' => '測試商品',
250
- 'SenderName' => '寄件人',
251
- 'SenderPhone' => '0912345678',
252
- 'ReceiverName' => '收件人',
253
- 'ReceiverPhone' => '0987654321',
254
- 'ReceiverStoreID' => '131386',
255
- 'ServerReplyURL' => 'https://your-site.com/logistics_notify',
256
- 'IsCollection' => 'N',
257
- ];
258
-
259
- $params['CheckMacValue'] = generateCheckMacValue($params, $hashKey, $hashIV);
260
-
261
- $ch = curl_init();
262
- curl_setopt_array($ch, [
263
- CURLOPT_URL => 'https://logistics-stage.ecpay.com.tw/Express/Create',
264
- CURLOPT_POST => true,
265
- CURLOPT_POSTFIELDS => http_build_query($params),
266
- CURLOPT_RETURNTRANSFER => true,
267
- ]);
268
-
269
- $response = curl_exec($ch);
270
- curl_close($ch);
271
-
272
- // 解析回應 (格式: 1|OK|MerchantID=xxx|...)
273
- ```
274
-
275
- ### 回應格式
276
-
277
- 成功回應:
278
- ```
279
- 1|OK|MerchantID=2000132|MerchantTradeNo=LOG1234567890|RtnCode=300|RtnMsg=交易成功|AllPayLogisticsID=1234567890|CVSPaymentNo=AB12345|CVSValidationNo=1234|LogisticsType=CVS|LogisticsSubType=UNIMART|GoodsAmount=500|UpdateStatusDate=2024/01/15 10:30:00|ReceiverName=收件人|ReceiverPhone=0987654321|ReceiverStoreID=131386|BookingNote=|CheckMacValue=ABC123...
280
- ```
281
-
282
- 失敗回應:
283
- ```
284
- 0|ErrorCode|ErrorMessage
285
- ```
286
-
287
- ---
288
-
289
- ## 超商電子地圖
290
-
291
- ### 端點
292
-
293
- ```
294
- POST /Express/map
295
- ```
296
-
297
- ### 參數
298
-
299
- | 參數 | 類型 | 必填 | 說明 |
300
- |------|------|------|------|
301
- | `MerchantID` | String(10) | | 商店代號 |
302
- | `LogisticsType` | String(20) | | 固定 `CVS` |
303
- | `LogisticsSubType` | String(20) | | 超商類型 |
304
- | `IsCollection` | String(1) | | 是否代收 `Y`/`N` |
305
- | `ServerReplyURL` | String(200) | | 選擇門市後的回傳網址 |
306
- | `ExtraData` | String(200) | 否 | 額外資料 (會原樣回傳) |
307
-
308
- ### 流程
309
-
310
- 1. 建立表單 POST 至電子地圖端點
311
- 2. 使用者在地圖選擇門市
312
- 3. ECPay POST 門市資料至 `ServerReplyURL`
313
-
314
- ### 回傳參數
315
-
316
- | 參數 | 說明 |
317
- |------|------|
318
- | `CVSStoreID` | 門市代號 |
319
- | `CVSStoreName` | 門市名稱 |
320
- | `CVSAddress` | 門市地址 |
321
- | `CVSTelephone` | 門市電話 |
322
- | `ExtraData` | 額外資料 |
323
-
324
- ### PHP 範例
325
-
326
- ```php
327
- <?php
328
- // 產生電子地圖表單
329
- $html = <<<HTML
330
- <form id="map-form" method="post" action="https://logistics-stage.ecpay.com.tw/Express/map" target="map-iframe">
331
- <input type="hidden" name="MerchantID" value="2000132">
332
- <input type="hidden" name="LogisticsType" value="CVS">
333
- <input type="hidden" name="LogisticsSubType" value="UNIMART">
334
- <input type="hidden" name="IsCollection" value="N">
335
- <input type="hidden" name="ServerReplyURL" value="https://your-site.com/map_callback">
336
- <input type="hidden" name="ExtraData" value="order_123">
337
- </form>
338
- <iframe name="map-iframe" width="100%" height="600"></iframe>
339
- <script>document.getElementById('map-form').submit();</script>
340
- HTML;
341
- ```
342
-
343
- ---
344
-
345
- ## 列印託運單
346
-
347
- ### 端點
348
-
349
- ```
350
- POST /helper/printTradeDocument
351
- ```
352
-
353
- ### 參數
354
-
355
- | 參數 | 類型 | 必填 | 說明 |
356
- |------|------|------|------|
357
- | `MerchantID` | String(10) | | 商店代號 |
358
- | `AllPayLogisticsID` | String(20) | | ECPay 物流編號 |
359
- | `CheckMacValue` | String | | 檢查碼 |
360
-
361
- ### 回應
362
-
363
- 成功時會回傳 PDF 檔案內容。
364
-
365
- ---
366
-
367
- ## 物流狀態查詢
368
-
369
- ### 端點
370
-
371
- ```
372
- POST /Helper/QueryLogisticsTradeInfo/V2
373
- ```
374
-
375
- ### 參數
376
-
377
- | 參數 | 類型 | 必填 | 說明 |
378
- |------|------|------|------|
379
- | `MerchantID` | String(10) | | 商店代號 |
380
- | `AllPayLogisticsID` | String(20) | | ECPay 物流編號 |
381
- | `CheckMacValue` | String | | 檢查碼 |
382
-
383
- ### 回應參數
384
-
385
- | 參數 | 說明 |
386
- |------|------|
387
- | `MerchantID` | 商店代號 |
388
- | `MerchantTradeNo` | 訂單編號 |
389
- | `AllPayLogisticsID` | ECPay 物流編號 |
390
- | `LogisticsType` | 物流類型 |
391
- | `LogisticsSubType` | 物流子類型 |
392
- | `LogisticsStatus` | 物流狀態碼 |
393
- | `GoodsAmount` | 商品金額 |
394
- | `UpdateStatusDate` | 狀態更新時間 |
395
- | `ReceiverName` | 收件人姓名 |
396
- | `ReceiverPhone` | 收件人電話 |
397
- | `ReceiverStoreID` | 收件門市代號 |
398
- | `TradeDate` | 交易時間 |
399
-
400
- ---
401
-
402
- ## 物流狀態通知
403
-
404
- ### 通知流程
405
-
406
- ECPay 會在物流狀態變更時 POST 資料至 `ServerReplyURL`。
407
-
408
- ### 通知參數
409
-
410
- | 參數 | 說明 |
411
- |------|------|
412
- | `MerchantID` | 商店代號 |
413
- | `MerchantTradeNo` | 訂單編號 |
414
- | `AllPayLogisticsID` | ECPay 物流編號 |
415
- | `LogisticsType` | 物流類型 |
416
- | `LogisticsSubType` | 物流子類型 |
417
- | `LogisticsStatus` | 物流狀態碼 |
418
- | `GoodsAmount` | 商品金額 |
419
- | `UpdateStatusDate` | 狀態更新時間 |
420
- | `ReceiverName` | 收件人姓名 |
421
- | `ReceiverPhone` | 收件人電話 |
422
- | `ReceiverStoreID` | 收件門市代號 |
423
- | `CheckMacValue` | 檢查碼 |
424
-
425
- ### 處理範例
426
-
427
- ```php
428
- <?php
429
-
430
- // 接收通知
431
- $postData = $_POST;
432
-
433
- // 取出 CheckMacValue
434
- $receivedMac = $postData['CheckMacValue'];
435
- unset($postData['CheckMacValue']);
436
-
437
- // 重新計算 CheckMacValue
438
- $calculatedMac = generateCheckMacValue($postData, $hashKey, $hashIV);
439
-
440
- // 驗證
441
- if ($receivedMac !== $calculatedMac) {
442
- echo '0|CheckMacValue Error';
443
- exit;
444
- }
445
-
446
- // 根據物流狀態更新訂單
447
- $logisticsStatus = $postData['LogisticsStatus'];
448
-
449
- switch ($logisticsStatus) {
450
- case '300':
451
- // 訂單建立成功
452
- updateOrderStatus($postData['MerchantTradeNo'], 'created');
453
- break;
454
- case '2030':
455
- // 已交寄
456
- updateOrderStatus($postData['MerchantTradeNo'], 'shipped');
457
- break;
458
- case '2063':
459
- // 配達完成
460
- updateOrderStatus($postData['MerchantTradeNo'], 'delivered');
461
- break;
462
- case '2067':
463
- // 消費者取貨完成
464
- updateOrderStatus($postData['MerchantTradeNo'], 'picked_up');
465
- break;
466
- case '2073':
467
- // 退貨中
468
- updateOrderStatus($postData['MerchantTradeNo'], 'returning');
469
- break;
470
- case '2074':
471
- // 退貨完成
472
- updateOrderStatus($postData['MerchantTradeNo'], 'returned');
473
- break;
474
- }
475
-
476
- // 回應 OK
477
- echo '1|OK';
478
- ```
479
-
480
- ---
481
-
482
- ## 錯誤碼對照表
483
-
484
- ### 物流狀態碼
485
-
486
- | 狀態碼 | 說明 |
487
- |--------|------|
488
- | `300` | 訂單建立成功 |
489
- | `2030` | 已交寄 |
490
- | `2063` | 配達完成 |
491
- | `2067` | 消費者取貨完成 |
492
- | `2073` | 退貨中 |
493
- | `2074` | 退貨完成 |
494
-
495
- ### 超商取貨詳細狀態
496
-
497
- | 狀態碼 | 說明 |
498
- |--------|------|
499
- | `300` | 訂單建立成功 |
500
- | `310` | 已產生托運單 |
501
- | `2030` | 已交寄 |
502
- | `2063` | 到達門市 |
503
- | `2067` | 消費者取貨完成 |
504
- | `2068` | 消費者取貨失敗 |
505
- | `2073` | 退貨中 |
506
- | `2074` | 退貨完成 |
507
-
508
- ### 宅配詳細狀態
509
-
510
- | 狀態碼 | 說明 |
511
- |--------|------|
512
- | `300` | 訂單建立成功 |
513
- | `310` | 已產生托運單 |
514
- | `2030` | 已交寄 |
515
- | `2063` | 配達完成 |
516
- | `2072` | 配達失敗 |
517
- | `2073` | 退貨中 |
518
- | `2074` | 退貨完成 |
519
-
520
- ### 常見錯誤訊息
521
-
522
- | 錯誤碼 | 說明 | 處理方式 |
523
- |--------|------|----------|
524
- | `10500001` | MerchantID 為必填 | 檢查參數 |
525
- | `10500002` | MerchantTradeNo 為必填 | 檢查參數 |
526
- | `10500003` | CheckMacValue 錯誤 | 重新計算 CheckMacValue |
527
- | `10500008` | LogisticsSubType 錯誤 | 確認物流類型代碼 |
528
- | `10500019` | ReceiverStoreID 錯誤 | 重新查詢門市代號 |
529
-
530
- ---
531
-
532
- ## 官方資源
533
-
534
- - **官方網站**: https://www.ecpay.com.tw/
535
- - **物流 API 文件**: https://developers.ecpay.com.tw/?p=7421
536
- - **技術客服**: techsupport@ecpay.com.tw
1
+ # ECPay Logistics API Reference
2
+
3
+ 綠界科技 (ECPay) 物流 API 完整參考文件。
4
+
5
+ ---
6
+
7
+ ## 目錄
8
+
9
+ 1. [API 端點總覽](#api-端點總覽)
10
+ 2. [測試環境](#測試環境)
11
+ 3. [物流類型](#物流類型)
12
+ 4. [CheckMacValue 計算](#checkmacvalue-計算)
13
+ 5. [建立物流訂單](#建立物流訂單)
14
+ 6. [超商電子地圖](#超商電子地圖)
15
+ 7. [列印託運單](#列印託運單)
16
+ 8. [物流狀態查詢](#物流狀態查詢)
17
+ 9. [物流狀態通知](#物流狀態通知)
18
+ 10. [錯誤碼對照表](#錯誤碼對照表)
19
+
20
+ ---
21
+
22
+ ## API 端點總覽
23
+
24
+ ### 基礎 API 路徑
25
+
26
+ | 環境 | 基礎路徑 |
27
+ |------|----------|
28
+ | **測試環境** | `https://logistics-stage.ecpay.com.tw` |
29
+ | **正式環境** | `https://logistics.ecpay.com.tw` |
30
+
31
+ ### 物流相關端點
32
+
33
+ | 功能 | 測試環境 | 正式環境 |
34
+ |------|----------|----------|
35
+ | 建立物流訂單 | `/Express/Create` | `/Express/Create` |
36
+ | 超商電子地圖 | `/Express/map` | `/Express/map` |
37
+ | 列印託運單 | `/helper/printTradeDocument` | `/helper/printTradeDocument` |
38
+ | 查詢訂單 | `/Helper/QueryLogisticsTradeInfo/V2` | `/Helper/QueryLogisticsTradeInfo/V2` |
39
+
40
+ ---
41
+
42
+ ## 測試環境
43
+
44
+ ### 測試帳號
45
+
46
+ ```
47
+ 測試網址: https://logistics-stage.ecpay.com.tw
48
+ 商店代號: 2000132
49
+ HashKey: 5294y06JbISpM5x9
50
+ HashIV: v77hoKGq4kWxNNIS
51
+ ```
52
+
53
+ ### 測試用超商門市
54
+
55
+ | 超商 | 測試門市代號 |
56
+ |------|-------------|
57
+ | 7-11 | `131386` |
58
+ | 全家 | `006598` |
59
+ | 萊爾富 | `2001` |
60
+ | OK | `1328` |
61
+
62
+ ---
63
+
64
+ ## 物流類型
65
+
66
+ ### 超商取貨類型
67
+
68
+ | 類型 | 代碼 | 說明 |
69
+ |------|------|------|
70
+ | 7-11 超商取貨 | `UNIMART` | 統一超商 B2C |
71
+ | 7-11 交貨便 | `UNIMARTC2C` | C2C 店到店 |
72
+ | 全家超商取貨 | `FAMI` | 全家便利商店 B2C |
73
+ | 全家店到店 | `FAMIC2C` | C2C 店到店 |
74
+ | 萊爾富超商取貨 | `HILIFE` | 萊爾富 B2C |
75
+ | 萊爾富店到店 | `HILIFEC2C` | C2C 店到店 |
76
+ | OK 超商取貨 | `OKMART` | OK 便利商店 B2C |
77
+ | OK 店到店 | `OKMARTC2C` | C2C 店到店 |
78
+
79
+ ### 宅配類型
80
+
81
+ | 類型 | 代碼 | 說明 |
82
+ |------|------|------|
83
+ | 黑貓宅急便 | `TCAT` | 宅配到府 |
84
+ | 宅配通 | `ECAN` | 常溫宅配 |
85
+
86
+ ### LogisticsType 對照
87
+
88
+ | 值 | 說明 |
89
+ |------|------|
90
+ | `CVS` | 超商取貨 |
91
+ | `Home` | 宅配 |
92
+
93
+ ---
94
+
95
+ ## CheckMacValue 計算
96
+
97
+ ### 計算步驟
98
+
99
+ 1. 將參數依照 Key 排序 (A-Z, 不分大小寫)
100
+ 2. 組合成 `key=value&key=value` 格式
101
+ 3. 前後加上 `HashKey={HashKey}&` 和 `&HashIV={HashIV}`
102
+ 4. URL Encode (RFC 1866)
103
+ 5. 轉小寫
104
+ 6. 計算 MD5
105
+ 7. 轉大寫
106
+
107
+ ### PHP 範例
108
+
109
+ ```php
110
+ <?php
111
+
112
+ function generateCheckMacValue(array $params, string $hashKey, string $hashIV): string
113
+ {
114
+ // 1. 排序參數 (不分大小寫)
115
+ uksort($params, 'strcasecmp');
116
+
117
+ // 2. 組合字串
118
+ $paramStr = urldecode(http_build_query($params));
119
+
120
+ // 3. 加上 HashKey 和 HashIV
121
+ $raw = "HashKey={$hashKey}&{$paramStr}&HashIV={$hashIV}";
122
+
123
+ // 4. URL Encode
124
+ $encoded = urlencode($raw);
125
+
126
+ // 5. 轉小寫
127
+ $lower = strtolower($encoded);
128
+
129
+ // 6. MD5
130
+ $md5 = md5($lower);
131
+
132
+ // 7. 轉大寫
133
+ return strtoupper($md5);
134
+ }
135
+ ```
136
+
137
+ ### Python 範例
138
+
139
+ ```python
140
+ import hashlib
141
+ import urllib.parse
142
+
143
+ def generate_check_mac_value(params: dict, hash_key: str, hash_iv: str) -> str:
144
+ # 1. 排序參數
145
+ sorted_params = sorted(params.items(), key=lambda x: x[0].lower())
146
+
147
+ # 2. 組合字串
148
+ param_str = '&'.join(f'{k}={v}' for k, v in sorted_params)
149
+
150
+ # 3. 加上 HashKey 和 HashIV
151
+ raw = f'HashKey={hash_key}&{param_str}&HashIV={hash_iv}'
152
+
153
+ # 4. URL Encode
154
+ encoded = urllib.parse.quote_plus(raw)
155
+
156
+ # 5. 轉小寫
157
+ lower = encoded.lower()
158
+
159
+ # 6. MD5
160
+ md5 = hashlib.md5(lower.encode('utf-8')).hexdigest()
161
+
162
+ # 7. 轉大寫
163
+ return md5.upper()
164
+ ```
165
+
166
+ ---
167
+
168
+ ## 建立物流訂單
169
+
170
+ ### 端點
171
+
172
+ ```
173
+ POST /Express/Create
174
+ ```
175
+
176
+ ### 通用參數
177
+
178
+ | 參數 | 類型 | 必填 | 說明 |
179
+ |------|------|------|------|
180
+ | `MerchantID` | String(10) | | 商店代號 |
181
+ | `MerchantTradeNo` | String(20) | | 訂單編號 (唯一) |
182
+ | `MerchantTradeDate` | String(20) | | 訂單日期 `yyyy/MM/dd HH:mm:ss` |
183
+ | `LogisticsType` | String(20) | | 物流類型 `CVS`/`Home` |
184
+ | `LogisticsSubType` | String(20) | | 物流子類型 |
185
+ | `GoodsAmount` | Integer | | 商品金額 |
186
+ | `GoodsName` | String(50) | | 商品名稱 |
187
+ | `SenderName` | String(10) | | 寄件人姓名 |
188
+ | `SenderPhone` | String(20) | | 寄件人電話 |
189
+ | `SenderCellPhone` | String(20) | 否 | 寄件人手機 |
190
+ | `ReceiverName` | String(10) | | 收件人姓名 |
191
+ | `ReceiverPhone` | String(20) | | 收件人電話 |
192
+ | `ReceiverCellPhone` | String(20) | 否 | 收件人手機 |
193
+ | `ServerReplyURL` | String(200) | | 物流狀態通知網址 |
194
+ | `CheckMacValue` | String | | 檢查碼 |
195
+
196
+ ### 超商取貨專用參數
197
+
198
+ | 參數 | 類型 | 必填 | 說明 |
199
+ |------|------|------|------|
200
+ | `ReceiverStoreID` | String(6) | | 收件門市代號 |
201
+ | `ReturnStoreID` | String(6) | 否 | 退貨門市代號 |
202
+ | `IsCollection` | String(1) | 否 | 是否代收貨款 `Y`/`N` |
203
+ | `CollectionAmount` | Integer | 否 | 代收金額 |
204
+
205
+ ### 宅配專用參數
206
+
207
+ | 參數 | 類型 | 必填 | 說明 |
208
+ |------|------|------|------|
209
+ | `SenderZipCode` | String(5) | | 寄件人郵遞區號 |
210
+ | `SenderAddress` | String(200) | | 寄件人地址 |
211
+ | `ReceiverZipCode` | String(5) | | 收件人郵遞區號 |
212
+ | `ReceiverAddress` | String(200) | | 收件人地址 |
213
+ | `Temperature` | String(4) | 否 | 溫層 `0001`常溫 `0002`冷藏 `0003`冷凍 |
214
+ | `Distance` | String(2) | 否 | 距離 `00`同縣市 `01`外縣市 `02`離島 |
215
+ | `Specification` | String(4) | 否 | 規格 (見下表) |
216
+ | `ScheduledDeliveryTime` | String(1) | 否 | 預定送達時段 |
217
+ | `ScheduledDeliveryDate` | String(10) | 否 | 預定送達日期 |
218
+
219
+ ### Specification 規格代碼
220
+
221
+ | 代碼 | 尺寸 |
222
+ |------|------|
223
+ | `0001` | 60cm |
224
+ | `0002` | 90cm |
225
+ | `0003` | 120cm |
226
+ | `0004` | 150cm |
227
+
228
+ ### ScheduledDeliveryTime 時段代碼
229
+
230
+ | 代碼 | 時段 |
231
+ |------|------|
232
+ | `1` | 13:00 前 |
233
+ | `2` | 14:00-18:00 |
234
+ | `3` | 不限時 |
235
+ | `4` | 任何時間 (黑貓夜配) |
236
+
237
+ ### PHP 範例 - 超商取貨
238
+
239
+ ```php
240
+ <?php
241
+
242
+ $params = [
243
+ 'MerchantID' => '2000132',
244
+ 'MerchantTradeNo' => 'LOG' . time(),
245
+ 'MerchantTradeDate' => date('Y/m/d H:i:s'),
246
+ 'LogisticsType' => 'CVS',
247
+ 'LogisticsSubType' => 'UNIMART',
248
+ 'GoodsAmount' => 500,
249
+ 'GoodsName' => '測試商品',
250
+ 'SenderName' => '寄件人',
251
+ 'SenderPhone' => '0912345678',
252
+ 'ReceiverName' => '收件人',
253
+ 'ReceiverPhone' => '0987654321',
254
+ 'ReceiverStoreID' => '131386',
255
+ 'ServerReplyURL' => 'https://your-site.com/logistics_notify',
256
+ 'IsCollection' => 'N',
257
+ ];
258
+
259
+ $params['CheckMacValue'] = generateCheckMacValue($params, $hashKey, $hashIV);
260
+
261
+ $ch = curl_init();
262
+ curl_setopt_array($ch, [
263
+ CURLOPT_URL => 'https://logistics-stage.ecpay.com.tw/Express/Create',
264
+ CURLOPT_POST => true,
265
+ CURLOPT_POSTFIELDS => http_build_query($params),
266
+ CURLOPT_RETURNTRANSFER => true,
267
+ ]);
268
+
269
+ $response = curl_exec($ch);
270
+ curl_close($ch);
271
+
272
+ // 解析回應 (格式: 1|OK|MerchantID=xxx|...)
273
+ ```
274
+
275
+ ### 回應格式
276
+
277
+ 成功回應:
278
+ ```
279
+ 1|OK|MerchantID=2000132|MerchantTradeNo=LOG1234567890|RtnCode=300|RtnMsg=交易成功|AllPayLogisticsID=1234567890|CVSPaymentNo=AB12345|CVSValidationNo=1234|LogisticsType=CVS|LogisticsSubType=UNIMART|GoodsAmount=500|UpdateStatusDate=2024/01/15 10:30:00|ReceiverName=收件人|ReceiverPhone=0987654321|ReceiverStoreID=131386|BookingNote=|CheckMacValue=ABC123...
280
+ ```
281
+
282
+ 失敗回應:
283
+ ```
284
+ 0|ErrorCode|ErrorMessage
285
+ ```
286
+
287
+ ---
288
+
289
+ ## 超商電子地圖
290
+
291
+ ### 端點
292
+
293
+ ```
294
+ POST /Express/map
295
+ ```
296
+
297
+ ### 參數
298
+
299
+ | 參數 | 類型 | 必填 | 說明 |
300
+ |------|------|------|------|
301
+ | `MerchantID` | String(10) | | 商店代號 |
302
+ | `LogisticsType` | String(20) | | 固定 `CVS` |
303
+ | `LogisticsSubType` | String(20) | | 超商類型 |
304
+ | `IsCollection` | String(1) | | 是否代收 `Y`/`N` |
305
+ | `ServerReplyURL` | String(200) | | 選擇門市後的回傳網址 |
306
+ | `ExtraData` | String(200) | 否 | 額外資料 (會原樣回傳) |
307
+
308
+ ### 流程
309
+
310
+ 1. 建立表單 POST 至電子地圖端點
311
+ 2. 使用者在地圖選擇門市
312
+ 3. ECPay POST 門市資料至 `ServerReplyURL`
313
+
314
+ ### 回傳參數
315
+
316
+ | 參數 | 說明 |
317
+ |------|------|
318
+ | `CVSStoreID` | 門市代號 |
319
+ | `CVSStoreName` | 門市名稱 |
320
+ | `CVSAddress` | 門市地址 |
321
+ | `CVSTelephone` | 門市電話 |
322
+ | `ExtraData` | 額外資料 |
323
+
324
+ ### PHP 範例
325
+
326
+ ```php
327
+ <?php
328
+ // 產生電子地圖表單
329
+ $html = <<<HTML
330
+ <form id="map-form" method="post" action="https://logistics-stage.ecpay.com.tw/Express/map" target="map-iframe">
331
+ <input type="hidden" name="MerchantID" value="2000132">
332
+ <input type="hidden" name="LogisticsType" value="CVS">
333
+ <input type="hidden" name="LogisticsSubType" value="UNIMART">
334
+ <input type="hidden" name="IsCollection" value="N">
335
+ <input type="hidden" name="ServerReplyURL" value="https://your-site.com/map_callback">
336
+ <input type="hidden" name="ExtraData" value="order_123">
337
+ </form>
338
+ <iframe name="map-iframe" width="100%" height="600"></iframe>
339
+ <script>document.getElementById('map-form').submit();</script>
340
+ HTML;
341
+ ```
342
+
343
+ ---
344
+
345
+ ## 列印託運單
346
+
347
+ ### 端點
348
+
349
+ ```
350
+ POST /helper/printTradeDocument
351
+ ```
352
+
353
+ ### 參數
354
+
355
+ | 參數 | 類型 | 必填 | 說明 |
356
+ |------|------|------|------|
357
+ | `MerchantID` | String(10) | | 商店代號 |
358
+ | `AllPayLogisticsID` | String(20) | | ECPay 物流編號 |
359
+ | `CheckMacValue` | String | | 檢查碼 |
360
+
361
+ ### 回應
362
+
363
+ 成功時會回傳 PDF 檔案內容。
364
+
365
+ ---
366
+
367
+ ## 物流狀態查詢
368
+
369
+ ### 端點
370
+
371
+ ```
372
+ POST /Helper/QueryLogisticsTradeInfo/V2
373
+ ```
374
+
375
+ ### 參數
376
+
377
+ | 參數 | 類型 | 必填 | 說明 |
378
+ |------|------|------|------|
379
+ | `MerchantID` | String(10) | | 商店代號 |
380
+ | `AllPayLogisticsID` | String(20) | | ECPay 物流編號 |
381
+ | `CheckMacValue` | String | | 檢查碼 |
382
+
383
+ ### 回應參數
384
+
385
+ | 參數 | 說明 |
386
+ |------|------|
387
+ | `MerchantID` | 商店代號 |
388
+ | `MerchantTradeNo` | 訂單編號 |
389
+ | `AllPayLogisticsID` | ECPay 物流編號 |
390
+ | `LogisticsType` | 物流類型 |
391
+ | `LogisticsSubType` | 物流子類型 |
392
+ | `LogisticsStatus` | 物流狀態碼 |
393
+ | `GoodsAmount` | 商品金額 |
394
+ | `UpdateStatusDate` | 狀態更新時間 |
395
+ | `ReceiverName` | 收件人姓名 |
396
+ | `ReceiverPhone` | 收件人電話 |
397
+ | `ReceiverStoreID` | 收件門市代號 |
398
+ | `TradeDate` | 交易時間 |
399
+
400
+ ---
401
+
402
+ ## 物流狀態通知
403
+
404
+ ### 通知流程
405
+
406
+ ECPay 會在物流狀態變更時 POST 資料至 `ServerReplyURL`。
407
+
408
+ ### 通知參數
409
+
410
+ | 參數 | 說明 |
411
+ |------|------|
412
+ | `MerchantID` | 商店代號 |
413
+ | `MerchantTradeNo` | 訂單編號 |
414
+ | `AllPayLogisticsID` | ECPay 物流編號 |
415
+ | `LogisticsType` | 物流類型 |
416
+ | `LogisticsSubType` | 物流子類型 |
417
+ | `LogisticsStatus` | 物流狀態碼 |
418
+ | `GoodsAmount` | 商品金額 |
419
+ | `UpdateStatusDate` | 狀態更新時間 |
420
+ | `ReceiverName` | 收件人姓名 |
421
+ | `ReceiverPhone` | 收件人電話 |
422
+ | `ReceiverStoreID` | 收件門市代號 |
423
+ | `CheckMacValue` | 檢查碼 |
424
+
425
+ ### 處理範例
426
+
427
+ ```php
428
+ <?php
429
+
430
+ // 接收通知
431
+ $postData = $_POST;
432
+
433
+ // 取出 CheckMacValue
434
+ $receivedMac = $postData['CheckMacValue'];
435
+ unset($postData['CheckMacValue']);
436
+
437
+ // 重新計算 CheckMacValue
438
+ $calculatedMac = generateCheckMacValue($postData, $hashKey, $hashIV);
439
+
440
+ // 驗證
441
+ if ($receivedMac !== $calculatedMac) {
442
+ echo '0|CheckMacValue Error';
443
+ exit;
444
+ }
445
+
446
+ // 根據物流狀態更新訂單
447
+ $logisticsStatus = $postData['LogisticsStatus'];
448
+
449
+ switch ($logisticsStatus) {
450
+ case '300':
451
+ // 訂單建立成功
452
+ updateOrderStatus($postData['MerchantTradeNo'], 'created');
453
+ break;
454
+ case '2030':
455
+ // 已交寄
456
+ updateOrderStatus($postData['MerchantTradeNo'], 'shipped');
457
+ break;
458
+ case '2063':
459
+ // 配達完成
460
+ updateOrderStatus($postData['MerchantTradeNo'], 'delivered');
461
+ break;
462
+ case '2067':
463
+ // 消費者取貨完成
464
+ updateOrderStatus($postData['MerchantTradeNo'], 'picked_up');
465
+ break;
466
+ case '2073':
467
+ // 退貨中
468
+ updateOrderStatus($postData['MerchantTradeNo'], 'returning');
469
+ break;
470
+ case '2074':
471
+ // 退貨完成
472
+ updateOrderStatus($postData['MerchantTradeNo'], 'returned');
473
+ break;
474
+ }
475
+
476
+ // 回應 OK
477
+ echo '1|OK';
478
+ ```
479
+
480
+ ---
481
+
482
+ ## 錯誤碼對照表
483
+
484
+ ### 物流狀態碼
485
+
486
+ | 狀態碼 | 說明 |
487
+ |--------|------|
488
+ | `300` | 訂單建立成功 |
489
+ | `2030` | 已交寄 |
490
+ | `2063` | 配達完成 |
491
+ | `2067` | 消費者取貨完成 |
492
+ | `2073` | 退貨中 |
493
+ | `2074` | 退貨完成 |
494
+
495
+ ### 超商取貨詳細狀態
496
+
497
+ | 狀態碼 | 說明 |
498
+ |--------|------|
499
+ | `300` | 訂單建立成功 |
500
+ | `310` | 已產生托運單 |
501
+ | `2030` | 已交寄 |
502
+ | `2063` | 到達門市 |
503
+ | `2067` | 消費者取貨完成 |
504
+ | `2068` | 消費者取貨失敗 |
505
+ | `2073` | 退貨中 |
506
+ | `2074` | 退貨完成 |
507
+
508
+ ### 宅配詳細狀態
509
+
510
+ | 狀態碼 | 說明 |
511
+ |--------|------|
512
+ | `300` | 訂單建立成功 |
513
+ | `310` | 已產生托運單 |
514
+ | `2030` | 已交寄 |
515
+ | `2063` | 配達完成 |
516
+ | `2072` | 配達失敗 |
517
+ | `2073` | 退貨中 |
518
+ | `2074` | 退貨完成 |
519
+
520
+ ### 常見錯誤訊息
521
+
522
+ | 錯誤碼 | 說明 | 處理方式 |
523
+ |--------|------|----------|
524
+ | `10500001` | MerchantID 為必填 | 檢查參數 |
525
+ | `10500002` | MerchantTradeNo 為必填 | 檢查參數 |
526
+ | `10500003` | CheckMacValue 錯誤 | 重新計算 CheckMacValue |
527
+ | `10500008` | LogisticsSubType 錯誤 | 確認物流類型代碼 |
528
+ | `10500019` | ReceiverStoreID 錯誤 | 重新查詢門市代號 |
529
+
530
+ ---
531
+
532
+ ## 官方資源
533
+
534
+ - **官方網站**: https://www.ecpay.com.tw/
535
+ - **物流 API 文件**: https://developers.ecpay.com.tw/?p=7421
536
+ - **技術客服**: techsupport@ecpay.com.tw