3 - Realizar Pagamento

Configure o SDK Tap to Pay no iOS e realize pagamentos com Swift e SwiftUI, incluindo tratamento seguro de erros com sdkError.

Realize seu primeiro pagamento Tap to Pay no iPhone configurando o SDK e enviando uma cobrança em crédito.

① Configurar credenciais  →  ② Aplicar setConfig()  →  ③ Chamar pay()

Optional: para antecipar a inicialização e a criação de sessão antes da primeira cobrança, veja 5 - Otimizar pagamento.

Antes de começar

  • Conclua a instalação do SDK em 1 - Adicionar ao projeto.
  • Importe TapOnPhoneSDK no arquivo em que fará a integração.
  • Separe suas credenciais de marketplace, seller e accessKey antes de configurar o SDK.

Configurar

Neste passo, você vai definir as credenciais e aplicar a configuração inicial do SDK.

⚠️

Chame TapOnPhone.setConfig() apenas uma vez, no ponto da aplicação que fizer mais sentido para a preparação inicial do app.

1. Criar as credenciais

Crie uma variável para armazenar as credenciais usadas pelo SDK.

let credentials = TapOnPhoneCredentials(
    marketplace: "coloque seu marketplace aqui",
    seller: "coloque seu seller aqui",
    accessKey: "coloque seu access key aqui"
)

2. Optional: Personalizar a aparência com SdkConfig

Use SdkConfig para alterar o tema e o comportamento visual do SDK. Se você não quiser customizar as views agora, pode manter a configuração padrão.

let sdkConfig = SdkConfig()
Quando usar SdkConfig?

Use SdkConfig quando você quiser alinhar a experiência do SDK com a identidade visual do seu app. Se a personalização não for necessária neste momento, basta omitir esse parâmetro ao chamar setConfig().

3. Aplicar a configuração do SDK

Depois de criar as credenciais, chame TapOnPhone.setConfig() para aplicar a configuração do SDK com os parâmetros da sua integração.

TapOnPhone.setConfig(
    configParameters: ConfigParameters(
        credentials: credentials,
        environment: .production, // opcional
        logLevel: .error, // opcional
        sdkConfig: sdkConfig // opcional
    )
)

Exemplo completo de configuração

O exemplo abaixo mostra uma configuração mínima dentro de uma View em SwiftUI.

import SwiftUI
import TapOnPhoneSDK

struct HomeView: View {
    var body: some View {
        Button("Configuração SDK") {
            let credentials = TapOnPhoneCredentials(
                marketplace: "coloque seu marketplace aqui",
                seller: "coloque seu seller aqui",
                accessKey: "coloque seu accessKey aqui"
            )

            TapOnPhone.setConfig(
                configParameters: ConfigParameters(
                    credentials: credentials,
                    environment: .production, // opcional
                    logLevel: .error, // opcional
                    sdkConfig: SdkConfig() // opcional
                )
            )
        }
    }
}

Pagamento

Use TapOnPhone.pay() para processar a venda e trate os erros com sdkError para diferenciar falhas de pagamento e falhas do SDK com mais segurança.

Prefira response.sdkError no callback onError. Essa abordagem evita casts frágeis com as! e ainda cobre casos desconhecidos com .unknown.

Se você quiser reduzir o tempo percebido no início da cobrança, veja 5 - Otimizar pagamento para antecipar initialize(...) e activateSession(...) antes de chamar pay().

1. Verificar os requisitos de plataforma

  • PaymentRequest e TapOnPhone.pay estão disponíveis a partir de iOS 16.4.
  • Antes de vender, aplique a configuração do SDK com TapOnPhone.setConfig().

2. Montar o PaymentRequest

Crie o PaymentRequest (objeto que representa os dados da transação) com o valor, tipo de pagamento e parâmetros opcionais da cobrança.

let request = PaymentRequest(
    amount: 100,
    paymentType: .credit,
    installments: 1
)
💡

O valor 100 representa R$ 1,00. Sempre envie amount em centavos.

Inicialização de PaymentRequest

