woocommerce / woocommerce-blocks

(Deprecated) This plugin has been merged into woocommerce/woocommerce
https://wordpress.org/plugins/woo-gutenberg-products-block/
GNU General Public License v3.0
406 stars 218 forks source link

Add an endpoint for getting pay-for-order orders #8922

Closed hsingyuc closed 1 year ago

hsingyuc commented 1 year ago

Currently, WooCommerce Blocks does not support pay-for-order checkouts. The first step in this process is to add an endpoint to the store API to get pay-for-order orders.

For context, in some extensions WooPay cannot be used because they use the pay-for-order flow.

When checking out in WooPay, we grab cart item and use CheckoutOrderSummaryCartItemsBlock. To make WooPay and pay-for-order flow compatible, we are going to create an endpoint for blocks to get the pay-for-order order for the pay-for-order block.

Describe the solution you'd like

Add an endpoint for pay-for-order orders so we can use that to hydrate an order block in the checkout.

nielslange commented 1 year ago

Hello @hsingyuc đź‘‹

Thanks for reaching out. I'll move this issue to our backlog.

hsingyuc commented 1 year ago

Hi @nielslange I'm already working on this since we need it for the pay-for-order flow. I'll have a PR soon.

ralucaStan commented 1 year ago

Hey, @hsingyuc thank you for adding this functionality đź‘Ź. Make sure to select the Rubik team as a reviewer when the PR is ready; we are here for you for any questions you might have.

I've added this to the Other teams column on our board.

hsingyuc commented 1 year ago

Noted. Thank you, @ralucaStan!

senadir commented 1 year ago

Hey @hsingyuc ! Thank you for working on this :D

I think it would be worth sharing a overview of the flow before diving into the code.

Processing orders here requires loading an order from an URL and we need to make sure this is done in a secure way.

It might also be worth exploring why we need a pay-for-order endpoint and why can't the checkout endpoint do that, constrainst that exist in classic checkout might not apply here, pay-for-order exists to handle existing order, something that classic Checkout can't handle but Store API and Checkout block is fully capable of doing, so we might just need to tweak some things to make it work.

hsingyuc commented 1 year ago

Hi @senadir! Thank you for reminding me about this should be done in a secure way. I'm planning on using the same way as checking the order_id and the order_key hash as well as the current user.

I think it would be worth sharing a overview of the flow before diving into the code.

We have a RFC thread here : RFC: WooPay Missing “Pay for order” flow

Here is the overview of the flow:

  1. The function should_show_platform_checkout_button turns off the button on pay-for-order pages and will need to be updated.
  2. This hook woocommerce_pay_order_before_submit can be used to place the express button, or we can add a new hook like woocommerce_pay_order_before_woocommerce_pay_form
  3. Pass the order_id , pay_for_order=true , and key from the merchant site to the WooPay checkout page.
  4. When checking out in WooPay, we’ll need to process the payment for the order instead of using the cart items.
  5. Create a CheckoutOrderPayForOrderBlock , this block would hydrate the order by itself as checkout blocks usually do.

It might also be worth exploring why we need a pay-for-order endpoint and why can't the checkout endpoint do that, constrainst that exist in classic checkout might not apply here, pay-for-order exists to handle existing order, something that classic Checkout can't handle but Store API and Checkout block is fully capable of doing, so we might just need to tweak some things to make it work.

The endpoint we are creating here is for "getting" pay-for-order orders not "paying' and we will use the checkout endpoint to actually pay for the orders.

mikejolley commented 1 year ago

I'm going to try to get all my thoughts down on there looking at this from the context of Store API at large and the block based checkout, because while this seems like a small addition, once accepted into the Store API codebase we'll have to maintain it going forward. Also wanted to point out some potential issues with the current flow in core that will impact Payment Gateways.

I will refer to pay-for-order-orders as just unpaid orders going forward, since the flow exists in WooCommerce core for any order that has pending status as well—you can test this by editing an order and changing the status to pending payment manually.

Also just for context, this is the current documented list of requirements for Store API endpoints:

- This is an unauthenticated API. It does not require API keys or authentication tokens for access.
- All API responses return JSON-formatted data.
- Data returned from the API is reflective of the current user (customer). Customer sessions in WooCommerce are cookie-- based.
- Store API cannot be used to look up other customers and orders by ID; only data belonging to the current user.
- Likewise, Store API cannot be used to write store data e.g. settings. For more extensive access, use the authenticated [WC - REST API.](https://woocommerce.github.io/woocommerce-rest-api-docs/#introduction)
- Endpoints that do allow writes, for example, updating the current customer address, require a [nonce-token](https://developer.wordpress.org/plugins/security/nonces/).
- Store API is render-target agnostic and should not make assumptions about where content will be displayed. For example, returning HTML would be discouraged unless the data type itself is HTML.

Security/Privacy concerns

Referring to our requirements, this point:

Store API cannot be used to look up other customers and orders by ID; only data belonging to the current user.

Current user being the key here. Any lookup of order data needs to check that the current user has access to that order. Extensions that use the pay for order flow (for example bookings) force registration, so this would be a non issue for them.

What about WooPay? Does it need to support lookup of guest orders?

Let's try to keep things locked down as much as possible.

Order routes and reusability

Following on from the above point, we don't want to create a solution that only WooPay can consume. Other use-cases for order data may exist in the future, for example, block based account pages, or the order received page.

With that in mind, I could see an /orders endpoint being useful (to list the logged in current user's orders only), with an /order/ID route for specific order details.

This also means we would want to grab orders with any status, not just unpaid orders.

Editable state of orders

In the current pay for order flow, everything within the order is fixed. This includes taxes, costs, shipping, and the customer address.

My main question here is, in the context of checkout, what happens if the shopper wants to pay with a billing method which has a different billing address?

If we're going to need to introduce editable addresses for unpaid orders, that opens us up to changes in totals/shipping/taxes as well, in which case it would probably be best to find a solution that converts the unpaid order into a cart so the regular checkout flow can be used, replacing the unpaid order once placed. cc @senadir @wavvves

Schema requirements

Regarding this comment, if we introduce the order endpoint we should ensure the schema is close to the schema of cart items for consistency.

One such example that I can see immediately from the PR is how totals/costs are represented. This should match the cart where we return totals in pence/cents, as well as $this->get_store_currency_properties().

Can we please review the existing schema and apply the same patterns to order data?

Anyone who reviews this work should also take note of this point and ensure the schemas are consistent @woocommerce/rubik

What about blocks?

My final note is more aimed at @woocommerce/rubik — if we support this endpoint we need to look into how this payment flow works for our block based checkout. It should be supported natively in blocks as well, not just WooPay. We'll need it before we become the default core experience if we want to support the account pages in core fully. cc @ralucaStan

—

That's all for now. I am going AFK for a week and wanted to get my thoughts written down before reviewing https://github.com/woocommerce/woocommerce-blocks/pull/8960.

ralucaStan commented 1 year ago

Adding @pmcpinto and @nerrad to this conversation for awarness

hsingyuc commented 1 year ago

Adding a discussion thread here. p1680686053947699-slack-C8X6Q7XQU