Question

Flutter - Consumable Purchase Validation on Server


Badge +1

Our Flutter app has subscriptions and consumables that are wired through RevenueCat.

For this specific new feature, after a consumable purchase, some entries must be updated on the server-side. We do have endpoints to inform the back-end that a purchase was made, however, this purchase needs to be validated in the server so that malicious users won't abuse that endpoint.

I found another thread with a similar issue, mostly described in this comment. We do have differences, however, which are that we need to send specific parameters (related to the purchase) to that endpoint and also that the Client must see the results with little to no delay, so using webhooks does not look like a good solution.

We require some sort of purchase/transaction ID that is received by the client right after the purchase and that can be sent to the server for validation. What would be the best way to achieve this?

Thank you in advance.


3 replies

Userlevel 6
Badge +8

Hey @Joshua Sardinha!

At this time, webhooks would be the most reliable way to ensure a consumable purchase was validated and successful via the Purchases SDK if you need additional metadata associated with the purchase. What I’d recommend is:

  • Before a purchase starts, send upcoming consumable product ID to your server paired with your user ID and the specific parameters about that purchase
  • Start the purchase with the RevenueCat SDK
  • When the purchase is completed, RevenueCat sends a webhook to your server with the product ID and user ID
  • Find that upcoming product ID and map it to the webhook, and unlock the content for the user
  • From your app, refresh the status of that consumable purchase from your server with the now unlocked content

Typically, webhooks are delivered within a few seconds of a successful purchase. We’ve found that being as communicative as possible with customers is best, letting them know the app is waiting for purchase verification before unlocking their purchase content.

It’s also worth noting that you can revoke content if a ‘REFUND’ webhook is sent, to be sure the user is only entitled to what they have purchased.

Userlevel 5
Badge +9

Another option here could be to make an API call to your server in the purchase completion handler and make a GET /subscribers API call to RevenueCat from your server to verify the purchased content. The REST API will be up-to-date even if the webhook is a few seconds behind.

Badge +4

I have a similar problem here.  My consumable productId will always be the same, but I unlock horse racing tracks for certain dates.  I think I’m going to pass this array returned from revcat in the purchaser info object to my server

along with the app_user_id:

nonSubscriptionTransactions:

[
    {
        "revenueCatId": "07163327dc",
        "productId": "propicks",
        "purchaseDateMillis": 1664229374000,
        "purchaseDate": "2022-09-26T21:56:14.000Z"
    },
    {
        "revenueCatId": "0350bf9515",
        "productId": "propicks",
        "purchaseDateMillis": 1664229511000,
        "purchaseDate": "2022-09-26T21:58:31.000Z"
    }
]

Then on my server i’m going to call the getsubscriber method and verify that the ID’s returned from revcat match up, hopefully this is a very snappy process b/c the receipt will have already been verified by revcat this is a second security check on my server to make sure that verification took place before consuming the consumable :

 

non_subscriptions":{"propicks":[{"id":"07163327dc","is_sandbox":true,"original_purchase_date":"2022-09-26T21:56:14Z","purchase_date":"2022-09-26T21:56:14Z","store":"play_store"},{"id":"0350bf9515","is_sandbox":true,"original_purchase_date":"2022-09-26T21:58:31Z","purchase_date":"2022-09-26T21:58:31Z","store":"play_store"},

 

I don’t like how getSubscriber will create the customer if it doesn’t exists, so I guess I’ll have to add another test to call the DeleteSubscriber method if I don’t find matching transaction ids...

 

 

Reply