stripe checkout improvement
_⚠️ Potential issue_ | _🔴 Critical_
**Missing idempotency guard — duplicate webhook deliveries will double-credit the organization.**
Stripe may deliver `checkout.session.completed` more than once. This handler unconditionally adds credits and inserts a transaction without checking for a prior completed transaction for the same session/payment intent.
Compare with the subscription checkout handler (Lines 306-314, 406-414) which checks for an existing transaction by `stripeInvoiceId` before inserting. This handler should apply the same pattern using `stripePaymentIntentId` or the session ID.
<details>
<summary>Proposed fix — add deduplication check before credit insertion</summary>
```diff
const stripePaymentIntentId = session.payment_intent as string | undefined;
+ // Check for existing transaction to prevent duplicate processing
+ if (stripePaymentIntentId) {
+ const existing = await db.query.transaction.findFirst({
+ where: {
+ stripePaymentIntentId: { eq: stripePaymentIntentId },
+ type: { eq: "credit_topup" },
+ status: { eq: "completed" },
+ },
+ });
+ if (existing) {
+ logger.info(
+ `Credit top-up transaction already exists for payment intent ${stripePaymentIntentId}; skipping duplicate`,
+ );
+ return;
+ }
+ }
+
const [completedTransaction] = await db
.insert(tables.transaction)
```
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
```
Verify each finding against the current code and only fix it if needed.
In `@apps/api/src/stripe.ts` around lines 560 - 589, Before updating organization
credits and inserting the transaction, add an idempotency/deduplication check
that queries tables.transaction for an existing completed record matching the
Stripe identifier (use stripePaymentIntentId or session.id) and skip the credit
update/insert if found; specifically, before the block that updates
tables.organization and calls db.insert(tables.transaction) create a lookup
(e.g., select where stripePaymentIntentId = stripePaymentIntentId OR
metadata/sessionId = session.id) and return early if a completed transaction
exists, ensuring the handler (session, stripePaymentIntentId,
tables.transaction, db.insert) does not double-credit on repeated webhook
deliveries.
```
</details>
<!-- fingerprinting:phantom:medusa:phoenix -->
<!-- This is an auto-generated comment by CodeRabbit -->
_Originally posted by @coderabbitai[bot] in https://github.com/theopenco/llmgateway/pull/1686#discussion_r2820750852_
关闭于 2026-02-22 0 条评论