# VyvaPay — AI integration playbook

Use this document as the single source of truth when generating client code, backend handlers, or tests for VyvaPay. Prefer **HTTPS** and **JSON** everywhere.

## Environment

- **API base URL (production):** `https://server.vyvapay.com`
- **API prefix (public integration):** `/api/v1`
- **Content-Type:** `application/json` unless noted.

## Authentication modes

### A) Public merchant API (recommended for external systems)

- Header: `Authorization: Bearer sk_live_<secret>`
- Obtain the key from the VyvaPay dashboard (API keys).
- All `/api/v1/*` routes described below use this mode.

### B) Dashboard session API (browser / logged-in merchant app)

- Header: `Authorization: Bearer <access_token>` from VyvaPay login session (JWT issued by VyvaPay backend after credential login).
- Header when the user has multiple organizations: `X-Account-Id: <account_uuid>`
- Many routes live under `/api/...` (not `/api/v1`). Only use this mode if you control the merchant session; **do not embed tokens in third-party apps.**

---

## 1) Deposit (PIX) — public API

### Create charge (order + PIX in one step)

`POST /api/v1/payments`

Headers:

- `Authorization: Bearer sk_live_...`
- `Content-Type: application/json`

Body (JSON):

```json
{
  "amount": 100.5,
  "customer_email": "buyer@example.com",
  "customer_name": "Buyer Name",
  "customer_cpf": "12345678909",
  "description": "Optional",
  "payment_method": "PIX"
}
```

Successful response (**201**): includes `id` (payment id), `order_id`, `pix_copy_paste`, `pix_qr_code_base64`, `expires_at`, `status` (`pending`).

### Poll / reconcile

The gateway notifies VyvaPay via an internal webhook; your system should rely on **merchant webhooks** (`ORDER_PAID`, etc.) or polling if needed.

Session-based alternatives (not v1): `POST /api/payments/deposit` with `{ "orderId": "..." }` requires an **existing order** with `payerEmail` set and merchant session auth — prefer **`POST /api/v1/payments`** for greenfield integrations.

---

## 2) Withdrawal (PIX) — public API

`POST /api/v1/withdrawals`

Headers:

- `Authorization: Bearer sk_live_...`
- `Content-Type: application/json`

Body (JSON):

```json
{
  "amount": 50,
  "pix_key": "email@example.com",
  "key_type": "EMAIL",
  "description": "optional",
  "document": "optional CPF/CNPJ digits for holder"
}
```

`key_type` enum: `CPF`, `EMAIL`, `CNPJ`, `PHONE`, `EVP`.

Successful response (**201**): includes `id` (internal transfer id), `amount`, `status` (`pending`), `gateway_response`.

### Session-based withdrawal (dashboard)

`POST /api/withdrawals/withdraw` with JSON fields:

- `amount`, `external_id` (idempotency), `pix_key`, `key_type` (`CPF`|`EMAIL`|`CNPJ`|`PHONE`), optional `key_document`, `description`
- Requires session + capability `finance.withdraw`.

---

## 3) Inbound webhook — payment gateway → VyvaPay

VyvaPay exposes:

`POST /api/webhooks/payment-gateway`

This endpoint is called by the **payment gateway**, not by the merchant. Merchants do **not** implement this unless they operate their own gateway bridge.

Authorization (when `WEBHOOK_SECRET` is set on VyvaPay):

- `Authorization: Bearer <WEBHOOK_SECRET>` **or**
- `X-Webhook-Secret: <WEBHOOK_SECRET>`

Payload (simplified; gateway-dependent):

```json
{
  "id": "gateway_transaction_id",
  "status": "succeeded | failed | pending",
  "type": "cash_in | cash_out",
  "end2endId": "optional",
  "idempotency_key": "optional (VyvaPay payment id)"
}
```

Behavior:

- `type === "cash_out"` → acknowledged; no order update in this handler.
- Deposit lifecycle: maps gateway `id` / `end2endId` / `idempotency_key` to internal payment, updates payment + order, fires **merchant webhooks** on success/failure.

Always respond quickly; use **`200`** with JSON `{ "ok": true }` when processed.

---

## 4) Outbound webhooks — VyvaPay → merchant URL

Merchants register HTTPS endpoints in the VyvaPay dashboard. VyvaPay delivers JSON events with HMAC signing.

### HTTP request

- Method: `POST`
- Headers:
  - `Content-Type: application/json`
  - `X-VyvaPay-Timestamp`: Unix seconds (string)
  - `X-VyvaPay-Signature`: lowercase hex HMAC-SHA256
  - `X-VyvaPay-Event-Id`: unique delivery id
  - `X-VyvaPay-Endpoint-Id`: endpoint row id

Body (UTF-8 JSON string used verbatim for signing):

```json
{
  "id": "<event_id>",
  "type": "ORDER_PAID",
  "created_at": "<ISO8601>",
  "data": { }
}
```

### Signature verification (merchant must implement)

Let `secret` be the endpoint secret from the dashboard (`whsec_...`).

Let `rawBody` be the **exact** request body string (bytes as sent).

Let `ts` be `X-VyvaPay-Timestamp`.

```
message = `${ts}.${rawBody}`
signature_hex = HMAC_SHA256(secret, message) encoded as lowercase hex
```

Compare `signature_hex` to `X-VyvaPay-Signature` using a constant-time comparison.

Reject if timestamp skew is too large (e.g. > 5 minutes) to mitigate replay.

### HTTP response

Return **`200`** or **`201`** on success. Other statuses trigger retries with backoff.

### Event types (`type` field)

- `PAYMENT_CREATED`
- `PAYMENT_APPROVED`
- `PAYMENT_REFUSED`
- `PAYMENT_REFUNDED`
- `PAYMENT_CHARGEBACK`
- `ORDER_CREATED`
- `ORDER_PAID`
- `ORDER_FAILED`
- `TRANSFER_CREATED`
- `TRANSFER_COMPLETED`

Subscribe only to needed events in the dashboard.

Typical **`ORDER_PAID` / `PAYMENT_APPROVED`** `data` fields include:

- `order_id`, `payment_id`, `amount`, `currency` (`BRL`)

---

## 5) Implementation checklist

1. Store **`sk_live_`** in a secret manager; rotate on leak.
2. Create PIX charges with **`POST /api/v1/payments`**.
3. Create withdrawals with **`POST /api/v1/withdrawals`** using correct `key_type`.
4. Expose **one HTTPS webhook URL**; verify **HMAC** and timestamp.
5. Treat webhook delivery as **at-least-once** — dedupe by `X-VyvaPay-Event-Id` or payload `id`.
6. Do **not** call `/api/webhooks/payment-gateway` from merchant systems unless you are the gateway.

---

## 6) Optional dashboard routes (session)

These exist for the merchant UI; external integrations should prefer **`/api/v1`**:

| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/payments/deposit` | PIX for existing `orderId` |
| GET | `/api/payments/deposit/:id` | Query deposit status via gateway |
| POST | `/api/withdrawals/withdraw` | PIX withdrawal (session + `external_id`) |
| GET | `/api/dashboard/webhooks` | List webhook endpoints |

---

## 7) Errors

- **`401`**: missing/invalid API key or session token.
- **`403`**: valid auth but insufficient organization capability (dashboard routes).
- **`400` / `422`**: validation (Zod / gateway).
- **`404`**: resource not found or wrong account scope.

Parse JSON `{ "error": "...", "code": "..." }` when present.
