| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <?php
- namespace App\Data\Pagarme\Request\PagarmeOrderRequestData;
- use App\Data\Pagarme\PagarmeData;
- use App\Models\PaymentSplit;
- use Illuminate\Support\Collection;
- /**
- * @param array<int, array{code?: string, amount?: int, quantity?: int, description?: string, ...}> $items Lista de itens do pedido
- * @param array<int, array{payment_method: string, ...}> $payments Lista de formas de pagamento
- * @param array<string, mixed> $metadata Metadados do pedido (ex: payment_id, schedule_id, client_id, provider_id)
- * @param array<string, mixed>|null $customer Dados do cliente (name, email, document, type, etc.)
- */
- final readonly class PagarmeOrderRequestData extends PagarmeData
- {
- public function __construct(
- public string $code,
- public array $items,
- public array $payments,
- public array $metadata,
- public ?array $customer = null,
- public ?string $customerId = null,
- public bool $closed = true,
- public ?string $channel = null,
- ) {
- self::requireFilled($this->code, 'code');
- if (empty($this->items)) {
- throw new \InvalidArgumentException('items nao pode estar vazio.');
- }
- if (empty($this->payments)) {
- throw new \InvalidArgumentException('payments nao pode estar vazio.');
- }
- foreach ($this->payments as $index => $payment) {
- if (! is_array($payment) || empty($payment['payment_method'])) {
- throw new \InvalidArgumentException("payments.{$index}.payment_method e obrigatorio.");
- }
- self::requireIn($payment['payment_method'], ['credit_card', 'pix'], "payments.{$index}.payment_method");
- }
- if (! $this->customerId && empty($this->customer)) {
- throw new \InvalidArgumentException('customer ou customer_id e obrigatorio.');
- }
- }
- public static function fromOrderPayload(
- string $code,
- array $items,
- array $customer,
- array $paymentMethod,
- array $metadata,
- mixed $customerId = null,
- bool $closed = true,
- ?string $channel = null,
- ): self {
- if (empty($items)) {
- throw new \InvalidArgumentException('items nao pode estar vazio.');
- }
- if (empty($paymentMethod['payment_method'])) {
- throw new \InvalidArgumentException('payment_method e obrigatorio.');
- }
- if (! in_array($paymentMethod['payment_method'], ['credit_card', 'pix'], true)) {
- throw new \InvalidArgumentException('payment_method deve ser credit_card ou pix.');
- }
- $customerIdPayload = self::filled($customerId) ? (string) $customerId : null;
- $customerPayload = self::filterFilledRecursiveStatic($customer);
- if (! $customerIdPayload && empty($customerPayload)) {
- throw new \InvalidArgumentException('customer ou customer_id e obrigatorio.');
- }
- return new self(
- code: $code,
- items: self::validateItems($items),
- payments: [self::filterFilledRecursiveStatic($paymentMethod)],
- metadata: $metadata,
- customer: ! empty($customerPayload) ? $customerPayload : null,
- customerId: $customerIdPayload,
- closed: $closed,
- channel: $channel,
- );
- }
- //
- public static function amountInCents(float $amount): int
- {
- return (int) round($amount * 100);
- }
- public static function creditCardPaymentMethod(array $creditCard, ?array $split = null): array
- {
- return self::paymentMethodWithOptionalSplit([
- 'payment_method' => 'credit_card',
- 'credit_card' => self::buildCreditCardPayload($creditCard),
- ], $split);
- }
- public static function pixPaymentMethod(array $pix, ?array $split = null): array
- {
- return self::paymentMethodWithOptionalSplit([
- 'payment_method' => 'pix',
- 'pix' => self::buildPixPayload($pix),
- ], $split);
- }
- public static function splitFromTransfers(Collection $transfers): array
- {
- return $transfers
- ->filter(fn (PaymentSplit $split) => ! empty($split->gateway_transfer_target_reference))
- ->map(function (PaymentSplit $split) {
- return [
- 'amount' => self::amountInCents((float) $split->gross_amount),
- 'recipient_id' => $split->gateway_transfer_target_reference,
- 'type' => 'flat',
- 'options' => [
- 'charge_processing_fee' => false,
- 'charge_remainder_fee' => false,
- 'liable' => false,
- ],
- ];
- })
- ->values()
- ->all();
- }
- //
- public function toArray(): array
- {
- return $this->filterFilledRecursive([
- 'code' => $this->code,
- 'items' => $this->items,
- 'payments' => $this->payments,
- 'closed' => $this->closed,
- 'metadata' => $this->metadata,
- 'customer_id' => $this->customerId,
- 'customer' => $this->customer,
- 'channel' => $this->channel,
- ]);
- }
- //
- private static function buildCreditCardPayload(array $creditCard): array
- {
- $payload = [];
- foreach ([
- 'installments',
- 'statement_descriptor',
- 'operation_type',
- 'recurrence_cycle',
- 'metadata',
- 'extended_limit_enabled',
- 'extended_limit_code',
- 'merchant_category_code',
- 'authentication',
- 'auto_recovery',
- 'payload',
- 'payment_type',
- 'funding_source',
- 'initiated_type',
- 'recurrence_model',
- 'channel',
- 'payment_origin',
- ] as $field) {
- if (array_key_exists($field, $creditCard) && self::filled($creditCard[$field])) {
- $payload[$field] = $creditCard[$field];
- }
- }
- $allowedCardOptions = ['card', 'card_id', 'card_token', 'network_token'];
- $provided = array_values(array_filter(
- $allowedCardOptions,
- static fn (string $field) => ! empty($creditCard[$field])
- ));
- if (count($provided) !== 1) {
- throw new \InvalidArgumentException('Informe exatamente uma opcao entre card, card_id, card_token ou network_token.');
- }
- $selected = $provided[0];
- $payload[$selected] = $creditCard[$selected];
- return $payload;
- }
- private static function buildPixPayload(array $pix): array
- {
- if (! self::filled($pix['expires_in'] ?? null) && ! self::filled($pix['expires_at'] ?? null)) {
- throw new \InvalidArgumentException('pix.expires_in ou pix.expires_at e obrigatorio.');
- }
- $payload = [];
- foreach (['expires_in', 'expires_at', 'additional_information'] as $field) {
- if (array_key_exists($field, $pix) && self::filled($pix[$field])) {
- $payload[$field] = $pix[$field];
- }
- }
- return $payload;
- }
- //
- private static function filled(mixed $value): bool
- {
- return $value !== null && $value !== '' && $value !== [];
- }
- private static function filterFilledRecursiveStatic(array $data): array
- {
- $filtered = [];
- foreach ($data as $key => $value) {
- if (is_array($value)) {
- $value = self::filterFilledRecursiveStatic($value);
- }
- if (self::filled($value)) {
- $filtered[$key] = $value;
- }
- }
- return $filtered;
- }
- private static function paymentMethodWithOptionalSplit(array $paymentMethod, ?array $split): array
- {
- if (! empty($split)) {
- $paymentMethod['split'] = $split;
- }
- return $paymentMethod;
- }
- private static function validateItems(array $items): array
- {
- return collect($items)
- ->map(function (array $item, int $index) {
- foreach (['code', 'amount', 'quantity'] as $field) {
- if (! array_key_exists($field, $item) || ! self::filled($item[$field])) {
- throw new \InvalidArgumentException("items.{$index}.{$field} e obrigatorio.");
- }
- }
- if ((int) $item['amount'] <= 0 || (int) $item['quantity'] <= 0) {
- throw new \InvalidArgumentException("items.{$index}.amount e quantity devem ser maiores que zero.");
- }
- return self::filterFilledRecursiveStatic($item);
- })
- ->values()
- ->all();
- }
- }
|