public init(
    amount: Int,
    paymentType: TapOnPhonePaymentType,
    installments: Int = 1,
    metadata: String? = nil,
    referenceId: String? = nil
)
CampoTipoPadrãoDescrição
amountIntobrigatórioValor da transação em centavos. Exemplo: 100 = R$ 1,00.
paymentTypeTapOnPhonePaymentTypeobrigatórioUse .credit ou .debit.
installmentsInt1Número de parcelas. Para parcelado, use 2 ou mais.
metadataString?nilMetadados opcionais com limite de 512 caracteres.
referenceIdString?nilIdentificador opcional definido pelo cliente. Pode retornar na resposta de sucesso.

3. Enviar o pagamento

Com a requisição pronta, chame TapOnPhone.pay() e trate os três retornos do fluxo: sucesso, erro e eventos.

import SwiftUI
import TapOnPhoneSDK

struct PaymentView: View {
    @State private var showSuccess = false
    @State private var showError = false
    @State private var successMessage = ""
    @State private var errorMessage = ""

    var body: some View {
        VStack(spacing: 20) {
            Button("Realizar pagamento") {
                let request = PaymentRequest(
                    amount: 1000, // R$ 10,00
                    paymentType: .credit,
                    installments: 2,
                    metadata: """
                    {
                        "clientId": "1234",
                        "name": "John Doe"
                    }
                    """,
                    referenceId: "order-abc-123"
                )

                TapOnPhone.pay(
                    payRequest: request,
                    onSuccess: { result in
                        successMessage = "Pagamento aprovado! id: \(result.transactionId)"
                        showSuccess = true
                    },
                    onError: { response in
                        switch response.sdkError {
                        case .payment(let paymentError):
                            let err = paymentError.error
                            errorMessage = "Pagamento negado — id: \(paymentError.transactionId ?? "desconhecido")\ncode: \(err.code.rawValue)\nname: \(err.code.description)\nmessage: \(err.message)\nsource: \(err.source)"

                            if let acquirer = paymentError.acquirerCode {
                                errorMessage += "\nacquirerCode: \(acquirer)"
                            }

                        case .tapOnPhone(let sdkError):
                            errorMessage = "Erro do SDK — code: \(sdkError.code.rawValue)\nname: \(sdkError.code.description)\nmessage: \(sdkError.message)\nsource: \(sdkError.source)"

                        case .unknown(let underlying):
                            errorMessage = "Erro não tipado: \(underlying)"
                        }

                        showError = true
                    },
                    onEvent: { event in
                        print("ApplicationEvent: \(event)")
                    }
                )
            }
        }
        .alert("Sucesso", isPresented: $showSuccess) {
            Button("OK", role: .cancel) { }
        } message: {
            Text(successMessage)
        }
        .alert("Erro", isPresented: $showError) {
            Button("OK", role: .cancel) { }
        } message: {
            Text(errorMessage)
        }
    }
}

Saída esperada em caso de sucesso

Pagamento aprovado! id: <transactionId>

Saída esperada em caso de erro de pagamento

Pagamento negado — id: <transactionId>
code: <código>
name: <nome do erro>
message: <mensagem>
source: <origem>
acquirerCode: <código da adquirente>

Saída esperada em caso de erro do SDK

Erro do SDK — code: <código>
name: <nome do erro>
message: <mensagem>
source: <origem>
O que cada callback faz?
  • onSuccess: recebe um PaymentApprovedResponse quando a transação é aprovada.
  • onError: recebe um ErrorResponse com o erro encapsulado em sdkError.
  • onEvent: recebe eventos intermediários do fluxo de pagamento.

4. Entender a resposta de sucesso

Quando o pagamento é aprovado, o SDK retorna um PaymentApprovedResponse com os principais identificadores da transação.

CampoDescrição
transactionIdIdentificador da transação na Zoop.
cardBrandBandeira do cartão, como Visa ou Mastercard.
readerIdentifierIdentificador do leitor, quando houver sessão.
referenceIdEco do referenceId enviado em PaymentRequest, se houver.
readResultIdIdentificador do resultado da leitura do cartão.
binNumberBIN do cartão, quando disponível.
paymentDeviceDispositivo usado no pagamento, como CARD, MOBILE, NON_CARD, WATCH ou UNKNOWN.
Estrutura completa de sucesso
public struct PaymentApprovedResponse: Equatable, Hashable {
    public let transactionId: String
    public let cardBrand: String
    public let readerIdentifier: String?
    public let referenceId: String?
    public let readResultId: String
    public let binNumber: String?
    public let paymentDevice: String?
}

5. Resolver erros com sdkError

Use sdkError como fonte principal para o tratamento de erro. Esse padrão é mais seguro porque expõe o tipo real do erro sem depender apenas de type e casts manuais.

onError: { response in
    switch response.sdkError {
    case .payment(let paymentError):
        let err = paymentError.error
        print("Pagamento negado — id:", paymentError.transactionId ?? "desconhecido")
        print("code:", err.code.rawValue)
        print("name:", err.code.description)
        print("message:", err.message)
        print("source:", err.source)

        if let acquirer = paymentError.acquirerCode {
            print("acquirerCode:", acquirer)
        }

    case .tapOnPhone(let sdkError):
        print("Erro do SDK — code:", sdkError.code.rawValue)
        print("name:", sdkError.code.description)
        print("message:", sdkError.message)
        print("source:", sdkError.source)

    case .unknown(let underlying):
        print("Erro não tipado:", underlying)
    }
}
CasoQuando esperarTipo retornado
.payment(PaymentErrorResponse)Pagamento negado ou falha após leitura do cartãoErro de pagamento com dados da transação, como transactionId, cardBrand e acquirerCode
.tapOnPhone(TapOnPhoneError)Erro de configuração, sessão, inicialização ou falha interna do SDKErro do SDK com code, message, source e outros metadados
.unknown(Any)Cenário não tipado ou inesperadoValor bruto para fallback e diagnóstico

Em fluxos de pagamento negado, type costuma ser .payment e sdkError será .payment(PaymentErrorResponse). Falhas antes ou durante a preparação do fluxo podem chegar como erro de SDK, normalmente encapsuladas em .tapOnPhone(TapOnPhoneError).

Optional: Tratamento legado com type e cast

Use este formato apenas para compatibilidade com implementações anteriores. Para novas integrações, prefira sdkError.

onError: { response in
    if response.type == .payment,
       let paymentError = response.error as? PaymentErrorResponse {
        // usar paymentError
    } else if let sdkError = response.error as? TapOnPhoneError {
        // usar sdkError
    }
}
Estruturas completas de erro
public struct ErrorResponse {
    public let type: ErrorType
    public let error: Any
    public var sdkError: SDKError { get }
}

public enum SDKError {
    case tapOnPhone(TapOnPhoneError)
    case payment(PaymentErrorResponse)
    case unknown(Any)
}

public enum ErrorType: String, Codable {
    case initialize
    case payment
    case onboarding
}

public struct PaymentErrorResponse: Equatable, Hashable, Error {
    public let error: TapOnPhoneError
    public let transactionId: String?
    public let cardBrand: String?
    public let readResultId: String?
    public let binNumber: String?
    public let paymentDevice: String?
    public let acquirerCode: String?
}

public struct TapOnPhoneError: Equatable, Error, Hashable {
    public let code: TapOnPhoneErrorCode
    public let source: TapOnPhoneErrorSource
    public let message: String
    public let description: String?
    public let readerIdentifier: String?
    public let timestamp: Double
}

Optional: Acompanhar eventos de pagamento

Use onEvent para acompanhar as etapas intermediárias do fluxo. Para mais detalhes sobre os eventos de pagamento, ver aqui.

Próximo passo

Depois de validar o fluxo básico de pagamento, avance para 5 - Otimizar pagamento para antecipar a inicialização e a criação de sessão. Se você precisar aprofundar o diagnóstico do fluxo, revise também a documentação de eventos do SDK.