Developers
Built by people who have integrated a payment API before
Idempotency keys that work, errors that say what went wrong, webhooks you do not have to poll around, and a sandbox that behaves like production. None of that is remarkable. It is just frequently missing.
Principles
What we committed to
These are properties of the API rather than features of a plan. They apply to every account.
Idempotency on every write
Send an Idempotency-Key header and a replayed request returns the original result instead of creating a second transaction.
Predictable errors
Every failure returns a stable machine-readable code alongside a human-readable message. Codes do not change between releases.
Webhooks over polling
State changes are pushed to your endpoint, signed so you can verify they came from us. Nothing requires a polling loop.
Versioned, not broken
Breaking changes ship behind a new version. Your integration keeps working against the version you built on.
Sandbox parity
The sandbox exposes the same endpoints, the same payloads and the same error codes as production, with no live funds.
Paise, not rupees
Amounts are integers in the smallest currency unit. No floating point anywhere near money.
Webhooks
Verify the signature, acknowledge fast
Every webhook is signed with HMAC-SHA256 over the raw body. Verify before you trust it, return a 2xx quickly, and do the work asynchronously — we retry anything that is not acknowledged.
Events
payment.capturedFunds captured against an order
payment.failedPayment failed permanently, with a reason code
refund.processedRefund submitted to the network
payout.queuedPayout accepted and awaiting processing
payout.processedPayout has left the account
payout.failedPayout rejected, with a reason code
payout.reversedPayout returned by the beneficiary bank
virtual_account.creditedInbound credit received and attributed
settlement.processedSettlement sent to your bank account
// Verify the signature before trusting the payload.
import crypto from 'node:crypto'
export function verify(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
// Constant-time compare — a plain === leaks timing information.
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature),
)
}
app.post('/webhooks/trustpay', (req, res) => {
if (!verify(req.rawBody, req.get('X-TrustPay-Signature'), SECRET)) {
return res.sendStatus(401)
}
const event = JSON.parse(req.rawBody)
// Acknowledge fast, process asynchronously. We retry on non-2xx.
queue.push(event)
res.sendStatus(200)
})Endpoints
The surface you will actually use
| POST | /v1/ordersCreate an order to collect against |
| POST | /v1/payments/:id/captureCapture an authorised payment |
| POST | /v1/refundsRefund a payment in full or in part |
| POST | /v1/payoutsSend a single payout |
| POST | /v1/payouts/batchSubmit a batch of payouts |
| POST | /v1/virtual-accountsIssue a virtual account for a customer |
| GET | /v1/transactionsQuery and export transactions |
| GET | /v1/settlementsRetrieve settlement records |
Errors
What a failure looks like
A stable code to switch on, the offending field, a request_id to quote at support, and an explicit retryable flag so you are not guessing.
{
"error": {
"code": "payout_account_invalid",
"message": "Beneficiary account number failed validation",
"field": "account.number",
"request_id": "req_9f2b41c8",
"retryable": false
}
}SDKs
Or just use HTTP
Node.js
npm install @trustpay/nodePython
pip install trustpayPHP
composer require trustpay/trustpay-phpJava
Maven and Gradle artifactsReact / React Native
Drop-in checkout componentsREST
Everything is available over plain HTTP if you preferQuestions
Integration, answered
How long does integration usually take?
A basic collection flow is typically a day of work against the sandbox. Payouts are faster because you can run them from the dashboard without any code while the integration is still being built.
Do you have a sandbox?
Yes, with the same endpoints, payloads and error codes as production and no live funds. Test credentials are issued at onboarding rather than after go-live.
How are webhooks secured?
Each webhook carries an HMAC-SHA256 signature computed over the raw request body using your webhook secret. Verify it with a constant-time comparison before acting on the payload.
What happens if my webhook endpoint is down?
We retry with exponential backoff over an extended window. Events are also queryable through the API, so a prolonged outage does not mean lost state.
Are amounts in rupees or paise?
Paise. All amounts are integers in the smallest currency unit, which avoids floating-point rounding on money.
Get sandbox credentials
Test keys are issued at onboarding, so you can build against the real API surface before anything goes live.