Entwicklerdokumentation

Gutscheine

Geschenkgutscheine mit renderVoucherShop verkaufen, Gäste ihr Guthaben prüfen lassen und Gutscheincodes in Buchungs- und Event-Widget annehmen.

Dieselbe SDK-Datei verkauft die Geschenkgutscheine einer Location und nimmt sie als Zahlung an:

  • renderVoucherShop – die Gutscheinangebote der Location, ein Formular für Käufer und optional Beschenkte und eine Weiterleitung zu Stripe Checkout, darunter eine Guthabenabfrage. Für eine Seite „Gutscheine“.
  • Gutscheinfelder in den Widgets – der Anzahlungsschritt des Buchungs-Widgets und der Ticket-Checkout des Event-Widgets nehmen beide einen Gutscheincode an.
  • Der Headless-Client – client.voucherOffers(), client.checkoutVoucher(), client.voucher() und client.payDeposit() für eigene Seiten, dazu Helfer, die einen Code prüfen, bevor er gesendet wird.

Der Online-Verkauf von Gutscheinen braucht den Premium-Tarif, ein verbundenes Auszahlungskonto und den eingeschalteten Online-Gutscheinverkauf der Location; die Guthabenabfrage funktioniert in jedem Tarif, und ein Gutschein fügt einer Zahlung keine Tarifbedingung hinzu – die Zahlungen, die einen annehmen, haben aber eigene: Eine Anzahlung braucht Anzahlungen und Tickets brauchen Ticketverkauf, beides Premium (Gutscheine).

Den Gutschein-Shop rendern

Laden Sie das SDK wie für das Buchungs-Widget (Installation), fügen Sie ein Element hinzu und rufen Sie renderVoucherShop auf:

<div id="bookdineplay-vouchers"></div>
<script src="https://cdn.bookdineplay.com/sdk/v0/bookdineplay.js"></script>
<script>
  window.BookDinePlay.renderVoucherShop({
    container: '#bookdineplay-vouchers',
    venueSlug: 'your-venue',
    apiBaseUrl: 'https://api.bookdineplay.com',
    publishableKey: 'bdp_pk_your_publishable_key',
    locale: 'de',
    theme: 'auto'
  });
</script>

Der Aufruf liefert die Shop-Instanz oder null, wenn er das Rendern verweigert – aus denselben Gründen wie das Buchungs-Widget: kein Container, kein venueSlug oder apiBaseUrl oder ein Schlüssel, der kein veröffentlichbarer bdp_pk_…-Schlüssel ist (Wenn es nicht rendert). Eine Seite kann den Shop neben einem Buchungs- oder Event-Widget haben; sie teilen Skript und Schlüssel.

Optionen

Option Pflicht Bedeutung
container ja Ein CSS-Selektor oder ein DOM-Element. Der Shop rendert darin.
venueSlug ja Der Slug Ihrer Location.
apiBaseUrl ja https://api.bookdineplay.com. Nur bei einer privaten Installation anders.
publishableKey ja Der bdp_pk_…-Schlüssel der Location. Nie ein geheimer Schlüssel.
locale nein en (Standard) oder de – die eigenen Texte des Shops und das Accept-Language, das er sendet, sodass Angebotstitel, Bedingungen und Problemmeldungen in derselben Sprache zurückkommen. language wird als Synonym angenommen; jedes de-*-Tag zählt als de.
successUrl nein Wohin Stripe Checkout den Käufer nach der Zahlung zurückschickt. Muss https sein und auf einem Origin liegen, den der Schlüssel erlaubt. Ohne Angabe gilt die eingebaute Dankeseite.
cancelUrl nein …nach einem Abbruch des Checkouts. Dieselbe Regel und derselbe Standard.
theme nein auto (folgt der Systemeinstellung des Besuchers, Standard), light oder dark. Der Shop nutzt dieselben --bdp-*-Eigenschaften wie die anderen Widgets (Theming).

