#alxizr

1 messages ยท Page 1 of 1 (latest)

keen compassBOT
hazy yacht
#

Hi ๐Ÿ‘‹ this forum is staffed by Stripe, I'll answer as best as I can ๐Ÿ˜„

marsh topaz
#

toby

#

hi

#

how are you

hazy yacht
#

I'm doing alright, how about yourself?

marsh topaz
#

im loosing my mind bro

#

honestly

#

nothing makes sense anymore

#

i will tell you what is the issue

#

and you tell me how to solve it please

#

if you can of course

#

need to develop a feature in that integrates Stripe in our project

#

we sell subscriptions

#

via the online platform

#

i created via the dashboard couple of test products

#

i am using react in the frontend

#

the requirement is to have the billing inside the project and not use the stripe checkout

#

we want to control the flow

#

we are using the Elements and the PaymentElement components

#

here is where the problem begins

#

the Elements component is a context provider that wraps the Payment Element component and also enables us to use the useStripe and useElements hook

#

the issue is that there is no way to render the PaymentElement component without having a client secret

#

in order to get a client secret, we need to create a payment intent

#

but the payment intent is not what we need because we are trying to create subscriptions

#

and not a one time payment

#

@hazy yacht do you understand what is wrong until this point ?

#

the docs are wrong

#

and not updated

#

there is plenty more to tell you

#

i want ot do it in mild steps

hazy yacht
#

This all sounds expected so far. You do want to use a Payment Intent, you just won't create it directly. Instead you create the Subscription, which will create an Invoice, which will create a Payment Intent that you can use to initialize the Payment Element.

This guide walks through the process of using the Payment Element with Subscriptions, is the guide that you're using as your reference?
https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements

Create and manage subscriptions to accept recurring payments.

marsh topaz
#

i have this link to the docs

#

what is written there is wrong

#

you cannot render the payment element without a client secret

#

it is needed beforehand

#

so the react app will not crash

#

if i am trying to create the subscription just as it showing in the link

#

the payment intent inside the subscription object is null

#

subscription.latest_invoice.payment_intent

#

is null

#

i can show you the code if you like

hazy yacht
#

Can you share the ID of the Subscription and Invoice?

marsh topaz
#

what do you mean

#

?

#

i am not saving them right now

#

they are created on the fly

#

i can generate a new one

hazy yacht
#

Those objects are always saved in Stripe.

#

That works too.

marsh topaz
#

customer id = cus_N4d2pEomXjz1fO

subscription id = sub_1MKTmXDWmnCCjaSNTNnYw7iO

latest_invoice id = in_1MKTmXDWmnCCjaSNGmeIEIpc

hazy yacht
#

Ah, since you're creating the Subscription with a trial period, the first Invoice that gets generated is a zero-dollar Invoice, and since there is no payment to be processed for that no Payment Intent is created.

#

Do you plan to consistently provide trials, or will you have a mixture of Subscriptions where some have trials and some don't?

marsh topaz
#

??? WHAT ???

#

all subscriptions have trials

#

some are 14 days and some 30 days

#

but even if i remove the trial, it still won't work

#

and the react project crashes

hazy yacht
#

Why does it crash? Your React code should be making a request to your backend server that provides it with a client secret, then once it has that client secret your React app can initialize the Payment Element.

If you're going to consistently be offering trials, then your Subscription object should include the ID of a Setup Intent in the pending_setup_intent field. You can use the client secret of that Setup Intent to initialize the Payment Element and use it to collect payment method details:
https://stripe.com/docs/api/subscriptions/object#subscription_object-pending_setup_intent

marsh topaz
#

the react app crashes because the elements instance is lacking the client secret and it cannot mount the Payment Element nor other elements

vivid valley
#

@marsh topaz Hi there ๐Ÿ‘‹ taking over for @hazy yacht

Can you step through your code and find out where the client-secret is being left without getting sent to the client?

marsh topaz
#

hi

#

give me a minute

#

i will paste the code

#

app.post("/api/create-customer", async (req, res, next) => {
const { email } = req.body;

const customer = await stripe?.customers?.create({
email,
name: "test customer " + Date.now().toLocaleString(),
description: "first test customer",
});

return res.status(200).json({
...customer,
});
});

#

this is the nodejs express code for creating a customer

#

app.post("/api/create-subscription", async (req, res, next) => {
try {
const { customer } = req.body; // customer id !!!
const { data } = await stripe?.products?.list();
const targetPlan = data[0]; //?.default_price;

const subscription = await stripe?.subscriptions?.create({
  customer,
  items: [
    {
      price: targetPlan?.default_price,
      quantity: 1,
      metadata: targetPlan?.metadata,
    },
  ],
  currency: "usd",
  payment_settings: {
    payment_method_types: ["card"],
    payment_method_options: {
      card: {
        request_three_d_secure: "automatic",
      },
    },
    save_default_payment_method: "on_subscription",
  },
  expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
  payment_behavior: "default_incomplete",
  trial_period_days: 14,
});

return res.status(200).json({
  ...subscription,
});

} catch (error) {
return res.status(555).json({
...error,
});
}
});

#

this is the nodejs express code for creating subscription

#

you can see that i now added the pending_setup_intent value to the expand property as toby pointed out

#

