Seamless SDK (Pago iOS)

En esta página, encontrará todos los pasos para agregar, configurar y usar el Seamless iOS SDK para realizar pagos en su proyecto iOS.

👍

SDK recomendado

Recomendamos utilizar el Seamless SDK iOS para una experiencia de integración sin problemas. Esta opción ofrece una solución de pago flexible con componentes de interfaz de usuario predefinidos y opciones de personalización.

Paso 1: Incluya la biblioteca en tu proyecto

Puede agregar la biblioteca usando CocoaPods o Swift Package Manager.

CocoaPods

Para añadir el SDK de Yuno a tu proyecto iOS, necesitas instalar el SDK de Yuno. Si no tienes un Podfile, sigue la guía de CocoaPods para crear uno. Después de crear el Podfile, integrarás el SDK de Yuno con Cocoapods añadiendo la siguiente línea a tu Podfile.

pod 'YunoSDK', '~> 1.19.0'

Después, necesita ejecutar la instalación:

instalar pod

Administrador de Paquetes Swift

Si estás usando el gestor de paquetes Swift, añade el SDK de Yuno como dependencia, tal y como se presenta en el siguiente bloque de código:

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

Paso 2: Initialize el SDK con la clave pública

Para empezar a utilizar el sistema de pago Yuno iOS Seamless, primero debes obtener tu ID de aplicación Yuno y tu clave API pública. A continuación, importa e initialize tal y como se muestra en el siguiente fragmento de código:

import YunoSDK

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

UISceneDelegate

Asegúrese de que si su aplicación utiliza un UISceneDelegateEl código de inicialización de Yuno se coloca dentro de su SceneDelegate.

La verificación sin interrupciones le permite configurar la apariencia del SDK. Es un paso opcional que se configura a través de la clase YunoConfig. Para configurar configuraciones, utiliza el siguiente bloque de código para configurar los elementos disponibles:

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

Configura el SDK con las siguientes opciones:

ParámetroDescripción
cardFormTypeEste campo se puede utilizar para elegir los flujos Payment y Enrollment Card . Es una propiedad opcional. Utiliza la opción .oneStep por defecto.
appearanceEste campo opcional define la apariencia de la caja. De forma predeterminada, utiliza los estilos de Yuno.
saveCardEnabledEste campo opcional le permite elegir si la casilla de verificación Guardar tarjeta se muestra en los flujos de tarjetas. Por defecto es falso.
keepLoaderEste campo opcional proporciona control sobre cuándo ocultar el cargador. Si se establece en true, la hideLoader() se debe llamar a la función para ocultar el cargador. De manera predeterminada, está configurada como falsa.
hideCardholderNameEste campo opcional te permite ocultar el campo del nombre del titular de la tarjeta en el formulario de la tarjeta. Cuando se establece en true, el campo del nombre del titular de la tarjeta no se muestra. Cuando no se especifica o se establece en false, se muestra el campo del nombre del titular de la tarjeta (comportamiento predeterminado). Ocultar el campo no afecta al PAN, la caducidad, la recopilación del CVV, la lógica BIN ni las validaciones 3DS/proveedor. El comerciante es responsable de garantizar que se proporcione el nombre del titular de la tarjeta cuando lo requiera tu pago .
📘

Cómo acceder a su clave API

Puedes recuperar tu Clave API desde la sección de Desarrolladores en el Panel de Control de Yuno.

Crear una sesión de checkout

Antes de comenzar el pago proceso, necesitas crear un checkout_session utilizando la función del endpoint Crear sesión de pago endpoint Esta sesión inicializa el pago flujo y se utilizará en el siguiente paso.

💳

Controlar la autenticación frente a la captura mediante el envío payment_method.detail.card.capture en la sesión de pago: false = solo autorizar, true = capturar inmediatamente.

Parámetros clave

ParámetroRequeridoDescripción
amountEl objeto de importe de la transacción primaria que contiene currency (código ISO 4217) y value (importe numérico en esa moneda).
workflowEstablece el valor en SDK_SEAMLESS para que el SDK pueda completar correctamente el pago .
alternative_amountNoUna representación monetaria alternativa del importe de la transacción con la misma estructura que amount (currency y value). Útil para escenarios multidivisa, como mostrar los precios a los clientes en su divisa preferida (por ejemplo, USD) mientras se procesa el pago en la divisa local (por ejemplo, COP).

