Paiement en magasin : Android Intent API
Présentation
L'Android Intent API est destinée aux applications de logiciel de caisse exécutées sur le terminal lui-même (ou sur le même appareil Android que Yavin Pay). Votre application envoie un Intent de deep link à Yavin Pay et reçoit le résultat dans onActivityResult. Choisissez-la lorsque le logiciel de caisse et l'application de paiement partagent le même appareil.
En bref
| URL de base | Schéma de deep link : yavin://com.yavin.macewindu/v4/<action>?data=$queryParams |
| Authentification | Connexion du marchand dans Yavin Pay avec ses identifiants My Yavin |
| Convention de nommage | camelCase pour tous les champs, sauf les entrées de tax_breakdown qui sont en snake_case |
| Version actuelle | v4 |
| Remise des résultats | Synchrone : renvoyée à votre activité via onActivityResult, dans l'extra response (JSON) et, en cas d'erreur, dans l'extra message |
Endpoints
| Action | Deep link | Usage |
|---|---|---|
| Paiement | /v4/payment | Démarrer une transaction de débit ou de remboursement |
| Impression | /v4/print | Imprimer du contenu libre |
| Partager un ticket | /v4/share-receipt | Partager un ticket par SMS, e-mail ou impression |
| Transactions | /v4/transactions | Récupérer l'historique des transactions |
| Annulation | /v4/reversal | Annuler une transaction récente (de moins de 16 heures) |
| Lecteur NFC | /v4/nfc-reader | Lire un tag NFC via Yavin Pay |
Versions
| Version de l'API | Dernière mise à jour |
|---|---|
v4 | 22/08/2024 |
v1 | 02/05/2022 |
Avant de commencer
Envoi de la requête
- Encodage du payload : la requête est un objet JSON sérialisé puis encodé en URI dans le paramètre de requête
datadu deep link - Code de requête : lors de l'appel à
startActivityForResult, définissez votre propre code de requête (un entier quelconque, par ex. 8888). Il vous sert de référence pour associer la réponse asynchrone à la requête, comme un identifiant de corrélation de webhook
Lecture de la réponse
- Le JSON de résultat se trouve dans l'extra
response - Lorsqu'un message d'erreur existe, il se trouve dans un extra
messagedistinct, jamais dans le JSONresponse - L'extra
responsepeut être absent (par exemple lorsque la requête est rejetée avant toute création de transaction, ou lorsquedataest lui-même null) : lisez les extras de manière défensive et ne transmettez jamais une chaîne null à votre parseur JSON
Réponses
- Les actions de paiement renvoient un objet Transaction, documenté à la fin de cette page avec les autres objets partagés, comportant un champ
status:oken cas de succès,koen cas d'échec - Les clés dont la valeur est null sont omises : tolérez l'absence de clés
- L'action transactions n'a pas de champ
status(voir Récupérer les transactions)
N'envoyez jamais de montant négatif. Sur l'Android Intent API, le terminal ne rejette pas un amount ou un giftAmount négatif : il utilise sa valeur absolue. Un amount de -1000 est traité comme un paiement de 10,00 €. Validez les montants côté logiciel de caisse avant d'envoyer l'Intent.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != REQUEST_CODE_PAYMENT) return
// Both extras are optional: data can be null (eg resultCode == RESULT_CANCELED)
val json: String? = data?.getStringExtra("response")
val message: String? = data?.getStringExtra("message")
if (json == null) {
// No transaction was returned: treat the attempt as failed
onPaymentFailed(message ?: "No response from Yavin Pay (resultCode=$resultCode)")
return
}
val response = Gson().fromJson(json, TransactionResponse::class.java)
if (response.status == "ok") {
onPaymentSucceeded(response)
} else {
// A declined payment carries no message in the JSON: use the message extra if any
onPaymentFailed(message ?: "Payment declined")
}
}Idempotence
Envoyez un idempotentUuid à chaque requête de paiement, un par tentative de paiement. Voir Gestion de l'IdempotentUuid.
Devise
La devise est celle configurée sur le profil du marchand dans la base de données Yavin.
Lancer une transaction de débit
Lance un paiement par débit sur le terminal.
Paramètres
amountIntegerrequis- Montant en centimes, doit être supérieur à 0. N'envoyez jamais de valeur négative : elle n'est pas rejetée et sa valeur absolue est débitée
transactionTypeStringpar défaut debit- Utilisez debit. Les valeurs sont strictes (debit ou refund) : toute autre valeur, y compris credit, est traitée silencieusement comme un débit
idempotentUuidStringpar défaut autogenerated- Identifiant unique de la tentative de paiement, voir Gestion de l'IdempotentUuid. Une transaction ko est renvoyée telle quelle : générez un nouvel UUID pour réessayer
customerCustomer- Informations client préremplies pour l'envoi du ticket
Afficher les paramètres de CustomerMasquer les paramètres de Customer
customer.firstNameString- Prénom
customer.lastNameString- Nom
customer.emailStringcustomer.phoneString- Format international commençant par +
customer.birthDateString- Date de naissance
enableGiftScreenBoolean- true : affiche l'écran de pourboire. false : l'ignore
giftAmountIntegerpar défaut 0- Pourboire ou don en centimes
receiptTicketReceiptTicket- Ticket imprimé avec le ticket carte
Afficher les paramètres de ReceiptTicketMasquer les paramètres de ReceiptTicket
receiptTicket.dataStringrequis- Contenu à imprimer
receiptTicket.formatStringpar défaut text- Format du contenu
receiptTicket.tax_breakdownArray- Tableau d'objets Tax, un par taux de taxe
receiptTicketJsonString- Payload JSON supplémentaire sous forme de chaîne
referenceString- Serveur ou personne qui traite la transaction
vendorVendor- Nom et version de votre logiciel
Afficher les paramètres de VendorMasquer les paramètres de Vendor
vendor.softwareNameString- Nom du logiciel de caisse
vendor.softwareVersionString- Version du logiciel de caisse
checkoutExternalIdString- Identifiant de commande unique du marchand, obligatoire dès qu'un des champs de commande externe ci-dessous est envoyé. Il doit être unique : une requête est rejetée si une transaction ok terminée utilise déjà cette valeur
externalTableNumberString- Numéro de table. Nécessite checkoutExternalId
externalOrderNumberString- Numéro de commande lisible. Nécessite checkoutExternalId
externalOrderIdString- Identifiant technique unique de la commande dans votre logiciel de caisse, pour le rapprochement. Nécessite checkoutExternalId
Requête
val request = TransactionRequest(
amount = 100,
transactionType = "debit",
idempotentUuid = "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
customer = Customer("John", "Doe", "john@yavin.com"),
vendor = Vendor("Awesome Partner", "1.2.3"),
receiptTicket = ReceiptTicket(data = "This is a wonderful\n receipt ticket to print", format = "text"),
receiptTicketJson = JSONObject("{\"transactionId\": \"123456\", \"amount\": 3500 }").toString(),
checkoutExternalId = "order-123",
externalTableNumber = "12",
externalOrderNumber = "A-123",
externalOrderId = "order-123"
)
val jsonData = Gson().toJson(request)
val queryParams = Uri.encode(jsonData)
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/payment?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_PAYMENT)Réponse
En cas de succès, la réponse est un objet Transaction avec status = ok et un transactionId ; les champs de la requête (reference, customer, idempotentUuid, checkoutExternalId et les champs de commande externe) sont renvoyés à l'identique.
Si une transaction existe déjà pour cet idempotentUuid, elle est renvoyée telle quelle, y compris lorsqu'elle est encore en cours : dans ce cas, elle n'a pas de champ status.
En cas d'échec, l'extra response contient un objet Transaction avec status = ko et sans message. Lorsqu'il y a un message d'erreur, il se trouve dans l'extra distinct message. Lorsque la requête est rejetée avant la création d'une transaction, l'extra response est absent et seul message est renseigné. Messages possibles :
Error: refund need to be activated contact support: les remboursements ne sont pas activés pour ce marchandTransaction cannot proceed: unknown payment gateway or amount is not greater than 0Error: amount exceeds the maximum authorized credit amount (N): remboursement supérieur au montant maximal autorisé NError: checkoutExternalId must not be null if any of the following values are provided: externalOrderId, externalOrderNumber, externalTableNumber, or items: un champ de commande externe a été envoyé sanscheckoutExternalIdError: checkoutExternalId: The checkoutExternalId is already in use. Please ensure you provide a unique value: une transactionokterminée utilise déjà cecheckoutExternalIdA request is already in progress: une autre requête est en cours- Un message de délai dépassé lorsque les écrans facultatifs (pourboire, référence, récapitulatif) dépassent 60 secondes
Unknown Errorou un message d'exception : erreur interne Android
Lancer une transaction de remboursement
Rembourse des fonds sur la carte du client. Même deep link et même payload que pour le débit, avec transactionType défini sur refund.
Paramètres
transactionTypeStringrequispar défaut debit- Doit être défini sur refund. Toute autre valeur, telle que credit, lance un débit
Réponse
Identique à l'action de débit : un objet Transaction avec transactionType = refund en cas de succès, status = ko avec la même liste de messages en cas d'échec.
Imprimer un ticket
Imprime un contenu libre, par exemple une facture.
Paramètres
dataStringrequis- Contenu à imprimer
formatStringpar défaut text- text ou escpos
Requête
val request = PrintRequest(format = "text", data = "Text to print")
val queryParams = Uri.encode(Gson().toJson(request))
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/print?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_PRINT)Réponse
status = ok signifie que la requête d'impression a été acceptée, et non que quelque chose a été imprimé : le contenu n'est pas validé, donc un data vide peut aussi renvoyer ok. Lorsque le paramètre de requête data du deep link est absent ou ne peut pas être décodé, status = ko avec le message Error: bad request print.
Récupérer les transactions
Récupère les transactions du terminal. Utilisez limit et offset pour la pagination. Si aucun filtre de date n'est défini, les 30 derniers jours sont récupérés.
Paramètres
startDateStringpar défaut last 30 days- Date de début incluse, ISO 8601
endDateStringpar défaut today- Date de fin incluse, ISO 8601
startTimeStringpar défaut 00:00:00Z- Heure de début le startDate, ISO 8601
endTimeStringpar défaut 23:59:59Z- Heure de fin le endDate, ISO 8601
limitIntegerpar défaut 50- Nombre maximal de transactions renvoyées, jusqu'à 200
offsetIntegerpar défaut 0- Décalage de pagination
Requête
val request = TransactionsRequest(
startDate = "2022-01-01",
endDate = "2022-02-01",
startTime = "00:00:00",
endTime = "23:59:59",
limit = 50
)
val queryParams = Uri.encode(Gson().toJson(request))
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/transactions?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_TRANSACTIONS)Réponse
Renvoie total, count, limit, offset et transactions. Il n'y a pas de champ status.
totalcorrespond au nombre de transactions dans cette réponse (égal àcount), et non au nombre total de transactions correspondantes. Pour paginer, augmentezoffsetdelimitjusqu'à ce qu'une page renvoie moins delimittransactions.- Chaque élément de
transactionscontient les champs deItemTransactionResponseci-dessus. Il s'agit d'un objet réduit, et non de l'objet Transaction complet : il ne contient nicardToken, nipaymentApplication, ni tickets, niidempotentUuid. createdAtest l'horodatage de la transaction sur le terminal, en ISO 8601 UTC.
Si data est absent ou invalide, aucune réponse n'est renvoyée et l'écran Yavin Pay reste ouvert. Validez la requête côté logiciel de caisse avant de l'envoyer.
Annuler une transaction
Annule une transaction récente.
Conditions d'annulation. La transaction d'origine est un débit ok traité sur le même terminal que celui qui émet l'annulation, il y a moins de 16 heures (heure du terminal), et le montant de l'annulation est égal au montant total d'origine, pourboire inclus (amount + giftAmount).
Paramètres
amountIntegerrequispar défaut 0- Montant en centimes, doit être égal au montant total d'origine (amount • giftAmount)
initialTransactionIdStringrequis- Transaction à annuler
Réponse
status = ok avec reversalTransactionId (la nouvelle transaction d'annulation) et initialTransactionId (la transaction annulée). En cas d'échec, status = ko avec l'une des valeurs suivantes :
Transaction ID is mandatory: initialTransactionId manquantTransaction not found for ID: <transactionId>: transaction inconnue sur ce terminalAmount mismatch: le montant diffère du montant total d'origine (amount+giftAmount)Transaction has already been reversedCannot reverse this Transaction or time window expired: la transaction n'est pas une transaction de débit ok ou VAD, ou date de plus de 16 heuresNo suitable gateway found to handle reversalAn unexpected error occurred. Please try again later.: erreur générique
Champs inversés en cas d'erreur. Dans une réponse d'erreur, reversalTransactionId contient l'ID de la transaction initiale et initialTransactionId contient l'ID de la tentative d'annulation. Lisez-les en conséquence.
Lire un tag NFC via Pay
Lit un tag NFC via Yavin Pay.
Paramètres
readerIncentiveString- Texte affiché pendant l'attente du tag
timeoutLongpar défaut 10000- Délai de lecture en millisecondes, maximum 10000 : une valeur supérieure est plafonnée à 10000
Requête
val request = NFCReaderRequestV4(10000, "Approchez votre carte")
val queryParams = Uri.encode(Gson().toJson(request))
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/nfc-reader?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_READ)Réponse
status est un booléen : true avec un objet TagInfo lorsque le tag a été lu, false sinon.
L'objet Transaction
Renvoyé par les actions de paiement. Les champs provenant de votre requête sont renvoyés tels quels. Les clés dont la valeur est null sont omises : tolérez les clés manquantes. Un paiement refusé a status = ko et aucun message (voir Lecture de la réponse).
Attributs
statusString- ok ou ko. Absent pour une transaction en cours
transactionIdString- Identifiant côté serveur
amountInteger- Montant en centimes, hors giftAmount
giftAmountInteger- Pourboire ou don en centimes
currencyCodeString- Code ISO 4217
transactionTypeString- En minuscules : debit ou refund. Tolérez les autres valeurs (par ex. na)
appVersionString- Version de l'application Yavin Pay
cardTokenString- Token unique de la carte du client
clientCardTicket / merchantCardTicketString- Tickets de carte du client et du marchand
paymentApplicationString- par ex. EMV_CONTACTLESS
schemeString- Réseau d'acceptation (par ex. VISA)
issuerString- Émetteur de la carte
idempotentUuidString- UUID issu de la requête
reference, customer- Renvoyé tel quel depuis la requête
checkoutExternalId, externalTableNumber, externalOrderNumber, externalOrderIdString- Renvoyé tel quel depuis la requête
{
"status": "ok",
"transactionId": "xPUyi4fmdibD",
"amount": 1000,
"giftAmount": 0,
"currencyCode": "EUR",
"transactionType": "debit",
"appVersion": "3.2.8",
"cardToken": "1234567890",
"clientCardTicket": "...",
"merchantCardTicket": "...",
"paymentApplication": "EMV_CONTACTLESS",
"scheme": "CB",
"issuer": "VISA",
"idempotentUuid": "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
"cartId": "ORDER-2026-000123",
"reference": "Luke",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@yavin.com"
},
"checkoutExternalId": "order-123",
"externalTableNumber": "12",
"externalOrderNumber": "A-123",
"externalOrderId": "order-123"
}L'objet Customer
Facultatif pour les actions de paiement et de partage de ticket. Préremplit les informations du client afin que le ticket puisse être envoyé par SMS ou e-mail sans demande sur le terminal.
Attributs
firstNameString- Prénom
lastNameString- Nom
emailStringphoneString- Format international commençant par +
birthDateString- Date de naissance
{
"customer": {
"firstName": "John",
"lastName": "Doe",
"phone": "+33612345678",
"email": "john@yavin.com"
}
}L'objet ReceiptTicket
Contenu imprimé avec le ticket de carte lors de l'action de paiement, ou partagé lors de l'action de partage de ticket.
Attributs
dataStringrequis- Contenu à imprimer
formatStringpar défaut text- Format du contenu
tax_breakdownArray- Tableau d'objets Tax, un par taux de taxe
{
"receiptTicket": {
"data": "Receipt ticket here to print if needed",
"format": "text"
}
}L'objet Tax
Ventilation facultative associée à un ReceiptTicket, sous la clé tax_breakdown, avec une entrée par taux de taxe.
La clé est tax_breakdown (et non tax), et ses entrées sont en snake_case.
Attributs
tax_amountInteger- Montant de taxe calculé en centimes
tax_percentageInteger- Pourcentage appliqué au montant (par ex. 20 pour 20 %)
{
"tax_breakdown": [
{ "tax_amount": 167, "tax_percentage": 20 },
{ "tax_amount": 50, "tax_percentage": 10 }
]
}L'objet Vendor
Identifie votre logiciel de caisse. Facultatif partout, mais fortement recommandé : il permet à notre équipe support de remonter d'un problème à une intégration et une version précises.
Attributs
softwareNameString- Nom du logiciel de caisse
softwareVersionString- Version du logiciel de caisse
{
"vendor": {
"softwareName": "MyPOS",
"softwareVersion": "1.0"
}
}L'objet TagInfo
Renvoyé par l'action du lecteur NFC, il décrit le tag qui a été lu.
Attributs
serialNumberString- Identifiant du tag
{
"tagInfo": {
"serialNumber": "04A2B3C4D5E6F7"
}
}