intermediate
shop-rentals
stripe
troubleshooting

Stripe Payment Failures

Common decline codes, when to retry, when to switch destinations, and how to handle disputes for shop rental payments

Levy Fleets TeamMay 7, 20265 min read

Things go wrong with cards. This guide covers the common failure modes in Shop Rentals and how to handle each.

Decline codes you'll see

CodeMeaningAction
card_declined (insufficient_funds)Customer's card has no balanceAsk them to use another card or refund/cancel
card_declined (do_not_honor)Generic decline; bank's risk system flaggedTry a different card, or contact bank
card_declined (lost_card / stolen_card)Card reported lost/stolenRefuse the rental; tell customer to replace card
expired_cardCard past expirationUpdate on customer profile, retry
incorrect_cvcCVC didn't matchRe-enter at the iPad
processing_errorStripe transient issueRetry after 1-2 min
authentication_required3DS / SCA challenge neededCustomer needs to complete the challenge on their phone
card_not_supportedCard type isn't accepted (e.g., debit-only on a credit-only flow)Use a different card

Test cards (demo subaccount only)

CardBehavior
4242 4242 4242 4242Succeeds
4000 0027 6000 3184Requires 3DS authentication
4000 0000 0000 9995Declined — insufficient funds
4000 0000 0000 0002Declined — generic
4000 0000 0000 0341Initial succeed, attach fails

Demo subaccount automatically routes to test mode via per-subaccount Stripe routing — see the Stripe per-subaccount routing internal doc.

SetupIntent vs PaymentIntent

The walk-in and customer flows use SetupIntents to attach a card to the customer for later off-session charges. Different from a one-time charge:

  • SetupIntent doesn't transfer money — it just authorizes the card for future use
  • The customer enters card details, the SetupIntent is "confirmed"
  • The resulting paymentMethodId is attached to the customer
  • Future charges (deposits, late fees, damage) use this saved PM off-session

If a SetupIntent fails:

  • Re-create with create_setup_intent action
  • Have customer re-enter the card via Stripe Elements
  • Common cause: customer typed the wrong CVC or card number

Off-session charge declines

When you charge the saved card off-session (operator-initiated, no customer present):

  • Insufficient funds — common; the card was good when added but the balance dropped. Reach out to the customer.
  • Authentication required — 3DS challenge can't be completed off-session. Set up a payment method update flow that prompts the customer to re-authenticate.
  • Card declined (generic) — bank's risk algorithm flagged. Try a different card.

Webhook failures

Webhook signature verification failures show up as 400 errors in the logs at /api/webhooks/stripe. Two common reasons:

  1. Wrong secret in STRIPE_WEBHOOK_SECRET — re-pull from Stripe Dashboard
  2. Test events arriving without STRIPE_WEBHOOK_SECRET_TEST set — ensure the test secret is populated when running demo subaccount tests

The handler now tries both secrets (live first, then test). If both fail, the request is rejected with "Invalid signature."

Refund failures

ErrorMeaningAction
"No eligible Stripe charges found"Customer paid via wallet onlyRefund to wallet instead
"Cannot refund directly to card while customer has negative wallet balance"Customer owes their walletRefund to wallet first, or zero out manually
"Unable to refund full requested amount with available charges"Multiple original charges, not enough remainingRefund what's available; partial-refund the rest separately
"Invalid signature" on refund webhooksWebhook secret mismatchRe-verify config

Dispute / chargeback

When a customer files a chargeback:

  1. Stripe sends a charge.dispute.created webhook
  2. The handler logs it and may auto-debit the customer's wallet (per project config)
  3. You have a defined window to respond with evidence (usually 7-21 days)

Evidence to gather:

  • The signed waiver (PDF)
  • The receipt PDF
  • The booking timeline (created → confirmed → checked_in → completed)
  • Any service log entries (damage photos, etc.)
  • The customer's prior rental history (good rep, repeat customer)
  • The cancellation policy text shown at checkout

The dashboard's Disputes section consolidates these into a submission package. See the dispute evidence article.

Auto-topup interactions

If your shop charges customers via auto-topup (wallet) and the auto-topup fails, the customer's wallet may go negative. This blocks new bookings and refunds-to-card. Resolution:

  1. Have the customer update their card via the manage flow
  2. Re-trigger the auto-topup
  3. Once positive, the customer can book again

For shops doing pure direct-to-card billing, this isn't an issue.

When Stripe is down

Stripe rarely has total outages, but partial failures happen. During an outage:

  • New bookings via the public page can't collect cards → bookings fail at the create step
  • Refunds time out
  • The walk-in wizard can still create reservations (no card collection in the wizard yet); deposits can be attempted but may fail

Watch status.stripe.com and queue operations to retry once recovered.

Logging

All Stripe API failures are logged to Sentry with context. To dig in:

  1. Go to Sentry → search for [ride refunds] or [reservation refunds]
  2. Filter by user/customer if known
  3. Read the full error trace including Stripe's response body

For unmonitored failures (e.g., a webhook that didn't trigger Sentry), check the Vercel function logs at the relevant route.

When to escalate to support

  • Persistent webhook signature failures despite re-pulling the secret
  • Unexplained idempotency conflicts on wallet credits
  • Refund flows that succeed in Stripe but don't update locally
  • Per-subaccount routing returning the wrong client mode

These point to deeper config issues. Open a support ticket with the Stripe event ID and the booking number.