Explorar el Código

refactor: passa etapa de antecipacao para dentro do webhook na confirmacao de pagamento com cartao

Gustavo Mantovani hace 2 horas
padre
commit
57dfb476d7

+ 54 - 11
app/Services/Pagarme/PagarmePaymentService.php

@@ -32,10 +32,6 @@ class PagarmePaymentService
     use FormatsPagarmeData;
     use SendsPagarmeRequests;
 
-    public function __construct(
-        private readonly PagarmeAnticipationService $anticipationService,
-    ) {}
-
     public function calculatePaymentAmounts(float $serviceAmount, string $paymentMethod): array
     {
         if ($serviceAmount <= 0) {
@@ -115,10 +111,6 @@ class PagarmePaymentService
                 return $result;
             }
 
-            $anticipationLimits = $this->anticipationService->fetchAnticipationLimitsForPayment($payment);
-
-            $this->anticipationService->createBulkAnticipation($payment, $anticipationLimits);
-
             return $result;
         }
 
@@ -246,6 +238,10 @@ class PagarmePaymentService
 
         $gatewayFee = $gatewayFeeCents > 0 ? round($gatewayFeeCents / 100, 2) : 0;
 
+        $gatewayPayload = $newStatus === PaymentStatusEnum::FAILED
+            ? $this->normalizeFailedGatewayPayload($orderResponse, $failureCode, $failureMessage)
+            : $orderResponse;
+
         $payment->forceFill([
             'gateway_provider'            => 'pagarme',
             'gateway_entity_reference'    => $order->gatewayEntityReference(),
@@ -255,7 +251,7 @@ class PagarmePaymentService
             'status'                      => $newStatus,
             'paid_at'                     => $order->paidAt(),
             'authorized_at'               => $order->authorizedAt(),
-            'gateway_payload'             => $orderResponse,
+            'gateway_payload'             => $gatewayPayload,
             'gateway_fee_amount'          => $gatewayFee,
             'failure_code'                => $failureCode,
             'failure_message'             => $failureMessage,
@@ -377,6 +373,7 @@ class PagarmePaymentService
         $order       = OrderResponseData::fromArray($orderResponse);
         $charge      = $order->firstCharge();
         $transaction = $order->lastTransaction();
+        $failureCode = $order->failureCode();
 
         Log::channel('pagarme')->info('Pagar.me credit card order result', [
             'source'             => $source,
@@ -388,13 +385,59 @@ class PagarmePaymentService
             'charge_status'      => $charge?->status,
             'transaction_id'     => $transaction?->id,
             'transaction_status' => $transaction?->status,
-            'failure_code'       => $order->failureCode(),
+            'failure_code'       => $failureCode,
             'failure_message'    => $order->failureMessage(),
             'acquirer_message'   => $transaction?->acquirerMessage,
-            'gateway_response'   => $transaction?->gatewayResponse,
+
+            'gateway_response' => $this->normalizeGatewayResponseForFailure(
+                $transaction?->gatewayResponse ?? [],
+                $failureCode,
+            ),
         ]);
     }
 
+    private function normalizeFailedGatewayPayload(array $payload, ?string $failureCode, ?string $failureMessage): array
+    {
+        $payload['failure_code']    = $failureCode;
+        $payload['failure_message'] = $failureMessage;
+
+        if (isset($payload['charges'][0]['last_transaction']['gateway_response'])
+            && is_array($payload['charges'][0]['last_transaction']['gateway_response'])) {
+            $payload['charges'][0]['last_transaction']['gateway_response'] = $this->normalizeGatewayResponseForFailure(
+                $payload['charges'][0]['last_transaction']['gateway_response'],
+                $failureCode,
+            );
+        }
+
+        return $payload;
+    }
+
+    private function normalizeGatewayResponseForFailure(array $gatewayResponse, ?string $failureCode): array
+    {
+        $code = $gatewayResponse['code'] ?? null;
+
+        if (! $failureCode || ! $this->isMisleadingGatewayCode($code)) {
+            return $gatewayResponse;
+        }
+
+        $gatewayResponse['raw_code'] = $code;
+        $gatewayResponse['code']     = $failureCode;
+
+        return $gatewayResponse;
+    }
+
+    private function isMisleadingGatewayCode(mixed $code): bool
+    {
+        if ($code === null || $code === '') {
+            return false;
+        }
+
+        $code = mb_strtolower((string) $code);
+
+        return preg_match('/^[1-2]\d{2}$/', $code) === 1
+            || in_array($code, ['00', '0', 'approved', 'success'], true);
+    }
+
     private function buildPhonePayload(?string $phone): ?array
     {
         $digits = $this->digits($phone);

+ 2 - 11
app/Services/ProviderWithdrawalService.php

@@ -234,7 +234,7 @@ class ProviderWithdrawalService
             ->when(
                 ! app()->environment('local', 'development'),
                 fn ($query) => $query->whereRaw(
-                    $this->scheduleEndedAtExpression().' <= ?',
+                    '(date + end_time) <= ?',
                     [$this->withdrawalReleaseCutoff()]
                 )
             );
@@ -247,21 +247,12 @@ class ProviderWithdrawalService
             ->where(function ($q) {
                 $q->where('code_verified', false)
                     ->orWhereRaw(
-                        $this->scheduleEndedAtExpression().' > ?',
+                        '(date + end_time) > ?',
                         [$this->withdrawalReleaseCutoff()]
                     );
             });
     }
 
-    private function scheduleEndedAtExpression(): string
-    {
-        return match (DB::connection()->getDriverName()) {
-            'pgsql'  => '(date + end_time)',
-            'sqlite' => "datetime(date || ' ' || end_time)",
-            default  => 'TIMESTAMP(date, end_time)',
-        };
-    }
-
     private function withdrawalReleaseCutoff(): string
     {
         return Carbon::now()->subDays(5)->format('Y-m-d H:i:s');

+ 36 - 14
app/Services/WebhookService.php

@@ -2,8 +2,10 @@
 
 namespace App\Services;
 
+use App\Enums\PaymentStatusEnum;
 use App\Models\Payment;
 use App\Models\Webhook;
+use App\Services\Pagarme\PagarmeAnticipationService;
 use App\Services\Pagarme\PagarmePaymentService;
 use Illuminate\Support\Facades\Log;
 
@@ -30,16 +32,19 @@ class WebhookService
     ];
 
     public function __construct(
-        private readonly PagarmePaymentService $pagarmePaymentService,
-        private readonly PaymentService $paymentService,
-        private readonly ProviderWithdrawalService $providerWithdrawalService,
-    ) {}
+        private readonly PagarmePaymentService      $pagarmePaymentService,
+        private readonly PagarmeAnticipationService $pagarmeAnticipationService,
+        private readonly PaymentService             $paymentService,
+        private readonly ProviderWithdrawalService  $providerWithdrawalService,
+    ) {
+    }
 
     public function handlePagarme(array $payload): ?Payment
     {
         $event = $payload['type'] ?? null;
-        $data = $payload['data'] ?? [];
-        $data = is_array($data) ? $data : [];
+        $data  = $payload['data'] ?? [];
+        $data  = is_array($data) ? $data : [];
+
         $webhook = $this->recordWebhook($payload, $event, $data);
 
         Log::channel('pagarme')->info('Pagar.me webhook received', [
@@ -68,6 +73,7 @@ class WebhookService
         }
 
         $orderResponse = $this->normalizeOrderResponse($event, $data);
+
         $payment = $this->findPayment($orderResponse);
 
         if (! $payment) {
@@ -93,6 +99,12 @@ class WebhookService
 
             $this->paymentService->syncScheduleStatusAfterPayment($payment->schedule, $payment);
 
+            if ($this->shouldCreateCreditCardAnticipation($event, $payment)) {
+                $anticipationLimits = $this->pagarmeAnticipationService->fetchAnticipationLimitsForPayment($payment);
+
+                $this->pagarmeAnticipationService->createBulkAnticipation($payment, $anticipationLimits);
+            }
+
             $webhook->update([
                 'payment_id'   => $payment->id,
                 'status'       => 'processed',
@@ -112,9 +124,12 @@ class WebhookService
         }
     }
 
+    //
+
     private function recordWebhook(array $payload, ?string $event, array $data): Webhook
     {
         $references = $this->extractReferences($event, $data);
+
         $hookId = $payload['id'] ?? null;
 
         $attributes = [
@@ -191,14 +206,14 @@ class WebhookService
 
     private function findPayment(array $orderResponse): ?Payment
     {
-        $charge = $orderResponse['charges'][0] ?? [];
-        $transaction = $charge['last_transaction'] ?? [];
-        $metadata = $orderResponse['metadata'] ?? $charge['metadata'] ?? [];
-        $metadataId = filter_var($metadata['payment_id'] ?? null, FILTER_VALIDATE_INT) ?: null;
-        $chargeId = $charge['id'] ?? null;
+        $charge        = $orderResponse['charges'][0] ?? [];
+        $transaction   = $charge['last_transaction'] ?? [];
+        $metadata      = $orderResponse['metadata'] ?? $charge['metadata'] ?? [];
+        $metadataId    = filter_var($metadata['payment_id'] ?? null, FILTER_VALIDATE_INT) ?: null;
+        $chargeId      = $charge['id'] ?? null;
         $transactionId = $transaction['id'] ?? null;
-        $orderId = $orderResponse['id'] ?? null;
-        $references = array_filter([$metadataId, $chargeId, $transactionId, $orderId]);
+        $orderId       = $orderResponse['id'] ?? null;
+        $references    = array_filter([$metadataId, $chargeId, $transactionId, $orderId]);
 
         if (empty($references)) {
             return null;
@@ -219,4 +234,11 @@ class WebhookService
             ->latest('id')
             ->first();
     }
-}
+
+    private function shouldCreateCreditCardAnticipation(?string $event, Payment $payment): bool
+    {
+        return $event === 'charge.paid'
+            && $payment->payment_method === 'credit_card'
+            && $payment->status === PaymentStatusEnum::PAID;
+    }
+}