#xmy_best-practices

1 messages ยท Page 1 of 1 (latest)

humble sableBOT
#

๐Ÿ‘‹ 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.

primal trout
#

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?

silk phoenix
#
"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.",
primal trout
#

Can you show me your relevant code?

silk phoenix
#

yeah sure

primal trout
#

Not sure I completely understand the flow you are doing here

silk phoenix
#

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

primal trout
#

Are you passing mandate_data when you do that on the frontend?

#

And how was this Paypal PaymentMethod initially created?

silk phoenix
#
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?

primal trout
#

Yes when you do that it is trying to create a new Mandate

silk phoenix
#

And how was this Paypal PaymentMethod initially created?
It is created when finishing the setup intent flow, or no?

primal trout
#

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?

silk phoenix
#

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?

primal trout
#

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

silk phoenix
#

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?

primal trout
#

If they PaymentMethod has already been collected in the past and set up for future use then yes.

silk phoenix
#

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

primal trout
#

Can you share the Subsription ID that you tested with?

silk phoenix
#

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

primal trout
#

Looking

#

Oh

#

You are setting cancel_at_period_end when you create your Sub

#

So that cancels at the end of the trial

silk phoenix
#

That's some interesting behavior

primal trout
#

How so?

silk phoenix
#

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

primal trout
#

Ah no, the trial is its own period

silk phoenix
#

That is good to know, so how would I achieve what I just described?

primal trout
#

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)

silk phoenix
#

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?

primal trout
#

Yes that's correct

#

Basically all depends on what you want and whether you care about the status of the Subscription or not

silk phoenix
#

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

primal trout
#

Then using cancel_at is a fine way to achieve that

silk phoenix
#

Awesome, I have one other quick question regarding PayPal

primal trout
#

Sure

silk phoenix
#

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

primal trout
#

Not exactly sure what you mean by that, you would need to tell me more about what you are doing / trying to do

silk phoenix
#

When I setup a new payment method with setupintent I get to this screen in testing mode

primal trout
#

Sure

silk phoenix
#

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

primal trout
#

No you can set up multiple PaymentMethods in production as well.

#

A new PaymentMethod is going to be created every time you confirm a new SetupIntent

silk phoenix
#

Ah so I could link my PayPal account multiple times ๐Ÿ˜†?

#

Alright, that's all I wanted to know. Thank you for being so helpful ๐Ÿ™