Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
vendor/
node_modules/
.idea/

# credenciais dos exemplos (nunca versionar)
examples/asaas/credentials.php
examples/efi/credentials.php
.idea/
examples/mercadopago/credentials.php
examples/pagbank/credentials.php

# configurações locais do Claude Code (pessoais, não versionar)
.claude/settings.local.json
15 changes: 11 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Orientações para o Claude Code trabalhar neste repositório.

PHPay (`phpay-io/phpay`) é uma **biblioteca PHP** (não uma aplicação) que padroniza a
integração com gateways de pagamento brasileiros. Hoje suporta **Asaas** (as cinco
capacidades), **Mercado Pago** (clientes, cobranças, assinaturas) e **Efí** (cobranças).
capacidades), **Mercado Pago** e **PagBank** (clientes, cobranças, assinaturas) e
**Efí** (cobranças).

Requisitos: PHP `^8.1` para consumir a lib; `^8.2` para rodar o ambiente de dev
(Pest 3 e Termwind 2 exigem 8.2+). Dependências de runtime: `ext-curl`, `ext-json`,
Expand Down Expand Up @@ -137,6 +138,12 @@ e rode `php examples/asaas/charges.php` (ou `make asaas resource=charges`).

- **Asaas** — `$sandbox` troca a base URL. Único com chaves Pix, porque é PSP.
- **Efí** — autoriza sob demanda (token em cache no gateway); `$sandbox` troca a base URL.
- **PagBank** — **duas APIs em hosts diferentes**: pedidos em `api.pagseguro.com`,
assinaturas em `api.assinaturas.pagseguro.com`. O trait expõe `clientPagBankBoot()`
e `clientPagBankSubscriptionsBoot()`; cada recurso boota o seu. **Todo valor é
inteiro em centavos** — os validadores recusam decimal, porque mandar `10.50` onde
se espera `1050` cobra onze centavos. Pix é `qr_codes` do pedido (um só por pedido,
copia-e-cola em `qr_codes[0].text`), não uma `charge`.
- **Mercado Pago** — **não tem URL de sandbox**: o ambiente vem do prefixo `TEST-` do
access token, então o construtor não recebe `$sandbox`. `POST /v1/payments` exige
`X-Idempotency-Key` (por isso `HasHttpClient::post()` aceita headers por requisição).
Expand All @@ -148,9 +155,9 @@ e rode `php examples/asaas/charges.php` (ou `make asaas resource=charges`).
carnê e NFe seguem pendentes na API do Asaas.
- A Efí só tem autorização e cobranças; `customer`, `webhook`, `pix` e `subscription`
lançam `NotImplementedException`.
- O Mercado Pago não implementa `SupportsWebhooks` nem `SupportsPixKeys`, e isso é
correto: webhooks só têm configuração por painel ou `notification_url` por pagamento,
e Pix lá é forma de pagamento. Não "resolva" isso criando stubs.
- Nem o Mercado Pago nem o PagBank implementam `SupportsWebhooks` ou `SupportsPixKeys`,
e isso é correto: webhooks só têm configuração por painel ou `notification_url(s)` por
cobrança, e Pix nos dois é forma de pagamento. Não "resolva" isso criando stubs.
- `Efi\Resources\Charge\Charge` tem `$items` e `$configuration` privados sem setter —
hoje sempre caem no fallback (`getItems()` monta um item a partir de
`description`/`value`; `getConfigurations()` usa fine 200 / interest 33).
Expand Down
95 changes: 86 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ O PHPay é uma biblioteca PHP que tem o objetivo tornar o trabalho de integraç

- Asaas (cobranças, clientes, webhooks, chaves Pix e assinaturas)
- Mercado Pago (cobranças, clientes e assinaturas)
- PagBank / PagSeguro (cobranças, assinantes e assinaturas)
- Efí (cobranças)

## ⬆️ Vindo da v1?
Expand Down Expand Up @@ -164,16 +165,17 @@ $phpay
Nem todo gateway oferece todo recurso. Cada gateway **declara** o que suporta
através de interfaces de capacidade, em vez de o contrato ser a união de tudo:

