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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ node_modules/

# credenciais dos exemplos (nunca versionar)
examples/abacatepay/credentials.php
examples/woovi/credentials.php
examples/asaas/credentials.php
examples/efi/credentials.php
examples/mercadopago/credentials.php
Expand Down
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ e rode `php examples/asaas/charges.php` (ou `make asaas resource=charges`).
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`.
- **Woovi/OpenPix** — **segundo gateway com as cinco capacidades**, junto com o Asaas.
AppID vai **cru** no `Authorization`, sem esquema. Sandbox tem **domínio próprio**
(`api.woovi-sandbox.com`). O webhook fica em `api/openpix/v1/` enquanto os demais
recursos ficam em `api/v1/` — herança da fusão das marcas, não erro. Todo objeto é
endereçável pelo `correlationID` (id do sistema de quem integra), então `find()` e
`destroy()` aceitam os dois ids. Valores em centavos.
- **AbacatePay** — host único e **sem prefixo de chave**: não dá para derivar o
ambiente da credencial, então **não existe `isSandbox()`** — inventar convenção aqui
seria mentira. A resposta da cobrança traz `devMode`, e é isso que `isDevMode()` lê.
Expand Down
99 changes: 85 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- [Cielo](#cielo)
- [Rede](#rede)
- [AbacatePay](#abacatepay)
- [Woovi/OpenPix](#wooviopenpix)
- [Efí](#efí)
- [Exemplos executáveis](#exemplos-executáveis)
- [Migrando da v1](#migrando-da-v1)
Expand Down Expand Up @@ -70,13 +71,20 @@ Trocar de gateway é trocar a linha do construtor.

## Gateways suportados

| Capacidade | Interface | Asaas | Mercado Pago | PagBank | Pagar.me | Cielo | Rede | Abacate | Efí |
| --- | --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| Clientes | `SupportsCustomers` | ✅ | ✅ | ✅ | ✅ | — | — | ✅ | — |
| Cobranças | `SupportsCharges` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Assinaturas | `SupportsSubscriptions` | ✅ | ✅ | ✅ | ✅ | ✅ | — | — | — |
| Webhooks | `SupportsWebhooks` | ✅ | — | — | — | — | — | — | — |
| Chaves Pix | `SupportsPixKeys` | ✅ | — | — | — | — | — | — | — |
| Gateway | Clientes | Cobranças | Assinaturas | Webhooks | Chaves Pix |
| --- | :---: | :---: | :---: | :---: | :---: |
| **Asaas** | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Woovi/OpenPix** | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Mercado Pago** | ✅ | ✅ | ✅ | — | — |
| **PagBank** | ✅ | ✅ | ✅ | — | — |
| **Pagar.me** | ✅ | ✅ | ✅ | — | — |
| **AbacatePay** | ✅ | ✅ | — | — | — |
| **Cielo** | — | ✅ | ✅ | — | — |
| **Rede** | — | ✅ | — | — | — |
| **Efí** | — | ✅ | — | — | — |

As interfaces correspondentes são `SupportsCustomers`, `SupportsCharges`,
`SupportsSubscriptions`, `SupportsWebhooks` e `SupportsPixKeys`.

Duas colunas merecem explicação, porque a ausência de ✅ **não** quer dizer que o
gateway não aceita Pix ou não manda webhook:
Expand Down Expand Up @@ -240,6 +248,7 @@ que cada um faz em vez de inventar um padrão:
| **Rede** | `$sandbox` no construtor — troca **as duas** URLs e o caminho do token |
| **Mercado Pago** | Prefixo do token (`TEST-`); host único, sem `$sandbox` |
| **Pagar.me** | Prefixo da chave (`sk_test_`); host único, sem `$sandbox` |
| **Woovi** | `$sandbox` no construtor — o sandbox tem **domínio próprio** |
| **AbacatePay** | Pela chave usada; host único, **sem prefixo** — a cobrança informa em `devMode` |

Nos dois últimos, `isSandbox()` diz em qual ambiente você está:
Expand All @@ -266,6 +275,7 @@ em vez de falhar:
| **Cielo** | Centavos (inteiro) | `10050` |
| **Rede** | Centavos (inteiro) | `10050` |
| **AbacatePay** | Centavos (inteiro, mín. 100) | `10050` |
| **Woovi** | Centavos (inteiro) | `10050` |
| **Efí** | Centavos (inteiro) | `10050` |

Nos gateways que usam centavos, o PHPay **recusa valor decimal na validação**,
Expand Down Expand Up @@ -709,6 +719,63 @@ $gateway->coupons()->create([
]);
```

### Woovi/OpenPix

**O segundo gateway com as cinco capacidades**, ao lado do Asaas — e o que
confirma que o modelo descreve o domínio, não um fornecedor: são duas empresas
independentes, com APIs independentes, preenchendo o mesmo contrato.

