VenturePay
SDK 1.0.0.0

Integração com a maquininha

Conecte seu aplicativo Android à maquininha VenturePay para cobrar no cartão e no PIX, confirmar e estornar vendas, consultar as últimas cobranças e imprimir comprovantes.

Pacote do SDK fornecido pela VenturePay
libs/venturepay_pos-1.0.0.0.aar
Plataforma
Android
Pagamentos
Cartão e PIX
Sandbox
ID 0
Etapas
4
Começando

Visão geral da integração

O SDK roda dentro do seu aplicativo Android instalado na maquininha. Ele conversa com o app de pagamentos do terminal (PayStore), que cuida da leitura do cartão, da comunicação com a adquirente e das telas de senha. Seu app só pede a cobrança e recebe o resultado.

1

Instalar as dependências

Bibliotecas obrigatórias e o .aar fornecido pela VenturePay.

2

Inicializar em sandbox

Credenciais de teste com applicationId = "0".

3

Implementar o fluxo

Pagar, confirmar, estornar, consultar e imprimir.

4

Enviar para a VenturePay

Gere o build do app e envie ao suporte para as próximas etapas.

Etapa 1 · Instalação

Dependências do projeto

Primeiro baixe as dependências de que o SDK precisa para funcionar. Depois instale o pacote .aar da VenturePay, que você recebe do nosso time.

  1. 1

    Adicione as dependências obrigatórias no app/build.gradle.kts e sincronize o projeto (Sync Now) para o Gradle baixá-las.

  2. 2

    Copie o arquivo venturepay_pos-1.0.0.0.aar para a pasta app/libs/ (crie a pasta se ela não existir).

  3. 3

    Adicione a linha do .aar e sincronize de novo.

app/build.gradle.kts
dependencies {
    // 1. Dependências obrigatórias do SDK
    implementation("com.jakewharton.threetenabp:threetenabp:1.0.5")

    // 2. SDK da maquininha fornecido pela VenturePay (arquivo em app/libs/)
    implementation(files("libs/venturepay_pos-1.0.0.0.aar"))
}

AndroidManifest.xml

Declare a consulta ao serviço de pagamentos dentro de <manifest> e fora de <application>. Sem isso, no Android 11 ou mais novo, o app não consegue se conectar ao serviço da maquininha.

app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <queries>
        <intent>
            <action android:name="phoebus.payments.api.controller" />
        </intent>
    </queries>

    <application ...>
        <!-- suas activities -->
    </application>
</manifest>
Etapa 2 · Inicialização

Inicialização do SDK

Antes de usar qualquer funcionalidade, inicialize o SDK com os dados abaixo. Eles acompanham todas as chamadas.

Nome Tipo Obrigatório Descrição
context Context Sim Use applicationContext.
applicationId String Sim ID da aplicação. Em sandbox é 0.
secretToken String Sim Token secreto associado ao applicationId. Em sandbox são 24 zeros.
softwareVersion String Sim Versão do seu app, para identificação e auditoria.

Credenciais

Durante o desenvolvimento, o app chama o SDK sempre com as credenciais de sandbox. As credenciais de produção, fornecidas pela VenturePay, ficam no código sempre comentadas.

Sandbox em uso
applicationId 0
secretToken 000000000000000000000000
Produção comentado

O APPLICATION_ID e o SECRET_TOKEN de produção são fornecidos pela VenturePay. Deixe as duas linhas comentadas no código: a ativação acontece nas próximas etapas, junto com o nosso time.

VenturePayConfig.java
public final class VenturePayConfig {

    // Produção: credenciais fornecidas pela VenturePay. Mantenha sempre comentado.
    // public static final String APPLICATION_ID = "";
    // public static final String SECRET_TOKEN   = "";

    // Sandbox: credenciais usadas para chamar o SDK durante o desenvolvimento.
    public static final String APPLICATION_ID = "0";
    public static final String SECRET_TOKEN   = "000000000000000000000000";

