# Список инвойсов

> Получайте список инвойсов со строгими фильтрами по статусу и датам и проходите результаты курсором без повторения страниц.

`GET /v1/invoices`

:::endpoint
GET /v1/invoices
:::

**API-ключ:** `Payment`

Возвращает только инвойсы аутентифицированного мерчанта, из окружения ключа и доступных ему проектов. Сортировка фиксирована: `created_at DESC`, затем `invoice_id DESC`.

## Параметры запроса

| Параметр | Тип | По умолчанию | Описание |
|----------|-----|---------------|----------|
| `limit` | integer | `20` | Размер страницы от 1 до 100. |
| `cursor` | string | — | Непрозрачный `next_cursor` из предыдущего ответа. |
| `status` | string | — | Точный статус в snake_case. Повторите параметр для нескольких статусов; значение через запятую не поддерживается. |
| `external_order_id` | string | — | Точный внешний ID заказа, до 128 символов. |
| `project_id` | string | — | ID с префиксом `prj_…`, доступный этому Payment-ключу. |
| `created_from` | Unix seconds | — | Включительная нижняя граница `created_at`. |
| `created_to` | Unix seconds | — | Исключительная верхняя граница `created_at`. Должна быть позже `created_from`. |

Неизвестные параметры, повторённые скалярные параметры, дубли статусов и неверный регистр enum отклоняются, а не игнорируются. Имя не из таблицы — это `400`, поэтому фильтр, который API принял, он и применил: опечатка не расширит выборку молча.

Таблица описывает весь набор фильтров целиком. Поиска по тексту, параметра сортировки и постраничной выборки по offset в списках Merchant API нет.

## Пример

```http
GET /v1/invoices?limit=50&status=paid&status=paid_over&created_from=1767225600 HTTP/1.1
Host: api.paymos.io
Authorization: HMAC-SHA256 pk_live_…:…
X-Request-Timestamp: 1767225660
```

## Примеры кода

В каждой вкладке используется официальный SDK соответствующего языка. Подпись, сериализацию, безопасные повторы и типизированные ошибки обрабатывает SDK.

```curl
API_KEY_ID="pk_live_xxxxxxxxxxxx"
API_SECRET="sk_live_xxxxxxxxxxxx"
QUERY="?status=paid&limit=50"

TS=$(date +%s)
# No body: the hash slot is the EMPTY STRING, not sha256("") — its fixed
# e3b0c442… digest signs a different payload and the request is rejected. The
# query keeps its leading "?" exactly as transmitted.
SIGNATURE=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" GET /v1/invoices "$QUERY" '' \
  | openssl dgst -sha256 -hmac "$API_SECRET" -binary | base64)

curl -sS "https://api.paymos.io/v1/invoices$QUERY" \
  -H "Authorization: HMAC-SHA256 $API_KEY_ID:$SIGNATURE" \
  -H "X-Request-Timestamp: $TS"
```

```javascript
import { Paymos } from '@paymos/sdk';

const paymos = new Paymos({
  apiKey: process.env.PAYMOS_API_KEY,
  apiSecret: process.env.PAYMOS_API_SECRET,
});

const page = await paymos.invoices.list({ status: ['paid', 'paid_over'], limit: 50 });

for await (const invoice of paymos.invoices.iterate({ status: ['paid'] }, 10)) {
  console.log(invoice.invoiceId);
}
```

```python
import os

from paymos import Paymos

paymos = Paymos(
    api_key=os.environ["PAYMOS_API_KEY"],
    api_secret=os.environ["PAYMOS_API_SECRET"],
)

page = paymos.invoices.list(status=["paid", "paid_over"], limit=50)

for invoice in paymos.invoices.iterate(10, status=["paid"]):
    print(invoice["invoice_id"])
```

```php
<?php
use Paymos\Client;
use Paymos\ClientConfig;

$paymos = new Client(new ClientConfig(
    getenv('PAYMOS_API_KEY'),
    getenv('PAYMOS_API_SECRET')
));

$page = $paymos->invoices()->listPage(array(
    'status' => array('paid', 'paid_over'),
    'limit' => 50,
));

foreach ($paymos->invoices()->iterate(array('status' => array('paid')), 10) as $invoice) {
    echo $invoice['invoice_id'] . PHP_EOL;
}
```

```go
package main

import (
	"context"
	"fmt"
	"os"

	paymos "github.com/Paymos-labs/go-sdk/v2"
)

func main() {
	client, err := paymos.NewClient(os.Getenv("PAYMOS_API_KEY"), os.Getenv("PAYMOS_API_SECRET"))
	if err != nil {
		panic(err)
	}

	page, err := client.Invoices.List(context.Background(), paymos.InvoiceListParams{
		Status: []paymos.InvoiceStatus{paymos.InvoicePaid, paymos.InvoicePaidOver},
		Limit:  50,
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(len(page.Items), page.NextCursor)

	iter := paymos.NewInvoiceIterator(client.Invoices, paymos.InvoiceListParams{
		Status: []paymos.InvoiceStatus{paymos.InvoicePaid},
	}, 10)
	for {
		invoice, ok, err := iter.Next(context.Background())
		if err != nil {
			panic(err)
		}
		if !ok {
			break
		}
		fmt.Println(invoice.InvoiceID)
	}
}
```

