Payment Bugs

Payment Succeeds but Order Remains Pending

The payment provider confirms a successful charge, but the application's order status is not updated from 'pending' to 'paid'. This leaves the order stuck — fulfilment is blocked, the customer is billed but receives nothing, and the only evidence of payment is in the payment provider's dashboard.

CriticalIntermediateAPI testingManual testingExploratory testing

// UNDERSTAND

// Symptoms

  • The payment provider dashboard shows a successful charge, but the order page shows 'pending'
  • The customer receives a bank or card statement entry but no order confirmation email from the application
  • Customer support receives tickets for orders that are charged but stuck in pending
  • Fulfilment and shipping are not triggered because the order never transitions to 'paid'

// Root Cause

  • The application relies solely on receiving the payment provider's webhook event to update the order status, but the webhook is not delivered, is rejected (e.g. signature verification failure), or the database write in the webhook handler fails
  • A race condition or unhandled exception in the synchronous payment-response handler causes the order-status write to fail even though the charge succeeded — leaving the order in its pre-payment state despite a confirmed charge
  • Without a periodic reconciliation job comparing local order statuses against the provider's charge records, stuck-pending orders can remain undetected indefinitely

// Where It Appears

  • E-commerce checkout flows that rely on webhook-based payment confirmation
  • Subscription billing systems where payment events are asynchronous
  • Any payment integration where order state is managed separately from the charge record
  • Environments where webhook endpoint availability is unreliable (e.g. staging with intermittent downtime)

// REPRODUCE & TEST

// How to Reproduce

  1. 01Create a test order and proceed to checkout; note the order ID (e.g. 456)
  2. 02In the staging environment, configure the webhook endpoint to return HTTP 500 (or block it) so payment events are not processed
  3. 03Complete the payment using a valid test card
  4. 04Confirm the charge appears in the payment provider's test dashboard
  5. 05Check the order status in the application by navigating to the order detail page or sending GET /api/orders/456
  6. 06Observe that the order status remains 'pending' despite the confirmed charge

// Test Data Needed

  • A test account with a valid test card (e.g. Stripe test card 4242 4242 4242 4242)
  • Ability to disable or block the webhook endpoint in the staging environment
  • Access to the payment provider's test dashboard to confirm the charge succeeded
  • The order ID to query the resulting status

// Manual Testing Ideas

  • Temporarily configure the webhook endpoint to return 500; complete a payment and verify the order remains pending
  • Complete a payment and immediately check the order status before and after the webhook fires to observe the transition
  • Manually resend a missed webhook event from the payment provider's test dashboard and verify the order status updates
  • Check application logs for webhook signature verification failures that would cause events to be silently discarded
  • Test whether the application has a polling or reconciliation job that detects and repairs stuck-pending orders
  • Trigger a payment timeout via network throttle to test the synchronous fallback path

// API Testing Ideas

  • Authenticate and create a test order; capture the order ID (e.g. 456) and confirm the initial status is 'pending' via GET /api/orders/456
  • Submit a payment for order 456 via the payment provider's test API and confirm the charge status is 'succeeded'
  • Immediately send GET /api/orders/456 and assert the order status field is 'paid' — not 'pending'
  • If the status is still 'pending', send a POST to the webhook endpoint with a correctly signed payment.succeeded event body for order 456 — use the Stripe CLI (stripe trigger payment_intent.succeeded) or construct the HMAC-SHA256 signature with the webhook secret per Stripe's signing documentation
  • Send GET /api/orders/456 again and assert the status is now 'paid'
  • Assert the status is 'paid'; if it stays 'pending' after a valid signed event, the webhook handler is failing to process it — check the handler's return status and application logs

// Automation Idea

Automate an end-to-end sandbox payment flow: create an order, process a charge via the provider's test API, then immediately GET the order status and assert it is 'paid'. Separately, test the webhook path by sending a mock payment.succeeded event to the webhook endpoint and asserting the order transitions to 'paid'.

// Expected Result

After a successful charge confirmed by the payment provider, the application updates the order status from 'pending' to 'paid' within the expected processing window.

// Actual Result (Example)

After completing payment for order 456, the Stripe test dashboard shows a successful charge, but GET /api/orders/456 returns { "status": "pending" } — the order was never updated.

// REPORT IT

Example Bug Report

Title
Order remains pending after successful payment charge
Severity
Critical
Environment
Staging environment Chrome 124 Stripe test card 4242 4242 4242 4242 Order 456
Steps to Reproduce
  1. 01Create a test order and proceed to checkout; note the order ID (456)
  2. 02Configure the webhook endpoint to return HTTP 500 in staging
  3. 03Complete the payment using the test card
  4. 04Confirm the charge appears as 'succeeded' in the Stripe test dashboard
  5. 05Send GET /api/orders/456 and observe the status field
Expected Result
The order status is 'paid' after the payment charge succeeds.
Actual Result
GET /api/orders/456 returns { "status": "pending" } despite the Stripe test dashboard showing a successful charge.
Impact
Customers are billed but their orders are not fulfilled — at scale this creates significant manual reconciliation work, refund liability, and loss of customer trust.

// RELATED