    private VenturePayConfig() {}
}
Credenciais.kt
// Classes do pacote br.com.phoebus.android.payments.api
val credentials = Credentials().apply {
    applicationId = VenturePayConfig.APPLICATION_ID
    secretToken   = VenturePayConfig.SECRET_TOKEN
}

val appInfo = ApplicationInfo().apply {
    this.credentials = credentials
    softwareVersion  = BuildConfig.VERSION_NAME
}

Com credenciais inválidas para o app ou para o ambiente, o SDK responde SDK - 0008: Credenciais inválidas.

Conexão com o serviço (PaymentClient)

Todas as operações passam por um PaymentClient conectado (bind) ao serviço de pagamentos da maquininha.

Conexao.kt
val client = PaymentClient()

client.bind(applicationContext, object : Client.OnConnectionCallback {
    override fun onConnected() {
        // pronto para chamar startPaymentV2, reversePaymentV2 etc.
    }
    override fun onDisconnected(forced: Boolean) {
        // o serviço caiu
    }
})

// ao sair da tela
if (client.isBound) client.unbind(applicationContext)

Regras da conexão

  • Chamar uma operação sem estar conectado lança ClientException.
  • Os callbacks podem chegar fora da main thread. Use runOnUiThread ou um Handler antes de mexer na tela.
  • O SDK aceita uma operação por vez. Espere o callback antes de chamar a próxima.

Todas as respostas usam o mesmo callback:

Callback.kt
object : PaymentClient.PaymentCallback<T> {
    override fun onSuccess(result: T) { }
    override fun onError(error: ErrorData) { }
}

ErrorData

Campo Descrição
paymentsResponseCode Código do SDK, por exemplo PC-0005 (cancelado) ou PC-0069 (parcelas fora da faixa)
responseMessage Mensagem legível
acquirerResponseCode Código da adquirente, por exemplo PS-2009
acquirerAdditionalMessage Mensagem extra da adquirente
Etapa 3 · Pagamentos

Fazer um pagamento

Monte um PaymentRequestV2 e chame startPaymentV2. A maquininha assume a tela, lê o cartão e devolve o resultado no callback.

FUNÇÃO client.startPaymentV2(request, callback)
Pagamento.kt
val request = PaymentRequestV2().apply {
    value            = BigDecimal("10.90")        // em reais, 2 casas
    installments     = 1                          // 1 = à vista
    paymentTypes     = listOf(PaymentType.CREDIT) // um tipo só: pula a tela "crédito ou débito"
    appTransactionId = "VP-${System.currentTimeMillis()}" // ID único seu
    appInfo          = appInfo
    isShowReceiptView      = false   // não mostrar o comprovante padrão
    printCustomerReceipt   = false   // não imprimir automaticamente
    printMerchantReceipt   = false
    previewCustomerReceipt = false
    previewMerchantReceipt = false
}

client.startPaymentV2(request, object : PaymentClient.PaymentCallback<PaymentV2> {
    override fun onSuccess(p: PaymentV2) {
        // aprovado, mas ainda PENDENTE: precisa confirmar
        val id = p.paymentId
    }
    override fun onError(e: ErrorData) { }
})

Campos de PaymentRequestV2

Campo Tipo Descrição
valueBigDecimalValor em reais (ex.: 10.90)
installmentsIntegerNúmero de parcelas. Acima de 1, use CREDIT ou PARCELED_CREDIT
paymentTypesList<PaymentType>Tipos aceitos. Com mais de um, a maquininha pergunta ao cliente
appTransactionIdStringSeu identificador da transação
appInfoApplicationInfoCredenciais e versão
isShowReceiptViewBooleanMostrar ou não a tela de comprovante padrão
printCustomerReceipt / printMerchantReceiptBooleanImprimir as vias automaticamente
previewCustomerReceipt / previewMerchantReceiptBooleanPré-visualizar as vias
note, additionalData, dniStringOpcionais
before…/afterPrintAdditionalInfoCustomer/MerchantStringTexto extra no comprovante padrão
tokenizeCard, tokenizeEmail, tokenizeNationalDocument—Tokenização de cartão (opcional)

