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:
https://www.youtube.com/watch?v=E__1-DNilOQ

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.

3 Likes

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:

5 Likes

WooCommerce has no ETA on the API shipping as stable…
And since we perhaps need to wait a long time before WooCommerce finish their own API. Perhaps even two years: https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/trunk/src/StoreApi

I think it’s smart to look at some alternatives…

You cannot wait two years or more on WooCommerce before implementing support for this… ECommerce is important for any framework! This should be prioritized and be implemented with the Frontity WordPress plugin.

I think it’s important for Frontity to add proper support for WooCommerce as soon as possible.

Have you looked into which of these that can be the best approach and that gives the most features?

or

2 Likes

Thanks a lot for the feedback :slightly_smiling_face: I agree that we have to keep in mind other alternatives to ease the WooCommerce integration, and the ones you shared look pretty interesting. Right now, it’s possible to build an ecommerce using WooCommerce and Frontity, but we would like to work on a package to make it easier, that is what the proof of concept was about.

As I said, you can still integrate WooCommerce yourself, just as you would do in a NextJS or Gatsby app, using whatever tools you prefer on top of Frontity and React.

Some members of the community have already tried WooCommerce in their Frontity projects, and some of them were able to integrate it using the CoCart plugin you suggest. Here you have some forum threads that might be useful → https://community.frontity.org/tag/woocommerce .

In addition, this is another good article on creating a React front end for WooCommerce that may help you with your project: https://www.joshuaiz.com/words/no-woocommerce-cart-api-no-problem.

If you try any of these alternatives, we would love to know your opinion :slightly_smiling_face:

3 Likes

This was made with Frontity and CoCart and it is quite impressive: https://www.texasmattressmakers.com/


There is another opportunity here: creating a commerce namespace.

Packages that would implement the commerce namespace should be interchangeable. After all, they all should do the same, they simply use different APIs to communicate with the backend.

The state and actions of that namespace would be things like:

  • state.commerce.cart
  • state.commerce.checkout
  • actions.commerce.addItemToCart()
  • actions.commerce.applyCoupon()

If there is someone willing to start working on a WooGraphQL or a CoCart package for Frontity, we could work with them on a standard @frontity/commerce namespace.

2 Likes

Hi Frontity. My name is Sébastien Dumont, creator of CoCart.

Happy to answer any questions or help collaborate on building any tools you need to help make CoCart work better with Frontity. I already have a JS library available

I’m still new to React so let me know if this enough or not.

I’m currently testing and fixing bugs in v3.0 of CoCart which will be out soon.

I look forward to helping the Frontity community any way I can.

4 Likes

That sounds awesome Sébastien! Thanks a lot for stopping by :slightly_smiling_face:

Yeah, I think so. It sounds like a cocart package for Frontity could be just a thin wrapper on top of that library to expose those actions and state in the Frontity store. Similar to what we did for the Store API proof of concept (this thread).

Awesome. Congratulations on CoCart by the way! :grinning_face_with_smiling_eyes:

3 Likes

CoCart v3 is currently in testing stages and your all welcome to try it. Once testing is complete and v3 is released, I will be focusing on finishing the checkout and customers dashboard ability.

Details about v3 of CoCart can be found here: CoCart v3 Preview - CoCart

I look forward to your feedback.

4 Likes