#rdhelms-paymentelement-block

1 messages · Page 1 of 1 (latest)

ornate merlinBOT
sudden sentinel
#

@maiden oriole no I've never seen this. Do you see an error in the console or anything? Do you have a simple way for me to reproduce/look at?

maiden oriole
#

No error except when we test it locally we see the warning about using https, but we've also tested in on https and get the same result.

#

We're mounting the Payment Element without a client_secret, creating the subscription on the backend (which returns the client_secret) and then passing the Elements instance and client_secret to the confirmPayment call. And just never moving past that. We've tried removing the redirect: 'if_required' parameter but we still don't see a redirect

sudden sentinel
#

is there any way you can make a repro available so that I can look at the exact code and calls?

maiden oriole
#

I’ll work on creating a close repro

sudden sentinel
#

If you have ngrok or equivalent I can likely try too

maiden oriole
#

If you go here and create an account (using an exam like Firefighter) and then press the top “Upgrade” button, and try to fill out the Payment fields and submit, then you’ll see it hang:

https://web-study-stripe-testing.onrender.com/

#

Or let me know if it works for you 🙂

sudden sentinel
#

(trying)

maiden oriole
#

Thanks for taking a look

sudden sentinel
#

I'm stuck at the PaymentMethod

#

I can't click Start Studying

maiden oriole
#

Have to fill out address info?

sudden sentinel
#

rdhelms-paymentelement-block

maiden oriole
#

Oh sorry yeah just pick the free plan during registration

sudden sentinel
#

gotcha, looking

maiden oriole
#

And then use the Upgrade button in the app’s nav bar after onboarding

sudden sentinel
#

yep trying to debug this

#
                                const {paymentIntent: u, error: c} = await r.confirmCardPayment(a, {
                                    payment_method: o.paymentMethod.id
                                }, {
                                    handleActions: !1
                                });
                                c ? o.complete("fail") : (o.complete("success"),
                                u.status === "requires_action" ? await r.confirmCardPayment(a) : await Ot.actions.activateSubscription({
                                    subscriptionId: l
                                }))
                            }```
#

that's the minified code I see

#

can you do unminified or at least add logs? I don't know what a and l are in this code but I assume you don't go in that code

#

also it's calling confirmCardPayment() which means you're not using PaymentElement at all right?

#

ah you have another part of the code with confirmPayment()

#

so yeah can you add logs to both places to figure out where we end up

#

I added breakpoints and don't seem to go in your if

maiden oriole
#

Yeah the confirmCardPayment is for the case where we’re planning to use Apple/Google pay via the payment request button

sudden sentinel
#

you know PaymentElement supports wallets too right? but we can get into that later

maiden oriole
#

In our logging we were getting right on the line above the confirmPayment call

#

And yes our design team just prefers the wallet options being in a section above lol

sudden sentinel
#

yeah I dunno I can't reach that breakpoint and it's all minified so it's tough to read

#

also every time I try I have to re fill all the address 😅

#

So I think the next step is to pause and do a simple/basic repro on just one simple page

maiden oriole
#

I’ll add some console logs for reference and I’ll copy more of the original code here in a minute

sudden sentinel
#

I must be looking at the wrong code, none of my breakpoints are being hit

maiden oriole
#

Ok I'm pushing some updates that will add some console logs and actually a debugger statement that will hopefully stop you right before the confirmPayment call

sudden sentinel
#

perfect

maiden oriole
#

Here's the original code up until the confirmPayment call

                if (import.meta.env.VUE_APP_STRIPE_PUBLISHABLE_KEY && this.plan) {
                    const stripe = await loadStripe(import.meta.env.VUE_APP_STRIPE_PUBLISHABLE_KEY)

                    const result = await this.elements.submit()
                    console.log('elements submit result', result)

                    // retrieves clientSecret and subscriptionId
                    const { clientSecret, subscriptionId } = 
                    await subscriptionModule.actions.createIncompleteSubscription({
                        plan: this.plan?.value as string,
                        referralId: this.validReferral ? this.validReferral.objectId : '',
                        impactClickId: this.impactClickId,
                    })
                    if (stripe && this.elements) {
                        if (clientSecret && subscriptionId) {
                            console.log('elements', toRaw(this.elements))
                            console.log('clientSecret', clientSecret)
                            console.log('window.location', window.location)
                            const elements = this.elements
                            debugger    // eslint-disable-line
                            const confirmResult = await stripe.confirmPayment({
                                elements: isProxy(elements) ? toRaw(elements) : elements,
                                clientSecret: clientSecret,
                                confirmParams: {
                                    return_url: `${window.location}`,
                                },
                                redirect: 'if_required',
                            })
                            console.log('confirmPayment done', confirmResult)
#

Will let you know when this deployment finishes

#

Ok it's deployed

#

Oh also I removed the address fields

sudden sentinel
#

okay looking

maiden oriole
#

I can at least see that I'm getting into https://js.stripe.com/v3/

sudden sentinel
#

yeah I can see that too. Trying to grasp your code. Is there really no way to unminify it all?

maiden oriole
#

It's going through both Vue 3/vite and TypeScript compiler layers - I'm working on surfacing better "production" sourcemaps but I don't think that will make a difference for walking through the stripe-js portion

#

If there's a piece of the Vue app that you're looking at I can share pieces of the original code

ornate merlinBOT
sudden sentinel
#

yeah I don't understand Vue whatsoever so that won't help :p

#

But I'm baffled right now, I'm going to pair with someone on my team to figure it out

maiden oriole
#

When I'm stepping through the stripe-js file, it eventually just dies and goes nowhere else haha

sudden sentinel
#

yeah and there's no logs either, I've never seen this. I'm trying things locally too without Vue

#

can you try replacing the returnURL

#

I think you gave it an object not the real URL you want window.location.href

maiden oriole
#

Here are some snippets from our code where we mount the PaymentElement

                    const stripe = await loadStripe(import.meta.env.VUE_APP_STRIPE_PUBLISHABLE_KEY)
                    ...
                    this.elements = stripe.elements({
                        mode: 'subscription',
                        amount: this.planAmount,
                        currency: 'usd',
                        appearance,
                    })

                    this.cardEl = this.elements.create('payment', {
                        layout: {
                            type: 'accordion',
                            defaultCollapsed: false,
                            radios: false,
                            spacedAccordionItems: true,
                        },
                        wallets: {
                            applePay: 'never',
                            googlePay: 'never',
                        },
                    })
sudden sentinel
#

Like your code seems to do return_url: ${window.location}

#

ugh with the ` before/after