Paso 3: Inicia el pago de pago .

El proceso de pago y compra sin inconvenientes se inicia con un único método. startPaymentSeamlessLite. En el ViewController, donde se mostrará Yuno, llame al Yuno.startPaymentSeamlessLite() Método. Puedes utilizar el método con async/await o mediante devoluciones de llamadas:

func startPaymentSeamlessLite(
    con parámetros SeamlessParams,
   paymentSelected: PaymentMethodSelected,
   showPaymentStatus: Bool = true
) async -> Resultado
func startPaymentSeamlessLite(
    con parámetros SeamlessParams,
   paymentSelected: PaymentMethodSelected,
   showPaymentStatus: Bool = true,
    devolución de llamada: @escaping ((Resultado) -> Void)
)

Para la versión integrada se requieren parámetros adicionales, entre ellos:

  • PaymentMethodSelected: El token almacenado y/o el método de pago que utilizará el cliente para realizar el pago.
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?
}

Parámetros

La siguiente tabla describe cada parámetro de SeamlessParams:

ParámetroDescripción
checkoutSessionSe refiere a la sesión de checkout del pago actual.
country_codeEste parámetro determina el país para el que se está configurando el proceso de pago. La lista completa de países admitidos y su código de país está disponible en la página Cobertura de países.
languageDefine el idioma que se utilizará en los formularios de pago. Puede establecerlo en una de las opciones de idioma disponibles:
  • es (español)
  • en (Inglés)
  • pt (portugués)
  • fil (filipino)
  • id (indonesio)
  • ms (malayo)
  • th (tailandés)
  • zh-TW (chino (tradicional, Taiwán))
  • zh-CN (chino simplificado, China)
  • vi (vietnamita)
  • fr (francés)
  • pl (polaco)
  • eso (italiano)
  • de (alemán)
  • ru (ruso)
  • tr (turco)
  • nl (holandés)
  • sv (sueco)
  • ko (coreano)
  • ja (japonés)
viewControllerEsta propiedad representa la UIViewController utilizado para presentar el flujo de pago . Aunque la propiedad sigue siendo opcional por compatibilidad con versiones anteriores, debes proporcionar un controlador visible para que el SDK pueda presentar su interfaz de usuario correctamente.
🚧

Requisitos de concurrencia de Swift 6

Si está utilizando Swift 6, deberá implementar el YunoPaymentDelegate Protocolo con consideraciones específicas de concurrencia. Swift 6 introduce requisitos de seguridad de subprocesos más estrictos que afectan la implementación de delegados. Consulte Implementando YunoPaymentDelegate con concurrencia Swift 6 Sección para opciones de implementación detalladas y mejores prácticas.

Paso 4: Gestiona pago (opcional)

❗️

Enlaces profundos y Mercado Pago Checkout Pro

Este paso solo es necesario si utilizas un método de pago que utiliza enlaces profundos o Mercado Pago Checkout Pro. Si tus métodos de pago no utilizan enlaces profundos, puedes omitir este paso.

Algunos métodos de pago sacan a los usuarios de la app para completar la transacción. Una vez finalizado el pago, se redirige al usuario a la app mediante un enlace profundo. El SDK utiliza este enlace profundo para comprobar qué ha sucedido, verificando si el pago se realizó correctamente, falló o se canceló, y puede mostrar una pantalla de estado al usuario.

Para solucionar esto, necesita actualizar su AppDelegate Para devolver la URL entrante al SDK de Yuno. Esto permite que el SDK lea el resultado y, opcionalmente, muestre el estado del pago. El siguiente fragmento de código muestra cómo añadirlo a la aplicación:

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)
}

Este código detecta enlaces profundos que abren tu aplicación. Al recibir una URL, comprueba si el esquema coincide con el que usaste en el callback_url durante la configuración de la sesión de pago. Si coincide, la URL se pasa al SDK de Yuno mediante Yuno.receiveDeeplink(...). A continuación, el SDK lee el resultado del pago y, si showStatusView está configurado para true, muestra la pantalla de estado apropiada al usuario.

