recipient_id)) { return $provider->recipient_id; } $bankAccountData = $data['recipient_default_bank_account']; $bankAccountData['holder_name'] = $this->normalizeBankAccountHolderName($bankAccountData['holder_name']); $metadata = $data['recipient_metadata'] ?? []; $paymentMode = $data['recipient_payment_mode']; $recipientType = $data['recipient_type'] ?? 'individual'; $addressParts = $this->extractAddressParts($data); $monthlyIncome = isset($data['monthly_income']) ? (int) $data['monthly_income'] : 1000; $occupation = $data['professional_occupation'] ?? 'autonomo'; $recipientCode = $this->ensureRecipientCode($provider); $defaultBankAccount = PagarmeRecipientBankAccountData::fromArray($bankAccountData); $payload = new PagarmeRecipientRequestData( code: $recipientCode, registerInformation: new PagarmeRecipientRegisterInformationData( name: $data['recipient_name'], email: $data['recipient_email'], document: preg_replace('/\D+/', '', $data['recipient_document']), type: $recipientType, birthdate: $this->formatBirthdate($data['birth_date'] ?? null), monthlyIncome: $monthlyIncome, professionalOccupation: $occupation, phoneNumbers: new PagarmeRecipientPhoneNumbersData($this->buildPhoneNumber($data['phone'] ?? null)), address: new PagarmeRecipientAddressData( street: $data['address'], complementary: $addressParts['complementary'], streetNumber: $addressParts['street_number'], neighborhood: $addressParts['neighborhood'], city: $data['city'] ?? null, state: $data['state'] ?? null, zipCode: preg_replace('/\D+/', '', $data['zip_code']), referencePoint: $addressParts['reference_point'], ), ), defaultBankAccount: $defaultBankAccount, transferSettings: new PagarmeRecipientTransferSettingsData( transferEnabled: false, transferInterval: 'Daily', transferDay: 0, ), automaticAnticipationSettings: new PagarmeRecipientAutomaticAnticipationSettingsData( enabled: false, ), ); $recipientData = PagarmeRecipientResponseData::fromArray($this->pagarmeRequest( method: 'POST', path: '/recipients', payload: $payload, idempotencyKey: $this->idempotencyKey($provider->id), errorMessage: 'Erro ao criar recebedor no Pagar.me.', )); $recipientId = $recipientData->id(); if (! $recipientId) { Log::channel('pagarme')->error('Pagar.me recipient creation returned empty id', [ 'response' => $recipientData->toArray(), ]); throw new \RuntimeException('Pagar.me recipient creation returned an empty id.'); } $provider->forceFill([ 'recipient_id' => $recipientId, 'recipient_name' => $data['recipient_name'], 'recipient_email' => $data['recipient_email'], 'recipient_description' => $data['recipient_description'], 'recipient_document' => $data['recipient_document'], 'recipient_type' => $recipientType, 'recipient_code' => $recipientCode, 'recipient_payment_mode' => $paymentMode, 'recipient_default_bank_account' => $bankAccountData, 'recipient_transfer_settings' => [ 'transfer_enabled' => false, 'transfer_interval' => 'daily', 'transfer_day' => 0, ], 'recipient_automatic_anticipation_settings' => [ 'enabled' => false, ], 'recipient_metadata' => $metadata, ])->save(); return $recipientId; } public function updateDefaultBankAccount(Provider $provider, array $bankAccountData): Provider { if (empty($provider->recipient_id)) { throw new \InvalidArgumentException('Prestador precisa ter recipient_id do Pagar.me para atualizar a conta bancaria.'); } $bankAccountData = $this->normalizeBankAccountPayload($bankAccountData); $bankAccountData['holder_name'] = $this->normalizeBankAccountHolderName($bankAccountData['holder_name']); $payload = PagarmeBankAccountUpdateRequestData::fromArray($bankAccountData); $recipientData = PagarmeRecipientResponseData::fromArray($this->pagarmeRequest( method: 'PATCH', path: "/recipients/{$provider->recipient_id}/default-bank-account", payload: $payload, idempotencyKey: $this->idempotencyKey($provider->id, 'default-bank-account-'.sha1(json_encode($payload->toArray()))), errorMessage: 'Erro ao atualizar conta bancaria do recebedor no Pagar.me.', )); $provider->forceFill([ 'recipient_default_bank_account' => $recipientData->defaultBankAccount()?->toArray() ?: $bankAccountData, ])->save(); return $provider->fresh(); } // private function idempotencyKey(int $providerId, string $suffix = 'recipient'): string { return "provider-{$providerId}-{$suffix}"; } private function buildPhoneNumber(?string $phone): PagarmeRecipientPhoneData { $digits = preg_replace('/\D+/', '', (string) $phone) ?? ''; if (strlen($digits) < 10) { return new PagarmeRecipientPhoneData( ddd: '11', number: '999999999', type: 'mobile', ); } if (str_starts_with($digits, '55')) { $digits = substr($digits, 2); } $areaCode = substr($digits, 0, 2); $number = substr($digits, 2); return new PagarmeRecipientPhoneData( ddd: $areaCode, number: $number, type: 'mobile', ); } private function ensureRecipientCode(Provider $provider): string { if ($this->hasUuidCode($provider->recipient_code, 'provider')) { return $provider->recipient_code; } $recipientCode = 'provider-'.(string) Str::uuid(); $provider->forceFill(['recipient_code' => $recipientCode])->save(); return $recipientCode; } private function hasUuidCode(?string $code, string $prefix): bool { return is_string($code) && preg_match("/^{$prefix}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i", $code) === 1; } private function extractAddressParts(array $data): array { $addressLine = trim((string) ($data['address'] ?? '')); $segments = array_map('trim', explode(',', $addressLine)); $streetSegment = $segments[0] ?? ''; $streetNumber = $data['number'] ?? null; $neighborhood = $data['district'] ?? null; $referencePoint = $data['reference_point'] ?? null; $complementary = $data['complement'] ?? null; if ($streetNumber === null) { preg_match('/^(\d+)/', $streetSegment, $matches); $streetNumber = $matches[1] ?? 'S/N'; } if ($neighborhood === null) { $neighborhood = $segments[1] ?? 'N/A'; } if ($referencePoint === null) { $referencePoint = 'N/A'; } if ($complementary === null) { $complementary = 'N/A'; } return [ 'street_number' => (string) $streetNumber, 'neighborhood' => (string) $neighborhood, 'reference_point' => (string) $referencePoint, 'complementary' => (string) $complementary, ]; } private function filterFilledRecursive(array $data): array { $filtered = []; foreach ($data as $key => $value) { if (is_array($value)) { $value = $this->filterFilledRecursive($value); } if ($value !== null && $value !== '' && $value !== []) { $filtered[$key] = $value; } } return $filtered; } private function formatBirthdate(mixed $birthdate): ?string { if ($birthdate === null || $birthdate === '') { return null; } if ($birthdate instanceof \DateTimeInterface) { return Carbon::instance($birthdate)->format('d/m/Y'); } $birthdate = trim((string) $birthdate); if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $birthdate) === 1) { return $birthdate; } if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthdate) === 1) { return Carbon::createFromFormat('Y-m-d', $birthdate)->format('d/m/Y'); } return Carbon::parse($birthdate)->format('d/m/Y'); } private function normalizeBankAccountHolderName(string $holderName): string { $holderName = trim(preg_replace('/\s+/', ' ', $holderName) ?? ''); if (Str::length($holderName) < 30) { return $holderName; } $parts = explode(' ', $holderName); if (count($parts) >= 3) { $firstName = array_shift($parts); $lastName = array_pop($parts); $initials = array_map( static fn (string $part): string => Str::upper(Str::substr($part, 0, 1)), $parts ); $abbreviated = trim($firstName.' '.implode(' ', $initials).' '.$lastName); if (Str::length($abbreviated) < 30) { return $abbreviated; } $firstAndLast = trim($firstName.' '.$lastName); if (Str::length($firstAndLast) < 30) { return $firstAndLast; } } return Str::limit($holderName, 29, ''); } private function normalizeBankAccountPayload(array $bankAccountData): array { return [ 'holder_name' => $bankAccountData['holder_name'], 'holder_type' => $bankAccountData['holder_type'], 'holder_document' => preg_replace('/\D+/', '', $bankAccountData['holder_document']), 'bank' => $bankAccountData['bank'], 'branch_number' => $bankAccountData['branch_number'], 'branch_check_digit' => $bankAccountData['branch_check_digit'] ?? null, 'account_number' => $bankAccountData['account_number'], 'account_check_digit' => $bankAccountData['account_check_digit'], 'type' => $bankAccountData['type'], 'metadata' => $bankAccountData['metadata'] ?? null, ]; } }