Stripe - SubscriptionSchedule with 3DS - stripe-payments

I am new with Stripe API and I want to create a Subscription per month with letting the possibility to enter a discount code if they want to.
For that, I use Subscription Schedule which allows me to create 2 phases inside. The first one would be, for example, the first week for 1€ and then the regular subscription.
Everything works well and I'm able to create the Subscription Schedule:
{
canceled_at: null
completed_at: null
created: 1602415626
current_phase: {end_date: 1602502026, start_date: 1602415626}
customer: "cus_IAzNj2kr9i1nw8"
default_settings: {billing_cycle_anchor: "automatic", billing_thresholds: null,
collection_method: "charge_automatically", default_payment_method: null, default_source: null,
…}
end_behavior: "release"
id: "sub_sched_1Hb2hiLShhG4xDx2Fw5Xn3mg"
livemode: false
metadata: {user_id: "263175", promo_code: "", sub_id_id: "11"}
object: "subscription_schedule"
phases: (2) [{…}, {…}]
released_at: null
released_subscription: null
renewal_interval: null
status: "active",
subscription: "sub_IBPWnRqS6gVVlm"
}
The only issue I'm facing is how to validate my user's cards with Subscription Schedule? When you create a regular Subscription you can have access to either pending_setup_intent or last_invoice.payment_intent, from which you can either ask 3DS or send an error message.
Can you guys tell me how I can achieve that?
Thank you

You'd need to retrieve the Subscription by its ID to get those, but then you should have access to them.

Related

How to add ADDON in stripe susbcription?

Is there a way to add multiple plans with different type in the same subscription?
I am trying to add two plans (one is recurring type and the other one single type) but stripe subscription not accepting it. Getting this error
Yes, you can. While the items property only accepts recurring prices, you can use the add_invoice_items property to add one time prices to the first invoice, when creating your subscription. Like this:
// Example in Node.js
const subscription = await stripe.subscriptions.create({
customer: `{CUSTOMER_ID}`,
items: [
{
price: ‘{RECURRING_PRICE_ID}’,
},
],
…
add_invoice_items: [{
price: `{ONE_TIME_PRICE_ID}`,
}]
})

Downgrading the Stripe Subscription results in user refund

I looked around at other solutions but none of them seem to be use case for me. We're not using webhooks instead Cron jobs on the 1st of the month after payments are processed.
My service offers X hours per month.
When user subscriptions they get X hours.
When the user upgrades they immediately get X hours difference between the two, only on upgrade. This works great, the sum is correct.
My issue is that when the user downgrade stripe issues a refund on the next invoice, incorret.
Here is an example that is incorrect, apprentely the user has nothing to pay the next billing cycle which makes no sense; they should pay the downgraded cost.
The code looks like so:
If it's an upgrade:
subscription = await stripe.subscriptions.update(existingSub.id, {
cancel_at_period_end: false,
proration_behavior: "always_invoice",
default_tax_rates: [taxRate.id],
items: [
{
// We need to upgrade the existing item.
id: existingSub.items.data[0].id,
price: price.id,
quantity: 1,
},
],
});
If it's a new sub
await stripe.subscriptions.create({
customer: req.user.stripeCustomerId,
items: [
{
price: price.id,
},
],
billing_cycle_anchor: endOfMonth.unix(),
default_tax_rates: [taxRate.id],
metadata: {
productId,
paymentMethodId,
amount,
userUid: req.user.uid,
},
default_payment_method: paymentMethodId,
});
I'm not sure how to downgrade.
In your scenario above, the user has already paid for the subscription upfront. When you downgraded the subscription, the difference between the subscriptions was pro-rated and credited to the Customer credit balance [0] and applied to the next invoice.
If you don’t want the difference to be prorated when downgrading (maybe your company has a no refund policy), then you would want to apply proration_behavior='none' [1] when downgrading
You can read more about upgrading/downgrading subscriptions here : https://stripe.com/docs/billing/subscriptions/upgrade-downgrade
[0]https://stripe.com/docs/billing/customer/balance#examples
[1]https://stripe.com/docs/api/subscriptions/update#update_subscription-proration_behavior

Stripe subscription webhooks missing metadata and client_reference_id

