Question

Stripe Integration: Totally lost!

  • 1 August 2022
  • 4 replies
  • 60 views

Badge

Hello,

 

We have a subscription-based, multi-platform product that’s available on Web, Android, and iOS.

Last week we set up Google Play and Apple Store integration with RC for our product and it’s finally working. We just started doing Stripe integration for our Web users, and it seems to be a lot more complicated and confusing than we expected.  We feel that Strip integration is a 3rd-class citizen of RC, since there are only one vague doc, and a 2 year old sample code and a lot of required manual work.

 

Here are our thoughts and questions.

 


 

1) Any use of SDK for Stripe at all?!

When we first evaluated RC, we thought the Purchases SDK of RC would give us a hand on all platforms. (See the first paragraph below)

 

However, it seems like the Client Side SDK of RC should be completely discarded on Web/Stripe, as it cannot do basic tasks like getPurchaserInfo (Source) even when we pass the strip_xxx Public API Key and app_user_id to the setup() method of SDK.  It throws cordova_not_available error.

 

So, the question is, is it really like this (basically RC doesn’t have any SDK for Stripe) or we are missing anything?

 


 

2) Working with Async Webhook/Events of Stripe

The doc for Stripe integration says:

(and by the way the link doesn’t work. It should be /receipts not #receipts)

 

2.1) “You can” or “You must”?

Is there any other way (than using the /receipts endpoint) that we can make RC+Stripe work?

 

2.2) Why can’t this be a webhook listener in RC?

From the example code it seems like the subscription_id should come from a webhook to checkout.session.completed.

We understand that some people might want their own custom server-side work, but if the whole purpose of such server-side endpoint is to transform the purchaseData and send it to the /receipts endpoint of RC, why wouldn’t RC take it directly?

 

2.3) What should be the order of the events here?

We are on RC Pro plan to get the most out of RC.  One of the features there is Stripe Server Notifications. In its doc it says:

And the webhook for Server Notifications should be listening to customer.subscription.updated event of Stripe. (and 2 other events.)

Now, putting all the pieces of this jigsaw puzzle together, it seems like order of the events should be:

  1. Customer purchases should be made through Stripe’s checkout.
  2. Stripe will fire checkout.session.completed which per the example old code, we should just take and transform and send to RC.
  3. RC will also listen to the customer.subscription.updated event via a webhook, but it fails if the receipt doesn’t exist.

Now, given all of these are async server-to-server calls (“2” goes through stripe→ours→RC servers, which is longer than “3” going through stripe→RC servers), how can we guarantee that “2” always happens before “3”? (or per the warning box, the event will fail on “3”.)

 

2.4) Any plan to update the docs?

It took us half a day of reading the doc and the sample code four times each plus going through half of the Stripe’s API docs to guess how the whole thing should be implemented for a simple “We just want the equivalent of App Store’s monthly subscription, on Web.”  Any plan to make it easier for future developers who will pick RC?

 


 

3) In an ideal world...

 

Last but not least, as a suggestion, in an ideal world here is how things would be working.

(Note that the majority of Cordova/Capacitor/Ionic users are having a Single Page Application in JavaScript (Angular, React, or Vue.js) that is shared between the 3 platforms of Web, iOS, Android.)

  1. Instead of the cordova_not_available error, the SDK (which takes the strip_xxx Public API Key and user ID) would make the necessary AJAX calls to the RC APIs (offering and subscribers) and it would return the offerings and purchaserInfo values in the SDK, seamlessly.
     
  2. purchaseProduct function of the SDK would create a checkout session (pop-up, or navigation with return-url), with that package/product ID. Payment link API could be an option, probably.
     
  3. There would be no required server-side code to process the
    checkout.session.completed event and notify RC of via receipts API.  It would be just a stripe-webhook→RC-API integration on checkout.session.completed event, that would just take 2 minutes to copy and paste a link.
     
  4. And that would be it to get Stripe working in 5 minutes, instead of 2 days of reading and 2 days of implementation!  And we would definitely be willing to pay 1.5% (instead of 1.2% of Pro) to get this done easier and faster. :)

Thanks.


4 replies

Userlevel 2
Badge +4

Hey Aideen,

 

Thanks for the feedback, and to not put too fine a point on it, yes, Stripe is a second class citizen in our ecosystem.

 

We added it as a platform with the use case of separate web experiences that likely already had Stripe integrated, or someone building for Stripe from scratch. The build once, run everywhere use case of cordova wasn’t a primary consideration, and is still kind of a niche. 

 

That said, I do think it doesn’t quite meet our promise, and even developers building directly for the web would benefit from a web RC SDK. It’s been on our list for a long time, just not the top of it.

 

I’ll ask a support eng to dig into your specific questions, just wanted to set some context. 

 

Userlevel 5
Badge +8

Hey @Aideen Nasirishargh 👋 support team here.

 

1) Any use of SDK for Stripe at all?!


At this time, no- we don’t yet have an SDK that supports making Stripe purchases. That being said, purchases from Stripe that you’ve sent to RevenueCat will be included as part of CustomerInfo for any other SDKs for the same user ID (e.g., a user can purchase on the web and have access on iOS for example).

 

2.1) “You can” or “You must”?


Yeah - “you must” is more accurate. This is the only way to get Stripe purchase in RevenueCat.
 

