Going live
Testing
Make things happen in sandbox that you cannot wait for in real life.
Sandbox is a separate world with separate credentials and separate data. Nothing you do there touches real money, and nothing you do in production can be undone by pretending it was a test.
Some outcomes take days in reality, a dispute being raised, an authorisation clearing. A simulation makes one happen now so you can build against it.
curl -X POST "https://api.tst.au.apgpay.dev/bezel/simulations" \
-H "Authorization: Bearer $API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "<useTransactionIdFromSeed>",
"disputeAmount": null,
"type": "SimCmdDispute"
}'Test cards
These work in sandbox and nowhere else. Any future expiry, any CVC.
4242 4242 4242 4242 Visa credit
4000 0566 5566 5556 Visa debit
5555 5555 5555 4444 Mastercard credit
5200 8282 8282 8210 Mastercard debit
5105 1051 0510 5100 Mastercard prepaid
3782 822463 10005 American ExpressEach of these is refused with a 400 and the errorCode named beside it, which is what you branch on:
4000 0000 0000 9995 insufficient_funds
4000 0000 0000 9987 lost_card
4000 0000 0000 9979 stolen_card
4000 0000 0000 0069 expired_card
4000 0000 0000 0127 invalid_cvc
4100 0000 0000 0019 fraudulent4000 0025 0000 3155 authorises but demands a 3-D Secure challenge first, which is the one to use when you are building the challenge handling rather than avoiding it.
770 is insufficient funds, 775 a lost card, 776 an invalid CVC. The card number only decides which decline the receipt names, so both halves have to agree, and a decline card charged a round amount simply succeeds. If you are testing refusals and getting approvals, this is why.Testing 3-D Secure
On a merchant acquired by the sandbox mock acquirer the last three digits of the minor-unit amount select the authentication outcome, the same way the amount selects a decline.
301 challenge, passes when it is completed
302 challenge, refused when it is completed
303 authentication unavailable, the payment proceeds without a liability shift
304 not supported, the payment proceeds without a liability shift
305 refused before any challenge
306 challenge, and the payer chooses the outcome on the page
307 challenge whose URL leads nowhere, for testing a provider that misbehaves
any other frictionless passAn amount of 10301 is challenged and passes, and 10302 is challenged and refused.
306 is the one to use by hand. The others settle the moment the challenge URL is opened, with nothing rendered, which suits a script and shows a person nothing. 306 serves a page with an approve and a decline button, and the button pressed is the outcome, so both paths are reachable from the one amount.
307 hands back a challenge URL that returns a 404. The payment stays waiting on a challenge that can never be completed, which is what a provider having a bad day looks like to you.
The "authentication required" response carries the challenge URL. In sandbox, open it directly: it completes the challenge and redirects to the returnUrl, and the payment is then finished with the continuation exactly as the 3-D Secure guide shows.
4000 0025 0000 3155 applies to Stripe-acquired merchants only.
Before you go live, check three things that are easy to miss: that you branch on errorCode rather than message text, that you verify webhook signatures, and that you read the resource's own status rather than inferring success from an HTTP 200.
