Skip to content

Simulate a payment's outcome

POST
/v1/test_helpers/payment_intents/{id}/simulate
curl --request POST \
--url https://api.example.com/v1/test_helpers/payment_intents/example/simulate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-1042-attempt-1' \
--data '{ "outcome": "paid" }'

Gives a test-mode payment the outcome you choose, now, as if your customer had paid it, or failed to. No customer pays a simulated QR code or a test cash reference, and no bank answers a simulated card, so this is how a test payment moves on from requires_action. It works with a test key only: a live key is refused with test_mode_only, and a live intent is never reached. See Testing your integration.

The intent must have a payment with the customer: confirmed, or, for an intent created without a method, with the method chosen on our checkout page. Outcomes are for the intent’s method:

outcome Method The intent then
paid qrph succeeded, with amount_received equal to amount. A code already past its time is expired instead.
expired qrph requires_payment_method, with last_payment_error payment_intent_payment_attempt_expired. The code can no longer be paid; confirm again for a new one.
paid cash succeeded through processing, as when the customer pays the exact amount at a cash collection location. A reference already past its time is expired instead.
expired cash requires_payment_method, with last_payment_error payment_intent_payment_attempt_expired. The reference can no longer be paid; confirm again for a new one.
approved card succeeded. A card payment of 40400 is then charged back: see Test mode and test values.
three_d_secure_passed card succeeded, as after the customer’s bank has authenticated them.
declined card requires_payment_method, with last_payment_error card_declined. A decline_reason adds a decline_code to it (test mode only).
three_d_secure_failed card requires_payment_method, with last_payment_error payment_intent_authentication_failure.
abandoned card requires_payment_method, with last_payment_error payment_intent_payment_attempt_failed: the customer left without paying.

The intent and its events are those of a real payment: payment_intent.succeeded or payment_intent.payment_failed is sent, and a failed card payment can be confirmed again. An intent already settled, whether paid, canceled, or its attempt failed or expired, is answered as it is, and nothing changes: asking again, for the same outcome or another, is safe. An outcome that is not one of the intent’s method is refused with validation_failure whatever the intent’s state.

Idempotency-Key is required, as on every write on an intent.

id
required
string

The object’s id.

Idempotency-Key
required
string
<= 255 characters

A value unique to this operation, such as your order number and attempt, of 1 to 255 characters. A retry with the same key and body is done at most once; see Idempotency.

Example
order-1042-attempt-1
Media typeapplication/json
object
outcome
required

paid or expired for a QR Ph or cash payment; approved, declined, three_d_secure_passed, three_d_secure_failed or abandoned for a card payment.

string
Allowed values: paid expired approved declined three_d_secure_passed three_d_secure_failed abandoned
decline_reason

With declined only: the reason the decline names, which comes back as last_payment_error.decline_code. Leave it out, or send null, for a plain decline.

string | null
Allowed values: insufficient_funds do_not_honor expired_card
Examples
{
"outcome": "paid"
}

The intent as it then is.

Media typeapplication/json
object
id
required
string
object
required
string
Allowed value: payment_intent
amount
required

In the currency’s minor unit.

integer
amount_received
required

The amount paid: amount once succeeded, and 0 until then.

integer
currency
required

A three-letter ISO 4217 code, in lower case.

string
/^[a-z]{3}$/
payment_method_types
required

The chosen method, such as [qrph], once there is one. Until then, the methods that can take this payment now: confirm with one. In an event for an intent with no method, the methods you allowed at creation, or null if you allowed any.

Array<string> | null
channel
required

Where the payment is taken: ecommerce for every intent your server creates. Payments taken on terminals will say pos (Pending).

string
status
required
string
Allowed values: requires_payment_method requires_confirmation requires_action processing succeeded canceled
next_action
required
One of:
object
type
required
string
Allowed value: qrph_display_qr_code
qrph_display_qr_code
required
object
data
required

The QR payload. Render it as a QR code for the customer to scan with their banking app.

string
expires_at
required

