Facebook Pixel: Extending
Facebook Pixel plugin for Sylius: custom CAPI consent-evidence providers, the PixelEventDispatched event and access-token encryption.
Facebook Pixel: Extending
Live Demo: Try this and other plugins live — no installation, no risk: demo.markus-michalski.net
Custom CAPI consent-evidence providers
The server-side consent check is done by CapiConsentPolicyInterface. It asks every service that implements Mmd\SyliusFacebookPixelPlugin\Consent\CapiConsentEvidenceProviderInterface in turn and allows sending if the channel does not require evidence or at least one provider returns true:
interface CapiConsentEvidenceProviderInterface
{
public function isConsentVerified(ChannelInterface $channel): bool;
}
The built-in provider (KlaroConsentChecker) reads the table mmd_sylius_klaro_channel_configuration of mmd/sylius-klaro-consent-plugin directly (no Composer dependency, detected at runtime). It only confirms consent when all of these conditions are met:
- The Klaro plugin is registered as a bundle.
- Klaro has an enabled configuration row for the channel.
- Klaro stores the consent state in a cookie (not in
localStorage, which the server cannot read). - The cookie contains a JSON object with
facebook_pixel: true.
In every other case it returns false.
To support another consent tool, implement the interface and read that tool's cookie or session state for the current request:
final class MyCmpEvidenceProvider implements CapiConsentEvidenceProviderInterface
{
public function __construct(private readonly RequestStack $requestStack) {}
public function isConsentVerified(ChannelInterface $channel): bool
{
$cookie = $this->requestStack->getCurrentRequest()?->cookies->get('my_cmp');
return null !== $cookie && str_contains($cookie, 'marketing:1');
}
}
Autoconfiguration tags every service that implements the interface automatically. Only with autoconfiguration disabled do you have to tag the service yourself with mmd_sylius_facebook_pixel_plugin.capi_consent_evidence_provider.
Only return
truewith real evidence, otherwisefalse. A throwing provider counts as "no evidence": the policy catches every exception and logs a warning. Providers run synchronously inside the storefront request or the add-to-cart action, so keep them fast.
The PixelEventDispatched event
For every tracked event, Mmd\SyliusFacebookPixelPlugin\Event\PixelEventDispatched is dispatched synchronously through the Symfony event dispatcher, while the request and customer are still available. The plugin itself listens to it with CapiEventListener. The event carries:
| Property | Content |
|---|---|
channel |
The Sylius channel. |
event |
The FacebookPixelEvent enum (PageView, ViewContent, ViewCategory, AddToCart, InitiateCheckout, Purchase). |
eventId |
The event_id that the browser call carries as well. |
serverContext |
IP address, user agent, source URL, _fbc and _fbp cookie of the request. |
customData |
The event data (content_ids, value, currency and so on). |
customerEmail, customerPhone |
Plaintext values where known (see Privacy). |
A custom listener on this event therefore sees the plaintext email and phone number; hashing only happens in the CapiEventListener. The event is only dispatched if the event is tracked for the channel. Whether anything is sent to Meta is decided afterwards by the CapiEventListener.
Access-token encryption
The Conversions API access token is stored encrypted (libsodium secretbox, random nonce per encryption). The key is derived from the host application's %kernel.secret% (APP_SECRET) plus a plugin-specific context; there is no separate secret to manage. Changing APP_SECRET makes every stored token unreadable: the plugin then sends nothing instead of crashing. Enter the token again afterwards.