SRS: UC_4.10.4 — Time Extension Purchase
UC_4.10.4: Time Extension Purchase
| Field | Value |
|---|---|
| BA in Charge | Huyen Dinh |
| Date Created | 2026-08-08 |
| Version | v1 |
| Document References | RFQ_ Stack Trading Prop Tech V7.pdf · RFQ_ Website and Dashboard Implementation V7.pdf (§Time Extension Module) · Zapier Integration V7.pdf (§Table J · §Global Variables Table) |
Document References
| # | Original Document | Key Sections Used |
|---|---|---|
| 1 | RFQ_ Website and Dashboard Implementation V7.pdf | §Time Extension Module — Trigger, UI, Action Flow (Path A / Path B), Modal UI (Headline, Line Items, Vaulted Card, CTA) |
| 2 | RFQ_ Stack Trading Prop Tech V7.pdf | §Scenario A —POST /purchase-time-extension endpoint |
| 3 | Zapier Integration V7.pdf | §Table J (Extension Fee per level: L1 $150, L2 $1,049, L5 $3,599) · §Global Variables Table (Challenge_Extension_Days = 30) · §Flow 19 |
1. Overview
| Field | Content |
|---|---|
| ID | UC_4.10.4 |
| Use Case | Time Extension Purchase |
| Description | An active SIM trader whose account has fewer than 10 days remaining can purchase a time extension (duration =Challenge_Extension_Days days from the Global Variables Table) to continue the evaluation. The UI trigger and "Extend Time" button behaviour are described in UC_4.7.4. Purchase flows through a 1-Click Confirmation Modal (Path A — vaulted card present) or Secure Checkout (Path B/C — no vaulted card or user changes payment method). |
| Zapier Flow | Flow 19 (Stagnation Monitor, SIM leg) — computes days_remaining from expiration_date and drives the 14/7/2/1/0-day warning email cascade; on expiry (days_remaining < 0) triggers Hard Breach + Flow 7B per BR_4.10.4.2 |
| Zapier Table | Table J (Extension Fee per level — L1: $150, L2: $1,049, L5: $3,599)Global Variables Table (Challenge_Extension_Days = 30) |
| 3rd Party | NMI (vaulted card charge)Quaderno (tax calculation via /calculate-cart)Compliance_geo_restrictions (sanctions check) |
2. Trigger
UI Trigger:
- User is in SIM evaluation (Status =
Active_SIM) days_remaining < 10- Visual trigger and "Extend Time" button behaviour → Ref: UC_4.7.4
User Action:
- Trader clicks the "Extend Time" button
3. Pre-conditions
- User Status =
Active_SIM(must be in active SIM evaluation) days_remaining < 10(less than 10 days remaining)- On the final day (
days_remaining = 0, same UTC date as expiration): extension is still purchasable - Once Status transitions away from
Active_SIM(e.g.,Failed,Promoting), extension is no longer available
4. Post-conditions
- Payment successful
POST /purchase-time-extension→new_expiration_date = current_expiration_date + Challenge_Extension_Dayscalendar days- Dashboard timer immediately updates to show new
days_remaining - Status remains
Active_SIM - No email, no Interstitial, Auth0 unchanged
- User continues trading without interruption
5. Basic Flow
Step 1 — Trader clicks "Extend Time":
- The "Extend Time" button is visible only when
days_remaining < 10 - On click → move to step 2.
Step 2 — Tax priming:
- FE calls
POST /calculate-cart→ Quaderno API calculates tax:billing_country + billing_region + user_ip → Tax. Final_Total = extension_fee (Table J, by level) + Tax.- Tax is primed before modal opens.
Step 3 — Time Extension Modal renders:
Wireframe reference:
Buy extension.png
Modal elements and layout → see §9.1 for full screen description.
Step 4 — Payment (Gate checks + 1-Click):
The system performs three gate checks in order:
Gate 1: Status = Active_SIM → Continue. If not → modal closes and display TE-DASH-01.
Gate 2: Sanctions — Query billing_country + billing_region from Users table → check Compliance_geo_restrictions → If match → display 403 screen, Ref: FP-02.
Gate 3: Check Users table for customer_vault_id (NMI):
- Present → Path A: 1-Click Purchase → Wireframe:
Buy extension.png - Not present → Path B: Secure Checkout Fallback → Wireframe:
Popup Time extension when users have not saved vaulted card info before.png
Path A — 1-Click Purchase (vaulted card present):
- User clicks CTA button → frontend calls
POST /purchase-time-extension. - Modal closes → Processing Payment popup displayed → Wireframe:
Processing payment modal.png - Success: Server adds
Challenge_Extension_Dayscalendar days tocurrent_expiration_date.days_remainingrecalculated → timer updates. → Brief success state: Wireframe:Buy extension successfully (1).png - Failure: Processing Payment popup closes → Time Extension modal re-displayed with banner error. User can retry or click "Change Payment Method" (Path C). See error handling → §6.
Path B — Secure Checkout Fallback (no vaulted card):
- System detects no
customer_vault_id→ returns UI Fallback payload. - Frontend displays Secure Checkout page (reuses Step 5 UI component from checkout funnel).
- Full payment flow (form validation, processing, success/failure handling, error recovery) → see §6 — Payment flow, all standard cases, citing UC_2.8.1 §5 directly.
Path C — Change Payment Method:
- User clicks "Change Payment Method" on the modal.
- System opens Secure Checkout page. Same flow as Path B. → Wireframe:
Change payment method.png
Step 5 — On successful payment:
POST /purchase-time-extensionprocesses the charge via NMI.- Server adds
Challenge_Extension_Dayscalendar days to the currentexpiration_date:new_expiration_date = current_expiration_date + Challenge_Extension_Days. days_remainingrecalculated → dashboard timer immediately updates.- Brief success popup displayed → modal closes. User continues trading without interruption. No re-login. Status unchanged.
6. Exceptional Flow
-
[Payment fails — vaulted card declined]
- CTA button click → Time Extension modal closes → Processing Payment popup displayed. NMI returns a decline code (hard decline, soft decline, insufficient funds, expired card, etc.) → Processing Payment popup closes → Time Extension modal re-displayed with a banner error message showing the failure reason. User can retry or click "Change Payment Method" (Path C).
- Same UX pattern as UC_4.10.3 §7 — Payment fails 1-Click Path A. No automatic fallback to Path B.
-
[Extension on final day (
days_remaining = 0, same UTC date as expiration)]- Extension is still purchasable — the user has the entire final day to purchase before the day rolls over.
- If the day rolls over with no extension purchased, Ref: BR_4.10.4.2 applies.
-
[Status no longer
Active_SIMat charge time]- If Status changed between modal open and CTA click (e.g., Flow 3 promotion, or the day-expiry Hard Breach in BR_4.10.4.2), the endpoint rejects the charge.
- Modal closes and display TE-DASH-01.
-
[Promo code — invalid, expired, or already redeemed]
- Same flow as UC_4.10.3 §6 Promo Code Flow.
- Server returns error → inline error below promo input field: Ref: IN-PROMO-02.
- Expanded section stays open for retry.
-
[Payment flow — all standard cases]
The 1-Click modal flow for Time Extension follows the same backend payment handling as the checkout flow. All cases (dedup check, card declines, 5-failure email lock, 10-minute timeout, post-payment restricted-region fail-safe, etc.) and their exact UX/reference are canonically documented in UC_4.10.3 §6a — Payment Flow, All Standard Cases.
7. Business Rules
BR_4.10.4.1: Frequency — Multiple Purchases Allowed
- Users can purchase time extension multiple times during a single SIM evaluation, with no frequency limit or cooldown.
- Condition to unlock purchase:
days_remaining < 10. - Each successful purchase adds
Challenge_Extension_Dayscalendar days tocurrent_expiration_dateat the time of purchase. - Example: user purchases at
days_remaining = 5→new_expiration_date = current_expiration_date + 30. If they later drop to< 10days remaining again, they can purchase again.
BR_4.10.4.2: Day Expires Without Extension Purchased — Hard Breach (Flow 19: Stagnation Monitor)
Flow 19: Stagnation Monitor
- Trigger: Scheduled Job (Database Filter/View).
- Query: Import only Traders where
expiration_date(SIM) orstagnation_deadline(LIVE) approaches (Days_Remaining= 14, 7, 2, 1, 0) ANDStatus != 'Admin_Pause'(International Exception). - Throttle: Add 1 second delay per loop to respect API Rate Limits.
- Logic Loop (Per Trader):
- Check: IF
Status == 'Promoting'→ SKIP (Race Condition Prevention). - Calculate:
Days_Remaining = expiration_date - Today(SIM) orstagnation_deadline - Today(LIVE). - Warning 1 (14 Days Left): IF
Days_Remaining == 14ANDCurrent_Profit < Profit_Target: SendStagnation_Warning_14_DaysEmail. - Warning 2 (7 Days Left): IF
Days_Remaining == 7ANDCurrent_Profit < Profit_Target: SendStagnation_Warning_7_DaysEmail. - Warning 3 (2 Days Left): IF
Days_Remaining == 2ANDCurrent_Profit < Profit_Target: SendStagnation_Warning_2_DaysEmail. - Warning 4 (1 Day Left): IF
Days_Remaining == 1ANDCurrent_Profit < Profit_Target: SendStagnation_Warning_1_DayEmail. - Final Warning (0 Days Left): Send Final Day Email. SIM users can still buy time extension on this day.
- Termination (
< 0Days Left): IFDays_Remaining < 0ANDCurrent_Profit < Profit_Target:- [LIVE] Defense Check: IF
Level >= 9ANDDefense_Used == False: SendStagnation_Defense_ActivatedEmail. Trigger Flow 4B Webhook. - [BOTH] ELSE: Send
Stagnation_TerminationEmail with Reason CodeSTAGNATION:- SIM: Trigger the same basic flow as Hard Breach (UC_4.10.2) — but do NOT send the
Challenge_Failedemail. (This email would normally be sent by Flow 7B Path A — Ref:Zapier_Flow_Full_Reference_v1.mdFlow 7B.) - LIVE: Trigger Flow 7 (Trader Failure/Stop Out).
- SIM: Trigger the same basic flow as Hard Breach (UC_4.10.2) — but do NOT send the
- [LIVE] Defense Check: IF
- Check: IF
Suppression rule: Zapier must suppress the native downstream emails (Drawdown_Defense_Activated_Initial and Live_Account_Closed or Challenge_Failed) when the incoming Reason Code is STAGNATION.
Klaviyo payload requirement: The marketing team needs to differentiate between a SIM user letting their evaluation expire, a LIVE user losing their funded account, and a Senior Trader entering Defense mode. The JSON payload sent to the Klaviyo API for BOTH the Stagnation_Defense_Activated and Stagnation_Termination events must include the user's status string (e.g. Active_Challenge vs Active_DMA) and current_level. This allows the marketing team to build conditional splits in Klaviyo and serve the proper email copy without SotaTek managing separate Zapier webhook events.
Note: All emails above (Stagnation_Warning_*, Stagnation_Defense_Activated, Stagnation_Termination) are sent via Klaviyo.
After this, control returns to UC_4.10.2 and proceeds via Reset or Start New Track, per UC_4.10.3.
8. Wireframe / UI
Wireframe references:
| Wireframe | File | Note |
|---|---|---|
| Time Extension Modal (1-Click, vaulted card) | References/Wireframe/Stage 2/Failure and Recovery/Buy extension.png | Extension-specific |
| No Vaulted Card — Secure Checkout (Extension) | References/Wireframe/Stage 2/Failure and Recovery/Popup Time extension when users have not saved vaulted card info before.png | Extension-specific |
| Buy Extension Successfully (Success popup) | References/Wireframe/Stage 2/Failure and Recovery/Buy extension successfully (1).png | Extension-specific |
| Change Payment Method | References/Wireframe/Stage 2/Failure and Recovery/Change payment method.png | Shared |
| Processing Payment | References/Wireframe/Stage 2/Failure and Recovery/Processing payment modal.png | Shared |
UX flow note: Unlike Hard Breach (Reset) where user clicks Hard Breach Popup CTA → tax calculation → modal opens: for Time Extension, user clicks "Extend Time" button on the dashboard → tax calculation fires immediately → Time Extension Modal opens directly.
9. Screen Description and Business Rules
9.1 Time Extension Modal (1-Click)
Wireframe reference:
Buy extension.png
| No. | Element | Type | Validation Rule / Behaviour |
|---|---|---|---|
| 1 | Days Remaining Display (in modal) | Label | Display rule:- Displays remaining time in the same format as the dashboard counter (e.g., "9 days, 9h remaining")- Format and display logic → Ref: UC_4.7.4Behaviour:- Dynamic: value sourced from days_remaining at modal open time |
| 2 | Modal Headline | Label | Display rule:- "Keep Your Progress. Add [Challenge_Extension_Days] Days."- Day-count pluralization: displays "1 Day" when the value is exactly 1; displays "X Days" when the value is greater than 1 (default Challenge_Extension_Days = 30 → "Add 30 Days.")Behaviour:- Dynamic copy — number of days reflects current Challenge_Extension_Days value |
| 3 | Promo Code — "Have a promo code?" | Expandable Text Link | Display rule:- Text link "Have a promo code?" positioned directly above the line items / price breakdown- Defaults to collapsed on modal open- On click → expands to show input + "Apply" button; input placeholder: "Enter promo code"Behaviour:- Same flow as UC_4.10.3 §6 Promo Code Flow |
| 4 | Line Items (Price Breakdown) | Label | Display rule:- Extension Fee line item: "Time Extension ([Challenge_Extension_Days] Day(s))" — same pluralization rule as row 2 (e.g. "Time Extension (30 days)")- Tax line item: "Tax ([billing_region])" — region/country name is dynamic per the user'sbilling_region - Promo line item: "Promo ([promo_code])" showing the discount as a negative amount — only rendered when a promo code has been successfully applied; not shown otherwise- Total line item: "Total"- All monetary values follow CR-11 Numeric/Monetary Value DisplayBehaviour:- extension_fee from Table J (by level): L1 $150, L2 $1,049, L5 $3,599. Tax added by Quaderno via billing_country + billing_region + user_ip. Final_Total = extension_fee + Tax − Promo discount (if applied).- Promo line item is added/removed dynamically as the promo is applied/removed via UC_4.10.3 §6 Promo Code Flow |
| 5 | Vaulted Card Display | Label (read-only) | Display rule:- Format: "[Brand] ending in [last 4 digits]" — no "Billed to" prefix- Card brand follows UC_4.10.3 BR_4.10.3.8- Example: "Visa ending in 4821"Behaviour:- Sourced from NMI Customer Vault via customer_vault_id |
| 6 | "Change Payment Method" Link | Link | Behaviour:- On click → Secure Checkout (Path C)- Ref: UC_4.10.3 §5.1 Path C |
| 7 | Primary CTA Button | Button (Primary) | Display rule:- Label: "Add [Challenge_Extension_Days] Days Now ($[Final_Total])" — e.g. "Add 30 Days Now ($162.38)" / with promo applied "Add 30 Days Now ($152.38)"- Both Challenge_Extension_Days and Final_Total are dynamic; Final_Total follows CR-11Behaviour:- On click → modal closes → Processing Payment popup displays- On success → Buy extension successfully (1).png popup shown → closes- On failure → Processing popup closes → modal re-displayed with banner error |
| 8 | "Cancel" Button | Button (Secondary) | Behaviour:- On click → modal closes; user returns to Dashboard- No unsaved-changes warning (modal contains no data-entry form state requiring confirmation) |
9.2 Payment Flow States
| State | Wireframe | Note |
|---|---|---|
| Time Extension Modal — Default | Buy extension.png | Extension-specific |
| Time Extension Modal — Promo Applied | Buy extension - Promo code applied.png | Extension-specific |
| Loading / Processing | Processing payment modal.png | Shared |
| Payment Success | Buy extension successfully (1).png | Extension-specific |
| No Vaulted Card | Popup Time extension when users have not saved vaulted card info before.png | Extension-specific |
| Change Payment Method | Change payment method.png | Shared |
9.3 Buy Extension Successfully Modal
Wireframe reference:
Buy extension successfully (1).png
| No. | Element | Type | Validation Rule / Behaviour |
|---|---|---|---|
| 1 | Success Icon | Icon | Display rule:- Checkmark iconBehaviour:- Static |
| 2 | Header | Label | Display rule:- "Extension Purchase Complete"Behaviour:- Fixed text |
| 3 | Body Text | Label | Display rule:- "30 days have been added to complete the Associate Track."- Sourced dynamically from Challenge_Extension_Days)Behaviour:- Renders immediately after POST /purchase-time-extension succeeds |
| 4 | "Return to Dashboard" | Button (Primary) | Behaviour:- On click → popup closes → user returns to Dashboard with updated days_remaining counter |
9.4 Processing Payment Modal
Wireframe reference:
Processing payment modal.png
| No. | Element | Type | Validation Rule / Behaviour |
|---|---|---|---|
| 1 | Circular Loading Spinner | Loading Indicator | Display rule:- Circular spinner, gold/cream colorBehaviour:- Animates continuously while awaiting NMI response- No close/cancel affordance; user cannot dismiss this popup |
| 2 | Header | Label | Display rule:- Fixed text: "Processing payment..."Behaviour:- Static |
| 3 | Subtext | Label | Display rule:- "Charging [Brand] ending in [last 4]" — e.g. "Charging Visa ending in 4821"- [Brand] and [last 4] sourced dynamically from the vaulted card in use (same card as displayed in §9.1 Row 5)Behaviour:- Dynamic per session |
Changelog
| Date | Version | Updated item | Before | After | Notes |
|---|---|---|---|---|---|
| 2026-08-08 | v1 | Initial creation | — | Full SRS with 6 BRs, Basic Flow, Exceptional Flow, Screen Description + Dynamic/Fixed Field Classification, Promo Code, Apple Pay / Google Pay card variants, Payment pattern reference to UC_4.9.3 | Init Flow Step 3: Architect |
| 2026-08-17 | v1.1 | Named Flow 19 as the source of days_remaining/warning emails; tightened BR_4.10.4.2 threshold and added Challenge_Failed suppression exception | v1 content | §1 Zapier Flow, §2 Trigger, §7 BR_4.10.4.2 updated to cite Flow 19 (expiration_date, days_remaining < 0) and note Challenge_Failed is not sent on this path | QnA FR-52 |