# NOWPayments-kompatible API

> Wechseln Sie von NOWPayments zu blockwallets, indem Sie Base-URL und Schlüssel ändern: unterstützte Endpoints, IPN-Callbacks und Signaturen, Währungskürzel und Unterschiede.
>
> HTML: https://blockwallets.io/docs/nowpayments?lang=de · English: https://blockwallets.io/docs/nowpayments.md?lang=en

## NOWPayments-kompatible API

Pfade, der Header `x-api-key`, Request- und Response-Felder, Status und die IPN-Signatur `x-nowpayments-sig` folgen der Dokumentation von NOWPayments, sodass vorhandener Code meist nur eine neue Base-URL, einen neuen API key und ein neues IPN secret braucht. Sie kommen von woanders? Siehe die [Migrationsübersicht](https://blockwallets.io/docs/migrate?lang=de).

Darunter liegt eine ganz normale blockwallets-Rechnung: Die Empfangsadressen gehören Ihnen, die Zahlungsseite ist unser gehosteter Checkout, und eine Zahlung zählt erst, wenn sie on-chain bestätigt und gegen einen zweiten Node geprüft wurde. Unten: drei Schritte, dann was jede API unterstützt und **wie sie sich vom Original unterscheidet** – Parameter, die wir nicht umsetzen, liefern einen Fehler, statt stillschweigend ignoriert zu werden.

### Drei Schritte

- Besorgen Sie sich ein Händlerkonto ([auf der Startseite beantragen](https://blockwallets.io/?lang=de#join)) und tragen Sie im Dashboard unter „Entwickler › Integration & Keys“ bei „Website-Domains“ Ihre Domains ein. Callback-URLs (`ipn_callback_url`) und Weiterleitungs-URLs (`success_url` / `cancel_url` / `redirect_url`) müssen auf diesen Domains oder der Domain Ihrer Callback-URL liegen – ein geleakter Schlüssel kann Webhooks so nicht woandershin lenken.

- Erzeugen Sie kompatible Zugangsdaten im Händler-Dashboard unter „Entwickler › Integration & Keys“, je einen Satz für Live und für Test: NOWPayments erhält einen **API key + IPN secret**, Coinbase Commerce einen **API key + Webhook shared secret**. Sie werden nur einmal angezeigt; wir speichern nur Hashes / Chiffretext.

- Richten Sie Ihren Code auf die untenstehende Base-URL und nutzen Sie die neuen Schlüssel. Testen Sie zuerst mit den Test-Zugangsdaten auf Testnets und wechseln Sie dann auf Live.

| Vorher | Nachher | Auth-Header | Webhook-Signatur |
| --- | --- | --- | --- |
| `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();
```

#### Unterstützte Endpoints

| Endpoint | Hinweise |
| --- | --- |
| `GET /status` | `{"message":"OK"}`, kein Schlüssel nötig |
| `GET /currencies` · `/merchant/coins` · `/full-currencies` | Coins, die Sie derzeit annehmen können (Kürzel unten); `fixed_rate` wird ignoriert |
| `GET /min-amount` · `GET /estimate` | Mindestbeträge und Schätzungen; nur Preise in USD (USDT / USDC zählen 1:1) |
| `POST /payment` | Zahlung erstellen; liefert `pay_address` / `pay_amount` sofort (White Label) |
| `POST /invoice` | Rechnung erstellen; `invoice_url` ist unser gehosteter Checkout, in dem der Kunde Coin und Netzwerk wählt |
| `POST /invoice-payment` | Coin für eine Rechnung wählen, liefert die Zahlung |
| `GET /payment/:id` | Zahlungsstatus |
| `GET /payment/` | Liste: `limit` (1–500) / `page` / `orderBy` / `dateFrom` / `dateTo` / `invoiceId`; `sortBy` nur `created_at` |
| `POST /auth` | Die Liste braucht nur `x-api-key`, kein JWT; dieser Endpoint liefert ein ungeprüftes Platzhalter-Token, damit Code nach dem Muster „anmelden, dann auflisten“ weiterläuft (E-Mail / Passwort werden nicht geprüft) |

Nicht verfügbar: Payouts, Conversions, Custody-Sub-Partner, Abonnements, `update-merchant-estimate`, Fiat-Zahlungen. Diese Pfade liefern 404.

#### IPN-Callbacks

Erreicht eine Zahlung einen Endstatus, senden wir ein POST an die beim Erstellen angegebene `ipn_callback_url`, mit `x-nowpayments-sig` = HMAC-SHA512(IPN secret, das rekursiv nach Schlüsseln sortierte JSON) – exakt der Prüfcode von NOWPayments. Der gesendete Body ist bereits sortiertes, kompaktes JSON, ein HMAC über den Rohtext funktioniert also ebenfalls (und ist in PHP / Python die sicherste Wahl, da deren erneutes Kodieren sehr kleiner Dezimalzahlen von JS abweichen kann). Fehlgeschlagene Callbacks werden nach 1 Min., 5 Min., 30 Min., … wiederholt – insgesamt 8 Versuche.

```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 | Wann | Gesendete IPN |
| --- | --- | --- |
| `waiting` | Wartet auf Zahlung (auch eine Rechnung, bei der noch kein Coin gewählt wurde) | — |
| `confirming` | Zahlung on-chain gesehen, wartet auf Bestätigungen | — (bei Abfrage sichtbar) |
| `finished` | Bezahlt (auch überzahlt; `actually_paid` ist das tatsächlich Eingegangene) | ✓ |
| `partially_paid` | Zu wenig: Der Kunde kann vor Ablauf an dieselbe Adresse nachzahlen (einmal bei der ersten Unterzahlung gesendet) und nochmals, wenn es bei Ablauf noch fehlt | ✓ |
| `expired` | Unbezahlt abgelaufen | ✓ |
| `refunded` | Sie haben die gesamte Zahlung im Dashboard erstattet und sie wurde gesendet | — (bei Abfrage sichtbar) |

`confirmed`, `sending` und `failed` erscheinen nie: Nach der Bestätigung ist es `finished` und wird Ihrem Guthaben gutgeschrieben; nichts wird weitergeleitet.

#### Währungskürzel

| Coin | Netzwerk | pay_currency | network | Minimum |
| --- | --- | --- | --- | --- |
| 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 |

#### Unterschiede zu NOWPayments

- `price_currency` ist nur `usd` (plus `usdt` / `usdc` zu 1:1); andere Fiat-Währungen liefern 400.

- `pay_amount` (ein fester Krypto-Betrag), `payout_address` / `payout_currency` / `payout_extra_id` und `customer_email` werden nicht unterstützt und liefern 400.

- `is_fixed_rate` wird akzeptiert, hat aber keine Wirkung: Wir fixieren den Kurs immer (BTC / LTC usw. zum Median mehrerer Börsen, für 30 Minuten). Bei `is_fee_paid_by_user` true wird die Plattformgebühr zu `pay_amount` addiert (die Plattformgebühr beträgt 1%, bei höherem Volumen weniger; siehe [Preise](https://blockwallets.io/pricing?lang=de)).

- Zahlungen sind bis zu 12 Stunden gültig (NOWPayments: 7 Tage). `partially_paid_url` wird gespeichert und zurückgegeben, aber der Checkout leitet nicht dorthin weiter.

- `payment_id` ist eine 10-stellige Zahl; bei mit `/invoice` erstellten Rechnungen entspricht die `payment_id` der Zahlung der `id` der Rechnung, und eine Rechnung hat genau eine Zahlung (keine erneuten Einzahlungen per `parent_payment_id`).

- Gesendet werden nur IPNs für Endstatus und `partially_paid` (Tabelle oben), keine für `waiting` / `confirming`; `fee.serviceFee` ist die Plattformgebühr (der erhaltene Betrag × der Satz bei Erstellung der Rechnung, Standard 1%), andere Gebühren sind 0.

### Testmodus

Mit Test-Zugangsdaten erstellte Charges und Zahlungen laufen im Testnet der jeweiligen Chain, und ihre Webhooks werden mit dem Test-IPN-secret / shared secret signiert – völlig getrennt von Live: Ein Test-Schlüssel sieht keine Live-Zahlungen.

Alles Übrige (Checkout, Bestätigungen, Netzwerke und Mindestbeträge, Erstattungen) funktioniert wie in der [Hauptdokumentation](https://blockwallets.io/docs/cryptomus?lang=de).
