Skip to content

feat(email): add org top-up confirmations#3084

Open
evanjacobson wants to merge 10 commits intomainfrom
feat/transactional-emails_org-topup
Open

feat(email): add org top-up confirmations#3084
evanjacobson wants to merge 10 commits intomainfrom
feat/transactional-emails_org-topup

Conversation

@evanjacobson
Copy link
Copy Markdown
Contributor

@evanjacobson evanjacobson commented May 7, 2026

Summary

Organization credit top-ups now send the transactional confirmation billing owners expect after team funds are added, closing the gap where successful org top-ups were only reflected in-app.

This extends the existing credit top-up email flow to organization purchases, including distinct manual and auto top-up copy, organization payment-detail links, Stripe receipt links when available, and durable send markers so webhook retries can recover missed sends without double-emailing recipients. The implementation follows the existing server-rendered email template conventions and reuses the transactional email log idempotency pattern already used for purchase confirmations.

Implementation notes
  • Adds organization-specific manual and auto variants to the existing credit top-up transactional email.
  • Sends confirmations to active organization owners and billing managers only.
  • Tracks organization top-up sends with transactional_email_log.organization_id so retry paths can remain idempotent.
  • Recovers missed org top-up emails from duplicate webhook/payment retry paths when the credit transaction already exists.
  • Preserves terminal NeverBounce handling while allowing retryable all-recipient failures to be retried later.
  • Passes auto top-up context through both initial org auto top-up setup payments and recurring invoice.paid org auto top-ups.

Verification

  • test coverage

Visual Changes

N/A

Reviewer Notes

Suggested review focus
  • Idempotency behavior around duplicate Stripe events and missed email recovery.
  • Recipient selection for active org owners and billing managers.
  • Manual versus auto organization top-up copy and organization payment-detail links.
  • Failure handling when some or all email recipients are rejected or fail to send.
  • The transactional_email_log.organization_id schema/index addition and GDPR retention note.
A few more details

  • We are not falling into the unrelated post-commit failure pitfall from Jean’s first comment. In this PR, processTopupForOrganization runs the test-only promo grant before scheduling the email, but that branch is gated to NODE_ENV === 'test' at apps/web/src/lib/organizations/organization-billing.ts:345. In production there is no unrelated bonus/promo step ahead of scheduleOrganizationTopUpConfirmationEmail at apps/web/src/lib/organizations/organization-billing.ts:355.

  • We are not falling into the retry-date pitfall from Jean’s second comment. Duplicate payment recovery loads the existing credit_transactions.created_at in getExistingOrganizationTopUpCreditTransaction and passes it as purchaseDate at apps/web/src/lib/organizations/organization-billing.ts:334-340. The email sender uses purchaseDate ?? new Date() at apps/web/src/lib/organizations/organization-billing.ts:165, so recovery uses the original transaction timestamp.

  • We do share the known insert-before-send marker limitations described in PR feat(emails): add top-up purchase confirmations #3050. This PR inserts a marker before sending at apps/web/src/lib/organizations/organization-billing.ts:141-149. That means a crash after marker insert but before provider acceptance can suppress a future retry. The code does mitigate multi-recipient failures by clearing the marker if all sends fail retryably at apps/web/src/lib/organizations/organization-billing.ts:220-222, but it is still not a durable outbox. That appears consistent with the explicit PR feat(emails): add top-up purchase confirmations #3050 decision to keep this pattern uniform rather than solve it locally.

@evanjacobson evanjacobson marked this pull request as ready for review May 7, 2026 01:53
Comment thread packages/db/src/migrations/0116_past_marauders.sql
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 7, 2026

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/db/src/migrations/0116_past_marauders.sql 4 Plain CREATE INDEX on existing transactional_email_log can block writes; use a concurrent index and consider a partial nullable-column index.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
N/A N/A No additional non-diff issues found.
Files Reviewed (11 files)
  • apps/web/src/emails/AGENTS.md - 0 issues
  • apps/web/src/lib/email.ts - 0 issues
  • apps/web/src/lib/organizations/organization-billing.test.ts - 0 issues
  • apps/web/src/lib/organizations/organization-billing.ts - 0 issues
  • apps/web/src/lib/purchase-emails.test.ts - 0 issues
  • apps/web/src/lib/stripe.ts - 0 issues
  • apps/web/src/lib/user.test.ts - 0 issues
  • apps/web/src/lib/user.ts - 0 issues
  • apps/web/src/tests/stripe.test.ts - 0 issues
  • packages/db/src/migrations/0116_past_marauders.sql - 1 issue
  • packages/db/src/schema.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by gpt-5.5-20260423 · 1,827,933 tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant