Synchronisation & Reliability

Ensuring synchronization between your system and billwerk is crucial. If your system provides a bronze plan to a customer, it's imperative that billwerk doesn't charge them for a platinum plan. This synchronization challenge brings forth the complexities of distributed computing, necessitating strategies to prevent race conditions.

The Concept of Orders

A significant concept in both signups and up/downgrades is the management of orders or, more broadly, state management on the server. While the following example uses the REST API, the principles apply to SubscriptionJS as well.

A Naive Implementation

Consider this overly simplistic implementation of an upgrade:

POST /contracts/:contractId/upgrade

    "newPlan" : "platinum"

This approach has several issues:

  • Reliability: If there's no response from the server, the state remains unknown unless another resource, the contract, is inspected.
  • Idempotency: The call isn't idempotent.
  • RESTfulness: The call isn't RESTful.

A More Robust Approach

A better strategy involves creating, modifying, and committing orders:

POST /orders/

    "contractId" : "...",
    "newPlan" : ""

// Idempotent commit operation
POST /orders/:orderId/commit

This way, you can query the order- or commit- status, because the order has an ID, and the actual operation is idempotent. The details are explained in the section upgrades & downgrades [TBD - we might want to show a long 'recipe' for this that comes with a number of examples]. The same argument applies to signups. If a customer is shown an amount of USD 120,- in step 2 of 3, it is desirable and sometimes required by law to ensure that this is the price charged when completing the payment. However, because the price could be changed in the meantime and because the price can depend on the checkout time (e.g. a plan that is billed a month in advance but costs per day causes a larger bill in 31-day months than in 30-day months or February), ensuring this on the client is impossible. Therefore, signups internally work by creating a signup order which is committed by the initial payment or using a separate commit call if no payment is required.

Also, webhooks help to ensure reliability by repeating requests many times unless they receive a response that indicates some kind of success, i.e. a 2xx HTTP status code. Again, this implies that webhook handlers must be idempotent because they can get called multiple times and in incorrect order.