Asegúrese de que url.scheme En este código coincide con el callback_url que usted proporcionó al crear el checkout_session.

Estado de la transacción

Una vez completado el pago, el SDK puede devolver distintos estados de transacción. La lista de todos los estados posibles y sus descripciones se presentan en la siguiente tabla:

Estado de la transacciónDescripción
successIndica que el proceso de transacción o pago se ha completado con éxito.
failEste estado indica que la transacción o el proceso de pago ha fallado. Significa que hubo un error o problema durante el proceso de pago que impidió que se completara correctamente.
processingIndica que el proceso de transacción o pago se está procesando actualmente. Por lo general, se usa cuando hay un retraso en el procesamiento del pago, como esperar la aprobación de un servicio de terceros o una institución financiera.
rejectEste estado indica que la transacción ha sido rechazada. El rechazo puede ocurrir por varios motivos, como fondos insuficientes, actividad fraudulenta o solicitudes que violen reglas o políticas específicas.
internalErrorSignifica que ocurrió un error inesperado dentro del sistema o la infraestructura que maneja el proceso de pago. Este estado sugiere un problema en el servidor o en el backend en lugar de un problema con la entrada o solicitud del usuario.
userCancellEste estado indica que el usuario ha cancelado o abortado voluntariamente la transacción o el proceso de pago. Se utiliza normalmente cuando el usuario tiene la opción de cancelar o abandonar el proceso de pago.

Validación pago

En esta sección se explica cómo gestiona el SDK pago cuando los usuarios cancelan o abandonan pago , y cómo se relaciona el estado del SDK con pago del backend en estos casos.

Sincronizar pago (Apple Pay)

En el caso de pago sincrónicos, como Apple Pay, cuando tú cancelas o cierras la interfaz de usuario de la cartera antes de recibir la respuesta del proveedor pago (PSP):

  • Estado del SDK: Devoluciones userCancell (CANCELLED)
  • pago en el backend: Restos PENDING hasta el tiempo de espera de PSP o la cancelación por parte del comerciante
  • ImportanteEl SDK no devolverá reject o processing en este escenario

Esto garantiza que el pago backend pago en estado pendiente y pueda ser gestionado correctamente por el sistema del comerciante.

pago asíncronos (métodos basados en PIX y QR)

Para pago asíncronos como PIX, cuando tú cierras la ventana del código QR (haces clic en X) antes de completar el pago:

  • Estado del SDK: Devoluciones PENDING, opcionalmente con un subestado como CLOSED_BY_USER
  • pago en el backend: Restos PENDING y el código QR sigue siendo válido hasta su fecha de caducidad.
  • Reutilización de la sesión de pago: al volver a abrir la misma sesión de pago, se puede mostrar el mismo código QR válido.
  • Sin cancelación automática: El pago PIX no pago cancela automáticamente cuando tú cierras la ventana QR.

Este comportamiento permite a los usuarios volver al pago y completar la transacción utilizando el mismo código QR antes de que caduque.

Pagos asíncronos caducados

Si un código QR PIX caduca de forma natural:

  • Estado del backendActualizado a EXPIRED
  • Estado del SDK: Las devoluciones de llamada del SDK y endpoints de sondeo endpoints EXPIRED de manera constante

Esto garantiza que los comerciantes reciban información precisa sobre el estado cuando un pago ha caducado.

El estado de la transacción se puede manejar de dos maneras cuando se utiliza el startPaymentSeamlessLite :

  • Async/Await: Utilice el método async/await para un flujo más ágil. Este método devuelve un resultado de forma asíncrona, lo que facilita la lectura y gestión del código.
  • Devolución de llamada: Puede manejar el estado de la transacción a través de una función de devolución de llamada, permitiendo la ejecución inmediata una vez que el resultado esté disponible.

Ambas opciones brindan flexibilidad dependiendo de su enfoque preferido para el código asincrónico.

enum Resultado {
    caso rechazar, éxito, fallo, procesamiento, internalError, userCancell

}

Características complementarias

