Integration Best Practices: Choosing the Right Tools
With the release of version 12 in 2018, Kentico made the switch from Web Forms-based Portal Engine to MVC as its primary development model. This opened the door for more flexible customization— having a separate front-end site allows developers to execute custom code from almost anywhere in that site’s lifecycle. However, sometimes more complex integrations need to tie in to the admin application as well, or don’t really make sense in the context of the live site, so it’s worth looking into the available tools for such scenarios.
In this article, the first of a two-part series, I’ll summarize the various integration options for the admin application in Kentico Xperience 13, and use the Stripe payment gateway integration as an example for how the right tools can be selected. The next article in the series will cover the specifics of integrating payment gateways, using Stripe as an example.
Appraising the available tools and choosing which are right for an integration
Custom scheduled tasks allow for custom code to be executed at regular intervals. They are often used for large batch operations that need to happen during off-peak hours. For example, a scheduled task might compile marketing or E-commerce data and send it to business analysis software that performs data aggregation and statistical analysis. They can also be used for less time-critical data synchronization during normal operation, for example, sending out notifications to customers whose orders have shipped once per hour.
Custom automation actions allow your code to be executed in the context of automation flows— complex workflows that can have branching conditional logic for processing online marketing contacts and their related data. These flows give non-developer users control over when the code is executed, through their choice in both triggers for the process and automation actions. They're ideal for tying in with CRM systems and other services that rely upon or process marketing data, with any combination of pushing, pulling, and calculating.
The Xperience REST service doesn’t allow for custom code execution in the context of Xperience, but it does allow for basic CRUD operations on most Xperience objects. It can come in handy if the integration you are working on allows code execution, which you could use to interface with the API, or if you plan on creating some kind of middleware in between Xperience and the third-party system. The REST service is worth considering for projects that just need access to structured data, like small microsites and mobile applications that are not built on the .NET framework.
Custom macro methods allow custom code to be executed during the evaluation of macros, often in the context of macro conditions. Though macros are no longer as ubiquitous as they were in Portal Engine, they are still commonly used for conditional logic in the admin site. For example, dynamic contact groups and conditional steps in a page workflow or marketing automation flow use macros, meaning custom macros that communicate with an external system can be used for them.
Global event handler
Event handlers execute custom code whenever certain actions happen in the application, usually when a certain operation is performed on an object of a given type. These events are typically used to somehow react when a specific change occurs in an object, by evaluating its state and making some calculation, modifying values, or synchronizing data. A global event handler could be used, for example, in an integration with an SMS service to trigger a text message alert whenever critical changes to specific fields happen in a user’s account.
Integration bus connector
The Integration bus allows for two-way synchronization between Xperience and an external system. It logs and processes synchronization tasks. The synchronization log keeps track of errors that occur during synchronization, and allows for retries, making integration connectors a robust option. They are ideal for synchronizing important data between systems, for example, keeping large product catalogs and users in-sync between Xperience and an ERP system.
ASP.NET Web APIs, when integrated into the admin application, allow custom web service endpoints to function in the admin solution, utilizing the Xperience APIs to process whatever input they receive. This makes them useful for integrations that send HTTP requests— either to pull data from Xperience based on some kind of event, or post updates that will keep Xperience in sync. For example, when a payment succeeds or fails, a payment service may send a POST request to a Web API in Xperience, which then updates the Xperience order status based on the message. Web APIs can also be used to cover more complex functionality that the REST API can’t handle on its own in Headless scenarios, if you have a mobile app or microsite that needs to do more than basic CRUD operations.
An example: Stripe
With these concepts in mind, let’s dig into a practical example— building out the Stripe payment gateway integration.
First, let’s consider which features of Stripe make sense to use. For example, Stripe is capable of maintaining a store’s whole inventory, storing products, prices, and promotions, and calculating prices for checkout. While this may be great for some projects, it doesn’t quite make sense with a DXP like Xperience 13; this approach would essentially bypass the customizable, modular calculation pipeline offered by the Xperience E-commerce module, which is its main appeal.
Integrations often require nuanced decisions based on the capabilities and limitations of each system, so let’s define a set of requirements for the Stripe integration.
- Allow customers to pay using Stripe.
- Keep the calculation of prices, taxes, shipping, and discounts on the Kentico Xperience side.
- This ensures that existing stores will have an easy time adding it as another payment option and can maintain their calculation customizations.
- Minimize data security risks by having customers enter their card information in Stripe, rather than in the Xperience-hosted site.
- This makes the store itself less of a target for hackers.
- I’m no lawyer, but to my understanding, this makes achieving certain compliances, such as the Payment Card Industry Data Security Standard (PCI), easier.
- Update the status of the order in Xperience based on what happens in Stripe (e.g. successful/failed payment).
- If possible, allow for delayed capture of payment, so that certain steps can be fulfilled prior to actually charging the customer’s card.
- Again, I’m no lawyer, but I believe this is standard in certain industries and may even be required under certain circumstances.
Requirement #1 would be met regardless of which Stripe features we need to use, so let’s take a closer look into the others.
Keeping the calculations in Xperience, and the card data in Stripe
In order to preserve the calculation pipeline, we need something that can accept a payment of an amount specified through the API, rather than calculating it on Stripe’s side. At a glance, it seems like this would rule out Stripe Checkout, which touts ways to specify IDs of products stored in Stripe, each as a separate line. However, taking a closer look at Stripe’s SessionLineItemOptions object, it turns out the prices of checkout line items can be specified from the Xperience side as well, via the UnitAmount or UnitAmountDecimal fields. This allows us to pass the calculated total price of the order, and means we don’t need to rule out Stripe Checkout as an option.
Requirement #2 doesn’t do much to narrow down the options, but the same is not true of Requirement #3. If we were to use the usual Stripe API endpoints to manually create and capture Payment Intents, it would mean gathering the customer’s card info on the Xperience live site and posting it to Stripe’s API endpoints. However, Stripe Checkout redirects the user to a payment form hosted by Stripe, making it unnecessary to handle such sensitive data on the Xperience side. For this reason, we’ll favor Stripe Checkout, barring any conflicts with other requirements.
Keeping orders up-to-date
Moving on to Requirement #4, we need to make sure Xperience orders are kept up-to-date, moving to appropriate statuses when events happen in Stripe. This should be handled in the Xperience admin application, rather than the front-end site, since it doesn’t involve direct input from the customer. This means we can start digging into the customization options outlined above.
Stripe’s APIs include GET operations that would allow Xperience to periodically check on the status of any open orders through a scheduled task. Stripe also offers the Webhooks feature, which can be registered to post to an endpoint whenever certain events happen on Stripe’s side. Let’s take a look at each.
If we store references to the associated Stripe objects in our Xperience orders, a scheduled task could loop through any incomplete orders and reach out to Stripe’s APIs to check the statuses of payments for any updates. This has the advantage of unlimited retries during network outages, as the scheduled task continues to run and check on the unfinished orders. However, it also creates more overhead in the admin application, as the task frequently queries incomplete orders to work out which ones are associated with Stripe payments and then reach out to the Stripe API endpoints. If the task was set to run at longer intervals to mitigate this, e.g. every 15 minutes, it could lead to headaches with out-of-date information for store managers on sites using our integration.
Conversely, webhooks allow Stripe to actively tell Xperience when a specific object is updated, with a POST request full of relevant data. This comes with the disadvantage that network outages could interfere with prevent the right orders from being updated. However, Stripe’s webhooks have a three-day retry policy to mitigate this issue, and can be manually re-sent from the Stripe UI at any time.
For our integration, webhooks seem ideal, so we will choose them over a scheduled task. Not every system that someone might want to integrate with Xperience offers webhooks though, so the scheduled task approach is worth keeping in mind for future projects. With the Stripe feature decided, let’s choose an integration option to deal with the webhook requests.
Most of the options for customization can pull data from another service when they execute code on the Xperience side, but with webhooks, we need to work on Stripe’s timeframe
, and react when a POST request is made. The options for listening for requests in Xperience are the REST service and a Web API endpoint. The REST service has an obstacle, though— the body of the POST requests sent by Stripe does not match the formatting required by the REST API. This means an intermediary piece of software would be needed , to parse though the stripe message and create a corresponding REST request. Because of this shortcoming in the REST service, let’s avoid the unnecessary middle steps and create a Web API endpoint directly in the admin application.
Adding delayed capture
Taking a look at the documented example for Stripe Checkout, at a glance it appears to support only direct capture, taking payment from the customer’s card immediately when they check out. However, we don’t have to choose between requirements #3 and #5— digging further in to the SessionCreateOptions object for a Stripe Checkout session, we can set the CaptureMethod of the PaymentIntentData property to “manual” for delayed capture. This means that Stripe Checkout is still a viable option, keeping card data off our servers and allowing for delayed capture.
Now that we’ve validated that Stripe Checkout can handle delayed capture, let’s move on to actually capturing the payments. Stripe’s API includes a fairly straightforward way to capture existing Payment Intents, so it’s a matter of deciding which integration option should run that code from in Xperience.
For this feature, REST and Web API are not applicable, as an action needs to be actively initiated from the Xperience side. Considering the other options, we should decide under what circumstances we want to execute our code. Since order statuses are widely used in the E-commerce module, and often already tied in to existing customizations, let’s capture the payment when the order has been set to a designated Order Status in Xperience.
A custom automation action can be ruled out, because the updated order status would not tie in neatly anywhere with contact-related triggers, and would likely require additional customization to connect. For similar reasons, a macro method can be ruled out, as there isn’t really anything that would tell the macro to fire when the order status updates. The Integration bus could work, if a subscription to the order object type was created, but it is typically suited to more in-depth scenarios than just communicating with a payment gateway. The two remaining options are worth exploring, though.
A scheduled task could find any orders which have the designated status, and iterate through them, capturing funds for each one. Alternately, a global event handler could fire each time an order is updated, and capture the payment if the event that triggered it was an update to the designated status. An additional advantage of the event handler is that the update before event includes the previous values, allowing comparisons to be made between the new and previous order statuses.
With this advantage in mind, let’s go with the event handler. This way, we can check that the order was previously in the status signifying that it was approved for payment. The event handler will run whenever an order is updated in Xperience. It will check if the update included moving the order from the approved status to the ready-for-capture status, and if so, it will reach out to Stripe to capture the funds.
The types of systems that you may want to integrate with Kentico Xperience can vary widely, and a payment gateway is just one example. Even if this is not very similar to your scenario, I hope that it at least provided some helpful points to consider as you plan your approach.
If you’re interested in details about the specifics of implementing a payment gateway integration, stay tuned for the next article in this series where I’ll walk through the specifics of the Stripe integration. If you’d rather just add Stripe to your store without worrying about building it yourself, you can find the completed integration here!