That system has no public interface, so if for example a credit card expires, the user needs to be able to renew the subscription through the website. Product information is synchronized with the external system and completed orders are sent to that system automatically for processing. It also does not support to buy multiple subscriptions at once, so we don’t need a cart.
Commerce 2.x has much improved support for storing payment methods and using them again. The default use case that it supports is to make those stored payment methods visible again to the users in the checkout process, allowing them to avoid entering the payment information again.
We internally need to store payment methods so we can send it to the subscription system. However, the vast majority of users only buys a new subscription if they don’t have one yet or if their old one expired, in which case they either have no credit card yet or it expired.
That’s why we decided to replace the default payment gateway selection checkout pane with a custom one, that does not offer to re-use existing payment methods. Still, having the built-in default storage to store the credit card alias from the external payment gateway is a great improvement over Commerce 1.x.
Storing and reusing payment methods… but not really
Commerce 2.x has much improved support for storing payment methods and using them again. The default use case that it supports is to make those stored payment methods visible again to the users in the checkout process, allowing them to avoid entering the payment information again.
We internally need to store payment methods so we can send it to the subscription system. However, the vast majority of users only buys a new subscription if they don’t have one yet or if their old one expired, in which case they either have no credit card yet or it expired.
That’s why we decided to replace the default payment gateway selection checkout pane with a custom one, that does not offer to re-use existing payment methods. Still, having the built-in default storage to store the credit card alias from the external payment gateway is a great improvement over Commerce 1.x.
Different address handling
- A required delivery address and optionally a different billing address
- A few additional fields to enable existing customers to connect through a subscription number. By entering a valid subscription number we automatically fetch the address and fill out the address field.
- The ability to provide a later subscription start date
- Custom validation to prevent that users can buy a subscription for a newspaper that they already have.
Additionally to replacing the payment gateway pane, we also replaced the address information pane with our own that had all those fields and validation logic in a single place, which simplifies maintenance.
Renewing an expired credit card
In case a credit card expires or the payment failed for another reason, the customer receives an e-mail with a link. That link leads to a page where they can do a manual payment again, which is then again sent to the external system. We wanted to make this step as fast as possible and jump directly to a review/info checkout page where the customer can just click once to start the payment.
This was surprisingly easy to implement, we defined a second checkout flow where we disabled all unnecessary checkout panes and pages, an order type that used that checkout flow as well as an order item type that used that order type and did not have a referenced product. We then programmatically create an order item, an order and send the user to the checkout process.
Synchronization of product variations and submitted orders
There can also be special promotions. To avoid that this information gets out of sync with the website, the shop manager only needs to select the offer as well as providing a few web specific things, everything else is then automatically fetched from the API and updated in the background. Inline Entity Form made this fairly easy, as we can easily control which fields are displayed and hook into the process of building an entity from that.
The opposite happens when a user completes the checkout process, then we need to send that data to the external system, so it can be reviewed and processed. To be able to do this, we defined a custom order workflow, which is easy to define in YAML file, then used that for our order types. When an order reaches a certain step in that workflow, we collect all the information, convert it to the structure that the external system understands and forward it that information.
E-Mail only registration
Our second and current approach is to improve the checkout pane instead so it is easier to customize and extend. We ended up needing that anyway, as we need to show a few additional fields on the registration form too.
Direct checkout
Not having a cart and instead going directly to the checkout is a fairly common requirement, so there was already an issue for this with other people looking for this feature.
We worked on that and provided a first patch that is currently being reviewed. A new setting changes the button to go directly to the checkout page and any messages about cart are skipped. Commerce 2.x currently has the same limitation as 1.x, that anonymous users can only go through the checkout if the order is in the cart session and we currently have to work around that a bit. That also means the checkout and cart functionality are still tightly coupled. Checkout currently depends on cart, so you can not have a checkout without having the cart module completely disabled. That is possibly something that will still be improved.
Conclusion
When combining all those changes, we ended up replacing or extending almost every checkout pane and a few other components that Commerce 2.x provides to be able to achieve the UI and processes we required. We were however able to do so in a very clean way and re-use a lot of code because Commerce 2.x is designed to support exactly that, thanks to the flexibility of the framework underneath the default UI.
Integrating with external systems also became easier in Drupal 8/Commerce 2.x compared to similar requirements in in a Commerce 1.x project. Converting data structures, error handling and general interaction with one or multiple external systems still requires a considerable amount of code and development time, services, plugins and improved APIs support developers in writing clean and maintainable code.
We are convinced that Commerce 2 is a great framework to use for both standard shops with products, shipping and a standard checkout process as well as heavily customized ecommerce solutions. We are looking forward to our next commerce projects.