กู้คืน Webhook การซื้อ LINE MINI App ที่ตกหล่น: Runbook กระทบยอดภายใน 7 วัน
หากต้องกู้คืน Webhook การซื้อ LINE MINI App ที่ตกหล่น ให้ query ประวัติ event ทางการของ LINE ภายใน 7 วัน, ทำ pagination โดยไม่เปลี่ยนช่วงเวลาหรือ filter เดิม และส่งทุก purchaseComplete event ที่ได้กลับเข้า handler แบบ idempotent ซึ่งใช้ orderId เป็น key การ reserve สำเร็จไม่ได้พิสูจน์ว่าชำระเงินแล้ว และ history endpoint เป็นแหล่งข้อมูลสำหรับกู้คืนเหตุขัดข้อง ไม่ใช่เหตุผลให้หยุดเฝ้าระวัง Webhook แบบ live แม้ LINE จะเป็นช่องทางลูกค้าที่สำคัญในไทย แต่สิทธิ์ใช้งาน LINE MINI App IAP ใน production ตามข้อเท็จจริงทางการที่อธิบายในบทความนี้ยังจำกัดอยู่ที่ญี่ปุ่น
ประเด็นสำคัญ
- ประวัติ event ของ LINE ครอบคลุมการส่ง Webhook ในช่วง 7 วันที่ผ่านมา และคืนข้อมูลได้สูงสุด 100 records ต่อหน้า
status=FAILEDหมายถึงการส่ง Webhook ล้มเหลว ไม่ได้หมายความว่าการซื้อของลูกค้าล้มเหลว- บันทึก
orderIdตอน reserve การซื้อ แล้วใช้ค่านี้ deduplicate ทั้งpurchaseCompleteevent แบบ live และที่กู้คืนมา - แยกการกู้คืน payment ออกจาก routing ข้อความบริการลูกค้า เพราะใช้ event type, credentials, signature และผู้รับผิดชอบด้าน operation คนละชุด
ทำไม reserve สำเร็จจึงไม่เท่ากับซื้อสำเร็จ
In-app purchase ของ LINE MINI App เป็น flow ทางการที่มีหลายขั้นตอน Server ของคุณ reserve การซื้อก่อนด้วย POST https://api.line.me/iap/v1/product/reserve จากนั้น LINE จะคืน orderId แต่ผู้ใช้ยังอาจปิดแอป, ยกเลิกใน app store, หลุดจากเครือข่าย หรือชำระเงินไม่จบ ด้วยเหตุนี้ คู่มือ integration ของ LINE จึงระบุให้ grant digital item หลังจากได้รับ Webhook ยืนยันการซื้อสำเร็จแล้วเท่านั้น
ขอบเขตนี้แยกคำถามเรื่อง failure ออกเป็นสี่ข้อ:
| คำถาม | หลักฐานที่ควรเชื่อถือ |
|---|---|
| Reserve request สำเร็จหรือไม่? | Reserve response, orderId ที่บันทึกไว้ และ x-line-request-id |
| การซื้อเสร็จสมบูรณ์หรือไม่? | purchaseComplete event หรือผลการกระทบยอดอย่างเป็นทางการ |
| Endpoint ของคุณได้รับ live delivery หรือไม่? | Raw request log และ Webhook processing record |
| Business handler มอบ entitlement เพียงครั้งเดียวหรือไม่? | Idempotency record ที่ใช้ orderId เป็น key |
คู่มือค่าธรรมเนียม LINE MINI App และ support Webhook อธิบายว่าทำไม payment event กับข้อความลูกค้าจึงควรอยู่คนละระบบ Runbook นี้เริ่มจากปัญหาที่ลึกขึ้นอีกระดับ: payment Webhook ควรมาถึงแล้ว แต่ endpoint ใช้งานไม่ได้หรือขั้นตอนประมวลผลล้มเหลว
วิธีกู้คืน Webhook การซื้อ LINE MINI App ที่ตกหล่น
1. ตรวจพบช่องว่างก่อนหน้าต่าง 7 วันปิด
บันทึกค่าต่อไปนี้เมื่อ reserve การซื้อ:
- checkout ID ภายในของคุณ;
orderIdของ LINE;- response header
x-line-request-id; - เวลา reserve และ product ที่คาดไว้;
- สถานะว่า
purchaseCompleteevent ถูกนำไปใช้แล้วหรือยัง
ตั้ง alert เมื่อ reservation ยังไม่ปิดหลังพ้นระยะ checkout ปกติ อย่ารีบระบุว่าชำระเงินแล้ว และอย่ารอถึงวันที่เจ็ดจึงเริ่มตรวจสอบ เพราะ history endpoint ทางการรับเฉพาะช่วงเวลาที่อยู่ภายใน 7 วันที่ผ่านมา
2. Query หน้าต่างกู้คืนที่กำหนดไว้ตายตัว
LINE MINI App API reference ทางการระบุ recovery endpoint นี้:
curl --get "https://api.line.me/iap/v1/webhook/events" \
-H "Authorization: Bearer ${LINE_CHANNEL_ACCESS_TOKEN}" \
--data-urlencode "startEpochSeconds=1784678400" \
--data-urlencode "endEpochSeconds=1784700000" \
--data-urlencode "pageSize=100" \
--data-urlencode "status=FAILED"
Timestamp ด้านบนเป็นเพียงตัวอย่างช่วงเวลาหนึ่งในวันที่ 22 กรกฎาคม 2026 ไม่ใช่ค่าที่ควรคัดลอกไปใช้ใน production ให้สร้าง UTC epoch seconds จากเวลาเริ่มและสิ้นสุดของ incident ใช้ status=FAILED เพื่อค้นหา delivery ที่ LINE ส่งไม่สำเร็จ หรือไม่ส่ง status เมื่อต้องการกระทบยอดทุก delivery ในช่วงนั้น SUCCESS และ FAILED บอกสถานะการส่ง ไม่ใช่ผลลัพธ์การชำระเงิน
3. คง query เดิมไว้ตลอดการทำ pagination
ผลลัพธ์เรียงตามเวลาที่ LINE เริ่มส่ง Webhook แต่ละรายการ หนึ่งหน้ามีได้สูงสุด 100 records และอาจมี nextCursor ทุกหน้าหลังจากหน้าแรกต้องคง startEpochSeconds, endEpochSeconds, pageSize และ status เดิมไว้ เปลี่ยนเฉพาะ cursor
ตัวอย่าง Node.js นี้ทำให้ขอบเขตดังกล่าวชัดเจน:
const baseUrl = "https://api.line.me/iap/v1/webhook/events";
const fixedQuery = {
startEpochSeconds: "1784678400",
endEpochSeconds: "1784700000",
pageSize: "100",
status: "FAILED",
};
let cursor;
do {
const query = new URLSearchParams(fixedQuery);
if (cursor) query.set("cursor", cursor);
const response = await fetch(`${baseUrl}?${query}`, {
headers: { Authorization: `Bearer ${process.env.LINE_CHANNEL_ACCESS_TOKEN}` },
});
if (!response.ok) {
throw new Error(`LINE event history failed: ${response.status}`);
}
const page = await response.json();
for (const record of page.events) {
if (record.event.type === "purchaseComplete") {
await applyPurchaseOnce(record.event.orderId, record.event);
}
}
cursor = page.nextCursor ?? undefined;
} while (cursor);
applyPurchaseOnce คือ transaction boundary ของธุรกิจ โดยควร insert idempotency record และ grant item ภายใน atomic operation เดียว หรือไม่ทำอะไรเมื่อ orderId เดิมถูกใช้ไปแล้ว แนวทางการพัฒนา in-app purchase ของ LINE แนะนำโดยเฉพาะให้ใช้ orderId ป้องกันการมอบสิทธิ์ซ้ำ เพราะ Webhook เดียวกันอาจถูกส่งมากกว่าหนึ่งครั้ง
4. กระทบยอด history กับ live processing
อย่าสร้าง entitlement path ชุดที่สองเพื่อใช้กับ recovery โดยเฉพาะ ให้แปลง event ที่กู้คืนเป็น internal command เดียวกับที่ live Webhook ใช้ พร้อมบันทึก source เป็น line_event_history จากนั้นเปรียบเทียบ:
- ค่า
orderIdที่ reserve แล้วแต่ยังไม่มีสถานะ completed ภายในระบบ; - live Webhook logs;
- history records ใน incident window ที่กำหนดไว้;
- idempotency records และการเปลี่ยนแปลง entitlement
History response ถูกเรียกด้วย channel access token มันไม่ใช่ HTTP delivery เดิม จึงไม่ควรคาดว่าจะมี header x-line-signature เดิมรวมอยู่ด้วย สำหรับ live Webhook ยังต้อง verify header นี้ต่อไป โดย LINE ใช้ channel secret คำนวณ HMAC-SHA256 digest จาก raw request body แล้ว encode เป็น Base64
5. ปิด incident ด้วย checkpoint ที่วัดผลได้
การกู้คืนจะเสร็จเมื่อ reservation ทุกตัวใน scope ถูกจัดประเภทว่า completed-and-applied, incomplete, canceled หรือส่งต่อเพื่อตรวจสอบด้วยคน บันทึกช่วง UTC ที่แน่นอน, filters, จำนวนหน้า, ค่า orderId ที่กู้คืน และเวลาที่ run สำเร็จล่าสุด ตั้ง schedule สำหรับ reconciliation job ขนาดเล็กให้ถี่กว่าขอบเขต retention 7 วัน เพื่อไม่ให้เหตุขัดข้องช่วงสุดสัปดาห์หมดอายุโดยไม่มีใครพบ
เอกสาร event history ปัจจุบันระบุว่า endpoint นี้ดึง purchaseComplete events และมีแผนรองรับ refund history แยกต่างหาก ตรวจสอบ live reference ก่อนสรุปว่า recovery path เดียวกันครอบคลุม refund แล้ว
UnifyPort เหมาะกับส่วนไหน — และไม่เหมาะกับส่วนไหน
UnifyPort ไม่ได้ reserve การซื้อ LINE MINI App, ยืนยัน payment จาก app store, กู้คืน LINE payment events, grant digital items หรือทำให้ผ่านข้อกำหนด review ของ LINE งานทั้งหมดนี้ต้องเป็นความรับผิดชอบของ official LINE in-app purchase flow
UnifyPort เหมาะเมื่อ event ถัดไปคือข้อความลูกค้าทั่วไป เช่น ผู้ซื้อส่งข้อความมาหลังชำระเงินเพราะ item ยังไม่ปรากฏ บัญชี LINE ที่รองรับสามารถส่ง conversation นั้นเป็น message.received event แบบ normalized ได้ หาก UnifyPort Webhook endpoint มี signing_secret การ delivery จะใช้ X-Device-Timestamp และ X-Device-Signature ซึ่งเป็น signature scheme แยกจาก x-line-signature ของ LINE payment Webhook
สำหรับทีมในไทย LINE มักเป็นช่องทางหลักในการสนทนากับลูกค้า แต่ความนิยมของช่องทางไม่ได้เปลี่ยน eligibility ของ LINE IAP หรือทำให้ข้อความ support เป็นหลักฐานการชำระเงิน ให้ route บทสนทนาเข้าสู่ระบบบริการลูกค้าแยกจาก payment reconciliation เสมอ
หากต้องการลงลึกเรื่อง retry และ idempotency ฝั่งข้อความลูกค้า ให้อ่าน การป้องกัน Webhook HMAC Replay: timestamp, retry และ idempotency แยก handler ทั้งสองไว้ แม้ท้ายที่สุดจะอัปเดตระบบ support หรือ order เดียวกันก็ตาม
ข้อจำกัดและสิ่งที่ต้องแลก
- History API ทางการคือ recovery path ที่ถูกต้องสำหรับ LINE MINI App purchase Webhook อินเทอร์เฟซที่ไม่เป็นทางการไม่สามารถขยาย retention หรือเรียกคืน payment records ของ platform ได้
- Lookback 7 วันไม่ใช่ ledger ระยะยาว คุณต้องเก็บ reservation, event, entitlement และ settlement records ของตัวเอง
status=FAILEDช่วยจำกัดผลลัพธ์ไว้ที่ delivery failures แต่อาจพลาด delivery ที่ endpoint รับไว้แล้วแต่ application ประมวลผลล้มเหลว ให้ทำ reconciliation ที่กว้างขึ้นเมื่อจุดล้มเหลวอยู่ที่ application ไม่ใช่ transport- In-app purchase ยังเป็น MINI App surface เฉพาะญี่ปุ่นที่ต้องผ่าน review แม้ LINE จะมีบทบาทสูงในชีวิตประจำวันและงานบริการลูกค้าในไทย ทีมไทยก็ไม่ได้มี IAP eligibility จากการใช้ LINE เพียงอย่างเดียว ตรวจสอบเงื่อนไขล่าสุดก่อนออกแบบ payment workflow; checklist MINI App ที่ผ่านและยังไม่ผ่านการยืนยัน ครอบคลุมการตัดสินใจก่อนหน้านั้น
FAQ
ดึงประวัติ LINE MINI App purchase Webhook ย้อนหลังได้นานเท่าใด?
Endpoint ทางการรับ Webhook history จากช่วง 7 วันที่ผ่านมา ให้ run recovery ก่อนพ้นขอบเขตนี้ และเก็บ ledger ที่ทนทานของตัวเองสำหรับ incident ที่เก่ากว่า
status=FAILED หมายความว่าลูกค้าชำระเงินไม่สำเร็จหรือไม่?
ไม่ใช่ ค่านี้หมายถึงการส่ง Webhook ของ LINE ล้มเหลว Purchase state กับ delivery state เป็นคนละเรื่อง ให้ใช้ event ที่ได้ร่วมกับ reservation และ entitlement records ของคุณเพื่อกระทบยอด order
Grant item หลัง reserve endpoint คืน 200 ได้หรือไม่?
ไม่ได้ Reserve สำเร็จไม่ได้รับประกันว่าการซื้อเสร็จสมบูรณ์ ให้ grant item หลังประมวลผล purchaseComplete event เท่านั้น และใช้ idempotency ด้วย orderId
History recovery อาจประมวลผลการซื้อเดิมสองครั้งหรือไม่?
Query อาจคืน event ที่ live handler นำไปใช้แล้ว ให้ทั้งสอง path เรียก atomic idempotent handler เดียวกันซึ่งใช้ orderId เป็น key เพื่อให้ความพยายามครั้งที่สองกลายเป็น no-op
x-line-signature ของ LINE เหมือนกับ Webhook signature ของ UnifyPort หรือไม่?
ไม่เหมือน LINE sign raw payment-Webhook body และส่ง Base64 signature ส่วน UnifyPort เมื่อเปิด signing_secret จะ sign timestamp ร่วมกับ raw body แล้วส่ง timestamp และ signature headers ของตัวเอง ต้อง verify แต่ละ protocol แยกจากกัน
ขั้นตอนถัดไป
Implement และ test recovery query ตาม LINE MINI App API reference ทางการ แล้ว schedule ให้อยู่ภายใน retention window 7 วัน หากความต้องการอีกส่วนคือรับข้อความ LINE support ทั่วไป ให้ทำตาม คู่มือการอนุญาต LINE ของ UnifyPort หลังจาก payment path มีความเสถียรแล้ว
แหล่งข้อมูล
- LINE MINI App API reference: in-app purchase และ Webhook event history
- การ integrate ฟีเจอร์ in-app purchase ของ LINE MINI App
- แนวทางการพัฒนา in-app purchase ของ LINE MINI App
ตรวจสอบข้อเท็จจริงทางการเมื่อวันที่ 22 กรกฎาคม 2026