Zum Inhalt springen
rdlabo.devdocs

Eine Zahlung abwickeln

Wickeln Sie eine Zahlung vor Ort mit Stripe Terminal ab: Registrieren Sie die Listener frühzeitig, initialisieren Sie das Plugin, verbinden Sie ein Lesegerät und bestätigen Sie einen PaymentIntent.

Voraussetzungen für den ersten Test

Bereiten Sie vor dem ersten Zahlungsversuch Folgendes vor:

  • Die Plattformeinstellungen aus Konfiguration, einschließlich der erforderlichen Android-Berechtigungen
  • Einen authentifizierten Endpunkt für Verbindungstoken, den Ihre App aufrufen kann
  • Einen auf Ihrem Server erstellten Test-PaymentIntent mit card_present
  • Eine Stripe-Terminal-locationId, die zum gesuchten Verbindungstyp passt

Der erste erfolgreiche Ablauf ist: Lesegerät verbinden → Zahlungsmethode erfassen → PaymentIntent bestätigen und ConfirmedPaymentIntent empfangen. Die Auftragsabwicklung muss weiterhin auf Ihren Stripe-Webhook warten. Verwenden Sie für simulierte Lesegeräte einen unterstützten Verbindungstyp mit isTest: true, wie unter Konfiguration beschrieben. TerminalConnectTypes.Simulated ist nicht universell einsetzbar.

Listener auf Anwendungsebene registrieren

Registrieren Sie Terminal-Ereignislistener einmal pro Start der JavaScript-Anwendung, möglichst früh beim Bootstrap, etwa in main.ts, einem Anwendungsinitialisierer oder einem beim Start initialisierten Singleton-Service, und vor jeder Initialisierung oder Operation. Lassen Sie sie für die gesamte Lebensdauer ihrer zuständigen Instanz auf Anwendungsebene registriert.

enum TerminalEventsEnum

Mitglied Wert
Loaded 'terminalLoaded'
DiscoveredReaders 'terminalDiscoveredReaders'
DiscoveringReaders 'terminalDiscoveringReaders'
CancelDiscoveredReaders 'terminalCancelDiscoveredReaders'
ConnectedReader 'terminalConnectedReader'
DisconnectedReader 'terminalDisconnectedReader'
ConnectionStatusChange 'terminalConnectionStatusChange'
UnexpectedReaderDisconnect 'terminalUnexpectedReaderDisconnect'
ConfirmedPaymentIntent 'terminalConfirmedPaymentIntent'
CollectedPaymentIntent 'terminalCollectedPaymentIntent'
Canceled 'terminalCanceled'
Failed 'terminalFailed'
RequestedConnectionToken 'terminalRequestedConnectionToken'
ReportAvailableUpdate 'terminalReportAvailableUpdate'
StartInstallingUpdate 'terminalStartInstallingUpdate'
ReaderSoftwareUpdateProgress 'terminalReaderSoftwareUpdateProgress'
FinishInstallingUpdate 'terminalFinishInstallingUpdate'
BatteryLevel 'terminalBatteryLevel'
ReaderEvent 'terminalReaderEvent'
RequestDisplayMessage 'terminalRequestDisplayMessage'
RequestReaderInput 'terminalRequestReaderInput'
PaymentStatusChange 'terminalPaymentStatusChange'
ReaderReconnectStarted 'terminalReaderReconnectStarted'
ReaderReconnectSucceeded 'terminalReaderReconnectSucceeded'
ReaderReconnectFailed 'terminalReaderReconnectFailed'

Die typisierten Überladungen von addListener decken die meisten dieser Einträge ab. DiscoveringReaders und CancelDiscoveredReaders werden beim Start und Abbruch der nativen Suche ausgelöst, haben aber keine eigenen Überladungen; siehe die Seite API.

Initialisieren

Bevorzugen Sie eine authentifizierte Anfrage aus der App über RequestedConnectionToken und setConnectionToken. So kann Ihre App ihre üblichen Autorisierungsdaten mitsenden und Fehler prüfen. Registrieren Sie den Listener vor initialize; das Terminal SDK fordert bei Bedarf jeweils ein neues Verbindungstoken zur einmaligen Verwendung an. Setzen Sie während der Entwicklung isTest.

method initialize(...)

Initialisiert das Stripe Terminal SDK und seinen Anbieter für Verbindungstoken.
Vor der Lesegerätesuche einmal aufrufen.

Wenn tokenProviderEndpoint angegeben ist, sendet das Plugin eine POST-Anfrage
und erwartet { secret: string }. Wenn der Wert fehlt, stattdessen
RequestedConnectionToken behandeln und setConnectionToken() aufrufen.

