Need Help! Snippet to POST request to API

Good morning
for the past few days I’ve been working non stop on trying to implement the serverside Facebook Conversion API to circumvent the iOS 14 limitations on the FBpixel.

I’m no backend developer so this thing has been driving me close to desperation, the API documentation is awful, but in the end i managed to pull off a working example “in the void” using a cURL request and now i have to somehow implement it over to MODX.

What i need is the gist of a acouple snippets that can be called in 2 different situations:

  • on page load which should be no problem, just drop the [[Snippet]] somewhere in the page
  • as a formit hook here things get trickier because i would like to also pass some values from the form itself.

At the moment i dont need anything overcomplicated and i don’t mind having a separate snippet for every “event”, i just need them to work.


Here’s what i got down so far:

First of all i prepare the API variables, i already stored these in the modx settings

$fbapi_token = $modx->getOption('fbapi_token');
$fbapi_pixelID = $modx->getOption('fbapi_pixelID');
$fbapi_url = 'https://graph.facebook.com/v9.0/' . $fbapi_pixelID . '/events?access_token=' . $fbapi_token;

Then i get some global variables, feel free to point me if there’s a better way to retrieve these.

$ip = $_SERVER['REMOTE_ADDR']; // the user ip
$agent = $_SERVER['HTTP_USER_AGENT']; // the user agent
$time = date_create()->getTimestamp(); // a timestamp
$fbp = $_COOKIE[_fbp]; // the value of the facebook cookie used for deduplication
$id = $modx->resource->get('id'); // gather the current page ID
$url = $modx->makeUrl($id, '', '', 'full'); // generates the current page url

Question: the url from where the requests starts may have parameters but the $url variable doesn’t carry them. How do i do it?

After this i prepare the payload:

$data = array(
    'data' => array(
        0 => array(
            'event_name' => 'EventName',
            'event_source_url' => $url,
            'event_time' => $timestamp,
            'action_source' => 'website',
            'user_data' => array(
                'client_ip_address' => $ip,
                'client_user_agent' => $agent,
                'fbp' => $fbp,
            ),
            'custom_data' => array(
                'PAGE ID' => $id,
                'FOO' => 'BAR'
            ),
        )
    ),
    'test_event_code' => 'XYZ', // for development test only
);

And convert it to JSON for the API to read it

$payload = json_encode($data);

At this point i need to send this payload to the API… In a pure php file this works but not on MODX:

$ch = curl_init($fbapi_url); // cURL init
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); // cURL payload
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));  // cURL JSON header
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // cURL return
$result = curl_exec($ch); // cURL execution
curl_close($ch); // cURL close

Question: I understand there’s a modREST method to do POST requests. Should i use that? I have no idea how to set it up so i need a few directions.
I’ve tried to implement it like this:

    $client = $modx->getService('rest', 'rest.modRest');
    $client->setOption('header', true);
    $client->setOption('format','JSON');
    $response = $client->post($fbapi_url, $data);
    if (property_exists($response->responseInfo, 'scalar')) {
        $code = $response->responseInfo->scalar;
    }
    echo $code; 

but if i try to pass the json encoded $payload i get a 500 error on the page and if i pass it with the raw array $data i get returmed a 400 error on the request. I need to send the data as a JSON string.

The main focus right now is having this thing to work and properly send the request…


Next step: When used as a formit hook i’d love to pass into the data array a few values of the form itself. Let’s say:

'user_data' => array(
    'em' => THE VALUE OF THE MAIL FIELD in SHA256 hash,
    'ln' => THE VALUE OF THE NAME FIELD in SHA256 hash
),

How do i do that?

UPDATE

Progress has been made!!!

Variables

$url/$pageurl: for some reason the makeUrl didnt work at all and gave a null value, so i rewrote that variable like this:

$pageurl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

this returns the whole url of the page, including parameters. Way to go.

POST CALL

I’ve tried a lot of modRest iteration, I even tried Guzzle nothing worked… until i changed the cURL code like this:

$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, array("content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, $fbapi_url);

$result = curl_exec($curl);

curl_close($curl);

AND IT WOOOOOORKKKKSSSS!


So now i just need help regarding the implementation as a formit hook, how to retrieve and hash values.
Any other hints or suggestion on how to make it lighter or whatever is always welcome.

Here’s the full snippet code for reference:

<?php
    
// VARS
$ip = $_SERVER['REMOTE_ADDR'];
$agent = $_SERVER['HTTP_USER_AGENT'];
$time = date_create()->getTimestamp();
$fbp = $_COOKIE[_fbp];
$pageid = $modx->resource->get('id');
$pageurl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

// API url
$fbapi_token = $modx->getOption('fbapi_token');
$fbapi_pixelID = $modx->getOption('fbapi_pixelID');
$fbapi_url = 'https://graph.facebook.com/v9.0/' . $fbapi_pixelID . '/events?access_token=' . $fbapi_token;

// Data
$data = array(
    'data' => array(
        0 => array(
            'event_name' => 'AddToCart',
            'event_source_url' => $pageurl,
            'event_time' => $time,
            'action_source' => 'website',
            'user_data' => array(
                'client_ip_address' => $ip,
                'client_user_agent' => $agent,
                'fbp' => $fbp,
            ),
            'custom_data' => array(
                'PAGE ID' => $pageid,
                'FOO' => 'BAR'
            ),
        )
    ),
    'test_event_code' => 'TEST22966',
);

// Encode Array => JSON        
$payload = json_encode($data);

$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, array("content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_URL, $fbapi_url);

$result = curl_exec($curl);

curl_close($curl);

return $result;

$fbp = $_COOKIE[_fbp]; should at least quote the key ($fbp = $_COOKIE['_fbp'];) but better to also account for it not being set at all, e.g. with a null coalescing operator like this: $fbp = $_COOKIE['_fbp'] ?? '';

Perhaps you should also skip sending the POST to facebook if the _fbp cookie isn’t set? Not sure if that matters from FB’s perspective.


Same as above, always quote array keys, and inside double quoted strings use {} brackets:

$pageurl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

=>

$pageurl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";

It’s weird $modx->makeUrl didn’t work for you - does the resource have an alias and uri set?

1 Like

Read the section about custom hooks in the documentation. You can retrieve the values from the $hook variable.

$email = $hook->getValue('email');
$allFormFields = $hook->getValues();
1 Like

Thanks for the suggestions, PHP is kind of an uncharted territory for me :sweat_smile:

The fbp cookie is used only to deduplicate the same event if it’s tracked by both the API and the pixel. Good idea to push it only if not empty.
I’d do the POST anyway as it’s the whole point of using the API, track events even if the user has neglected cookies, uses blockers and whatnot.

I’m sure users will be thrilled that despite their efforts to not have their personal info and actions around the web sent to Facebook, you still found a way to do so. :stuck_out_tongue:

I know right?! :sweat_smile:
I’d be the first to raise an eyebrow about the ethics of this… unfortunately facebook allows you to do so… and a client’s request is a client’s request…