Seamless SDK (Paiement iOS)

Sur cette page, vous trouverez toutes les étapes pour ajouter, configurer et utiliser le SDK iOS Seamless pour effectuer des paiements dans votre projet iOS.

👍

SDK recommandé

Nous recommandons d'utiliser le SDK iOS Seamless pour une expérience d'intégration fluide. Cette option fournit une solution de paiement flexible avec des composants d'interface utilisateur pré-intégrés et des options de personnalisation.

Étape 1 : Inclure la bibliothèque dans votre projet

Vous pouvez ajouter la bibliothèque en utilisant CocoaPods ou Swift Package Manager.

CocoaPods

Pour ajouter le SDK Yuno à votre projet iOS, vous devez installer le SDK Yuno. Si vous n'avez pas de Podfile, suivez le guide CocoaPods pour en créer un. Après avoir créé le Podfile, intégrez le SDK Yuno avec Cocoapods en ajoutant la ligne ci-dessous à votre Podfile.

pod 'YunoSDK', '~> 1.19.0'

Ensuite, vous devez exécuter l'installation :

installation du pod

Swift Package Manager

Si vous utilisez le Swift Package Manager, ajoutez le SDK Yuno comme dépendance, comme présenté dans le bloc de code suivant :

dépendances : [
    .package(url : "https://github.com/yuno-payments/yuno-sdk-ios.git", .upToNextMajor(from : "1.1.17"))
]

Étape 2 : Initialiser le SDK avec la clé publique

Pour commencer à utiliser Yuno iOS Seamless checkout, vous devez d'abord obtenir votre identifiant d'application Yuno et votre clé API publique. Ensuite, importez et initialize comme indiqué dans l'extrait de code suivant :

import YunoSDK

Yuno.initialize(
    apiKey: "PUBLIC_API_KEY",
    config: YunoConfig(),
    callback: { (value: Bool) in }
)
🚧

UISceneDelegate

Assurez-vous que si votre application utilise un UISceneDelegatele code d'initialisation de Yuno est placé dans votre SceneDelegate.

Le paiement Seamless vous permet de configurer l'apparence du SDK. C'est une étape optionnelle que vous configurez via la classe YunoConfig. Pour définir les configurations, utilisez le bloc de code suivant pour configurer les éléments disponibles :

final class YunoConfig {
    let cardFormType: CardFormType,
    let appearance: Yuno.Appearance,
    let saveCardEnabled: Bool,
    let keepLoader: Bool
}

Configurez le SDK avec les options suivantes :

ParamètresDescription
cardFormTypeCe champ peut être utilisé pour choisir le flux de carte Payment  et  Enrollment Card C'est une propriété optionnelle. Il utilise l'option .oneStep par défaut.
appearanceCe champ optionnel définit l'apparence du paiement. Par défaut, il utilise les styles Yuno.
saveCardEnabledCe champ optionnel vous permet de choisir si la case à cocher Enregistrer la carte est affichée sur les flux de carte. Il est false par défaut.
keepLoaderCe champ optionnel permet de contrôler quand masquer le loader. S'il est défini sur true, la fonction hideLoader() doit être appelée pour masquer le loader. Il est défini sur false par défaut.
hideCardholderNameCe champ facultatif vous permet de masquer le champ du nom du titulaire de la carte dans le formulaire de carte. Lorsqu'il est défini sur true, le champ du nom du titulaire de la carte n'est pas affiché. Lorsqu'il n'est pas spécifié ou défini sur false, le champ du nom du titulaire de la carte s'affiche (comportement par défaut). Le fait de masquer ce champ n'a aucune incidence sur le PAN, la date d'expiration, la collecte du CVV, la logique BIN ou les validations 3DS/fournisseur. Le commerçant est tenu de s'assurer que le nom du titulaire de la carte est fourni lorsque son fournisseur de services de paiement l'exige.
📘

Accès à votre Clé API

Vous pouvez récupérer votre Clé API depuis la section Développeurs dans le Tableau de Bord Yuno.

Créer une Session de Paiement

