Created
August 12, 2012 01:19
-
-
Save jmc734/3328652 to your computer and use it in GitHub Desktop.
Shopify Batch Discount Automation (Create, Enable/Disable, Delete)
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
<?php | |
/** | |
* Discount | |
* | |
* Create, modify, and delete Shopify discounts | |
* | |
* PHP version 5 | |
* | |
* @author Jacob McDonald <jmc734@gmail.com> | |
*/ | |
class BatchDiscount { | |
private $base; | |
private $login; | |
private $password; | |
private $curl; | |
private $authToken; | |
/** | |
* Constructor | |
* | |
* initialize Shopify session | |
* | |
* @param string $login email used to login to Shopify | |
* @param string $password password used to login to Shopify | |
* @param string $base admin panel URL (e.g. https://test-store.myshopify.com/admin) | |
* @param string $cookies filepath to store cookies in | |
* | |
* @return boolean true if successfully logged in, else, exception thrown | |
* @access public | |
*/ | |
public function __construct($login, $password, $base, $cookies) { | |
// Set properties | |
$this->login = $login; | |
$this->password = $password; | |
$this->base = $base; | |
$this->cookies = $cookies; | |
// Add login and password to post | |
$post = array( | |
'login' => $login, | |
'password' => $password | |
); | |
// Send login request to Shopify | |
$ch = $this->initCurl(); | |
curl_setopt($ch, CURLOPT_URL, $base.'/auth/login'); | |
curl_setopt($ch, CURLOPT_POST, true); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); | |
curl_exec($ch); | |
// Check if CURL returned an error | |
if(curl_errno($ch) !== 0){ | |
$exception = new Exception('Login Request Failed: '.curl_error($ch).' ('.curl_errno($ch).')'); | |
curl_close($ch); | |
throw $exception; | |
} | |
// Check if HTTP error returned | |
if(curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200){ | |
curl_close($ch); | |
throw new Exception('HTTP Error: '.curl_getinfo($ch, CURLINFO_HTTP_CODE)); | |
} | |
// Check if login was not successful | |
if(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL) !== $base){ | |
curl_close($ch); | |
throw new Exception('Login Failure: Wrong email or password'); | |
} | |
curl_close($ch); | |
return true; | |
} | |
/** | |
* Create | |
* | |
* create new discounts | |
* | |
* @param array $codes codes for the new discounts, array of strings | |
* @param string $discountType type of discount (fixed_amount, percentage, or shipping) | |
* @param int,float $value value of discount ($ or %). only for fixed_amount or percentage | |
* @param string $start activation date (YYYY-MM-DD). leave blank to activate immediately | |
* @param string $end deactivation date (YYYY-MM-DD). leave blank to keep alive until manual deactivation | |
* @param int $usageLimit number of uses allowed. pass empty string for unlimited | |
* @param string $resourceType type of resource that the discount is applied to (minimum_order_amount, custom_collection, product, customer_group). leave blank to apply to all orders | |
* @param int,float $minOrderAmount minimum order value required to apply discount (only if $resourceType is minimum_order_amount) | |
* @param string $resourceId ID of the resource the discount applies to | |
* | |
* @return string ID of new discount if discount created successfully, else, exception thrown | |
* @access public | |
*/ | |
public function create($codes, $discountType, $value, $start = '', $end = '', $usageLimit = '', $resourceType = '', $minOrderAmount = 0, $resourceId = '') { | |
$mh = curl_multi_init(); | |
$handles = array(); | |
for($i=0; $i<count($codes); $i++){ | |
$code = $codes[$i]; | |
// Check for valid Resource Type and corresponding argument (Minimum Order Amount, Resource ID) | |
switch($resourceType){ | |
case(''): break; | |
case('minimum_order_amount'): | |
if($minOrderAmount <= 0){ | |
throw new Exception('Argument Error: Must supply minimum order amount'); | |
} | |
break; | |
case('custom_collection'): | |
case('product'): | |
case('customer_group'): | |
if($resourceId === ''){ | |
throw new Exception('Argument Error: Must supply an Resource ID'); | |
} | |
break; | |
default: | |
throw new Exception('Argument Error: Invalid Resource Type supplied'); | |
} | |
// Build post string | |
$post = 'utf8=%E2%9C%93&authenticity_token='.$this->getAuthenticityToken().'&discount%5Bcode%5D='.$code.'&discount%5Bdiscount_type%5D='.$discountType.'&discount%5Bvalue%5D='.$value.'&discount%5Bapplies_to_resource%5D='.$resourceType.'&discount%5Bminimum_order_amount%5D='.$minOrderAmount.'&applies_to_product='.$resourceId.'&applies_to_collection='.$resourceId.'&applies_to_customer_group='.$resourceId.'&discount%5Bstarts_at%5D='.$start.'&discount%5Bends_at%5D='.$end.'&discount%5Busage_limit%5D='.$usageLimit.'&commit=Create%20discount&page=1'; | |
// Send request to Shopify to create Discount | |
$ch = $this->initCurl(); | |
curl_setopt($ch, CURLOPT_URL, $this->base.'/discounts'); | |
curl_setopt($ch, CURLOPT_POST, true); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); | |
// Add cURL handle to curl_multi handle | |
curl_multi_add_handle($mh,$ch); | |
// Add reference to cURL handle to array with the associated code as the key, used to get the Shopify ID later | |
$handles[$code] = &$ch; | |
} | |
// Wait for all requests to finish | |
$running = null; | |
do { | |
curl_multi_exec($mh,$running); | |
} while($running > 0); | |
// Extract new Discount ID from returns | |
// TODO: Current method of extracting ID is terrible (unreliable). Multiple requests come back with the same ID. | |
// Somehow find the current code in the response then find and extract the associated ID | |
$return = array(); | |
$match = array(); | |
foreach($handles as $code=>$ch){ | |
preg_match('@discount-([^"]*)"@', curl_multi_getcontent($ch), $match); | |
print_object(curl_multi_getcontent($ch).'<br/><br/><br/><br/>'); | |
if(count($match) == 2){ | |
$return[$code] = $match[1]; | |
} | |
curl_multi_remove_handle($mh, $ch); | |
} | |
curl_multi_close($mh); | |
return $return; | |
} | |
/** | |
* Delete | |
* | |
* delete existing discounts | |
* | |
* @param array $ids IDs of the discounts to delete | |
* | |
* @return array IDs of discounts that could not be modified | |
* @access public | |
*/ | |
public function delete($ids){ | |
return $this->modify($ids, 'authenticity_token='.$this->getAuthenticityToken().'&_method=delete'); | |
} | |
/** | |
* Disable | |
* | |
* disable existing discounts | |
* | |
* @param array $ids IDs of the discounts to disable | |
* | |
* @return array IDs of discounts that could not be modified | |
* @access public | |
*/ | |
public function disable($ids){ | |
return $this->modify($ids, 'authenticity_token='.$this->getAuthenticityToken(), '/disable'); | |
} | |
/** | |
* Enable | |
* | |
* enable existing discounts | |
* | |
* @param array $ids IDs of the discounts to disable | |
* | |
* @return array IDs of discounts that could not be modified | |
* @access public | |
*/ | |
public function enable($ids){ | |
return $this->modify($ids, 'authenticity_token='.$this->getAuthenticityToken(), '/enable'); | |
} | |
/** | |
* Destructor | |
* | |
* kill Shopify session | |
* | |
* @access public | |
*/ | |
public function __destruct() { | |
$ch = $this->initCurl(); | |
curl_setopt($ch, CURLOPT_URL, $this->base.'/auth/logout'); | |
curl_exec($ch); | |
curl_close($ch); | |
} | |
/** | |
* Modify | |
* | |
* modify existing discounts | |
* | |
* @param string $uri string to append to URL | |
* @param string $post post string to send in request | |
* | |
* @return string response from Shopify if successful, else, exception thrown | |
* @access private | |
*/ | |
private function modify($ids, $post, $append = ''){ | |
$mh = curl_multi_init(); | |
$handles = array(); | |
foreach($ids as $id){ | |
$ch = $this->initCurl(); | |
curl_setopt($ch, CURLOPT_URL, $this->base.'/discounts/'.$id.$append); | |
curl_setopt($ch, CURLOPT_POST, true); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); | |
curl_multi_add_handle($mh,$ch); | |
$handles[$id] = $ch; | |
} | |
$running = null; | |
do { | |
curl_multi_exec($mh,$running); | |
} while($running > 0); | |
$failed = array(); | |
foreach($handles as $id=>$ch){ | |
if(!preg_match('@Messenger.notice\("([^"]*)"@', curl_multi_getcontent($ch))){ | |
$failed[] = $id; | |
} | |
curl_multi_remove_handle($mh, $ch); | |
} | |
curl_multi_close($mh); | |
return $failed; | |
} | |
/** | |
* Initialize cURL | |
* | |
* create a new cURL handle with some presets | |
* | |
* @return cURL handle initialized cURL handle | |
* @access private | |
*/ | |
private function initCurl() { | |
if(empty($this->curl)){ | |
$options = array( | |
CURLOPT_SSL_VERIFYPEER => false, | |
CURLOPT_COOKIEJAR => $this->cookies, | |
CURLOPT_COOKIEFILE => $this->cookies, | |
CURLOPT_FOLLOWLOCATION => true, | |
CURLOPT_RETURNTRANSFER => true | |
); | |
$this->curl = curl_init(); | |
curl_setopt_array($this->curl, $options); | |
} | |
return curl_copy_handle($this->curl); | |
} | |
/** | |
* Get Authenticity Token | |
* | |
* get token required in post to create/modify discounts | |
* | |
* @return string authenticity token | |
* @access private | |
*/ | |
private function getAuthenticityToken() { | |
if(empty($this->authToken)){ | |
// Load Promotion page | |
$ch = $this->initCurl(); | |
curl_setopt($ch, CURLOPT_URL, $this->base.'/marketing'); | |
curl_setopt($ch, CURLOPT_HEADER, false); | |
$response = curl_exec($ch); | |
curl_close($ch); | |
// Extract Authenticity Token from the response HTML | |
$match = array(); | |
preg_match('@name="authenticity_token" type="hidden" value="([^"]*)"@', $response, $match); | |
if(count($match)<2){ | |
throw new Exception('Parse Error: Could not extract Authenticity Token'); | |
} | |
// Encode the token for passing in the post | |
$this->authToken = urlencode($match[1]); | |
} | |
return $this->authToken; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note: You may want to increase your PHP maximum execution time limit. Making all the calls to Shopify can take a while.