Maersk API Comparison — Project WGCL / Mappoint Asia
เปรียบเทียบ API สองตัวของ Maersk ที่เกี่ยวกับ GPS tracking ทางบก — ตัวหนึ่งจาก Developer Portal สาธารณะ อีกตัวเป็น spec ที่ Maersk Contract Logistics ส่งให้โปรเจกต์ WGCL โดยตรง
Developer Portal สาธารณะ
เอกสาร Internal — ส่งตรงให้ partner
จุดต่างที่ใหญ่ที่สุดมี 2 เรื่อง:
① ทิศทาง — 🅰 ยิงข้อมูลเข้า Maersk ฝ่ายเดียว แต่ 🅱 เป็นสองทาง (ฝั่งเราต้องเปิด endpoint รับคำสั่ง pairing จาก Maersk ด้วย) และ
② แหล่งข้อมูล order — 🅰 ข้อมูล Work Order/เลขตู้ไม่ไหลผ่าน API เลย (ไปทางบริษัทรถ เราต้องหาทางเอามาเอง)
แต่ 🅱 Maersk ป้อนข้อมูล shipment เข้าระบบเราให้เองผ่าน pairDevice
🅰 Supplier Track API — ทางเดียว (push อย่างเดียว)
┌─── Work Order + เลขตู้ ส่งให้บริษัทรถ ───┐
│ (ช่องทางจองงานปกติ — ไม่ผ่าน API นี้!) │
▼ │
┌──────────────┐ ❓ mapping ┌───────────────┴──┐
│ Carrier │ ─── รถ↔WO↔ตู้ ───► ??? │ Maersk │
│ (บริษัทรถ) │ (GPS provider ต้องออกแบบ │ Inland │
└──────────────┘ ช่องทางรับเอง — จุดอ่อน!) │ │
│ │ │
┌──────────────┐ ▼ │ │
│ GPS Provider │ POST /vendor/positions │ │
│ (เรา) │ ─────────────────────────► │ │
└──────────────┘ บังคับแนบ WO + เลขตู้ └──────────────────┘
ทุก payload ← ถ้าไม่มี = ไม่ผ่าน validation
🅱 SCE-TMS API — สองทาง (ต้องมี endpoint ทั้งสองฝั่ง)
┌──────────────────┐ POST pairDevice / unpairDevice ┌────────────┐
│ Mappoint / │ ◄──────────────────────────────── │ Maersk │
│ GPS Provider │ ★ ข้อมูลทริปมาทางนี้เอง: │ SCE TMS │
│ │ Shipment ↔ Truck + จุดจอด │ (WGCL) │
│ ต้องเปิด endpoint │ + geofence + ETA/ETD │ │
│ ให้ Maersk ยิงเข้า │ │ │
│ │ POST insertHeartbeat / │ │
│ │ ────────────────────────────────► │ │
└──────────────────┘ insertEvent — echo ShipmentRefNo │ │
ที่ได้รับมา (ไม่ต้องหา order เอง) └────────────┘
| มิติ | 🅰 Supplier Track v1.1 | 🅱 SCE-TMS v1.8 |
|---|---|---|
| หน่วยธุรกิจ | Maersk Ocean / Inland (ตู้คอนเทนเนอร์) | Maersk Contract Logistics (WGCL) |
| Base URL (Prod) | api.maersk.com/inlandrvp-inboundapi |
api.ocf.fulfillment.maersk.com/tms/mys/gps/p/v1/ |
| Base URL (Test) | api-stage.maersk.com/inlandrvp-inboundapi |
stage-api.ocf.fulfillment.maersk.com/tms/mys/gps/p/v1/ |
| ทิศทางข้อมูล | ขาเข้าอย่างเดียว (เรา → Maersk) | สองทาง — Maersk ยิงเข้าหาเราด้วย (ต้องเปิด endpoint ฝั่งเรา) |
| Key อ้างอิงข้อมูล | Work Order Number + Container Number | Shipment Reference No + Truck/Device ID (ผ่านการ pairing) |
| ข้อมูล order/ทริป มาจากไหน |
ต้องหาเอง — API ไม่มีช่องทางดึง order; ต้องได้ WO + เลขตู้จาก carrier เอง (ดูหัวข้อ 5) | Maersk ส่งมาให้เอง — ผ่าน pairDevice บอกครบว่ารถไหนผูก shipment ไหน + จุดจอด + geofence |
| Endpoints | 2: /vendor/positions, /vendor/gps |
4: pairDevice, unpairDevice, insertHeartbeat, insertEvent |
| Authentication | OAuth 2.0 + Consumer-Key header + Bearer JWT |
OAuth 2.0 ผ่าน Apigee (Maersk IT ออก credential) · ขา outbound เราต้องมี auth endpoint ให้ Maersk |
| รูปแบบ Body | JSON ตรงๆ (application/json) |
JSON → Base64 → URLEncode ใส่ใน form field Data (application/x-www-form-urlencoded) |
| Timestamp | ISO 8601 + UTC offset2024-01-23T10:46:43+00:00 |
YYYYMMDDhhmmss (UTC)20240101123456 |
| ข้อมูล GPS ที่ส่ง | lat, long, timestamp เท่านั้น | lat, long, timestamp + Address (reverse geocode), Speed, Course, IgnitionStatus, Temperature |
| Event model | Milestone 4 ค่า: DEPARTOP / ARRIVECU / DEPARTCU / ARRIVEOP |
Exception events: SPD ขับเร็ว · HBK เบรกแรง · HAC เร่งแรง · SAC เร็วเข้าโค้ง · SSR |
| Geofence | ไม่มี | มี — pairDevice ส่ง Stops[] พร้อม Polygon/Point+Radius (GeoJSON RFC 7946) |
| ความถี่ส่ง GPS | ทุก 10–15 นาที | ตาม heartbeat ที่ตกลง (ประชุมเสนอ 5–10 นาทีสำหรับหาง) |
| Batch | 1 event ต่อ 1 request | Data[] สูงสุด 100 รายการ/request · 1 transaction/request · 10 concurrent threads |
| Rate limit | 6,000 calls/ชม. · validation 2,000 msg/ชม. (100/นาที) | ไม่ระบุตัวเลขใน spec (คุมที่ concurrent threads = 10) |
| Error handling | HTTP status + detailedErrors[] + correlationIdentifier |
Success: true/false + code S01–S04 · retrigger สูงสุด 3 ครั้ง |
| การเข้าถึง spec | สาธารณะ — developer.maersk.com | Internal — Maersk ส่ง PDF ให้โดยตรง |
| หน้าที่ | 🅰 Supplier Track | 🅱 SCE-TMS |
|---|---|---|
| จับคู่รถ ↔ งาน | ไม่มี — อ้าง Work Order ในทุก payload แทน | POSTpairDevice (Maersk → เรา) พร้อม Stops + geofence + ETA/ETD |
| ยกเลิกจับคู่ | ไม่มี | POSTunpairDevice (Maersk → เรา) |
| ส่งตำแหน่ง GPS | POST/vendor/positions (event เว้นว่าง)POST /vendor/gps (legacy) |
POSTinsertHeartbeat (เรา → Maersk) |
| ส่ง event / milestone | POST/vendor/positions พร้อม event = DEPARTOP ฯลฯ |
POSTinsertEvent — เน้น exception (ขับเร็ว/เบรกแรง) มากกว่า arrival/departure milestone |
ข้อกังวลหลักของผู้ให้บริการ GPS คือ มีแค่ทะเบียนรถ + พิกัด + เวลา แต่ไม่มีข้อมูล order/ตู้/ทริป — สอง API นี้จัดการเรื่องนี้ต่างกันโดยสิ้นเชิง:
workOrderNumber + containerNumber
— แม้แต่ schema ส่งพิกัดอย่างเดียวก็ยังบังคับpairDevice เข้าระบบเราก่อนเริ่มทริป บอกครบ:
รถ/device ไหน ↔ ShipmentRefNo ไหน + จุดจอด (Stops) + geofence + ETA/ETDShipmentRefNo ที่ได้รับมา กลับไปพร้อมพิกัดใน insertHeartbeatunpairDevice มาบอกให้หยุด
Maersk ──① Transport/Work Order──► Carrier (บริษัทรถ) ★ คนเดียวที่มีข้อมูลครบ
│ ในใบงานมี: เลข WO, booking, จุดรับ-ส่ง
▼
② จ่ายงาน: "ทะเบียน กข-1234 วิ่ง WO นี้"
▼
③ คนขับรับตู้ที่ท่า → เลขตู้ยืนยันแน่นอน
(พ่นข้างตู้ + ใบ EIR)
▼
★ จุดที่ข้อมูลต้องไหลเข้าระบบ GPS ของเรา ★
| ทางแก้ | วิธีการ | ภาระฝั่งเรา |
|---|---|---|
| ทาง A — กลับหัวระบบ ⭐ | ให้ Maersk มา ดึงพิกัดจาก API ของเราแทน (FAQ ยืนยันว่าทำได้ — ติดต่อ inlandtrackingsupport@maersk.com) แล้ว Maersk จับคู่กับ order เองฝั่งเขา | ต่ำสุด — เราตอบแค่ "รถทะเบียนนี้อยู่ไหนเมื่อไหร่" ไม่ต้องแตะข้อมูล order เลย |
| ทาง B — Carrier กรอก | หน้าจอ dispatch / driver app (สแกนเลขตู้ + validate check digit ISO 6346) / sync จาก TMS ของ carrier — ผูก mapping ตอนปล่อยงาน แล้วระบบเรา enrich พิกัดก่อนยิง | ปานกลาง — ต้องพัฒนาช่องทางรับ mapping + พึ่งวินัยการกรอกของ carrier |
| ทาง C — B + Geofence | ต่อยอดทาง B: รู้พิกัดท่า/โรงงานลูกค้า → ยิง milestone 4 events อัตโนมัติ (API ไม่บอกพิกัดปลายทาง ต้องได้จาก carrier เช่นกัน) | สูงสุด — เพิ่ม geofence engine + master data สถานที่ |
{
"uniqueIdentifier": "3",
"latitude": 59.515137,
"longitude": 17.674397,
"timestamp": "2024-03-03T07:45:00+01:00",
"workOrderNumber": "67000009954",
"bookingNumber": "1",
"containerNumber": "BMOU2430595",
"event": ""
}
ส่งเป็น JSON ตรงๆ · Header: Consumer-Key + Authorization: Bearer
{
"TransID": "123345",
"AccountID": "SCE123",
"Data": [{
"Country": "THA", "TenantID": "0",
"Type": "Shipment",
"Heartbeat": {
"DeviceID": "S8788M",
"TimeStamp": "20240101123456",
"ShipmentRefNo": "otmshipmentID1",
"Latitude": 28.544663,
"Longitude": 77.119275,
"Address": "reverse geocoded address",
"Speed": 25.0, "Course": 320.0,
"IgnitionStatus": true,
"Temperature": 28.2
}
}]
}
JSON นี้ต้อง Base64 → URLEncode แล้วส่งเป็นRequestTime=...&Version=1.0&Data=<base64>
pairDevice / unpairDevice — พร้อมระบบ authentication ให้ Maersk ขอ token จากเราได้Address เป็น required ใน heartbeatinsertEventSuccess=false หรือไม่ตอบSupplier Track API (🅰) เป็นของหน่วยธุรกิจ Ocean/Inland คนละส่วนกัน — ไม่เกี่ยวกับ scope งาน WGCL แต่รู้ไว้มีประโยชน์: ถ้าในอนาคต Maersk ฝั่ง Ocean ต้องการข้อมูลตู้คอนเทนเนอร์จากรถที่ Mappoint track อยู่ อาจถูกขอให้ integrate ตัวนี้เพิ่ม ซึ่งเป็น integration แยกอีกชุด (คนละ credential, คนละ host, คนละ data model) — และถ้าถึงวันนั้นจริง ให้กลับไปดูหัวข้อ 5 ก่อน: เราไม่มีข้อมูล order/เลขตู้ ทางที่คุ้มที่สุดคือเจรจาทาง A ให้ Maersk มาดึงพิกัดจาก API เราแทน ไม่ใช่รับภาระหา order มายิงเอง