Stripe default card in checkout - node.js

Is there a way to fill fields in a Stripe checkout page when customer comes back? I've tried to set default payment method for customer with webhooks (payment_method.attached) but this default credit card won't show up during checkout. Instead Stripe creates new payment method for the customer every time he pays. It's strange to have dozen of exactly the same credit cards in a Stripe dashboard. Here is my current code:
const checkout = await stripe.checkout.sessions.create({
cancel_url,
success_url,
payment_method_types: ["card"],
mode: "payment",
customer: stripeCustomer,
client_reference_id: stripeCustomer,
metadata: {
//...
},
line_items: [{
price_data: {
currency: 'usd',
product_data: {
name: packetDisplayName,
},
unit_amount: packetPrice,
},
quantity: 1
}]
})
I later use session id to redirect to checkout then perform few operations in a webhook endpoint
Thanks for taking the time to read my question, cheers

Checkout does not currently support using Payment Methods already attached to the provided Customer.
More broadly, there is not a concept "default" payment method for one-time payments for a customer, only for invoices.
If you already have a known Customer and an attached payment method, you also have the option of creating the payment yourself, directly, using those details.

try its work for me in Node.JS
For add card in node js
let card = await stripe.customers.createSource(customerid, {
source: token
});
Make card as default card
let updatecard = await stripe.customers.update(customerid, {
default_source: card.id
});
you can replace card.id if you just want update card only

Related

Stripe Node - Calculating application fee AFTER discount coupon is used in checkout

I have a Stripe Checkout Session linked to a connected account. The connected account is subject to a 10% charge from my platform. It works well currently, however now I have added the ability for customers to use coupons in the checkout window, the application fee is being calculated from the total amount before the discount is applied.
For example, if I charge €32, the application fee is €3.20. If a discount reduces the charge to €10, the fee should be €1. But it is still charging €3.20.
I have a function which calculates the application fee amount, the problem is that this is done when the checkout session is created - this happens before the customer enters the discount.
I can't think of a way around this issue since the session is always created prior to the discount being applied. Any help/suggestions are appreciated!!
const calculateApplicationFeeAmount = (total) => total * site.stripeFee;
const session = await stripe.checkout.sessions.create(
{
payment_method_types: ["card"],
line_items: [
{
name: price.name,
amount: price.amount,
currency: site.currency,
quantity: 1,
},
],
payment_intent_data: {
application_fee_amount: calculateApplicationFeeAmount(price.amount),
description: price.name,
},
mode: "payment",
allow_promotion_codes: true,
success_url: `${config.baseUrl.url}/unlock/session/{CHECKOUT_SESSION_ID}`,
cancel_url: `${config.baseUrl.url}/unlock/${deviceID}`,
metadata: {
deviceID: deviceID,
},
},
{
stripeAccount: site.stripeAccount,
}
);
return session;
The best solution here would probably be to :
Use payment_intent_data.capture_method:manual when creating the checkout session
After receiving the checkout.session.completed event, update the application_fee_amount on the PaymentIntent
Capture the PaymentIntent
The limitation is that not all payment methods support manual capture.
Alternatively, you could also refund the application fee later : https://stripe.com/docs/api/fee_refunds/create

Stripe Node.js - How to split a payment between several beneficiaries (connected accounts)?

For my first implementation, I transfer the amount of the transaction directly to a beneficiary from a paymentMethod Id retrieved from my app (Flutter). Now my goal would be to split the payment between several beneficiaries on the same transaction.
First implementation code:
var clonedPaymentMethod = await stripe.paymentMethods.create(
{
payment_method: paym,
},
{
stripeAccount: stripeVendorAccount,
}
);
var paymentIntent = await stripe.paymentIntents.create(
{
amount: amount,
currency: currency,
payment_method: clonedPaymentMethod.id,
confirmation_method: "automatic",
confirm: true,
application_fee_amount: fee,
description: "desc",
},
{
stripeAccount: stripeVendorAccount,
}
);
const paymentIntentReference = paymentIntent;
By checking the documentation, I noticed that the stripe.paymentMethods.create method was no longer specified, and that the stripe.transfers.create methods were added.
This code is attempting to clone a payment method to a connected account and then create a direct charge on that account. In order to create charges and then to split the funds of that charge to separate connected accounts a different approach would be needed. The model would be closer to "Separate charges and transfers", where the charge is made on that platform and then some amounts are shared among the connected accounts using transfers. A full description is outlined here [1].
[1] https://stripe.com/docs/connect/charges-transfers

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.

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

Stripe Checkout server-side load Customer cards

In Stripe Checkout, using the server integration, is it possible to preload the customer saved cards?
I have a customer with a previous purchase history and I would like to let him pick a saved card details during checkout.
In the CRM I can see that cus_EyGeAZaPVHwUVg has the credit card stored (the test one for now, 4242...), but upon a new checkout I am not presented with that, even though the email is filled correctly.
I haven't seen any option in the docs, this is the code:
stripe.checkout.sessions.create({
payment_method_types: [paymentMethod],
line_items: [{
name: name,
description: description,
images: [imageUrl],
amount: amount,
currency: currency,
quantity: 1
}],
success_url: 'http://localhost:3000/success',
cancel_url: 'http://localhost:3000/cancel',
customer: 'cus_EyGeAZaPVHwUVg'
})

Resources