#xmy_best-practices
1 messages ยท Page 1 of 1 (latest)
๐ Welcome to your new thread!
โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
๐ This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1292856248324067410
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
Hello
Yes this should be possible afaik as long as you have recurring payments enabled for Paypal
What is the exact error you are running into?
"code": "setup_intent_invalid_parameter",
"doc_url": "https://stripe.com/docs/error-codes/setup-intent-invalid-parameter",
"message": "The PayPal payment method does not allow attaching multiple mandates. Please set up a new Payment Method to attach a new mandate.",
Can you show me your relevant code?
yeah sure
Not sure I completely understand the flow you are doing here
Ok so my flow is basically
- Creating a setup intent
- Adding a payment method
- Showing a plan selector / payment method selector
Based on the price id / payment method id I call a create subscription endpoint
const subscription = (await stripe.subscriptions.create({
...(user.is_trial_used
? {}
: {
trial_period_days: 7,
}),
// Cancel the subscription at the end of the period
cancel_at_period_end: true,
customer: customer.id,
items: [
{
price: price_id,
},
],
payment_behavior: "default_incomplete",
expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
})) as Stripe.Response<ExpandedSubscription>;
return res.send({
subscription_type: user.is_trial_used ? "payment" : "setup",
subscription_id: subscription.id,
payment_method: providedCustomerPaymentMethod,
client_secret: user.is_trial_used
? // Handled with `expand: ["latest_invoice.payment_intent"]`
subscription.latest_invoice.payment_intent.client_secret
: // Handled with `expand: ["pending_setup_intent"]`
subscription.pending_setup_intent.client_secret,
// Handled with `expand: ["plan.product"]`
plan: subscription.plan,
});
On my frontend I would then check on subscription_type and call either confirmPayPalSetup or confirmPayPalPayment
Are you passing mandate_data when you do that on the frontend?
And how was this Paypal PaymentMethod initially created?
const { setupIntent, error } = await stripe.confirmPayPalSetup(
clientSecret,
{
return_url: `${process.env.NEXT_PUBLIC_DOMAIN_NAME}/paid`,
mandate_data: {
customer_acceptance: {
type: "online",
online: {
infer_from_client: true,
},
},
},
}
);
Ah, so that's the issue?
Yes when you do that it is trying to create a new Mandate
And how was this Paypal PaymentMethod initially created?
It is created when finishing the setup intent flow, or no?
Not sure I understand? You are passing it from your backend to your frontend via payment_method: providedCustomerPaymentMethod, no? That means you already have it?
Or is your intention here to create a new one?
Yeah so on this step:
Showing a plan selector / payment method selector
I list the existing payment methods and subscription plans. Then the subscription creation endpoint takes the payment method id and the price id of the subscription, verifies they are valid and then creates the subscription with the payment method id
So all I have to do is removing this mandate data I suppose?
Okay gotcha, so if you are using an existing PaymentMethod here you really don't need to pass it back to your frontend at all.
You just set it as the default_payment_method on the Subscription when you create the Subscription
And you are good to go
I'd recommend testing this out with test clocks (https://docs.stripe.com/billing/testing/test-clocks) to make sure the full process works as you expect (after the trial ends it charges that Paypal PM)
Ok so in both cases I don't need to call confirmPaypalSetup or confirmPaypalPayment after creating the subscription?
I just need to attach the payment method and I'm good to go?
If they PaymentMethod has already been collected in the past and set up for future use then yes.
How do I set it up for future use?
const setupIntent = await stripe.setupIntents.create({
usage: "off_session",
customer: customer.id,
payment_method_types: ["card", "paypal"],
payment_method_data: {
type: "paypal",
},
});
This is how my setup intent endpoint looks like
I can see the subscription being created with the right payment method when I add the default payment method you have mentioned
But if I advance the time it will change the status to canceled
Can you share the Subsription ID that you tested with?
sub_1Q7IF2Gqob0NJqQXmKY5qCFv
Oh I think I selected a year in advance. let me double check
Verified again, didn't know the advance time persists, but it still auto cancels
sub_1Q7IPLGqob0NJqQXWLUUUcmY
Looking
Oh
You are setting cancel_at_period_end when you create your Sub
So that cancels at the end of the trial
That's some interesting behavior
How so?
I was assuming it would roll over the trial into the subscription duration if not canceled before that
And then cancel for example after a year if the subscription was for that duration
Ah no, the trial is its own period
That is good to know, so how would I achieve what I just described?
If you want that behavior then you should use cancel_at
Or you would use a Subscription Schedule
Or you update the Subscription to set cancel_at_period_end after the trial ends, using a Webhook
Those are basically your options
(We don't really recommend cancel_at tbh because you can end up being off by a few seconds)
I guess the easiest is I just set a cancel_at date after my trial date which is 7 days so for example 10 days
Then the customer is still billed but the subscription not auto renewed?
Yes that's correct
Basically all depends on what you want and whether you care about the status of the Subscription or not
Yeah I just care about that the trial auto rolls over into the subscription unless the customer cancels the trial subscription before the trial is over
Then using cancel_at is a fine way to achieve that
Awesome, I have one other quick question regarding PayPal
Sure
When setting up the payment method I saw that it would create multiple paypal ones. Not sure if that's related to the test mode or each new authorization creates a new paypal payment method entry
Not exactly sure what you mean by that, you would need to tell me more about what you are doing / trying to do
When I setup a new payment method with setupintent I get to this screen in testing mode
Sure
Every time I authorize it creates a new paypal payment method
But the behavior wouldn't be the same in production if I were to use my personal paypal account, right?
It would only work once per account