From Commerce Store to Commerce Marketplace

Marketplace

The module I have been working on recently, known before as Commerce Store, has just gone through a major facelift. Now called Commerce Marketplace, for the first time it covers full checkout process, allowing a customer to complete their marketplace experience.

To list few bigger changes:

  • Store Reference submodule doesn't exist anymore - now it uses Entity Reference
  • main Commerce Store module's task is to provide a commerce_store entity type, and all the code not related to this has been moved to other submodules
  • Store Overrides submodule has been removed completely
  • the whole package has been renamed to commerce_marketplace, and lots of commerce_marketplace_* submodules has been added, containing the code removed from late Commerce Store and Commerce Overrides

But wait...

...isn't there already another Commerce Marketplace module, you might ask? And yes, you would be perfectly right, because there is one - Commerce Marketplace developed by farhadhf.

Why the two of them then?

Well, for one, because when I started working on Commerce Store, farhadhf's Commerce Marketplace did not exist yet.

It all started quite some time ago in d.o. issue #990204: The path to Marketplace functionality, where my Commerce Store and farhadhf's Commerce Marketplace appeared around the same time (with just one month's difference between them), presenting slightly different approach to dealing with the marketplace orders.

Some of the differences between these two modules were explained in nielsdefeyter's Analyses of Drupal Commerce Multiseller implementations, the main point for me though (and the reason why this module is still being worked on) is that it tries to keep the external behaviors of Drupal Commerce as much intact as possible, while changing the inner workings of it.

The idea behind this approach is that Drupal Commerce is the known in a marketplace equation (meaning that in any possible commerce site configuration we know what it does and how it does it), so we should be safe enough to modify its internal behaviors. On the other hand, all the other commerce-related contrib modules, not being part of the core Drupal Commerce suite, are the unknowns, where we can't really predict which ones will be used, how they would behave and what would they expect.

My approach therefore is that I am ok with overriding inner workings of Drupal Commerce itself, but changing its APIs/flows/communication etc with external contrib modules should not happen (or at least be kept to minimum as much as possible).

This approach means that (for example) no extra payment or shipping methods should need to be developed to use the marketplace functionality (at least in its basic form) - it should work with all already existing methods out of the box. Also, no new types of anything (orders, products, whatever really) should need to be created - as this might increase risks of stuff breaking down, and unnecessarily confuse site/store owners. And so on.

Essentially, everything should still work just how it did work before - with the only difference being doing it for multiple orders instead of just a single one.

So, how does it work then?

Some of the features described below could be seen in action in the demo store available at http://dev-commerce-marketplace.gotpantheon.com/. Alternatively you could grab module code from its project page and give it a test ride locally on your own server.

There are quite a few modules in the Commerce Marketplace package - essentially one marketplace module for (almost) every DC module, which means that all overrides of core DC features should always be found in quite obvious place.

Stores

The Commerce Store module, already well-known from before, provides the commerce_store entity type, with administration interface provided by Commerce Store UI:

Each store entity has a store owner, who is a user that has access to store administration:

and to its payment methods management:

(And speaking of store owner users - once a store entity is saved, the selected store owner user is automatically granted the merchant role, created by Commerce Store module during its installation.)

Also, the store owner has access to store product and order administration in the Merchant section of their user profile:

Speaking of store-specific payment methods - each store can enable its own set of payment methods, choosing from those enabled globally for the whole site (as configured in Store » Configuration » Payment methods), and obviously each store can have its own configuration for each of those payment methods:

Store-specific payment method configuration:

Store references

entityreference fields are by default added to commerce_product and commerce_order entity types (this might by extended by implementing hook_commerce_marketplace_referencing_entity_types_alter()), meaning that after enabling the marketplace module each product and each order is assigned to a specific store. Selecting a store for a product is not required though, so both products and orders might not have any store assigned to them.

The module also alters product listing view to show which store the product belongs to:

Order groups

Now, when adding products from two different stores to the shopping cart, it still is displayed as one order for a customer:

even though in reality two new orders are being created:

The same happens on the Checkout page - two orders are still displayed as one to a customer:

How it works is that commerce_order entity type now has a new property - order_group - and based on its value both views know that they should display all the orders belonging to the same order group

Checkout pages and panes

The real fun starts on the Review checkout page, when we begin to mess with default pane configuration.

And so, new Review orders checkout page is added (although I'm planning on changing its name, not sure yet though to what exactly - ideas?), which task is to display all assigned panes multiple times - for each order in the order group.

By default, the Payment pane (renamed to Marketplace: Payment) is moved there, and displayed together with new Marketplace: Review pane (plus Shipping pane renamed to Marketplace: Shipping - if available):

Note that you don't want to manually move those new panes around on the admin Checkout settings page, as this is done automatically by the module, based on several dynamic criteria (mainly number of stores you are ordering from and their available payment methods).

This means that payment selection pane is no longer displayed on standard Review order page:


Instead, it is added (in this case together with shipping method selection pane) to the next checkout page - new Review orders (again, better name for it, anyone?):

This page's whole forms are Ajax-powered, thus regenerating content of relevant Shopping cart contents view automatically each time a shipping or payment method is changed, which means always displaying correct amounts without the need to refresh the whole page.

Then, a customer needs to go through payment process for each order separately, and after completing each payment process they come back to this page again, with orders already paid displayed as such:

Only when all orders are paid, customer is redirected to the Checkout complete page (which still needs to be reworked).

That whole process described above works more or less fine, although...

There are still some issues

Well, it's still being developed, so issues are to be expected.

What I've noticed so far, but haven't yet fixed:

  • On the new Review orders checkout page, where pane form is repeated multiple times for each order in the order group, when a form (any other than first) is submitted (Pay for this order) and there are any form/payment errors, and then a user selects different payment method, the form is updated with content of the first form instead of the current one.
  • Access rights need to be re-checked - there still might be some fishy things going on there...

There are probably many more, as everything hasn't yet been thoroughly tested, so if you spot anything, do not hesitate to let me know in the issue queue.

Ok, so what's next?

Marketplace in its current state supports only simple payments (meaning direct payments from a single customer to a single merchant, as described in more detail below), and the most important improvement (besides testing the heck out of everything) would be to add support for different payment flows.

Payment flows

The payment flow between a customer and a merchant could take different forms:

  • Simple payment, which enables a sender to send a single payment directly to a single receiver.
  • From a customer perspective, in case of multiple orders from multiple merchants, it means sending multiple payments, thus going through the checkout process multiple times for each order, each time entering credit card details. Not really a user-friendly solution.

  • Parallel payment, which is a payment from a sender that is split directly among multiple receivers.
  • From a customer perspective it means a single payment for multiple items from multiple merchants (stores), thus the need to enter credit card details only once during the whole checkout process.

    From the marketplace perspective it is beneficial too, as the money do not go through the marketplace account, so the danger of potential financial complaints, disputes or tax issues does not apply here.

  • Chained payment is a payment from a customer that is indirectly split among multiple receivers.
  • In this case the payment is first send to the primary receiver (the marketplace), which then splits the money and forwards payments to final recipients (merchants).

    Comparing their benefits, parallel payments seem to be the best solution - easiest for customers, while at the same time bringing in least potential issues for marketplace owner. Chained payments are still easy for customers, while might introduce some issues for the marketplace owner (complaints, disputes, higher fees, tax issues etc), while simple payments are ok for marketplace owners, at the same time being most troublesome for customers.

    Therefore, when paying for multiple orders from multiple merchants, a parallel payment method should always be used by marketplace whenever only available and possible. If not possible, and if allowed by the marketplace, a chained payment method should be used, and only as the very last fallback - a simple payment method (which advantage is that it will be possible to use it in all cases).

    This might be illustrated with the following flowchart:

    Implementing support for parallel and chained payments is the most important next step in this module further development. It would mean changing the checkout flow again to adapt it to support those new payment flows, as well as providing some kind of configuration for the marketplace administrator to enable this feature for the customers (or perhaps even force it on the merchants?).