Zum Inhalt springen

API

Auf dieser Seite

Authentifizierung

Jede Anfrage an die Händler-API mit HMAC-SHA256 authentifizieren: erforderliche Header, kanonische Zeichenkette, Zeitstempel und Schutz vor Wiedereinspielung.

Jede Anfrage an die Paymos-API trägt eine HMAC-SHA256-Signatur über eine kanonische Zeichenkette. Die Zugangsdaten erzeugen Sie im Dashboard — Arten, Geltungsbereiche und Lebenszyklus stehen unter API-Schlüssel.

Signaturverfahren

Jede API-Anfrage trägt zwei erforderliche Header und einen optionalen:

Header Beschreibung
Authorization HMAC-SHA256 {apiKeyId}:{base64signature} — Ihre Schlüssel-ID (etwa pk_live_...) und die Signatur
X-Request-Timestamp Aktueller Unix-Zeitstempel (Sekunden)
X-Correlation-Id (optional) Ihre Korrelations-ID zur Nachverfolgung der Anfrage

Zu signierender Inhalt

Die Signatur ist Base64(HMAC-SHA256(api_secret, string_to_sign)), wobei gilt:

string_to_sign = timestamp + "\n" + METHOD + "\n" + path + "\n" + query + "\n" + bodyHash
Bestandteil Beschreibung
timestamp Derselbe Wert wie X-Request-Timestamp
METHOD HTTP-Methode in Großbuchstaben (POST, GET)
path URL-dekodierter Pfad der Anfrage, ohne Query-String (/v1/invoices)
query Query-String einschließlich ? (leere Zeichenkette, wenn keiner vorhanden ist)
bodyHash SHA-256 des Anfragekörpers als Hex in Kleinbuchstaben. Leere Zeichenkette, wenn es keinen Körper gibt — hashen Sie KEINE leere Zeichenkette

Gibt es keinen Körper, ist bodyHash die leere Zeichenkette. Hashen Sie die leere Zeichenkette nicht: SHA-256("") liefert einen festen, nicht leeren Digest, sodass der Server ein anderes string_to_sign berechnet als Sie und die Signatur abweist.

Aufbau der Signatur

Beispiel: POST /v1/invoices mit JSON-Körper

1709000000\nPOST\n/v1/invoices\n\n<sha256hex of body>

Hier ist der Query-Bestandteil leer (keine Query-Parameter), und bodyHash ist der SHA-256-Digest des Anfragekörpers als Hex in Kleinbuchstaben.

Beispiel: GET /v1/invoices/inv_74BPZFhr9qy9Uz2fbRkdJX

1709000000\nGET\n/v1/invoices/inv_74BPZFhr9qy9Uz2fbRkdJX\n\n

Es gibt weder Query-String noch Anfragekörper, deshalb sind die beiden letzten Bestandteile leere Zeichenketten.

Schutz vor Wiedereinspielung

Der X-Request-Timestamp muss innerhalb eines akzeptierten Zeitfensters um die Serveruhr liegen (standardmäßig ±5 Minuten). Anfragen außerhalb des Fensters werden als nicht autorisiert abgewiesen (timestamp_expired).

Wenn die Uhr Ihres Servers abweichen kann, lesen Sie die Serveruhr über den nicht authentifizierten Zeit-Endpunkt und korrigieren Sie Ihren Versatz vor dem Signieren:

GET /v1/time
{ "server_time": 1739280600 }

server_time ist die aktuelle Serverzeit in Unix-Sekunden. Eine Authentifizierung ist nicht erforderlich. Vergleichen Sie den Wert mit Ihrer eigenen Uhr und passen Sie den gesendeten X-Request-Timestamp entsprechend an.

Übergangsfrist beim Wechsel des Geheimnisses

Wenn Sie Ihr API-Geheimnis wechseln, bleibt das vorherige für eine Übergangsfrist gültig. In diesem Zeitfenster werden Signaturen akzeptiert, die mit dem aktuellen oder dem vorherigen Geheimnis gebildet wurden. Ein neuer Wechsel lässt sich erst starten, wenn die laufende Übergangsfrist abgelaufen ist.

SDK-Beispiel

Das offizielle SDK baut die kanonische Zeichenkette und signiert jede Anfrage. Ihr Anwendungscode liefert nur Zugangsdaten und typisierte Anfragefelder.

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);