Email workspaceSubscription confirmation contract
Implementation: App\Mail\subscriptionNotification. Baseline view: resources/views/emails/users/subscriptionNotification.blade.php. Usage: app/Http/Controllers/Billing/AppSubscriptionsController.php:184–217, subscribe(). 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: Subscription Successful (existing). HTML document title does not set the actual mail header.
Suggested preheader: Your {{plan_name}} subscription for {{business_name}} was successful.
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. |
| {{first_name}} | $user->first_name | Required escaped text; preserve the existing value. |
| {{plan_name}} | $plan->planname (constructor AppBilling argument, not the local SubscriptionPlan price lookup) | Required escaped text; preserve the existing value. |
| {{business_name}} | $business->name | Required escaped text; preserve the existing value. |
| {{billing_cycle}} | $plan->cycle == 1 ? 'Monthly' : 'Every ' . $plan->subscription_cycle . ' Months' | Required escaped derived label. The source tests cycle, although AppBilling declares/populates subscription_cycle. Preserve and flag the mismatch; do not silently normalize. |
| {{subtotal}} | $currency . number_format($plan->amount / 1.20, 2) | Conditional source tax value; required only in the GH currency_symbol branch. |
| {{vat}} | $currency . number_format(($plan->amount / 1.20) * 0.15, 2) | Conditional source VAT (15%) value. |
| {{nhil}} | $currency . number_format(($plan->amount / 1.20) * 0.025, 2) | Conditional source NHIL (2.5%) value. |
| {{getfund}} | $currency . number_format(($plan->amount / 1.20) * 0.025, 2) | Conditional source GETFund (2.5%) value. |
| {{amount_label}} | business.country.currency_symbol == 'GH' ? 'Total' : 'Amount' | Required escaped fixed label from the original body branch. |
| {{amount}} | $currency . number_format($plan->amount, 2) | Required existing full amount; do not replace with base amount or infer a currency from country. |
Content, states and integration decisions
- Recipient: subscribing $user, with existing BCC and separate production internal copies, including country-specific copies. The same view greets the subscribing user even in staff copies; do not reinterpret that first name as the staff recipient. Preserve routing server-side and do not copy live address lists into samples.
- All caller paths explicitly queue. Mailable uses configured default From settings and attaches subscription_invoice.pdf from PDF.user.invoice. Keep the existing country-plan lookup/DefaultPlan fallback, constructor currency default GHS and null-coalescing fallback to billplan.currency_code. This draft does not change price selection, recipients or queue behavior.
- The body condition is business.country.currency_symbol == GH; PDF.user.invoice tests country.code == GH instead. They can disagree. The percentages and amount / 1.20 are legacy source calculations, not a claim of current tax-law correctness. A later authorized billing review must resolve the mismatch separately; do not update tax law or accounting in this isolated design task.
- Standalone HTML contains a marked optional tax table. Retain it and bind all four values only when the original body predicate is true. Otherwise remove the complete marked table and the four corresponding plain-text lines, and use Amount as amount_label. Do not leave unresolved tokens or an empty breakdown caption.
- Monthly applies only if plan.cycle == 1. AppBilling declares subscription_cycle and the shown caller populates that field, with no cycle accessor found. The legacy-one-month state illustrates Every 1 Months; Monthly fixtures are view-contract examples, not proof the live caller reaches that branch. Correcting the label/condition is an explicit integration decision.
- Keep PDF-only fields: bill ID / BASS invoice identifier, plan start/end dates, business address, extension quantities/prices, employee_extension and onsite_visits names/hours/visit frequencies, extraAmount and the full plan amount. Employee/onsite amounts are included in the existing PDF logic, not exposed as new email body fields. The PDF extensions fixture attaches no real file.
- States: monthly with source taxes, three months with source taxes, monthly without taxes, six months without taxes, PDF extensions, legacy one-month label and long values. Removed decorative success_icon image; original Built logo and all meaningful source text/data remain. The added PDF instruction describes the existing attachment.
Safe integration, text and images
ready/subscription-confirmed.html and ready/subscription-confirmed.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.