Subscriptions & reminders
Deployment status updated 27 September 2026. See Testing & launch for verified checks and remaining acceptance work. Email sending is disabled.
Atlas Pay · Prepared by Kyle Campbell
Deployment status
Buyer-owned subscriptions, trial setup, secure billing-portal entry and buyer-to-bot linking are deployed. Email sending is deliberately disabled, including subscription reminders. Stripe test-clock renewals and native Atlas client acceptance remain outstanding; deployment alone does not verify them.
Merchant workflow
Create a Subscription payment link from the dashboard or /newsubscription in the bot. Choose the amount, currency, cadence and a free trial from 0 to 730 days. Zero means no trial. The buyer-facing Atlas link requires the buyer to sign in, review the offer and explicitly agree to recurring billing before opening Stripe Checkout. A payment method is required, including for trials. Trial completion uses Stripe Billing; Atlas never implements its own recurring PaymentIntent loop or manual-capture renewal flow.
Buyer enrollment uses one fixed-price recurring Stripe Price with quantity one. The reviewed offer is versioned, so a changed price or trial requires another review. The price and trial terms are persisted with the buyer's consent version. Stripe's tax setting on the payment link is preserved. Configure and validate applicable taxes before launch; this is not automatic worldwide tax registration or coverage.
Stripe Billing is responsible for renewals, authentication and retries. Configure the merchant's retry/recovery policies in Stripe. Bank declines are not always insufficient funds; customers receive a general payment-attention message and complete recovery securely on Stripe.
Buyer workflow
Open My subscriptions. The list includes the merchant, plan, base recurring price, status, trial end, current period end and scheduled cancellation. Base price is not a guarantee of the next invoice total: tax, proration and adjustments can change it.
- Continue setup reuses the original enrollment and Checkout Session, including recovery after a lost response.
- Refresh status retrieves current Stripe state for that subscription. Reload list reads the recorded state and displays when it was last checked.
- Manage on Stripe opens a portal for that buyer's connected-account Customer. Configure the portal's invoices, payment-method updates and cancellation features before launch. Plan changes are available only when configured and supported by Stripe; pause/resume, usage billing, multi-item subscriptions and arbitrary in-chat billing mutations are not implemented in this preview.
- Canceling is different from a refund. Removing a saved wallet card does not cancel a subscription.
Only subscriptions enrolled through the signed-in Atlas flow are linked. Existing direct Stripe links, customer-specific checkout links and unrelated services are not imported merely because their email matches. The server never accepts a customer ID or email supplied in chat as proof of ownership. Each enrollment has its own connected-account Customer, isolated from the shared one-time-purchase wallet.
Atlas bot
Choose Connect Atlas bot in My subscriptions and send the generated /connectbuyer ... command to Atlas Pay in a private chat. It expires after five minutes and works once. The database stores only its hash; consuming it is atomic. This buyer connection is separate from /link, which connects a merchant workspace.
Ask “What are all my subscriptions?”, “Show my subscriptions” or use /subscriptions. Results are paginated and scoped to the linked buyer and active test/live environment. /unlinkbuyer or Disconnect bot on the web immediately removes future bot access without canceling any plans. Shared/group chats and contexts without verified private-chat status are rejected.
Cancellation or payment-method changes happen on the secure management page, not as an inferred action from natural-language chat. Native Atlas push notifications and mobile payment sheets still require mobile-app integration; this preview provides bot-on-demand summaries, a web inbox and queued email delivery.
Notifications and Resend
The private web inbox records subscription start, trial ending, renewal upcoming, cancellation scheduled, subscription ended, payment received and payment-attention notifications. Trial/renewal reminders are generated when a current processor read finds the relevant date within three days. Short trials may produce the reminder immediately. The inbox shows recorded events, including earlier events that have since been superseded.
Live-mode email requires all three server settings:
| Setting | Purpose |
|---|---|
RESEND_API_KEY | Server-side Resend sending key stored as a Worker secret |
RESEND_FROM_EMAIL | Verified sender, for example Atlas Pay <billing@your-domain> |
SUBSCRIPTION_EMAILS_ENABLED | Explicitly set to true after acceptance testing |
The recipient is the buyer's verified Atlas account email, never arbitrary input or an unverified Stripe email. Email/password accounts without email verification remain inbox-only; a verified Google account can receive email. Test-mode notifications never send email. No marketing email is added.
Email work is persisted in D1 and processed off the webhook request path. A five-minute scheduled invocation reconciles at most three subscriptions and attempts at most two live emails. These deliberately small preview batches are not a production throughput guarantee. Before scaling, monitor oldest due records and move delivery/reconciliation to appropriately provisioned queues or increase measured capacity within Worker/D1 limits. Reminder delivery can be late when the backlog exceeds capacity.
Each email has a stable Resend idempotency key, frozen payload, lease and retry backoff. Failed responses do not imply that the email was not sent. Once 23 hours have passed from the first attempt, an uncertain send becomes needs_review, rather than risking a duplicate beyond Resend's 24-hour idempotency window. Changed recipient addresses also require review. Trial/renewal reminders are rechecked before delivery and suppressed if canceled, expired or superseded. Resend acceptance is recorded as accepted, not proof of delivery to an inbox. Bounce/complaint/delivery webhooks and email preference controls remain future work.
Buyer API
Web requests use /api/atlas/wallet; Worker paths use /api/wallet. All require the buyer's actual session. Mutations require the configured frontend Origin and requests are limited to 8 KiB. Merchant API keys do not authorize buyer access.
| Method and path | Purpose |
|---|---|
GET /subscriptions?after=... | Owned subscriptions, newest notifications and connection state |
GET /subscriptions/offers/:workspace/:link | Current eligible offer and version |
POST /subscriptions/start | Begin versioned, explicitly consented enrollment |
POST /subscriptions/:id/continue | Resume the original pending enrollment |
POST /subscriptions/:id/refresh | Reconcile current processor state |
POST /subscriptions/:id/portal | Create buyer-specific secure management session |
POST /buyer-link | Generate a short-lived one-time bot connection command |
DELETE /buyer-link | Revoke buyer bot access and unused connection code |
POST /notifications/:id/read | Mark the buyer's own notification as read |
Enrollment requires workspace_id, link_id, UUID request_id, current offer_version, consent: true, and consent_version: "atlas-subscriptions-2026-09-06". Retry the same request after uncertain errors; never start another purchase as a workaround. Concurrent enrollments for the same buyer, merchant, mode and Price are blocked while the first is open. Closed enrollments retain history; a genuinely new enrollment requires a new request ID.
Deployment and acceptance gates
- Apply migration
0010_buyer_subscriptions.sqlafter migrations 0006–0009 in staging first. Then deploy the Worker and frontend together. - Register connected-account subscription events, including
customer.subscription.trial_will_end, and invoice paid, payment failed and payment action required events. The checked-in registration script includes these; the production test/live Connect destinations have these event subscriptions configured. - Confirm webhook signature verification, retry delivery and account/mode routing. Configure and test the appropriate Stripe Connect Customer Portal settings. Do not expose a cancellation promise before confirming that the portal supports it.
- Verify the Resend domain/sender, store the server secrets and keep live delivery disabled until controlled email acceptance is complete. Inspect outbox status and configure monitoring for
needs_review, stale reminders and missing/failed cron invocations. - Use Stripe test clocks in staging to exercise trial start/end, renewal, failure recovery, authentication required, scheduled cancellation and completion. Verify invoices and access/fulfillment decisions against real processor events; a trial or
activestatus alone is not a paid invoice. - Test the actual Atlas phone client: link/unlink, private-chat enforcement, pagination and portal return flow. No native phone-client testing has been performed in this change.
Production email deliverability, Stripe test-clock acceptance, load/soak testing, notification preferences, native push and buyer subscription entitlement/merchant fulfillment webhooks are not complete. Existing catalog order fulfillment events remain a separate feature.
Implementation references: Stripe trials, Stripe Customer Portal, Resend idempotency.
Automatic fraud holds
Subscription payments are assessed from current connected-account processor evidence. A high-risk payment, dispute or issuer early fraud warning blocks service and queues a collection pause for Atlas-created subscriptions. Elevated risk and failed checks require service review. The buyer and merchant views expose risk_decision, service_allowed, and collection_hold separately from Stripe's subscription status. An active subscription alone is not service authorization. These fields describe the risk gate; current invoice, trial and cancellation checks still apply. Enforce them in the merchant's service; Atlas does not control external entitlements.
High-risk collection changes are asynchronous: pending means not yet confirmed; applied means Atlas verified the pause and processed existing open/draft invoice retries; manual_review means Atlas could not safely apply the change to a subscription without Atlas ownership metadata. Atlas uses keep_as_draft for future collection and disables auto_advance on existing open/draft invoices. Already collected funds are not refunded and charges already in flight can complete. These controls do not prevent an operator from charging or changing settings directly in Stripe. No automatic resume or cancellation is performed. Admin acknowledgment does not release an Atlas block, and this release has no block-override workflow.
Stripe normally scores the initial subscription payment rather than every renewal. An unscored renewal can pass only with a prior successful allowed payment for the same subscription and payment method, and no new failed checks, unresolved review, dispute or warning. A new payment method or missing baseline requires review. Trial/renewal reminders are suppressed while service is on review or block.
This extension requires migration 0016_payment_risk_and_alerts.sql and the updated Connect webhook event list. See Stripe risk assessment, collection pauses and invoice automatic advancement.
Payment currency
Eligible enrollment sessions request Adaptive Pricing. The buyer reviews Stripe’s currency choices, conversion and recurring terms before confirming. The wallet offer begins with the merchant price; use Stripe invoices and the billing portal for billed amounts. Payout settings are separate. See Currencies & FX rates. Embedded subscriptions are not available through the one-time merchant checkout endpoint.