Collapse Menu
Docs Home
Extensibility Options
Contact Support

Webhooks Overview

Overview

Use FastSpring Webhooks with your backend or third-party systems for advanced integration and tracking events.

There are two types of Webhooks

  • Server Webhooks: Post JSON data from FastSpring to one or more external URLs or endpoints. Write scripts to parse the data and update your databases or trigger other events based on the contents of the data.
  • Browser scripts: Execute a JavaScript function in the consumer's browser, passing order data in a JSON object. The function is executed upon completion of an order. Design the function to parse the order data and trigger events based on its contents.

You can have multiple Webhooks and each type of Webhook event can be sent to as many different URLs as you need. Each post from FastSpring to your endpoint may contain more than one Webhook event in its payload.

Design your scripts for parsing server Webhooks to be able to receive duplicate events. For example, if your script receives more than one event with the same event ID, any subsequent identical events should be ignored. Your scripts should be able to handle multiple posts with unique event IDs for the same event for the same transaction and update your records with any new info contained in subsequent posts.

Set Up Webhooks

In to the FastSpring App, select the Integrations menu and select the Webhooks tab. An empty Webhook container (for both live and test events) is created for you. Click Add Webhook to create additional Webhook configurations.

 

Add a Server Webhook

  1. Click Add Webhook URL. The Add Webhook URL popup dialog appears.

  2. In the URL field, enter the external URL or endpoint where you want FastSpring to post the JSON data. We strongly recommend using HTTPS endpoints so that data will be encrypted against interception. 
    • Webhook posts use port 8443 of the targeted endpoint unless otherwise specified. If you need to specify a different port in your URL, please use one of the following ports: 3443, 8282, 9191, 9000, 9999. If you specify any other port in your URL, we may be unable to connect, resulting in a timeout error. 
  3. In the HMAC SHA256 Secret field, you can optionally enter a secret phrase for creating a digest of the payload to provide an additional layer of security. See Message Secret / Security for more details.
  4. In the Events section, select the checkbox for each event that you want FastSpring to post to the URL or endpoint for this webhook configuration.
  5. Scroll down to the bottom of the dialog (if necessary) and click Add.

We recommend using http://webhook.site as an easy way to capture Webhooks for initial testing. If you plan to use the /events endpoint of the FastSpring API to manage your Webhook events, please note that the /events endpoint currently only works with events posted to the first or topmost Webhook configuration on this page.

Add a Browser Script

  1. Click Add Browser Script. The Add Browser Script popup dialog appears.
  2. In the Name field, optionally enter an internal name for the script to help you identify it if you create more than one.
  3. In the Events section, select browser.order.completed.
  4. In the Function text area, enter or paste your JavaScript code.
  5. Click Add.

Create Additional Webhooks and Edit Existing Webhooks

If you need to send the same Webhook event(s) to multiple URLs/endpoints, start by following this procedure to create additional Webhooks configurations. Then, in the new webhook configurations, click the ADD SERVER WEBHOOK command to add new URLs and select the events for the new configurations.

  1. If you click Add Webhook to create an additional webhook configuration, or if you click Edit in a webhook configuration (next to Add Browser Script), a window similar to the following appears.
  2. The optional Title field is for internal use. It helps you to distinguish (in addition to the URL) between one webhook configuration and another.
  3. By default, the Get webhooks from field is set to send webhook events for both Live and Test Orders. If you only want FastSpring to send events via this webhook configuration for live orders--or only for test orders--click the drop-down and select the desired option.
  4. If you want this webhook to use expanded JSON data, select Enable webhook expansion. (More information about expansion is available directly below.)
  5. Click Add to add a new webhook or Save to save your changes.

Webhook Expansion

By default, webhook events include only data that is relevant to the current event type. For example, in an order.completed event, the customer's account ID will be included by default, but not the remainder of the account details. The remaining details are available in a separate account.created webhook event.

However, if you select Enable webhook expansion, then the objects listed below are fully expanded. All of each expandable object's data appears in relevant events other than their own event types. To continue the example above, with this option enabled, order.completed includes the customer's complete account object rather than just the account ID. Depending upon your implementation, this may make it unnecessary to subscribe to some webhook events (e.g., account.created), or to make API calls to fill in details of those objects.

Expandable Webhook Event Objects

  • account
  • order
  • product
  • subscription

View Recent Server Webhook Activity

The Webhooks tab of the Integrations menu includes a Recent Activity command towards the bottom right-hand corner of the page. This command lets you view all recent server webhook events on demand, right in your browser window.

Clicking Recent Activity opens a scrollable popup window that shows up to 250 of the most recent events for which you have configured server webhooks. For each event sent to your webhook URL(s), you can see the date, time, and JSON contents of the individual event.

You can click the FILTER drop-down selector and choose whether the listed activity includes Processed events, Unprocessed events, or All (which is the default setting).


If you believe that you may have missed receiving one or more recent webhook events, you can also use the /events/unprocessed endpoint of the FastSpring API to request the contents of any recent server webhooks that were not delivered successfully. The structure of the API response is identical to that of the webhook events. See Retrieving Missed or Unprocessed Events below.

Server Webhooks

Server-to-server webhook events are delivered as the POST body JSON payload to your specified URL(s). Upon firing, some webhooks also trigger an email message to the customer, depending on the context.

Request Payload

Your webhooks endpoint should be able to receive 1 or more events. During setup, you define which types of events a given URL receives. FastSpring might send multiple webhook events with unique event IDs in a single, combined payload.

The following is an example request that your endpoint could receive containing two different event types. You could receive a post with multiple events of the same type as well.

{
    "events": [
        {
            "id": "jazYJQw5RSWVR474tU2Obw",
            "live": true,
            "processed": false,
            "type": "subscription.activated"       
            "created": 1426560444800,
            "data": {
                .... See webhook payload examples for "data" contents ....
            }
        }
    ,   
        {
            "id": "VOe5PQx-T4S6t8yS_ziYeA",
            "live": true,
            "processed": false,
            "type": "subscription.deactivated"     
            "created": 1426560444900,
            "data": {
                .... See webhook payload examples for "data" contents ....
            }
        }
    ]
}

Common Fields for All Types of Events

  • "id" - Unique ID of the event.
  • "live" - Whether this event is for live data instead of test data.
  • "processed" - Whether this event has been marked processed. For a new event this will always be false.
  • "type" - Identifier for the event that occurred.
  • "created" - Timestamp for when the event was created.
  • "data" - Varies per event "type". To see examples, click the link for each event in the Event Name column of the Server Webhooks table above.

Mark Events as Processed

The goal of a Webhook is to mark received events as processed. There are two techniques:

  1. Bulk. Return HTTP status code 200 to consider all events received as successfully processed.
  2. Individual. Return HTTP status code 202 to exert more control over what is considered processed. In the response body, emit one event "id" on each new line (separated by character 10). Each event "id" given will be considered successfully processed.

    For example, if you receive a POST containing event IDs 8675309EeIEn, 10001110101, and ONOIML8ICU812, and you only want to mark the first two events as processed, you would return HTTP status code 202 and the body of your response would be as follows:
    • 8675309EeIEn
    • 10001110101

All other HTTP status codes are considered failures. However, for failures, we recommend returning status codes in the 50X range, depending on the cause of the failure.

Automatic Retries for Unprocessed Webhook Events

For each webhook event that is not marked processed (see Marking Events as Processed above), FastSpring will automatically attempt to repost the event every ten minutes, for a period of 24 hours or until the event is marked processed successfully. If the event is not marked processed within 24 hours, FastSpring will discontinue attempts to repost that specific webhook event, but it can still be retrieved via the /events endpoint of the FastSpring API (see Retrieving Missed or Unprocessed Events below).

When events have failed to process for 24 hours, a red Failed in last 24 hours indicator will appear on the card for the affected webhook configuration in the FastSpring App.



Optional alert messages can be sent by email to notify you of the issue. If you do not have an alert email address configured, you will see a notice near the top of the Webhooks tab in the FastSpring App. You can configure the email address to which these alerts will be sent by clicking the Specify alert email address link.


Clicking the link opens the Default Notify Settings page. At the bottom of that page is the Store Notification Addresses section.


This section has a separate line for each Store in your account. If your account has multiple Stores, you can specify the Default Email and the Alert Email Addresses separately for each one. To configure alert addresses for failed webhook event notifications, enter one or more addresses in the Alert Email Addresses field(s) for the desired Store(s). If you enter more than one address, separate the addresses with commas, as in the example below.

alert@yourexamplestore.com,orders@yourexamplestore.com

If an address is specified in the Alert Email Addresses field, alert messages will continue to be sent periodically as long as there are failed event posts within the past 24 hours. Once the underlying issue (e.g., connectivity or timeout problems on your end) has been resolved, messages that have failed within the past 24 hours can be reposted successfully on subsequent automatic retries. At that point, the statuses for those events will changed to processed. When there are no more unprocessed events within the past 24 hours, the Failing indicator will disappear from the webhook configuration card, and no further alert email messages will be sent.

Retrieve Missed or Unprocessed Events

In the event that your server does not process one or more events successfully (e.g., if your server is temporarily unavailable at the time an event is posted), you can repost them manually via the order or subscription details page, OR retrieve any and all missed events by making a call to the /events /unprocessed endpoint of the FastSpring API.

Simply make a GET call to /events/unprocessed endpoint and all subscribed webhook events that have not been marked as processed (see above) will be returned in the API response. The response structure is nearly identical to the structures of the unprocessed webhook events, so you can process the response in the same way you would have processed the corresponding/missed webhook posts.

After processing any events that had been missed, you should mark those events as processed via a POST to the /events endpoint of the FastSpring API so that they will not be returned the next time you get unprocessed events. For example: POST /events/{id}. For more information, please see /events.

The response returned from the /events endpoint is limited to 25 unique events. If the "more" attribute is present with a "true" value on the last line of the response, you may need to make multiple requests in order to retrieve all events. You can optionally specify a time frame with your request (e.g. beginning with the time of the last event in the response) if you want to page through all events before marking any of them processed. For more information, please see /events.

It may be a good idea to set up an automatic, scheduled task that runs daily (or more often) on your servers to call /events/unprocessed and automatically retrieve any webhook events that might have been missed during the interval since the job last ran. After parsing the events, the script should mark them as processed. When retrieving unprocessed events, you can optionally specify a time frame, but that may not be necessary in this context. For more information, please see /events

Message Secret / Security

Each webhook can optionally define a secret cryptographic key in the HMAC SHA256 Secret field. FastSpring's server will use that key to generate a hashed digest of each webhook payload. The resulting digest will be encoded to base64 and included in the webhook's X-FS-Signature header. Your server can then use the same process, creating a hashed digest of the payload using the same secret key on your side, and then encoding the resulting hash to base64 before comparing the it to the value in that header.

A post with a valid, matching digest in the header can only have originated from a source that uses the correct secret key. Therefore, if you have only provided the secret key to FastSpring via the webhook interface in the FastSpring App (i.e., you have not given the key to anyone else or used it anywhere else), this confirms that the webhook data is authentic (and also intact), since nobody else knows your secret key and the key is not (of course) included in the post. You can find more information about hash-based message authentication at https://en.wikipedia.org/wiki/Hash-based_message_authentication_code.

The X-FS-Signature header we send is case-insensitive, but there have been some reports that it comes through with varying case (all lower case, or mixed case). We recommend that you capture the incoming webhook data--including the header–for verification while adding/registering. The console will log the request and response and you can inspect it in case there are questions about the header contents.

The following is an example of using the PHP hash_hmac function to create the hashed digest, where $secret is the secret key entered in the HMAC SHA256 Secret field in the FastSpring App:

Example of using PHP to compute and compare the HMAC hash

Note: nginx users need to enable underscores in headers to use this.

$hash = base64_encode( hash_hmac( 'sha256', file_get_contents('php://input') , $secret, true ) ); 

if ($hash == $_SERVER['X-Fs-Signature']) { 
/* Your code here */ 
}


Here is an example of computing and comparing the HMAC hash using Node.js:

Example of using Node.js to compute and compare the HMAC hash

const crypto = require('crypto');


/**
 * Validates a FastSpring webhook
 *
 * @param {string} req    Node request object, where 'req.body' is a Node
 *                        Buffer object containing the request body
 * @param {string} secret the secret string saved in the FastSpring App
 */
const isValidSignature = (req, secret) => {
  const fsSignature = req.headers['X-FS-Signature'];
  const computedSignature = crypto.createHmac('sha256', secret)
    .update(req.body)
    .digest()
    .toString('base64');
  return fsSignature === computedSignature;
}

Browser Scripts

Browser scripts (previously called "browser webhooks") are custom-defined JavaScript functions that run inside the browser window. For Web Storefronts, browser scripts will be executed inside the sandbox. For Popup Storefronts, browser scripts will be executed inside the sandbox and passed to the container page via the callback defined using the JavaScript API. For Popup Storefronts, you can leave function () empty if you only want the event to be passed to the container page and not run inside the sandbox. 

BROWSER.ORDER.COMPLETED - fired upon successful order completion after fulfillment actions were performed.

browser.order.completed Example

{  
   "id":"ZxcLBaJaR2i7N2DYAo7PbQ",                    // hook id - do not use
   "created":1475702220909,                          // created timestamp, in milliseconds
   "type":"browser.order.completed",                 // type of event
   "live":false,                                     // true if not a test order
   "data":{                                          // order data
      "id":"8nEf7SIgR4SjUUspka4oWQ",                 // FastSpring-internal order ID to be used for all order-related requests
      "reference":"KYR161005-9065-20156",            // customer-facing order ID
      "live":false,                                  // true if not a test order
      "currency":"USD",
      "total":15,                                    // order total
	  "totalDisplay":"USD 15.00",                    // order total, formatted for display
	  "totalInPayoutCurrency":"15",                  // order total in payout currency
	  "totalInPayoutCurrencyDisplay":"USD 15.00",    // order total in payout currency, formatted for display
      "tax":0,
	  "taxDisplay":"USD 0.00",
	  "taxInPayoutCurrency":"0"
	  "taxInPayoutCurrencyDisplay":"USD 0.00",
      "subtotal":15,
	  "subtotalDisplay":"USD 15.00",
	  "subtotalInPayoutCurrency":"15",
	  "subtotalInPayoutCurrencyDisplay":"USD 15.00",
      "discount":0,
	  "discountDisplay":"USD 0.00",
	  "discountInPayoutCurrency":0,
	  "discountInPayoutCurrencyDisplay":"USD 0.00",
      "discountWithTax":0,
	  "discountWithTaxDisplay":"USD 0.00",
	  "discountWithTaxInPayoutCurrency":0,
	  "discountWithTaxInPayoutCurrencyDisplay":"USD 0.00",
      "payoutCurrency":"USD",
      "payment":{  
         "type":"test",
         "cardEnding":"4242"
      },
      "total":15,
	  "totalDisplay":"USD 15.00",
	  "totalInPayoutCurrency":15,
	  "totalInPayoutCurrencyDisplay":"USD 15.00",
      "account":"FwlUjl4DSkOnZY8OqORkTw",
      "tags": {
        "key1":"value1"                              // custom order-level tags defined via Store Builder Library or Custom Orders
      },
      "items":[                                      // array of items in the order
         {  
            "product":"subRegular",
            "quantity":1,
            "subtotal":10,
			"subtotalDisplay":"USD 10.00",
			"subtotalInPayoutCurrency":"10",
			"subtotalinPayoutCurrencyDisplay":"USD 10.00",
            "coupon":null,
            "sku":null,
            "discount":0,
			"discountDisplay":"USD 0.00",
			"discountInPayoutCurrency":0,
			"discountInPayoutCurrencyDisplay":"USD 0.00",
            "subscription":"bZ3zfvNgRiycCGNNDao2Cw",
            "fulfillments":{  
            }
         },
         {  
            "product":"jason-s-test-product",
            "quantity":1,
            "subtotal":5,
			"subtotalDisplay":"USD 5.00",
			"subtotalInPayoutCurrency":5,
			"subtotalInPayoutCurrencyDisplay":"USD 5.00",
            "coupon":null,
            "sku":null,
            "discount":0,
			"discountDisplay":"USD 0.00",
			"discountInPayoutCurrency":0,
			"discountInPayoutCurrencyDisplay":"USD 0.00",
            "fulfillments":{  
               "jason-s-test-product_license_0":[  
                  {  
                     "license":"asdf",
                     "display":"License Key",
                     "type":"license"
                  }
               ],
               "instructions":"<p><br/> License Key: asdf<br/></p>"
            }
         }
      ]
   },
   "hook":"5e1d04bb4f8110f1e35008ab37ebfc7b557eca795cc0fd974b4aaef4a73241c6",
   "digest":null                                     // digest checksum
}

Function

A browser script is defined as a JavaScript function similar to this:

function(event) {      
  /* Custom Code Here */
}

Load External Scripts

It is common to require external scripts within the hook. To load external scripts, use the following approach within the function:

this.load('https://scripturl';, callback);
//"callback" is a function() to execute after the script has been successfully loaded.
 

Try FastSpring

Get a free account and see why FastSpring is the ecommerce partner of choice for software providers around the world. Try our full-service ecommerce solution today to unlock revenue growth for your online company.