payloadcms/payload · error · Error

No transaction found for the provided PaymentIntent ID

Error message

No transaction found for the provided PaymentIntent ID

What it means

Thrown by Stripe confirmOrder after querying the transactions collection by stripe.paymentIntentID — if no transaction document is found (totalDocs === 0 or no doc returned), the PaymentIntent ID is unknown to the system. This means initiatePayment never persisted a transaction for that intent, or the transactions slug/name differs. It separates 'Stripe knows this intent' from 'the local system recorded it'.

Source

Thrown at packages/plugin-ecommerce/src/payments/adapters/stripe/confirmOrder.ts:74

          email: customerEmail,
        })
      }

      // Find our existing transaction by the payment intent ID
      const transactionsResults = await payload.find({
        collection: transactionsSlug,
        req,
        where: {
          'stripe.paymentIntentID': {
            equals: paymentIntentID,
          },
        },
      })

      const transaction = transactionsResults.docs[0]

      if (!transactionsResults.totalDocs || !transaction) {
        throw new Error('No transaction found for the provided PaymentIntent ID')
      }

      const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentID)

      if (paymentIntent.status !== 'succeeded') {
        throw new Error(`Payment not completed.`)
      }

      const cartID = paymentIntent.metadata.cartID
      const cartItemsSnapshot = paymentIntent.metadata.cartItemsSnapshot
        ? JSON.parse(paymentIntent.metadata.cartItemsSnapshot)
        : undefined

      const shippingAddress = paymentIntent.metadata.shippingAddress
        ? JSON.parse(paymentIntent.metadata.shippingAddress)
        : undefined

      if (!cartID) {

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Ensure initiatePayment ran to completion for this paymentIntentID and a transaction document exists in the transactions collection.
  2. Confirm the transactionsSlug is identical for both initiatePayment and confirmOrder (default 'transactions').
  3. Inspect the transactions collection for stripe.paymentIntentID === <id> to verify persistence.
  4. If the intent was created outside the plugin, recreate it via initiatePayment so a transaction is recorded.

Example fix

// before: confirm an intent created outside the plugin
await payments.confirmOrder({ data: { paymentIntentID: externalIntentId } })
// after: always go through initiatePayment first so a transaction row exists
const { paymentIntentID } = await payments.initiatePayment({ data: cartData })
// ...user pays with Stripe checkout...
await payments.confirmOrder({ data: { paymentIntentID } })
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: confirm a transaction exists for this intent before calling confirmOrder
async function transactionExistsForIntent(payload, transactionsSlug: string, paymentIntentID: string): Promise<boolean> {
  const res = await payload.find({
    collection: transactionsSlug,
    where: { 'stripe.paymentIntentID': { equals: paymentIntentID } },
    limit: 1,
  })
  return res.totalDocs > 0
}

if (!(await transactionExistsForIntent(payload, 'transactions', id))) {
  throw new Error('No local transaction; run initiatePayment first')
}

Type guard

export function hasLocalTransaction(res: { totalDocs: number; docs: unknown[] }): boolean {
  return res.totalDocs > 0 && res.docs.length > 0
}

Try / catch

try {
  await payments.confirmOrder({ data: { paymentIntentID } })
} catch (err) {
  if (err instanceof Error && err.message === 'No transaction found for the provided PaymentIntent ID') {
    // intent is foreign or initiate didn't persist — re-run initiate or surface to support
    return { ok: false, reason: 'orphan-intent' }
  }
  throw err
}

Prevention

When it happens

Trigger: confirmOrder is called with a paymentIntentID that was never created via this plugin's initiatePayment (e.g. an intent created directly in the Stripe dashboard or a different integration), or the transaction was deleted, or transactionsSlug passed to confirmOrder differs from the one used in initiatePayment.

Common situations: Mixing Stripe dashboards / accounts between initiate and confirm; running confirm against a test intent from the Stripe UI; the transactions collection slug was customized on one side but not the other; database reset between the two steps; race condition where confirm runs before initiate's transaction create commits.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/2adf0b876be4073f. Report an issue: GitHub.