#warren_error
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/1293322587257962527
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
Hello! Can you link me to the page where this is happening?
Also, I'm not seeing anything from anyone else on this server about shadow DOM issues recently. The most recent one, besides this thread, was nearly a month ago.
(someone elses example: #1283232625762242685 message)
Yeah, that one was over a month ago. Let's focus on your specific issue rather than look at older threads.
sure, here's the page: https://authress.io/app/#/settings?focus=billing&tab=plans
We have another product with the same problem, over here: https://standup-and-prosper.com/app/#/settings
That page requires a login. Can you provide a direct link to a page that doesn't require logging in please, like a test case/test page?
we don't have something like that
Can you build a minimal test case quickly?
This kind of error is incredibly difficult to debug if I can't actually see the page.
/sigh
Sorry, I don't know what to tell you. You can't call a mechanic and have them tell you what's wrong with your car by holding your phone up to the engine. You need to bring that thing into the shop, you know? ๐
The only thing I can do with what you gave me so far is guess, and I don't really like to guess when it comes to stuff like this.
I'll give it a try though: given that it only impacts some users, my leading theory is a browser extension that only some of them have installed which is altering the page in a way that injects shadow DOM in a way the Card Element doesn't like.
Now, my confidence level on that guess is quite low, but it's all I've got for now without actually being able to see the page.
it isn't browser related nor extension related. So both of those are unfortunately off the table
Can you share more details? Knowing how you're confident it's not browser or extension related might help me help you more.
for one user loading it in firefox and in brave on their linux machine both of them run into the same issue
And you've confirmed they don't share any extensions in common?
for sure
You mentioned you haven't changed anything on your end; does that include not updating anything? No patches, upgrades, anything like that?
well don't know for sure what users have done. It's certainly possible there are new browser updates that interact poorly with the Stripe script that is loaded remotely from stirpe.com. We don't have any way of controlling what's in that script, so right now that is the likely source of the problem. Is there a way we can see all the changes that the stripe script has gone through?
No, we don't publish anything like that, but I can say that you are literally the only person I'm aware of having shadow DOM issues at the moment.
fantastic, I have a working repo, you can jump to this page that doesn't include a login any more: https://standup-and-prosper.com/app/?bypass#/settings
If it was an issue with Stripe.js in general there would be tons of users with issues, we'd have an incident going internally, etc.
at the bottom of the page there is a button to add a payment method (click that)
Ah, I see the error now, thanks! Looking, give me a few minutes...
It would be one thing if we could easily see a shadow dom here somewhere, we do have experience with it. But there isn't anything in our app that would be wrapping the stripe elements with it. What's weirder is that the payment element works (although it's possible since that one works differently, that the security is different).
I can reproduce this on my machine, and I know someone who is on windows who can't repo it (edge, firefix, chrome all work). I can't think of anything that would be OS relevant here, for such a native browser feature, so it might just be a red-herring that it works on some of our users.
It's hard to debug the minified code, but it looks like your code is creating a <div> in the shadow DOM and then trying to mount the Card Element to that. It seems like there's some kind of fallback after that error where it then gets mounted in the light DOM.
You do, for sure, have code on this page that's messing around with the shadow DOM. This line, for example:
r.call(this, (c.functional ? this.parent : this).$root.$options.shadowRoot)
That's in the same JS file where you're mounting the Card Element. There are also other numerous references to the shadow DOM in your vendors JS. I think it's likely one of your libraries or frameworks is doing shadow DOM shenanigans without your knowledge.
there are definitely shadow DOM references in the vendors file because there are components in the website that use it, but there shouldn't be any of them on this page. We can start pulling things out, but I'm skeptical that one of them will magically turn up as the problem.
If you look at the elements of the page as it is, there isn't any shadow DOM at that moment. There's nothing special happening, when the Add payment button is clicked,, so I'm at a loss what is happening at that moment.
I think the shadow DOM it's initially trying to mount to is being destroyed after the attempt fails.
I tried to set a breakpoint to catch it, but the dev tools wouldn't let me due to an error: Could not load content for webpack:///js/settings-legacy.3661cf59.js (Fetch through target failed: Unsupported URL scheme; Fallback: HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME)
Oh, let me try one other thing...
(I'm cutting out some of the known shadom DOM usage at the moment, and trying to see if there is anything left after)
(Sorry, wrong thread.)
Oh, the page stopped working. ๐
I was still trying to poke around, but it looks like you changed something on your end?
we just deployed an inline source map, so that you colud better debug
page is should still be working, but I do see a weird error
Ah, it just started working again...
Huh.
OK takes me to a Slack login. Cancel is a page that spins forever.
Ah, I'm trying to do a local override, and it doesn't like that.
Well, I'm not sure what to do if I can't do a local override, to be honest.
I think your best bet is to either start from a super-basic Card Element integration, then add in your libraries and frameworks and whatnot until you find the one that's causing the issue. Or do it the other way around and start stripping stuff out until you find the culprit.
I've almost stripped everything, and I'm still getting the problem
You can't reproduce the issue on this page, right? https://4242.io/test/card-element/
It almost feels like there's some kind of race condition going on, where there's shadow DOM somewhere only briefly, and it's happening at roughly the same time as the Card Element mount.
Any luck?
I have no code left and it still doesn't work, The library we were using and now inline we are doing this:
<template>
<div ref="mountingElement" />
</template>
<script>
export default {
name: 'StripeElement',
props: {
stripeKey: {
type: String,
required: true
},
currency: {
type: [String, undefined],
default: () => 'usd'
}
},
data() {
return {
stripeElement: undefined,
elements: null,
instance: null
};
},
computed: {
},
watch: {
},
beforeDestroy() {
if (this.stripeElement) {
this.stripeElement.unmount();
this.stripeElement.destroy();
}
},
created() {
this.createElements();
},
methods: {
async createElements() {
if (this.elements) {
return;
}
this.instance = window.Stripe(this.stripeKey, {});
this.elements = await this.instance.elements({
mode: 'subscription',
currency: this.currency?.toLowerCase() || 'usd',
amount: 0,
loader: 'always',
setupFutureUsage: 'off_session',
paymentMethodTypes: ['card']
});
this.stripeElement = await this.elements.create('card', { fields: { billingDetails: { name: 'never', email: 'never' } }, business: { name: 'Authress' } });
const domEl = document.createElement('div');
this.stripeElement.mount(domEl);
this.$refs.mountingElement.appendChild(domEl);
// See stripe element events: https://stripe.com/docs/js/element/events
const eventTypes = ['change', 'ready', 'focus', 'blur', 'escape'];
eventTypes.forEach(type => {
this.stripeElement.on(type, this.eventHandler.bind(this, type));
});
},
async submit({ email, name }) {
const result = await this.instance.confirmPayment({
elements: this.elements,
confirmParams: {
return_url: 'https://authress.io/app/#/settings?focus=billing&linkBillingAccount=true',
payment_method_data: {
billing_details: {
email,
name
}
}
},
redirect: 'if_required'
});
return result;
},
eventHandler(type, e) {
return this.$emit(type, e);
}
}
};
</script>
well that's it, this.stripeElement.mount('#mountingElement'); works but creating an element and mounting on that doesn't
If you swap these two lines does it make any difference?
this.stripeElement.mount(domEl);
this.$refs.mountingElement.appendChild(domEl);
Like if you append first, then mount?
that's it
That fixed it?
well "fixed is a weird thing to say" because I think that is the problem
Does that change make the issue go away on your full site?
well it's in a library, so we'll need to put something together to fix this, you can repro this here: https://4242.io/test/card-element/
cardElement.unmount(); cardElement.mount(document.createElement('div'));
if you run that it shows the same error
I wish I had caught that earlier. It makes sense; you wouldn't want to mount to an element that's not in the DOM tree yet.
sure, maybe, but this worked until, idk, recently
Do you know how recently?
well it is still working on some of our users machines, so I'm guessing really recently,
I mean, the library you're using is almost a year past end of life, so it seems expected that there would be breakage at this point.
nah
if anything you can blame that createSource is deprecated
the library is like 10 lines of code, and this hasn't changed.
I'm guessing either the logic on Shadow DOM validation changed on the stripe side OR the os/browsers are starting to do something different for mounting on elements that aren't yet in the DOM.
Could be either of those, yeah... ๐ค
it would be nice if the error message could be changed on the stripe side
One sec...
however it would be even better if there was a clear conversion guide from using createSource to using setupIntent for offline sessions for subscruptions that are using metered billing, because it isn't clear how to do this without having an invoice for a real payment amount created for our customers.
We did change our code related to Shadow DOM detection on September 10th. Let me flag this internally.
Does September 10th sound right?
It looks like we then reverted the change, but added more logging... I think that's why the error is happening but the Card Element works anyway.
I would definitely buy "the last month" to be an accurate timeline
Okay, flagged internally. Looks like there's an existing investigation into this general issue, if not this specific instance of it.
Regardless, though, I would recommend mounting the Card Element only after the element you're mounting it to has been appended to the page.
That's going to be way less fragile in general.
Let's just say this library is doing more than one thing that doesn't make sense here. But realistically until we can understand how to migrate off the createSource flow, I think we are likely going to keep running into potentially legacy/likely-to-break functionality.
I'm a little confused, to be honest. Card Sources still work just fine. Are you taking non-Card payments?
no
however it would be even better if there was a clear conversion guide from using
createSourceto using setupIntent for offline sessions for subscruptions that are using metered billing, because it isn't clear how to do this without having an invoice for a real payment amount created for our customers.
We don't have a guide for that specific migration, but the high-level version is that when you create the Subscription it should have a pending_setup_intent on it which you can use to collect payment details: https://docs.stripe.com/api/subscriptions/object#subscription_object-pending_setup_intent
Yeah, that's deprecated, but it still works for card payments.
You should migrate to something modern, but createSource for card payments works.
See the warning at the top of this page: https://docs.stripe.com/sources
we should absolutely do that, the problem is that the configuration options for elements require things like amounts and also looks like it requires an existing customer
They don't require any of those. You can use those, but there are flows where you don't need to.
Although you do need a Customer object to create a Subscription.
yes
You can use a standalone Setup Intent without a Subscription or a Customer though.
Then attach the resulting Payment Method to a Customer later.
but right now we are creating the source in the UI before a customer exists, and then sending the source to our api to handling everything offline
Yeah, you can do that.
You can use a standalone Setup Intent without a Subscription or a Customer though.
hmmm, is there a guide on that
I feel like we do something that stripe does not optimize their documentation to explain
You're correct, we don't have specific documentation for this flow. Our documentation is focused on the recommended/widely-applicable use cases. What you're doing is neither of those, but it is possible.
Essentially you would do this:
- Create a Setup Intent
- Pass the Setup Intent's client secret to your frontend
- Use the Payment Element to collect payment details and confirm the Setup Intent using
confirmSetup - Send the resulting Payment Method ID to your server
- Create the Customer, attach the Payment Method, set it as the default for Invoices, create the Subscription, etc.
I see, so we have to start the flow on the server, where we aren't doing that today
well it isn't necessarily that we want to avoid it, just that, it's completely unnecessary to force us to do that
You can do that by initializing Elements without an Intent in setup mode: https://docs.stripe.com/js/elements_object/create_without_intent
Then you can create a Confirmation Token: https://docs.stripe.com/js/confirmation_tokens/create_confirmation_token
Then you can essentially follow this guide: https://docs.stripe.com/payments/finalize-payments-on-the-server?platform=web&type=setup
So that's two different ways to do "what we are doing today", a third of course is if we managed to completely upend of the flow and invert it to server-first. Is there a recommendation on "stripe-blessed" approach here?
Generally creating the Setup Intent first and then confirming it client-side with the client secret is the most recommended approach.
can I ask, why is that? Or maybe, is there a page somewhere that details why that is best?
I'm not sure we have a page that explains why, exactly. There are a lot of reasons, some of which may or may not apply to your specific situation. It sounds like you're really resistant to doing anything on your server up front, so I recommend you explore the Confirmation Token approach instead.
oh for the record it is a much weaker opinion than that, the perspective if it is valuable, is a resounding why. I'm going to get challenged if I suggest we change it, and I just want to be prepared with some arguments. We for sure can take a look at the confirmation token. But it's good to know that at least the recommendation is to have our server be the one to start the flow. I totally get why it might benefit stripe to do that (can cut down on potential abuse of stripe's endpoints), but it isn't necessarily clear the benefits for us.
Thank you for providing this information, you have been a delight in helping resolve this problem. Thank you so much.
Yeah, if it's going to be a hard sell for you, skip the headache and go with the Confirmation Token approach!
And I'm glad we found the core issue and a workaround/solution for the Stripe.js error!
Hi @willow junco I'm taking over this thread, let me know if you have any follow-up questions.
will do, Thank you @rare parrot