Sub-flow › Checkout via BCEL › Checkout

Checkout

Order Summary
Item
Data Engineering 101
Details
1-year access (12 months)
Amount
₭ 1,500,000
Reference No.
BIL-20260527-000128
Billing Information
+856
Payment Method
BCEL
BCEL One
Scan QR code (desktop) or open the BCEL One app directly (mobile)
Cancel
How to pay with BCEL
  1. Tap "Next" — the system creates a payment record and requests a QR code or link from BCEL.
  2. Desktop: a QR code dialog overlays this page — scan it with the BCEL One app.
  3. Mobile: the system opens the BCEL One app directly.
  4. Confirm the payment in the app.
  5. The system receives the result automatically (server-to-server) — no need to refresh.
  6. The request auto-expires after 5 minutes if no confirmation is received.

Screen Flow (SUC-BIL-01):

→ Mobile: paid in BCEL One app → Subscription Success → Cancel at QR (AF 6A)
📝 Spec notes — SUC-BIL-01 / SCR-BIL.02 Checkout (fields, actions, requirements, returnUrl context)
Identity
  • Screen: SCR-BIL.02 — Checkout (page)
  • Sub-use case: SUC-BIL-01 — Checkout via BCEL
Brief description

Checkout screen of SUC-BIL-01 — shows the order summary (read-only, pre-computed by the caller before invoking the SUC), then lets the user review/edit billing info (name/address/contact) pre-filled from the latest BillingProfile snapshot. "Next" saves a new BillingProfile snapshot, creates a Transaction (status: pending), and branches by device — desktop shows the QR code dialog (SCR-BIL.06) overlaying this page; mobile deep-links to the BCEL One app. "Cancel" returns a failure signal to the caller. No payment-channel selector in v1 (BCEL only, shown as static info).

Fields
  • item_name "Item" — text · param.item_name · always · read-only · data_ref: Product.name
  • item_qty_label "Details" — text · param.item_qty_label · visible when item_qty_label != "" · read-only
  • amount "Amount" — number · param.amount · always · read-only · data_ref: Transaction.amount
  • ref_code "Reference No." — text · param.ref_code · always · read-only · data_ref: Transaction.ref_code
  • billing_name "Invoice Name" — text · default: BillingProfile.name · max_length 255 · required · data_ref: BillingProfile.name
  • billing_address "Address" — textarea · default: BillingProfile.address · max_length 500 · required · data_ref: BillingProfile.address
  • billing_country_code "Country Code" — text · literal +856 · always · read-only
  • billing_contact "Contact Number" — phone · default: BillingProfile.contact · format: phone · required · data_ref: BillingProfile.contact
  • payment_gateway_label "Payment Method" — text · literal: BCEL · always · read-only · data_ref: Transaction.gateway
  • gateway_error_message "Error Message" — text · visible when gateway_error_message != "" · read-only (AF 5A error display)
Actions
  • next "Next" — enabled when required_fields_valid · triggers SUC-BIL-01-M-4
  • cancel "Cancel" — always enabled · triggers SUC-BIL-01-A-4A-1
  • retry_after_gateway_error "Try again" — enabled when gateway_error_message != "" · triggers SUC-BIL-01-A-5A-3
Additional requirements
  1. Supports concurrent checkout sessions per NFR-01 (100–200 peak) [refs: NFR-01]
  2. Works on connection ≤1 Mbps (no heavy assets, lazy validate) [refs: NFR-02]
  3. TLS 1.2+ on every request; never store payment credentials or card data on screen/client; BillingProfile encrypted at rest (AES-256) [refs: NFR-03]
  4. Responsive (desktop/laptop/tablet/smartphone) — single column on mobile [refs: NFR-08]
  5. WCAG — label-input pair, error inline under input, required indicator (*), focus order, "Next" keyboard-accessible [refs: NFR-09]
  6. The "Next" button disables immediately after click to prevent an accidental double submission; the system creates one payment record with a unique reference code, so a resubmitted request cannot create a duplicate. [refs: SS-BIL.02]
  7. No partial-success state — gateway error (AF 5A) must show clear retry/cancel; BillingProfile snapshot saved and Transaction status=failed (not stuck pending) [refs: —]
  8. Order summary must be read-only (params from caller); to change qty/product the user goes back to SCR-BIL.04 or SCR-BIL.01 [refs: —]
ReturnUrl context
  • Current returnUrl: /renew-subscription/
  • Fallback when empty: /
  • On success → Subscription Success page: /checkout-via-bcel/subscription-success?returnUrl=%2Frenew-subscription%2F
  • Cancel target: /renew-subscription/?sub_cancelled=1 (appends sub_cancelled=1)
  • Propagation: ?returnUrl=… carried on all SUC nav links