initialize(options: StripeTerminalInitializationOptions) => Promise<void>

Im Web erfordert initialize eine neue Plugin-Instanz: Ein erneuter Aufruf nach erfolgreicher Initialisierung löst Stripe Terminal has already been initialized aus.

Ein Verbindungstoken sicher bereitstellen

Lassen Sie tokenProviderEndpoint weg und registrieren Sie RequestedConnectionToken vor initialize. Benötigt das SDK ein Token, löst das Plugin dieses Ereignis aus und wartet auf setConnectionToken({ token }).

Rufen Sie das Token mit Ihrem üblichen Autorisierungsmechanismus ab, verlangen Sie eine erfolgreiche Antwort, prüfen Sie secret und übergeben Sie es als token. Rufen Sie setConnectionToken nur auf, während ein Tokenabruf aussteht; Android und iOS weisen zusätzliche Aufrufe mit Stripe Terminal do not pending fetchConnectionToken zurück. Protokollieren Sie niemals die Antwort oder das Token.

method setConnectionToken(...)

Übergibt ein Verbindungstoken-Secret, nachdem RequestedConnectionToken
ausgelöst wurde. Jeden Token auf dem Server erstellen und nur einmal verwenden.

setConnectionToken(options: SetConnectionTokenOptions) => Promise<void>

Kompatibilitätsmodus tokenProviderEndpoint

tokenProviderEndpoint steht für einfache Bereitstellungen zur Verfügung. Die nativen Clients der Version v8.2.1 senden jedoch einen einfachen HTTP-POST: Aufrufer können weder einen Autorisierungsheader noch einen Anfragekörper hinzufügen. Verwenden Sie ihn nur, wenn Ihr Server die Anfrage auf anderem Weg authentifizieren und absichern kann. Stellen Sie niemals einen uneingeschränkt öffentlichen Endpunkt zur Tokenerstellung bereit.

Ist tokenProviderEndpoint gesetzt, sendet das Plugin einen HTTP-POST mit leerem Anfragekörper. Die Antwort muss JSON mit einer Zeichenfolge secret sein:

{ "secret": "pst_..." }

Dieser Wert ist ein Stripe-Terminal-Verbindungstoken. Erstellen Sie es auf dem Server mit Ihrem geheimen API-Schlüssel (stripe.terminal.connectionTokens.create()). Hinterlegen Sie den geheimen Schlüssel, eingeschränkte Schlüssel mit Berechtigung zur Tokenerstellung oder unverarbeitete Verbindungstoken niemals im App-Binary, in Protokollen oder in einer öffentlichen Clientkonfiguration.

Die offizielle Demo stellt POST /connection/token bereit und gibt { secret } zurück; passen Sie deren Authentifizierung und Autorisierung an Ihre Anwendung an.

Einen PaymentIntent im Backend erstellen

Erstellen Sie den PaymentIntent auf Ihrem Server. Die offizielle Demo verwendet POST /connection/intent und gibt { paymentIntent } als Client-Secret zurück.

Voraussetzungen passend zum Plugin und zur Demo:

  • payment_method_types muss card_present enthalten
  • Bewahren Sie den geheimen Stripe-Schlüssel auf dem Server auf
  • Übergeben Sie nur das Client-Secret an collectPaymentMethod({ paymentIntent })
  • Erstellen oder bestätigen Sie keine PaymentIntents für Zahlungen mit physisch vorliegender Karte mit einem veröffentlichbaren Schlüssel in der App

Serverbeispiel aus der Demo:

await stripe.paymentIntents.create({
  amount: 1000,
  currency: 'usd',
  payment_method_types: ['card_present'],
  capture_method: 'automatic',
});

Lesegeräte suchen

Suchen Sie nach Lesegeräten in der Nähe oder nach simulierten Lesegeräten. Geben Sie einen Wert aus TerminalConnectTypes und eine Stripe-Terminal-locationId an, sofern der Verbindungstyp sie benötigt.

locationId wird bei der Internetsuche verwendet und ist beim Verbinden von Tap-to-Pay-, Bluetooth- und Android-USB-Lesegeräten erforderlich. Die Internetsuche kann nach Standort filtern; Tap to Pay und Bluetooth übernehmen den Standort in die Verbindungskonfiguration.

