Tapaya
Acceptation des paiements en magasinGuide d’intégrationIntégration du SDK mobile

Intégration du SDK mobile

Android

Intégrez le Tapaya Accept SDK dans votre application mobile.

Le Tapaya Accept SDK intègre le traitement des paiements par carte directement dans votre application. Il est disponible pour Android (Kotlin) et pour React Native / Expo, et tous deux ciblent les appareils Android. Le package React Native est une fine surcouche du SDK Android : les interfaces, les types et les cas d’échec décrits ci-dessous sont donc identiques sur les deux.

Chaque exemple de code de cette page est proposé dans des onglets par langage. Choisissez-en un et toute la page s’aligne sur ce choix.

Installation

build.gradle.kts
dependencies {
    implementation("com.tapaya:accept:1.15.0")
}

Android : ajoutez la dépendance au fichier build.gradle.kts de votre module, puis synchronisez le projet.

React Native : installez le package avec npx expo install @tapayadot/accept-react-native, ajoutez le plugin de configuration comme indiqué, puis régénérez le projet natif avec npx expo prebuild --clean et lancez-le avec npx expo run:android.

Initialisation

Accept est le point d’entrée de toutes les fonctionnalités du SDK. Initialisez-le une seule fois au démarrage de l’application (sur Android, généralement dans votre classe Application), puis authentifiez-vous séparément avec un jeton commerçant.

Étape 1 : initialiser

// Sandbox is the default (isProduction = false)
Accept.initialize(context = this)

// Target production explicitly
Accept.initialize(context = this, isProduction = true)

// Or drive it from your build type
Accept.initialize(context = this, isProduction = !BuildConfig.DEBUG)

Il n’y a pas de context sur React Native ; le module natif le conserve pour vous.

Étape 2 : s’authentifier

Appelez Accept.auth.authenticate() après l’initialisation pour connecter un commerçant avec un jeton obtenu depuis votre backend. L’appel est asynchrone et échoue avec une erreur du SDK. En cas de succès, l’état du SDK passe à authenticated.

// suspend; call from a coroutine
try {
    val sdkToken = myBackendApi.getSdkToken()
    Accept.auth.authenticate(sdkToken)
    // Accept.state now emits SdkState.Authenticated
} catch (e: AcceptException) {
    // handle authentication failure
}

Observez le cycle de vie pour réagir à ces transitions : idle → initialized → authenticated.

// Accept.state is a StateFlow<SdkState>
Accept.state.collect { state ->
    if (state == SdkState.Authenticated) loadMerchant()
}

Concepts clés

L’objet Accept expose le SDK au travers d’un petit nombre d’interfaces :

InterfaceRôle
Accept.authAuthentifier une session commerçant.
Accept.merchantProfil du commerçant, configuration, statut d’onboarding.
Accept.paymentsCréer et gérer les paiements par carte.
Accept.pluginInstallation et activation du terminal (application plugin).
Accept.displaysInformations sur les écrans de l’appareil pour les terminaux à double face (« double-sided »).
Accept.sdkVersion du SDK, environnement, identifiant de l’appareil, montants minimaux.
Accept.stateÉtat du cycle de vie : StateFlow<SdkState> en Kotlin, un getter plus addStateListener() sur React Native.

Accepter un paiement

Appelez Accept.payments.pay() avec le montant (dans la plus petite unité de la devise) et un code de devise ISO 4217, puis observez le flux d’événements PaymentEvent qu’il émet : d’abord la progression, puis le résultat final. pay() ne lève jamais d’exception ; un échec de création du paiement arrive à la place sous forme d’événement CreationFailed.

Seule la manière d’observer ce flux diffère : Kotlin renvoie un Flow<PaymentEvent> que vous collectez depuis une coroutine, tandis que React Native prend un listener et renvoie un abonnement que vous pouvez résilier avec remove().

amount et currency sont obligatoires. Pour les paramètres facultatifs (paymentToken, receiptConfig, nfcPosition, metadata, tip, transactionTimeout, display), consultez Créer un paiement.

