Aller au contenu
rdlabo.devdocs

Encaisser un paiement

Encaissez un paiement en personne avec Stripe Terminal : enregistrez les écouteurs dès le démarrage, initialisez le plugin, connectez un lecteur et confirmez un PaymentIntent.

Prérequis pour le premier test

Avant la première tentative d’encaissement, préparez :

  • Les réglages de plateforme décrits dans Configuration, y compris les autorisations Android nécessaires
  • Un point de terminaison authentifié fournissant des jetons de connexion, que votre application peut appeler
  • Un PaymentIntent de test avec card_present, créé sur votre serveur
  • Un locationId Stripe Terminal adapté au type de connexion à rechercher

Le premier résultat à obtenir est : connecter un lecteur → recueillir un moyen de paiement → confirmer le PaymentIntent et recevoir ConfirmedPaymentIntent. L’exécution de la commande doit toujours attendre votre webhook Stripe. Pour les lecteurs simulés, utilisez un type de connexion pris en charge avec isTest: true, comme indiqué dans Configuration : TerminalConnectTypes.Simulated n’est pas universel.

Enregistrer les écouteurs au niveau de l’application

Enregistrez les écouteurs d’événements Terminal une seule fois par démarrage de l’application JavaScript, le plus tôt possible pendant l’amorçage — par exemple dans main.ts, un initialiseur d’application ou un service singleton initialisé au démarrage — et avant toute initialisation ou opération. Conservez-les pendant toute la durée de vie du composant qui en est responsable au niveau de l’application.

enum TerminalEventsEnum

Membre Valeur
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'

Les surcharges typées de addListener couvrent la plupart de ces membres. DiscoveringReaders et CancelDiscoveredReaders sont émis au démarrage et à l’annulation de la recherche native, mais n’ont pas de surcharge dédiée ; consultez la page API.

Initialiser

Privilégiez une requête authentifiée côté application via RequestedConnectionToken et setConnectionToken. Votre application peut ainsi joindre ses identifiants d’autorisation habituels et contrôler les erreurs. Enregistrez l’écouteur avant initialize ; le SDK Terminal demande un nouveau jeton de connexion à usage unique chaque fois qu’il en a besoin. Activez isTest pendant le développement.

method initialize(...)

Initialise le SDK Stripe Terminal et son fournisseur de jetons de connexion.
Appelez cette méthode une fois avant de découvrir les lecteurs.

Lorsque tokenProviderEndpoint est fourni, le plugin envoie une requête POST et attend { secret: string }. Si ce paramètre est omis, gérez plutôt RequestedConnectionToken et appelez setConnectionToken().

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

Sur le Web, initialize nécessite une nouvelle instance du plugin : un nouvel appel après une initialisation réussie lève Stripe Terminal has already been initialized.

Fournir un jeton de connexion de manière sécurisée

Omettez tokenProviderEndpoint et enregistrez RequestedConnectionToken avant initialize. Lorsque le SDK a besoin d’un jeton, le plugin émet cet événement et attend setConnectionToken({ token }).

Effectuez la requête avec votre mécanisme d’autorisation habituel, exigez une réponse réussie, validez secret, puis transmettez-le comme token. Appelez setConnectionToken uniquement pendant qu’une demande de jeton est en attente ; Android et iOS rejettent les appels supplémentaires avec Stripe Terminal do not pending fetchConnectionToken. Ne journalisez jamais la réponse ni le jeton.

method setConnectionToken(...)

Fournit un secret de jeton de connexion après l’émission de RequestedConnectionToken. Créez chaque jeton sur votre serveur et utilisez-le une seule fois.

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

Mode de compatibilité tokenProviderEndpoint

tokenProviderEndpoint convient aux déploiements simples, mais les clients natifs v8.2.1 envoient un POST HTTP minimal : l’appelant ne peut ajouter ni en-tête d’autorisation ni corps de requête. Utilisez-le uniquement si votre serveur peut authentifier et protéger cette requête par d’autres moyens. N’exposez jamais un point de terminaison public de création de jetons sans restriction.

Lorsque tokenProviderEndpoint est défini, le plugin envoie un POST HTTP avec un corps vide. La réponse doit être un objet JSON contenant une chaîne secret :

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

Cette valeur est un jeton de connexion Stripe Terminal. Créez-le sur le serveur avec votre clé API secrète (stripe.terminal.connectionTokens.create()). Ne placez jamais la clé secrète, des clés restreintes permettant de créer des jetons ou des jetons de connexion bruts dans le binaire de l’application, les journaux ou une configuration cliente publique.

La démo officielle expose POST /connection/token et renvoie { secret } ; adaptez son authentification et son autorisation à votre application.

Créer un PaymentIntent sur votre backend

Créez le PaymentIntent sur votre serveur. La démo officielle utilise POST /connection/intent et renvoie { paymentIntent } en tant que secret client.

Exigences correspondant au plugin et à la démo :

  • payment_method_types doit inclure card_present
  • Conservez la clé secrète Stripe sur le serveur
  • Transmettez uniquement le secret client à collectPaymentMethod({ paymentIntent })
  • Ne créez ni ne confirmez de PaymentIntents pour paiements en présence de la carte avec une clé publique dans l’application

Exemple côté serveur tiré de la démo :

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

Rechercher des lecteurs

Recherchez des lecteurs à proximité ou simulés. Fournissez une valeur TerminalConnectTypes et un locationId Stripe Terminal lorsque le type de connexion l’exige.

locationId est utilisé lors de la recherche Internet et requis pour connecter les lecteurs Tap to Pay, Bluetooth et USB Android. La recherche Internet peut filtrer par emplacement ; Tap to Pay et Bluetooth transmettent l’emplacement à la configuration de connexion.

