Seamless SDK (pagamento iOS)

Nesta página, você encontrará todas as etapas para adicionar, configurar e usar o Seamless iOS SDK para fazer pagamentos em seu projeto iOS.

👍

SDK recomendado

Recomendamos o uso do iOS Seamless SDK para uma experiência de integração tranquila. Essa opção oferece uma solução de pagamento flexível com componentes de IU pré-criados e opções de personalização.

Etapa 1: Inclua a biblioteca em seu projeto

Você pode adicionar a biblioteca usando o CocoaPods ou o Swift Package Manager.

CocoaPods

Para adicionar o Yuno SDK ao seu projeto iOS, você precisa instalar o Yuno SDK. Se você não tiver um Podfile, siga o guia CocoaPods para criar um. Depois de criar o Podfile, você integrará o Yuno SDK ao Cocoapods adicionando a linha abaixo ao seu Podfile.

pod 'YunoSDK', '~> 1.19.0'

Depois, você precisa executar a instalação:

instalação do pod

Gerenciador de pacotes Swift

Se você estiver usando o Swift Package Manager, adicione o Yuno SDK como uma dependência, conforme apresentado no bloco de código a seguir:

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

Etapa 2: Initialize o SDK com a chave pública

Para começar a utilizar o checkout Yuno iOS Seamless, primeiro você precisa obter o ID do aplicativo Yuno e a chave API pública. Em seguida, importe e initialize conforme apresentado no seguinte trecho de código:

import YunoSDK

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

UISceneDelegate

Certifique-se de que, se seu aplicativo usar um UISceneDelegateO código de inicialização do Yuno é colocado em seu SceneDelegate.

O checkout contínuo permite que você configure a aparência do SDK. É uma etapa opcional que você configura por meio da classe YunoConfig. Para definir as configurações, use o seguinte bloco de código para configurar os elementos disponíveis:

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

Configure o SDK com as seguintes opções:

ParâmetroDescrição
cardFormTypeEsse campo pode ser usado para escolher Payment e Enrollment Card fluxo. É uma propriedade opcional. Ela usa o .oneStep por padrão.
appearanceEsse campo opcional define a aparência do checkout. Por padrão, ele usa os estilos Yuno.
saveCardEnabledEsse campo opcional permite que você escolha se a caixa de seleção Salvar cartão é exibida nos fluxos de cartões. Por padrão, ela é falsa.
keepLoaderEsse campo opcional fornece controle sobre quando ocultar o carregador. Se definido como true, o hideLoader() deve ser chamada para ocultar o carregador. Por padrão, ela é definida como false.
hideCardholderNameEste campo opcional permite ocultar o campo do nome do titular do cartão no formulário do cartão. Quando definido como true, o campo do nome do titular do cartão não é exibido. Quando não especificado ou definido como false, o campo do nome do titular do cartão é exibido (comportamento padrão). Ocultar o campo não afeta o PAN, a validade, a coleta do CVV, a lógica do BIN ou as validações 3DS/provedor. O comerciante é responsável por garantir que o nome do titular do cartão seja fornecido quando exigido pelo seu provedor de pagamentos.
📘

Como acessar sua chave de API

Você pode recuperar sua chave de API na seção Desenvolvedores no Painel de Controle do Yuno.

Criar uma sessão de checkout

Antes de iniciar o processo de pagamento, você precisa criar uma conta checkout_session usando o Criar sessão de checkout endpoint. Essa sessão inicializa o fluxo de pagamento e será usada na próxima etapa.

💳

Controle a autenticação versus captura enviando payment_method.detail.card.capture na sessão de checkout: false = apenas autorizar, true = capturar imediatamente.

Parâmetros-chave

ParâmetroNecessárioDescrição
amountSimO objeto de valor da transação primária que contém currency (código ISO 4217) e value (valor numérico nessa moeda).
workflowSimDefina o valor como SDK_SEAMLESS para que o SDK possa concluir o fluxo de pagamento corretamente.
alternative_amountNãoUma representação de moeda alternativa do valor da transação com a mesma estrutura de amount (currency e value). Útil para cenários com várias moedas, como a exibição de preços para os clientes em sua moeda preferida (por exemplo, dólar americano) e o processamento do pagamento na moeda local (por exemplo, COP).