Sur React Native

Les mêmes paramètres sont transmis dans un seul objet PaymentRequest, pay({ amount, currency, tip, … }), avec number à la place de Long et Record<string, string> à la place de Map<String, String>. Un seul renommage : le délai d’expiration s’appelle transactionTimeoutMs et prend de simples millisecondes (0…120000) au lieu d’une Duration.

 Parcours de paiement

Accept.payments.pay(amount = 15000, currency = "CZK") // 150.00 Kč
    .collect { event ->
        when (event) {
            PaymentEvent.Creating -> Log.d("pay", "creating")
            is PaymentEvent.Created -> Log.d("pay", "created ${event.paymentToken}")
            PaymentEvent.Launched -> Log.d("pay", "launched")
            is PaymentEvent.Result -> Log.d("pay", event.payResult.toString())
            is PaymentEvent.CreationFailed -> Log.e("pay", event.cause.toString())
        }
    }

L’événement de résultat final contient un PayResult : succès (avec paymentToken, metadata, authCode, receiptDetails), refus (metadata, receiptDetails), annulation (metadata) ou échec (reason, metadata). Kotlin les modélise sous forme de sous-classes PayResult.Success / Declined / Canceled / Failed ; React Native sous forme d’union discriminée dont vous distinguez les cas avec payResult.type === 'success'. Consultez Résultat du paiement pour le détail complet. Interrogez un paiement ultérieurement avec Accept.payments.status(paymentToken), arrêtez-le avec Accept.payments.cancel(paymentToken) ou remboursez-le avec Accept.payments.refund(paymentToken, reason).

Uniquement le résultat (React Native)

Si votre interface n’affiche pas les états intermédiaires, payAsync() réduit l’ensemble du flux à son PayResult final, et sa promesse est rejetée avec une AcceptError dans tous les cas que pay() aurait signalés comme creationFailed. Cette méthode n’a pas d’équivalent en Kotlin : côté Kotlin, collectez le Flow, ou ne prenez que son dernier événement.

const result = await Accept.payments.payAsync({ amount: 15000, currency: 'CZK' });
if (result.type === 'success') {
    console.log(result.paymentToken, result.receiptUrl);
}

Informations sur le commerçant et le SDK

Accept.merchant expose le profil et la configuration de paiement du commerçant connecté. Accept.sdk expose des informations sur le build du SDK et sur cet appareil. Chaque membre ci-dessous nécessite d’abord Accept.initialize() (sauf Accept.sdk.version), et tout ce qui relève de Accept.merchant nécessite en outre un appel Accept.auth.authenticate() abouti ; sinon, l’appel lève NotInitialized ou NotAuthenticated.

Commerçant

val info = Accept.merchant.info()                      // MerchantInformation?
val config = Accept.merchant.config()                  // MerchantConfig
val currencies = Accept.merchant.availableCurrencies() // List<String>
val status = Accept.merchant.onboardingStatus()        // OnboardingStatus
MembreRenvoieDescription
info()MerchantInformation?Le profil d’entreprise enregistré du commerçant. null si le profil n’est pas encore disponible.
config()MerchantConfigLa configuration de paiement du commerçant.
availableCurrencies()List<String>Codes ISO 4217 que le commerçant peut accepter. Raccourci pour config().availableCurrencies.
onboardingStatus()OnboardingStatusIndique si le commerçant peut déjà accepter des paiements.

Ces quatre fonctions sont suspend et lèvent NotAuthenticated, NoInternetConnection ou Unknown en cas d’échec.

MerchantInformation contient le profil d’entreprise enregistré. Toujours présents : merchantId (UUID), email, businessType (BusinessType), legalName, registrationNumber, mcc et l’adresse enregistrée (addressLine1, city, country). Facultatifs (null sauf si le commerçant les a fournis) : vatNumber, incorporationDate, addressLine2, postalCode, state, phone, averageItemValue, expectedMonthlyTurnover, businessModelDescription, numberOfEmployees, annualTurnover, balanceSheetTotal, statementDescriptor, termsVersion.

