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 diff --git a/README.md b/README.md index 481989e..766654b 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,26 @@ composer require phpclassic/php-shopify PHPShopify uses curl extension for handling http calls. So you need to have the curl extension installed and enabled with PHP. >However if you prefer to use any other available package library for handling HTTP calls, you can easily do so by modifying 1 line in each of the `get()`, `post()`, `put()`, `delete()` methods in `PHPShopify\HttpRequestJson` class. +You can pass additional curl configuration to `ShopifySDK` +```php +$config = array( + 'ShopUrl' => 'yourshop.myshopify.com', + 'ApiKey' => '***YOUR-PRIVATE-API-KEY***', + 'Password' => '***YOUR-PRIVATE-API-PASSWORD***', + 'Curl' => array( + CURLOPT_TIMEOUT => 10, + CURLOPT_FOLLOWLOCATION => true + ) +); + +PHPShopify\ShopifySDK::config($config); +``` ## Usage You can use PHPShopify in a pretty simple object oriented way. #### Configure ShopifySDK -If you are using your own private API, provide the ApiKey and Password. +If you are using your own private API (except GraphQL), provide the ApiKey and Password. ```php $config = array( @@ -31,7 +45,8 @@ $config = array( PHPShopify\ShopifySDK::config($config); ``` -For Third party apps, use the permanent access token. +For Third party apps, use the permanent access token. +> For GraphQL, AccessToken is required. If you are using private API for GraphQL, use your password as AccessToken here. ```php $config = array( @@ -39,6 +54,17 @@ $config = array( 'AccessToken' => '***ACCESS-TOKEN-FOR-THIRD-PARTY-APP***', ); +PHPShopify\ShopifySDK::config($config); +``` +You can use specific Shopify API Version by adding in the config array + +```php +$config = array( + 'ShopUrl' => 'yourshop.myshopify.com', + 'AccessToken' => '***ACCESS-TOKEN-FOR-THIRD-PARTY-APP***', + 'ApiVersion' => '2022-07', +); + PHPShopify\ShopifySDK::config($config); ``` ##### How to get the permanent access token for a shop? @@ -171,7 +197,7 @@ $shopify->Order($orderID)->put($updateInfo); ```php $webHookID = 453487303; -$shopify->Webhook($webHookID)->delete()); +$shopify->Webhook($webHookID)->delete(); ``` @@ -295,14 +321,17 @@ Some resources are available directly, some resources are only available through > Use the resources only by listed resource map. Trying to get a resource directly which is only available through parent resource may end up with errors. - [AbandonedCheckout](https://help.shopify.com/api/reference/abandoned_checkouts) +- [ApiDeprecations](https://shopify.dev/api/admin-rest/2022-04/resources/deprecated-api-calls#get-deprecated-api-calls) - [ApplicationCharge](https://help.shopify.com/api/reference/applicationcharge) +- [AssignedFulfillmentOrder](https://shopify.dev/docs/api/admin-rest/2023-04/resources/assignedfulfillmentorder) - [Blog](https://help.shopify.com/api/reference/blog/) - Blog -> [Article](https://help.shopify.com/api/reference/article/) - Blog -> Article -> [Event](https://help.shopify.com/api/reference/event/) - Blog -> Article -> [Metafield](https://help.shopify.com/api/reference/metafield) - Blog -> [Event](https://help.shopify.com/api/reference/event/) - Blog -> [Metafield](https://help.shopify.com/api/reference/metafield) -- [CarrierService](https://help.shopify.com/api/reference/carrierservice/) +- [CarrierService](https://help.shopify.com/api/reference/carrierservice/)- +- [Cart](https://shopify.dev/docs/themes/ajax-api/reference/cart) (read only) - [Collect](https://help.shopify.com/api/reference/collect/) - [Comment](https://help.shopify.com/api/reference/comment/) - Comment -> [Event](https://help.shopify.com/api/reference/event/) @@ -323,16 +352,21 @@ Some resources are available directly, some resources are only available through - [DiscountCode](https://help.shopify.com/en/api/reference/discounts/discountcode) - [Event](https://help.shopify.com/api/reference/event/) - [FulfillmentService](https://help.shopify.com/api/reference/fulfillmentservice) +- [Fulfillment](https://shopify.dev/api/admin-rest/2023-01/resources/fulfillment) +- [FulfillmentOrder](https://shopify.dev/api/admin-rest/2023-01/resources/fulfillmentorder) +- FulfillmentOrder -> [FulfillmentRequest](https://shopify.dev/api/admin-rest/2023-01/resources/fulfillmentrequest) +- FulfillmentOrder -> [Fulfillment](https://shopify.dev/api/admin-rest/2023-01/resources/fulfillment) - [GiftCard](https://help.shopify.com/api/reference/gift_card) _(Shopify Plus Only)_ +- GiftCard -> [Adjustment](https://shopify.dev/docs/api/admin-rest/2023-01/resources/gift-card-adjustment) _(Shopify Plus Only)_ - [InventoryItem](https://help.shopify.com/api/reference/inventoryitem) - [InventoryLevel](https://help.shopify.com/api/reference/inventorylevel) - [Location](https://help.shopify.com/api/reference/location/) _(read only)_ - Location -> [InventoryLevel](https://help.shopify.com/api/reference/inventorylevel) +- Location -> [Metafield](https://help.shopify.com/api/reference/metafield) - [Metafield](https://help.shopify.com/api/reference/metafield) - [Multipass](https://help.shopify.com/api/reference/multipass) _(Shopify Plus Only, API not available yet)_ - [Order](https://help.shopify.com/api/reference/order) -- Order -> [Fulfillment](https://help.shopify.com/api/reference/fulfillment) -- Order -> Fulfillment -> [Event](https://help.shopify.com/api/reference/fulfillmentevent) +- Order -> [FulfillmentOrder](https://shopify.dev/api/admin-rest/2023-01/resources/fulfillmentorder) - Order -> [Risk](https://help.shopify.com/api/reference/order_risks) - Order -> [Refund](https://help.shopify.com/api/reference/refund) - Order -> [Transaction](https://help.shopify.com/api/reference/transaction) @@ -359,6 +393,8 @@ Some resources are available directly, some resources are only available through - [Shop](https://help.shopify.com/api/reference/shop) _(read only)_ - [SmartCollection](https://help.shopify.com/api/reference/smartcollection) - SmartCollection -> [Event](https://help.shopify.com/api/reference/event/) +- [ShopifyPayment](https://shopify.dev/docs/admin-api/rest/reference/shopify_payments/) +- ShopifyPayment -> [Dispute](https://shopify.dev/docs/admin-api/rest/reference/shopify_payments/dispute/) _(read only)_ - [Theme](https://help.shopify.com/api/reference/theme) - Theme -> [Asset](https://help.shopify.com/api/reference/asset/) - [User](https://help.shopify.com/api/reference/user) _(read only, Shopify Plus Only)_ @@ -482,6 +518,38 @@ The custom methods are specific to some resources which may not be available for - User -> - [current()](https://help.shopify.com/api/reference/user#current) Get the current logged-in user + +### FulfillmentRequest Resource - including actions +- Mapped FulfillmentOrder->FulfillmentRequest +- Mapped Order(id)->FulfillmentOrder + +```php +// Requesting the FulfilmentOrder for a given order +$fo = $client->Order("1234567890")->FulfillmentOrder()->get(); + +// Requesting assigned fulfillment orders (with status fulfillment_requested) +$shopify->AssignedFulfillmentOrder()->get(["assignment_status" => "fulfillment_requested"]); + +// Creating a FulfilmentRequest +// Follow instructions to get partial fulfilments +$fr = $client->FulfillmentOrder('0987654321')->FulfillmentRequest->post([]); + +// Accepting \ Rejecting a FulfilmentRequest +$fr = $client->FulfillmentOrder('0987654321')->FulfillmentRequest->accept(); +$fr = $client->FulfillmentOrder('0987654321')->FulfillmentRequest->reject(); + +// Communicating fulfillment +$client->Fulfillment->post($body) +``` + +### Shopify API features headers +To send `X-Shopify-Api-Features` headers while using the SDK, you can use the following: + +``` +$config['ShopifyApiFeatures'] = ['include-presentment-prices']; +$shopify = new PHPShopify\ShopifySDK($config); +``` + ## Reference - [Shopify API Reference](https://help.shopify.com/api/reference/) diff --git a/lib/AccessScope.php b/lib/AccessScope.php new file mode 100644 index 0000000..0ce0c97 --- /dev/null +++ b/lib/AccessScope.php @@ -0,0 +1,36 @@ +getResourcePath() . '.json'; + } +} diff --git a/lib/ApiDeprecations.php b/lib/ApiDeprecations.php new file mode 100644 index 0000000..8d1b698 --- /dev/null +++ b/lib/ApiDeprecations.php @@ -0,0 +1,39 @@ + + * @author Steve Barbera + * Created at 8/18/16 3:39 PM UTC+06:00 + * + * @see https://shopify.dev/api/admin-rest/2022-04/resources/deprecated-api-calls#get-deprecated-api-calls Shopify API Reference for API Deprecations + */ + +namespace PHPShopify; + + +class ApiDeprecations extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'deprecated_api_calls'; + + /** + * @inheritDoc + */ + public $readOnly = true; + + /** + * @inheritDoc + */ + public $countEnabled = false; + + /** + * @inheritDoc + */ + public function pluralizeKey() + { + //Only api deprecations, so no pluralize + return 'deprecated_api_calls'; + } +} 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/AssignedFulfillmentOrder.php b/lib/AssignedFulfillmentOrder.php new file mode 100644 index 0000000..108983d --- /dev/null +++ b/lib/AssignedFulfillmentOrder.php @@ -0,0 +1,16 @@ += 400) { + throw new SdkException("The shop is invalid or the authorization code has already been used."); + } + return isset($response['access_token']) ? $response['access_token'] : null; } else { throw new SdkException("This request is not initiated from a valid shopify shop!"); diff --git a/lib/Balance.php b/lib/Balance.php new file mode 100644 index 0000000..4144c5c --- /dev/null +++ b/lib/Balance.php @@ -0,0 +1,51 @@ + + * @author Matthew Crigger + * + * @see https://help.shopify.com/en/api/reference/shopify_payments/balance Shopify API Reference for Shopify Payment Balance + */ + +namespace PHPShopify; + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * + * + */ +class Balance extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'balance'; + + /** + * Get the pluralized version of the resource key + * + * Normally its the same as $resourceKey appended with 's', when it's different, the specific resource class will override this function + * + * @return string + */ + protected function pluralizeKey() + { + return $this->resourceKey; + } + + /** + * If the resource is read only. (No POST / PUT / DELETE actions) + * + * @var boolean + */ + public $readOnly = true; + + /** + * @inheritDoc + */ + protected $childResource = array( + 'Transactions' + ); +} \ No newline at end of file diff --git a/lib/Batch.php b/lib/Batch.php new file mode 100644 index 0000000..49e125b --- /dev/null +++ b/lib/Batch.php @@ -0,0 +1,33 @@ + Batch action + * -------------------------------------------------------------------------- + * + */ + +class Batch extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'batch'; + + protected function getResourcePath() + { + return $this->resourceKey; + } + + protected function wrapData($dataArray, $dataKey = null) + { + return ['discount_codes' => $dataArray]; + } + +} diff --git a/lib/Cart.php b/lib/Cart.php new file mode 100644 index 0000000..15ad000 --- /dev/null +++ b/lib/Cart.php @@ -0,0 +1,24 @@ + + * 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/Collection.php b/lib/Collection.php index 1d2c84a..0c21a40 100644 --- a/lib/Collection.php +++ b/lib/Collection.php @@ -8,8 +8,10 @@ * -------------------------------------------------------------------------- * * @property-read Product $Product + * @property-read Metafield $Metafield * * @method Product Product(integer $id = null) + * @method Metafield Metafield(integer $id = null) * * @see https://shopify.dev/docs/admin-api/rest/reference/products/collection * @@ -31,5 +33,6 @@ class Collection extends ShopifyResource */ protected $childResource = array( 'Product', + 'Metafield', ); -} \ No newline at end of file +} diff --git a/lib/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 73567e1..b4517e4 100644 --- a/lib/CurlRequest.php +++ b/lib/CurlRequest.php @@ -35,6 +35,20 @@ class CurlRequest */ public static $lastHttpResponseHeaders = array(); + /** + * Total time spent in sleep during multiple requests (in seconds) + * + * @var int + */ + public static $totalRetrySleepTime = 0; + + /** + * Curl additional configuration + * + * @var array + */ + protected static $config = array(); + /** * Initialize the curl resource * @@ -57,6 +71,10 @@ protected static function init($url, $httpHeaders = array()) curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'PHPClassic/PHPShopify'); + foreach (self::$config as $option => $value) { + curl_setopt($ch, $option, $value); + } + $headers = array(); foreach ($httpHeaders as $key => $value) { $headers[] = "$key: $value"; @@ -139,6 +157,16 @@ public static function delete($url, $httpHeaders = array()) return self::processRequest($ch); } + /** + * Set curl additional configuration + * + * @param array $config + */ + public static function config($config = array()) + { + self::$config = $config; + } + /** * Execute a request, release the resource and return output * @@ -160,13 +188,23 @@ protected static function processRequest($ch) break; } - $limitHeader = explode('/', $response->getHeader('X-Shopify-Shop-Api-Call-Limit'), 2); + $apiCallLimit = $response->getHeader('X-Shopify-Shop-Api-Call-Limit'); - if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { - throw new ResourceRateLimitException($response->getBody()); + if (!empty($apiCallLimit)) { + $limitHeader = explode('/', $apiCallLimit, 2); + if (isset($limitHeader[1]) && $limitHeader[0] < $limitHeader[1]) { + throw new ResourceRateLimitException($response->getBody()); + } } + + $retryAfter = $response->getHeader('Retry-After'); - usleep(500000); + if ($retryAfter === null) { + break; + } + + self::$totalRetrySleepTime += (float)$retryAfter; + sleep((float)$retryAfter); } if (curl_errno($ch)) { @@ -180,5 +218,4 @@ protected static function processRequest($ch) return $response->getBody(); } - } diff --git a/lib/CurlResponse.php b/lib/CurlResponse.php index 02a9eec..b6922e9 100644 --- a/lib/CurlResponse.php +++ b/lib/CurlResponse.php @@ -53,6 +53,7 @@ public function getHeaders() */ public function getHeader($key) { + $key = strtolower($key); return isset($this->headers[$key]) ? $this->headers[$key] : null; } diff --git a/lib/Customer.php b/lib/Customer.php index a058624..e358ccc 100644 --- a/lib/Customer.php +++ b/lib/Customer.php @@ -22,7 +22,7 @@ * -------------------------------------------------------------------------- * Customer -> Custom actions * -------------------------------------------------------------------------- - * @method array search() Search for customers matching supplied query + * @method array search(string $query = '') Search for customers matching supplied query */ class Customer extends ShopifyResource { @@ -60,4 +60,21 @@ public function send_invite($customer_invite = array()) return $this->post($dataArray, $url, false); } -} \ No newline at end of file + + /** + * Create account_activation_link for customer. + * + * @param array $customer_id + * + * @return array + */ + public function account_activation_url($customer_id = 0) + { + if (!(int)$customer_id > 0) { + return false; + } + + $url = $this->generateUrl(array(), $customer_id.'/account_activation_url'); + return $this->post(array(), $url, false); + } +} diff --git a/lib/Dispute.php b/lib/Dispute.php new file mode 100644 index 0000000..e769872 --- /dev/null +++ b/lib/Dispute.php @@ -0,0 +1,30 @@ + + * Created at 01/06/2020 16:45 AM UTC+03:00 + * + * @see https://shopify.dev/docs/admin-api/rest/reference/shopify_payments/dispute Shopify API Reference for Dispute + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShopifyResource $DiscountCode + * + * @method ShopifyResource DiscountCode(integer $id = null) + * + */ +class Dispute extends ShopifyResource +{ + /** + * @inheritDoc + */ + public $resourceKey = 'dispute'; + + +} \ No newline at end of file diff --git a/lib/DraftOrder.php b/lib/DraftOrder.php index 902cee5..9e3be91 100644 --- a/lib/DraftOrder.php +++ b/lib/DraftOrder.php @@ -12,6 +12,14 @@ /** + * -------------------------------------------------------------------------- + * DraftOrder -> Child Resources + * -------------------------------------------------------------------------- + * + * @property-read Metafield $Metafield + * + * @method Metafield Metafield(integer $id = null) + * * -------------------------------------------------------------------------- * DraftOrder -> Custom actions * -------------------------------------------------------------------------- @@ -39,4 +47,11 @@ class DraftOrder extends ShopifyResource protected $customPutActions = array( 'complete', ); -} \ No newline at end of file + + /** + * @inheritDoc + */ + protected $childResource = array( + 'Metafield', + ); +} diff --git a/lib/Fulfillment.php b/lib/Fulfillment.php index 18138d2..ec9cd75 100644 --- a/lib/Fulfillment.php +++ b/lib/Fulfillment.php @@ -24,6 +24,7 @@ * @method array complete() Complete a fulfillment * @method array open() Open a pending fulfillment * @method array cancel() Cancel a fulfillment + * @method array update_tracking(array $data) Updates the tracking information for a fulfillment. * */ class Fulfillment extends ShopifyResource @@ -47,5 +48,6 @@ class Fulfillment extends ShopifyResource 'complete', 'open', 'cancel', + 'update_tracking', ); -} \ No newline at end of file +} diff --git a/lib/FulfillmentOrder.php b/lib/FulfillmentOrder.php new file mode 100644 index 0000000..8d20011 --- /dev/null +++ b/lib/FulfillmentOrder.php @@ -0,0 +1,55 @@ + Child Resources + * -------------------------------------------------------------------------- + * @property-read FulfillmentRequest $FulfillmentRequest + * @property-read Fulfillment $Fulfillment + * + * -------------------------------------------------------------------------- + * Fulfillment -> Custom actions + * -------------------------------------------------------------------------- + * @method array cancel() Cancel a fulfillment order + * @method array open() Open a fulfillment order + * @method array close() Close a fulfillment order + * @method array move() Move a fulfilment order to a new location + * @method array reschedule() Reschedule fulfill_at_time of a scheduled fulfillment order + * @method array hold(array $data) Hold a fulfillment order + * @method array release_hold() Release hold on a fulfillment order + */ +class FulfillmentOrder extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'fulfillment_order'; + + /** + * @inheritDoc + */ + protected $childResource = array ( + 'FulfillmentRequest', + 'Fulfillment' + ); + + /** + * @inheritDoc + */ + protected $customPostActions = array( + 'close', + 'open', + 'cancel', + 'move', + 'reschedule', + 'hold', + 'release_hold' + ); +} \ No newline at end of file diff --git a/lib/FulfillmentRequest.php b/lib/FulfillmentRequest.php new file mode 100644 index 0000000..e887c73 --- /dev/null +++ b/lib/FulfillmentRequest.php @@ -0,0 +1,47 @@ + + * Created at 8/19/16 5:28 PM UTC+06:00 + * + * @see https://help.shopify.com/api/reference/fulfillmentservice Shopify API Reference for FulfillmentService + */ + +namespace PHPShopify; + +/** + * -------------------------------------------------------------------------- + * FulfillmentRequest -> Child Resources + * -------------------------------------------------------------------------- + * + * -------------------------------------------------------------------------- + * FulfillmentRequest -> Custom actions + * -------------------------------------------------------------------------- + * @method array accept() Accept a fulfilment order + * @method array reject() Rejects a fulfillment order + */ +class FulfillmentRequest extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'fulfillment_request'; + + /** + * @inheritDoc + */ + public $countEnabled = false; + + /** + * @inheritDoc + */ + protected $customPostActions = array( + 'accept', + 'reject' + ); + + protected function pluralizeKey() + { + return $this->resourceKey; + } +} \ No newline at end of file diff --git a/lib/GiftCard.php b/lib/GiftCard.php index 2391064..07d5428 100644 --- a/lib/GiftCard.php +++ b/lib/GiftCard.php @@ -45,4 +45,11 @@ public function disable() return $this->post($dataArray, $url); } -} \ No newline at end of file + + /** + * @inheritDoc + */ + protected $childResource = array( + 'GiftCardAdjustment' => 'Adjustment' + ); +} diff --git a/lib/GiftCardAdjustment.php b/lib/GiftCardAdjustment.php new file mode 100644 index 0000000..933f54b --- /dev/null +++ b/lib/GiftCardAdjustment.php @@ -0,0 +1,40 @@ + + * Created at: 8/21/16 8:39 AM UTC+06:00 + * + * @see https://shopify.dev/docs/api/admin-rest/2023-01/resources/gift-card-adjustment Shopify API Reference for Gift Card Adjustment + * @note - requires gift_card_adjustments access scope enabled by Shopify Support + * + * @usage: + * + $shopify = \PHPShopify\ShopifySDK::config($config); + + $gift_card_id = 88888888888; + + $gift_card_adjustment_id = 999999999999; + + // Get all gift card adjustments + $shopify->GiftCard($gift_card_id)->Adjustment()->get(); + + // Get a single gift card adjustment + $shopify->GiftCard($gift_card_id)->Adjustment($gift_card_adjustment_id)->get(); + + // Create a gift card adjustment + $shopify->GiftCard($gift_card_id)->Adjustment()->post([ + 'amount' => 5, + 'note' => 'Add $5 to gift card' + ]); + */ + +namespace PHPShopify; + + +class GiftCardAdjustment extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'adjustment'; +} diff --git a/lib/GraphQL.php b/lib/GraphQL.php index 185593b..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); } @@ -75,4 +75,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 8aef907..d6e28bb 100644 --- a/lib/HttpRequestGraphQL.php +++ b/lib/HttpRequestGraphQL.php @@ -45,13 +45,16 @@ protected static function prepareRequest($httpHeaders = array(), $data = array() } self::$httpHeaders = $httpHeaders; + self::$httpHeaders['Content-type'] = 'application/json'; 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::$postDataGraphQL = json_encode(['query' => $data]); } + + self::$httpHeaders['Content-Length'] = strlen(self::$postDataGraphQL); + } /** @@ -66,10 +69,11 @@ protected static function prepareRequest($httpHeaders = array(), $data = array() */ public static function post($url, $data, $httpHeaders = array(), $variables = null) { + self::prepareRequest($httpHeaders, $data, $variables); - $response = CurlRequest::post($url, self::$postDataGraphQL, self::$httpHeaders); + self::$postDataJSON = self::$postDataGraphQL; - return self::processResponse($response); + return self::processRequest('POST', $url); } -} \ No newline at end of file +} diff --git a/lib/HttpRequestJson.php b/lib/HttpRequestJson.php index ceab78f..642e4ec 100644 --- a/lib/HttpRequestJson.php +++ b/lib/HttpRequestJson.php @@ -19,7 +19,6 @@ */ class HttpRequestJson { - /** * HTTP request headers * @@ -32,7 +31,7 @@ class HttpRequestJson * * @var string */ - private static $postDataJSON; + protected static $postDataJSON; /** @@ -68,9 +67,7 @@ public static function get($url, $httpHeaders = array()) { self::prepareRequest($httpHeaders); - $response = CurlRequest::get($url, self::$httpHeaders); - - return self::processResponse($response); + return self::processRequest('GET', $url); } /** @@ -86,9 +83,7 @@ public static function post($url, $dataArray, $httpHeaders = array()) { self::prepareRequest($httpHeaders, $dataArray); - $response = CurlRequest::post($url, self::$postDataJSON, self::$httpHeaders); - - return self::processResponse($response); + return self::processRequest('POST', $url); } /** @@ -104,9 +99,7 @@ public static function put($url, $dataArray, $httpHeaders = array()) { self::prepareRequest($httpHeaders, $dataArray); - $response = CurlRequest::put($url, self::$postDataJSON, self::$httpHeaders); - - return self::processResponse($response); + return self::processRequest('PUT', $url); } /** @@ -121,9 +114,68 @@ public static function delete($url, $httpHeaders = array()) { self::prepareRequest($httpHeaders); - $response = CurlRequest::delete($url, self::$httpHeaders); + return self::processRequest('DELETE', $url); + } - return self::processResponse($response); + /** + * Process a curl request and return decoded JSON response + * + * @param string $method Request http method ('GET', 'POST', 'PUT' or 'DELETE') + * @param string $url Request URL + * + * @throws CurlException if response received with unexpected HTTP code. + * + * @return array + */ + public static function processRequest($method, $url) { + $retry = 0; + $raw = null; + + while(true) { + try { + switch($method) { + case 'GET': + $raw = CurlRequest::get($url, self::$httpHeaders); + break; + case 'POST': + $raw = CurlRequest::post($url, self::$postDataJSON, self::$httpHeaders); + break; + case 'PUT': + $raw = CurlRequest::put($url, self::$postDataJSON, self::$httpHeaders); + break; + case 'DELETE': + $raw = CurlRequest::delete($url, self::$httpHeaders); + break; + default: + throw new \Exception("unexpected request method '$method'"); + } + + return self::processResponse($raw); + } catch(\Exception $e) { + if (!self::shouldRetry($raw, $e, $retry++)) { + throw $e; + } + } + } + } + + /** + * Evaluate if send again a request + * + * @param string $response Raw request response + * @param exception $error the request error occured + * @param integer $retry the current number of retry + * + * @return bool + */ + public static function shouldRetry($response, $error, $retry) { + $config = ShopifySDK::$config; + + if (isset($config['RequestRetryCallback'])) { + return $config['RequestRetryCallback']($response, $error, $retry); + } + + return false; } /** @@ -131,12 +183,33 @@ public static function delete($url, $httpHeaders = array()) * * @param string $response * - * @return array + * @return string */ protected static function processResponse($response) { + $responseArray = json_decode($response, true); - return json_decode($response, true); - } + if ($responseArray === null) { + //Something went wrong, Checking HTTP Codes + $httpOK = 200; //Request Successful, OK. + $httpCreated = 201; //Create Successful. + $httpDeleted = 204; //Delete Successful + $httpOther = 303; //See other (headers). + + $lastHttpResponseHeaders = CurlRequest::$lastHttpResponseHeaders; + + //should be null if any other library used for http calls + $httpCode = CurlRequest::$lastHttpCode; + + if ($httpCode == $httpOther && array_key_exists('location', $lastHttpResponseHeaders)) { + return ['location' => $lastHttpResponseHeaders['location']]; + } -} \ No newline at end of file + if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { + throw new Exception\CurlException("Request failed with HTTP Code $httpCode.", $httpCode); + } + } + + return $responseArray; + } +} diff --git a/lib/Location.php b/lib/Location.php index f4a589a..c5c1247 100644 --- a/lib/Location.php +++ b/lib/Location.php @@ -9,6 +9,14 @@ namespace PHPShopify; +/** + * -------------------------------------------------------------------------- + * Location -> Child Resources + * -------------------------------------------------------------------------- + * @property-read Metafield $Metafield + * + * @method Metafield Metafield(integer $id = null) + */ class Location extends ShopifyResource { @@ -32,5 +40,6 @@ class Location extends ShopifyResource */ protected $childResource = array( 'InventoryLevel', + 'Metafield', ); -} \ No newline at end of file +} diff --git a/lib/Order.php b/lib/Order.php index 98efd2a..5ae5f5e 100644 --- a/lib/Order.php +++ b/lib/Order.php @@ -15,6 +15,7 @@ * -------------------------------------------------------------------------- * Order -> Child Resources * -------------------------------------------------------------------------- + * @property-read FulfillmentOrder $FulfillmentOrder * @property-read Fulfillment $Fulfillment * @property-read OrderRisk $Risk * @property-read Refund $Refund @@ -49,6 +50,7 @@ class Order extends ShopifyResource */ protected $childResource = array ( 'Fulfillment', + 'FulfillmentOrder', 'OrderRisk' => 'Risk', 'Refund', 'Transaction', diff --git a/lib/Payouts.php b/lib/Payouts.php new file mode 100644 index 0000000..e43245b --- /dev/null +++ b/lib/Payouts.php @@ -0,0 +1,25 @@ + + * @author Matthew Crigger + * + * @see https://help.shopify.com/en/api/reference/shopify_payments/payout Shopify API Reference for Shopify Payment Payouts + */ + +namespace PHPShopify; + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * + * + */ +class Payouts extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'payout'; +} \ No newline at end of file diff --git a/lib/PriceRule.php b/lib/PriceRule.php index de08014..15c77a9 100644 --- a/lib/PriceRule.php +++ b/lib/PriceRule.php @@ -17,6 +17,7 @@ * @property-read ShopifyResource $DiscountCode * * @method ShopifyResource DiscountCode(integer $id = null) + * @method ShopifyResource Batch() * */ class PriceRule extends ShopifyResource @@ -26,15 +27,11 @@ class PriceRule extends ShopifyResource */ public $resourceKey = 'price_rule'; - /** - * @inheritDoc - */ - public $countEnabled = false; - /** * @inheritDoc */ protected $childResource = array( - 'DiscountCode' + 'DiscountCode', + 'Batch', ); -} \ No newline at end of file +} diff --git a/lib/Refund.php b/lib/Refund.php index c91edbe..2fe6e50 100644 --- a/lib/Refund.php +++ b/lib/Refund.php @@ -14,7 +14,7 @@ * -------------------------------------------------------------------------- * Refund -> Custom actions * -------------------------------------------------------------------------- - * @method array calculate() Calculate a Refund. + * @method array calculate(array $calculation = null) Calculate a Refund. * */ class Refund extends ShopifyResource @@ -30,4 +30,4 @@ class Refund extends ShopifyResource protected $customPostActions = array ( 'calculate', ); -} \ No newline at end of file +} 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/ShopifyPayment.php b/lib/ShopifyPayment.php new file mode 100644 index 0000000..54fc6b3 --- /dev/null +++ b/lib/ShopifyPayment.php @@ -0,0 +1,53 @@ + + * Created at 01/06/2020 16:45 AM UTC+03:00 + * + * @see https://shopify.dev/docs/admin-api/rest/reference/shopify_payments Shopify API Reference for ShopifyPayment + */ + +namespace PHPShopify; + + +/** + * -------------------------------------------------------------------------- + * ShopifyPayment -> Child Resources + * -------------------------------------------------------------------------- + * @property-read ShopifyResource $Dispute + * + * @method ShopifyResource Dispute(integer $id = null) + * + * @property-read ShopifyResource $Balance + * + * @method ShopifyResource Balance(integer $id = null) + * + * @property-read ShopifyResource $Payouts + * + * @method ShopifyResource Payouts(integer $id = null) + * + + */ +class ShopifyPayment extends ShopifyResource +{ + /** + * @inheritDoc + */ + public $resourceKey = 'shopify_payment'; + + /** + * If the resource is read only. (No POST / PUT / DELETE actions) + * + * @var boolean + */ + public $readOnly = true; + + /** + * @inheritDoc + */ + protected $childResource = array( + 'Balance', + 'Dispute', + 'Payouts', + ); +} \ No newline at end of file diff --git a/lib/ShopifyResource.php b/lib/ShopifyResource.php index 66ce2b7..19bc198 100644 --- a/lib/ShopifyResource.php +++ b/lib/ShopifyResource.php @@ -136,6 +136,18 @@ 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 + * @var string $discountLocation + */ + private $discountLocation = null; + public function __construct($id = null, $parentResourceUrl = '') { $this->id = $id; @@ -149,6 +161,12 @@ public function __construct($id = null, $parentResourceUrl = '') } elseif (!isset($config['ApiKey']) || !isset($config['Password'])) { throw new SdkException("Either AccessToken or ApiKey+Password Combination (in case of private API) is required to access the resources. Please check SDK configuration!"); } + + if (isset($config['ShopifyApiFeatures'])) { + foreach($config['ShopifyApiFeatures'] as $apiFeature) { + $this->httpHeaders['X-Shopify-Api-Features'] = $apiFeature; + } + } } /** @@ -313,7 +331,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)) : ''); } /** @@ -508,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 @@ -516,45 +534,51 @@ 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) { - //Something went wrong, Checking HTTP Codes - $httpOK = 200; //Request Successful, OK. - $httpCreated = 201; //Create Successful. - $httpDeleted = 204; //Delete Successful + $lastResponseHeaders = CurlRequest::$lastHttpResponseHeaders; + + $this->getLinks($lastResponseHeaders); - //should be null if any other library used for http calls - $httpCode = CurlRequest::$lastHttpCode; + $this->getLocationHeader($lastResponseHeaders); - if ($httpCode != null && $httpCode != $httpOK && $httpCode != $httpCreated && $httpCode != $httpDeleted) { - throw new Exception\CurlException("Request failed with HTTP Code $httpCode."); - } - } + $httpCode = CurlRequest::$lastHttpCode; + $this->httpCode = $httpCode; - $lastResponseHeaders = CurlRequest::$lastHttpResponseHeaders; - $this->getLinks($lastResponseHeaders); + if (isset($response['errors'])) { + $message = $this->castString($response['errors']); - if (isset($responseArray['errors'])) { - $message = $this->castString($responseArray['errors']); + //check account already enabled or not + if($message=='account already enabled'){ + return array('account_activation_url'=>false); + } - throw new ApiException($message, CurlRequest::$lastHttpCode); + throw new ApiException($message, $httpCode); } - if ($dataKey && isset($responseArray[$dataKey])) { - return $responseArray[$dataKey]; + if ($dataKey && isset($response[$dataKey])) { + return $response[$dataKey]; } else { - return $responseArray; + return $response; } } - public function getLinks($responseHeaders){ + public function getLinks($responseHeaders) { $this->nextLink = $this->getLink($responseHeaders,'next'); $this->prevLink = $this->getLink($responseHeaders,'previous'); } + public function getLocationHeader($responseHeaders) { + + if(!empty($responseHeaders['location'])) { + $this->discountLocation = $responseHeaders['location']; + } + + } + public function getLink($responseHeaders, $type='next'){ if(array_key_exists('x-shopify-api-version', $responseHeaders) @@ -590,6 +614,10 @@ public function getNextLink(){ return $this->nextLink; } + public function getDiscountLocation(){ + return $this->discountLocation; + } + public function getUrlParams($url) { if ($url) { $parts = parse_url($url); diff --git a/lib/ShopifySDK.php b/lib/ShopifySDK.php index e6e66f5..5686107 100644 --- a/lib/ShopifySDK.php +++ b/lib/ShopifySDK.php @@ -67,9 +67,15 @@ use PHPShopify\Exception\SdkException; /** + * @property-read ApplicationCredit $applicationCredit * @property-read AbandonedCheckout $AbandonedCheckout + * @property-read AccessScope $AccessScope + * @property-read ApiDeprecations $ApiDeprecations + * @property-read ApplicationCharge $ApplicationCharge + * @property-read AssignedFulfillmentOrder $AssignedFulfillmentOrder * @property-read Blog $Blog * @property-read CarrierService $CarrierService + * @property-read Cart $Cart * @property-read Collect $Collect * @property-read Collection $Collection * @property-read Comment $Comment @@ -81,10 +87,13 @@ * @property-read Discount $Discount * @property-read DiscountCode $DiscountCode * @property-read DraftOrder $DraftOrder - * @property-read PriceRule $PriceRule + * @property-read Checkout $Checkout * @property-read Event $Event + * @property-read Fulfillment $Fulfillment + * @property-read FulfillmentOrder $FulfillmentOrder * @property-read FulfillmentService $FulfillmentService * @property-read GiftCard $GiftCard + * @property-read GiftCardAdjustment $GiftCardAdjustment * @property-read InventoryItem $InventoryItem * @property-read InventoryLevel $InventoryLevel * @property-read Location $Location @@ -93,23 +102,34 @@ * @property-read Order $Order * @property-read Page $Page * @property-read Policy $Policy + * @property-read PriceRule $PriceRule * @property-read Product $Product * @property-read ProductListing $ProductListing + * @property-read CollectionListing $CollectionListing * @property-read ProductVariant $ProductVariant * @property-read RecurringApplicationCharge $RecurringApplicationCharge * @property-read Redirect $Redirect + * @property-read Report $Report * @property-read ScriptTag $ScriptTag * @property-read ShippingZone $ShippingZone * @property-read Shop $Shop + * @property-read ShopifyPayment $ShopifyPayment * @property-read SmartCollection $SmartCollection + * @property-read TenderTransaction $TenderTransaction * @property-read Theme $Theme * @property-read User $User * @property-read Webhook $Webhook * @property-read GraphQL $GraphQL * + * @method ApplicationCredit ApplicationCredit(integer $id = null) * @method AbandonedCheckout AbandonedCheckout(integer $id = null) + * @method AssignedFulfillmentOrder AssignedFulfillmentOrder(string $assignment_status = null, array $location_ids = null) + * @method AccessScope AccessScope() + * @method ApiDeprecations ApiDeprecations() + * @method ApplicationCharge ApplicationCharge(integer $id = null) * @method Blog Blog(integer $id = null) * @method CarrierService CarrierService(integer $id = null) + * @method Cart Cart(string $cart_token = null) * @method Collect Collect(integer $id = null) * @method Collection Collection(integer $id = null) * @method Comment Comment(integer $id = null) @@ -121,27 +141,35 @@ * @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 Fulfillment Fulfillment(integer $id = null) * @method FulfillmentService FulfillmentService(integer $id = null) + * @method FulfillmentOrder FulfillmentOrder(integer $id = null) * @method GiftCard GiftCard(integer $id = null) + * @method GiftCardAdjustment GiftCardAdjustment(integer $id = null) * @method InventoryItem InventoryItem(integer $id = null) * @method InventoryLevel InventoryLevel(integer $id = null) * @method Location Location(integer $id = null) * @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 PriceRule PriceRule(integer $id = null) * @method RecurringApplicationCharge RecurringApplicationCharge(integer $id = null) * @method Redirect Redirect(integer $id = null) + * @method Report Report(integer $id = null) * @method ScriptTag ScriptTag(integer $id = null) * @method ShippingZone ShippingZone(integer $id = null) * @method Shop Shop(integer $id = null) + * @method ShopifyPayment ShopifyPayment() * @method SmartCollection SmartCollection(integer $id = null) + * @method TenderTransaction TenderTransaction() * @method Theme Theme(int $id = null) * @method User User(integer $id = null) * @method Webhook Webhook(integer $id = null) @@ -156,9 +184,15 @@ class ShopifySDK */ protected $resources = array( 'AbandonedCheckout', + 'Adjustment', + 'ApplicationCredit', + 'AssignedFulfillmentOrder', + 'AccessScope', + 'ApiDeprecations', 'ApplicationCharge', 'Blog', 'CarrierService', + 'Cart', 'Collect', 'Collection', 'Comment', @@ -171,7 +205,9 @@ class ShopifySDK 'DiscountCode', 'DraftOrder', 'Event', + 'Fulfillment', 'FulfillmentService', + 'FulfillmentOrder', 'GiftCard', 'InventoryItem', 'InventoryLevel', @@ -179,11 +215,13 @@ class ShopifySDK 'Metafield', 'Multipass', 'Order', + 'Checkout', 'Page', 'Policy', 'Product', 'ProductListing', 'ProductVariant', + 'CollectionListing', 'PriceRule', 'RecurringApplicationCharge', 'Redirect', @@ -192,6 +230,8 @@ class ShopifySDK 'ShippingZone', 'Shop', 'SmartCollection', + 'ShopifyPayment', + 'TenderTransaction', 'Theme', 'User', 'Webhook', @@ -211,7 +251,7 @@ class ShopifySDK /** * @var string Default Shopify API version */ - public static $defaultApiVersion = '2020-01'; + public static $defaultApiVersion = '2025-01'; /** * Shop / API configurations @@ -229,16 +269,22 @@ class ShopifySDK protected $childResources = array( 'Article' => 'Blog', 'Asset' => 'Theme', + 'Balance' => 'ShopifyPayment', 'CustomerAddress' => 'Customer', + 'GiftCardAdjustment'=> 'GiftCard', + 'Dispute' => 'ShopifyPayment', 'Fulfillment' => 'Order', 'FulfillmentEvent' => 'Fulfillment', 'OrderRisk' => 'Order', + 'Payouts' => 'ShopifyPayment', 'ProductImage' => 'Product', 'ProductVariant' => 'Product', 'DiscountCode' => 'PriceRule', 'Province' => 'Country', 'Refund' => 'Order', 'Transaction' => 'Order', + 'ShippingRate' => 'Checkout', + 'Transactions' => 'Balance', 'UsageCharge' => 'RecurringApplicationCharge', ); @@ -334,6 +380,10 @@ public static function config($config) static::$timeAllowedForEachApiCall = $config['AllowedTimePerCall']; } + if (isset($config['Curl']) && is_array($config['Curl'])) { + CurlRequest::config($config['Curl']); + } + return new ShopifySDK; } @@ -382,6 +432,28 @@ public static function getApiUrl() { return self::$config['ApiUrl']; } + /** + * Returns the appropriate URL for the host that should load the embedded app. + * + * @param string $host The host value received from Shopify + * + * @return string + */ + public static function getEmbeddedAppUrl($host) + { + if (empty($host)) { + throw new SdkException("Host value cannot be empty"); + } + + $decodedHost = base64_decode($host, true); + if (!$decodedHost) { + throw new SdkException("Host was not a valid base64 string"); + } + + $apiKey = self::$config['ApiKey']; + return "https://$decodedHost/apps/$apiKey"; + } + /** * Maintain maximum 2 calls per second to the API * diff --git a/lib/SmartCollection.php b/lib/SmartCollection.php index d39925c..2d7a347 100644 --- a/lib/SmartCollection.php +++ b/lib/SmartCollection.php @@ -15,8 +15,10 @@ * SmartCollection -> Child Resources * -------------------------------------------------------------------------- * @property-read Event $Event + * @property-read Metafield $Metafield * * @method Event Event(integer $id = null) + * @method Metafield Metafield(integer $id = null) * * -------------------------------------------------------------------------- * SmartCollection -> Custom actions diff --git a/lib/TenderTransaction.php b/lib/TenderTransaction.php new file mode 100644 index 0000000..4265c41 --- /dev/null +++ b/lib/TenderTransaction.php @@ -0,0 +1,18 @@ + + * @author Matthew Crigger + * + * @see https://help.shopify.com/en/api/reference/shopify_payments/transaction Shopify API Reference for Shopify Payment Transactions + */ + +namespace PHPShopify; + + +class Transactions extends ShopifyResource +{ + /** + * @inheritDoc + */ + protected $resourceKey = 'transaction'; + + /** + * If the resource is read only. (No POST / PUT / DELETE actions) + * + * @var boolean + */ + public $readOnly = true; +} diff --git a/tests/CountryTest.php b/tests/CountryTest.php index 1a0ddc2..1637cde 100644 --- a/tests/CountryTest.php +++ b/tests/CountryTest.php @@ -22,7 +22,7 @@ class CountryTest extends TestSimpleResource * @inheritDoc */ public $putArray = array( - "tax" => 0.01, + "tax" => 0.15, ); /** @@ -32,4 +32,4 @@ class CountryTest extends TestSimpleResource public function testGet() { $this->assertEquals(1, 1); } -} \ No newline at end of file +} diff --git a/tests/GraphQLTest.php b/tests/GraphQLTest.php new file mode 100644 index 0000000..fcc12c4 --- /dev/null +++ b/tests/GraphQLTest.php @@ -0,0 +1,39 @@ +GraphQL->post($graphQL); + + $this->assertNotEmpty($return['data']['shop']); + } + + +} diff --git a/tests/MetafieldTest.php b/tests/MetafieldTest.php index 5038381..b58877a 100644 --- a/tests/MetafieldTest.php +++ b/tests/MetafieldTest.php @@ -17,7 +17,7 @@ class MetafieldTest extends TestSimpleResource "namespace" => "inventory", "key" => "warehouse", "value" => 25, - "value_type" => "integer", + "type" => "integer", ); /** @@ -25,6 +25,6 @@ class MetafieldTest extends TestSimpleResource */ public $putArray = array( "value" => "something new", - "value_type" => "string", + "type" => "string", ); } \ No newline at end of file diff --git a/tests/TestResource.php b/tests/TestResource.php index 1c9a268..3fd3b2d 100644 --- a/tests/TestResource.php +++ b/tests/TestResource.php @@ -23,6 +23,7 @@ public static function setUpBeforeClass() 'ShopUrl' => getenv('SHOPIFY_SHOP_URL'), //Your shop URL 'ApiKey' => getenv('SHOPIFY_API_KEY'), //Your Private API Key 'Password' => getenv('SHOPIFY_API_PASSWORD'), //Your Private API Password + 'AccessToken' => getenv('SHOPIFY_API_PASSWORD'), //Your Access Token(Private API Password) ); self::$shopify = ShopifySDK::config($config);