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
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,25 @@ e rode `php examples/asaas/charges.php` (ou `make asaas resource=charges`).
`PHPay\Efi\`, `PHPay\MercadoPago\` → `src/Gateways/<Gateway>/`. Gateway novo
precisa de um root novo no `composer.json` — não introduza `PHPay\Gateways\...`.

## Cliente

Use **`PHPay\Support\Customer`**. O mesmo campo tem seis grafias entre os gateways
(`cpfCnpj`, `tax_id`, `document`, `taxId`, `taxID`, `cpf_cnpj`), e o VO é a forma única.

- `Customer::make()` limpa pontuação de documento e telefone; o construtor não.
- **O mapeamento mora na classe `*CustomerRequest` de cada gateway**, em
`fromCustomer(Customer): array` — ela já detém o conhecimento do schema daquele
gateway, então validação e mapeamento ficam juntos. Gateway novo com cliente deve
ter o mesmo método.
- Todo `setCustomer()` e `customer()` aceita `Customer|array`. Array continua
funcionando; não remova esse caminho sem major.
- Lógica derivada mora no VO, não nos gateways: `isIndividual()`, `documentType()`,
`firstName()`/`lastName()`, `phoneParts()`. Se um gateway novo precisar de outra
derivação, acrescente lá em vez de calcular no mapper.
- `withExtra()` é para campo específico de um gateway. **Nenhum campo obrigatório
precisa dele hoje** — se um gateway novo precisar, é sinal de que o VO está
faltando algo.

## Valores monetários

Use **`PHPay\Support\Money`**. Os gateways discordam da unidade — Asaas e Mercado
Expand Down
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,50 @@ Nos dois últimos, `isSandbox()` diz em qual ambiente você está:
> **Nunca** versione credenciais. Os arquivos `examples/*/credentials.php` são
> ignorados pelo git por padrão.

### Cliente

O mesmo campo tem **seis grafias** entre os gateways: `cpfCnpj` no Asaas,
`tax_id` no PagBank, `document` no Pagar.me, `taxId` no AbacatePay, `taxID` no
Woovi, `cpf_cnpj` no Efí. Código escrito para um não migra para outro, e nada
no tipo avisa.

`Customer` é a forma única. Cada gateway mapeia para o formato dele:

```php
use PHPay\Support\Customer;

$cliente = Customer::make(
name: 'Mário Lucas',
document: '123.456.789-01', // pontuação é limpa
email: 'fale@phpay.io',
phone: '(11) 94002-8922',
);

$phpay->charge()->setCustomer($cliente); // funciona nos nove
```

Ele também carrega o que cada gateway deriva do cliente, e que antes ficava
espalhado:

```php
$cliente->isIndividual(); // CPF: o Pagar.me precisa como type: 'individual'
$cliente->documentType(); // 'CPF' | 'CNPJ': a Cielo quer em IdentityType
$cliente->firstName(); // o Mercado Pago quer nome e sobrenome separados
$cliente->phoneParts(); // ['country' => '55', 'area' => '11', ...] para o PagBank
```

Para um cliente que já existe no gateway, ou para campos que só aquele gateway
tem:

```php
$cliente->withId('cus_000006337812'); // reaproveita em vez de criar
$cliente->withExtra(['externalReference' => 'x']); // vai junto no payload
```

> O `withExtra()` existe para o que é específico de um gateway — endereço,
> data de nascimento, referência externa. **Nenhum campo obrigatório de
> nenhum dos nove gateways precisa dele**: o value object cobre todos.

### Unidade monetária

Os gateways discordam sobre a unidade, e **errar não quebra a integração — ela
Expand Down
4 changes: 3 additions & 1 deletion src/Contracts/SupportsCustomers.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace PHPay\Contracts;

use PHPay\Support\Customer as CustomerData;

/**
* the gateway exposes customers as a resource of their own.
*/
Expand All @@ -13,5 +15,5 @@ interface SupportsCustomers extends GatewayInterface
* @param array<mixed> $customer
* @return object
*/
public function customer(array $customer = []): object;
public function customer(CustomerData|array $customer = []): object;
}
8 changes: 7 additions & 1 deletion src/Gateways/AbacatePay/AbacatePayGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

use GuzzleHttp\Client;
use PHPay\AbacatePay\Interface\AbacatePayGatewayInterface;
use PHPay\AbacatePay\Requests\AbacatePayCustomerRequest;
use PHPay\AbacatePay\Resources\Charge\Charge;
use PHPay\AbacatePay\Resources\Coupon\Coupon;
use PHPay\AbacatePay\Resources\Customer\Customer;
use PHPay\Support\Customer as CustomerData;