Was der Gast sieht

  1. Die Angebote – eine Karte je Produkt, das die Location online verkauft, mit Titel und Preis oder, bei einem Produkt mit freiem Betrag, dem Bereich, aus dem der Käufer wählt. Ein einzelnes Angebot ist schon ausgewählt. Darunter die Gutscheinbedingungen der Location und wie lange ein heute gekaufter Gutschein gilt.
  2. Das Formular – der Betrag bei einem Angebot mit freiem Betrag (zwischen Minimum und Maximum), Name und E-Mail des Käufers und „Es ist ein Geschenk“: Name und E-Mail der beschenkten Person, eine Grußbotschaft (bis 500 Zeichen, mit Zähler) und ein optionales Versanddatum. Ein Versanddatum braucht die E-Mail der beschenkten Person. Ein unsichtbares Honeypot-Feld verwirft Bot-Absendungen stillschweigend.
  3. Checkout – der Shop startet einen Checkout und schickt den Käufer zu Stripe. Er sendet einen Idempotency-Key je Satz von Angaben und verwendet ihn wieder, wenn der Käufer es erneut versucht, sodass ein zweiter Tipp nie eine zweite Zahlung öffnet. Der Gutscheincode wird per E-Mail verschickt, sobald die Zahlung eintrifft: an den Käufer und an die beschenkte Person – am Versanddatum, wenn eines gewählt wurde.
  4. Nicht verfügbar – verkauft die Location keine Gutscheine online, sagt der Shop das, statt Angebote zu zeigen. „Dieses Lokal verkauft keine Gutscheine online“ erscheint nur bei not-offered; jeder andere Grund liest sich als dasselbe „gerade nicht“, sodass ein Gast nie erfährt, welche Einstellung der Location fehlt.

Darunter öffnet in jedem Zustand „Schon einen Gutschein? Guthaben prüfen“ ein Feld für einen Code. Es zeigt Guthaben oder Wert, Ablaufdatum und Status und ruft die schlüssellose Abfrage auf, ohne einen Schlüssel zu senden. Ein vertippter Code fällt vor jeder Anfrage auf.

Der Shop meldet Änderungen an Screenreader (aria-live="polite") und rendert wie die anderen Widgets in einem eigenen Shadow DOM.

Gutscheinfelder in den Widgets

Ticket-Checkout. Das Formular des Event-Widgets für ein bezahltes Event zeigt unter der Summe „Gutschein einlösen?“. Der Gast tippt oder fügt einen Code ein; der Gutschein bezahlt, was er deckt, und die Karte den Rest (Bezahlte Events).

Anzahlungsschritt. Kommt eine Buchung Pending mit einer offenen Anzahlung zurück und meldet das Profil der Location onlineDepositsAvailable, zeigt die Bestätigung des Buchungs-Widgets „Anzahlung bezahlen“ und „Gutschein einlösen?“. Der Kartenteil öffnet die gehostete Seite von Stripe; ein Gutschein, der die ganze Anzahlung deckt, bestätigt die Buchung an Ort und Stelle, ohne Weiterleitung. Das Buchungs-Widget nimmt für diesen Schritt eine Option language (en oder de) an (Buchungsablauf).

An beiden Stellen wird der Code zuerst lokal geprüft, sodass ein Tippfehler keine Anfrage kostet, und dann in kanonischer Form im Request-Body gesendet. Ein abgelehnter Gutschein zeigt einen Satz zu seinem Problem: nicht gefunden, aufgebraucht oder abgelaufen, für diese Zahlung nicht verwendbar (Prozent- und Artikelgutscheine gelten für die Rechnung im Lokal), gerade in Verwendung oder ein Kartenrest unter 0,50 €.

Der Headless-Client

BookDinePlay.createClient (Eigene Formulare) hat vier Gutschein-Aufrufe. Jeder liefert ein Promise des geparsten JSON-Bodys, nimmt { signal } im letzten Argument und lehnt mit einem BookDinePlayError ab (Fehler):

