Zum Inhalt springen

API

Auf dieser Seite

Erstellen

Eine Rechnung für eine Krypto-Zahlung erstellen. Zwei Wege: direkt in Krypto (Token und Netzwerk fest) und in Fiat (der Kunde wählt das Token im Checkout).

POST/v1/invoices

API-Schlüssel: Payment

Anfragekörper

Parameter Typ Erforderlich Beschreibung
project_id Zeichenkette Ja Projektkennung mit Präfix (prj_...). Der API-Schlüssel gilt für den Händler; hiermit legen Sie fest, welches Projekt die Rechnung erhält
amount Zeichenkette Ja Zahlbetrag als Dezimalzeichenkette (etwa "50.00")
currency Zeichenkette Ja Fiat-Währungscode (USD, EUR und weitere) oder Krypto-Kürzel. Die vollständige Liste steht unter Unterstützte Währungen
network Zeichenkette Bedingt Code des Blockchain-Netzwerks — TRC20, ERC20, BEP20, POLYGON, ARBITRUM, OPTIMISM, BASE, TON, AVAX, SOL, NEAR, SUI oder PLASMA (siehe Unterstützte Währungen). Erforderlich, wenn Sie eine Krypto-Rechnung auf ein bestimmtes Netzwerk festlegen möchten
external_order_id Zeichenkette Ja Ihre eindeutige Bestellkennung (höchstens 128 Zeichen), eindeutig je Projekt. Macht die Rechnungserstellung idempotent: Existiert im Projekt bereits eine Rechnung mit dieser external_order_id, bekommen Sie diese bestehende Rechnung mit 200 OK zurück — kein Duplikat, kein Fehler
client_id Zeichenkette Nein Ihre Kundenkennung (höchstens 200 Zeichen). Wird an der Rechnung gespeichert und beim Lesen sowie in Listeneinträgen zurückgegeben, damit Sie Rechnungen nach Ihrem eigenen Kunden gruppieren können. Paymos leitet daraus keine Begrenzung ab
allow_multiple_payments Boolescher Wert Nein Ob die Rechnung mehrere Teilzahlungen annimmt. Standard: true
customer_fee_percent Ganzzahl Nein Prozentsatz der dem Kunden berechneten Gebühr (0–100). Überschreibt für diese Rechnung die Einstellung auf Projektebene

Genauigkeit von Fiat-Beträgen

Bei Rechnungen im Fiat-Weg muss amount zur Untereinheit von currency passen.

Untereinheiten Währungen
0 Nachkommastellen JPY, KRW, VND, CLP
2 Nachkommastellen USD, EUR, GBP, CHF, CAD, AUD, NZD, PLN, CZK, DKK, SEK, NOK, HUF, RUB, UAH, GEL, CNY, HKD, INR, IDR, MYR, PHP, THB, PKR, SGD, BRL, MXN, ARS, TRY, AED, ILS, NGN, ZAR, BDT, BMD, LKR, MMK, SAR, TWD, VEF
3 Nachkommastellen BHD, KWD

Beispiele: 23.34 ist für USD gültig; 23.345 wird für USD abgewiesen; 23 ist für JPY gültig; 23.01 wird für JPY abgewiesen.

Bestimmung des Wegs

Welcher Weg gilt, ergibt sich aus den Feldern, die Sie mitgeben. currency entscheidet, ob der Betrag in einem Token oder in Fiat ausgezeichnet ist; network entscheidet, ob die Chain vorab feststeht oder vom Kunden gewählt wird.

currency network Weg Was der Kunde noch wählt
Token (USDT, USDC, …) ein Netzwerk (TRC20, ERC20, …) Direkt in Kryptoamount ist der Token-Betrag, auf einer festen Chain Nichts — Token und Netzwerk stehen fest
Token (USDT, USDC, …) Krypto-Wegamount ist der Token-Betrag, die Chain ist offen Das Netzwerk, in dem gezahlt wird
Fiat (USD, EUR, …) Fiat-Wegamount ist ein Fiat-Betrag Das Token und das Netzwerk

