We use an AWS Lambda function for the callback handler. It accepts requests with query params for the qualification (complete or quality_term) and the tap id, which are passed through Survey Monkey on survey completion. The hash is generated and appended to the callback URL as outlined in the documentation here: http://docs.tapresearch.com/#callbacks The function then calls that URL with a GET and logs the callback URL and any success/failure. Though the callback URL returns a 200 request, we never see successfully completions in TAP campaigns. We end up seeing incompletes, abandons, and Failed partner callback security check
.
Last active
January 2, 2025 18:56
-
-
Save mercul3s/9dbf18bc5f1b63bc56e24b2c6c1cf872 to your computer and use it in GitHub Desktop.
TAP Callback
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hashlib | |
import hmac | |
import logging | |
import os | |
import sys | |
sys.path.insert(0, "src/vendor") | |
import requests | |
app_version = "1.0.0" | |
logger = logging.getLogger(name="tap-callback-handler") | |
logger.setLevel(os.environ.get("LOG_LEVEL", "INFO")) | |
tap_base_url = f"{os.environ.get('TAP_BASE_URL')}/cps/" | |
# TAP Base URL: | |
# https://www.tapresearch.com/router/customers/0fa67175621bb1db8cfca37fc351c642 | |
# Example Callback URL: | |
# https://www.tapresearch.com/router/customers/0fa67175621bb1db8cfca37fc351c642/cps/complete?tid=39fd5a382f6116147d9a7a7cb1b96a6d&tr_sech=5bca903a568f9e003fc1f6988bffe2b2612d4919 | |
def callback(event, context): | |
""" | |
AWS Lambda handler function to process survey callback requests from Survey Monkey. | |
This function extracts qualification and tid parameters from the event, | |
generates a callback URL based on the qualification (complete or , sends a GET request to that URL, and logs the result. | |
Args: | |
event (dict): The AWS Lambda event object containing request data. | |
context (object): The AWS Lambda context object. | |
Returns: | |
dict: A response object with a 200 status code and a thank you message. | |
Raises: | |
None, but logs errors if the request to TAP fails. | |
""" | |
params = event.get("queryStringParameters") | |
qualification = params.get("qualification") | |
tid = params.get("tid") | |
callback_url = generate_url(qualification, tid) | |
logger.debug(callback_url) | |
result = requests.get(callback_url) | |
if result.ok: | |
logger.debug(f"Successfully sent request to TAP: {tid} {qualification}") | |
else: | |
logger.error( | |
f"Error sending request to TAP: {result.reason} TAP_ID: {tid} QUALIFICATION: {qualification}" | |
) | |
return {"statusCode": 200, "body": "Thank you for participating in the survey!"} | |
def generate_url(qualification, tid): | |
""" | |
Generate a secure URL for TAP callback. | |
This function creates a URL with the given qualification and tid, | |
then adds a security hash using HMAC-SHA1 with a secret key. | |
Args: | |
qualification (str): The qualification parameter for the URL. | |
tid (str): The tid parameter for the URL. | |
Returns: | |
str: The generated URL with the security hash appended. | |
Raises: | |
None, but may raise exceptions if encoding or hashing fails. | |
""" | |
tap_secret = get_tap_secret() | |
callback_url = f"{tap_base_url}{qualification}?tid={tid}" | |
security_hash = hmac.new( | |
tap_secret.encode("utf-8"), | |
callback_url.encode("utf-8"), | |
hashlib.sha1, | |
) | |
hashed_url = f"{callback_url}&tr_sech={security_hash.hexdigest()}" | |
return hashed_url | |
def get_tap_secret(): | |
""" | |
Retrieve the TAP secret from environment variables or AWS Secrets Manager. | |
This function first checks for the TAP_SECRET in environment variables. | |
If not found, it attempts to retrieve the secret from AWS Secrets Manager | |
using the AWS Lambda Secrets Manager extension. | |
Returns: | |
str: The TAP secret string. | |
Raises: | |
None, but logs an error if the secret cannot be retrieved from Secrets Manager. | |
""" | |
tap_secret = os.environ.get("TAP_SECRET") | |
if tap_secret: | |
return tap_secret | |
else: | |
secrets_extension_endpoint = ( | |
"http://localhost:2773/secretsmanager/get?secretId=tap-secret" | |
) | |
headers = { | |
"X-Aws-Parameters-Secrets-Token": os.environ.get("AWS_SESSION_TOKEN") | |
} | |
response = requests.get(secrets_extension_endpoint, headers=headers) | |
tap_secret = response.json().get("SecretString") | |
return tap_secret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment