The Trinity Beast — Stripe Implementation

Subscription Lifecycle Management — SDK Integration, Webhooks, Customer Portal

Payment: Stripe Payment Links Lambda: trinity-beast-receipt Version: v16 Updated: May 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,9990 (none)0 (none)0Yes
Lifetime999,999,9990 (none)0 (none)0Yes

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. Webhook Push Subscription Tiers

The Webhook Push service is a separate Stripe product line from LPO subscriptions. Associates subscribe to receive real-time price pushes via UDP (fire-and-forget) and/or HTTPS (signed POST with HMAC verification). Each tier defines a push interval, maximum monitored assets, and monthly price.

Tier Configuration

TierPriceIntervalMax AssetsDelivery
webhook_starter$30/moEvery 60 seconds9UDP + HTTPS
webhook_standard$90/moEvery 15 seconds30UDP + HTTPS
webhook_professional$210/moEvery 6 seconds75UDP + HTTPS
webhook_enterprise$540/moEvery 3 seconds150 (all)UDP + HTTPS

Application Parameters (per tier)

ParameterStarterStandardProfessionalEnterprise
webhook_{tier}_interval_sec601563
webhook_{tier}_max_assets93075150
webhook_{tier}_price3090210540

Delivery Engine Parameters

ParameterValueDescription
webhook_delivery_retry_max3Maximum retry attempts for failed HTTPS deliveries
webhook_delivery_timeout_ms5000Timeout per HTTPS delivery attempt (ms)
webhook_https_backoff_base_ms1000Base delay for exponential backoff on retries (ms)

Checkout Flow

  1. Associate selects a webhook tier on the subscription page
  2. Redirected to Stripe Payment Link (stored in payment_links table, link_type = 'webhook')
  3. Stripe fires checkout.session.completed with metadata tier = webhook_starter|standard|professional|enterprise
  4. Lambda creates a webhook_subscriptions row with status = 'pending_verification'
  5. Associate configures their endpoint via POST /webhook/configure (UDP IP:port and/or HTTPS URL)
  6. Associate verifies ownership via POST /webhook/verify (token challenge)
  7. Status transitions to active — delivery engine begins pushing prices

Webhook Subscription State Machine

stateDiagram-v2
    [*] --> pending_verification: checkout.session.completed
    pending_verification --> active: /webhook/verify (token match)
    active --> active: /webhook/configure (update endpoint/assets)
    active --> paused: payment_failed (grace period)
    active --> canceled: subscription.deleted
    paused --> active: invoice.paid (payment recovered)
    paused --> canceled: grace period expired
    canceled --> [*]
        

Database: webhook_subscriptions Table

ColumnTypePurpose
idUUIDPrimary key
api_key_idUUIDFK → api_keys.id (the Associate's API key)
tierTEXTwebhook_starter, webhook_standard, webhook_professional, webhook_enterprise
udp_endpoint_ipTEXTAssociate's UDP receive IP address
udp_endpoint_portINTEGERAssociate's UDP receive port
https_endpoint_urlTEXTAssociate's HTTPS webhook receive URL
delivery_methodTEXTudp, https, or both
interval_secondsINTEGERPush interval (from tier config)
assetsTEXT[]Array of monitored asset symbols
max_assetsINTEGERMaximum allowed assets (from tier config)
statusTEXTpending_verification, active, paused, canceled
verification_tokenTEXTToken sent to endpoint for ownership verification
verified_atTIMESTAMPTZWhen endpoint was verified
monthly_push_countINTEGERRunning count of pushes this month
stripe_customer_idTEXTStripe customer ID for this webhook subscription
stripe_subscription_idTEXTStripe subscription ID for billing
subscription_statusTEXTMirrors Stripe status: active, past_due, canceled

Database: webhook_delivery_log Table

ColumnTypePurpose
idSERIALPrimary key
subscription_idUUIDFK → webhook_subscriptions.id
assetTEXTAsset symbol pushed (e.g., BTC)
priceNUMERIC(20)Price value delivered
sourceTEXTPrice source (coinbase_ws, gemini_ws, etc.)
delivery_methodTEXTudp or https
statusTEXTdelivered, failed, retrying
latency_msDOUBLE PRECISIONDelivery latency in milliseconds
attemptINTEGERAttempt number (1 = first try)
errorTEXTError message if delivery failed
sequence_numberBIGINTMonotonic sequence for ordering
created_atTIMESTAMPTZWhen this delivery was attempted

Delivery Engine Architecture

BeastWebhook Service (WEBHOOK_SERVER)

The 4th ECS service runs SERVER_TYPE=WEBHOOK_SERVER. It connects to the same 6 WebSocket feeds as the LPO services, maintaining its own sync.Map price cache. Every interval_seconds, it reads the latest prices from the local cache and pushes to each active subscription's configured endpoints.

  • UDP delivery: Fire-and-forget — single packet per asset per push cycle. Zero retries. ~0.1ms latency.
  • HTTPS delivery: Signed POST with HMAC-SHA256 (X-Webhook-Signature header). Retries with exponential backoff on failure. ~50-200ms latency.
  • Price source: Local wsPriceCache (sync.Map) — same WebSocket feeds as LPO services, zero network hop for price reads.

Stripe Product Metadata

ProductMetadata KeyValue
Webhook Startertierwebhook_starter
Webhook Standardtierwebhook_standard
Webhook Professionaltierwebhook_professional
Webhook Enterprisetierwebhook_enterprise

API Endpoints (managed by LPO server, not Lambda)

EndpointMethodPurpose
/webhook/configurePOSTSet/update UDP and HTTPS endpoints, delivery method, assets
/webhook/verifyPOSTSubmit verification token to activate delivery
/webhook/statusGETCurrent subscription status, 24h delivery stats, endpoint health
/webhook/assetsPOSTUpdate monitored asset list (within tier max)

11. 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.

12. 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.

13. 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).

14. Webhook Idempotency

Every webhook event is deduplicated via ElastiCache:

15. 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

16. 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 KeyValueType
FreetierfreeLPO Subscription
ProtierproLPO Subscription
EnterprisetierenterpriseLPO Subscription
UnlimitedtierunlimitedLPO Subscription
LifetimetierlifetimeLPO One-Time
LRS Add-OntierlrsLRS Add-On
Webhook Startertierwebhook_starterWebhook Push
Webhook Standardtierwebhook_standardWebhook Push
Webhook Professionaltierwebhook_professionalWebhook Push
Webhook Enterprisetierwebhook_enterpriseWebhook Push

17. 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.