$items Lista de itens do pedido * @param array $payments Lista de formas de pagamento * @param array $metadata Metadados do pedido (ex: payment_id, schedule_id, client_id, provider_id) * @param array|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(); } }