Jede Rechnung entsteht in awaiting_client — eine Zahlungsadresse existiert noch nicht. Erst die Wahl des Kunden im gehosteten Checkout vergibt die Adresse und setzt die Rechnung auf awaiting_payment. Im direkten Krypto-Fall bleibt nichts zu wählen, also bestätigt der Checkout das vorgegebene Paar aus Token und Netzwerk und vergibt die Adresse in einem Schritt. Im Fiat-Weg wird in diesem Moment der Kurs von Fiat zu Krypto fixiert und als payment.exchange_rate zurückgegeben.

Den vollständigen Lebenszyklus, die Anfangsstatus und die Bedeutung der Kursfixierung beschreibt Zahlungsablauf → Zwei Wege der Erstellung.

Beispielanfrage — Fiat-Weg

Der Betrag ist in Fiat; der Kunde wählt auf der gehosteten Seite ein Token, und der Kurs wird im Moment der Auswahl fixiert.

{
  "project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
  "amount": "50.00",
  "currency": "USD",
  "external_order_id": "order-12345",
  "client_id": "customer-67890"
}

Beispielanfrage — direkt in Krypto

Token und Netzwerk stehen bei der Erstellung fest; eine Adresse wird sofort vergeben.

{
  "project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
  "amount": "50.00",
  "currency": "USDT",
  "network": "TRC20",
  "external_order_id": "order-12345",
  "client_id": "customer-67890"
}

Codebeispiele

Jeder Reiter nutzt das offizielle SDK der jeweiligen Sprache. Signatur, Serialisierung, sichere Wiederholungen und typisierte Fehler übernimmt das SDK.

API_KEY_ID="pk_live_xxxxxxxxxxxx"
API_SECRET="sk_live_xxxxxxxxxxxx"
BODY='{"project_id":"prj_xxxxxxxxxxxx","amount":"100.00","currency":"USD","external_order_id":"order-123","client_id":"customer-456"}'

TS=$(date +%s)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | sed 's/^.* //')
SIGNATURE=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" POST /v1/invoices '' "$BODY_HASH" \
  | openssl dgst -sha256 -hmac "$API_SECRET" -binary | base64)

curl -sS https://api.paymos.io/v1/invoices \
  -H "Authorization: HMAC-SHA256 $API_KEY_ID:$SIGNATURE" \
  -H "X-Request-Timestamp: $TS" \
  -H "Content-Type: application/json" \
  -d "$BODY"
import { Paymos, externalOrderId } from '@paymos/sdk';

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

const invoice = await paymos.invoices.create({
  projectId: 'prj_xxxxxxxxxxxx',
  amount: '100.00',
  currency: 'USD',
  externalOrderId: externalOrderId('order'),
  clientId: 'customer-456',
});

console.log(invoice.invoiceId, invoice.paymentUrl);
import os

from paymos import Paymos

paymos = Paymos(
    api_key=os.environ["PAYMOS_API_KEY"],
    api_secret=os.environ["PAYMOS_API_SECRET"],
)
invoice = paymos.invoices.create(
    project_id="prj_xxxxxxxxxxxx",
    amount="100.00",
    currency="USD",
    external_order_id="order-123",
    client_id="customer-456",
)

print(invoice["invoice_id"], invoice["payment_url"])
<?php
use Paymos\Client;
use Paymos\ClientConfig;
use Paymos\IdempotencyKey;

$paymos = new Client(new ClientConfig(
    getenv('PAYMOS_API_KEY'),
    getenv('PAYMOS_API_SECRET')
));
$invoice = $paymos->invoices()->create(array(
    'project_id' => 'prj_xxxxxxxxxxxx',
    'amount' => '100.00',
    'currency' => 'USD',
    'external_order_id' => IdempotencyKey::externalOrderId('order'),
    'client_id' => 'customer-456',
));

