使用Python串接黑貓宅急便

即上一篇 WooCommerce訂單管理 REST API

接下來的就是物流的應用,宅急便的技術文件請自己合法去跟黑貓技術客服要

*授權碼請至統一速達契約客戶專區登入頁面的「取得印單 API授權碼」點選連結查詢 。
https://www.takkyubin.com.tw/YMTContract/aspx/Login.aspx
本服務網址如
後 說明:
測試 環境 https://egs.suda.com.tw:8443/api/Egs/{服務名稱 }
正式 環境 https://api.suda.com.tw/api/Egs/{服務名稱 }

json格式

[
    {
        "訂單編號": 81209,
        "下單時間": "2025-02-25T09:16:55",
        "姓名": "陳大頭",
        "聯絡電話": "0911212151",
        "Email": "your@mail.com",
        "地址": "709 台南市 安南區 1234567",
        "消費金額": "4956",
        "付款方式": "LINE Pay",
        "訂單備註": "",
        "處理訂單備註": [
            {
                "id": 1435511,
                "key": "is_vat_exempt",
                "value": "no"
            },
            {
                "id": 1435512,
                "key": "additional_temp",
                "value": "冷凍"
            },
            {
                "id": 1435513,
                "key": "cpreftrack_woocommerce_referrer",
                "value": ""
            },
            {
                "id": 1435514,
                "key": "_wc_cancel_key",
                "value": "df41de84581d148c1174b1e0beabb804"
            },
            {
                "id": 1435516,
                "key": "billing_phone",
                "value": "0932894722"
            },
            {
                "id": 1435517,
                "key": "additional_deliver_date",
                "value": "不指定"
            },
            {
                "id": 1435518,
                "key": "additional_shipping_receive_time",
                "value": "不指定"
            },
            {
                "id": 1435519,
                "key": "additional_gift2",
                "value": "無送禮需求"
            },
            {
                "id": 1435520,
                "key": "smilepayei_donate",
                "value": ""
            },
            {
                "id": 1435521,
                "key": "smilepayei_lovekey",
                "value": ""
            },
            {
                "id": 1435522,
                "key": "smilepayei_carrier_type",
                "value": "EJ0113"
            },
            {
                "id": 1435523,
                "key": "smilepayei_carrier_id",
                "value": ""
            },
            {
                "id": 1435524,
                "key": "smilepayei_buyer_id",
                "value": ""
            },
            {
                "id": 1435526,
                "key": "ywot_tracking_code",
                "value": ""
            },
            {
                "id": 1435527,
                "key": "ywot_tracking_postcode",
                "value": ""
            },
            {
                "id": 1435528,
                "key": "ywot_carrier_id",
                "value": "UNKNOWN"
            },
            {
                "id": 1435529,
                "key": "ywot_pick_up_date",
                "value": ""
            },
            {
                "id": 1435530,
                "key": "ywot_picked_up",
                "value": ""
            },
            {
                "id": 1435531,
                "key": "_linepay_payment_status",
                "value": "confirmed"
            },
            {
                "id": 1435532,
                "key": "_linepay_reserved_transaction_id",
                "value": "2025022562005572410"
            },
            {
                "id": 1435551,
                "key": "_new_order_email_sent",
                "value": "true"
            },
            {
                "id": 1435563,
                "key": "_linepay_transaction_balanced_amount",
                "value": "4956"
            },
            {
                "id": 1435564,
                "key": "_ga_tracked",
                "value": "1"
            },
            {
                "id": 1435789,
                "key": "oshwoo_aggregated",
                "value": "3"
            }
        ],
        "商品": [
            {
                "商品名稱": "商品1",
                "SKU": "P01234512",
                "數量": 1,
                "單價": 1180
            },
            {
                "商品名稱": "商品2",
                "SKU": "P02112121",
                "數量": 1,
                "單價": 0
            }
        ],
        "收件人姓名": "陳小明",
        "收件人電話": "0912112111",
        "收件人地址": "台南市中西區12313212號"
    }
import json
import requests
from datetime import datetime

# -------------------------------
# 1. 列印託運單 (PrintOBT)
# -------------------------------

# 讀取 export_order.json 中的訂單資料
with open("export_order.json", "r", encoding="utf-8") as f:
    orders = json.load(f)

# 測試環境與認證資料
customer_id = "95525512"
customer_token = "11231151"

# 寄件人資訊(更新後)
sender_info = {
    "SenderName": "寄件者姓名",
    "SenderTel": "寄件者電話",
    "SenderMobile": "0952691828",  # 更新後
    "SenderZipCode": "70523K",     # 更新後
    "SenderAddress": "台南市安南區1234567號"
}

# 組合 API 所需訂單資料
api_orders = []
for order in orders:
    order_id = str(order.get("訂單編號"))
    recipient_name = order.get("收件人姓名", order.get("姓名"))
    recipient_tel = order.get("收件人電話", order.get("聯絡電話"))
    recipient_mobile = order.get("聯絡電話")
    recipient_address = order.get("收件人地址", order.get("地址"))
    # 以下單時間轉換為 yyyyMMdd 格式作為出貨與配達日期
    order_date = datetime.fromisoformat(order.get("下單時間")).strftime("%Y%m%d")
    
    # 根據 API 規格組合每筆訂單資料,ProductName 固定使用 "食品"
    api_order = {
        "OBTNumber": "",            # 採用系統分配時,此欄留空
        "OrderId": order_id,
        "Thermosphere": "0001",       # 例如:0001 表示常溫
        "Spec": "0001",               # 例如:0001 表示 60cm
        "ReceiptLocation": "01",      # 01 表示到宅
        "ReceiptStationNo": "",
        "RecipientName": recipient_name,
        "RecipientTel": recipient_tel,
        "RecipientMobile": recipient_mobile,
        "RecipientAddress": recipient_address,
        "SenderName": sender_info["SenderName"],
        "SenderTel": sender_info["SenderTel"],
        "SenderMobile": sender_info["SenderMobile"],
        "SenderZipCode": sender_info["SenderZipCode"],
        "SenderAddress": sender_info["SenderAddress"],
        "ShipmentDate": order_date,
        "DeliveryDate": order_date,   # 這裡暫以下單日作為出貨及配達日期,可根據需求調整
        "DeliveryTime": "04",         # 04 表示不指定
        "IsFreight": "N",
        "IsCollection": "N",
        "CollectionAmount": 0,
        "IsSwipe": "Y",
        "IsDeclare": "N",
        "DeclareAmount": 0,
        "ProductTypeId": "0015",      # 例如:0015 表示其他類別
        "ProductName": "食品",        # 固定使用 "食品"
        "Memo": order.get("訂單備註", "")
    }
    api_orders.append(api_order)

# 組合最終的印單 API 請求內容
print_payload = {
    "CustomerId": customer_id,
    "CustomerToken": customer_token,
    "PrintType": "01",       # 01 表示使用系統分配託運單號
    "PrintOBTType": "01",    # 例如:01 代表 A4 二模宅配
    "Orders": api_orders
}

# 測試環境印單 API URL
print_api_url = "https://egs.suda.com.tw:8443/api/Egs/PrintOBT"
headers = {"Content-Type": "application/json"}

# 呼叫印單 API
response_print = requests.post(print_api_url, json=print_payload, headers=headers, verify=False)

# 組合印單交換資訊
print_exchange = {
    "request": print_payload,
    "response": response_print.json() if response_print.status_code == 200 else {"error": response_print.text}
}

# 保存印單交換資訊至 tcat.json
with open("tcat.json", "w", encoding="utf-8") as f:
    json.dump(print_exchange, f, ensure_ascii=False, indent=4)

print("PrintOBT API 交換資訊已存檔至 tcat.json")

# -------------------------------
# 2. 下載 PDF (DownloadOBT)
# -------------------------------

# 從印單交換資訊中取得 FileNo
file_no = print_exchange.get("response", {}).get("Data", {}).get("FileNo", "")
if not file_no:
    print("找不到 FileNo,請確認 tcat.json 中有正確回傳 FileNo。")
    exit(1)

# 組合下載 PDF 請求內容
download_payload = {
    "CustomerId": customer_id,
    "CustomerToken": customer_token,
    "FileNo": file_no
    # 若需要補印特定託運單,可加入 "Orders" 欄位
}

# 測試環境下載 API URL
download_api_url = "https://egs.suda.com.tw:8443/api/Egs/DownloadOBT"

# 呼叫下載 API (回傳內容為二進位資料流)
response_download = requests.post(download_api_url, json=download_payload, headers=headers, verify=False)

# 組合下載交換資訊
download_exchange = {
    "request": download_payload,
    "response": {
        "status_code": response_download.status_code,
        "headers": dict(response_download.headers)
    }
}

if response_download.status_code == 200:
    pdf_data = response_download.content
    # 保存 PDF 檔案
    with open("consignment.pdf", "wb") as f:
        f.write(pdf_data)
    download_exchange["response"]["content_length"] = len(pdf_data)
    print("託運單 PDF 已下載並保存為 consignment.pdf")
else:
    download_exchange["response"]["error"] = response_download.text
    print("下載 PDF 失敗,錯誤資訊:", response_download.text)

# 保存下載交換資訊至 tcat_download.json
with open("tcat_download.json", "w", encoding="utf-8") as f:
    json.dump(download_exchange, f, ensure_ascii=False, indent=4)

print("DownloadOBT API 交換資訊已存檔至 tcat_download.json")

PS.這個版本的python目前還沒對應完全,像溫層,貨到付款的判定...等等

底下則是另一個再更進階的版本,上半部是託運單,下半部是撿貨單

import json
import requests
from datetime import datetime

# -------------------------------
# 1. 列印託運單 (採用撿貨明細版型:PrintOBTByPickingList)
# -------------------------------

# 讀取 export_order.json 中的訂單資料
with open("export_order.json", "r", encoding="utf-8") as f:
    orders = json.load(f)

# 認證資料與寄件人資訊(更新後)
customer_id = "89502222"
customer_token = "2na2211"
sender_info = {
    "SenderName": "寄件者姓名",
    "SenderTel": "寄件者電話",
    "SenderMobile": "0952232112",  # 更新後
    "SenderZipCode": "70523K",     # 更新後
    "SenderAddress": "台南市安南區1234567號"
}

# 組合 API 所需訂單資料,每筆訂單同時帶入 Detail(商品明細)
api_orders = []
for order in orders:
    order_id = str(order.get("訂單編號"))
    recipient_name = order.get("收件人姓名", order.get("姓名"))
    recipient_tel = order.get("收件人電話", order.get("聯絡電話"))
    recipient_mobile = order.get("聯絡電話")
    recipient_address = order.get("收件人地址", order.get("地址"))
    # 以下單時間轉換為 yyyyMMdd 格式作為出貨及配達日期
    order_date = datetime.fromisoformat(order.get("下單時間")).strftime("%Y%m%d")
    
    # 建立 Detail 資料,從訂單中的 "商品" 陣列帶入商品明細
    products = order.get("商品", [])
    detail_body = []
    total_quantity = 0
    total_amount = 0
    for product in products:
        qty = product.get("數量", 0)
        unit_price = product.get("單價", 0)
        # 若單價為負,則用 0 計算
        if unit_price < 0:
            unit_price = 0
        amount = qty * unit_price
        total_quantity += qty
        total_amount += amount
        detail_body.append({
            "OrderId": order_id,
            "ProductTypeId": "0015",  # 固定使用 0015(其他),可依需求調整
            "ProductName": product.get("商品名稱", ""),  # 使用原本商品名稱
            "Quantity": qty,
            "Price": unit_price,
            "Amount": amount,
            "Column01": "",
            "Column02": "",
            "Column03": "",
            "Column04": ""
        })
    
    # 將 export_order.json 中的「訂單備註」加入頁尾 Row01,
    # 並在 Row02 寫入 "感謝訂購,客服專線061234521"
    detail = {
        "Title": {
            "Column01": "品項",
            "Column02": "數量",
            "Column03": "單價",
            "Column04": "合計"
        },
        "Body": detail_body,
        "TotalQuantity": total_quantity,
        "TotalAmount": total_amount,
        "Footer": {
            "Row01": order.get("訂單備註", ""),
            "Row02": "感謝訂購,客服專線061234521",
            "Row03": "",
            "Row04": ""
        }
    }
    
    # 組合單筆訂單資料
    api_order = {
        "OBTNumber": "",             # 採用系統分配託運單號,此欄留空
        "OrderId": order_id,
        "Thermosphere": "0001",      # 例如:0001 表示常溫
        "Spec": "0001",              # 例如:0001 表示 60cm
        "ReceiptLocation": "01",     # 01 表示到宅
        "ReceiptStationNo": "",
        "RecipientName": recipient_name,
        "RecipientTel": recipient_tel,
        "RecipientMobile": recipient_mobile,
        "RecipientAddress": recipient_address,
        "SenderName": sender_info["SenderName"],
        "SenderTel": sender_info["SenderTel"],
        "SenderMobile": sender_info["SenderMobile"],
        "SenderZipCode": sender_info["SenderZipCode"],
        "SenderAddress": sender_info["SenderAddress"],
        "ShipmentDate": order_date,
        "DeliveryDate": order_date,   # 依需求調整出貨與配達日期
        "DeliveryTime": "04",         # 04 表示不指定
        "IsFreight": "N",
        "IsCollection": "N",
        "CollectionAmount": 0,
        "IsSwipe": "Y",
        "IsDeclare": "N",
        "DeclareAmount": 0,
        "ProductTypeId": "0015",      # 固定使用 0015(其他)
        "ProductName": "食品",        # 此欄固定使用 "食品"
        "Memo": order.get("訂單備註", ""),  # 主單備註也可保留
        "Detail": detail              # 將 Detail 帶入
    }
    api_orders.append(api_order)

# 組合最終的請求內容,使用 A4 二模撿貨單 (PrintOBTType 設為 "02")
print_payload = {
    "CustomerId": customer_id,
    "CustomerToken": customer_token,
    "PrintType": "01",       # 01 表示使用系統分配託運單號
    "PrintOBTType": "02",    # 02 代表 A4 二模撿貨單
    "Orders": api_orders
}

# 使用撿貨明細版型的 API URL
print_api_url = "https://egs.suda.com.tw:8443/api/Egs/PrintOBTByPickingList"
headers = {"Content-Type": "application/json"}

# 呼叫印單 API
response_print = requests.post(print_api_url, json=print_payload, headers=headers, verify=False)

# 組合印單交換資訊
print_exchange = {
    "request": print_payload,
    "response": response_print.json() if response_print.status_code == 200 else {"error": response_print.text}
}

# 將印單交換資訊存檔至 tcat.json
with open("tcat.json", "w", encoding="utf-8") as f:
    json.dump(print_exchange, f, ensure_ascii=False, indent=4)

print("PrintOBTByPickingList API 交換資訊已存檔至 tcat.json")

# -------------------------------
# 2. 下載 PDF (DownloadOBT)
# -------------------------------

# 從印單回應中取得 FileNo
file_no = print_exchange.get("response", {}).get("Data", {}).get("FileNo", "")
if not file_no:
    print("找不到 FileNo,請確認 tcat.json 中有正確回傳 FileNo。")
    exit(1)

# 組合下載 PDF 請求內容
download_payload = {
    "CustomerId": customer_id,
    "CustomerToken": customer_token,
    "FileNo": file_no
}

download_api_url = "https://egs.suda.com.tw:8443/api/Egs/DownloadOBT"
response_download = requests.post(download_api_url, json=download_payload, headers=headers, verify=False)

download_exchange = {
    "request": download_payload,
    "response": {
        "status_code": response_download.status_code,
        "headers": dict(response_download.headers)
    }
}

if response_download.status_code == 200:
    pdf_data = response_download.content
    # 將 PDF 存檔
    with open("consignment.pdf", "wb") as f:
        f.write(pdf_data)
    download_exchange["response"]["content_length"] = len(pdf_data)
    print("託運單 PDF 已下載並保存為 consignment.pdf")
else:
    download_exchange["response"]["error"] = response_download.text
    print("下載 PDF 失敗,錯誤資訊:", response_download.text)

# 將下載 API 的交換資訊保存成 tcat_download.json
with open("tcat_download.json", "w", encoding="utf-8") as f:
    json.dump(download_exchange, f, ensure_ascii=False, indent=4)

print("DownloadOBT API 交換資訊已存檔至 tcat_download.json")
瀏覽次數:92