Passo 3: Inicie o processo de checkout e pagamento

O processo contínuo de checkout e pagamento é iniciado com um único método startPaymentSeamlessLite. No ViewControlleronde o Yuno será exibido, ligue para o Yuno.startPaymentSeamlessLite() método. Você pode usar o método com async/await ou usando callbacks:

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

Parâmetros adicionais são necessários para a versão sem costura. Esses parâmetros incluem:

  • PaymentMethodSelected: O token armazenado token o método de pagamento que o cliente utilizará para efetuar o pagamento.
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

A tabela a seguir descreve cada parâmetro de SeamlessParams:

ParâmetroDescrição
checkoutSessionRefere-se à sessão de checkout do pagamento atual.
country_codeEsse parâmetro determina o país para o qual o processo de pagamento está sendo configurado. A lista completa de países compatíveis e seu código de país está disponível na página Cobertura do país.
languageDefine o idioma a ser usado nos formulários de pagamento. Você pode defini-lo como uma das opções de idioma disponíveis:
  • es (espanhol)
  • en (inglês)
  • pt (português)
  • fil (filipino)
  • id (indonésio)
  • ms (malaio)
  • th (tailandês)
  • zh-TW (chinês (tradicional, Taiwan))
  • zh-CN (chinês (simplificado, China))
  • vi (vietnamita)
  • fr (francês)
  • pl (polonês)
  • it (italiano)
  • de (alemão)
  • ru (russo)
  • tr (turco)
  • nl (holandês)
  • sv (sueco)
  • ko (coreano)
  • ja (japonês)
viewControllerEssa propriedade representa o UIViewController usado para apresentar o fluxo de pagamento. Embora a propriedade permaneça opcional para compatibilidade com versões anteriores, você deve fornecer um controlador visível para que o SDK possa apresentar sua IU corretamente.
🚧

Requisitos de simultaneidade do Swift 6

Se estiver usando o Swift 6, você precisará implementar o YunoPaymentDelegate com considerações específicas de simultaneidade. O Swift 6 apresenta requisitos mais rigorosos de segurança de thread que afetam a forma como você implementa os delegados. Consulte a seção Implementação YunoPaymentDelegate com Swift 6 Concurrency para obter opções de implementação detalhadas e práticas recomendadas.

Etapa 4: Lidar com o status do pagamento (opcional)

❗️

Deep Links e Mercado Pago Checkout Pro

Essa etapa só é necessária se você estiver usando um método de pagamento que dependa de links diretos ou do Mercado Pago Checkout Pro. Se seus métodos de pagamento não usarem deep links, você pode pular esta etapa.

Alguns métodos de pagamento levam os usuários para fora do seu aplicativo para concluir a transação. Depois que o pagamento é concluído, o usuário é redirecionado de volta ao seu aplicativo usando um deep link. O SDK usa esse link direto para verificar o que aconteceu, verificando se o pagamento foi bem-sucedido, falhou ou foi cancelado, e pode mostrar uma tela de status para o usuário.

Para lidar com isso, você precisa atualizar seu AppDelegate para passar a URL de entrada de volta para o Yuno SDK. Isso permite que o SDK leia o resultado e, opcionalmente, exiba o status do pagamento. O trecho de código a seguir mostra como você pode adicioná-lo ao seu aplicativo:

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

Esse código escuta os links diretos que abrem seu aplicativo. Quando uma URL é recebida, ele verifica se o esquema corresponde ao que você usou na função callback_url durante a configuração da sessão de checkout. Se corresponder, a URL é passada para o Yuno SDK usando Yuno.receiveDeeplink(...). O SDK então lê o resultado do pagamento e, se showStatusView é definido como truemostra a tela de status apropriada para o usuário.

Certifique-se de que o url.scheme nesse código corresponde ao callback_url que você forneceu ao criar o checkout_session.

