mivita/app/Services/CheckoutFunnelTracker.php
2026-04-14 18:07:45 +02:00

128 lines
3.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Models\CheckoutFunnelEvent;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Session;
/**
* Tracks checkout funnel events for internal analytics.
*
* All public methods are fire-and-forget: exceptions are caught and logged
* so that a tracking failure never breaks the checkout flow.
*/
class CheckoutFunnelTracker
{
/**
* Step 1 User opens the checkout page.
*
* @param array<string, mixed> $metadata
*/
public static function visitedCheckout(?int $consultantUserId = null, array $metadata = []): void
{
self::record('checkout_visited', [
'consultant_user_id' => $consultantUserId,
'metadata' => $metadata ?: null,
]);
}
/**
* Step 2 User submits the checkout form successfully (ShoppingOrder created).
*
* @param array<string, mixed> $metadata
*/
public static function submittedForm(
int $shoppingUserId,
int $shoppingOrderId,
?int $consultantUserId,
?string $paymentMethod,
?int $amountCents,
array $metadata = [],
): void {
self::record('form_submitted', [
'shopping_user_id' => $shoppingUserId,
'shopping_order_id' => $shoppingOrderId,
'consultant_user_id' => $consultantUserId,
'payment_method' => $paymentMethod,
'amount_cents' => $amountCents,
'metadata' => $metadata ?: null,
]);
}
/**
* Step 3 User is redirected to PAYONE (ShoppingPayment created).
*
* @param array<string, mixed> $metadata
*/
public static function initiatedPayment(
int $shoppingUserId,
int $shoppingOrderId,
int $shoppingPaymentId,
?int $consultantUserId,
?string $paymentMethod,
?int $amountCents,
array $metadata = [],
): void {
self::record('payment_initiated', [
'shopping_user_id' => $shoppingUserId,
'shopping_order_id' => $shoppingOrderId,
'shopping_payment_id' => $shoppingPaymentId,
'consultant_user_id' => $consultantUserId,
'payment_method' => $paymentMethod,
'amount_cents' => $amountCents,
'metadata' => $metadata ?: null,
]);
}
/**
* Step 4 User returns from PAYONE redirect (success / cancel / error).
*
* @param array<string, mixed> $metadata
*/
public static function returnedFromPayment(
int $shoppingPaymentId,
string $returnStatus,
array $metadata = [],
): void {
self::record('payment_returned', [
'shopping_payment_id' => $shoppingPaymentId,
'return_status' => $returnStatus,
'metadata' => $metadata ?: null,
]);
}
/**
* Step 5 PAYONE IPN callback received (PaymentTransaction created).
*
* @param array<string, mixed> $metadata
*/
public static function confirmedPayment(
int $shoppingPaymentId,
string $txaction,
array $metadata = [],
): void {
self::record('payment_confirmed', [
'shopping_payment_id' => $shoppingPaymentId,
'metadata' => array_merge(['txaction' => $txaction], $metadata) ?: null,
]);
}
/** @param array<string, mixed> $attributes */
private static function record(string $event, array $attributes): void
{
try {
CheckoutFunnelEvent::create(array_merge([
'event' => $event,
'session_id' => Session::getId(),
'domain' => Request::getHost(),
], $attributes));
} catch (\Throwable $e) {
Log::warning('CheckoutFunnelTracker: could not record event', [
'event' => $event,
'error' => $e->getMessage(),
]);
}
}
}