Você está na página 1de 36

Autorizao de transaes

no Nubank
Consumindo servios dos anos 80
com tecnologias atuais

Lucas Cavalcanti
Luiz Hespanha

@lucascs @luiz_hespanha
Contexto
Microservios
~120 servios
~80 desenvolvedores
~10 squads
3,5 anos
ARQUITETURA GERAL NUBANK
Tecnologias

Datomic
MOTIVAO
processo de compra com carto de crdito

Adquirente

Estabelecimento Bandeira

Link fsico
Cliente Processadora

Emissor

Mquinas fsicas
DESAFIOS

Mquinas fsicas
Protocolos no convencionais
Comunicao via Socket
Hardware de Criptografia
Baixa latncia

SO PAULO, BRASIL
MQUINAS FSICAS
Processadora
Prod S0
DMZ
MIP

Prod S1

switch

VPC
COMUNICAES
processo de compra com carto de crdito

Adquirente

Estabelecimento Bandeira

???

Cliente Processadora

Emissor

Kafka + JSON
Padro ISO que define o formato da
mensagem e o fluxo de comunicao

ISO 8583
ISO 8583
ISO 8583
SCODEC
Scala combinator library for working with binary data

scodec.org
SCODEC

val de02 = LLIntString.partialField(1, "DE02 - PAN)

case class ProcessingCode(transactionType: IntString,


fromAccountType: IntString,
toAccountType: IntString)

val processingCode: Codec[ProcessingCode] =


(intString(2) :: intString(2) :: intString(2)).as[ProcessingCode]

val de03 = processingCode.partialField(2, "DE03 - Processing Code")

val de04 = longPadded(12).partialField(3, "DE04 - Amount, Transaction")

//
SCODEC

de02 :: de03 :: de04 :: de05 :: de06 :: de07 :: de09 :: de10 :: de11 :: de12 ::

de13 :: de14 :: de15 :: de16 :: de18 :: de20 :: de22 :: de23 :: de26 :: de28 ::

de32 :: de33 :: de35 :: de37 :: de38 :: de39 :: de41 :: de42 :: de43 :: de44 ::

de45 :: de47 :: de48 :: de49 :: de50 :: de51 :: de52 :: de53 :: de54 :: de55 ::

de60 :: de61 :: de62 :: de63 :: Nil


SCODEC

"The AdditionalData codec" should "encode a message" in {

val expected = BitVector("025R2001P6315MCS0110HL0216 ".getBytes)

val message = AdditionalData.empty("R").copy(

se20 = Some(CardholderVerificationMethod("P")),

se63 = Some(TraceID("MCS", "0110HL", MonthDay.of(2, 16))))

additionalData.codec.encode(message).require should be (expected)

it should "decode a message" in {

val message = BitVector("025R2001P6315MCS0110HL0216 ".getBytes)


COMUNICAO VIA SOCKET

No cliente/servidor
Multiplex
Depois de 7s no adianta mais responder
FINAGLE
A Protocol-Agnostic RPC System
FINAGLE
Funcionalidades

Pool de conexo
Deteco de falhas
Retentativas
Circuit Breaker
Timeout
Load Balancer
Diversas monitorias
FINAGLE
Como funciona

type Service[Req, Rep] = Req => Future[Rep]

Req
Cliente Future[Rep] Servidor
FINAGLE
Protocolos
FINAGLE
Protocolo ISO8583

def getPipeline: ChannelPipeline = {


val pipeline = Channels.pipeline()
pipeline.addLast("frame", new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2))
pipeline.addLast("decoder", new ScodecDecoder(codec))
pipeline.addLast("frameEnc", new LengthFieldPrepender(2, false))
pipeline.addLast("encoder", new ScodecEncoder(codec))

pipeline
}

val client: Service[Iso8583Msg, Iso8583Msg] = ClientBuilder()


.codec(Codec.ofPipeline(getPipeline))
.hosts(address)
.build()
FINAGLE
Protocolo Proprietrio HSM
(-> (bc/builder)
(bc/report-to stats-receiver)
(bc/hosts hosts)
(bc/codec
(.ofPipelineFactory
Codec$/MODULE$
(scala/Function0
(doto (Channels/pipeline)
(.addLast "frame" (LengthFieldBasedFrameDecoder. 65535, 0, 2, 0, 2))
(.addLast "decoder" (decoder))
(.addLast "frameEnc" (LengthFieldPrepender. 2 false))
(.addLast "encoder" (encoder))))))

(defn decoder []
(proxy [OneToOneDecoder] []
(defn encoder []
(decode [ctx chan value]
(proxy [OneToOneEncoder] []
(let [counter (cmd/<-str value 4)
(encode [ctx chan value]
cmd (keyword (cmd/<-str value 2))
(if-let [[cmd params] (when (vector? value) value)]
return-code (cmd/<-str value 2)]
(-> (ChannelBuffers/dynamicBuffer)
(cond
(cmd/->str 4 "0001")
(#{"00" "02"} return-code)
(cmd/->str 2 (name cmd))
[:ok (cmd/decode value cmd)]
(cmd/encode cmd params))
value))))
(#{"01"} return-code)
[:nok])))))
FINAGLE
Futuros

Futuros compem com map, flatmap, handle e rescue

Callbacks:

onSuccess(f : A => Unit)


onFailure(ex: Throwable => Unit)
SERVIOS
Mastercard MIP
ISO8583 ISO8583

Finagle Client Finagle Client

Router Router
Finagle Client Finagle Client

Thrift Thrift
Thrift Thrift

Finagle Server Finagle Server Finagle Server Finagle Server

Autorizador Autorizador Autorizador Autorizador


Finagle Client Finagle Client Finagle Client Finagle Client
Thrift Thrift Thrift Thrift

Finagle Server Finagle Server Finagle Server Finagle Server

Criptografia Detector de Criptografia Detector de


Finagle Client Fraude Finagle Client Fraude
Proprietrio Proprietrio

HSM HSM
BANCO DE DADOS
BANCO DE DADOS NA MEMRIA
Representao

{5162920000001234" {:limite 96000M


:status :ativo}
5162920000004321" {:limite 0M
:status :ativo}
.
.
.
5162920000009876" {:limite 15M
:status :bloqueado}}
BANCO DE DADOS NA MEMRIA
ATOMS

Gerencia estado compartilhado, sncrono e independente.


Mudanas em ATOMS so sempre livres de race conditions
Como faz quando precisa reiniciar a aplicao?
KAFKA
Funcionamento
EVENT SOURCING

Garante que todas as mudanas de estado na aplicao


sejam armazenadas como uma sequncia de eventos.
EVENTOS

{516212: {:status :bloqueado}}


{516223: {:status :bloqueado}}

Kafka topic

{516223: {:status :ativo}}


{516212: {:limite 10M}}
{516212: {:status 5M}}
SNAPSHOT
Gerao

1) Lemos o valor do offset de cada uma das parties do tpico.


2) Mapa em memria serializado e encriptado.
3) Geramos um arquivo com estes dois dados e guardamos.
SNAPSHOT
Leitura

1) Pegamos o snapshot mais recente.


2) decriptamos e desserializamos o mapa.
3) Mudamos o offset das parties para os valores do arquivo
4) Aguardamos o replay" das mensagens e liberamos a instncia.
DESAFIOS

Mquinas fsicas
Protocolos no convencionais
Comunicao via Socket
Hardware de Criptografia
Baixa latncia

SO PAULO, BRASIL
TEMOS VAGAS
Devops
Engenheiro de Software

Mais infos em:


https://nubank.com.br/jobs

Nosso stand no evento


OBRIGADO!