fix(billing): defers succeeded payment intent status update to process payments reliably#2552
fix(billing): defers succeeded payment intent status update to process payments reliably#2552ygrishajev merged 1 commit intomainfrom
Conversation
📝 WalkthroughWalkthroughCreate an internal DB transaction before calling Stripe, add Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as API / Billing Service
participant DB as Transactions DB
participant Stripe
Client->>API: createPaymentIntent(request)
API->>DB: create internal transaction (status: pending)
DB-->>API: internalTransaction{id}
API->>Stripe: PaymentIntent.create(metadata: {..., internal_transaction_id: id})
Stripe-->>API: PaymentIntent(response, status)
alt status NOT in DEFERRED_STATUSES
API->>DB: updateById(id, {stripePaymentIntentId, status})
else status in DEFERRED_STATUSES
API->>DB: updateById(id, {stripePaymentIntentId})
end
Note over Stripe,API: Later: Stripe sends webhook with metadata.internal_transaction_id
Stripe->>API: webhook(event)
API->>DB: findById(internal_transaction_id)
DB-->>API: existingTransaction
API->>DB: updateById(existingTransaction.id, {stripePaymentIntentId, status, receiptUrl, payment details...})
DB-->>API: updatedTransaction
API-->>Client: emit/return final status
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/api/src/billing/services/stripe/stripe.service.ts`:
- Around line 227-233: The transaction can be missing stripePaymentIntentId when
a webhook races in; to fix, persist stripePaymentIntentId on the transaction
before calling Stripe (set the field in the same code path that builds the
PaymentIntent metadata using stripeTransactionRepository.updateById with
stripePaymentIntentId populated), add a fallback lookup in the webhook handler
so findByPaymentIntentId() falls back to finding by userId + customerId when
missing, and make updateStatusByPaymentIntentId() return/throw on failure so the
caller (the code that calls topUpWallet) checks the return value and
aborts/retries and logs an error instead of proceeding to topUpWallet when the
status update did not succeed.
Codecov Report❌ Patch coverage is
❌ Your project status has failed because the head coverage (79.30%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #2552 +/- ##
==========================================
- Coverage 50.77% 50.48% -0.29%
==========================================
Files 1070 1060 -10
Lines 29718 29380 -338
Branches 6587 6551 -36
==========================================
- Hits 15089 14833 -256
+ Misses 14217 14135 -82
Partials 412 412
*This pull request uses carry forward flags. Click here to find out more.
🚀 New features to boost your workflow:
|
41d6bf5 to
aa00199
Compare
aa00199 to
65aeead
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/api/src/billing/services/stripe-webhook/stripe-webhook.service.ts`:
- Around line 92-97: The code currently uses assert(existingTransaction, 500,
...) which will throw when a matching transaction isn't found; change this to a
graceful early exit: after resolving existingTransaction (from
paymentIntent.metadata.internal_transaction_id ?
this.stripeTransactionRepository.findById(...) :
this.stripeTransactionRepository.findByPaymentIntentId(...)), check if
existingTransaction is falsy, log a warning (e.g., this.logger.warn or similar)
describing the missing internal transaction for the given
paymentIntent.id/metadata.internal_transaction_id and return from the webhook
handler (do not throw) so Stripe receives a 2xx and the event is skipped for
intents that never created internal transactions.
63ebc69 to
6ff1a6d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/api/src/billing/services/stripe-webhook/stripe-webhook.service.spec.ts`:
- Around line 120-144: The test for tryToTopUpWalletFromPaymentIntent is missing
an assertion that stripeTransactionRepository.updateById is called to mark the
internal transaction as "succeeded" with charge details; update the test to
include latest_charge in the created PaymentIntent event and mock
stripeService.charges.retrieve to return a charge object (id,
payment_method_details.card.brand, payment_method_details.card.last4,
receipt_url), then assert stripeTransactionRepository.updateById was called with
the internalTransaction.id and an update payload containing status: "succeeded"
plus stripeChargeId, cardBrand, cardLast4, and receiptUrl (and keep existing
asserts for refillService.topUpWallet and findById).
apps/api/src/billing/services/stripe-webhook/stripe-webhook.service.spec.ts
Show resolved
Hide resolved
31770d2 to
975e16e
Compare
…s payments reliably
975e16e to
9b146f6
Compare
We assign credits via webhook which in turn skips this is transaction is succeeded already. So succeeded status should be assigned by a webhook handler
Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.