PaymentType

CREDITDEBITPARCELED_CREDITCREDIT_STORECREDIT_ADMINPIXVOUCHERVOUCHER_MEALVOUCHER_FEEDVOUCHER_FLEETPRE_AUTHORIZATIONPRE_AUTHORIZATION_CONFIRMATIONPRE_AUTHORIZATION_CREDIT_STOREPRE_AUTHORIZATION_CREDIT_ADMINPRE_AUTHORIZATION_SUBSTITUTIVEDEBIT_POSTDATEDPRIVATE_LABELPRIVATE_LABEL_QUERY

Retorno PaymentV2

paymentIdvalueoriginalValueinstallmentspaymentTypepaymentStatusacquirerNameacquirerIdacquirerAuthorizationNumberacquirerNsunsuTerminalacquirerResponseCodeacquirerAdditionalMessagecardHolderNamecardcaptureTypeterminalIdbatchNumberticketNumberappTransactionIdpaymentDateUTCacquirerResponseDateUTCreceipt.clientViareceipt.merchantViacardTokencorrelationId

receipt.clientVia e receipt.merchantVia trazem o texto das vias do cliente e do estabelecimento, para você imprimir do seu jeito.

Confirmar ou desfazer uma transação

Toda transação aprovada fica pendente até ser confirmada. Se não for confirmada, ela é desfeita automaticamente.

Confirmacao.kt
// confirma (liquida)
client.confirmPayment(paymentId, object : PaymentClient.PaymentCallback<Any?> {
    override fun onSuccess(r: Any?) { /* CONFIRMED */ }
    override fun onError(e: ErrorData) { }
})

// desiste da transação pendente (desfazimento)
client.cancelPayment(paymentId, callback)

Transação ponta a ponta

O fluxo completo é pagar, confirmar e, se quiser, imprimir.

PontaAPonta.kt
fun pagarPontaAPonta(req: PaymentRequestV2, done: (PaymentV2?, ErrorData?) -> Unit) {
    client.startPaymentV2(req, object : PaymentClient.PaymentCallback<PaymentV2> {
        override fun onSuccess(p: PaymentV2) {
            client.confirmPayment(p.paymentId, object : PaymentClient.PaymentCallback<Any?> {
                override fun onSuccess(r: Any?) = done(p, null)    // aprovado e confirmado
                override fun onError(e: ErrorData) = done(null, e) // falhou ao confirmar
            })
        }
        override fun onError(e: ErrorData) = done(null, e)         // negado ou cancelado
    })
}

Se o seu sistema precisa registrar a venda antes de liquidar, confirme só depois de registrar. Se o registro falhar, chame cancelPayment.

Consultas

Listar as últimas cobranças

As transações da maquininha ficam disponíveis por um content provider exportado, que não exige permissão.

QUERY content://br.com.phoebus.android.payments.provider/payments

Parâmetros da URI

Parâmetro Obrigatório Descrição
applicationIdSimMesmo das credenciais
secretTokenSimMesmo das credenciais
softwareVersionSimVersão do seu app
startDateNãoInício, ISO-8601 UTC (2026-10-01T00:00:00.000Z)
finishDateNãoFim, ISO-8601 UTC
statusNãoStatus separados por vírgula (ex.: 0,1,2,3)
allResultsNãotrue para trazer todas, sem paginação
UltimasCobrancas.kt
val iso = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
    timeZone = TimeZone.getTimeZone("UTC")
}

val uri = Uri.parse("content://br.com.phoebus.android.payments.provider/payments").buildUpon()
    .appendQueryParameter("softwareVersion", BuildConfig.VERSION_NAME)
    .appendQueryParameter("applicationId", VenturePayConfig.APPLICATION_ID)
    .appendQueryParameter("secretToken", VenturePayConfig.SECRET_TOKEN)
    .appendQueryParameter("startDate", iso.format(inicio))
    .appendQueryParameter("finishDate", iso.format(fim))
    .appendQueryParameter("status", PaymentStatus.entries.joinToString(",") { it.ordinal.toString() })
    .appendQueryParameter("allResults", "true")
    .build()

// rode fora da main thread
applicationContext.contentResolver.query(uri, null, null, null, null)?.use { c ->
    while (c.moveToNext()) {
        val id     = c.getString(c.getColumnIndexOrThrow("id"))
        val valor  = c.getString(c.getColumnIndexOrThrow("value"))
        val status = PaymentStatus.entries[c.getInt(c.getColumnIndexOrThrow("status"))]
        val tipo   = PaymentType.entries[c.getInt(c.getColumnIndexOrThrow("paymentType"))]
        val data   = c.getLong(c.getColumnIndexOrThrow("dateUTC"))      // epoch em ms
    }
}

Colunas retornadas

idvaluepaymentTypeinstallmentsstatusdatedateUTCacquirerNameacquirerAuthorizationNumberacquirerNsunsuTerminalcardBrandcardPanLast4DigitscardHolderNamecaptureTypeappTrnsIdreceiptClientreceiptMerchant

As colunas status e paymentType vêm como a posição no enum (ordinal), não como o id. Converta com PaymentStatus.entries[n] e PaymentType.entries[n]. Com fromId(n), uma venda confirmada aparece como "Pendente".

PaymentStatus

O número é a posição no enum, que é o valor usado no filtro status e retornado na coluna.

Posição Status Posição Status
0PENDING6UNREACHABLE
1CONFIRMED7WAITING_VALIDATION
2CANCELLED8WAITING_CAPTURE
3REVERSED9REFUNDED_DEVOLUTION
4PROCESSING10REFUNDED
5DENIED11APPROVED

Para ordenar da mais recente para a mais antiga, use dateUTC em ordem decrescente. Para trazer só as aprovadas, filtre com status=1 (CONFIRMED).

Dados do terminal

Também é um content provider exportado, sem parâmetros.

QUERY content://br.com.phoebus.android.terminalinfo.provider/terminalInfo
Terminal.kt
applicationContext.contentResolver.query(
    Uri.parse("content://br.com.phoebus.android.terminalinfo.provider/terminalInfo"),
    null, null, null, null
)?.use { c ->
    if (c.moveToFirst()) {
        val nome = c.getString(c.getColumnIndexOrThrow("merchantName"))
        val tid  = c.getString(c.getColumnIndexOrThrow("terminalId"))
        val cnpj = c.getString(c.getColumnIndexOrThrow("nationalId"))
    }
}
Coluna Descrição
merchantIdCódigo do estabelecimento
merchantNameRazão social
merchantCommercialNameNome fantasia
terminalIdTID
nationalIdCNPJ ou CPF
merchantNationalTypeTipo do documento
merchantCategoryCodeMCC
subAcquirerIdSubadquirente
street, addressNumber, complement, neighbourhood, city, state, stateAbbreviation, country, postalCodeEndereço
merchantWebsite, merchantEmail, merchantPhoneContato
terminalCurrencyCode, terminalCurrencyIso4217Moeda (ex.: 986 = BRL)

O número de série da maquininha não vem nesse provider. Leia da propriedade ro.serialno, ro.boot.serialno ou ro.sunmi.serial.

Pós-venda

Estornar uma cobrança

Use o paymentId da cobrança (a coluna id do provider) e o mesmo appTransactionId usado no pagamento.

FUNÇÃO client.reversePaymentV2(request, callback)
Estorno.kt
val request = ReversePaymentRequestV2().apply {
    paymentId        = "7"                 // id da cobrança (coluna id do provider)
    appTransactionId = "VP-1729..."        // o mesmo usado no pagamento
    value            = BigDecimal("10.90") // valor total da cobrança
    credentials      = credentials
    isShowReceiptView      = false
    printCustomerReceipt   = false
    printMerchantReceipt   = false
    previewCustomerReceipt = false
    previewMerchantReceipt = false
    autoConfirm      = true                // confirma o estorno automaticamente
}

client.reversePaymentV2(request, object : PaymentClient.PaymentCallback<ReversePayment> {
    override fun onSuccess(r: ReversePayment) {
        val viaCliente = r.receipt?.clientVia   // comprovante do estorno
    }
    override fun onError(e: ErrorData) { }
})

Retorno ReversePayment

paymentIdstatuscancelableacquirerIdacquirerAuthorizationNumberacquirerResponseCodeacquirerResponseDateUTCacquirerAdditionalMessagensuTerminalbatchNumberticketNumbercardHolderNamecardBinpanLast4DigitsterminalIdreceipt.clientViareceipt.merchantViacorrelationId

Com autoConfirm = false, o estorno também precisa ser confirmado ou desfeito depois:

ConfirmarEstorno.kt
client.confirmReversePayment(paymentId, callback)
client.cancelReversePayment(paymentId, callback)

Também existe o reversePaymentWithFilter(ReversePaymentFilterRequest, callback), que estorna buscando a transação por filtro em vez do paymentId.

Imprimir ou reimprimir uma via

Imprima uma imagem própria (bitmap de 384 px de largura, em base64 PNG ou WEBP), um texto (por exemplo, o receiptClient vindo do provider) ou reimprima o comprovante padrão.

Impressao.kt
// imagem própria (bitmap de 384 px, base64 PNG ou WEBP)
client.printFromBase64(applicationContext, base64, /* feed */ true, callback)

// texto (ex.: receiptClient do provider)
client.printFromString(applicationContext, texto, true, callback)

// reimprimir o comprovante padrão
client.reprintV2(ReprintReceiptRequestV2(), callback)
Referência

Outras funções do PaymentClient

Função Uso
startInitialization(InitializationRequest(), cb)Reinicializa o terminal (parâmetros da adquirente)
startEchoTest(cb)Testa a comunicação com a adquirente
setMainApp(pacote, cb) / getPackageName(cb)Define qual app o botão início abre / obtém o pacote do app de pagamentos. Não use setMainApp com o seu pacote: trava o terminal.
setTheme(nome, cb) / getTheme(cb)Tema de cores das telas de pagamento
getLogo(cb) / getReceiptLogo(cb)Lê os logos configurados (não permite trocar)
closeBatchV2 / openBatchFechamento e abertura de lote
refundPaymentV2(RefundRequestV2, cb)Devolução
startContactlessPix / abortContactlessPixPIX por aproximação

Erros comuns

Código Causa
SDK - 0008Credenciais inválidas para este app ou ambiente
PC-0005Operação cancelada pelo usuário
PC-0069 "parcelas fora da faixa 0X - 0X"Terminal não habilitado para parcelamento (configuração na adquirente)
PS-2009 "terminal não inicializado com adquirente"O terminal precisa ser inicializado ou configurado na adquirente
ClientException "not binding"Chamada feita antes do onConnected
Etapa 4 · Publicação

Enviar o aplicativo para a VenturePay

Quando o aplicativo estiver pronto, gere o build e envie para o suporte VenturePay. Nosso time valida a integração e conduz as próximas etapas até a produção.

Antes de enviar, confira

  • As dependências e o venturepay_pos-1.0.0.0.aar estão instalados e o projeto sincroniza sem erros.
  • O app chama o SDK com as credenciais de sandbox (applicationId = "0" e 24 zeros no secretToken).
  • As linhas APPLICATION_ID e SECRET_TOKEN de produção continuam comentadas.
  • O fluxo de pagamento confirma toda venda aprovada e o estorno foi testado.
1

Gere o build

Gere o pacote final do aplicativo com a integração concluída.

2

Envie ao suporte

Mande o arquivo para o suporte VenturePay informando a versão do app.

3

Aguarde as próximas etapas

Nosso time valida o app e retorna com as orientações para produção.

Enviar para o suporte VenturePay
Suporte técnico

Precisa de ajuda na integração?

Nosso time acompanha a integração do seu app com a maquininha, da instalação do SDK até a primeira venda em produção.

Copiado para a área de transferência