Besonderheiten:

  • Web unterstützt nur Internet. Alle anderen Werte für type sind nicht verfügbar.
  • iOS Bluetooth meldet Lesegeräte mehrfach über DiscoveredReaders, während sich die Suche aktualisiert. Siehe Stripe: Ein Bluetooth-Lesegerät verbinden (iOS). Setzen Sie bluetoothScanWaitTime (in Millisekunden), damit discoverReaders vor der Auflösung mit der aktuellen Liste wartet. Bei 0 oder ohne Angabe wird das erste Suchergebnis zurückgegeben.
  • iOS löst beim Suchstart außerdem DiscoveringReaders aus. USB, HandOff und Simulated als type sind nicht implementiert.
  • Android benötigt ACCESS_FINE_LOCATION zur Laufzeit; andernfalls wird discoverReaders abgewiesen. Simulated wird als Bluetooth-Suche behandelt. HandOff entspricht Apps on Devices.
  • Rufen Sie cancelDiscoverReaders auf, wenn der Nutzer die Suchoberfläche verlässt. Im Web hat der Abbruch keine Wirkung. Geben Sie dem Nutzer immer eine Möglichkeit, eine lange Bluetooth-Suche zu beenden.

Hören Sie zusätzlich zum Warten auf das Promise auf DiscoveredReaders. Unter iOS mit Bluetooth liefert der Listener die laufend aktualisierte Liste; das Promise kann bereits vor dem letzten Ereignis aufgelöst werden.

method discoverReaders(...)

Sucht Lesegeräte mit der gewünschten Verbindungsart. Die zurückgegebenen Lesegeräte sind
Momentaufnahmen; auf DiscoveredReaders reagieren, wenn die fortlaufende Suche
weitere Ergebnisse liefern kann.

discoverReaders(options: DiscoverReadersOptions) => Promise<{ readers: ReaderInterface[]; }>

interface DiscoverReadersOptions

Eigenschaft Typ Beschreibung Seit
type TerminalConnectTypes Zu verwendende Suchmethode und Verbindungsart des Lesegeräts. 5.1.0
locationId string Stripe Terminal Location ID zur Eingrenzung der Suche nach Internetlesegeräten und zur Lesegeräteregistrierung, soweit erforderlich. 5.1.0
bluetoothScanWaitTime number Gilt nur für die Bluetooth-Suche (nur iOS). Während der Suche werden Lesegeräte über DiscoveryDelegate.didUpdateDiscoveredReaders gemeldet. Dieses Zeitlimit bestimmt, wie lange gewartet wird, bevor die Methode discoverReaders mit der aktuellen Liste aufgelöst wird. Wenn die Einstellung fehlt oder 0 ist, werden die ersten Suchergebnisse zurückgegeben. 7.2.0

enum TerminalConnectTypes

Mitglied Wert
Simulated 'simulated'
Internet 'internet'
Bluetooth 'bluetooth'
Usb 'usb'
TapToPay 'tap-to-pay'
HandOff 'hand-off'

Ein Lesegerät verbinden

Verbinden Sie eines der gefundenen Lesegeräte, bevor Sie Zahlungsdaten erfassen. Das Objekt reader muss aus dem aktuellen Suchergebnis stammen (serialNumber ist die primäre Kennung des Plugins).

autoReconnectOnUnexpectedDisconnect ist standardmäßig false und wird für Tap to Pay und Bluetooth angewendet. Android USB aktiviert derzeit die automatische Wiederverbindung in der nativen Verbindungskonfiguration. Internetverbindungen verwenden dieses Flag nicht.

merchantDisplayName und onBehalfOf gelten für iOS Tap to Pay (LocalMobileReader). Unter Android setzen Sie die Angaben zum verbundenen Konto und zur Anzeige stattdessen am PaymentIntent.

method connectReader(...)

Verbindet ein von discoverReaders() zurückgegebenes Lesegerät.

connectReader(options: ConnectReaderOptions) => Promise<void>

Eine Zahlungsmethode erfassen

Übergeben Sie das Client-Secret des PaymentIntent aus Ihrem Backend an collectPaymentMethod. Das Plugin ruft diesen PaymentIntent ab und erfasst anschließend die Zahlungsmethode am verbundenen Lesegerät.

method collectPaymentMethod(...)

Erfasst eine Zahlungsmethode für einen serverseitig erstellten PaymentIntent. Den
erfassten Intent mit confirmPaymentIntent() bestätigen.

collectPaymentMethod(options: CollectPaymentMethodOptions) => Promise<void>

Den PaymentIntent bestätigen

Verarbeiten und bestätigen Sie den PaymentIntent, dessen Zahlungsmethode erfasst wurde. confirmPaymentIntent wird abgewiesen, wenn die Erfassung zuvor nicht erfolgreich war (PaymentIntent not found for confirmPaymentIntent).

