WooCommerce Proof of Concept

We are going to investigate how a WooCommerce package for Frontity could look like. This proof of concept is going to be built on top of a new client-side API the WooCommerce has started working on for their Gutenberg blocks, called Store API.

We did an intro meeting to talk about the first goals:

Disclaimer: This doesn’t mean that we are going to start working on the official WooCommerce package after this proof of concept yet. The WooCommerce team is not committed to releasing a v1 version of this Store API for public use yet. So please take this as it is: a proof of concept to start experimenting with Frontity and this new client-side API.

1 Like

We have done a follow-up meeting today to review @david’s progress:

Things that are already working:

  • The shop page (archive of products).
  • The products pages.
  • The cart page.
  • The add-to-cart buttons.

Things missing for this proof of concept:

  • Some cart actions (remove items, add coupons…).
  • The checkout page.

These are the changes that it seems we would need from the Store API:

  • Adding the slug to the product schema.
  • Adding the slug to the query filters: /products?slug=my-product
  • Changing permalink to link to be consistent with the standard REST API.
  • Either exposing the product_categories and product_tags in the standard REST API or in the Store API.
  • An endpoint for the thanks page that can be queried using the order_key.

@david is going to publish his work in a repo with instructions on how to install it for anyone that wants to check it out.

1 Like

Here’s a link to the WooCommerce Proof of Concept repository:

4 Likes

A couple of things more I’ve found while working on this proof of concept:

Payment methods

Ideally, the available payment methods would be fetched from the /wc/store/ API. Right now it is not possible, so a WooCommerce theme would have to obtain that information from somewhere else (the frontity.settings.js file, for example).

Product categories and tags

There are endpoints to fetch those entities:

  • /wc/store/products/categories/
  • /wc/store/products/tags/

Currently, they don’t allow to filter the response by slug.

To get products from a specific category you should use the category attribute, which only allows you to filter products by a category ID (with tags is similar):

  • /wc/store/products?category=21

Therefore, is really hard – even impossible – to write a @frontity/wp-json handler that, given a link like /product-category/accessories/, fetches the products belonging to the “Accessories” category from the REST API.

