feat: add support for promotion codes and coupons#287
feat: add support for promotion codes and coupons#287Nika0000 wants to merge 2 commits intosupabase:mainfrom
Conversation
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThis pull request extends the Stripe sync engine to support coupons and promotion codes. Changes include adding database schema for promotion codes, defining entity schemas for both resource types, implementing webhook event handlers for coupon and promotion code events (created, updated, deleted), adding corresponding sync and CRUD methods to the sync engine, updating type definitions to include the new entities, extending test fixtures and mocks, and updating documentation. No existing functionality is modified or removed. Sequence DiagramsequenceDiagram
actor Stripe as Stripe API
participant Handler as Webhook Handler
participant Engine as Sync Engine
participant DB as Database
Stripe->>Handler: Send promotion_code.created event
Handler->>Engine: processWebhookEvent(event)
Engine->>Stripe: Fetch promotion code details
Stripe-->>Engine: PromotionCode object
alt Backfill related entities
Engine->>Stripe: Fetch associated coupon
Stripe-->>Engine: Coupon object
Engine->>DB: upsertCoupons(coupon)
DB-->>Engine: Success
end
Engine->>DB: upsertPromotionCodes(promotionCode)
DB-->>Engine: Success
Engine-->>Handler: Event processed
Handler-->>Stripe: Webhook acknowledged
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/sync-engine/src/stripeSync.ts`:
- Around line 709-712: The code only handles webhook writes and single-entity
promo lookups via the promo_ branch calling this.upsertPromotionCodes, but omits
any bulk import/backfill paths for historical coupons and promotion codes; add
implementations for syncCoupons and syncPromotionCodes (or a generic
syncBackfill branch) inside the StripeSync class in stripeSync.ts that call the
Stripe list APIs, page through results, transform them into the same shape as
upsertPromotionCodes/upsertCoupons, and call the existing upsert methods to seed
a fresh database; ensure these methods are invoked from whatever backfill/sync
dispatcher currently handles other entities so historical coupons/promotion
codes are imported on initial sync.
- Around line 439-463: The webhook handler for promotion_code.created/updated
passes false into upsertPromotionCodes, which prevents the backfill logic from
running and therefore skips syncing referenced coupons when Stripe sends a
coupon ID string; change the call in the promotion code webhook branch to invoke
upsertPromotionCodes with backfill=true (or compute a boolean that enables
backfill for webhook events) so the backfill block in upsertPromotionCodes
(lines handling coupon backfill) runs; also ensure that upsertCoupons is invoked
or backfill logic will fetch by ID when coupon is a string—refer to
fetchOrUseWebhookData, getSyncTimestamp, upsertCoupons, and upsertPromotionCodes
to locate and adjust the behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 53f989de-5534-4325-95db-a2decadbefe7
📒 Files selected for processing (10)
README.mdpackages/fastify-app/src/test/helpers/mockStripe.tspackages/fastify-app/src/test/stripe/coupon_created.jsonpackages/fastify-app/src/test/stripe/promotion_code_created.jsonpackages/fastify-app/src/test/webhooks.test.tspackages/sync-engine/src/database/migrations/0043_promotion_codes.sqlpackages/sync-engine/src/schemas/coupon.tspackages/sync-engine/src/schemas/promotion_code.tspackages/sync-engine/src/stripeSync.tspackages/sync-engine/src/types.ts
| case 'promotion_code.created': | ||
| case 'promotion_code.updated': { | ||
| const { entity: promotionCode, refetched } = await this.fetchOrUseWebhookData( | ||
| event.data.object as Stripe.PromotionCode, | ||
| (id) => this.stripe.promotionCodes.retrieve(id) | ||
| ) | ||
|
|
||
| const syncTimestamp = this.getSyncTimestamp(event, refetched) | ||
|
|
||
| this.config.logger?.info( | ||
| `Received webhook ${event.id}: ${event.type} for promotionCode ${promotionCode.id}` | ||
| ) | ||
|
|
||
| const coupon = | ||
| (promotionCode as Stripe.PromotionCode & { coupon?: string | Stripe.Coupon | null }) | ||
| .coupon ?? | ||
| promotionCode.promotion?.coupon ?? | ||
| null | ||
|
|
||
| if (coupon && typeof coupon !== 'string') { | ||
| await this.upsertCoupons([coupon], syncTimestamp) | ||
| } | ||
|
|
||
| await this.upsertPromotionCodes([promotionCode], false, syncTimestamp) | ||
| break |
There was a problem hiding this comment.
Promotion code webhooks skip the related coupon/customer sync in the common case.
Line 462 passes false into upsertPromotionCodes, so the backfill logic on Lines 1566-1591 never runs for webhook events. The manual coupon sync on Lines 458-460 only executes when coupon is already an expanded object; when Stripe sends the usual string ID, the promotion code row is stored without syncing the referenced coupon row, which breaks the stated “sync coupon data used by promotion codes” behavior. As per coding guidelines, "Highlight only issues that could cause runtime errors, data loss, or severe maintainability issues."
Also applies to: 1566-1591
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/sync-engine/src/stripeSync.ts` around lines 439 - 463, The webhook
handler for promotion_code.created/updated passes false into
upsertPromotionCodes, which prevents the backfill logic from running and
therefore skips syncing referenced coupons when Stripe sends a coupon ID string;
change the call in the promotion code webhook branch to invoke
upsertPromotionCodes with backfill=true (or compute a boolean that enables
backfill for webhook events) so the backfill block in upsertPromotionCodes
(lines handling coupon backfill) runs; also ensure that upsertCoupons is invoked
or backfill logic will fetch by ID when coupon is a string—refer to
fetchOrUseWebhookData, getSyncTimestamp, upsertCoupons, and upsertPromotionCodes
to locate and adjust the behavior.
| } else if (stripeId.startsWith('promo_')) { | ||
| return this.stripe.promotionCodes | ||
| .retrieve(stripeId) | ||
| .then((it) => this.upsertPromotionCodes([it])) |
There was a problem hiding this comment.
Historical coupons/promotion codes still cannot be synced.
These additions only cover webhook writes/backfills and promo_ single-entity lookup. I don’t see any syncCoupons / syncPromotionCodes implementation or any syncBackfill branch for either entity in this file, so a fresh database will never import existing coupons or promotion codes from Stripe. That leaves the new feature incomplete unless every record arrives via webhook after deploy. As per coding guidelines, "Highlight only issues that could cause runtime errors, data loss, or severe maintainability issues."
Also applies to: 1420-1439, 1561-1627
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/sync-engine/src/stripeSync.ts` around lines 709 - 712, The code only
handles webhook writes and single-entity promo lookups via the promo_ branch
calling this.upsertPromotionCodes, but omits any bulk import/backfill paths for
historical coupons and promotion codes; add implementations for syncCoupons and
syncPromotionCodes (or a generic syncBackfill branch) inside the StripeSync
class in stripeSync.ts that call the Stripe list APIs, page through results,
transform them into the same shape as upsertPromotionCodes/upsertCoupons, and
call the existing upsert methods to seed a fresh database; ensure these methods
are invoked from whatever backfill/sync dispatcher currently handles other
entities so historical coupons/promotion codes are imported on initial sync.
Adds support for coupon and promotion code syncing by:
coupon.created,coupon.updated, andcoupon.deletedpromotion_code.createdandpromotion_code.updatedpromotion_codestable