← Back to report Page 3 of 3
Full Transcript
Stripe - Sonnet Transcript
Report: Stripe - Universal Baseline Model: Sonnet 4.6 Tested: 2026-03-24 Agent CLI: Claude Code v2.1.81
Task 4
Error handling
OperatorStep 1
Using the Stripe test mode API, do the following: 1. Attempt to create a Payment Intent with an invalid currency code 2. Attempt to charge a test card that is configured to be declined (find the right test card number from Stripe's docs) 3. Attempt to retrieve a customer that doesn't exist For each error: show me the full error response, explain whether the error message gave you enough information to understand what went wrong, and describe what you would do to recover.
AgentStep 2
First, let me pull the declined card number from Stripe's docs rather than rely on memory.
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing#declined-payments",
"prompt": "List the test card numbers for declined payments — specifically the generic decline card and any others that simulate specific decline reasons. Include the card number and the decline reason for each."
}WebFetchTool Result
Result
Show output
# Test Card Numbers for Declined Payments Based on the provided documentation, the page mentions that test cards can simulate "Kartenfehler aufgrund von Ablehnungen" (card errors due to declines), but **the specific declined payment test card numbers and their corresponding decline reasons are not included in the content provided**. The documentation states that test cards enable simulation of various scenarios including declines, but the actual table or list with declined card numbers appears to be truncated or missing from the excerpt you shared. The content cuts off before reaching sections that would detail specific decline test cases. To access this information, you would need to view the complete Stripe testing documentation, likely in sections covering "declined payments" or "payment error scenarios."
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing",
"prompt": "List the test card numbers that simulate declined payments. I need the actual card number (16 digits) and the decline reason/code for each declined card scenario. Include any generic decline card and specific decline codes like insufficient_funds, card_declined, etc."
}WebFetchTool Result
Result
Show output
# Test Card Numbers for Declined Payments Based on the provided documentation, **no specific test card numbers for declined payments are listed**. The document mentions that declined payment scenarios exist: "Kartenfehler aufgrund von Ablehnungen" (card errors due to rejections) are available for testing, with a reference link to `https://docs.stripe.com/testing.md#declined-payments`. However, the actual card numbers, decline codes, and specific scenarios for testing declined payments are **not included in the content provided**. To access this information, you would need to consult the full Stripe testing documentation at the referenced URL, which appears to contain a dedicated section on simulating declined transactions.
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing.md",
"prompt": "List every test card number that simulates a declined payment. Include the full 16-digit card number and the associated decline code or reason for each one."
}WebFetchTool Result
Result
Show output
Based on the provided content, **no test card numbers that simulate declined payments are listed**.
The document mentions that declined payment scenarios exist ("Kartenfehler aufgrund von Ablehnungen") and references a section on declined payments, but the actual test card numbers for declined transactions are not included in the excerpt provided. The card numbers shown are all for successful payments across various card brands and countries.
To find declined payment test cards, you would need to access the complete Stripe testing documentation, specifically the section referenced as "declined-payments."WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing#cards",
"prompt": "Show me all test card numbers listed on this page, especially those for declined or failed payments. Include the exact card number, description, and any error code associated with each card."
}WebFetchTool Result
Result
Show output
# Test Card Numbers for Failed/Declined Payments Based on the provided documentation, **the page does not list specific test card numbers for declined or failed payments with associated error codes.** The document mentions that test cards can simulate "Kartenfehler aufgrund von Ablehnungen" (card errors due to declines), "Betrug" (fraud), or "ungültigen Daten" (invalid data), but it does not provide the actual card numbers or error codes for these scenarios. The page only provides: 1. **Successful payment test cards** organized by card brand (Visa, Mastercard, Amex, etc.) and country 2. **PaymentMethod identifiers** for testing (e.g., `pm_card_visa`) 3. **Legacy tokens** (e.g., `tok_visa`) For declined payment testing, the documentation references other sections: "Kartenfehler aufgrund von [Ablehnungen](https://docs.stripe.com/testing.md#declined-payments)" but those detailed instructions are not included in the provided content. To access test cards for declined payments, you would need to consult the linked sections in Stripe's full testing documentation.
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing#declined-payments",
"prompt": "What is the full 16-digit card number for the generic declined card test case? Also list any other declined card numbers with their specific decline codes (e.g. insufficient_funds, lost_card, stolen_card, etc). Show the raw table data if present."
}WebFetchTool Result
Result
Show output
# Declined Test Cards Information The provided documentation does not contain information about a generic declined card test case or a specific declined card table with decline codes (insufficient_funds, lost_card, stolen_card, etc.). The page mentions that test cards can simulate "Kartenfehler aufgrund von Ablehnungen" (card errors due to declines), but the actual table with declined card numbers and their corresponding decline codes is not included in the excerpt provided. To find this information, you would need to access the complete Stripe testing documentation, likely in sections labeled for declined payment scenarios or fraud prevention testing.
WebFetchTool Use
Input
Show input
{
"url": "https://stripe.com/docs/testing",
"prompt": "List all test card numbers for declined payments. Include the 16-digit number and decline reason. Also show any successful test card numbers."
}WebFetchTool Result
Result
Show output
REDIRECT DETECTED: The URL redirects to a different host. Original URL: https://stripe.com/docs/testing Redirect URL: https://docs.stripe.com/testing Status: 301 Moved Permanently To complete your request, I need to fetch content from the redirected URL. Please use WebFetch again with these parameters: - url: "https://docs.stripe.com/testing" - prompt: "List all test card numbers for declined payments. Include the 16-digit number and decline reason. Also show any successful test card numbers."
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing.md#declined-payments",
"prompt": "Return the raw markdown content for the declined-payments section. I need the actual card numbers (16-digit strings) used to simulate declined payments, along with their decline codes."
}WebFetchTool Result
Result
Show output
I've reviewed the provided content, but the document does not contain a dedicated "declined-payments" section with specific card numbers and decline codes for simulating declined payments. The document mentions that test cards can simulate "Kartenfehler aufgrund von Ablehnungen" (card errors due to declines), but the actual section with card numbers and decline codes is not included in the excerpt you provided. To find this information, you would need to access the complete Stripe testing documentation, specifically the section covering declined payment scenarios, which would contain the 16-digit test card numbers and their associated decline codes.
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing#visa",
"prompt": "What card numbers are listed for testing? Show ALL rows from any tables on the page verbatim, including card number, description, and any codes. Do not summarize — return table contents as-is."
}WebFetchTool Result
Result
Show output
# Test Card Numbers from Stripe Documentation Here are all card numbers listed in the tables: ## By Card Brand (Kartennummern) | Brand | Number | CVC | Date | |-------|--------|-----|------| | Visa | 4242424242424242 | Any 3 digits | Any future date | | Visa (Debit) | 4000056655665556 | Any 3 digits | Any future date | | Mastercard | 5555555555554444 | Any 3 digits | Any future date | | Mastercard (2er-Reihe) | 2223003122003222 | Any 3 digits | Any future date | | Mastercard (Debit) | 5200828282828210 | Any 3 digits | Any future date | | Mastercard (Prepaid) | 5105105105105100 | Any 3 digits | Any future date | | American Express | 378282246310005 | Any 4 digits | Any future date | | American Express | 371449635398431 | Any 4 digits | Any future date | | Discover | 6011111111111117 | Any 3 digits | Any future date | | Discover | 6011000990139424 | Any 3 digits | Any future date | | Discover (Debit) | 6011981111111113 | Any 3 digits | Any future date | | Diners Club | 3056930009020004 | Any 3 digits | Any future date | | Diners Club (14-digit) | 36227206271667 | Any 3 digits | Any future date | | BCcard/DinaCard | 6555900000604105 | Any 3 digits | Any future date | | JCB | 3566002020360505 | Any 3 digits | Any future date | | UnionPay | 6200000000000005 | Any 3 digits | Any future date | | UnionPay (Debit) | 6200000000000047 | Any 3 digits | Any future date | | UnionPay (19-digit) | 6205500000000000004 | Any 3 digits | Any future date | ## Co-Branded Cards (Kartennummern) | Brand/Co-Brand | Number | CVC | Date | |---|---|---|---| | Cartes Bancaires/Visa | 4000002500001001 | Any 3 digits | Any future date | | Cartes Bancaires/Mastercard | 5555552500001001 | Any 3 digits | Any future date | | EFTPOS Australia/Visa | 4000050360000001 | Any 3 digits | Any future date | | EFTPOS Australia/Mastercard | 5555050360000080 | Any 3 digits | Any future date | ## By Country (Selection) | Country | Number | Brand | |---------|--------|-------| | United States | 4242424242424242 | Visa | | Argentina | 4000000320000021 | Visa | | Brazil | 4000000760000002 | Visa | | Canada | 4000001240000000 | Visa | | Mexico | 4000004840008001 | Visa | | UK | 4000008260000000 | Visa | | Australia | 4000000360000006 | Visa | | Japan | 4000003920000003 | Visa |
AgentStep 3
The docs page only returned the success cards table. Let me try fetching the raw Stripe API reference directly for test payment methods, which includes declined card tokens:
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/testing#use-test-cards",
"prompt": "What PaymentMethod token or card number should I use to simulate a declined payment in test mode? Show any pm_card_* tokens and card numbers that cause declines, including the token name and decline code."
}WebFetchTool Result
Result
Show output
Based on the provided documentation, I cannot find specific PaymentMethod tokens or card numbers designated for simulating declined payments in test mode. The document mentions that test cards can simulate "Kartenfehler aufgrund von Ablehnungen" (card errors due to declines), but it does not list the actual payment method tokens or card numbers that trigger decline responses. To test declined payment scenarios, you would need to consult additional Stripe documentation sections specifically covering decline testing (referenced as links like `https://docs.stripe.com/testing.md#declined-payments`), which are not included in this excerpt.
WebFetchTool Use
Input
Show input
{
"url": "https://docs.stripe.com/api/payment_methods/object#payment_method_object-type",
"prompt": "List any test payment method tokens for declined cards, such as pm_card_chargeDeclined or similar. What tokens does Stripe provide for simulating declined payments?"
}WebFetchTool Result
Result
Show output
Based on the provided documentation, **no test payment method tokens for declined cards are listed**. The content describes the PaymentMethod object structure and attributes comprehensively, but does not include a section on test tokens or simulated declined payment scenarios. To find Stripe's test card numbers and decline simulation tokens (like `pm_card_chargeDeclined`), you would need to consult Stripe's testing documentation or API guides, which are not included in this excerpt.
AgentStep 4
The Stripe docs page appears to be dynamically rendered — the table with declined cards isn't coming through in any fetch attempt. I'll use the Stripe API itself as the source of truth: I'll probe `pm_card_chargeDeclined` (a known Stripe test fixture named in their API reference), and the response will confirm whether it's valid. Now running all three error scenarios: --- ### Error 1 — Invalid currency code
BashTool Use
Input
Show input
{
"command": "curl -s https://api.stripe.com/v1/payment_intents \\\n -u sk_test_[REDACTED]: \\\n -d amount=9900 \\\n -d currency=xyz \\\n | python3 -m json.tool",
"description": "Attempt to create a PaymentIntent with invalid currency code \"xyz\"",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
{
"error": {
"message": "Invalid currency: xyz. Stripe currently supports these currencies: usd, aed, afn, all, amd, ang, aoa, ars, aud, awg, azn, bam, bbd, bdt, bgn, bhd, bif, bmd, bnd, bob, brl, bsd, bwp, byn, bzd, cad, cdf, chf, clp, cny, cop, crc, cve, czk, djf, dkk, dop, dzd, egp, etb, eur, fjd, fkp, gbp, gel, gip, gmd, gnf, gtq, gyd, hkd, hnl, hrk, htg, huf, idr, ils, inr, isk, jmd, jod, jpy, kes, kgs, khr, kmf, krw, kwd, kyd, kzt, lak, lbp, lkr, lrd, lsl, mad, mdl, mga, mkd, mmk, mnt, mop, mur, mvr, mwk, mxn, myr, mzn, nad, ngn, nio, nok, npr, nzd, omr, pab, pen, pgk, php, pkr, pln, pyg, qar, ron, rsd, rub, rwf, sar, sbd, scr, sek, sgd, shp, sle, sos, srd, std, szl, thb, tjs, tnd, top, try, ttd, twd, tzs, uah, ugx, uyu, uzs, vnd, vuv, wst, xaf, xcd, xcg, xof, xpf, yer, zar, zmw, usdc, btn, ghs, eek, lvl, svc, vef, ltl, sll, mro",
"param": "currency",
"request_log_url": "https://dashboard.stripe.com/acct_1HDZokLSFkr2JHff/test/workbench/logs?object=req_rC8gM0UcsCFe31",
"type": "invalid_request_error"
}
}AgentStep 5
**Error 1 analysis:** - **Type**: `invalid_request_error` - **`param`: `currency`** — Stripe pinpoints exactly which field is wrong - **Message**: Tells you `xyz` is invalid and dumps the full list of ~160 supported currencies inline **Is the error message enough?** Yes — extremely actionable. You know the bad field, the bad value, and the full set of valid values in one response. No guessing required. **Recovery**: Fix `currency` to a valid ISO code. Since this account's default is `eur` and the customer base is likely US-based for `$99`, use `currency=usd`. --- ### Error 2 — Declined test card First confirm the `pm_card_chargeDeclined` token is valid by using it, then fall back to the raw card number if not:
BashTool Use
Input
Show input
{
"command": "# Create a fresh PaymentIntent, then try to confirm with the declined-card test PM\nPI=$(curl -s https://api.stripe.com/v1/payment_intents \\\n -u sk_test_[REDACTED]: \\\n -d amount=9900 \\\n -d currency=usd \\\n -d customer=cus_UCsIF9bS5c49z6 \\\n | python3 -c \"import json,sys; print(json.load(sys.stdin)['id'])\")\n\necho \"PaymentIntent ID: $PI\"\n\ncurl -s https://api.stripe.com/v1/payment_intents/$PI/confirm \\\n -u sk_test_[REDACTED]: \\\n -d payment_method=pm_card_chargeDeclined \\\n | python3 -m json.tool",
"description": "Create PaymentIntent then confirm with pm_card_chargeDeclined test token",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
PaymentIntent ID: pi_3TESbLLSFkr2JHff1CciO5NB
{
"error": {
"advice_code": "try_again_later",
"charge": "ch_3TESbLLSFkr2JHff1CL1zQVz",
"code": "card_declined",
"decline_code": "generic_decline",
"doc_url": "https://stripe.com/docs/error-codes/card-declined",
"message": "Your card was declined.",
"network_decline_code": "01",
"payment_intent": {
"id": "pi_3TESbLLSFkr2JHff1CciO5NB",
"object": "payment_intent",
"amount": 9900,
"amount_capturable": 0,
"amount_details": {
"tip": {}
},
"amount_received": 0,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [
{
"id": "ch_3TESbLLSFkr2JHff1CL1zQVz",
"object": "charge",
"amount": 9900,
"amount_captured": 0,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": null,
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": null,
"name": null,
"phone": null,
"tax_id": null
},
"calculated_statement_descriptor": "PMJOBLIST.COM",
"captured": false,
"created": 1774349664,
"currency": "usd",
"customer": "cus_UCsIF9bS5c49z6",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_balance_transaction": null,
"failure_code": "card_declined",
"failure_message": "Your card was declined.",
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"advice_code": "try_again_later",
"network_advice_code": null,
"network_decline_code": "01",
"network_status": "declined_by_network",
"reason": "generic_decline",
"risk_level": "normal",
"risk_score": 0,
"seller_message": "The bank did not return any further details with this decline.",
"type": "issuer_declined"
},
"paid": false,
"payment_intent": "pi_3TESbLLSFkr2JHff1CciO5NB",
"payment_method": "pm_1TESbMLSFkr2JHff9ZER54cY",
"payment_method_details": {
"card": {
"amount_authorized": null,
"authorization_code": "641655",
"brand": "visa",
"checks": {
"address_line1_check": null,
"address_postal_code_check": null,
"cvc_check": "pass"
},
"country": "US",
"exp_month": 3,
"exp_year": 2027,
"extended_authorization": {
"status": "disabled"
},
"fingerprint": "vZ0gr7Ae5Qei8bU6",
"funding": "credit",
"incremental_authorization": {
"status": "unavailable"
},
"installments": null,
"last4": "0002",
"mandate": null,
"multicapture": {
"status": "unavailable"
},
"network": "visa",
"network_token": {
"used": false
},
"network_transaction_id": "118904810311455",
"overcapture": {
"maximum_amount_capturable": 9900,
"status": "unavailable"
},
"regulated_status": "unregulated",
"three_d_secure": null,
"wallet": null
},
"type": "card"
},
"radar_options": {},
"receipt_email": null,
"receipt_number": null,
"receipt_url": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_3TESbLLSFkr2JHff1CL1zQVz/refunds"
},
"review": null,
"shipping": null,
"source": null,
"source_transfer": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "failed",
"transfer_data": null,
"transfer_group": null
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_3TESbLLSFkr2JHff1CciO5NB"
},
"client_secret": "pi_3TESbLLSFkr2JHff1CciO5NB_secret_kY1NGbrav6uqQjebFqBCJG8qE",
"confirmation_method": "automatic",
"created": 1774349663,
"currency": "usd",
"customer": "cus_UCsIF9bS5c49z6",
"customer_account": null,
"description": null,
"excluded_payment_method_types": null,
"invoice": null,
"last_payment_error": {
"advice_code": "try_again_later",
"charge": "ch_3TESbLLSFkr2JHff1CL1zQVz",
"code": "card_declined",
"decline_code": "generic_decline",
"doc_url": "https://stripe.com/docs/error-codes/card-declined",
"message": "Your card was declined.",
"network_decline_code": "01",
"payment_method": {
"id": "pm_1TESbMLSFkr2JHff9ZER54cY",
"object": "payment_method",
"allow_redisplay": "unspecified",
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": null,
"name": null,
"phone": null,
"tax_id": null
},
"card": {
"brand": "visa",
"checks": {
"address_line1_check": null,
"address_postal_code_check": null,
"cvc_check": "pass"
},
"country": "US",
"display_brand": "visa",
"exp_month": 3,
"exp_year": 2027,
"fingerprint": "vZ0gr7Ae5Qei8bU6",
"funding": "credit",
"generated_from": null,
"last4": "0002",
"networks": {
"available": [
"visa"
],
"preferred": null
},
"regulated_status": "unregulated",
"three_d_secure_usage": {
"supported": true
},
"wallet": null
},
"created": 1774349664,
"customer": null,
"customer_account": null,
"livemode": false,
"metadata": {},
"type": "card"
},
"type": "card_error"
},
"latest_charge": "ch_3TESbLLSFkr2JHff1CL1zQVz",
"livemode": false,
"metadata": {},
"next_action": null,
"on_behalf_of": null,
"payment_method": null,
"payment_method_configuration_details": null,
"payment_method_options": {
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
}
},
"payment_method_types": [
"card"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "requires_payment_method",
"transfer_data": null,
"transfer_group": null
},
"payment_method": {
"id": "pm_1TESbMLSFkr2JHff9ZER54cY",
"object": "payment_method",
"allow_redisplay": "unspecified",
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": null,
"name": null,
"phone": null,
"tax_id": null
},
"card": {
"brand": "visa",
"checks": {
"address_line1_check": null,
"address_postal_code_check": null,
"cvc_check": "pass"
},
"country": "US",
"display_brand": "visa",
"exp_month": 3,
"exp_year": 2027,
"fingerprint": "vZ0gr7Ae5Qei8bU6",
"funding": "credit",
"generated_from": null,
"last4": "0002",
"networks": {
"available": [
"visa"
],
"preferred": null
},
"regulated_status": "unregulated",
"three_d_secure_usage": {
"supported": true
},
"wallet": null
},
"created": 1774349664,
"customer": null,
"customer_account": null,
"livemode": false,
"metadata": {},
"type": "card"
},
"request_log_url": "https://dashboard.stripe.com/acct_1HDZokLSFkr2JHff/test/workbench/logs?object=req_L7320C9gOU0y3i",
"type": "card_error"
}
}AgentStep 6
`pm_card_chargeDeclined` worked — confirmed it maps to card ending `0002` (Stripe's generic decline Visa). The charge was attempted and rejected by the network. **Error 2 analysis:** - **Type**: `card_error` — distinct from `invalid_request_error`; this means the request was valid but the card issuer refused it - **`code`**: `card_declined` — the top-level category - **`decline_code`**: `generic_decline` — the specific sub-reason from the issuer - **`advice_code`**: `try_again_later` — Stripe's guidance on what to do next - **`network_decline_code`**: `01` — the raw code from the card network - **Charge created but `status: failed`** — the charge attempt is recorded; `captured: false`, `paid: false`, `amount_received: 0` - **PaymentIntent reverts to `requires_payment_method`** — it stays open so the customer can retry with a different card **Is the error message enough?** Mostly yes, but with nuance. `"Your card was declined."` is the customer-facing message (deliberately vague for security reasons — issuers don't always reveal why). The `decline_code: generic_decline` tells you the issuer gave no specific reason. For more actionable codes you'd look at other decline tokens like `insufficient_funds` or `lost_card`. The `doc_url` in the response links directly to Stripe's error code reference. **Recovery**: The PaymentIntent is reusable — it's back to `requires_payment_method`. Show the user the decline message, prompt them to try a different card, and confirm the same PaymentIntent with the new payment method. --- ### Error 3 — Retrieve a non-existent customer
BashTool Use
Input
Show input
{
"command": "curl -s https://api.stripe.com/v1/customers/cus_doesnotexist99999 \\\n -u sk_test_[REDACTED]: \\\n | python3 -m json.tool",
"description": "Attempt to retrieve a customer ID that does not exist",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
{
"error": {
"code": "resource_missing",
"doc_url": "https://stripe.com/docs/error-codes/resource-missing",
"message": "No such customer: 'cus_doesnotexist99999'",
"param": "id",
"request_log_url": "https://dashboard.stripe.com/acct_1HDZokLSFkr2JHff/test/workbench/logs?object=req_a5yxYZi93kNTN9",
"type": "invalid_request_error"
}
}AgentStep 7
**Error 3 analysis:** - **Type**: `invalid_request_error` - **`code`**: `resource_missing` - **`param`**: `id` — Stripe identifies the field that refers to the missing resource - **Message**: `"No such customer: 'cus_doesnotexist99999'"` — includes the exact ID you passed, so you know immediately it's a lookup failure, not a permissions issue or typo in the endpoint **Is the error message enough?** Yes — unambiguous. The ID you used doesn't exist in this account (or at all). The message quotes your ID back at you which is useful for logging. **Recovery**: Three possible causes — (1) the ID was never created, (2) it was created in live mode and you're querying test mode (or vice versa), (3) it belongs to a different Stripe account. Check your database for the correct `cus_` ID, verify you're using the right mode keys, and confirm the customer was created under the same account. --- ## Summary across all three errors | Error | HTTP concept | Stripe `type` | `code` | Self-explanatory? | |-------|-------------|---------------|--------|-------------------| | Invalid currency `xyz` | 400 | `invalid_request_error` | — | **Yes** — lists valid currencies | | Card declined | 402 | `card_error` | `card_declined` / `generic_decline` | **Partially** — vague by design; `decline_code` adds detail | | Missing customer | 404 | `invalid_request_error` | `resource_missing` | **Yes** — quotes bad ID back | One structural thing worth noting: Stripe always returns a single `error` object at the top level — never a 2xx with an error buried in the body. That makes it straightforward to handle: check for the presence of `error`, then branch on `error.type` and `error.code`.
Task 5
Cleanup
Related findings: F-008
OperatorStep 1
Clean up everything we created during this test: 1. Delete the test customer 2. Archive or delete the product 3. Confirm no test data remains If any of these can't be done via the API, tell me what manual steps would be needed.
AgentStep 2
Let me first enumerate everything we created across the session before touching anything.
BashTool Use
Input
Show input
{
"command": "# List all objects we created - customers, products, prices, payment intents\necho \"=== CUSTOMERS ===\" && \\\ncurl -s \"https://api.stripe.com/v1/customers?email=test%40example.com\" \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; [print(f' {c[\\\"id\\\"]} | {c[\\\"name\\\"]} | {c[\\\"email\\\"]}') for c in json.load(sys.stdin)['data']]\" && \\\n\necho \"=== PRODUCTS ===\" && \\\ncurl -s \"https://api.stripe.com/v1/products?limit=5\" \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; [print(f' {p[\\\"id\\\"]} | {p[\\\"name\\\"]} | active={p[\\\"active\\\"]}') for p in json.load(sys.stdin)['data']]\" && \\\n\necho \"=== PRICES ===\" && \\\ncurl -s \"https://api.stripe.com/v1/prices?product=prod_UCsIU0SZWdiEba\" \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; [print(f' {p[\\\"id\\\"]} | \\${p[\\\"unit_amount\\\"]/100:.2f} {p[\\\"currency\\\"].upper()} | active={p[\\\"active\\\"]}') for p in json.load(sys.stdin)['data']]\" && \\\n\necho \"=== PAYMENT INTENTS (recent) ===\" && \\\ncurl -s \"https://api.stripe.com/v1/payment_intents?limit=10\" \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; [print(f' {p[\\\"id\\\"]} | \\${p[\\\"amount\\\"]/100:.2f} {p[\\\"currency\\\"].upper()} | {p[\\\"status\\\"]}') for p in json.load(sys.stdin)['data']]\"",
"description": "Enumerate all test objects created across the session",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
=== CUSTOMERS === cus_UCsIF9bS5c49z6 | Test User | test@example.com === PRODUCTS === prod_UCsIU0SZWdiEba | Usability Audit | active=True prod_U7zszVb1IbUQX8 | Usability Audit | active=False prod_U7dlsUY8vt0FHx | Usability Audit | active=False prod_IEqu1iuSbOyhsk | Job Listing | active=True === PRICES === price_1TESYBLSFkr2JHffmFMsC0Fk | $99.00 USD | active=True === PAYMENT INTENTS (recent) === pi_3TESbLLSFkr2JHff1CciO5NB | $99.00 USD | requires_payment_method pi_3TESYKLSFkr2JHff180KdHja | $99.00 USD | succeeded pi_3TESWgLSFkr2JHff1y7gGR6C | $20.00 EUR | requires_payment_method pi_3T9jvwLSFkr2JHff123HMsHG | $10.00 USD | requires_payment_method pi_3T9jtCLSFkr2JHff1bCErlAJ | $99.00 USD | succeeded pi_3T9OgjLSFkr2JHff1Sp0MvuJ | $50.00 USD | requires_payment_method pi_3T9OUKLSFkr2JHff0WFdaBW2 | $99.00 USD | succeeded pi_1HkrtELSFkr2JHff8cyUupKN | $199.00 USD | succeeded pi_1HkrsMLSFkr2JHffgcVckAOO | $199.00 USD | canceled pi_1HeQZGLSFkr2JHffsRoIp1lK | $199.00 USD | succeeded
AgentStep 3
Good picture. Our objects are clearly scoped — I can see pre-existing data on the account (`Job Listing` product, older PIs) that I'll leave untouched. Here's what we created this session: - `cus_UCsIF9bS5c49z6` — customer - `prod_UCsIU0SZWdiEba` — product - `price_1TESYBLSFkr2JHffmFMsC0Fk` — price - `pi_3TESWgLSFkr2JHff1y7gGR6C`, `pi_3TESYKLSFkr2JHff180KdHja`, `pi_3TESbLLSFkr2JHff1CciO5NB` — payment intents Now cleaning up in order: --- ### Step 1 — Delete the customer
BashTool Use
Input
Show input
{
"command": "curl -s https://api.stripe.com/v1/customers/cus_UCsIF9bS5c49z6 \\\n -u sk_test_[REDACTED]: \\\n -X DELETE \\\n | python3 -m json.tool",
"description": "Delete customer cus_UCsIF9bS5c49z6",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
{
"id": "cus_UCsIF9bS5c49z6",
"object": "customer",
"deleted": true
}AgentStep 4
**Step 1 ✓** `deleted: true` confirmed. --- ### Step 2 — Archive the price, then archive the product Stripe doesn't allow deleting prices or products that have been used in charges — archiving (`active=false`) is the correct API-supported approach.
BashTool Use
Input
Show input
{
"command": "echo \"--- Archive price ---\" && \\\ncurl -s https://api.stripe.com/v1/prices/price_1TESYBLSFkr2JHffmFMsC0Fk \\\n -u sk_test_[REDACTED]: \\\n -d active=false \\\n | python3 -c \"import json,sys; p=json.load(sys.stdin); print(f' id={p[\\\"id\\\"]} | active={p[\\\"active\\\"]}')\" && \\\n\necho \"--- Archive product ---\" && \\\ncurl -s https://api.stripe.com/v1/products/prod_UCsIU0SZWdiEba \\\n -u sk_test_[REDACTED]: \\\n -d active=false \\\n | python3 -c \"import json,sys; p=json.load(sys.stdin); print(f' id={p[\\\"id\\\"]} | name={p[\\\"name\\\"]} | active={p[\\\"active\\\"]}')\"",
"description": "Archive the price and product (set active=false)",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
--- Archive price --- id=price_1TESYBLSFkr2JHffmFMsC0Fk | active=False --- Archive product --- id=prod_UCsIU0SZWdiEba | name=Usability Audit | active=False
AgentStep 5
**Step 2 ✓** Both archived — `active=False` on price and product. --- ### Step 3 — Verify: re-fetch all three objects
BashTool Use
Input
Show input
{
"command": "echo \"--- Customer (expect deleted/404) ---\" && \\\ncurl -s https://api.stripe.com/v1/customers/cus_UCsIF9bS5c49z6 \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; r=json.load(sys.stdin); print(f' deleted={r.get(\\\"deleted\\\")} | error={r.get(\\\"error\\\",{}).get(\\\"code\\\")}')\" && \\\n\necho \"--- Price (expect active=False) ---\" && \\\ncurl -s https://api.stripe.com/v1/prices/price_1TESYBLSFkr2JHffmFMsC0Fk \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; p=json.load(sys.stdin); print(f' id={p[\\\"id\\\"]} | active={p[\\\"active\\\"]}')\" && \\\n\necho \"--- Product (expect active=False) ---\" && \\\ncurl -s https://api.stripe.com/v1/products/prod_UCsIU0SZWdiEba \\\n -u sk_test_[REDACTED]: \\\n | python3 -c \"import json,sys; p=json.load(sys.stdin); print(f' id={p[\\\"id\\\"]} | name={p[\\\"name\\\"]} | active={p[\\\"active\\\"]}')\"",
"description": "Re-fetch all three objects to confirm their final state",
"dangerouslyDisableSandbox": true
}BashTool Result
Result
Show output
--- Customer (expect deleted/404) --- deleted=True | error=None --- Price (expect active=False) --- id=price_1TESYBLSFkr2JHffmFMsC0Fk | active=False --- Product (expect active=False) --- id=prod_UCsIU0SZWdiEba | name=Usability Audit | active=False
AgentStep 6
**Step 3 ✓** All confirmed.
---
## Final state
| Object | ID | Result | Method |
|--------|----|--------|--------|
| Customer | `cus_UCsIF9bS5c49z6` | `deleted: true` | `DELETE /v1/customers/{id}` |
| Price | `price_1TESYBLSFkr2JHffmFMsC0Fk` | `active: false` | `POST /v1/prices/{id}` with `active=false` |
| Product | `prod_UCsIU0SZWdiEba` | `active: false` | `POST /v1/products/{id}` with `active=false` |
| PaymentIntents (3) | `pi_3TESW…`, `pi_3TESY…`, `pi_3TESb…` | **Permanent — cannot delete** | N/A |
**On the PaymentIntents**: Stripe does not expose a delete endpoint for PaymentIntents or Charges — by design, for financial audit trail integrity. The three we created will remain as records in your test mode Dashboard. Since they're in test mode, they have no financial consequence and will never appear on a real statement. If you want a completely clean slate, the only option is to create a brand-new Stripe test account — but for normal development this is unnecessary. Task 6
Reflection
Related findings: F-007
OperatorStep 1
Looking back at everything you just did — discovering Stripe, onboarding, completing the payment workflow, handling errors, and cleaning up: 1. Which steps were straightforward and which felt unnecessarily complex? 2. Where did you have to work around something rather than use what felt like the intended path? 3. What would have made this easier? Be specific — refer to actual moments from the tasks above.
AgentStep 2
## Retrospective
### What was straightforward
**The core payment primitives.** Creating a customer, creating a PaymentIntent, confirming it, reading the result — these were each a single API call with predictable request/response shapes. The API is consistent: every object has an `id`, creation always returns the full object, and status is always a named string (`requires_payment_method`, `succeeded`, etc.). Once you've seen one Stripe object you can read any of them.
**Error responses.** This was genuinely impressive. The invalid currency error returned the complete list of valid currencies inline in the message — that's the error response equivalent of self-documenting code. The `resource_missing` error quoted the exact bad ID back. The card decline gave you `code`, `decline_code`, `advice_code`, and `network_decline_code` as separate fields, which maps cleanly to different audiences (show `message` to the user, log `decline_code` for your backend, act on `advice_code` for retry logic). I didn't have to guess at any of these — the error told me what was wrong and what to do.
**Cleanup via API.** Customer deletion and product/price archiving both worked in one call each, and the responses immediately confirmed the state change. Nothing required going to the Dashboard.
---
### Where I worked around something
**The declined card documentation.** This was the most visible friction point. I made seven separate WebFetch attempts against `docs.stripe.com/testing` trying to get the declined card table, and every response came back incomplete — partly German-localized, partly cut off before the relevant table. The page is dynamically rendered JavaScript and the fetch tool was getting a shell with missing content. I ended up using `pm_card_chargeDeclined`, a server-side test fixture I knew exists at the API level, and confirmed it worked by running it. That worked fine — but the path was: try docs repeatedly, fail, fall back to a token I couldn't verify from docs, try it empirically. That's a workaround, not the intended path.
**The Product/Price model for a one-off charge.** You asked me to create a product called "Usability Audit" priced at $99, which I did — but technically that step had nothing to do with the actual payment. The PaymentIntent only needed `amount=9900` and `currency=usd`. The Product and Price objects are Stripe's billing catalog, designed for subscription management and invoicing. They're never referenced by the PaymentIntent. I built the catalog because the task asked for it, but if someone is reading this session to learn how to charge a card, the Product/Price step could mislead them into thinking it's required when it isn't.
**The test confirmation flow doesn't reflect production.** Confirming a PaymentIntent with `pm_card_chargeDeclined` or `pm_card_visa` is a server-side shortcut that only works in test mode. In production, card details are collected client-side by Stripe.js and never touch your server. The workflow we demonstrated — create PI on server, confirm on server with a test PM token — short-circuits the frontend entirely. It's valid for testing the API surface, but it creates a gap: the session shows a complete flow that you can't actually replicate in live mode without building the frontend piece.
**PaymentIntents are permanent.** When cleaning up, I had to note that three PaymentIntents couldn't be deleted. This is by design (audit trail), but it creates a gap between the user's mental model ("clean up test data") and what's actually possible ("most things, but not transaction records"). There's no API endpoint for it and no in-session workaround — I had to escalate to "the only way is a new account."
---
### What would have made this easier
**Static-renderable docs.** If `docs.stripe.com/testing` had a plain markdown or text version with the full tables intact, the declined card investigation would have been a single fetch instead of seven failed ones. Stripe does have `llms.txt` at the docs root, which was a good index, but it pointed to pages whose content wasn't actually fetchable. An `llms-full.txt` with the actual reference tables inline would have solved this.
**Clearer separation between "charge a card" and "manage a product catalog."** The docs path for one-off payments leads you through Payment Intents cleanly, but the framing of "create a product priced at $99" implied catalog setup was part of the charge flow. A note like "for one-off charges you only need `amount` and `currency` — Products and Prices are optional catalog metadata" would avoid that confusion.
**A way to bulk-delete test data.** Stripe has a test clock feature for subscription testing, but no equivalent "teardown this test session" mechanism. For anyone running integration tests repeatedly, the accumulation of permanent PaymentIntent records is noise. Even a test-mode-only delete endpoint for PIs would close that gap cleanly.