Midas Wallet
Cette page n’est disponible qu’en allemand. La version allemande fait juridiquement foi. Pour toute question, notre support se fera un plaisir de vous aider : support@midaswallet.de
Für Entwickler & Kassenhersteller

Kassen-Connect API

Binde ein Kassensystem in unter einer Stunde an: automatisch stempeln beim Bezahlen, Belohnungen an der Kasse einlösen, Stornos und Tagesabgleich — über eine kleine, stabile REST-API.

Fertige Anbindungen: ready2order, Shopify (Kasse wie Online-Shop), helloCash und KORONA.pos verbindet der Händler ohne eine Zeile Code direkt im Dashboard.

Schnellstart

  1. Der Ladenbesitzer erstellt im Dashboard unter Kassen-Connect einen POS-Token (mw_pos_…) — pro Kasse bzw. Filiale ein eigener Token.
  2. Deine Kasse ruft nach jedem Bezahlvorgang POST /v1/pos/transaction mit Seriennummer (QR der Kundenkarte), Bon-Betrag und Bon-Nr. auf.
  3. Fertig — Stempel-Regeln (1/Bon oder nach Umsatz, Deckel) pflegt der Händler selbst im Dashboard. Zum Testen gibt es dort einen eingebauten Simulator gegen die echte API.
Erster Request
curl -X POST https://midaswallet.de/api/v1/pos/transaction \
  -H "Authorization: Bearer mw_pos_DEIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "passId": "MW-1234-5678",
    "amountCents": 2350,
    "externalRef": "BON-2026-000482"
  }'

Basis-URL: https://midaswallet.de/api · Alle Antworten sind JSON (UTF-8). Beträge immer in Cent.

Authentifizierung

Jeder Request trägt den POS-Token — wahlweise in einem von zwei Headern (für Kassensysteme, die den Authorization-Header nicht frei setzen können):

Standard
Authorization: Bearer mw_pos_…
Alternative
X-Pos-Key: mw_pos_…
  • Tokens sind pro Laden (und optional pro Filiale) — sie sehen nie Daten anderer Läden.
  • Der Händler kann Tokens jederzeit im Dashboard widerrufen; widerrufene Tokens antworten mit 401.
  • Mit GET /v1/pos/ping validierst du Token + Verbindung, bevor der Händler das Feature aktiviert hat.

Sandbox & Testen

Für die Entwicklung erstellt der Ladenbesitzer im Dashboard einen Sandbox-Token (mw_pos_test_…). Er nutzt exakt dieselben Endpunkte wie ein echter Token, aber:

  • Er bucht ausschließlich auf die auto-provisionierte Test-Karte des Ladens — die Seriennummer liefert GET /v1/pos/ping im Feld testPass. Echte Kundenkarten sind für Sandbox-Token unsichtbar.
  • Er funktioniert vor dem Go-Live: der Aktivierungs-Schalter des Händlers wird übersprungen.
  • Test-Buchungen laufen in einem eigenen Idempotenz-Raum und tauchen weder in der Umsatz-Statistik des Händlers noch im Abgleich echter Tokens auf — eine in der Sandbox verbrauchte Bon-Nr. blockiert nie die spätere echte Buchung.
  • GET /v1/pos/transactions und GET /v1/pos/summary zeigen einem Sandbox-Token nur seine Sandbox-Bewegungen.

Zum manuellen Ausprobieren gibt es im Händler-Dashboard außerdem einen Simulator gegen die echte API.

Endpunkte

GET/v1/pos/ping

Verbindungs- & Token-Test

Prüft Token und Verbindung, liefert Laden, Kassen-Standort und die aktive Stempel-Regel. Funktioniert auch, bevor der Händler Kassen-Connect scharf schaltet (Status steht in config.enabled) — ideal für den Einrichtungs-Assistenten deiner Kasse.

