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

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.

Related

Stripe update payment on subscription to make it use the default on the customer

So we have some Stripe customers with subscriptions that are setup with payment methods and they work just fine.
The issue is, when the subscription were setup, They were explicitly given the payment method id, instead of letting it default to the one on the customer ( dun... dun.. the same payment method. ).
So what this means is, if a customer updates their payment method, we have to explicitly update each subscription to use it, since the subscription has its own payment method ( the old default ).
The question is, without rebuilding the subscription, how do we tell the subscription to forget about the payment method it has, and start using the default source on the Customer?
The docs on the Subscription talk here about setting the default value, but not how do you clear it?
I attempted to null it out by running this node.js snippet:
const subscription = await stripe.subscriptions.update(
subscriptionId,
{
default_payment_method: undefined
}
);
and it succeeded, but did nothing.
Thanks in advance.
You should be able to send an empty string to clear the value of default_payment_method:
const subscription = await stripe.subscriptions.update(
subscriptionId,
{
default_payment_method: "",
}
);
Then, if default_source isn't set on the subscription, the customer's invoice_settings.default_payment_method or default_source will be used.

NodeJs Update default credit card of customer for Stripe subscriptions and payment

I am building an app in which i am using stripe to handle all payments. And in our app we have a common feature where user can update the default card for their subscriptions but it not working as expected. I Have followed the instruction to update the customer payment method as described in the stripe documentation but it's not working as expected i still see the old credit card details as default payment method in stripe dashboard.
Updating the customer:
const customer = await stripe.customers.update(body.customerId, {
invoice_settings: {default_payment_method: body.pId},
});
Attaching payment method to customer and updating subscription:
const paymentMethod = await stripe.paymentMethods.attach(body.pId, {
customer: body.customerId,
});
await stripe.subscriptions.update(subscriptionDetails?.planId as string, {
default_payment_method: paymentMethod.id,
});
But still nothing, i am not sure what i am missing here but i couldn't update the default payment method.
The screenshot you shared refers to the payment methods saved on the Customer. If you wish to set the default payment method on the Customer, it will be under invoice_settings.default_payment_method on Customer API.
Subscription's default payment method is different from Customer's default payment method. With default_payment_method on Subscription API, it will only update the default on the Subscription object, not Customer object like the page you see. You should be able to find Subscription's default payment method at the Subscription page.
Subscription page link will be: https://dashboard.stripe.com/test/subscriptions/sub_xxx where sub_xxx is the Subscription ID.

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 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);
}
}

What to do when Strong Customer Authentication fails?

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

Resources