I am building WhatsApp Flow using the MetaUI, but I am having issues testing the Flow data-exchange action using my endpoint on the Meta Ui Flow Builder. Screenshot of Flow Builder
I'm using a GoogleScripts endpoint that receives the Encrypted payload Meta send you about the flow "data-exchange" actions encrypted using the public key I´ve uploaded to the Meta servers.
My Google Scripts Function handles this request and then calls a Lambda function that decrypts the data, and based on certain logic it decides which screen should be next in the WhatsApp Flow:
var mainss = SpreadsheetApp.openById('11wVWWD17CvhHChcTnJ4r6MbI6F1GUHCpDXhQZWxMwDA');
var flowsLog = mainss.getSheetByName('Flow Log')
function doPost(e) {
try {
// Extract the payload
var requestBody = e.postData.contents;
flowsLog.appendRow([new Date(), "Received data", JSON.stringify(requestBody)]);
//Preserve encryption values
var encryptionVariables = JSON.parse(requestBody);
// Check for error notification
if (encryptionVariables.action === 'data_exchange' && encryptionVariables.data.hasOwnProperty('error_key')) {
flowsLog.appendRow([new Date(), "Error Notification", JSON.stringify(encryptionVariables)]);
// Respond to acknowledge the error notification
var errorAckResponse = {
"data": {
"acknowledged": true
}
};
return ContentService.createTextOutput(JSON.stringify(errorAckResponse))
.setMimeType(ContentService.MimeType.JSON);
}
//Decrypt payload call Lambda through Gateway API
var decryptedPayload = decryptData(encryptionVariables);
var decryptedData = JSON.parse(decryptedPayload.decrypted_data);
var action = decryptedData.action;
flowsLog.appendRow([new Date(), "Descrypted Data", JSON.stringify(decryptedData)]);
// Check for a health check request
if (action === 'ping') {
return ContentService.createTextOutput(JSON.stringify({
'version': '3.0',
'data': {
'status': 'active'
}
})).setMimeType(ContentService.MimeType.JSON);
} else if (action === 'data_exchange') {
// Extract the current screen ID
var screen = decryptedData.screen
var data = decryptedData.data
// Initialize variables for the next screen and response data
var nextScreen = '';
var responseData = {};
// Determine the next screen and response data based on the current screen
//switch (screen) {}
//Forcing some data to test the flow
var response = {
"rawData": {
"version": "3.0",
"screen": "SESSION_ONE",
"data": responseData
},
"encrypted_aes_key": encryptionVariables.encrypted_aes_key,
"initial_vector": encryptionVariables.initial_vector
};
//Encrypt data calling Lambda Function
var encryptedData = encryptData(response);
flowsLog.appendRow([new Date(), "Raw Data", JSON.stringify(response.rawData)]);
flowsLog.appendRow([new Date(), "Encrypted Data", encryptedData]);
// Devolver la respuesta
return ContentService.createTextOutput(encryptedData).setMimeType(ContentService.MimeType.TEXT);
} else {
return ContentService.createTextOutput("ERROR in Request")
}
}catch(e){
flowsLog.appendRow([new Date(), "Error in endpoint", e])
Logger.log(e);
}
}
Here´s an example of the logs I´m adding to my "flowLog" sheet:
Received from Meta:
"{\"encrypted_flow_data\":\"ikGRIjrW1sBzHECFHvVFkTXcsiVR24eW2Jvd5gmPvrkTp38NA4kG4mLCBB3qWyi36r5IX6BVBZ415Tar0fBHmWiyEEirVjmyPF1N\\/WEcVEHcgCuHAOC0Vj2z1tRSBQOSMfQdUbsGqWncMpWEEztD\\/a8CfrD5WTRWFG52p0twN6XnbnuX20DOjHoOMMGgAuPr8rB4rE88UkXS4XOBuSbxomS89UBPZsVfn4ZSe8xj70dVuAmNqmBOvZQVlLr2edOENssLTb6dj0cTvdGOEAia6HbyxZoOXNzMp5q38guT7Jw=\",\"encrypted_aes_key\":\"q4Ww1XFn0TadlE5H0pnJ6PzX3lXfeZwqYSZPKop\\/jWCweaY9d+Z1pMjfM1UX3te\\/GdHIpa8sa1chPSLOgxiYDbAcQ3uiJqlfOKLbdCuz\\/HTvVoTvrn3e\\/BJl9ALHeqOCqqIwGuHz2YcXvqc7zD4asU3mczrRa8Xvo8+YY89qebfF62F4f77ApXjT3LT\\/ynn7hYkxbMKo02bdIbteZAwSh+OMFVoMJo6vwOmH3fvbLSzcA80C8QIf7WVKQ7imik5+u2wETQOUDZ2Drmeyyh+xc\\/bTDi+GrSEpfXrI4jtnbd4YxPZLSlcaXAooK6ot9DP4ioOGSTxCCsAmz8ht87mHGQ==\",\"initial_vector\":\"SFWNZlzEXH6Cs0JeWMcJJQ==\"}"
Decrypted data:
{"version":"3.0","action":"data_exchange","screen":"WELCOME_SCREEN_EVENTS","data":{"event_type":"${form.event_type}","house":"${form.house}","event_date":"${form.date}"},"flow_token":"flows-builder-adacf0e2"}
Raw response:
{"version":"3.0","screen":"SESSION_ONE","data":{"event_type":"2","house":"1","event_date":"30/11/2023"}}
Encrypted response:
4/qcTvBZpQmZtKVPjow0jpISGTG3SicgOmCoxJKYUnhJqWylgZY2sNzp0ICFb1J3STDZugVWuolrmfAOSeTS6mCLUGj4sBStAZrTBz1p0NNlXW/XXVBe/a5c3d3iSiAO0YZdaQRvKuKW47YZHbFcUxuTX6AWiBZ3j6dADvLElwpPBg==
Additionally, here´s what the Flow Builder is instructing me to send as encrypted payload in my response in order to navigate to my screen "SESSION_ONE":
// Navigate to screen: SESSION_ONE
{
"version": "3.0",
"screen": "SESSION_ONE",
"data": {
"event_type": "Sesi\u00f3n fotogr\u00e1fica",
"house": "Casa Kali",
"event_date": "22\/12\/2023"
}
}
Here's the final part of my lambda encrypting function:
# Cifrar rawData
cipher = Cipher(algorithms.AES(key), modes.GCM(flipped_iv))
encryptor = cipher.encryptor()
encrypted = encryptor.update(json.dumps(raw_data).encode("utf-8")) + encryptor.finalize() + encryptor.tag
encrypted_response_b64 = b64encode(encrypted).decode("utf-8")
# Crear la respuesta
response = {
'encrypted_response': encrypted_response_b64
}
# Modificación: Solo devolver encrypted_response
return {
'statusCode': 200,
'body': json.dumps(encrypted_response_b64)
}
So far I know my decrypting/encrypting lambdas are working fine, I've decrypted my encrypted response and it gives my raw response back perfectly.
It seems that your response object is not the right format, it should be just the raw response like
response = {
"version": "3.0",
"screen": "SESSION_ONE",
"data": responseData
}
And then the response you return from the lambda should be plain text and not json encoded, like this
return {
'statusCode': 200,
'body': encrypted_response_b64
}
You can also checkout the full encryption code samples in python & nodejs in the docs here https://developers.facebook.com/docs/whatsapp/flows/guides/implementingyourflowendpoint#request-decryption-and-encryption