#rdhelms-paymentelement-block
1 messages · Page 1 of 1 (latest)
@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?
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.
I don't have a good way to share a reproduction right away, but our flow is using the flow from these steps
https://stripe.com/docs/payments/accept-a-payment-deferred?type=subscription
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
is there any way you can make a repro available so that I can look at the exact code and calls?
I’ll work on creating a close repro
If you have ngrok or equivalent I can likely try too
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:
Or let me know if it works for you 🙂
(trying)
Thanks for taking a look
Have to fill out address info?
Oh sorry yeah just pick the free plan during registration
gotcha, looking
And then use the Upgrade button in the app’s nav bar after onboarding
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
Yeah the confirmCardPayment is for the case where we’re planning to use Apple/Google pay via the payment request button
you know PaymentElement supports wallets too right? but we can get into that later
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
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
I’ll add some console logs for reference and I’ll copy more of the original code here in a minute
I must be looking at the wrong code, none of my breakpoints are being hit
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
perfect
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
okay looking
I can at least see that I'm getting into https://js.stripe.com/v3/
yeah I can see that too. Trying to grasp your code. Is there really no way to unminify it all?
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
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
When I'm stepping through the stripe-js file, it eventually just dies and goes nowhere else haha
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
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',
},
})
(longer snippet)
Like your code seems to do return_url: ${window.location}
ugh with the ` before/after
it seems not rendered properly
Yeah the stringified ${window.location} template literal string resolves to window.location.href
yeah can you try hardcoding the url for a sec though to be safe?
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
yeah I was hoping it'd be that but that code works locally for me in vanilla JS too
Yep no difference with the stripe url
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 😹
Sounds great haha - appreciate the extra eyes!
can you share the bits where you call the submit() and such
wait I get to console.log("confirmPayment done", p) now
hum did you change the code?
No?
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
Did you actually get "confirmPayment done" in your console?
yeah with undefined for some reason
Well I haven't even been able to get to that line so that's very interesting
doesn't happen anymore though so ¯_(ツ)_/¯ maybe some weird timeout
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/...?
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
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
Can you tell us more about this intake/v2/rum/events events logging that seems to be failing?
That's just an elastic logger event
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
I'll turn off the elastic logging and see if that has an impact
And it could be interfering with things
Will that disable the fetch override?
or if not, do you know where activateFetchInterceptor comes from?
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
Do you still see that error after refreshing?
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
What file are you in when viewing activateFetchInterceptor? I haven't been able to find that
680f3a2d1c01df074b25.js ?
Ah I found it - that's just a Usersnap integration
I can turn that off too but I think very unlikely to be related
https://resources.usersnap.com/widget-assets/js/entries/setup/680f3a2d1c01df074b25.js
yea, usersnap
can you remove the debugger before confirm too?
Can you explain the isProxy and toRaw around elements in this source code?
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
I see, hmm.
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
Still trying to trace through where this actually fails since nothing is throwing :/
No, i think that would break things
Yeah the lack of an error is what made me suspect some kind of infinite loop.
Yea i think there's a reduce over a long list of methods, i saw similar
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
My best guess is that this is related to the way Vue is interacting with the elements and payment element
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
We don't have first party support for Vue, but you can compare eg to how vue-stripe manages this: https://github.com/vue-stripe/vue-stripe/blob/main/src/elements/index.js
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
Ah, gotcha. I also don't know if what they have supports the deferred intent flow you're trying to do.
Are you still there @maiden oriole ? Got one idea
Not at my computer but definitely let me know what you’re thinking and I can try it next chance I get
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
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
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 🙂