PagarmeRecipientService.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <?php
  2. namespace App\Services\Pagarme;
  3. use App\Models\Provider;
  4. use Illuminate\Support\Facades\Http;
  5. use Illuminate\Support\Facades\Log;
  6. class PagarmeRecipientService
  7. {
  8. public function createRecipientForProvider(Provider $provider, array $data): string
  9. {
  10. if (! empty($provider->recipient_id)) {
  11. return $provider->recipient_id;
  12. }
  13. $bankAccountData = $data['recipient_default_bank_account'];
  14. $metadata = $data['recipient_metadata'] ?? [];
  15. $paymentMode = $data['recipient_payment_mode'];
  16. $recipientType = $data['recipient_type'] ?? 'individual';
  17. $addressParts = $this->extractAddressParts($data);
  18. $monthlyIncome = isset($data['monthly_income']) ? (int) $data['monthly_income'] : 1000;
  19. $occupation = $data['professional_occupation'] ?? 'autonomo';
  20. $payload = $this->filterFilledRecursive([
  21. 'code' => preg_replace('/\D+/', '', $data['recipient_code']),
  22. 'register_information' => [
  23. 'name' => $data['recipient_name'],
  24. 'email' => $data['recipient_email'],
  25. 'document' => preg_replace('/\D+/', '', $data['recipient_document']),
  26. 'type' => $recipientType,
  27. 'birthdate' => $data['birth_date'] ?? null,
  28. 'monthly_income' => $monthlyIncome,
  29. 'professional_occupation' => $occupation,
  30. 'phone_numbers' => $this->buildPhoneNumbers($data['phone'] ?? null),
  31. 'address' => [
  32. 'street' => $data['address'],
  33. 'complementary' => $addressParts['complementary'],
  34. 'street_number' => $addressParts['street_number'],
  35. 'neighborhood' => $addressParts['neighborhood'],
  36. 'city' => $data['city'] ?? null,
  37. 'state' => $data['state'] ?? null,
  38. 'zip_code' => preg_replace('/\D+/', '', $data['zip_code']),
  39. 'reference_point' => $addressParts['reference_point'],
  40. ],
  41. ],
  42. 'default_bank_account' => [
  43. 'holder_name' => $bankAccountData['holder_name'],
  44. 'holder_type' => $bankAccountData['holder_type'],
  45. 'holder_document' => preg_replace('/\D+/', '', $bankAccountData['holder_document']),
  46. 'bank' => $bankAccountData['bank'],
  47. 'branch_number' => $bankAccountData['branch_number'],
  48. 'branch_check_digit' => $bankAccountData['branch_check_digit'] ?? null,
  49. 'account_number' => $bankAccountData['account_number'],
  50. 'account_check_digit' => $bankAccountData['account_check_digit'],
  51. 'type' => $bankAccountData['type'],
  52. ],
  53. 'transfer_settings' => [
  54. 'transfer_enabled' => false,
  55. 'transfer_interval' => 'Daily',
  56. 'transfer_day' => 0,
  57. ],
  58. 'automatic_anticipation_settings' => [
  59. 'enabled' => false,
  60. ],
  61. ]);
  62. $response = $this->pagarmeRequest($provider->id)
  63. ->post($this->pagarmeUrl('/recipients'), $payload);
  64. if ($response->failed()) {
  65. Log::channel('pagarme')->error('Pagar.me recipient creation failed', [
  66. 'status' => $response->status(),
  67. 'body' => $response->json() ?? $response->body(),
  68. 'payload' => $payload,
  69. ]);
  70. throw new \RuntimeException('Erro ao criar recebedor no Pagar.me.');
  71. }
  72. $recipientData = $response->json();
  73. $recipientId = $recipientData['id'] ?? null;
  74. if (! $recipientId) {
  75. Log::channel('pagarme')->error('Pagar.me recipient creation returned empty id', [
  76. 'response' => $recipientData,
  77. ]);
  78. throw new \RuntimeException('Pagar.me recipient creation returned an empty id.');
  79. }
  80. $provider->forceFill([
  81. 'recipient_id' => $recipientId,
  82. 'recipient_name' => $data['recipient_name'],
  83. 'recipient_email' => $data['recipient_email'],
  84. 'recipient_description' => $data['recipient_description'],
  85. 'recipient_document' => $data['recipient_document'],
  86. 'recipient_type' => $recipientType,
  87. 'recipient_code' => $data['recipient_code'],
  88. 'recipient_payment_mode' => $paymentMode,
  89. 'recipient_default_bank_account' => $bankAccountData,
  90. 'recipient_transfer_settings' => [
  91. 'transfer_enabled' => false,
  92. 'transfer_interval' => 'daily',
  93. 'transfer_day' => 0,
  94. ],
  95. 'recipient_automatic_anticipation_settings' => [
  96. 'enabled' => false,
  97. ],
  98. 'recipient_metadata' => $metadata,
  99. ])->save();
  100. $this->applyAutomaticAnticipationSettings($provider->id, $recipientId);
  101. return $recipientId;
  102. }
  103. //
  104. private function pagarmeUrl(string $path): string
  105. {
  106. return rtrim(config('services.pagarme.base_url'), '/').'/'.ltrim($path, '/');
  107. }
  108. private function idempotencyKey(int $providerId, string $suffix = 'recipient'): string
  109. {
  110. return "provider-{$providerId}-{$suffix}";
  111. }
  112. //
  113. private function pagarmeRequest(int $providerId, string $suffix = 'recipient')
  114. {
  115. $secretKey = config('services.pagarme.secret_key');
  116. if (empty($secretKey)) {
  117. Log::channel('pagarme')->error('PAGARME_SECRET_KEY is not configured.');
  118. throw new \RuntimeException('PAGARME_SECRET_KEY is not configured.');
  119. }
  120. return Http::withBasicAuth($secretKey, '')
  121. ->withHeaders([
  122. 'Idempotency-Key' => $this->idempotencyKey($providerId, $suffix),
  123. 'Content-Type' => 'application/json',
  124. 'Accept' => 'application/json',
  125. ]);
  126. }
  127. private function applyAutomaticAnticipationSettings(int $providerId, string $recipientId): void
  128. {
  129. $payload = [
  130. 'enabled' => false,
  131. ];
  132. $response = $this->pagarmeRequest($providerId, 'auto-anticipation')
  133. ->patch($this->pagarmeUrl("/recipients/{$recipientId}/automatic-anticipation-settings"), $payload);
  134. if ($response->failed()) {
  135. Log::channel('pagarme')->error('Pagar.me automatic anticipation settings update failed', [
  136. 'status' => $response->status(),
  137. 'body' => $response->json() ?? $response->body(),
  138. 'payload' => $payload,
  139. ]);
  140. throw new \RuntimeException('Erro ao atualizar antecipação automática do recebedor no Pagar.me.');
  141. }
  142. }
  143. private function extractAddressParts(array $data): array
  144. {
  145. $addressLine = trim((string) ($data['address'] ?? ''));
  146. $segments = array_map('trim', explode(',', $addressLine));
  147. $streetSegment = $segments[0] ?? '';
  148. $streetNumber = $data['number'] ?? null;
  149. $neighborhood = $data['district'] ?? null;
  150. $referencePoint = $data['reference_point'] ?? null;
  151. $complementary = $data['complement'] ?? null;
  152. if ($streetNumber === null) {
  153. preg_match('/^(\d+)/', $streetSegment, $matches);
  154. $streetNumber = $matches[1] ?? 'S/N';
  155. }
  156. if ($neighborhood === null) {
  157. $neighborhood = $segments[1] ?? 'N/A';
  158. }
  159. if ($referencePoint === null) {
  160. $referencePoint = 'N/A';
  161. }
  162. if ($complementary === null) {
  163. $complementary = 'N/A';
  164. }
  165. return [
  166. 'street_number' => (string) $streetNumber,
  167. 'neighborhood' => (string) $neighborhood,
  168. 'reference_point' => (string) $referencePoint,
  169. 'complementary' => (string) $complementary,
  170. ];
  171. }
  172. private function buildPhoneNumbers(?string $phone): array
  173. {
  174. $digits = preg_replace('/\D+/', '', (string) $phone) ?? '';
  175. if (strlen($digits) < 10) {
  176. return [[
  177. 'ddd' => '11',
  178. 'number' => '999999999',
  179. 'type' => 'mobile',
  180. ]];
  181. }
  182. if (str_starts_with($digits, '55')) {
  183. $digits = substr($digits, 2);
  184. }
  185. $areaCode = substr($digits, 0, 2);
  186. $number = substr($digits, 2);
  187. return [[
  188. 'ddd' => $areaCode,
  189. 'number' => $number,
  190. 'type' => 'mobile',
  191. ]];
  192. }
  193. /**
  194. * @param array<string, mixed> $data
  195. * @return array<string, mixed>
  196. */
  197. private function filterFilledRecursive(array $data): array
  198. {
  199. $filtered = [];
  200. foreach ($data as $key => $value) {
  201. if (is_array($value)) {
  202. $value = $this->filterFilledRecursive($value);
  203. }
  204. if ($value !== null && $value !== '' && $value !== []) {
  205. $filtered[$key] = $value;
  206. }
  207. }
  208. return $filtered;
  209. }
  210. }