React Native
Le package @wajub/react-native, une interface de paiement prête à l'emploi pour votre application React Native.
Le SDK React Native vous permet d'encaisser des paiements Mobile Money, par carte et par
portefeuille dans votre application, avec une interface de paiement prête à l'emploi. present()
l'ouvre et se résout une fois, à sa fermeture.
@wajub/react-native
Stable · GAnpm
- Version
- 1.1.1
- Runtime
- React 18+, React Native 0.74+
- Frameworks
- Android and iOS
Couvre
- Paiements
- Mobile Money
- Cartes
Installer
npm install @wajub/react-native @react-native-community/netinfo @react-native-async-storage/async-storagenpm install @stripe/stripe-react-nativecd ios && pod install| Dépendance homologue | Plage | Requise |
|---|---|---|
react | >=18 | Toujours |
react-native | >=0.74 | Toujours |
@react-native-community/netinfo | >=11.0.0 | Toujours |
@react-native-async-storage/async-storage | >=1.17.0 | Toujours |
@stripe/stripe-react-native | >=0.38.0 | Facultative, pour les cartes Stripe. Sans elle, l'interface ne les propose pas, voir Cartes |
@stripe/stripe-react-native impose ses propres versions minimales d'iOS et d'Android, qui
deviennent les vôtres dès que vous l'installez. Aucun provider n'est nécessaire à la racine de votre
application : usePayment() fonctionne dans n'importe quel composant.
Démarrage rapide
- Votre serveur crée le paiement avec sa clé secrète et transmet
authorization_tokenà l'application. - L'application appelle
present()avec ce jeton. Affichez l'élémentPaymentSheetque le hook renvoie dans le même composant. - Votre serveur confirme le paiement, avec le
webhook
payment.succeededouGET /payments/{id}, avant de livrer. Le résultat dans l'application est ce que le payeur a vu, pas une preuve de paiement.
import { Button } from 'react-native';
import { usePayment } from '@wajub/react-native';
function CheckoutScreen({ authorizationToken }: { authorizationToken: string }) {
const { present, PaymentSheet } = usePayment();
const pay = async () => {
const result = await present({
sessionToken: authorizationToken,
returnUrl: 'https://shop.example.com/app/payment-return', // optional, a universal/app link
});
switch (result.status) {
case 'complete':
// Paid on the device. Deliver once your server has confirmed it.
return showReceipt(result.payment.id);
case 'processing':
// Closed while a payment was running: an approval may still land.
return showPending();
case 'failed':
return showError(result.error.message);
case 'cancelled':
// result.reason: 'payer_cancelled', 'dismissed' or 'host_gone'
return;
}
};
return (
<>
<Button title="Pay" onPress={pay} />
{PaymentSheet}
</>
);
}present() se résout exactement une fois, avec la valeur que reçoit onComplete. Si le composant
qui a appelé le hook est démonté pendant que l'interface est ouverte, il se résout quand même :
cancelled avec host_gone avant toute tentative, processing dès qu'une tentative a commencé,
complete après un succès. Un appel pendant qu'une interface est ouverte est rejeté avec une
WajubError de code sheet_already_presented, sans toucher à l'interface ouverte.
PaymentSheet de usePayment est un élément, affichez-le
usePayment() renvoie { present, PaymentSheet }, où PaymentSheet est un élément ou null.
Placez {PaymentSheet} dans votre JSX : <PaymentSheet /> lève une erreur, car ce n'est pas un
composant.
Options
Tout se passe dans l'unique objet que prend present(), callbacks de la section suivante compris.
| Option | Type | Par défaut | Rôle |
|---|---|---|---|
sessionToken | string | Obligatoire | Le authorization_token du paiement |
locale | string | Celle du paiement, sinon celle de l'appareil | 'fr' ou 'en'. Toute autre valeur donne l'anglais |
appearance | Appearance | Aucune | L'apparence de l'interface, voir Apparence |
loader | 'skeleton' ou 'spinner' | 'skeleton' | Ce qui s'affiche pendant le chargement du paiement |
readyTimeout | number, en ms | 15000 | Passé ce délai, une interface encore en chargement affiche une erreur avec Réessayer, et onLoadError reçoit ready_timeout. 0 désactive le délai |
returnUrl | string | Aucune | Un lien universel ou d'application https vers lequel la page du prestataire renvoie le payeur. Sans lui, le payeur revient dans l'application par lui-même |
mobileMoney.country | string, ISO-2 | Le pays du payeur s'il est proposé, sinon le premier | Le pays sur lequel le formulaire s'ouvre |
mobileMoney.countryLocked | boolean | false | Masque le sélecteur de pays, quand country est proposé |
mobileMoney.operatorMode | 'choose' ou 'detect' | 'choose' | 'detect' masque la liste des opérateurs tant que le numéro suffit à désigner l'opérateur |
cancelOnDismiss | boolean | true | Fermer l'interface avant toute tentative annule le paiement. false le laisse ouvert, pour rappeler present() |
autoCloseSuccess | number, en ms | 3000 | La durée d'affichage de l'écran de succès. 0 le garde jusqu'à ce que le payeur le ferme |
stripe.appPublishableKey | string | Aucune | La clé Stripe de votre application, si elle encaisse aussi ses propres paiements Stripe, voir Cartes |
Callbacks et états
Tous facultatifs, tous appelés sur le thread JavaScript, dans cet ordre pour un paiement :
onReady, puis onStateChange à mesure que le payeur avance, onPaymentStarted et onRedirect
quand ils s'appliquent, onSuccess, onError ou onCancel, et onComplete en dernier, une seule
fois. Un callback qui lève une erreur est journalisé, et l'interface continue.
| Callback | Reçoit | Quand |
|---|---|---|
onReady | { sessionId, environment, amount, currency, methods } | Le premier écran est affiché, y compris pour un paiement déjà payé ou terminé. environment vaut 'sandbox' ou 'live', methods liste les moyens de paiement affichés |
onStateChange | { state, method } | Après onReady, puis à chaque changement d'état. method vaut mobile_money, card, wallet ou unknown |
onLoadError | WajubError | L'interface ne peut proposer aucun paiement : session_not_found, session_terminal_expired, session_terminal_cancelled, session_terminal_failed, load_error, ready_timeout ou no_payment_method |
onPaymentStarted | { method, channel } | La demande de paiement est acceptée. Dès lors, fermer l'interface donne processing |
onRedirect | { url, method: 'GET' } | Juste avant l'ouverture d'une page de prestataire dans le navigateur du système |
onSuccess | PaymentSuccess | Le paiement a réussi. method vaut unknown s'il était payé avant l'ouverture de l'interface |
onError | WajubError | Chaque échec que voit le payeur, y compris ceux qui permettent un nouvel essai, hors validation des champs |
onCancel | { reason, redirectUrl } | L'interface se ferme en cancelled |
onComplete | CheckoutResult | L'interface se ferme : la valeur avec laquelle present() se résout |
onStateChange signale l'un de ces états : INITIATED (liste des moyens), COLLECTING_DETAILS (le
formulaire d'un moyen), PROCESSING (Payer pressé), APPROVAL_REQUIRED, USSD_REQUIRED,
PAYER_OTP_REQUIRED (le code que génère le payeur), OTP_REQUIRED, PIN_REQUIRED,
PHONE_REQUIRED, BIRTHDAY_REQUIRED, ADDRESS_REQUIRED, 3DS_REQUIRED, VERIFYING, PENDING,
SUCCESS, FAILED, EXPIRED et CANCELLED. L'écran
derrière chacun est décrit dans SDKs mobiles.
Résultats
present() se résout avec l'un de quatre résultats. Faites un switch sur status.
status | Quand | Contenu |
|---|---|---|
complete | L'interface s'est fermée après l'écran de succès, y compris pour un paiement payé avant son ouverture | payment (PaymentSuccess), transaction |
processing | L'interface s'est fermée pendant qu'un paiement était en cours. L'interface n'affiche jamais un échec dont elle n'est pas sûre : de l'argent peut encore bouger, confirmez donc sur votre serveur | transaction, et instruction quand le payeur en avait une |
failed | L'interface s'est fermée sur un échec, un paiement expiré, une erreur de chargement, ou un paiement annulé ailleurs (payment_cancelled) | error, transaction ou null |
cancelled | Annuler le paiement, une fermeture avant toute tentative, ou le démontage de l'hôte d'abord | reason (payer_cancelled, dismissed ou host_gone), redirectUrl |
PaymentSuccess contient id (l'identifiant de paiement de Wajub, celui que prend
GET /payments/{id}), reference (la vôtre), amount (ce que le payeur a payé, frais compris, en
unité principale), currency, method, status: 'success', timestamp, et receipt_url quand il
existe un reçu.
Une application tuée ne peut résoudre aucune promesse. Au lancement suivant, demandez le statut du paiement à votre serveur.
Annuler et fermer
Le payeur ferme l'interface avec ✕ dans son en-tête, le bouton retour d'Android, ou Fermer sur un écran final. Il n'y a pas de glissement vers le bas.
| Le payeur ferme l'interface | Résultat |
|---|---|
| Pendant le chargement du paiement | cancelled, dismissed |
| Sur la liste des moyens ou un formulaire, avant toute tentative ou après Réessayer | cancelled, dismissed. Le paiement est annulé, sauf si cancelOnDismiss vaut false |
| Pendant qu'un paiement est en cours : le payeur approuve, confirme une étape ou attend | processing. Le paiement continue : confirmez-le sur votre serveur |
| Sur l'écran d'échec | failed, avec l'erreur que le payeur a vue. Le paiement est annulé s'il peut encore l'être, sauf si cancelOnDismiss vaut false |
| Sur l'écran de succès | complete |
| Avec Annuler le paiement | cancelled, payer_cancelled. Le paiement est annulé pour de bon |
Annuler le paiement s'affiche sous la liste des moyens et sous les formulaires, jamais pendant qu'un paiement est en cours. Les règles sont dans SDKs mobiles.
Apparence
appearance règle l'aspect de l'interface : un préréglage, un mode de couleur et les valeurs que
vous voulez changer.
await present({
sessionToken,
appearance: {
theme: 'stripe', // a preset: 'stripe', 'night', 'flat', 'none'; or 'light', 'dark', 'auto', 'system'
colorScheme: 'auto', // follows the phone, live
variables: { colorPrimary: '#0F766E', borderRadius: '12px', fontFamily: 'Inter' },
buttonTextColor: '#FFFFFF',
},
});- Mode de couleur :
colorSchemevautlight,darkouauto, qui suit le téléphone.inherit: truesuit plutôt le mode de couleur de votre application. - Image de marque : ce que vous laissez vide vient de l'image de marque du marchand dans le Dashboard, et un marchand en marque blanche masque « Sécurisé par Wajub ».
- Polices :
fontFamilydoit être une police que votre application embarque, sinon la police de la plateforme est utilisée. - Libellés :
labels: 'above', la valeur par défaut, place chaque libellé au-dessus de son champ ;'floating'le place dans le champ vide. - Mouvement :
disableAnimations: true, ou le réglage de réduction des animations du téléphone, arrête les animations.
Chaque clé et chaque préréglage sont décrits dans Apparence.
Mobile Money
Un seul formulaire couvre tous les opérateurs Mobile Money du paiement : le payeur choisit le pays et l'opérateur et saisit le numéro, mis en forme pendant la saisie. Le préfixe du numéro présélectionne son opérateur, et un numéro d'un autre opérateur proposé est refusé avant tout envoi. Après Payer, « Validez le paiement sur votre téléphone » affiche le numéro et l'instruction de l'opérateur.
mobileMoney.country ouvre le formulaire sur un pays, countryLocked masque le sélecteur, et
operatorMode: 'detect' garde la liste des opérateurs masquée tant que le numéro suffit à désigner
l'opérateur. Plus de détails dans SDKs mobiles.
Cartes
Les cartes Stripe requièrent la dépendance homologue facultative @stripe/stripe-react-native ;
aucune autre configuration Stripe n'est nécessaire. Si votre application encaisse aussi ses propres
paiements Stripe, passez la clé de votre application dans stripe: { appPublishableKey } pour que
vos propres paiements Stripe continuent de fonctionner.
Les autres cartes ouvrent une page sécurisée du prestataire de paiement dans le navigateur du système. En sandbox, l'interface affiche un formulaire de carte de test.
Étapes de confirmation
Quand un paiement demande une étape de plus après Payer, l'interface l'affiche en natif : un code à usage unique, le code PIN de la carte, un numéro de téléphone, une date de naissance ou une adresse de facturation, avec les essais restants après une mauvaise réponse. En sandbox, une carte qui demande 3-D Secure reçoit un code par e-mail. Les étapes et leurs états sont dans SDKs mobiles.
Erreurs
Chaque échec est une WajubError, une sous-classe d'Error. onLoadError, onError et un
résultat failed en transmettent une. Ses champs sont en snake_case.
| Champ | Utilité |
|---|---|
type | api_error, authentication_error, invalid_request_error, payment_error ou rate_limit_error |
code | Ce qui s'est passé, pour orienter votre code |
message | Pour le payeur, dans la langue de l'interface |
decline_code | Sur un refus, son motif |
retryable | Indique si un nouvel essai peut aboutir |
param | Le champ en cause, quand il y en a un |
correlation_id | L'identifiant de requête à communiquer au support Wajub |
retry_after_seconds | L'attente sur rate_limited |
block_reference | Sur shield_blocked, la référence à communiquer au support Wajub |
import { WajubError } from '@wajub/react-native';
try {
const result = await present({
sessionToken,
onError: (error) => log('wajub', error.code, error.correlation_id),
});
if (result.status === 'failed') {
// insufficient_funds, payment_attempt_ended, session_terminal_expired, ready_timeout…
report(result.error.code, result.error.retryable);
}
} catch (error) {
if (error instanceof WajubError && error.code === 'sheet_already_presented') {
// A sheet is already open.
}
}Les codes sur lesquels brancher votre code : insufficient_funds, payment_declined,
payment_attempt_ended (refusé, rien n'a été débité), payment_failed, payment_cancelled,
session_expired, shield_blocked, merchant_restricted, rate_limited,
session_terminal_expired, unsupported_action, redirect_unavailable et
sheet_already_presented. Chaque code est dans SDKs mobiles.
Votre propre interface
Les écrans de l'interface ne sont pas exportés. Une application qui dessine ses propres écrans de
paiement utilise WajubSession, le client de plus bas niveau, que renvoie createSession(token) et
que documente le README du paquet.
Confidentialité
Pour effectuer le paiement, le SDK envoie à Wajub ce que saisit le payeur. Pour la prévention de la fraude, il envoie aussi des informations de base sur l'appareil et un identifiant d'installation aléatoire, qui n'est pas l'identifiant publicitaire. Ce qu'il faut déclarer dans les étiquettes de confidentialité de l'App Store et dans le formulaire de sécurité des données de Google Play, catégorie par catégorie, est sur SDKs mobiles.
Tests
Un paiement que votre serveur crée avec une clé de sandbox fait passer tout le parcours en sandbox, et l'interface affiche un badge « MODE TEST ». Les numéros et les cartes de test figurent dans Scénarios de test.
Pages associées
- SDKs mobilesÉtats, résultats et codes d'erreur de l'interface de paiement.
- Créer un paiementL'appel serveur qui génère le jeton.
- WebhooksLa confirmation qui déclenche la livraison de la commande.
- Recette React NativeMobile Money dans une application React Native, de bout en bout.
- FlutterLe SDK pour les applications Flutter.
- AndroidLe SDK pour les applications Android, en Kotlin et Compose.
- iOSLe SDK pour les applications iOS, en Swift.