# API compatível com a NOWPayments

> Passe da NOWPayments para a blockwallets trocando a URL base e as chaves: endpoints compatíveis, callbacks e assinaturas IPN, tickers de moedas e diferenças.
>
> HTML: https://blockwallets.io/docs/nowpayments?lang=pt · English: https://blockwallets.io/docs/nowpayments.md?lang=en

## API compatível com a NOWPayments

Os caminhos, o cabeçalho `x-api-key`, os campos de requisição e resposta, os status e a assinatura IPN `x-nowpayments-sig` seguem a documentação da NOWPayments, então o código existente normalmente só precisa de uma nova URL base, uma nova API key e um novo IPN secret. Vindo de outro lugar? Veja a [visão geral da migração](https://blockwallets.io/docs/migrate?lang=pt).

Por baixo é uma cobrança comum da blockwallets: os endereços de recebimento são seus, a página de pagamento é o nosso checkout hospedado, e um pagamento só vale depois de confirmado na blockchain e conferido com um segundo nó. A seguir: três passos, depois o que cada API suporta e **como ela difere da original** — parâmetros que não implementamos retornam erro em vez de serem ignorados em silêncio.

### Três passos

- Consiga uma conta de lojista ([solicite na página inicial](https://blockwallets.io/?lang=pt#join)) e cadastre os seus domínios em “Domínios do site”, em “Desenvolvedores › Integração e chaves” no painel. As URLs de callback (`ipn_callback_url`) e de redirecionamento (`success_url` / `cancel_url` / `redirect_url`) precisam estar nesses domínios ou no domínio da sua URL de callback — uma chave vazada não consegue apontar webhooks para outro lugar.

- Gere credenciais compatíveis no painel do lojista, em “Desenvolvedores › Integração e chaves”, um conjunto para live e outro para test: a NOWPayments recebe uma **API key + IPN secret**, a Coinbase Commerce uma **API key + webhook shared secret**. Elas são exibidas uma única vez; guardamos apenas hashes / texto cifrado.

- Aponte o seu código para a URL base abaixo e use as novas chaves. Rode primeiro nas testnets com as credenciais de test e depois passe para live.

| Antes | Depois | Cabeçalho de autenticação | Assinatura do webhook |
| --- | --- | --- | --- |
| `https://api.nowpayments.io/v1` | `https://blockwallets.io/nowpayments/v1` | `x-api-key` | `x-nowpayments-sig` (HMAC-SHA512) |

### NOWPayments

```javascript
- const NP = "https://api.nowpayments.io/v1";
+ const NP = "https://blockwallets.io/nowpayments/v1";

const res = await fetch(`${NP}/payment`, {
  method: "POST",
  headers: { "x-api-key": process.env.NOWPAYMENTS_API_KEY, "content-type": "application/json" },
  body: JSON.stringify({
    price_amount: 49.9, price_currency: "usd", pay_currency: "usdttrc20",
    order_id: "A-1001", order_description: "Pro plan",
    ipn_callback_url: "https://shop.example/ipn/nowpayments",
  }),
});
const { payment_id, pay_address, pay_amount } = await res.json();
```

#### Endpoints suportados

| Endpoint | Observações |
| --- | --- |
| `GET /status` | `{"message":"OK"}`, sem necessidade de chave |
| `GET /currencies` · `/merchant/coins` · `/full-currencies` | Moedas que você pode aceitar agora (tickers abaixo); `fixed_rate` é ignorado |
| `GET /min-amount` · `GET /estimate` | Mínimos e estimativas; precificação apenas em USD (USDT / USDC contam 1:1) |
| `POST /payment` | Cria um pagamento; retorna `pay_address` / `pay_amount` na hora (white-label) |
| `POST /invoice` | Cria uma fatura (invoice); `invoice_url` é o nosso checkout hospedado, onde o cliente escolhe moeda e rede |
| `POST /invoice-payment` | Escolhe a moeda de uma fatura (invoice) e retorna o pagamento |
| `GET /payment/:id` | Status do pagamento |
| `GET /payment/` | Lista: `limit` (1–500) / `page` / `orderBy` / `dateFrom` / `dateTo` / `invoiceId`; `sortBy` somente `created_at` |
| `POST /auth` | A lista só precisa de `x-api-key`, sem JWT; este retorna um token de preenchimento sem verificação para que o código “fazer login e depois listar” continue funcionando (e-mail / senha não são verificados) |

Não disponíveis: payouts, conversões, subparceiros de custódia, assinaturas, `update-merchant-estimate`, pagamentos em moeda fiduciária. Esses caminhos retornam 404.

#### Callbacks IPN

Quando um pagamento chega a um status final, enviamos um POST para a `ipn_callback_url` informada na criação, com `x-nowpayments-sig` = HMAC-SHA512(IPN secret, o JSON ordenado recursivamente por chave) — exatamente o código de verificação da NOWPayments. O corpo que enviamos já é um JSON compacto e ordenado, então um HMAC sobre o corpo bruto também funciona (e é a escolha mais segura em PHP / Python, cuja recodificação de decimais muito pequenos pode diferir da do JS). Callbacks que falham são reenviados após 1 min, 5 min, 30 min, … — 8 tentativas no total.

```nodejs
import crypto from "node:crypto";

// Same as the NOWPayments docs (our body is already sorted, so hashing the raw body works too)
function sortObject(obj) {
  return Object.keys(obj).sort().reduce((r, k) => {
    r[k] = obj[k] && typeof obj[k] === "object" ? sortObject(obj[k]) : obj[k];
    return r;
  }, {});
}
function verifyIpn(params, sig, ipnSecret) {
  const hmac = crypto.createHmac("sha512", ipnSecret).update(JSON.stringify(sortObject(params))).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(sig || ""));
}
// verifyIpn(req.body, req.headers["x-nowpayments-sig"], process.env.NOWPAYMENTS_IPN_SECRET)
```

```php
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
tksort($data);  // the recursive ksort from the NOWPayments docs
$hmac = hash_hmac('sha512', json_encode($data, JSON_UNESCAPED_SLASHES), trim($ipnSecret));
$ok = hash_equals($hmac, $_SERVER['HTTP_X_NOWPAYMENTS_SIG'] ?? '');
```

| payment_status | Quando | IPN enviado |
| --- | --- | --- |
| `waiting` | Aguardando pagamento (inclusive uma fatura (invoice) sem moeda escolhida ainda) | — |
| `confirming` | Pagamento visto na blockchain, aguardando confirmações | — (visível ao consultar) |
| `finished` | Pago (inclusive com pagamento a mais; `actually_paid` é o que chegou) | ✓ |
| `partially_paid` | Insuficiente: o cliente ainda pode complementar no mesmo endereço antes de expirar (enviado uma vez no primeiro pagamento insuficiente), e de novo se ainda faltar na expiração | ✓ |
| `expired` | Expirado sem pagamento | ✓ |
| `refunded` | Você reembolsou o pagamento inteiro pelo painel e ele foi enviado | — (visível ao consultar) |

`confirmed`, `sending` e `failed` nunca aparecem: depois de confirmado vira `finished` e é creditado no seu saldo; nada é repassado.

#### Tickers das moedas

| Moeda | Rede | pay_currency | network | Mínimo |
| --- | --- | --- | --- | --- |
| USDT | TRON | `usdttrc20` | `trx` | $1 |
| USDT | BNB Chain | `usdtbsc` usdtbep20 | `bsc` | $1 |
| USDC | BNB Chain | `usdcbsc` usdcbep20 | `bsc` | $1 |
| USDT | Ethereum | `usdterc20` | `eth` | $5 |
| USDC | Ethereum | `usdc` usdcerc20 | `eth` | $5 |
| USDC | Base | `usdcbase` | `base` | $1 |
| USDT | Polygon | `usdtmatic` usdtpolygon | `matic` | $1 |
| USDC | Polygon | `usdcmatic` usdcpolygon | `matic` | $1 |
| USDT | Arbitrum | `usdtarb` | `arbitrum` | $1 |
| USDC | Arbitrum | `usdcarb` | `arbitrum` | $1 |
| USDT | Optimism | `usdtop` | `op` | $1 |
| USDC | Optimism | `usdcop` | `op` | $1 |
| USDT | Avalanche | `usdtarc20` usdtavax / usdtavaxc | `avaxc` | $1 |
| USDC | Avalanche | `usdcarc20` usdcavax / usdcavaxc | `avaxc` | $1 |
| USDT | X Layer | `usdtxlayer` | `xlayer` | $1 |
| USDC | X Layer | `usdcxlayer` | `xlayer` | $1 |
| USD1 | BNB Chain | `usd1bsc` | `bsc` | $1 |
| FDUSD | BNB Chain | `fdusdbsc` | `bsc` | $1 |
| DAI | Ethereum | `dai` daierc20 | `eth` | $5 |
| DAI | Arbitrum | `daiarb` | `arbitrum` | $1 |
| DAI | Base | `daibase` | `base` | $1 |
| BTC | Bitcoin | `btc` | `btc` | $5 |
| LTC | Litecoin | `ltc` | `ltc` | $1 |
| DOGE | Dogecoin | `doge` | `doge` | $1 |
| BCH | Bitcoin Cash | `bch` | `bch` | $1 |
| TRX | TRON | `trx` | `trx` | $1 |
| BNB | BNB Chain | `bnbbsc` bnb | `bsc` | $1 |
| ETH | Ethereum | `eth` | `eth` | $5 |
| ETH | Base | `ethbase` | `base` | $1 |
| ETH | Arbitrum | `etharb` | `arbitrum` | $1 |
| ETH | Optimism | `ethop` | `op` | $1 |
| POL | Polygon | `maticmainnet` pol / polmainnet / matic | `matic` | $1 |
| AVAX | Avalanche | `avaxc` avax | `avaxc` | $1 |
| OKB | X Layer | `okbxlayer` | `xlayer` | $1 |
| USDT | Solana | `usdtsol` | `sol` | $1 |
| USDC | Solana | `usdcsol` | `sol` | $1 |
| SOL | Solana | `sol` | `sol` | $1 |
| USDT | TON | `usdtton` | `ton` | $1 |
| TON | TON | `ton` | `ton` | $1 |

#### Diferenças em relação à NOWPayments

- `price_currency` é apenas `usd` (mais `usdt` / `usdc` a 1:1); qualquer outra moeda fiduciária retorna 400.

- `pay_amount` (um valor fixo em cripto), `payout_address` / `payout_currency` / `payout_extra_id` e `customer_email` não são suportados e retornam 400.

- `is_fixed_rate` é aceito mas não tem efeito: sempre travamos a cotação (BTC / LTC etc. pela mediana de várias corretoras, por 30 minutos). Com `is_fee_paid_by_user` true, a taxa da plataforma é somada a `pay_amount` (a taxa da plataforma é 1%, menor com mais volume; veja os [preços](https://blockwallets.io/pricing?lang=pt)).

- Os pagamentos valem por até 12 horas (NOWPayments: 7 dias). `partially_paid_url` é guardado e devolvido como está, mas o checkout não redireciona para ele.

- `payment_id` é um número de 10 dígitos; nas faturas (invoices) criadas com `/invoice`, o `payment_id` do pagamento é igual ao `id` da fatura, e uma fatura tem exatamente um pagamento (sem redepósitos com `parent_payment_id`).

- Só são enviados os IPNs de status final e de `partially_paid` (tabela acima), nenhum de `waiting` / `confirming`; `fee.serviceFee` é a taxa da plataforma (o valor recebido × a taxa quando a cobrança foi criada, 1% padrão), as demais taxas são 0.

### Modo de teste

Cobranças e pagamentos criados com credenciais de test rodam na testnet de cada rede e os webhooks deles são assinados com o IPN secret / shared secret de test — totalmente separados do live: uma chave de test não enxerga pagamentos live.

Todo o resto (checkout, confirmações, redes e mínimos, reembolsos) funciona como na [documentação principal](https://blockwallets.io/docs/cryptomus?lang=pt).