Estado da transação

Depois que o pagamento é concluído, o SDK pode retornar diferentes estados de transação. A lista de todos os estados possíveis e suas descrições são apresentadas na tabela a seguir:

Estado da transaçãoDescrição
successIndica que a transação ou o processo de payment foi concluído com êxito.
failEsse estado indica que a transação ou o processo de pagamento falhou. Isso significa que houve um erro ou problema durante o processo de pagamento que impediu que ele fosse concluído com êxito.
processingIndica que a transação ou o processo de pagamento está sendo processado no momento. Normalmente, é usado quando há um atraso no processamento do pagamento, como a espera de aprovação de um serviço de terceiros ou de uma instituição financeira.
rejectEsse estado indica que a transação foi rejeitada. A rejeição pode ocorrer por vários motivos, como fundos insuficientes, atividade fraudulenta ou solicitações que violam regras ou políticas específicas.
internalErrorIsso significa que ocorreu um erro inesperado no sistema ou na infraestrutura que está lidando com o processo de pagamento. Esse estado sugere um problema no servidor ou no backend, e não um problema com a entrada ou a solicitação do usuário.
userCancellEsse estado indica que o usuário cancelou ou abortou voluntariamente a transação ou o processo de pagamento. Normalmente, é usado quando o usuário tem a opção de cancelar ou abandonar o processo de pagamento.

Validação do status do pagamento

Esta seção explica como o SDK lida com o status do pagamento quando os usuários cancelam ou abandonam os fluxos de pagamento e como o status do SDK se relaciona com o status do pagamento no back-end nesses cenários.

Sincronizar métodos de pagamento (Apple Pay)

Para métodos de pagamento síncronos, como o Apple Pay, quando um usuário cancela ou fecha a interface do usuário da carteira antes de receber a resposta do provedor de serviços de pagamento (PSP):

  • Status do SDK: Devoluções userCancell (CANCELLED)
  • Status do pagamento no backend: Restos mortais PENDING até ao tempo limite do PSP ou ao cancelamento pelo comerciante
  • Importante: O SDK não retornará reject ou processing nesse cenário

Isso garante que o pagamento no backend permaneça em estado pendente e possa ser tratado adequadamente pelo sistema do comerciante.

Formas de pagamento assíncronas (PIX e métodos baseados em QR)

Para métodos de pagamento assíncronos como o PIX, quando um usuário fecha a janela do código QR (clica em X) antes de concluir o pagamento:

  • Status do SDK: Devoluções PENDING, opcionalmente com um subestatuto, como CLOSED_BY_USER
  • Status do pagamento no backend: Restos mortais PENDING e o código QR permanece válido até ao seu prazo de validade.
  • Reutilização da sessão de checkout: reabrir a mesma sessão de checkout pode exibir o mesmo código QR válido.
  • Sem cancelamento automático: O pagamento PIX não é cancelado automaticamente quando o usuário fecha a janela do QR.

Esse comportamento permite que os usuários retornem ao fluxo de pagamento e concluam a transação usando o mesmo código QR antes que ele expire.

Pagamentos assíncronos expirados

Se um código QR PIX expirar naturalmente:

  • Status do backendAtualizado para EXPIRED
  • Status do SDK: endpoints de chamadas de retorno do SDK e endpoints de pesquisa EXPIRED de forma consistente

Isso garante que os comerciantes recebam informações precisas sobre o status quando um método de pagamento expirar.

O estado da transação pode ser tratado de duas maneiras ao usar o startPaymentSeamlessLite método:

  • Async/Await: use a abordagem async/await para obter um fluxo mais simplificado. Esse método retorna um resultado de forma assíncrona, tornando o código mais fácil de ler e gerenciar.
  • Retorno de chamada: Você pode tratar o estado da transação por meio de uma função de retorno de chamada, permitindo a execução imediata quando o resultado estiver disponível.

Ambas as opções oferecem flexibilidade, dependendo de sua abordagem preferida para o código assíncrono.

enum Result {
    case reject, success, fail, processing, internalError, userCancell

}

Recursos complementares

O SDK do Yuno iOS oferece serviços e configurações adicionais que você pode usar para melhorar a experiência dos clientes. Use as personalizações do SDK para alterar a aparência do SDK de acordo com sua marca ou para configurar o carregador.

  • Carregador: Controle o uso do carregador por meio das opções de configuração do SDK.
  • Salvar cartão para pagamentos futuros: Além disso, você pode exibir uma caixa de seleção para salvar ou registrar cartões usando cardSaveEnable: true. Abaixo, você pode encontrar exemplos da caixa de seleção para ambas as renderizações do formulário de cartão.
Exemplo de caixa de seleção
  • Você também pode escolher uma das opções de renderização para o formulário do cartão. Abaixo, você encontra capturas de tela que apresentam a diferença entre as opções de renderização cardFormType ONE_STEP e STEP_BY_STEP.
Opções de renderização

Implementação YunoPaymentDelegate com Swift 6 Concurrency

O Swift 6 apresenta requisitos de simultaneidade mais rígidos que afetam a forma como você implementa o YunoPaymentDelegate protocolo. Esta seção explica os desafios e oferece soluções para diferentes cenários de implementação.

📘

Entendendo a simultaneidade no Swift 6

A simultaneidade é a capacidade do seu aplicativo de gerenciar várias tarefas simultaneamente. Com o Swift 6, as regras de simultaneidade se tornaram mais rigorosas para aumentar a estabilidade do aplicativo e evitar falhas. Isso significa que seu código deve ser estruturado com mais cuidado para garantir a segurança dos threads e o gerenciamento adequado das tarefas.

O problema

Com o Swift 6, os protocolos que herdam do Sendable exigem que todas as suas implementações sejam thread-safe. Isso gera avisos ao implementar o delegado em classes marcadas como @MainActor.

Thread-safe significa que seu código pode ser chamado com segurança de vários threads sem causar falhas ou comportamento inesperado. @MainActor garante que o código seja executado no thread principal (thread da interface do usuário).

Nossa decisão de design

Não marcamos protocolos como @MainActor porque:

  • Isso forçaria todas as implementações a serem MainActor compatível
  • Isso reduziria a flexibilidade para os comerciantes que não usam MainActor
  • Cada implementação tem necessidades de simultaneidade diferentes

Responsabilidade do comerciante

É responsabilidade do comerciante lidar com a simultaneidade de acordo com sua implementação. Abaixo estão três abordagens diferentes que você pode usar, dependendo de suas necessidades específicas.

Opção 1: Propriedades imutáveis

Essa abordagem usa propriedades imutáveis que são automaticamente thread-safe, o que as torna ideais para configurações simples. Ela é mais adequada para aplicativos simples com valores de configuração fixos que não mudam durante o tempo de execução.

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

Opção 2: Propriedades mutáveis com MainActor.assumeIsolated

Essa abordagem, melhor para aplicativos em que os valores de configuração podem ser alterados durante o tempo de execução (como as preferências do usuário), permite propriedades mutáveis e, ao mesmo tempo, mantém a segurança do thread usando 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 }
    }
}

Opção 3: Para não MainActor aulas

Essa abordagem é adequada para classes de serviço que não requerem MainActor o que o torna melhor para serviços em segundo plano ou classes utilitárias que não interagem com a interface do usuário.

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

⚠️ Considerações importantes

Ao implementar a simultaneidade em seu delegado, tenha em mente estes pontos-chave:

  • MainActor.assumeIsolated: Use apenas quando você garantir que ele seja chamado de MainActor. Esse é um mecanismo de segurança que diz ao Swift "confie em mim, eu sei que isso está sendo executado no thread principal".
  • nonisolated: Significa que pode ser acessado de qualquer thread, portanto, deve ser thread-safe. Use-o quando suas propriedades ou métodos não dependerem do estado da interface do usuário.
  • viewController: Permanece como @MainActor porque ele deve ser sempre acessado pelo thread principal. Os componentes da interface do usuário devem sempre ser executados no thread principal para evitar falhas.