BusinessType prend l’une des valeurs INDIVIDUAL, COMPANY, NONPROFIT_ORGANIZATION, GOVERNMENT_ENTITY ou PARTNERSHIP.

MerchantConfig contient availableCurrencies: List<String> et la devise par défaut du commerçant, currency: String?.

OnboardingStatus est une interface scellée : soit ReadyForPayments, soit RequiresAction(state), où state est un OnboardingState : NOT_STARTED, PENDING, ACTIVE, ATTENTION_NEEDED, ACTION_REQUIRED, BLOCKED, WAITING_FOR_KYB, WAITING_FOR_STAKEHOLDERS ou PENDING_ACQUIRER_APPROVAL. Consultez Onboarding des commerçants pour la signification de chaque état.

SDK

Accept.sdk.version            // String, e.g. "1.15.0"
Accept.sdk.isProduction       // Boolean
Accept.sdk.deviceId()         // String?  (suspend)
Accept.sdk.minimumAmounts()   // List<MinimumAmount>  (suspend)
MembreRenvoieDescription
versionStringLa version du SDK. Lisible avant initialize().
isProductionBooleanIndique si initialize() a ciblé la production (true) ou la sandbox (false).
deviceId()String?L’identifiant stable de cet appareil, attribué lors du premier authenticate() réussi et conservé jusqu’à la désinstallation de l’application. null avant cela. Indiquez-le dans vos demandes de support.
minimumAmounts()List<MinimumAmount>Le montant minimal payable par devise prise en charge. Chaque entrée est de la forme currency: String, amount: Long, dans la plus petite unité de la devise.

Application plugin

Chaque paiement par carte est confié à Tapaya Terminal, l’application plugin de Tapaya ; elle est toujours requise, aucun parcours de paiement par carte ne s’en passe. Vérifiez si elle est installée, puis activez le terminal, ou ouvrez sa fiche dans le store pour que le commerçant puisse l’installer.

Utilisez pour cela Accept.plugin.isInstalled(), Accept.plugin.activateTerminal() et Accept.plugin.install(). Consultez Installer et activer le terminal pour le code et tous les résultats d’activation possibles.

Lisez l’état actuel du plugin avec Accept.plugin.status(), ou déconnectez le commerçant du plugin avec Accept.plugin.logout().

Autorisations

Les paiements par carte exigent que l’application hôte dispose de l’autorisation de localisation (ACCESS_FINE_LOCATION). Le SDK ne la demande pas à votre place ; accordez-la avec les API d’autorisation Android standard avant d’appeler pay().

class PaymentActivity : AppCompatActivity() {
    private val requestLocation = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted ->
        if (granted) launchPaymentFlow() else showPermissionRationale()
    }

    fun startPayment() {
        requestLocation.launch(Manifest.permission.ACCESS_FINE_LOCATION)
    }
}

Si la localisation est absente ou ne peut pas être déterminée lors de l’exécution de pay(), le flux émet PaymentEvent.CreationFailed avec pour cause LocationPermissionRequired, LocationUnavailable ou LocationTimeout.

Gestion des erreurs

Chaque échec contrôlé porte l’un des noms ci-dessous. En Kotlin, chacun est une sous-classe de la classe scellée AcceptException ; sur React Native, ils relèvent tous d’une seule classe AcceptError dont le code contient le nom, les champs de payload (minimum, currency, state, …) étant rattachés au même objet.

Les appels asynchrones (authenticate, merchant.*, payments.status/cancel/refund, plugin.activateTerminal/status/logout/updateInfo, clear) échouent en levant l’une de ces erreurs. payments.pay() fait exception : il ne lève jamais d’exception et signale ces mêmes échecs sous forme d’événement CreationFailed portant la cause.

Types d’exception

