The Trinity Beast — Stripe Implementation

Subscription Lifecycle Management — SDK Integration, Webhooks, Customer Portal

Payment: Stripe Payment Links Lambda: trinity-beast-receipt Version: v15 Updated: April 2026

Table of Contents

1. Overview

The Trinity Beast Stripe Implementation provides full subscription lifecycle management using the Stripe Go SDK. It replaces the previous raw HTTP Stripe API calls with type-safe SDK interactions and adds webhook-driven subscription state management.

Subscribers have two paths for managing their subscriptions:

Stripe Customer Portal (Self-Service)

Linked from every SES email receipt. Subscribers can cancel subscriptions, update payment methods, and view invoice history. Actions trigger the same webhook events as SDK operations.

Stripe SDK (Programmatic)

The trinity-beast-receipt Lambda uses the Stripe Go SDK for checkout session processing, subscription creation, tier changes, and LRS add-on management.

Both paths converge on the same Stripe webhook events, processed by a single Lambda handler. No separate code paths exist for portal vs. SDK — the webhook is the single source of truth.

2. System Architecture

graph TB
    subgraph Subscriber
        Portal["Stripe Customer Portal"]
        Website["cpmp-site.org"]
    end
    subgraph Stripe
        StripeAPI["Stripe API"]
        Webhooks["Stripe Webhooks"]
    end
    subgraph AWS
        Lambda["trinity-beast-receipt Lambda"]
        Aurora["Aurora PostgreSQL"]
        ElastiCache["ElastiCache Valkey"]
        SES["SES Email"]
        Main["BeastMain"]
        Mirror["BeastMirror"]
        LRS["BeastLRS"]
    end
    Portal -->|cancel / update| StripeAPI
    Website -->|checkout| StripeAPI
    StripeAPI -->|events| Webhooks
    Webhooks -->|POST /webhook| Lambda
    Lambda -->|SDK calls| StripeAPI
    Lambda -->|read/write| Aurora
    Lambda -->|idempotency + cache| ElastiCache
    Lambda -->|receipts| SES
    Lambda -->|invalidate-key| Main
    Lambda -->|invalidate-key| Mirror
    Lambda -->|invalidate-key| LRS
    SES -->|portal link| Portal
        

Key Components

ComponentRole
trinity-beast-receipt LambdaProcesses checkout sessions and webhook events. Uses Stripe Go SDK v82.
Aurora PostgreSQLAuthoritative source for api_keys table with Stripe metadata columns.
ElastiCache ValkeyWebhook idempotency keys (72h TTL), API key cache (24h TTL).
ECS Services (3)BeastMain, BeastMirror, BeastLRS — receive cache invalidation calls.
SESSends receipts with Customer Portal link (portal_url template variable).

3. Subscription State Machine

stateDiagram-v2
    [*] --> active: checkout.session.completed
    active --> active: subscription.updated (upgrade)
    active --> pending_downgrade: downgrade scheduled
    active --> pending_cancel: cancel_at_period_end
    active --> past_due: invoice.payment_failed
    pending_downgrade --> active: period end (new tier)
    pending_cancel --> canceled: subscription.deleted
    past_due --> active: invoice.paid
    past_due --> canceled: grace period expired
    canceled --> active: re-subscribe
    canceled --> [*]
        

pending_downgrade is tracked via tier_effective_date in Aurora. pending_cancel is tracked by Stripe's cancel_at_period_end flag. The subscription_status column stores Stripe-canonical values: active, past_due, canceled, trialing, incomplete.

4. Stripe SDK Integration

The Lambda uses github.com/stripe/stripe-go/v82 for all Stripe API interactions:

OperationSDK PackageReplaces
Checkout session retrievalcheckout/session.Get()Raw GET /v1/checkout/sessions/{id}
Customer Portal sessionbillingportal/session.New()Raw POST /v1/billing_portal/sessions
Payment intent (receipt URL)paymentintent.Get()Raw GET /v1/payment_intents/{id}
Webhook signature verificationwebhook.ConstructEvent()N/A (new)
Subscription managementsubscription.Update(), subscription.List()N/A (new)

Authentication: stripe.Key is set from STRIPE_SECRET_KEY in trinity-beast-secrets during Lambda init().

5. Webhook Event Processing

The Lambda exposes a /webhook endpoint that processes Stripe webhook events:

Event TypeHandlerAction
customer.subscription.updatedhandleSubscriptionUpdatedTier changes, status updates
customer.subscription.deletedhandleSubscriptionDeletedCancellation (LPO or LRS addon)
invoice.payment_failedhandlePaymentFailedMark as past_due
invoice.paidhandlePaymentRecoveredRestore to active

Processing Flow

  1. Verify webhook signature using STRIPE_WEBHOOK_SECRET
  2. Check idempotency via ElastiCache (webhook:event:{event_id})
  3. Route to event-specific handler
  4. Update Aurora api_keys table
  5. Invalidate caches (ElastiCache + sync.Map)
  6. Mark event as processed (72h TTL)

All webhook events return HTTP 200 to Stripe (even on processing errors) to prevent retries. Errors are logged for manual review.

6. Customer Portal (Self-Service)

Every SES email receipt includes a portal_url linking to the Stripe Customer Portal. The portal is configured to allow:

Portal return URL: https://cpmp-site.org

Portal sessions are generated using the Stripe SDK: billingportal/session.New() with the subscriber's stripe_customer_id.

When a subscriber cancels through the portal, Stripe fires the same customer.subscription.updated and customer.subscription.deleted events — no separate handling required.

