BuiltEmail workspace

Payment-link receipt contract

Implementation: App\Mail\PaymentLinkReciept, app/Mail/PaymentLinkReciept.php. Preserve the existing class spelling. Original view is resources/views/emails/paynentLinkReciept.blade.php (also misspelled, with case-sensitive path). This API-owned body acknowledges a processed payment-link transaction to the payer; it is not the merchant’s separate payment notification or an invoice/sales receipt.

Subject: Payment receipt from {{wallet_business_name}} (existing exact pattern).

Suggested preheader: Payment receipt #{{receipt_id}} from {{wallet_business_name}}.

Portable placeholders

TokenExisting source / derivationHandling
{{logo_url}}Original approved Built app_name PNGOptional deployment override; concrete approved HTTPS logo is present. Use approved HTTPS or an actual matching CID.
{{preheader}}Suggested existing receipt-ID/wallet summary aboveRequired escaped text. No wallet-settlement, refund, fee or timing guarantee.
{{receipt_id}}$transaction->idRequired escaped source receipt identifier. It is not uuid, provider reference, wallet number or invoice ID. Treat large identifiers as text, without JavaScript numeric coercion/precision loss.
{{wallet_business_name}}$wallet->business_nameRequired escaped body, subject and Thanks sign-off identity. Not necessarily Business.name used by From.
{{paid_amount}}$wallet->currency . number_format($transaction->amount, 2)Required complete existing display, preserving prefix, decimal formatting, zero and sign. Do not subtract transaction.charge, calculate a net payout, replace with link.amount or use business base/FX currency.
{{transaction_date}}$transaction->created_at->format('d-M-Y')Required original record-creation date, retaining source formatting/timezone semantics. New label Transaction date clarifies its origin. It is not collection/provider settlement timestamp, updated_at or email-send time.

No customer name/email, provider name, payment method, charge breakdown, status, receipt URL or PDF path is supplied by the original body. They are not added as body tokens. Source customer_email remains necessary for recipient routing even though it is not printed.

Trigger and payer audience

app/Http/Repository/PaymentRepository.php::resolveMobileMoneyCollection starts at :688. The relevant branches are inside the strict data.code === '00' path. For payment_link, it resolves MOMOCheckout, PaymentLinkTransaction, PaymentLink and Wallet, performs the existing wallet top-up, marks transaction/payment processed and deactivates a one-time link before the receipt send at :1215. The receipt is sent only when transaction.customer_email is truthy.

The card_payment_link and monnify_payment_link branch resolves transaction/link/wallet, deactivates one-time links, tops up the wallet and marks the transaction processed before the source receipt send at :1308, likewise guarded by customer_email. Preserve existing lookup/error behavior, processing order, one-time rules, vendor notifications and separate WhatsApp/callback paths; a template refactor does not authorize changes to them.

Both sites call Mail::to(transaction.customer_email)->send(new PaymentLinkReciept(wallet, transaction)). This is the payer email stored on the transaction, not PaymentLink.customer_email, the business owner, wallet user or request-selected receipt recipient. A missing payer email skips this email; it is not a different delivered-body state. Source-native send() calls, with no ShouldQueue declaration, must remain unchanged; the underlying configured transport may still use an asynchronous outbox.

Header identity and business data

The Mailable loads User through wallet.user_id and Business through wallet.business_id without events. From uses config(mail.from.address) with business.name. Reply-To is business.email ?: wallet user.email and supplies no display-name argument. Subject/body/sign-off use wallet.business_name. This fallback differs from SendLinkEmail, which uses business.owner_id and a Reply-To display name. Preserve the distinction rather than sharing the wrong lookup/name policy.

Requested native headers are not proof of transport delivery; inspect the catalog’s provider/outbox limitations in the consuming project. Do not replace Reply-To with the shared footer support address or claim final bank settlement from the wallet processing status.

Body, attachments and states

The draft preserves receipt ID, wallet label, existing amount, source date and Thanks sign-off. It replaces the old green success panel/animation with the quiet shared shell and labels the creation-date field Transaction date. No new status badge, customer identity, processing guarantee, fee/net calculation or payment action is added.

There is no source business attachment/PDF and no body URL. Do not copy invoice receipt’s View receipt or attached-PDF wording into this different notification. Shared footer app-store/contact links and inline PNG requirements remain separate from transactional workflow actions and business attachments. There are no legacy duplicate email link fields.

Five preview states illustrate mobile-money, card and Monnify source branches, supplied zero as a render check and long fields. Provider names are contextual descriptions in the workspace; they do not become new body fields or separate notification types. The large ID sample is a string within signed bigint range, deliberately above JavaScript’s safe-integer range. Zero does not establish an independently valid zero-payment send trigger. All identities/values are fictional.

Integration and safe values

Portable {{placeholder_name}} tokens are not automatically Blade variables. Use the existing Mailable’s native data with a complete HTML Blade view and separately registered text view. Do not nest the full document in mail::message or add a framework. Preserve the processing/caller guards, send(), recipient, header lookups, amount and created_at date semantics. Do not regenerate a receipt, collection or payment to populate a design.

Escape all IDs, wallet labels, formatted amounts, dates and attributes. For any logo/badge override validate approved absolute HTTPS host/path or an actual attached CID separately, using docs/integration/shared-footer.md. If using replacement, use one pass so inserted token-shaped strings stay literal text and do not become markup or a second template evaluation. Required missing values need an explicit adapter decision rather than invented defaults. No workflow URL validation is needed because none is supplied; do not invent one.

Files: public/ready/payment-link-receipt.html and .txt; public/previews/payment-link-receipt-*.html/.txt. No separate project-owned text file was found for the source Markdown view; the .txt is proposed equivalent content. Review tabs embed local footer PNGs; downloaded extracts retain real CID/HTTPS integration requirements.

No production template, transaction, wallet balance, collection, PDF, mail transport or sender code is modified or invoked. External providers trigger API-owned content here; externally authored CRM campaigns remain excluded. Browser checks do not establish actual payment/settlement correctness, Gmail/Outlook/iOS Mail, embedded-image delivery or current production delivery.