Stripe: Preventing dunning on invoices with no credit card on file - stripe-payments

I would like to handle a scenario in Stripe where a trial has ended and no credit card has been entered. Currently in Stripe...
At the end of the trial period, an invoice is created (invoice.created).
An hour later the invoice payment is attempted.
If the attempt fails, the invoice enters dunning (per how they've been configured).
However, I would like to "short-circuit" the attempt on the invoice if there is no credit card on file. Instead of failing and entering the dunning cycle, I'd like to immediately mark the invoice as failed (or some other status that allows me to pay for it later).
This way...
A customer on trial who has no intention of continuing (i.e. they never entered a credit card), will not be forced to sit through the dunning process and receive payment failure emails via Stripe.
If they decide to come back, upon entering CC info, I could pay the outstanding invoice via the API and reactivate them on our end.
There doesn't seem to be a straightforward way of doing this. Specifically, while I could process an invoice.created webhook event and then figure out if the customer has a CC on file, I can only mark that invoice as closed, which, to my knowledge, means I cannot reopen it later to pay if they decide to come back.
Has anyone dealt with a scenario like this? It seems like there is no elegant scenario for handling a trial end when a customer has not entered CC info.

I've figured out a workaround. Posting here in case anyone else stumbles upon this issue one day.
Rather than short-circuiting the dunning cycle for an invoice with no CC, I simply let the dunning cycle proceed.
However, I've disabled Stripe failed payment emails from being sent from Stripe. This way, I can decide what emails to send (or not send) upon any payment failure via the webhook.
Now, when I receive an invoice.payment_failed hook, I can check against the customer's ID passed in the event to see if that customer has a CC. If they don't, then I can custom send a "Trial ended" email on my end and suspend the account (again, on my end).
On subsequent failures during the dunning process, I will be able to check if the account was already suspended. If so, I simply don't send any email notification.
The net effect is that a customer gets the "Trial ended" email on the first invoice.payment_failed event, and nothing on subsequent events.
For customers who do have a CC, I can send off a "payment failed" email on receiving each event.

Related

What's the best practice for handling bill details with Stripe?

In my mobile app when the user has selected some items, I would like them to be able to review their order and see a preview of their bill (the price of each item, subtotal, taxes and total). However I would not want them to pay immediately. Once they place the order, they will not be charged until the order is accepted.
Do I calculate the user's subtotal, tax and total on my server or does stripe provide an api to handle these?
I've read the docs on invoicing and checkout, and checkout seems to be the api for my situation but I'm not 100% sure.
It depends on how you define "place the order" and "order is accepted". Normally when the Stripe's Checkout Session page is displayed to your customer and they push the "Pay" button, the transaction will be executed immediately (they paid), then the customer can be redirected back to your page or app.
If you want to somehow delay the payment until a specific timing (that you want to review or execute some logic, before eventually "accept" the order), you can consider using Checkout with Setup Intent.

When to send out booking confirmation with Stripe webhooks (payment_intent.succeeded taking too long)?

We are using Stripe.js + Elements + Webhooks, our payment methods are Cards, Sofort and SEPA.
Our question is on the usage of webhooks: Is it normal to always wait for the payment_intent.succeeded event before sending out a booking confirmation to the buyer? With some payment methods (SEPA, banking) this takes hours/days/too long.
What are best practices here? Only wait for payment_intent.processing?
We are selling courses, some of which may be booked shortly before the course starts, so we cannot wait a long time for the payment_intent.succeeded event. But then how do you deal with fake bookings?
Lets say we offer a course:
Somebody buys it using e.g. SEPA payment method.
He clicks "Order now!" on our page -> We get event payment_intent.processing
We send out confirmation -> He can access the course
He never pays, there is never a payment_intent.succeeded event, but he was still able to access it.
Alternatively, we wait until payment_intent.succeeded event -> But in this case participants cannot book a course on the same day using SEPA.
How is this case handled usually? Do I need to pay for Stripe Radar to identify fraud/fake transactions before I get the payment_intent.succeeded/payment_intent.failed event?
Any help is much appreciated!
Stripe Radar only works for card payments - https://stripe.com/docs/radar/risk-evaluation#not-evaluated.
Yes, you should only send out booking confirmation to the buyer only upon receipt of payment_intent.succeeded webhook. Otherwise, like what you mentioned, if the payment fails, you would have provided access to the course for free.
If you want to receive payments immediately, then you should limit the available payment methods to card payments only.
As the Stripe documentation mentions - SEPA and SOFORT are both delayed notification payment method, which means that funds are not immediately available after payment.
Maybe you can consider offering SEPA / SOFORT as an option only if the customer is making payment X days before the course starts. You would want to check what is the maximum time for the payment to arrive in your Stripe account for either of these payment methods :
https://stripe.com/docs/payments/sepa-debit/accept-a-payment
https://stripe.com/docs/payments/sofort/accept-a-payment

Can Stripe change the payment intent status between the confirm payment call, if successful, and the webhook they send me?

I have a social app where my customer is going to make single purchases (credits/tokens/likes) using a credit card (Visa, Master, Amex, Discover). When the purchase is complete, and I receive a "succeeded" in the result "status" from the Stripe result, the customer will then be given 10, 20, 50, 100, etc. tokens to their profile that can be used now for "liking" someone.
FYI - If the user has a card saved and chooses that card, that Stripe card number and customer number is used for the payment intent, otherwise I'm collecting the card details, and passing that to the "confirmpayment()".
QUESTION - Once I call Stripe "confirmpayment()" client side, and get the result back, which shows the status (succeeded, failed, incomplete, etc.) Is it possible to receive a "succeeded" status in the result and then at some later date via Stripe's webhook, receive a different status other than "succeeded"?
FYI - I've gone through almost all test card scenarios to test for this type of condition, but it doesn't seem like it will happen? It seems like the payment will either succeed or fail on the confirmpayment() call. I'm also double checking the status result on the server side by fetching the paymentIntent from Stripe. Just to make sure the user (client side) hasn't changed it in the code (JS/Typescript).
Concern - The customer finishes their purchase, the confirmpayment result shows a status of "succeeded", their "tokens" are added to their profile at that moment and then they proceed to use them to send "likes" to another user. Then later on I get the Stripe webhook back showing a different status with "failed" or "incomplete". But by this point, "tokens" will have been used and a monetary amount based on the amount of the "token" will have been moved from my Connect account where the original purchased fund will be sent to the intended receiver's connect account. If this happens I foresee it being extremely difficult to back all this out and reverse it will out a huge headache.
ALTERNATIVE WORKFLOW - I could create a "payment" entity on the server side with a pending status and wait until I receive the Stripe webhook before I add any "tokens" to the user account/profile. This will insure there is no possibility for error, but it makes the user wait until the webhook comes in before they will see a status of "success" on their payment page and won't be able to use any "tokens" from that purchase until I get the webhook. I wanted to give them a more instant experience.
Any help or advice will be much appreciated.
The result you get back after the Promise confirmCardPayment() resolves will be the same as the status you get back on the webhook event so it will never be that a succeeded PaymentIntent transitions back to requires_payment_method or requires_action, as succeeded is an end state of the lifecycle.
Keep in mind, webhook events can be out of order, so technically you could get a payment_intent.requires_action after your payment_intent.succeeded event (if they happen very quickly.
The best thing to do is to fetch the PaymentIntent from the API (after an event) to get the "true" state of the PaymentIntent.

How to prevent shopping cart alterations when payments are asynchronous

The steps below illustrate my problem with Stripe's PaymentIntent flow, but you could come up with something similar for the other payment gateways I've looked at where the final notification of a successful payment is sent asynchronously from the payment gateway to the merchant site.
Customer adds 10 x item A to their shopping cart, total now $100
Customer goes to checkout page. Server creates a Stripe PaymentIntent for the $100 total and sends the 'client_secret' to the browser.
Customer's browser displays the checkout page, showing $100 total, and Stripe's payment form.
Customer opens a new tab, and adds 10 x item B to their shopping cart, total now $200.
Customer returns to checkout tab, and completes $100 payment with Stripe (nothing the site can do to prevent this - it's all happening client side)
Asynchronously, Stripe notifies the site via webhooks that we've got a $100 payment. What do we do now?
The payment total no longer matches the cart total. Do we have to refund the payment and cancel the order? How do we notify the customer? We've probably already shown them an 'order complete - thank you' page, because we had no way of knowing the total was wrong until the asynchronous notification arrived. The customer's probably left our site already. What do we do with their shopping cart?
-- Some further background to all this:
I used to always turn to Stripe whenever my clients wanted to take online payments on their websites, because Stripe's synchronous model made my code nice and easy. The customer would enter their card details, Stripe would then return a token representing the payment, finally my server side code would check all the details were correct, use Stripe's API to collect the money, and return a 'thank you' message to the customer's browser.
But now it seems Stripe are moving away from this model to an asynchronous model (PaymentIntents), where your server is supposed to listen for notifications for completed payments, before fulfilling orders. In Stripe's terminology, we should set up 'webhooks' listening for the 'payment_intent.succeeded' event.
All the other payment gateways I've used in the past also have an asynchronous model, in the sense that your webserver has to wait for some kind of callback from the gateway notifying us of the payment, before we can safely start processing the order. PayPal calls it 'Instant Payment Notification', Worldpay called it 'Order Webhooks', Adflex called it 'Server2ServerNotification'... etc.
Where I'm struggling, is trying to cope with things that might happen during the gap between checkout starting, and payment notification being received. Given that these payment gateways are all using these kind of asynchronous models, there must be a simple solution to this (and similar) problems, but I'm really stuck - any suggestions would be much appreciated.
I think the main point you're missing here is that the PaymentIntent amount is set server-side. This means that when your customer opens a new tab and adds more items to their cart, you should be updating the PaymentIntent on your server to reflect the new amount. Then when they switch back to the other tab and complete the payment, you should have the total amount reflected in your PaymentIntent.
Your customer might still see the checkout process for an amount different to what is actually being charged, in which case I suggest you look at implementing websockets to make sure they always see the total amount in their shopping cart.

How to update subscription memo field with stripe api?

there is the memo field that stripe allows to edit on dashboard
and it's said that This will appear on any invoices and receipts.
That's exactly what I need - field that is inherited by invoices and viewed in invoice emails
Though there is no memo field on subscription object that api returns https://stripe.com/docs/api#subscription_object
Where is it?
There is somewhat of an incomplete work-around, if anyone is still looking for a solution. Stripe will create a draft invoice an hour or so before finalizing and collecting payment. If you register a webhook for the invoice.created event, you can use it to grab the invoice object and update the memo field. The very 1st invoice however (when the subscription is created), is finalized immediately; before the webhook is called. And once it is finalized, you cannot update it. So the technique works for all but the first invoice sent out.
It is, of course, not a great solution. Besides the 1st invoice problem it is complex; relying on a webhook for the update is problematic.
Sucks, I know, but it's the only thing I could come up with.

Resources