ขั้นตอนชำระเงินฝั่งเซิร์ฟเวอร์ที่รองรับลองซ้ำและป้องกันรายการซ้ำ
1. เตรียมรหัสและสิทธิ์เข้าถึง
เริ่มจากร้านที่เปิดและวิธีชำระที่ทดสอบแล้ว ใน การตั้งค่า → การเข้าถึง APIสร้างข้อมูลรับรองอ่าน/เขียนที่จำกัดเฉพาะโปรเจกต์ที่ต้องใช้ เก็บโทเคนบนแบ็กเอนด์ ห้ามใส่ในโค้ดเบราว์เซอร์หรือคลังซอร์สสาธารณะ
คัดลอก รหัส API โปรเจกต์ และ รหัส API ร้าน จากกล่อง พื้นฐาน → รหัส API ของร้าน รหัสเหล่านี้คือ UUID ไม่ใช่ชื่อระบุโปรเจกต์ที่อ่านง่ายหรือเลขคำสั่งซื้อ ใช้ชื่อโฮสต์ API ของคุณเอง
ใน ร้าน → IPNสร้างคีย์ลับลงนามก่อนระบุ ipn_urlตัวรับ HTTPS ของคุณต้องเข้าถึงได้จาก VPS ผู้ค้า
2. สร้างใบแจ้งหนี้
แทนที่ตัวแทนค่าแล้วส่งคำขอนี้จากแบ็กเอนด์ ยอดเป็นสตริงทศนิยม ไม่ใช่ผลคำนวณเลขทศนิยมแบบ floating-point
curl --fail-with-body --request POST \
'https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices' \
--header 'Authorization: Bearer YOUR_MERCHANT_API_TOKEN' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-1042-attempt-1' \
--data '{
"amount": "10.00",
"currency": "EUR",
"order_id": "order-1042",
"description": "Example order",
"ipn_url": "https://shop.example.com/payments/wholly-ipn",
"metadata": {
"cart_id": "cart-1042"
}
}'บันทึก data.invoice_id ไว้กับคำสั่งซื้อ แล้วเปลี่ยนเส้นทางลูกค้าไป links.checkoutใช้คีย์ idempotency ไม่ซ้ำสำหรับการพยายามชำระครั้งใหม่ หากหมดเวลารอ ให้ลองซ้ำด้วย ข้อมูลรับรอง คีย์ และไบต์เนื้อหาเดิมทุกประการ.
หากไม่ระบุ payment_methods จะใช้วิธีที่ร้านรับ คุณจำกัดรายใบแจ้งหนี้ด้วย slug เชนและชื่อย่อได้ แต่ไม่เปิดสินทรัพย์ที่ร้านไม่ได้รับ ฟิลด์คำขอและตัวอย่างคำตอบทั้งหมด →
3. เลือก IPN, webhook หรือทั้งสอง
IPN ติดตามวงจรใบแจ้งหนี้ ตั้ง URL IPN เริ่มต้นร้านหรือกำหนดทับด้วย ipn_url สำหรับใบแจ้งหนี้นั้น Webhook สมัครปลายทางให้รับเหตุการณ์ที่เลือก เช่น invoice.settled.
ใช้รูปแบบลายเซ็นเดียวกัน แต่ คีย์ลับต่างกัน: IPN ใช้คีย์ลับ IPN ร้าน แต่ละปลายทาง webhook มีคีย์ลับของตน ทั้งสองไม่ใช้โทเคน API bearer ลงนาม
หากทั้งสองส่งเข้าแอป คาดว่าจะมีการแจ้งเตือนทับซ้อน อย่าเพิ่มยอดคำสั่งซื้อสองครั้ง
4. ตรวจและบันทึกการแจ้งเตือน
- อ่านเนื้อหาคำขอดิบให้ตรงทุกไบต์ก่อนแยก JSON ตรวจ
Wholly-Signatureด้วยคีย์ลับที่ตรง พร้อมตรวจเวลาและป้องกัน replay SDK ทางการมีตัวตรวจให้ - ตรวจตัวตนโปรเจกต์ ร้าน ใบแจ้งหนี้ และเหตุการณ์ในเนื้อหาที่ลงนาม ส่วนหัวส่งที่ไม่ลงนามไม่ใช่แหล่งยืนยันตัวตน
- เก็บเหตุการณ์อย่างถาวรพร้อมค่าที่ไม่ซ้ำของ
event_idแล้วคืน HTTP 2xx โดยเร็ว ประมวลผลคำสั่งซื้อในตัวทำงานเบื้องหลัง - เรียกใบแจ้งหนี้ปัจจุบันจากโฮสต์ API ที่ตั้งไว้ ไม่ใช่โฮสต์ใด ๆ ที่ส่งมาในคำขอ เทียบโปรเจกต์ ร้าน ยอด สกุลเงิน และข้อมูลอ้างอิงคำสั่งซื้อที่บันทึก
PHP · Python · JavaScript / TypeScript · ข้อกำหนดลายเซ็นและตัวอย่างตัวรับ
5. ดำเนินการตามคำสั่งซื้อครั้งเดียวเมื่อชำระเสร็จ
สำหรับตัวรับ ที่อิงเหตุการณ์ ให้จัดการ event_type = invoice.settledแล้วตรวจค่าปัจจุบันของ status = settled และนโยบายข้อยกเว้น ดำเนินการตามคำสั่งซื้อครั้งเดียวภายใต้ธุรกรรมฐานข้อมูล/ข้อจำกัดรหัสคำสั่งซื้อไม่ซ้ำ
เชนที่สรุปผลเร็วอาจส่งทั้ง payment.received และ invoice.settled ร่วมกับ status = settledบนเชนอื่น payment.received อาจยังระบุ processing ทั้งสองกรณีไม่ใช่ข้อผิดพลาด
ตัดเหตุการณ์ซ้ำด้วย event_idไม่ใช่ sequence อย่างเดียว: เหตุการณ์ต่างชนิดอาจใช้ sequence เดียวกัน ตัวจัดการ SDK แบบอิงสถานะจะรวมรุ่นแก้ไขใบแจ้งหนี้และตรวจสถานะโดยไม่สนชนิดเหตุการณ์แทน อย่าผสมการรวมรุ่นนั้นกับตัวกรองชนิดเหตุการณ์ ทั้งสองวิธียังต้องป้องกันซ้ำระดับคำสั่งซื้อ
ตรวจ requires_review และผลการแก้ด้วยตนเองก่อนดำเนินการตามคำสั่งซื้อ amount_status = paid อย่างเดียวไม่พิสูจน์การยืนยัน ฟิลด์สินทรัพย์ที่ชำระระดับบนสรุปการชำระเสร็จ ส่วน payment_info เก็บรายละเอียดเงินที่รับและข้อมูลราคาเสนอ สถานะ เหตุการณ์ และกฎข้อยกเว้นทั้งหมด →
6. ทดสอบการลองซ้ำและกู้คืน
ทดสอบชำระยอดเล็ก การส่งซ้ำ ใบแจ้งหนี้หมดอายุ และตัวรับที่ใช้ไม่ได้ชั่วคราว การเล่นเหตุการณ์ซ้ำต้องไม่เพิ่มยอดคำสั่งซื้อครั้งที่สอง จัดการเหตุการณ์มาผิดลำดับโดยไม่เขียนทับสถานะใหม่กว่า
ตรวจ ร้าน → IPN / Webhook → ประวัติ → รายละเอียดหรือส่วนการส่งในรายละเอียดใบแจ้งหนี้ การส่งซ้ำใช้เหตุการณ์ที่บันทึกไว้ ไม่ใช่การชำระเสร็จใหม่
เครดิตประมวลผลต่ำพัก IPN/webhook แต่การรับชำระยังทำงาน กระทบยอดคำสั่งซื้อค้างผ่าน API และจัดการการส่งที่เก็บไว้หลังฟื้นการทำงาน ห้ามดำเนินการตามคำสั่งซื้อจากการเปลี่ยนเส้นทางเบราว์เซอร์หรือภาพหน้าจอลูกค้า