When the code stops being payable, as a Unix timestamp.

integer | null
last_payment_error
required
One of:
object
code
required

card_declined: the card was declined. expired_card: the card has expired (test mode only today). payment_intent_authentication_failure: the customer did not complete the card’s authentication. payment_intent_payment_attempt_failed: the attempt failed, or the customer left without paying. payment_intent_payment_attempt_expired: the customer did not pay in time.

string
Allowed values: card_declined expired_card payment_intent_authentication_failure payment_intent_payment_attempt_failed payment_intent_payment_attempt_expired
decline_code

Why the card was declined, when the decline names a reason. Test mode only today: it appears when you simulate a decline with a decline_reason (see Simulate a payment’s outcome), and a live decline does not carry it yet, so never depend on it there.

string
Allowed values: insufficient_funds do_not_honor expired_card
message
required

Words you can show the customer. May be reworded, so never branch on it.

string
cancellation_reason
required

The reason given when it was canceled, if any.

string | null
Allowed values: requested_by_customer duplicate fraudulent abandoned
canceled_at
required

When it was canceled, as a Unix timestamp.

integer | null
description
required
string | null
metadata
required

The metadata you gave it. {} if none.

object
key
additional properties
string
livemode
required
boolean
created
required

When it was created, as a Unix timestamp.

integer
Examples

A QR Ph payment simulated paid

{
"id": "pi_01j9x2m8k4q7r5t3v6w8y0z2a4",
"object": "payment_intent",
"amount": 10000,
"amount_received": 10000,
"currency": "php",
"payment_method_types": [
"qrph"
],
"channel": "ecommerce",
"status": "succeeded",
"next_action": null,
"last_payment_error": null,
"cancellation_reason": null,
"canceled_at": null,
"description": "Order 1042",
"metadata": {
"order_id": "1042"
},
"livemode": false,
"created": 1790411400
}
X-Request-ID
string

This request’s id. Quote it when you contact support.

validation_failure with invalid_parameter: param is outcome when it is missing, not an outcome, or not one for the intent’s method; decline_reason when it is not insufficient_funds, do_not_honor or expired_card, or is sent with an outcome other than declined; Idempotency-Key when the header is missing.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure",
"code": "invalid_parameter",
"message": "One of paid, expired for a qrph payment.",
"param": "outcome",
"request_id": "req_4f1c0b2e9a7d"
}
}

authentication_failure: access_token_required when no token was sent, invalid_access_token when it is not valid, has expired, or is a client secret. Sent with a WWW-Authenticate: Bearer header, which adds error="invalid_token" when a token was sent.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}

authorization_failure with test_mode_only: a live key was used. Nothing is simulated in live mode, and a live intent is not reachable even with a test key. Also merchant_mismatch, as on every call.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "authorization_failure",
"code": "test_mode_only",
"message": "Test helpers take a test key: no outcome is simulated in live mode.",
"param": null,
"request_id": "req_4f1c0b2e9a7d"
}
}

not_found: resource_missing for an id this key cannot see, route_missing for a path that does not exist.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}

conflict with intent_state: the intent has no payment with the customer to give an outcome to. It is not confirmed yet, it is requires_action at our checkout page with no method chosen, or it is processing. Confirm it, or have the customer choose a method, and ask again. idempotency_key_reused and idempotency_key_in_use as on every write.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}

dependency_failure with service_unavailable: something on our side, or a payment network behind it, did not answer, or failed part-way. Whether the call took effect is not known, so do not assume it did not. Retry the same request with the same Idempotency-Key, and never make a new key for the retry: if the first call took effect, you are answered with its result (the payment intent or refund it made, or the intent as it now is) and nothing is done a second time. A retry refused with conflict and intent_state means the intent is no longer in a state that allows the call: the first call may have taken effect, or the customer may have acted since. Read the intent. If the failure came while the answer was being stored, the key is held for five minutes and a retry in that time answers 409 with idempotency_key_in_use: retry shortly. See When a write answers 503.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}