echo $invoice['invoice_id'] . ' ' . $invoice['payment_url'];
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)
	}
	invoice, err := client.Invoices.Create(context.Background(), paymos.CreateInvoiceParams{
		ProjectID:       "prj_xxxxxxxxxxxx",
		Amount:          "100.00",
		Currency:        "USD",
		ExternalOrderID: "order-123",
		ClientID:        stringPointer("customer-456"),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(invoice.InvoiceID, invoice.PaymentURL)
}

func stringPointer(value string) *string { return &value }
using Paymos;

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

var invoice = await paymos.Invoices.CreateAsync(new CreateInvoiceRequest(
    ProjectId: "prj_xxxxxxxxxxxx",
    Amount: "100.00",
    Currency: "USD",
    ExternalOrderId: "order-123",
    ClientId: "customer-456"));

Console.WriteLine($"{invoice.InvoiceId} {invoice.PaymentUrl}");
import io.paymos.CreateInvoiceRequest;
import io.paymos.Invoice;
import io.paymos.PaymosClient;

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

Invoice invoice = paymos.invoices.create(
    CreateInvoiceRequest.builder()
        .projectId("prj_xxxxxxxxxxxx")
        .amount("100.00")
        .currency("USD")
        .externalOrderId("order-123")
        .clientId("customer-456")
        .build());

System.out.println(invoice.invoiceId() + " " + invoice.paymentUrl());
require 'paymos'

paymos = Paymos::Client.new(
  api_key: ENV.fetch('PAYMOS_API_KEY'),
  api_secret: ENV.fetch('PAYMOS_API_SECRET')
)
invoice = paymos.invoices.create(
  project_id: 'prj_xxxxxxxxxxxx',
  amount: '100.00',
  currency: 'USD',
  external_order_id: 'order-123',
  client_id: 'customer-456'
)

puts "#{invoice.invoice_id} #{invoice.payment_url}"
use paymos::{CreateInvoiceRequest, PaymosClient};

let paymos = PaymosClient::new(
    std::env::var("PAYMOS_API_KEY")?,
    std::env::var("PAYMOS_API_SECRET")?,
)?;
let invoice = paymos
    .invoices()
    .create(&CreateInvoiceRequest {
        project_id: "prj_xxxxxxxxxxxx".to_owned(),
        amount: "100.00".to_owned(),
        currency: "USD".to_owned(),
        external_order_id: "order-123".to_owned(),
        network: None,
        allow_multiple_payments: None,
        customer_fee_percent: None,
        client_id: Some("customer-456".to_owned()),
    })
    .await?;

println!("{} {}", invoice.invoice_id, invoice.payment_url);

Antwort (201 Created / 200 bei idempotenter Übereinstimmung)

{
  "invoice_id": "inv_5CcyDYmMUGtzYL10q0Iimr",
  "project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
  "status": "awaiting_client",
  "is_final": false,
  "is_test": true,
  "payment_url": "https://checkout.paymos.io/invoice/inv_5CcyDYmMUGtzYL10q0Iimr",
  "order": {
    "external_id": "order-12345",
    "client_id": "customer-67890",
    "amount": "50.00",
    "currency": "USD"
  },
  "created_at": 1743594000,
  "updated_at": 1743594000,
  "expires_at": 1743597600
}

Felder der Antwort

Feld Typ Beschreibung
invoice_id Zeichenkette Rechnungskennung mit Präfix (inv_...)
project_id Zeichenkette Projektkennung mit Präfix (prj_...)
status Zeichenkette Rechnungsstatus (snake_case — siehe Rechnungsstatus)
is_final Boolescher Wert Ob die Rechnung in einem Endzustand ist
is_test Boolescher Wert true bei Sandbox-Rechnungen
payment_url Zeichenkette Zahlungs-URL: bei einem Telegram-Bot-Projekt ein Deeplink in den Paymos-Bot, bei jedem anderen die gehostete Checkout-Seite
order Objekt Bestelldaten aus Sicht des Händlers
order.external_id Zeichenkette Bestellnummer des Händlers
order.client_id Zeichenkette? Kundenkennung des Händlers
order.amount Zeichenkette Angeforderter Betrag
order.currency Zeichenkette Vom Händler angeforderter Fiat-Währungscode oder Krypto-Kürzel
order.network Zeichenkette? Angefordertes Netzwerk bei direkten Krypto-Rechnungen
payment Objekt? Zahlungsdetails — vorhanden, sobald Token und Netzwerk gewählt sind. Enthält die Einzahlungsadresse, den erwarteten Krypto-Betrag, den fixierten Kurs und den Fortschritt je Transfer. Die vollständige Referenz der Unterfelder steht unter Rechnung abrufen → Felder der Zahlung
created_at Ganzzahl Unix-Zeitstempel, Sekunden
updated_at Ganzzahl Unix-Zeitstempel, Sekunden
expires_at Ganzzahl Unix-Zeitstempel, Sekunden. Wird bei der Erstellung gesetzt und ist deshalb an jeder Rechnung in jedem Status vorhanden
completed_at Ganzzahl? Zeitstempel des Abschlusses

Rechnungsstatus

Jede Rechnung meldet eine Zeichenkette status. Derselbe Wert steuert die Antwort der Händler-API, den SSE-Stream des Checkouts und den Webhook-Inhalt — es gibt eine einzige Quelle der Wahrheit. Die Tabelle unten ist die Referenz je Status: wann der Wert auftritt, ob er endgültig ist und welche genaue Bedingung dahintersteht. Das Übergangsdiagramm und die beiden Wege der Erstellung stehen unter Zahlungsablauf.

Ein endgültiger Status ist final: Die Rechnung bleibt dort stehen und bewegt sich nicht mehr. Fünf Status sind endgültig — paid, paid_over, underpaid, expired, cancelled.

Status Endgültig Wann er gilt
awaiting_client Nein Der Startstatus jeder Rechnung. Token und Netzwerk sind noch nicht gewählt, deshalb existiert keine Einzahlungsadresse. Nur hier ist eine Stornierung erlaubt
awaiting_payment Nein Token, Netzwerk und Einzahlungsadresse stehen fest. Paymos beobachtet die Adresse auf einen eingehenden Transfer
confirming Nein Ein Transfer ist on-chain eingegangen und sammelt die für seine Betragsstufe erforderlichen Bestätigungen
underpaid_waiting Nein Weniger als der erwartete Betrag ist durch, und die Rechnung bleibt für den Rest offen. Wird nur erreicht, wenn allow_multiple_payments auf true steht
underpaid Ja Die Rechnung wurde mit Fehlbetrag geschlossen — entweder lief sie unterhalb des erwarteten Betrags ab, oder eine einzelne Zahlung blieb zurück, während allow_multiple_payments auf false stand
expired Ja Die Frist lief ohne Eingang ab, oder im Fiat-Weg verstrich das Zeitfenster für die Token-Auswahl, bevor ein Token gewählt wurde
cancelled Ja Der Händler hat die Rechnung storniert, solange sie noch in awaiting_client stand

Idempotenz

Senden Sie bei der Rechnungserstellung immer eine external_order_id. Eine Wiederholung desselben Aufrufs mit derselben external_order_id gibt die bestehende Rechnung zurück — niemals ein Duplikat. Der Antwortstatus ist 200 OK bei einer idempotenten Übereinstimmung und 201 Created bei einer neuen Rechnung.

Das ist Ihr Sicherheitsnetz gegen wackelige Netzwerke, abgestürzte Worker und Wiederholungsschleifen. Verwenden Sie dieselbe Kennung für dasselbe Geschäftsereignis und eine neue Kennung für ein neues Ereignis.

Nutzen Sie eine UUID v4 oder Ihre eigene Bestellnummer — alles, was je Geschäftsereignis stabil und eindeutig ist. Verwenden Sie versehentlich eine Kennung erneut, bekommen Sie die dazu bestehende Rechnung zurück (die oben beschriebene idempotente Übereinstimmung mit 200 OK), keine neue.

Fehler

Den vollständigen Katalog finden Sie unter Fehlercodes. Die type-URI in jeder Fehlerantwort verweist direkt auf die passende Zeile.