#

it seems not rendered properly

maiden oriole
#

Yeah the stringified ${window.location} template literal string resolves to window.location.href

sudden sentinel
#

yeah can you try hardcoding the url for a sec though to be safe?

maiden oriole
#

We did do that earlier but it didn't change anything. I'll try it with the stripe url though just to see if that makes a difference

sudden sentinel
#

yeah I was hoping it'd be that but that code works locally for me in vanilla JS too

maiden oriole
#

Yep no difference with the stripe url

sudden sentinel
#

okay I have to run but I just paired with @tacit frost to explain the state and they are going to ping someone else to look at what the heck is blocking that call entirely 😹

maiden oriole
#

Sounds great haha - appreciate the extra eyes!

sudden sentinel
#

can you share the bits where you call the submit() and such

#

wait I get to console.log("confirmPayment done", p) now

maiden oriole
#

??

#

You got that console log?

sudden sentinel
#

hum did you change the code?

maiden oriole
#

No?

sudden sentinel
#

it seems to reach confirmPayment() before even calling the Subscription creation

#

ah wait might be one of my own breakpoints sorry, let me try again slowly

#

so strange I did just get that undefined

maiden oriole
#

Did you actually get "confirmPayment done" in your console?

sudden sentinel
#

yeah with undefined for some reason

maiden oriole
#

Well I haven't even been able to get to that line so that's very interesting

sudden sentinel
#

doesn't happen anymore though so ¯_(ツ)_/¯ maybe some weird timeout

maiden oriole
#

It may also be worth mentioning that we can see in the Stripe dashboard that incomplete subscriptions are being created but never becoming active

#

I...think I'm inside an infinite loop in https://js.stripe.com/v3/...?

tacit frost
#

Hi there. We're still looking into this

#

Will get back to you

stoic carbon
#

Also helping poke at this a bit

#

I see above you suggested picking the free plan to register, that blocked me too

#

but are you aware of the integration error during that step with the missing amount?

#
Uncaught (in promise) IntegrationError: Invalid value for options: amount should be a number. You specified: null.
#

Ill revert to free and try the upgrade, but wanted to mention that

maiden oriole
#

