engineering
Bug Report Writing
Write actionable bug reports with precise reproduction steps, expected vs actual behavior, environment details, severity classification, and root cause analysis templates.
bugsbug-reportsreproductiondebuggingquality-assurance
Works well with agents
Works well with skills
bug-report-writing/
payment-failure.md
Markdown| 1 | # [Checkout] Stripe payment fails silently when billing address contains accented characters |
| 2 | |
| 3 | ## Summary |
| 4 | |
| 5 | Users with accented characters in their billing address (e.g., "Rue de la Paix, 3e etage") see a blank error state after clicking "Pay now." No charge is created, but the user receives no feedback explaining the failure. First reported on March 11, 2026. Affects approximately 14% of EU-based customers. Revenue impact estimated at $38K/week in abandoned checkouts. |
| 6 | |
| 7 | ## Reproduction Steps |
| 8 | |
| 9 | 1. Log in as any customer with a saved EU billing address, or create a new account. |
| 10 | 2. Add any item to cart and proceed to `/checkout/payment`. |
| 11 | 3. Enter valid test card `4242 4242 4242 4242`, expiry `12/27`, CVC `123`. |
| 12 | 4. In the billing address field, enter: `Rue Francois-Gerard, Batiment C, 3eme etage`. |
| 13 | 5. Click "Pay now." |
| 14 | 6. Observe: the button spinner appears for ~2 seconds, then the page returns to the idle state. No success page, no error toast, no console error visible to the user. |
| 15 | |
| 16 | Reproduction rate: 10/10 when address contains characters with diacritical marks (e, a, u, etc.). Works correctly with ASCII-only addresses. |
| 17 | |
| 18 | ## Expected vs Actual Behavior |
| 19 | |
| 20 | ``` |
| 21 | Expected: Payment is processed successfully regardless of address characters. User sees the confirmation page. |
| 22 | Actual: The Stripe API call returns a 400 error due to unescaped Unicode in the address payload. The frontend catches the error but renders nothing — the catch block calls `setLoading(false)` without setting an error message. |
| 23 | ``` |
| 24 | |
| 25 | ## Environment |
| 26 | |
| 27 | - **App version**: v2.14.3 (commit `a8f2c1d`) |
| 28 | - **API environment**: Production (also reproducible on staging) |
| 29 | - **Browser**: Chrome 124, Firefox 125, Safari 17.4 (all affected) |
| 30 | - **OS**: macOS 14.4, Windows 11, iOS 17.4 |
| 31 | - **Stripe SDK**: `@stripe/stripe-js` v3.1.0 |
| 32 | - **Feature flags**: `new_checkout_flow=true` (enabled for 100% of users since March 8) |
| 33 | |
| 34 | ## Severity |
| 35 | |
| 36 | **Critical** — Active revenue loss ($38K/week), no automated workaround, affects all EU customers with accented characters in their address. Users are not told the payment failed, so they assume the order went through and receive nothing. |
| 37 | |
| 38 | ## Screenshots / Logs |
| 39 | |
| 40 | **Server log (sanitized)**: |
| 41 | ``` |
| 42 | 2026-03-11T14:22:07Z ERROR stripe_client: PaymentIntent creation failed |
| 43 | error_type: invalid_request_error |
| 44 | message: "Invalid characters in billing_details.address.line1" |
| 45 | customer_id: cus_***redacted*** |
| 46 | request_id: req_8fKz2mNpQx |
| 47 | ``` |
| 48 | |
| 49 | **Frontend**: No error rendered. Network tab shows the `/api/create-payment-intent` endpoint returning HTTP 400, but the response body is swallowed by the catch block in `useCheckout.ts:87`. |
| 50 | |
| 51 | ## Root Cause Analysis |
| 52 | |
| 53 | - **Root cause**: The `formatAddress()` utility in `lib/checkout/address.ts:42` passes raw Unicode to the Stripe API without normalizing. Stripe's API rejects certain combining characters in address fields. The frontend catch block at `useCheckout.ts:87` calls `setLoading(false)` but does not call `setError()`. |
| 54 | - **Fix**: Normalize address strings with `String.normalize('NFC')` before sending to Stripe. Add error state handling in the catch block to display the error toast with a retry prompt. |
| 55 | - **Prevention**: Add integration tests with non-ASCII address fixtures. Add a global error boundary for payment API calls that guarantees user-visible feedback on any failure. |
| 56 |