Testing Guide
Sandbox test card tokens for every payment outcome — without spending real money.
What this is and who needs it
When you are building or testing an integration, you do not want to spend real money to see if your code works. EPD’s sandbox lets you simulate every important payment outcome — successful charges, declines, expired cards, chargebacks — using fake card tokens that move no real funds.
If you are building, debugging, QA-ing, or running automated tests, this is the page you need.
How testing works
EPD does not have a separate sandbox URL. Instead:
- Sign in to the Merchant Portal and switch to your Demo Company — it was auto-created at sign-up and acts as the sandbox. Use the profile avatar (top right) to switch companies.
- Open the gear icon (top right) → API Key → Create API Key. Keys created inside the Demo Company are sandbox keys and start with
epd_test_sk_. - Send that key with your request. Any request made with a sandbox key runs in sandbox mode automatically.
- Use one of the sandbox card tokens below as the
billing_id(also called the EPD Gateway customer vault id) when adding a payment method to a customer.
Sandbox card tokens work only with sandbox keys. Sending one with a live key returns error.code = invalid_vault_id.
Sandbox card tokens
Pass these strings as the billing_id field on POST /v1/customers/:id/payment_methods. Each one simulates a different downstream outcome when the card is later charged.
Success
| Token | Network | Outcome |
|---|---|---|
card_visa | Visa | Charges succeed |
card_mastercard | Mastercard | Charges succeed |
card_amex | American Express | Charges succeed |
card_discover | Discover | Charges succeed |
Decline & failure
| Token | Outcome | error.code |
|---|---|---|
card_visa_declined | Generic Visa decline | card_declined |
card_mastercard_declined | Generic Mastercard decline | card_declined |
card_insufficient_funds | Card valid but no funds available | insufficient_funds |
card_expired | Card has expired | expired_card |
card_processing_error | Gateway-side processing error (retryable) | gateway_error |
card_cvv_mismatch | CVV check failed | card_declined |
card_chargeback | Charge succeeds, then a chargeback is simulated | payment_failed |
End-to-end example: simulate a successful charge
curl https://api.epd.com/v1/customers \
-H "Authorization: Bearer $EPD_TEST_KEY" \
-H "epd-version: 2026-02-11" \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"phone": "+14155551234"
}'
Pass the test token as billing_id. EPD mirrors the card from the gateway vault and returns a UUID id for the payment method.
curl https://api.epd.com/v1/customers/$CUSTOMER_ID/payment_methods \
-H "Authorization: Bearer $EPD_TEST_KEY" \
-H "epd-version: 2026-02-11" \
-H "X-EPD-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"billing_id": "card_visa",
"set_as_default": true
}'
Orders charge against a product, not a free-form amount. Create one and save its id as $PRODUCT_ID.
curl https://api.epd.com/v1/products \
-H "Authorization: Bearer $EPD_TEST_KEY" \
-H "epd-version: 2026-02-11" \
-H "X-EPD-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "Sandbox Test Product",
"sku": "sandbox-test-product",
"description": "A throwaway product for sandbox testing.",
"pricing": { "amount": 2000, "currency": "usd" },
"requires_shipping": false
}'
curl https://api.epd.com/v1/orders \
-H "Authorization: Bearer $EPD_TEST_KEY" \
-H "epd-version: 2026-02-11" \
-H "X-EPD-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "'"$CUSTOMER_ID"'",
"payment_method_id": "'"$PM_ID"'",
"items": [
{ "product_id": "'"$PRODUCT_ID"'", "quantity": 1 }
]
}'
Example: simulate an insufficient-funds decline
Reuse the flow above but use card_insufficient_funds:
curl https://api.epd.com/v1/customers/$CUSTOMER_ID/payment_methods \
-H "Authorization: Bearer $EPD_TEST_KEY" \
-H "epd-version: 2026-02-11" \
-H "X-EPD-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "billing_id": "card_insufficient_funds" }'
When you create the order, the response will be:
{
"error": {
"type": "processing_error",
"code": "insufficient_funds",
"message": "Your card has insufficient funds.",
"request_id": "req_..."
}
}
What sandbox does not simulate
- Real settlement timing. Sandbox confirmations are immediate; live cards settle 1–3 business days later.
- Issuer-specific edge cases. Some banks reject otherwise-valid charges based on internal risk rules; sandbox cannot reproduce those.
- 3-D Secure / SCA challenges. EPD Commerce currently does not require 3DS for sandbox or live transactions through this API.
If you need an outcome not listed above, contact support — many additional decline codes can be enabled per-account.