Checkout
- Item
- Data Engineering 101
- Details
- 1-year access (12 months)
- Amount
- ₭ 1,500,000
- Reference No.
BIL-20260527-000128
- Tap "Next" — the system creates a payment record and requests a QR code or link from BCEL.
- Desktop: a QR code dialog overlays this page — scan it with the BCEL One app.
- Mobile: the system opens the BCEL One app directly.
- Confirm the payment in the app.
- The system receives the result automatically (server-to-server) — no need to refresh.
- The request auto-expires after 5 minutes if no confirmation is received.
Screen Flow (SUC-BIL-01):
📝 Spec notes — SUC-BIL-01 / SCR-BIL.02 Checkout (fields, actions, requirements, returnUrl context)
- Screen: SCR-BIL.02 — Checkout (page)
- Sub-use case: SUC-BIL-01 — Checkout via BCEL
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).
item_name"Item" — text · param.item_name · always · read-only · data_ref: Product.nameitem_qty_label"Details" — text · param.item_qty_label · visible when item_qty_label != "" · read-onlyamount"Amount" — number · param.amount · always · read-only · data_ref: Transaction.amountref_code"Reference No." — text · param.ref_code · always · read-only · data_ref: Transaction.ref_codebilling_name"Invoice Name" — text · default: BillingProfile.name · max_length 255 · required · data_ref: BillingProfile.namebilling_address"Address" — textarea · default: BillingProfile.address · max_length 500 · required · data_ref: BillingProfile.addressbilling_country_code"Country Code" — text · literal +856 · always · read-onlybilling_contact"Contact Number" — phone · default: BillingProfile.contact · format: phone · required · data_ref: BillingProfile.contactpayment_gateway_label"Payment Method" — text · literal: BCEL · always · read-only · data_ref: Transaction.gatewaygateway_error_message"Error Message" — text · visible when gateway_error_message != "" · read-only (AF 5A error display)
next"Next" — enabled when required_fields_valid · triggers SUC-BIL-01-M-4cancel"Cancel" — always enabled · triggers SUC-BIL-01-A-4A-1retry_after_gateway_error"Try again" — enabled when gateway_error_message != "" · triggers SUC-BIL-01-A-5A-3
- Supports concurrent checkout sessions per NFR-01 (100–200 peak) [refs: NFR-01]
- Works on connection ≤1 Mbps (no heavy assets, lazy validate) [refs: NFR-02]
- 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]
- Responsive (desktop/laptop/tablet/smartphone) — single column on mobile [refs: NFR-08]
- WCAG — label-input pair, error inline under input, required indicator (*), focus order, "Next" keyboard-accessible [refs: NFR-09]
- 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]
- No partial-success state — gateway error (AF 5A) must show clear retry/cancel; BillingProfile snapshot saved and Transaction status=failed (not stuck pending) [refs: —]
- 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: —]
- Current returnUrl: /purchase-course/subscription-success
- Fallback when empty:
/ - On success → Subscription Success page:
/checkout-via-bcel/subscription-success?returnUrl=%2Fpurchase-course%2Fsubscription-success - Cancel target:
/purchase-course/subscription-success?sub_cancelled=1(appendssub_cancelled=1) - Propagation:
?returnUrl=…carried on all SUC nav links
- 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
- 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
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).
item_name"Item" — text · lookup Transaction.product_id · always · read-only · data_ref: Product.nameamount"Amount" — number · lookup Transaction.amount · always · read-only · data_ref: Transaction.amountref_code"Reference No." — text · lookup Transaction.ref_code · always · read-only · data_ref: Transaction.ref_codeqr_payload"BCEL QR" — text · computed · always · read-onlycountdown_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-onlydecline_reason"Decline reason" — text · visible when status == declined · read-only
cancel"Cancel" — enabled when status == waiting · triggers SUC-BIL-01-A-6A-1retry"Try again" — enabled when status == declined or status == expired · triggers SUC-BIL-01-A-9B-3, SUC-BIL-01-A-6B-3back_to_checkout"Back to edit" — enabled when status == declined · triggers SUC-BIL-01-A-9B-3close"Close" — enabled when status == expired · triggers SUC-BIL-01-A-6B-3 (user selects Cancel instead of Retry — sends failure signal to caller)
- Supports concurrent payment-wait sessions per NFR-01 (100–200 peak) [refs: NFR-01]
- Works on connection ≤1 Mbps — lightweight QR image (SVG/PNG ≤10KB), 3-second polling [refs: NFR-02]
- 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]
- Dialog renders desktop only — mobile deep-links to BCEL One; device classified at SUC step 6 (tablet = desktop) [refs: —]
- Responsive modal — not full-screen on large viewports, scrollable on small viewports [refs: NFR-08]
- WCAG — modal focus trap + ESC = Cancel; countdown aria-live="polite"; QR has alt + ref_code text fallback; all buttons keyboard-accessible [refs: NFR-09]
- 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]
- 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]
- 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]
- 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]