128 lines
3.9 KiB
PHP
128 lines
3.9 KiB
PHP
<?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(),
|
||
]);
|
||
}
|
||
}
|
||
}
|