# Quickstart

> Create an API key, take your first payment and receive the webhook in about five minutes.

### 1. Create your API keys

1. Activate **Fina Pay** on your personal account in [/app/wallet](https://fina.do/app/wallet).
2. Open your business panel, edit your business and go to the **Connectors** tab → **Fina Pay**.
3. Create the connection: pick the income account and category where paid charges are booked.
4. Copy the **public key** (`pk_live_…`) and the **secret key** (`sk_live_…`). The secret is shown only once — store it as `FINA_SECRET_KEY` in your server environment.
5. Optional but recommended: set a **webhook URL** (HTTPS) and an **IPN secret**. Store the secret as `FINA_IPN_SECRET`.

> **Warning:** The secret key moves money. Keep it on your server only — never in a browser, a mobile app or a Git repository. If it leaks, regenerate it from the same connector.

### 2. Create your first charge

**POST /charges — curl**

```bash
curl -X POST https://fina.do/api/wallet/v1/charges \
  -H "Authorization: Bearer $FINA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": 150000,
    "description": "Order #42",
    "reference": "order-42",
    "metadata": { "order_id": "42" }
  }'
```

**POST /charges — TypeScript**

```ts
// Node 18+, Bun, Deno or any edge runtime: no SDK needed.
const res = await fetch('https://fina.do/api/wallet/v1/charges', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FINA_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount_minor: 150000, // RD$1,500.00 in centavos
    description: 'Order #42',
    reference: 'order-42', // idempotent: retrying returns the same charge
  }),
})
if (!res.ok) throw new Error(`Fina Pay ${res.status}: ${(await res.json()).error}`)
const charge: { id: string; status: string; payment_url: string } = await res.json()

// Send the customer to Fina to pay:
// res.redirect(charge.payment_url)
```

**POST /charges — Python**

```python
import os
import requests

res = requests.post(
    "https://fina.do/api/wallet/v1/charges",
    headers={"Authorization": f"Bearer {os.environ['FINA_SECRET_KEY']}"},
    json={
        "amount_minor": 150000,  # RD$1,500.00 in centavos
        "description": "Order #42",
        "reference": "order-42",  # idempotent: retrying returns the same charge
    },
    timeout=15,
)
res.raise_for_status()
charge = res.json()
print(charge["payment_url"])  # redirect the customer here
```

**POST /charges — PHP**

```php
<?php
$ch = curl_init('https://fina.do/api/wallet/v1/charges');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('FINA_SECRET_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount_minor' => 150000, // RD$1,500.00 in centavos
        'description' => 'Order #42',
        'reference' => 'order-42', // idempotent: retrying returns the same charge
    ]),
]);
$charge = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) !== 201) {
    throw new RuntimeException('Fina Pay: ' . ($charge['error'] ?? 'unknown_error'));
}
header('Location: ' . $charge['payment_url']);
```

**201 Created — JSON**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "payment_url": "https://fina.do/pay/c/3f9c…",
  "amount_minor": 150000,
  "currency_code": "DOP",
  "reference": "order-42",
  "expires_at": "2026-10-06T14:00:00.000Z"
}
```

Redirect the customer to `payment_url`. They sign in to Fina (or open the Fina app through the universal link), confirm with their wallet PIN and pay from their balance.

### 3. Receive the webhook

When the charge is paid, Fina sends `POST` to your webhook URL with the event in the body and an `x-fina-sig` header. Verify the signature, then fulfil the order.

**Webhook handler — TypeScript**

```ts
// Express. Any framework works: you need the raw body and the x-fina-sig header.
import express from 'express'
import { verifyFinaSignature } from './fina'

const app = express()

app.post('/webhooks/fina', express.text({ type: 'application/json' }), async (req, res) => {
  if (!verifyFinaSignature(req.body, req.get('x-fina-sig') ?? '', process.env.FINA_IPN_SECRET!)) {
    return res.status(401).end()
  }
  const event = JSON.parse(req.body)
  if (event.event === 'charge.paid') {
    // Idempotent: a retried delivery must not ship the order twice.
    await markOrderPaid({ reference: event.reference, chargeId: event.charge_id, netMinor: event.net_minor })
  }
  res.status(200).end() // answer 2xx fast; do slow work in a queue
})
```

**Webhook handler — Python**

```python
# Flask
import os
from flask import Flask, request

from fina import verify_fina_signature

app = Flask(__name__)


@app.post("/webhooks/fina")
def fina_webhook():
    raw = request.get_data()
    if not verify_fina_signature(raw, request.headers.get("x-fina-sig", ""), os.environ["FINA_IPN_SECRET"]):
        return "", 401
    event = request.get_json()
    if event["event"] == "charge.paid":
        # Idempotent: a retried delivery must not ship the order twice.
        mark_order_paid(reference=event["reference"], charge_id=event["charge_id"], net_minor=event["net_minor"])
    return "", 200
```

**Webhook handler — PHP**

```php
<?php
require __DIR__ . '/fina.php';

$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_FINA_SIG'] ?? '';
if (!verify_fina_signature($raw, $sig, getenv('FINA_IPN_SECRET'))) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true);
if ($event['event'] === 'charge.paid') {
    // Idempotent: a retried delivery must not ship the order twice.
    mark_order_paid($event['reference'], $event['charge_id'], $event['net_minor']);
}
http_response_code(200);
```

The `verifyFinaSignature` helper is in [Webhooks](https://fina.do/developers/webhooks.md). No webhook URL yet? Poll `GET /charges/{id}` until `status` is `paid`.

> **Tip:** That is the whole integration. Next: read [Charges](https://fina.do/developers/charges.md) for statuses, fees and refunds, and [Testing](https://fina.do/developers/testing.md) before going live.

Source: https://fina.do/developers#quickstart