Yuno iOS SDK proporciona servicios y configuraciones adicionales que puedes utilizar para mejorar la experiencia de los clientes. Utiliza las personalizaciones del SDK para cambiar la apariencia del SDK para que coincida con tu marca o para configurar el cargador.

  • Cargador: Controla el uso del cargador mediante las opciones de configuración del SDK.
  • Guardar tarjeta para pagos futuros: Además, puede mostrar una casilla de verificación para guardar o inscribir tarjetas usando cardSaveEnable: true. A continuación, puede encontrar ejemplos de la casilla de verificación para ambos renderizados de formularios de tarjetas.
Ejemplo de casilla de verificación
  • También puedes elegir una de las opciones de renderizado para el formulario de la tarjeta. A continuación encontrará capturas de pantalla que presentan la diferencia entre cardFormType ONE_STEP y STEP_BY_STEP.
Opciones de renderizado

Implementando YunoPaymentDelegate con concurrencia Swift 6

Swift 6 introduce requisitos de concurrencia más estrictos que afectan la forma en que se implementa el YunoPaymentDelegate Protocolo. Esta sección explica los desafíos y ofrece soluciones para diferentes escenarios de implementación.

📘

Comprensión de la concurrencia en Swift 6

La concurrencia es la capacidad de tu aplicación para gestionar múltiples tareas simultáneamente. Con Swift 6, las reglas de concurrencia se han vuelto más estrictas para mejorar la estabilidad de la aplicación y evitar fallos. Esto significa que tu código debe estar estructurado con mayor cuidado para garantizar la seguridad de los subprocesos y una gestión adecuada de las tareas.

El problema

Con Swift 6, los protocolos que heredan de Sendable Requieren que todas sus implementaciones sean seguras para subprocesos. Esto genera advertencias al implementar el delegado en clases marcadas como @MainActor.

Seguro para subprocesos significa que tu código puede llamarse de forma segura desde múltiples subprocesos sin provocar fallas o comportamientos inesperados. @MainActor garantiza que el código se ejecute en el hilo principal (hilo de UI).

Nuestra decisión de diseño

No marcamos los protocolos como @MainActor porque:

  • Obligaría a todas las implementaciones a ser MainActor compatible
  • Reduciría la flexibilidad para los comerciantes que no utilizan MainActor
  • Cada implementación tiene diferentes necesidades de concurrencia

Responsabilidad del comerciante

Es responsabilidad del comerciante gestionar la concurrencia según su implementación. A continuación, se presentan tres enfoques diferentes que puede utilizar según sus necesidades específicas.

Opción 1: Propiedades inmutables

Este enfoque utiliza propiedades inmutables que son automáticamente seguras para subprocesos, lo que las hace ideales para configuraciones simples. Es ideal para aplicaciones sencillas con valores de configuración fijos que no cambian durante la ejecución.

@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
        }
    }
}

Opción 2: Propiedades mutables con MainActor.assumeIsolated

Este enfoque, ideal para aplicaciones en las que los valores de configuración pueden cambiar durante el tiempo de ejecución (como las preferencias del usuario), permite propiedades mutables y al mismo tiempo mantiene la seguridad de los subprocesos mediante el uso 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 }
    }
}

Opción 3: Para los que no MainActor clases

Este enfoque es adecuado para clases de servicio que no requieren MainActor por lo que es ideal para servicios en segundo plano o clases utilitarias que no interactúan con la interfaz de usuario.

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) {
    }
}

⚠️ Consideraciones importantes

Al implementar la concurrencia en su delegado, tenga en cuenta estos puntos clave:

  • MainActor.assumeIsolated: Utilízalo sólo cuando garantices que se llama desde MainActor. Se trata de un mecanismo de seguridad que le dice a Swift "confía en mí, sé que esto se está ejecutando en el hilo principal".
  • nonisolated: Significa que se puede acceder a él desde cualquier subproceso, por lo que debe ser seguro para subprocesos. Utilízalo cuando tus propiedades o métodos no dependan del estado de la interfaz de usuario.
  • viewController: Permanece como @MainActor porque siempre se debe acceder desde el hilo principal. Los componentes de la interfaz de usuario siempre deben ejecutarse en el hilo principal para evitar fallos.