PagarmeRecipientService.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <?php
  2. namespace App\Services\Pagarme;
  3. use App\Data\Pagarme\Request\BankAccountUpdateRequestData;
  4. use App\Data\Pagarme\Request\RecipientRequestData\RecipientAddressData;
  5. use App\Data\Pagarme\Request\RecipientRequestData\RecipientAutomaticAnticipationSettingsData;
  6. use App\Data\Pagarme\Request\RecipientRequestData\RecipientBankAccountData;
  7. use App\Data\Pagarme\Request\RecipientRequestData\RecipientPhoneData;
  8. use App\Data\Pagarme\Request\RecipientRequestData\RecipientPhoneNumbersData;
  9. use App\Data\Pagarme\Request\RecipientRequestData\RecipientRegisterInformationData;
  10. use App\Data\Pagarme\Request\RecipientRequestData\RecipientRequestData;
  11. use App\Data\Pagarme\Request\RecipientRequestData\RecipientTransferSettingsData;
  12. use App\Data\Pagarme\Response\RecipientResponseData\RecipientResponseData;
  13. use App\Models\Provider;
  14. use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
  15. use Carbon\Carbon;
  16. use Illuminate\Support\Str;
  17. class PagarmeRecipientService
  18. {
  19. use SendsPagarmeRequests;
  20. public function createRecipientForProvider(Provider $provider, array $data): string
  21. {
  22. if (! empty($provider->recipient_id)) {
  23. return $provider->recipient_id;
  24. }
  25. $metadata = $data['recipient_metadata'] ?? [];
  26. $paymentMode = $data['recipient_payment_mode'];
  27. $recipientCode = $this->ensureRecipientCode($provider);
  28. $addressParts = $this->extractAddressParts($data);
  29. $registerInformation = new RecipientRegisterInformationData(
  30. name: $data['recipient_name'],
  31. email: $data['recipient_email'],
  32. document: $this->onlyDigits($data['recipient_document'] ?? null),
  33. type: $data['recipient_type'] ?? 'individual',
  34. birthdate: $this->formatBirthdate($data['birth_date'] ?? null),
  35. monthlyIncome: isset($data['monthly_income']) ? (int) $data['monthly_income'] : 1000,
  36. professionalOccupation: $data['professional_occupation'] ?? 'autonomo',
  37. phoneNumbers: new RecipientPhoneNumbersData(
  38. $this->buildRecipientPhone($data['phone'] ?? null),
  39. ),
  40. address: new RecipientAddressData(
  41. street: $data['address'],
  42. complementary: $addressParts['complementary'],
  43. streetNumber: $addressParts['street_number'],
  44. neighborhood: $addressParts['neighborhood'],
  45. city: $data['city'] ?? null,
  46. state: $data['state'] ?? null,
  47. zipCode: $this->onlyDigits($data['zip_code'] ?? null),
  48. referencePoint: $addressParts['reference_point'],
  49. ),
  50. );
  51. $defaultBankAccount = $this->buildRecipientBankAccount(
  52. $data['recipient_default_bank_account'],
  53. );
  54. $payload = new RecipientRequestData(
  55. code: $recipientCode,
  56. registerInformation: $registerInformation,
  57. defaultBankAccount: $defaultBankAccount,
  58. transferSettings: new RecipientTransferSettingsData(
  59. transferEnabled: false,
  60. transferInterval: 'Daily',
  61. transferDay: 0,
  62. ),
  63. automaticAnticipationSettings: new RecipientAutomaticAnticipationSettingsData(
  64. enabled: false,
  65. ),
  66. );
  67. $bankAccountData = $payload->defaultBankAccount->toArray();
  68. $raw = $this->pagarmeRequest(
  69. method: 'POST',
  70. path: '/recipients',
  71. payload: $payload,
  72. idempotencyKey: $this->idempotencyKey($provider->id),
  73. errorMessage: 'Erro ao criar recebedor no Pagar.me.',
  74. );
  75. $recipientData = RecipientResponseData::fromArray($raw);
  76. $recipientId = $recipientData->requireId();
  77. $provider->forceFill([
  78. 'recipient_id' => $recipientId,
  79. 'recipient_name' => $data['recipient_name'],
  80. 'recipient_email' => $data['recipient_email'],
  81. 'recipient_description' => $data['recipient_description'],
  82. 'recipient_document' => $data['recipient_document'],
  83. 'recipient_type' => $payload->registerInformation->type,
  84. 'recipient_code' => $recipientCode,
  85. 'recipient_payment_mode' => $paymentMode,
  86. 'recipient_default_bank_account' => $bankAccountData,
  87. 'recipient_transfer_settings' => [
  88. 'transfer_enabled' => false,
  89. 'transfer_interval' => 'daily',
  90. 'transfer_day' => 0,
  91. ],
  92. 'recipient_automatic_anticipation_settings' => [
  93. 'enabled' => false,
  94. ],
  95. 'recipient_metadata' => $metadata,
  96. ])->save();
  97. return $recipientId;
  98. }
  99. public function updateDefaultBankAccount(Provider $provider, array $bankAccountData): Provider
  100. {
  101. $payload = new BankAccountUpdateRequestData(
  102. holderName: $this->normalizeHolderName($bankAccountData['holder_name']),
  103. holderType: $bankAccountData['holder_type'],
  104. holderDocument: $this->onlyDigits($bankAccountData['holder_document']),
  105. bank: $bankAccountData['bank'],
  106. branchNumber: $bankAccountData['branch_number'],
  107. branchCheckDigit: $bankAccountData['branch_check_digit'] ?? null,
  108. accountNumber: $bankAccountData['account_number'],
  109. accountCheckDigit: $bankAccountData['account_check_digit'],
  110. type: $bankAccountData['type'],
  111. );
  112. $raw = $this->pagarmeRequest(
  113. method: 'PATCH',
  114. path: "/recipients/{$provider->recipient_id}/default-bank-account",
  115. payload: $payload,
  116. idempotencyKey: $this->idempotencyKey($provider->id, 'default-bank-account-'.sha1(json_encode($payload->toArray()))),
  117. errorMessage: 'Erro ao atualizar conta bancaria do recebedor no Pagar.me.',
  118. );
  119. $recipientData = RecipientResponseData::fromArray($raw);
  120. $provider->forceFill([
  121. 'recipient_default_bank_account' => $recipientData->defaultBankAccount?->toArray() ?: $payload->toArray()['bank_account'],
  122. ])->save();
  123. return $provider->fresh();
  124. }
  125. //
  126. private function buildRecipientBankAccount(array $data): RecipientBankAccountData
  127. {
  128. return new RecipientBankAccountData(
  129. holderName: $this->normalizeHolderName($data['holder_name']),
  130. holderType: $data['holder_type'],
  131. holderDocument: $this->onlyDigits($data['holder_document']),
  132. bank: $data['bank'],
  133. branchNumber: $data['branch_number'],
  134. branchCheckDigit: $data['branch_check_digit'] ?? null,
  135. accountNumber: $data['account_number'],
  136. accountCheckDigit: $data['account_check_digit'],
  137. type: $data['type'],
  138. );
  139. }
  140. private function buildRecipientPhone(?string $phone): RecipientPhoneData
  141. {
  142. $digits = $this->onlyDigits($phone);
  143. if (strlen($digits) < 10) {
  144. return new RecipientPhoneData(
  145. ddd: '11',
  146. number: '999999999',
  147. type: 'mobile',
  148. );
  149. }
  150. if (str_starts_with($digits, '55')) {
  151. $digits = substr($digits, 2);
  152. }
  153. return new RecipientPhoneData(
  154. ddd: substr($digits, 0, 2),
  155. number: substr($digits, 2),
  156. type: 'mobile',
  157. );
  158. }
  159. private function extractAddressParts(array $data): array
  160. {
  161. $addressLine = trim((string) ($data['address'] ?? ''));
  162. $segments = array_map('trim', explode(',', $addressLine));
  163. $streetSegment = $segments[0] ?? '';
  164. if (($data['number'] ?? null) === null) {
  165. preg_match('/^(\d+)/', $streetSegment, $matches);
  166. }
  167. return [
  168. 'street_number' => (string) ($data['number'] ?? $matches[1] ?? 'S/N'),
  169. 'neighborhood' => (string) ($data['district'] ?? $segments[1] ?? 'N/A'),
  170. 'reference_point' => (string) ($data['reference_point'] ?? 'N/A'),
  171. 'complementary' => (string) ($data['complement'] ?? 'N/A'),
  172. ];
  173. }
  174. private function formatBirthdate(mixed $birthdate): ?string
  175. {
  176. if ($birthdate === null || $birthdate === '') {
  177. return null;
  178. }
  179. if ($birthdate instanceof \DateTimeInterface) {
  180. return Carbon::instance($birthdate)->format('d/m/Y');
  181. }
  182. $birthdate = trim((string) $birthdate);
  183. if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $birthdate) === 1) {
  184. return $birthdate;
  185. }
  186. if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthdate) === 1) {
  187. return Carbon::createFromFormat('Y-m-d', $birthdate)->format('d/m/Y');
  188. }
  189. return Carbon::parse($birthdate)->format('d/m/Y');
  190. }
  191. private function normalizeHolderName(string $holderName): string
  192. {
  193. $holderName = trim(preg_replace('/\s+/', ' ', $holderName) ?? '');
  194. if (Str::length($holderName) < 30) {
  195. return $holderName;
  196. }
  197. $parts = explode(' ', $holderName);
  198. if (count($parts) >= 3) {
  199. $firstName = array_shift($parts);
  200. $lastName = array_pop($parts);
  201. $initials = array_map(
  202. static fn (string $part): string => Str::upper(Str::substr($part, 0, 1)),
  203. $parts
  204. );
  205. $abbreviated = trim($firstName.' '.implode(' ', $initials).' '.$lastName);
  206. if (Str::length($abbreviated) < 30) {
  207. return $abbreviated;
  208. }
  209. $firstAndLast = trim($firstName.' '.$lastName);
  210. if (Str::length($firstAndLast) < 30) {
  211. return $firstAndLast;
  212. }
  213. }
  214. return Str::limit($holderName, 29, '');
  215. }
  216. private function onlyDigits(?string $value): string
  217. {
  218. return preg_replace('/\D+/', '', (string) $value) ?? '';
  219. }
  220. // evita criacao duplica de recipient
  221. private function idempotencyKey(int $providerId, string $suffix = 'recipient'): string
  222. {
  223. return "provider-{$providerId}-{$suffix}";
  224. }
  225. private function ensureRecipientCode(Provider $provider): string
  226. {
  227. if (! empty($provider->recipient_code)) {
  228. return $provider->recipient_code;
  229. }
  230. $recipientCode = 'provider-'.(string) Str::uuid();
  231. $provider->forceFill(['recipient_code' => $recipientCode])->save();
  232. return $recipientCode;
  233. }
  234. }