Antwort 200
{
  "ok": true,
  "apiVersion": "1.3",
  "serverTime": "2026-07-06T14:03:11.000Z",
  "merchant": { "id": "m_abc123", "name": "Döner-Palast" },
  "location": { "id": "loc_1", "name": "Hauptfiliale" },
  "sandbox": false,
  "testPass": null,
  "config": { "enabled": true, "centsPerStamp": 500, "maxStampsPerTransaction": 10 }
}
GET/v1/pos/pass/{passId}

Kundenkarte abfragen (Kassendisplay)

Liest Saldo, fehlende Stempel und Belohnungs-Status, ohne etwas zu buchen — z. B. um an der Kasse „Noch 2 Stempel bis zur Belohnung“ anzuzeigen. passId akzeptiert die gescannte Seriennummer (QR-Inhalt) oder die interne Pass-ID.

Antwort 200
{
  "passId": "p_xyz789",
  "serialNumber": "MW-1234-5678",
  "status": "ACTIVE",
  "customerName": "Max M.",
  "balance": 8,
  "pointsRequiredForReward": 10,
  "stampsToReward": 2,
  "rewardAvailable": false,
  "rewardDescription": "1 Döner gratis",
  "stage": null,
  "tier": { "level": 2, "name": "Silber" },
  "lifetimeStamps": 43,
  "lastVisitAt": "2026-06-28T11:42:00.000Z"
}
GET/v1/pos/pass?phone=…

Kundenkarte ohne Scan abfragen (Handynummer/E-Mail)

Neu in v1.4: Der Kunde nennt an der Kasse seine Handynummer (oder E-Mail), statt den Karten-QR zu zeigen — ?phone= oder ?email=. Telefonnummern dürfen in jeder üblichen Schreibweise kommen (serverseitig E.164-normalisiert, ohne Ländervorwahl gilt +49); aufgelöst wird die neueste aktive Karte des Ladens. Dieselben Felder customerPhone/customerEmail funktionieren auch direkt in transaction, transactions/batch und redeem als Ersatz für passId.

Antwort 200
{
  "passId": "p_xyz789",
  "serialNumber": "MW-1234-5678",
  "status": "ACTIVE",
  "customerName": "Max M.",
  "balance": 8,
  "stampsToReward": 2,
  "rewardAvailable": false
}
POST/v1/pos/enroll

Karte direkt an der Kasse ausstellen (v1.6)

Schließt die Lücke „Kunde hat noch keine Karte": Handynummer (oder E-Mail) rein, fertige Stempelkarte raus. Die Antwort enthält den Google-Wallet- saveUrl — als QR-Code unten auf den Kassenbon drucken oder aufs Kundendisplay legen; der Kunde fügt die Karte mit einem Scan hinzu. Optional wird der aktuelle Bon direkt mitgebucht (gleiche Felder wie /transaction). Idempotent: existiert im Laden bereits eine Karte zu dieser Nummer/E-Mail, kommt dieselbe Karte mit frischem Save-Link zurück (created: false).

Request-Body
{
  "customerPhone": "0151 23456789",
  "displayName": "Anna",
  "amountCents": 1250,
  "externalRef": "BON-2026-000483"
}
Antwort 200
{
  "created": true,
  "passId": "p_abc123",
  "serialNumber": "MW-1234-5678",
  "saveUrl": "https://pay.google.com/gp/v/save/eyJhbGciOi…",
  "googleLive": true,
  "transaction": { "stampsAdded": 2, "newBalance": 2, "duplicate": false }
}
POST/v1/pos/transaction

Stempel aus einem Kassenbon gutschreiben

Rechnet den Bon-Betrag nach der im Dashboard konfigurierten Regel in Stempel um (klassisch 1 Stempel/Bon oder betragsabhängig, mit Deckel) und schreibt sie gut. Die Wallet-Karte des Kunden aktualisiert sich automatisch per Push. externalRef (Bon-Nr.) immer mitschicken: macht die Buchung idempotent und ist Voraussetzung für ein späteres Storno. Die Bon-Positionen (items, optional) ebenfalls immer mitschicken: hat der Händler Warengruppen-Regeln aktiviert (z. B. „nur Kaffee zählt", „Pfand zählt nicht"), rechnet die API nur die Summe der passenden Positionen in Stempel um — ohne items zählt der volle Betrag. Bei aktiver Produkt-Stempel-Regel (v1.5) gilt: Stempel = Anzahl der passenden Positionen — die optionale quantity je Position zählt mehrfach (2× Cappuccino = 2 Stempel); ohne items gibt es dann 1 Stempel je Bon. Mit redeemIfAvailable: true (v1.6) wird eine voll werdende Karte direkt in derselben Buchung eingelöst — die Antwort trägt dann autoRedeem. Statt passId geht seit v1.4 auch customerPhone oder customerEmail — für Kassen ohne Scanner. Optional kann die Kasse eine Kassierer-Kennung cashierRef (v1.7) mitschicken — sie fließt in das Missbrauchs-Radar des Händlers und in den Transaktions-Audit, hat aber keine Wirkung auf die Buchung.

Request-Body
{
  "passId": "MW-1234-5678",
  "amountCents": 2350,
  "externalRef": "BON-2026-000482",
  "items": [
    { "name": "Cappuccino groß", "totalCents": 840, "quantity": 2 },
    { "name": "Pfand Mehrwegbecher", "totalCents": 100 }
  ]
}
Antwort 200
{
  "passId": "p_xyz789",
  "newBalance": 9,
  "stampsAdded": 4,
  "pointsRequiredForReward": 10,
  "stampsToReward": 1,
  "rewardAvailable": false,
  "rewardTriggered": false,
  "duplicate": false
}
POST/v1/pos/transactions/batch

Offline-/Batch-Nachlieferung (bis 100 Buchungen)

Liefert nach einem Verbindungsabbruch mehrere Bons in einem Aufruf nach. Jeder Eintrag entspricht einer transaction-Buchung, optional mit occurredAt (Original- Verkaufszeitpunkt, max. 30 Tage alt) — Abgleich und Tages-Statistik zeigen dann den echten Zeitpunkt. Fehler einzelner Einträge brechen den Batch nicht ab, sondern stehen als Per-Item-Fehler im Ergebnis. Mit externalRef je Eintrag ist der komplette Batch beliebig wiederholbar.

Request-Body
{
  "transactions": [
    {
      "passId": "MW-1234-5678",
      "amountCents": 2350,
      "externalRef": "BON-2026-000482",
      "occurredAt": "2026-07-05T18:42:00+02:00"
    },
    {
      "passId": "MW-1234-5678",
      "amountCents": 990,
      "externalRef": "BON-2026-000483"
    }
  ]
}
Antwort 200
{
  "results": [
    { "index": 0, "ok": true, "transaction": { "newBalance": 9, "stampsAdded": 4, "duplicate": false } },
    { "index": 1, "ok": false, "error": { "status": 404, "message": "Pass in diesem Laden nicht gefunden" } }
  ],
  "succeeded": 1,
  "failed": 1
}
GET/v1/pos/summary

Tagesabschluss (Z-Bericht) als fertige Aggregat-Zeile

Aggregiert alle Kassen-Connect-Bewegungen eines Berlin-Kalendertags — Bons, Brutto-/ Netto-Umsatz (nach Storno), Ø-Bon, Stempel, Einlösungen, Kunden. Query-Parameter day (YYYY-MM-DD, Default heute). Erspart deiner Kasse das Selbst-Aggregieren der transactions-Liste.

Antwort 200
{
  "day": "2026-07-05",
  "sandbox": false,
  "receipts": 42,
  "revenueCents": 61250,
  "netRevenueCents": 58900,
  "voidedReceipts": 1,
  "avgReceiptCents": 1458,
  "stampsIssued": 118,
  "stampsReversed": 4,
  "rewardsRedeemed": 7,
  "customers": 31
}
POST/v1/pos/redeem

Belohnung an der Kasse einlösen

Zieht die volle Belohnungs-Schwelle vom Pass ab und liefert die Beschreibung der eingelösten Belohnung fürs Kassendisplay oder den Bon. Vorher per Karten-Abfrage prüfen, ob rewardAvailable ist — bei zu wenig Stempeln antwortet die API mit 400, ohne etwas zu buchen.