The same happens with products. In that case, a workaround was implemented using the /wp/v2/product endpoint to get the ID of the product with the given slug (see /packages/woocommerce/src/handlers/product.ts#L19-L33). That cannot be done for product categories or tags because they are not regular categories/tags and so they don’t appear in any /wp/v2/ endpoint.

We would need to filter these entities by slug from the /wc/store/ API directly.

That is actually expected. I am sorry I forgot to mention it before. This is what the WooCommerce team said about handling payment with the Store API:

The Store API should start providing some access to payment methods, although exactly how we’ll handle something like e.g. offsite PayPal payment, I’m not sure yet.

Nice to see this here. Glad to see the Woocommerce team finally working on the store API.

I have built headless websites with WooCommerce before and the current public API makes things impossible without custom WP/PHP code.

5 Likes

I have set up a site so people can test this out in Decoupled mode without the need to start and configure a WordPress project locally.

The URL is https://woocommerce.frontity.org/. Just add it to the frontity.settings.js file:

{
  name: "@frontity/wp-source",
  state: {
    source: {
      url: "https://woocommerce.frontity.org/",
    },
  },
};

Feel free to use the cart and the checkout as many times as you want because the WordPress is restored each 24 hours.

To be able to use this in Decoupled mode, I had to do a small change in the WooCommerce plugin to add a filter for secure and samesite in their setcookie function:

$cookie_options = array (
  'expires' => $expire,
  'path' => COOKIEPATH ? COOKIEPATH : '/',
  'domain' => COOKIE_DOMAIN,
  'secure' => apply_filters( 'woocommerce_cookie_secure', $httponly, $name, $value, $expire, $secure ),
  'httponly' => apply_filters( 'woocommerce_cookie_httponly', $httponly, $name, $value, $expire, $secure ),
  'samesite' => apply_filters( 'woocommerce_cookie_samesite', $httponly, $name, $value, $expire, $secure ),
  );
setcookie( $name, $value, $cookie_options );

The change is in this line: https://github.com/woocommerce/woocommerce/blob/master/includes/wc-core-functions.php#L1052. I will do a PR later.

Then, I added this snippet to the site:

add_action( 'rest_api_init', function () {
  add_filter( 'woocommerce_store_api_disable_nonce_check', '__return_true' );
  add_filter( 'woocommerce_cookie_secure', '__return_true' );
  add_filter( 'woocommerce_cookie_samesite', function() { return "None"; } );
} );

This is the last meeting where @david shared the latest progress with us :slight_smile:

1 Like

A note about fetch() and cookies :cookie:

I first used the Frontity Embedded Mode - [Proof of Concept] plugin to avoid any CORS problem regarding cookies. In fact, when I started doing tests with the WooCommerce Store API I saw that cookies were not sent back to the WordPress server, and I immediately assumed that it was not possible to make the request from a different domain.

Well, I was not completely right.

In order to receive cookies and send them back to the server, you would need to specify credentials: "include" in the init option of any fetch call (1).

Also, the server should respond with these headers for cross-origin requests (2):

  • Access-Control-Allow-Origin, set to the same value as the Origin request header (the * wildcard cannot be returned here).
  • Access-Control-Allow-Credentials, set to true.

The credentials option was used and the Access-Control headers added, so everything fine here. However, the Set-Cookie header with the wp_woocommerce_session cookie, added by the WooCommerce plugin here, was missing the SameSite attribute, which should be set to None if we want to do cross-site requests including that cookie. That attribute also requires the Secure attribute.

That was the reason why we modified the wc_setcookie() function, as @luisherranz mentioned above. Doing those changes, it is possible to do requests to the WooCommerce Store API from a different domain, and maintain the customer session.

Of course, using the Frontity Embedded Mode - [Proof of Concept] plugin it works “out of the box”, as everything happens in the same domain.

2 Likes

I’ve updated the README.md file in the repository with the changes we did yesterday.

Check it out:

1 Like

Would it be possible to use the Embedded mode in woocommerce.frontity.org and deploy the GitHub repo to Vercel? Or this would affect somehow the people using woocommerce.frontity.org to test it in Decoupled?

I ask because it could be nice that users can have a look at the status of the proof of concept directly in woocommerce.frontity.org.

It should work the same, as Frontity in decoupled mode only interacts with the REST API.

Just take in mind that the WordPress instance Luis deployed is restored every 24 hours so, if you want to add the embedded mode, you would have to talk first with him!

I have set it up in woocommerce.frontity.org as well. It should be working fine now.

I have also set up automatic deploys to Vercel and Changesets in that repo so we can have a changelog of the changes.

I think the proof of concept is good enough at this point to get some external feedback about it.

To be honest, we learned a lot and the Store API shows a lot of promise, so I think we can consider this research a success. Also, I think this opens an opportunity to collaborate more closely with the WooCommerce team :slightly_smiling_face:


Apart from that, these are my impressions.

Store API

These are the fixes that would make Frontity integration with the Store API easier.

Adding the slug field to the products

The response of the Store API doesn’t include the slug yet, which is something we use in Frontity.

For example: https://woocommerce.frontity.org/wp-json/wc/store/products/34

Filtering products by slug

The Store API has a lot of filters, but it doesn’t include a slug one yet. It would be great to have one as Frontity relies a lot on that to do the match between entities and frontend URLs.

Current filters: https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/trunk/src/StoreApi/docs/products.md#list-products

Renaming permalink to link

The WordPress REST API uses link in all their endpoints, but the Store API is using permalink.

For consistency with the regular REST API, and because Frontity does a transformation of those link fields to remove the domain so they can be used in decoupled mode, it would be great it the Store API would use link instead of permalink.

Remaining work in the package

This is non-exhaustive list of things we still need to do.

Prepare the package for optimistic updates

Right now we are copying everything that we get from the Store API into the state, but we should improve in this regard.

We should prepare the package (state and actions) for optimistic updates. That means that:

  • The information in the state should be only once. If some information can be derived from other parts of the state, it should be written as derived state. This is the “source of truth” principle.
  • Actions should update the state first, then send the request, then revert the state if the request failed.

For example, right now the total price is here:

const total = state.woocommerce.cart.totals.total_price;

But it can be derived from the sum of all the item totals. Something like this:

const total = ({ state }) =>
  state.woocommerce.cart.items.reduce(
    (total, item) => totel + item.totals.line_total,
    0
  );

And actually, the total of each item should also be derived from the price of that item, multiplied by the quantity, and so on…

The goal of having a single source of truth is that it simplifies state mutations a lot: if an action modifies a single part of the state, for example the quantity of a product, the rest of the fields will reflect that change immediatly in the UI.

It also means that the application will be less prone to bugs because the derived state is managed by the state manager and nothing can get out of sync.

Handle errors

We should add a way to handle errors, both in the interface and in the state.

Maybe for the interface this package could show some of the errors using toasts. As toasts are displayed on top of the theme, that would make things easier for theme developers. Of course that would need to be optional.

Support variable products

We should add support for variable products in the theme, to make sure there is no problem with that.

Create the commerce namespace

We should work on a common commerce package to allow people to use other implementations, like for example: https://github.com/wp-graphql/wp-graphql-woocommerce.

Make it work with nonces

We should add a way to make this work with nonces. Right now they are disabled.

We could use ajax. Actually, there is something in the WordPress Core already for that, I don’t know if we could use it: https://github.com/WordPress/wordpress-develop/blob/master/src/wp-admin/includes/ajax-actions.php#L5323-L5330

Handlers for the product taxonomies

We should add handlers for the product taxonomies: product categories and tags.

Handlers for the filtered products

We should add handlers for all the queries that WooCommerce supports. Thinkgs like /shop/?orderby=price and so on. That way, theme creators can add filters that simple use actions.router.set to render the new list.

Support all themes

For this proof of concept we made a “WooCommerce theme”, but this package should take care of adding the shop, product pages, cart, checkout and thank you pages with a default UI for themes that don’t have direct support for WooCommerce in the same way that you can use WooCommerce with a PHP theme that doesn’t have WooCommerce support.

Support WooCommerce Blocks

We should add support for the new WooCommerce Blocks. Things like capturing the “Add to cart” buttons with a processor and linking those buttons to our actions.woocommerce.addToCart() actions.

For now it seems like these blocks are not writing HTML to the database and they rely on React for client-side rendering, but I guess that will change at some point. We should ask the WooCommerce team about their plans :slightly_smiling_face:

Release some ready-to-use components

To facilitate the creation of WooCommerce themes, we could include React components for that can be reused for the common UI elements, like:

  • Filters for products: Price range, selectors…
  • The Cart.
  • The Checkout.
  • An Image slider with augmentation tool.
  • The Order info.
  • And so on.

Product Post Previews

Right now the post previews are not working because the Product handler is not using the standard REST API and therefore doesn’t have an endpoint to get the last revisions. We should look for a solution for this.

Retrieval of orders by order_key

After a sucessful checkout we are showing the order information because it is stored in the state, but we don’t have access to it once the users refreshes the page. We need an endpoint where we can fetch the information of an order using the order_key to simulate what WooCommerce does in WordPress.

Support different payment methods

We should experiment with more realistic payment methods, like Stripe or Paypal.


Last but not least, the main drawback I see with the Store API is that they are not using the standard REST API endpoints for the products and product taxonomies. I am not sure if they studied that option and discarded it. It would be another interesting point to talk with them :slightly_smiling_face:

3 Likes

This is a demo and a bit of a summary as well of the current state of the proof-of-concept:

Feel free to share it :slight_smile:

4 Likes