class AbacatePayGateway implements AbacatePayGatewayInterface
{
Expand Down Expand Up @@ -43,8 +45,12 @@ public function name(): string
* @param array<mixed> $customer
* @return Customer
*/
public function customer(array $customer = []): Customer
public function customer(CustomerData|array $customer = []): Customer
{
if ($customer instanceof CustomerData) {
$customer = AbacatePayCustomerRequest::fromCustomer($customer);
}

return new Customer($this->token, $customer, $this->client);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PHPay\AbacatePay\Resources\Coupon\Coupon;
use PHPay\AbacatePay\Resources\Customer\Customer;
use PHPay\Contracts\{SupportsCharges, SupportsCustomers};
use PHPay\Support\Customer as CustomerData;

/**
* the AbacatePay gateway offers customers and charges.
Expand All @@ -28,7 +29,7 @@ interface AbacatePayGatewayInterface extends
* @param array<mixed> $customer
* @return Customer
*/
public function customer(array $customer = []): Customer;
public function customer(CustomerData|array $customer = []): Customer;

/**
* get resource charge from gateway.
Expand Down
18 changes: 18 additions & 0 deletions src/Gateways/AbacatePay/Requests/AbacatePayCustomerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPay\AbacatePay\Requests;

use PHPay\Exceptions\ValidationException;
use PHPay\Support\Customer;

class AbacatePayCustomerRequest
{
Expand Down Expand Up @@ -57,4 +58,21 @@ public static function messages(): object
'taxId' => 'O campo taxId é obrigatório — é o CPF ou CNPJ do cliente.',
];
}

/**
* map the library's Customer onto the payload AbacatePay expects.
*
* @param Customer $customer
* @return array<mixed>
*/
public static function fromCustomer(Customer $customer): array
{
return array_filter([
'name' => $customer->name,
'email' => $customer->email,
'cellphone' => $customer->phone,
'taxId' => $customer->document,
], fn ($value) => $value !== null) + $customer->extra();
}

}
14 changes: 11 additions & 3 deletions src/Gateways/AbacatePay/Resources/Charge/Charge.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

use GuzzleHttp\Client;
use PHPay\AbacatePay\Enums\{BillingFrequencyEnum, BillingMethodEnum};
use PHPay\AbacatePay\Requests\AbacatePayBillingRequest;
use PHPay\AbacatePay\Requests\{AbacatePayBillingRequest, AbacatePayCustomerRequest};
use PHPay\AbacatePay\Resources\Charge\Interface\ChargeInterface;
use PHPay\AbacatePay\Traits\HasAbacatePayClient;
use PHPay\Exceptions\{ApiException, ValidationException};
use PHPay\Support\Money;
use PHPay\Support\{Customer as CustomerData, Money};

/**
* billings of the AbacatePay API.
Expand Down Expand Up @@ -86,8 +86,16 @@ public function setCustomerId(string $customerId): ChargeInterface
* @param array<mixed> $customer
* @return ChargeInterface
*/
public function setCustomer(array $customer): ChargeInterface
public function setCustomer(CustomerData|array $customer): ChargeInterface
{
if ($customer instanceof CustomerData) {
if ($customer->id !== null) {
return $this->setCustomerId($customer->id);
}

$customer = AbacatePayCustomerRequest::fromCustomer($customer);
}

if (isset($customer['id']) && is_string($customer['id']) && $customer['id'] !== '') {
return $this->setCustomerId($customer['id']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace PHPay\AbacatePay\Resources\Charge\Interface;

use PHPay\Support\Money;
use PHPay\Support\{Customer as CustomerData, Money};

interface ChargeInterface
{
Expand All @@ -28,7 +28,7 @@ public function setCustomerId(string $customerId): ChargeInterface;
* @param array<mixed> $customer
* @return ChargeInterface
*/
public function setCustomer(array $customer): ChargeInterface;
public function setCustomer(CustomerData|array $customer): ChargeInterface;

/**
* set the products being charged
Expand Down
8 changes: 7 additions & 1 deletion src/Gateways/Asaas/AsaasGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

use GuzzleHttp\Client;
use PHPay\Asaas\Interface\AsaasGatewayInterface;
use PHPay\Asaas\Requests\AsaasCustomerRequest;
use PHPay\Asaas\Resources\Charge\Charge;
use PHPay\Asaas\Resources\Customer\Customer;
use PHPay\Asaas\Resources\Pix\Pix;
use PHPay\Asaas\Resources\Subscription\Subscription;
use PHPay\Asaas\Resources\Webhook\Webhook;
use PHPay\Support\Customer as CustomerData;

class AsaasGateway implements AsaasGatewayInterface
{
Expand Down Expand Up @@ -42,8 +44,12 @@ public function name(): string
* @param array<mixed> $customer
* @return Customer
*/
public function customer(array $customer = []): Customer
public function customer(CustomerData|array $customer = []): Customer
{
if ($customer instanceof CustomerData) {
$customer = AsaasCustomerRequest::fromCustomer($customer);
}

return new Customer($this->token, $customer, $this->sandbox, $this->client);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Gateways/Asaas/Interface/AsaasGatewayInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PHPay\Asaas\Resources\Subscription\Subscription;
use PHPay\Asaas\Resources\Webhook\Webhook;
use PHPay\Contracts\{SupportsCharges, SupportsCustomers, SupportsPixKeys, SupportsSubscriptions, SupportsWebhooks};
use PHPay\Support\Customer as CustomerData;

/**
* the Asaas gateway offers every capability the library models.
Expand All @@ -25,7 +26,7 @@ interface AsaasGatewayInterface extends
* @param array<mixed> $customer
* @return Customer
*/
public function customer(array $customer = []): Customer;
public function customer(CustomerData|array $customer = []): Customer;

/**
* get resource charge from gateway.
Expand Down
18 changes: 18 additions & 0 deletions src/Gateways/Asaas/Requests/AsaasCustomerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPay\Asaas\Requests;

use PHPay\Exceptions\ValidationException;
use PHPay\Support\Customer;

class AsaasCustomerRequest
{
Expand Down Expand Up @@ -38,4 +39,21 @@ public static function messages(): object
'cpfCnpj' => 'CPF/CNPJ do cliente é obrigatório e deve ser uma string não vazia.',
];
}

/**
* map the library's Customer onto the payload Asaas expects.
*
* @param Customer $customer
* @return array<mixed>
*/
public static function fromCustomer(Customer $customer): array
{
return array_filter([
'name' => $customer->name,
'cpfCnpj' => $customer->document,
'email' => $customer->email,
'mobilePhone' => $customer->phone,
], fn ($value) => $value !== null) + $customer->extra();
}

}
14 changes: 11 additions & 3 deletions src/Gateways/Asaas/Resources/Charge/Charge.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
namespace PHPay\Asaas\Resources\Charge;

use GuzzleHttp\Client;
use PHPay\Asaas\Requests\AsaasChargeRequest;
use PHPay\Asaas\Requests\{AsaasChargeRequest, AsaasCustomerRequest};
use PHPay\Asaas\Resources\Charge\Interface\ChargeInterface;
use PHPay\Asaas\Resources\Customer\Customer;
use PHPay\Asaas\Traits\HasAsaasClient;
use PHPay\Exceptions\{ApiException, ValidationException};
use PHPay\Support\Money;
use PHPay\Support\{Customer as CustomerData, Money};

class Charge implements ChargeInterface
{
Expand Down Expand Up @@ -113,8 +113,16 @@ public function setCustomerId(string $customerId): ChargeInterface
* @return ChargeInterface
* @throws ValidationException|ApiException
*/
public function setCustomer(array $customer): ChargeInterface
public function setCustomer(CustomerData|array $customer): ChargeInterface
{
if ($customer instanceof CustomerData) {
if ($customer->id !== null) {
return $this->setCustomerId($customer->id);
}

$customer = AsaasCustomerRequest::fromCustomer($customer);
}

if (isset($customer['id']) && is_string($customer['id']) && $customer['id'] !== '') {
return $this->setCustomerId($customer['id']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace PHPay\Asaas\Resources\Charge\Interface;

use PHPay\Support\Money;
use PHPay\Support\{Customer as CustomerData, Money};

interface ChargeInterface
{
Expand Down Expand Up @@ -67,7 +67,7 @@ public function setCustomerId(string $customerId): ChargeInterface;
* @param array<mixed> $customer
* @return ChargeInterface
*/
public function setCustomer(array $customer): ChargeInterface;
public function setCustomer(CustomerData|array $customer): ChargeInterface;

/**
* set the amount of the charge, in reais
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace PHPay\Asaas\Resources\Subscription\Interface;

use PHPay\Support\Customer as CustomerData;

interface SubscriptionInterface
{
/**
Expand All @@ -18,7 +20,7 @@ public function setCustomerId(string $customerId): SubscriptionInterface;
* @param array<mixed> $customer
* @return SubscriptionInterface
*/
public function setCustomer(array $customer): SubscriptionInterface;
public function setCustomer(CustomerData|array $customer): SubscriptionInterface;

/**
* create subscription
Expand Down
12 changes: 11 additions & 1 deletion src/Gateways/Asaas/Resources/Subscription/Subscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
namespace PHPay\Asaas\Resources\Subscription;

use GuzzleHttp\Client;
use PHPay\Asaas\Requests\AsaasCustomerRequest;
use PHPay\Asaas\Resources\Customer\Customer;
use PHPay\Asaas\Resources\Subscription\Interface\SubscriptionInterface;
use PHPay\Asaas\Resources\Subscription\Requests\StoreSubscriptionAsaasRequest;
use PHPay\Asaas\Traits\HasAsaasClient;
use PHPay\Exceptions\{ApiException, ValidationException};
use PHPay\Support\Customer as CustomerData;

class Subscription implements SubscriptionInterface
{
Expand Down Expand Up @@ -65,8 +67,16 @@ public function setCustomerId(string $customerId): SubscriptionInterface
* @return SubscriptionInterface
* @throws ValidationException|ApiException
*/
public function setCustomer(array $customer): SubscriptionInterface
public function setCustomer(CustomerData|array $customer): SubscriptionInterface
{
if ($customer instanceof CustomerData) {
if ($customer->id !== null) {
return $this->setCustomerId($customer->id);
}

$customer = AsaasCustomerRequest::fromCustomer($customer);
}

if (isset($customer['id']) && is_string($customer['id']) && $customer['id'] !== '') {
return $this->setCustomerId($customer['id']);
}
Expand Down
Loading
Loading