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