Problem
The Stripe webhook answers 400 to any correctly signed event other than payment_intent.succeeded or payment_intent.payment_failed.
packages/payments-stripe/src/index.ts:577 returns UNKNOWN_EVENT for every other event type.
packages/plugin/src/webhooks/stripe-settle-route.ts:129-132 (settleResultToResponse) maps UNKNOWN_EVENT to 400, alongside INVALID_SIGNATURE and MALFORMED.
Stripe retries 4xx replies and, after repeated failures, emails the account owner and can disable the endpoint. Once it's disabled, payment_intent.succeeded stops arriving too, so no order can settle.
DEPLOYMENT.md never tells operators which events to subscribe the endpoint to. A store that subscribes to more than the two handled events (for example charge.refunded or charge.dispute.created) will therefore get these 400s.
Why it's safe to change
The event type is checked only after the signature is verified (index.ts:552, before normalizeEvent). Replying 200 to a verified event we don't handle tells an attacker nothing new. Bad signatures and malformed bodies should keep answering 400.
Proposed fix
- Map
UNKNOWN_EVENT to 200 (acknowledged, nothing done) in settleResultToResponse, with no change to the order.
- Add a line to
DEPLOYMENT.md listing the two events the endpoint must be subscribed to.
Related, out of scope here
Refunds made in the Stripe dashboard (charge.refunded) and disputes are not handled at all, so they never reach the order. That deserves its own issue if we want it.
Validation
- A unit test that
UNKNOWN_EVENT maps to status 200.
- A sandbox webhook test: a signed
charge.refunded event gets 200 and leaves the order unchanged.
- The existing
INVALID_SIGNATURE and MALFORMED tests still answer 400.
Found in a read-only audit of main @ 3264354.
Problem
The Stripe webhook answers 400 to any correctly signed event other than
payment_intent.succeededorpayment_intent.payment_failed.packages/payments-stripe/src/index.ts:577returnsUNKNOWN_EVENTfor every other event type.packages/plugin/src/webhooks/stripe-settle-route.ts:129-132(settleResultToResponse) mapsUNKNOWN_EVENTto 400, alongsideINVALID_SIGNATUREandMALFORMED.Stripe retries 4xx replies and, after repeated failures, emails the account owner and can disable the endpoint. Once it's disabled,
payment_intent.succeededstops arriving too, so no order can settle.DEPLOYMENT.mdnever tells operators which events to subscribe the endpoint to. A store that subscribes to more than the two handled events (for examplecharge.refundedorcharge.dispute.created) will therefore get these 400s.Why it's safe to change
The event type is checked only after the signature is verified (
index.ts:552, beforenormalizeEvent). Replying 200 to a verified event we don't handle tells an attacker nothing new. Bad signatures and malformed bodies should keep answering 400.Proposed fix
UNKNOWN_EVENTto 200 (acknowledged, nothing done) insettleResultToResponse, with no change to the order.DEPLOYMENT.mdlisting the two events the endpoint must be subscribed to.Related, out of scope here
Refunds made in the Stripe dashboard (
charge.refunded) and disputes are not handled at all, so they never reach the order. That deserves its own issue if we want it.Validation
UNKNOWN_EVENTmaps to status 200.charge.refundedevent gets 200 and leaves the order unchanged.INVALID_SIGNATUREandMALFORMEDtests still answer 400.Found in a read-only audit of
main@ 3264354.