taiwan-logistics-skill 1.0.3 → 1.0.5
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.
- package/README.md +5 -5
- package/assets/taiwan-logistics/EXAMPLES.md +3183 -3183
- package/assets/taiwan-logistics/README.md +5 -5
- package/assets/taiwan-logistics/SKILL.md +827 -827
- package/assets/taiwan-logistics/references/NEWEBPAY_LOGISTICS_REFERENCE.md +774 -774
- package/assets/taiwan-logistics/references/ecpay-logistics-api.md +536 -536
- package/assets/taiwan-logistics/references/payuni-logistics-api.md +712 -712
- package/assets/templates/base/quick-reference.md +85 -0
- package/assets/templates/base/skill-content.md +361 -0
- package/assets/templates/platforms/agent.json +26 -0
- package/assets/templates/platforms/claude.json +26 -0
- package/assets/templates/platforms/codebuddy.json +25 -0
- package/assets/templates/platforms/codex.json +26 -0
- package/assets/templates/platforms/continue.json +25 -0
- package/assets/templates/platforms/copilot.json +25 -0
- package/assets/templates/platforms/cursor.json +26 -0
- package/assets/templates/platforms/gemini.json +25 -0
- package/assets/templates/platforms/kiro.json +25 -0
- package/assets/templates/platforms/opencode.json +25 -0
- package/assets/templates/platforms/qoder.json +25 -0
- package/assets/templates/platforms/roocode.json +25 -0
- package/assets/templates/platforms/trae.json +25 -0
- package/assets/templates/platforms/windsurf.json +25 -0
- package/dist/index.js +126 -19
- package/package.json +1 -1
|
@@ -1,712 +1,712 @@
|
|
|
1
|
-
# PayUni Logistics API Reference
|
|
2
|
-
|
|
3
|
-
統一金流 (PAYUNi) 物流 API 完整參考文件。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 目錄
|
|
8
|
-
|
|
9
|
-
1. [API 端點總覽](#api-端點總覽)
|
|
10
|
-
2. [測試環境](#測試環境)
|
|
11
|
-
3. [加密機制](#加密機制)
|
|
12
|
-
4. [物流類型](#物流類型)
|
|
13
|
-
5. [7-11 超商取貨](#7-11-超商取貨)
|
|
14
|
-
6. [黑貓宅配](#黑貓宅配)
|
|
15
|
-
7. [物流狀態通知](#物流狀態通知)
|
|
16
|
-
8. [物流狀態查詢](#物流狀態查詢)
|
|
17
|
-
9. [錯誤碼對照表](#錯誤碼對照表)
|
|
18
|
-
10. [常見問題排解](#常見問題排解)
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## API 端點總覽
|
|
23
|
-
|
|
24
|
-
### 基礎 API 路徑
|
|
25
|
-
|
|
26
|
-
| 環境 | 基礎路徑 |
|
|
27
|
-
|------|----------|
|
|
28
|
-
| **測試環境** | `https://sandbox-api.payuni.com.tw/api/` |
|
|
29
|
-
| **正式環境** | `https://api.payuni.com.tw/api/` |
|
|
30
|
-
|
|
31
|
-
### 物流相關端點
|
|
32
|
-
|
|
33
|
-
| 功能 | 端點路徑 | 說明 |
|
|
34
|
-
|------|----------|------|
|
|
35
|
-
| 建立物流訂單 | `/logistics/create` | 建立物流託運單 |
|
|
36
|
-
| 物流狀態查詢 | `/logistics/query` | 查詢物流狀態 |
|
|
37
|
-
| 取消物流訂單 | `/logistics/cancel` | 取消物流訂單 |
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## 測試環境
|
|
42
|
-
|
|
43
|
-
### 測試帳號
|
|
44
|
-
|
|
45
|
-
測試帳號請至 PayUni 後台申請:
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
後台網址: https://www.payuni.com.tw/
|
|
49
|
-
路徑: 會員 > 商店清單 > 指定商店名稱 > 串接設定
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
取得以下資訊:
|
|
53
|
-
- **商店代號 (MerID)**
|
|
54
|
-
- **Hash Key**
|
|
55
|
-
- **Hash IV**
|
|
56
|
-
|
|
57
|
-
### 測試環境端點
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
https://sandbox-api.payuni.com.tw/api/{endpoint}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### 注意事項
|
|
64
|
-
|
|
65
|
-
1. 物流幕後 API 需向 PayUni 申請開通
|
|
66
|
-
2. 建議使用固定 IP 主機,避免 IP 變動造成功能失效
|
|
67
|
-
3. 非即時付款 (超商代碼、虛擬帳號) 需等付款完成才會建立物流單
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## 加密機制
|
|
72
|
-
|
|
73
|
-
PayUni 採用 **AES-256-GCM** 加密與 **SHA256 HMAC** 驗證。
|
|
74
|
-
|
|
75
|
-
### 加密流程
|
|
76
|
-
|
|
77
|
-
1. **準備參數** - 組合所有請求參數
|
|
78
|
-
2. **URL Encode** - 將參數轉為 Query String
|
|
79
|
-
3. **AES-256-GCM 加密** - 使用 Hash Key 和 Hash IV 加密
|
|
80
|
-
4. **產生 HashInfo** - 使用 SHA256 計算驗證碼
|
|
81
|
-
5. **發送請求** - 將加密資料 POST 至 API
|
|
82
|
-
|
|
83
|
-
### PHP 加密範例
|
|
84
|
-
|
|
85
|
-
```php
|
|
86
|
-
<?php
|
|
87
|
-
|
|
88
|
-
class PayuniEncryption
|
|
89
|
-
{
|
|
90
|
-
private string $merKey;
|
|
91
|
-
private string $merIV;
|
|
92
|
-
|
|
93
|
-
public function __construct(string $merKey, string $merIV)
|
|
94
|
-
{
|
|
95
|
-
$this->merKey = $merKey;
|
|
96
|
-
$this->merIV = $merIV;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* AES-256-GCM 加密
|
|
101
|
-
*/
|
|
102
|
-
public function encrypt(array $params): string
|
|
103
|
-
{
|
|
104
|
-
// 1. 組合 Query String
|
|
105
|
-
$queryString = http_build_query($params);
|
|
106
|
-
|
|
107
|
-
// 2. AES-256-GCM 加密
|
|
108
|
-
$tag = '';
|
|
109
|
-
$encrypted = openssl_encrypt(
|
|
110
|
-
$queryString,
|
|
111
|
-
'aes-256-gcm',
|
|
112
|
-
$this->merKey,
|
|
113
|
-
OPENSSL_RAW_DATA,
|
|
114
|
-
$this->merIV,
|
|
115
|
-
$tag,
|
|
116
|
-
'',
|
|
117
|
-
16
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
// 3. 組合加密結果 (加密資料 + tag)
|
|
121
|
-
$encryptInfo = bin2hex($encrypted . $tag);
|
|
122
|
-
|
|
123
|
-
return $encryptInfo;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* AES-256-GCM 解密
|
|
128
|
-
*/
|
|
129
|
-
public function decrypt(string $encryptInfo): array
|
|
130
|
-
{
|
|
131
|
-
// 1. Hex 轉 Binary
|
|
132
|
-
$data = hex2bin($encryptInfo);
|
|
133
|
-
|
|
134
|
-
// 2. 分離加密資料和 tag
|
|
135
|
-
$encrypted = substr($data, 0, -16);
|
|
136
|
-
$tag = substr($data, -16);
|
|
137
|
-
|
|
138
|
-
// 3. AES-256-GCM 解密
|
|
139
|
-
$decrypted = openssl_decrypt(
|
|
140
|
-
$encrypted,
|
|
141
|
-
'aes-256-gcm',
|
|
142
|
-
$this->merKey,
|
|
143
|
-
OPENSSL_RAW_DATA,
|
|
144
|
-
$this->merIV,
|
|
145
|
-
$tag
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// 4. 解析 Query String
|
|
149
|
-
parse_str($decrypted, $result);
|
|
150
|
-
|
|
151
|
-
return $result;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 產生 HashInfo (SHA256)
|
|
156
|
-
*/
|
|
157
|
-
public function hashInfo(string $encryptInfo): string
|
|
158
|
-
{
|
|
159
|
-
$raw = $encryptInfo . $this->merKey . $this->merIV;
|
|
160
|
-
return strtoupper(hash('sha256', $raw));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Python 加密範例
|
|
166
|
-
|
|
167
|
-
```python
|
|
168
|
-
"""PayUni AES-256-GCM 加密"""
|
|
169
|
-
|
|
170
|
-
from Crypto.Cipher import AES
|
|
171
|
-
from urllib.parse import urlencode, parse_qs
|
|
172
|
-
import hashlib
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
class PayuniEncryption:
|
|
176
|
-
def __init__(self, mer_key: str, mer_iv: str):
|
|
177
|
-
self.mer_key = mer_key.encode('utf-8')
|
|
178
|
-
self.mer_iv = mer_iv.encode('utf-8')
|
|
179
|
-
|
|
180
|
-
def encrypt(self, params: dict) -> str:
|
|
181
|
-
"""AES-256-GCM 加密"""
|
|
182
|
-
# 1. 組合 Query String
|
|
183
|
-
query_string = urlencode(params)
|
|
184
|
-
|
|
185
|
-
# 2. AES-256-GCM 加密
|
|
186
|
-
cipher = AES.new(self.mer_key, AES.MODE_GCM, nonce=self.mer_iv)
|
|
187
|
-
encrypted, tag = cipher.encrypt_and_digest(query_string.encode('utf-8'))
|
|
188
|
-
|
|
189
|
-
# 3. 組合加密結果
|
|
190
|
-
encrypt_info = (encrypted + tag).hex()
|
|
191
|
-
|
|
192
|
-
return encrypt_info
|
|
193
|
-
|
|
194
|
-
def decrypt(self, encrypt_info: str) -> dict:
|
|
195
|
-
"""AES-256-GCM 解密"""
|
|
196
|
-
# 1. Hex 轉 Binary
|
|
197
|
-
data = bytes.fromhex(encrypt_info)
|
|
198
|
-
|
|
199
|
-
# 2. 分離加密資料和 tag
|
|
200
|
-
encrypted = data[:-16]
|
|
201
|
-
tag = data[-16:]
|
|
202
|
-
|
|
203
|
-
# 3. AES-256-GCM 解密
|
|
204
|
-
cipher = AES.new(self.mer_key, AES.MODE_GCM, nonce=self.mer_iv)
|
|
205
|
-
decrypted = cipher.decrypt_and_verify(encrypted, tag)
|
|
206
|
-
|
|
207
|
-
# 4. 解析 Query String
|
|
208
|
-
result = dict(parse_qs(decrypted.decode('utf-8')))
|
|
209
|
-
return {k: v[0] for k, v in result.items()}
|
|
210
|
-
|
|
211
|
-
def hash_info(self, encrypt_info: str) -> str:
|
|
212
|
-
"""產生 HashInfo (SHA256)"""
|
|
213
|
-
raw = encrypt_info + self.mer_key.decode() + self.mer_iv.decode()
|
|
214
|
-
return hashlib.sha256(raw.encode('utf-8')).hexdigest().upper()
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## 物流類型
|
|
220
|
-
|
|
221
|
-
### 支援的物流服務
|
|
222
|
-
|
|
223
|
-
| 物流類型 | 代碼 | 溫層 | 說明 |
|
|
224
|
-
|----------|------|------|------|
|
|
225
|
-
| 7-11 店到店 (C2C) | `PAYUNi_Logistic_711` | 常溫 | 消費者自行寄件 |
|
|
226
|
-
| 7-11 店到店冷凍 | `PAYUNi_Logistic_711_Freeze` | 冷凍 | 冷凍店到店 |
|
|
227
|
-
| 7-11 大宗寄倉 (B2C) | `PAYUNi_Logistic_711_B2C` | 常溫 | 商家寄倉 |
|
|
228
|
-
| 黑貓宅配常溫 | `PAYUNi_Logistic_Tcat` | 常溫 | 宅配到府 |
|
|
229
|
-
| 黑貓宅配冷凍 | `PAYUNi_Logistic_Tcat_Freeze` | 冷凍 | 冷凍宅配 |
|
|
230
|
-
| 黑貓宅配冷藏 | `PAYUNi_Logistic_Tcat_Cold` | 冷藏 | 冷藏宅配 |
|
|
231
|
-
|
|
232
|
-
### GoodsType 溫層代碼
|
|
233
|
-
|
|
234
|
-
| 代碼 | 溫層 | 適用物流 |
|
|
235
|
-
|------|------|----------|
|
|
236
|
-
| `1` | 常溫 | 全部 |
|
|
237
|
-
| `2` | 冷凍 | 7-11 冷凍、黑貓冷凍 |
|
|
238
|
-
| `3` | 冷藏 | 僅黑貓宅配 |
|
|
239
|
-
|
|
240
|
-
### 撥款時間
|
|
241
|
-
|
|
242
|
-
| 物流類型 | 撥款時間 |
|
|
243
|
-
|----------|----------|
|
|
244
|
-
| 超商取貨 | 取貨日 + 7 天 |
|
|
245
|
-
| 黑貓宅配 | 取貨日 + 15 天 |
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
## 7-11 超商取貨
|
|
250
|
-
|
|
251
|
-
### 建立物流訂單
|
|
252
|
-
|
|
253
|
-
#### 端點
|
|
254
|
-
|
|
255
|
-
```
|
|
256
|
-
POST /api/logistics/create
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
#### EncryptInfo 參數
|
|
260
|
-
|
|
261
|
-
| 參數 | 類型 | 長度 | 必填 | 說明 |
|
|
262
|
-
|------|------|------|------|------|
|
|
263
|
-
| `MerID` | String | 20 |
|
|
264
|
-
| `MerTradeNo` | String | 50 |
|
|
265
|
-
| `LogisticsType` | String | 50 |
|
|
266
|
-
| `GoodsType` | Integer | - |
|
|
267
|
-
| `GoodsAmount` | Integer | - |
|
|
268
|
-
| `GoodsName` | String | 50 |
|
|
269
|
-
| `SenderName` | String | 10 |
|
|
270
|
-
| `SenderPhone` | String | 20 |
|
|
271
|
-
| `SenderStoreID` | String | 10 | 否 | 寄件門市代號 (C2C 必填) |
|
|
272
|
-
| `ReceiverName` | String | 10 |
|
|
273
|
-
| `ReceiverPhone` | String | 20 |
|
|
274
|
-
| `ReceiverStoreID` | String | 10 |
|
|
275
|
-
| `NotifyURL` | String | 500 |
|
|
276
|
-
| `Timestamp` | Integer | - |
|
|
277
|
-
|
|
278
|
-
### C2C vs B2C 差異
|
|
279
|
-
|
|
280
|
-
| 項目 | C2C 店到店 | B2C 大宗寄倉 |
|
|
281
|
-
|------|-----------|-------------|
|
|
282
|
-
| 寄件方式 | 消費者自行至門市寄件 | 商家統一寄倉 |
|
|
283
|
-
| SenderStoreID | 必填 | 不需要 |
|
|
284
|
-
| 適用場景 | 個人賣家 | 企業電商 |
|
|
285
|
-
|
|
286
|
-
### 門市查詢
|
|
287
|
-
|
|
288
|
-
7-11 門市代號可透過以下方式取得:
|
|
289
|
-
|
|
290
|
-
```
|
|
291
|
-
7-11 電子地圖: https://emap.pcsc.com.tw/
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### PHP 範例
|
|
295
|
-
|
|
296
|
-
```php
|
|
297
|
-
<?php
|
|
298
|
-
|
|
299
|
-
$encryption = new PayuniEncryption($merKey, $merIV);
|
|
300
|
-
|
|
301
|
-
$params = [
|
|
302
|
-
'MerID' => 'YOUR_MER_ID',
|
|
303
|
-
'MerTradeNo' => 'LOG' . time(),
|
|
304
|
-
'LogisticsType' => 'PAYUNi_Logistic_711',
|
|
305
|
-
'GoodsType' => 1,
|
|
306
|
-
'GoodsAmount' => 500,
|
|
307
|
-
'GoodsName' => '測試商品',
|
|
308
|
-
'SenderName' => '寄件人',
|
|
309
|
-
'SenderPhone' => '0912345678',
|
|
310
|
-
'SenderStoreID' => '123456', // C2C 必填
|
|
311
|
-
'ReceiverName' => '收件人',
|
|
312
|
-
'ReceiverPhone' => '0987654321',
|
|
313
|
-
'ReceiverStoreID' => '654321',
|
|
314
|
-
'NotifyURL' => 'https://your-site.com/payuni_shipping_711_notify',
|
|
315
|
-
'Timestamp' => time(),
|
|
316
|
-
];
|
|
317
|
-
|
|
318
|
-
$encryptInfo = $encryption->encrypt($params);
|
|
319
|
-
$hashInfo = $encryption->hashInfo($encryptInfo);
|
|
320
|
-
|
|
321
|
-
$ch = curl_init();
|
|
322
|
-
curl_setopt_array($ch, [
|
|
323
|
-
CURLOPT_URL => 'https://api.payuni.com.tw/api/logistics/create',
|
|
324
|
-
CURLOPT_POST => true,
|
|
325
|
-
CURLOPT_POSTFIELDS => http_build_query([
|
|
326
|
-
'MerID' => $params['MerID'],
|
|
327
|
-
'Version' => '1.0',
|
|
328
|
-
'EncryptInfo' => $encryptInfo,
|
|
329
|
-
'HashInfo' => $hashInfo,
|
|
330
|
-
]),
|
|
331
|
-
CURLOPT_RETURNTRANSFER => true,
|
|
332
|
-
]);
|
|
333
|
-
|
|
334
|
-
$response = curl_exec($ch);
|
|
335
|
-
curl_close($ch);
|
|
336
|
-
|
|
337
|
-
$result = json_decode($response, true);
|
|
338
|
-
|
|
339
|
-
if ($result['Status'] === 'SUCCESS') {
|
|
340
|
-
$data = $encryption->decrypt($result['EncryptInfo']);
|
|
341
|
-
// $data['LogisticsID'] - 物流編號
|
|
342
|
-
// $data['CVSPaymentNo'] - 超商繳費代碼
|
|
343
|
-
print_r($data);
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### 回應參數 (解密後)
|
|
348
|
-
|
|
349
|
-
| 參數 | 說明 |
|
|
350
|
-
|------|------|
|
|
351
|
-
| `LogisticsID` | PayUni 物流編號 |
|
|
352
|
-
| `MerTradeNo` | 商店訂單編號 |
|
|
353
|
-
| `CVSPaymentNo` | 超商繳費代碼 |
|
|
354
|
-
| `CVSValidationNo` | 超商驗證碼 |
|
|
355
|
-
| `ExpireDate` | 取貨期限 |
|
|
356
|
-
|
|
357
|
-
---
|
|
358
|
-
|
|
359
|
-
## 黑貓宅配
|
|
360
|
-
|
|
361
|
-
### 建立物流訂單
|
|
362
|
-
|
|
363
|
-
#### 端點
|
|
364
|
-
|
|
365
|
-
```
|
|
366
|
-
POST /api/logistics/create
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
#### EncryptInfo 參數
|
|
370
|
-
|
|
371
|
-
| 參數 | 類型 | 長度 | 必填 | 說明 |
|
|
372
|
-
|------|------|------|------|------|
|
|
373
|
-
| `MerID` | String | 20 |
|
|
374
|
-
| `MerTradeNo` | String | 50 |
|
|
375
|
-
| `LogisticsType` | String | 50 |
|
|
376
|
-
| `GoodsType` | Integer | - |
|
|
377
|
-
| `GoodsAmount` | Integer | - |
|
|
378
|
-
| `GoodsName` | String | 50 |
|
|
379
|
-
| `GoodsWeight` | Integer | - | 否 | 商品重量 (g) |
|
|
380
|
-
| `SenderName` | String | 10 |
|
|
381
|
-
| `SenderPhone` | String | 20 |
|
|
382
|
-
| `SenderZipCode` | String | 5 |
|
|
383
|
-
| `SenderAddress` | String | 200 |
|
|
384
|
-
| `ReceiverName` | String | 10 |
|
|
385
|
-
| `ReceiverPhone` | String | 20 |
|
|
386
|
-
| `ReceiverZipCode` | String | 5 |
|
|
387
|
-
| `ReceiverAddress` | String | 200 |
|
|
388
|
-
| `ScheduledPickupDate` | String | 10 | 否 | 預定取貨日期 `yyyy/MM/dd` |
|
|
389
|
-
| `ScheduledDeliveryDate` | String | 10 | 否 | 預定配達日期 `yyyy/MM/dd` |
|
|
390
|
-
| `ScheduledDeliveryTime` | String | 2 | 否 | 預定配達時段 |
|
|
391
|
-
| `NotifyURL` | String | 500 |
|
|
392
|
-
| `Timestamp` | Integer | - |
|
|
393
|
-
|
|
394
|
-
### ScheduledDeliveryTime 配達時段
|
|
395
|
-
|
|
396
|
-
| 代碼 | 時段 |
|
|
397
|
-
|------|------|
|
|
398
|
-
| `01` | 13:00 前 |
|
|
399
|
-
| `02` | 14:00 - 18:00 |
|
|
400
|
-
| `03` | 不指定 |
|
|
401
|
-
|
|
402
|
-
### 尺寸與重量限制
|
|
403
|
-
|
|
404
|
-
| 溫層 | 材積 | 重量 |
|
|
405
|
-
|------|------|------|
|
|
406
|
-
| 常溫 | 150cm | 20kg |
|
|
407
|
-
| 冷凍/冷藏 | 120cm | 15kg |
|
|
408
|
-
|
|
409
|
-
**材積計算**: 長 + 寬 + 高 ≤ 限制
|
|
410
|
-
|
|
411
|
-
### PHP 範例
|
|
412
|
-
|
|
413
|
-
```php
|
|
414
|
-
<?php
|
|
415
|
-
|
|
416
|
-
$encryption = new PayuniEncryption($merKey, $merIV);
|
|
417
|
-
|
|
418
|
-
$params = [
|
|
419
|
-
'MerID' => 'YOUR_MER_ID',
|
|
420
|
-
'MerTradeNo' => 'LOG' . time(),
|
|
421
|
-
'LogisticsType' => 'PAYUNi_Logistic_Tcat',
|
|
422
|
-
'GoodsType' => 1, // 常溫
|
|
423
|
-
'GoodsAmount' => 1000,
|
|
424
|
-
'GoodsName' => '測試商品',
|
|
425
|
-
'GoodsWeight' => 500, // 500g
|
|
426
|
-
'SenderName' => '寄件人',
|
|
427
|
-
'SenderPhone' => '0912345678',
|
|
428
|
-
'SenderZipCode' => '100',
|
|
429
|
-
'SenderAddress' => '台北市中正區某某路1號',
|
|
430
|
-
'ReceiverName' => '收件人',
|
|
431
|
-
'ReceiverPhone' => '0987654321',
|
|
432
|
-
'ReceiverZipCode' => '300',
|
|
433
|
-
'ReceiverAddress' => '新竹市東區某某路2號',
|
|
434
|
-
'ScheduledDeliveryTime' => '02', // 14:00-18:00
|
|
435
|
-
'NotifyURL' => 'https://your-site.com/payuni_shipping_tcat_notify',
|
|
436
|
-
'Timestamp' => time(),
|
|
437
|
-
];
|
|
438
|
-
|
|
439
|
-
$encryptInfo = $encryption->encrypt($params);
|
|
440
|
-
$hashInfo = $encryption->hashInfo($encryptInfo);
|
|
441
|
-
|
|
442
|
-
$ch = curl_init();
|
|
443
|
-
curl_setopt_array($ch, [
|
|
444
|
-
CURLOPT_URL => 'https://api.payuni.com.tw/api/logistics/create',
|
|
445
|
-
CURLOPT_POST => true,
|
|
446
|
-
CURLOPT_POSTFIELDS => http_build_query([
|
|
447
|
-
'MerID' => $params['MerID'],
|
|
448
|
-
'Version' => '1.0',
|
|
449
|
-
'EncryptInfo' => $encryptInfo,
|
|
450
|
-
'HashInfo' => $hashInfo,
|
|
451
|
-
]),
|
|
452
|
-
CURLOPT_RETURNTRANSFER => true,
|
|
453
|
-
]);
|
|
454
|
-
|
|
455
|
-
$response = curl_exec($ch);
|
|
456
|
-
curl_close($ch);
|
|
457
|
-
|
|
458
|
-
$result = json_decode($response, true);
|
|
459
|
-
|
|
460
|
-
if ($result['Status'] === 'SUCCESS') {
|
|
461
|
-
$data = $encryption->decrypt($result['EncryptInfo']);
|
|
462
|
-
// $data['LogisticsID'] - 物流編號
|
|
463
|
-
// $data['ShipmentNo'] - 託運單號
|
|
464
|
-
print_r($data);
|
|
465
|
-
}
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
### 回應參數 (解密後)
|
|
469
|
-
|
|
470
|
-
| 參數 | 說明 |
|
|
471
|
-
|------|------|
|
|
472
|
-
| `LogisticsID` | PayUni 物流編號 |
|
|
473
|
-
| `MerTradeNo` | 商店訂單編號 |
|
|
474
|
-
| `ShipmentNo` | 黑貓託運單號 |
|
|
475
|
-
| `BookingNote` | 取貨編號 |
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
## 物流狀態通知
|
|
480
|
-
|
|
481
|
-
### Notify URL 設定
|
|
482
|
-
|
|
483
|
-
| 物流類型 | Notify URL 格式建議 |
|
|
484
|
-
|----------|---------------------|
|
|
485
|
-
| 超商物流 | `https://your-site.com/payuni_shipping_711_notify` |
|
|
486
|
-
| 黑貓宅配 | `https://your-site.com/payuni_shipping_tcat_notify` |
|
|
487
|
-
|
|
488
|
-
### 通知流程
|
|
489
|
-
|
|
490
|
-
```
|
|
491
|
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
492
|
-
│ 物流商 │────▶│ PayUni │────▶│ 商店 │
|
|
493
|
-
│ 狀態更新 │ │ 處理 │ │ NotifyURL │
|
|
494
|
-
└─────────────┘ └─────────────┘ └─────────────┘
|
|
495
|
-
│
|
|
496
|
-
│ POST (加密資料)
|
|
497
|
-
▼
|
|
498
|
-
┌─────────────┐
|
|
499
|
-
│ 商店 │
|
|
500
|
-
│ 解密處理 │
|
|
501
|
-
└─────────────┘
|
|
502
|
-
│
|
|
503
|
-
│ 回應 "SUCCESS"
|
|
504
|
-
▼
|
|
505
|
-
┌─────────────┐
|
|
506
|
-
│ PayUni │
|
|
507
|
-
│ 確認收到 │
|
|
508
|
-
└─────────────┘
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### 通知參數
|
|
512
|
-
|
|
513
|
-
PayUni 會 POST 加密資料到 `NotifyURL`:
|
|
514
|
-
|
|
515
|
-
| 參數 | 說明 |
|
|
516
|
-
|------|------|
|
|
517
|
-
| `MerID` | 商店代號 |
|
|
518
|
-
| `EncryptInfo` | 加密的物流狀態 |
|
|
519
|
-
| `HashInfo` | SHA256 驗證碼 |
|
|
520
|
-
|
|
521
|
-
### 解密後的通知內容
|
|
522
|
-
|
|
523
|
-
| 參數 | 說明 |
|
|
524
|
-
|------|------|
|
|
525
|
-
| `MerID` | 商店代號 |
|
|
526
|
-
| `MerTradeNo` | 商店訂單編號 |
|
|
527
|
-
| `LogisticsID` | PayUni 物流編號 |
|
|
528
|
-
| `LogisticsType` | 物流類型 |
|
|
529
|
-
| `LogisticsStatus` | 物流狀態碼 |
|
|
530
|
-
| `LogisticsStatusMsg` | 物流狀態訊息 |
|
|
531
|
-
| `UpdateTime` | 狀態更新時間 |
|
|
532
|
-
|
|
533
|
-
### 處理範例
|
|
534
|
-
|
|
535
|
-
```php
|
|
536
|
-
<?php
|
|
537
|
-
|
|
538
|
-
// 接收通知
|
|
539
|
-
$encryptInfo = $_POST['EncryptInfo'] ?? '';
|
|
540
|
-
$hashInfo = $_POST['HashInfo'] ?? '';
|
|
541
|
-
$merID = $_POST['MerID'] ?? '';
|
|
542
|
-
|
|
543
|
-
// 驗證 HashInfo
|
|
544
|
-
$encryption = new PayuniEncryption($merKey, $merIV);
|
|
545
|
-
$calculatedHash = $encryption->hashInfo($encryptInfo);
|
|
546
|
-
|
|
547
|
-
if ($hashInfo !== $calculatedHash) {
|
|
548
|
-
echo 'HashInfo Error';
|
|
549
|
-
exit;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// 解密
|
|
553
|
-
$data = $encryption->decrypt($encryptInfo);
|
|
554
|
-
|
|
555
|
-
// 根據物流狀態更新訂單
|
|
556
|
-
switch ($data['LogisticsStatus']) {
|
|
557
|
-
case '11':
|
|
558
|
-
// 已出貨
|
|
559
|
-
updateOrderLogisticsStatus($data['MerTradeNo'], 'shipped');
|
|
560
|
-
break;
|
|
561
|
-
case '21':
|
|
562
|
-
// 已到店
|
|
563
|
-
updateOrderLogisticsStatus($data['MerTradeNo'], 'arrived');
|
|
564
|
-
break;
|
|
565
|
-
case '22':
|
|
566
|
-
// 已取貨
|
|
567
|
-
updateOrderLogisticsStatus($data['MerTradeNo'], 'picked_up');
|
|
568
|
-
break;
|
|
569
|
-
case '31':
|
|
570
|
-
case '32':
|
|
571
|
-
// 退貨
|
|
572
|
-
updateOrderLogisticsStatus($data['MerTradeNo'], 'returned');
|
|
573
|
-
break;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// 回應 SUCCESS
|
|
577
|
-
echo 'SUCCESS';
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
|
|
582
|
-
## 物流狀態查詢
|
|
583
|
-
|
|
584
|
-
### 端點
|
|
585
|
-
|
|
586
|
-
```
|
|
587
|
-
POST /api/logistics/query
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
### EncryptInfo 參數
|
|
591
|
-
|
|
592
|
-
| 參數 | 類型 | 必填 | 說明 |
|
|
593
|
-
|------|------|------|------|
|
|
594
|
-
| `MerID` | String |
|
|
595
|
-
| `MerTradeNo` | String |
|
|
596
|
-
| `Timestamp` | Integer |
|
|
597
|
-
|
|
598
|
-
### 回應參數 (解密後)
|
|
599
|
-
|
|
600
|
-
| 參數 | 說明 |
|
|
601
|
-
|------|------|
|
|
602
|
-
| `LogisticsID` | PayUni 物流編號 |
|
|
603
|
-
| `MerTradeNo` | 商店訂單編號 |
|
|
604
|
-
| `LogisticsType` | 物流類型 |
|
|
605
|
-
| `LogisticsStatus` | 物流狀態碼 |
|
|
606
|
-
| `LogisticsStatusMsg` | 物流狀態訊息 |
|
|
607
|
-
| `ShipmentNo` | 託運單號 |
|
|
608
|
-
| `ReceiverStoreID` | 收件門市代號 (超商) |
|
|
609
|
-
| `UpdateTime` | 狀態更新時間 |
|
|
610
|
-
|
|
611
|
-
### 貨態即時查詢
|
|
612
|
-
|
|
613
|
-
| 物流類型 | 查詢網址 |
|
|
614
|
-
|----------|----------|
|
|
615
|
-
| 7-11 | https://eservice.7-11.com.tw/E-Tracking/search.aspx |
|
|
616
|
-
| 黑貓宅配 | https://www.t-cat.com.tw/inquire/trace.aspx |
|
|
617
|
-
|
|
618
|
-
---
|
|
619
|
-
|
|
620
|
-
## 錯誤碼對照表
|
|
621
|
-
|
|
622
|
-
### 物流狀態碼 (LogisticsStatus)
|
|
623
|
-
|
|
624
|
-
| 狀態碼 | 說明 |
|
|
625
|
-
|--------|------|
|
|
626
|
-
| `11` | 已出貨 |
|
|
627
|
-
| `21` | 已到店 (超商) / 配達中 (宅配) |
|
|
628
|
-
| `22` | 已取貨 / 配達完成 |
|
|
629
|
-
| `31` | 退貨中 |
|
|
630
|
-
| `32` | 退貨完成 |
|
|
631
|
-
|
|
632
|
-
### 詳細物流狀態
|
|
633
|
-
|
|
634
|
-
#### 7-11 超商
|
|
635
|
-
|
|
636
|
-
| 狀態碼 | 說明 |
|
|
637
|
-
|--------|------|
|
|
638
|
-
| `11` | 已出貨 (寄件門市已收件) |
|
|
639
|
-
| `21` | 已到店 (到達取件門市) |
|
|
640
|
-
| `22` | 已取貨 (消費者已取件) |
|
|
641
|
-
| `31` | 退貨中 (超過取貨期限) |
|
|
642
|
-
| `32` | 退貨完成 |
|
|
643
|
-
|
|
644
|
-
#### 黑貓宅配
|
|
645
|
-
|
|
646
|
-
| 狀態碼 | 說明 |
|
|
647
|
-
|--------|------|
|
|
648
|
-
| `11` | 已出貨 (黑貓已收件) |
|
|
649
|
-
| `21` | 配達中 |
|
|
650
|
-
| `22` | 配達完成 |
|
|
651
|
-
| `31` | 退貨中 (配達失敗) |
|
|
652
|
-
| `32` | 退貨完成 |
|
|
653
|
-
|
|
654
|
-
### 常見錯誤訊息
|
|
655
|
-
|
|
656
|
-
| 錯誤訊息 | 說明 | 處理方式 |
|
|
657
|
-
|----------|------|----------|
|
|
658
|
-
| `參數錯誤` | 必填參數缺失或格式錯誤 | 檢查參數格式 |
|
|
659
|
-
| `商店代號錯誤` | MerID 不存在 | 確認商店代號 |
|
|
660
|
-
| `門市代號錯誤` | StoreID 無效 | 重新查詢門市代號 |
|
|
661
|
-
| `物流類型錯誤` | LogisticsType 無效 | 確認物流類型代碼 |
|
|
662
|
-
| `HashInfo 驗證失敗` | 加密資料不正確 | 重新計算 HashInfo |
|
|
663
|
-
| `超過尺寸限制` | 材積/重量超過限制 | 調整商品包裝 |
|
|
664
|
-
|
|
665
|
-
---
|
|
666
|
-
|
|
667
|
-
## 常見問題排解
|
|
668
|
-
|
|
669
|
-
### 門市代號無效
|
|
670
|
-
|
|
671
|
-
**問題**: 收到 `門市代號錯誤`
|
|
672
|
-
|
|
673
|
-
**解決**:
|
|
674
|
-
1. 至 7-11 電子地圖重新查詢門市代號
|
|
675
|
-
2. 確認門市是否仍在營運
|
|
676
|
-
3. 確認門市是否支援店到店服務
|
|
677
|
-
|
|
678
|
-
### 物流狀態通知未收到
|
|
679
|
-
|
|
680
|
-
**問題**: 物流狀態變更但沒收到通知
|
|
681
|
-
|
|
682
|
-
**檢查項目**:
|
|
683
|
-
1. NotifyURL 是否為 HTTPS
|
|
684
|
-
2. 伺服器是否能被外網存取
|
|
685
|
-
3. 是否正確回應 `SUCCESS`
|
|
686
|
-
4. 防火牆是否阻擋 PayUni IP
|
|
687
|
-
|
|
688
|
-
### 黑貓取貨時間
|
|
689
|
-
|
|
690
|
-
**問題**: 如何安排黑貓取貨時間
|
|
691
|
-
|
|
692
|
-
**說明**:
|
|
693
|
-
1. 使用 `ScheduledPickupDate` 指定取貨日期
|
|
694
|
-
2. 黑貓會在指定日期至寄件地址取貨
|
|
695
|
-
3. 取貨時段通常為 9:00-18:00
|
|
696
|
-
|
|
697
|
-
### 超商取貨期限
|
|
698
|
-
|
|
699
|
-
**問題**: 超商取貨期限是多久
|
|
700
|
-
|
|
701
|
-
**說明**:
|
|
702
|
-
- 7-11: 7 天
|
|
703
|
-
- 超過期限未取件會自動退貨
|
|
704
|
-
|
|
705
|
-
---
|
|
706
|
-
|
|
707
|
-
## 官方資源
|
|
708
|
-
|
|
709
|
-
- **官方網站**: https://www.payuni.com.tw/
|
|
710
|
-
- **物流服務**: https://www.payuni.com.tw/shipping
|
|
711
|
-
- **API 文件**: https://www.payuni.com.tw/docs/web/
|
|
712
|
-
- **GitHub**: https://github.com/payuni
|
|
1
|
+
# PayUni Logistics API Reference
|
|
2
|
+
|
|
3
|
+
統一金流 (PAYUNi) 物流 API 完整參考文件。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 目錄
|
|
8
|
+
|
|
9
|
+
1. [API 端點總覽](#api-端點總覽)
|
|
10
|
+
2. [測試環境](#測試環境)
|
|
11
|
+
3. [加密機制](#加密機制)
|
|
12
|
+
4. [物流類型](#物流類型)
|
|
13
|
+
5. [7-11 超商取貨](#7-11-超商取貨)
|
|
14
|
+
6. [黑貓宅配](#黑貓宅配)
|
|
15
|
+
7. [物流狀態通知](#物流狀態通知)
|
|
16
|
+
8. [物流狀態查詢](#物流狀態查詢)
|
|
17
|
+
9. [錯誤碼對照表](#錯誤碼對照表)
|
|
18
|
+
10. [常見問題排解](#常見問題排解)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## API 端點總覽
|
|
23
|
+
|
|
24
|
+
### 基礎 API 路徑
|
|
25
|
+
|
|
26
|
+
| 環境 | 基礎路徑 |
|
|
27
|
+
|------|----------|
|
|
28
|
+
| **測試環境** | `https://sandbox-api.payuni.com.tw/api/` |
|
|
29
|
+
| **正式環境** | `https://api.payuni.com.tw/api/` |
|
|
30
|
+
|
|
31
|
+
### 物流相關端點
|
|
32
|
+
|
|
33
|
+
| 功能 | 端點路徑 | 說明 |
|
|
34
|
+
|------|----------|------|
|
|
35
|
+
| 建立物流訂單 | `/logistics/create` | 建立物流託運單 |
|
|
36
|
+
| 物流狀態查詢 | `/logistics/query` | 查詢物流狀態 |
|
|
37
|
+
| 取消物流訂單 | `/logistics/cancel` | 取消物流訂單 |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 測試環境
|
|
42
|
+
|
|
43
|
+
### 測試帳號
|
|
44
|
+
|
|
45
|
+
測試帳號請至 PayUni 後台申請:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
後台網址: https://www.payuni.com.tw/
|
|
49
|
+
路徑: 會員 > 商店清單 > 指定商店名稱 > 串接設定
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
取得以下資訊:
|
|
53
|
+
- **商店代號 (MerID)**
|
|
54
|
+
- **Hash Key**
|
|
55
|
+
- **Hash IV**
|
|
56
|
+
|
|
57
|
+
### 測試環境端點
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
https://sandbox-api.payuni.com.tw/api/{endpoint}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 注意事項
|
|
64
|
+
|
|
65
|
+
1. 物流幕後 API 需向 PayUni 申請開通
|
|
66
|
+
2. 建議使用固定 IP 主機,避免 IP 變動造成功能失效
|
|
67
|
+
3. 非即時付款 (超商代碼、虛擬帳號) 需等付款完成才會建立物流單
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 加密機制
|
|
72
|
+
|
|
73
|
+
PayUni 採用 **AES-256-GCM** 加密與 **SHA256 HMAC** 驗證。
|
|
74
|
+
|
|
75
|
+
### 加密流程
|
|
76
|
+
|
|
77
|
+
1. **準備參數** - 組合所有請求參數
|
|
78
|
+
2. **URL Encode** - 將參數轉為 Query String
|
|
79
|
+
3. **AES-256-GCM 加密** - 使用 Hash Key 和 Hash IV 加密
|
|
80
|
+
4. **產生 HashInfo** - 使用 SHA256 計算驗證碼
|
|
81
|
+
5. **發送請求** - 將加密資料 POST 至 API
|
|
82
|
+
|
|
83
|
+
### PHP 加密範例
|
|
84
|
+
|
|
85
|
+
```php
|
|
86
|
+
<?php
|
|
87
|
+
|
|
88
|
+
class PayuniEncryption
|
|
89
|
+
{
|
|
90
|
+
private string $merKey;
|
|
91
|
+
private string $merIV;
|
|
92
|
+
|
|
93
|
+
public function __construct(string $merKey, string $merIV)
|
|
94
|
+
{
|
|
95
|
+
$this->merKey = $merKey;
|
|
96
|
+
$this->merIV = $merIV;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* AES-256-GCM 加密
|
|
101
|
+
*/
|
|
102
|
+
public function encrypt(array $params): string
|
|
103
|
+
{
|
|
104
|
+
// 1. 組合 Query String
|
|
105
|
+
$queryString = http_build_query($params);
|
|
106
|
+
|
|
107
|
+
// 2. AES-256-GCM 加密
|
|
108
|
+
$tag = '';
|
|
109
|
+
$encrypted = openssl_encrypt(
|
|
110
|
+
$queryString,
|
|
111
|
+
'aes-256-gcm',
|
|
112
|
+
$this->merKey,
|
|
113
|
+
OPENSSL_RAW_DATA,
|
|
114
|
+
$this->merIV,
|
|
115
|
+
$tag,
|
|
116
|
+
'',
|
|
117
|
+
16
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// 3. 組合加密結果 (加密資料 + tag)
|
|
121
|
+
$encryptInfo = bin2hex($encrypted . $tag);
|
|
122
|
+
|
|
123
|
+
return $encryptInfo;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* AES-256-GCM 解密
|
|
128
|
+
*/
|
|
129
|
+
public function decrypt(string $encryptInfo): array
|
|
130
|
+
{
|
|
131
|
+
// 1. Hex 轉 Binary
|
|
132
|
+
$data = hex2bin($encryptInfo);
|
|
133
|
+
|
|
134
|
+
// 2. 分離加密資料和 tag
|
|
135
|
+
$encrypted = substr($data, 0, -16);
|
|
136
|
+
$tag = substr($data, -16);
|
|
137
|
+
|
|
138
|
+
// 3. AES-256-GCM 解密
|
|
139
|
+
$decrypted = openssl_decrypt(
|
|
140
|
+
$encrypted,
|
|
141
|
+
'aes-256-gcm',
|
|
142
|
+
$this->merKey,
|
|
143
|
+
OPENSSL_RAW_DATA,
|
|
144
|
+
$this->merIV,
|
|
145
|
+
$tag
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// 4. 解析 Query String
|
|
149
|
+
parse_str($decrypted, $result);
|
|
150
|
+
|
|
151
|
+
return $result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 產生 HashInfo (SHA256)
|
|
156
|
+
*/
|
|
157
|
+
public function hashInfo(string $encryptInfo): string
|
|
158
|
+
{
|
|
159
|
+
$raw = $encryptInfo . $this->merKey . $this->merIV;
|
|
160
|
+
return strtoupper(hash('sha256', $raw));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Python 加密範例
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
"""PayUni AES-256-GCM 加密"""
|
|
169
|
+
|
|
170
|
+
from Crypto.Cipher import AES
|
|
171
|
+
from urllib.parse import urlencode, parse_qs
|
|
172
|
+
import hashlib
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class PayuniEncryption:
|
|
176
|
+
def __init__(self, mer_key: str, mer_iv: str):
|
|
177
|
+
self.mer_key = mer_key.encode('utf-8')
|
|
178
|
+
self.mer_iv = mer_iv.encode('utf-8')
|
|
179
|
+
|
|
180
|
+
def encrypt(self, params: dict) -> str:
|
|
181
|
+
"""AES-256-GCM 加密"""
|
|
182
|
+
# 1. 組合 Query String
|
|
183
|
+
query_string = urlencode(params)
|
|
184
|
+
|
|
185
|
+
# 2. AES-256-GCM 加密
|
|
186
|
+
cipher = AES.new(self.mer_key, AES.MODE_GCM, nonce=self.mer_iv)
|
|
187
|
+
encrypted, tag = cipher.encrypt_and_digest(query_string.encode('utf-8'))
|
|
188
|
+
|
|
189
|
+
# 3. 組合加密結果
|
|
190
|
+
encrypt_info = (encrypted + tag).hex()
|
|
191
|
+
|
|
192
|
+
return encrypt_info
|
|
193
|
+
|
|
194
|
+
def decrypt(self, encrypt_info: str) -> dict:
|
|
195
|
+
"""AES-256-GCM 解密"""
|
|
196
|
+
# 1. Hex 轉 Binary
|
|
197
|
+
data = bytes.fromhex(encrypt_info)
|
|
198
|
+
|
|
199
|
+
# 2. 分離加密資料和 tag
|
|
200
|
+
encrypted = data[:-16]
|
|
201
|
+
tag = data[-16:]
|
|
202
|
+
|
|
203
|
+
# 3. AES-256-GCM 解密
|
|
204
|
+
cipher = AES.new(self.mer_key, AES.MODE_GCM, nonce=self.mer_iv)
|
|
205
|
+
decrypted = cipher.decrypt_and_verify(encrypted, tag)
|
|
206
|
+
|
|
207
|
+
# 4. 解析 Query String
|
|
208
|
+
result = dict(parse_qs(decrypted.decode('utf-8')))
|
|
209
|
+
return {k: v[0] for k, v in result.items()}
|
|
210
|
+
|
|
211
|
+
def hash_info(self, encrypt_info: str) -> str:
|
|
212
|
+
"""產生 HashInfo (SHA256)"""
|
|
213
|
+
raw = encrypt_info + self.mer_key.decode() + self.mer_iv.decode()
|
|
214
|
+
return hashlib.sha256(raw.encode('utf-8')).hexdigest().upper()
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 物流類型
|
|
220
|
+
|
|
221
|
+
### 支援的物流服務
|
|
222
|
+
|
|
223
|
+
| 物流類型 | 代碼 | 溫層 | 說明 |
|
|
224
|
+
|----------|------|------|------|
|
|
225
|
+
| 7-11 店到店 (C2C) | `PAYUNi_Logistic_711` | 常溫 | 消費者自行寄件 |
|
|
226
|
+
| 7-11 店到店冷凍 | `PAYUNi_Logistic_711_Freeze` | 冷凍 | 冷凍店到店 |
|
|
227
|
+
| 7-11 大宗寄倉 (B2C) | `PAYUNi_Logistic_711_B2C` | 常溫 | 商家寄倉 |
|
|
228
|
+
| 黑貓宅配常溫 | `PAYUNi_Logistic_Tcat` | 常溫 | 宅配到府 |
|
|
229
|
+
| 黑貓宅配冷凍 | `PAYUNi_Logistic_Tcat_Freeze` | 冷凍 | 冷凍宅配 |
|
|
230
|
+
| 黑貓宅配冷藏 | `PAYUNi_Logistic_Tcat_Cold` | 冷藏 | 冷藏宅配 |
|
|
231
|
+
|
|
232
|
+
### GoodsType 溫層代碼
|
|
233
|
+
|
|
234
|
+
| 代碼 | 溫層 | 適用物流 |
|
|
235
|
+
|------|------|----------|
|
|
236
|
+
| `1` | 常溫 | 全部 |
|
|
237
|
+
| `2` | 冷凍 | 7-11 冷凍、黑貓冷凍 |
|
|
238
|
+
| `3` | 冷藏 | 僅黑貓宅配 |
|
|
239
|
+
|
|
240
|
+
### 撥款時間
|
|
241
|
+
|
|
242
|
+
| 物流類型 | 撥款時間 |
|
|
243
|
+
|----------|----------|
|
|
244
|
+
| 超商取貨 | 取貨日 + 7 天 |
|
|
245
|
+
| 黑貓宅配 | 取貨日 + 15 天 |
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 7-11 超商取貨
|
|
250
|
+
|
|
251
|
+
### 建立物流訂單
|
|
252
|
+
|
|
253
|
+
#### 端點
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
POST /api/logistics/create
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### EncryptInfo 參數
|
|
260
|
+
|
|
261
|
+
| 參數 | 類型 | 長度 | 必填 | 說明 |
|
|
262
|
+
|------|------|------|------|------|
|
|
263
|
+
| `MerID` | String | 20 | ● | 商店代號 |
|
|
264
|
+
| `MerTradeNo` | String | 50 | ● | 商店訂單編號 |
|
|
265
|
+
| `LogisticsType` | String | 50 | ● | 物流類型代碼 |
|
|
266
|
+
| `GoodsType` | Integer | - | ● | 溫層 `1`:常溫 `2`:冷凍 |
|
|
267
|
+
| `GoodsAmount` | Integer | - | ● | 商品金額 |
|
|
268
|
+
| `GoodsName` | String | 50 | ● | 商品名稱 |
|
|
269
|
+
| `SenderName` | String | 10 | ● | 寄件人姓名 |
|
|
270
|
+
| `SenderPhone` | String | 20 | ● | 寄件人電話 |
|
|
271
|
+
| `SenderStoreID` | String | 10 | 否 | 寄件門市代號 (C2C 必填) |
|
|
272
|
+
| `ReceiverName` | String | 10 | ● | 收件人姓名 |
|
|
273
|
+
| `ReceiverPhone` | String | 20 | ● | 收件人電話 |
|
|
274
|
+
| `ReceiverStoreID` | String | 10 | ● | 收件門市代號 |
|
|
275
|
+
| `NotifyURL` | String | 500 | ● | 物流狀態通知網址 |
|
|
276
|
+
| `Timestamp` | Integer | - | ● | Unix 時間戳 |
|
|
277
|
+
|
|
278
|
+
### C2C vs B2C 差異
|
|
279
|
+
|
|
280
|
+
| 項目 | C2C 店到店 | B2C 大宗寄倉 |
|
|
281
|
+
|------|-----------|-------------|
|
|
282
|
+
| 寄件方式 | 消費者自行至門市寄件 | 商家統一寄倉 |
|
|
283
|
+
| SenderStoreID | 必填 | 不需要 |
|
|
284
|
+
| 適用場景 | 個人賣家 | 企業電商 |
|
|
285
|
+
|
|
286
|
+
### 門市查詢
|
|
287
|
+
|
|
288
|
+
7-11 門市代號可透過以下方式取得:
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
7-11 電子地圖: https://emap.pcsc.com.tw/
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### PHP 範例
|
|
295
|
+
|
|
296
|
+
```php
|
|
297
|
+
<?php
|
|
298
|
+
|
|
299
|
+
$encryption = new PayuniEncryption($merKey, $merIV);
|
|
300
|
+
|
|
301
|
+
$params = [
|
|
302
|
+
'MerID' => 'YOUR_MER_ID',
|
|
303
|
+
'MerTradeNo' => 'LOG' . time(),
|
|
304
|
+
'LogisticsType' => 'PAYUNi_Logistic_711',
|
|
305
|
+
'GoodsType' => 1,
|
|
306
|
+
'GoodsAmount' => 500,
|
|
307
|
+
'GoodsName' => '測試商品',
|
|
308
|
+
'SenderName' => '寄件人',
|
|
309
|
+
'SenderPhone' => '0912345678',
|
|
310
|
+
'SenderStoreID' => '123456', // C2C 必填
|
|
311
|
+
'ReceiverName' => '收件人',
|
|
312
|
+
'ReceiverPhone' => '0987654321',
|
|
313
|
+
'ReceiverStoreID' => '654321',
|
|
314
|
+
'NotifyURL' => 'https://your-site.com/payuni_shipping_711_notify',
|
|
315
|
+
'Timestamp' => time(),
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
$encryptInfo = $encryption->encrypt($params);
|
|
319
|
+
$hashInfo = $encryption->hashInfo($encryptInfo);
|
|
320
|
+
|
|
321
|
+
$ch = curl_init();
|
|
322
|
+
curl_setopt_array($ch, [
|
|
323
|
+
CURLOPT_URL => 'https://api.payuni.com.tw/api/logistics/create',
|
|
324
|
+
CURLOPT_POST => true,
|
|
325
|
+
CURLOPT_POSTFIELDS => http_build_query([
|
|
326
|
+
'MerID' => $params['MerID'],
|
|
327
|
+
'Version' => '1.0',
|
|
328
|
+
'EncryptInfo' => $encryptInfo,
|
|
329
|
+
'HashInfo' => $hashInfo,
|
|
330
|
+
]),
|
|
331
|
+
CURLOPT_RETURNTRANSFER => true,
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
$response = curl_exec($ch);
|
|
335
|
+
curl_close($ch);
|
|
336
|
+
|
|
337
|
+
$result = json_decode($response, true);
|
|
338
|
+
|
|
339
|
+
if ($result['Status'] === 'SUCCESS') {
|
|
340
|
+
$data = $encryption->decrypt($result['EncryptInfo']);
|
|
341
|
+
// $data['LogisticsID'] - 物流編號
|
|
342
|
+
// $data['CVSPaymentNo'] - 超商繳費代碼
|
|
343
|
+
print_r($data);
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### 回應參數 (解密後)
|
|
348
|
+
|
|
349
|
+
| 參數 | 說明 |
|
|
350
|
+
|------|------|
|
|
351
|
+
| `LogisticsID` | PayUni 物流編號 |
|
|
352
|
+
| `MerTradeNo` | 商店訂單編號 |
|
|
353
|
+
| `CVSPaymentNo` | 超商繳費代碼 |
|
|
354
|
+
| `CVSValidationNo` | 超商驗證碼 |
|
|
355
|
+
| `ExpireDate` | 取貨期限 |
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 黑貓宅配
|
|
360
|
+
|
|
361
|
+
### 建立物流訂單
|
|
362
|
+
|
|
363
|
+
#### 端點
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
POST /api/logistics/create
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### EncryptInfo 參數
|
|
370
|
+
|
|
371
|
+
| 參數 | 類型 | 長度 | 必填 | 說明 |
|
|
372
|
+
|------|------|------|------|------|
|
|
373
|
+
| `MerID` | String | 20 | ● | 商店代號 |
|
|
374
|
+
| `MerTradeNo` | String | 50 | ● | 商店訂單編號 |
|
|
375
|
+
| `LogisticsType` | String | 50 | ● | 物流類型代碼 |
|
|
376
|
+
| `GoodsType` | Integer | - | ● | 溫層 `1`:常溫 `2`:冷凍 `3`:冷藏 |
|
|
377
|
+
| `GoodsAmount` | Integer | - | ● | 商品金額 |
|
|
378
|
+
| `GoodsName` | String | 50 | ● | 商品名稱 |
|
|
379
|
+
| `GoodsWeight` | Integer | - | 否 | 商品重量 (g) |
|
|
380
|
+
| `SenderName` | String | 10 | ● | 寄件人姓名 |
|
|
381
|
+
| `SenderPhone` | String | 20 | ● | 寄件人電話 |
|
|
382
|
+
| `SenderZipCode` | String | 5 | ● | 寄件人郵遞區號 |
|
|
383
|
+
| `SenderAddress` | String | 200 | ● | 寄件人地址 |
|
|
384
|
+
| `ReceiverName` | String | 10 | ● | 收件人姓名 |
|
|
385
|
+
| `ReceiverPhone` | String | 20 | ● | 收件人電話 |
|
|
386
|
+
| `ReceiverZipCode` | String | 5 | ● | 收件人郵遞區號 |
|
|
387
|
+
| `ReceiverAddress` | String | 200 | ● | 收件人地址 |
|
|
388
|
+
| `ScheduledPickupDate` | String | 10 | 否 | 預定取貨日期 `yyyy/MM/dd` |
|
|
389
|
+
| `ScheduledDeliveryDate` | String | 10 | 否 | 預定配達日期 `yyyy/MM/dd` |
|
|
390
|
+
| `ScheduledDeliveryTime` | String | 2 | 否 | 預定配達時段 |
|
|
391
|
+
| `NotifyURL` | String | 500 | ● | 物流狀態通知網址 |
|
|
392
|
+
| `Timestamp` | Integer | - | ● | Unix 時間戳 |
|
|
393
|
+
|
|
394
|
+
### ScheduledDeliveryTime 配達時段
|
|
395
|
+
|
|
396
|
+
| 代碼 | 時段 |
|
|
397
|
+
|------|------|
|
|
398
|
+
| `01` | 13:00 前 |
|
|
399
|
+
| `02` | 14:00 - 18:00 |
|
|
400
|
+
| `03` | 不指定 |
|
|
401
|
+
|
|
402
|
+
### 尺寸與重量限制
|
|
403
|
+
|
|
404
|
+
| 溫層 | 材積 | 重量 |
|
|
405
|
+
|------|------|------|
|
|
406
|
+
| 常溫 | 150cm | 20kg |
|
|
407
|
+
| 冷凍/冷藏 | 120cm | 15kg |
|
|
408
|
+
|
|
409
|
+
**材積計算**: 長 + 寬 + 高 ≤ 限制
|
|
410
|
+
|
|
411
|
+
### PHP 範例
|
|
412
|
+
|
|
413
|
+
```php
|
|
414
|
+
<?php
|
|
415
|
+
|
|
416
|
+
$encryption = new PayuniEncryption($merKey, $merIV);
|
|
417
|
+
|
|
418
|
+
$params = [
|
|
419
|
+
'MerID' => 'YOUR_MER_ID',
|
|
420
|
+
'MerTradeNo' => 'LOG' . time(),
|
|
421
|
+
'LogisticsType' => 'PAYUNi_Logistic_Tcat',
|
|
422
|
+
'GoodsType' => 1, // 常溫
|
|
423
|
+
'GoodsAmount' => 1000,
|
|
424
|
+
'GoodsName' => '測試商品',
|
|
425
|
+
'GoodsWeight' => 500, // 500g
|
|
426
|
+
'SenderName' => '寄件人',
|
|
427
|
+
'SenderPhone' => '0912345678',
|
|
428
|
+
'SenderZipCode' => '100',
|
|
429
|
+
'SenderAddress' => '台北市中正區某某路1號',
|
|
430
|
+
'ReceiverName' => '收件人',
|
|
431
|
+
'ReceiverPhone' => '0987654321',
|
|
432
|
+
'ReceiverZipCode' => '300',
|
|
433
|
+
'ReceiverAddress' => '新竹市東區某某路2號',
|
|
434
|
+
'ScheduledDeliveryTime' => '02', // 14:00-18:00
|
|
435
|
+
'NotifyURL' => 'https://your-site.com/payuni_shipping_tcat_notify',
|
|
436
|
+
'Timestamp' => time(),
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
$encryptInfo = $encryption->encrypt($params);
|
|
440
|
+
$hashInfo = $encryption->hashInfo($encryptInfo);
|
|
441
|
+
|
|
442
|
+
$ch = curl_init();
|
|
443
|
+
curl_setopt_array($ch, [
|
|
444
|
+
CURLOPT_URL => 'https://api.payuni.com.tw/api/logistics/create',
|
|
445
|
+
CURLOPT_POST => true,
|
|
446
|
+
CURLOPT_POSTFIELDS => http_build_query([
|
|
447
|
+
'MerID' => $params['MerID'],
|
|
448
|
+
'Version' => '1.0',
|
|
449
|
+
'EncryptInfo' => $encryptInfo,
|
|
450
|
+
'HashInfo' => $hashInfo,
|
|
451
|
+
]),
|
|
452
|
+
CURLOPT_RETURNTRANSFER => true,
|
|
453
|
+
]);
|
|
454
|
+
|
|
455
|
+
$response = curl_exec($ch);
|
|
456
|
+
curl_close($ch);
|
|
457
|
+
|
|
458
|
+
$result = json_decode($response, true);
|
|
459
|
+
|
|
460
|
+
if ($result['Status'] === 'SUCCESS') {
|
|
461
|
+
$data = $encryption->decrypt($result['EncryptInfo']);
|
|
462
|
+
// $data['LogisticsID'] - 物流編號
|
|
463
|
+
// $data['ShipmentNo'] - 託運單號
|
|
464
|
+
print_r($data);
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### 回應參數 (解密後)
|
|
469
|
+
|
|
470
|
+
| 參數 | 說明 |
|
|
471
|
+
|------|------|
|
|
472
|
+
| `LogisticsID` | PayUni 物流編號 |
|
|
473
|
+
| `MerTradeNo` | 商店訂單編號 |
|
|
474
|
+
| `ShipmentNo` | 黑貓託運單號 |
|
|
475
|
+
| `BookingNote` | 取貨編號 |
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## 物流狀態通知
|
|
480
|
+
|
|
481
|
+
### Notify URL 設定
|
|
482
|
+
|
|
483
|
+
| 物流類型 | Notify URL 格式建議 |
|
|
484
|
+
|----------|---------------------|
|
|
485
|
+
| 超商物流 | `https://your-site.com/payuni_shipping_711_notify` |
|
|
486
|
+
| 黑貓宅配 | `https://your-site.com/payuni_shipping_tcat_notify` |
|
|
487
|
+
|
|
488
|
+
### 通知流程
|
|
489
|
+
|
|
490
|
+
```
|
|
491
|
+
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
492
|
+
│ 物流商 │────▶│ PayUni │────▶│ 商店 │
|
|
493
|
+
│ 狀態更新 │ │ 處理 │ │ NotifyURL │
|
|
494
|
+
└─────────────┘ └─────────────┘ └─────────────┘
|
|
495
|
+
│
|
|
496
|
+
│ POST (加密資料)
|
|
497
|
+
▼
|
|
498
|
+
┌─────────────┐
|
|
499
|
+
│ 商店 │
|
|
500
|
+
│ 解密處理 │
|
|
501
|
+
└─────────────┘
|
|
502
|
+
│
|
|
503
|
+
│ 回應 "SUCCESS"
|
|
504
|
+
▼
|
|
505
|
+
┌─────────────┐
|
|
506
|
+
│ PayUni │
|
|
507
|
+
│ 確認收到 │
|
|
508
|
+
└─────────────┘
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### 通知參數
|
|
512
|
+
|
|
513
|
+
PayUni 會 POST 加密資料到 `NotifyURL`:
|
|
514
|
+
|
|
515
|
+
| 參數 | 說明 |
|
|
516
|
+
|------|------|
|
|
517
|
+
| `MerID` | 商店代號 |
|
|
518
|
+
| `EncryptInfo` | 加密的物流狀態 |
|
|
519
|
+
| `HashInfo` | SHA256 驗證碼 |
|
|
520
|
+
|
|
521
|
+
### 解密後的通知內容
|
|
522
|
+
|
|
523
|
+
| 參數 | 說明 |
|
|
524
|
+
|------|------|
|
|
525
|
+
| `MerID` | 商店代號 |
|
|
526
|
+
| `MerTradeNo` | 商店訂單編號 |
|
|
527
|
+
| `LogisticsID` | PayUni 物流編號 |
|
|
528
|
+
| `LogisticsType` | 物流類型 |
|
|
529
|
+
| `LogisticsStatus` | 物流狀態碼 |
|
|
530
|
+
| `LogisticsStatusMsg` | 物流狀態訊息 |
|
|
531
|
+
| `UpdateTime` | 狀態更新時間 |
|
|
532
|
+
|
|
533
|
+
### 處理範例
|
|
534
|
+
|
|
535
|
+
```php
|
|
536
|
+
<?php
|
|
537
|
+
|
|
538
|
+
// 接收通知
|
|
539
|
+
$encryptInfo = $_POST['EncryptInfo'] ?? '';
|
|
540
|
+
$hashInfo = $_POST['HashInfo'] ?? '';
|
|
541
|
+
$merID = $_POST['MerID'] ?? '';
|
|
542
|
+
|
|
543
|
+
// 驗證 HashInfo
|
|
544
|
+
$encryption = new PayuniEncryption($merKey, $merIV);
|
|
545
|
+
$calculatedHash = $encryption->hashInfo($encryptInfo);
|
|
546
|
+
|
|
547
|
+
if ($hashInfo !== $calculatedHash) {
|
|
548
|
+
echo 'HashInfo Error';
|
|
549
|
+
exit;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// 解密
|
|
553
|
+
$data = $encryption->decrypt($encryptInfo);
|
|
554
|
+
|
|
555
|
+
// 根據物流狀態更新訂單
|
|
556
|
+
switch ($data['LogisticsStatus']) {
|
|
557
|
+
case '11':
|
|
558
|
+
// 已出貨
|
|
559
|
+
updateOrderLogisticsStatus($data['MerTradeNo'], 'shipped');
|
|
560
|
+
break;
|
|
561
|
+
case '21':
|
|
562
|
+
// 已到店
|
|
563
|
+
updateOrderLogisticsStatus($data['MerTradeNo'], 'arrived');
|
|
564
|
+
break;
|
|
565
|
+
case '22':
|
|
566
|
+
// 已取貨
|
|
567
|
+
updateOrderLogisticsStatus($data['MerTradeNo'], 'picked_up');
|
|
568
|
+
break;
|
|
569
|
+
case '31':
|
|
570
|
+
case '32':
|
|
571
|
+
// 退貨
|
|
572
|
+
updateOrderLogisticsStatus($data['MerTradeNo'], 'returned');
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// 回應 SUCCESS
|
|
577
|
+
echo 'SUCCESS';
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## 物流狀態查詢
|
|
583
|
+
|
|
584
|
+
### 端點
|
|
585
|
+
|
|
586
|
+
```
|
|
587
|
+
POST /api/logistics/query
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### EncryptInfo 參數
|
|
591
|
+
|
|
592
|
+
| 參數 | 類型 | 必填 | 說明 |
|
|
593
|
+
|------|------|------|------|
|
|
594
|
+
| `MerID` | String | ● | 商店代號 |
|
|
595
|
+
| `MerTradeNo` | String | ● | 商店訂單編號 |
|
|
596
|
+
| `Timestamp` | Integer | ● | Unix 時間戳 |
|
|
597
|
+
|
|
598
|
+
### 回應參數 (解密後)
|
|
599
|
+
|
|
600
|
+
| 參數 | 說明 |
|
|
601
|
+
|------|------|
|
|
602
|
+
| `LogisticsID` | PayUni 物流編號 |
|
|
603
|
+
| `MerTradeNo` | 商店訂單編號 |
|
|
604
|
+
| `LogisticsType` | 物流類型 |
|
|
605
|
+
| `LogisticsStatus` | 物流狀態碼 |
|
|
606
|
+
| `LogisticsStatusMsg` | 物流狀態訊息 |
|
|
607
|
+
| `ShipmentNo` | 託運單號 |
|
|
608
|
+
| `ReceiverStoreID` | 收件門市代號 (超商) |
|
|
609
|
+
| `UpdateTime` | 狀態更新時間 |
|
|
610
|
+
|
|
611
|
+
### 貨態即時查詢
|
|
612
|
+
|
|
613
|
+
| 物流類型 | 查詢網址 |
|
|
614
|
+
|----------|----------|
|
|
615
|
+
| 7-11 | https://eservice.7-11.com.tw/E-Tracking/search.aspx |
|
|
616
|
+
| 黑貓宅配 | https://www.t-cat.com.tw/inquire/trace.aspx |
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
## 錯誤碼對照表
|
|
621
|
+
|
|
622
|
+
### 物流狀態碼 (LogisticsStatus)
|
|
623
|
+
|
|
624
|
+
| 狀態碼 | 說明 |
|
|
625
|
+
|--------|------|
|
|
626
|
+
| `11` | 已出貨 |
|
|
627
|
+
| `21` | 已到店 (超商) / 配達中 (宅配) |
|
|
628
|
+
| `22` | 已取貨 / 配達完成 |
|
|
629
|
+
| `31` | 退貨中 |
|
|
630
|
+
| `32` | 退貨完成 |
|
|
631
|
+
|
|
632
|
+
### 詳細物流狀態
|
|
633
|
+
|
|
634
|
+
#### 7-11 超商
|
|
635
|
+
|
|
636
|
+
| 狀態碼 | 說明 |
|
|
637
|
+
|--------|------|
|
|
638
|
+
| `11` | 已出貨 (寄件門市已收件) |
|
|
639
|
+
| `21` | 已到店 (到達取件門市) |
|
|
640
|
+
| `22` | 已取貨 (消費者已取件) |
|
|
641
|
+
| `31` | 退貨中 (超過取貨期限) |
|
|
642
|
+
| `32` | 退貨完成 |
|
|
643
|
+
|
|
644
|
+
#### 黑貓宅配
|
|
645
|
+
|
|
646
|
+
| 狀態碼 | 說明 |
|
|
647
|
+
|--------|------|
|
|
648
|
+
| `11` | 已出貨 (黑貓已收件) |
|
|
649
|
+
| `21` | 配達中 |
|
|
650
|
+
| `22` | 配達完成 |
|
|
651
|
+
| `31` | 退貨中 (配達失敗) |
|
|
652
|
+
| `32` | 退貨完成 |
|
|
653
|
+
|
|
654
|
+
### 常見錯誤訊息
|
|
655
|
+
|
|
656
|
+
| 錯誤訊息 | 說明 | 處理方式 |
|
|
657
|
+
|----------|------|----------|
|
|
658
|
+
| `參數錯誤` | 必填參數缺失或格式錯誤 | 檢查參數格式 |
|
|
659
|
+
| `商店代號錯誤` | MerID 不存在 | 確認商店代號 |
|
|
660
|
+
| `門市代號錯誤` | StoreID 無效 | 重新查詢門市代號 |
|
|
661
|
+
| `物流類型錯誤` | LogisticsType 無效 | 確認物流類型代碼 |
|
|
662
|
+
| `HashInfo 驗證失敗` | 加密資料不正確 | 重新計算 HashInfo |
|
|
663
|
+
| `超過尺寸限制` | 材積/重量超過限制 | 調整商品包裝 |
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## 常見問題排解
|
|
668
|
+
|
|
669
|
+
### 門市代號無效
|
|
670
|
+
|
|
671
|
+
**問題**: 收到 `門市代號錯誤`
|
|
672
|
+
|
|
673
|
+
**解決**:
|
|
674
|
+
1. 至 7-11 電子地圖重新查詢門市代號
|
|
675
|
+
2. 確認門市是否仍在營運
|
|
676
|
+
3. 確認門市是否支援店到店服務
|
|
677
|
+
|
|
678
|
+
### 物流狀態通知未收到
|
|
679
|
+
|
|
680
|
+
**問題**: 物流狀態變更但沒收到通知
|
|
681
|
+
|
|
682
|
+
**檢查項目**:
|
|
683
|
+
1. NotifyURL 是否為 HTTPS
|
|
684
|
+
2. 伺服器是否能被外網存取
|
|
685
|
+
3. 是否正確回應 `SUCCESS`
|
|
686
|
+
4. 防火牆是否阻擋 PayUni IP
|
|
687
|
+
|
|
688
|
+
### 黑貓取貨時間
|
|
689
|
+
|
|
690
|
+
**問題**: 如何安排黑貓取貨時間
|
|
691
|
+
|
|
692
|
+
**說明**:
|
|
693
|
+
1. 使用 `ScheduledPickupDate` 指定取貨日期
|
|
694
|
+
2. 黑貓會在指定日期至寄件地址取貨
|
|
695
|
+
3. 取貨時段通常為 9:00-18:00
|
|
696
|
+
|
|
697
|
+
### 超商取貨期限
|
|
698
|
+
|
|
699
|
+
**問題**: 超商取貨期限是多久
|
|
700
|
+
|
|
701
|
+
**說明**:
|
|
702
|
+
- 7-11: 7 天
|
|
703
|
+
- 超過期限未取件會自動退貨
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## 官方資源
|
|
708
|
+
|
|
709
|
+
- **官方網站**: https://www.payuni.com.tw/
|
|
710
|
+
- **物流服務**: https://www.payuni.com.tw/shipping
|
|
711
|
+
- **API 文件**: https://www.payuni.com.tw/docs/web/
|
|
712
|
+
- **GitHub**: https://github.com/payuni
|