export const StripeSubscriptionPage = () => {
const [stripePromise, setStripePromise] = useState(null);
// const [clientSecret, setClientSecret] = useState("");

// stripe promise
useEffect(() => {
setStripePromise(loadStripe(PUBLIC_KEY));
}, []);

if (!stripePromise) {
return null;
}
// if (!clientSecret) {
// return null;
// }

return (
<div className="flex flex-col w-full">
<header className={clsx("text-black font-medium text-xl p-5")}>
<h1>Stripe Subscription Page </h1>
</header>

  <Elements
    options={{
      appearance: {
        theme: "stripe",
      },
    }}
    stripe={stripePromise}
  >
    <p>Stripe subscription form goes here </p>
    <StripeSubscriptionForm />
  </Elements>
</div>

);
};

#

react Page component for this example that renders the Elements context provider

#

react component for this example that renders the Payment Elements component with the requests to create customer & subscription as per the docs

#

this is obviously not very clean and structured but for the sake of the development with all the issues i wrote it tis way until everything is working

#

the idea is that it is going to be a multi step form

#

user will provide the details to create the customer object, then press next ( subscription checkout) then payment elements component will render and fills in the card details and then press submit ( subscription checkout 2)

vivid valley
#

So, the code isn't very helpful for me to see, I just meant: can you do an incremental step-through of the code on your own and add a console.log(); to find out why the client secret is not being sent to the client

#

That seems like the root of the issue, because the Element should be mounted with a client secret

marsh topaz
#

the thing is this

#

payment element cannot be rendered without the secret key

#

in order to get secret key we need to create customer and subscription

#

the FORM part of the code is not rendered because of this reason until we create the subscription

#

we can now get the secret key after toby explained that the payment intent returns null because we have free trial days

#

so i added the pending_setup_intent to the expand property in the nodejs api code responsible for creating the subscription

#

once this HTTP request comes back to the client, i have a local state for secretKey and i set it

#

the issue is that it all happens AFTER the original page load / render and we are using the elements hook

#

so at this point i am manually setting the secret key inside the elements

#

but it does not matter because all the components are rendered and the elements instance is already created

#

and what happens is that the elements that are rendered in the page are not binded to the elements instance

#

so the next step to create the payment is crashing at the method stripe.confirmPayment

#

i will give you the error, one second please

vivid valley
#

payment element cannot be rendered without the secret key
in order to get secret key we need to create customer and subscription
This doesn't make any sense. What secret key are you talking about? Why can't you get it without creating a Customer or Subscription?

It sounds like you need to choose either to (a) re-render the page asynchronously, or (b) not render the page until you have all the information you need up front

marsh topaz
#

no no no

#

this is react js no react ssr like next or gatsby

#

like i said

#

the target is to create subscriptions

#

using the payment element

#

payment element will render ONLY within an Elements context provider that must have a client secret from an intent

#

either payment intent or setup intent

#

this is what the docs say

#

and the subscription integration in the docs is just wrong

#

this is the error that i am talking about

#

"Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element or Pay Button Element. "

#

after you press the button you get the client secret from the intent

#

then you see the payment element

#

ignore the other elements, i put it in the component to check that it works

#

as you can see i also have google pay enables

#

now if you press the second button which is the PAY button essentially

#

we get the error

#

"Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element or Pay Button Element. "

#

btw

#

it is all the same with the CARD element

#

@vivid valley are you here ?

vivid valley
#

Do you have a URL for a test-mode payment page that we can look at to see this behavior in real-time? I'm still failing to understand what's causing your Payment Element to throw that error.

Is it possible you need to update the Payment Element at some point after the page renders, using element.update()? Like, is this a race condition? Why is confirmPayment() not aware of the mounted Payment Element?

marsh topaz
#

i can show you the code

#

and i can deploy it somewhere so you can see it first hand

#

i have already setup in vercel

#

i explained why confirmPayment is not aware of the elements

#

it is because there is no client secret

#

to begin with

vivid valley
#

it is because there is no client secret
to begin with
Why can't you create the Payment Intent ahead of rendering the Payment Element?

marsh topaz
#

choose subscription in the top nav

vivid valley
#

Apologies if this has been gone over before, but I'm just failing to see why you don't have a client secret to begin with.

marsh topaz
#

payment element cannot be rendered without the secret key
in order to get secret key we need to create customer and subscriptio

marsh topaz
#

i guess for better security or something

vivid valley
#

Wait, back up. When you say "secret key" do you mean the secret API key?

marsh topaz
#

no

#

just a second

marsh topaz
#

the deployment is ready

#

open the dev tools in your browser

#

i added console logs where needed

vivid valley
#

Alright, will circle back in a couple minutes

marsh topaz
#

๐Ÿ‘

vivid valley
#

Sorry, this code is really hard to read and I'm going in circles. It feels like you're trying to pass a promise in to the elements instance, but the promise hasn't resolved yet. If I had to guess, I'd say that's what's happening. This is mentioned briefly here: https://stripe.com/docs/stripe-js/react#useelements-hook

Did you try using elements.update() to see if that gets the Element instance to a non-null state?

Learn about React components for Stripe.js and Stripe Elements.

marsh topaz
marsh topaz
#

what code are you trying to read ?

#

i didnt send you the code repo

#

just the link for a version that you can experience yourself

vivid valley
#

I was looking at the code you sent earlier in the thread, plus what I can see in the dev tools

marsh topaz
#

ok

#

you want link to the repo

#

?

#

it will be easier