# Subscription Upgrade Implementation Plan ## 1. Purpose Implement a safe subscription-upgrade workflow for companies that already have an active plan and pay manually. The workflow must calculate a defensible upgrade amount, collect payment evidence, require platform-admin verification, activate the new plan without losing history, and communicate in the company's selected language: English (`en`), French (`fr`), or Arabic (`ar`). ## 2. Product Policy ### 2.1 Supported upgrade options | Option | Billing treatment | Activation | Renewal date | Availability | |---|---|---|---|---| | Upgrade now — prorated | Charge the net price difference for the unused part of the current term | After payment verification | Unchanged | Primary self-service option | | Upgrade at renewal | No immediate charge; schedule the new plan for the next term | At renewal | Existing renewal date | Secondary self-service option | | Upgrade now — reset term | Credit eligible unused current-plan value and begin a full new term | After payment verification | Reset to activation date | Platform-admin assisted | | Temporary add-on | Keep the current plan and charge for a defined feature or limit | After payment verification | Unchanged | Out of scope until add-on rules exist | ### 2.2 Mandatory rules 1. A company may upgrade only from an active or grace-period subscription to a higher eligible plan. 2. A submitted receipt is evidence, not proof of cleared payment. 3. Upgraded access is granted only after a platform admin verifies payment. 4. The existing subscription record and plan history must never be overwritten or deleted. 5. Only one non-terminal upgrade request may exist per subscription. 6. An issued quote is immutable. If it expires, create a new quote using current inputs. 7. Upgrades, renewals, downgrades, add-ons, and plan changes made by platform admins must use distinct actions and audit events. 8. A term reset requires explicit company confirmation because it changes the renewal date. 9. Only company billing contacts and company admins receive customer payment communications. Do not notify every company user. 10. All customer-facing communication uses the company's selected supported language. Arabic content must render in true RTL. ## 3. Scope ### In scope - Immediate prorated upgrade. - Upgrade scheduled for renewal. - Manual payment instructions and receipt upload. - Platform-admin review, approval, rejection, and request-for-correction actions. - Plan-entitlement activation after verified payment. - EN, FR, and AR user interfaces and notifications. - Quote expiry, cancellation, audit history, idempotency, and reporting. - Migration-safe data changes and automated/manual testing. ### Out of scope for the first release - Automatic card or bank collection. - Automatic refunds or cash credits. - Downgrades. - Temporary add-ons unless pricing, duration, renewal, and removal behavior are separately defined. - Mid-upgrade currency changes. - Self-service term resets. - Changing the billing cadence during a standard prorated upgrade. ## 4. Roles and Permissions | Action | Company user | Billing contact | Company admin | Platform admin | |---|---:|---:|---:|---:| | View current plan and available upgrades | Optional | Yes | Yes | Yes | | Request an upgrade | No | Yes | Yes | Yes, on behalf of company | | View quote and payment instructions | No | Yes | Yes | Yes | | Upload or replace payment evidence | No | Yes | Yes | Yes | | Cancel an unpaid request | No | Yes | Yes | Yes | | Verify or reject payment | No | No | No | Yes | | Activate an upgrade | No | No | No | System after verification | | Override price, eligibility, or effective date | No | No | No | Separate privileged permission plus reason | | View internal review notes | No | No | No | Yes | Enforce permissions on the server. Hiding a control in the UI is not authorization. ## 5. Upgrade Eligibility The eligibility service must check all of the following before showing or quoting a target plan: - The subscription exists and belongs to the requesting company. - Subscription status is `active` or an explicitly supported `grace_period` state. - The target plan is active, sellable, and ranked above the current plan. - Current and target plans use the same currency and billing cadence for the standard prorated path. - The target plan supports the company's region and contractual conditions. - No non-terminal upgrade request already exists. - The subscription is not suspended for fraud, legal, or administrative reasons. - There is enough time remaining to justify an immediate upgrade under the configured minimum-charge policy. - Any company-specific contract or negotiated pricing is supported by the quote engine; otherwise route to platform-admin assistance. Never infer that a more expensive plan is necessarily an upgrade. Use an explicit plan transition matrix or plan rank plus compatibility rules. ## 6. Pricing and Proration ### 6.1 Standard formula Use day-based proration for a same-cadence immediate upgrade: ```text term_days = renewal_date - term_start_date remaining_days = renewal_date - effective_date remaining_new_plan_value = target_net_term_price × remaining_days ÷ term_days remaining_current_plan_credit = eligible_current_net_term_price × remaining_days ÷ term_days upgrade_subtotal = max(0, remaining_new_plan_value - remaining_current_plan_credit) upgrade_total = upgrade_subtotal + taxes + explicit_fees ``` For a current annual price of $1,200, a target annual price of $2,400, and exactly half the term remaining: ```text ($2,400 - $1,200) × 0.5 = $600 ``` ### 6.2 Pricing rules - Store money as integer minor units and use a fixed decimal/rounding policy. Never use binary floating point for billing. - Use the subscription's actual eligible net price, not merely the current public catalog price. - Decide explicitly whether current-plan discounts carry to the new plan. Default: they do not carry unless contractually marked transferable. - Calculate tax using the legally required point and method. Do not assume the simple price difference is tax-complete. - Define whether the effective date is the quote date, payment-received date, or approval date. Recommended: approval timestamp, with the quote calculated through a clearly stated valid-until date and recalculated if expired. - If partial-day precision is unnecessary, use the company's billing timezone and calendar dates consistently. Do not mix UTC timestamps with local-date proration. - Set a minimum charge and rounding rule per currency. If the calculated charge is below the minimum, offer upgrade at renewal or allow a documented admin waiver. - Store every pricing input and output in the quote snapshot so later catalog or tax changes cannot rewrite history. ### 6.3 Quote contents Each quote must show and store: - Current plan and target plan. - Current term start and renewal date. - Proposed activation method. - Proration dates and fraction. - Current-plan eligible credit. - Target-plan remaining value. - Discounts, taxes, fees, subtotal, and total. - Currency and rounding result. - Quote creation and expiry timestamps. - Price-list, tax-rule, and calculation-version identifiers. - Terms accepted by the requesting user. ## 7. State Model ### 7.1 Upgrade request states | State | Meaning | Allowed next states | |---|---|---| | `draft` | Target plan selected; quote not submitted | `quoted`, `cancelled` | | `quoted` | Immutable quote issued | `payment_pending`, `expired`, `cancelled` | | `payment_pending` | Company accepted quote and received payment instructions | `payment_review`, `expired`, `cancelled` | | `payment_review` | Evidence submitted for platform review | `correction_required`, `approved`, `rejected`, `expired` | | `correction_required` | Evidence or payment details need correction | `payment_review`, `expired`, `cancelled` | | `approved` | Payment verified and activation transaction authorized | `activated`, `activation_failed` | | `activated` | New plan and entitlements are active | Terminal | | `scheduled` | Upgrade is scheduled for renewal | `activated`, `cancelled`, `superseded` | | `rejected` | Payment or request rejected | Terminal | | `expired` | Quote/request validity elapsed | Terminal; requote creates a new request | | `cancelled` | Cancelled before activation | Terminal | | `activation_failed` | Payment approved but activation did not complete | `activated` through idempotent retry or manual incident resolution | | `superseded` | Scheduled request replaced by a newer valid request | Terminal | Do not encode upgrade progress as the primary subscription status. Keep the subscription active on its existing plan while the upgrade request is pending. ### 7.2 Transition controls - Every transition must validate the current state to prevent stale or duplicate actions. - Approval and activation must be idempotent. - Only the activation transaction may change the active plan and entitlements. - `approved` must not become `rejected`; use a reversal/incident process if an approval was mistaken. - Scheduled upgrades must be revalidated before renewal activation. ## 8. Data Model ### 8.1 `subscription_upgrade_requests` - `id` - `company_id` - `subscription_id` - `request_type` (`immediate_prorated`, `at_renewal`, `term_reset`) - `from_plan_id`, `from_plan_version_id` - `to_plan_id`, `to_plan_version_id` - `status` - `requested_by_user_id` - `requested_at` - `effective_at` - `scheduled_for` - `quote_id` - `company_language_snapshot` - `billing_timezone_snapshot` - `accepted_terms_version` - `accepted_at` - `cancelled_at`, `cancelled_by`, `cancellation_reason` - `expires_at` - optimistic-lock/version column - created/updated timestamps ### 8.2 `subscription_upgrade_quotes` - `id`, `upgrade_request_id` - currency - current eligible net term price - target net term price - term start, renewal date, pricing effective date - term days, remaining days, proration numerator/denominator - current credit, target remaining value - discounts, taxes, fees, subtotal, total in minor units - calculation version and serialized input snapshot - created/expired timestamps - immutable hash or integrity field if supported ### 8.3 Payment evidence and review Reuse the existing manual-payment model when possible. It must relate evidence to the exact upgrade request and quote, and record: - Evidence file metadata and secure storage reference. - Amount claimed, currency, payment method, reference number, and claimed payment date. - Uploader and upload time. - Review status, reviewer, review time, internal notes, and rejection/correction reason. - Verified amount and currency. - Malware-scan status and access controls. Do not store sensitive full bank-account or payment-card data in notes or uploaded filenames. ### 8.4 `subscription_plan_history` - subscription, company, previous plan/version, new plan/version - change type (`upgrade`) - source upgrade request and approved payment references - effective timestamp - previous and new term dates - actor and approving platform admin - entitlement snapshot or version references - audit correlation ID ### 8.5 Constraints and indexes - Partial unique constraint: one non-terminal upgrade request per subscription. - Unique activation key per upgrade request. - Foreign keys to company, subscription, immutable plan versions, quote, and payment review. - Indexes for company/status, subscription/status, expiry, scheduled activation, and admin review queue. ## 9. Backend Services and API ### 9.1 Services - Eligibility service: returns permitted plan transitions and reasons for ineligibility. - Quote service: calculates and freezes price snapshots. - Upgrade workflow service: validates transitions and authorization. - Manual-payment service: issues instructions and manages evidence/review. - Activation service: atomically changes plan history, active plan/version, entitlements, and audit events. - Notification service: selects recipients, language, template, and channel. - Expiry/scheduling worker: expires quotes and activates renewal-scheduled upgrades safely. ### 9.2 Suggested endpoints ```text GET /api/subscriptions/{id}/upgrade-options POST /api/subscriptions/{id}/upgrade-quotes POST /api/upgrade-requests/{id}/accept POST /api/upgrade-requests/{id}/payment-evidence POST /api/upgrade-requests/{id}/cancel GET /api/upgrade-requests/{id} GET /api/admin/upgrade-requests?status=payment_review POST /api/admin/upgrade-requests/{id}/request-correction POST /api/admin/upgrade-requests/{id}/approve-payment POST /api/admin/upgrade-requests/{id}/reject-payment POST /api/admin/upgrade-requests/{id}/retry-activation ``` Mutation endpoints must accept an idempotency key and reject stale version numbers. ### 9.3 Activation transaction In one database transaction or equivalent consistency boundary: 1. Lock the upgrade request and subscription. 2. Confirm request state is `approved` and not already activated. 3. Recheck company/subscription ownership and target-plan validity. 4. Write the subscription plan-history record. 5. Set the active immutable plan version. 6. Apply new entitlements and limits. 7. Preserve or reset term dates according to the accepted request type. 8. Mark the request `activated`. 9. Write audit/outbox events. Send notifications after commit through an outbox/queue. A failed email must not roll back an activated subscription. ## 10. User Experience ### 10.1 Company flow 1. Billing contact/admin opens Billing > Current plan. 2. System displays eligible higher plans and a comparison of entitlements and limits. 3. User selects `Upgrade now` or `Upgrade at renewal`. 4. System displays a transparent quote, effective-date policy, renewal-date effect, and expiry. 5. User accepts the upgrade terms. 6. For immediate upgrade, system displays localized manual-payment instructions. 7. User uploads payment evidence and can see review status. 8. Platform admin reviews payment. 9. After approval, the system activates the plan and displays confirmation, effective date, next renewal date, and new limits. The confirmation screen must not imply immediate access before payment verification. ### 10.2 Platform-admin flow - Review queue sorted by aging and quote expiry risk. - Company, current/target plan, quote breakdown, claimed and expected payment, evidence, and previous attempts visible in one view. - Actions: approve, request correction, reject, or open company/contact record. - Approval requires confirmation of amount, currency, cleared-payment reference, and reviewer identity. - Overrides require elevated permission and a mandatory reason. - Approved-but-failed activations appear in a separate incident queue and must not invite a second approval. ### 10.3 Accessibility and responsiveness - Keyboard-accessible comparison, forms, uploads, dialogs, and admin actions. - Visible focus, descriptive validation, status announcements, and sufficient contrast. - Do not encode status by color alone. - Responsive layouts for company and admin flows. - Currency, dates, and numbers formatted for the selected locale while stored canonically. ## 11. Localization and Notifications ### 11.1 Language selection - Resolve the company's selected communication language at request creation and store a snapshot. - Use only `en`, `fr`, or `ar`; define a deliberate fallback, recommended `en`, and log missing translations. - Use company language for shared company communications. If future requirements demand per-recipient language, make that a separate policy decision. - Arabic templates, PDFs, email layouts, and in-app screens must use RTL layout, not merely translated strings. ### 11.2 Notification events | Event | Company billing contact/admin | Platform admin | |---|---|---| | Quote created | In-app + email | Optional in-app | | Payment instructions issued | In-app + email | Optional in-app | | Evidence submitted | In-app confirmation | In-app queue notification | | Correction required | In-app + email | In-app status | | Payment rejected | In-app + email | In-app status | | Upgrade activated | In-app + email | In-app confirmation | | Quote/request expiring soon | In-app + email | In-app for aging requests | | Quote/request expired | In-app + email | In-app status | | Activation failed after approval | Generic processing message only | Urgent in-app + operational alert | Templates must include company name, current and target plans, amount/currency where appropriate, quote expiry, effective-date rule, renewal-date effect, and a safe deep link. Never attach raw payment evidence to email. ## 12. Limits and Entitlements Define behavior for every entitlement before implementation: - Higher quantitative limits become available only at activation. - Feature flags follow the immutable target plan version. - Existing usage is preserved. - No destructive data migration may occur during an upgrade. - Cache invalidation must occur immediately after commit. - Active sessions must receive updated authorization without requiring an unsafe manual workaround. - If entitlement propagation fails, mark activation as failed or partially failed according to a defined recovery policy; do not silently claim success. ## 13. Expiry, Cancellation, and Recovery - Recommended quote validity: configurable, initially 7 calendar days, capped by renewal date. - Send an expiry warning 48 hours before expiry when the request is still actionable. - Expiry workers must be idempotent and must not expire an already approved request. - Company cancellation is allowed only before approval/activation and does not delete history or evidence. - Requoting creates a new request or quote version linked to the expired one; it never mutates the expired quote. - A rejected payment cannot be reopened silently. Start a new review attempt or request according to the audit policy. - Activation retry uses the same activation key and cannot create duplicate plan history. - Define an operational reversal procedure for mistaken approval; do not improvise a downgrade through the upgrade endpoint. ## 14. Security and Audit - Validate company tenancy on every company endpoint. - Apply least-privilege permissions to payment evidence and internal notes. - Restrict file type and size; scan uploads before review/download. - Use signed, short-lived file access rather than public URLs. - Rate-limit quote generation, evidence uploads, and admin mutations. - Record actor, timestamp, previous state, next state, reason, IP/session metadata where permitted, and correlation ID for every material action. - Make financial/audit records append-only where practical. - Redact sensitive data from logs, analytics, and notification payloads. - Define retention rules for evidence and audit data according to applicable law and contract requirements. ## 15. Observability and Reporting Track at minimum: - Upgrade options viewed. - Quote creation, acceptance, expiry, and cancellation rates. - Payment-review turnaround time. - Correction and rejection reasons. - Approval-to-activation latency. - Activation failures and retries. - Upgrade conversion by current plan, target plan, cadence, language, and company segment. - Incremental recurring/contract value, calculated from plan history rather than UI analytics. Alert on approved requests stuck before activation, repeated worker failures, duplicate-transition errors, unusual override volume, and notification failure spikes. ## 16. Testing Strategy ### 16.1 Unit tests - Full, half, one-day, leap-year, and boundary-date proration. - Currency rounding and minimum-charge rules. - Discounts, taxes, fees, and negotiated-price eligibility. - Valid and invalid plan transitions. - State-transition guards and idempotency. - Language selection and fallback. ### 16.2 Integration tests - Quote snapshot remains unchanged after catalog price changes. - Duplicate active request is rejected under concurrency. - Evidence upload is tied to the correct company, request, and quote. - Only authorized platform admins can approve. - Approval triggers exactly one activation. - Activation atomically writes plan history and entitlements. - Notification failures do not roll back activation. - Expiry and scheduled-renewal workers are idempotent. - Stale admin screens cannot approve an already changed request. ### 16.3 End-to-end scenarios 1. Immediate annual upgrade with exact half-term proration. 2. Immediate upgrade near renewal using minimum-charge behavior. 3. Upgrade at renewal, then successful scheduled activation. 4. Correction required, replacement evidence, approval, and activation. 5. Rejected evidence with no entitlement change. 6. Quote expires before approval and requires requoting. 7. Two admins approve concurrently; only one activation occurs. 8. Company changes language after quote; existing communication follows the documented snapshot policy. 9. Arabic flow verifies translations, RTL layout, dates, currency, email, and PDF/payment instructions. 10. Activation fails after approval and succeeds through idempotent retry. 11. Unauthorized user and cross-tenant access attempts are denied. 12. Catalog plan changes while a quote is pending; frozen quote and plan versions remain reproducible. ### 16.4 Manual UI testing - Desktop, tablet, and mobile widths. - EN/FR LTR and AR RTL. - Light and dark themes if supported by the product. - Keyboard-only and screen-reader-critical flows. - Long French and Arabic content, large currency values, error messages, empty states, upload progress, and slow network behavior. ## 17. Delivery Phases ### Phase 1 — Policy and contract decisions - Approve proration basis, effective-date rule, quote validity, minimum charges, discount transfer, tax treatment, cancellation, and admin override policy. - Define eligible plan transition matrix. - Inventory current subscription, plan, payment, role, localization, notification, and entitlement architecture. Exit criterion: no unresolved decision can change stored financial amounts, renewal dates, or activation authorization. ### Phase 2 — Data and domain foundation - Add upgrade request, quote, plan history, payment linkage, constraints, indexes, and migrations. - Implement state machine, eligibility, quote calculation, and audit events. - Add backfill only where required; do not fabricate historical quotes. Exit criterion: domain and migration tests pass, including rollback/forward compatibility. ### Phase 3 — Company workflow - Build plan comparison, quote review, acceptance, payment instructions, evidence upload, status tracking, cancellation, and scheduled-upgrade flow. - Add EN/FR/AR copy and RTL behavior. Exit criterion: a company admin can complete each allowed path without platform-admin database intervention. ### Phase 4 — Platform-admin workflow - Build review queue, evidence review, correction, rejection, approval, override controls, audit view, and activation incident queue. Exit criterion: payment review and recovery are fully operable through authorized product interfaces. ### Phase 5 — Activation and communication - Implement atomic activation, entitlement refresh, outbox processing, notifications, expiry worker, and scheduled-renewal activation. Exit criterion: exactly-once business effects are demonstrated under retries and concurrent actions. ### Phase 6 — Hardening and release - Complete security review, accessibility audit, localization QA, performance tests, operational dashboards, alerts, runbook, and staged rollout. - Pilot with internal/test companies before general availability. Exit criterion: all release blockers are closed and rollback/recovery has been rehearsed. ## 18. Definition of Done - All product-policy decisions in Phase 1 are documented and approved. - Immediate prorated and renewal-scheduled upgrades work for eligible subscriptions. - Financial calculations are reproducible from immutable snapshots. - No upgraded feature is accessible before verified payment and successful activation. - Subscription and plan history are preserved and auditable. - Duplicate requests, duplicate approvals, and duplicate activations are prevented. - EN, FR, and AR flows are complete; Arabic is verified RTL. - Correct recipients receive localized notifications; ordinary company users do not receive billing notices. - Security, accessibility, automated, and manual tests pass. - Monitoring, alerts, support documentation, and recovery runbook are operational. - Release can be rolled back without corrupting active subscriptions or losing approved-payment records. ## 19. Decisions Required Before Coding 1. Is proration based on calendar days, exact timestamps, or whole billing months? Recommendation: calendar days in the billing timezone. 2. Which date controls the price: quote creation, claimed payment, cleared payment, or admin approval? Recommendation: quote is valid through a fixed expiry; after expiry, requote. 3. How long is a quote valid? Recommendation: 7 days, capped at renewal. 4. Are taxes included in displayed plan prices, and how is tax recomputed for upgrades? 5. Do negotiated discounts transfer to target plans? Recommendation: only when explicitly marked transferable. 6. What is the minimum charge per currency? 7. Can grace-period subscriptions upgrade, or must overdue balances be cleared first? Recommendation: clear overdue balances first unless a platform admin approves a combined settlement. 8. Can platform admins override the calculated amount? Recommendation: only with a separate permission, reason, and audit event. 9. What happens if the company pays after quote expiry? Recommendation: do not activate automatically; reconcile payment against a new approved quote. 10. Does the company language snapshot remain fixed for the request, or follow later company-language changes? Recommendation: snapshot financial documents; use the current company language for later status notifications only if this distinction is clearly implemented. These are blocking business rules, not implementation details. Coding before resolving them risks incorrect charges, disputed renewal dates, and unauthorized access.