diff --git a/README.md b/README.md index 65bdfe3a..f483b89a 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ Running tests: `composer run-script test` #### Orders (V3) -- ☐ Transactions -- ☐ Order Refunds +- ☑️ Transactions +- ☑️ Order Refunds #### Payment Methods diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 00a2ab67..f55391ba 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,13 +1,8 @@ #### New Features -Add support for the [Themes API](https://developer.bigcommerce.com/api-reference/store-management/themes) +Add support for the [Orders v3 API](https://developer.bigcommerce.com/api-reference/store-management/order-transactions) Includes -- Themes -- Theme Actions -- Theme Jobs - -#### Code Improvements - -- Refactor PaginatedResponse to be simpler to implement \ No newline at end of file +- Transactions +- Order refunds diff --git a/src/BigCommerce/Client.php b/src/BigCommerce/Client.php index 2dc45df4..50d14eeb 100644 --- a/src/BigCommerce/Client.php +++ b/src/BigCommerce/Client.php @@ -2,6 +2,7 @@ namespace BigCommerce\ApiV3; +use BigCommerce\ApiV3\Orders\OrdersApi; use BigCommerce\ApiV3\Customers\CustomersApi; use BigCommerce\ApiV3\PriceLists\PriceListsApi; use BigCommerce\ApiV3\Themes\ThemesApi; @@ -122,4 +123,9 @@ public function theme(string $uuid): ThemesApi $api->setUuid($uuid); return $api; } + + public function order(int $orderId): OrdersApi + { + return new OrdersApi($this, $orderId); + } } diff --git a/src/BigCommerce/Orders/OrdersApi.php b/src/BigCommerce/Orders/OrdersApi.php new file mode 100644 index 00000000..7a201ffa --- /dev/null +++ b/src/BigCommerce/Orders/OrdersApi.php @@ -0,0 +1,31 @@ +getClient()->getRestClient()->get( + sprintf(self::TRANSACTIONS_ENDPOINT, $this->getResourceId()) + ); + + return new TransactionsResponse($response); + } + + public function refunds(): RefundsApi + { + return new RefundsApi($this->getClient(), null, $this->getResourceId()); + } + + public function refund(): RefundsApi + { + return $this->refunds(); + } +} diff --git a/src/BigCommerce/Orders/RefundsApi.php b/src/BigCommerce/Orders/RefundsApi.php new file mode 100644 index 00000000..aaa5cf08 --- /dev/null +++ b/src/BigCommerce/Orders/RefundsApi.php @@ -0,0 +1,56 @@ +getClient()->getRestClient()->post( + sprintf(self::REFUND_ENDPOINT, $this->getParentResourceId()), + [ + RequestOptions::JSON => [] + ] + ); + + return new RefundResponse($response); + } + + public function createQuote(): RefundQuoteResponse + { + $response = $this->getClient()->getRestClient()->post( + sprintf(self::REFUND_QUOTE_ENDPOINT, $this->getParentResourceId()), + [ + RequestOptions::JSON => [] + ] + ); + + return new RefundQuoteResponse($response); + } + + public function getAll(): RefundsResponse + { + $response = $this->getClient()->getRestClient()->get( + sprintf(self::REFUND_ENDPOINT, $this->getParentResourceId()) + ); + + return new RefundsResponse($response); + } +} diff --git a/src/BigCommerce/ResourceModels/Order/OrderRefundItem.php b/src/BigCommerce/ResourceModels/Order/OrderRefundItem.php new file mode 100644 index 00000000..72bb2e1a --- /dev/null +++ b/src/BigCommerce/ResourceModels/Order/OrderRefundItem.php @@ -0,0 +1,19 @@ +items = array_map(fn($i) => new OrderRefundItem($i), $optionObject->items); + unset($optionObject->items); + parent::__construct($optionObject); + } +} diff --git a/src/BigCommerce/ResourceModels/Order/RefundQuote.php b/src/BigCommerce/ResourceModels/Order/RefundQuote.php new file mode 100644 index 00000000..1ddf8157 --- /dev/null +++ b/src/BigCommerce/ResourceModels/Order/RefundQuote.php @@ -0,0 +1,16 @@ +quote; + } + + protected function addData(stdClass $rawData): void + { + $this->quote = new RefundQuote($rawData); + } +} diff --git a/src/BigCommerce/ResponseModels/Order/RefundResponse.php b/src/BigCommerce/ResponseModels/Order/RefundResponse.php new file mode 100644 index 00000000..bd7a33dd --- /dev/null +++ b/src/BigCommerce/ResponseModels/Order/RefundResponse.php @@ -0,0 +1,22 @@ +refund; + } + + protected function addData(stdClass $rawData): void + { + $this->refund = new Refund($rawData); + } +} diff --git a/src/BigCommerce/ResponseModels/Order/RefundsResponse.php b/src/BigCommerce/ResponseModels/Order/RefundsResponse.php new file mode 100644 index 00000000..74aae1f1 --- /dev/null +++ b/src/BigCommerce/ResponseModels/Order/RefundsResponse.php @@ -0,0 +1,21 @@ +getData(); + } + protected function resourceClass(): string + { + return Refund::class; + } +} diff --git a/src/BigCommerce/ResponseModels/Order/TransactionsResponse.php b/src/BigCommerce/ResponseModels/Order/TransactionsResponse.php new file mode 100644 index 00000000..911092f1 --- /dev/null +++ b/src/BigCommerce/ResponseModels/Order/TransactionsResponse.php @@ -0,0 +1,25 @@ +getData(); + } + + protected function resourceClass(): string + { + return Transaction::class; + } +} diff --git a/tests/BigCommerce/Orders/OrdersApiTest.php b/tests/BigCommerce/Orders/OrdersApiTest.php new file mode 100644 index 00000000..58902a24 --- /dev/null +++ b/tests/BigCommerce/Orders/OrdersApiTest.php @@ -0,0 +1,28 @@ +getApi()->order($orderId); + $this->assertInstanceOf(OrdersApi::class, $ordersApi); + $this->assertEquals($orderId, $ordersApi->getResourceId()); + } + + public function testCanGetOrderTransactions() + { + $this->setReturnData('orders__transactions__get.json'); + $orderId = 121; + + $transactionsResponse = $this->getApi()->order($orderId)->transactions(); + $this->assertCount(1, $transactionsResponse->getTransactions()); + $this->assertEquals(1, $transactionsResponse->getPagination()->total); + } +} diff --git a/tests/BigCommerce/Orders/RefundsApiTest.php b/tests/BigCommerce/Orders/RefundsApiTest.php new file mode 100644 index 00000000..8a4ddbda --- /dev/null +++ b/tests/BigCommerce/Orders/RefundsApiTest.php @@ -0,0 +1,61 @@ +getApi()->order($orderId)->refunds(); + $this->assertInstanceOf(RefundsApi::class, $refundsApi); + $this->assertEquals($orderId, $refundsApi->getParentResourceId()); + } + + public function testCanCreateQuoteRefund() + { + $this->setReturnData('orders__order_refunds__create_quote.json', 201); + $orderId = 1; + + $response = $this->getApi()->order($orderId)->refunds()->createQuote(); + $this->assertEquals($orderId, $response->getQuote()->order_id); + } + + public function testCanGetAll() + { + $this->setReturnData('orders__order_refunds__get_all.json', 201); + $orderId = 1; + + $response = $this->getApi()->order($orderId)->refunds()->getAll(); + $this->assertCount(1, $response->refunds()); + $this->assertCount(1, $response->refunds()[0]->items); + $this->assertInstanceOf(OrderRefundItem::class, $response->refunds()[0]->items[0]); + } + + public function testCanCreateRefund() + { + $this->setReturnData('orders__order_refunds__create.json', 201); + $orderId = 1; + $reason = 'test reason'; + + $item = new OrderRefundItem(); + $item->item_id = 1; + $item->item_type = OrderRefundItem::ITEM_TYPE__HANDLING; + $item->quantity = 1; + $item->reason = ""; + + $response = $this->getApi()->order($orderId)->refunds()->create([$item], $reason); + $refund = $response->getRefund(); + $responseItem = $refund->items[0]; + + $this->assertEquals($reason, $refund->reason); + $this->assertInstanceOf(OrderRefundItem::class, $responseItem); + $this->assertEquals($item->item_id, $responseItem->item_id); + $this->assertEquals($item->item_type, $responseItem->item_type); + } +} diff --git a/tests/BigCommerce/responses/orders__order_refunds__create.json b/tests/BigCommerce/responses/orders__order_refunds__create.json new file mode 100644 index 00000000..724e56e2 --- /dev/null +++ b/tests/BigCommerce/responses/orders__order_refunds__create.json @@ -0,0 +1,31 @@ +{ + "data": { + "id": 1, + "order_id": 1, + "user_id": 1, + "created": "", + "reason": "test reason", + "total_amount": 1.99, + "total_tax": 1, + "items": [ + { + "item_type": "HANDLING", + "item_id": 1, + "reason": "", + "quantity": 1, + "requested_amount": 0.05 + } + ], + "payments": [ + { + "id": 1, + "provider_id": "checkout_paypalexpress", + "amount": 1.99, + "offline": true, + "is_declined": true, + "declined_message": "" + } + ] + }, + "meta": {} +} diff --git a/tests/BigCommerce/responses/orders__order_refunds__create_quote.json b/tests/BigCommerce/responses/orders__order_refunds__create_quote.json new file mode 100644 index 00000000..30b135be --- /dev/null +++ b/tests/BigCommerce/responses/orders__order_refunds__create_quote.json @@ -0,0 +1,14 @@ +{ + "data": { + "order_id": 1, + "total_refund_amount": 1.99, + "total_refund_tax_amount": 1.95, + "rounding": 1, + "adjustment": 1.99, + "tax_inclusive": true, + "refund_methods": [ + "" + ] + }, + "meta": {} +} \ No newline at end of file diff --git a/tests/BigCommerce/responses/orders__order_refunds__get_all.json b/tests/BigCommerce/responses/orders__order_refunds__get_all.json new file mode 100644 index 00000000..95f20b47 --- /dev/null +++ b/tests/BigCommerce/responses/orders__order_refunds__get_all.json @@ -0,0 +1,42 @@ +{ + "data": [{ + "id": 1, + "order_id": 1, + "user_id": 1, + "created": "", + "reason": "test reason", + "total_amount": 1.99, + "total_tax": 1, + "items": [ + { + "item_type": "HANDLING", + "item_id": 1, + "reason": "", + "quantity": 1, + "requested_amount": 0.05 + } + ], + "payments": [ + { + "id": 1, + "provider_id": "checkout_paypalexpress", + "amount": 1.99, + "offline": true, + "is_declined": true, + "declined_message": "" + } + ] + }], + "meta": { + "pagination": { + "total": 1, + "count": 1, + "per_page": 50, + "current_page": 1, + "total_pages": 1, + "links": { + "current": "?page=1&limit=50" + } + } + } +} diff --git a/tests/BigCommerce/responses/orders__transactions__get.json b/tests/BigCommerce/responses/orders__transactions__get.json new file mode 100644 index 00000000..e0e449fa --- /dev/null +++ b/tests/BigCommerce/responses/orders__transactions__get.json @@ -0,0 +1,48 @@ +{ + "data": [ + { + "id": 85926313, + "order_id": "121", + "event": "purchase", + "method": "nonce", + "amount": 1, + "currency": "USD", + "gateway": "squarev2", + "gateway_transaction_id": "pN5Kd7R9ilEI2ygBawCy7tMF|qwnAFAxRZ7tYRtIpZULg1yMF", + "status": "ok", + "test": false, + "fraud_review": false, + "reference_transaction_id": null, + "date_created": "2018-05-08T15:06:12+00:00", + "avs_result": { + "code": "", + "message": "", + "street_match": "", + "postal_match": "" + }, + "cvv_result": { + "code": "", + "message": "" + }, + "credit_card": {}, + "gift_certificate": {}, + "store_credit": {}, + "offline": {}, + "custom": {}, + "payment_instrument_token": "qwsdfghyuio", + "payment_method_id": "squarev2.card" + } + ], + "meta": { + "pagination": { + "total": 1, + "count": 1, + "per_page": 50, + "current_page": 1, + "total_pages": 1, + "links": { + "current": "?page=1&limit=50" + } + } + } +} \ No newline at end of file