Skip to content

US-003: Manage Subscription with Stripe Customer Portal #43

@Mateus-Mannes

Description

@Mateus-Mannes

Summary

Add Stripe Customer Portal access for Pro users so they can update payment details or cancel renewal. The app will create short-lived portal sessions on demand, return users to /user, sync Stripe subscription state after portal return, and rely on the US-001 entitlement resolver to keep canceling users Pro until currentPeriodEndUtc.

Key Changes

  • Extend users-service billing configuration:
    • Add Stripe:PortalConfigurationId and Stripe:PortalReturnUrl.
    • Use the Stripe Dashboard portal configuration ID, with cancellation configured to end at period end.
  • Add authenticated users-service billing endpoints:
    • POST /users/billing/portal-session: require logged-in Pro/canceling user with StripeCustomerId; create Stripe Billing Portal session with customer, portal configuration, and return URL; return { portalUrl }.
    • POST /users/billing/sync: retrieve the current Stripe subscription for the user’s stored StripeCustomerId/StripeSubscriptionId, update local status, current period end, and cancel-at-period-end flag, then return entitlement fields.
    • Return clear non-portal errors for Free users, missing Stripe customer/subscription IDs, expired Pro users, and Stripe failures.
  • Update Angular user page:
    • Show plan status from AuthSessionStore: Free, Pro active, Pro canceling, Pro expired.
    • Show canceling access end date from currentPeriodEndUtc.
    • Show “Manage subscription” for pro_active and pro_canceling; clicking calls portal-session endpoint and redirects to portalUrl.
    • On /user?billing=returned, call billing sync, refresh session, and show a non-blocking status message.
  • Entitlement behavior:
    • Canceling keeps isPro=true until currentPeriodEndUtc.
    • Once currentPeriodEndUtc is in the past, /session resolves the user as Free/expired even if no webhook has arrived yet.
    • US-004 webhooks remain responsible for long-term event-driven consistency.

Test Plan

  • Users-service tests:
    • Unauthenticated portal session returns 401.
    • Free user cannot create portal session.
    • Pro user with Stripe customer/subscription gets a portal URL.
    • Missing Stripe customer/subscription returns a recoverable billing error.
    • Billing sync maps cancel_at_period_end=true to pro_canceling and preserves access through period end.
    • Billing sync maps expired/canceled subscription to non-Pro entitlement.
  • Angular tests:
    • User page renders Free, Pro active, Pro canceling with end date, and Pro expired states.
    • Manage button calls portal-session endpoint and redirects to returned URL.
    • billing=returned query param triggers sync and session refresh.
    • Sync/portal failures show recoverable messages without breaking progress loading.

Assumptions

  • US-001 and US-002 fields/endpoints exist before this starts, including StripeCustomerId, StripeSubscriptionId, StripeSubscriptionStatus, SubscriptionCurrentPeriodEndUtc, and SubscriptionCancelAtPeriodEnd.
  • Portal configuration is created in Stripe Dashboard and its ID is provided through app config.
  • Portal return URL is /user?billing=returned.
  • Stripe webhooks are not implemented in this story; US-003 adds explicit return sync to make portal changes visible immediately after return.
  • Official Stripe references: Customer Portal sessions and integration docs: https://docs.stripe.com/api/customer_portal/sessions and https://docs.stripe.com/customer-management/integrate-customer-portal

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions