No iPhone, a leitura do cartão usa o Tap to Pay on iPhone da Apple. Além do código, o app do parceiro precisa de uma autorização da Apple, o entitlement. Comece pelo pedido à Apple: ele depende de aprovação e é a etapa mais demorada da integração.
Requisitos
| Item | Valor |
|---|---|
| iOS | 17 ou mais novo |
| Aparelho | iPhone compatível com Tap to Pay on iPhone; simulador não é suportado |
| Conta Apple | Apple Developer Program de organização (conta individual não pode pedir o entitlement) |
| Entitlement | com.apple.developer.proximity-reader.payment.acceptance |
Habilitar o Tap to Pay on iPhone na conta Apple do parceiro
O app fica na conta Apple Developer do parceiro e é assinado pelo time do parceiro. Ele não precisa estar na conta da Stone. A Stone está listada pela Apple como provedora de pagamentos (PSP) no Brasil.
- Pedido do entitlement: a pessoa com o papel Account Holder da conta faz o pedido no formulário de Tap to Pay on iPhone da Apple e indica a Stone como provedora de pagamentos. Em muitas empresas o Account Holder não é do time de desenvolvimento, então combine isso cedo.
- Análise da Apple: a Apple avalia cada pedido e não informa prazo. Quando aprova, o entitlement aparece na conta como managed capability.
- Entitlement de desenvolvimento: a primeira aprovação libera só o uso em desenvolvimento, com o app instalado pelo Xcode. Ele não permite TestFlight nem App Store.
- Entitlement de distribuição: depois dos testes, peça a liberação para distribuição respondendo o e-mail original da Apple. Só com ela é possível enviar builds pelo TestFlight e publicar na App Store.
- Revisão do app: a Apple confere se o app segue a Human Interface Guidelines e as diretrizes de marketing do Tap to Pay on iPhone, além das App Review Guidelines. O SDK cuida da vinculação da conta do lojista (aceite dos termos da Apple). A tela que explica ao lojista como usar o Tap to Pay fica com o app do parceiro.
Planeje o cronograma com as duas aprovações: o QA só recebe builds pelo TestFlight depois da segunda.
Configuração no Xcode
- No App ID do app (Certificates, Identifiers & Profiles), habilite a capability de Tap to Pay on iPhone.
- Gere um provisioning profile novo para esse App ID.
- No arquivo
.entitlementsdo app:
<key>com.apple.developer.proximity-reader.payment.acceptance</key>
<true/>
Aparelho de teste (Sandbox)
Em SDX, o iPhone de teste precisa estar no ambiente de teste da Apple:
- Crie uma Sandbox Apple Account no App Store Connect da conta do parceiro.
- No iPhone de teste, entre com essa conta, conforme a orientação da Apple para a versão do iOS do aparelho.
- Reinicie o aparelho e reinstale o app.
Sem isso, o setup falha com 314 (INVALID_READER_TOKEN), mesmo com credenciais corretas.
Aceite dos termos pelo lojista
Na primeira ativação de cada aparelho, o iOS pede ao lojista que aceite os termos do Tap to Pay on iPhone com a conta Apple dele. O aparelho precisa estar com o iCloud conectado (senão o setup volta 309). Se o lojista recusar, volta 307.
Dependência
O canal de distribuição do pacote iOS (TapSDKiOS, com o framework TapCore) e as instruções de acesso são enviados pela Stone no início da integração.
Assim como no Android, existem as variantes de testes (SDX) e de produção.
Configuração
A criação do SDK e o setup estão em Setup do SDK (seções 3.1 e 3.2). Os tipos vêm do framework TapCore:
import TapCore
No Swift, os parâmetros com valor padrão no Kotlin precisam ser passados explicitamente, e objetos Kotlin são acessados por .shared (por exemplo, TapFeeScreens.shared).
Processar transação
O setup deve ser feito antes — veja Setup do SDK. O SDK exibe todas as telas da venda (Setup do SDK, seção 3.4); no iPhone, a tela de aproximação é a do Tap to Pay da Apple. O app só trata o resultado final.
let data = TransactionData(
amount: 1000, // R$ 10,00 (centavos)
paymentMethod: .credit, // .credit, .debit ou .voucher
installments: 1, // 1 = à vista
softDescriptor: "" // obrigatório; texto na fatura do cliente
)
paymentHandle = TapService.shared.sdk
.pay(input: data)
.watch(onCollect: { event in
guard let onResult = event as? TransactionEvent.OnResult else { return }
if onResult.result == ErrorStoneStatus.companion.SUCCESS {
handleApproved(onResult.transactionResult)
} else {
handleError(onResult.result, onResult.transactionResult)
}
}, onError: { error in
print("Erro na transação: \(error)")
})
- A forma de pagamento e as parcelas finais são escolhidas pelo lojista na tela do SDK. Com repasse da taxa, o valor cobrado muda; ele chega em
TapFeeScreens.shared.onPaymentMethodConfirmed. - O
watchentrega os eventos na main thread. OOnResulté sempre o último evento; os demais podem ser ignorados.
Cancelamento
O lojista cancela pelas telas do SDK ou pela tela da Apple; nesse caso o OnResult vem com 7011 (OPERATION_CANCELLED_BY_USER). Não chame dispose() no handle antes do OnResult: o SDK fecha as telas e o resultado se perde.
Exemplo completo — ObservableObject
Um ObservableObject que expõe o estado da venda para a UI observar via @Published.
final class PaymentViewModel: ObservableObject {
@Published var uiState: PaymentUiState = .idle
private var paymentHandle: TapDisposableHandle?
init() {
// Valor final da venda (pode ter repasse da taxa)
TapFeeScreens.shared.onPaymentMethodConfirmed = { [weak self] finalData in
self?.saveFinalAmount(finalData)
}
// Saída da venda aprovada: nova venda ou fechar
TapFeeScreens.shared.onSaleFinished = { [weak self] exit in
self?.uiState = exit == .newSale ? .newSale : .home
}
}
func processPayment(amountCents: Int32) {
let data = TransactionData(
amount: amountCents,
paymentMethod: .credit,
installments: 1,
softDescriptor: ""
)
paymentHandle = TapService.shared.sdk
.pay(input: data)
.watch(onCollect: { [weak self] event in
guard let self,
let r = event as? TransactionEvent.OnResult else { return }
if r.result == ErrorStoneStatus.companion.SUCCESS {
self.registerSale(r.transactionResult)
} else {
self.uiState = .error(r.result.message)
}
}, onError: { [weak self] error in
self?.uiState = .error(error.localizedDescription)
})
}
}
Veja também
Integração: Tap to Pay para Terceiros
Etapa 5 de 7