Avant de démarrer le processus de paiement, vous devez créer une checkout_session à l'aide du endpoint  Créer une session de paiement . Cette session initialise le flux de paiement et sera utilisée à l'étape suivante.

💳

Contrôle de l'authentification par rapport à la capture par envoi payment_method.detail.card.capture dans la session de paiement : false = autoriser uniquement, true = capturer immédiatement.

Paramètres clés

ParamètresRequisDescription
amountOuiL'objet montant de la transaction principale contenant la currency (code ISO 4217) et la value (montant numérique dans cette devise).
workflowOuiDéfinissez la valeur sur SDK_SEAMLESS afin que le SDK puisse effectuer correctement le flux de paiement.
alternative_amountNonUne représentation de la devise alternative du montant de la transaction avec la même structure que amount (currency  et  value). Utile pour les scénarios multi-devises, comme l'affichage des prix aux clients dans leur devise préférée (par exemple, USD) tout en traitant le paiement dans la devise locale (par exemple, COP).

Étape 3 : Lancez le processus de paiement et de validation de la commande.

Le processus de Paiement et de paiement seamless est initié avec une seule méthode :  startPaymentSeamlessLite. Dans le ViewControlleroù Yuno sera affiché, appelez la méthode Yuno.startPaymentSeamlessLite() . Vous pouvez utiliser la méthode avec async/await ou avec des Rappels.

func startPaymentSeamlessLite(
    with params: SeamlessParams,
 paymentSelected: PaymentMethodSelected,
 showPaymentStatus: Bool = true)
 async -> Result
func startPaymentSeamlessLite(
    with params: SeamlessParams,
 paymentSelected: PaymentMethodSelected,
 showPaymentStatus: Bool = true,
    callback: @escaping ((Result) -> Void)
)

Des paramètres additionnels sont requis pour la version seamless. Ceux-ci incluent :

  • PaymentMethodSelected: Le Token stocké (vaulted token) et/ou la méthode de paiement que le client utilisera pour effectuer le paiement.
protocol PaymentMethodSelected {
    var vaultedToken: String? { get }
    var paymentMethodType: String { get }
}
  • SeamlessParams
class SeamlessParams {
    var checkoutSession: String
    var country_code: String
    var language: String?
    var viewController: UIViewController?
}

Paramètres

Le tableau suivant décrit chaque paramètre de SeamlessParams:

ParamètresDescription
checkoutSessionFait référence à la session de paiement du paiement actuel.
country_codeCe paramètre détermine le pays pour lequel le processus de paiement est configuré. La liste complète des pays pris en charge et de leur code pays est disponible sur la page Couverture des pays.
languageDéfinit la langue à utiliser dans les formulaires de paiement. Vous pouvez choisir l'une des options linguistiques disponibles :
  • Es (Espagnol)
  • En (Anglais)
  • Po (Portugais)
  • Ph (Philippin)
  • In (Indonésien)
  • Ma (Malais)
  • Th (Thaïlandais)
  • zh-TW (chinois (traditionnel, taïwanais))
  • zh-CN (chinois (simplifié, Chine))
  • vi (vietnamien)
  • fr (français)
  • pl (polonais)
  • it (Italien)
  • de (allemand)
  • ru (russe)
  • tr (turc)
  • nl (néerlandais)
  • sv (suédois)
  • ko (coréen)
  • ja (japonais)
viewControllerCette propriété représente le UIViewController utilisé pour présenter le flux de paiement. Même si cette propriété reste facultative pour des raisons de compatibilité ascendante, vous devez fournir un contrôleur visible afin que le SDK puisse présenter correctement son interface utilisateur.
🚧

Exigences de Concurrence Swift 6

Si vous utilisez Swift 6, vous devrez implémenter le protocole YunoPaymentDelegate avec des considérations spécifiques sur la concurrence. Swift 6 introduit des exigences de sécurité des fils d'exécution plus strictes qui affectent la manière dont vous implémentez les délégués. Consultez la section  Implémentation de YunoPaymentDelegate avec la Concurrence Swift 6  pour les options d'implémentation détaillées et les meilleures pratiques.

