Rechnung stornieren
POST/v1/invoices/{id}/cancel
Vertrag v1 / 1.0.0 62 / 2026-10-10
Auf dieser Seite
Offene Rechnung stornieren, ohne sie zu löschen. Sie bleibt für den Abgleich verfügbar. Ein unvereinbarer geschlossener Status liefert 409.
Schlüsselberechtigungen: invoices / write / payments.
Parameter
| Parameter | Eingehend | Erforderlich | Beschreibung |
|---|---|---|---|
id | path | Ja | Undurchsichtige Ressourcen-ID.string minLength: 1 |
Anfragebeispiele
Beispiele führen eine echte Aktion in Ihrem Konto aus. Die im Code genannten Umgebungsvariablen setzen. Schlüssel geheim halten; Schreibanfragen werden nicht automatisch wiederholt.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_RESOURCE_ID:?Set CRYPTANIO_RESOURCE_ID}"
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
urlencode() {
local LC_ALL=C text="$1" char index
for ((index=0; index<${#text}; index++)); do
char="${text:index:1}"
case "$char" in [a-zA-Z0-9.~_-]) printf '%s' "$char" ;; *) printf '%%%02X' "'$char" ;; esac
done
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request POST \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
"https://api.cryptanio.com/v1/invoices/$(urlencode "$CRYPTANIO_RESOURCE_ID")/cancel" \
--output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
cat "$response_file"
exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
// Go; standard library only. Run: go run request.go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func env(name string) string {
value := os.Getenv(name)
if value == "" { fmt.Fprintln(os.Stderr, "Set", name); os.Exit(1) }
return value
}
func run() error {
path := "/v1/invoices/{id}/cancel"
path = strings.ReplaceAll(path, "{id}", url.PathEscape(env("CRYPTANIO_RESOURCE_ID")))
endpoint, err := url.Parse("https://api.cryptanio.com" + path)
if err != nil { return err }
query := endpoint.Query()
endpoint.RawQuery = query.Encode()
request, err := http.NewRequest("POST", endpoint.String(), nil)
if err != nil { return err }
request.Header.Set("Authorization", "Bearer " + env("CRYPTANIO_API_KEY"))
request.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 30 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}
response, err := client.Do(request)
if err != nil { return err }
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
body, err := io.ReadAll(response.Body)
if err != nil { return err }
return fmt.Errorf("HTTP %d: %s", response.StatusCode, body)
}
_, err = io.Copy(os.Stdout, response.Body)
return err
}
func main() {
if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
}
// Cargo.toml dependencies:
// reqwest = { version = "=0.12.24", features = ["blocking", "json"] }
// serde_json = "=1.0.145"
use std::{error::Error, io, time::Duration};
use reqwest::{blocking::Client, redirect::Policy, Method, Url};
fn env(name: &str) -> Result<String, Box<dyn Error>> {
let value = std::env::var(name)?;
if value.is_empty() { return Err(format!("Set {name}").into()); }
Ok(value)
}
fn run() -> Result<(), Box<dyn Error>> {
let mut url = Url::parse("https://api.cryptanio.com")?;
{
let mut segments = url.path_segments_mut().map_err(|_| "Invalid base URL")?;
segments.clear();
segments.push("v1");
segments.push("invoices");
segments.push(&env("CRYPTANIO_RESOURCE_ID")?);
segments.push("cancel");
}
let client = Client::builder().timeout(Duration::from_secs(30)).redirect(Policy::none()).build()?;
let request = client.request(Method::POST, url)
.header("Authorization", format!("Bearer {}", env("CRYPTANIO_API_KEY")?))
.header("Accept", "application/json".to_owned());
let mut response = request.send()?;
let status = response.status();
if !status.is_success() { return Err(format!("HTTP {}: {}", status.as_u16(), response.text()?).into()); }
io::copy(&mut response, &mut io::stdout())?;
Ok(())
}
fn main() {
if let Err(error) = run() { eprintln!("{error}"); std::process::exit(1); }
}
<?php
// PHP 8 with the cURL extension. Run this on your server.
function env(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') throw new RuntimeException('Set ' . $name);
return $value;
}
try {
$path = '/v1/invoices/{id}/cancel';
$path = str_replace('{id}', rawurlencode(env('CRYPTANIO_RESOURCE_ID')), $path);
$query = [
];
$url = 'https://api.cryptanio.com' . $path . ($query ? '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986) : '');
$headers = [
'Authorization: ' . 'Bearer ' . env('CRYPTANIO_API_KEY'),
'Accept: ' . 'application/json',
];
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($curl);
if ($response === false) throw new RuntimeException(curl_error($curl));
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($status < 200 || $status >= 300) throw new RuntimeException('HTTP ' . $status . ': ' . $response);
echo $response;
} catch (Throwable $error) {
fwrite(STDERR, $error->getMessage() . PHP_EOL);
exit(1);
}
# Python 3; standard library only.
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode
from urllib.request import HTTPRedirectHandler, Request, build_opener
def env(name):
result = os.environ.get(name)
if not result:
raise ValueError("Set " + name)
return result
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
try:
path = "/v1/invoices/{id}/cancel"
path = path.replace("{id}", quote(env("CRYPTANIO_RESOURCE_ID"), safe=""))
query = {
}
url = "https://api.cryptanio.com" + path + ("?" + urlencode(query) if query else "")
headers = {
"Authorization": "Bearer " + env("CRYPTANIO_API_KEY"),
"Accept": "application/json",
}
data = None
request = Request(url, data=data, headers=headers, method="POST")
with build_opener(NoRedirect()).open(request, timeout=30) as response:
if not 200 <= response.status < 300:
raise ValueError("HTTP " + str(response.status))
sys.stdout.buffer.write(response.read())
except HTTPError as error:
print("HTTP " + str(error.code) + ": " + error.read().decode("utf-8", errors="replace"), file=sys.stderr)
sys.exit(1)
except (ValueError, URLError, OSError) as error:
print(str(error), file=sys.stderr)
sys.exit(1)
// Node.js 18+. Run on your server, never in a browser.
const env = name => {
const result = process.env[name];
if (!result) throw new Error(`Set ${name}`);
return result;
};
try {
let path = "/v1/invoices/{id}/cancel";
path = path.replace("{id}", encodeURIComponent(env("CRYPTANIO_RESOURCE_ID")));
const url = new URL(path, "https://api.cryptanio.com");
const headers = {
"Authorization": "Bearer " + env("CRYPTANIO_API_KEY"),
"Accept": "application/json",
};
const response = await fetch(url, {
method: "POST", headers, redirect: "manual",
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
if (response.body) {
for await (const chunk of response.body) process.stdout.write(chunk);
}
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
Antworten und Fehler
200 Erfolgreiche Antwort.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
checkout_url | string | Nein | Von Cryptanio bereitgestellte Zahlungsseite. Erstellung und Wiederholung einer Rechnung mit Methodenauswahl geben einen interaktiven Zugangslink zurück, der die Auswahl erlaubt, bis eine Zahlung erkannt wird. Bei fester Methode und bei jedem GET oder Listenabruf wird ein separater Link mit reinem Lesezugriff zurückgegeben. Beide Links nur mit dem vorgesehenen Zahler teilen; keiner gewährt Zugriff auf Konto oder API-Schlüssel. Zahlung über die Rechnungs-API oder einen verifizierten Webhook bestätigen, nicht anhand einer Browserweiterleitung.uri |
id | string | Ja | Undurchsichtige Kennung; als Zeichenfolge behalten. minLength: 1 |
status | string | Ja | pending wartet auf Zahlung; seen erkannt; partial teilweise bezahlt; paid beglichen; underpaid/overpaid/late erfordern Entscheidung; expired Frist abgelaufen; canceled storniert.pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
project_id | string | Ja | Undurchsichtige Kennung; als Zeichenfolge behalten. minLength: 1 |
reference | string | Nein | Bestellreferenz. |
customer | object | Nein | |
product | object | Nein | |
payer | object | Nein | |
metadata | object | Nein | Bis zu 20 Paare, höchstens 8192 JSON-Bytes. Keine Geheimnisse einschließen. |
invoice_currency | string | Nein | Währung des Bestellangebots. |
invoice_amount | string | Nein | Exakter Dezimalbetrag als Zeichenfolge; keine Gleitkommazahlen verwenden. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
chain | string | Ja | Netzwerkcode. |
asset | string | Ja | Zahlungsasset. |
address | string | Ja | Zahlungsadresse. |
payment_route | object | Nein | Erforderliche Zuordnung auf der Blockchain, wenn eine automatische Quelle einen Tag, ein Memo, einen TON-Kommentar oder eine Solana-Pay-Referenz ausgibt. Speichern Sie diese zusammen mit der Empfangsadresse; die Bestellreferenz des Händlers ist ein separates Feld. |
payment_uri | string | Nein | Solana-Pay-Transferanfrage mit Empfänger, genauem Betrag, Mint des unterstützten Tokens und erforderlicher Referenz. Verwenden Sie diese URI im Zahlungs-QR anstelle der reinen Empfangsadresse.uri |
amount | string | string | Ja | Tatsächlicher Betrag, den der Käufer senden muss; kann zur Unterscheidung von Rechnungen vom angefragten Betrag abweichen. Leer, bis eine Methode ausgewählt wurde. |
amount_raw | string | string | Ja | Ganzzahliger Betrag in der kleinsten Einheit als Zeichenfolge. Leer, bis eine Methode ausgewählt wurde. |
asked | string | string | Ja | Ursprünglich angefragter Kryptobetrag. Leer, bis eine Methode ausgewählt wurde. |
decimals | integer | Ja | Dezimalstellen des Assets. minimum: 0 |
tolerance | string | Nein | Toleranz in kleinsten Einheiten; anders als request.tolerance. pattern: ^[0-9]+$ |
sandbox | boolean | Ja | Eine Testrechnung belegt keine echte Blockchain-Zahlung. |
needs_decision | boolean | Nein | Bei underpaid, overpaid oder late ist eine Entscheidung erforderlich. |
paid | string | Nein | Erhaltener Betrag in kleinsten Einheiten. pattern: ^[0-9]+$ |
tx_id | string | Nein | Kennung der Zahlungstransaktion. |
paid_at | string | Nein | UTC-Datum und -Uhrzeit nach RFC 3339.date-time |
expires_at | string | Ja | UTC-Datum und -Uhrzeit nach RFC 3339.date-time |
created_at | string | Ja | UTC-Datum und -Uhrzeit nach RFC 3339.date-time |
{
"id": "2100",
"status": "pending",
"project_id": "2002",
"reference": "order-42",
"chain": "ethereum",
"asset": "USDC",
"address": "0x52908400098527886E0F7030069857D2E4169EE7",
"amount": "12.5",
"amount_raw": "12500000",
"asked": "12.5",
"decimals": 6,
"sandbox": true,
"expires_at": "2026-10-06T11:00:00Z",
"created_at": "2026-10-06T10:00:00Z"
}400 Ungültige Anfrage (bad_request, unsupported_asset).
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}401 Kein gültiger aktiver API-Schlüssel (unauthorized).
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}403 Schlüssel verbietet Aktion, Projekt, Dienst oder IP; E-Mail unbestätigt.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}404 Objekt im Schlüsselumfang nicht gefunden (not_found).
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}409 Zustandskonflikt, abweichende Wiederholungsparameter oder Zahlungsressourcenlimit (conflict, limit_reached, service_inactive).
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}429 Zu viele Anfragen (rate_limited); nach Retry-After wiederholen.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}500 Interner Fehler (internal).
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}503 Vorübergehend nicht verfügbar (unavailable); mit Backoff wiederholen.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}Verschachtelte Objekte
Invoice
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
checkout_url | string | Nein | Von Cryptanio bereitgestellte Zahlungsseite. Erstellung und Wiederholung einer Rechnung mit Methodenauswahl geben einen interaktiven Zugangslink zurück, der die Auswahl erlaubt, bis eine Zahlung erkannt wird. Bei fester Methode und bei jedem GET oder Listenabruf wird ein separater Link mit reinem Lesezugriff zurückgegeben. Beide Links nur mit dem vorgesehenen Zahler teilen; keiner gewährt Zugriff auf Konto oder API-Schlüssel. Zahlung über die Rechnungs-API oder einen verifizierten Webhook bestätigen, nicht anhand einer Browserweiterleitung.uri |
id | string | Ja | Undurchsichtige Kennung; als Zeichenfolge behalten. minLength: 1 |
status | string | Ja | pending wartet auf Zahlung; seen erkannt; partial teilweise bezahlt; paid beglichen; underpaid/overpaid/late erfordern Entscheidung; expired Frist abgelaufen; canceled storniert.pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
project_id | string | Ja | Undurchsichtige Kennung; als Zeichenfolge behalten. minLength: 1 |
reference | string | Nein | Bestellreferenz. |
customer | object | Nein | |
product | object | Nein | |
payer | object | Nein | |
metadata | object | Nein | Bis zu 20 Paare, höchstens 8192 JSON-Bytes. Keine Geheimnisse einschließen. |
invoice_currency | string | Nein | Währung des Bestellangebots. |
invoice_amount | string | Nein | Exakter Dezimalbetrag als Zeichenfolge; keine Gleitkommazahlen verwenden. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
chain | string | Ja | Netzwerkcode. |
asset | string | Ja | Zahlungsasset. |
address | string | Ja | Zahlungsadresse. |
payment_route | object | Nein | Erforderliche Zuordnung auf der Blockchain, wenn eine automatische Quelle einen Tag, ein Memo, einen TON-Kommentar oder eine Solana-Pay-Referenz ausgibt. Speichern Sie diese zusammen mit der Empfangsadresse; die Bestellreferenz des Händlers ist ein separates Feld. |
payment_uri | string | Nein | Solana-Pay-Transferanfrage mit Empfänger, genauem Betrag, Mint des unterstützten Tokens und erforderlicher Referenz. Verwenden Sie diese URI im Zahlungs-QR anstelle der reinen Empfangsadresse.uri |
amount | string | string | Ja | Tatsächlicher Betrag, den der Käufer senden muss; kann zur Unterscheidung von Rechnungen vom angefragten Betrag abweichen. Leer, bis eine Methode ausgewählt wurde. |
amount_raw | string | string | Ja | Ganzzahliger Betrag in der kleinsten Einheit als Zeichenfolge. Leer, bis eine Methode ausgewählt wurde. |
asked | string | string | Ja | Ursprünglich angefragter Kryptobetrag. Leer, bis eine Methode ausgewählt wurde. |
decimals | integer | Ja | Dezimalstellen des Assets. minimum: 0 |
tolerance | string | Nein | Toleranz in kleinsten Einheiten; anders als request.tolerance. pattern: ^[0-9]+$ |
sandbox | boolean | Ja | Eine Testrechnung belegt keine echte Blockchain-Zahlung. |
needs_decision | boolean | Nein | Bei underpaid, overpaid oder late ist eine Entscheidung erforderlich. |
paid | string | Nein | Erhaltener Betrag in kleinsten Einheiten. pattern: ^[0-9]+$ |
tx_id | string | Nein | Kennung der Zahlungstransaktion. |
paid_at | string | Nein | UTC-Datum und -Uhrzeit nach RFC 3339.date-time |
expires_at | string | Ja | UTC-Datum und -Uhrzeit nach RFC 3339.date-time |
created_at | string | Ja | UTC-Datum und -Uhrzeit nach RFC 3339.date-time |
ID
Undurchsichtige Kennung. Format nicht garantiert; nicht in eine Zahl umwandeln.
string Undurchsichtige Kennung. Format nicht garantiert; nicht in Zahl umwandeln.
InvoiceStatus
pending wartet auf Zahlung; seen erkannt; partial teilweise bezahlt; paid beglichen; underpaid/overpaid/late erfordern Entscheidung; expired Frist abgelaufen; canceled storniert.
string pending wartet auf Zahlung; seen erkannt; partial teilweise bezahlt; paid beglichen; underpaid/overpaid/late erfordern Entscheidung; expired Frist abgelaufen; canceled storniert.
Customer
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
id | string | Ja | Kundenkennung in Ihrem System. maxLength: 120 |
Product
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
id | string | Nein | Produktkennung in Ihrem System. maxLength: 120 |
name | string | Nein | Produktname. maxLength: 200 |
quantity | integer | Nein | Menge. minimum: 1; maximum: 1000000 |
Payer
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
comment | string | Ja | Kommentar des Zahlenden. maxLength: 500 |
InvoiceMetadata
Bis zu 20 Paare, höchstens 8192 JSON-Bytes. Keine Geheimnisse einschließen.
object Bis zu 20 Paare, höchstens 8192 JSON-Bytes. Keine Geheimnisse einschließen.
Decimal
string
RawAmount
string
Error
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
error | object | Ja |