recordWebhook($payload, $event, $data); Log::channel('pagarme')->info('Pagar.me webhook received', [ 'hook_id' => $payload['id'] ?? null, 'event' => $event, ]); if (! in_array($event, self::PAYMENT_EVENTS, true)) { $webhook->update([ 'status' => 'ignored', 'processed_at' => now(), ]); return null; } $orderResponse = $this->normalizeOrderResponse($event, $data); $payment = $this->findPayment($orderResponse); if (! $payment) { $webhook->update([ 'status' => 'payment_not_found', 'processed_at' => now(), ]); Log::channel('pagarme')->warning('Payment not found for Pagar.me webhook', [ 'hook_id' => $payload['id'] ?? null, 'event' => $event, 'order_id' => $orderResponse['id'] ?? null, 'charge_id' => $orderResponse['charges'][0]['id'] ?? null, 'transaction_id' => $orderResponse['charges'][0]['last_transaction']['id'] ?? null, 'metadata' => $orderResponse['metadata'] ?? [], ]); return null; } try { $payment = $this->pagarmePaymentService->applyGatewayResponseToPayment($payment, $orderResponse); $this->paymentService->syncScheduleStatusAfterPayment($payment->schedule, $payment); $webhook->update([ 'payment_id' => $payment->id, 'status' => 'processed', 'processed_at' => now(), ]); return $payment; } catch (\Throwable $e) { $webhook->update([ 'payment_id' => $payment->id, 'status' => 'failed', 'processed_at' => now(), 'error_message' => $e->getMessage(), ]); throw $e; } } private function recordWebhook(array $payload, ?string $event, array $data): Webhook { $references = $this->extractReferences($event, $data); $hookId = $payload['id'] ?? null; $attributes = [ 'hook_id' => $hookId, 'provider' => 'pagarme', 'event' => $event, 'account_id' => $payload['account']['id'] ?? null, 'order_id' => $references['order_id'], 'charge_id' => $references['charge_id'], 'transaction_id' => $references['transaction_id'], 'status' => 'received', 'payload' => $payload, 'received_at' => now(), 'processed_at' => null, 'error_message' => null, ]; if (empty($hookId)) { return Webhook::create($attributes); } $webhook = Webhook::firstOrNew([ 'provider' => 'pagarme', 'hook_id' => $hookId, ]); $webhook->fill([ ...$attributes, 'attempts_count' => $webhook->exists ? $webhook->attempts_count + 1 : 1, ])->save(); return $webhook; } private function extractReferences(?string $event, array $data): array { if (str_starts_with((string) $event, 'order.')) { $charge = $data['charges'][0] ?? []; return [ 'order_id' => $data['id'] ?? null, 'charge_id' => $charge['id'] ?? null, 'transaction_id' => $charge['last_transaction']['id'] ?? null, ]; } if (str_starts_with((string) $event, 'charge.')) { return [ 'order_id' => $data['order']['id'] ?? $data['order_id'] ?? null, 'charge_id' => $data['id'] ?? null, 'transaction_id' => $data['last_transaction']['id'] ?? null, ]; } return [ 'order_id' => null, 'charge_id' => null, 'transaction_id' => null, ]; } private function normalizeOrderResponse(?string $event, array $data): array { if (str_starts_with((string) $event, 'order.')) { return $data; } return [ 'id' => $data['order']['id'] ?? $data['order_id'] ?? null, 'metadata' => $data['metadata'] ?? $data['order']['metadata'] ?? [], 'charges' => [$data], ]; } 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; $transactionId = $transaction['id'] ?? null; $orderId = $orderResponse['id'] ?? null; $references = array_filter([$metadataId, $chargeId, $transactionId, $orderId]); if (empty($references)) { return null; } return Payment::query() ->where('gateway_provider', 'pagarme') ->where(function ($query) use ($metadataId, $chargeId, $transactionId, $orderId) { $query ->when($metadataId, fn ($query) => $query->orWhere('id', $metadataId)) ->when($chargeId, fn ($query) => $query->orWhere('gateway_entity_reference', $chargeId)) ->when($transactionId, fn ($query) => $query->orWhere('gateway_operation_reference', $transactionId)) ->when($orderId, fn ($query) => $query->orWhere(function ($query) use ($orderId) { $query->where('gateway_entity_label', 'order') ->where('gateway_entity_reference', $orderId); })); }) ->latest('id') ->first(); } }