DocRaptor

Implementing Chargify Webhooks

If you are a heavy user of Chargify (as we are), you probably need to get information into your system when an event like payment or product change happens on Chargify’s end. Hopefully, you are aware that postbacks will no longer be available starting July 1st 2015, in favor of their webhook system. If you are not using the postback system, don’t worry, I’m only going to mention it one more time.

Postbacks!

For every piece of information that lives in both places, you need to decide which end will be the Source of Authority for that information. It’s so important I capitalized it!

For data like your user’s name, address (email or otherwise), and application-specific data (for DocRaptor, things like what version of Prince to use as your default), your application should be authoritative. You can (and in fact, must) push some user data to Chargify in order for the system to work, but there is no reason your system should accept changes from Chargify for a change that originated inside your system.

If your application and Chargify disagree about the value for any given piece of data, when do you want to use Chargify’s version? For us, it’s a very small amount of data:

  • Current subscription period started at
  • Subscription state (active or disabled)
  • Which product is being used (e.g. Professional, Max, Silver)

If something is wrong with one of those pieces of information in either Chargify or your application, something Very Bad is going to happen. Any time your application sends that data (as in a DocRaptor customer upgrading their plan), you should carefully monitor for problems. If you can’t trust Chargify’s information on your customers’ product levels, you also can’t trust Chargify to bill them properly.

Because of the highly custom nature of this code, we use Instrumental for this monitoring, but there are plenty of good solutions available.

So, once you’ve properly limited the pieces of information you care about, let’s figure out which webhooks you actually need to receive. Chargify provides a nice table of all webhooks in their documentation. There’s no direct link to the table, but trust me: it’s there. For the purposes of the three pieces of data above, we care about these events:

  • subscription_state_change – maybe someone let their card expire
  • billing_date_change – usually due to support activity
  • subscription_product_change – someone changed which product they use
  • upgrade_downgrade_success – someone changed which product they use
  • expiration_date_change – card information was updated
  • payment_success – $$$

Other webhooks like customer_updated may be interesting based on your requirements, but the six I’ve listed above basically define anything that can impact the billing cycle. These are things you need to know so you can properly control your product’s premium features.

Now it’s time for the bad news: all the webhooks you’ve enabled will be sent to all the endpoints you’ve defined. You aren’t going to define something like webhook_events#payment_success, but rather, one endpoint to rule them all. If you aren’t going to handle an event, you should just return 200 as quickly as possible.

Not all webhooks have the same payload – which makes sense, and also makes this more complicated. Generally, each hook contains all the information you need to handle the event. A customer_updated payload does not include any subscription information – it only deals with basic customer information. Test each event to see exactly what the shape of the payload is. Sometimes the documentation lags behind the actual implementation.

Fortunately, every webhook body contains two pieces of information outside of the payload itself: a unique ID for that event (which you want to log) and the name of the event (e.g. payment_success). From these, it’s a simple matter to log the webhook to an appropriate handler.

While the webhook payload should contain enough information for you to take an appropriate action, you may find it easier to use a sort of brute force approach. Any time an event occurs, just fetch all the pieces of information that MAY have changed from Chargify’s API.

Okay, now I’ve written an awful lot about how to slice and dice your customers’ data over the internet, and I haven’t mentioned security once. Don’t worry, it’s pretty easy.

Each payload includes a header, X-Chargify-Webhook-Signature-Hmac-Sha-256, which contains a Sha256 signature of the payload, signed with your site’s shared secret key. You can also receive the signature as a parameter on the call, but that requires putting some magic values in your webhook configuration. I’ll always err on the side of less configuration, so we’ll stick to the header. Verifying this signature will verify that the information you are receiving is from Chargify and absolutely authentic.

Refer to the Chargify docs to find your secret key. Verifying that requests are authentic is then as easy as this:

Please, do not ever accept unverified data over your webhook. If you do, Very Bad People will steal your internet money and your customers will have a bad time. You have been warned!

So, there’s a little insight into how we think about and use webhooks at DocRaptor, and a few things we learned along the way. If you perform any support activities like refunds or adjustments for your customers, you absolutely need to make sure that Chargify and your application are in sync at all times. Internet!