Skip to content

PHPAY-83: adicionar o gateway Cielo (API E-commerce 3.0) - #84

Merged
mariolucasdev merged 1 commit into
developfrom
feat/phpay-83
Sep 21, 2026
Merged

mariolucasdev merged 1 commit into
developfrom
feat/phpay-83

Conversation

@mariolucasdev

Copy link
Copy Markdown
Collaborator

Closes #83

Sexto gateway, e a primeira adquirente da biblioteca. A Cielo lidera o mercado por número de usuários (28%, ~30% no varejo).

Capacidades: 2 de 5

Capacidade Declara? Motivo
SupportsCharges POST /1/sales, captura, cancelamento
SupportsSubscriptions Recorrência via /1/RecurrentPayment
SupportsCustomers Não há recurso de cliente — ele é um campo da venda
SupportsWebhooks Notificação configurada no backoffice ou por transação
SupportsPixKeys Pix é tipo de pagamento da venda

É a forma mais estreita depois do Efí, e é a esperada para uma adquirente. Adquirente autoriza e captura transação; cadastro de cliente e cobrança recorrente rica são domínio de gateway.

A particularidade que mexeu no código

A Cielo separa dois hosts por tipo de operação, não por domínio:

Operação Sandbox
Escrita apisandbox.cieloecommerce.cielo.com.br
Consulta apiquerysandbox.cieloecommerce.cielo.com.br

O PagBank também tem dois hosts, mas divididos por domínio (pedidos vs assinaturas) — lá cada recurso boota um client. Aqui o mesmo recurso precisa dos dois: create() vai num, find() no outro.

Por isso HasHttpClient::request() passa a aceitar um client opcional em vez de usar sempre $this->client. Mudança retrocompatível — nenhum gateway existente foi tocado — e o trait da Cielo a usa em queryGet(). Quem consome a biblioteca não vê diferença: o roteamento é interno.

Há teste verificando que find() não encosta no host de escrita.

Outras decisões de modelagem

A recorrência não tem endpoint de criação. Ela nasce de uma venda carregando um bloco RecurrentPayment, e só então ganha um RecurrentPaymentId próprio para ser gerenciada. O recurso Subscription expressa isso em vez de fingir um create() direto:

$recorrencia = $phpay->subscription()
    ->setCustomer(['Name' => 'Mário Lucas'])
    ->setCard($cartao)
    ->setInterval(RecurrentIntervalEnum::MONTHLY)
    ->create(15700);   // vai para POST /1/sales

Os updates da recorrência recebem JSON puro, não objeto: PUT .../Amount com corpo 19900, PUT .../Interval com corpo "Monthly". Daí o putValue() privado, e um teste que assevera o corpo exato.

Valores em centavos inteiros, recusados na validação se vierem decimais.

Header RequestId como chave de idempotência, gerado por chamada e fixável com setRequestId().

Recursos

  • ChargesetOrderId, setCustomer, setPix, setBoleto, setCreditCard, setRequestId, create, find, findByOrderId, getStatus, getPixCode, capture, cancel
  • SubscriptionsetCard, setInterval, setEndDate, create, find, deactivate, reactivate, updateAmount, updateInterval, updateEndDate, updateNextPaymentDate

Verificação

179 testes (463 asserções), PHPStan nível 9 limpo, Pint limpo. Endpoints, hosts, autenticação e o modelo de recorrência foram conferidos na documentação oficial da Cielo.

O sandbox dela é self-service — o cadastro devolve MerchantId e MerchantKey sem exigir afiliação comercial, o que é incomum para adquirente e torna viável testar de verdade.

Sexto gateway, e a primeira adquirente da biblioteca. Lidera o mercado por
número de usuários (28%, ~30% no varejo).

Declara duas das cinco capacidades:

  SupportsCharges       /1/sales, captura, cancelamento
  SupportsSubscriptions recorrência via /1/RecurrentPayment

É a forma mais estreita depois do Efí, e é a esperada para uma adquirente: não
existe recurso de cliente — ele é um campo da venda. Webhooks são configurados
no backoffice, e Pix é tipo de pagamento.

A particularidade que mais mexeu no código é que a Cielo separa dois hosts por
TIPO DE OPERAÇÃO, não por domínio: escritas vão para api.cieloecommerce, e
consultas para apiquery.cieloecommerce. O PagBank também tem dois hosts, mas
divididos por domínio, e lá cada recurso boota um client. Aqui o mesmo recurso
precisa dos dois — create() num, find() no outro.

Para isso, HasHttpClient::request() passa a aceitar um client opcional em vez
de usar sempre $this->client. É uma mudança retrocompatível, e o trait da Cielo
usa isso em queryGet(). Quem consome a biblioteca não vê diferença: o
roteamento é interno.

Outras decisões:

- A recorrência não tem endpoint de criação. Ela nasce de uma venda com bloco
  RecurrentPayment e só então ganha um RecurrentPaymentId próprio. O recurso
  Subscription expressa isso em vez de fingir um create() direto.
- Os endpoints de update da recorrência recebem um valor JSON puro no corpo
  (19900, "Monthly"), não um objeto. Daí o putValue() privado.
- Valores em centavos inteiros, validados antes de qualquer chamada.
- Header RequestId como chave de idempotência, gerado por chamada e fixável
  com setRequestId().

179 testes no total. Os da Cielo cobrem o roteamento entre os dois hosts, a
autenticação por MerchantId/MerchantKey, o corpo JSON puro dos updates de
recorrência e a exigência de cartão na assinatura.
@mariolucasdev
mariolucasdev merged commit fe0b95e into develop Sep 21, 2026
6 checks passed
@mariolucasdev
mariolucasdev deleted the feat/phpay-83 branch September 21, 2026 01:21
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.

Adicionar o gateway Cielo (API E-commerce 3.0)

1 participant