From eeacfc62fafdcc990b431b193c6cb475c00f7e1c Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 17:52:23 -0700 Subject: [PATCH 01/84] added additional endpoints --- .DS_Store | Bin 0 -> 6148 bytes lib/.DS_Store | Bin 0 -> 10244 bytes lib/ApplicationCharge.php | 7 +-- lib/AuthHelper.php | 19 +------- lib/Checkout.php | 53 +++++++++++++++++++++++ lib/CollectionListing.php | 32 ++++++++++++++ lib/CurlRequest.php | 33 +++++--------- lib/Currency.php | 38 ---------------- lib/Customer.php | 22 ++-------- lib/CustomerAddress.php | 10 +++-- lib/CustomerOrder.php | 37 ++++++++++++++++ lib/DiscountCode.php | 15 ------- lib/DraftOrder.php | 25 +++++++---- lib/GraphQL.php | 70 ------------------------------ lib/HttpRequestGraphQL.php | 68 ----------------------------- lib/HttpRequestJson.php | 2 +- lib/Location.php | 7 --- lib/PriceRule.php | 2 +- lib/ProductVariant.php | 5 --- lib/ShippingRate.php | 19 ++++++++ lib/ShopifyResource.php | 69 ++++++++++++++++++++++++++--- lib/ShopifySDK.php | 81 ++++++++++++++--------------------- tests/CurrencyTest.php | 15 ------- tests/ProductListingTest.php | 15 ------- tests/TestResource.php | 6 +-- 25 files changed, 279 insertions(+), 371 deletions(-) create mode 100644 .DS_Store create mode 100644 lib/.DS_Store create mode 100644 lib/Checkout.php create mode 100644 lib/CollectionListing.php delete mode 100644 lib/Currency.php create mode 100644 lib/CustomerOrder.php delete mode 100644 lib/GraphQL.php delete mode 100644 lib/HttpRequestGraphQL.php create mode 100644 lib/ShippingRate.php delete mode 100644 tests/CurrencyTest.php delete mode 100644 tests/ProductListingTest.php diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8f4ee880222ab9379103e9b7adc3d2e83b933c25 GIT binary patch literal 6148 zcmeHK%Z?I36ukwE(|{9Mn2hdvW8#7kIvGhOW)fszL88XkMi**kx}gKDO&6h?VP+7+ z#(yyS1^$W~mwt#Jpr;;VpcxZa#%SH7>eS<&+tlgXRTL4i*0k>sRfxz!5m+dpy2Ql1 zC>xg3BUgb;#OOx6=SSQ}Uj#H}6fg?>-3o|rw?H)-P)KR!`@M^~+uxt9(eU)Z{`ZCNF&v{QQN4*ASYy>yzi zyvcLXd*r1ud9{p^4{?jm+9aXL$@;LRvIG9Ls*bm_H;#nB?`K-mKaX*o9Ts2`? zMXPvJxjCOV9H(mU?bjDod+yw6>{ac3r?FTRt=(JBLHBrcc0PZzc>4isfidjW(ynQ| zfRAWs6;9kq9PxO75#hZ}_o+*V)S-vuQYWL)7kI4ZNg3~Lh%!ZvAgl21PB-6q{vg9D z(qqKGM{Rlltbx&Ae-4R!e7};9TZkPXD^f}_o+1lZJ<&C`p@7ZW&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW $value) { - $paramStrings[] = "$key=$value"; - } - return join('&', $paramStrings); - } - /** * Verify if the request is made from shopify using hmac hash value * @@ -78,7 +61,7 @@ public static function verifyShopifyRequest() unset($data['signature']); } //Create data string for the remaining url parameters - $dataString = self::buildQueryString($data); + $dataString = http_build_query($data); $realHmac = hash_hmac('sha256', $dataString, $sharedSecret); diff --git a/lib/Checkout.php b/lib/Checkout.php new file mode 100644 index 0000000..401882a --- /dev/null +++ b/lib/Checkout.php @@ -0,0 +1,53 @@ + + * Created at 8/19/16 2:59 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/sales-channels/checkout Shopify API Reference for Checkout + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * Order -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShippingRate $ShippingRate + * + * @method ShippingRate ShippingRate(integer $id = null) + * + + * -------------------------------------------------------------------------- + * Checkout -> Custom actions + * -------------------------------------------------------------------------- + * @method array complete() Completes Checkout without payment + * + */ +class Checkout extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'checkout'; + + /** + * @inheritDoc + */ + public $countEnabled = false; + + /** + * @inheritDoc + */ + protected $childResource = array ( + 'ShippingRate', + ); + /** + * @inheritDoc + */ + protected $customPostActions = array( + 'complete', + 'payments' + ); +} diff --git a/lib/CollectionListing.php b/lib/CollectionListing.php new file mode 100644 index 0000000..bc80f84 --- /dev/null +++ b/lib/CollectionListing.php @@ -0,0 +1,32 @@ + + * Created at: 6/2/18 1:38 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/sales_channels/collectionlisting + */ + +namespace PHPShopify; +/** + * -------------------------------------------------------------------------- + * CollectionListing -> Custom actions + * -------------------------------------------------------------------------- + * @method array productIds() Sets the address as default for the customer + */ + +class CollectionListing extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'collection_listing'; + + /** + * @inheritDoc + */ + protected $customGetActions = array( + 'product_ids' => 'productIds', + ); + +} diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index 87014b5..eb59477 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -9,7 +9,6 @@ use PHPShopify\Exception\CurlException; -use PHPShopify\Exception\ResourceRateLimitException; /* |-------------------------------------------------------------------------- @@ -48,9 +47,6 @@ protected static function init($url, $httpHeaders = array()) //Return the transfer as a string curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, true); - curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify'); - $headers = array(); foreach ($httpHeaders as $key => $value) { $headers[] = "$key: $value"; @@ -145,24 +141,15 @@ public static function delete($url, $httpHeaders = array()) protected static function processRequest($ch) { # Check for 429 leaky bucket error - while (1) { - $output = curl_exec($ch); - $response = new CurlResponse($output); - - self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if (self::$lastHttpCode != 429) { - break; - } - - $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit'), 2); - - if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { - throw new ResourceRateLimitException($response->getBody()); - } - - usleep(500000); + while(1) { + $output = curl_exec($ch); + self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if(self::$lastHttpCode != 429) { + break; + } + usleep(500000); } - + if (curl_errno($ch)) { throw new Exception\CurlException(curl_errno($ch) . ' : ' . curl_error($ch)); } @@ -170,7 +157,7 @@ protected static function processRequest($ch) // close curl resource to free up system resources curl_close($ch); - return $response->getBody(); + return $output; } -} +} \ No newline at end of file diff --git a/lib/Currency.php b/lib/Currency.php deleted file mode 100644 index a93a68b..0000000 --- a/lib/Currency.php +++ /dev/null @@ -1,38 +0,0 @@ - Custom actions * -------------------------------------------------------------------------- @@ -42,22 +44,6 @@ class Customer extends ShopifyResource protected $childResource = array( 'CustomerAddress' => 'Address', 'Metafield', - 'Order' + 'CustomerOrder' => 'Order', ); - - /** - * Sends an account invite to a customer. - * - * @param array $customer_invite Customized invite data - * - * @return array - */ - public function send_invite($customer_invite = array()) - { - if (empty ( $customer_invite ) ) $customer_invite = new \stdClass(); - $url = $this->generateUrl(array(), 'send_invite'); - $dataArray = $this->wrapData($customer_invite, 'customer_invite'); - - return $this->post($dataArray, $url, false); - } -} \ No newline at end of file +} diff --git a/lib/CustomerAddress.php b/lib/CustomerAddress.php index 32c60c1..6cfdfc2 100644 --- a/lib/CustomerAddress.php +++ b/lib/CustomerAddress.php @@ -15,7 +15,7 @@ * CustomerAddress -> Custom actions * -------------------------------------------------------------------------- * @method array makeDefault() Sets the address as default for the customer - * + * @method array set() update multiple */ class CustomerAddress extends ShopifyResource { @@ -29,6 +29,7 @@ class CustomerAddress extends ShopifyResource */ protected $customPutActions = array( 'default' => 'makeDefault', + 'set', ); /** @@ -48,10 +49,11 @@ protected function pluralizeKey() * @return array */ //TODO Issue (Getting Error from API) : Internal server error - public function set($params) + /*public function set($params) { + $url = $this->generateUrl($params, 'set'); return $this->put(array(), $url); - } -} \ No newline at end of file + }*/ +} diff --git a/lib/CustomerOrder.php b/lib/CustomerOrder.php new file mode 100644 index 0000000..3735dd8 --- /dev/null +++ b/lib/CustomerOrder.php @@ -0,0 +1,37 @@ + + * Created at 8/19/16 12:07 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/customeraddress Shopify API Reference for CustomerAddress + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * CustomerOrder -> Custom actions + * -------------------------------------------------------------------------- + * @method array get() Sets the address as default for the customer + * + */ +class CustomerOrder extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'order'; + + + /** + * @inheritDoc + */ + protected function pluralizeKey() + { + return 'orders'; + } + + +} diff --git a/lib/DiscountCode.php b/lib/DiscountCode.php index e13889e..7bd6493 100644 --- a/lib/DiscountCode.php +++ b/lib/DiscountCode.php @@ -10,25 +10,10 @@ namespace PHPShopify; -/** - * -------------------------------------------------------------------------- - * DiscountCode -> Custom actions - * -------------------------------------------------------------------------- - * @method array lookup() Retrieves the location of a discount code. - * - */ - class DiscountCode extends ShopifyResource { /** * @inheritDoc */ protected $resourceKey = 'discount_code'; - - /** - * @inheritDoc - */ - protected $customGetActions = array( - 'lookup', - ); } \ No newline at end of file diff --git a/lib/DraftOrder.php b/lib/DraftOrder.php index 902cee5..aaeba8a 100644 --- a/lib/DraftOrder.php +++ b/lib/DraftOrder.php @@ -1,8 +1,8 @@ - * Created at 8/14/19 18:28 PM UTC+02:00 + * @author Tareq Mahmood + * Created at 8/19/16 2:59 PM UTC+06:00 * * @see https://help.shopify.com/api/reference/draftorder Shopify API Reference for DraftOrder */ @@ -12,11 +12,19 @@ /** + * -------------------------------------------------------------------------- + * DraftOrder -> Child Resources + * -------------------------------------------------------------------------- + * @property-read Metafield $Metafield + * + + * @method Metafield Metafield(integer $id = null) + * * -------------------------------------------------------------------------- * DraftOrder -> Custom actions * -------------------------------------------------------------------------- - * @method array send_invoice() Send the invoice for a DraftOrder - * @method array complete() Complete a DraftOrder + * @method array sendInvoice() Sends an invoice for the order + * @method array complete() Completes Draft Order * */ class DraftOrder extends ShopifyResource @@ -29,14 +37,15 @@ class DraftOrder extends ShopifyResource /** * @inheritDoc */ - protected $customPostActions = array( - 'send_invoice', + protected $childResource = array ( + 'Metafield', ); /** * @inheritDoc */ - protected $customPutActions = array( + protected $customPostActions = array( + 'send_invoice' => 'sendInvoice', 'complete', ); -} \ No newline at end of file +} diff --git a/lib/GraphQL.php b/lib/GraphQL.php deleted file mode 100644 index e6fa430..0000000 --- a/lib/GraphQL.php +++ /dev/null @@ -1,70 +0,0 @@ -generateUrl(); - - $response = HttpRequestGraphQL::post($url, $graphQL, $this->httpHeaders); - - return $this->processResponse($response); - } - - /** - * @inheritdoc - */ - public function get($urlParams = array(), $url = null, $dataKey = null) - { - throw new SdkException("Only POST method is allowed for GraphQL!"); - } - - /** - * @inheritdoc - */ - public function put($dataArray, $url = null, $wrapData = true) - { - throw new SdkException("Only POST method is allowed for GraphQL!"); - } - - /** - * @inheritdoc - */ - public function delete($urlParams = array(), $url = null) - { - throw new SdkException("Only POST method is allowed for GraphQL!"); - } -} \ No newline at end of file diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php deleted file mode 100644 index b4595a8..0000000 --- a/lib/HttpRequestGraphQL.php +++ /dev/null @@ -1,68 +0,0 @@ - Child Resources * -------------------------------------------------------------------------- diff --git a/lib/ProductVariant.php b/lib/ProductVariant.php index fd34066..1e88fd4 100644 --- a/lib/ProductVariant.php +++ b/lib/ProductVariant.php @@ -26,11 +26,6 @@ class ProductVariant extends ShopifyResource */ protected $resourceKey = 'variant'; - /** - * @inheritDoc - */ - public $searchEnabled = true; - /** * @inheritDoc */ diff --git a/lib/ShippingRate.php b/lib/ShippingRate.php new file mode 100644 index 0000000..e5de310 --- /dev/null +++ b/lib/ShippingRate.php @@ -0,0 +1,19 @@ + + * Created at 8/19/16 7:27 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/shipping_rates Shopify API Reference for ShippingRate + */ + +namespace PHPShopify; + + +class ShippingRate extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'shipping_rate'; +} diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index e8f6542..c01ab1f 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -119,7 +119,7 @@ public function __construct($id = null, $parentResourceUrl = '') $config = ShopifySDK::$config; - $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); + $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['AdminUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; @@ -293,6 +293,20 @@ public function generateUrl($urlParams = array(), $customAction = null) return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } + /** + * Generate the custom url for api request based on the params and custom action (if any) + * + * @param array $urlParams + * @param string $customAction + * + * @return string + */ + public function addShopUrl($urlParams = array(), $url) + { + + return $this->resourceUrl . ($url ? "/$url" : '') . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); + } + /** * Generate a HTTP GET request and return results as an array * @@ -392,7 +406,13 @@ public function post($dataArray, $url = null, $wrapData = true) public function put($dataArray, $url = null, $wrapData = true) { + /*if (!$url) { + $url = $this->generateUrl(); + } else { + $url = $this->addShopUrl($dataArray, $url); + }*/ if (!$url) $url = $this->generateUrl(); + if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); @@ -446,16 +466,38 @@ protected function wrapData($dataArray, $dataKey = null) */ protected function castString($array) { - if ( ! is_array($array)) return (string) $array; + if (is_string($array)) return $array; $string = ''; $i = 0; foreach ($array as $key => $val) { + + if ($key === "line_items") { + $line_id = array_keys($val)[0]; + $val = reset($val); + + if (isset($val['quantity']) && isset($val['quantity'][0]) && isset($val['quantity'][0]['message'])) { + $string = "Line item {$line_id} {$val['quantity'][0]['message']}"; + return $string; + } + + + } //Add values separated by comma //prepend the key string, if it's an associative key //Check if the value itself is another array to be converted to string - $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; - $i++; + if (is_array($val) || is_object($val)) { + $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; + $i++; + } else if (is_array($key) || is_object($key)) { + $string .= ($i === $key ? '' : "$key - ") . $this->castString($key) . ', '; + $i++; + } else if (!is_array($val)) { + $string .= $val . ", "; + } else if (!is_array($key)) { + $string .= $key . " - "; + } + } //Remove trailing comma and space @@ -491,9 +533,24 @@ public function processResponse($responseArray, $dataKey = null) } if (isset($responseArray['errors'])) { - $message = $this->castString($responseArray['errors']); - throw new ApiException($message); + $message = $responseArray; + + if (is_array($message['errors'])) { + foreach ($message['errors'] as $key => $error) { + if (is_array($message['errors'][$key])) { + + throw new ApiException($this->castString($message['errors'])); + } else { + throw new ApiException($key . " " . $error); + } + + } + + } else { + throw new ApiException($responseArray['errors']); + } + } if ($dataKey && isset($responseArray[$dataKey])) { diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index b8d00f0..f985d65 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -60,9 +60,6 @@ | //Get variants of a product (using Child resource) | $products = $shopify->Product($productID)->Variant->get(); | -| //GraphQL -| $data = $shopify->GraphQL->post($graphQL); -| */ use PHPShopify\Exception\SdkException; @@ -73,14 +70,10 @@ * @property-read Collect $Collect * @property-read Comment $Comment * @property-read Country $Country - * @property-read Currency $Currency * @property-read CustomCollection $CustomCollection * @property-read Customer $Customer * @property-read CustomerSavedSearch $CustomerSavedSearch * @property-read Discount $Discount - * @property-read DiscountCode $DiscountCode - * @property-read DraftOrder $DraftOrder - * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard @@ -90,10 +83,13 @@ * @property-read Metafield $Metafield * @property-read Multipass $Multipass * @property-read Order $Order + * @property-read DraftOrder $DraftOrder + * @property-read Checkout $Checkout * @property-read Page $Page * @property-read Policy $Policy * @property-read Product $Product * @property-read ProductListing $ProductListing + * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect @@ -104,7 +100,6 @@ * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook - * @property-read GraphQL $GraphQL * * @method AbandonedCheckout AbandonedCheckout(integer $id = null) * @method Blog Blog(integer $id = null) @@ -112,14 +107,10 @@ * @method Collect Collect(integer $id = null) * @method Comment Comment(integer $id = null) * @method Country Country(integer $id = null) - * @method Currency Currency(integer $id = null) * @method CustomCollection CustomCollection(integer $id = null) * @method Customer Customer(integer $id = null) * @method CustomerSavedSearch CustomerSavedSearch(integer $id = null) * @method Discount Discount(integer $id = null) - * @method DraftOrder DraftOrder(integer $id = null) - * @method DiscountCode DiscountCode(integer $id = null) - * @method PriceRule PriceRule(integer $id = null) * @method Event Event(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) * @method GiftCard GiftCard(integer $id = null) @@ -129,11 +120,14 @@ * @method Metafield Metafield(integer $id = null) * @method Multipass Multipass(integer $id = null) * @method Order Order(integer $id = null) + * @method DraftOrder DraftOrder(integer $id = null) + * @method Checkout Checkout(integer $id = null) * @method Page Page(integer $id = null) * @method Policy Policy(integer $id = null) * @method Product Product(integer $id = null) - * @method ProductListing ProductListing(integer $id = null) * @method ProductVariant ProductVariant(integer $id = null) + * @method ProductListing ProductListing(integer $id = null) + * @method CollectionListing CollectionListing(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) @@ -143,10 +137,26 @@ * @method Theme Theme(int $id = null) * @method User User(integer $id = null) * @method Webhook Webhook(integer $id = null) - * @method GraphQL GraphQL() */ class ShopifySDK { + /** + * @var float microtime of last api call + */ + public static $microtimeOfLastApiCall; + + /** + * @var float Minimum gap in seconds to maintain between 2 api calls + */ + public static $timeAllowedForEachApiCall = .01; + + /** + * Shop / API configurations + * + * @var array + */ + public static $config = array(); + /** * List of available resources which can be called from this client * @@ -160,13 +170,10 @@ class ShopifySDK 'Collect', 'Comment', 'Country', - 'Currency', 'CustomCollection', 'Customer', 'CustomerSavedSearch', 'Discount', - 'DiscountCode', - 'DraftOrder', 'Event', 'FulfillmentService', 'GiftCard', @@ -176,11 +183,14 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', + 'DraftOrder', + 'Checkout', 'Page', 'Policy', 'Product', - 'ProductListing', 'ProductVariant', + 'ProductListing', + 'CollectionListing', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -192,26 +202,6 @@ class ShopifySDK 'Theme', 'User', 'Webhook', - 'GraphQL' - ); - - /** - * @var float microtime of last api call - */ - public static $microtimeOfLastApiCall; - - /** - * @var float Minimum gap in seconds to maintain between 2 api calls - */ - public static $timeAllowedForEachApiCall = .5; - - /** - * Shop / API configurations - * - * @var array - */ - public static $config = array( - 'ApiVersion' => '2019-04' ); /** @@ -232,6 +222,7 @@ class ShopifySDK 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', + 'ShippingRate' => 'Checkout', 'UsageCharge' => 'RecurringApplicationCharge', ); @@ -245,7 +236,8 @@ class ShopifySDK public function __construct($config = array()) { if(!empty($config)) { - ShopifySDK::config($config); + ShopifySDK::$config = $config; + ShopifySDK::setAdminUrl(); } } @@ -334,7 +326,6 @@ public static function setAdminUrl() //Remove https:// and trailing slash (if provided) $shopUrl = preg_replace('#^https?://|/$#', '', $shopUrl); - $apiVersion = self::$config['ApiVersion']; if(isset(self::$config['ApiKey']) && isset(self::$config['Password'])) { $apiKey = self::$config['ApiKey']; @@ -345,7 +336,6 @@ public static function setAdminUrl() } self::$config['AdminUrl'] = $adminUrl; - self::$config['ApiUrl'] = $adminUrl . "api/$apiVersion/"; return $adminUrl; } @@ -359,15 +349,6 @@ public static function getAdminUrl() { return self::$config['AdminUrl']; } - /** - * Get the api url of the configured shop - * - * @return string - */ - public static function getApiUrl() { - return self::$config['ApiUrl']; - } - /** * Maintain maximum 2 calls per second to the API * diff --git a/tests/CurrencyTest.php b/tests/CurrencyTest.php deleted file mode 100644 index 70ac526..0000000 --- a/tests/CurrencyTest.php +++ /dev/null @@ -1,15 +0,0 @@ - getenv('SHOPIFY_SHOP_URL'), //Your shop URL - 'ApiKey' => getenv('SHOPIFY_API_KEY'), //Your Private API Key - 'Password' => getenv('SHOPIFY_API_PASSWORD'), //Your Private API Password + 'ShopUrl' => 'phpclassic.myshopify.com', + 'ApiKey' => '81781200c08b31208031f983ab930f2a', + 'Password' => '5260904f8293bce93ddd4d65c535faa4', ); self::$shopify = ShopifySDK::config($config); From 882a18a62cc925aa72954012641ab07cd11af1bf Mon Sep 17 00:00:00 2001 From: TuunStudio <55007058+TuunStudio@users.noreply.github.com> Date: Fri, 6 Sep 2019 18:49:12 -0700 Subject: [PATCH 02/84] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 8f4ee880222ab9379103e9b7adc3d2e83b933c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%Z?I36ukwE(|{9Mn2hdvW8#7kIvGhOW)fszL88XkMi**kx}gKDO&6h?VP+7+ z#(yyS1^$W~mwt#Jpr;;VpcxZa#%SH7>eS<&+tlgXRTL4i*0k>sRfxz!5m+dpy2Ql1 zC>xg3BUgb;#OOx6=SSQ}Uj#H}6fg?>-3o|rw?H)-P)KR!`@M^~+uxt9(eU)Z{`ZCNF&v{QQN4*ASYy>yzi zyvcLXd*r1ud9{p^4{?jm+9aXL$@;LRvIG9Ls*bm_H;#nB?`K-mKaX*o9Ts2`? zMXPvJxjCOV9H(mU?bjDod+yw6>{ac3r?FTRt=(JBLHBrcc0PZzc>4isfidjW(ynQ| zfRAWs6;9kq9PxO75#hZ}_o+*V)S-vuQYWL)7kI4ZNg3~Lh%!ZvAgl21PB-6q{vg9D z(qqKGM{Rlltbx&Ae-4R!e7};9TZkPXD^f}_o+1lZJ<&C`p@7Z Date: Fri, 6 Sep 2019 18:49:26 -0700 Subject: [PATCH 03/84] Delete .DS_Store --- lib/.DS_Store | Bin 10244 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/.DS_Store diff --git a/lib/.DS_Store b/lib/.DS_Store deleted file mode 100644 index 4d34aed146a9b61f69f4ad5dd618a4cdd197aa41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHM-EJF26h0ffuuh!BO#+1*W&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW Date: Fri, 6 Sep 2019 18:52:25 -0700 Subject: [PATCH 04/84] added checkout endpoints --- .DS_Store | Bin 0 -> 8196 bytes lib/.DS_Store | Bin 0 -> 10244 bytes lib/Checkout.php | 53 ++++++++++++++++++++++++++++++++++++++ lib/CollectionListing.php | 32 +++++++++++++++++++++++ lib/CustomerOrder.php | 37 ++++++++++++++++++++++++++ lib/ShippingRate.php | 19 ++++++++++++++ 6 files changed, 141 insertions(+) create mode 100644 .DS_Store create mode 100644 lib/.DS_Store create mode 100644 lib/Checkout.php create mode 100644 lib/CollectionListing.php create mode 100644 lib/CustomerOrder.php create mode 100644 lib/ShippingRate.php diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7a1e21648e4703c021d76dbbc5d50771d2110c96 GIT binary patch literal 8196 zcmeHMUr!TR6h9a0GA)Y?3JVxbCfk^pNDORPcre6TAlkS}rGy31tuyVVj7;xsW~Lyp zn7;S{_HE;4KY*WM6Q4KxvX4F)KY}Jc`K0ImDVC`w`(%uJPjc@$_nh-P)BB@yr)K~F zl5bY00LB2o!XdQXkJCp)#Kq{69_=}anjjtu0k3!g_i$}@WgCZKz%XDKFbo(53XIfmkfyW!NDPPPV1J^^60=xwg9M8=$3-E{OOO{5f0Qjty@Z? z2ZbmoY6T^_#UQ$KAPSE0oX&44t>8f9m9dV#GSM9h(TxXjWIB+X(q=Xc7zVl-5WRZ} zEFG_K_@>XYGFE0FbFZ|;cT2GwH>128ZirRUjki2) zuW%k5*{-+h)7`Q?JkMF|`izDW>{ltK-2K^SRlb|&O;(L$ostSzR@zFJ?eT+y=^th$ zoF6Br4=0?1soAO731?<@=I}6Wjh>&HE8MT|?H~Ml_~;4l1%>DX76#S&X#N8&&4ORq z(fcFM+WUKH+wC2uGyUHVoE)@=PMsba9vT_>Zggy9{LGoNW&5<}R<>)Q_^k6h;9)*! z(v4c^S4$1I;MqRMx*LWrtI_=4y}w~OBAF(0f;-}yLCt+r77%&WcMGTPCLTMq78*2YdNbXJa+76EUIE0A2meLY} mlWfOP%XS>~(LW5)b}&_7a$2{PMh}X45g=(WlVRYqGVmV2K~{VK literal 0 HcmV?d00001 diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4d34aed146a9b61f69f4ad5dd618a4cdd197aa41 GIT binary patch literal 10244 zcmeHM-EJF26h0ffuuh!BO#+1*W&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW + * Created at 8/19/16 2:59 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/sales-channels/checkout Shopify API Reference for Checkout + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * Order -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShippingRate $ShippingRate + * + * @method ShippingRate ShippingRate(integer $id = null) + * + + * -------------------------------------------------------------------------- + * Checkout -> Custom actions + * -------------------------------------------------------------------------- + * @method array complete() Completes Checkout without payment + * + */ +class Checkout extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'checkout'; + + /** + * @inheritDoc + */ + public $countEnabled = false; + + /** + * @inheritDoc + */ + protected $childResource = array ( + 'ShippingRate', + ); + /** + * @inheritDoc + */ + protected $customPostActions = array( + 'complete', + 'payments' + ); +} diff --git a/lib/CollectionListing.php b/lib/CollectionListing.php new file mode 100644 index 0000000..bc80f84 --- /dev/null +++ b/lib/CollectionListing.php @@ -0,0 +1,32 @@ + + * Created at: 6/2/18 1:38 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/sales_channels/collectionlisting + */ + +namespace PHPShopify; +/** + * -------------------------------------------------------------------------- + * CollectionListing -> Custom actions + * -------------------------------------------------------------------------- + * @method array productIds() Sets the address as default for the customer + */ + +class CollectionListing extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'collection_listing'; + + /** + * @inheritDoc + */ + protected $customGetActions = array( + 'product_ids' => 'productIds', + ); + +} diff --git a/lib/CustomerOrder.php b/lib/CustomerOrder.php new file mode 100644 index 0000000..3735dd8 --- /dev/null +++ b/lib/CustomerOrder.php @@ -0,0 +1,37 @@ + + * Created at 8/19/16 12:07 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/customeraddress Shopify API Reference for CustomerAddress + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * CustomerOrder -> Custom actions + * -------------------------------------------------------------------------- + * @method array get() Sets the address as default for the customer + * + */ +class CustomerOrder extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'order'; + + + /** + * @inheritDoc + */ + protected function pluralizeKey() + { + return 'orders'; + } + + +} diff --git a/lib/ShippingRate.php b/lib/ShippingRate.php new file mode 100644 index 0000000..e5de310 --- /dev/null +++ b/lib/ShippingRate.php @@ -0,0 +1,19 @@ + + * Created at 8/19/16 7:27 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/shipping_rates Shopify API Reference for ShippingRate + */ + +namespace PHPShopify; + + +class ShippingRate extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'shipping_rate'; +} From 0db069b2251e21e8797ff1ed9aefe019361dee4e Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 18:52:56 -0700 Subject: [PATCH 05/84] added checkout endpoints --- .DS_Store | Bin 8196 -> 8196 bytes .gitignore | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.DS_Store b/.DS_Store index 7a1e21648e4703c021d76dbbc5d50771d2110c96..e4db4f5337e95f314e5e1b05027f42f576948c52 100644 GIT binary patch delta 250 zcmZp1XmQx^UXa7c&`?Lg*w|w7cfkW{`o>P>L}c$Y5s3VaQ}iDhn>k%gN76 ZXJD8tCvtT2Lm6e}i48oP*(Lt60{}mDJZAs^ delta 242 zcmZp1XmQx^UXa7Y$V^AU)WBr&cfkHm6KE1Apk4_6l2h4aAfdh2xG`*sA8DNFqdH^!)}HX40jpc zGyG&^XOv`AV$^0dVYFqmXY^nUWDH^qVT@#qVvL69XJUk!BMYUW6k`gI!OW1ukjXHa ML*&Bd$0CZ{07SSqG5`Po diff --git a/.gitignore b/.gitignore index 432b975..8f721c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor /nbproject/private/ -.idea/ \ No newline at end of file +.idea/ +.DS_Store \ No newline at end of file From da955757e61dd03b35a61fb2259b2ae6044797fb Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 19:00:23 -0700 Subject: [PATCH 06/84] added sales channel sdk endpoints --- lib/ApplicationCharge.php | 7 +++- lib/AuthHelper.php | 19 ++++++++- lib/CurlRequest.php | 33 ++++++++++----- lib/Currency.php | 38 +++++++++++++++++ lib/Customer.php | 22 ++++++++-- lib/CustomerAddress.php | 10 ++--- lib/DiscountCode.php | 15 +++++++ lib/DraftOrder.php | 25 ++++------- lib/GraphQL.php | 70 +++++++++++++++++++++++++++++++ lib/HttpRequestGraphQL.php | 68 ++++++++++++++++++++++++++++++ lib/HttpRequestJson.php | 2 +- lib/Location.php | 7 ++++ lib/PriceRule.php | 2 +- lib/ProductVariant.php | 5 +++ lib/ShopifyResource.php | 69 +++--------------------------- lib/ShopifySDK.php | 81 ++++++++++++++++++++++-------------- tests/CurrencyTest.php | 15 +++++++ tests/ProductListingTest.php | 15 +++++++ tests/TestResource.php | 6 +-- 19 files changed, 371 insertions(+), 138 deletions(-) create mode 100644 lib/Currency.php create mode 100644 lib/GraphQL.php create mode 100644 lib/HttpRequestGraphQL.php create mode 100644 tests/CurrencyTest.php create mode 100644 tests/ProductListingTest.php diff --git a/lib/ApplicationCharge.php b/lib/ApplicationCharge.php index 2e55b77..b557911 100644 --- a/lib/ApplicationCharge.php +++ b/lib/ApplicationCharge.php @@ -21,4 +21,9 @@ class ApplicationCharge extends ShopifyResource * @inheritDoc */ public $countEnabled = false; -} \ No newline at end of file + + // To activate ApplicationCharge + protected $customPostActions = array( + 'activate', + ); +} diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index 3a6ff27..e879964 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -32,6 +32,23 @@ public static function getCurrentUrl() return "$protocol://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; } + /** + * Build a query string from a data array + * This is a replacement for http_build_query because that returns an url-encoded string. + * + * @param array $data Data array + * + * @return array + */ + public static function buildQueryString($data) + { + $paramStrings = []; + foreach ($data as $key => $value) { + $paramStrings[] = "$key=$value"; + } + return join('&', $paramStrings); + } + /** * Verify if the request is made from shopify using hmac hash value * @@ -61,7 +78,7 @@ public static function verifyShopifyRequest() unset($data['signature']); } //Create data string for the remaining url parameters - $dataString = http_build_query($data); + $dataString = self::buildQueryString($data); $realHmac = hash_hmac('sha256', $dataString, $sharedSecret); diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index eb59477..87014b5 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -9,6 +9,7 @@ use PHPShopify\Exception\CurlException; +use PHPShopify\Exception\ResourceRateLimitException; /* |-------------------------------------------------------------------------- @@ -47,6 +48,9 @@ protected static function init($url, $httpHeaders = array()) //Return the transfer as a string curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify'); + $headers = array(); foreach ($httpHeaders as $key => $value) { $headers[] = "$key: $value"; @@ -141,15 +145,24 @@ public static function delete($url, $httpHeaders = array()) protected static function processRequest($ch) { # Check for 429 leaky bucket error - while(1) { - $output = curl_exec($ch); - self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if(self::$lastHttpCode != 429) { - break; - } - usleep(500000); + while (1) { + $output = curl_exec($ch); + $response = new CurlResponse($output); + + self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if (self::$lastHttpCode != 429) { + break; + } + + $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit'), 2); + + if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { + throw new ResourceRateLimitException($response->getBody()); + } + + usleep(500000); } - + if (curl_errno($ch)) { throw new Exception\CurlException(curl_errno($ch) . ' : ' . curl_error($ch)); } @@ -157,7 +170,7 @@ protected static function processRequest($ch) // close curl resource to free up system resources curl_close($ch); - return $output; + return $response->getBody(); } -} \ No newline at end of file +} diff --git a/lib/Currency.php b/lib/Currency.php new file mode 100644 index 0000000..a93a68b --- /dev/null +++ b/lib/Currency.php @@ -0,0 +1,38 @@ + Custom actions * -------------------------------------------------------------------------- @@ -44,6 +42,22 @@ class Customer extends ShopifyResource protected $childResource = array( 'CustomerAddress' => 'Address', 'Metafield', - 'CustomerOrder' => 'Order', + 'Order' ); -} + + /** + * Sends an account invite to a customer. + * + * @param array $customer_invite Customized invite data + * + * @return array + */ + public function send_invite($customer_invite = array()) + { + if (empty ( $customer_invite ) ) $customer_invite = new \stdClass(); + $url = $this->generateUrl(array(), 'send_invite'); + $dataArray = $this->wrapData($customer_invite, 'customer_invite'); + + return $this->post($dataArray, $url, false); + } +} \ No newline at end of file diff --git a/lib/CustomerAddress.php b/lib/CustomerAddress.php index 6cfdfc2..32c60c1 100644 --- a/lib/CustomerAddress.php +++ b/lib/CustomerAddress.php @@ -15,7 +15,7 @@ * CustomerAddress -> Custom actions * -------------------------------------------------------------------------- * @method array makeDefault() Sets the address as default for the customer - * @method array set() update multiple + * */ class CustomerAddress extends ShopifyResource { @@ -29,7 +29,6 @@ class CustomerAddress extends ShopifyResource */ protected $customPutActions = array( 'default' => 'makeDefault', - 'set', ); /** @@ -49,11 +48,10 @@ protected function pluralizeKey() * @return array */ //TODO Issue (Getting Error from API) : Internal server error - /*public function set($params) + public function set($params) { - $url = $this->generateUrl($params, 'set'); return $this->put(array(), $url); - }*/ -} + } +} \ No newline at end of file diff --git a/lib/DiscountCode.php b/lib/DiscountCode.php index 7bd6493..e13889e 100644 --- a/lib/DiscountCode.php +++ b/lib/DiscountCode.php @@ -10,10 +10,25 @@ namespace PHPShopify; +/** + * -------------------------------------------------------------------------- + * DiscountCode -> Custom actions + * -------------------------------------------------------------------------- + * @method array lookup() Retrieves the location of a discount code. + * + */ + class DiscountCode extends ShopifyResource { /** * @inheritDoc */ protected $resourceKey = 'discount_code'; + + /** + * @inheritDoc + */ + protected $customGetActions = array( + 'lookup', + ); } \ No newline at end of file diff --git a/lib/DraftOrder.php b/lib/DraftOrder.php index aaeba8a..902cee5 100644 --- a/lib/DraftOrder.php +++ b/lib/DraftOrder.php @@ -1,8 +1,8 @@ - * Created at 8/19/16 2:59 PM UTC+06:00 + * @author Thomas Hondema + * Created at 8/14/19 18:28 PM UTC+02:00 * * @see https://help.shopify.com/api/reference/draftorder Shopify API Reference for DraftOrder */ @@ -12,19 +12,11 @@ /** - * -------------------------------------------------------------------------- - * DraftOrder -> Child Resources - * -------------------------------------------------------------------------- - * @property-read Metafield $Metafield - * - - * @method Metafield Metafield(integer $id = null) - * * -------------------------------------------------------------------------- * DraftOrder -> Custom actions * -------------------------------------------------------------------------- - * @method array sendInvoice() Sends an invoice for the order - * @method array complete() Completes Draft Order + * @method array send_invoice() Send the invoice for a DraftOrder + * @method array complete() Complete a DraftOrder * */ class DraftOrder extends ShopifyResource @@ -37,15 +29,14 @@ class DraftOrder extends ShopifyResource /** * @inheritDoc */ - protected $childResource = array ( - 'Metafield', + protected $customPostActions = array( + 'send_invoice', ); /** * @inheritDoc */ - protected $customPostActions = array( - 'send_invoice' => 'sendInvoice', + protected $customPutActions = array( 'complete', ); -} +} \ No newline at end of file diff --git a/lib/GraphQL.php b/lib/GraphQL.php new file mode 100644 index 0000000..e6fa430 --- /dev/null +++ b/lib/GraphQL.php @@ -0,0 +1,70 @@ +generateUrl(); + + $response = HttpRequestGraphQL::post($url, $graphQL, $this->httpHeaders); + + return $this->processResponse($response); + } + + /** + * @inheritdoc + */ + public function get($urlParams = array(), $url = null, $dataKey = null) + { + throw new SdkException("Only POST method is allowed for GraphQL!"); + } + + /** + * @inheritdoc + */ + public function put($dataArray, $url = null, $wrapData = true) + { + throw new SdkException("Only POST method is allowed for GraphQL!"); + } + + /** + * @inheritdoc + */ + public function delete($urlParams = array(), $url = null) + { + throw new SdkException("Only POST method is allowed for GraphQL!"); + } +} \ No newline at end of file diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php new file mode 100644 index 0000000..b4595a8 --- /dev/null +++ b/lib/HttpRequestGraphQL.php @@ -0,0 +1,68 @@ + Child Resources * -------------------------------------------------------------------------- diff --git a/lib/ProductVariant.php b/lib/ProductVariant.php index 1e88fd4..fd34066 100644 --- a/lib/ProductVariant.php +++ b/lib/ProductVariant.php @@ -26,6 +26,11 @@ class ProductVariant extends ShopifyResource */ protected $resourceKey = 'variant'; + /** + * @inheritDoc + */ + public $searchEnabled = true; + /** * @inheritDoc */ diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index c01ab1f..e8f6542 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -119,7 +119,7 @@ public function __construct($id = null, $parentResourceUrl = '') $config = ShopifySDK::$config; - $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['AdminUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); + $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; @@ -293,20 +293,6 @@ public function generateUrl($urlParams = array(), $customAction = null) return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } - /** - * Generate the custom url for api request based on the params and custom action (if any) - * - * @param array $urlParams - * @param string $customAction - * - * @return string - */ - public function addShopUrl($urlParams = array(), $url) - { - - return $this->resourceUrl . ($url ? "/$url" : '') . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); - } - /** * Generate a HTTP GET request and return results as an array * @@ -406,13 +392,7 @@ public function post($dataArray, $url = null, $wrapData = true) public function put($dataArray, $url = null, $wrapData = true) { - /*if (!$url) { - $url = $this->generateUrl(); - } else { - $url = $this->addShopUrl($dataArray, $url); - }*/ if (!$url) $url = $this->generateUrl(); - if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); @@ -466,38 +446,16 @@ protected function wrapData($dataArray, $dataKey = null) */ protected function castString($array) { - if (is_string($array)) return $array; + if ( ! is_array($array)) return (string) $array; $string = ''; $i = 0; foreach ($array as $key => $val) { - - if ($key === "line_items") { - $line_id = array_keys($val)[0]; - $val = reset($val); - - if (isset($val['quantity']) && isset($val['quantity'][0]) && isset($val['quantity'][0]['message'])) { - $string = "Line item {$line_id} {$val['quantity'][0]['message']}"; - return $string; - } - - - } //Add values separated by comma //prepend the key string, if it's an associative key //Check if the value itself is another array to be converted to string - if (is_array($val) || is_object($val)) { - $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; - $i++; - } else if (is_array($key) || is_object($key)) { - $string .= ($i === $key ? '' : "$key - ") . $this->castString($key) . ', '; - $i++; - } else if (!is_array($val)) { - $string .= $val . ", "; - } else if (!is_array($key)) { - $string .= $key . " - "; - } - + $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; + $i++; } //Remove trailing comma and space @@ -533,24 +491,9 @@ public function processResponse($responseArray, $dataKey = null) } if (isset($responseArray['errors'])) { + $message = $this->castString($responseArray['errors']); - $message = $responseArray; - - if (is_array($message['errors'])) { - foreach ($message['errors'] as $key => $error) { - if (is_array($message['errors'][$key])) { - - throw new ApiException($this->castString($message['errors'])); - } else { - throw new ApiException($key . " " . $error); - } - - } - - } else { - throw new ApiException($responseArray['errors']); - } - + throw new ApiException($message); } if ($dataKey && isset($responseArray[$dataKey])) { diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index f985d65..b8d00f0 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -60,6 +60,9 @@ | //Get variants of a product (using Child resource) | $products = $shopify->Product($productID)->Variant->get(); | +| //GraphQL +| $data = $shopify->GraphQL->post($graphQL); +| */ use PHPShopify\Exception\SdkException; @@ -70,10 +73,14 @@ * @property-read Collect $Collect * @property-read Comment $Comment * @property-read Country $Country + * @property-read Currency $Currency * @property-read CustomCollection $CustomCollection * @property-read Customer $Customer * @property-read CustomerSavedSearch $CustomerSavedSearch * @property-read Discount $Discount + * @property-read DiscountCode $DiscountCode + * @property-read DraftOrder $DraftOrder + * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard @@ -83,13 +90,10 @@ * @property-read Metafield $Metafield * @property-read Multipass $Multipass * @property-read Order $Order - * @property-read DraftOrder $DraftOrder - * @property-read Checkout $Checkout * @property-read Page $Page * @property-read Policy $Policy * @property-read Product $Product * @property-read ProductListing $ProductListing - * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect @@ -100,6 +104,7 @@ * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook + * @property-read GraphQL $GraphQL * * @method AbandonedCheckout AbandonedCheckout(integer $id = null) * @method Blog Blog(integer $id = null) @@ -107,10 +112,14 @@ * @method Collect Collect(integer $id = null) * @method Comment Comment(integer $id = null) * @method Country Country(integer $id = null) + * @method Currency Currency(integer $id = null) * @method CustomCollection CustomCollection(integer $id = null) * @method Customer Customer(integer $id = null) * @method CustomerSavedSearch CustomerSavedSearch(integer $id = null) * @method Discount Discount(integer $id = null) + * @method DraftOrder DraftOrder(integer $id = null) + * @method DiscountCode DiscountCode(integer $id = null) + * @method PriceRule PriceRule(integer $id = null) * @method Event Event(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) * @method GiftCard GiftCard(integer $id = null) @@ -120,14 +129,11 @@ * @method Metafield Metafield(integer $id = null) * @method Multipass Multipass(integer $id = null) * @method Order Order(integer $id = null) - * @method DraftOrder DraftOrder(integer $id = null) - * @method Checkout Checkout(integer $id = null) * @method Page Page(integer $id = null) * @method Policy Policy(integer $id = null) * @method Product Product(integer $id = null) - * @method ProductVariant ProductVariant(integer $id = null) * @method ProductListing ProductListing(integer $id = null) - * @method CollectionListing CollectionListing(integer $id = null) + * @method ProductVariant ProductVariant(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) @@ -137,26 +143,10 @@ * @method Theme Theme(int $id = null) * @method User User(integer $id = null) * @method Webhook Webhook(integer $id = null) + * @method GraphQL GraphQL() */ class ShopifySDK { - /** - * @var float microtime of last api call - */ - public static $microtimeOfLastApiCall; - - /** - * @var float Minimum gap in seconds to maintain between 2 api calls - */ - public static $timeAllowedForEachApiCall = .01; - - /** - * Shop / API configurations - * - * @var array - */ - public static $config = array(); - /** * List of available resources which can be called from this client * @@ -170,10 +160,13 @@ class ShopifySDK 'Collect', 'Comment', 'Country', + 'Currency', 'CustomCollection', 'Customer', 'CustomerSavedSearch', 'Discount', + 'DiscountCode', + 'DraftOrder', 'Event', 'FulfillmentService', 'GiftCard', @@ -183,14 +176,11 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', - 'DraftOrder', - 'Checkout', 'Page', 'Policy', 'Product', - 'ProductVariant', 'ProductListing', - 'CollectionListing', + 'ProductVariant', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -202,6 +192,26 @@ class ShopifySDK 'Theme', 'User', 'Webhook', + 'GraphQL' + ); + + /** + * @var float microtime of last api call + */ + public static $microtimeOfLastApiCall; + + /** + * @var float Minimum gap in seconds to maintain between 2 api calls + */ + public static $timeAllowedForEachApiCall = .5; + + /** + * Shop / API configurations + * + * @var array + */ + public static $config = array( + 'ApiVersion' => '2019-04' ); /** @@ -222,7 +232,6 @@ class ShopifySDK 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', - 'ShippingRate' => 'Checkout', 'UsageCharge' => 'RecurringApplicationCharge', ); @@ -236,8 +245,7 @@ class ShopifySDK public function __construct($config = array()) { if(!empty($config)) { - ShopifySDK::$config = $config; - ShopifySDK::setAdminUrl(); + ShopifySDK::config($config); } } @@ -326,6 +334,7 @@ public static function setAdminUrl() //Remove https:// and trailing slash (if provided) $shopUrl = preg_replace('#^https?://|/$#', '', $shopUrl); + $apiVersion = self::$config['ApiVersion']; if(isset(self::$config['ApiKey']) && isset(self::$config['Password'])) { $apiKey = self::$config['ApiKey']; @@ -336,6 +345,7 @@ public static function setAdminUrl() } self::$config['AdminUrl'] = $adminUrl; + self::$config['ApiUrl'] = $adminUrl . "api/$apiVersion/"; return $adminUrl; } @@ -349,6 +359,15 @@ public static function getAdminUrl() { return self::$config['AdminUrl']; } + /** + * Get the api url of the configured shop + * + * @return string + */ + public static function getApiUrl() { + return self::$config['ApiUrl']; + } + /** * Maintain maximum 2 calls per second to the API * diff --git a/tests/CurrencyTest.php b/tests/CurrencyTest.php new file mode 100644 index 0000000..70ac526 --- /dev/null +++ b/tests/CurrencyTest.php @@ -0,0 +1,15 @@ + 'phpclassic.myshopify.com', - 'ApiKey' => '81781200c08b31208031f983ab930f2a', - 'Password' => '5260904f8293bce93ddd4d65c535faa4', + 'ShopUrl' => getenv('SHOPIFY_SHOP_URL'), //Your shop URL + 'ApiKey' => getenv('SHOPIFY_API_KEY'), //Your Private API Key + 'Password' => getenv('SHOPIFY_API_PASSWORD'), //Your Private API Password ); self::$shopify = ShopifySDK::config($config); From 184894e4e21549dda6781d3d9452ea407e457e83 Mon Sep 17 00:00:00 2001 From: TuunStudio <55007058+TuunStudio@users.noreply.github.com> Date: Fri, 6 Sep 2019 19:01:52 -0700 Subject: [PATCH 07/84] Delete .DS_Store --- .DS_Store | Bin 8196 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index e4db4f5337e95f314e5e1b05027f42f576948c52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMK~Eb;6n+CvS(_4;1PW9=SVfg01w}ZCLZu4DF`(3YMIv?_=3X2JLHO5B=nm7djW?+sB`thqjFs}^2VuD?b zu%Iwa1!^iwQVf=44ouTAUrhYF8a16*d}YK*uFR4Qg(Zmx%g64-iW*I86fg?(E1+`s z4Ben6c@(Ac_xCV&?ccb>#9Yr0zD*J&DIlKWe4pYBgA>8ep?$DIj1H+q{&^OgOtD6& zj_56FqYa6Je}g_lPm)M6zq|m`2d_?hDdyiP<{d#-#AL6*;qu@UPc-B~Np?gdjN?@Ggk@!|Y|WlNKE7Ef z%;s<1diP{De_WU^%+KcU%-=aV$y$@|6c);lTZc!-pPzj34c3Cfk^`1T_4u^?7QI9T zufCtGkNwzN-ygQ!TVXag{QAh{QG4vl)v@uhiHV6fCMTw*-<+=5S6!#R*9_FXE=pep zCBMVBn}OG;ww<~snwt@S5>>q;UbC;HT-SKx(NojqA6Y^-Y zW{-yTK>B_~hF;`Jv2|v4>p=+B%Movt#g4=k_o4D?Te*cBJP3h({LL*V2prMmrNf85 z<8pshlw=gi)}~jNLf`pl92Ub$z*WeZyMJjBXNoJm&Nbb!X-0SO7H+e3;k0;mrdZLo zEFOv1=^lMZ8?;A9^c8(aKhRI~GyP6~(m!m7+3an0o!w>&>@ItsmDvibu#efNnm*9` zA>ZmVrvGetrW9JfLb8P;I!jz{vY|eUOXrBKN!4#vw z1r!+6K2s|HUw;1g{|iJiE=B>Pz{{!t%PduwO1S&p8$)^_D%Z}VK1NkV`E@nM6f|)< j4vf=r;ORdM(a*zGaf%6cHNt{oJ_N)vn8qmZzbfz_ql{Gv From 4a9ca6fa6eeeea2aeef04f773ac662873ef38ae5 Mon Sep 17 00:00:00 2001 From: TuunStudio <55007058+TuunStudio@users.noreply.github.com> Date: Fri, 6 Sep 2019 19:02:14 -0700 Subject: [PATCH 08/84] Delete .DS_Store --- lib/.DS_Store | Bin 10244 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/.DS_Store diff --git a/lib/.DS_Store b/lib/.DS_Store deleted file mode 100644 index 4d34aed146a9b61f69f4ad5dd618a4cdd197aa41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHM-EJF26h0ffuuh!BO#+1*W&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW Date: Fri, 6 Sep 2019 19:15:25 -0700 Subject: [PATCH 09/84] added sales channel sdk endpoints --- lib/ShopifySDK.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index b8d00f0..58d7f29 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -80,6 +80,7 @@ * @property-read Discount $Discount * @property-read DiscountCode $DiscountCode * @property-read DraftOrder $DraftOrder + * @property-read Checkout $Checkout * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService @@ -94,6 +95,7 @@ * @property-read Policy $Policy * @property-read Product $Product * @property-read ProductListing $ProductListing + * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect @@ -129,11 +131,13 @@ * @method Metafield Metafield(integer $id = null) * @method Multipass Multipass(integer $id = null) * @method Order Order(integer $id = null) + * @method Checkout Checkout(integer $id = null) * @method Page Page(integer $id = null) * @method Policy Policy(integer $id = null) * @method Product Product(integer $id = null) * @method ProductListing ProductListing(integer $id = null) * @method ProductVariant ProductVariant(integer $id = null) + * @method CollectionListing CollectionListing(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) @@ -176,11 +180,13 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', + 'Checkout', 'Page', 'Policy', 'Product', 'ProductListing', 'ProductVariant', + 'CollectionListing', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -232,6 +238,7 @@ class ShopifySDK 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', + 'ShippingRate' => 'Checkout', 'UsageCharge' => 'RecurringApplicationCharge', ); From 9b361f6201f02c5638e17b59bfd716483cd525a7 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 19:24:51 -0700 Subject: [PATCH 10/84] removed customer order --- lib/CustomerOrder.php | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 lib/CustomerOrder.php diff --git a/lib/CustomerOrder.php b/lib/CustomerOrder.php deleted file mode 100644 index 3735dd8..0000000 --- a/lib/CustomerOrder.php +++ /dev/null @@ -1,37 +0,0 @@ - - * Created at 8/19/16 12:07 PM UTC+06:00 - * - * @see https://help.shopify.com/api/reference/customeraddress Shopify API Reference for CustomerAddress - */ - -namespace PHPShopify; - - -/** - * -------------------------------------------------------------------------- - * CustomerOrder -> Custom actions - * -------------------------------------------------------------------------- - * @method array get() Sets the address as default for the customer - * - */ -class CustomerOrder extends ShopifyResource -{ - /** - * @inheritDoc - */ - protected $resourceKey = 'order'; - - - /** - * @inheritDoc - */ - protected function pluralizeKey() - { - return 'orders'; - } - - -} From 8560d2ddd39c69f0a82b047f0e3bf58e7586c9c9 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 20 Dec 2019 17:14:58 -0800 Subject: [PATCH 11/84] added support for cursor based pagination --- lib/CurlRequest.php | 5 +- lib/HttpRequestJson.php | 6 +- lib/ShopifyResource.php | 168 ++++++++++++++++++++++------------------ 3 files changed, 98 insertions(+), 81 deletions(-) diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index 87014b5..943d884 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -170,7 +170,8 @@ protected static function processRequest($ch) // close curl resource to free up system resources curl_close($ch); - return $response->getBody(); + return $response; + } - + } diff --git a/lib/HttpRequestJson.php b/lib/HttpRequestJson.php index 0427cc6..bb66da3 100644 --- a/lib/HttpRequestJson.php +++ b/lib/HttpRequestJson.php @@ -131,12 +131,12 @@ public static function delete($url, $httpHeaders = array()) * * @param string $response * - * @return array + * @return string */ protected static function processResponse($response) { - return json_decode($response, true); + return $response; } -} \ No newline at end of file +} diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index e8f6542..4263c2d 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -6,13 +6,10 @@ * * @see https://help.shopify.com/api/reference Shopify API Reference */ - namespace PHPShopify; - use PHPShopify\Exception\ApiException; use PHPShopify\Exception\SdkException; use PHPShopify\Exception\CurlException; - /* |-------------------------------------------------------------------------- | Shopify API SDK Base Class @@ -29,7 +26,12 @@ abstract class ShopifyResource * @var array */ protected $httpHeaders = array(); - + /** + * HTTP response headers + * + * @var array + */ + protected static $httpResponseHeaders = array(); /** * The base URL of the API Resource (excluding the '.json' extension). * @@ -38,14 +40,12 @@ abstract class ShopifyResource * @var string */ protected $resourceUrl; - /** * Key of the API Resource which is used to fetch data from request responses * * @var string */ protected $resourceKey; - /** * List of child Resource names / classes * @@ -55,28 +55,24 @@ abstract class ShopifyResource * @var array */ protected $childResource = array(); - /** * If search is enabled for the resource * * @var boolean */ public $searchEnabled = false; - /** * If count is enabled for the resource * * @var boolean */ public $countEnabled = true; - /** * If the resource is read only. (No POST / PUT / DELETE actions) * * @var boolean */ public $readOnly = false; - /** * List of custom GET / POST / PUT / DELETE actions * @@ -95,7 +91,6 @@ abstract class ShopifyResource protected $customPostActions = array(); protected $customPutActions = array(); protected $customDeleteActions = array(); - /** * The ID of the resource * @@ -104,7 +99,6 @@ abstract class ShopifyResource * @var integer */ public $id; - /** * Create a new Shopify API resource instance. * @@ -116,18 +110,14 @@ abstract class ShopifyResource public function __construct($id = null, $parentResourceUrl = '') { $this->id = $id; - $config = ShopifySDK::$config; - $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); - if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; } elseif (!isset($config['ApiKey']) || !isset($config['Password'])) { throw new SdkException("Either AccessToken or ApiKey+Password Combination (in case of private API) is required to access the resources. Please check SDK configuration!"); } } - /** * Return ShopifyResource instance for the child resource. * @@ -142,7 +132,6 @@ public function __get($childName) { return $this->$childName(); } - /** * Return ShopifyResource instance for the child resource or call a custom action for the resource * @@ -166,23 +155,16 @@ public function __call($name, $arguments) if (ctype_upper($name[0])) { //Get the array key of the childResource in the childResource array $childKey = array_search($name, $this->childResource); - if ($childKey === false) { throw new SdkException("Child Resource $name is not available for " . $this->getResourceName()); } - //If any associative key is given to the childname, then it will be considered as the class name, //otherwise the childname will be the class name $childClassName = !is_numeric($childKey) ? $childKey : $name; - $childClass = __NAMESPACE__ . "\\" . $childClassName; - //If first argument is provided, it will be considered as the ID of the resource. $resourceID = !empty($arguments) ? $arguments[0] : null; - - $api = new $childClass($resourceID, $this->resourceUrl); - return $api; } else { $actionMaps = array( @@ -191,30 +173,23 @@ public function __call($name, $arguments) 'get' => 'customGetActions', 'delete'=> 'customDeleteActions', ); - //Get the array key for the action in the actions array foreach ($actionMaps as $httpMethod => $actionArrayKey) { $actionKey = array_search($name, $this->$actionArrayKey); if ($actionKey !== false) break; } - if ($actionKey === false) { throw new SdkException("No action named $name is defined for " . $this->getResourceName()); } - //If any associative key is given to the action, then it will be considered as the method name, //otherwise the action name will be the method name $customAction = !is_numeric($actionKey) ? $actionKey : $name; - - //Get the first argument if provided with the method call $methodArgument = !empty($arguments) ? $arguments[0] : array(); - //Url parameters $urlParams = array(); //Data body $dataArray = array(); - //Consider the argument as url parameters for get and delete request //and data array for post and put request if ($httpMethod == 'post' || $httpMethod == 'put') { @@ -222,9 +197,7 @@ public function __call($name, $arguments) } else { $urlParams = $methodArgument; } - $url = $this->generateUrl($urlParams, $customAction); - if ($httpMethod == 'post' || $httpMethod == 'put') { return $this->$httpMethod($dataArray, $url, false); } else { @@ -232,7 +205,6 @@ public function __call($name, $arguments) } } } - /** * Get the resource name (or the class name) * @@ -242,7 +214,6 @@ public function getResourceName() { return substr(get_called_class(), strrpos(get_called_class(), '\\') + 1); } - /** * Get the resource key to be used for while sending data to the API * @@ -254,7 +225,6 @@ public function getResourcePostKey() { return $this->resourceKey; } - /** * Get the pluralized version of the resource key * @@ -266,7 +236,6 @@ protected function pluralizeKey() { return $this->resourceKey . 's'; } - /** * Get the resource path to be used to generate the api url * @@ -279,7 +248,6 @@ protected function getResourcePath() { return $this->pluralizeKey(); } - /** * Generate the custom url for api request based on the params and custom action (if any) * @@ -292,7 +260,6 @@ public function generateUrl($urlParams = array(), $customAction = null) { return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } - /** * Generate a HTTP GET request and return results as an array * @@ -307,15 +274,10 @@ public function generateUrl($urlParams = array(), $customAction = null) public function get($urlParams = array(), $url = null, $dataKey = null) { if (!$url) $url = $this->generateUrl($urlParams); - $response = HttpRequestJson::get($url, $this->httpHeaders); - if (!$dataKey) $dataKey = $this->id ? $this->resourceKey : $this->pluralizeKey(); - return $this->processResponse($response, $dataKey); - } - /** * Get count for the number of resources available * @@ -328,12 +290,9 @@ public function count($urlParams = array()) if (!$this->countEnabled) { throw new SdkException("Count is not available for " . $this->getResourceName()); } - $url = $this->generateUrl($urlParams, 'count'); - return $this->get(array(), $url, 'count'); } - /** * Search within the resouce * @@ -348,14 +307,10 @@ public function search($query) if (!$this->searchEnabled) { throw new SdkException("Search is not available for " . $this->getResourceName()); } - if (!is_array($query)) $query = array('query' => $query); - $url = $this->generateUrl($query, 'search'); - return $this->get(array(), $url); } - /** * Call POST method to create a new resource * @@ -370,14 +325,10 @@ public function search($query) public function post($dataArray, $url = null, $wrapData = true) { if (!$url) $url = $this->generateUrl(); - if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); - $response = HttpRequestJson::post($url, $dataArray, $this->httpHeaders); - return $this->processResponse($response, $this->resourceKey); } - /** * Call PUT method to update an existing resource * @@ -391,16 +342,11 @@ public function post($dataArray, $url = null, $wrapData = true) */ public function put($dataArray, $url = null, $wrapData = true) { - if (!$url) $url = $this->generateUrl(); - if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); - $response = HttpRequestJson::put($url, $dataArray, $this->httpHeaders); - return $this->processResponse($response, $this->resourceKey); } - /** * Call DELETE method to delete an existing resource * @@ -414,12 +360,9 @@ public function put($dataArray, $url = null, $wrapData = true) public function delete($urlParams = array(), $url = null) { if (!$url) $url = $this->generateUrl($urlParams); - $response = HttpRequestJson::delete($url, $this->httpHeaders); - return $this->processResponse($response); } - /** * Wrap data array with resource key * @@ -431,10 +374,8 @@ public function delete($urlParams = array(), $url = null) protected function wrapData($dataArray, $dataKey = null) { if (!$dataKey) $dataKey = $this->getResourcePostKey(); - return array($dataKey => $dataArray); } - /** * Convert an array to string * @@ -447,7 +388,6 @@ protected function wrapData($dataArray, $dataKey = null) protected function castString($array) { if ( ! is_array($array)) return (string) $array; - $string = ''; $i = 0; foreach ($array as $key => $val) { @@ -457,17 +397,14 @@ protected function castString($array) $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; $i++; } - //Remove trailing comma and space $string = rtrim($string, ', '); - return $string; } - /** * Process the request response * - * @param array $responseArray Request response in array format + * @param string $response Request response in array format * @param string $dataKey Keyname to fetch data from response array * * @throws ApiException if the response has an error specified @@ -475,31 +412,110 @@ protected function castString($array) * * @return array */ - public function processResponse($responseArray, $dataKey = null) + public function processResponse($response, $dataKey = null) { + $responseArray = json_decode($response->getBody(), true); if ($responseArray === null) { //Something went wrong, Checking HTTP Codes $httpOK = 200; //Request Successful, OK. $httpCreated = 201; //Create Successful. - //should be null if any other library used for http calls $httpCode = CurlRequest::$lastHttpCode; - if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated) { throw new Exception\CurlException("Request failed with HTTP Code $httpCode."); } } - if (isset($responseArray['errors'])) { $message = $this->castString($responseArray['errors']); - throw new ApiException($message); } - + self::$httpResponseHeaders = $response->getHeaders(); if ($dataKey && isset($responseArray[$dataKey])) { return $responseArray[$dataKey]; } else { return $responseArray; } } + /** + * Checks response headers for existence of next page info + * + * @return boolean + */ + static public function lastResourceContainsNextPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"next\"/", $headers["Link"], $matchData)) { + // found rel="next" + return true; + } + } + return false; + } + /** + * Checks response headers for existence of previous page info + * + * @return boolean + */ + static public function lastResourceContainsPrevPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"previous\"/", $headers["Link"], $matchData)) { + // found rel="prev" + return true; + } + } + return false; + } + /** + * Gets next page info string for use in pagination + * + * @return string + */ + static public function getNextPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"next\"/", $headers["Link"], $matchData)) { + // found rel="next" + $query = parse_url($matchData[1], PHP_URL_QUERY); + $pairs = explode( "&", $query ); + foreach( $pairs as $p ) { + list( $key, $value) = explode( "=", $p ); + if( $key == "page_info" ) { + return $value; + } + } + } + } + return false; + } + /** + * Gets previous page info string for use in pagination + * + * @return string + */ + static public function getPrevPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"previous\"/", $headers["Link"], $matchData)) { + // found rel="prev" + $query = parse_url($matchData[1], PHP_URL_QUERY); + $pairs = explode( "&", $query ); + foreach( $pairs as $p ) { + list( $key, $value) = explode( "=", $p ); + if( $key == "page_info" ) { + return $value; + } + } + } + } + return false; + } } From 91c0ad53f7aaf904e682d0ee4389bd33f09653d3 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 17:52:23 -0700 Subject: [PATCH 12/84] added additional endpoints --- .DS_Store | Bin 0 -> 6148 bytes lib/.DS_Store | Bin 0 -> 10244 bytes lib/ApplicationCharge.php | 7 +--- lib/AuthHelper.php | 19 +--------- lib/Checkout.php | 53 +++++++++++++++++++++++++++ lib/CollectionListing.php | 32 ++++++++++++++++ lib/CurlRequest.php | 33 +++++------------ lib/Currency.php | 38 ------------------- lib/Customer.php | 22 ++--------- lib/CustomerAddress.php | 10 +++-- lib/CustomerOrder.php | 37 +++++++++++++++++++ lib/DiscountCode.php | 15 -------- lib/DraftOrder.php | 25 +++++++++---- lib/HttpRequestGraphQL.php | 2 +- lib/HttpRequestJson.php | 2 +- lib/Location.php | 7 ---- lib/PriceRule.php | 2 +- lib/ProductVariant.php | 5 --- lib/ShippingRate.php | 19 ++++++++++ lib/ShopifyResource.php | 69 ++++++++++++++++++++++++++++++++--- lib/ShopifySDK.php | 61 ++++++++++++++++--------------- tests/CurrencyTest.php | 15 -------- tests/ProductListingTest.php | 15 -------- tests/TestResource.php | 6 +-- 24 files changed, 280 insertions(+), 214 deletions(-) create mode 100644 .DS_Store create mode 100644 lib/.DS_Store create mode 100644 lib/Checkout.php create mode 100644 lib/CollectionListing.php delete mode 100644 lib/Currency.php create mode 100644 lib/CustomerOrder.php create mode 100644 lib/ShippingRate.php delete mode 100644 tests/CurrencyTest.php delete mode 100644 tests/ProductListingTest.php diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8f4ee880222ab9379103e9b7adc3d2e83b933c25 GIT binary patch literal 6148 zcmeHK%Z?I36ukwE(|{9Mn2hdvW8#7kIvGhOW)fszL88XkMi**kx}gKDO&6h?VP+7+ z#(yyS1^$W~mwt#Jpr;;VpcxZa#%SH7>eS<&+tlgXRTL4i*0k>sRfxz!5m+dpy2Ql1 zC>xg3BUgb;#OOx6=SSQ}Uj#H}6fg?>-3o|rw?H)-P)KR!`@M^~+uxt9(eU)Z{`ZCNF&v{QQN4*ASYy>yzi zyvcLXd*r1ud9{p^4{?jm+9aXL$@;LRvIG9Ls*bm_H;#nB?`K-mKaX*o9Ts2`? zMXPvJxjCOV9H(mU?bjDod+yw6>{ac3r?FTRt=(JBLHBrcc0PZzc>4isfidjW(ynQ| zfRAWs6;9kq9PxO75#hZ}_o+*V)S-vuQYWL)7kI4ZNg3~Lh%!ZvAgl21PB-6q{vg9D z(qqKGM{Rlltbx&Ae-4R!e7};9TZkPXD^f}_o+1lZJ<&C`p@7ZW&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW $value) { - $paramStrings[] = "$key=$value"; - } - return join('&', $paramStrings); - } - /** * Verify if the request is made from shopify using hmac hash value * @@ -78,7 +61,7 @@ public static function verifyShopifyRequest() unset($data['signature']); } //Create data string for the remaining url parameters - $dataString = self::buildQueryString($data); + $dataString = http_build_query($data); $realHmac = hash_hmac('sha256', $dataString, $sharedSecret); diff --git a/lib/Checkout.php b/lib/Checkout.php new file mode 100644 index 0000000..401882a --- /dev/null +++ b/lib/Checkout.php @@ -0,0 +1,53 @@ + + * Created at 8/19/16 2:59 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/sales-channels/checkout Shopify API Reference for Checkout + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * Order -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShippingRate $ShippingRate + * + * @method ShippingRate ShippingRate(integer $id = null) + * + + * -------------------------------------------------------------------------- + * Checkout -> Custom actions + * -------------------------------------------------------------------------- + * @method array complete() Completes Checkout without payment + * + */ +class Checkout extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'checkout'; + + /** + * @inheritDoc + */ + public $countEnabled = false; + + /** + * @inheritDoc + */ + protected $childResource = array ( + 'ShippingRate', + ); + /** + * @inheritDoc + */ + protected $customPostActions = array( + 'complete', + 'payments' + ); +} diff --git a/lib/CollectionListing.php b/lib/CollectionListing.php new file mode 100644 index 0000000..bc80f84 --- /dev/null +++ b/lib/CollectionListing.php @@ -0,0 +1,32 @@ + + * Created at: 6/2/18 1:38 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/sales_channels/collectionlisting + */ + +namespace PHPShopify; +/** + * -------------------------------------------------------------------------- + * CollectionListing -> Custom actions + * -------------------------------------------------------------------------- + * @method array productIds() Sets the address as default for the customer + */ + +class CollectionListing extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'collection_listing'; + + /** + * @inheritDoc + */ + protected $customGetActions = array( + 'product_ids' => 'productIds', + ); + +} diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index 87014b5..eb59477 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -9,7 +9,6 @@ use PHPShopify\Exception\CurlException; -use PHPShopify\Exception\ResourceRateLimitException; /* |-------------------------------------------------------------------------- @@ -48,9 +47,6 @@ protected static function init($url, $httpHeaders = array()) //Return the transfer as a string curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, true); - curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify'); - $headers = array(); foreach ($httpHeaders as $key => $value) { $headers[] = "$key: $value"; @@ -145,24 +141,15 @@ public static function delete($url, $httpHeaders = array()) protected static function processRequest($ch) { # Check for 429 leaky bucket error - while (1) { - $output = curl_exec($ch); - $response = new CurlResponse($output); - - self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if (self::$lastHttpCode != 429) { - break; - } - - $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit'), 2); - - if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { - throw new ResourceRateLimitException($response->getBody()); - } - - usleep(500000); + while(1) { + $output = curl_exec($ch); + self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if(self::$lastHttpCode != 429) { + break; + } + usleep(500000); } - + if (curl_errno($ch)) { throw new Exception\CurlException(curl_errno($ch) . ' : ' . curl_error($ch)); } @@ -170,7 +157,7 @@ protected static function processRequest($ch) // close curl resource to free up system resources curl_close($ch); - return $response->getBody(); + return $output; } -} +} \ No newline at end of file diff --git a/lib/Currency.php b/lib/Currency.php deleted file mode 100644 index a93a68b..0000000 --- a/lib/Currency.php +++ /dev/null @@ -1,38 +0,0 @@ - Custom actions * -------------------------------------------------------------------------- @@ -42,22 +44,6 @@ class Customer extends ShopifyResource protected $childResource = array( 'CustomerAddress' => 'Address', 'Metafield', - 'Order' + 'CustomerOrder' => 'Order', ); - - /** - * Sends an account invite to a customer. - * - * @param array $customer_invite Customized invite data - * - * @return array - */ - public function send_invite($customer_invite = array()) - { - if (empty ( $customer_invite ) ) $customer_invite = new \stdClass(); - $url = $this->generateUrl(array(), 'send_invite'); - $dataArray = $this->wrapData($customer_invite, 'customer_invite'); - - return $this->post($dataArray, $url, false); - } -} \ No newline at end of file +} diff --git a/lib/CustomerAddress.php b/lib/CustomerAddress.php index 32c60c1..6cfdfc2 100644 --- a/lib/CustomerAddress.php +++ b/lib/CustomerAddress.php @@ -15,7 +15,7 @@ * CustomerAddress -> Custom actions * -------------------------------------------------------------------------- * @method array makeDefault() Sets the address as default for the customer - * + * @method array set() update multiple */ class CustomerAddress extends ShopifyResource { @@ -29,6 +29,7 @@ class CustomerAddress extends ShopifyResource */ protected $customPutActions = array( 'default' => 'makeDefault', + 'set', ); /** @@ -48,10 +49,11 @@ protected function pluralizeKey() * @return array */ //TODO Issue (Getting Error from API) : Internal server error - public function set($params) + /*public function set($params) { + $url = $this->generateUrl($params, 'set'); return $this->put(array(), $url); - } -} \ No newline at end of file + }*/ +} diff --git a/lib/CustomerOrder.php b/lib/CustomerOrder.php new file mode 100644 index 0000000..3735dd8 --- /dev/null +++ b/lib/CustomerOrder.php @@ -0,0 +1,37 @@ + + * Created at 8/19/16 12:07 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/customeraddress Shopify API Reference for CustomerAddress + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * CustomerOrder -> Custom actions + * -------------------------------------------------------------------------- + * @method array get() Sets the address as default for the customer + * + */ +class CustomerOrder extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'order'; + + + /** + * @inheritDoc + */ + protected function pluralizeKey() + { + return 'orders'; + } + + +} diff --git a/lib/DiscountCode.php b/lib/DiscountCode.php index e13889e..7bd6493 100644 --- a/lib/DiscountCode.php +++ b/lib/DiscountCode.php @@ -10,25 +10,10 @@ namespace PHPShopify; -/** - * -------------------------------------------------------------------------- - * DiscountCode -> Custom actions - * -------------------------------------------------------------------------- - * @method array lookup() Retrieves the location of a discount code. - * - */ - class DiscountCode extends ShopifyResource { /** * @inheritDoc */ protected $resourceKey = 'discount_code'; - - /** - * @inheritDoc - */ - protected $customGetActions = array( - 'lookup', - ); } \ No newline at end of file diff --git a/lib/DraftOrder.php b/lib/DraftOrder.php index 902cee5..aaeba8a 100644 --- a/lib/DraftOrder.php +++ b/lib/DraftOrder.php @@ -1,8 +1,8 @@ - * Created at 8/14/19 18:28 PM UTC+02:00 + * @author Tareq Mahmood + * Created at 8/19/16 2:59 PM UTC+06:00 * * @see https://help.shopify.com/api/reference/draftorder Shopify API Reference for DraftOrder */ @@ -12,11 +12,19 @@ /** + * -------------------------------------------------------------------------- + * DraftOrder -> Child Resources + * -------------------------------------------------------------------------- + * @property-read Metafield $Metafield + * + + * @method Metafield Metafield(integer $id = null) + * * -------------------------------------------------------------------------- * DraftOrder -> Custom actions * -------------------------------------------------------------------------- - * @method array send_invoice() Send the invoice for a DraftOrder - * @method array complete() Complete a DraftOrder + * @method array sendInvoice() Sends an invoice for the order + * @method array complete() Completes Draft Order * */ class DraftOrder extends ShopifyResource @@ -29,14 +37,15 @@ class DraftOrder extends ShopifyResource /** * @inheritDoc */ - protected $customPostActions = array( - 'send_invoice', + protected $childResource = array ( + 'Metafield', ); /** * @inheritDoc */ - protected $customPutActions = array( + protected $customPostActions = array( + 'send_invoice' => 'sendInvoice', 'complete', ); -} \ No newline at end of file +} diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php index 8aef907..1a08088 100644 --- a/lib/HttpRequestGraphQL.php +++ b/lib/HttpRequestGraphQL.php @@ -72,4 +72,4 @@ public static function post($url, $data, $httpHeaders = array(), $variables = nu return self::processResponse($response); } -} \ No newline at end of file +} diff --git a/lib/HttpRequestJson.php b/lib/HttpRequestJson.php index ee8372a..91baddf 100644 --- a/lib/HttpRequestJson.php +++ b/lib/HttpRequestJson.php @@ -25,7 +25,7 @@ class HttpRequestJson * * @var array */ - protected static $httpHeaders; + private static $httpHeaders; /** * Prepared JSON string to be posted with request diff --git a/lib/Location.php b/lib/Location.php index f4a589a..2c960b4 100644 --- a/lib/Location.php +++ b/lib/Location.php @@ -26,11 +26,4 @@ class Location extends ShopifyResource * @inheritDoc */ public $readOnly = true; - - /** - * @inheritDoc - */ - protected $childResource = array( - 'InventoryLevel', - ); } \ No newline at end of file diff --git a/lib/PriceRule.php b/lib/PriceRule.php index de08014..7486415 100644 --- a/lib/PriceRule.php +++ b/lib/PriceRule.php @@ -10,7 +10,7 @@ namespace PHPShopify; -/** +/* * -------------------------------------------------------------------------- * PriceRule -> Child Resources * -------------------------------------------------------------------------- diff --git a/lib/ProductVariant.php b/lib/ProductVariant.php index fd34066..1e88fd4 100644 --- a/lib/ProductVariant.php +++ b/lib/ProductVariant.php @@ -26,11 +26,6 @@ class ProductVariant extends ShopifyResource */ protected $resourceKey = 'variant'; - /** - * @inheritDoc - */ - public $searchEnabled = true; - /** * @inheritDoc */ diff --git a/lib/ShippingRate.php b/lib/ShippingRate.php new file mode 100644 index 0000000..e5de310 --- /dev/null +++ b/lib/ShippingRate.php @@ -0,0 +1,19 @@ + + * Created at 8/19/16 7:27 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/shipping_rates Shopify API Reference for ShippingRate + */ + +namespace PHPShopify; + + +class ShippingRate extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'shipping_rate'; +} diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index e8f6542..c01ab1f 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -119,7 +119,7 @@ public function __construct($id = null, $parentResourceUrl = '') $config = ShopifySDK::$config; - $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); + $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['AdminUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; @@ -293,6 +293,20 @@ public function generateUrl($urlParams = array(), $customAction = null) return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } + /** + * Generate the custom url for api request based on the params and custom action (if any) + * + * @param array $urlParams + * @param string $customAction + * + * @return string + */ + public function addShopUrl($urlParams = array(), $url) + { + + return $this->resourceUrl . ($url ? "/$url" : '') . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); + } + /** * Generate a HTTP GET request and return results as an array * @@ -392,7 +406,13 @@ public function post($dataArray, $url = null, $wrapData = true) public function put($dataArray, $url = null, $wrapData = true) { + /*if (!$url) { + $url = $this->generateUrl(); + } else { + $url = $this->addShopUrl($dataArray, $url); + }*/ if (!$url) $url = $this->generateUrl(); + if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); @@ -446,16 +466,38 @@ protected function wrapData($dataArray, $dataKey = null) */ protected function castString($array) { - if ( ! is_array($array)) return (string) $array; + if (is_string($array)) return $array; $string = ''; $i = 0; foreach ($array as $key => $val) { + + if ($key === "line_items") { + $line_id = array_keys($val)[0]; + $val = reset($val); + + if (isset($val['quantity']) && isset($val['quantity'][0]) && isset($val['quantity'][0]['message'])) { + $string = "Line item {$line_id} {$val['quantity'][0]['message']}"; + return $string; + } + + + } //Add values separated by comma //prepend the key string, if it's an associative key //Check if the value itself is another array to be converted to string - $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; - $i++; + if (is_array($val) || is_object($val)) { + $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; + $i++; + } else if (is_array($key) || is_object($key)) { + $string .= ($i === $key ? '' : "$key - ") . $this->castString($key) . ', '; + $i++; + } else if (!is_array($val)) { + $string .= $val . ", "; + } else if (!is_array($key)) { + $string .= $key . " - "; + } + } //Remove trailing comma and space @@ -491,9 +533,24 @@ public function processResponse($responseArray, $dataKey = null) } if (isset($responseArray['errors'])) { - $message = $this->castString($responseArray['errors']); - throw new ApiException($message); + $message = $responseArray; + + if (is_array($message['errors'])) { + foreach ($message['errors'] as $key => $error) { + if (is_array($message['errors'][$key])) { + + throw new ApiException($this->castString($message['errors'])); + } else { + throw new ApiException($key . " " . $error); + } + + } + + } else { + throw new ApiException($responseArray['errors']); + } + } if ($dataKey && isset($responseArray[$dataKey])) { diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index c9040ba..c9b05c1 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -60,9 +60,6 @@ | //Get variants of a product (using Child resource) | $products = $shopify->Product($productID)->Variant->get(); | -| //GraphQL -| $data = $shopify->GraphQL->post($graphQL); -| */ use PHPShopify\Exception\SdkException; @@ -73,14 +70,10 @@ * @property-read Collect $Collect * @property-read Comment $Comment * @property-read Country $Country - * @property-read Currency $Currency * @property-read CustomCollection $CustomCollection * @property-read Customer $Customer * @property-read CustomerSavedSearch $CustomerSavedSearch * @property-read Discount $Discount - * @property-read DiscountCode $DiscountCode - * @property-read DraftOrder $DraftOrder - * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard @@ -90,10 +83,13 @@ * @property-read Metafield $Metafield * @property-read Multipass $Multipass * @property-read Order $Order + * @property-read DraftOrder $DraftOrder + * @property-read Checkout $Checkout * @property-read Page $Page * @property-read Policy $Policy * @property-read Product $Product * @property-read ProductListing $ProductListing + * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect @@ -104,7 +100,6 @@ * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook - * @property-read GraphQL $GraphQL * * @method AbandonedCheckout AbandonedCheckout(integer $id = null) * @method Blog Blog(integer $id = null) @@ -112,14 +107,10 @@ * @method Collect Collect(integer $id = null) * @method Comment Comment(integer $id = null) * @method Country Country(integer $id = null) - * @method Currency Currency(integer $id = null) * @method CustomCollection CustomCollection(integer $id = null) * @method Customer Customer(integer $id = null) * @method CustomerSavedSearch CustomerSavedSearch(integer $id = null) * @method Discount Discount(integer $id = null) - * @method DraftOrder DraftOrder(integer $id = null) - * @method DiscountCode DiscountCode(integer $id = null) - * @method PriceRule PriceRule(integer $id = null) * @method Event Event(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) * @method GiftCard GiftCard(integer $id = null) @@ -129,11 +120,14 @@ * @method Metafield Metafield(integer $id = null) * @method Multipass Multipass(integer $id = null) * @method Order Order(integer $id = null) + * @method DraftOrder DraftOrder(integer $id = null) + * @method Checkout Checkout(integer $id = null) * @method Page Page(integer $id = null) * @method Policy Policy(integer $id = null) * @method Product Product(integer $id = null) - * @method ProductListing ProductListing(integer $id = null) * @method ProductVariant ProductVariant(integer $id = null) + * @method ProductListing ProductListing(integer $id = null) + * @method CollectionListing CollectionListing(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) @@ -143,10 +137,26 @@ * @method Theme Theme(int $id = null) * @method User User(integer $id = null) * @method Webhook Webhook(integer $id = null) - * @method GraphQL GraphQL() */ class ShopifySDK { + /** + * @var float microtime of last api call + */ + public static $microtimeOfLastApiCall; + + /** + * @var float Minimum gap in seconds to maintain between 2 api calls + */ + public static $timeAllowedForEachApiCall = .01; + + /** + * Shop / API configurations + * + * @var array + */ + public static $config = array(); + /** * List of available resources which can be called from this client * @@ -160,13 +170,10 @@ class ShopifySDK 'Collect', 'Comment', 'Country', - 'Currency', 'CustomCollection', 'Customer', 'CustomerSavedSearch', 'Discount', - 'DiscountCode', - 'DraftOrder', 'Event', 'FulfillmentService', 'GiftCard', @@ -176,11 +183,14 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', + 'DraftOrder', + 'Checkout', 'Page', 'Policy', 'Product', - 'ProductListing', 'ProductVariant', + 'ProductListing', + 'CollectionListing', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -232,6 +242,7 @@ class ShopifySDK 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', + 'ShippingRate' => 'Checkout', 'UsageCharge' => 'RecurringApplicationCharge', ); @@ -245,7 +256,8 @@ class ShopifySDK public function __construct($config = array()) { if(!empty($config)) { - ShopifySDK::config($config); + ShopifySDK::$config = $config; + ShopifySDK::setAdminUrl(); } } @@ -341,7 +353,6 @@ public static function setAdminUrl() //Remove https:// and trailing slash (if provided) $shopUrl = preg_replace('#^https?://|/$#', '', $shopUrl); - $apiVersion = self::$config['ApiVersion']; if(isset(self::$config['ApiKey']) && isset(self::$config['Password'])) { $apiKey = self::$config['ApiKey']; @@ -352,7 +363,6 @@ public static function setAdminUrl() } self::$config['AdminUrl'] = $adminUrl; - self::$config['ApiUrl'] = $adminUrl . "api/$apiVersion/"; return $adminUrl; } @@ -366,15 +376,6 @@ public static function getAdminUrl() { return self::$config['AdminUrl']; } - /** - * Get the api url of the configured shop - * - * @return string - */ - public static function getApiUrl() { - return self::$config['ApiUrl']; - } - /** * Maintain maximum 2 calls per second to the API * diff --git a/tests/CurrencyTest.php b/tests/CurrencyTest.php deleted file mode 100644 index 70ac526..0000000 --- a/tests/CurrencyTest.php +++ /dev/null @@ -1,15 +0,0 @@ - getenv('SHOPIFY_SHOP_URL'), //Your shop URL - 'ApiKey' => getenv('SHOPIFY_API_KEY'), //Your Private API Key - 'Password' => getenv('SHOPIFY_API_PASSWORD'), //Your Private API Password + 'ShopUrl' => 'phpclassic.myshopify.com', + 'ApiKey' => '81781200c08b31208031f983ab930f2a', + 'Password' => '5260904f8293bce93ddd4d65c535faa4', ); self::$shopify = ShopifySDK::config($config); From 1417d2324d86b95e334884906d5f570f86c932d9 Mon Sep 17 00:00:00 2001 From: TuunStudio <55007058+TuunStudio@users.noreply.github.com> Date: Fri, 6 Sep 2019 18:49:12 -0700 Subject: [PATCH 13/84] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 8f4ee880222ab9379103e9b7adc3d2e83b933c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%Z?I36ukwE(|{9Mn2hdvW8#7kIvGhOW)fszL88XkMi**kx}gKDO&6h?VP+7+ z#(yyS1^$W~mwt#Jpr;;VpcxZa#%SH7>eS<&+tlgXRTL4i*0k>sRfxz!5m+dpy2Ql1 zC>xg3BUgb;#OOx6=SSQ}Uj#H}6fg?>-3o|rw?H)-P)KR!`@M^~+uxt9(eU)Z{`ZCNF&v{QQN4*ASYy>yzi zyvcLXd*r1ud9{p^4{?jm+9aXL$@;LRvIG9Ls*bm_H;#nB?`K-mKaX*o9Ts2`? zMXPvJxjCOV9H(mU?bjDod+yw6>{ac3r?FTRt=(JBLHBrcc0PZzc>4isfidjW(ynQ| zfRAWs6;9kq9PxO75#hZ}_o+*V)S-vuQYWL)7kI4ZNg3~Lh%!ZvAgl21PB-6q{vg9D z(qqKGM{Rlltbx&Ae-4R!e7};9TZkPXD^f}_o+1lZJ<&C`p@7Z Date: Fri, 6 Sep 2019 18:49:26 -0700 Subject: [PATCH 14/84] Delete .DS_Store --- lib/.DS_Store | Bin 10244 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/.DS_Store diff --git a/lib/.DS_Store b/lib/.DS_Store deleted file mode 100644 index 4d34aed146a9b61f69f4ad5dd618a4cdd197aa41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHM-EJF26h0ffuuh!BO#+1*W&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW Date: Fri, 6 Sep 2019 18:52:25 -0700 Subject: [PATCH 15/84] added checkout endpoints --- .DS_Store | Bin 0 -> 8196 bytes lib/.DS_Store | Bin 0 -> 10244 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .DS_Store create mode 100644 lib/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7a1e21648e4703c021d76dbbc5d50771d2110c96 GIT binary patch literal 8196 zcmeHMUr!TR6h9a0GA)Y?3JVxbCfk^pNDORPcre6TAlkS}rGy31tuyVVj7;xsW~Lyp zn7;S{_HE;4KY*WM6Q4KxvX4F)KY}Jc`K0ImDVC`w`(%uJPjc@$_nh-P)BB@yr)K~F zl5bY00LB2o!XdQXkJCp)#Kq{69_=}anjjtu0k3!g_i$}@WgCZKz%XDKFbo(53XIfmkfyW!NDPPPV1J^^60=xwg9M8=$3-E{OOO{5f0Qjty@Z? z2ZbmoY6T^_#UQ$KAPSE0oX&44t>8f9m9dV#GSM9h(TxXjWIB+X(q=Xc7zVl-5WRZ} zEFG_K_@>XYGFE0FbFZ|;cT2GwH>128ZirRUjki2) zuW%k5*{-+h)7`Q?JkMF|`izDW>{ltK-2K^SRlb|&O;(L$ostSzR@zFJ?eT+y=^th$ zoF6Br4=0?1soAO731?<@=I}6Wjh>&HE8MT|?H~Ml_~;4l1%>DX76#S&X#N8&&4ORq z(fcFM+WUKH+wC2uGyUHVoE)@=PMsba9vT_>Zggy9{LGoNW&5<}R<>)Q_^k6h;9)*! z(v4c^S4$1I;MqRMx*LWrtI_=4y}w~OBAF(0f;-}yLCt+r77%&WcMGTPCLTMq78*2YdNbXJa+76EUIE0A2meLY} mlWfOP%XS>~(LW5)b}&_7a$2{PMh}X45g=(WlVRYqGVmV2K~{VK literal 0 HcmV?d00001 diff --git a/lib/.DS_Store b/lib/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4d34aed146a9b61f69f4ad5dd618a4cdd197aa41 GIT binary patch literal 10244 zcmeHM-EJF26h0ffuuh!BO#+1*W&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW Date: Fri, 6 Sep 2019 18:52:56 -0700 Subject: [PATCH 16/84] added checkout endpoints --- .DS_Store | Bin 8196 -> 8196 bytes .gitignore | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.DS_Store b/.DS_Store index 7a1e21648e4703c021d76dbbc5d50771d2110c96..e4db4f5337e95f314e5e1b05027f42f576948c52 100644 GIT binary patch delta 250 zcmZp1XmQx^UXa7c&`?Lg*w|w7cfkW{`o>P>L}c$Y5s3VaQ}iDhn>k%gN76 ZXJD8tCvtT2Lm6e}i48oP*(Lt60{}mDJZAs^ delta 242 zcmZp1XmQx^UXa7Y$V^AU)WBr&cfkHm6KE1Apk4_6l2h4aAfdh2xG`*sA8DNFqdH^!)}HX40jpc zGyG&^XOv`AV$^0dVYFqmXY^nUWDH^qVT@#qVvL69XJUk!BMYUW6k`gI!OW1ukjXHa ML*&Bd$0CZ{07SSqG5`Po diff --git a/.gitignore b/.gitignore index 432b975..8f721c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor /nbproject/private/ -.idea/ \ No newline at end of file +.idea/ +.DS_Store \ No newline at end of file From e577d9a49dbbcfc58ee640edb5e7d4f9566e74d1 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 19:00:23 -0700 Subject: [PATCH 17/84] added sales channel sdk endpoints --- lib/ApplicationCharge.php | 7 +++- lib/AuthHelper.php | 19 +++++++++- lib/CurlRequest.php | 33 +++++++++++------ lib/Currency.php | 38 ++++++++++++++++++++ lib/Customer.php | 22 +++++++++--- lib/CustomerAddress.php | 10 +++--- lib/DiscountCode.php | 15 ++++++++ lib/DraftOrder.php | 25 +++++-------- lib/GraphQL.php | 2 +- lib/HttpRequestGraphQL.php | 4 +-- lib/HttpRequestJson.php | 2 +- lib/Location.php | 7 ++++ lib/PriceRule.php | 2 +- lib/ProductVariant.php | 5 +++ lib/ShopifyResource.php | 69 ++++-------------------------------- lib/ShopifySDK.php | 61 ++++++++++++++++--------------- tests/CurrencyTest.php | 15 ++++++++ tests/ProductListingTest.php | 15 ++++++++ tests/TestResource.php | 6 ++-- 19 files changed, 216 insertions(+), 141 deletions(-) create mode 100644 lib/Currency.php create mode 100644 tests/CurrencyTest.php create mode 100644 tests/ProductListingTest.php diff --git a/lib/ApplicationCharge.php b/lib/ApplicationCharge.php index 2e55b77..b557911 100644 --- a/lib/ApplicationCharge.php +++ b/lib/ApplicationCharge.php @@ -21,4 +21,9 @@ class ApplicationCharge extends ShopifyResource * @inheritDoc */ public $countEnabled = false; -} \ No newline at end of file + + // To activate ApplicationCharge + protected $customPostActions = array( + 'activate', + ); +} diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index 3a6ff27..e879964 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -32,6 +32,23 @@ public static function getCurrentUrl() return "$protocol://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; } + /** + * Build a query string from a data array + * This is a replacement for http_build_query because that returns an url-encoded string. + * + * @param array $data Data array + * + * @return array + */ + public static function buildQueryString($data) + { + $paramStrings = []; + foreach ($data as $key => $value) { + $paramStrings[] = "$key=$value"; + } + return join('&', $paramStrings); + } + /** * Verify if the request is made from shopify using hmac hash value * @@ -61,7 +78,7 @@ public static function verifyShopifyRequest() unset($data['signature']); } //Create data string for the remaining url parameters - $dataString = http_build_query($data); + $dataString = self::buildQueryString($data); $realHmac = hash_hmac('sha256', $dataString, $sharedSecret); diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index eb59477..87014b5 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -9,6 +9,7 @@ use PHPShopify\Exception\CurlException; +use PHPShopify\Exception\ResourceRateLimitException; /* |-------------------------------------------------------------------------- @@ -47,6 +48,9 @@ protected static function init($url, $httpHeaders = array()) //Return the transfer as a string curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify'); + $headers = array(); foreach ($httpHeaders as $key => $value) { $headers[] = "$key: $value"; @@ -141,15 +145,24 @@ public static function delete($url, $httpHeaders = array()) protected static function processRequest($ch) { # Check for 429 leaky bucket error - while(1) { - $output = curl_exec($ch); - self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if(self::$lastHttpCode != 429) { - break; - } - usleep(500000); + while (1) { + $output = curl_exec($ch); + $response = new CurlResponse($output); + + self::$lastHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if (self::$lastHttpCode != 429) { + break; + } + + $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit'), 2); + + if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { + throw new ResourceRateLimitException($response->getBody()); + } + + usleep(500000); } - + if (curl_errno($ch)) { throw new Exception\CurlException(curl_errno($ch) . ' : ' . curl_error($ch)); } @@ -157,7 +170,7 @@ protected static function processRequest($ch) // close curl resource to free up system resources curl_close($ch); - return $output; + return $response->getBody(); } -} \ No newline at end of file +} diff --git a/lib/Currency.php b/lib/Currency.php new file mode 100644 index 0000000..a93a68b --- /dev/null +++ b/lib/Currency.php @@ -0,0 +1,38 @@ + Custom actions * -------------------------------------------------------------------------- @@ -44,6 +42,22 @@ class Customer extends ShopifyResource protected $childResource = array( 'CustomerAddress' => 'Address', 'Metafield', - 'CustomerOrder' => 'Order', + 'Order' ); -} + + /** + * Sends an account invite to a customer. + * + * @param array $customer_invite Customized invite data + * + * @return array + */ + public function send_invite($customer_invite = array()) + { + if (empty ( $customer_invite ) ) $customer_invite = new \stdClass(); + $url = $this->generateUrl(array(), 'send_invite'); + $dataArray = $this->wrapData($customer_invite, 'customer_invite'); + + return $this->post($dataArray, $url, false); + } +} \ No newline at end of file diff --git a/lib/CustomerAddress.php b/lib/CustomerAddress.php index 6cfdfc2..32c60c1 100644 --- a/lib/CustomerAddress.php +++ b/lib/CustomerAddress.php @@ -15,7 +15,7 @@ * CustomerAddress -> Custom actions * -------------------------------------------------------------------------- * @method array makeDefault() Sets the address as default for the customer - * @method array set() update multiple + * */ class CustomerAddress extends ShopifyResource { @@ -29,7 +29,6 @@ class CustomerAddress extends ShopifyResource */ protected $customPutActions = array( 'default' => 'makeDefault', - 'set', ); /** @@ -49,11 +48,10 @@ protected function pluralizeKey() * @return array */ //TODO Issue (Getting Error from API) : Internal server error - /*public function set($params) + public function set($params) { - $url = $this->generateUrl($params, 'set'); return $this->put(array(), $url); - }*/ -} + } +} \ No newline at end of file diff --git a/lib/DiscountCode.php b/lib/DiscountCode.php index 7bd6493..e13889e 100644 --- a/lib/DiscountCode.php +++ b/lib/DiscountCode.php @@ -10,10 +10,25 @@ namespace PHPShopify; +/** + * -------------------------------------------------------------------------- + * DiscountCode -> Custom actions + * -------------------------------------------------------------------------- + * @method array lookup() Retrieves the location of a discount code. + * + */ + class DiscountCode extends ShopifyResource { /** * @inheritDoc */ protected $resourceKey = 'discount_code'; + + /** + * @inheritDoc + */ + protected $customGetActions = array( + 'lookup', + ); } \ No newline at end of file diff --git a/lib/DraftOrder.php b/lib/DraftOrder.php index aaeba8a..902cee5 100644 --- a/lib/DraftOrder.php +++ b/lib/DraftOrder.php @@ -1,8 +1,8 @@ - * Created at 8/19/16 2:59 PM UTC+06:00 + * @author Thomas Hondema + * Created at 8/14/19 18:28 PM UTC+02:00 * * @see https://help.shopify.com/api/reference/draftorder Shopify API Reference for DraftOrder */ @@ -12,19 +12,11 @@ /** - * -------------------------------------------------------------------------- - * DraftOrder -> Child Resources - * -------------------------------------------------------------------------- - * @property-read Metafield $Metafield - * - - * @method Metafield Metafield(integer $id = null) - * * -------------------------------------------------------------------------- * DraftOrder -> Custom actions * -------------------------------------------------------------------------- - * @method array sendInvoice() Sends an invoice for the order - * @method array complete() Completes Draft Order + * @method array send_invoice() Send the invoice for a DraftOrder + * @method array complete() Complete a DraftOrder * */ class DraftOrder extends ShopifyResource @@ -37,15 +29,14 @@ class DraftOrder extends ShopifyResource /** * @inheritDoc */ - protected $childResource = array ( - 'Metafield', + protected $customPostActions = array( + 'send_invoice', ); /** * @inheritDoc */ - protected $customPostActions = array( - 'send_invoice' => 'sendInvoice', + protected $customPutActions = array( 'complete', ); -} +} \ No newline at end of file diff --git a/lib/GraphQL.php b/lib/GraphQL.php index 7d49d25..85ef395 100644 --- a/lib/GraphQL.php +++ b/lib/GraphQL.php @@ -68,4 +68,4 @@ public function delete($urlParams = array(), $url = null) { throw new SdkException("Only POST method is allowed for GraphQL!"); } -} \ No newline at end of file +} diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php index 1a08088..8d44d15 100644 --- a/lib/HttpRequestGraphQL.php +++ b/lib/HttpRequestGraphQL.php @@ -44,14 +44,14 @@ protected static function prepareRequest($httpHeaders = array(), $data = array() throw new SdkException("The GraphQL Admin API requires an access token for making authenticated requests!"); } - self::$httpHeaders = $httpHeaders; - if (is_array($variables)) { self::$postDataGraphQL = json_encode(['query' => $data, 'variables' => $variables]); self::$httpHeaders['Content-type'] = 'application/json'; } else { self::$httpHeaders['Content-type'] = 'application/graphql'; } + self::$httpHeaders['Content-type'] = 'application/graphql'; + } /** diff --git a/lib/HttpRequestJson.php b/lib/HttpRequestJson.php index 91baddf..ee8372a 100644 --- a/lib/HttpRequestJson.php +++ b/lib/HttpRequestJson.php @@ -25,7 +25,7 @@ class HttpRequestJson * * @var array */ - private static $httpHeaders; + protected static $httpHeaders; /** * Prepared JSON string to be posted with request diff --git a/lib/Location.php b/lib/Location.php index 2c960b4..f4a589a 100644 --- a/lib/Location.php +++ b/lib/Location.php @@ -26,4 +26,11 @@ class Location extends ShopifyResource * @inheritDoc */ public $readOnly = true; + + /** + * @inheritDoc + */ + protected $childResource = array( + 'InventoryLevel', + ); } \ No newline at end of file diff --git a/lib/PriceRule.php b/lib/PriceRule.php index 7486415..de08014 100644 --- a/lib/PriceRule.php +++ b/lib/PriceRule.php @@ -10,7 +10,7 @@ namespace PHPShopify; -/* +/** * -------------------------------------------------------------------------- * PriceRule -> Child Resources * -------------------------------------------------------------------------- diff --git a/lib/ProductVariant.php b/lib/ProductVariant.php index 1e88fd4..fd34066 100644 --- a/lib/ProductVariant.php +++ b/lib/ProductVariant.php @@ -26,6 +26,11 @@ class ProductVariant extends ShopifyResource */ protected $resourceKey = 'variant'; + /** + * @inheritDoc + */ + public $searchEnabled = true; + /** * @inheritDoc */ diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index c01ab1f..e8f6542 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -119,7 +119,7 @@ public function __construct($id = null, $parentResourceUrl = '') $config = ShopifySDK::$config; - $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['AdminUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); + $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; @@ -293,20 +293,6 @@ public function generateUrl($urlParams = array(), $customAction = null) return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } - /** - * Generate the custom url for api request based on the params and custom action (if any) - * - * @param array $urlParams - * @param string $customAction - * - * @return string - */ - public function addShopUrl($urlParams = array(), $url) - { - - return $this->resourceUrl . ($url ? "/$url" : '') . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); - } - /** * Generate a HTTP GET request and return results as an array * @@ -406,13 +392,7 @@ public function post($dataArray, $url = null, $wrapData = true) public function put($dataArray, $url = null, $wrapData = true) { - /*if (!$url) { - $url = $this->generateUrl(); - } else { - $url = $this->addShopUrl($dataArray, $url); - }*/ if (!$url) $url = $this->generateUrl(); - if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); @@ -466,38 +446,16 @@ protected function wrapData($dataArray, $dataKey = null) */ protected function castString($array) { - if (is_string($array)) return $array; + if ( ! is_array($array)) return (string) $array; $string = ''; $i = 0; foreach ($array as $key => $val) { - - if ($key === "line_items") { - $line_id = array_keys($val)[0]; - $val = reset($val); - - if (isset($val['quantity']) && isset($val['quantity'][0]) && isset($val['quantity'][0]['message'])) { - $string = "Line item {$line_id} {$val['quantity'][0]['message']}"; - return $string; - } - - - } //Add values separated by comma //prepend the key string, if it's an associative key //Check if the value itself is another array to be converted to string - if (is_array($val) || is_object($val)) { - $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; - $i++; - } else if (is_array($key) || is_object($key)) { - $string .= ($i === $key ? '' : "$key - ") . $this->castString($key) . ', '; - $i++; - } else if (!is_array($val)) { - $string .= $val . ", "; - } else if (!is_array($key)) { - $string .= $key . " - "; - } - + $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; + $i++; } //Remove trailing comma and space @@ -533,24 +491,9 @@ public function processResponse($responseArray, $dataKey = null) } if (isset($responseArray['errors'])) { + $message = $this->castString($responseArray['errors']); - $message = $responseArray; - - if (is_array($message['errors'])) { - foreach ($message['errors'] as $key => $error) { - if (is_array($message['errors'][$key])) { - - throw new ApiException($this->castString($message['errors'])); - } else { - throw new ApiException($key . " " . $error); - } - - } - - } else { - throw new ApiException($responseArray['errors']); - } - + throw new ApiException($message); } if ($dataKey && isset($responseArray[$dataKey])) { diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index c9b05c1..c9040ba 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -60,6 +60,9 @@ | //Get variants of a product (using Child resource) | $products = $shopify->Product($productID)->Variant->get(); | +| //GraphQL +| $data = $shopify->GraphQL->post($graphQL); +| */ use PHPShopify\Exception\SdkException; @@ -70,10 +73,14 @@ * @property-read Collect $Collect * @property-read Comment $Comment * @property-read Country $Country + * @property-read Currency $Currency * @property-read CustomCollection $CustomCollection * @property-read Customer $Customer * @property-read CustomerSavedSearch $CustomerSavedSearch * @property-read Discount $Discount + * @property-read DiscountCode $DiscountCode + * @property-read DraftOrder $DraftOrder + * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard @@ -83,13 +90,10 @@ * @property-read Metafield $Metafield * @property-read Multipass $Multipass * @property-read Order $Order - * @property-read DraftOrder $DraftOrder - * @property-read Checkout $Checkout * @property-read Page $Page * @property-read Policy $Policy * @property-read Product $Product * @property-read ProductListing $ProductListing - * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect @@ -100,6 +104,7 @@ * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook + * @property-read GraphQL $GraphQL * * @method AbandonedCheckout AbandonedCheckout(integer $id = null) * @method Blog Blog(integer $id = null) @@ -107,10 +112,14 @@ * @method Collect Collect(integer $id = null) * @method Comment Comment(integer $id = null) * @method Country Country(integer $id = null) + * @method Currency Currency(integer $id = null) * @method CustomCollection CustomCollection(integer $id = null) * @method Customer Customer(integer $id = null) * @method CustomerSavedSearch CustomerSavedSearch(integer $id = null) * @method Discount Discount(integer $id = null) + * @method DraftOrder DraftOrder(integer $id = null) + * @method DiscountCode DiscountCode(integer $id = null) + * @method PriceRule PriceRule(integer $id = null) * @method Event Event(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) * @method GiftCard GiftCard(integer $id = null) @@ -120,14 +129,11 @@ * @method Metafield Metafield(integer $id = null) * @method Multipass Multipass(integer $id = null) * @method Order Order(integer $id = null) - * @method DraftOrder DraftOrder(integer $id = null) - * @method Checkout Checkout(integer $id = null) * @method Page Page(integer $id = null) * @method Policy Policy(integer $id = null) * @method Product Product(integer $id = null) - * @method ProductVariant ProductVariant(integer $id = null) * @method ProductListing ProductListing(integer $id = null) - * @method CollectionListing CollectionListing(integer $id = null) + * @method ProductVariant ProductVariant(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) @@ -137,26 +143,10 @@ * @method Theme Theme(int $id = null) * @method User User(integer $id = null) * @method Webhook Webhook(integer $id = null) + * @method GraphQL GraphQL() */ class ShopifySDK { - /** - * @var float microtime of last api call - */ - public static $microtimeOfLastApiCall; - - /** - * @var float Minimum gap in seconds to maintain between 2 api calls - */ - public static $timeAllowedForEachApiCall = .01; - - /** - * Shop / API configurations - * - * @var array - */ - public static $config = array(); - /** * List of available resources which can be called from this client * @@ -170,10 +160,13 @@ class ShopifySDK 'Collect', 'Comment', 'Country', + 'Currency', 'CustomCollection', 'Customer', 'CustomerSavedSearch', 'Discount', + 'DiscountCode', + 'DraftOrder', 'Event', 'FulfillmentService', 'GiftCard', @@ -183,14 +176,11 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', - 'DraftOrder', - 'Checkout', 'Page', 'Policy', 'Product', - 'ProductVariant', 'ProductListing', - 'CollectionListing', + 'ProductVariant', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -242,7 +232,6 @@ class ShopifySDK 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', - 'ShippingRate' => 'Checkout', 'UsageCharge' => 'RecurringApplicationCharge', ); @@ -256,8 +245,7 @@ class ShopifySDK public function __construct($config = array()) { if(!empty($config)) { - ShopifySDK::$config = $config; - ShopifySDK::setAdminUrl(); + ShopifySDK::config($config); } } @@ -353,6 +341,7 @@ public static function setAdminUrl() //Remove https:// and trailing slash (if provided) $shopUrl = preg_replace('#^https?://|/$#', '', $shopUrl); + $apiVersion = self::$config['ApiVersion']; if(isset(self::$config['ApiKey']) && isset(self::$config['Password'])) { $apiKey = self::$config['ApiKey']; @@ -363,6 +352,7 @@ public static function setAdminUrl() } self::$config['AdminUrl'] = $adminUrl; + self::$config['ApiUrl'] = $adminUrl . "api/$apiVersion/"; return $adminUrl; } @@ -376,6 +366,15 @@ public static function getAdminUrl() { return self::$config['AdminUrl']; } + /** + * Get the api url of the configured shop + * + * @return string + */ + public static function getApiUrl() { + return self::$config['ApiUrl']; + } + /** * Maintain maximum 2 calls per second to the API * diff --git a/tests/CurrencyTest.php b/tests/CurrencyTest.php new file mode 100644 index 0000000..70ac526 --- /dev/null +++ b/tests/CurrencyTest.php @@ -0,0 +1,15 @@ + 'phpclassic.myshopify.com', - 'ApiKey' => '81781200c08b31208031f983ab930f2a', - 'Password' => '5260904f8293bce93ddd4d65c535faa4', + 'ShopUrl' => getenv('SHOPIFY_SHOP_URL'), //Your shop URL + 'ApiKey' => getenv('SHOPIFY_API_KEY'), //Your Private API Key + 'Password' => getenv('SHOPIFY_API_PASSWORD'), //Your Private API Password ); self::$shopify = ShopifySDK::config($config); From d23e51bd9d4f929d21a703c82457e62eaf0235fb Mon Sep 17 00:00:00 2001 From: TuunStudio <55007058+TuunStudio@users.noreply.github.com> Date: Fri, 6 Sep 2019 19:01:52 -0700 Subject: [PATCH 18/84] Delete .DS_Store --- .DS_Store | Bin 8196 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index e4db4f5337e95f314e5e1b05027f42f576948c52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMK~Eb;6n+CvS(_4;1PW9=SVfg01w}ZCLZu4DF`(3YMIv?_=3X2JLHO5B=nm7djW?+sB`thqjFs}^2VuD?b zu%Iwa1!^iwQVf=44ouTAUrhYF8a16*d}YK*uFR4Qg(Zmx%g64-iW*I86fg?(E1+`s z4Ben6c@(Ac_xCV&?ccb>#9Yr0zD*J&DIlKWe4pYBgA>8ep?$DIj1H+q{&^OgOtD6& zj_56FqYa6Je}g_lPm)M6zq|m`2d_?hDdyiP<{d#-#AL6*;qu@UPc-B~Np?gdjN?@Ggk@!|Y|WlNKE7Ef z%;s<1diP{De_WU^%+KcU%-=aV$y$@|6c);lTZc!-pPzj34c3Cfk^`1T_4u^?7QI9T zufCtGkNwzN-ygQ!TVXag{QAh{QG4vl)v@uhiHV6fCMTw*-<+=5S6!#R*9_FXE=pep zCBMVBn}OG;ww<~snwt@S5>>q;UbC;HT-SKx(NojqA6Y^-Y zW{-yTK>B_~hF;`Jv2|v4>p=+B%Movt#g4=k_o4D?Te*cBJP3h({LL*V2prMmrNf85 z<8pshlw=gi)}~jNLf`pl92Ub$z*WeZyMJjBXNoJm&Nbb!X-0SO7H+e3;k0;mrdZLo zEFOv1=^lMZ8?;A9^c8(aKhRI~GyP6~(m!m7+3an0o!w>&>@ItsmDvibu#efNnm*9` zA>ZmVrvGetrW9JfLb8P;I!jz{vY|eUOXrBKN!4#vw z1r!+6K2s|HUw;1g{|iJiE=B>Pz{{!t%PduwO1S&p8$)^_D%Z}VK1NkV`E@nM6f|)< j4vf=r;ORdM(a*zGaf%6cHNt{oJ_N)vn8qmZzbfz_ql{Gv From da78ce916a874cac897cd16ced969142786a2272 Mon Sep 17 00:00:00 2001 From: TuunStudio <55007058+TuunStudio@users.noreply.github.com> Date: Fri, 6 Sep 2019 19:02:14 -0700 Subject: [PATCH 19/84] Delete .DS_Store --- lib/.DS_Store | Bin 10244 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/.DS_Store diff --git a/lib/.DS_Store b/lib/.DS_Store deleted file mode 100644 index 4d34aed146a9b61f69f4ad5dd618a4cdd197aa41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 zcmeHM-EJF26h0ffuuh!BO#+1*W&!5=kf>$la;l`HkEIxd(C&p^AfYE0L2}YU#j9w*Zhu+}4I`>;n{2P2^T0C$Us0bjG^}FM)c= z7Q>6-_`S;RkXwnI#4?797sJI%M)r~&il?IkSJm#~ZN)M_=N`yCFzo^E-CvV!X-We- z_w@d~i+y}co?}NpmXW+Ged$VnKZsh*AZh}|M7xjU;16xMiZ%7oGmsvz4d8nEMqn4% zh79BxblNrjp3$@bn+A$+L7M~RO){twP(yq=E{o^h$RL)0;QYFB52ygPcQCHi#W7a80Fa1pGfFIz6} zTw_er@r+SNas*W9LXibtF~x!yHCDtxPVL?iMBG3v!@@DV638J&`wG_`MAE@LM&3ZL zDVqHwSZmHT=Abo37rLiTstMheQtK7*VUSN(LWPehz%sQtmxdhQX8TOOP_;a%#^oFC2 zjbFUELh;%wuloMHKfgHtWxX4n)JKicc(~ITe9mw88>4PBxqc8u!Nql>`LNeHd0biB zjlxl*7j^~;?zKDU^2MWG*p5ziqH)+Bs-AlMgYTF8(&NgTXJ?zW+Isco?Tz#G>RIj9 z=FRo$?b_z~dC9-=cJ00WCr77WpM7)w?RUs`G%s0H$#VSt;`}|X=+BIIj_ZRkh{9)t z;#ijV!HxjY>S=|USuI#ay|gda9Oz4;sJWBY^SVRN6|1aaQ&~R1T*$t-Vz=$Q)qF|Y zu!^V12%mnYhJA<};+t_R`!##gTCU4J`Z8z9;Ru;+V1`LkltZ~#1Y z1`Zvl$A*rw9AND9Q`~Ajv&q0M%EuUGq}j{oT!6f{BAHcFNb-m|*azNOH_0IbS-PxB zkSzvcF@-qP8u#38B`d6f^x^G>dnH3o82#tbpHE({h#b#CGQhwr0>f1TYto_S){M$o zSWiWML6NMrW1t;RPu5HWRmq_C!L%83WXuixN(O(R9%URu*XoGTpQ+;^ET9j#E_KX< zJTP>W6Fa!33e|g7=8+8s?k&yN7JL@3&S>>EIQTbKvgC`QyR5p~u!w5`XH3W&12?a@ zr?83Za_c!}bmwV@oGu_h)%ia(?n zvMiq}j=Q%j?c@x!uW08=nyfJTFUTW|)5HuTd>mDoTrg0Qx0m)nAx2dB{{OD~|NocP zn*(wW Date: Fri, 6 Sep 2019 19:15:25 -0700 Subject: [PATCH 20/84] added sales channel sdk endpoints --- lib/ShopifySDK.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index c9040ba..6c98d63 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -80,6 +80,7 @@ * @property-read Discount $Discount * @property-read DiscountCode $DiscountCode * @property-read DraftOrder $DraftOrder + * @property-read Checkout $Checkout * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService @@ -94,6 +95,7 @@ * @property-read Policy $Policy * @property-read Product $Product * @property-read ProductListing $ProductListing + * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect @@ -129,11 +131,13 @@ * @method Metafield Metafield(integer $id = null) * @method Multipass Multipass(integer $id = null) * @method Order Order(integer $id = null) + * @method Checkout Checkout(integer $id = null) * @method Page Page(integer $id = null) * @method Policy Policy(integer $id = null) * @method Product Product(integer $id = null) * @method ProductListing ProductListing(integer $id = null) * @method ProductVariant ProductVariant(integer $id = null) + * @method CollectionListing CollectionListing(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) @@ -176,11 +180,13 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', + 'Checkout', 'Page', 'Policy', 'Product', 'ProductListing', 'ProductVariant', + 'CollectionListing', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -232,6 +238,7 @@ class ShopifySDK 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', + 'ShippingRate' => 'Checkout', 'UsageCharge' => 'RecurringApplicationCharge', ); From d33273606add67918922bf9fe01075487d962062 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 19:24:51 -0700 Subject: [PATCH 21/84] removed customer order --- lib/CustomerOrder.php | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 lib/CustomerOrder.php diff --git a/lib/CustomerOrder.php b/lib/CustomerOrder.php deleted file mode 100644 index 3735dd8..0000000 --- a/lib/CustomerOrder.php +++ /dev/null @@ -1,37 +0,0 @@ - - * Created at 8/19/16 12:07 PM UTC+06:00 - * - * @see https://help.shopify.com/api/reference/customeraddress Shopify API Reference for CustomerAddress - */ - -namespace PHPShopify; - - -/** - * -------------------------------------------------------------------------- - * CustomerOrder -> Custom actions - * -------------------------------------------------------------------------- - * @method array get() Sets the address as default for the customer - * - */ -class CustomerOrder extends ShopifyResource -{ - /** - * @inheritDoc - */ - protected $resourceKey = 'order'; - - - /** - * @inheritDoc - */ - protected function pluralizeKey() - { - return 'orders'; - } - - -} From c5bfe0ea3f1f48bdffff99beac2d8dc353fff632 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 20 Dec 2019 17:14:58 -0800 Subject: [PATCH 22/84] added support for cursor based pagination --- lib/CurlRequest.php | 5 +- lib/HttpRequestJson.php | 6 +- lib/ShopifyResource.php | 168 ++++++++++++++++++++++------------------ 3 files changed, 98 insertions(+), 81 deletions(-) diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index 87014b5..943d884 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -170,7 +170,8 @@ protected static function processRequest($ch) // close curl resource to free up system resources curl_close($ch); - return $response->getBody(); + return $response; + } - + } diff --git a/lib/HttpRequestJson.php b/lib/HttpRequestJson.php index ee8372a..1b8b098 100644 --- a/lib/HttpRequestJson.php +++ b/lib/HttpRequestJson.php @@ -131,12 +131,12 @@ public static function delete($url, $httpHeaders = array()) * * @param string $response * - * @return array + * @return string */ protected static function processResponse($response) { - return json_decode($response, true); + return $response; } -} \ No newline at end of file +} diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index e8f6542..4263c2d 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -6,13 +6,10 @@ * * @see https://help.shopify.com/api/reference Shopify API Reference */ - namespace PHPShopify; - use PHPShopify\Exception\ApiException; use PHPShopify\Exception\SdkException; use PHPShopify\Exception\CurlException; - /* |-------------------------------------------------------------------------- | Shopify API SDK Base Class @@ -29,7 +26,12 @@ abstract class ShopifyResource * @var array */ protected $httpHeaders = array(); - + /** + * HTTP response headers + * + * @var array + */ + protected static $httpResponseHeaders = array(); /** * The base URL of the API Resource (excluding the '.json' extension). * @@ -38,14 +40,12 @@ abstract class ShopifyResource * @var string */ protected $resourceUrl; - /** * Key of the API Resource which is used to fetch data from request responses * * @var string */ protected $resourceKey; - /** * List of child Resource names / classes * @@ -55,28 +55,24 @@ abstract class ShopifyResource * @var array */ protected $childResource = array(); - /** * If search is enabled for the resource * * @var boolean */ public $searchEnabled = false; - /** * If count is enabled for the resource * * @var boolean */ public $countEnabled = true; - /** * If the resource is read only. (No POST / PUT / DELETE actions) * * @var boolean */ public $readOnly = false; - /** * List of custom GET / POST / PUT / DELETE actions * @@ -95,7 +91,6 @@ abstract class ShopifyResource protected $customPostActions = array(); protected $customPutActions = array(); protected $customDeleteActions = array(); - /** * The ID of the resource * @@ -104,7 +99,6 @@ abstract class ShopifyResource * @var integer */ public $id; - /** * Create a new Shopify API resource instance. * @@ -116,18 +110,14 @@ abstract class ShopifyResource public function __construct($id = null, $parentResourceUrl = '') { $this->id = $id; - $config = ShopifySDK::$config; - $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); - if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; } elseif (!isset($config['ApiKey']) || !isset($config['Password'])) { throw new SdkException("Either AccessToken or ApiKey+Password Combination (in case of private API) is required to access the resources. Please check SDK configuration!"); } } - /** * Return ShopifyResource instance for the child resource. * @@ -142,7 +132,6 @@ public function __get($childName) { return $this->$childName(); } - /** * Return ShopifyResource instance for the child resource or call a custom action for the resource * @@ -166,23 +155,16 @@ public function __call($name, $arguments) if (ctype_upper($name[0])) { //Get the array key of the childResource in the childResource array $childKey = array_search($name, $this->childResource); - if ($childKey === false) { throw new SdkException("Child Resource $name is not available for " . $this->getResourceName()); } - //If any associative key is given to the childname, then it will be considered as the class name, //otherwise the childname will be the class name $childClassName = !is_numeric($childKey) ? $childKey : $name; - $childClass = __NAMESPACE__ . "\\" . $childClassName; - //If first argument is provided, it will be considered as the ID of the resource. $resourceID = !empty($arguments) ? $arguments[0] : null; - - $api = new $childClass($resourceID, $this->resourceUrl); - return $api; } else { $actionMaps = array( @@ -191,30 +173,23 @@ public function __call($name, $arguments) 'get' => 'customGetActions', 'delete'=> 'customDeleteActions', ); - //Get the array key for the action in the actions array foreach ($actionMaps as $httpMethod => $actionArrayKey) { $actionKey = array_search($name, $this->$actionArrayKey); if ($actionKey !== false) break; } - if ($actionKey === false) { throw new SdkException("No action named $name is defined for " . $this->getResourceName()); } - //If any associative key is given to the action, then it will be considered as the method name, //otherwise the action name will be the method name $customAction = !is_numeric($actionKey) ? $actionKey : $name; - - //Get the first argument if provided with the method call $methodArgument = !empty($arguments) ? $arguments[0] : array(); - //Url parameters $urlParams = array(); //Data body $dataArray = array(); - //Consider the argument as url parameters for get and delete request //and data array for post and put request if ($httpMethod == 'post' || $httpMethod == 'put') { @@ -222,9 +197,7 @@ public function __call($name, $arguments) } else { $urlParams = $methodArgument; } - $url = $this->generateUrl($urlParams, $customAction); - if ($httpMethod == 'post' || $httpMethod == 'put') { return $this->$httpMethod($dataArray, $url, false); } else { @@ -232,7 +205,6 @@ public function __call($name, $arguments) } } } - /** * Get the resource name (or the class name) * @@ -242,7 +214,6 @@ public function getResourceName() { return substr(get_called_class(), strrpos(get_called_class(), '\\') + 1); } - /** * Get the resource key to be used for while sending data to the API * @@ -254,7 +225,6 @@ public function getResourcePostKey() { return $this->resourceKey; } - /** * Get the pluralized version of the resource key * @@ -266,7 +236,6 @@ protected function pluralizeKey() { return $this->resourceKey . 's'; } - /** * Get the resource path to be used to generate the api url * @@ -279,7 +248,6 @@ protected function getResourcePath() { return $this->pluralizeKey(); } - /** * Generate the custom url for api request based on the params and custom action (if any) * @@ -292,7 +260,6 @@ public function generateUrl($urlParams = array(), $customAction = null) { return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } - /** * Generate a HTTP GET request and return results as an array * @@ -307,15 +274,10 @@ public function generateUrl($urlParams = array(), $customAction = null) public function get($urlParams = array(), $url = null, $dataKey = null) { if (!$url) $url = $this->generateUrl($urlParams); - $response = HttpRequestJson::get($url, $this->httpHeaders); - if (!$dataKey) $dataKey = $this->id ? $this->resourceKey : $this->pluralizeKey(); - return $this->processResponse($response, $dataKey); - } - /** * Get count for the number of resources available * @@ -328,12 +290,9 @@ public function count($urlParams = array()) if (!$this->countEnabled) { throw new SdkException("Count is not available for " . $this->getResourceName()); } - $url = $this->generateUrl($urlParams, 'count'); - return $this->get(array(), $url, 'count'); } - /** * Search within the resouce * @@ -348,14 +307,10 @@ public function search($query) if (!$this->searchEnabled) { throw new SdkException("Search is not available for " . $this->getResourceName()); } - if (!is_array($query)) $query = array('query' => $query); - $url = $this->generateUrl($query, 'search'); - return $this->get(array(), $url); } - /** * Call POST method to create a new resource * @@ -370,14 +325,10 @@ public function search($query) public function post($dataArray, $url = null, $wrapData = true) { if (!$url) $url = $this->generateUrl(); - if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); - $response = HttpRequestJson::post($url, $dataArray, $this->httpHeaders); - return $this->processResponse($response, $this->resourceKey); } - /** * Call PUT method to update an existing resource * @@ -391,16 +342,11 @@ public function post($dataArray, $url = null, $wrapData = true) */ public function put($dataArray, $url = null, $wrapData = true) { - if (!$url) $url = $this->generateUrl(); - if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); - $response = HttpRequestJson::put($url, $dataArray, $this->httpHeaders); - return $this->processResponse($response, $this->resourceKey); } - /** * Call DELETE method to delete an existing resource * @@ -414,12 +360,9 @@ public function put($dataArray, $url = null, $wrapData = true) public function delete($urlParams = array(), $url = null) { if (!$url) $url = $this->generateUrl($urlParams); - $response = HttpRequestJson::delete($url, $this->httpHeaders); - return $this->processResponse($response); } - /** * Wrap data array with resource key * @@ -431,10 +374,8 @@ public function delete($urlParams = array(), $url = null) protected function wrapData($dataArray, $dataKey = null) { if (!$dataKey) $dataKey = $this->getResourcePostKey(); - return array($dataKey => $dataArray); } - /** * Convert an array to string * @@ -447,7 +388,6 @@ protected function wrapData($dataArray, $dataKey = null) protected function castString($array) { if ( ! is_array($array)) return (string) $array; - $string = ''; $i = 0; foreach ($array as $key => $val) { @@ -457,17 +397,14 @@ protected function castString($array) $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; $i++; } - //Remove trailing comma and space $string = rtrim($string, ', '); - return $string; } - /** * Process the request response * - * @param array $responseArray Request response in array format + * @param string $response Request response in array format * @param string $dataKey Keyname to fetch data from response array * * @throws ApiException if the response has an error specified @@ -475,31 +412,110 @@ protected function castString($array) * * @return array */ - public function processResponse($responseArray, $dataKey = null) + public function processResponse($response, $dataKey = null) { + $responseArray = json_decode($response->getBody(), true); if ($responseArray === null) { //Something went wrong, Checking HTTP Codes $httpOK = 200; //Request Successful, OK. $httpCreated = 201; //Create Successful. - //should be null if any other library used for http calls $httpCode = CurlRequest::$lastHttpCode; - if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated) { throw new Exception\CurlException("Request failed with HTTP Code $httpCode."); } } - if (isset($responseArray['errors'])) { $message = $this->castString($responseArray['errors']); - throw new ApiException($message); } - + self::$httpResponseHeaders = $response->getHeaders(); if ($dataKey && isset($responseArray[$dataKey])) { return $responseArray[$dataKey]; } else { return $responseArray; } } + /** + * Checks response headers for existence of next page info + * + * @return boolean + */ + static public function lastResourceContainsNextPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"next\"/", $headers["Link"], $matchData)) { + // found rel="next" + return true; + } + } + return false; + } + /** + * Checks response headers for existence of previous page info + * + * @return boolean + */ + static public function lastResourceContainsPrevPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"previous\"/", $headers["Link"], $matchData)) { + // found rel="prev" + return true; + } + } + return false; + } + /** + * Gets next page info string for use in pagination + * + * @return string + */ + static public function getNextPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"next\"/", $headers["Link"], $matchData)) { + // found rel="next" + $query = parse_url($matchData[1], PHP_URL_QUERY); + $pairs = explode( "&", $query ); + foreach( $pairs as $p ) { + list( $key, $value) = explode( "=", $p ); + if( $key == "page_info" ) { + return $value; + } + } + } + } + return false; + } + /** + * Gets previous page info string for use in pagination + * + * @return string + */ + static public function getPrevPageInfo() + { + $headers = self::$httpResponseHeaders; + if (isset($headers["Link"])) { + $matchData = array(); + if (preg_match("/<([^>]*)>; rel=\"previous\"/", $headers["Link"], $matchData)) { + // found rel="prev" + $query = parse_url($matchData[1], PHP_URL_QUERY); + $pairs = explode( "&", $query ); + foreach( $pairs as $p ) { + list( $key, $value) = explode( "=", $p ); + if( $key == "page_info" ) { + return $value; + } + } + } + } + return false; + } } From 4e13a2af149a952058b74e8695b67dfdda808e08 Mon Sep 17 00:00:00 2001 From: Zachary Fair Date: Mon, 27 Jan 2020 12:30:52 -0800 Subject: [PATCH 23/84] fixed get access token request --- lib/AuthHelper.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index e879964..84cfabc 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -169,8 +169,10 @@ public static function getAccessToken() ); $response = HttpRequestJson::post($config['AdminUrl'] . 'oauth/access_token', $data); + $body = json_decode($response->getBody(), true); - return isset($response['access_token']) ? $response['access_token'] : null; + return isset($body['access_token']) ? $body['access_token'] : null; + } else { throw new SdkException("This request is not initiated from a valid shopify shop!"); } From 304761d221d8f5dcd373917cb92c4c76dfe8ec69 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 15 Apr 2020 12:03:19 -0700 Subject: [PATCH 24/84] fixed conflicts --- lib/ShopifyResource.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 1f6c446..495bca7 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -10,11 +10,7 @@ use PHPShopify\Exception\ApiException; use PHPShopify\Exception\SdkException; use PHPShopify\Exception\CurlException; -<<<<<<< HEAD -======= -use Psr\Http\Message\ResponseInterface; ->>>>>>> a8ab4fda3087c48d3c8b6c17cdd7da0a2ed91108 /* |-------------------------------------------------------------------------- | Shopify API SDK Base Class @@ -459,12 +455,9 @@ protected function castString($array) */ public function processResponse($response, $dataKey = null) { -<<<<<<< HEAD + $responseArray = json_decode($response->getBody(), true); -======= - self::$lastHttpResponseHeaders = CurlRequest::$lastHttpResponseHeaders; ->>>>>>> a8ab4fda3087c48d3c8b6c17cdd7da0a2ed91108 if ($responseArray === null) { //Something went wrong, Checking HTTP Codes $httpOK = 200; //Request Successful, OK. From ddf9bce291dfe455e279dc71ad40399ad0691513 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 15 Apr 2020 12:21:44 -0700 Subject: [PATCH 25/84] fixed shopify resource --- lib/ShopifyResource.php | 84 +++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 495bca7..66ce2b7 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -6,10 +6,13 @@ * * @see https://help.shopify.com/api/reference Shopify API Reference */ + namespace PHPShopify; + use PHPShopify\Exception\ApiException; use PHPShopify\Exception\SdkException; use PHPShopify\Exception\CurlException; +use Psr\Http\Message\ResponseInterface; /* |-------------------------------------------------------------------------- @@ -27,12 +30,7 @@ abstract class ShopifyResource * @var array */ protected $httpHeaders = array(); - /** - * HTTP response headers - * - * @var array - */ - protected static $httpResponseHeaders = array(); + /** * HTTP response headers of last executed request * @@ -48,12 +46,14 @@ abstract class ShopifyResource * @var string */ protected $resourceUrl; + /** * Key of the API Resource which is used to fetch data from request responses * * @var string */ protected $resourceKey; + /** * List of child Resource names / classes * @@ -63,24 +63,28 @@ abstract class ShopifyResource * @var array */ protected $childResource = array(); + /** * If search is enabled for the resource * * @var boolean */ public $searchEnabled = false; + /** * If count is enabled for the resource * * @var boolean */ public $countEnabled = true; + /** * If the resource is read only. (No POST / PUT / DELETE actions) * * @var boolean */ public $readOnly = false; + /** * List of custom GET / POST / PUT / DELETE actions * @@ -99,6 +103,7 @@ abstract class ShopifyResource protected $customPostActions = array(); protected $customPutActions = array(); protected $customDeleteActions = array(); + /** * The ID of the resource * @@ -107,6 +112,7 @@ abstract class ShopifyResource * @var integer */ public $id; + /** * Create a new Shopify API resource instance. * @@ -133,14 +139,18 @@ abstract class ShopifyResource public function __construct($id = null, $parentResourceUrl = '') { $this->id = $id; + $config = ShopifySDK::$config; + $this->resourceUrl = ($parentResourceUrl ? $parentResourceUrl . '/' : $config['ApiUrl']) . $this->getResourcePath() . ($this->id ? '/' . $this->id : ''); + if (isset($config['AccessToken'])) { $this->httpHeaders['X-Shopify-Access-Token'] = $config['AccessToken']; } elseif (!isset($config['ApiKey']) || !isset($config['Password'])) { throw new SdkException("Either AccessToken or ApiKey+Password Combination (in case of private API) is required to access the resources. Please check SDK configuration!"); } } + /** * Return ShopifyResource instance for the child resource. * @@ -155,6 +165,7 @@ public function __get($childName) { return $this->$childName(); } + /** * Return ShopifyResource instance for the child resource or call a custom action for the resource * @@ -178,16 +189,23 @@ public function __call($name, $arguments) if (ctype_upper($name[0])) { //Get the array key of the childResource in the childResource array $childKey = array_search($name, $this->childResource); + if ($childKey === false) { throw new SdkException("Child Resource $name is not available for " . $this->getResourceName()); } + //If any associative key is given to the childname, then it will be considered as the class name, //otherwise the childname will be the class name $childClassName = !is_numeric($childKey) ? $childKey : $name; + $childClass = __NAMESPACE__ . "\\" . $childClassName; + //If first argument is provided, it will be considered as the ID of the resource. $resourceID = !empty($arguments) ? $arguments[0] : null; + + $api = new $childClass($resourceID, $this->resourceUrl); + return $api; } else { $actionMaps = array( @@ -196,23 +214,30 @@ public function __call($name, $arguments) 'get' => 'customGetActions', 'delete'=> 'customDeleteActions', ); + //Get the array key for the action in the actions array foreach ($actionMaps as $httpMethod => $actionArrayKey) { $actionKey = array_search($name, $this->$actionArrayKey); if ($actionKey !== false) break; } + if ($actionKey === false) { throw new SdkException("No action named $name is defined for " . $this->getResourceName()); } + //If any associative key is given to the action, then it will be considered as the method name, //otherwise the action name will be the method name $customAction = !is_numeric($actionKey) ? $actionKey : $name; + + //Get the first argument if provided with the method call $methodArgument = !empty($arguments) ? $arguments[0] : array(); + //Url parameters $urlParams = array(); //Data body $dataArray = array(); + //Consider the argument as url parameters for get and delete request //and data array for post and put request if ($httpMethod == 'post' || $httpMethod == 'put') { @@ -220,7 +245,9 @@ public function __call($name, $arguments) } else { $urlParams = $methodArgument; } + $url = $this->generateUrl($urlParams, $customAction); + if ($httpMethod == 'post' || $httpMethod == 'put') { return $this->$httpMethod($dataArray, $url, false); } else { @@ -228,6 +255,7 @@ public function __call($name, $arguments) } } } + /** * Get the resource name (or the class name) * @@ -237,6 +265,7 @@ public function getResourceName() { return substr(get_called_class(), strrpos(get_called_class(), '\\') + 1); } + /** * Get the resource key to be used for while sending data to the API * @@ -248,6 +277,7 @@ public function getResourcePostKey() { return $this->resourceKey; } + /** * Get the pluralized version of the resource key * @@ -259,6 +289,7 @@ protected function pluralizeKey() { return $this->resourceKey . 's'; } + /** * Get the resource path to be used to generate the api url * @@ -271,6 +302,7 @@ protected function getResourcePath() { return $this->pluralizeKey(); } + /** * Generate the custom url for api request based on the params and custom action (if any) * @@ -283,6 +315,7 @@ public function generateUrl($urlParams = array(), $customAction = null) { return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); } + /** * Generate a HTTP GET request and return results as an array * @@ -300,10 +333,15 @@ public function generateUrl($urlParams = array(), $customAction = null) public function get($urlParams = array(), $url = null, $dataKey = null) { if (!$url) $url = $this->generateUrl($urlParams); + $response = HttpRequestJson::get($url, $this->httpHeaders); + if (!$dataKey) $dataKey = $this->id ? $this->resourceKey : $this->pluralizeKey(); + return $this->processResponse($response, $dataKey); + } + /** * Get count for the number of resources available * @@ -320,9 +358,12 @@ public function count($urlParams = array()) if (!$this->countEnabled) { throw new SdkException("Count is not available for " . $this->getResourceName()); } + $url = $this->generateUrl($urlParams, 'count'); + return $this->get(array(), $url, 'count'); } + /** * Search within the resouce * @@ -339,10 +380,14 @@ public function search($query) if (!$this->searchEnabled) { throw new SdkException("Search is not available for " . $this->getResourceName()); } + if (!is_array($query)) $query = array('query' => $query); + $url = $this->generateUrl($query, 'search'); + return $this->get(array(), $url); } + /** * Call POST method to create a new resource * @@ -360,10 +405,14 @@ public function search($query) public function post($dataArray, $url = null, $wrapData = true) { if (!$url) $url = $this->generateUrl(); + if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); + $response = HttpRequestJson::post($url, $dataArray, $this->httpHeaders); + return $this->processResponse($response, $this->resourceKey); } + /** * Call PUT method to update an existing resource * @@ -380,11 +429,16 @@ public function post($dataArray, $url = null, $wrapData = true) */ public function put($dataArray, $url = null, $wrapData = true) { + if (!$url) $url = $this->generateUrl(); + if ($wrapData && !empty($dataArray)) $dataArray = $this->wrapData($dataArray); + $response = HttpRequestJson::put($url, $dataArray, $this->httpHeaders); + return $this->processResponse($response, $this->resourceKey); } + /** * Call DELETE method to delete an existing resource * @@ -401,9 +455,12 @@ public function put($dataArray, $url = null, $wrapData = true) public function delete($urlParams = array(), $url = null) { if (!$url) $url = $this->generateUrl($urlParams); + $response = HttpRequestJson::delete($url, $this->httpHeaders); + return $this->processResponse($response); } + /** * Wrap data array with resource key * @@ -415,8 +472,10 @@ public function delete($urlParams = array(), $url = null) protected function wrapData($dataArray, $dataKey = null) { if (!$dataKey) $dataKey = $this->getResourcePostKey(); + return array($dataKey => $dataArray); } + /** * Convert an array to string * @@ -429,6 +488,7 @@ protected function wrapData($dataArray, $dataKey = null) protected function castString($array) { if ( ! is_array($array)) return (string) $array; + $string = ''; $i = 0; foreach ($array as $key => $val) { @@ -438,14 +498,17 @@ protected function castString($array) $string .= ($i === $key ? '' : "$key - ") . $this->castString($val) . ', '; $i++; } + //Remove trailing comma and space $string = rtrim($string, ', '); + return $string; } + /** * Process the request response * - * @param string $response Request response in array format + * @param array $responseArray Request response in array format * @param string $dataKey Keyname to fetch data from response array * * @throws ApiException if the response has an error specified @@ -453,10 +516,9 @@ protected function castString($array) * * @return array */ - public function processResponse($response, $dataKey = null) + public function processResponse($responseArray, $dataKey = null) { - - $responseArray = json_decode($response->getBody(), true); + self::$lastHttpResponseHeaders = CurlRequest::$lastHttpResponseHeaders; if ($responseArray === null) { //Something went wrong, Checking HTTP Codes @@ -480,7 +542,7 @@ public function processResponse($response, $dataKey = null) throw new ApiException($message, CurlRequest::$lastHttpCode); } - self::$httpResponseHeaders = $response->getHeaders(); + if ($dataKey && isset($responseArray[$dataKey])) { return $responseArray[$dataKey]; } else { From 01ddc1b4393810a48b372e18135f915c4b6dc638 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 15 Apr 2020 13:34:48 -0700 Subject: [PATCH 26/84] added json_decode --- lib/ShopifyResource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 66ce2b7..6b1b1b3 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -544,9 +544,9 @@ public function processResponse($responseArray, $dataKey = null) } if ($dataKey && isset($responseArray[$dataKey])) { - return $responseArray[$dataKey]; + return json_decode($responseArray[$dataKey]); } else { - return $responseArray; + return json_decode($responseArray); } } From 5bdf347a80e61bb480de3c5e336e80d19e2d57df Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 15 Apr 2020 15:17:34 -0700 Subject: [PATCH 27/84] paging --- lib/ShopifyResource.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 6b1b1b3..a618dd9 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -516,11 +516,12 @@ protected function castString($array) * * @return array */ - public function processResponse($responseArray, $dataKey = null) + public function processResponse($response, $dataKey = null) { + self::$lastHttpResponseHeaders = CurlRequest::$lastHttpResponseHeaders; - if ($responseArray === null) { + if ($response === null) { //Something went wrong, Checking HTTP Codes $httpOK = 200; //Request Successful, OK. $httpCreated = 201; //Create Successful. @@ -534,7 +535,10 @@ public function processResponse($responseArray, $dataKey = null) } } + $responseArray = json_decode($response, true); + $lastResponseHeaders = CurlRequest::$lastHttpResponseHeaders; + $this->getLinks($lastResponseHeaders); if (isset($responseArray['errors'])) { @@ -544,9 +548,9 @@ public function processResponse($responseArray, $dataKey = null) } if ($dataKey && isset($responseArray[$dataKey])) { - return json_decode($responseArray[$dataKey]); + return $responseArray[$dataKey]; } else { - return json_decode($responseArray); + return $responseArray; } } From c5121206ccbc34b71d6fb9081a21f554ae4d0076 Mon Sep 17 00:00:00 2001 From: Saiful Islam Date: Thu, 16 Apr 2020 15:31:54 +0600 Subject: [PATCH 28/84] remove extra Parentheses --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 34ad31c..28bee4a 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ $shopify->Order($orderID)->put($updateInfo); ```php $webHookID = 453487303; -$shopify->Webhook($webHookID)->delete()); +$shopify->Webhook($webHookID)->delete(); ``` From e71baca5f2524687d50958825aa9a88c7b1cbfe1 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Tue, 21 Apr 2020 14:58:38 -0700 Subject: [PATCH 29/84] fixed auth helper --- lib/AuthHelper.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index 84cfabc..43ca912 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -169,10 +169,16 @@ public static function getAccessToken() ); $response = HttpRequestJson::post($config['AdminUrl'] . 'oauth/access_token', $data); - $body = json_decode($response->getBody(), true); + + if (!is_string($response) && is_object($response)) { + $body = json_decode($response->getBody(), true); + } else if (is_string($response)) { + $body = json_decode($response, true); + } + return isset($body['access_token']) ? $body['access_token'] : null; - + } else { throw new SdkException("This request is not initiated from a valid shopify shop!"); } From eebe407d4b22d53ef6f79fa468655a1bea818bb8 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Thu, 23 Apr 2020 15:39:53 -0700 Subject: [PATCH 30/84] updated to current version --- lib/GraphQL.php | 2 +- lib/HttpRequestGraphQL.php | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/GraphQL.php b/lib/GraphQL.php index 5fe728a..00f4f4f 100644 --- a/lib/GraphQL.php +++ b/lib/GraphQL.php @@ -45,7 +45,7 @@ public function post($graphQL, $url = null, $wrapData = false, $variables = null if (!$url) $url = $this->generateUrl(); $response = HttpRequestGraphQL::post($url, $graphQL, $this->httpHeaders, $variables); - + return $this->processResponse($response); } diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php index 8d44d15..2d02820 100644 --- a/lib/HttpRequestGraphQL.php +++ b/lib/HttpRequestGraphQL.php @@ -46,12 +46,16 @@ protected static function prepareRequest($httpHeaders = array(), $data = array() if (is_array($variables)) { self::$postDataGraphQL = json_encode(['query' => $data, 'variables' => $variables]); - self::$httpHeaders['Content-type'] = 'application/json'; + $httpHeaders['Content-type'] = 'application/json'; } else { - self::$httpHeaders['Content-type'] = 'application/graphql'; + $httpHeaders['Content-type'] = 'application/graphql'; } - self::$httpHeaders['Content-type'] = 'application/graphql'; + $httpHeaders['Content-type'] = 'application/graphql'; + + $httpHeaders['X-Shopify-Access-Token'] = $httpHeaders['X-Shopify-Access-Token']; + + self::$httpHeaders = $httpHeaders; } /** @@ -66,6 +70,8 @@ protected static function prepareRequest($httpHeaders = array(), $data = array() */ public static function post($url, $data, $httpHeaders = array(), $variables = null) { + $domain = explode('/admin/api', $url)[0]; + $url = "{$domain}/admin/api/graphql.json"; self::prepareRequest($httpHeaders, $data, $variables); $response = CurlRequest::post($url, self::$postDataGraphQL, self::$httpHeaders); From 6eaa205a93362d9a2adcae5cb7b378b83dc9bf88 Mon Sep 17 00:00:00 2001 From: Steffen Persch Date: Tue, 5 May 2020 12:28:31 +0200 Subject: [PATCH 31/84] price rule endpoint supports count --- lib/PriceRule.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/PriceRule.php b/lib/PriceRule.php index de08014..ab77a4e 100644 --- a/lib/PriceRule.php +++ b/lib/PriceRule.php @@ -26,11 +26,6 @@ class PriceRule extends ShopifyResource */ public $resourceKey = 'price_rule'; - /** - * @inheritDoc - */ - public $countEnabled = false; - /** * @inheritDoc */ From 989d7b35788c3d4a1f70e9d5f77b167ce2d88c42 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 14 May 2020 10:08:23 +0200 Subject: [PATCH 32/84] Update ShopifySDK.php Add missing @property-read and @method helpers. --- lib/ShopifySDK.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index e6e66f5..1d9276d 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -68,6 +68,7 @@ /** * @property-read AbandonedCheckout $AbandonedCheckout + * @property-read ApplicationCharge $ApplicationCharge * @property-read Blog $Blog * @property-read CarrierService $CarrierService * @property-read Collect $Collect @@ -81,7 +82,6 @@ * @property-read Discount $Discount * @property-read DiscountCode $DiscountCode * @property-read DraftOrder $DraftOrder - * @property-read PriceRule $PriceRule * @property-read Event $Event * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard @@ -96,8 +96,10 @@ * @property-read Product $Product * @property-read ProductListing $ProductListing * @property-read ProductVariant $ProductVariant + * @property-read PriceRule $PriceRule * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect + * @property-read Report $Report * @property-read ScriptTag $ScriptTag * @property-read ShippingZone $ShippingZone * @property-read Shop $Shop @@ -108,6 +110,7 @@ * @property-read GraphQL $GraphQL * * @method AbandonedCheckout AbandonedCheckout(integer $id = null) + * @method ApplicationCharge ApplicationCharge(integer $id = null) * @method Blog Blog(integer $id = null) * @method CarrierService CarrierService(integer $id = null) * @method Collect Collect(integer $id = null) @@ -121,7 +124,6 @@ * @method Discount Discount(integer $id = null) * @method DraftOrder DraftOrder(integer $id = null) * @method DiscountCode DiscountCode(integer $id = null) - * @method PriceRule PriceRule(integer $id = null) * @method Event Event(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) * @method GiftCard GiftCard(integer $id = null) @@ -136,8 +138,10 @@ * @method Product Product(integer $id = null) * @method ProductListing ProductListing(integer $id = null) * @method ProductVariant ProductVariant(integer $id = null) + * @method PriceRule PriceRule(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) + * @method Report Report(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) * @method ShippingZone ShippingZone(integer $id = null) * @method Shop Shop(integer $id = null) From 270b7dd1d62c9fce3d64ad339913d8ec6311574b Mon Sep 17 00:00:00 2001 From: Victor Kislichenko Date: Mon, 1 Jun 2020 22:35:17 +0300 Subject: [PATCH 33/84] added ShopifyPayment Dispute resource --- README.md | 2 ++ lib/Dispute.php | 30 ++++++++++++++++++++++++++++++ lib/ShopifyPayment.php | 35 +++++++++++++++++++++++++++++++++++ lib/ShopifyResource.php | 2 +- lib/ShopifySDK.php | 2 ++ 5 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 lib/Dispute.php create mode 100644 lib/ShopifyPayment.php diff --git a/README.md b/README.md index a507d8a..a48fd44 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,8 @@ Some resources are available directly, some resources are only available through - [Shop](https://help.shopify.com/api/reference/shop) _(read only)_ - [SmartCollection](https://help.shopify.com/api/reference/smartcollection) - SmartCollection -> [Event](https://help.shopify.com/api/reference/event/) +- [ShopifyPayment](https://shopify.dev/docs/admin-api/rest/reference/shopify_payments/) +- ShopifyPayment -> [Dispute](https://shopify.dev/docs/admin-api/rest/reference/shopify_payments/dispute/) _(read only)_ - [Theme](https://help.shopify.com/api/reference/theme) - Theme -> [Asset](https://help.shopify.com/api/reference/asset/) - [User](https://help.shopify.com/api/reference/user) _(read only, Shopify Plus Only)_ diff --git a/lib/Dispute.php b/lib/Dispute.php new file mode 100644 index 0000000..e769872 --- /dev/null +++ b/lib/Dispute.php @@ -0,0 +1,30 @@ + + * Created at 01/06/2020 16:45 AM UTC+03:00 + * + * @see https://shopify.dev/docs/admin-api/rest/reference/shopify_payments/dispute Shopify API Reference for Dispute + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShopifyResource $DiscountCode + * + * @method ShopifyResource DiscountCode(integer $id = null) + * + */ +class Dispute extends ShopifyResource +{ + /** + * @inheritDoc + */ + public $resourceKey = 'dispute'; + + +} \ No newline at end of file diff --git a/lib/ShopifyPayment.php b/lib/ShopifyPayment.php new file mode 100644 index 0000000..a6da406 --- /dev/null +++ b/lib/ShopifyPayment.php @@ -0,0 +1,35 @@ + + * Created at 01/06/2020 16:45 AM UTC+03:00 + * + * @see https://shopify.dev/docs/admin-api/rest/reference/shopify_payments Shopify API Reference for ShopifyPayment + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShopifyResource $Dispute + * + * @method ShopifyResource Dispute(integer $id = null) + * + */ +class ShopifyPayment extends ShopifyResource +{ + /** + * @inheritDoc + */ + public $resourceKey = 'shopify_payment'; + + /** + * @inheritDoc + */ + protected $childResource = array( + 'Dispute' + ); +} \ No newline at end of file diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 66ce2b7..5e4ca00 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -530,7 +530,7 @@ public function processResponse($responseArray, $dataKey = null) $httpCode = CurlRequest::$lastHttpCode; if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { - throw new Exception\CurlException("Request failed with HTTP Code $httpCode."); + throw new Exception\CurlException("Request failed with HTTP Code $httpCode.", $httpCode); } } diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 1d9276d..94dbe81 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -104,6 +104,7 @@ * @property-read ShippingZone $ShippingZone * @property-read Shop $Shop * @property-read SmartCollection $SmartCollection + * @property-read ShopifyPayment $ShopifyPayment * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook @@ -196,6 +197,7 @@ class ShopifySDK 'ShippingZone', 'Shop', 'SmartCollection', + 'ShopifyPayment', 'Theme', 'User', 'Webhook', From d4c9467587a401815d668e6cea3d113921d25667 Mon Sep 17 00:00:00 2001 From: wissem Date: Wed, 17 Jun 2020 18:47:33 +0200 Subject: [PATCH 34/84] Add Shopify API headers as a config to the SDK --- README.md | 9 +++++++++ lib/ShopifyResource.php | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/README.md b/README.md index a48fd44..80d02fe 100644 --- a/README.md +++ b/README.md @@ -485,6 +485,15 @@ The custom methods are specific to some resources which may not be available for - [current()](https://help.shopify.com/api/reference/user#current) Get the current logged-in user +### Shopify API features headers +To send `X-Shopify-Api-Features` headers while using the SDK, you can use the following: + +``` +$config['ShopifyApiFeatures'] = ['include-presentment-prices']; +$shopify = new PHPShopify\ShopifySDK($config); +``` + + ## Reference - [Shopify API Reference](https://help.shopify.com/api/reference/) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 5e4ca00..f1942ff 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -149,6 +149,12 @@ public function __construct($id = null, $parentResourceUrl = '') } elseif (!isset($config['ApiKey']) || !isset($config['Password'])) { throw new SdkException("Either AccessToken or ApiKey+Password Combination (in case of private API) is required to access the resources. Please check SDK configuration!"); } + + if (isset($config['ShopifyApiFeatures'])) { + foreach($config['ShopifyApiFeatures'] as $apiFeature) { + $this->httpHeaders['X-Shopify-Api-Features'] = $apiFeature; + } + } } /** From 764fa9d558a6261d480262c161eaa32bbf3ddd99 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 3 Jul 2020 12:26:32 -0700 Subject: [PATCH 35/84] added discount code location --- lib/ShopifyResource.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index a618dd9..090df9b 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -136,6 +136,13 @@ abstract class ShopifyResource */ private $prevLink = null; + /** + * Response Header Location, used for discount code lookup + * @see: https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode?api[version]=2020-04#lookup-2020-04 + * @var string $discountLocation + */ + private $discountLocation = null; + public function __construct($id = null, $parentResourceUrl = '') { $this->id = $id; @@ -541,6 +548,8 @@ public function processResponse($response, $dataKey = null) $this->getLinks($lastResponseHeaders); + $this->getDiscountLocation($lastResponseHeaders); + if (isset($responseArray['errors'])) { $message = $this->castString($responseArray['errors']); @@ -554,11 +563,19 @@ public function processResponse($response, $dataKey = null) } } - public function getLinks($responseHeaders){ + public function getLinks($responseHeaders) { $this->nextLink = $this->getLink($responseHeaders,'next'); $this->prevLink = $this->getLink($responseHeaders,'previous'); } + public function getDiscountLocation($responseHeaders) { + + if(!empty($responseHeaders['location'])) { + $this->discountLocation = $responseHeaders['location']; + } + + } + public function getLink($responseHeaders, $type='next'){ if(array_key_exists('x-shopify-api-version', $responseHeaders) From e49925f20ddf3e59fcbd9cd666242635922c7a56 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 3 Jul 2020 12:34:02 -0700 Subject: [PATCH 36/84] added discount code location --- lib/ShopifyResource.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 090df9b..2362b27 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -548,7 +548,7 @@ public function processResponse($response, $dataKey = null) $this->getLinks($lastResponseHeaders); - $this->getDiscountLocation($lastResponseHeaders); + $this->getLocationHeader($lastResponseHeaders); if (isset($responseArray['errors'])) { $message = $this->castString($responseArray['errors']); @@ -568,7 +568,7 @@ public function getLinks($responseHeaders) { $this->prevLink = $this->getLink($responseHeaders,'previous'); } - public function getDiscountLocation($responseHeaders) { + public function getLocationHeader($responseHeaders) { if(!empty($responseHeaders['location'])) { $this->discountLocation = $responseHeaders['location']; @@ -611,6 +611,10 @@ public function getNextLink(){ return $this->nextLink; } + public function getDiscountLocation(){ + return $this->discountLocation; + } + public function getUrlParams($url) { if ($url) { $parts = parse_url($url); From dfeee2b1e3b56b2a17f22c91d5c4282ed1fb3166 Mon Sep 17 00:00:00 2001 From: Matthew Crigger Date: Tue, 14 Jul 2020 13:41:55 -0400 Subject: [PATCH 37/84] Initial Commit Initial commit based on code from rjacobso --- lib/Balance.php | 44 ++++++++++++++++++++++++++++++++++++++++++ lib/Payouts.php | 25 ++++++++++++++++++++++++ lib/ShopifyPayment.php | 11 ++++++++++- lib/ShopifySDK.php | 5 +++++ lib/Transactions.php | 19 ++++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 lib/Balance.php create mode 100644 lib/Payouts.php create mode 100644 lib/Transactions.php diff --git a/lib/Balance.php b/lib/Balance.php new file mode 100644 index 0000000..112e250 --- /dev/null +++ b/lib/Balance.php @@ -0,0 +1,44 @@ + Child Resources + * -------------------------------------------------------------------------- + * + * + */ +class Balance extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'balance'; + + /** + * Get the pluralized version of the resource key + * + * Normally its the same as $resourceKey appended with 's', when it's different, the specific resource class will override this function + * + * @return string + */ + protected function pluralizeKey() + { + return $this->resourceKey; + } + + /** + * If the resource is read only. (No POST / PUT / DELETE actions) + * + * @var boolean + */ + public $readOnly = true; + + /** + * @inheritDoc + */ + protected $childResource = array( + 'Transactions' + ); +} \ No newline at end of file diff --git a/lib/Payouts.php b/lib/Payouts.php new file mode 100644 index 0000000..0cd2ec6 --- /dev/null +++ b/lib/Payouts.php @@ -0,0 +1,25 @@ + + * Created at 11/11/19 12:26 PM UTC-05:00 + * + * @see https://help.shopify.com/en/api/reference/shopify_payments/payout Shopify API Reference for Shopify Payment Payouts + */ + +namespace PHPShopify; + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * + * + */ +class Payouts extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'payout'; +} \ No newline at end of file diff --git a/lib/ShopifyPayment.php b/lib/ShopifyPayment.php index a6da406..5577499 100644 --- a/lib/ShopifyPayment.php +++ b/lib/ShopifyPayment.php @@ -26,10 +26,19 @@ class ShopifyPayment extends ShopifyResource */ public $resourceKey = 'shopify_payment'; + /** + * If the resource is read only. (No POST / PUT / DELETE actions) + * + * @var boolean + */ + public $readOnly = true; + /** * @inheritDoc */ protected $childResource = array( - 'Dispute' + 'Balance', + 'Dispute', + 'Payouts', ); } \ No newline at end of file diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 94dbe81..9000e84 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -146,6 +146,7 @@ * @method ScriptTag ScriptTag(integer $id = null) * @method ShippingZone ShippingZone(integer $id = null) * @method Shop Shop(integer $id = null) + * @method ShopifyPayment ShopifyPayment() * @method SmartCollection SmartCollection(integer $id = null) * @method Theme Theme(int $id = null) * @method User User(integer $id = null) @@ -235,16 +236,20 @@ class ShopifySDK protected $childResources = array( 'Article' => 'Blog', 'Asset' => 'Theme', + 'Balance' => 'ShopifyPayment', 'CustomerAddress' => 'Customer', + 'Dispute' => 'ShopifyPayment', 'Fulfillment' => 'Order', 'FulfillmentEvent' => 'Fulfillment', 'OrderRisk' => 'Order', + 'Payouts' => 'ShopifyPayment', 'ProductImage' => 'Product', 'ProductVariant' => 'Product', 'DiscountCode' => 'PriceRule', 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', + 'Transactions' => 'Balance', 'UsageCharge' => 'RecurringApplicationCharge', ); diff --git a/lib/Transactions.php b/lib/Transactions.php new file mode 100644 index 0000000..792f8b4 --- /dev/null +++ b/lib/Transactions.php @@ -0,0 +1,19 @@ + Date: Fri, 17 Jul 2020 12:30:42 -0400 Subject: [PATCH 38/84] Update ShopifyPayment.php Added property / method lines for balance and payouts. --- lib/ShopifyPayment.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/ShopifyPayment.php b/lib/ShopifyPayment.php index 5577499..54fc6b3 100644 --- a/lib/ShopifyPayment.php +++ b/lib/ShopifyPayment.php @@ -18,6 +18,15 @@ * * @method ShopifyResource Dispute(integer $id = null) * + * @property-read ShopifyResource $Balance + * + * @method ShopifyResource Balance(integer $id = null) + * + * @property-read ShopifyResource $Payouts + * + * @method ShopifyResource Payouts(integer $id = null) + * + */ class ShopifyPayment extends ShopifyResource { From 6a114af6763217d0b32920c09b4a4a55663dca08 Mon Sep 17 00:00:00 2001 From: Matthew Crigger Date: Fri, 17 Jul 2020 12:43:33 -0400 Subject: [PATCH 39/84] Author and Links Updated authoring and help links. --- lib/Balance.php | 7 +++++++ lib/Payouts.php | 2 +- lib/Transactions.php | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Balance.php b/lib/Balance.php index 112e250..4144c5c 100644 --- a/lib/Balance.php +++ b/lib/Balance.php @@ -1,4 +1,11 @@ + * @author Matthew Crigger + * + * @see https://help.shopify.com/en/api/reference/shopify_payments/balance Shopify API Reference for Shopify Payment Balance + */ namespace PHPShopify; diff --git a/lib/Payouts.php b/lib/Payouts.php index 0cd2ec6..e43245b 100644 --- a/lib/Payouts.php +++ b/lib/Payouts.php @@ -2,7 +2,7 @@ /** * Created by PhpStorm. * @author Robert Jacobson - * Created at 11/11/19 12:26 PM UTC-05:00 + * @author Matthew Crigger * * @see https://help.shopify.com/en/api/reference/shopify_payments/payout Shopify API Reference for Shopify Payment Payouts */ diff --git a/lib/Transactions.php b/lib/Transactions.php index 792f8b4..0b45020 100644 --- a/lib/Transactions.php +++ b/lib/Transactions.php @@ -1,4 +1,11 @@ + * @author Matthew Crigger + * + * @see https://help.shopify.com/en/api/reference/shopify_payments/transaction Shopify API Reference for Shopify Payment Transactions + */ namespace PHPShopify; From 279d5214312d4f8afe7ca24b59327364ce724fd2 Mon Sep 17 00:00:00 2001 From: Erdal G Date: Sun, 23 Aug 2020 13:29:35 +0200 Subject: [PATCH 40/84] Update Customer.php Customer search takes (a very useful) query string parameter: https://shopify.dev/docs/admin-api/rest/reference/customers/customer#search-2020-07 --- lib/Customer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Customer.php b/lib/Customer.php index a058624..3c3a675 100644 --- a/lib/Customer.php +++ b/lib/Customer.php @@ -22,7 +22,7 @@ * -------------------------------------------------------------------------- * Customer -> Custom actions * -------------------------------------------------------------------------- - * @method array search() Search for customers matching supplied query + * @method array search(string $query = '') Search for customers matching supplied query */ class Customer extends ShopifyResource { @@ -60,4 +60,4 @@ public function send_invite($customer_invite = array()) return $this->post($dataArray, $url, false); } -} \ No newline at end of file +} From d0e7fe7e9e046af71966ca55b9597d3790ed580c Mon Sep 17 00:00:00 2001 From: Saumil Nagariya Date: Mon, 24 Aug 2020 20:55:30 +0530 Subject: [PATCH 41/84] Created function to generate account_activation_link for a specific customer --- lib/Customer.php | 17 +++++++++++++++++ lib/ShopifyResource.php | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/lib/Customer.php b/lib/Customer.php index a058624..de5dbe4 100644 --- a/lib/Customer.php +++ b/lib/Customer.php @@ -60,4 +60,21 @@ public function send_invite($customer_invite = array()) return $this->post($dataArray, $url, false); } + + /** + * Create account_activation_link for customer. + * + * @param array $customer_id + * + * @return array + */ + public function account_activation_url($customer_id = 0) + { + if (!(int)$customer_id > 0) { + return false; + } + + $url = $this->generateUrl(array(), $customer_id.'/account_activation_url'); + return $this->post(array(), $url, false); + } } \ No newline at end of file diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 5e4ca00..0e07fe5 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -540,6 +540,11 @@ public function processResponse($responseArray, $dataKey = null) if (isset($responseArray['errors'])) { $message = $this->castString($responseArray['errors']); + //check account already enabled or not + if($message=='account already enabled'){ + return array('account_activation_url'=>false); + } + throw new ApiException($message, CurlRequest::$lastHttpCode); } From da9cc984e968d71978a1c5f611d4cb12b0d025b5 Mon Sep 17 00:00:00 2001 From: andyexeter Date: Mon, 12 Oct 2020 17:41:31 +0100 Subject: [PATCH 42/84] Fix bug introduced by #143 by lowercasing key in getHeader --- lib/CurlResponse.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/CurlResponse.php b/lib/CurlResponse.php index 02a9eec..b6922e9 100644 --- a/lib/CurlResponse.php +++ b/lib/CurlResponse.php @@ -53,6 +53,7 @@ public function getHeaders() */ public function getHeader($key) { + $key = strtolower($key); return isset($this->headers[$key]) ? $this->headers[$key] : null; } From f6fb33955b63bdf17431e5cfafe51408b19a1141 Mon Sep 17 00:00:00 2001 From: andyexeter Date: Mon, 12 Oct 2020 17:51:22 +0100 Subject: [PATCH 43/84] Use Retry-After header as value to sleep for before retrying request --- lib/CurlRequest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index 73567e1..25df584 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -166,7 +166,13 @@ protected static function processRequest($ch) throw new ResourceRateLimitException($response->getBody()); } - usleep(500000); + $retryAfter = $response->getHeader('Retry-After'); + + if ($retryAfter === null) { + break; + } + + sleep((float)$retryAfter); } if (curl_errno($ch)) { From 32e6782ae9f2a854bd94448f68c9a28ac090bc80 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Tue, 13 Oct 2020 23:56:32 -0700 Subject: [PATCH 44/84] added application credit endpoint --- lib/ApplicationCredit.php | 26 ++++++++++++++++++++++++++ lib/ShopifySDK.php | 3 +++ 2 files changed, 29 insertions(+) create mode 100644 lib/ApplicationCredit.php diff --git a/lib/ApplicationCredit.php b/lib/ApplicationCredit.php new file mode 100644 index 0000000..58f8551 --- /dev/null +++ b/lib/ApplicationCredit.php @@ -0,0 +1,26 @@ + + * Created at 8/18/16 9:50 AM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/applicationcharge Shopify API Reference for ApplicationCharge + */ + +namespace PHPShopify; + + +class ApplicationCredit extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'application_credit'; + + /** + * @inheritDoc + */ + public $countEnabled = false; + + +} diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 608b02e..707bffa 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -67,6 +67,7 @@ use PHPShopify\Exception\SdkException; /** + * @property-read ApplicationCredit $applicationCredit * @property-read AbandonedCheckout $AbandonedCheckout * @property-read Blog $Blog * @property-read CarrierService $CarrierService @@ -109,6 +110,7 @@ * @property-read Webhook $Webhook * @property-read GraphQL $GraphQL * + * @method ApplicationCredit ApplicationCredit(integer $id = null) * @method AbandonedCheckout AbandonedCheckout(integer $id = null) * @method Blog Blog(integer $id = null) * @method CarrierService CarrierService(integer $id = null) @@ -160,6 +162,7 @@ class ShopifySDK */ protected $resources = array( 'AbandonedCheckout', + 'ApplicationCredit', 'ApplicationCharge', 'Blog', 'CarrierService', From 473a3fca21f96074f9aebd38332f1d923587a372 Mon Sep 17 00:00:00 2001 From: Steffen Persch Date: Sun, 25 Oct 2020 18:04:22 +0100 Subject: [PATCH 45/84] Support DiscountCode batch Action --- lib/Batch.php | 33 +++++++++++++++++++++++++++++++++ lib/PriceRule.php | 6 ++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 lib/Batch.php diff --git a/lib/Batch.php b/lib/Batch.php new file mode 100644 index 0000000..49e125b --- /dev/null +++ b/lib/Batch.php @@ -0,0 +1,33 @@ + Batch action + * -------------------------------------------------------------------------- + * + */ + +class Batch extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'batch'; + + protected function getResourcePath() + { + return $this->resourceKey; + } + + protected function wrapData($dataArray, $dataKey = null) + { + return ['discount_codes' => $dataArray]; + } + +} diff --git a/lib/PriceRule.php b/lib/PriceRule.php index ab77a4e..15c77a9 100644 --- a/lib/PriceRule.php +++ b/lib/PriceRule.php @@ -17,6 +17,7 @@ * @property-read ShopifyResource $DiscountCode * * @method ShopifyResource DiscountCode(integer $id = null) + * @method ShopifyResource Batch() * */ class PriceRule extends ShopifyResource @@ -30,6 +31,7 @@ class PriceRule extends ShopifyResource * @inheritDoc */ protected $childResource = array( - 'DiscountCode' + 'DiscountCode', + 'Batch', ); -} \ No newline at end of file +} From 478382a57e5e981f299fd1b4fb6a129d3da56420 Mon Sep 17 00:00:00 2001 From: Howard Chung Date: Tue, 17 Nov 2020 15:13:43 -0500 Subject: [PATCH 46/84] Fix Issue No. 189 --- lib/Refund.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Refund.php b/lib/Refund.php index c91edbe..43460db 100644 --- a/lib/Refund.php +++ b/lib/Refund.php @@ -14,7 +14,7 @@ * -------------------------------------------------------------------------- * Refund -> Custom actions * -------------------------------------------------------------------------- - * @method array calculate() Calculate a Refund. + * @method array calculate(array $config) Calculate a Refund. * */ class Refund extends ShopifyResource From 9f47fb1a77c38acd27c46e45f8b2439bf881a470 Mon Sep 17 00:00:00 2001 From: Tony DeStefano Date: Thu, 19 Nov 2020 11:11:24 -0800 Subject: [PATCH 47/84] calculate() takes an optional array as a parameter. When I pass in the array, I get a warning in my IDE since it's not defined in this class. So I updated the definition. --- lib/Refund.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Refund.php b/lib/Refund.php index c91edbe..2fe6e50 100644 --- a/lib/Refund.php +++ b/lib/Refund.php @@ -14,7 +14,7 @@ * -------------------------------------------------------------------------- * Refund -> Custom actions * -------------------------------------------------------------------------- - * @method array calculate() Calculate a Refund. + * @method array calculate(array $calculation = null) Calculate a Refund. * */ class Refund extends ShopifyResource @@ -30,4 +30,4 @@ class Refund extends ShopifyResource protected $customPostActions = array ( 'calculate', ); -} \ No newline at end of file +} From b182f7a02c770b0747f75bce383fade45c1840d4 Mon Sep 17 00:00:00 2001 From: Lee Hilton Date: Wed, 16 Dec 2020 17:55:10 -0800 Subject: [PATCH 48/84] Resolves #198, Missing TenderTransaction endpoint --- lib/ShopifySDK.php | 3 +++ lib/TenderTransaction.php | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 lib/TenderTransaction.php diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 9000e84..765a663 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -105,6 +105,7 @@ * @property-read Shop $Shop * @property-read SmartCollection $SmartCollection * @property-read ShopifyPayment $ShopifyPayment + * @property-read TenderTransaction $TenderTransaction * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook @@ -148,6 +149,7 @@ * @method Shop Shop(integer $id = null) * @method ShopifyPayment ShopifyPayment() * @method SmartCollection SmartCollection(integer $id = null) + * @method TenderTransaction TenderTransaction() * @method Theme Theme(int $id = null) * @method User User(integer $id = null) * @method Webhook Webhook(integer $id = null) @@ -199,6 +201,7 @@ class ShopifySDK 'Shop', 'SmartCollection', 'ShopifyPayment', + 'TenderTransaction', 'Theme', 'User', 'Webhook', diff --git a/lib/TenderTransaction.php b/lib/TenderTransaction.php new file mode 100644 index 0000000..4265c41 --- /dev/null +++ b/lib/TenderTransaction.php @@ -0,0 +1,18 @@ + Date: Tue, 19 Jan 2021 15:48:26 -0700 Subject: [PATCH 49/84] Added the Cart Resource The Cart resource is read only, where you would provide the cart_token to retrieve the cart object. Example: `$shopify->Cart('ea4a55031cbbcd5621918d3a893869b0')->get()` You can retrieve the cart token on your website by using the following javascript snippet `var cart_token = document.cookie.match('(^|; )cart=([^;]*)');` --- README.md | 3 ++- lib/Cart.php | 24 ++++++++++++++++++++++++ lib/ShopifySDK.php | 3 +++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 lib/Cart.php diff --git a/README.md b/README.md index 80d02fe..07bea6e 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,8 @@ Some resources are available directly, some resources are only available through - Blog -> Article -> [Metafield](https://help.shopify.com/api/reference/metafield) - Blog -> [Event](https://help.shopify.com/api/reference/event/) - Blog -> [Metafield](https://help.shopify.com/api/reference/metafield) -- [CarrierService](https://help.shopify.com/api/reference/carrierservice/) +- [CarrierService](https://help.shopify.com/api/reference/carrierservice/)- +- [Cart](https://shopify.dev/docs/themes/ajax-api/reference/cart) (read only) - [Collect](https://help.shopify.com/api/reference/collect/) - [Comment](https://help.shopify.com/api/reference/comment/) - Comment -> [Event](https://help.shopify.com/api/reference/event/) diff --git a/lib/Cart.php b/lib/Cart.php new file mode 100644 index 0000000..15ad000 --- /dev/null +++ b/lib/Cart.php @@ -0,0 +1,24 @@ + Date: Sun, 21 Mar 2021 21:08:30 +0600 Subject: [PATCH 50/84] Update Default API Version --- lib/ShopifySDK.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 765a663..52f101a 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -221,7 +221,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2020-01'; + public static $defaultApiVersion = '2021-01'; /** * Shop / API configurations From 05eb12398828545071711eb9c746f9d4d7095980 Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Sun, 21 Mar 2021 21:18:24 +0600 Subject: [PATCH 51/84] Update CountryTest.php --- tests/CountryTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CountryTest.php b/tests/CountryTest.php index 1a0ddc2..1637cde 100644 --- a/tests/CountryTest.php +++ b/tests/CountryTest.php @@ -22,7 +22,7 @@ class CountryTest extends TestSimpleResource * @inheritDoc */ public $putArray = array( - "tax" => 0.01, + "tax" => 0.15, ); /** @@ -32,4 +32,4 @@ class CountryTest extends TestSimpleResource public function testGet() { $this->assertEquals(1, 1); } -} \ No newline at end of file +} From 90c329d1c434379230a3d35bf0f67e0369788554 Mon Sep 17 00:00:00 2001 From: Liam Cooper Date: Wed, 14 Apr 2021 09:48:12 +0100 Subject: [PATCH 52/84] Throw an exception if the request failed (bad auth code, invalid shop) --- lib/AuthHelper.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index e879964..265989e 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -170,6 +170,10 @@ public static function getAccessToken() $response = HttpRequestJson::post($config['AdminUrl'] . 'oauth/access_token', $data); + if (CurlRequest::$lastHttpCode >= 400) { + throw new SdkException("Invalid request. The shop domain is incorrect or the authorization code has already been used."); + } + return isset($response['access_token']) ? $response['access_token'] : null; } else { throw new SdkException("This request is not initiated from a valid shopify shop!"); From c70bd52324a54c5e7dac157d30deb853a8a07359 Mon Sep 17 00:00:00 2001 From: Liam Cooper Date: Wed, 14 Apr 2021 09:54:47 +0100 Subject: [PATCH 53/84] Slight change in vocabulary --- lib/AuthHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index 265989e..d6461e5 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -171,7 +171,7 @@ public static function getAccessToken() $response = HttpRequestJson::post($config['AdminUrl'] . 'oauth/access_token', $data); if (CurlRequest::$lastHttpCode >= 400) { - throw new SdkException("Invalid request. The shop domain is incorrect or the authorization code has already been used."); + throw new SdkException('The shop is invalid or the authorization code has already been used.'); } return isset($response['access_token']) ? $response['access_token'] : null; From fe7e971bf894d6936c0ba136f651617d64e08bf7 Mon Sep 17 00:00:00 2001 From: Liam Cooper Date: Wed, 14 Apr 2021 10:03:12 +0100 Subject: [PATCH 54/84] Follow string quote convention --- lib/AuthHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index d6461e5..617f542 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -171,7 +171,7 @@ public static function getAccessToken() $response = HttpRequestJson::post($config['AdminUrl'] . 'oauth/access_token', $data); if (CurlRequest::$lastHttpCode >= 400) { - throw new SdkException('The shop is invalid or the authorization code has already been used.'); + throw new SdkException("The shop is invalid or the authorization code has already been used."); } return isset($response['access_token']) ? $response['access_token'] : null; From c2e14416f47309bd94bb583e67d8bacbb5733a67 Mon Sep 17 00:00:00 2001 From: Liam Cooper Date: Tue, 20 Apr 2021 14:02:01 +0100 Subject: [PATCH 55/84] Return location header as array if response is 303 --- lib/ShopifyResource.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 4c2262d..463958f 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -531,10 +531,15 @@ public function processResponse($responseArray, $dataKey = null) $httpOK = 200; //Request Successful, OK. $httpCreated = 201; //Create Successful. $httpDeleted = 204; //Delete Successful + $httpOther = 303; //See other (headers). //should be null if any other library used for http calls $httpCode = CurlRequest::$lastHttpCode; + if ($httpCode == $httpOther && array_key_exists('location', self::$lastHttpResponseHeaders)) { + return ['location' => self::$lastHttpResponseHeaders['location']]; + } + if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { throw new Exception\CurlException("Request failed with HTTP Code $httpCode.", $httpCode); } From 6ecbaf9d1f17bdbbddb48e63ff304e37a71bb947 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Tue, 4 May 2021 20:04:48 -0700 Subject: [PATCH 56/84] updated generate url to support numeric indexed arrays --- lib/ShopifyResource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 2362b27..7e3330c 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -320,7 +320,7 @@ protected function getResourcePath() */ public function generateUrl($urlParams = array(), $customAction = null) { - return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . http_build_query($urlParams) : ''); + return $this->resourceUrl . ($customAction ? "/$customAction" : '') . '.json' . (!empty($urlParams) ? '?' . preg_replace('/\%5B\d+\%5D/', '%5B%5D', http_build_query($urlParams)) : ''); } /** From 96cfdc3d572f79efba92a7b0dccc5e4fdcab08ff Mon Sep 17 00:00:00 2001 From: Denis Waleev Date: Thu, 13 May 2021 15:00:09 +0500 Subject: [PATCH 57/84] Remove query params from current url --- lib/AuthHelper.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index e879964..d0c5ede 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -28,8 +28,11 @@ public static function getCurrentUrl() else { $protocol = 'http'; } + + $url = $protocol . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + $url = false !== ($qsPos = strpos($url, '?')) ? substr($url, 0, $qsPos) : $url; // remove query params - return "$protocol://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; + return $url; } /** From ccbe6e783eb3495b2b9fdc3950451c91247507b8 Mon Sep 17 00:00:00 2001 From: marxolly Date: Fri, 21 May 2021 12:09:38 +1000 Subject: [PATCH 58/84] Adding Fulfillment Order --- lib/FulfillmentOrder.php | 46 ++++++++++++++++++++++++++++++++++++++++ lib/Order.php | 1 + 2 files changed, 47 insertions(+) create mode 100644 lib/FulfillmentOrder.php diff --git a/lib/FulfillmentOrder.php b/lib/FulfillmentOrder.php new file mode 100644 index 0000000..6fb4f0c --- /dev/null +++ b/lib/FulfillmentOrder.php @@ -0,0 +1,46 @@ + + * Created at 5/21/21 11:27 AM UTC+10:00 + * + * @see https://shopify.dev/docs/admin-api/rest/reference/shipping-and-fulfillment/fulfillmentorder Shopify API Reference for Fulfillment Order + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * FulfillmentOrder -> Child Resources + * -------------------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * Fulfillment -> Custom actions + * -------------------------------------------------------------------------- + * @method array cancel() Cancel a fulfillment order + * @method array open() Open a fulfillment order + * @method array close() Close a fulfillment order + * @method array move() Move a fulfilment order to a new location + * @method array reschedule() Reschedule fulfill_at_time of a scheduled fulfillment order + * + */ +class FulfillmentOrder extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'fulfillment_order'; + + + /** + * @inheritDoc + */ + protected $customPostActions = array( + 'close', + 'open', + 'cancel', + 'move', + 'reschedule' + ); +} \ No newline at end of file diff --git a/lib/Order.php b/lib/Order.php index 98efd2a..2a26fab 100644 --- a/lib/Order.php +++ b/lib/Order.php @@ -49,6 +49,7 @@ class Order extends ShopifyResource */ protected $childResource = array ( 'Fulfillment', + 'FulfillmentOrder', 'OrderRisk' => 'Risk', 'Refund', 'Transaction', From 19693b0db0fae40e1c4c29da383e6a7f0f24b8ad Mon Sep 17 00:00:00 2001 From: Yorkii Date: Fri, 27 Aug 2021 13:32:30 +0200 Subject: [PATCH 59/84] Add additional curl configuration --- README.md | 14 ++++++++++++++ lib/CurlRequest.php | 21 +++++++++++++++++++++ lib/ShopifySDK.php | 4 ++++ 3 files changed, 39 insertions(+) diff --git a/README.md b/README.md index 07bea6e..946e03e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,20 @@ composer require phpclassic/php-shopify PHPShopify uses curl extension for handling http calls. So you need to have the curl extension installed and enabled with PHP. >However if you prefer to use any other available package library for handling HTTP calls, you can easily do so by modifying 1 line in each of the `get()`, `post()`, `put()`, `delete()` methods in `PHPShopify\HttpRequestJson` class. +You can pass additional curl configuration to `ShopifySDK` +```php +$config = array( + 'ShopUrl' => 'yourshop.myshopify.com', + 'ApiKey' => '***YOUR-PRIVATE-API-KEY***', + 'Password' => '***YOUR-PRIVATE-API-PASSWORD***', + 'Curl' => array( + CURLOPT_TIMEOUT => 10, + CURLOPT_FOLLOWLOCATION => true + ) +); + +PHPShopify\ShopifySDK::config($config); +``` ## Usage You can use PHPShopify in a pretty simple object oriented way. diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index 25df584..f9d3c93 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -35,6 +35,13 @@ class CurlRequest */ public static $lastHttpResponseHeaders = array(); + /** + * Curl additional configuration + * + * @var array + */ + protected static $config = array(); + /** * Initialize the curl resource * @@ -57,6 +64,10 @@ protected static function init($url, $httpHeaders = array()) curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify'); + foreach (self::$config as $option => $value) { + curl_setopt($ch, $option, $value); + } + $headers = array(); foreach ($httpHeaders as $key => $value) { $headers[] = "$key: $value"; @@ -139,6 +150,16 @@ public static function delete($url, $httpHeaders = array()) return self::processRequest($ch); } + /** + * Set curl additional configuration + * + * @param array $config + */ + public static function config($config = array()) + { + self::$config = $config; + } + /** * Execute a request, release the resource and return output * diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 0b2c056..02cf6c5 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -351,6 +351,10 @@ public static function config($config) static::$timeAllowedForEachApiCall = $config['AllowedTimePerCall']; } + if (isset($config['Curl']) && is_array($config['Curl'])) { + CurlRequest::config($config['Curl']); + } + return new ShopifySDK; } From 74ec175cf2e8c5c2ace90d7a73cd494af491d220 Mon Sep 17 00:00:00 2001 From: Fabio Date: Tue, 14 Sep 2021 17:30:05 +0200 Subject: [PATCH 60/84] add retry request callback --- lib/HttpRequestGraphQL.php | 6 +-- lib/HttpRequestJson.php | 105 +++++++++++++++++++++++++++++++------ lib/ShopifyResource.php | 19 ------- 3 files changed, 92 insertions(+), 38 deletions(-) diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php index 8aef907..708f334 100644 --- a/lib/HttpRequestGraphQL.php +++ b/lib/HttpRequestGraphQL.php @@ -68,8 +68,8 @@ public static function post($url, $data, $httpHeaders = array(), $variables = nu { self::prepareRequest($httpHeaders, $data, $variables); - $response = CurlRequest::post($url, self::$postDataGraphQL, self::$httpHeaders); + self::$postDataJSON = self::$postDataGraphQL; - return self::processResponse($response); + return self::processRequest('POST', $url); } -} \ No newline at end of file +} diff --git a/lib/HttpRequestJson.php b/lib/HttpRequestJson.php index ceab78f..c325b74 100644 --- a/lib/HttpRequestJson.php +++ b/lib/HttpRequestJson.php @@ -19,7 +19,6 @@ */ class HttpRequestJson { - /** * HTTP request headers * @@ -32,7 +31,7 @@ class HttpRequestJson * * @var string */ - private static $postDataJSON; + protected static $postDataJSON; /** @@ -68,9 +67,7 @@ public static function get($url, $httpHeaders = array()) { self::prepareRequest($httpHeaders); - $response = CurlRequest::get($url, self::$httpHeaders); - - return self::processResponse($response); + return self::processRequest('GET', $url); } /** @@ -86,9 +83,7 @@ public static function post($url, $dataArray, $httpHeaders = array()) { self::prepareRequest($httpHeaders, $dataArray); - $response = CurlRequest::post($url, self::$postDataJSON, self::$httpHeaders); - - return self::processResponse($response); + return self::processRequest('POST', $url); } /** @@ -104,9 +99,7 @@ public static function put($url, $dataArray, $httpHeaders = array()) { self::prepareRequest($httpHeaders, $dataArray); - $response = CurlRequest::put($url, self::$postDataJSON, self::$httpHeaders); - - return self::processResponse($response); + return self::processRequest('PUT', $url); } /** @@ -121,9 +114,68 @@ public static function delete($url, $httpHeaders = array()) { self::prepareRequest($httpHeaders); - $response = CurlRequest::delete($url, self::$httpHeaders); + return self::processRequest('DELETE', $url); + } + + /** + * Process a curl request and return decoded JSON response + * + * @param string $method Request http method ('GET', 'POST', 'PUT' or 'DELETE') + * @param string $url Request URL + * + * @throws CurlException if response received with unexpected HTTP code. + * + * @return array + */ + public static function processRequest($method, $url) { + $retry = 0; + $raw = null; + + while(true) { + try { + switch($method) { + case 'GET': + $raw = CurlRequest::get($url, self::$httpHeaders); + break; + case 'POST': + $raw = CurlRequest::post($url, self::$postDataJSON, self::$httpHeaders); + break; + case 'PUT': + $raw = CurlRequest::put($url, self::$postDataJSON, self::$httpHeaders); + break; + case 'DELETE': + $raw = CurlRequest::delete($url, self::$httpHeaders); + break; + default: + throw new \Exception("unexpected request method '$method'"); + } + + return self::processResponse($raw); + } catch(\Exception $e) { + if (!self::shouldRetry($raw, $e, $retry++)) { + throw $e; + } + } + } + } + + /** + * Evaluate if send again a request + * + * @param string $response Raw request response + * @param exception $error the request error occured + * @param integer $retry the current number of retry + * + * @return bool + */ + public static function shouldRetry($response, $error, $retry) { + $config = ShopifySDK::$config; + + if (isset($config['RequestRetryCallback'])) { + return $config['RequestRetryCallback']($response, $error, $retry); + } - return self::processResponse($response); + return false; } /** @@ -135,8 +187,29 @@ public static function delete($url, $httpHeaders = array()) */ protected static function processResponse($response) { + $responseArray = json_decode($response, true); - return json_decode($response, true); - } + if ($responseArray === null) { + //Something went wrong, Checking HTTP Codes + $httpOK = 200; //Request Successful, OK. + $httpCreated = 201; //Create Successful. + $httpDeleted = 204; //Delete Successful + $httpOther = 303; //See other (headers). + + $lastHttpResponseHeaders = CurlRequest::$lastHttpResponseHeaders; + + //should be null if any other library used for http calls + $httpCode = CurlRequest::$lastHttpCode; + + if ($httpCode == $httpOther && array_key_exists('location', $lastHttpResponseHeaders)) { + return ['location' => $lastHttpResponseHeaders['location']]; + } -} \ No newline at end of file + if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { + throw new Exception\CurlException("Request failed with HTTP Code $httpCode.", $httpCode); + } + } + + return $responseArray; + } +} diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 463958f..dd3b2cb 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -526,25 +526,6 @@ public function processResponse($responseArray, $dataKey = null) { self::$lastHttpResponseHeaders = CurlRequest::$lastHttpResponseHeaders; - if ($responseArray === null) { - //Something went wrong, Checking HTTP Codes - $httpOK = 200; //Request Successful, OK. - $httpCreated = 201; //Create Successful. - $httpDeleted = 204; //Delete Successful - $httpOther = 303; //See other (headers). - - //should be null if any other library used for http calls - $httpCode = CurlRequest::$lastHttpCode; - - if ($httpCode == $httpOther && array_key_exists('location', self::$lastHttpResponseHeaders)) { - return ['location' => self::$lastHttpResponseHeaders['location']]; - } - - if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { - throw new Exception\CurlException("Request failed with HTTP Code $httpCode.", $httpCode); - } - } - $lastResponseHeaders = CurlRequest::$lastHttpResponseHeaders; $this->getLinks($lastResponseHeaders); From ae81bdf9d5a0de668bcc2f1028a9b9e42efa8e91 Mon Sep 17 00:00:00 2001 From: Yorkii Date: Tue, 12 Oct 2021 19:38:35 +0200 Subject: [PATCH 61/84] FulfillmentOrder hold and relase_hold methods --- lib/FulfillmentOrder.php | 7 +++++-- lib/ShopifySDK.php | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/FulfillmentOrder.php b/lib/FulfillmentOrder.php index 6fb4f0c..45de89a 100644 --- a/lib/FulfillmentOrder.php +++ b/lib/FulfillmentOrder.php @@ -23,7 +23,8 @@ * @method array close() Close a fulfillment order * @method array move() Move a fulfilment order to a new location * @method array reschedule() Reschedule fulfill_at_time of a scheduled fulfillment order - * + * @method array hold(array $data) Hold a fulfillment order + * @method array release_hold() Release hold on a fulfillment order */ class FulfillmentOrder extends ShopifyResource { @@ -41,6 +42,8 @@ class FulfillmentOrder extends ShopifyResource 'open', 'cancel', 'move', - 'reschedule' + 'reschedule', + 'hold', + 'release_hold' ); } \ No newline at end of file diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 02cf6c5..1ee6a8a 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -130,6 +130,7 @@ * @method DiscountCode DiscountCode(integer $id = null) * @method Event Event(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) + * @method FulfillmentOrder FulfillmentOrder(integer $id = null) * @method GiftCard GiftCard(integer $id = null) * @method InventoryItem InventoryItem(integer $id = null) * @method InventoryLevel InventoryLevel(integer $id = null) @@ -183,6 +184,7 @@ class ShopifySDK 'DraftOrder', 'Event', 'FulfillmentService', + 'FulfillmentOrder', 'GiftCard', 'InventoryItem', 'InventoryLevel', From 0f421109143d131905390c327b4c8f6d7d7f5b9d Mon Sep 17 00:00:00 2001 From: seka1 <2714877+seka19@users.noreply.github.com> Date: Sun, 17 Oct 2021 02:27:05 +0200 Subject: [PATCH 62/84] feat: AccessScope resource --- lib/AccessScope.php | 36 ++++++++++++++++++++++++++++++++++++ lib/ShopifySDK.php | 3 +++ 2 files changed, 39 insertions(+) create mode 100644 lib/AccessScope.php diff --git a/lib/AccessScope.php b/lib/AccessScope.php new file mode 100644 index 0000000..0ce0c97 --- /dev/null +++ b/lib/AccessScope.php @@ -0,0 +1,36 @@ +getResourcePath() . '.json'; + } +} diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 02cf6c5..dfe364a 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -68,6 +68,7 @@ /** * @property-read AbandonedCheckout $AbandonedCheckout + * @property-read AccessScope $AccessScope * @property-read ApplicationCharge $ApplicationCharge * @property-read Blog $Blog * @property-read CarrierService $CarrierService @@ -113,6 +114,7 @@ * @property-read GraphQL $GraphQL * * @method AbandonedCheckout AbandonedCheckout(integer $id = null) + * @method AccessScope AccessScope() * @method ApplicationCharge ApplicationCharge(integer $id = null) * @method Blog Blog(integer $id = null) * @method CarrierService CarrierService(integer $id = null) @@ -166,6 +168,7 @@ class ShopifySDK */ protected $resources = array( 'AbandonedCheckout', + 'AccessScope', 'ApplicationCharge', 'Blog', 'CarrierService', From 921d83d4e9fa30ba4b63271f8ae4de0808409b66 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Mon, 1 Nov 2021 12:39:16 -0700 Subject: [PATCH 63/84] added http code to shopify resource --- lib/ShopifyResource.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 7e3330c..523042a 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -136,6 +136,11 @@ abstract class ShopifyResource */ private $prevLink = null; + /** + * HTTP code used to check if we need to poll or not + */ + public $httpCode = null; + /** * Response Header Location, used for discount code lookup * @see: https://shopify.dev/docs/admin-api/rest/reference/discounts/discountcode?api[version]=2020-04#lookup-2020-04 @@ -536,10 +541,14 @@ public function processResponse($response, $dataKey = null) //should be null if any other library used for http calls $httpCode = CurlRequest::$lastHttpCode; + $this->httpCode = $httpCode; if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { throw new Exception\CurlException("Request failed with HTTP Code $httpCode."); } + } else { + $httpCode = CurlRequest::$lastHttpCode; + $this->httpCode = $httpCode; } $responseArray = json_decode($response, true); From 32ec82af1bad7c1cf52363b4711fcc3a820abfbc Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Sat, 18 Dec 2021 17:23:03 +0600 Subject: [PATCH 64/84] [Fixes #251] Update Default API Version to 2021-10 --- lib/ShopifySDK.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index e35455f..c6f9a51 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -229,7 +229,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2021-01'; + public static $defaultApiVersion = '2021-10'; /** * Shop / API configurations From 7233f962dd1f70957739acd3670938b8286094ef Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Tue, 21 Dec 2021 16:15:27 +0600 Subject: [PATCH 65/84] $config updated(added AccessToken for running graphql test) --- tests/TestResource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/TestResource.php b/tests/TestResource.php index 1c9a268..3fd3b2d 100644 --- a/tests/TestResource.php +++ b/tests/TestResource.php @@ -23,6 +23,7 @@ public static function setUpBeforeClass() 'ShopUrl' => getenv('SHOPIFY_SHOP_URL'), //Your shop URL 'ApiKey' => getenv('SHOPIFY_API_KEY'), //Your Private API Key 'Password' => getenv('SHOPIFY_API_PASSWORD'), //Your Private API Password + 'AccessToken' => getenv('SHOPIFY_API_PASSWORD'), //Your Access Token(Private API Password) ); self::$shopify = ShopifySDK::config($config); From 65f64f19d388de2a304c20582996b4b3fb9f80f1 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Tue, 21 Dec 2021 16:16:42 +0600 Subject: [PATCH 66/84] test case added for GraphQL Query --- tests/GraphQLTest.php | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/GraphQLTest.php diff --git a/tests/GraphQLTest.php b/tests/GraphQLTest.php new file mode 100644 index 0000000..fcc12c4 --- /dev/null +++ b/tests/GraphQLTest.php @@ -0,0 +1,39 @@ +GraphQL->post($graphQL); + + $this->assertNotEmpty($return['data']['shop']); + } + + +} From fa79086695985a6bd90638e4271091aeee7afe65 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Tue, 4 Jan 2022 00:09:33 +0600 Subject: [PATCH 67/84] config array updated - for private api --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 946e03e..6ad6895 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ $config = array( 'ShopUrl' => 'yourshop.myshopify.com', 'ApiKey' => '***YOUR-PRIVATE-API-KEY***', 'Password' => '***YOUR-PRIVATE-API-PASSWORD***', + 'AccessToken' => '***YOUR-PRIVATE-API-PASSWORD***', // optional but needed if you want to use graphql ); PHPShopify\ShopifySDK::config($config); From 034a88db2ffa93119a489c07189b77e97a1d1413 Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Wed, 23 Feb 2022 12:26:58 +0600 Subject: [PATCH 68/84] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ad6895..3b634a8 100644 --- a/README.md +++ b/README.md @@ -33,20 +33,20 @@ PHPShopify\ShopifySDK::config($config); You can use PHPShopify in a pretty simple object oriented way. #### Configure ShopifySDK -If you are using your own private API, provide the ApiKey and Password. +If you are using your own private API (except GraphQL), provide the ApiKey and Password. ```php $config = array( 'ShopUrl' => 'yourshop.myshopify.com', 'ApiKey' => '***YOUR-PRIVATE-API-KEY***', 'Password' => '***YOUR-PRIVATE-API-PASSWORD***', - 'AccessToken' => '***YOUR-PRIVATE-API-PASSWORD***', // optional but needed if you want to use graphql ); PHPShopify\ShopifySDK::config($config); ``` -For Third party apps, use the permanent access token. +For Third party apps, use the permanent access token. +> For GraphQL, AccessToken is required. If you are using private API for GraphQL, use your password as AccessToken here. ```php $config = array( From 59e6f8c7e8e7264e5512b13a803f186d0d916a6b Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Wed, 23 Feb 2022 12:32:49 +0600 Subject: [PATCH 69/84] Update API Version --- lib/ShopifySDK.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index c6f9a51..f5383bb 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -229,7 +229,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2021-10'; + public static $defaultApiVersion = '2022-01'; /** * Shop / API configurations From 75cdc9cc6dc3ad27ed3edb689efbf2d49e61d6c8 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 23 Feb 2022 15:25:00 +0600 Subject: [PATCH 70/84] Migrating from value_type to type --- tests/MetafieldTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MetafieldTest.php b/tests/MetafieldTest.php index 5038381..b58877a 100644 --- a/tests/MetafieldTest.php +++ b/tests/MetafieldTest.php @@ -17,7 +17,7 @@ class MetafieldTest extends TestSimpleResource "namespace" => "inventory", "key" => "warehouse", "value" => 25, - "value_type" => "integer", + "type" => "integer", ); /** @@ -25,6 +25,6 @@ class MetafieldTest extends TestSimpleResource */ public $putArray = array( "value" => "something new", - "value_type" => "string", + "type" => "string", ); } \ No newline at end of file From 5bc3d3affeb4fcb0c5d7a1233ff58d1b503bad80 Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Thu, 14 Jul 2022 21:39:59 +0600 Subject: [PATCH 71/84] Update API Version --- lib/ShopifySDK.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index f5383bb..4c63665 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -229,7 +229,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2022-01'; + public static $defaultApiVersion = '2022-07'; /** * Shop / API configurations From 5e63d9705c2a925fc648b42d493996a49b1fa188 Mon Sep 17 00:00:00 2001 From: Rick Guyer Date: Mon, 18 Jul 2022 15:24:04 -0500 Subject: [PATCH 72/84] Add Metafield property and method to docblock SmartCollection has Metafield support, but static analysis will complain unless it is described in the docblock. --- lib/SmartCollection.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/SmartCollection.php b/lib/SmartCollection.php index d39925c..2d7a347 100644 --- a/lib/SmartCollection.php +++ b/lib/SmartCollection.php @@ -15,8 +15,10 @@ * SmartCollection -> Child Resources * -------------------------------------------------------------------------- * @property-read Event $Event + * @property-read Metafield $Metafield * * @method Event Event(integer $id = null) + * @method Metafield Metafield(integer $id = null) * * -------------------------------------------------------------------------- * SmartCollection -> Custom actions From 9c5a5281aec28efc8a6ef02ed11dbac0a26722ce Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Thu, 29 Sep 2022 12:33:23 +0600 Subject: [PATCH 73/84] Rollback API Version (Breaking changes) --- lib/ShopifySDK.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 4c63665..f5383bb 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -229,7 +229,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2022-07'; + public static $defaultApiVersion = '2022-01'; /** * Shop / API configurations From 81e6cc838043fd45863f916fd5bc08a4f4c6fb63 Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Thu, 29 Sep 2022 12:35:29 +0600 Subject: [PATCH 74/84] Update API Version to 2022-07 --- lib/ShopifySDK.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index f5383bb..4c63665 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -229,7 +229,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2022-01'; + public static $defaultApiVersion = '2022-07'; /** * Shop / API configurations From 3b6b0231dedf511722e1f846e0dad4e957c8a272 Mon Sep 17 00:00:00 2001 From: Tareq Mahmood Date: Thu, 29 Sep 2022 12:59:43 +0600 Subject: [PATCH 75/84] Custom API Version in $config --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 3b634a8..6a69c9f 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,17 @@ $config = array( 'AccessToken' => '***ACCESS-TOKEN-FOR-THIRD-PARTY-APP***', ); +PHPShopify\ShopifySDK::config($config); +``` +You can use specific Shopify API Version by adding in the config array + +```php +$config = array( + 'ShopUrl' => 'yourshop.myshopify.com', + 'AccessToken' => '***ACCESS-TOKEN-FOR-THIRD-PARTY-APP***', + 'ApiVersion' => '2022-07', +); + PHPShopify\ShopifySDK::config($config); ``` ##### How to get the permanent access token for a shop? From 1816de456de3b36c9ecebbbd12ab36581e2bdacf Mon Sep 17 00:00:00 2001 From: Jin Date: Mon, 3 Oct 2022 21:01:43 +0700 Subject: [PATCH 76/84] Metafield as a child resource of Collection & DraftOrder --- lib/Collection.php | 5 ++++- lib/DraftOrder.php | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Collection.php b/lib/Collection.php index 1d2c84a..0c21a40 100644 --- a/lib/Collection.php +++ b/lib/Collection.php @@ -8,8 +8,10 @@ * -------------------------------------------------------------------------- * * @property-read Product $Product + * @property-read Metafield $Metafield * * @method Product Product(integer $id = null) + * @method Metafield Metafield(integer $id = null) * * @see https://shopify.dev/docs/admin-api/rest/reference/products/collection * @@ -31,5 +33,6 @@ class Collection extends ShopifyResource */ protected $childResource = array( 'Product', + 'Metafield', ); -} \ No newline at end of file +} diff --git a/lib/DraftOrder.php b/lib/DraftOrder.php index 902cee5..9e3be91 100644 --- a/lib/DraftOrder.php +++ b/lib/DraftOrder.php @@ -12,6 +12,14 @@ /** + * -------------------------------------------------------------------------- + * DraftOrder -> Child Resources + * -------------------------------------------------------------------------- + * + * @property-read Metafield $Metafield + * + * @method Metafield Metafield(integer $id = null) + * * -------------------------------------------------------------------------- * DraftOrder -> Custom actions * -------------------------------------------------------------------------- @@ -39,4 +47,11 @@ class DraftOrder extends ShopifyResource protected $customPutActions = array( 'complete', ); -} \ No newline at end of file + + /** + * @inheritDoc + */ + protected $childResource = array( + 'Metafield', + ); +} From 8b2c2a09e6bed833f7b73eed2e4b6107fcbd0f4a Mon Sep 17 00:00:00 2001 From: Jin Date: Thu, 6 Oct 2022 22:50:59 +0700 Subject: [PATCH 77/84] added fulfillment update_tracking action --- lib/Fulfillment.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Fulfillment.php b/lib/Fulfillment.php index 18138d2..ec9cd75 100644 --- a/lib/Fulfillment.php +++ b/lib/Fulfillment.php @@ -24,6 +24,7 @@ * @method array complete() Complete a fulfillment * @method array open() Open a pending fulfillment * @method array cancel() Cancel a fulfillment + * @method array update_tracking(array $data) Updates the tracking information for a fulfillment. * */ class Fulfillment extends ShopifyResource @@ -47,5 +48,6 @@ class Fulfillment extends ShopifyResource 'complete', 'open', 'cancel', + 'update_tracking', ); -} \ No newline at end of file +} From 4d49e3bdfaaea01c5234285391bb0e0c23f3ad91 Mon Sep 17 00:00:00 2001 From: Jin Date: Thu, 6 Oct 2022 23:02:13 +0700 Subject: [PATCH 78/84] make fulfillment api as parent resource --- lib/ShopifySDK.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 4c63665..7dffb55 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -85,6 +85,7 @@ * @property-read DiscountCode $DiscountCode * @property-read DraftOrder $DraftOrder * @property-read Event $Event + * @property-read Fulfillment $Fulfillment * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard * @property-read InventoryItem $InventoryItem @@ -131,6 +132,7 @@ * @method DraftOrder DraftOrder(integer $id = null) * @method DiscountCode DiscountCode(integer $id = null) * @method Event Event(integer $id = null) + * @method Fulfillment Fulfillment(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) * @method FulfillmentOrder FulfillmentOrder(integer $id = null) * @method GiftCard GiftCard(integer $id = null) @@ -186,6 +188,7 @@ class ShopifySDK 'DiscountCode', 'DraftOrder', 'Event', + 'Fulfillment', 'FulfillmentService', 'FulfillmentOrder', 'GiftCard', From 44dadde3133ee9d89f1f7ffedaf2eb9b3ed3c7e8 Mon Sep 17 00:00:00 2001 From: Rex Bengil Date: Sat, 22 Oct 2022 07:19:34 +0800 Subject: [PATCH 79/84] #266 - PHP Deprecated: explode(): Passing null to parameter #2 ($string) of type string is deprecated --- lib/CurlRequest.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/CurlRequest.php b/lib/CurlRequest.php index f9d3c93..648296d 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -181,12 +181,15 @@ protected static function processRequest($ch) break; } - $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit'), 2); + $apiCallLimit = $response->getHeader('X-Shopify-Shop-Api-Call-Limit'); - if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { - throw new ResourceRateLimitException($response->getBody()); + if (!empty($apiCallLimit)) { + $limitHeader = explode('/', $apiCallLimit, 2); + if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { + throw new ResourceRateLimitException($response->getBody()); + } } - + $retryAfter = $response->getHeader('Retry-After'); if ($retryAfter === null) { From 34066f664541cb713c72de3088f9ea3f6a0d6f1b Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 7 Dec 2022 13:55:13 -0800 Subject: [PATCH 80/84] fixed response array issues --- lib/ShopifyResource.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 5cbaa8a..6f122f1 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -526,7 +526,7 @@ protected function castString($array) /** * Process the request response * - * @param array $responseArray Request response in array format + * @param array $response Request response in array format * @param string $dataKey Keyname to fetch data from response array * * @throws ApiException if the response has an error specified @@ -545,8 +545,8 @@ public function processResponse($response, $dataKey = null) $this->getLocationHeader($lastResponseHeaders); - if (isset($responseArray['errors'])) { - $message = $this->castString($responseArray['errors']); + if (isset($response['errors'])) { + $message = $this->castString($response['errors']); //check account already enabled or not if($message=='account already enabled'){ @@ -556,10 +556,10 @@ public function processResponse($response, $dataKey = null) throw new ApiException($message, CurlRequest::$lastHttpCode); } - if ($dataKey && isset($responseArray[$dataKey])) { - return $responseArray[$dataKey]; + if ($dataKey && isset($response[$dataKey])) { + return $response[$dataKey]; } else { - return $responseArray; + return $response; } } From 98b357df031a0148472f469e75ae10415b5dca36 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Thu, 8 Dec 2022 11:08:21 -0800 Subject: [PATCH 81/84] added httpcode to resource --- lib/ShopifyResource.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 6f122f1..14a35fc 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -545,6 +545,9 @@ public function processResponse($response, $dataKey = null) $this->getLocationHeader($lastResponseHeaders); + $httpCode = CurlRequest::$lastHttpCode; + $this->httpCode = $httpCode; + if (isset($response['errors'])) { $message = $this->castString($response['errors']); @@ -553,7 +556,7 @@ public function processResponse($response, $dataKey = null) return array('account_activation_url'=>false); } - throw new ApiException($message, CurlRequest::$lastHttpCode); + throw new ApiException($message, CurlRequest::$httpCode); } if ($dataKey && isset($response[$dataKey])) { From f266b2e6ed4ae1e1a554b1e668b8a2a3519588f3 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 28 Feb 2023 14:49:28 -0800 Subject: [PATCH 82/84] Added gift card adjustment functionality --- lib/GiftCard.php | 9 ++++++++- lib/GiftCardAdjustment.php | 36 ++++++++++++++++++++++++++++++++++++ lib/ShopifySDK.php | 2 ++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 lib/GiftCardAdjustment.php diff --git a/lib/GiftCard.php b/lib/GiftCard.php index 2391064..e749b2d 100644 --- a/lib/GiftCard.php +++ b/lib/GiftCard.php @@ -29,6 +29,13 @@ class GiftCard extends ShopifyResource */ public $searchEnabled = true; + /** + * @inheritDoc + */ + protected $childResource = array( + 'GiftCardAdjustment' => 'Adjustment' + ); + /** * Disable a gift card. * Disabling a gift card is permanent and cannot be undone. @@ -45,4 +52,4 @@ public function disable() return $this->post($dataArray, $url); } -} \ No newline at end of file +} diff --git a/lib/GiftCardAdjustment.php b/lib/GiftCardAdjustment.php new file mode 100644 index 0000000..1592913 --- /dev/null +++ b/lib/GiftCardAdjustment.php @@ -0,0 +1,36 @@ + + * Created at 8/19/16 12:07 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/customeraddress Shopify API Reference for CustomerAddress + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * GiftCardAdjustment -> Custom actions + * -------------------------------------------------------------------------- + * @method array makeDefault() Sets the address as default for the customer + * + */ +class GiftCardAdjustment extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'adjustment'; + + /** + * @inheritDoc + */ + protected function pluralizeKey() + { + return 'adjustments'; + } + + +} diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index 2239b2a..aa50e05 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -177,6 +177,7 @@ class ShopifySDK */ protected $resources = array( 'AbandonedCheckout', + 'Adjustment', 'ApplicationCredit', 'AccessScope', 'ApplicationCharge', @@ -261,6 +262,7 @@ class ShopifySDK 'Asset' => 'Theme', 'Balance' => 'ShopifyPayment', 'CustomerAddress' => 'Customer', + 'GiftCardAdjustment'=> 'GiftCard', 'Dispute' => 'ShopifyPayment', 'Fulfillment' => 'Order', 'FulfillmentEvent' => 'Fulfillment', From a9c22b1070519290041db6ae4b2818d88cbd167c Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 7 Jun 2023 18:12:21 -0700 Subject: [PATCH 83/84] added last httpCode --- lib/ShopifyResource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 6f122f1..4a30a56 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -545,6 +545,8 @@ public function processResponse($response, $dataKey = null) $this->getLocationHeader($lastResponseHeaders); + $this->httpCode = CurlRequest::$lastHttpCode; + if (isset($response['errors'])) { $message = $this->castString($response['errors']); From bf36ab23534fffe983d707dbda159cb1a49491f5 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 8 Dec 2023 15:22:26 -0800 Subject: [PATCH 84/84] fixed content type header --- lib/HttpRequestGraphQL.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/HttpRequestGraphQL.php b/lib/HttpRequestGraphQL.php index 8183f8f..1dc27d8 100644 --- a/lib/HttpRequestGraphQL.php +++ b/lib/HttpRequestGraphQL.php @@ -51,8 +51,6 @@ protected static function prepareRequest($httpHeaders = array(), $data = array() $httpHeaders['Content-type'] = 'application/graphql'; } - $httpHeaders['Content-type'] = 'application/graphql'; - $httpHeaders['X-Shopify-Access-Token'] = $httpHeaders['X-Shopify-Access-Token']; self::$httpHeaders = $httpHeaders;