Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Validate available payment methods before attempting payment or updat…
Browse files Browse the repository at this point in the history
…ing order statuses (#5440)

* Make payment method required

* removed unused imports

* Remove PUT method

* Validate available method when updating order

* Enable bacs for tests
  • Loading branch information
mikejolley authored Jan 4, 2022
1 parent a1e2af7 commit b8de0ab
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 178 deletions.
71 changes: 20 additions & 51 deletions src/StoreApi/Routes/Checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\Payments\PaymentContext;
use Automattic\WooCommerce\Blocks\Payments\PaymentResult;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\AbstractSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartException;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\OrderController;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStockException;
/**
Expand Down Expand Up @@ -87,12 +83,6 @@ public function get_args() {
$this->schema->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE )
),
],
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( $this, 'get_response' ),
'permission_callback' => '__return_true',
'args' => $this->schema->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
],
'schema' => [ $this->schema, 'get_public_item_schema' ],
'allow_batch' => [ 'v1' => true ],
];
Expand Down Expand Up @@ -141,30 +131,7 @@ protected function get_route_response( \WP_REST_Request $request ) {
}

/**
* Update the current order.
*
* @internal Customer data is updated first so OrderController::update_addresses_from_cart uses up to date data.
*
* @throws RouteException On error.
* @param \WP_REST_Request $request Request object.
* @return \WP_REST_Response
*/
protected function get_route_update_response( \WP_REST_Request $request ) {
$this->update_customer_from_request( $request );
$this->create_or_update_draft_order();
$this->update_order_from_request( $request );

return $this->prepare_item_for_response(
(object) [
'order' => $this->order,
'payment_result' => new PaymentResult(),
],
$request
);
}

/**
* Update and process an order.
* Process an order.
*
* 1. Obtain Draft Order
* 2. Process Request
Expand Down Expand Up @@ -448,7 +415,7 @@ private function update_customer_from_request( \WP_REST_Request $request ) {
*/
private function update_order_from_request( \WP_REST_Request $request ) {
$this->order->set_customer_note( $request['customer_note'] ?? '' );
$this->order->set_payment_method( $request['payment_method'] ?? '' );
$this->order->set_payment_method( $this->get_request_payment_method_id( $request ) );

/**
* Fires when the Checkout Block/Store API updates an order's from the API request data.
Expand Down Expand Up @@ -551,17 +518,7 @@ private function process_payment( \WP_REST_Request $request, PaymentResult $paym
* @return string
*/
private function get_request_payment_method_id( \WP_REST_Request $request ) {
$payment_method_id = wc_clean( wp_unslash( $request['payment_method'] ?? '' ) );

if ( empty( $payment_method_id ) ) {
throw new RouteException(
'woocommerce_rest_checkout_missing_payment_method',
__( 'No payment method provided.', 'woo-gutenberg-products-block' ),
400
);
}

return $payment_method_id;
return $this->get_request_payment_method( $request )->id;
}

/**
Expand All @@ -572,18 +529,30 @@ private function get_request_payment_method_id( \WP_REST_Request $request ) {
* @return \WC_Payment_Gateway
*/
private function get_request_payment_method( \WP_REST_Request $request ) {
$payment_method_id = $this->get_request_payment_method_id( $request );
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$request_payment_method = wc_clean( wp_unslash( $request['payment_method'] ?? '' ) );

if ( ! isset( $available_gateways[ $payment_method_id ] ) ) {
if ( empty( $request_payment_method ) ) {
throw new RouteException(
'woocommerce_rest_checkout_missing_payment_method',
__( 'No payment method provided.', 'woo-gutenberg-products-block' ),
400
);
}

if ( ! isset( $available_gateways[ $request_payment_method ] ) ) {
throw new RouteException(
'woocommerce_rest_checkout_payment_method_disabled',
__( 'This payment gateway is not available.', 'woo-gutenberg-products-block' ),
sprintf(
// Translators: %s Payment method ID.
__( 'The %s payment gateway is not available.', 'woo-gutenberg-products-block' ),
esc_html( $request_payment_method )
),
400
);
}

return $available_gateways[ $payment_method_id ];
return $available_gateways[ $request_payment_method ];
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/StoreApi/Schemas/CheckoutSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public function get_properties() {
'type' => 'string',
'context' => [ 'view', 'edit' ],
'enum' => wc()->payment_gateways->get_payment_gateway_ids(),
'required' => true,
],
'create_account' => [
'description' => __( 'Whether to create a new user account as part of order processing.', 'woo-gutenberg-products-block' ),
Expand Down
173 changes: 75 additions & 98 deletions src/StoreApi/docs/checkout.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ The checkout API facilitates the creation of orders (from the current cart) and
All checkout endpoints require [Nonce Tokens](nonce-tokens.md).

- [Get Checkout Data](#get-checkout-data)
- [Update Checkout Data](#update-checkout-data)
- [Process Order and Payment](#process-order-and-payment)

## Get Checkout Data
Expand All @@ -28,70 +27,48 @@ curl --header "X-WC-Store-API-Nonce: 12345" --request GET https://example-store.

```json
{
"order_id": 146,
"status": "checkout-draft",
"order_key": "wc_order_VPffqyvgWVqWL",
"customer_note": "",
"customer_id": 1,
"billing_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US",
"email": "[email protected]",
"phone": "555-2368"
},
"shipping_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US"
},
"payment_method": "",
"payment_result": {
"payment_status": "",
"payment_details": [],
"redirect_url": ""
}
"order_id": 146,
"status": "checkout-draft",
"order_key": "wc_order_VPffqyvgWVqWL",
"customer_note": "",
"customer_id": 1,
"billing_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US",
"email": "[email protected]",
"phone": "555-2368"
},
"shipping_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US"
},
"payment_method": "",
"payment_result": {
"payment_status": "",
"payment_details": [],
"redirect_url": ""
}
}
```

## Update Checkout Data

Allows the client to update checkout data, and returns an updated response.

This endpoint will return an error unless a valid [Nonce Token](nonce-tokens.md) is provided.

```http
PUT /wc/store/checkout
```

| Attribute | Type | Required | Description |
| :----------------- | :------ | :------: | :-------------------------------------------------------------- |
| `billing_address` | array | No | Array of updated billing address data for the customer. |
| `shipping_address` | integer | No | Array of updated shipping address data for the customer. |
| `customer_note` | string | No | Note added to the order by the customer during checkout. |
| `payment_method` | string | No | The ID of the payment method being used to process the payment. |

```http
curl --header "X-WC-Store-API-Nonce: 12345" --request PUT https://example-store.com/wp-json/wc/store/checkout?payment_method=paypal
```

Returns either updated checkout data (See [Get Checkout Data](#get-checkout-data)), or an error response.

## Process Order and Payment

Posts final checkout data, including data from payment methods, and attempts payment.
Accepts the final customer addresses and chosen payment method, and any additional payment data, then attempts payment and
returns the result.

This endpoint will return an error unless a valid [Nonce Token](nonce-tokens.md) is provided.

Expand All @@ -101,10 +78,10 @@ POST /wc/store/checkout

| Attribute | Type | Required | Description |
| :----------------- | :------ | :------: | :------------------------------------------------------------------ |
| `billing_address` | array | No | Array of updated billing address data for the customer. |
| `shipping_address` | integer | No | Array of updated shipping address data for the customer. |
| `billing_address` | array | Yes | Array of updated billing address data for the customer. |
| `shipping_address` | integer | Yes | Array of updated shipping address data for the customer. |
| `customer_note` | string | No | Note added to the order by the customer during checkout. |
| `payment_method` | string | No | The ID of the payment method being used to process the payment. |
| `payment_method` | string | Yes | The ID of the payment method being used to process the payment. |
| `payment_data` | array | No | Data to pass through to the payment method when processing payment. |

```http
Expand All @@ -115,40 +92,40 @@ curl --header "X-WC-Store-API-Nonce: 12345" --request POST https://example-store

```json
{
"order_id": 146,
"status": "on-hold",
"order_key": "wc_order_VPffqyvgWVqWL",
"customer_note": "",
"customer_id": 1,
"billing_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US",
"email": "[email protected]",
"phone": "555-2368"
},
"shipping_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US"
},
"payment_method": "cheque",
"payment_result": {
"payment_status": "success",
"payment_details": [],
"redirect_url": "https:\/\/local.wordpress.test\/block-checkout\/order-received\/146\/?key=wc_order_VPffqyvgWVqWL"
}
"order_id": 146,
"status": "on-hold",
"order_key": "wc_order_VPffqyvgWVqWL",
"customer_note": "",
"customer_id": 1,
"billing_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US",
"email": "[email protected]",
"phone": "555-2368"
},
"shipping_address": {
"first_name": "Peter",
"last_name": "Venkman",
"company": "",
"address_1": "550 Central Park West",
"address_2": "Corner Penthouse Spook Central",
"city": "New York",
"state": "NY",
"postcode": "10023",
"country": "US"
},
"payment_method": "cheque",
"payment_result": {
"payment_status": "success",
"payment_details": [],
"redirect_url": "https://local.wordpress.test/block-checkout/order-received/146/?key=wc_order_VPffqyvgWVqWL"
}
}
```
Loading

0 comments on commit b8de0ab

Please sign in to comment.