2.2) Why can’t this be a webhook listener in RC?

From the example code it seems like the subscription_id should come from a webhook to checkout.session.completed.

We understand that some people might want their own custom server-side work, but if the whole purpose of such server-side endpoint is to transform the purchaseData and send it to the /receipts endpoint of RC, why wouldn’t RC take it directly?


We can’t listen to this directly, because in order for us to track the receipt it needs to be paired with an app user ID. It definitely isn’t ideal to have to set up a separate server, so I’d be happy to share this with the product team for further investigation on supporting this in an easier way (cc @AnnaToro).

 

2.3) What should be the order of the events here?


In general I’d recommend using Stripe Checkout, and sending receipts to RevenueCat after the `checkout.session.completed` event. Once we receive the receipt, we’ll keep it updated on our end and it doesn’t require any further action from you. The order doesn’t necessarily matter - if we receive a notification for a receipt that we haven’t received from you yet, it’s ignored. Once we do receive it, we’ll keep it updated.

To get Stripe purchases updated on our end faster, you can also set up Platform Server Notifications: https://docs.revenuecat.com/docs/stripe-server-notifications

Again, the order doesn’t matter- these notifications will be ignored if we haven’t yet received the receipt. The notifications will just help to get later events processed more quickly.
 

2.4) Any plan to update the docs?

 

Sure - we’re always looking to keep things as simple as possible, and documentation improvements can always help. I’ll add this to the team’s backlog so we can work to improve these docs.

 

I can’t speak to the requests in #3 directly, but I know @AnnaToro always welcomes this kind of feedback!

 

Let me know if there’s anything else I can clarify.

Badge

Thanks for your responses, Cody and Jacob

2.2) Why can’t this be a webhook listener in RC?

From the example code it seems like the subscription_id should come from a webhook to checkout.session.completed.

We understand that some people might want their own custom server-side work, but if the whole purpose of such server-side endpoint is to transform the purchaseData and send it to the /receipts endpoint of RC, why wouldn’t RC take it directly?

 

We can’t listen to this directly, because in order for us to track the receipt it needs to be paired with an app user ID. It definitely isn’t ideal to have to set up a separate server, so I’d be happy to share this with the product team for further investigation on supporting this in an easier way (cc @AnnaToro).

 

Well, even in your example code (line 74) you are getting the user_id from the purchase object of the Stripe’s payload.  Additionally, you also attach the stripe_customer_id in line 85 as an attribute.

So, I think the answer is “Yes, you can.”  Just the example Heroku app that doesn’t have a DB and doesn’t get any info from any source other than Stripe’s notification/webhooks payload.

 

2.3) What should be the order of the events here?


In general I’d recommend using Stripe Checkout, and sending receipts to RevenueCat after the `checkout.session.completed` event. Once we receive the receipt, we’ll keep it updated on our end and it doesn’t require any further action from you. The order doesn’t necessarily matter - if we receive a notification for a receipt that we haven’t received from you yet, it’s ignored. Once we do receive it, we’ll keep it updated.

To get Stripe purchases updated on our end faster, you can also set up Platform Server Notifications: https://docs.revenuecat.com/docs/stripe-server-notifications

Again, the order doesn’t matter- these notifications will be ignored if we haven’t yet received the receipt. The notifications will just help to get later events processed more quickly.

To verify, we are talking about a scenario where, in chronological order:

  1. The checkout.session.completed event gets delayed (because clearly we should process it on our servers)
  2. So, RC receives customer.subscription.updated first, directly from Stripe, but since the receipt is not known to RC, it gets ignored. (warning on the doc)
  3. Our servers will send the purchase to RC via API to /receipts from step 1.

So, you are saying in this case, even when RC hasn’t processed customer.subscription.updated, everything will work the same?


Thanks again for your time, Cody.

 

Userlevel 5
Badge +8

 

Well, even in your example code (line 74) you are getting the user_id from the purchase object of the Stripe’s payload.  Additionally, you also attach the stripe_customer_id in line 85 as an attribute.

So, I think the answer is “Yes, you can.”  Just the example Heroku app that doesn’t have a DB and doesn’t get any info from any source other than Stripe’s notification/webhooks payload.

Yeah, of course - this is definitely something that our product team will consider when planning Stripe improvements. This specific implementation example uses metadata on the Stripe payload to send as the app user ID in the request to our API, but generally speaking, this answer was meant to reflect that currently, it’s still required to make a POST /receipts request directly, however you get the app user ID.

 

To verify, we are talking about a scenario where, in chronological order:

  1. The checkout.session.completed event gets delayed (because clearly we should process it on our servers)
  2. So, RC receives customer.subscription.updated first, directly from Stripe, but since the receipt is not known to RC, it gets ignored. (warning on the doc)
  3. Our servers will send the purchase to RC via API to /receipts from step 1.

So, you are saying in this case, even when RC hasn’t processed customer.subscription.updated, everything will work the same?


Thanks again for your time, Cody.

 

 

Yes, it will work the same way with keeping subscription status in sync. Once we get the subscription from your POST /receipts request, we’ll then keep it updated by refreshing it manually on our end. The `customer.subscription.updated` event in step 2 would be ignored, since it doesn’t have the context of the purchase/user at that time.

Reply