| Capacidade | Interface | Asaas | Mercado Pago | Efí |
| --- | --- | :---: | :---: | :---: |
| Clientes | `SupportsCustomers` | ✅ | ✅ | — |
| Cobranças | `SupportsCharges` | ✅ | ✅ | ✅ |
| Webhooks | `SupportsWebhooks` | ✅ | — | — |
| Chaves Pix | `SupportsPixKeys` | ✅ | — | — |
| Assinaturas | `SupportsSubscriptions` | ✅ | ✅ | — |
| Capacidade | Interface | Asaas | Mercado Pago | PagBank | Efí |
| --- | --- | :---: | :---: | :---: | :---: |
| Clientes | `SupportsCustomers` | ✅ | ✅ | ✅ | — |
| Cobranças | `SupportsCharges` | ✅ | ✅ | ✅ | ✅ |
| Webhooks | `SupportsWebhooks` | ✅ | — | — | — |
| Chaves Pix | `SupportsPixKeys` | ✅ | — | — | — |
| Assinaturas | `SupportsSubscriptions` | ✅ | ✅ | ✅ | — |

> O Mercado Pago não expõe CRUD de webhooks por API: eles são configurados no
> painel "Suas integrações", ou por pagamento através do campo `notification_url`.
> Nem Mercado Pago nem PagBank expõem CRUD de webhooks por API: eles são
> registrados no painel, ou por cobrança através de `notification_url` /
> `notification_urls`.

> `SupportsPixKeys` é mais estreito que "aceita Pix": ele significa gerenciar
> chaves e QR Code estático, algo que só um PSP que emite chave própria oferece.
Expand Down Expand Up @@ -310,6 +312,73 @@ $phpay->setPayerEmail('comprador@exemplo.test')
->create(['back_url' => 'https://exemplo.test/retorno']);
```

## 🏦 PagBank (PagSeguro)

Duas particularidades que o PHPay resolve por você.

**Duas APIs em hosts diferentes.** Pedidos vivem em `api.pagseguro.com`,
assinaturas em `api.assinaturas.pagseguro.com`. Cada recurso boota o client
da API certa — você não precisa saber disso.

**Todo valor é inteiro em centavos.** R$ 100,50 é `10050`. Mandar `100.50`
cobraria um real. O PHPay recusa decimal na validação, antes de chegar na API.

```php
use PHPay\PagBank\PagBankGateway;

$phpay = PHPay::gateway(new PagBankGateway(TOKEN_PAGBANK_SANDBOX))->charge();
```

No PagBank o **Pix não é uma cobrança**: ele entra como `qr_codes` do pedido, e
só um por pedido. A conta precisa ter uma chave Pix ativa.

```php
$pedido = $phpay
->setCustomer(['name' => 'Mário', 'email' => 'fale@phpay.io', 'tax_id' => '12345678901'])
->addItem('Assinatura PHPay', 10050) // R$ 100,50
->setQrCode(10050)
->setNotificationUrls(['https://exemplo.test/webhook/pagbank'])
->create();

$phpay->getPixCode($pedido['id']); // copia-e-cola, de qr_codes[0].text
```

Cartão e boleto, aí sim, vão em `charges`:

```php
$phpay
->setCustomer($customer)
->addItem('Camiseta', 5990, 2)
->setCharges([[
'reference_id' => 'cobranca-1',
'amount' => ['value' => 11980, 'currency' => 'BRL'],
'payment_method' => ['type' => 'CREDIT_CARD', 'installments' => 1, 'capture' => true],
]])
->create();
```

Assinaturas sempre pertencem a um plano, e o assinante pode nascer junto:

```php
$phpay = PHPay::gateway(new PagBankGateway(TOKEN_PAGBANK_SANDBOX))->subscription();

$plano = $phpay->createPlan([
'name' => 'Plano PHPay Mensal',
'amount' => ['value' => 4990, 'currency' => 'BRL'], // R$ 49,90
'interval' => ['unit' => 'MONTHS', 'length' => 1],
]);

