- Home
- Developers
Start accepting crypto in minutes
A REST API compatible with NOWPayments v1: the same endpoints, fields, status values and currency codes. Authenticate with x-api-key and receive status updates as signed IPNs.
https://omniwallet.ewin888.com/api/v1/https://omniwallet-dev.ewin888.com/api/v1/Quickstart
- 01
Get an API key
Sign up, open the console and create a key under Developers → API keys, then set an IPN secret under IPN. We recommend building against the sandbox first.
- 02
Create a payment
Call
POST /v1/paymentwith the amount, price currency and pay currency. The response containspay_address, the payment’s unique address, andpay_amount, the amount due.POST /v1/paymentcurlcurl -X POST "https://omniwallet.ewin888.com/api/v1/payment" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-A-1024" \ -d '{ "price_amount": 120, "price_currency": "usd", "pay_currency": "usdttrc20", "order_id": "A-1024", "order_description": "Pro plan, 12 months", "ipn_callback_url": "https://example.com/ipn" }'ResponseJSON{ "payment_id": 355446374401130, "payment_status": "waiting", "pay_address": "TJ8nq3M5yVfQ9C2dWkPz7aH4sLrE6uXbGt", "price_amount": 120, "price_currency": "usd", "pay_amount": 120.02, "pay_currency": "usdttrc20", "order_id": "A-1024", "network": "TRC20", "expiration_estimate_date": "2026-10-02T09:15:00.000Z", "omni_payment_url": "https://omniwallet.ewin888.com/payment/4fKq9ZtR2mXcL8vBnW1pYs" } - 03
Collect the payment
Show the address and amount (or a QR code) to your customer — or use
POST /v1/invoiceto create a hosted invoice and redirect the customer to itsinvoice_url.POST /v1/invoicecurlcurl -X POST "https://omniwallet.ewin888.com/api/v1/invoice" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "price_amount": 49.9, "price_currency": "usd", "order_id": "A-1025", "order_description": "Starter plan", "ipn_callback_url": "https://example.com/ipn", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/cart" }'ResponseJSON{ "id": "355447603200201", "order_id": "A-1025", "price_amount": "49.9", "price_currency": "usd", "pay_currency": null, "invoice_url": "https://omniwallet.ewin888.com/invoice/8hTz2QpLm4XwN7cVb1RkYe", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/cart", "created_at": "2026-10-01T09:20:00.000Z" } - 04
Receive IPNs
Every time the payment status changes we POST to your
Verify IPNsipn_callback_url. Verify the signature first, then update the order based onpayment_status. - 05
Go live
Once your business is verified, create a new API key and IPN secret in production and switch the base URL.
Verify IPN signatures
We compute an HMAC-SHA512 with your IPN secret over the JSON body with keys sorted alphabetically, and send it hex-encoded in the x-omni-sig header. The request body we send is exactly that sorted JSON, so you can verify the raw body as received.
- Compare signatures in constant time
- IPNs can arrive more than once or out of order: de-duplicate by
payment_idand status, and callGET /v1/payment/{id}for the latest state when in doubt - Any 2xx response acknowledges the IPN; other responses and timeouts are retried automatically
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const IPN_SECRET = process.env.OMNI_IPN_SECRET;
// Keep the raw body: the signature covers the exact bytes we send.
app.post('/ipn', express.raw({ type: 'application/json' }), (req, res) => {
const received = String(req.get('x-omni-sig') || '');
const expected = crypto
.createHmac('sha512', IPN_SECRET)
.update(req.body)
.digest('hex');
const valid = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send('invalid signature');
const payment = JSON.parse(req.body.toString('utf8'));
// Idempotent update: the same status can be delivered more than once.
// await orders.applyStatus(payment.order_id, payment.payment_status);
res.sendStatus(200);
});
app.listen(3000);<?php
$secret = getenv('OMNI_IPN_SECRET');
$raw = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_OMNI_SIG'] ?? '';
// The signature covers the exact bytes we send.
$expected = hash_hmac('sha512', $raw, $secret);
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit('invalid signature');
}
$payment = json_decode($raw, true);
// Idempotent update: the same status can be delivered more than once.
// apply_status($payment['order_id'], $payment['payment_status']);
http_response_code(200);import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
IPN_SECRET = os.environ["OMNI_IPN_SECRET"].encode()
@app.post("/ipn")
def ipn():
raw = request.get_data() # the exact bytes we signed
expected = hmac.new(IPN_SECRET, raw, hashlib.sha512).hexdigest()
received = request.headers.get("x-omni-sig", "")
if not hmac.compare_digest(expected, received):
abort(401)
payment = json.loads(raw)
# Idempotent update: the same status can be delivered more than once.
# apply_status(payment["order_id"], payment["payment_status"])
return "", 200{
"actually_paid": 120.02,
"actually_paid_at_fiat": 120,
"invoice_id": null,
"order_description": "Pro plan, 12 months",
"order_id": "A-1024",
"outcome_amount": 119.42,
"outcome_currency": "usdttrc20",
"parent_payment_id": null,
"pay_address": "TJ8nq3M5yVfQ9C2dWkPz7aH4sLrE6uXbGt",
"pay_amount": 120.02,
"pay_currency": "usdttrc20",
"payment_id": 355446374401130,
"payment_status": "finished",
"price_amount": 120,
"price_currency": "usd",
"purchase_id": null,
"updated_at": "2026-10-01T09:17:42.000Z"
}Payment statuses
Status values are identical to NOWPayments.
| Status | Meaning |
|---|---|
waitingWaiting | Waiting for the customer to pay |
confirmingConfirming | Transaction detected, waiting for block confirmations |
confirmedConfirmed | Confirmed and credited |
sendingSettling | Being settled to your wallet |
partially_paidPartially paid | Underpaid; the customer can top up before expiry |
finishedFinished | Payment complete |
failedFailed | Payment failed, e.g. a sanctions list match |
refundedRefunded | Refunded to the payer |
expiredExpired | Not paid in time |
Migrate from NOWPayments
The API is compatible, so most of your code stays the same.
- Change the base URLReplace
https://api.nowpayments.io/v1/with the OmniWallet URL below. - Use an OmniWallet API keyThe header is still
x-api-key. - Update IPN verificationUse your OmniWallet IPN secret and the
x-omni-sigheader. To keep your existing handler unchanged, enable thex-nowpayments-sigcompatibility header in the console. - Check supported coinsCurrency codes are the same (for example
usdttrc20orusdcsol), but only the coins we support are available. CheckGET /v1/merchant/coinsor the supported coins page. - Review payout settingsPayouts still use a 5-minute JWT from
POST /v1/auth. In addition, API keys with payout permissions need an IP allowlist, and destination addresses must be whitelisted first.
- https://api.nowpayments.io/v1/
+ https://omniwallet.ewin888.com/api/v1/Stays the same
- Endpoint paths and HTTP methods
- Request and response field names
- Payment and payout status values
- Currency codes
- IPN signature algorithm
Differences to note
- The IPN signature header is
x-omni-sig(optionally also sent asx-nowpayments-sig) - OmniWallet extension fields start with
omni_ - Fiat payouts are not supported (HTTP 501)
- Settlements always go to the wallet registered in the console
- The sandbox runs on real blockchain testnets
Sandbox
The sandbox is a separate deployment connected to each chain’s testnet, with exactly the same API as production.
- Accounts, API keys and data are separate from production, so sign up separately; merchant verification is approved automatically
- Pay with testnet coins; transactions are visible in each testnet’s block explorer
- Test coins have no value — never send mainnet funds to sandbox addresses
| Network | Testnet | Available currency codes |
|---|---|---|
| Ethereum | Ethereum Sepolia | eth usdc |
| BNB Smart Chain | BNB Smart Chain Testnet | bnbbsc usdtbsc |
| Polygon | Polygon Amoy | maticmainnet usdcmatic |
| Avalanche C-Chain | Avalanche Fuji | avax usdcavax |
| Arbitrum | Arbitrum Sepolia | etharb usdcarb |
| Base | Base Sepolia | ethbase usdcbase |
| Optimism | OP Sepolia | ethop usdcop |
| TRON | TRON Nile | trx usdttrc20 |
| Solana | Solana Devnet | sol usdcsol |
| Bitcoin | Bitcoin Testnet4 | btc |
Idempotency and retries
Every POST request that creates a resource accepts an Idempotency-Key header. If a request times out, retry it with the same key and no duplicate payment or payout will be created.
- Same key, same body: the original result is returned
- Same key, different body: HTTP 422
IDEMPOTENCY_KEY_REUSED - First request still in progress: HTTP 409
IDEMPOTENCY_IN_FLIGHT— retry shortly
POST /api/v1/payout HTTP/1.1
Host: omniwallet.ewin888.com
x-api-key: YOUR_API_KEY
Authorization: Bearer YOUR_JWT
Idempotency-Key: 6c0a8e6e-3f5b-4f7a-9a52-1f0c2d9b7e41
Content-Type: application/jsonStart accepting crypto payments
Sign up for free, build your integration in the sandbox, and switch to production once your business is verified.