Preparatory phase
First of all, you needs to make sure that the following steps have been completed:
- Wallet was added, generated API key, IPN secret key;
- The correct endpoint address for receiving webhooks is added to the IPN callback url field in the dashboard;
No further actions are required in dashboard, but it is essential that these actions are performed. If IPN secret key is missing, webhooks will not be sent due to technological features of the service.
Configuration on your side
The most difficult and beyond our control is the configuration of the endpoint to accept webhooks on your side. For this you will need to either use a ready-made solution or write the logic manually. How the basic script for receiving webhooks should look like is described in our API documentation.
In brief:
- The signature received with the webhook body should be saved to a variable;
- The webhook body (text between curly braces { }, including them ) should be sorted alphabetically by key;
- The sorted callback body is hashed using the HMAC(SHA-512) algorithm with the IPN secret key as the key;
- The resulting string must be identical to the received signature, otherwise the webhook is not considered valid;
We strongly recommend to hire coding specialist to minimise possible integration difficulties.
Server configuration
Server setup is fully on your side, since we don't know particular characteristics and specifications. The main points your developer should pay attention to:
- A firewall should not prevent our POST requests from passing through to the address specified as the IPN callback url.
- There can be more than one firewall in the system.
- The IP addresses of our notification server should be waitlisted;
- 51.89.194.21
- 51.75.77.69
- 138.201.172.58
- 65.21.158.36
- Endpoint must not be closed by authorisation, or other such requirements;
- The endpoint shall respond within 3000 ms;
- We can't send post request to localhost.
We do not recommend to use shared-based hosting services, since they are unreliable and you need to be extremely careful when setting up Cloudflare if it is an external firewall. It has a peculiarity not only to completely block POST requests from us, but also to partially cut them off.
IPN Setup
Instant Payments Notifications IPN (Instant payment notifications, or callbacks) are used to notify you when transaction status is changed.
To use them, you should complete the following steps:
- Generate and save the IPN Secret key in Payment Settings tab at the Dashboard;
- Insert your URL address where you want to get callbacks in create_payment request. The parameter name is ipn_callback_url.
- You will receive payment updates (statuses) to this URL address. Please, take note that we cannot send callbacks to your localhost unless it has dedicated IP address.
- Important! Please make sure that firewall software on your server (i.e. Cloudflare) does allow our requests to come through. It may be required to whitelist our IP addresses on your side to get it.
- You will receive all the parameters at the URL address you specified in (2) by POST request; The POST request will contain the x-nowpayments-sig parameter in the header. The body of the request is similiar to a get payment status response body. You can see examples in "Webhook examples" section.
- Sort the POST request by keys and convert the it to string using JSON.stringify (params, Object.keys(params).sort()) or the same function;
- Sign a string with an IPN-secret key with HMAC and sha-512 key;
- Compare the signed string from the previous step with the x-nowpayments-sig , which is stored in the header of the callback request; If these strings are similar it is a success.
Example of creating a signed string at Node.JS
function sortObject(obj) {
return Object.keys(obj).sort().reduce(
(result, key) => {
result[key] = (obj[key] && typeof obj[key] === 'object') ? sortObject(obj[key]) : obj[key]
return result
},
{}
)
}
const hmac = crypto.createHmac('sha512', notificationsKey);
hmac.update(JSON.stringify(sortObject(params)));
const signature = hmac.digest('hex');
Example of creating a signed string in PHP
function tksort(&$array)
{
ksort($array);
foreach(array_keys($array) as $k)
{
if(gettype($array[$k])=="array")
{
tksort($array[$k]);
}
}
}
function check_ipn_request_is_valid()
{
$error_msg = "Unknown error";
$auth_ok = false;
$request_data = null;
if (isset($_SERVER['HTTP_X_NOWPAYMENTS_SIG']) && !empty($_SERVER['HTTP_X_NOWPAYMENTS_SIG'])) {
$recived_hmac = $_SERVER['HTTP_X_NOWPAYMENTS_SIG'];
$request_json = file_get_contents('php://input');
$request_data = json_decode($request_json, true);
tksort($request_data);
$sorted_request_json = json_encode($request_data, JSON_UNESCAPED_SLASHES);
if ($request_json !== false && !empty($request_json)) {
$hmac = hash_hmac("sha512", $sorted_request_json, trim($this->ipn_secret));
if ($hmac == $recived_hmac) {
$auth_ok = true;
} else {
$error_msg = 'HMAC signature does not match';
}
} else {
$error_msg = 'Error reading POST data';
}
} else {
$error_msg = 'No HMAC signature sent.';
}
}
Notes for PHP only:
- If you are using utf-8 signs in your request body you should add JSON_UNESCAPED_UNICODE like this:
- $sorted_request_json = json_encode($request_json, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)
- Set your precision as 6 (precision means number of decimal places). It should be set on your server.
Example comparing signed signatures in Python
import json
import hmac
import hashlib
def np_signature_check(np_secret_key, np_x_signature, message):
sorted_msg = json.dumps(message, separators=(',', ':'), sort_keys=True)
digest = hmac.new(
str(np_secret_key).encode(),
f'{sorted_msg}'.encode(),
hashlib.sha512)
signature = digest.hexdigest()
if signature == np_x_signature:
return
else:
print("HMAC signature does not match")
Usually you will get a notification per each step of processing payments, withdrawals, or transfers, related to custodial recurring payments.
The webhook is being sent automatically once the transaction status is changed. You also can request an additional IPN notification using your NOWPayments dashboard.
Please note that you should set up an endpoint which can receive POST requests from our server. Before going production we strongly recommend to test the endpoint by making a test request to it by yourself to ensure it's working properly.
Recurrent payment notifications
If an error is detected, the payment will be flagged and will receive additional recurrent notifications (number of recurrent notifications can be changed in your Payment Settings-> Instant Payment Notifications).
If an error is received again during the payment processing, recurrent notifications will be initiated again. Example: "Timeout" is set to 1 minute and "Number of recurrent notifications" is set to 3. Once an error is detected, you will receive 3 notifications at 1 minute intervals.
Webhook examples
Payments
{
"payment_id":123456789,
"parent_payment_id":987654321,
"invoice_id":null,
"payment_status":"finished",
"pay_address":"address",
"payin_extra_id":null,
"price_amount":1,
"price_currency":"usd",
"pay_amount":15,
"actually_paid":15,
"actually_paid_at_fiat":0,
"pay_currency":"trx",
"order_id":null,
"order_description":null,
"purchase_id":"123456789",
"outcome_amount":14.8106,
"outcome_currency":"trx",
"payment_extra_ids":null
"fee": {
"currency":"btc",
"depositFee":0.09853637216235617,
"withdrawalFee":0,
"serviceFee":0
}
}
Withdrawals
{
"id":"123456789",
"batch_withdrawal_id":"987654321",
"status":"CREATING",
"error":null,
"currency":"usdttrc20",
"amount":"50",
"address":"address",
"fee":null,
"extra_id":null,
"hash":null,
"ipn_callback_url":"callback_url",
"created_at":"2023-07-27T15:29:40.803Z",
"requested_at":null,
"updated_at":null
}
Custodial recurring payments
{
"id":"1234567890",
"status":"FINISHED",
"currency":"trx",
"amount":"12.171365564140688",
"ipn_callback_url":"callback_url",
"created_at":"2023-07-26T14:20:11.531Z",
"updated_at":"2023-07-26T14:20:21.079Z"
}