GA4 Measurement Protocol purchase event not appearing in DebugView when sent from backend using stored _ga client_id

I’m trying to send a purchase event to Google Analytics 4 using the Measurement Protocol from my Laravel backend. I’m not using gtag.js or Firebase — only server-side requests.

Here’s what I’m doing:

  1. On the frontend, I extract the client_id from the _ga cookie using JavaScript and send it to the backend (e.g., via Ajax or form data).
  2. I store that client_id in the database (associated with the user or session).
  3. Later, when the user completes a purchase (which could be minutes or even days after the initial visit), I use the previously saved client_id to send a purchase event via Measurement Protocol.

Here’s a simplified version of the code I use:

public function gtmPaidOrder() {
    $clientId = $this->client_id; // previously saved from frontend _ga cookie

    if (!$clientId) return false;

    $measurement_id = 'G-XXXXXXX';
    $api_secret = MY_SECRET;

    $payload = [
        'client_id' => $clientId,
        'events' => [
            [
                'name'   => 'purchase',
                'params' => [
                    'transaction_id' => $this->id,
                    'value'          => $this->getTotalPrice(),
                    'currency'       => $this->currency,
                    'items'          => [
                        [
                            'item_id' => '123',
                            'item_name' => 'Test Product',
                            'price' => 20,
                            'quantity' => 1
                        ]
                    ],
                    'debug_mode' => true
                ]
            ]
        ]
    ];

    $url = "https://www.google-analytics.com/debug/mp/collect?measurement_id={$measurement_id}&api_secret={$api_secret}";

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    Log::info('GA4 MP response', ['response' => $response]);
}

Google returns a 200 OK response with the following body:

{
  "validationMessages": [ ]
}

All other events sent from the frontend appear in DebugView, but this event does not. Why?