├── .gitignore ├── .eslintignore ├── src ├── types.d.ts └── index.ts ├── .idea ├── vcs.xml ├── jsLinters │ └── eslint.xml ├── .gitignore ├── inspectionProfiles │ └── Project_Default.xml ├── facebook-conversion-api.iml └── modules.xml ├── .eslintrc.js ├── tsconfig.json ├── package.json ├── dist ├── index.d.ts └── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'facebook-nodejs-business-sdk'; 2 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/jsLinters/eslint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/facebook-conversion-api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es2021: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | parser: '@typescript-eslint/parser', 11 | parserOptions: { 12 | ecmaVersion: 12, 13 | }, 14 | plugins: [ 15 | '@typescript-eslint', 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "es6", 5 | "module": "commonjs", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "declaration": true, 12 | }, 13 | "exclude": [ 14 | "./dist", 15 | "./node_modules", 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rivercode/facebook-conversion-api", 3 | "version": "1.1.8", 4 | "description": "Facebook Conversion API Wrapper for Node.js", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "lint": "eslint . --ext .ts", 9 | "lint:tsc": "tsc", 10 | "watch": "tsc --watch", 11 | "build": "tsc --build", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/RivercodeAB/facebook-conversion-api.git" 17 | }, 18 | "author": "Rivercode", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/RivercodeAB/facebook-conversion-api/issues" 22 | }, 23 | "homepage": "https://github.com/RivercodeAB/facebook-conversion-api#readme", 24 | "dependencies": { 25 | "facebook-nodejs-business-sdk": "^13.0.0" 26 | }, 27 | "devDependencies": { 28 | "@types/node": "^16.7.1", 29 | "@typescript-eslint/eslint-plugin": "^4.29.3", 30 | "@typescript-eslint/parser": "^4.29.3", 31 | "eslint": "^7.32.0", 32 | "eslint-config-airbnb-base": "^14.2.1", 33 | "eslint-plugin-import": "^2.24.1", 34 | "typescript": "^4.3.5" 35 | }, 36 | "keywords": [ 37 | "facebook-conversion-api", 38 | "fb-conversion-api", 39 | "node", 40 | "typescript" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | declare class FacebookConversionAPI { 2 | #private; 3 | accessToken: string; 4 | pixelId: string; 5 | fbp: string | null; 6 | fbc: string | null; 7 | userData: any; 8 | contents: any; 9 | debug: boolean; 10 | /** 11 | * Constructor. 12 | * 13 | * @param accessToken 14 | * @param pixelId 15 | * @param emails 16 | * @param phones 17 | * @param clientIpAddress 18 | * @param clientUserAgent 19 | * @param fbp 20 | * @param fbc 21 | * @param debug 22 | */ 23 | constructor(accessToken: string, pixelId: string, emails: Array | null, phones: Array | null, clientIpAddress: string, clientUserAgent: string, fbp: string | null, fbc: string | null, debug?: boolean); 24 | /** 25 | * Add product to contents array. 26 | * 27 | * @param sku 28 | * @param quantity 29 | */ 30 | addProduct(sku: string, quantity: number): void; 31 | /** 32 | * Send event to Facebook Conversion API and clear contents array after event is fired. 33 | * 34 | * @param eventName 35 | * @param sourceUrl 36 | * @param purchaseData 37 | * @param eventData 38 | */ 39 | sendEvent(eventName: string, sourceUrl: string, purchaseData?: { 40 | value?: number; 41 | currency?: string; 42 | }, eventData?: { 43 | eventId?: string; 44 | }): void; 45 | } 46 | export default FacebookConversionAPI; 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Facebook Conversion API 2 | 3 | > Node.js wrapper for [Facebook's Conversion API](https://developers.facebook.com/docs/marketing-api/conversions-api/) 4 | 5 | ## Install 6 | 7 | ```bash 8 | npm install @rivercode/facebook-conversion-api 9 | ``` 10 | 11 | ## Initiate Facebook Conversion API 12 | ```node 13 | // ES6 import or TypeScript 14 | import FacebookConversionAPI from '@rivercode/facebook-conversion-api'; 15 | // CommonJS 16 | const FacebookConversionAPI = require('@rivercode/facebook-conversion-api').default; 17 | 18 | const FBConversionAPI = new FacebookConversionAPI( 19 | 'accessToken', 20 | 'pixelId', 21 | ['email1', 'email2'], // or null 22 | ['phone1', 'phone2'], // or null 23 | 'clientIpAddress', 24 | 'clientUserAgent', 25 | 'fbp', // or null 26 | 'fpc', // or null 27 | 'debug', // default to false 28 | ); 29 | ``` 30 | 31 | Read more here on how you can get your [access token](https://developers.facebook.com/docs/marketing-api/conversions-api/get-started/#access-token) and [fbp/fpc identifiers](https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/fbp-and-fbc/). 32 | 33 | ### ViewContent Event 34 | ```node 35 | FBConversionAPI.addProduct('productSku', quantity); 36 | FBConversionAPI.sendEvent('ViewContent', sourceUrl, { value: 1000, currency: 'USD' }, { eventId: 'eventId' }); 37 | ``` 38 | 39 | ### Add To Cart Event 40 | ```node 41 | FBConversionAPI.addProduct('productSku', quantity); 42 | FBConversionAPI.sendEvent('AddToCart', sourceUrl, { value: 1000, currency: 'USD' }, { eventId: 'eventId' }); 43 | ``` 44 | 45 | ### Initiate Checkout Event 46 | ```node 47 | FBConversionAPI.addProduct('productSku', quantity); 48 | FBConversionAPI.sendEvent('InitiateCheckout', sourceUrl, { value: 1000, currency: 'USD' }, { eventId: 'eventId' }); 49 | ``` 50 | 51 | ### Purchase Event 52 | ```node 53 | FBConversionAPI.addProduct('productSku', quantity); 54 | FBConversionAPI.sendEvent('Purchase', sourceUrl, { value: 1000, currency: 'USD' }, { eventId: 'eventId' }); 55 | ``` 56 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | const bizSdk = require('facebook-nodejs-business-sdk'); 3 | 4 | class FacebookConversionAPI { 5 | accessToken: string; 6 | 7 | pixelId: string; 8 | 9 | fbp: string | null; 10 | 11 | fbc: string | null; 12 | 13 | userData: any; 14 | 15 | contents: any; 16 | 17 | debug: boolean; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param accessToken 23 | * @param pixelId 24 | * @param emails 25 | * @param phones 26 | * @param clientIpAddress 27 | * @param clientUserAgent 28 | * @param fbp 29 | * @param fbc 30 | * @param debug 31 | */ 32 | constructor( 33 | accessToken: string, pixelId: string, emails: Array|null, 34 | phones: Array|null, clientIpAddress: string, clientUserAgent: string, 35 | fbp: string | null, fbc: string | null, debug: boolean = false, 36 | ) { 37 | this.accessToken = accessToken; 38 | this.pixelId = pixelId; 39 | this.fbp = fbp; 40 | this.fbc = fbc; 41 | this.debug = debug; 42 | this.userData = (new bizSdk.UserData()) 43 | .setEmails(emails) 44 | .setPhones(phones) 45 | .setClientIpAddress(clientIpAddress) 46 | .setClientUserAgent(clientUserAgent) 47 | .setFbp(fbp) 48 | .setFbc(fbc); 49 | this.contents = []; 50 | 51 | if (this.debug) { 52 | console.log(`User Data: ${JSON.stringify(this.userData)}\n`); 53 | } 54 | } 55 | 56 | /** 57 | * Add product to contents array. 58 | * 59 | * @param sku 60 | * @param quantity 61 | */ 62 | addProduct(sku: string, quantity: number): void { 63 | this.contents.push((new bizSdk.Content()).setId(sku).setQuantity(quantity)); 64 | 65 | if (this.debug) { 66 | console.log(`Add To Cart: ${JSON.stringify(this.contents)}\n`); 67 | } 68 | } 69 | 70 | /** 71 | * Send event to Facebook Conversion API and clear contents array after event is fired. 72 | * 73 | * @param eventName 74 | * @param sourceUrl 75 | * @param purchaseData 76 | * @param eventData 77 | */ 78 | sendEvent( 79 | eventName: string, sourceUrl: string, 80 | purchaseData?: { value?: number, currency?: string }, eventData?: { eventId?: string }, 81 | ): void { 82 | const eventRequest = (new bizSdk.EventRequest(this.accessToken, this.pixelId)) 83 | .setEvents([this.#getEventData(eventName, sourceUrl, purchaseData, eventData)]); 84 | 85 | this.contents = []; 86 | 87 | eventRequest.execute().then( 88 | (response: any) => response, 89 | (error: any) => error, 90 | ); 91 | 92 | if (this.debug) { 93 | console.log(`Event Request: ${JSON.stringify(eventRequest)}\n`); 94 | } 95 | } 96 | 97 | /** 98 | * Get event data. 99 | * 100 | * @param eventName 101 | * @param sourceUrl 102 | * @param purchaseData 103 | * @param eventData 104 | */ 105 | #getEventData( 106 | eventName: string, 107 | sourceUrl: string, 108 | purchaseData?: { value?: number, currency?: string }, 109 | eventData?: { eventId?: string }, 110 | ): any { 111 | const currentTimestamp = Math.floor(new Date() as any / 1000); 112 | 113 | return (new bizSdk.ServerEvent()) 114 | .setEventName(eventName) 115 | .setEventTime(currentTimestamp) 116 | .setEventId(eventData?.eventId) 117 | .setUserData(this.userData) 118 | .setCustomData((new bizSdk.CustomData()) 119 | .setContents(this.contents) 120 | .setCurrency(purchaseData?.currency) 121 | .setValue(purchaseData?.value)) 122 | .setEventSourceUrl(sourceUrl) 123 | .setActionSource('website'); 124 | } 125 | } 126 | 127 | export default FacebookConversionAPI; 128 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { 3 | if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); 4 | if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); 5 | return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); 6 | }; 7 | var _FacebookConversionAPI_instances, _FacebookConversionAPI_getEventData; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | /* eslint-disable @typescript-eslint/no-explicit-any */ 10 | const bizSdk = require('facebook-nodejs-business-sdk'); 11 | class FacebookConversionAPI { 12 | /** 13 | * Constructor. 14 | * 15 | * @param accessToken 16 | * @param pixelId 17 | * @param emails 18 | * @param phones 19 | * @param clientIpAddress 20 | * @param clientUserAgent 21 | * @param fbp 22 | * @param fbc 23 | * @param debug 24 | */ 25 | constructor(accessToken, pixelId, emails, phones, clientIpAddress, clientUserAgent, fbp, fbc, debug = false) { 26 | _FacebookConversionAPI_instances.add(this); 27 | this.accessToken = accessToken; 28 | this.pixelId = pixelId; 29 | this.fbp = fbp; 30 | this.fbc = fbc; 31 | this.debug = debug; 32 | this.userData = (new bizSdk.UserData()) 33 | .setEmails(emails) 34 | .setPhones(phones) 35 | .setClientIpAddress(clientIpAddress) 36 | .setClientUserAgent(clientUserAgent) 37 | .setFbp(fbp) 38 | .setFbc(fbc); 39 | this.contents = []; 40 | if (this.debug) { 41 | console.log(`User Data: ${JSON.stringify(this.userData)}\n`); 42 | } 43 | } 44 | /** 45 | * Add product to contents array. 46 | * 47 | * @param sku 48 | * @param quantity 49 | */ 50 | addProduct(sku, quantity) { 51 | this.contents.push((new bizSdk.Content()).setId(sku).setQuantity(quantity)); 52 | if (this.debug) { 53 | console.log(`Add To Cart: ${JSON.stringify(this.contents)}\n`); 54 | } 55 | } 56 | /** 57 | * Send event to Facebook Conversion API and clear contents array after event is fired. 58 | * 59 | * @param eventName 60 | * @param sourceUrl 61 | * @param purchaseData 62 | * @param eventData 63 | */ 64 | sendEvent(eventName, sourceUrl, purchaseData, eventData) { 65 | const eventRequest = (new bizSdk.EventRequest(this.accessToken, this.pixelId)) 66 | .setEvents([__classPrivateFieldGet(this, _FacebookConversionAPI_instances, "m", _FacebookConversionAPI_getEventData).call(this, eventName, sourceUrl, purchaseData, eventData)]); 67 | this.contents = []; 68 | eventRequest.execute().then((response) => response, (error) => error); 69 | if (this.debug) { 70 | console.log(`Event Request: ${JSON.stringify(eventRequest)}\n`); 71 | } 72 | } 73 | } 74 | _FacebookConversionAPI_instances = new WeakSet(), _FacebookConversionAPI_getEventData = function _FacebookConversionAPI_getEventData(eventName, sourceUrl, purchaseData, eventData) { 75 | const currentTimestamp = Math.floor(new Date() / 1000); 76 | return (new bizSdk.ServerEvent()) 77 | .setEventName(eventName) 78 | .setEventTime(currentTimestamp) 79 | .setEventId(eventData === null || eventData === void 0 ? void 0 : eventData.eventId) 80 | .setUserData(this.userData) 81 | .setCustomData((new bizSdk.CustomData()) 82 | .setContents(this.contents) 83 | .setCurrency(purchaseData === null || purchaseData === void 0 ? void 0 : purchaseData.currency) 84 | .setValue(purchaseData === null || purchaseData === void 0 ? void 0 : purchaseData.value)) 85 | .setEventSourceUrl(sourceUrl) 86 | .setActionSource('website'); 87 | }; 88 | exports.default = FacebookConversionAPI; 89 | --------------------------------------------------------------------------------