Testing in the sandbox

The sandbox is a separate environment with its own address, its own merchant account and its own API keys. Nothing in it moves real money. Its API is at https://merchant-api-sandbox.jeton.com/v2, and it works exactly like the production API: the same endpoints, the same request signing, the same errors and webhooks.

Everything you call in the sandbox is the API you will call in production: a payment is created with POST /v2/payments here too. What the sandbox adds is the simulator, a handful of endpoints under /v2/simulator that do what the other side does in production: a customer paying, money arriving, a bank or a chain deciding a payout. They take the same path the real outcome takes, so your integration sees the events, webhooks and account entries it will see in production. One more names the sandbox's test counterparties, so a payout or a transfer has someone to reach. In production these endpoints do not exist and answer 404.

Complete a payment

In production, your customer pays the checkout. In the sandbox, the sandbox's test customer can pay it for you:

  1. Create the payment with POST /v2/payments. Keep the checkout.url from the response: it is returned only once.

  2. Complete it with POST /v2/simulator/payments/{paymentId}/complete, sending that URL and an Idempotency-Key:

    { "checkoutUrl": "<checkout.url from the create response>" }
    
  3. Follow the outcome as you will in production. The answer is 202 with the payment as it stands, usually still pending or processing. Shortly after, the payment reads completed, its payment.completed event is recorded and sent to your webhook, and your account shows the credit in GET /v2/accounts/{accountId}/transactions.

The test customer pays through the real checkout, so the real rules apply: a payment whose checkout has expired, that is already paid, or that is being paid is refused with payment_not_payable.

Add funds to an account

To test with a balance, credit one of your fiat accounts as if a bank transfer had arrived on it:

  1. Pick the account from GET /v2/accounts and note its id and currency.

  2. Deposit with POST /v2/simulator/accounts/{accountId}/deposits and an Idempotency-Key, in the account's currency:

    { "amount": { "value": "1000.00", "currency": "EUR" } }
    
  3. See the credit. The answer is 202 with the account as it stands. Shortly after, the account's transactions show a deposit, and its balance rises by the amount less the deposit fee your pricing sets, if any.

The deposit takes the path a real one takes, so the real rules apply: an account that is not active is not credited. There is no deposit event, so no webhook is sent; read GET /v2/accounts/{accountId}/transactions instead.

Decide a payout's outcome

A payout to one of these destinations is accepted as usual, then ends as listed, the way a real payout ends: its status changes, and payout.completed or payout.failed is sent to your webhook. The rule is the last digit: …01 completes, …02 fails. None of them belongs to a real account.

destination Value Outcome
bank_account IBAN DE09000000000000000001 completed
bank_account IBAN DE79000000000000000002 failed
crypto_wallet address on an EVM network (Ethereum, BNB Smart Chain, Polygon …) 0x0000000000000000000000000000000000000001 completed
crypto_wallet address on an EVM network 0x0000000000000000000000000000000000000002 failed
crypto_wallet address on TRON T9yD14Nj9j7xAB4dbGeiX9h8unkKLxmGkn completed
crypto_wallet address on TRON T9yD14Nj9j7xAB4dbGeiX9h8unkKT76qbH failed
  1. Create the payout with POST /v2/payouts to one of these destinations. The answer is 202, as for any payout.
  2. Follow the outcome. Shortly after, the payout reads completed or failed; its event follows as it does in production and is sent to your webhook. A failed payout gives the money back to your account.

Everything else about the payout is real: your balance, your limits and the fee are checked and applied as they are in production. A payout to any other destination is sent on as in production, so its outcome is not yours to decide.

Send money to a test customer or business

A payout to a Jeton account needs an individual on the other end, and a transfer needs another business. The sandbox has one of each, the same for every merchant:

  1. Find them with GET /v2/simulator/counterparties. Each comes as the destination its request takes:

    {
      "payoutDestination": { "type": "jeton_account", "customerNumber": "<the test customer's number>" },
      "transferDestination": { "type": "business", "merchantNumber": "<the test merchant's number>" }
    }
    
  2. Pay out with POST /v2/payouts, sending payoutDestination as the destination, or transfer with POST /v2/transfers, sending transferDestination.

  3. Follow the outcome as you will in production: the status, the event and the webhook.

These are real payouts and transfers: your balance, your limits and the fee apply. If the counterparty has no active account in the currency you send, the request is refused with the code anyone's would get. A counterparty the sandbox does not have is absent from the answer.

What you cannot make happen yet

  • A failed payment. A payment has no failed outcome: a failed attempt leaves it pending while the customer may retry. A payment that is never paid ends expired when its checkout's lifetime (15 minutes) runs out, so to test payment.expired, create a payment and leave it.
  • A crypto deposit. Only fiat accounts can be funded.
  • A returned payout. A payout that completed and is later sent back by the receiving bank is not simulated.