Ir al contenido

API

En esta página

Listar

Lista facturas con filtros estrictos de estado y fecha, avanza con paginación por cursor y concilia los resultados sin páginas duplicadas.

GET/v1/invoices

Clave de API: Payment

Devuelve únicamente las facturas del comercio autenticado, dentro del entorno de la clave y de los proyectos que esta ve. El orden es created_at DESC y, a igualdad, invoice_id DESC.

Parámetros de consulta

Parámetro Tipo Por defecto Descripción
limit integer 20 Tamaño de página, de 1 a 100.
cursor string El next_cursor opaco de la respuesta anterior.
status string Estado exacto en snake_case. Repite el parámetro para casar con varios estados; no envíes un valor separado por comas.
external_order_id string Identificador externo de pedido exacto, de hasta 128 caracteres.
project_id string Un identificador con prefijo prj_… visible para esta clave Payment.
created_from Unix seconds Límite inferior de created_at, inclusive.
created_to Unix seconds Límite superior de created_at, exclusivo. Debe ser posterior a created_from.

Los parámetros desconocidos, los parámetros escalares repetidos, los estados duplicados y las mayúsculas incorrectas en un enum se rechazan en lugar de ignorarse en silencio. Un nombre que no esté en la tabla devuelve 400, así que un filtro que la API aceptó es un filtro que aplicó: una errata nunca te amplía el resultado sin avisar.

La tabla es también todo el juego de filtros. En las listas de la Merchant API no hay búsqueda por texto, ni parámetro de orden, ni paginación por offset.

Ejemplo

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

Ejemplos de código

Cada pestaña usa el SDK oficial de ese lenguaje. La firma, la serialización, los reintentos seguros y los errores tipados los resuelve el SDK.

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"
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);
}
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
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;
}
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)
	}
}
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);
}
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());
}
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
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);
}

Respuesta (200 OK)

{
  "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…"
}

El elemento de la lista es deliberadamente compacto. Para pagos, transferencias, dirección de depósito y detalles de la página de pago, usa Obtener factura. La respuesta lleva Cache-Control: private, no-store.

Reglas de paginación

  • No hay número de página ni total_count: sigue next_cursor hasta que valga null.
  • El cursor es opaco, está protegido contra manipulación y vence a las 24 horas.
  • Está ligado al recurso de facturas y al conjunto exacto de filtros. Mantén los filtros sin cambios entre páginas; limit sí puede variar.
  • Un cursor ausente o final se representa con next_cursor: null.
  • Si el cursor no es válido, ha vencido, se usa en otro recurso o se combina con filtros distintos, empieza de nuevo sin cursor.

Errores

Una clave Payout (rk_) se rechaza con 403 payment_key_required. Los filtros no válidos devuelven un 400 por campo; un cursor no válido devuelve 400 pagination_cursor_invalid. Un project_id fuera del alcance de la clave devuelve 404 sin revelar si existe. Consulta Códigos de error.