Email workspaceSubscription cancellation contract
Implementation: App\Mail\CancelSubscription. Baseline view: resources/views/emails/users/CancelSubscription.blade.php. Usage: app/Http/Controllers/Billing/AppSubscriptionsController.php:459, cancel(). Static references are evidence, not proof of current production delivery. Original templates use mail::message; the standalone draft uses the shared compact table shell.
Suggested subject: Built subscription canceled (existing). HTML document title does not set the actual mail header.
Suggested preheader: Your subscription has been canceled and your plan downgraded to basic.
Portable placeholder contract
| Token | Existing source / derivation | Handling |
|---|---|---|
| {{logo_url}} | Original Built app_name PNG | Optional deployment override; concrete project HTTPS image is already present. Approved absolute HTTPS or matching CID. |
| {{preheader}} | Suggested text above, assembled only from existing context | Required escaped text; no new backend record or timing promise. |
| {{plan_name}} | $bill->planname; caller obtains AppSubscription name for the bill | Required escaped text; preserve the existing value. |
| {{started_at}} | $bill->start_date | Required original bill start value; retain source format and do not invent a new period. |
| {{ended_at}} | $bill->end_date; cancel() updates it with Carbon::now() | Required cancellation-time end value, not a promise of access until a future billing-period end. |
Content, states and integration decisions
- Audience: authenticated request.user(api).email; explicitly queued. Preserve default sender behavior and subject. No business name, recipient first name or refund amount is supplied by the original body, so none is inserted.
- Retain cancellation, named source plan, downgrade to the free basic plan, start/end values, phone 030 397 4832 and support@built.africa. The source view asserts the basic downgrade; the shown controller ends the bill but does not explicitly assign a basic plan. Confirm actual entitlement logic before production integration. Do not silently invent a grace period or change cancellation behavior here.
- The controller queues this notice before FlutterWave deleteCard and optional Paystack disableSubscriptions complete. Do not promise that every future debit has stopped or that provider cleanup succeeded. No refund claim, immediate account deletion, retry action or attachment is added.
- States: standard and long plan/timestamp strings. The request cancellation reason is persisted in a separate record but not passed to this view; it is not promoted into a new email requirement.
Safe integration, text and images
ready/subscription-canceled.html and ready/subscription-canceled.txt are portable extracts; every populated state under previews/ has HTML and text. No separate project-owned plain-text alternative was found for these four views, so the supplied text versions are proposed equivalents.
Portable {{placeholder_name}} tokens are not automatically Laravel Blade variables. Prefer an explicitly mapped full Blade view and a separate text alternative inside the existing Mailable, retaining constructors, sender/header choices, recipient routing, attachments and queue behavior. A full document should not be nested inside mail::message. No new templating framework is needed.
Escape all dynamic text, attribute values, failure reasons and internal references. Never insert provider text as raw HTML. Validate any image override separately against approved HTTPS hosts or an actual attached CID before escaping it. If string replacement is chosen, use one pass; inserted token-shaped text must remain text. There are no application action destinations or duplicate legacy action-link fields in this batch; the source failed-attempt button is empty and intentionally removed. Only the confirmation tax block is conditional; remove its entire marked section when false in both HTML/text. Other documented fields are required except the explicit channel fallback.
Default logo: https://built-storage.ams3.cdn.digitaloceanspaces.com/Built/app_name.png. Retain the unchanged assets/built-logo.png original. Use absolute HTTPS hosting or a matching CID attachment, preserve its proportions and account for remote-image blocking. Dark mode is progressive enhancement and needs real client tests.
Fixtures use fictional names, amounts, reasons and references. No payment, subscription change, mail send or provider action is executed. Browser rendering is not proof of Gmail, Outlook or iOS Mail compatibility, actual attachment generation or production delivery. See subscriptions-batch.md, subscriptions-validation.md and integration-notes.html.