I'm having issues linking stripe webhooks to customers, since I generally use the client_reference_id or metadata field, however subscription webhooks seem to not have these fields. For example the event checkout.session.completed does contain the client_reference_id, whereas invoice.paid does not.
NodeJS code to generate payment:
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'usd',
product_data: {
name: `Premium license`,
},
unit_amount: 600,
recurring: {
interval: "month",
interval_count: 1
},
},
quantity: 1
}],
subscription_data: {
trial_period_days: 1,
},
metadata: { 'userId': userId },
client_reference_id: userId,
mode: 'subscription',
customer_email: customerEmail,
success_url: `...`,
cancel_url: `...`,
});
Yes, it's a major missing that there is no option to link between events. This becomes critical when you can't rely on the order of webhook events.
I did work around this using metadata though. Pass your own reference id, say, my_database_reference_id to the metadata of both checkout.session.create and also in the subscription_data in checkout creation. This can be the same value you pass inside client_reference_id. Now you can use this while listening to webhook, to connect between the checkout session and subscription object, irrespective of the order it comes in.
NB: Please be warned that metadata can be edited by the Stripe Account, so please be careful if you are a platform and relying on this for any logic.
The client_reference_id is a property of Checkout Session objects in Stripe, but not any other Stripe objects.
Subscription events (like customer.subscription.created) describe Subscription objects, and Invoice events (like invoice.paid) describe Invoices, neither of which are Checkout Sessions, so the property is missing.
Typically the way all of this is linked together is with the Customer object (cus_123) in Stripe, which should be present on all of the events mentioned in the customer property. Checkout will create the Customer object for you if you don't specify an existing one.

PayPal subscription and webhooks

I made an website with a service that I am charging for. I want to create a PayPal subscription. I need that subscription to be connected with my backend (firebase functions - node.js) so I can change some data in the database to serve my users different content depending if they are paying or not. I wanted to use PayPal buttons for my subscription but I can't find a way to connect that button with my backend so it seems PayPal buttons aren't optimal for my problem. I can't use Stripe because it's not supported in my country. Can you offer me a different solution for my subscripton payments or show how can I use PayPal?
You can use Paypal Node SDK for your use case instead of relying to the embeddable Paypal subscribe button. The SDK will give you better integration with NodeJs.
There are basically 2 steps to do this:
1.) Define the Billing Plan Object
The billing plan object defines the subscription plan, including the number of cycles, frequency of payment, any setup fees, and so on.
var billingPlanAttribs = {
name: 'Food of the World Club Membership: Standard',
description: 'Monthly plan for getting the t-shirt of the month.',
type: 'fixed',
payment_definitions: [{
name: 'Standard Plan',
type: 'REGULAR',
frequency_interval: '1',
frequency: 'MONTH',
cycles: '11',
amount: {
currency: 'USD',
value: '19.99'
}
}],
merchant_preferences: {
setup_fee: {
currency: 'USD',
value: '1'
},
cancel_url: 'http://localhost:3000/cancel',
return_url: 'http://localhost:3000/processagreement',
max_fail_attempts: '0',
auto_bill_amount: 'YES',
initial_fail_amount_action: 'CONTINUE'
}
};
Of course, you will need to change cancel_url and return_url to your actual Firebase functions endpoints (or localhost if you are running your functions in localhost for development purposes)
2.) Create and Activate Billing Plan, so once you created or defined your billing - you will need to create that object and activate the billing plan like so:
paypal.billingPlan.create(billingPlanAttribs, function (error, billingPlan){
var billingPlanUpdateAttributes;
if (error){
console.error(JSON.stringify(error));
throw error;
} else {
// Create billing plan patch object
billingPlanUpdateAttributes = [{
op: 'replace',
path: '/',
value: {
state: 'ACTIVE'
}
}];
// Activate the plan by changing status to active
paypal.billingPlan.update(billingPlan.id, billingPlanUpdateAttributes, function(error, response){
if (error){
console.error(JSON.stringify(error));
throw error;
} else {
console.log('Billing plan created under ID: ' + billingPlan.id);
}
});
}
});
Again, all of these are documented in Paypal's Developer Section.
Here's also a link to their github example using NodeJs (which is same underlying backend as a Firebase Function)

Stripe v3 - SetupIntents and Subscriptions

I'm trying to use SetupIntents to validate and save a Payment Method and then create a Subscription using it to charge the customer (inmediately and then monthly) for the required amount.
This appears to be working fine:
The Card is validated (including SCA if needed)
The Payment Method is created, attached to customer as default and enabled for future usage with the status SUCEEDED.
The Subscription is created and uses the above payment method
The problem is that Stripe then generates the corresponding Invoice and Payment Intent but the latter with the status "requires_action" whenever the provided Card requires Secure Customer Authorization (SCA), even though the right payment method (enabled for future usage) is being used and the card validation has been already performed.
I thought the whole point of using SetupIntents was precisely to validate the payment method beforehand and be able to charge the customer afterwards.
Is my assumption simply wrong or this is actually possible and I might just be missing something?
Thanks in advance
EDIT: This is the subscription creation code in the backend:
# Set the default payment method on the customer
Stripe::Customer.update(
stripe_customer_id,
invoice_settings: {
default_payment_method: #requested_source
}
)
subscription = Stripe::Subscription.create({
"customer" => stripe_customer_id,
"proration_behavior" => 'create_prorations',
"items" => [
[
"plan" => "#{#stripe_plan_api_id}",
],
],
'default_tax_rates' => [
"#{#stripe_tax_rate_id}",
],
"expand" => ["latest_invoice.payment_intent"]
});
Thanks for the question, Eduardo.
There are a couple ways to create a Subscription while gaining the Customers SCA authentication and permission to charge the card later. Assuming we already have a Stripe Customer object created and their ID is stored in stripe_customer_id. With the flow you have now there are a couple steps:
Create the SetupIntent. Note that if you create it with usage: 'off_session' and the Customer it'll attach the resulting PaymentMethod when confirmed.
setup_intent = Stripe::SetupIntent.create({
customer: stripe_customer_id,
usage: 'off_session',
})
Collect payment details and confirm the SetupIntent using its client_secret on the client which will attach the PaymentMethod to the customer. Note that it will attach but will not be set as the invoice_settings.default_payment_method by default, so you'll need to make a separate API call later to update the Customer (see step 3).
stripe.confirmCardSetup(
'{{setup_intent.client_secret}}',
{
payment_method: {
card: cardElement,
},
}
).then(function(result) {
// Handle result.error or result.setupIntent
});
Update the Customer and set its invoice_settings.default_payment_method equal to the ID of the PaymentMethod on the successfully confirmed SetupIntent.
Stripe::Customer.update(
stripe_customer_id, {
invoice_settings: {
default_payment_method: 'pm_xxxx', # passed to server from client. On the client this is available on the result of confirmCardSetup
}
})
Create the Subscription with off_session: true
subscription = Stripe::Subscription.create({
customer: stripe_customer_id,
proration_behavior: 'create_prorations',
items: [{
plan: "#{#stripe_plan_api_id}",
}],
default_tax_rates: [
"#{#stripe_tax_rate_id}",
],
expand: ["latest_invoice.payment_intent"],
off_session: true,
})
This uses what's called a "Merchant Initiated Transaction" (MIT) for the Subscription's first payment. This is technically okay if the Subscription is created later after the Customer leaves and should technically work.
If the customer is on your site/app when you create the Subscription, there's another flow that is a bit more correct and doesn't require using a MIT exemption for SCA. The flow is the following for a Subscription without a trial:
Collect card details with createPaymentMethod on the client (no SetupIntent)
stripe.createPaymentMethod({
type: 'card',
card: cardElement,
}).then(function(result) {
//pass result to your server.
})
Attach those card details to the Customer
Stripe::PaymentMethod.attach(
"pm_xxx", {
customer: stripe_customer_id
}
)
Update the Customer's invoice_settings.default_payment_method
Stripe::Customer.update(
stripe_customer_id,
invoice_settings: {
default_payment_method: #requested_source
}
)
Create the Subscription (without off_session: true)
subscription = Stripe::Subscription.create(
customer: data['customerId'],
items: [
{
price: 'price_H1NlVtpo6ubk0m'
}
],
expand: ['latest_invoice.payment_intent']
)
Use the Subscription's latest_invoice's payment_intent's client_secret to collect payment details and confirm on the client.
stripe.confirmCardPayment(
'{{subscription.latest_invoice.payment_intent.client_secret}}', { ......
This second payment flow is a bit more correct from an SCA standpoint for getting authorization to charge the card. The second approach is outlined in the guide here: https://stripe.com/docs/billing/subscriptions/fixed-price
We also have a Stripe Sample you can use to experiment here: https://github.com/stripe-samples/subscription-use-cases/tree/master/fixed-price-subscriptions

Resources