Are non-paying customers burning out your support team? Here’s how to fix it.

by Andrew Wiseman

Can’t prioritize customer support without payment data?

Some customers are deadbeats. They don’t pay their invoices yet they lob calls and tickets into your support channel. Like most support teams, you probably have more customers to support than time to do so. So how do you ensure that the process is equipped to prioritize your most valuable accounts….the ones that pay their bills?

Lack of dev resources to integrate systems?

The beating heart of this challenge, is a dataset stored in tools that are not integrated. Your payments or finance systems like Stripe, NetSuite or Zuora are not connected to the software that your support team uses like Salesforce, Zendesk, or Intercom.

You might hope that native integrations offered by your software vendors are up for the task. However, native integrations often fall short. Sometimes they do not support the unique process of your company because they could be lacking key features like support for custom fields. In other cases, native integrations do not support every application in your stack. This can mean orphaned data that sits outside of important processes.

You may try to solve the integration challenge by manually uploading data. That can be an option if you have few customers or have payment and customer data that doesn’t change very often. Yet, most companies have hundreds to thousands of customers, hundreds to thousands of support tickets a day and typically 2% to 10% of your accounts not paying their invoice on time. Whatever solution you pursue needs to be real-time.

What about asking your IT team for help? If you’re lucky enough to have an IT team, they are likely busy supporting mission critical applications. Or they are completely overwhelmed trying to support the 1000+ cloud applications that are in use at typical enterprises.

What if your Finance Director or Support Operations manager could integrate these systems themselves? Bingo! Take matters into your own hands. You can do it.

How to integrate your payments and CRM software

Here’s a step-by-step guide on how to build an integration between your payments system and your CRM system. We’ll use Stripe and Salesforce in this example. The approach will be similar if you happen to use different applications. First, I’ll provide a quick glossary of common workflow terms that I’ll be using throughout this guide:

  • Pagination: When calling an API, records are returned in pages, instead of all at once.  Paginating results means pulling back records one page at a time until all records have been processed.
  • Callable Workflow: A Tray workflow with a Callable Trigger, which can be activated by an event in a secondary Tray workflow.  Callable workflows may also be sent data from secondary flows.
  • Recursive call: A single logical sequence, or Tray workflow, is run again with a different input, but as part of the same overall integration.
  • Timed interval: An event that happens at a specific time of day or on a specific cadence, repeatedly.  Eg. every Saturday at 5am, or every 4 hours.

For this integration, we’re going to need two workflows. Due to the way pagination in Stripe works, we’ll need to use a callable workflow that recursively calls itself to paginate through all of our Stripe records (Flow #2). To kick this recursive flow off on a timed interval, we’ll call our Stripe flow with a primary flow that uses a timed trigger, and a ‘call workflow’ connector (Flow #1). This way, we can start the recursion in Flow #2 at regular intervals. In this example, it will be once per week.

1. Create a new workflow with a Callable trigger.

2. In the Connectors Panel on the left-hand side, find a Stripe connector and drag it into your flow.

3. Click ‘Add Authentication’ to authenticate your Stripe account.  Find your API key in your Stripe settings, and enter it in the authentication panel displayed below.

4. Change the operation on your Stripe connector to ‘List Customers’. Also, remember to name your workflow steps for readability! Double-click on the text beside the Stripe connector, and label it ‘list customers’. This will be very helpful later when you are making changes to your flow, and want to remember what each step does.

5. The List Customers operation returns a list of customers.  In Tray, when dealing with lists, we’ll want to use a Loop. Drag in a Loop Collection connector under your Stripe connector. Pass in the entire list of records being returned from the Stripe connector.

At this point, the loop has been passed the entire list of data that was output from the above Stripe connector. The loop will take each element in the array of Stripe data and process it one-at-a-time.

Before we configure passing our data to Salesforce, let’s setup our pagination. The Stripe API limits 100 records being returned at a time. To provision for the case where more than 100 records are returned for an API call, Stripe lets you pass in a value called Starting After. By default, all of your records will be returned in a list sorted by their ID.  So, what we need to do is pull 100 records, and store the last ID processed. Then, when we make the next Stripe call, we’ll get another 100 records Starting After the last ID processed in the previous batch.

Vocab for this section: Boolean: a logical comparison of two values that returns either TRUE or FALSE.

6. Install a Boolean check in your loop to determine when the last customer is processed. Set the first value of the Boolean to be $.steps.loop-1.is_last and the second value to TRUE (checked box). The loop returns two booleans on every iteration, which are is_first, and is_last, and let you know what iteration of the loop you are currently on. This type of logic makes this scenario very straightforward to configure.

When the last customer is processed, we want to save their ID. We’ll then pass that ID into the next iteration of the workflow.

7. Insert a Data Storage connector inside the Boolean you just installed.  We want to store the ID of the customer who is being processed in the last iteration of the loop.

7a. Set the Scope to Workflow.  This way, the data storage element will always be accessible inside this workflow.

7b. Set the key to WL_stripe_last_customer.  We need a unique key so we don’t accidentally overwrite.  Use the best-practice of WL for Workflow Level, and then the name of the key.

7c. Set the value to the customer ID from the current iteration of the loop (shown in GIF).

Now, we check to see if there are more customers in Stripe. If so, the workflow will call itself recursively. If not, we will reset our Workflow Level Stripe offset.

8. Install Boolean to check if there are more Customers that need to be processed from Stripe.

1st Value: $.steps.stripe-1.has_more

Comparison Type: Equal to

2nd Value: TRUE (checked box)

9. If the Boolean returns FALSE, we will clear out Workflow Level Data Storage.

Operation: Set Value

Key: WL_stripe_last_customer

Value: NULL

10. If the Boolean returns TRUE, the workflow will recursively call itself.

Operation: Fire and Forget

Workflow: The name of the workflow you are currently working in. This menu ONLY shows workflows with callable triggers.

Data: This is required, but for this case, we don’t need to pass any data so we’ll use NULL. Click ‘Add Property to Data’, name the property ‘data’ and set the value to null.

Now that we are handling the pagination at the end of the workflow, we’ll want to be sure to pass in the offset to the beginning of the flow. We cannot pass in a null value to the Stripe API, so we’ll have to configure around that.

11. As the first step in your workflow, install a Data Storage connector to retrieve the offset we’ve stored in a previous iteration of the flow (remember, the flow calls itself).

Operation: Get Value

Scope: Workflow

Key: WL_stripe_last_customer

12. Data Storage defaults to NULL if no value has been set, so we’ll need to check to make sure it isn’t NULL. If it is, we’ll change the default value to an Empty String. Install a Boolean to check if the first Data Storage connector is NULL.

13.  If our Boolean defaults to TRUE, we’ll need to set the value of WL_stripe_last_customer to an empty string, which is what the Stripe API expects when there is no offset. Create an empty string as a configuration variable. In the top-left, click the Gear. Select Config Data.  In the first slot, write the name ‘empty’ and leave the value blank.

14. Install a Data Storage connector in the TRUE branch of the Boolean.  Pass in your config variable as the value.

Operation: Set Value

Scope: Workflow

Key: WL_stripe_last_customer

Value: $.config.empty

Now that we’ve ensured the Data Storage value is something Stripe can accept, let’s GET the current offset value out of Data Storage and pass it into Stripe.

15. Install a Data Storage connector and GET the current offset.

Operation: Get Value

Scope: Workflow

Key: WL_stripe_last_customer

16. Pass the current offset into Stripe.

Now that we’ve configured our pagination, let’s finish the flow to pull the rest of the necessary data from Stripe.

First, let’s make sure that every Salesforce record has the associated Stripe ID stored. For this example, we’ll be using the Contract record type. We will be using email to associate the Stripe customer and Salesforce Contract.  

17. Install a Salesforce connector in your loop.  Add your Salesforce authentication, as described here: https://tray.io/docs/article/authenticate-your-workflow

Configure the Salesforce connector as follows:

Operation: Find Records

Record Type: Contract

Fields >> Field 01: Stripe ID

Fields >> Field 02: Contract ID

Conditions >> Conditions 01: Email Equal to $.steps.loop-1.value.email

18. Install a Boolean on the TRUE branch to detect if the Stripe ID is null on the Salesforce record that we have just found.  We do not need to do anything with the FALSE branch, as it indicates we have found no Salesforce record to process.

Conditions:

Condition 01:

1st value: $.steps.salesforce-1.total

Comparison type: Greater than

2nd value: 0

Condition 02:

1st value: $.steps.salesforce-1.records[0].Stripe_ID__c

Comparison type: Equal to

2nd value: NULL

19. If the Boolean in (19) evaluates to TRUE, update the Salesforce record we found in (17) with the Stripe ID for the current record we are processing.

Operation: Update record

Record type: Contract

Record ID: $.steps.salesforce-1.records[0].id

Record fields >>

Field 01 >>

Name: Stripe ID

Value: $.steps.loop-1.value.id

Now, sync the subscriptions into Salesforce.

20. Install a Boolean connector to check if the Stripe record we are currently processing has subscriptions. Change the operation to: Property exists. All subscriptions have a status, so we are going to check to see if each subscription has a status, which will tell us if that subscription exists.

Operation: Property exists

JSONPath Property: $.steps.loop-1.value.subscriptions.data[0].status

If the Boolean evaluates to TRUE, we can update our Salesforce Contract with any information from the Stripe subscription that we want by simply dragging and dropping.

21. Install a Salesforce connector in the TRUE path of the Boolean. Pass in the Record ID from the previous ‘get contract by email’ Salesforce step.

Operation: Update record

Record Type: Contract

Record ID: $.steps.salesforce-1.records[0].Id

Record fields: Here, add as many fields as you want. For each field, drag the Value to the ‘loop customers’ connector. You’ll be presented with the entire output of the Stripe call, and can choose exactly which data elements to map into Salesforce.

And… you’re done! Now, you’re looping through all of your Stripe records, checking to make sure they are associated with your Salesforce contracts, associating them if they aren’t already, and then mapping in attributes from the subscription on each Stripe account. Use this knowledge to expand on this case, dig deeper into your Stripe account, and map in more attributes.

If you’d like to create a workflow like this one in Tray.io, request a demo.

Happy building!

Get updates from the Tray.io blog directly to your inbox

All the latest product news, tips and tricks direct to your inbox weekly. You can unsubscribe when ever you want!