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).
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 Krypto — amount ist der Token-Betrag, auf einer festen Chain |
Nichts — Token und Netzwerk stehen fest |
Token (USDT, USDC, …) |
— | Krypto-Weg — amount ist der Token-Betrag, die Chain ist offen |
Das Netzwerk, in dem gezahlt wird |
Fiat (USD, EUR, …) |
— | Fiat-Weg — amount 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 |
paid |
Ja | Der erwartete Betrag ist vollständig durch (oder innerhalb der Toleranz für Unterzahlung des Projekts) |
paid_over |
Ja | Mehr als der erwartete Betrag ist durch; der gesamte Transfer wird gutgeschrieben |
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.