Zum Inhalt springen

API

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.

POST/v1/payment-channels

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 Created mit einem Location-Header auf den neuen Kanal.
  • Jeder spätere Aufruf mit demselben Paar → 200 OK mit demselben Kanal. Ein Duplikat entsteht nicht.
  • Dieselbe external_id in 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, deren tokens-Array nicht leer ist. Ein Eintrag auf provisioning hat 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.