Points particuliers :

  • Web prend uniquement en charge Internet. Tout autre type est indisponible.
  • Bluetooth sur iOS signale les lecteurs via DiscoveredReaders à plusieurs reprises à mesure que la recherche évolue. Consultez Stripe : connecter un lecteur Bluetooth (iOS). Définissez bluetoothScanWaitTime (en millisecondes) pour que discoverReaders attende avant de se terminer avec la liste actuelle. La valeur 0, ou l’absence de valeur, renvoie le premier résultat de recherche.
  • iOS émet aussi DiscoveringReaders au démarrage de la recherche. USB, HandOff et Simulated comme type ne sont pas implémentés.
  • Android exige ACCESS_FINE_LOCATION à l’exécution, sinon discoverReaders est rejeté. Simulated est traité comme une recherche Bluetooth. HandOff correspond à Apps on Devices.
  • Appelez cancelDiscoverReaders si l’utilisateur quitte l’interface de recherche. Sur le Web, l’annulation est sans effet. Donnez toujours à l’utilisateur un moyen d’arrêter une longue recherche Bluetooth.

Écoutez DiscoveredReaders en plus d’attendre la promesse. Sur iOS avec Bluetooth, l’écouteur fournit la liste actualisée ; la promesse peut se terminer avant le dernier événement.

method discoverReaders(...)

Découvre les lecteurs avec le mode de communication demandé. Les lecteurs renvoyés sont des instantanés ; écoutez DiscoveredReaders lorsque la découverte continue peut produire des résultats supplémentaires.

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

interface DiscoverReadersOptions

Propriété Type Description Depuis
type TerminalConnectTypes Méthode de découverte et mode de communication avec le lecteur à utiliser. 5.1.0
locationId string Identifiant Stripe Terminal Location utilisé pour limiter la découverte des lecteurs Internet et leur enregistrement lorsqu’il est requis. 5.1.0
bluetoothScanWaitTime number S’applique uniquement à la découverte par balayage Bluetooth (iOS uniquement). Pendant la découverte, les lecteurs sont signalés via DiscoveryDelegate.didUpdateDiscoveredReaders. Ce délai détermine le temps d’attente avant de résoudre la méthode discoverReaders avec la liste courante. Si ce paramètre n’est pas fourni ou vaut 0, les résultats du balayage initial sont renvoyés. 7.2.0

enum TerminalConnectTypes

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

Connecter un lecteur

Connectez un des lecteurs découverts avant de recueillir les données de paiement. L’objet reader doit provenir du résultat de recherche actuel (serialNumber est l’identifiant principal du plugin).

autoReconnectOnUnexpectedDisconnect vaut false par défaut et s’applique à Tap to Pay et Bluetooth. Pour USB sur Android, la configuration de connexion native active actuellement la reconnexion automatique. Les connexions Internet n’utilisent pas ce paramètre.

merchantDisplayName et onBehalfOf s’appliquent à Tap to Pay sur iOS (LocalMobileReader). Sur Android, définissez plutôt les valeurs de compte connecté et d’affichage sur le PaymentIntent.

method connectReader(...)

Se connecte à un lecteur renvoyé par discoverReaders().

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

Recueillir un moyen de paiement

Transmettez le secret client du PaymentIntent fourni par votre backend à collectPaymentMethod. Le plugin récupère ce PaymentIntent, puis recueille le moyen de paiement sur le lecteur connecté.

method collectPaymentMethod(...)

Recueille un moyen de paiement pour un PaymentIntent créé sur le serveur. Confirmez l’intent recueilli avec confirmPaymentIntent().

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

Confirmer le PaymentIntent

Traitez et confirmez le PaymentIntent dont le moyen de paiement a été recueilli. confirmPaymentIntent est rejeté si cette collecte n’a pas préalablement réussi (PaymentIntent not found for confirmPaymentIntent).

method confirmPaymentIntent()

Confirme le PaymentIntent recueilli le plus récemment par le lecteur.

confirmPaymentIntent() => Promise<void>

ConfirmedPaymentIntent est un signal pour l’interface cliente, pas une autorisation d’exécuter la commande. N’exécutez celle-ci qu’après vérification, par votre backend, d’un webhook Stripe tel que payment_intent.succeeded.

Gérer l’annulation et les erreurs

  • cancelCollectPaymentMethod annule une collecte en cours. En cas de réussite, la promesse est résolue et Canceled est émis.
  • Failed est émis si collectPaymentMethod ou confirmPaymentIntent échoue. La promesse de cet appel est également rejetée. La charge utile peut contenir message, code et declineCode.
  • N’utilisez pas ConnectionStatusChange pour détecter une déconnexion inattendue. Utilisez UnexpectedReaderDisconnect, ainsi que DisconnectedReader pour Bluetooth/USB. Consultez Cycle de vie du lecteur.

method cancelCollectPaymentMethod()

Annule un appel à collectPaymentMethod() en cours.

cancelCollectPaymentMethod() => Promise<void>

Déconnecter le lecteur

Déconnectez le lecteur lorsque le parcours de paiement est terminé ou que le lecteur n’est plus nécessaire.

method disconnectReader()

Déconnecte le lecteur actif. Se résout immédiatement si aucun lecteur n’est connecté.

disconnectReader() => Promise<void>

Après le premier succès

Consultez Cycle de vie du lecteur pour la déconnexion, la reconnexion et les mises à jour. Pour accepter les paiements avec un téléphone comme lecteur, consultez Tap to Pay. Les signatures formelles restent sur la page 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,
});