Eine Zahlungsintegration, die du testen, abgleichen und automatisieren kannst, ohne Kundenzahlung und Verkäuferauszahlung zu verwechseln.
1. Kläre die Aufgaben
Dein Shop verwaltet Katalog, Verkäuferkonten, Warenkorb, Versand und Bestellungen. Wholly Crypto übernimmt Checkout, Zahlungsprüfung, Verkäuferanteile und freigegebene Auszahlungen. Ein Verkäuferdatensatz ist kein Login; bestehende Shop-Plugins teilen Warenkörbe nicht automatisch auf mehrere Verkäufer auf.
- Ein Warenkorb
- Eine Kundenrechnung
- Geprüfte Verkäuferanteile
- Freigegebene Auszahlungen
Kunden zahlen zuerst in die Projekt-Wallets. Du kontrollierst die Schlüssel und hältst Geld, das den Verkäufern zusteht. Das ist keine direkte Aufteilung vom Kunden an die Verkäufer und kein verwahrungsfreier Dienst für deine Verkäufer.
Marketplace unterstützt Bitcoin-Mainnet sowie unterstützte EVM-Coins und Standard-ERC-20-Token. Verkäufer bekommen das vom Kunden verwendete Asset im selben Netzwerk, keinen automatischen Fiat-Umtausch. Nicht alle 30 Empfangs-Chains sind für Marketplace-Auszahlungen verfügbar.
2. Rechne die Beträge durch
Drei Verkäufer verkaufen Waren für je 100 Dollar. Setze die Projektprovision auf 4%, für dieses Beispiel ohne abweichende Store- oder Verkäuferwerte.
| Anteil | Brutto | Deine Provision | Verkäufer bekommt |
|---|---|---|---|
| Je Verkäufer | $100 | $4 | $96 |
| Alle drei | $300 | $12 | $288 |
Das sind Gegenwerte zum festgehaltenen Rechnungskurs, ausgezahlt in Krypto. Spätere Dollarwerte sind nicht garantiert. Die übliche 1%-Verarbeitungsgebühr verbraucht bei dieser 300-Dollar-Rechnung einmal 3 Dollar vorausbezahltes Guthaben, nicht einmal pro Verkäufer. Von deinen 12 Dollar Bruttoprovision bleiben damit 9 Dollar vor Netzwerkgebühren, wenn diese Gebühr gilt.
Halte freie native Coins für Bitcoin-Gebühren oder EVM-Gas separat bereit. Gebühren dürfen die geschützten Verkäuferbeträge nicht aufbrauchen. Normale Sweeps können Marketplace-Empfangsadressen nicht ausgeben.
3. Bereite Wallets und Verkäufer vor
- Öffne Project → Marketplace → Settings, aktiviere Marketplace, wähle die Stores und setze die Provision. Lass Auszahlungen während der Einrichtung pausiert und automatische Regeln aus.
- Sichere Projekt-Wallets und Datenbank. Aktiviere die gewünschten BTC/EVM-Methoden im Store, prüfe die Scanner und fülle Verarbeitungsguthaben sowie separate native Gebührenmittel auf.
- Lege jeden Verkäufer an. Speichere seine UUID bei der Verkäufer-ID deines Shops; external_id kann diese Referenz aufnehmen. Prüfe jede Auszahlungsadresse unabhängig und gib sie für die genaue Chain und das Netzwerk frei.
Für jede Checkout-Methode braucht jeder beteiligte Verkäufer ein passendes freigegebenes Ziel. Spätere Adressänderungen leiten bestehende Verpflichtungen nicht stillschweigend um.
Auszahlungsprüfungen brauchen positive Bestätigungen und zwei unabhängige passende Anbieter, auch wenn Checkout null Bestätigungen oder nur einen Scanner erlaubt.
4. Verbinde den gemeinsamen Warenkorb
Erstelle unter Settings → API access einen Marketplace-Key für das Projekt. Gib deinem Checkout-Backend marketplace.read und invoices.write, bei Bedarf auf den Store begrenzt. Rechte zur Adress- und Auszahlungsfreigabe gehören nicht in diesen Key.
Berechne Preise, Rabatte, Steuern und Versand auf deinem Server und ordne sie den Verkäuferanteilen zu. Sende 1–100 unterschiedliche Verkäufer mit positiven Dezimal-Strings; ihre Bruttoanteile müssen genau den Rechnungsbetrag ergeben. Vertraue keiner Aufteilung aus dem Browser und nutze keine Gleitkommazahlen für Geld.
Ersetze unten API-Hostname und UUID-Platzhalter. Lade WHOLLY_TOKEN aus der Serverumgebung. Die Anfrage übernimmt die eingestellten 4% Provision und braucht keine Berechtigung zum Überschreiben der Provision.
Anfrage in cURL, JavaScript, PHP oder Python öffnen
cURL
: "${WHOLLY_TOKEN:?Set WHOLLY_TOKEN to your server-side API token}"
# Keep this key and the exact body for retries; use a new key for each new operation.
curl --fail-with-body --max-time 30 \
--request POST \
--url "https://api.example.com/v1/marketplace/invoices" \
--header "Authorization: Bearer $WHOLLY_TOKEN" \
--header 'Idempotency-Key: cart-1042-marketplace-v1' \
--header 'Content-Type: application/json' \
--data-raw '{
"project_id": "YOUR_PROJECT_ID",
"store_id": "YOUR_STORE_ID",
"amount": "300.00",
"currency": "USD",
"order_id": "cart-1042",
"description": "One order from three vendors",
"ipn_url": "https://shop.example.com/payments/wholly-ipn",
"metadata": {
"cart_id": "cart-1042"
},
"allocations": [
{
"vendor_id": "VENDOR_A_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_B_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_C_UUID",
"gross_amount": "100.00"
}
]
}'JavaScript
// Node.js 18+ · run on your server, never in browser code.
const token = process.env.WHOLLY_TOKEN;
if (!token) throw new Error("Set WHOLLY_TOKEN");
// Keep this key and the exact body for retries; use a new key for each new operation.
const body = `{
"project_id": "YOUR_PROJECT_ID",
"store_id": "YOUR_STORE_ID",
"amount": "300.00",
"currency": "USD",
"order_id": "cart-1042",
"description": "One order from three vendors",
"ipn_url": "https://shop.example.com/payments/wholly-ipn",
"metadata": {
"cart_id": "cart-1042"
},
"allocations": [
{
"vendor_id": "VENDOR_A_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_B_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_C_UUID",
"gross_amount": "100.00"
}
]
}`;
const response = await fetch("https://api.example.com/v1/marketplace/invoices", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Idempotency-Key": "cart-1042-marketplace-v1",
"Content-Type": "application/json"
},
body,
redirect: "error",
signal: AbortSignal.timeout(30000)
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(await response.json());PHP
<?php
// PHP 8+ with the cURL extension; run on your server.
$token = getenv('WHOLLY_TOKEN');
if (!$token) { throw new RuntimeException('Set WHOLLY_TOKEN'); }
// Keep this key and the exact body for retries; use a new key for each new operation.
$body = <<<'JSON'
{
"project_id": "YOUR_PROJECT_ID",
"store_id": "YOUR_STORE_ID",
"amount": "300.00",
"currency": "USD",
"order_id": "cart-1042",
"description": "One order from three vendors",
"ipn_url": "https://shop.example.com/payments/wholly-ipn",
"metadata": {
"cart_id": "cart-1042"
},
"allocations": [
{
"vendor_id": "VENDOR_A_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_B_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_C_UUID",
"gross_amount": "100.00"
}
]
}
JSON;
$ch = curl_init("https://api.example.com/v1/marketplace/invoices");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token, "Idempotency-Key: cart-1042-marketplace-v1", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) { throw new RuntimeException(curl_error($ch)); }
curl_close($ch);
if ($status < 200 || $status >= 300) { throw new RuntimeException("HTTP $status: $response"); }
print_r(json_decode($response, true, 512, JSON_THROW_ON_ERROR));Python
# Python 3 · standard library; run on your server.
import json
import os
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
# Keep this key and the exact body for retries; use a new key for each new operation.
headers = {
"Idempotency-Key": "cart-1042-marketplace-v1",
"Content-Type": "application/json"
}
headers["Authorization"] = "Bearer " + os.environ["WHOLLY_TOKEN"]
body = """{
"project_id": "YOUR_PROJECT_ID",
"store_id": "YOUR_STORE_ID",
"amount": "300.00",
"currency": "USD",
"order_id": "cart-1042",
"description": "One order from three vendors",
"ipn_url": "https://shop.example.com/payments/wholly-ipn",
"metadata": {
"cart_id": "cart-1042"
},
"allocations": [
{
"vendor_id": "VENDOR_A_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_B_UUID",
"gross_amount": "100.00"
},
{
"vendor_id": "VENDOR_C_UUID",
"gross_amount": "100.00"
}
]
}""".encode("utf-8")
request = Request("https://api.example.com/v1/marketplace/invoices",
method="POST", headers=headers, data=body)
# Non-2xx responses raise HTTPError. Do not retry writes with a new key.
with build_opener(NoRedirect()).open(request, timeout=30) as response:
print(json.load(response))Speichere Anfrageinhalt und Idempotency-Key vor dem Senden. Nutze nach einem Timeout denselben Inhalt und denselben Key. Speichere data.invoice_id bei deiner Bestellung und leite zu links.checkout auf der obersten Antwortebene weiter. API-Keys gehören nie in den Kundenbrowser.
Wo findest du Projekt- und Store-UUIDs? →
Lieber ein SDK? Starte mit den Marketplace-Beispielen:
5. Trenne Zahlung und Auszahlung
Die Rückleitung vom Checkout beweist keine Zahlung. Prüfe Callback-Signaturen am unveränderten Body, Zeitstempel und erwartetes Projekt. Speichere event_id eindeutig, bevor du bestätigst. Eine zusätzliche Sperre je Bestellung verhindert doppelte Erfüllung bei Wiederholungen.
| Ereignis | Was es dir sagt |
|---|---|
invoice.settled | Die Kundenrechnung ist abgeschlossen. Prüfe Prüfhinweise und Marketplace-Sperren vor der Erfüllung. |
marketplace.allocations.available | Geprüfte Anteile können ausgezahlt werden. Noch muss kein Verkäufer bezahlt worden sein. |
marketplace.payout.confirmed | Die Auszahlung hat ihre Bestätigungsprüfungen abgeschlossen. |
Rechnungs-IPN nutzt das IPN-Secret des Stores. Marketplace-Webhooks haben ein eigenes Secret je Endpoint. Halte die Empfänger getrennt. Lies bei fehlenden oder vertauschten Ereignissen den aktuellen Rechnungs- oder Auszahlungsstand per API nach.
Rechnungs-Callbacks und Signaturprüfung · Marketplace-Ereignisse und API-Referenz
6. Erst prüfen, dann automatisieren
Sobald geprüfte Anteile verfügbar sind, hebe die Auszahlungspause auf, lass automatische Regeln aber aus. Bereite unter Marketplace → Payouts die Anteile vor, prüfe Empfänger und native Gebühren-/Gaslimits und gib den genauen Plan einmal frei. Verfolge ihn bis Paid, nicht nur Broadcast.
Bitcoin bündelt alle unbezahlten Anteile einer ausgewählten Rechnung. EVM-Token brauchen eventuell zuerst Gas-Finanzierung. Das sind mehrere Transaktionen, keine atomare Alles-oder-nichts-Aufteilung.
Nach einem erfolgreichen kleinen Test richtest du je Asset eine automatische Regel ein: Mindestbetrag, Limits je Auszahlung und Tag, native Gebühren-/Gasbudgets, Intervall und Bestätigungen. Mit dem Aktivieren erlaubst du Senden ohne weitere Klicks. Wenig Verarbeitungsguthaben oder eine Transfersperre des Servers können Ausgaben trotzdem pausieren.
7. Kläre die Sonderfälle
Unterzahlung, Verspätung, gemischte Zahlungen oder Reorgs
Ein abgeschlossener Checkout garantiert keine voll gedeckten Verkäuferanteile. Toleranz erzeugt kein fehlendes Geld. Prüfe gesperrte Zuteilungen, warte auf Nachzahlung, erstatte oder gib ausdrücklich eine kleinere, voll gedeckte Aufteilung frei. Überzahlungen sind nicht automatisch zusätzliche Marktplatzeinnahmen.
Eine Auszahlung hängt oder eine Anfrage läuft ins Timeout
Prüfe gespeicherte Transaktions-Hashes, Quellguthaben, Gas und Prüfgrund. Setze die bestehende Auszahlung fort oder gleiche sie ab. Erstelle keine zweite Überweisung, nur weil die Antwort fehlt. Nach einem Restore bleiben Auszahlungen pausiert, bis Blockchain-Ergebnisse und Journal übereinstimmen.
Der Kunde braucht eine Erstattung
Prüfe eine vom Kunden kontrollierte Erstattungsadresse. Unterstützt sind vollständige Erstattungen im selben Asset. Wurden alle Verkäufer bezahlt, fülle die primäre Wallet separat auf; Wholly Crypto kann nichts von ihnen zurückbuchen. Ein teilweise bezahlter Verkäufer-Batch braucht manuellen Abgleich, keinen vermeintlichen Teil-Erstattungsbutton.
8. Prüfe vor dem Start
- Teste eine kleine BTC- und/oder EVM-Zahlung bis zu bestätigten Verkäuferauszahlungen. Prüfe Chain, Token-Vertrag, Ziele, Provision und separate Gebühren.
- Teste wiederholte Callbacks, einen API-Timeout, eine Unterzahlung und eine gesperrte Auszahlung. Nichts davon darf zu doppelter Erfüllung oder doppeltem Senden führen.
- Bewahre private Backups außerhalb des Servers auf, überwache Auszahlungsfehler und gleiche Verkäuferverpflichtungen regelmäßig mit den Onchain-Mitteln ab.
Starte mit geprüften Auszahlungen und wenigen Verkäufern. Automatisiere nur Assets, Budgets und Ziele, die du getestet hast.