```csharp
using Paymos;

using var paymos = new PaymosClient(
    Environment.GetEnvironmentVariable("PAYMOS_API_KEY")!,
    Environment.GetEnvironmentVariable("PAYMOS_API_SECRET")!);

var page = await paymos.Invoices.ListAsync(new InvoiceListOptions(
    Status: [InvoiceStatus.Paid, InvoiceStatus.PaidOver],
    Limit: 50));

await foreach (var invoice in paymos.Invoices.IterateAsync(
    new InvoiceListOptions(Status: [InvoiceStatus.Paid]), maxPages: 10))
{
    Console.WriteLine(invoice.InvoiceId);
}
```

```java
import io.paymos.InvoiceListItem;
import io.paymos.InvoiceListOptions;
import io.paymos.InvoiceStatus;
import io.paymos.Page;
import io.paymos.PaymosClient;

import java.util.List;

PaymosClient paymos = new PaymosClient(
    System.getenv("PAYMOS_API_KEY"),
    System.getenv("PAYMOS_API_SECRET"));

Page<InvoiceListItem> page = paymos.invoices.list(InvoiceListOptions.builder()
    .status(List.of(InvoiceStatus.PAID, InvoiceStatus.PAID_OVER))
    .limit(50)
    .build());

for (InvoiceListItem invoice : paymos.invoices.iterate(
        InvoiceListOptions.builder().status(List.of(InvoiceStatus.PAID)).build(), 10)) {
    System.out.println(invoice.invoiceId());
}
```

```ruby
require 'paymos'

paymos = Paymos::Client.new(
  api_key: ENV.fetch('PAYMOS_API_KEY'),
  api_secret: ENV.fetch('PAYMOS_API_SECRET')
)

page = paymos.invoices.list(status: ['paid', 'paid_over'], limit: 50)

paymos.invoices.each(max_pages: 10, status: ['paid']) do |invoice|
  puts invoice.invoice_id
end
```

```rust
use paymos::{InvoiceListParams, InvoiceStatus, PaymosClient};

let paymos = PaymosClient::new(
    std::env::var("PAYMOS_API_KEY")?,
    std::env::var("PAYMOS_API_SECRET")?,
)?;

let page = paymos
    .invoices()
    .list(&InvoiceListParams {
        status: Some(vec![InvoiceStatus::Paid, InvoiceStatus::PaidOver]),
        limit: Some(50),
        ..Default::default()
    })
    .await?;
println!("{} items, next_cursor={:?}", page.items.len(), page.next_cursor);

let mut pager = paymos.invoices().pager(
    InvoiceListParams { status: Some(vec![InvoiceStatus::Paid]), ..Default::default() },
    Some(10),
)?;
while let Some(invoice) = pager.next().await? {
    println!("{}", invoice.invoice_id);
}
```

## Ответ (200 OK)

```json
{
  "items": [
    {
      "invoice_id": "inv_74BPZFhr9qy9Uz2fbRkdJX",
      "project_id": "prj_7gK2mR9xQ4vN8cT1bY5dL3",
      "external_order_id": "order-1042",
      "client_id": "customer-42",
      "status": "paid",
      "is_final": true,
      "is_test": false,
      "amount": "49.95",
      "currency": "USDT",
      "network": "TRC20",
      "created_at": 1767225600,
      "expires_at": 1767227400,
      "completed_at": 1767225908
    }
  ],
  "next_cursor": "CfDJ8JvN…"
}
```

Элемент списка специально сокращён. Платежи, переводы, адрес для оплаты и данные платёжной страницы доступны в [Получении инвойса](/ru/docs/invoices/get.md). Ответ содержит `Cache-Control: private, no-store`.

## Правила пагинации

- Номеров страниц и `total_count` нет: переходите по `next_cursor`, пока он не станет `null`.
- Курсор непрозрачный, защищён от подделки и действует 24 часа.
- Он привязан к ресурсу invoices и точному набору фильтров. Между страницами фильтры не меняются; `limit` менять можно.
- Последняя страница явно возвращает `next_cursor: null`.
- Если курсор испорчен, истёк, применён к другому ресурсу или совмещён с другими фильтрами, начните заново без курсора.

## Ошибки

Payout-ключ (`rk_`) получает `403 payment_key_required`. Ошибочные фильтры возвращают `400` с указанием поля, неверный курсор — `400 pagination_cursor_invalid`. Чужой `project_id` возвращает `404`, не раскрывая факт его существования. См. [Коды ошибок](/ru/docs/errors/codes.md).