Étape 4 : Gérer le statut du paiement (facultatif)

❗️

Liens Profonds et Mercado Pago Checkout Pro

Cette étape n'est requise que si vous utilisez une méthode de paiement qui repose sur des liens profonds ou Mercado Pago Checkout Pro. Si vos méthodes de paiement n'utilisent pas de liens profonds, vous pouvez ignorer cette étape.

Certaines méthodes de paiement font sortir les utilisateurs de votre application pour finaliser la transaction. Une fois le paiement terminé, l'utilisateur est redirigé vers votre application à l'aide d'un lien profond. Le SDK utilise ce lien profond pour vérifier ce qui s'est passé (paiement réussi, échec ou annulation) et peut afficher un écran de statut à l'utilisateur.

Pour gérer cela, vous devez mettre à jour votre AppDelegate pour transmettre l'URL entrante au SDK Yuno. Cela permet au SDK de lire le résultat et éventuellement d'afficher le statut du paiement. Le code suivant montre comment l'ajouter à votre application :

func application(_ app: UIApplication,
                 open url: URL,
                 options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {

  guard url.scheme == "yunoexample" else { return false }

  return Yuno.receiveDeeplink(url, showStatusView: true)
}

Ce code est à l'écoute des liens profonds qui ouvrent votre application. Lorsqu'une URL est reçue, il vérifie si le schéma correspond à celui que vous avez utilisé le callback_url lors de la configuration de la session de paiement. Si cela correspond, l'URL est transmise au SDK Yuno via Yuno.receiveDeeplink(...). Le SDK lit ensuite le résultat du paiement et, si showStatusView est fixé à true, affiche l'écran de statut approprié à l'utilisateur.

Assurez-vous que le url.scheme dans ce code correspond au callback_url que vous avez fourni lors de la création de la checkout_session.

État de la transaction

Une fois le paiement effectué, le SDK peut renvoyer différents états de la transaction. La liste de tous les états possibles et leurs descriptions sont présentées dans le tableau suivant :

État de la transactionDescription
successIndique que la transaction ou le processus de paiement a été complété avec succès.
failIndique que la transaction ou le processus de paiement a échoué. Cela signifie qu'il y a eu une erreur ou un problème au cours de la procédure de paiement qui l'a empêchée de s'achever avec succès.
processingCet état Indique que la transaction est en cours de traitement. Il est généralement utilisé lorsqu'il y a un retard dans le traitement du paiement, par exemple dans l'attente de l'approbation d'un service tiers ou d'une institution financière.
rejectCet état indique que la transaction a été rejetée. Le rejet peut avoir diverses raisons, telles qu'une insuffisance de fonds, une activité frauduleuse ou des demandes qui enfreignent des règles ou des politiques spécifiques.
internalErrorCela signifie qu'une erreur inattendue s'est produite dans le système ou l'infrastructure qui gère le processus de paiement. Cet état suggère un problème du côté du serveur ou du backend plutôt qu'un problème lié à l'entrée ou à la demande de l'utilisateur.
userCancellIndique que l'utilisateur a volontairement annulé ou abandonné le processus de paiement.Il est généralement utilisé lorsque l'utilisateur a la possibilité d'annuler ou d'abandonner la procédure de paiement.

Validation du statut de paiement

Cette section explique comment le SDK gère le statut des paiements lorsque les utilisateurs annulent ou quittent les flux de paiement, et comment le statut du SDK est lié au statut des paiements backend dans ces scénarios.

Synchroniser les modes de paiement (Apple Pay)

Pour les méthodes de paiement synchronisées telles qu'Apple Pay, lorsqu'un utilisateur annule ou ferme l'interface utilisateur du portefeuille avant de recevoir la réponse du prestataire de services de paiement (PSP) :

  • Statut du SDK: Retours userCancell (CANCELLED)
  • Statut du paiement backend: Restes PENDING jusqu'à l'expiration du délai PSP ou l'annulation par le commerçant
  • Important: Le SDK ne renverra pas reject ou processing dans ce scénario

Cela garantit que le paiement backend reste en attente et peut être correctement traité par le système du commerçant.

Modes de paiement asynchrones (PIX et méthodes basées sur les QR codes)

Pour les méthodes de paiement asynchrones telles que PIX, lorsqu'un utilisateur ferme la fenêtre du code QR (clique sur X) avant d'avoir terminé le paiement :

  • Statut du SDK: Retours PENDING, éventuellement avec un sous-statut tel que CLOSED_BY_USER
  • Statut du paiement backend: Restes PENDING et le code QR reste valide jusqu'à son expiration.
  • Réutilisation de la session de paiement: la réouverture de la même session de paiement permet d'afficher le même code QR valide.
  • Pas d'annulation automatique: le paiement PIX n'est pas automatiquement annulé lorsque l'utilisateur ferme la fenêtre QR.

Ce comportement permet aux utilisateurs de revenir au flux de paiement et de terminer la transaction à l'aide du même code QR avant son expiration.

Paiements asynchrones expirés

Si un code QR PIX expire naturellement :

  • Statut du backend: Mis à jour vers EXPIRED
  • Statut du SDK: Les rappels SDK et endpoints d'interrogation endpoints EXPIRED de manière cohérente

Cela garantit que les commerçants reçoivent des informations précises sur le statut lorsqu'un moyen de paiement a expiré.

'état de la transaction peut être géré de deux manières lors de l'utilisation de la méthode startPaymentSeamlessLite : Indicateurs booléens

  • Async/Await: Utilisez l'approche async/await pour un flux plus rationalisé. Cette méthode renvoie un résultat de manière asynchrone, ce qui facilite la lecture et la gestion du code.
  • Rappel (callback) : Vous pouvez gérer l'état de la transaction par le biais d'une fonction de rappel, ce qui permet une exécution immédiate dès que le résultat est disponible.

Les deux options offrent une certaine flexibilité en fonction de l'approche que vous préférez pour le code asynchrone.

énumération Résultat {
    cas rejet, succès, échec, traitement, internalError, userCancell
 utilisateur
}

Fonctionnalités Complémentaires

Le SDK iOS de Yuno fournit des services et des configurations supplémentaires que vous pouvez utiliser pour améliorer l'expérience de vos clients. Utilisez les personnalisations du SDK pour modifier l'apparence du SDK afin qu'il corresponde à votre marque ou pour configurer le loader.

  • Loader: Contrôlez l'utilisation du Loader par le biais des options de configuration du SDK.
  • Sauvegarder la carte pour des paiements ultérieurs : En outre, vous pouvez afficher une case à cocher pour enregistrer ou inscrire des cartes à l'aide de cardSaveEnable: true. Vous trouverez ci-dessous des exemples de cases à cocher pour les deux rendus de formulaires de cartes.
Exemple de case à cocher
  • Vous pouvez également choisir l'une des options de rendu pour le formulaire de la carte. Ci-dessous, vous trouverez des captures d'écran présentant la différence entre les options de rendu du formulaire de carte  cardFormType ONE_STEP  et  STEP_BY_STEP.
Options de rendu

Implémentation de YunoPaymentDelegate avec la Concurrence Swift 6

Swift 6 introduit des exigences de concurrence plus strictes qui affectent la manière dont vous implémentez le protocole YunoPaymentDelegate . Cette section explique les défis et fournit des solutions pour différents scénarios d'implémentation.

📘

Comprendre la Concurrence dans Swift 6

La concurrence est la capacité de votre application à gérer plusieurs tâches simultanément. Avec Swift 6, les règles de concurrence sont devenues plus rigoureuses afin d'améliorer la stabilité de l'application et de prévenir les plantages. Cela signifie que votre code doit être structuré plus soigneusement pour garantir la sécurité des fils d'exécution et une gestion appropriée des tâches.

Le Problème

Avec Swift 6, les protocoles qui héritent de Sendable exigent que toutes leurs implémentations soient sécurisées pour les fils d'exécution. Cela génère des avertissements lorsque l'on implémente le délégué dans des classes marquées avec @MainActor.

La sécurité des fils d'exécution signifie que votre code peut être appelé en toute sécurité à partir de plusieurs fils d'exécution sans provoquer de plantage ou de comportement inattendu. @MainActor assure l'exécution du code sur le fil d'exécution principal (fil d'exécution de l'interface utilisateur).

Notre Décision de Conception

Nous ne marquons pas les protocoles comme @MainActor car :

  • Cela obligerait toutes les implémentations à être MainActor compatible
  • Cela réduirait la flexibilité des commerçants qui n'utilisent pas MainActor
  • Chaque implémentation a des besoins de concurrence différents

Responsabilité du commerçant

Il incombe au commerçant de gérer la concurrence en fonction de son implémentation. Vous trouverez ci-dessous trois approches différentes que vous pouvez utiliser en fonction de vos besoins spécifiques.

Option 1 : Propriétés immuables

Cette approche utilise des propriétés immuables qui sont automatiquement sécurisées pour les fils d'exécution, les rendant idéales pour des configurations simples. Elle est la mieux adaptée aux applications simples avec des valeurs de configuration fixes qui ne changent pas pendant l'exécution.

@MainActor
class MyViewController: UIViewController, YunoPaymentDelegate {
    
    private let _countryCode = "CO"
    private let _language = "EN"
    
    nonisolated var countryCode: String { _countryCode }
    nonisolated var language: String? { _language }
    nonisolated var checkoutSession: String { _checkoutSession }
    
    nonisolated func yunoPaymentResult(_ result: Yuno.Result) {
        Task { @MainActor in
        }
    }
}

Option 2 : Propriétés mutables avec MainActor.assumeIsolated

Cette approche, idéale pour les applications où les valeurs de configuration pourraient changer pendant l'exécution (comme les préférences utilisateur), permet des propriétés mutables tout en maintenant la sécurité des fils d'exécution grâce à l'utilisation de MainActor.assumeIsolated.

@MainActor
class MyViewController: UIViewController, YunoPaymentDelegate {
    
    @Published var configLanguage: String = "EN"
    @Published var configCountryCode: String = "CO"
    
    nonisolated var language: String? {
        MainActor.assumeIsolated { configLanguage }
    }
    
    nonisolated var countryCode: String {
        MainActor.assumeIsolated { configCountryCode }
    }
}

Option 3 : Pour les non MainActor cours

Cette approche convient aux classes de service qui ne nécessitent pas MainActor isolement, ce qui le rend idéal pour les services d'arrière-plan ou les classes utilitaires qui n'interagissent pas avec l'interface utilisateur.

class MyService: YunoPaymentDelegate {
    
    let countryCode: String
    let language: String?
    let checkoutSession: String
    let viewController: UIViewController?
    
    init(countryCode: String, language: String?, checkoutSession: String, viewController: UIViewController?) {
        self.countryCode = countryCode
        self.language = language
        self.checkoutSession = checkoutSession
        self.viewController = viewController
    }
    
    func yunoPaymentResult(_ result: Yuno.Result) {
    }
}

⚠️ Considérations Importantes

Lors de l'implémentation de la concurrence dans votre délégué, gardez ces points clés à l'esprit :

  • MainActor.assumeIsolated: À utiliser uniquement lorsque vous garantissez qu'il est appelé depuis MainActorIl s'agit d'un mécanisme de sécurité qui indique à Swift « fais-moi confiance, je sais que cela s'exécute sur le thread principal ».
  • nonisolated: Cela signifie qu'il est accessible depuis n'importe quel thread, il doit donc être thread-safe. Utilisez cette option lorsque vos propriétés ou méthodes ne dépendent pas de l'état de l'interface utilisateur.
  • viewController: Reste en @MainActor car il doit toujours être accédé depuis le fil d'exécution principal. Les composants de l'interface utilisateur doivent toujours s'exécuter sur le fil principal pour éviter les plantages.