> ## Documentation Index
> Fetch the complete documentation index at: https://o1pay.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Payments API

> Buat payment order dan QRIS dinamis, lalu cek status pembayaran.

Buat payment order + QRIS dinamis. Direkomendasikan untuk integrasi payment gateway dengan order ID dan status.

| Method | Endpoint | Fungsi |
| - | - | - |
| POST | /api/v1/payments/create | [Buat payment](#buat-payment) |
| GET | /api/v1/payments/:orderId | [Detail payment](#detail-payment) |
| GET | /api/v1/payments/:orderId/status | [Cek status payment](#cek-status-payment) |
| GET | /api/v1/payments | [Daftar payment](#daftar-payment) |

Semua endpoint di halaman ini memakai autentikasi `agentCode` + `merchantCode` dari halaman **Toko Saya**.

## Buat payment

> [!NOTE]
>
> `POST /api/v1/payments/create`

Membuat payment order baru untuk QRIS, Virtual Account, atau E-Wallet. Response berisi `orderId`, QR Code dinamis, dan `status: "pending"`.

### Body

| Parameter | Tipe | Wajib | Deskripsi |
| - | - | - | - |
| agentCode | string | Ya | Agent code toko aktif |
| merchantCode | string | Ya | Merchant code toko aktif |
| amount | number | Ya | Nominal pembayaran dalam Rupiah |
| externalId | string | Tidak | ID order dari sistem Anda |
| channel | string | Tidak | qris, va, atau ewallet (default: qris) |
| dstCode | string | Tidak | QRIS, BCA, DANA, dll. Default sesuai channel. Lihat [Daftar Pembayaran](https://o1pay.mintlify.site/api/daftar-pembayaran.md) |
| fee | object | Tidak | Fee transaksi. Berisi type (fixed atau percentage) dan value (number) |
| feeBearer | string | Tidak | Siapa yang membayar fee: merchant (default) atau customer |
| customerName | string | Tidak | Nama customer |
| customerPhone | string | Tidak | Nomor HP customer |
| customerEmail | string | Tidak | Email customer |

> [!NOTE]
>
> **feeBearer**
>
> * `merchant` (default): saldo toko dipotong sesuai fee. Customer bayar tanpa tambahan.
> * `customer`: fee ditambahkan ke total bayaran customer. Saldo toko utuh.

### Channel

> [tab] QRIS
>
> | channel | dstCode | Max amount |
> | - | - | - |
> | qris (default) | QRIS | Rp 10.000.000 |

> [tab] Virtual Account
>
> | channel | dstCode | Max amount |
> | - | - | - |
> | va | BCA, BNI, BRI, MANDIRI, PERMATA, dll. | Rp 50.000.000 (BCA: Rp 2.000.000) |

> [tab] E-Wallet
>
> | channel | dstCode | Max amount |
> | - | - | - |
> | ewallet | DANA, OVO, SHOPEEPAY, LINKAJA | Rp 20.000.000 |

### Contoh request

```bash cURL theme={null}
curl -X POST http://localhost:5173/api/v1/payments/create \
  -H "Content-Type: application/json" \
  -d '{
    "agentCode": "AGT-187F2398D5A6",
    "merchantCode": "MERCHANT-AGT-187F2398D5A6",
    "amount": 50000,
    "externalId": "INV-001",
    "customerName": "John Doe",
    "customerPhone": "08123456789",
    "customerEmail": "john@example.com",
    "channel": "qris",
    "dstCode": "QRIS",
    "fee": { "type": "fixed", "value": 1000 },
    "feeBearer": "merchant"
  }'
```

```javascript JavaScript theme={null}
const response = await fetch("http://localhost:5173/api/v1/payments/create", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    agentCode: "AGT-187F2398D5A6",
    merchantCode: "MERCHANT-AGT-187F2398D5A6",
    amount: 50000,
    externalId: "INV-001",
    customerName: "John Doe",
    customerPhone: "08123456789",
    customerEmail: "john@example.com",
    channel: "qris",
    dstCode: "QRIS",
    fee: { type: "fixed", value: 1000 },
    feeBearer: "merchant",
  }),
});

const { data } = await response.json();
console.log(data.providerOrderId, data.qrCode);
```

```php PHP theme={null}
<?php
$payload = [
    "agentCode" => "AGT-187F2398D5A6",
    "merchantCode" => "MERCHANT-AGT-187F2398D5A6",
    "amount" => 50000,
    "externalId" => "INV-001",
    "customerName" => "John Doe",
    "customerPhone" => "08123456789",
    "customerEmail" => "john@example.com",
    "channel" => "qris",
    "dstCode" => "QRIS",
    "fee" => ["type" => "fixed", "value" => 1000],
    "feeBearer" => "merchant",
];

$ch = curl_init("http://localhost:5173/api/v1/payments/create");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true)["data"];
echo $data["orderId"] . " " . $data["qrCode"];
```

```javascript Node.js theme={null}
import axios from "axios";

const { data: res } = await axios.post(
  "http://localhost:5173/api/v1/payments/create",
  {
    agentCode: "AGT-187F2398D5A6",
    merchantCode: "MERCHANT-AGT-187F2398D5A6",
    amount: 50000,
    externalId: "INV-001",
    customerName: "John Doe",
    customerPhone: "08123456789",
    customerEmail: "john@example.com",
    channel: "qris",
    dstCode: "QRIS",
    fee: { type: "fixed", value: 1000 },
    feeBearer: "merchant",
  }
);

console.log(res.data.orderId, res.data.qrCode);
```

```python Python theme={null}
import requests

payload = {
    "agentCode": "AGT-187F2398D5A6",
    "merchantCode": "MERCHANT-AGT-187F2398D5A6",
    "amount": 50000,
    "externalId": "INV-001",
    "customerName": "John Doe",
    "customerPhone": "08123456789",
    "customerEmail": "john@example.com",
    "channel": "qris",
    "dstCode": "QRIS",
    "fee": {"type": "fixed", "value": 1000},
    "feeBearer": "merchant",
}

res = requests.post("http://localhost:5173/api/v1/payments/create", json=payload)
data = res.json()["data"]
print(data["orderId"], data["qrCode"])
```

```go Golang theme={null}
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	payload := map[string]any{
		"agentCode":     "AGT-187F2398D5A6",
		"merchantCode":  "MERCHANT-AGT-187F2398D5A6",
		"amount":        50000,
		"externalId":    "INV-001",
		"customerName":  "John Doe",
		"customerPhone": "08123456789",
		"customerEmail": "john@example.com",
		"channel":       "qris",
		"dstCode":       "QRIS",
		"fee":           map[string]any{"type": "fixed", "value": 1000},
		"feeBearer":     "merchant",
	}
	body, _ := json.Marshal(payload)

	resp, err := http.Post(
		"http://localhost:5173/api/v1/payments/create",
		"application/json",
		bytes.NewReader(body),
	)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	var result map[string]any
	json.NewDecoder(resp.Body).Decode(&result)
	data := result["data"].(map[string]any)
	fmt.Println(data["orderId"], data["qrCode"])
}
```

```rust Rust theme={null}
// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, serde_json = "1", tokio = { version = "1", features = ["full"] }
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let payload = json!({
        "agentCode": "AGT-187F2398D5A6",
        "merchantCode": "MERCHANT-AGT-187F2398D5A6",
        "amount": 50000,
        "externalId": "INV-001",
        "customerName": "John Doe",
        "customerPhone": "08123456789",
        "customerEmail": "john@example.com",
        "channel": "qris",
        "dstCode": "QRIS",
        "fee": { "type": "fixed", "value": 1000 },
        "feeBearer": "merchant"
    });

    let res: Value = reqwest::Client::new()
        .post("http://localhost:5173/api/v1/payments/create")
        .json(&payload)
        .send()
        .await?
        .json()
        .await?;

    println!("{} {}", res["data"]["orderId"], res["data"]["qrCode"]);
    Ok(())
}
```

### Contoh response

```json 200 theme={null}
{
  "success": true,
  "data": {
    "orderId": "P_20260817wN9dsiK545H9uxgk2mXj",
    "externalId": "INV-001",
    "storeId": "(id internal toko)",
    "storeName": "Toko Saya",
    "merchantName": "Wati Toto",
    "merchantCode": "MERCHANT-AGT-187F2398D5A6",
    "amount": 50000,
    "fee": { "type": "fixed", "value": 1000 },
    "totalAmount": 51000,
    "qrisDynamic": "00020101021226...(294 karakter QR dinamis)",
    "qrCode": "00020101021226...",
    "status": "pending",
    "customerName": "John Doe",
    "customerEmail": "john@example.com",
    "paidAt": null,
    "expiredAt": "2026-08-17T14:30:00.000Z",
    "createdAt": "2026-08-17T14:15:00.000Z",
    "updatedAt": "2026-08-17T14:15:00.000Z",
    "providerId": "(id internal DB provider)",
    "providerCode": "qris",
    "provider": "qris",
    "providerOrderId": "P_20260817wN9dsiK545H9uxgk2mXj",
    "channel": "qris",
    "paymentMethod": "QRIS",
    "providerFee": 1500,
    "vaNumber": null
  }
}
```

> [!TIP]
>
> Belum atur webhook? Buka **Toko Saya → Webhook Callback**. Order Anda tetap diproses meskipun webhook belum diatur. Lihat [Webhook Callback](https://o1pay.mintlify.site/api/webhook.md).

## Detail payment

> [!NOTE]
>
> `GET /api/v1/payments/:orderId`

Ambil detail lengkap satu payment order berdasarkan Order ID.

| Parameter | Lokasi | Tipe | Wajib | Deskripsi |
| - | - | - | - | - |
| orderId | path | string | Ya | orderId dari response create payment |
| agentCode | query | string | Ya | Agent code toko aktif |
| merchantCode | query | string | Ya | Merchant code toko aktif |

## Cek status payment

> [!NOTE]
>
> `GET /api/v1/payments/:orderId/status`

Polling ringan status payment, hanya mengembalikan status terkini.

| Parameter | Lokasi | Tipe | Wajib | Deskripsi |
| - | - | - | - | - |
| orderId | path | string | Ya | orderId dari response create payment |
| agentCode | query | string | Ya | Agent code toko aktif |
| merchantCode | query | string | Ya | Merchant code toko aktif |

## Daftar payment

> [!NOTE]
>
> `GET /api/v1/payments`

Ambil daftar semua payment order milik toko, dengan filter status opsional.

| Parameter | Lokasi | Tipe | Wajib | Deskripsi |
| - | - | - | - | - |
| agentCode | query | string | Ya | Agent code toko aktif |
| merchantCode | query | string | Ya | Merchant code toko aktif |
| status | query | string | Tidak | Filter berdasarkan status payment |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.