Skip to content

PHPAY-173: feat: billets everywhere they exist, cards where the number fits - #177

Closed
mariolucasdev wants to merge 1 commit into
feat/phpay-173-fase2from
feat/phpay-173-fase3
Closed

mariolucasdev wants to merge 1 commit into
feat/phpay-173-fase2from
feat/phpay-173-fase3

Conversation

@mariolucasdev

Copy link
Copy Markdown
Collaborator

Fase 3 de #173. Empilhado sobre o #176 — a base é feat/phpay-173-fase2,
então mergeie o #176 primeiro. Como o merge é squash, eu preciso rebasear este
com --onto depois; o botão "reaponta sozinho" não serve aqui.

Começo pelo que mudou de premissa

A fase 3 era "boleto(), cartao(), Card e os gateways que faltam". Ao ler
como cada um recebe cartão, o cartao() universal se mostrou impossível,
não só trabalhoso:

gateway como recebe o cartão dá para usar Card?
Cielo o número, em Payment.CreditCard ✅
Rede o número, em campos planos ✅
Mercado Pago um token do SDK de navegador deles não — o número nunca chega ao servidor
PagBank, Pagar.me cifrado ou tokenizado não
Asaas token ou número — mas só modelado em assinatura não

Então entregue: Card para os dois que recebem o número, e os outros não
declaram CREDIT_CARD
. É exatamente o caso de uso que o supportedMethods()
da fase 1 existe para cobrir.

O que entra

peça onde
PHPay\Support\Card número, titular, validade, CVV, bandeira
ChargeIntent::boleto() vencimento obrigatório, ao contrário do Pix
ChargeIntent::credito() com Card e parcelas
Asaas, Efí, Pagar.me, Cielo boleto
Cielo, Rede crédito

Os dez declaram a capacidade agora. A Cielo faz os três meios; a Rede, que
só faz crédito, entra pela primeira vez no caminho portável.

O Card e o PAN

  • Luhn antes de qualquer requisição. Um dígito trocado custa nada se for
    pego aqui e custa uma recusa no checkout de alguém se for pego no gateway.
  • Nunca num dump. #[\SensitiveParameter] no número e no CVV, e
    __debugInfo() devolve **** **** **** 1111 e ***. Tem teste provando que
    print_r não vaza nenhum dos dois.
  • A bandeira não é adivinhada. A Cielo a exige; derivá-la da faixa do
    número seria a biblioteca inventando um fato sobre o cartão. Sem ela, recusa
    antes de qualquer HTTP.

Débito ficou de fora, de propósito

Cielo e Pagar.me conhecem DebitCard/debit_card, mas débito pede
autenticação 3DS, que a biblioteca não modela. Nenhum dos dez declara
Method::DEBIT_CARD, e há teste varrendo isso — declarar um meio que a chamada
recusaria é o erro que esta capacidade foi feita para evitar.

A Efí faz boleto, não Pix

Lá charge() é o boleto e o Pix é pixCharge(), noutra API. Rotear Pix
mudaria o retorno de charge() e quebraria a v2 — então supportedMethods()
dela diz BOLETO, e a mensagem de recusa do Pix diz isso a quem tentar.

Uma consequência que quebrou um teste, e o conserto é melhor que o original

Com os dez declarando a capacidade, não sobrou gateway para provar a recusa
da facade
. O teste passou a usar um gateway anônimo que implementa só
SupportsCharges — o que é mais correto, porque quem está sendo testado é o
guard da facade, não um gateway específico.

Dois tropeços meus que valem registro

  • Dois arquivos de teste tinham sido realinhados pelo Pint (['appId' => virou
    ['appId' =>), e minha substituição por texto exato falhou em
    silêncio
    porque ali eu não tinha posto assert. Refiz por intervalo de
    linhas. A lição é a de sempre: substituição sem verificação não é edição, é
    esperança.
  • O smoke test afirmava "boleto ainda não existe no caminho portável", o que
    esta fase tornou falso em quatro gateways. Passou a usar DEBIT_CARD, que
    nenhum faz e cuja ausência já é afirmada noutro teste.

Portão

  • 845 testes (+24), 2310 asserções
  • PHPStan nível 9 limpo
  • matriz regerada sozinha: Efí, Cielo e Rede ganharam a coluna
  • smoke: 105 verificações, agora com os dez exercitando createCharge()

O que fica aberto na #173

Não é gateway, é meio: cartão nos que pedem token ou blob cifrado e
débito em qualquer um. Os dois precisam de desenho próprio — para o cartão,
provavelmente um CardToken ao lado do Card; para o débito, 3DS. Nenhum dos
dois é "mais um fromIntent()".

…r fits

boleto() and credito() finish the portable path on all ten gateways, and
Card describes a card for the two that take the number on the server.

Card cannot cover every gateway, and pretending otherwise would be the
lie: the Cielo and the Rede take the number, but the Mercado Pago takes
a token minted by its own browser SDK, so the number never reaches the
server at all, and the PagBank and the Pagar.me want it encrypted or
tokenized. Those simply do not declare CREDIT_CARD, which is what
supportedMethods() is for.

The number is checked with Luhn before any request, because a wrong
digit costs nothing here and a refusal on someone's checkout there. The
number and the CVV take SensitiveParameter and never reach a dump. The
brand is not derived from the number range: the Cielo requires it and
guessing would be the library inventing a fact about the card.

Debit is absent from all ten. The Cielo and the Pagar.me know the type,
but it needs 3DS authentication, which the library does not model, and
declaring a method the call would refuse is the mistake this capability
was built to avoid.

On the Efi the portable path does billets, not Pix: charge() there IS
the billet and the Pix charge is pixCharge(), on another API, so routing
Pix would change what charge() returns and break the v2.
@mariolucasdev

Copy link
Copy Markdown
Collaborator Author

Fechado pelo GitHub quando a branch da fase 2 foi apagada no merge do #176 — PR cujo branch-base some é fechado automaticamente, e não reabre.

Erro de ordem meu: o reapontamento para develop tinha de vir antes do merge, não depois.

Continua em #178, com a mesma branch já rebaseada (git rebase --onto origin/develop 4577cb3) e o mesmo conteúdo. Nada se perdeu.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant