https://stripe.com/docs/testing#regulatory-cards
I have an issue with 3d secure cards when trial ends.
Steps to reproduce: Node(SERVER):
const customer = await this.getInstance().customers.create({
email: userData.email,
name: userData.fullname,
});
const subscription = await this.getInstance().subscriptions.create({
customer,
items: [
{
price: priceId,
},
],
off_session: true,
promotion_code,
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent', 'pending_setup_intent'],
metadata,
trial_from_plan: true,
});
const invoice = subscription.latest_invoice as Stripe.Invoice;
const intentType = subscription.pending_setup_intent ? 'setup' : 'payment';
const intent = intentType === 'payment'
? (invoice.payment_intent as Stripe.PaymentIntent)
: (subscription.pending_setup_intent as Stripe.SetupIntent);
const formattedSubscription = pick(subscription,['cancel_at_period_end','cancel_at','canceled_at']);
return {
intentType,
...pick(intent, ['id', 'client_secret', 'status']),
...formattedSubscription,
};
From the client side:
const response = await this.$pricesService.buyPlan(this.selectedPlan.id, {
fullname: this.nameOnTheCard,
email: this.email,
hash: this.couponApplied?.hash,
});
if (response.error) {
throw new Error(response.error);
}
const { intentSecret, intentID, intentType, ...restOfBuyResponse } = response;
if (intentType === 'payment') {
stripeResponse = await StripeInstance.confirmCardPayment(intentSecret,
{
payment_method: { card: this.$refs.cardElement.$refs.element._element },
});
} else {
stripeResponse = await StripeInstance.confirmCardSetup(intentSecret, {
payment_method: { card: this.$refs.cardElement.$refs.element._element },
});
}
Everything works as it should until this next point.
To test trail mode as fast as it can be done, I've made API to remove trial from the subscription
const subscription = await this.getInstance().subscriptions.update(subscriptionId, {
trial_end: 'now',
});
After that, Invoice is OverDue, subscription is failed (with switching to pending). I've noticed that default_payment_method is absent, so I've even returned setup intent payment method from the fronted, and attached to the customer, even invoice settings. But no matter what I've modified, new invoice and payment intent never used that information.
Is this expected behavior due to some regulations or am I missing something?
By default, both creating & updating a subscription is considered to be an on-session request and hence the card requires authentication.
If this is purely for testing purposes, you can include off_session=true
.
const subscription = await this.getInstance().subscriptions.update(subscriptionId, {
trial_end: 'now',
off_session : true
});
If you let the trial for a subscription end and progress naturally (without making an API call to update the subscription), you'd see that 3DS will not be requested (using card ending with 3155).
You try this out by creating a subscription with trial_end
set to the nearest possible time. You can make an API call to finalize and pay the invoice if you don't want to wait for 1 hour for it to be done automatically.
const subscription = await stripe.subscriptions.create({
customer: 'cus_FfkjLYmWcepurw',
items: [
{price: 'price_1JXgRCJQtHgRImA7fDIHY4B2'},
],
trial_end : 1634536700
});