ExceptionDescription
Cycle de vie
NotInitializedUn appel nécessite d’abord Accept.initialize().
NotAuthenticatedUn appel nécessite d’abord Accept.auth.authenticate().
SessionExpiredLa session authentifiée a expiré ; authentifiez-vous de nouveau.
UnknownEnvironment(key)Clé d’environnement non reconnue (surcharge interne d’initialize).
Paiements
PaymentInProgressUn pay() précédent attend encore un résultat.
CurrencyNotAvailableForMerchant(code)Devise non configurée pour le commerçant.
UnsupportedCurrency(code)Devise non reconnue par le SDK.
AmountBelowMinimum(minimum, currency)Montant inférieur au minimum configuré.
TransactionTimeoutOutOfRange(transactionTimeout)transactionTimeout hors de la plage de 0 à 120 secondes.
PaymentCreationFailed(cause)Tout autre échec lors de la création d’un paiement.
PaymentNotFoundJeton de status()/cancel()/refund() inconnu du backend.
Localisation
LocationPermissionRequiredL’application hôte ne dispose pas de l’autorisation de localisation.
LocationUnavailableAucun fournisseur n’a pu déterminer la position.
LocationTimeoutLa position n’a pas été déterminée à temps.
Plugin / terminal
PluginUnavailableApplication plugin non installée, ou impossible de se lier à son service.
PluginTimeoutLe plugin n’a pas répondu à temps.
ActivationInProgressUn activateTerminal() précédent attend encore un résultat.
NoTidsAvailableForMerchantLe commerçant n’a plus de terminal (TID) non attribué à activer.
PluginAlreadyAuthenticatedPlugin déjà authentifié pour ce commerçant.
PluginMerchantMismatchPlugin authentifié pour un autre commerçant ; appelez d’abord plugin.logout().
PluginNotReadyForPayments(status)pay() a été exécuté alors que le plugin n’était pas à l’état PluginStatus.ReadyForPayments ; status contient l’état actuel.
MerchantOnboardingIncomplete(state)Le commerçant n’a pas terminé l’onboarding.
Système
NoInternetConnectionL’appareil n’a pas de connexion réseau.
Unknown(cause)Tout échec que le SDK ne reconnaît pas comme l’un des siens.

Résultat ou exception

Un paiement qui atteint le terminal mais n’aboutit pas n’est pas une exception ; il arrive sous forme de PaymentEvent.Result contenant PayResult.Declined, Canceled ou Failed(reason). De même, l’activation du terminal renvoie ActivateTerminalResult.Failed(reason).

Gérer les exceptions

Encapsulez l’appel dans un try/catch et traitez séparément les cas que vous gérez, avec un catch générique en dernier recours. Kotlin distingue les cas par sous-classe d’exception ; React Native vérifie avec AcceptError.is() puis fait un switch sur code.

try {
    val status = Accept.payments.status(paymentToken)
} catch (e: SessionExpired) {
    // Re-authenticate
} catch (e: NoInternetConnection) {
    // Ask the user to check connectivity
} catch (e: AcceptException) {
    // Handle any other SDK error
    Log.e("Payment", "Error: ${e.message}")
}

Journalisation

Activez la journalisation de débogage pendant le développement pour diagnostiquer les problèmes.

Accept.setDebugLoggingEnabled(true)

Déconnexion

Effacez tout l’état du SDK conservé localement (jeton d’authentification, identifiant de l’appareil, configuration en cache) et ramenez l’état du SDK à idle. Appelez cette méthode lorsque l’utilisateur se déconnecte. Pour déconnecter également le commerçant de l’application Tapaya Terminal, appelez d’abord Accept.plugin.logout().

Accept.plugin.logout() // suspend; signs out of the Tapaya Terminal app
Accept.clear()         // suspend; clears local SDK state
Il manque quelque chose sur cette page ?Demandez une fonctionnalité. Nous joignons cette page pour que l’équipe ait le contexte.