Created
February 18, 2017 02:41
-
-
Save oneillo/f5c034cba04e0ec1f175a3a8377b9d64 to your computer and use it in GitHub Desktop.
Perl script to create Shopify discount codes
This file contains 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
#!/usr/bin/perl | |
# v21 | |
use strict; | |
use warnings; | |
use LWP::UserAgent; | |
use HTTP::Request; | |
use JSON::XS; | |
use JSON; | |
use POSIX qw(ceil strftime); | |
use Time::HiRes qw(usleep); | |
use Time::Local; | |
use DateTime::Format::Strptime; | |
# GLOBAL VARIABLES | |
# I KNOW THIS IS BAD...SO SHOOT ME | |
my $timestamp = `date +"%Y%m%d%H%M"`; # Timestamp captures when the script is run and is used to name the output files that it creates | |
chomp($timestamp); | |
my $scriptVersion = "21"; | |
my $discountStartDate = `date +"%Y-%m-%d"`; | |
chomp($discountStartDate); | |
my $discountEndDate; | |
my $discountAmount; | |
my $discountType; | |
my $discountCode = ""; | |
my $discountCount = 1; | |
my $discountUsageLimit; | |
my $minOrderAmount=0; | |
my $newCustomerGroup = '756190596'; # This is the Shopify code for a custom group created to include any email accounts that have 0 orders. You might have to update this by getting your value from the URL for that group | |
my $limitToNewCustomers = 0; | |
my $appliesOnce = 'false'; | |
my $fileNameForCodes; | |
# | |
# parse_args READS IN OPTIONS THAT ARE PASSED TO THE SCRIPT WHEN IT IS RUN, I.E. get_shopify_data.pl -s 2015-07-01 -e 2015-07-31 | |
# THE ONLY OPTIONS RIGHT NOW ARE -H FOR HELP AND -S AND -E FOR THE START AND END DATES TO GET DATA FOR | |
sub parse_args() | |
{ | |
my $arg; | |
my @argv = @ARGV; #THIS GETS THE ARRAY OF OPTIONS PASSED WHEN THE SCRIPT WAS RUN | |
# CALL THE HELP FUNCTION IF NO OPTIONS WERE PASSED WHEN IT WAS RUN | |
if($#argv == "-1") | |
{ | |
help(); | |
} | |
# GO THROUGH THE OPTIONS AND PULL OUT THE START AND END DATE | |
while(@argv) | |
{ | |
$arg = shift(@argv); | |
if($arg eq "-s" || $arg eq "-S") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing start date after -s option\n"; | |
} | |
$discountStartDate = shift(@argv); | |
} | |
elsif($arg eq "-e" || $arg eq "-E") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing end date after -e option\n"; | |
} | |
$discountEndDate = shift(@argv); | |
} | |
elsif($arg eq "-v" || $arg eq "-V") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the discount amount after -v option\n"; | |
} | |
$discountAmount = shift(@argv); | |
} | |
elsif($arg eq "-f" || $arg eq "-F") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the file name after -f option\n"; | |
} | |
$fileNameForCodes = shift(@argv); | |
} | |
elsif($arg eq "-n" || $arg eq "-N") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the number of discount codes to create after -N option\n"; | |
} | |
$discountCount = shift(@argv); | |
} | |
elsif($arg eq "-c" || $arg eq "-C") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the discount code after -c option\n"; | |
} | |
$discountCode = shift(@argv); | |
} | |
elsif($arg eq "-l" || $arg eq "-L") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the discount usage limit after -l option\n"; | |
} | |
$discountUsageLimit = shift(@argv); | |
} | |
elsif($arg eq "-m" || $arg eq "-M") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the min order amount after -m option\n"; | |
} | |
$minOrderAmount = shift(@argv); | |
} | |
elsif($arg eq "-d" || $arg eq "-D") | |
{ | |
if($#argv == "-1") | |
{ | |
die "Error: Missing the discount type after -d option\n"; | |
} | |
my $tempDT = shift(@argv); | |
if(lc($tempDT) eq "p") | |
{ | |
$discountType = "percentage"; | |
} | |
else | |
{ | |
$discountType = "fixed_amount"; | |
} | |
} | |
elsif($arg eq "-o" || $arg eq "-O") | |
{ | |
$appliesOnce = "true"; | |
} | |
elsif($arg eq "-y" || $arg eq "-Y") | |
{ | |
$limitToNewCustomers = 1; | |
} | |
elsif($arg eq "-h" || $arg eq "-H") | |
{ | |
help(); | |
} | |
else | |
{ | |
die "Illegal option: $arg.\n"; | |
} | |
} | |
} | |
# | |
# HELP FUNCTION THAT OUTPUTS THE OPTIONS THAT NEED TO BE PASSED TO THE SCRIPT WHEN IT'S RUN | |
sub help | |
{ | |
print "Usage:\n", | |
" create_discount_codes.pl <OPTIONS>\n", | |
" Required:\n", | |
" -d TYPE : Discount Type: f or p (fixed or percentage) \n", | |
" -v VALUE : Amount to discount, either fixed dollars or percentage, e.g. 50.00 or 15.0\n", | |
" -s START_DATE : The first day in the period you want to pull data for. In YYYY-MM-DD format, e.g. 2015-07-01 \n", | |
" -e END_DATE : The last day in the period you want to pull data for. In YYYY-MM-DD format, e.g. 2015-07-31 \n", | |
" -c CODE : Discount code\n", | |
" -f FILENAME : file in same directory as script with codes to create on separate lines. This supercedes -c CODE\n", | |
" -l LIMIT : Number of times code can be used\n", | |
" -m MINORDERAMT : Minimum amount customer needs to spend on order to use discount\n", | |
" -n # OF DISCOUNTS : Number of discount codes to generate\n", | |
" -o : To set the code to apply once per customer\n", | |
" -y : To apply to new customers only\n", | |
" -h : See the help info \n"; | |
die "\n"; | |
} | |
# RANDOM STRING GENERATOR | |
# USE THIS TO CREATE DISCOUNT CODES IF THEY AREN'T PROVIDED VIA A FILENAME OR WHEN RUNNING THE COMMAND' | |
sub rndStr{ join'', @_[ map{ rand @_ } 1 .. shift ] } | |
# | |
# THE MAIN FUNCTION THAT LOGS INTO THE SHOPIFY ACCOUNT, CREATES THE DISCOUNT CODES, AND RECORDS THE CODES TO A FILE IN THE SAME FOLDER WHERE THE SCRIPT IS LOCATED | |
sub create_discount_codes | |
{ | |
# | |
# ARRAY OF SHOPIFY STORE LOGIN INFORMATION | |
# INCLUDE THE CREDENTIALS FOR ALL OF THE SHOPIFY STORES THAT YOU WANT TO PULL DATA FROM | |
# STORE NAME (WHATEVER YOU WANT TO CALL EACH ONE), THE STORE'S URL, THE SHOPIFY API KEY, AND THE SHOPIFY API PASSWORD | |
# | |
my @shopifyStores = | |
( | |
{ | |
#INFO FOR THE FIRST SHOPIFY STORE/ACCOUNT | |
name => 'CAN BE ANY NAME YOU WANT TO ASSIGN THE SHOPIFY STORE', # IF YOU HAVE MULTIPLE STOREFRONTS, THIS LETS YOU ASSIGN A NAME TO IDENTIFY EACH ONE. CAN BE ANY STRING VALUE THAT YOU WANT | |
url => 'URL TO THE SHOPIFY STORE', # URL SHOULD BE SOMETHING LIKE HELLOWORLD.SHOPIFY.COM | |
apiKey => 'APIKEY', # REPLACE APIKEY WITH THE ACTUAL API KEY FROM SHOPIFY | |
apiPass => 'APIPASS' # REPLACE APIPASS WITH THE ACTUAL API PASSWORD FROM SHOPIFY | |
} | |
); | |
# | |
# GO THROUGH EACH STORE DEFINED IN THE ARRAY ABOVE | |
foreach my $shopifyStore (@shopifyStores) | |
{ | |
my $storeUrl = $shopifyStore->{'url'}; | |
my $apiKey = $shopifyStore->{'apiKey'}; | |
my $apiPass = $shopifyStore->{'apiPass'}; | |
# | |
# Format the start and end dates | |
my $parser = DateTime::Format::Strptime->new(pattern => '%Y-%m-%d'); | |
my $origEnd = $discountEndDate; | |
my $origStartDate = $parser->parse_datetime($discountStartDate); | |
# | |
# Create the variable with the date the discount codes should expire if it was provided | |
if (defined $discountEndDate) | |
{ | |
my $origEndDate = $parser->parse_datetime($discountEndDate); | |
$discountEndDate = $origEndDate->strftime('%Y-%m-%d'); | |
} | |
# | |
# If you passed a file that has discount codes that you want to use in it, read the file and create each of the codes | |
if(defined $fileNameForCodes) | |
{ | |
open my $FILE, $fileNameForCodes or die $!; | |
while (my $discCode = <$FILE>) | |
{ | |
chomp($discCode); | |
print "Discount Code = $discCode\n"; | |
my %discountCodeInfo; | |
if($limitToNewCustomers) | |
{ | |
%discountCodeInfo = (discount=>{discount_type=>$discountType,code=>$discCode,starts_at=>$discountStartDate,ends_at=>$discountEndDate,usage_limit=>$discountUsageLimit, applies_once_per_customer=>$appliesOnce,applies_to_resource=>"customer_saved_search",applies_to_id=>$newCustomerGroup,value=>$discountAmount,minimum_order_amount=>$minOrderAmount}); | |
} | |
else | |
{ | |
%discountCodeInfo = (discount=>{discount_type=>$discountType,code=>$discCode,starts_at=>$discountStartDate,ends_at=>$discountEndDate,usage_limit=>$discountUsageLimit,applies_once_per_customer=>$appliesOnce,value=>$discountAmount,minimum_order_amount=>$minOrderAmount}); | |
} | |
my $json = JSON->new; | |
my $discdata = $json->encode(\%discountCodeInfo); | |
# http://user:password@www.domain.com/script.pl | |
# CREATE THE REQUEST AND PASS IT TO SHOPIFY TO CREATE THE DISCOUNT CODE | |
my $URL = "https://$apiKey:$apiPass\@$storeUrl/admin/discounts.json"; | |
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); | |
my $header = HTTP::Request->new('POST' => $URL); | |
my $request = HTTP::Request->new('POST', $URL, $header, $discdata); | |
$request->content_type('application/json'); | |
# GET THE RESPONSE CODE BACK TO CHECK IF IT WAS SUCCESSFUL | |
my $response = $ua->request($request); | |
if ($response->is_success) | |
{ | |
my $arrayref = decode_json($response->content); | |
my $headerContent = $response->header('X-Shopify-Shop-Api-Call-Limit'); | |
my @apiLimitBucket = split("/",$headerContent); | |
if($apiLimitBucket[0] > $apiLimitBucket[1] - 20) | |
{ | |
usleep(18000000); | |
} | |
} | |
else | |
{ | |
print "HTTP POST error code: ", $response->code, "\n"; | |
print "HTTP POST error message: ", $response->message, "\n"; | |
} | |
} | |
close $FILE; | |
} | |
else | |
{ | |
# | |
# CREATE THE LOG FILE NAMES WHERE THE GENERATED DISCOUNT CODES WILL BE SAVED | |
my $discountCodesFile; | |
$discountCodesFile = "Shopify_discount_codes_$timestamp\_$scriptVersion.csv"; # CSV file where the summary metrics are stored when the script finishes | |
# AND THEN OPEN THE FILE TO WRITE DATA TO IT | |
open(my $summaryFile, '>', $discountCodesFile) or die "Could not open file '$discountCodesFile' $!"; | |
# | |
# PRINT THE HEADERS FOR THE COLUMNS OF DATA TO THE OUTPUT FILES | |
print $summaryFile "Discount_Code,Amount,# of uses,NewCustomers?,Min Order Amount,ExpiresOn\n"; | |
my $i = 0; | |
for ($i; $i < $discountCount; $i++) | |
{ | |
my %discountCodeInfo; | |
if($discountCode eq "") | |
{ | |
# Generate a random code | |
$discountCode = rndStr 16, 0..9, 'a'..'z'; | |
} | |
print "$i: DiscountCode = $discountCode\n"; | |
if($limitToNewCustomers) | |
{ | |
%discountCodeInfo = (discount=>{discount_type=>$discountType,code=>$discountCode,starts_at=>$discountStartDate,ends_at=>$discountEndDate,usage_limit=>$discountUsageLimit, applies_once_per_customer=>$appliesOnce,applies_to_resource=>"customer_saved_search",applies_to_id=>$newCustomerGroup,value=>$discountAmount,minimum_order_amount=>$minOrderAmount}); | |
} | |
else | |
{ | |
%discountCodeInfo = (discount=>{discount_type=>$discountType,code=>$discountCode,starts_at=>$discountStartDate,ends_at=>$discountEndDate,usage_limit=>$discountUsageLimit,applies_once_per_customer=>$appliesOnce,value=>$discountAmount,minimum_order_amount=>$minOrderAmount}); | |
} | |
my $json = JSON->new; | |
my $discdata = $json->encode(\%discountCodeInfo); | |
# http://user:password@www.domain.com/script.pl | |
# CREATE THE REQUEST AND PASS IT TO SHOPIFY TO CREATE THE DISCOUNT CODE | |
my $URL = "https://$apiKey:$apiPass\@$storeUrl/admin/discounts.json"; | |
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); | |
my $header = HTTP::Request->new('POST' => $URL); | |
my $request = HTTP::Request->new('POST', $URL, $header, $discdata); | |
$request->content_type('application/json'); | |
# GET THE RESPONSE CODE BACK TO CHECK IF IT WAS SUCCESSFUL | |
my $response = $ua->request($request); | |
if ($response->is_success) | |
{ | |
# PRINT THE DISCOUNT CODE INFO INTO THE LOG | |
if(defined $discountUsageLimit) | |
{ | |
print $summaryFile "$discountCode, $discountAmount, $discountUsageLimit, $limitToNewCustomers, $minOrderAmount, $discountEndDate\n"; | |
} | |
else | |
{ | |
print $summaryFile "$discountCode, $discountAmount, , $limitToNewCustomers, $minOrderAmount, $discountEndDate\n"; | |
} | |
my $arrayref = decode_json($response->content); | |
my $headerContent = $response->header('X-Shopify-Shop-Api-Call-Limit'); | |
my @apiLimitBucket = split("/",$headerContent); | |
if($apiLimitBucket[0] > $apiLimitBucket[1] - 20) | |
{ | |
usleep(18000000); | |
} | |
} | |
else | |
{ | |
print "HTTP POST error code: ", $response->code, "\n"; | |
print "HTTP POST error message: ", $response->message, "\n"; | |
$i--; | |
} | |
$discountCode = ""; | |
} | |
# | |
# CLOSE THE LOG FILES THAT THE SCRIPT WAS WRITING TO | |
close $summaryFile; | |
} | |
} | |
} | |
################################################# | |
# | |
# RUN THE FUNCTIONS DEFINED ABOVE | |
# | |
parse_args(); | |
create_discount_codes(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment