# NOWPayments integration (agentic developer guide)

This document describes how to integrate **NOWPayments** when building or operating against **this application’s simplified proxy** (`/api/nowpayments/...`). The proxy forwards requests to NOWPayments’ REST API (`https://api.nowpayments.io/v1`) with server-side credentials, validates required fields, normalizes responses, and handles **IPN** (webhooks) for local transaction updates.

**Upstream reference:** [NOWPayments Postman collection](https://documenter.getpostman.com/view/7907941/2s93JusNJt) — use it for field-level details beyond what this guide lists.

---

## Mental model for agents

1. **Secrets stay on the server.** Clients (browsers, mobile apps, other services) must **not** receive `nowpayments.api_key` or JWTs. Integrations should call **your deployed base URL** + `api/nowpayments/...`, or internal server code that uses `App\Libraries\NowPaymentsService`.
2. **Two credential layers:**
  - `**x-api-key`** — payments, balance, address validation, crypto payout status.
  - `**Authorization: Bearer <JWT>**` — crypto payout create/verify; fiat account + fiat payout request; fiat catalog; fiat payout list. JWT can be supplied as `nowpayments.bearer_token` or obtained via dashboard email/password (`POST /v1/auth`, cached ~4 minutes; tokens expire in ~5 minutes).
3. **IPN is the source of truth for async completion.** Poll `GET /payment/{id}` if needed, but configure IPN for reliable status updates.
4. **Stable correlation:** When creating a payment, set `**order_id`** to your internal id (must match `payment_transactions.transaction_id` or `reference` for IPN to update a row).

---

## Environment configuration


| Variable                    | Purpose                                                                                                                                    |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `nowpayments.api_key`       | Required for payment collection and most crypto payout reads. Sent as `x-api-key`.                                                         |
| `nowpayments.bearer_token`  | Optional static JWT for Bearer-only or api_key+bearer routes.                                                                              |
| `nowpayments.auth_email`    | Dashboard login email; used with `nowpayments.auth_password` if `bearer_token` is unset.                                                   |
| `nowpayments.auth_password` | Dashboard password; server calls `POST .../v1/auth` and caches JWT **240 seconds**.                                                        |
| `nowpayments.ipn_secret`    | Must match NOWPayments dashboard. Used to verify `x-nowpayments-sig` on IPN. If empty, signatures are **not** verified (development only). |
| `nowpayments.base_url`      | Optional. Default `https://api.nowpayments.io/v1`.                                                                                         |


**Health probe:** `GET {base}/api/nowpayments/health` returns flags such as `configured` (API key set), `bearer_configured`, `ipn_secret_configured` — use for smoke tests, not as proof of upstream connectivity.

---

## Base URL

All paths below are relative to:

`{APP_BASE_URL}/api/nowpayments/`

Replace `{APP_BASE_URL}` with your deployment (e.g. `https://example.com/`).

---

## Response envelope

Successful and failed proxy calls typically return JSON:

```json
{
  "success": true,
  "message": "OK",
  "data": { }
}
```

- `**success**` — whether the upstream call succeeded (HTTP 2xx).
- `**message**` — human-readable status or upstream error message.
- `**data**` — parsed upstream JSON. Non-JSON bodies may appear under `data.raw`.

HTTP status on failure often mirrors upstream (`status_code` when available); otherwise expect **400** for validation errors.

---

## Endpoints (summary)


| Method | Path                                   | Server auth            | Notes                                                                                                   |
| ------ | -------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------- |
| GET    | `/health`                              | None                   | Local config flags only.                                                                                |
| POST   | `/ipn`                                 | NOWPayments signs body | Webhook from NOWPayments; see [IPN](#ipn-instant-payment-notification).                                 |
| POST   | `/payment`                             | API key                | Body requires `price_amount`, `price_currency`, `pay_currency`.                                         |
| GET    | `/payment/{payment_id}`                | API key                | Status poll.                                                                                            |
| GET    | `/balance`                             | API key                | Crypto balances.                                                                                        |
| POST   | `/payout/validate-address`             | API key                | Body: `address`, `currency`; `extra_id` optional.                                                       |
| POST   | `/payout`                              | API key + Bearer       | Body: `withdrawals[]` with `address`, `currency`, `amount` per row.                                     |
| POST   | `/payout/{batch_withdrawal_id}/verify` | API key + Bearer       | Body: `verification_code`.                                                                              |
| GET    | `/payout/{payout_id}`                  | API key                | Status.                                                                                                 |
| GET    | `/fiat-payouts/providers`              | Bearer                 | Catalog.                                                                                                |
| GET    | `/fiat-payouts/fiat-currencies`        | Bearer                 |                                                                                                         |
| GET    | `/fiat-payouts/crypto-currencies`      | Bearer                 | Query: optional `provider`, `currency`.                                                                 |
| GET    | `/fiat-payouts/payment-methods`        | Bearer                 | Query: `provider`, `currency` (proxy may send nulls if omitted).                                        |
| POST   | `/fiat-payouts/account`                | API key + Bearer       | Requires `currency`, `paymentCode`, `fields`, `provider`, `countryCode`.                                |
| POST   | `/fiat-payouts`                        | API key + Bearer       | Requires `fiatCurrency`, `cryptoCurrency`, `amount`, `provider`.                                        |
| GET    | `/fiat-payouts`                        | Bearer                 | List; optional query filters (see implementation: `id`, `provider`, `limit`, `page`, date range, etc.). |


---

## Payments

### Create payment

- **POST** `/payment`
- **Body (required):** `price_amount` (numeric), `price_currency`, `pay_currency`
- **Recommended:** `order_id` (your reference), `ipn_callback_url` pointing to `{APP_BASE_URL}api/nowpayments/ipn`, plus any optional fields from NOWPayments docs.

Example:

```json
{
  "price_amount": 49.99,
  "price_currency": "usd",
  "pay_currency": "btc",
  "order_id": "INV-2026-001",
  "ipn_callback_url": "https://your-app.example.com/api/nowpayments/ipn",
  "order_description": "Order INV-2026-001"
}
```

**Agent checklist:** Persist `order_id` in your DB before calling; show the customer `pay_address` / QR from `data` when returned.

### Payment status

- **GET** `/payment/{payment_id}`  
Use for polling; prefer IPN for final state.

---

## IPN (Instant Payment Notification)

- **POST** `{APP_BASE_URL}api/nowpayments/ipn`
- **Configure** the same URL and **IPN secret** in the NOWPayments dashboard.
- **Header:** `x-nowpayments-sig` — hex **HMAC-SHA512** over the **raw JSON body** after **recursively sorting object keys** (see NOWPayments IPN documentation).
- **Body:** JSON payment object (`payment_id`, `payment_status`, `order_id`, …).

**Local behavior:** If `order_id` matches `payment_transactions.transaction_id` or `reference` (and typically `payment_method` `nowpayments`), the app updates:


| NOWPayments `payment_status`    | Local `status` |
| ------------------------------- | -------------- |
| `finished`, `confirmed`         | `SUCCESS`      |
| `failed`, `expired`, `refunded` | `FAILED`       |
| other / empty                   | `PENDING`      |


`gateway_reference` stores `payment_id`; `metadata` includes `nowpayments_ipn` with a snapshot.

**Responses:** `200` accepted; `400` bad body; `403` invalid or missing signature when `nowpayments.ipn_secret` is set.

**Agent checklist:** Treat IPN as **at-least-once** delivery; updates should be idempotent. If no row matches `order_id`, the handler logs and still returns `200` after verification (adjust your workflow if you need queueing).

---

## Crypto payouts

1. **Validate address** — `POST /payout/validate-address`
2. **Create batch** — `POST /payout` (requires Bearer + API key on server)
3. **Verify** — `POST /payout/{batch_withdrawal_id}/verify` with `verification_code` when required
4. **Status** — `GET /payout/{payout_id}`

---

## Fiat payouts

Use Bearer-backed catalog endpoints to discover providers, currencies, and methods, then create accounts and request payouts per upstream schemas. This app enforces required wrapper fields on `POST /fiat-payouts/account` and `POST /fiat-payouts`; see `app/Controllers/NowPayments.php` for exact validation.

---

## Troubleshooting (for automated triage)


| Symptom                                     | Likely cause                                                                                                   |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `success: false`, message about API key     | `nowpayments.api_key` missing or wrong.                                                                        |
| Fiat or payout create fails, Bearer message | No `bearer_token` and no valid `auth_email`/`auth_password`, or JWT expired — check `/health` and server logs. |
| IPN 403                                     | Wrong `nowpayments.ipn_secret` or signature mismatch (body altered in transit).                                |
| IPN 200 but DB unchanged                    | `order_id` does not match `transaction_id` / `reference`, or row missing.                                      |


---

## Code references (this repo)

- Routes: `app/Config/Routes.php` — group `api/nowpayments`
- Controller (validation + IPN): `app/Controllers/NowPayments.php`
- Upstream client: `app/Libraries/NowPaymentsService.php`
- Human-readable API docs (HTML): `app/Views/api_documentation.php` — search for `NOWPayments API`

---

## Security checklist for agentic workflows

- Never log or echo full API keys or JWTs.
- Use HTTPS for all callbacks (`ipn_callback_url`).
- In production, always set `nowpayments.ipn_secret` and reject unsigned IPN.
- Prefer **server-side** creation of payments; if a client must trigger payment, use a **short-lived session or signed intent**, not raw secrets.

