NEW 我們Astro v5.1的新blog上線啦 »

· Joseph · Marketing  · 5 min read

Facebook Pixel and Facebook Conversions API

Facebook Pixel and Facebook Conversions API

Recently I got a Trello ticket to add Facebook Pixel tracking code, and they gave me a token to use. As a frontend engineer on that project, it’s confusing to me, because the token is for Facebook Conversions API. The biggest difference between Facebook Conversions API and Facebook Pixel is Facebook conversions API is invoked on the server, and Facebook Pixel is on the client. Besides, Facebook Conversions API is to solve IOS 14 updates. Now, let me write the usage of Facebook Conversions API down.

Get Facebook Conversions API token.

pixel-home-page

You may be familiar with this page if you’re using Facebook Pixel. Let’s go to Settings and click Get Started in Conversions API, and select Events we are interested. conversions-api-settings

In the final step, clicking Open Implementation Guide then redirects to Using the Conversions API page to Generate Access Token. open-guidegenerate-token

Make a POST request

To send new events, make a POST request to this API’s /events edge from this path: https://graph.facebook.com/{API_VERSION}/{PIXEL_ID}/events?access_token={TOKEN}.

As we saw here, it’s just a POST request. We can try it with Graph API Explorer. test-event

{
  "data": [
    {
      "action_source": "website",
      "event_id": 12345,
      "event_name": "TestEvent",
      "event_time": 1633340316,
      "user_data": {
        "client_user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1",
        "em": "f660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44a",
        "client_ip_address": "11.22.33.44" 
      }
    }
  ],
  "test_event_code": "TEST99012"
}

You have to add client_ip_address to JSON, or the POST request won’t be tracked. Now back to the Test Events tab to see the result. test-events-result

Also, the View Details of TestEvents in Overview shows the POST request log. view-details

Let’s Code.

I run serverless lambda by AWS CDK, and set the token to AWS SecretManager. The following code is my Stack sample.

import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigw from '@aws-cdk/aws-apigatewayv2';
import { CorsHttpMethod } from '@aws-cdk/aws-apigatewayv2/lib/http/api';
import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
import * as apigwIntergration from '@aws-cdk/aws-apigatewayv2-integrations';

const params = {
  ApiStack: {
    ENV: 'staging',
    secretManager: {
      arn: '{SecretARN}',
      keys: ['facebookConversionApiToken']
    },
    fbPixelID: 'YOUR_PIXEL_ID'
  },
}

export class ApiStack extends cdk.Stack {
  constructor(scope: cdk.App, id: "ApiStack", props?: cdk.StackProps) {
    super(scope, id, props);

    const envParams = params[id];
    // Dynamoose();
    const { ENV } = envParams;
    const layerCommon: lambda.ILayerVersion = new lambda.LayerVersion(this, "LayerCommon", {
      compatibleRuntimes: [lambda.Runtime.NODEJS_14_X],
      code: lambda.Code.fromAsset('layer/common'),
    });

    const secret = secretsmanager.Secret.fromSecretCompleteArn(scope, 'ImportedSecret', envParams.secretManager.arn);
    const secretParams = envParams.secretManager.keys.reduce((params, key) => ({ ...params, [key]: secret.secretValueFromJson(key) }), {}) as { facebookConversionApiToken: string };
    const funcDefaultProps = {
      runtime: lambda.Runtime.NODEJS_14_X,
      code: new lambda.AssetCode('src'),
      memorySize: 1536,
      environment: { ENV }
    };
    const facebookConversionLambda = (layers: lambda.ILayerVersion[]) => new lambda.Function(scope, 'facebookConversion', {
      ...funcDefaultProps,
      handler: 'controllers/facebook_conversion/index.handler',
      layers,
      tracing: lambda.Tracing.ACTIVE,
      timeout: cdk.Duration.seconds(300),
    })

    const FacebookConversionLambda = facebookConversionLambda([layerCommon]);

    [ FacebookConversionLambda ].forEach((lambdaFunc) => {
      const envs: {[key: string]: string} = { fbToken: secretParams.facebookConversionApiToken, fbPixelID: envParams.fbPixelID }
      Object.keys(envs).forEach((key) => {
        lambdaFunc.addEnvironment(key, envs[key]);
      });
    });

    const eventLambdaMapping = {
      facebookConversion: FacebookConversionLambda,
    };

    const httpApi = new apigw.HttpApi(scope, id, {
      createDefaultStage: false,
      corsPreflight: {
        allowHeaders: ['Authorization', 'Content-Type'],
        allowMethods: [CorsHttpMethod.GET, CorsHttpMethod.HEAD, CorsHttpMethod.OPTIONS, CorsHttpMethod.POST, CorsHttpMethod.PUT, CorsHttpMethod.DELETE],
        allowOrigins: ['*'],
      },
    })
    httpApi.addRoutes({
      path: '/fb_conversion',
      methods: [apigw.HttpMethod.POST],
      integration: new apigwIntergration.LambdaProxyIntegration({
        handler: eventLambdaMapping["facebookConversion"],
      }),
    });
  }
}

And here is my lambda and class. lambda

import {
  responseSuccess,
  responseError,
} from '../../helpers/response';

const baseHandler = async (event: APIGatewayProxyEventV2) => {
  /***** Controller actions begin ******/
  const postConversion = async () => {
    const { event_name, event_id, event_source_url, user_data = {}, custom_data = {} } = JSON.parse(event.body || '{}');
    const facebookSsApi = new FacebookServerSideApi(
      { eventName: event_name, eventId: event_id, eventSourceUrl: event_source_url },
      user_data,
      custom_data
    );
    const result = await facebookSsApi.request();
    return result ? responseSuccess({}) : responseError({ statusCode: 400, message: 'Facebook Conversions Api request failed' });
  };

  switch (event.requestContext.http.method) {
    case "POST":
      return await postConversion();
    default:
      break;
  }
  return responseError({ message: "Undefined method" });
};

export const handler = baseHandler;

FacebookServerSideApi

const fetch = require("node-fetch");

class FacebookServerSideApi {
  private uri: string;
  private fbToken: string;
  private eventName: string;
  private eventId: string;
  private eventSourceUrl: string;
  private userData: Record<string, any>;
  private customData: Record<string,any>;

  constructor(eventObj: {eventName: string, eventId: string, eventSourceUrl?: string}, userData: Record<string, any>, customData: Record<string, any>) {
    const pixelId = process.env.fbPixelID as string;
    this.fbToken = process.env.fbToken as string;
    this.uri = `https://graph.facebook.com/v11.0/${pixelId}/events`;

    this.eventName = eventObj.eventName;
    this.eventId = eventObj.eventId;
    this.eventSourceUrl = eventObj.eventSourceUrl;
    this.userData = userData; //Should hash some key-value
    this.customData = customData;
  }

  public async request(): Promise<boolean> {
    if (!this.fbToken) return false;
    try {
      const event = {
        data: [
          {
            event_name: this.eventName,
            event_id: this.eventId,
            event_source_url: this.eventSourceUrl,
            event_time: Math.floor(+(new Date()) / 1000),
            user_data: this.userData,
            custom_data: this.customData,
            action_source: "website"
          }
        ],
      };
      const body = JSON.stringify(event);

      const response = await fetch(`${this.uri}?access_token=${this.fbToken}`, {
        method: 'POST',
        body,
        headers: {'Content-Type': 'application/json'}
      });
      const data = await response.json();
      return data['events_received'] == 1;
    } catch (e) {
      return false;
    }
  }
}

export default FacebookServerSideApi;

I removed hashing logic used on Line 20, and it should hash each key described here. Finally, after Deploying to AWS and sending the request by Postman or the frontend library, the Overview should show the event log.

That’s all. Now we can send such as PageView or other events defined by Facebook Pixel and Facebook Conversions API on the same page.

Related Posts

View All Posts »
Google Ads搜尋關鍵字廣告設定

Google Ads搜尋關鍵字廣告設定

Google Ads搜尋關鍵字廣告設定 當我們有需要被解決的需求,像牙齒痛,想知道哪裡有診所可以治療? 要買電視,想知道電視的商品資訊及評價?多數人會使用Google搜尋引擎搜尋找解答,用戶搜尋意圖明確,因此在Google搜尋引擎可以接觸到精準的潛在顧客,已是電商品牌行銷布局必要的媒體工具之一。 想投放Google搜…

Google Ads account setting

Google Ads account setting

數位科技蓬勃時代,多數人大部分時間都在使用手機及社群媒體分享生活圈維持情感連繫,品牌電商想要快速累積知名度及訂單,在社群或搜尋引擎投放廣告是最快達成成效的行銷方式,那要選擇哪個廣告平台呢?如果你的廣告預算有限,可以先從受眾精準的**Google ads搜尋廣告**開始 !

Upgrade hexo and hexo-theme-materialize

Upgrade hexo and hexo-theme-materialize

終於在年節時間把這個部落格升級到Hexo 6.2了!2019年建立這個部落格那時候還是用Hexo 4,前年曾經升級到Hexo 5,並且嘗試把hexo-theme-materialize一起升級到v4,不料這次theme升級幅度有點大,hexo-theme-materialize v4他們把Stylus都給改成scss…

設定Google Analytics 4 / Google Tag Manager追蹤user id

設定Google Analytics 4 / Google Tag Manager追蹤user id

這篇文章我決定用中文寫,主要原因是中文的資料太少,而且也很多不是Google Analytics 4 + Google Tag Manager (GA4+GTM) 的設定,所以我覺得用中文記錄一下,好讓大家要搜尋的可以搜得到。 在以前GA追蹤埋追蹤碼時,我們要紀錄的經過規劃後會視情況放在, , , 或複雜一點的cus…