What to do when Strong Customer Authentication fails? - stripe-payments

I am trying to implement a Subscription signup and payment flow with Stripe.js V3 and Strong Customer Authentication (SCA) like this:
var stripe = Stripe('pk_test_9hA8gecxBFTY3O6kUm7hl16j');
var paymentIntentSecret = 'pi_91_secret_W9';
stripe.handleCardPayment(
paymentIntentSecret
).then(function (result) {
if (result.error) {
// Display error.message in your UI.
} else {
// The payment has succeeded. Display a success message.
}
})
Everything works great when the payment succeeds.
But what am I supposed to do when it fails?
Should I redirect the user to the initial payment screen, so s/he can start over?
When I do that I get this error:
You cannot confirm this PaymentIntent because it has a status of canceled. Only a PaymentIntent with one of the following statuses may be confirmed: requires_confirmation, requires_action.
Or should I delete everything, including the previously created stripe_customer and stripe_subscription, and then start over?
Thanks for any help.

If the payment fails entirely(maybe it was declined, or 3D Secure was attempted but not completed successfully) , the PaymentIntent from the first invoice should be in the requires_payment_method state and the subscription is incomplete.
You can choose to attempt to collect new payment information from the user and use that to complete the invoice payment and activate the subscription. You can re-use the same PaymentIntent throughout this and try as many times as you wish. For example, if you had a payment form with a Card Element for collecting details, you can have the user enter a new card and call this again :
stripe.handleCardPayment(cardElement,
paymentIntentSecret
).then(function(res){...})
Alternatively you can choose to cancel the subscription entirely if you wish. Otherwise if you do nothing, or the customer isn't able to provide a payment method that works, after 24 hours, Stripe effectively cancels the subscription for you.
Your error message seems to indicate the PaymentIntent was cancelled, which might mean you cancelled the subscription, or you're trying this more than 24 hours after the initial payment, I'm not sure.
This link goes into more detail:
https://stripe.com/docs/billing/lifecycle#incomplete

Related

How to retrieve receipt_url upon successful payment completion in Stripe

We are using the Stripe API to make payments for invoices using a SAPUI5/Fiori UI. The payment intent create happens via a node.js project. We are successfully able to initiate the payment and from the Stripe dashboard Payments section we can see that the payment gets processed successfully.
We have the requirement, that upon successful payment completion, we need to redirect the user to the receipt URL (receipt_url) to display the payment receipt of the just processed invoice. Below is the code we are using to invoke the create payment intent on the Stripe server:
const paymentIntent = await stripe.paymentIntents.create(
{
payment_method_types: ['card', 'us_bank_account'],
metadata: {
....
....
....
},
},
{apiKey: secretKey}
);
res.send({
clientSecret: paymentIntent.client_secret,
});
According to the Stripe documentation, we can retrieve the receipt URL by retrieving the charge within the paymentIntent, but the response we receive upon successful processing of the payment by Stripe does not contain the charge object, it just has the payment id. Is it possible in any way, to retrieve the receipt URL using only the payment intent id?
Calling the payment intent create on Stripe to process the payment, but we are not getting in the response the receipt_url value where we want to redirect the user to, upon successful payment completion.
You can find the receipt_url property on the latest_charge property (make sure you use an API version greater than 2022-11-15; in older versions, it was the last item of charges property), which you can expand on your successful Payment Intent.
For that, you need to add a parameter expand when you create a Payment Intent:
const paymentIntent = await stripe.paymentIntents.create({
…
expand: [“latest_charge”]
});
const receiptUrl = paymentIntent.latest_charge.receipt_url;

Stripe adding paymentMethod to trial subscription and later validate credit card

I'm working on stripe integration with react node.js
While creating a subscription without trial, I have no issues, and all use-cases are much simple.
But, I'm trying to create free trial without collecting credit card details, later when trial expired, I would like to collect credit card details
Server side: Creating Trial Subscription without payment method:
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{ price: priceId }],
trial_end: expiredAt.unix(),
expand: ['latest_invoice.payment_intent'],
});
ClientSide: Later when trial expired, I would like to collect credit card details(paymentMethod), check if user action is required due to failures or 3ds and update the subscription by updating the customer's default payment_method:
const updateCustomerDefaultPaymentMethod = await stripe.customers.update(
customerId,
{
invoice_settings: {
default_payment_method: req.body.paymentMethodId,
},
}
);
How can I update the subscription to perform paymentIntent or charge the user, and return to client same subscription object with status 'in_complete' same as when creating subscription without trial_period?
Currently when running my code, I keep getting status='active' because the first invoice is in status='paid' with price of '0$'.
In the case of a free trial, creating a subscription won't result in an initial payment, and Stripe will instead create a SetupIntent under the hood for you automatically. This SetupIntent is meant to be used when collecting the customer's payment method. So at any point during the trial, the customer would need to return to your app to enter their credit card details, which you would then save using the SetupIntent:
subscription.pending_setup_intent
Confirm the SetupIntent with the collected card details
Once the payment method is setup and saved to the customer (as the default payment method for invoices) it will be used to pay for the subscription's first invoice when the trial ends. The first payment after a trial is considered an off-session payment (last FAQ), so if that payment fails Stripe will attempt to recollect the payment using the usual smart retries and dunning functionality.
In a nutshell, you need to use the SetupIntent that's created (and attached to the subscription) to save the user's payment information.

