Auf dieser Seite
Erstellen
Eine dauerhafte Einzahlungsadresse je Netzwerk für einen einzelnen Kunden anlegen, geführt über Ihre eigene externe Kennung und beliebig oft wiederverwendbar.
API-Schlüssel: Payment
Ein Zahlungskanal ist die dauerhafte Einzahlungsadresse eines einzelnen Kunden, je unterstütztem Netzwerk eine. Anders als eine Rechnung hat er weder Betrag noch Frist noch Endzustand. Sie legen ihn einmal an, zeigen dem Kunden seine Adressen, und jeder Transfer, der dort eingeht, wird zu einer Einzahlung und Ihrem Guthaben gutgeschrieben.
Anfragekörper
| Parameter | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
project_id |
Zeichenkette | Ja | Projektkennung mit Präfix (prj_…). Das Projekt entscheidet, welche Token der Kanal annimmt. |
external_id |
Zeichenkette | Ja | Ihre eigene, stabile Kennung für den Kunden, höchstens 128 Zeichen. Nach dem Anlegen unveränderlich. |
{
"project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
"external_id": "customer-42"
}
Idempotenz
Das Paar aus project_id und external_id ist der Idempotenzschlüssel, innerhalb der Umgebung des Schlüssels, mit dem Sie signieren. Mehr gibt es nicht zu senden, deshalb kann eine Wiederholung auch keinen Konflikt im Inhalt auslösen.
- —Erster Aufruf →
201 Createdmit einemLocation-Header auf den neuen Kanal. - —Jeder spätere Aufruf mit demselben Paar →
200 OKmit demselben Kanal. Ein Duplikat entsteht nicht. - —Dieselbe
external_idin Sandbox und in Produktion ergibt zwei voneinander unabhängige Kanäle.
Senden Sie die Kundenkennung, die Sie in Ihrem eigenen System ohnehin führen, und rufen Sie diese Route bei jedem Checkout auf, statt die pc_-Kennung selbst zwischenzuspeichern. Eine Wiederholung ist weder ein Fehler noch ein Duplikat: Beide Statuscodes liefern einen Kanal, und die 200 enthält die Adressen und Token, die der Kanal heute hat. Die Wiederholung einer abgebrochenen Anfrage liefert den ursprünglichen Kanal, statt einen zweiten zu eröffnen.
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","external_id":"customer-42"}'
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/payment-channels '' "$BODY_HASH" \
| openssl dgst -sha256 -hmac "$API_SECRET" -binary | base64)
curl -sS https://api.paymos.io/v1/payment-channels \
-H "Authorization: HMAC-SHA256 $API_KEY_ID:$SIGNATURE" \
-H "X-Request-Timestamp: $TS" \
-H "Content-Type: application/json" \
-d "$BODY"
import { Paymos } from '@paymos/sdk';
const paymos = new Paymos({
apiKey: process.env.PAYMOS_API_KEY,
apiSecret: process.env.PAYMOS_API_SECRET,
});
const channel = await paymos.paymentChannels.create({
projectId: 'prj_xxxxxxxxxxxx',
externalId: 'customer-42',
});
for (const rail of channel.networks) {
console.log(rail.network, rail.address ?? rail.status);
}
import os
from paymos import Paymos
paymos = Paymos(
api_key=os.environ["PAYMOS_API_KEY"],
api_secret=os.environ["PAYMOS_API_SECRET"],
)
channel = paymos.payment_channels.create(
project_id="prj_xxxxxxxxxxxx",
external_id="customer-42",
)
for rail in channel["networks"]:
print(rail["network"], rail.get("address", rail["status"]))
<?php
use Paymos\Client;
use Paymos\ClientConfig;
$paymos = new Client(new ClientConfig(
getenv('PAYMOS_API_KEY'),
getenv('PAYMOS_API_SECRET')
));
$channel = $paymos->paymentChannels()->create(array(
'project_id' => 'prj_xxxxxxxxxxxx',
'external_id' => 'customer-42',
));
foreach ($channel['networks'] as $rail) {
$address = isset($rail['address']) ? $rail['address'] : $rail['status'];
echo $rail['network'] . ' ' . $address . 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)
}
channel, err := client.PaymentChannels.Create(context.Background(), paymos.CreatePaymentChannelParams{
ProjectID: "prj_xxxxxxxxxxxx",
ExternalID: "customer-42",
})
if err != nil {
panic(err)
}
for _, rail := range channel.Networks {
fmt.Println(rail.Network, addressOrStatus(rail))
}
}
func addressOrStatus(rail paymos.PaymentChannelNetwork) string {
if rail.Address == nil {
return string(rail.Status)
}
return *rail.Address
}
using Paymos;
using var paymos = new PaymosClient(
Environment.GetEnvironmentVariable("PAYMOS_API_KEY")!,
Environment.GetEnvironmentVariable("PAYMOS_API_SECRET")!);
var channel = await paymos.PaymentChannels.CreateAsync(new CreatePaymentChannelRequest(
ProjectId: "prj_xxxxxxxxxxxx",
ExternalId: "customer-42"));
foreach (var rail in channel.Networks)
{
Console.WriteLine($"{rail.Network} {rail.Address ?? rail.Status}");
}
import io.paymos.CreatePaymentChannelRequest;
import io.paymos.PaymentChannel;
import io.paymos.PaymentChannelNetwork;
import io.paymos.PaymosClient;
PaymosClient paymos = new PaymosClient(
System.getenv("PAYMOS_API_KEY"),
System.getenv("PAYMOS_API_SECRET"));
PaymentChannel channel = paymos.paymentChannels.create(
new CreatePaymentChannelRequest("prj_xxxxxxxxxxxx", "customer-42"));
for (PaymentChannelNetwork rail : channel.networks()) {
System.out.println(rail.network() + " "
+ (rail.address() == null ? rail.status() : rail.address()));
}
require 'paymos'
paymos = Paymos::Client.new(
api_key: ENV.fetch('PAYMOS_API_KEY'),
api_secret: ENV.fetch('PAYMOS_API_SECRET')
)
channel = paymos.payment_channels.create(
project_id: 'prj_xxxxxxxxxxxx',
external_id: 'customer-42'
)
channel.networks.each do |rail|
puts "#{rail.network} #{rail.address || rail.status}"
end
use paymos::{CreatePaymentChannelRequest, PaymosClient};
let paymos = PaymosClient::new(
std::env::var("PAYMOS_API_KEY")?,
std::env::var("PAYMOS_API_SECRET")?,
)?;
let channel = paymos
.payment_channels()
.create(&CreatePaymentChannelRequest {
project_id: "prj_xxxxxxxxxxxx".to_owned(),
external_id: "customer-42".to_owned(),
})
.await?;
for rail in &channel.networks {
println!("{} {}", rail.network, rail.address.as_deref().unwrap_or(&rail.status));
}
Antwort (201 Created, bei einer Wiederholung 200)
{
"id": "pc_2QhKZv6mRt9WdA3nYpLbXf",
"project_id": "prj_xFukZuAJZR06pLVBh3uwzv",
"external_id": "customer-42",
"status": "active",
"is_accepting_payments": true,
"is_fully_provisioned": false,
"is_test": false,
"applied_fee_percent": 1.0,
"customer_fee_percent": 0,
"networks": [
{
"network": "TRC20",
"status": "active",
"address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
"tokens": [
{ "symbol": "USDT", "minimum_deposit": "1.00" },
{ "symbol": "USDC", "minimum_deposit": "1.00" }
]
},
{
"network": "ERC20",
"status": "active",
"address": "0x9f2a6c1b4e8d7350ab11c9e5f0d2743b86ce41d9",
"tokens": [
{ "symbol": "USDT", "minimum_deposit": "12.00" },
{ "symbol": "USDC", "minimum_deposit": "12.00" }
]
},
{
"network": "SOL",
"status": "provisioning",
"tokens": [
{ "symbol": "USDC", "minimum_deposit": null }
]
}
],
"created_at": 1767225600,
"updated_at": 1767225780
}
Netzwerke und Adressen
Ein Kanal führt keine eigene Tokenliste. Er nimmt an, was das Projekt annimmt: Aktivieren Sie ein Token im Projekt, steht es in jedem Kanal dieses Projekts zur Verfügung, und deaktivieren Sie es, verschwindet es überall. Verwalten Sie die angenommenen Token unter Dashboard → Projekte.
Jeder Eintrag in networks beschreibt eine Chain:
| Feld | Bedeutung |
|---|---|
network |
Code des Netzwerks, etwa TRC20 oder ERC20. |
status |
active, sobald die Adresse existiert, davor provisioning. |
address |
Die dauerhafte Einzahlungsadresse. Fehlt, bis dieses Netzwerk bereitgestellt ist — noch nicht, nicht gar nicht. Einmal zurückgegeben, ändert sie sich nie mehr. |
tokens |
Was dieses Netzwerk gerade annimmt. Jeder Eintrag ist ein Objekt aus symbol und minimum_deposit, kein bloßes Kürzel. |
Zwei Regeln bestimmen, was Sie dem Kunden zeigen:
- —Zeigen Sie nur Netzwerke auf
active, derentokens-Array nicht leer ist. Ein Eintrag aufprovisioninghat noch keine Adresse anzuzeigen. - —Nehmen Sie einen Weg aus der Anzeige, sobald die API ihn nicht mehr zurückgibt. Adressen werden nie neu vergeben, aber ein Netzwerk, das Sie aus dem Projekt entfernt haben, fällt aus der Antwort, und Geld, das dorthin geht, wird nicht mehr erwartet.
Eine einmal zurückgegebene Adresse gehört diesem Kanal für immer. Legen Sie sie im Cache ab. Ein erneutes Lesen des Kanals liefert denselben Wert, und dieselbe Adresse bedient jedes Token dieses Netzwerks.
Jeder Token-Eintrag führt sein eigenes minimum_deposit — den kleinsten Transfer, den dieser Weg gutschreibt. Nehmen Sie den Wert aus derselben Antwort, die Sie gerade anzeigen, und stellen Sie ihn neben die Adresse. Kanal abrufen erklärt das Feld, auch den Fall null.
Bereitstellung
Ein neuer Produktionskanal startet auf provisioning und ohne Adressen. Jedes Netzwerk wird einzeln bereitgestellt und ist nutzbar, sobald seine eigene Adresse existiert — auf die letzte Chain müssen Sie nicht warten. Bei der ersten fertigen Adresse springt der Kanal auf active und bleibt dort.
is_fully_provisioned sagt Ihnen, ob jedes erforderliche Netzwerk fertig ist. Ein Kanal kann active und trotzdem nicht vollständig bereitgestellt sein; das ist der Normalfall, solange die übrigen Chains nachziehen.
Kanäle in der Sandbox sind sofort fertig: Ihre Adressen entstehen lokal und berühren keine Chain.
Gebühren
applied_fee_percent und customer_fee_percent sind Ihre aktuellen Sätze. Sie stehen zur Orientierung dort und sind kein Angebot — sie ändern sich, wenn sich Ihre Konditionen ändern.
Verbindlich sind die Zahlen auf der einzelnen Einzahlung. Jede Einzahlung hält die Sätze fest, die im Moment ihrer Zuordnung galten, und weist ihr eigenes gross, fee und net aus. Eine Änderung der Konditionen schreibt eine bereits eingegangene Zahlung nie um. Eine Route für eine Gebührenvorschau gibt es nicht: Legen Sie eine Einzahlung in der Sandbox an, wenn Sie die Rechenweise sehen wollen.
Fehler
| Code | HTTP | Wann |
|---|---|---|
payment_channels_disabled |
503 | Zahlungskanäle sind für Ihr Konto in dieser Umgebung nicht freigeschaltet. |
payment_key_required |
403 | Die Anfrage wurde mit einem Payout-Schlüssel (rk_) signiert. |
project_not_found |
404 | project_id führt zu nichts, was für diese Zugangsdaten sichtbar wäre. |
payment_channel_external_id_invalid |
400 | external_id ist leer, besteht nur aus Leerzeichen oder ist länger als 128 Zeichen. |
payment_channel_project_has_no_supported_tokens |
409 | Das Projekt aktiviert kein Token, das ein Kanal einsammeln könnte. |
Den vollständigen Katalog finden Sie unter Fehlercodes.
Was diese API nicht hat
Es gibt keine Route zum Aktualisieren, zum Löschen, zum Neuzuweisen, für eine Callback-URL, für Token je Kanal oder für eine Gebührenvorschau. Die Identität eines Kanals ist bewusst unveränderlich: Eine Adresse, die ein Kunde gespeichert hat, darf nie jemand anderem gehören. Um einen Kanal stillzulegen, sperren Sie ihn.