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
- Set up the ecommerce plugin with Stripe (PayPal enabled)
- Create a cart and initiate payment
- On Stripe's PayPal test page, click "Fail payment"
- Stripe redirects back to the
return_url with redirect_status=failed
- The client calls
/api/payments/stripe/confirm-order
- 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."
Bug
The Stripe adapter's
confirmOrderhandler in@payloadcms/plugin-ecommerceretrieves the PaymentIntent but never checkspaymentIntent.statusbefore creating an order. This means a failed or canceled payment (e.g. PayPal test failure) still results in:purchasedAt)succeededSource
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
statusis never checked before proceeding to create an order at line 58.Reproduction
return_urlwithredirect_status=failed/api/payments/stripe/confirm-orderExpected Behavior
confirmOrdershould verifypaymentIntent.status === 'succeeded'before creating an order. For any other status, it should return an error.Suggested Fix
Stripe Documentation
Stripe explicitly recommends verifying payment status server-side before fulfillment: