I am trying to implement a system that uses Squares subscriptions. However, I am having problems. The problem I am having is that I can’t figure out how I am supposed to associate the different data that I receive back from Square via the Webhook to data in my own database. I will give you an example of two of the problems I am having:
Customer attempts to buy a subscription with their card.
I check if they already have a customer account on Square. If they dont, then I make one.
I check if their card is already stored, if it’s not then I store it on their customer file.
I call SubscriptionsApi.CreateSubscriptionAsync() and pass in the CreateSubscriptionRequest data to buy a subscription.
CreateSubscriptionAsync returns success (along with some data that includes the new subscription id)
I am about to store the new subscription id in my database… but oh no… the Webhook has just received a subscription.created message and my system has not yet stored the subscription id in its database, and so when I try to select the relevant data using the subscription id nothing is returned. I am then unable to process the subscription.created webhook message, and my system is out of sync with Square.
Or here is another issue:
Customer attempts to buy a subscription with their card.
I check if they already have a customer account on Square. If they dont, then I make one.
I check if their card is already stored, if it’s not then I store it on their customer file.
I call SubscriptionsApi.CreateSubscriptionAsync() and pass in the CreateSubscriptionRequest data to buy a subscription.
My Webhook receives subscription.created and also payment.created.
I want to store payment data in my database, and associate it with the subscription that it is for. But there is no subscription data in the payment.created json, so how am I supposed to know what this payment is for? How do I associate this payment data with a subscription when they are completely separate and there is no way to relate them to each other?
I searched in the SDK to see if there was a way that I could attach my own personal data to the CreateSubscription request, and have it returned to me via the Webhook messages, then I could add my own database IDs and stuff, but there doesn’t appear to be any such thing.
The nearest I could figure out is that the customer ID is sent with the webhook data, so at least I can associate the webhook data with a particular customer. But I can’t figure out how I am supposed to know what subscription any of my payment messages are related to.
I’m Square Dev AI, here to help answer your question!
Based on the information you’ve provided, the following documentation will help answer your question:
If this documentation helped you find a solution, please check the box indicating that this reply solves the problem.
Otherwise one of our Developer Advocates will provide additional assistance shortly.
For handling subscription.created Webhook Before Storing Subscription ID:
Immediate Storage of Subscription ID:
As soon as you receive the subscription ID from CreateSubscriptionAsync, store it in your database along with a temporary status (e.g., pending).
This ensures that even if the webhook arrives immediately, your system can find the subscription ID.
Webhook Processing:
When you receive the subscription.created webhook, update the status of the subscription in your database from pending to active or whatever status is appropriate.
This way, your system can handle the webhook even if it arrives before you finish processing the initial subscription creation.
Associating Payments with Subscriptions:
To associate payments with subscriptions, you need to use the order and invoice data as intermediaries.
When a subscription is billed an invoice will be created. That invoice will have an order_id associated to it. You can get the invoice_id by calling SearchInvoices and filter on the customer_id in the payment.updated webhook event. Once you have the invoice you’ll be able to match the order_id in the payment webhook to the paid invoice. That invoice will have the subscription_id that you can match with what’s in your database.
Your first solution still has the same flaw that I pointed out though. There is no guarantee that my app will be able to store the subscription ID in the database before the subscription.created webhook message arrives. It might work 99.9% of the time, but one day there will be a moment of lag between my app and the SQL server, or the web app service will be running slow, and that subscription.created message will arrive before I have written to the database. The results of this happening will be disastrous. My app cannot just assume that those two event happen in the same order every time in blind faith.
There is also a similar issue with your final bulletpoint. Lets say that my back end has a function called ‘CreateSubscriptionAndChargeUser’ that I use for creating a new subscription. Somewhere in my function CreateSubscriptionAsync gets called, and returns with success. Then my function immediately creates a ‘Pending’ subscription and returns. The user is then given a ‘Payment Confirmed. Welcome to your new subscription of My App!’ message to tell them that it was successful. But there are 2 problems. First of all, they dont have a working subscription at all. Its in ‘Pending’ state. If they immediately click something then it will give an error saying they dont have a subscription. Secondly, I have no idea if the payment is complete or not. What if the payment fails? I just told them it was successful and that they have a subscription. Before my CreateSubscriptionAndChargeUser function returns I must know for 100% certain that the subscription is created and that the payment was successful. Otherwise how can I correctly inform the user that payment succeeded or failed?
When you receive the subscription.updated webhook how many time are you looking for a subscription? You may want to put a for-loop function in to look for the subscription every second if the first attempt to get the subscription fails.
For your second point why would the subscription be pending if you charged them? We don’t have a pending subscription state that I’m aware of. Or is your pending subscription supposed to start at a later date? If the subscription starts at a later date and the initial charge fails we will send the customer an invoice that they’ll need to pay. The subscription will continue unless your application takes action. Also any reminders to pay should come from your app.
When I receive subscription.updated the subscription is already made (I make it before calling CreateSubscriptionAsync and I set it to Pending status). However it is not guaranteed that it will have the square Subscription ID written to it at that point, because the two operations are completely asynchronous and there is no guarantee which will happen first. I did considering doing a for loop to keep checking until I receive the webhooks, but that seems like a really hacky way of doing things. Also if there is every some issue that causes a webhook message to not be received, then it will be a some time before the Square API retries sending the message, so my function will be stuck in the for loop waiting, and may time out.
The subscription should start immediatly, and it is only Pending between the point where it is created, and the point where the payment is acknowleged as having been successful via one of the payment webhooks.
Here is a scenario to better explain my problem:
The user buys a subscription and my back end CreateSubscriptionAndChargeUser function is called
CreateSubscriptionAndChargeUser creates a ‘Pending’ subscription in my database
CreateSubscriptionAndChargeUser calls Square.CreateSubscriptionAsync - it succeeds
CreateSubscriptionAndChargeUser returns and tells the user a success message
Webhook message is received telling me that the payment failed. But its too late. I already told the user that it was successful.
Another option I am considering is that I might be better to just store a users card on the square system and then have my own system charge it when required. Is there any problem with me doing it that way that you are aware of?