Payment & Refund Requirements
หน้านี้รวม Functional Requirements, Acceptance Criteria และ Business Rule Matrix สำหรับ QA/BA
อ้างอิง: ccr-payment-service, PaymentTransactionService.cs, RefundController, Omise webhook docs
US-P01: ชำระเงิน Booking (Customer)
As a ลูกค้า
I want to ชำระเงินค่าจอง
So that booking ถูก confirm และฉันได้รับการยืนยันการจอง
Acceptance Criteria
| # | เงื่อนไข | ผลลัพธ์ที่คาดหวัง |
|---|---|---|
| AC1 | เลือกชำระผ่าน PromptPay/TrueMoney | แสดง QR Code ที่สแกนได้ (ScannableCodeUri) |
| AC2 | เลือกชำระผ่านบัตรเครดิต/เดบิต (ใหม่) | แสดง form กรอกข้อมูลบัตร; บัตร Member ถูกบันทึกเพื่อใช้ครั้งต่อไป |
| AC3 | Member เลือกบัตรที่บันทึกไว้ | ชำระเงินโดยไม่ต้องกรอกข้อมูลบัตรใหม่ |
| AC4 | ชำระเงินสำเร็จ (Omise webhook charge.complete + status = successful) | Booking สถานะ Confirm, ลูกค้าได้รับ email confirmation |
| AC5 | ชำระเงินไม่สำเร็จ (Omise webhook status ≠ successful) | Transaction NotCompleted, payment status Failed, แจ้งลูกค้า |
| AC6 | ไม่ชำระเงินภายในเวลาที่กำหนด | Booking สถานะ Expired, ไม่สามารถชำระได้อีก |
| AC7 | Guest พยายามชำระ booking ของ Member | ระบบ reject |
| AC8 | Member พยายามชำระ booking ของคนอื่น | ระบบ reject |
Business Rules
| BR# | Rule | ตัวอย่าง |
|---|---|---|
| BR-P01-1 | Guest payment: booking ต้องไม่มี CreatedBy | Booking ที่สร้างโดย Guest ไม่มี customerId |
| BR-P01-2 | Member payment: booking.CreatedBy ต้องตรงกับ customerId ใน token | Member A ชำระได้แค่ booking ตัวเอง |
| BR-P01-3 | Expired booking: status = Expired หรือ now >= ExpireAt → reject | ถ้าหมดเวลา ระบบปฏิเสธการชำระ |
| BR-P01-4 | ยอดชำระ = AdditionalAmount ถ้าบวก; ไม่งั้นใช้ booking.Amount | Edit เพิ่มเงิน 500 → ต้องชำระ 500 ไม่ใช่ยอดเต็ม |
| BR-P01-5 | ทุกการชำระ: สร้าง transaction status Pending ก่อนเรียก Omise | ป้องกัน duplicate charge |
| BR-P01-6 | Non-card (PromptPay/TrueMoney): flow CreateChargeWithSourceAsync | ได้ AuthorizeUri และ ScannableCodeUri |
| BR-P01-7 | Member ชำระด้วยบัตรใหม่: save CustomerToken ใน SavedPaymentMethods | ครั้งต่อไปเลือกบัตรเดิมได้ |
US-P02: Payment Timeout และ Retry
As a ลูกค้า
I want to รู้ว่า QR code / payment link หมดอายุ
So that ฉันสามารถดำเนินการต่อได้อย่างถูกต้อง
Acceptance Criteria
| # | เงื่อนไข | ผลลัพธ์ที่คาดหวัง |
|---|---|---|
| AC1 | QR code / payment timeout | แสดง modal payment-time-expired |
| AC2 | Booking status Expired | ลูกค้าไม่สามารถชำระเงินซ้ำได้, ต้องสร้าง booking ใหม่ |
| AC3 | ชำระ PromptPay แล้วรอ webhook ส่งมาช้า (Omise processing) | แสดง pending state; ไม่ confirm ทันที |
US-P03: Refund หลังยกเลิก Booking
As a ลูกค้า
I want to ได้รับเงินคืนหลังยกเลิก booking
So that ฉันมั่นใจว่าได้เงินตาม cancellation policy
Acceptance Criteria
| # | เงื่อนไข | ผลลัพธ์ที่คาดหวัง |
|---|---|---|
| AC1 | ยกเลิก booking สำเร็จ | ระบบสร้าง Refund record สถานะ Created อัตโนมัติ |
| AC2 | Payment method = PromptPay / TrueMoney | ระบบร้องขอ bank account (bank code, account number, account name, image) |
| AC3 | Payment method = บัตรเครดิต | ระบบ refund ผ่าน Omise (สถานะขึ้นกับ Backoffice workflow) |
| AC4 | Backoffice อัปเดต refund status ตาม workflow | แสดงสถานะล่าสุดให้ลูกค้าเห็น |
| AC5 | Refund amount ถูกคำนวณถูกต้อง | Refund = TotalPrice - CancellationFee |
:::warning ขอบเขต Phase 1 Auto Refund อยู่นอก Phase 1 — การคืนเงินเป็น Manual Workflow โดย Backoffice ทั้งหมด :::
Refund Allocation Rule
| Rule | รายละเอียด | ตัวอย่าง |
|---|---|---|
| ลำดับการตัดยอด | ตัดจาก transaction ที่ PaidAt เก่าที่สุดก่อน | จ่าย 2 ครั้ง: 2,000 (วันที่ 1) และ 1,000 (วันที่ 3) → refund 1,500 → ตัดจาก 2,000 ก่อน |
| Bank account | Required เมื่อ payment method เป็น transfer-like method (PromptPay, TrueMoney) | ถ้าไม่มี bank account → flow หยุด |
US-P04: Backoffice — ดำเนินการ Refund
As a Staff (Backoffice)
I want to อัปเดตสถานะ refund ตาม workflow
So that ลูกค้าได้รับเงินคืนอย่างถูกต้องและมีการตรวจสอบหลายระดับ
Acceptance Criteria
| # | เงื่อนไข | ผลลัพธ์ที่คาดหวัง |
|---|---|---|
| AC1 | Staff มี permission operation:refund:update-sale-checked | สามารถเปลี่ยนสถานะเป็น SaleChecked ได้ |
| AC2 | Staff มี permission operation:refund:update-sale-approved | สามารถเปลี่ยนสถานะเป็น SaleApproved ได้ |
| AC3 | Staff มี permission operation:refund:update-accounting-approved | สามารถเปลี่ยนสถานะเป็น AccountingApproved ได้ |
| AC4 | Staff มี permission operation:refund:update-refunded | สามารถเปลี่ยนสถานะเป็น Refunded ได้ |
| AC5 | ข้ามขั้นตอน เช่น Created → AccountingApproved | ระบบ reject (ต้องทำตามลำดับ) |
| AC6 | Refund ถูก Rejected แล้ว | ไม่สามารถเปลี่ยนสถานะต่อได้ |
| AC7 | Staff ไม่มี permission | ปุ่ม action ไม่แสดง, API ตอบ 403 |
Refund Status Workflow
Created
│
▼
SaleChecked ◄── sale team ตรวจสอบ (permission: update-sale-checked)
│
▼
SaleApproved ◄── sale team อนุมัติ (permission: update-sale-approved)
│
▼
AccountingApproved ◄── accounting อนุมัติ (permission: update-accounting-approved)
│
▼
Refunded ◄── บันทึกว่าโอนเงินแล้ว (permission: update-refunded)
ทุก stage สามารถ → Rejected
US-P05: Edit Booking ที่ต้องชำระเพิ่ม
As a ลูกค้า
I want to ชำระเงินส่วนต่างหลังแก้ไข booking
So that booking ถูก confirm กลับมาหลังชำระส่วนต่าง
Acceptance Criteria
| # | เงื่อนไข | ผลลัพธ์ที่คาดหวัง |
|---|---|---|
| AC1 | Edit booking และราคาใหม่สูงกว่าเดิม | Booking กลับเป็น Created พร้อม AdditionalAmount |
| AC2 | ลูกค้าชำระส่วนต่างสำเร็จ | Booking กลับเป็น Confirm |
| AC3 | ลูกค้าไม่ชำระส่วนต่างภายในเวลาที่กำหนด | Booking Expired |
| AC4 | Edit booking และราคาใหม่ต่ำกว่าเดิม | Booking ยังสถานะ Confirm มี RefundAmount รอดำเนินการ Backoffice |
Business Rule Summary — Payment & Refund
| ID | Rule | ค่า |
|---|---|---|
| BR-P-01 | Guest ชำระ booking ที่ไม่มี CreatedBy | ต้องไม่มี CreatedBy |
| BR-P-02 | Member ชำระ booking ที่ CreatedBy == customerId | ต้องตรงกัน |
| BR-P-03 | Booking expired เมื่อ status = Expired หรือ now >= ExpireAt | reject payment |
| BR-P-04 | Additional amount: ใช้ AdditionalAmount ถ้าบวก | ชำระแค่ส่วนต่าง |
| BR-P-05 | Non-card flow | CreateChargeWithSourceAsync → QR |
| BR-P-06 | Card save | Member ชำระด้วยบัตรใหม่ → save token |
| BR-P-07 | Webhook charge.complete + successful → Confirm booking | Omise callback |
| BR-P-08 | Refund allocation: ตัดจาก transaction เก่าก่อน | PaidAt ascending |
| BR-P-09 | Bank account required เมื่อ refund ผ่าน PromptPay/TrueMoney | ต้องกรอกก่อน |
| BR-P-10 | Refund Phase 1 = Manual Workflow | Auto refund ยังไม่มี |
| BR-P-11 | Refund ต้องทำตาม workflow ตามลำดับ | ข้ามขั้นตอนไม่ได้ |