How to prevent creating duplicate subscription in stripe when trial period is set on subscription

I am encoutering a problem with stripe. let me explain my working
scenario.my requirement is do not charge user for 14 days with card up front
1)user enter card details
2)sca popup appear
3)regardless of user complete the authentication or not a subscription is created in stripe because i set trial_end_date=>now()+14 days
4)user payment fails in some reason and attempt again, another subscription created
i am worried about the duplicate subscription as the stripe will attempt to pay after the 14 days for both of these subscription as it send a Stripe-hosted link for cardholders for both of these subscription
let me give a snapshot of what i have so far
$data['customer']='customerId';
$data['items']=[];
$data['items'][0]['plan']='stripe_plan_id'
$data['default_payment_method']='pm_xxxx'
$data['trial_end']= strtotime('+14 days');
$data['prorate']=true;
$data['tax_percent']=env('VAT_PERCENTAGE');
$data['expand']= ['latest_invoice.payment_intent', 'pending_setup_intent'];
try {
$subscription=\Stripe\Subscription::create($data);
}
catch(Exception $e) {
return response()->json(['success' => false,'message'=>$e->getMessage()]);
}
what i am missing? how to prevent the duplicate subscription scenario.please expain with the correct example which is i am missing.thanks in advance
I think the problem is in your payment flow, there is not a stripe api that explicitly detects duplicate subscription. You are after all allowed to assign more then 1 subscription per customer id. You can create idempotent keys, but that isn't for the same thing you're talking about, idempotent keys are for accidently hitting the submit button twice within the same timeframe.
The solution would be to attach a payment method to your stripe customer id before your subscription trial is over. For example if you are using stripe elements you would call
Node/JS Example below :
const result = await stripe.createPaymentMethod({
type: 'card',
card: card
})
then pass that result to your backend
const result = await stripe.paymentMethods.attach(paymentMethodId, {customerId})
You do not need to create a new subscription, as one has already been created for that user. Credit cards are assigned to customer Ids, and not to subscriptions. Stripe will do the rest.
You do also need to update the customer with the default payment method as follows :
const customer_update = await stripe.customers.update(stripeCustomerId,{invoice_settings: {default_payment_method:paymentMethodId}});
Now when you visit your dashboard you will see a default card assigned to your customer. Once the subscriptions falls out of the trail period, the default card will be charged.
So in this case there wont be a duplicate subscription created, as you are not calling stripe.subscriptions.create again.

Stripe Session Getting Customer Name

I would also like to retrieve the name of the person who paid. How can I get the name value from the stripe session? I have tried this:
const sessions = await stripe.checkout.sessions.list({
limit: 1,
});
console.log(sessions.data[0].customer)
const customer = await stripe.customers.retrieve(sessions.data[0].customer);
console.log(customer)
which gets me the email, but the name is null.
UPDATE: Customers created through Checkout have a null name because it is not collected/set. The "name on card" field on the Checkout page is for the cardholder name for the payment method, not the name of the customer (see https://stripe.com/docs/api/payment_methods/object#payment_method_object-billing_details-name). If you want a Customer created through Checkout to not have a null name, you'll have to update the Customer after the session is completed.
Stripe has a dashboard setting (https://dashboard.stripe.com/settings/emails) you can enable that automatically sends receipts to customers for successful payments completed in live mode. Payments completed in test mode will not automatically send an email, but you can trigger one manually through the dashboard. You can read more about sending receipts here: https://stripe.com/docs/receipts#automatically-send-receipts-when-payments-are-successful.
If you want to grab the customer’s email and send a receipt yourself it’ll be a bit more complicated.
Wait for the Checkout Session to finish by listening for checkout_session.completed through a webhook endpoint (https://stripe.com/docs/payments/checkout/fulfill-orders#handle-the-checkoutsessioncompleted-event).
Grab the customer ID from the Checkout Session object that comes in with the checkout_session.completed event.
Using the customer ID from the previous step, retrieve the Customer object (https://stripe.com/docs/api/customers/retrieve) and get the customer’s email.

How to implement Paypal subscription in a vue application?

I am working on a project which needs to deducts a fixed amount from a user's account to a business account on monthly basis. I am trying to create a Paypal button client side (vue) for accepting subscriptions on monthly basis. The users deduction information on monthly basis shall be stored in a database. The user shall also provided an option to cancel the subscription. Any help would be greatly appreciated.
Well i found out an answer after a little digging in the documentation provided at https://developer.paypal.com/docs/subscriptions/integrate and went through the steps they provided.
Steps
Create a product through their API by providing access_token in the headers.
After the product has been created, get product-Id from response and then create plan using the product_id in request body.
After the plan has been successfully created copy Plan_id and replace the id in smart subscription button.
paypal.Buttons({
createSubscription: function(data, actions) {
return actions.subscription.create({
'plan_id': 'P-your_plan_id'
});
onApprove: function(data, actions) {
alert('You have successfully created subscription ' + data.subscriptionID);
}
}

Resources