Skip to content

plugin-ecommerce: confirmOrder creates order without verifying PaymentIntent status #15862

@jhb-dev

Description

@jhb-dev

Bug

The Stripe adapter's confirmOrder handler in @payloadcms/plugin-ecommerce retrieves the PaymentIntent but never checks paymentIntent.status before creating an order. This means a failed or canceled payment (e.g. PayPal test failure) still results in:

  • An order document being created
  • The cart marked as purchased (purchasedAt)
  • The transaction set to succeeded

Source

https://github.com/payloadcms/payload/blob/main/packages/plugin-ecommerce/src/payments/adapters/stripe/confirmOrder.ts

The PaymentIntent is retrieved at line 48 but its status is never checked before proceeding to create an order at line 58.

Reproduction

  1. Set up the ecommerce plugin with Stripe (PayPal enabled)
  2. Create a cart and initiate payment
  3. On Stripe's PayPal test page, click "Fail payment"
  4. Stripe redirects back to the return_url with redirect_status=failed
  5. The client calls /api/payments/stripe/confirm-order
  6. An order is created in the CMS despite the payment having failed

Expected Behavior

confirmOrder should verify paymentIntent.status === 'succeeded' before creating an order. For any other status, it should return an error.

Suggested Fix

 const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentID)
+
+if (paymentIntent.status !== 'succeeded') {
+  throw new Error(`Payment not completed. Status: ${paymentIntent.status}`)
+}
+
 const cartID = paymentIntent.metadata.cartID

Stripe Documentation

Stripe explicitly recommends verifying payment status server-side before fulfillment:

"Do not attempt to handle order fulfillment on the client side, as customers may leave the page after payment completes but before fulfillment begins."

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions