Getnet DocsGetnet Docs

Implementar os serviços de hardware

Este guia mostra as chamadas exatas que a Middleware App e o Devkit fazem contra sua Manufacturer Service App, serviço por serviço. Use-o para confirmar que sua implementação responde como o software da Getnet espera.

Como funciona

Todas as chamadas abaixo chegam ao seu serviço por mainService, o stub IMainService que a Middleware App obtém depois de se vincular ao seu serviço. Cada serviço — cartão, impressora, beeper e assim por diante — está vinculado ao mainService como uma propriedade própria. A maioria das operações reporta seu resultado por um callback, e não por um valor de retorno.

Antes de começar

Conclua a configuração da integração HAL para que seu serviço se vincule corretamente e execute o Devkit para testar cada chamada conforme a implementa.

Beeper

O Devkit chama custom com uma duração em milissegundos:

mainService.beeper.custom(500) // 500 milliseconds

Sua implementação de IBeeperService também deve suportar quatro sons nomeados, cada um com duração fixa:

SomDuração
successBipe de 500 ms.
errorBipe de 1000 ms.
digitBipe de 100 ms.
nfcBipe de 100 ms, pausa de 300 ms, bipe de 100 ms.

Cartão

Obtenha o serviço de cartão uma vez e depois chame o método de busca correspondente ao tipo de leitura que quem chama precisa:

val cardService = mainService.card

Cada tipo de busca usa uma constante fixa:

ConstanteValor
MAG"1"
CHIP"2"
NFC"3"

Busca por chip:

cardService.searchChip(1000, object : ICardCallback.Stub() {
    override fun onCard(cardResponse: CardResponse?) {
        val pan = cardResponse?.pan
        cardService.stopAllReaders()
    }
    override fun onMessage(message: String?) {
        cardService.stopAllReaders()
    }
    override fun onError(error: String?) {
        cardService.stopAllReaders()
    }
})

A busca por tarja magnética devolve track1, track2 e track3 em onCard, em vez de pan. A busca NFC devolve o mesmo formato de CardResponse que a busca por chip.

As duas seguem o mesmo padrão onCard / onMessage / onError, e as duas devem chamar stopAllReaders() assim que um resultado chega.

Mifare

O IMifareService cobre presença de cartão, autenticação e leitura/escrita em nível de bloco:

mainService.mifare.searchCard(object : IMifareCallback.Stub() {
    override fun onCard(type: Int) { /* card type detected */ }
    override fun onError(error: String?) { /* handle error */ }
})

mainService.mifare.searchCardAndActivate(object : IMifareActivateCallback.Stub() {
    override fun onActivate(key: ByteArray?) { /* card activated */ }
    override fun onError(error: String?) { /* handle error */ }
})

A autenticação de setor e de bloco recebe uma chave de 6 bytes:

val key = byteArrayOf(0xff.toByte(), 0xff.toByte(), 0xff.toByte(), 0xff.toByte(), 0xff.toByte(), 0xff.toByte())
mainService.mifare.authenticateSectorWithKeyA(sector = 2, key)
mainService.mifare.authenticateBlockWithKeyA(block = 10, key)
mainService.mifare.authenticateSectorWithKeyB(sector = 2, key)
mainService.mifare.authenticateBlockWithKeyB(block = 10, key)

Depois da autenticação, seu serviço deve suportar decrement, increment, readBlock, restore, transfer e writeBlock sobre um índice de bloco dado. Também deve suportar close, isExist, activate e halt para controle de sessão. getCardSerialNo devolve o UID do cartão como string hexadecimal.

LED

O ILedService expõe um par liga/desliga por cor — vermelho, azul, amarelo e verde:

mainService.led.turnOnRed()
mainService.led.turnOffRed()

Implemente o mesmo par para azul, amarelo e verde.

Impressora

Todo método de IPrinterService deve lançar IllegalStateException se seu serviço ainda não tiver inicializado a impressora:

mainService.printer?.init()
    ?: throw IllegalStateException("service isn't initiated")

Uma vez inicializada, seu serviço monta um trabalho de impressão a partir de uma sequência de chamadas — addText, addBarCode, addQrCode, addImageBitmap, addImageByteArray — seguida de print ou printAndRemovePaper:

mainService.printer?.addText(align, text)
mainService.printer?.print(object : IPrinterCallback.Stub() {
    override fun onSuccess() { /* job printed */ }
    override fun onError(cause: Int) { /* map cause to a status */ }
})

setGray define o nível de escala de cinza, e defineFontFormat define a fonte ativa. Consulte a referência de interfaces de serviço para as especificações fixas de impressora — tamanho de imagem, limites de caracteres e o limiar de escala de cinza que seu serviço deve aplicar.

Câmera

readFront lê a câmera frontal com um tempo limite e reporta o resultado por um callback:

mainService.camera.readFront(
    timeout = 2000,
    callback = object : ICameraCallback.Stub() {
        override fun onSuccess(code: String?) { /* code read */ }
        override fun onTimeout() { /* no result within timeout */ }
        override fun onCancel() { /* caller canceled the read */ }
        override fun onError(error: String?) { /* handle error */ }
    },
)

Estatísticas do sistema

O IStatService reporta as contagens de sucesso e de falha por tipo de leitura, tanto no dispositivo inteiro quanto por aplicação chamadora:

mainService.stats.getAllStatisticsByApp(object : IStatCallback.Stub() {
    override fun onStatistic(statResponse: StatResponse?) {
        val paperStatus = statResponse?.generalPaperStatus
        val mifareStatus = statResponse?.generalMifareStatus
    }
    override fun onError(error: String?) { /* handle error */ }
})

Alguns métodos HAL, como print(userId, callback) e searchMag(userId, timeout, callback), recebem um parâmetro userId que identifica a aplicação chamadora. Resolva-o para um nome de pacote com packageManager.getNameForUid(userId). Use esse nome de pacote para atribuir as estatísticas à aplicação correta.

Ler o resultado

Uma implementação correta sempre reporta os eventos de hardware pelo callback de quem chama, nunca só por um valor de retorno. Ela também chama stopAllReaders() depois que uma leitura de cartão termina, tendo tido sucesso ou não.

Os métodos de impressora são a única exceção que merece menção própria: qualquer um deles chamado antes de init() deve lançar IllegalStateException em vez de falhar em silêncio.

Próximos passos