Callbacks receiving
Transaction event payload
Here is an example of a callback payload:
Callback payload example
Header: x-callback-signature
Body:
{
"redirectUrl": "https://buy.moonpay.com/?CurrencyCode=eth&baseCurrencyCode=usd¤cyCode=eth&baseCurrencyAmount=150&externalTransactionId=71ahw34&walletAddress=0x8cfbd31371e9bec8c82ae101e25bd9394c03a227",
"orderId": "5154302e-3stl-75p4",
"status": "pending",
"externalUserId": "122hd",
"externalOrderId": "71ahw34",
"providerCode": "moonpay",
"currencyFrom": "USD",
"currencyTo": "ETH",
"amountFrom": "150",
"country": "EE",
"state": null,
"ip": null,
"walletAddress": "0x8cfbd31371e9bec8c82ae101e25bd9394c03a227",
"walletExtraId": null,
"paymentMethod": "card",
"userAgent": null,
"metadata": null,
"createdAt": "2019-07-22T10:10:09.000",
"payinAmount": "150",
"payoutAmount": "0.0756",
"payinCurrency": "USD",
"payoutCurrency": "ETH",
"transactionHash": "f418********************************38530e9831e9e16"
}
Response schema (application/json):
| Parameter | Description |
|---|---|
| redirectUrl | URL to the provider's purchase page. |
| orderId | Internal order ID provided by Fiat API. You can use this field to get information that the transaction status was updated. |
| status | Transaction status. [Possible values](Possible values). You can use this field to get information that the transaction status was updated. |
| externalUserId | User ID provided by you. |
| externalOrderId | Order ID provided by you. You can use this field to get information that the transaction status was updated. |
| providerCode | The On-Ramp or Off-Ramp provider code. Possible values. |
| currencyFrom | Ticker of the payin currency (in uppercase). |
| currencyTo | Ticker of the payout currency (in uppercase). |
| amountFrom | Amount of currency the user is going to pay. |
| country | Country ISO 3166-1 code (Alpha-2). |
| state | State ISO 3166-2 code. |
| ip | User's IP address. |
| walletAddress | Recipient wallet address. |
| walletExtraId | Property required for wallet addresses of currencies that use an additional ID for transaction processing (XRP, XLM, EOS, BNB). |
| paymentMethod | The payment method code. Possible values. |
| userAgent | User Agent. |
| metadata | Metadata object, which can contain any parameters you need:
|
| createdAt | Time in ISO 8601 format. |
| updatedAt | Time in ISO 8601 format. |
| payinAmount | Payin amount. |
| payoutAmount | The estimated payout amount. |
| payinCurrency | Ticker of the payin currency. |
| payoutCurrency | Ticker of the payout currency. |
| transactionHash | Transaction hash in the blockchain. |
Supported transaction statuses
Webhook payload passes a transaction status in the status field. Learn more details about supported transaction statuses.
Pay attention that some providers do not support certain transaction statuses.
Webhooks signature
To make sure that the incoming requests are originating from a trusted source, you can validate them using our webhook signature. We highly recommend using this practice for security reasons.
Changelly signs webhook events sent to you. To use webhooks signature, we will generate public and private API keys. Your account manager will send you a public key to verify the signature in webhooks.
Each event includes a signature which is done using the signature header. This header includes orderId signed by the private key. This will allow you to validate that the events have been submitted by Changelly, not a third party.
Before you can verify signatures for webhook events, please notice that all requests must contain the following header:
| Header | Description |
|---|---|
| x-callback-api-key | Your public API key to verify the signature in webhooks. |
| x-callback-signature | The serialized string with an orderId signed by our private key according to the RSA-SHA256 method. |
There is an example of how to validate your x-callback-signature parameter in webhooks with Node.js:
Form an object with the orderId parameter that is sent in the response body in the webhook.
Serialize the generated object in JSON format.
Use the generated string and the public key given by your account manager to verify the signature received from us by checking the SHA256 signature.
There are code examples to validate signature:
- Express.js
- PHP
import crypto from 'crypto';
import express from 'express';
const CALLBACK_API_KEY = '<Your API key>';
const CALLBACK_PUBLIC_KEY = '<Your public callback key>';
function _validateSignature(signature, payload) {
const publicKeyObject = crypto.createPublicKey({
key: CALLBACK_PUBLIC_KEY,
type: 'pkcs1',
format: 'pem',
encoding: 'base64',
});
const payloadBuffer = Buffer.from(payload);
const signatureBuffer = Buffer.from(signature, 'base64');
return crypto.verify('sha256', payloadBuffer, publicKeyObject, signatureBuffer);
}
const app = express();
app.use(express.json());
app.post('/callback', (req, res) => {
const payload = req.body;
const apiKey = req.headers['x-callback-api-key'];
const signature = req.headers['x-callback-signature'];
if (apiKey !== CALLBACK_API_KEY) {
return res.status(400).send({status: 'error'});
}
const signaturePayload = JSON.stringify({orderId: payload.orderId});
if (!_validateSignature(signature, signaturePayload)) {
return res.status(400).send({status: 'error'});
}
console.log('Callback payload: ', payload);
return res.status(200).send({status: 'success'});
});
app.listen(4200, () => {
console.log('Server listening on port http://localhost:4200');
});
<?php
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
const CALLBACK_API_KEY = '<Your API key>';
const CALLBACK_PUBLIC_KEY = '<Your public callback key>';
function isValidSignature(string $signature, array $payload): bool
{
$key = PublicKeyLoader::load(base64_decode(self::API_CALLBACK_PUBLIC_KEY))->withPadding(RSA::ENCRYPTION_PKCS1);
$publicKey = openssl_pkey_get_public($key);
$signature = base64_decode($signature);
$signaturePayload = json_encode(['orderId' => $payload['orderId']]);
return openssl_verify($signaturePayload, $signature, $publicKey, OPENSSL_ALGO_SHA256);
}
?>
For reading PKCS1 key you must use phpseclib.