Sendo PSP Pix-nativo, ele gerencia chaves e QR Code estático de verdade.

```php
use PHPay\Woovi\Enums\PixKeyTypeEnum;
use PHPay\Woovi\WooviGateway;

$phpay = PHPay::gateway(new WooviGateway(WOOVI_APP_ID));

/* chaves Pix da conta */
$phpay->pix()->createKey(PixKeyTypeEnum::RANDOM);
$phpay->pix()->getAll();

/* consulta uma chave antes de pagar */
$phpay->pix()->verifyKey('fale@phpay.io');

/* QR Code estático, com ou sem valor */
$phpay->pix()->staticQrCode('Caixa 1');
$phpay->pix()->staticQrCode('Caixa 2', 2500);
```

Três particularidades:

**O AppID vai cru no `Authorization`** — sem `Bearer`, sem `Basic`.

**O sandbox tem domínio próprio**: `api.woovi-sandbox.com` contra
`api.openpix.com.br`.

**Todo objeto é endereçável pelo `correlationID`**, o id no *seu* sistema —
nenhum outro gateway da biblioteca oferece isso:

```php
$cobranca = $phpay->charge()
->setCorrelationId('pedido-1')
->setCustomer(['name' => 'Mário Lucas', 'email' => 'fale@phpay.io'])
->create(10050); // R$ 100,50

$phpay->charge()->getPixCode($cobranca);
$phpay->charge()->find('pedido-1'); // pelo SEU id, não pelo do gateway
```

Webhooks têm CRUD por API — junto com o Asaas, os únicos:

```php
$phpay->webhook(['name' => 'PHPay', 'url' => 'https://exemplo.test/webhook'])->create();
$phpay->webhook()->getAll();
```

> Repare que o webhook fica em `api/openpix/v1/`, enquanto os demais recursos
> ficam em `api/v1/` — herança da fusão das duas marcas. O PHPay trata isso
> internamente.

### Efí

Só cobranças, por enquanto. O gateway **não faz chamada de rede no construtor**
Expand Down Expand Up @@ -785,13 +852,17 @@ Dois pontos merecem auditoria de quem vem da v1:

### Cobertura por gateway

| | Asaas | Mercado Pago | PagBank | Pagar.me | Cielo | Rede | Abacate | Efí |
| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| Cobranças | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Clientes | ✅ | ✅ | ✅ | ✅ | — | — | ✅ | 🕥 |
| Assinaturas | ✍️ | ✅ | ✅ | ✅ | ✅ | — | — | 🕥 |
| Webhooks | ✅ | — | — | leitura ✅ | — | — | — | 🕥 |
| Pix | ✅ | ✅ | ✅ | ✅ | ✅ | 🕥 | ✅ | 🕥 |
| Gateway | Cobranças | Clientes | Assinaturas | Webhooks | Pix |
| --- | :---: | :---: | :---: | :---: | :---: |
| **Asaas** | ✅ | ✅ | ✍️ | ✅ | ✅ |
| **Woovi/OpenPix** | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Mercado Pago** | ✅ | ✅ | ✅ | — | ✅ |
| **PagBank** | ✅ | ✅ | ✅ | — | ✅ |
| **Pagar.me** | ✅ | ✅ | ✅ | leitura ✅ | ✅ |
| **AbacatePay** | ✅ | ✅ | — | — | ✅ |
| **Cielo** | ✅ | — | ✅ | — | ✅ |
| **Rede** | ✅ | — | — | — | 🕥 |
| **Efí** | ✅ | 🕥 | 🕥 | 🕥 | 🕥 |