Request-Body
{
  "passId": "MW-1234-5678",
  "externalRef": "BON-2026-000483"
}
Antwort 200
{
  "passId": "p_xyz789",
  "serialNumber": "MW-1234-5678",
  "redeemed": true,
  "rewardDescription": "1 Döner gratis",
  "newBalance": 0,
  "pointsRequiredForReward": 10,
  "duplicate": false
}
POST/v1/pos/void

Storno einer Stempel-Buchung

Bucht die Stempel einer früheren Buchung zurück — referenziert über deren externalRef. Der Betrag wird auf den aktuellen Saldo geklemmt: hat der Kunde zwischenzeitlich eingelöst, rutscht er nie unter 0. Idempotent — ein zweites Storno derselben Referenz bewegt nichts mehr. Teilstorno (v1.7): mit amountCents (erstatteter Betrag) wird anteilig zurückgenommen — der behaltene Betrag „verdient" floor-anteilig Stempel, mehrere Teilstornos kumulieren. Bei Teilstornos refundRef je Erstattung mitschicken (Idempotenz bei Retries); ohne refundRef dedupliziert der Server über den Betrag.

Request-Body
{
  "externalRef": "BON-2026-000482",
  "reason": "1 Getränk zurückgegeben",
  "amountCents": 450,
  "refundRef": "REF-2026-000107"
}
Antwort 200
{
  "passId": "p_xyz789",
  "voided": true,
  "originalStamps": 4,
  "stampsReversed": 1,
  "newBalance": 8,
  "duplicate": false,
  "partial": true
}
GET/v1/pos/transactions

Abgleich-Liste (Tagesabschluss / Z-Bericht)

Alle über Kassen-Connect entstandenen Bewegungen des Ladens (Stempel, Einlösungen, Stornos), neueste zuerst, cursor-paginiert. Query-Parameter: since, until (ISO 8601), limit (max. 500), cursor.

Antwort 200
{
  "items": [
    {
      "id": "t_001",
      "passId": "p_xyz789",
      "type": "EARN",
      "kind": "earn",
      "pointsDelta": 4,
      "balanceAfter": 9,
      "externalRef": "BON-2026-000482",
      "amountCents": 2350,
      "createdAt": "2026-07-02T13:58:00.000Z"
    }
  ],
  "nextCursor": null
}

Webhooks: Midas ruft deine Kasse

Optional pusht Midas Ereignisse an eine URL, die der Ladenbesitzer im Dashboard unter Kassen-Connect → Webhook hinterlegt — z. B. damit das Kassendisplay von sich aus „Belohnung verfügbar!“ anzeigen kann:

  • reward_available — eine Karte hat soeben die Belohnungs-Schwelle erreicht (egal ob per Kasse, Staff-Scan oder Bonus).
  • reward_redeemed — eine Belohnung wurde eingelöst.
  • pass_created — ein Kunde hat eine neue Karte erstellt.

Jede Zustellung ist HMAC-signiert (Header X-Midas-Signature, Secret whsec_… aus dem Dashboard). Antworte mit einer 2xx; sonst folgt genau ein Wiederholversuch nach 30 Sekunden — dedupe deshalb über die Event-ID. Zustellungen sind best-effort und blockieren nie eine Buchung; die API bleibt die verbindliche Wahrheit.

Beispiel-Event
{
  "id": "evt_5f0c2e7a-…",
  "type": "reward_available",
  "createdAt": "2026-07-06T14:07:31.000Z",
  "sandbox": false,
  "merchantId": "m_abc123",
  "data": {
    "passId": "p_xyz789",
    "serialNumber": "MW-1234-5678",
    "customerName": "Max M.",
    "balance": 10,
    "threshold": 10,
    "stampsToReward": 0,
    "rewardDescription": "1 Döner gratis"
  }
}
Signatur prüfen (Node.js / Express)
// Express-Beispiel: Signatur prüfen (Raw-Body nötig!)
const crypto = require('node:crypto');
const SECRET = process.env.MIDAS_WEBHOOK_SECRET; // whsec_… aus dem Dashboard

app.post('/midas-hook', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.header('X-Midas-Signature') ?? ''; // t=<unix>,v1=<hex>
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header);
  if (!m) return res.sendStatus(400);

  const [, t, v1] = m;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.sendStatus(400); // Replay

  const expected = crypto.createHmac('sha256', SECRET)
    .update(`${t}.${req.body}`).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v1, 'hex'))) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body); // { id, type, sandbox, data: { serialNumber, … } }
  // event.id dedupen — bei Zustell-Retry kommt dieselbe id erneut.
  res.sendStatus(200);
});

Idempotenz, Retries & Storno

Schicke bei jeder Buchung die Bon-Nr. als externalRef mit. Sie ist der Idempotenz-Schlüssel: derselbe externalRef bucht nie doppelt — Netzwerk-Timeouts kannst (und sollst) du also gefahrlos mit einem identischen Retry beantworten. Die Antwort auf einen Retry trägt "duplicate": true und das Ursprungs-Ergebnis.

externalRef ist außerdem der Anker fürs Storno: POST /v1/pos/void bucht die Stempel des referenzierten Bons zurück. Die Rückbuchung wird auf den aktuellen Saldo geklemmt — ein Kunde, der zwischen Kauf und Storno bereits eingelöst hat, rutscht nie ins Minus. Buchungen ohne externalRef sind nicht stornierbar.

Stempeln (transaction) und Einlösen (redeem) nutzen getrennte Idempotenz-Räume — dieselbe Bon-Nr. darf also in einem Bezahlvorgang sowohl eine Einlösung als auch neue Stempel tragen.

Fehlercodes

StatusBedeutungEmpfohlene Reaktion
400Validierungsfehler (Body/Query) oder nicht genug Stempel beim EinlösenRequest korrigieren; bei redeem vorher rewardAvailable prüfen
401POS-Token fehlt, ist ungültig oder widerrufenToken im Dashboard prüfen/neu erstellen; Header-Format kontrollieren
403Kassen-Connect nicht aktiviert ODER merchantId passt nicht zum TokenHändler aktiviert das Feature im Dashboard unter „Kassen-Connect“
404Pass bzw. externalRef in diesem Laden nicht gefundenSeriennummer prüfen — Pässe anderer Läden sind bewusst unsichtbar
5xxServerfehlerMit gleichem externalRef erneut senden — Retries buchen nie doppelt

Fehler-Antworten folgen dem üblichen Schema { "statusCode": 403, "message": "…" }.

Code-Beispiele

Node.js (fetch)
const BASE = 'https://midaswallet.de/api';
const TOKEN = process.env.MIDAS_POS_TOKEN; // mw_pos_…

async function stampReceipt(passId, amountCents, receiptNo) {
  const res = await fetch(`${BASE}/v1/pos/transaction`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ passId, amountCents, externalRef: receiptNo }),
  });
  if (!res.ok) throw new Error(`Midas Wallet: HTTP ${res.status}`);
  return res.json(); // { newBalance, stampsAdded, rewardAvailable, … }
}
PHP (curl)
<?php
function midasStampReceipt(string $passId, int $amountCents, string $receiptNo): array {
    $ch = curl_init('https://midaswallet.de/api/v1/pos/transaction');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . getenv('MIDAS_POS_TOKEN'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'passId'      => $passId,
            'amountCents' => $amountCents,
            'externalRef' => $receiptNo, // Bon-Nr. => idempotent + stornierbar
        ]),
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    if ($status !== 200) throw new RuntimeException("Midas Wallet: HTTP $status");
    return json_decode($body, true);
}

Du baust eine Kassen-Integration?

Wir unterstützen Kassenhersteller und Agenturen gerne direkt — mit Test-Zugängen, Review der Anbindung und einem kurzen Draht bei Fragen.

API Kassen-Connect pour développeurs — Midas Wallet