From eeacfc62fafdcc990b431b193c6cb475c00f7e1c Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 6 Sep 2019 17:52:23 -0700 Subject: [PATCH 01/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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/39] 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 e71baca5f2524687d50958825aa9a88c7b1cbfe1 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Tue, 21 Apr 2020 14:58:38 -0700 Subject: [PATCH 28/39] 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 29/39] 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 764fa9d558a6261d480262c161eaa32bbf3ddd99 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Fri, 3 Jul 2020 12:26:32 -0700 Subject: [PATCH 30/39] 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 31/39] 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 32e6782ae9f2a854bd94448f68c9a28ac090bc80 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Tue, 13 Oct 2020 23:56:32 -0700 Subject: [PATCH 32/39] 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 6ecbaf9d1f17bdbbddb48e63ff304e37a71bb947 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Tue, 4 May 2021 20:04:48 -0700 Subject: [PATCH 33/39] 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 921d83d4e9fa30ba4b63271f8ae4de0808409b66 Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Mon, 1 Nov 2021 12:39:16 -0700 Subject: [PATCH 34/39] 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 34066f664541cb713c72de3088f9ea3f6a0d6f1b Mon Sep 17 00:00:00 2001 From: Zac Fair Date: Wed, 7 Dec 2022 13:55:13 -0800 Subject: [PATCH 35/39] 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 36/39] 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 37/39] 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 38/39] 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 39/39] 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;