Sandbox Simulator
The sandbox simulator is available exclusively in the sandbox environment. It has no effect in production, where payment and enrollment outcomes are driven by the customer and their bank.
Overview
Payment methods that settle asynchronously leave the payment in a pending (PE) status until the customer's bank reports the outcome, and recurring payment methods need an enrollment that the customer accepts in their bank app before any charge. In sandbox there is no bank and no customer app, so you drive those outcomes yourself: the simulator endpoints move a pending sandbox payment or enrollment to the final state a real outcome would have produced.
Use it to test the states your integration must handle in production: payment confirmation, user cancellation, expiration and the different liquidation failures, including the decline reason each one returns, plus enrollment acceptance and every way an enrollment can end.
Simulating a payment outcome
Send a POST request to /ws/simulator/payment with the payment hash and the outcome you want:
| Parameter | Requirement | Description |
|---|---|---|
integration_key | Required | Your integration key |
hash | Required | The unique payment identification returned in the payment request response |
simulate_action | Required | The outcome to simulate: confirm, cancel, expire, fail or insufficient-funds |
Request example:
curl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/simulator/payment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "your_ebanx_integration_key",
"hash": "<payment-hash>",
"simulate_action": "insufficient-funds"
}'
A successful request returns a JSON response confirming the action that was applied:
{
"status": "SUCCESS",
"entity": "payment",
"operation": "insufficient-funds"
}
The simulator is meant for payments that are still pending. Once a payment reaches a final status, confirmed (CO) or cancelled (CA), simulating another outcome for it is not supported.
What each action produces
After simulating an action, query the payment with /ws/query to see the resulting status and, for cancellations, the reason. The reason is returned in the transaction_status node, in both description_code (machine-readable) and description (human-readable):
"transaction_status": {
"acquirer": "EBANX",
"code": "NOK",
"description": "Payment cancellation requested by the user",
"description_code": "CANCELED_BY_PAYER"
}
simulate_action | Resulting payment status | description_code |
|---|---|---|
confirm | CO (confirmed) | ACCEPTED |
cancel | CA (cancelled) | CANCELED_BY_PAYER |
expire | CA (cancelled) | EXPIRED_BY_USER_INACTION |
insufficient-funds | CA (cancelled) | INSUFFICIENT_FUNDS |
fail | CA (cancelled) | a generic decline reason |
Use fail to test how your integration handles a decline it cannot classify: it produces a generic decline reason instead of a specific one, and the exact code depends on the payment method.
The codes above are the ones the simulator produces, so you can assert on them in your test suite. In production, the same payment can be declined with other codes from the APM response codes reference, and a payment method can report an equivalent outcome under a different code. Treat the reason as an open set of values rather than an exhaustive list, and always handle an unknown description_code.
Simulating an enrollment outcome
Recurring payment methods charge against an enrollment: an agreement the customer accepts once, in their bank app, before any payment is created. In sandbox, POST /ws/simulator/userenrollment drives an enrollment to its final state, so you can test both the acceptance path and every refusal path.
| Parameter | Requirement | Description |
|---|---|---|
integration_key | Required | Your integration key |
merchant_enrollment_code | Required | The unique enrollment identification you sent in the enrollment request |
payment_type_code | Required | The payment type code the enrollment was created with |
simulate_action | Required | The outcome to simulate: confirm, reject, cancel, expire or revoke |
Request example:
curl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/simulator/userenrollment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "your_ebanx_integration_key",
"merchant_enrollment_code": "<enrollment-identifier>",
"payment_type_code": "<payment-type-code>",
"simulate_action": "confirm"
}'
A successful request returns a JSON response confirming the action that was applied:
{
"status": "SUCCESS",
"entity": "enrollment",
"operation": "confirm"
}
Query the enrollment with /ws/userenrollments/query to see the resulting status:
simulate_action | Enrollment state it applies to | Resulting enrollment status |
|---|---|---|
confirm | pending | accepted |
reject | pending | not_accepted |
cancel | pending or accepted | cancelled |
expire | pending | expired |
revoke | accepted | revoked |
confirm and reject simulate the customer answering the agreement request in their bank app, so both apply while the enrollment is still pending. expire simulates an enrollment the customer never answered in time, cancel an enrollment terminated before or after acceptance, and revoke the customer withdrawing an agreement they had already accepted: to test that transition, confirm the enrollment first and then call the endpoint again with revoke.
When the enrollment was created together with a payment, the simulated enrollment outcome can be applied to that pending payment too: confirm confirms it for payment methods that charge at enrollment time, and the other actions cancel it for payment methods that support closing an enrollment charge. Otherwise the pending payment is left untouched, and you drive it separately with /ws/simulator/payment.
Simulating from the browser
Payment and enrollment requests in sandbox also return a redirect_url that opens a simulation page for that payment or enrollment. The page offers the outcomes as buttons, which is convenient for a quick manual test. The API endpoints above are the ones to use in automated tests.
Simulating request-time failures
Timeouts, service unavailable responses, HTTP 500/503/504 and specific BP-DR responses are not driven by the simulator endpoints. Use the X-EBANX-Simulate-Internal-Error header on /ws/direct instead, described in System Failures.
Still need help?
We hope this article was helpful. If you still have questions, you can explore the following options:
- Merchant support: Contact our support team at sales.engineering@ebanx.com for assistance.
- Not a partner yet? Please complete the Merchant Signup Form, and our commercial team will reach out to you.