**✅** pronto · **✍️** parcial · **🕥** planejado · **—** não existe na API do gateway

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"PHPay\\PagarMe\\": "src/Gateways/PagarMe/",
"PHPay\\Cielo\\": "src/Gateways/Cielo/",
"PHPay\\Rede\\": "src/Gateways/Rede/",
"PHPay\\AbacatePay\\": "src/Gateways/AbacatePay/"
"PHPay\\AbacatePay\\": "src/Gateways/AbacatePay/",
"PHPay\\Woovi\\": "src/Gateways/Woovi/"
}
},
"autoload-dev": {
Expand Down
70 changes: 70 additions & 0 deletions examples/woovi/charges.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

use PHPay\Exceptions\PHPayException;
use PHPay\PHPay;
use PHPay\Woovi\Enums\PixKeyTypeEnum;
use PHPay\Woovi\WooviGateway;

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

require_once __DIR__ . '/credentials.php';

/**
* O Woovi é o segundo gateway da biblioteca com as cinco capacidades — o
* outro é o Asaas.
*
* @var WooviGateway $gateway
*/
$gateway = new WooviGateway(WOOVI_APP_ID);

$phpay = PHPay::gateway($gateway);

try {
/*
| Cobrança. Valor em CENTAVOS, e o correlationID é o id no SEU sistema —
| é por ele que você consulta depois, sem precisar guardar o id do gateway.
*/
$cobranca = $phpay->charge()
->setCorrelationId('pedido-' . time())
->setCustomer(['name' => NAME, 'email' => EMAIL])
->create(10050); /* R$ 100,50 */

echo $phpay->charge()->getPixCode($cobranca) . PHP_EOL;

$phpay->charge()->find('pedido-1');
$phpay->charge()->setQueryParams(['status' => 'ACTIVE'])->getAll();

/*
| Chaves Pix. Junto com o Asaas, é o único gateway da biblioteca que
| gerencia chaves de verdade — por ser PSP.
*/
$chave = $phpay->pix()->createKey(PixKeyTypeEnum::RANDOM);

$phpay->pix()->getAll();

/* consulta uma chave de terceiro antes de pagar */
$phpay->pix()->verifyKey('destinatario@exemplo.test');

/* QR Code estático: sem valor, o pagador escolhe quanto pagar */
$phpay->pix()->staticQrCode('Caixa 1');
$phpay->pix()->staticQrCode('Mensalidade', 4990, 'mensalidade-2026');

/* Webhooks com CRUD por API — também só aqui e no Asaas */
$phpay->webhook([
'name' => 'PHPay',
'url' => 'https://exemplo.test/webhook/woovi',
])->create();

$phpay->webhook()->getAll();

/* Assinatura: cobrança recorrente por Pix */
$phpay->subscription()
->setCustomer(['name' => NAME, 'email' => EMAIL])
->setDayGenerateCharge(10)
->create(4990);

/* Cliente avulso */
$phpay->customer(['name' => NAME, 'email' => EMAIL])->create();
} catch (PHPayException $exception) {
echo $exception->getMessage() . PHP_EOL;
}
14 changes: 14 additions & 0 deletions examples/woovi/credentials.example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

/*
| Copie este arquivo para credentials.php e preencha com o seu AppID.
| credentials.php é ignorado pelo git — nunca commite credencial real.
|
| O AppID sai do painel. O sandbox tem domínio próprio
| (api.woovi-sandbox.com), então a credencial de teste é criada lá.
*/

const WOOVI_APP_ID = '';

const NAME = 'Mário Lucas';
const EMAIL = 'fale@phpay.io';
12 changes: 12 additions & 0 deletions src/Gateways/Woovi/Enums/ChargeStatusEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace PHPay\Woovi\Enums;

enum ChargeStatusEnum: string
{
case ACTIVE = 'ACTIVE';
case COMPLETED = 'COMPLETED';
case EXPIRED = 'EXPIRED';
case PARTIALLY_REFUNDED = 'PARTIALLY_REFUNDED';
case REFUNDED = 'REFUNDED';
}
17 changes: 17 additions & 0 deletions src/Gateways/Woovi/Enums/PixKeyTypeEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace PHPay\Woovi\Enums;

/**
* types of Pix key that can be registered.
*
* PHONE and EMAIL need extra permission on the account, per the Woovi docs.
*/
enum PixKeyTypeEnum: string
{
case CPF = 'CPF';
case CNPJ = 'CNPJ';
case EMAIL = 'EMAIL';
case PHONE = 'PHONE';
case RANDOM = 'EVP';
}
62 changes: 62 additions & 0 deletions src/Gateways/Woovi/Interface/WooviGatewayInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace PHPay\Woovi\Interface;

use PHPay\Contracts\{SupportsCharges, SupportsCustomers, SupportsPixKeys, SupportsSubscriptions, SupportsWebhooks};
use PHPay\Woovi\Resources\Charge\Charge;
use PHPay\Woovi\Resources\Customer\Customer;
use PHPay\Woovi\Resources\Pix\Pix;
use PHPay\Woovi\Resources\Subscription\Subscription;
use PHPay\Woovi\Resources\Webhook\Webhook;

/**
* the Woovi/OpenPix gateway offers every capability the library models.
*
* it is the second to do so, after Asaas — and the two are independent
* companies with independent APIs, which is what tells us the capability
* model describes the domain rather than one vendor.
*/
interface WooviGatewayInterface extends
SupportsCustomers,
SupportsCharges,
SupportsWebhooks,
SupportsPixKeys,
SupportsSubscriptions
{
/**
* get resource customer from gateway.
*
* @param array<mixed> $customer
* @return Customer
*/
public function customer(array $customer = []): Customer;

/**
* get resource charge from gateway.
*
* @return Charge
*/
public function charge(): Charge;

/**
* get resource webhook from gateway.
*
* @param array<mixed> $webhook
* @return Webhook
*/
public function webhook(array $webhook = []): Webhook;

/**
* get resource pix from gateway.
*
* @return Pix
*/
public function pix(): Pix;

/**
* get resource subscription from gateway.
*
* @return Subscription
*/
public function subscription(): Subscription;
}
Loading
Loading