Start with the full payment flow
Complete the common setup, create a payment, configure IPN, test the full flow, then choose how incoming payments should work for your business.
For developers
Use API snippets and IPN validation examples to connect your backend.
For product teams
Choose the right payment logic: fixed-price payments, balance top-ups, or payouts.
For launch
Use dashboard settings and the go-live checklist before production.
1. Set up your account
Configure the account settings required before creating and processing payments.
Generate API key
Your API key is generated automatically and is required for API integration.
Set base currency
Base currency is the currency in which the price is displayed.
Generate IPN secret key
Generate and save the IPN Secret key in Payment Settings tab at the Dashboard.
Whitelist NOWPayments IP addresses
Make sure firewall software on your server allows NOWPayments requests. It may be required to whitelist NOWPayments IP addresses on your side. For assistance contact support@nowpayments.io. Don't forget to add IPv-4 and IPv-6 addresses, if you have both. It's important.
2. Create your first payment
Use the Create Payment request and include your IPN callback URL.
Create Payment API request
Insert your URL address where you want to get callbacks in the create_payment request. The parameter name is ipn_callback_url.
import requests
import json
url = "https://api.nowpayments.io/v1/payment"
payload = json.dumps({
"price_amount": 3999.5,
"price_currency": "usd",
"pay_currency": "btc",
"ipn_callback_url": "https://nowpayments.io",
"order_id": "RGDBP-21314",
"order_description": "Apple Macbook Pro 2019 x 1"
})
headers = {
'x-api-key': '{{api-key}}',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
Payment address
In response, you get pay_address. This is the deposit address where the user should send the money.
3. Set up IPN
IPN sends payment updates to the callback URL specified in your Create Payment request.
IPN flow
| Requirement | Details |
|---|---|
| Endpoint | Set up an endpoint which can receive POST requests from NOWPayments server. |
| Header | The POST request contains the x-nowpayments-sig parameter in the header. |
| Body | The body is similar to a get payment status response body. |
| Before production | Make a test request to this endpoint to ensure it works properly. |
Server configuration
| Check | Requirement |
|---|---|
| Firewall | A firewall should not prevent NOWPayments POST requests from passing through to the address specified as the IPN callback URL. There can be more than one firewall in the system. |
| Notification server IPs | Whitelist 51.89.194.21, 51.75.77.69, 138.201.172.58, 65.21.158.36. |
| Endpoint access | Endpoint must not be closed by authorisation, or other such requirements. |
| Response time | The endpoint shall respond within 3000 ms. |
| Hosting / Cloudflare | NOWPayments does not recommend shared-based hosting services. Be extremely careful when setting up Cloudflare if it is an external firewall. |
Rate limits
| Endpoint | Limit | If exceeded |
|---|---|---|
| GET estimate | 7 requests per second from one outgoing IP address | 429 error code |
| POST payment | 3 requests per second from one outgoing IP address | 429 error code |
| GET /payment/:id | 10 requests per second | 429 error code |
Signature validation steps
Sort the POST request by keys
Convert it to string using JSON.stringify(params, Object.keys(params).sort()) or the same function.
Sign the string
Sign a string with an IPN-secret key with HMAC and sha-512 key.
Compare signatures
Compare the signed string with x-nowpayments-sig, stored in the callback request header.
IPN validation examples
Webhook example: 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
}
}| Parameter | Use |
|---|---|
| payment_status | Use payment status when processing payment updates. |
| outcome_amount | Check the actual amount received. |
| outcome_currency | Check the currency actually received. |
| x-nowpayments-sig | Validate the callback signature. |
| fee | Review fee details when present in the webhook body. |
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');
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.';
}
}
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")
4. Test the full payment flow
Create a payment, receive payment status updates through IPN, and verify the final payment data before granting goods or crediting a balance.
Test checklist
Payment statuses
| Status | Meaning |
|---|---|
waiting | Waiting for the customer to send the payment. |
confirming | The transaction is being processed on the blockchain. |
confirmed | The process is confirmed by the blockchain. |
sending | The funds are being sent to your personal address or custody balance. |
finished | The funds have reached your personal address or custody balance and the payment is finished. |
partially_paid | A completed payment. This status means you've already received your funds, but the amount sent by your customer was less than the settled price. |
failed | The payment was not completed due to an error. |
expired | The user did not send funds within the payment window. |
confirming or confirmed statuses.5. Choose incoming payment logic
Select how incoming payments should be handled. Only the selected logic is shown.
Fixed-price payments
User pays for a product or service with a fixed price.
Balance top-ups
User deposits funds to credit an internal balance.
6. Configure dashboard features
Use dashboard features to solve payment processing cases before going live.
Repeated Payments
Set the default payment status for repeated payments made by your user to the same deposit address.
Wrong-asset deposits auto processing
Process or do not process wrong-asset deposits automatically. Payment status can be set to Partially Paid or Finished.
Payment Covering
Set the percentage of the payment that needs to be paid for it to be considered completed.
Network Fee Optimization
Among your wallets, a payment picks the most suitable one and undergoes a conversion resulting in the lowest network fee in total.
Payment Markup
Allows setting a markup percentage over the initial price, from 0% to 10%.
My Team
Add team members and customize their access to the main account. Team members need to have a NOWPayments account.
7. Send payouts
Choose how you want to send funds: to users by email with ChangeNOW Pro, or to crypto wallets through payouts.
ChangeNOW Pro Payouts
Payout to email with $0 fees and instant delivery.
Crypto Payouts
Send payouts from Custody to crypto wallet addresses.
8. Go live checklist
Complete the checklist before going live.
finished, unless your selected scenario uses partially_paid for balance top-ups.