PagarmeAnticipationService.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. namespace App\Services\Pagarme;
  3. use App\Data\Pagarme\Anticipation\AnticipationLimitData;
  4. use App\Data\Pagarme\Anticipation\BulkAnticipationLimitsResponseData;
  5. use App\Data\Pagarme\Anticipation\BulkAnticipationRequestData;
  6. use App\Data\Pagarme\Anticipation\BulkAnticipationResponseData;
  7. use App\Data\Pagarme\Order\OrderRequestData;
  8. use App\Models\Payment;
  9. use App\Services\Pagarme\Concerns\MocksPagarmeRequests;
  10. use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
  11. use Illuminate\Support\Facades\Http;
  12. use Illuminate\Support\Facades\Log;
  13. use Throwable;
  14. class PagarmeAnticipationService
  15. {
  16. use MocksPagarmeRequests;
  17. use SendsPagarmeRequests;
  18. public function createBulkAnticipation(
  19. Payment $payment,
  20. ?BulkAnticipationLimitsResponseData $limits = null,
  21. ): ?BulkAnticipationResponseData
  22. {
  23. $provider = $payment->provider()->first();
  24. if (! $provider) {
  25. return null;
  26. }
  27. $recipientId = $provider->recipient_id;
  28. if (empty($recipientId)) {
  29. return null;
  30. }
  31. $providerSplit = $payment->splits()
  32. ->where('provider_id', $payment->provider_id)
  33. ->first();
  34. if (! $providerSplit) {
  35. return null;
  36. }
  37. $requestedAmount = OrderRequestData::amountInCents((float) $providerSplit->gross_amount);
  38. if ($requestedAmount <= 0) {
  39. return null;
  40. }
  41. $paymentDate = $this->getAnticipationPaymentDate();
  42. $limits ??= $this->fetchAnticipationLimits($recipientId, $paymentDate);
  43. if ($limits && $limits->maximum->amount <= 0) {
  44. Log::channel('pagarme')->warning('Antecipacao ignorada: recebedor sem valor disponivel para antecipar.', [
  45. 'payment_id' => $payment->id,
  46. 'provider_id' => $payment->provider_id,
  47. 'recipient_id' => $recipientId,
  48. 'requested_amount' => $requestedAmount,
  49. 'minimum_amount' => $limits->minimum->amount,
  50. 'maximum_amount' => $limits->maximum->amount,
  51. ]);
  52. return null;
  53. }
  54. if ($limits && $limits->minimum->amount > 0 && $requestedAmount < $limits->minimum->amount) {
  55. Log::channel('pagarme')->warning('Antecipacao ignorada: valor abaixo do minimo do recebedor.', [
  56. 'payment_id' => $payment->id,
  57. 'provider_id' => $payment->provider_id,
  58. 'recipient_id' => $recipientId,
  59. 'requested_amount' => $requestedAmount,
  60. 'minimum_amount' => $limits->minimum->amount,
  61. 'maximum_amount' => $limits->maximum->amount,
  62. ]);
  63. return null;
  64. }
  65. if ($limits && $limits->maximum->amount > 0 && $requestedAmount > $limits->maximum->amount) {
  66. Log::channel('pagarme')->warning('Antecipacao ignorada: valor acima do maximo disponivel do recebedor.', [
  67. 'payment_id' => $payment->id,
  68. 'provider_id' => $payment->provider_id,
  69. 'recipient_id' => $recipientId,
  70. 'requested_amount' => $requestedAmount,
  71. 'minimum_amount' => $limits->minimum->amount,
  72. 'maximum_amount' => $limits->maximum->amount,
  73. ]);
  74. return null;
  75. }
  76. try {
  77. if ($this->shouldMockPagarmeRequest()) {
  78. return new BulkAnticipationResponseData(
  79. id: $this->mockPagarmeId('ba', "{$payment->id}-{$payment->provider_id}"),
  80. status: 'pending',
  81. amount: $requestedAmount,
  82. fee: 0,
  83. fraudCoverageFee: 0,
  84. anticipationFee: 0,
  85. automaticTransfer: false,
  86. type: 'full',
  87. timeframe: 'start',
  88. paymentDate: $paymentDate,
  89. createdAt: now()->toISOString(),
  90. updatedAt: now()->toISOString(),
  91. anticipationTax: ['mocked' => true],
  92. );
  93. }
  94. $response = BulkAnticipationResponseData::fromArray($this->pagarmeRequest(
  95. method: 'POST',
  96. path: "/recipients/{$recipientId}/bulk_anticipations",
  97. payload: new BulkAnticipationRequestData(
  98. paymentDate: $paymentDate,
  99. timeframe: 'start',
  100. requestedAmount: $requestedAmount,
  101. automaticTransfer: false,
  102. ),
  103. idempotencyKey: "bulk-ant-{$payment->id}-{$payment->provider_id}",
  104. errorMessage: 'Erro ao criar antecipacao no Pagar.me.',
  105. ));
  106. $response->requireId();
  107. return $response;
  108. } catch (Throwable $e) {
  109. Log::channel('pagarme')->warning('Falha ao criar antecipacao no Pagar.me.', [
  110. 'payment_id' => $payment->id,
  111. 'provider_id' => $payment->provider_id,
  112. 'recipient_id' => $recipientId,
  113. 'requested_amount' => $requestedAmount,
  114. 'minimum_amount' => $limits?->minimum->amount,
  115. 'maximum_amount' => $limits?->maximum->amount,
  116. 'error' => $e->getMessage(),
  117. ]);
  118. return null;
  119. }
  120. }
  121. public function fetchAnticipationLimitsForPayment(Payment $payment): ?BulkAnticipationLimitsResponseData
  122. {
  123. $provider = $payment->provider()->first();
  124. if (! $provider || empty($provider->recipient_id)) {
  125. return null;
  126. }
  127. return $this->fetchAnticipationLimits(
  128. recipientId: $provider->recipient_id,
  129. paymentDate: $this->getAnticipationPaymentDate(),
  130. );
  131. }
  132. private function fetchAnticipationLimits(string $recipientId, string $paymentDate): ?BulkAnticipationLimitsResponseData
  133. {
  134. try {
  135. if ($this->shouldMockPagarmeRequest()) {
  136. return new BulkAnticipationLimitsResponseData(
  137. maximum: new AnticipationLimitData(
  138. amount: 100000000,
  139. anticipationFee: 0,
  140. fee: 0,
  141. fraudCoverageFee: 0,
  142. ),
  143. minimum: new AnticipationLimitData(
  144. amount: 1,
  145. anticipationFee: 0,
  146. fee: 0,
  147. fraudCoverageFee: 0,
  148. ),
  149. );
  150. }
  151. $secretKey = config('services.pagarme.secret_key');
  152. if (empty($secretKey)) {
  153. return null;
  154. }
  155. $endpoint = $this->pagarmeUrl("/recipients/{$recipientId}/bulk_anticipations/limits");
  156. $response = Http::withBasicAuth($secretKey, '')
  157. ->withHeaders([
  158. 'Content-Type' => 'application/json',
  159. 'Accept' => 'application/json',
  160. ])
  161. ->get($endpoint, [
  162. 'payment_date' => $paymentDate,
  163. 'timeframe' => 'start',
  164. ])
  165. ->throw();
  166. $body = $response->json() ?? [];
  167. $limits = BulkAnticipationLimitsResponseData::fromArray($body);
  168. Log::channel('pagarme')->info('Limites de antecipacao consultados no Pagar.me.', [
  169. 'recipient_id' => $recipientId,
  170. 'payment_date' => $paymentDate,
  171. 'minimum_amount' => $limits->minimum->amount,
  172. 'maximum_amount' => $limits->maximum->amount,
  173. ]);
  174. return $limits;
  175. } catch (Throwable $e) {
  176. Log::channel('pagarme')->warning('Falha ao consultar limites de antecipacao.', [
  177. 'recipient_id' => $recipientId,
  178. 'payment_date' => $paymentDate,
  179. 'error' => $e->getMessage(),
  180. ]);
  181. return null;
  182. }
  183. }
  184. private function getAnticipationPaymentDate(): string
  185. {
  186. $now = now()->timezone('America/Sao_Paulo');
  187. if ($now->hour < 11) {
  188. return $now->toISOString();
  189. }
  190. $nextBusinessDay = $now->copy()->addDay();
  191. while ($nextBusinessDay->isWeekend()) {
  192. $nextBusinessDay->addDay();
  193. }
  194. return $nextBusinessDay->startOfDay()->toISOString();
  195. }
  196. }