7. Tier Upgrades & Downgrades

Upgrades (Immediate, Pro-Rated)

When a subscriber upgrades to a higher tier, the Stripe subscription is updated with proration_behavior: create_prorations. The subscriber is charged the pro-rated difference for the remainder of the billing period. New tier limits take effect immediately in Aurora and all caches are invalidated.

Downgrades (End-of-Period)

When a subscriber downgrades, the change is scheduled for the end of the billing period using proration_behavior: none. The tier_effective_date column is set in Aurora. The subscriber keeps their current (higher) tier limits until the period ends. When the customer.subscription.updated event fires at period end, the new lower tier is applied.

Tier Configuration

TierQuery LimitQPSBurstMin Wait (s)LRS Included
Free1,000151.0No
Pro50,00010200.1No
Enterprise500,000501000.02No
Unlimited999,999,9991002000.01Yes
Lifetime999,999,9991002000.01Yes

8. Subscription Cancellation

Cancellations are always at end of billing period — subscribers keep access until they've used what they paid for.

LPO Subscription Cancelled

  1. Subscriber cancels via Customer Portal or SDK
  2. Stripe sets cancel_at_period_end = true
  3. At period end, customer.subscription.deleted fires
  4. Lambda downgrades to Free tier (1,000 queries, 1 QPS)
  5. Sets lrs_enabled = false — LRS add-on auto-cancelled
  6. If LRS addon subscription exists, cancels it in Stripe via SDK
  7. Invalidates all caches

LRS Add-On Only Cancelled

  1. LRS addon subscription deleted in Stripe
  2. Lambda matches sub.ID == stripe_lrs_subscription_id
  3. Sets lrs_enabled = false, clears stripe_lrs_subscription_id
  4. LPO subscription and tier remain unchanged

9. LRS Add-On Lifecycle

The LRS add-on ($20/mo) enables unlimited LRS reports for Free, Pro, and Enterprise tiers. Unlimited and Lifetime tiers receive LRS included at no extra cost.

ActionFlow
PurchaseCheckout → Lambda enables lrs_enabled, stores stripe_lrs_subscription_id
Cancel (LRS only)Webhook → Lambda disables lrs_enabled, clears subscription ID
Cancel (LPO)Webhook → Lambda disables LRS, cancels LRS addon in Stripe
Re-subscribeNew checkout → Lambda re-enables lrs_enabled, new subscription ID
Unlimited/Lifetime purchaseRejected — LRS already included with tier

10. Lifetime Tier Handling

$3,000 One-Time Payment

Lifetime tier is a one-time Stripe payment — no recurring subscription. The stripe_subscription_id column remains NULL. LRS is automatically enabled (lrs_enabled = true). Downgrades are rejected — Lifetime subscriptions are permanent.

11. Payment Failure & Grace Period

When invoice.payment_failed fires:

  1. subscription_status set to past_due
  2. payment_failed_at set to current timestamp (first failure only)
  3. Subscriber retains access for the grace period (default: 7 days)
  4. Grace period is configurable via payment_grace_period_days application parameter

When invoice.paid fires for a past_due subscription:

  1. subscription_status restored to active
  2. payment_failed_at cleared

If the grace period expires, the LPO server returns HTTP 402: 🛑 [LPO] [us-east-2] [node] [402] Payment past due. Please update your payment method at the Stripe Customer Portal.

12. Cache Invalidation

Every subscription state change triggers a 3-layer cache flush:

  1. ElastiCache — Delete apikey:{api_key} hash
  2. BeastMain sync.Map — GET https://api.cpmp-site.org/admin/invalidate-key?api_key={key}
  3. BeastMirror/LRS sync.Map — GET https://lrs.cpmp-site.org/admin/invalidate-key?api_key={key}

If any invalidation call fails, the system logs a warning and continues. Eventual consistency is guaranteed by ElastiCache TTL (24h) and sync.Map TTL (5min).

13. Webhook Idempotency

Every webhook event is deduplicated via ElastiCache:

14. Database Schema

New Columns on api_keys

ColumnTypeDefaultPurpose
stripe_customer_idTEXTNULLStripe cus_xxx ID
stripe_subscription_idTEXTNULLLPO tier subscription ID
stripe_lrs_subscription_idTEXTNULLLRS add-on subscription ID
subscription_statusTEXT'active'Mirrors Stripe status
tier_effective_dateTIMESTAMPTZNULLPending downgrade date
payment_failed_atTIMESTAMPTZNULLFirst payment failure timestamp

Indexes

Application Parameter

KeyValueDescription
payment_grace_period_days7Days to allow access after payment failure

15. Stripe Dashboard Configuration

Webhook Endpoint

URL: Lambda Function URL /webhook path

Events: customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed, invoice.paid, checkout.session.completed

Signing secret: Stored as STRIPE_WEBHOOK_SECRET in trinity-beast-secrets

Customer Portal

Cancel subscription: Enabled (at end of billing period)

Update payment method: Enabled

View invoices: Enabled

Return URL: https://cpmp-site.org

Product Metadata

ProductMetadata KeyValue
Freetierfree
Protierpro
Enterprisetierenterprise
Unlimitedtierunlimited
Lifetimetierlifetime
LRStierlrs

16. Error Logging

All subscription lifecycle errors use the Trinity Beast Convention format:

🛑 [LPO] [us-east-2] [ReceiptLambda] [code] Message.

Success events use module-specific loggers:

All log messages include stripe_customer_id, stripe_subscription_id, and api_key for traceability.