$phpay->setPlan($plano['id'])
->setCustomer(['name' => 'Mário', 'email' => 'fale@phpay.io', 'tax_id' => '12345678901'])
->create();
```

Para conferir contra o sandbox de verdade:

```bash
PAGBANK_TOKEN='...' php examples/pagbank/sandbox-check.php
```

## 📝 Roadmap

- Definições de Arquitetura ✅
Expand Down Expand Up @@ -337,6 +406,14 @@ $phpay->setPayerEmail('comprador@exemplo.test')
- Webhook — sem CRUD por API
- Pix ✅ (como forma de pagamento)

- PagBank.

- Cobranças ✅
- Assinantes ✅
- Assinaturas ✅ (com planos)
- Webhook — sem CRUD por API
- Pix ✅ (como QR Code do pedido)

- Efí.

- Autorização ✅
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"PHPay\\": "src/",
"PHPay\\Asaas\\": "src/Gateways/Asaas/",
"PHPay\\Efi\\": "src/Gateways/Efi/",
"PHPay\\MercadoPago\\": "src/Gateways/MercadoPago/"
"PHPay\\MercadoPago\\": "src/Gateways/MercadoPago/",
"PHPay\\PagBank\\": "src/Gateways/PagBank/"
}
},
"autoload-dev": {
Expand Down
78 changes: 78 additions & 0 deletions examples/pagbank/charges.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

use PHPay\Exceptions\PHPayException;
use PHPay\PagBank\Enums\PaymentMethodEnum;
use PHPay\PagBank\PagBankGateway;
use PHPay\PagBank\Resources\Charge\Charge;
use PHPay\PHPay;

require_once __DIR__ . '/../../vendor/autoload.php';

require_once __DIR__ . '/credentials.php';

/**
* @var Charge $phpay
*/
$phpay = PHPay::gateway(new PagBankGateway(TOKEN_PAGBANK_SANDBOX))->charge();

$customer = [
'name' => NAME,
'email' => EMAIL,
'tax_id' => TAX_ID,
];

try {
/*
| Pedido com Pix. Repare que o Pix NÃO é uma charge: ele entra como
| qr_codes do pedido, e só um por pedido. A conta precisa ter uma chave
| Pix ativa no PagBank.
|
| Todo valor é inteiro em CENTAVOS: R$ 100,50 é 10050.
*/
$pedido = $phpay
->setCustomer($customer)
->addItem('Assinatura PHPay', 10050)
->setQrCode(10050)
/* sem CRUD de webhook na API: a notificação é por pedido */
->setNotificationUrls(['https://exemplo.test/webhook/pagbank'])
->create();

$pedidoId = (string) $pedido['id'];

/* código copia-e-cola, que vem em qr_codes[0].text */
echo $phpay->getPixCode($pedidoId) . PHP_EOL;

/* consulta do pedido */
$phpay->find($pedidoId);

/*
| Pedido com cartão. Aqui sim a cobrança vai em charges.
| O card exige tokenização — veja a documentação do PagBank.
*/
$comCartao = PHPay::gateway(new PagBankGateway(TOKEN_PAGBANK_SANDBOX))
->charge()
->setCustomer($customer)
->addItem('Camiseta', 5990, 2)
->setCharges([[
'reference_id' => 'cobranca-1',
'description' => 'Camiseta',
'amount' => ['value' => 11980, 'currency' => 'BRL'],
'payment_method' => [
'type' => PaymentMethodEnum::CREDIT_CARD->value,
'installments' => 1,
'capture' => true,
/* 'card' => ['encrypted' => '...'] */
],
]])
->create();

$cobrancaId = (string) $comCartao['charges'][0]['id'];

echo $phpay->getStatus($cobrancaId) . PHP_EOL;

/* estorno parcial e total, também em centavos */
$phpay->refund($cobrancaId, 2500);
$phpay->refund($cobrancaId);
} catch (PHPayException $exception) {
echo $exception->getMessage() . PHP_EOL;
}
16 changes: 16 additions & 0 deletions examples/pagbank/credentials.example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
| Copie este arquivo para credentials.php e preencha com o token de sandbox.
| credentials.php é ignorado pelo git — nunca commite credencial real.
|
| O PagBank separa ambiente por host: sandbox.api.pagseguro.com e
| api.pagseguro.com. O token de sandbox sai do painel de sandbox e não
| funciona em produção.
*/

const TOKEN_PAGBANK_SANDBOX = '';

const NAME = 'Mário Lucas';
const EMAIL = 'comprador@sandbox.pagseguro.com.br';
const TAX_ID = '00000000000';
Loading
Loading