Methode Endpunkt Liefert
client.voucherOffers() GET /api/venues/{venueSlug}/vouchers/offers onlineSaleAvailable, unavailableReason, offers[], terms, validityDescription
client.checkoutVoucher(input, { idempotencyKey }) POST /api/venues/{venueSlug}/vouchers/checkout reference, checkoutUrl, expiresAt – schicken Sie den Käufer zu checkoutUrl
client.voucher(code) GET /api/vouchers/{code} Wert, Guthaben, Ablaufdatum und Status des Gutscheins; null, wenn der Code unbekannt ist, und null ohne Anfrage, wenn er vertippt ist. Es wird kein Schlüssel gesendet
client.payDeposit(input) POST /api/venues/{venueSlug}/payment-intents Der Payment Intent – checkoutUrl, voucherAmount, cardAmount

checkoutVoucher(input) nimmt productId, amount, buyerName, buyerEmail, recipientName, recipientEmail, giftMessage, deliverOn, language, successUrl und cancelUrl; die Rückkehr-URLs fallen auf die eigenen des Clients aus createClient({ successUrl, cancelUrl }) zurück. payDeposit(input) nimmt reservationReference, amount, currency und einen optionalen voucherCode. Auch checkoutEventTickets(eventSlug, input) nimmt einen optionalen voucherCode (Events).

client.voucher() braucht keinen venueSlug; eine Guthabenseite kann ihren Client also allein mit apiBaseUrl und publishableKey erstellen. Die Abfrage ist auf 30 pro Minute und Besucher begrenzt – ein weiterer Grund, den Code zuerst lokal zu prüfen.

const client = window.BookDinePlay.createClient({
  apiBaseUrl: 'https://api.bookdineplay.com',
  publishableKey: 'bdp_pk_your_publishable_key',
  venueSlug: 'your-venue',
  language: 'de'
});

const voucher = await client.voucher(input.value); // null: unbekannt oder vertippt
if (voucher) {
  showBalance(voucher.balance, voucher.currency, voucher.expiresOn, voucher.status);
}

const intent = await client.payDeposit({
  reservationReference: reservation.reference,
  amount: reservation.depositAmount,
  currency: reservation.currency,
  voucherCode: input.value // optional
});
if (intent.cardAmount === 0) {
  showConfirmed(); // der Gutschein hat alles bezahlt
} else {
  window.location.assign(intent.checkoutUrl);
}

Helfer für Gutscheincodes

BookDinePlay.helpers enthält die Code-Regeln der API samt Prüfzeichen, sodass eine Seite einen Tippfehler abfangen kann, bevor sie etwas sendet:

  • parseVoucherCode(input) – ein getippter, eingefügter oder gescannter Code oder eine vollständige URL der Gastseite in kanonischer Form (20 Zeichen, ohne Bindestriche), oder null. Bindestriche und Leerzeichen fallen weg, Groß- und Kleinschreibung zählt nicht, O liest sich als 0 und I oder L als 1.
  • formatVoucherCode(code) – die Anzeigeform, fünf Vierergruppen (K7QM-2XRP-9DTA-HV3N-8W4F), oder '' für alles, was kein Code ist.
  • isValidVoucherCanonical(canonical) – ob 20 Zeichen ein gültiger Code sind.
const canonical = window.BookDinePlay.helpers.parseVoucherCode(field.value);
if (!canonical) {
  field.setCustomValidity('Das sieht nicht nach einem Gutscheincode aus.');
} else {
  field.value = window.BookDinePlay.helpers.formatVoucherCode(canonical);
}

Ein Code ist ein Credential: Wer ihn hat, kann den Gutschein ausgeben. Loggen Sie ihn nicht, setzen Sie ihn in keine URL und schicken Sie ihn an keine Analyse (Der Code ist ein Credential).

Nächste Schritte

  • Gutschein-API – jedes Feld und die Regeln, die jede Gutscheinzahlung teilt.
  • WordPress-Plugin – der Shop als Shortcode.
  • Fehler – die Gutschein-Probleme hinter jedem Satz.