None of the onboarding payment flow has been upgraded (we're migrating from the Card Element)

#

The Payment Element integration we're testing is currently only in the Upgrade Side Panel that opens after you register as a free user

stoic carbon
#

Can you tell us more about this intake/v2/rum/events events logging that seems to be failing?

maiden oriole
#

That's just an elastic logger event

stoic carbon
#

This seems to happen when the confirm in expected, and it breaks

#

I'm suspicious about how this is wired in

#

I see activateFetchInterceptor() {...} where that fails

#

And this seems to be from a package that monkeypatches fetch

maiden oriole
#

I'll turn off the elastic logging and see if that has an impact

stoic carbon
#

And it could be interfering with things

#

Will that disable the fetch override?

#

or if not, do you know where activateFetchInterceptor comes from?

maiden oriole
#

Where are you seeing activateFetchInterceptor? In a console message, or during debugging steps?

#

My guess is it's part of one of our logging tools but I'm disabling that so we can see if that changes

#

Ok just finished deploying the update that removes that elastic logging

stoic carbon
#

Where that rum event throws, the codepoint for that show it

maiden oriole
#

Do you still see that error after refreshing?

stoic carbon
#

No not that one now

#

but still seeing the interceptor in search results:

#

Not sure its implicated, but it smells weird

#

In a previous refresh that same error pointed me to something overwriting window.fetch too, but can't seem to find that now

maiden oriole
#

What file are you in when viewing activateFetchInterceptor? I haven't been able to find that

stoic carbon
#

680f3a2d1c01df074b25.js ?

maiden oriole
#

Ah I found it - that's just a Usersnap integration

#

I can turn that off too but I think very unlikely to be related

stoic carbon
#

https://resources.usersnap.com/widget-assets/js/entries/setup/680f3a2d1c01df074b25.js

#

yea, usersnap

#

can you remove the debugger before confirm too?

maiden oriole
#

Yep

#

Deploying updates

#

Ok updates are live

stoic carbon
maiden oriole
#

Yes - Vue 3 uses Proxies to create reactive component values when properties are stored on a component's state. So in this case the Elements instance is captured in a child component, emitted up to a parent component, and then it's being passed to confirmPayment by accessing it on that parent component's state. By default that value will be a Proxy, but isProxy and toRaw are Vue helper functions to unwrap a Proxy's value to get the original

stoic carbon
#

I see, hmm.

maiden oriole
#

There's also a markRaw Vue method to try to completely opt out of Vue's reactivity for a particular property - I'm trying that to see if it makes a difference

#

Well, that did seem to prevent the this.elements value from being a Proxy, but the page still hangs on the confirmPayment call

#

Would you expect confirmPayment to work even with a stringified+parsed version of the original return from stripe.elements()? Like if someone did JSON.parse(JSON.stringify(elements))?

#

I'm signing off for today, but I appreciate the 👀 and I'll also continue debugging on my end and will update if we come up with any solutions. The closest I seemed to get was when I was F9-ing with the debugger through steps in the https://js.stripe.com/v3/ file, and it seemed like I was going back and forth between a couple different areas of code over and over. If it wasn't an infinite loop, then it was at least an extremely long loop that seemed to have very similar repeated contexts

stoic carbon
#

Still trying to trace through where this actually fails since nothing is throwing :/

stoic carbon
maiden oriole
#

Yeah the lack of an error is what made me suspect some kind of infinite loop.

stoic carbon
maiden oriole
#

I’m wondering if attempting to pass the elements instance around is somehow stripping it of something that stripe.confirmPayments is expecting. If you have any other working instance of confirmPayments being called it might be interesting to try wrapping it in that stringify + parse wrapper and see if it reproduces the same behavior

stoic carbon
#

My best guess is that this is related to the way Vue is interacting with the elements and payment element

maiden oriole
#

We can try making sure that the original elements instance is passed directly to the confirmPayment call and see if that helps, but if that is the problem then it would be great if the handling on the stripe side could recognize that the format is invalid and throw a meaningful error

stoic carbon
maiden oriole
#

Hm honestly that actually makes it look like storing the Elements instance on Vue state and passing it around should work fine

#

We considered using vue-stripe but they haven’t released Vue 3 support yet

stoic carbon
#

Ah, gotcha. I also don't know if what they have supports the deferred intent flow you're trying to do.

ornate merlinBOT
stoic carbon
#

Are you still there @maiden oriole ? Got one idea

maiden oriole
#

Not at my computer but definitely let me know what you’re thinking and I can try it next chance I get

stoic carbon
# maiden oriole Here's the original code up until the confirmPayment call ```ts ...

Referring back to this code ☝️
You don't show where you first created elements which was saved on this.elements and used here.

But here you create a new stripe instance:

const stripe = await loadStripe(import.meta.env.VUE_APP_STRIPE_PUBLISHABLE_KEY)
const result = await this.elements.submit()

Which importantly is not (cannot be, based on this code) the same instance of stripe that created that elements
then when you confirm:

const confirmResult = await stripe.confirmPayment({
elements: isProxy(elements) ? toRaw(elements) : elements,

You're mixing stripe & elements from different instances.
You need to manage a persistent stripe thats paired with that elements

This is what vue-stripe is managing here, for example:

 const stripe = window.Stripe(pk, { stripeAccount, apiVersion, locale });
    stripe.registerAppInfo(STRIPE_PARTNER_DETAILS);
    const elements = stripe.elements(elementsOptions);
    Vue.prototype.$stripe = stripe;
    Vue.prototype.$stripeElements = elements;

That stripe and elements are a matched pair

maiden oriole
#

Hmm interesting - that’s a great thought. From the stripe-JS docs (https://github.com/stripe/stripe-js/blob/master/README.md) I assumed that:
“When you call loadStripe, it will use the existing script tag.”
meant that it would also return the same stripe instance if one had been created previously. But if it’s creating a new instance then that’s a really good observation

GitHub

Loading wrapper for Stripe.js. Contribute to stripe/stripe-js development by creating an account on GitHub.

#

We’ll try unifying those when we get a chance and I’ll let you know how it goes

#

If that does turn out to be the issue, would be great to get an error saying that the stripe instance doesn’t match the elements instance if confirmPayment is called with a mismatch 🙂