method confirmPaymentIntent()

Bestätigt den zuletzt vom Lesegerät erfassten PaymentIntent.

confirmPaymentIntent() => Promise<void>

ConfirmedPaymentIntent ist ein Signal für die Clientoberfläche und keine Freigabe zur Auftragsabwicklung. Wickeln Sie den Auftrag erst ab, nachdem Ihr Backend einen Stripe-Webhook wie payment_intent.succeeded geprüft hat.

Abbruch und Fehler behandeln

  • cancelCollectPaymentMethod bricht eine laufende Erfassung ab. Bei Erfolg wird das Promise aufgelöst und Canceled ausgelöst.
  • Failed wird ausgelöst, wenn collectPaymentMethod oder confirmPaymentIntent fehlschlägt. Das Promise desselben Aufrufs wird ebenfalls abgewiesen. Die Nutzdaten können message, code und declineCode enthalten.
  • Verwenden Sie ConnectionStatusChange nicht zur Erkennung unerwarteter Verbindungsabbrüche. Nutzen Sie UnexpectedReaderDisconnect und bei Bluetooth/USB zusätzlich DisconnectedReader. Siehe Lebenszyklus des Lesegeräts.

method cancelCollectPaymentMethod()

Bricht einen laufenden Aufruf von collectPaymentMethod() ab.

cancelCollectPaymentMethod() => Promise<void>

Das Lesegerät trennen

Trennen Sie das Lesegerät, wenn der Zahlungsablauf abgeschlossen ist oder das Lesegerät nicht mehr benötigt wird.

method disconnectReader()

Trennt die Verbindung zum aktiven Lesegerät. Wird sofort aufgelöst, wenn keines verbunden ist.

disconnectReader() => Promise<void>

Nach dem ersten erfolgreichen Ablauf

Siehe Lebenszyklus des Lesegeräts für Trennen, Wiederverbinden und Updates. Zum Akzeptieren von Zahlungen mit dem Telefon als Lesegerät siehe Tap to Pay. Die formalen Signaturen bleiben auf der Seite API.

collect-payment.ts
import {
  StripeTerminal,
  TerminalConnectTypes,
  TerminalEventsEnum,
} from '@capacitor-community/stripe-terminal';

const paymentStatusListener = await StripeTerminal.addListener(
  TerminalEventsEnum.PaymentStatusChange,
  ({ status }) => console.log(status),
);
const confirmedListener = await StripeTerminal.addListener(
  TerminalEventsEnum.ConfirmedPaymentIntent,
  () => console.log('Payment processed; waiting for the server webhook'),
);
const failedListener = await StripeTerminal.addListener(
  TerminalEventsEnum.Failed,
  (error) => console.error(error),
);

// Register the authenticated RequestedConnectionToken provider first.
await StripeTerminal.initialize({ isTest: true });

const { readers } = await StripeTerminal.discoverReaders({
  type: TerminalConnectTypes.TapToPay,
  locationId: '**************',
});

const reader = readers[0];
if (!reader) throw new Error('No compatible reader found');

await StripeTerminal.connectReader({
  reader,
});

try {
  const response = await fetch('https://example.com/connection/intent', {
    method: 'POST',
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  if (!response.ok) throw new Error(`PaymentIntent request failed: ${response.status}`);
  const { paymentIntent } = (await response.json()) as { paymentIntent: string };

  await StripeTerminal.collectPaymentMethod({ paymentIntent });
  await StripeTerminal.confirmPaymentIntent();
} finally {
  await StripeTerminal.disconnectReader();
}

// Remove the three listeners when their application-level owner is destroyed.

connection-token.ts
import { StripeTerminal, TerminalEventsEnum } from '@capacitor-community/stripe-terminal';

await StripeTerminal.addListener(
  TerminalEventsEnum.RequestedConnectionToken,
  async () => {
    try {
      const response = await fetch('https://example.com/connection/token', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
        },
      });
      if (!response.ok) throw new Error(`Connection token request failed: ${response.status}`);
      const data = (await response.json()) as { secret?: unknown };
      if (typeof data.secret !== 'string' || !data.secret) {
        throw new Error('Connection token response is missing secret');
      }
      await StripeTerminal.setConnectionToken({ token: data.secret });
    } catch (error) {
      // An empty token fails the pending native callback instead of leaving it hanging.
      try {
        await StripeTerminal.setConnectionToken({ token: '' });
      } finally {
        console.error('Unable to supply a connection token', error);
      }
    }
  },
);

await StripeTerminal.initialize({
  isTest: true,
});