Sub-flow context
  • SUC ID: SUC-BIL-01 · SUC Name: Checkout via BCEL
  • Invoked by: UC-BIL.01 (Purchase Course), UC-BIL.03 (Renew Subscription)
📝 Spec notes — SUC-BIL-01 / SCR-BIL.06 Payment QR Dialog (dialog) — fields, actions, requirements
Identity
  • Screen: SCR-BIL.06 — Payment QR Dialog (type: dialog — modal embedded in parent Checkout)
  • Parent state (flow.puml): Checkout → QrDialog
  • Sub-use case: SUC-BIL-01 — Checkout via BCEL
Brief description

Modal dialog of SUC-BIL-01 overlaying the Checkout page after the user proceeds on desktop — shows a BCEL QR code + scan instructions for the BCEL One app + a 5-minute countdown + a read-only order summary. The system waits for a server-to-server result, then transitions: callback verified success → close dialog + SUC returns a success signal to the caller (UC-BIL.01-M-9 / UC-BIL.03-M-10); signature reject (AF 9A) → no change, await next callback; payment failed (AF 9B) → error + Try again / Back to edit; timeout 5 min (AF 6B) → error + Try again (new QR) / Close (failure signal to caller); cancel (AF 6A) → failure signal. Mobile does not show this dialog — it deep-links to the BCEL One app directly (device classification at SUC step 6, caller side).

Fields
  • item_name "Item" — text · lookup Transaction.product_id · always · read-only · data_ref: Product.name
  • amount "Amount" — number · lookup Transaction.amount · always · read-only · data_ref: Transaction.amount
  • ref_code "Reference No." — text · lookup Transaction.ref_code · always · read-only · data_ref: Transaction.ref_code
  • qr_payload "BCEL QR" — text · computed · always · read-only
  • countdown_seconds "Time remaining" — number · literal 300 · always · read-only (aria-live="polite")
  • status "Status" — enum [waiting, declined, expired, cancelled] · literal waiting · data_ref: Transaction.status · read-only
  • decline_reason "Decline reason" — text · visible when status == declined · read-only
Actions
  • cancel "Cancel" — enabled when status == waiting · triggers SUC-BIL-01-A-6A-1
  • retry "Try again" — enabled when status == declined or status == expired · triggers SUC-BIL-01-A-9B-3, SUC-BIL-01-A-6B-3
  • back_to_checkout "Back to edit" — enabled when status == declined · triggers SUC-BIL-01-A-9B-3
  • close "Close" — enabled when status == expired · triggers SUC-BIL-01-A-6B-3 (user selects Cancel instead of Retry — sends failure signal to caller)
Additional requirements
  1. Supports concurrent payment-wait sessions per NFR-01 (100–200 peak) [refs: NFR-01]
  2. Works on connection ≤1 Mbps — lightweight QR image (SVG/PNG ≤10KB), 3-second polling [refs: NFR-02]
  3. TLS 1.2+ on every request (incl. polling + gateway callback verify); QR payload bound to ref_code — no leak across users [refs: NFR-03, SS-BIL.01]
  4. Dialog renders desktop only — mobile deep-links to BCEL One; device classified at SUC step 6 (tablet = desktop) [refs: —]
  5. Responsive modal — not full-screen on large viewports, scrollable on small viewports [refs: NFR-08]
  6. WCAG — modal focus trap + ESC = Cancel; countdown aria-live="polite"; QR has alt + ref_code text fallback; all buttons keyboard-accessible [refs: NFR-09]
  7. A duplicate payment confirmation (e.g. the gateway resending the same result) must not create a second payment record — the learner keeps seeing one consistent status, with no flicker. [refs: SS-BIL.02]
  8. Every step of the payment wait (QR generated, confirmation received, success, declined, signature rejected, cancelled, expired) is recorded in the audit log with its reference code, timestamp, and signature-verification result. [refs: NFR-11, SS-BIL.01]
  9. A pending payment expires after 5 minutes if no successful confirmation is received; the on-screen countdown is only a visual guide — the system's own timeout check is what actually determines expiry. [refs: SS-BIL.02]
  10. If the learner closes the browser or dialog while waiting, the payment confirmation is still processed normally when it arrives; if the learner comes back and reloads the Checkout page, the system looks up the payment by its reference code and shows the current status (pending/success/failed). [refs: SS-BIL.02]