Last active
June 9, 2022 18:52
-
-
Save HubSpotHanevold/cd7c78c1875b08a6e89e039d77f867e0 to your computer and use it in GitHub Desktop.
Adding promo codes to contact lists in HubSpot
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
<?php | |
// CREATED 06.07.2022 | |
// CREATED BY TYLER HANEVOLD | |
// NOTE THAT THIS SCRIPT WILL GIVE ACCESS TO WRITING CONTENT TO PROPERTIES ON CONTACT RECORDS. DO NOT HAVE PUBLICLY AVAILABLE | |
// ADD YOUR HAPI KEY HERE | |
$hapi_key = 'USE YOUR TOKEN HERE'; | |
// CHECK FOR FORM SUBMIT | |
if (isset($_POST["submit"])) { | |
// RETRIEVE THE POST DATA FROM THE FORM | |
// NOTE THAT THIS IS NOT ESCAPED | |
$entered_property = $_POST["entered_property"]; | |
$list_id = $_POST["list_selected"]; | |
// IF THE LIMIT OF 100K WAS HIT, THIS IS USED TO START BACK UP WHERE THE LAST UPLOAD LEFT OFF | |
if(empty($_POST["offset"])) { | |
$vid_offset = ''; | |
} else { | |
$vid_offset = $_POST["offset"]; | |
} | |
// DON'T ALLOW ANY/ALL PROPERTIES TO BE UPDATED, ONLY ALLOW SOME | |
// IF THIS IS NOT SET, ANYONE COULD REWRITE CONTACT DATA IN BATCH IN YOUR HUBSPOT PORTAL | |
if($entered_property !== 'sample_coupon_code') { | |
echo 'Error: You have entered a property that is not allowed to be updated. Please try again.'; | |
exit(); | |
} | |
// WHERE TO SAVE THE CSV FILE THAT WAS UPLOADED | |
$target_dir = ""; | |
$target_file = $target_dir . basename($_FILES["file_uploaded"]["name"]); | |
$uploadOk = 1; | |
$FileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION)); | |
// ONLY ALLOW CSV FILES | |
if($FileType != "csv" ) { | |
echo "Sorry, only CSV files are allowed."; | |
$uploadOk = 0; | |
} | |
// REWRITE THE NAME PROVIDED AND SAVE AS VALUE BELOW | |
$newfilename = $target_dir . 'uploaded_coupon_file.csv'; | |
// ERROR HANDLING, IF UPLOADOK = 0 IT WILL STOP THE UPLOAD | |
if ($uploadOk == 0) { | |
echo "Sorry, your file was not uploaded."; | |
exit(); | |
// UPLOAD WAS OKAY, START NEXT STEPS | |
} else { | |
// SAVE THE UPLOADED FILE TO THE DIRECTORY | |
if (move_uploaded_file($_FILES["file_uploaded"]["tmp_name"], $newfilename)) { | |
// OPEN THE CSV FILE | |
if (($open = fopen("uploaded_coupon_file.csv", "r")) !== FALSE) { | |
while (($data = fgetcsv($open, 1000, ",")) !== FALSE) { | |
// ALL OF THE DATA IS STORE IN AN ARRAY FOR CALL BACK LATER | |
$uploaded_array[] = $data; | |
} | |
fclose($open); | |
} | |
// COUNT THE NUMBER OF ROWS IN THE CSV | |
$row_count = count($uploaded_array); | |
// i IS THE NUMBER OF TIMES THE LOOP WILL BATCH IMPORT PROMO CODES - NOTE THAT THIS IS SET TO UPDATE 100 CONTACT RECORDS AT A TIME | |
$i = 0; | |
// IF YOU WANTED TO CHANGE THE COUNT OF CONTACTS RETRIEVE AND UPDATED PER REQUEST, CHANGE THIS VALUE. THIS VALUE CAN'T BE LARGER THAN 100 OR THINGS WILL BREAK. | |
$count_limiter = 100; | |
// START THE REQUESTS FROM HUBSPOT LISTS AND THEN UPDATING THE CONTACTS IN THAT LIST WITH BATCH UPDATE | |
// THIS IS USING V1 OF HUBSPOT API TO DO THE BATCH UPDATE - MAY BE DEPRACATED IN THE FUTURE | |
do { | |
// RETRIEVE THE CONTACTS IN THAT LIST | |
$curl = curl_init(); | |
curl_setopt_array($curl, array( | |
CURLOPT_URL => "https://api.hubapi.com/contacts/v1/lists/$list_id/contacts/all?hapikey=$hapi_key&count=$count_limiter&vidOffset=$vid_offset", | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_ENCODING => '', | |
CURLOPT_MAXREDIRS => 10, | |
CURLOPT_TIMEOUT => 0, | |
CURLOPT_FOLLOWLOCATION => true, | |
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | |
CURLOPT_CUSTOMREQUEST => 'GET', | |
)); | |
$list_response = curl_exec($curl); | |
curl_close($curl); | |
$list_data = json_decode($list_response, true); | |
$list_contacts = $list_data['contacts']; | |
$count_list_contacts = count($list_contacts); | |
// TWO LOOP COUNTERS - ONE FOR THE RETURNED CONTACTS JSON "X" AND ONE FOR THE CSV FILE ARRAY "Y" | |
$x = 0; | |
$y = 0; | |
// GENERATE A PAYLOAD AND STORE THE DATA AS A SINGLE VARIABLE | |
ob_start(); | |
echo '['; | |
while($x < $count_list_contacts) { | |
// THE RETURNED ROW IN CSV VARIABLE BELOW WILL CONTINUE TO LOOP THROUGH THE NEXT BATCH OF 100 ROWS FOR EACH LOOP | |
$returned_row_in_csv = $count_limiter * $i + $y; | |
echo '{'; | |
echo '"vid": "'.$list_data['contacts'][$x]['vid'].'",'; | |
echo '"properties": ['; | |
echo '{'; | |
echo '"property": "'.$entered_property.'",'; | |
echo '"value": "'.$uploaded_array[$returned_row_in_csv][0].'"'; | |
echo '}'; | |
echo ']'; | |
echo '}'; if($x < ($count_list_contacts - 1) ) { echo ','; } else { echo ''; } | |
$y++; | |
$x++; | |
} | |
echo ']'; | |
// SET THE PAYLOAD AS THE SINGLE VARIABLE | |
$payload = ob_get_contents(); | |
ob_end_clean(); | |
ob_end_flush(); | |
// BATCH UPDATE THE CONTACT RECORDS | |
$curl = curl_init(); | |
curl_setopt_array($curl, array( | |
CURLOPT_URL => "https://api.hubapi.com/contacts/v1/contact/batch/?hapikey=$hapi_key", | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_ENCODING => '', | |
CURLOPT_MAXREDIRS => 10, | |
CURLOPT_TIMEOUT => 0, | |
CURLOPT_FOLLOWLOCATION => true, | |
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | |
CURLOPT_CUSTOMREQUEST => 'POST', | |
CURLOPT_POSTFIELDS =>$payload, | |
CURLOPT_HTTPHEADER => array( | |
'Content-Type: application/json' | |
), | |
)); | |
$response = curl_exec($curl); | |
curl_close($curl); | |
// THE API REQUEST DOESN'T RETURN A RESPONSE WHEN SUCCESSFUL. IF THERE IS A RESPONSE ECHO THE ERROR BELOW. | |
if(empty($response)) { | |
} else { | |
// ECHO THE ERROR FOR BATCH API RESPONSE | |
echo '<h3>Error: '.$response.'</h3>'; | |
} | |
// CHECK IF THE LIST OF CONTACTS HAS MORE RECORDS | |
$has_more = $list_data['has-more']; | |
// WHAT IS THE OFFSET FOR THE NEXT BATCH OF RECORDS | |
$vid_offset = $list_data['vid-offset']; | |
// LOOP COUNTER | |
$i++; | |
// SET AN UPPER LIMIT OF ONLY 100K CONTACT RECORDS TO BE UPDATED AT A TIME | |
if($i == 1000) { | |
echo '<h3>100,000 update limit hit. Please wait and update a next batch.</h3>'; | |
$limit_reached = 'true'; | |
$has_more = 'false'; | |
} else { | |
$limit_reached = 'false'; | |
} | |
// THE END OF THE DO WHILE LOOP | |
} while ($has_more == 'true'); | |
// COUNT OF HOW MANY RECORDS WERE UPDATED | |
$num_updated = $returned_row_in_csv + 1; | |
// DUMP/DELETE THE CSV FILE AFTER IT'S DONE | |
unlink($newfilename); | |
} else { | |
echo "Sorry, there was an error uploading your file."; | |
} | |
} | |
} | |
?> | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>CSV Promo Code Uploader</title> | |
<style> | |
body { | |
margin: 0px; | |
background-color: #f0f0f0; | |
font-family: Arial, Helvetica, sans-serif; | |
} | |
main { | |
float: left; width: 100%; | |
} | |
.container { | |
float: left; | |
margin-left: 25%; | |
width: 50%; | |
padding: 10px; | |
padding-left: 25px; | |
padding-bottom: 50px; | |
background-color: white; | |
border: solid 1px gray; | |
margin-top: 25px; | |
} | |
</style> | |
</head> | |
<body> | |
<main> | |
<div class="container" style=" "> | |
<div style="float: left; width: 100%; margin-top: 15px; margin-bottom: 25px; border: solid 1px gray; border-width: 0px 0px 1px 0px; padding-bottom: 15px;"> | |
<h2>How-to guide:</h2> | |
<ol> | |
<li>Get a list of promo codes you want to add to a contact list in HubSpot</li> | |
<li>Make sure the CSV file doesn't have a header row.</li> | |
<li>Select which list you want to update using the dropdown select.</li> | |
<li>Select the property you want to hold the promo code data</li> | |
<ul> | |
<li>Note that you must use the internal name provided by HubSpot. <a href="https://knowledge.hubspot.com/crm-setup/manage-your-properties" target="_blank">Instructions on finding that value here.</a></li> | |
</ul> | |
<li>Select 'Upload' and the script will process your request. Please allow 1-2 minutes for the request to complete.</li> | |
<li><i>Note that this uploader will only handle 100k contact updates at a time.</i></li> | |
</ol> | |
<strong>For batches greater than 100k contacts</strong> | |
<ol> | |
<li>The uploader will stop after 100k contacts have been updated.</li> | |
<li>An offset value will be placed into the form to know where to pick up where the previous batch left off.</li> | |
<li>Remove the promo codes that were already used. The last promo code used will be displayed.</li> | |
<li>Upload the updated CSV file with used promo codes remove.</li> | |
<li>Run the script for the next 100k batch.</li> | |
</ol> | |
<i>Note that each 100k contacts updated takes 2000 API requests per batch update.</i> | |
</div> | |
<h3>Upload your Promo Code CSV file</h3> | |
<form method="POST" action="" enctype="multipart/form-data"> | |
<select name="list_selected" required> | |
<option>Select a list</option> | |
<?php | |
// RETRIEVES THE LISTS FROM THE PORTAL | |
$curl = curl_init(); | |
curl_setopt_array($curl, array( | |
CURLOPT_URL => "https://api.hubapi.com/contacts/v1/lists?hapikey=$hapi_key", | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_ENCODING => '', | |
CURLOPT_MAXREDIRS => 10, | |
CURLOPT_TIMEOUT => 0, | |
CURLOPT_FOLLOWLOCATION => true, | |
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | |
CURLOPT_CUSTOMREQUEST => 'GET', | |
)); | |
$response = curl_exec($curl); | |
curl_close($curl); | |
$data = json_decode($response, true); | |
$results = $data['lists']; | |
$results_count = count($results); | |
$x = 0; | |
while($x < $results_count) { | |
// PUT THE LISTS IN A DROPDOWN SELECT AND INCLUDE THE LIST SIZE FOR EASY USE | |
echo '<option value="'.$data['lists'][$x]['listId'].'">'.$data['lists'][$x]['name'].' ('.$data['lists'][$x]['metaData']['size'].')</option>'; | |
$x++; | |
} | |
?> | |
</select></br></br> | |
<input type="text" name="entered_property" placeholder="Property Name to Update" value="" required /></br></br> | |
<?php | |
if(empty($limit_reached)) { | |
$limit_reached = 'false'; | |
} | |
// ADD THE OFFSET VALUE AS AN INPUT TO BE USED IN THE NEXT BATCH OF 100K CONTACTS - PUT THE VALUE FROM THE FINAL RESPONSE AS THE VALUE OF THE INPUT. | |
if($limit_reached == 'true') { | |
echo 'Offset value: (do not change)</br> | |
<input type="text" name="offset" value="'.$list_data['vid-offset'].'" required /></br></br>'; | |
} | |
?> | |
<input type="file" name="file_uploaded" required /></br></br> | |
<input style="padding: 10px; font-size: 120%; text-align: center; width: 400px; cursor: pointer; background-color: black; color: white;" type="submit" name="submit" value="Upload" /></br> | |
</form> | |
<?php | |
if($num_updated > 0) { | |
// GIVE USER FEEDBACK FROM WHAT WAS UPDATED | |
echo '<br>'; | |
echo '<br>'; | |
echo 'Number of Records Updated: '.$num_updated.'</br>'; | |
echo 'Last Promo Code Used: '.$uploaded_array[$returned_row_in_csv][0].'<br>'; | |
// IF THE LIMIT OF 100K IS REACHED, SHARE THE NEXT OFFSET VALUE WITH THE USER | |
if($limit_reached == 'true') { | |
echo 'Offset: '.$list_data['vid-offset'].'<br>'; | |
} else { | |
} | |
} else { | |
} | |
?> | |
</div> | |
</main> | |
</body> | |
</html> |
Fixed a few things that were causing errors to be logged. Moved the status updates (After import) to below the form.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Singe page php script that allows you to update 100k contacts with a promo code provided from a CSV file.