#warren_error

1 messages ยท Page 1 of 1 (latest)

limber muralBOT
#

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

rare parrot
#

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.

willow junco
rare parrot
#

Yeah, that one was over a month ago. Let's focus on your specific issue rather than look at older threads.

rare parrot
#

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?

willow junco
#

we don't have something like that

rare parrot
#

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.

willow junco
#

/sigh

rare parrot
#

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.

willow junco
#

it isn't browser related nor extension related. So both of those are unfortunately off the table

rare parrot
#

Can you share more details? Knowing how you're confident it's not browser or extension related might help me help you more.

willow junco
#

for one user loading it in firefox and in brave on their linux machine both of them run into the same issue

rare parrot
#

And you've confirmed they don't share any extensions in common?

willow junco
#

for sure

rare parrot
#

You mentioned you haven't changed anything on your end; does that include not updating anything? No patches, upgrades, anything like that?

willow junco
#

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?

rare parrot
#

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.

willow junco
rare parrot
#

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.

willow junco
#

at the bottom of the page there is a button to add a payment method (click that)

rare parrot
#

Ah, I see the error now, thanks! Looking, give me a few minutes...

willow junco
#

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.

rare parrot
#

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.

willow junco
#

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.

rare parrot
#

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...

willow junco
#

(I'm cutting out some of the known shadom DOM usage at the moment, and trying to see if there is anything left after)

rare parrot
#

(Sorry, wrong thread.)

rare parrot
#

Oh, the page stopped working. ๐Ÿ˜…

#

I was still trying to poke around, but it looks like you changed something on your end?

willow junco
#

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

rare parrot
#

Ah, it just started working again...

#

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.

willow junco
#

I've almost stripped everything, and I'm still getting the problem

rare parrot
willow junco
#

that page for sure loads without an issue

#

fantastic

rare parrot
#

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.

rare parrot
#

Any luck?

willow junco
#

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>


rare parrot
#

๐Ÿ‘€

#

What library are you using?

willow junco
#

well that's it, this.stripeElement.mount('#mountingElement'); works but creating an element and mounting on that doesn't

rare parrot
#

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?

willow junco
#

that's it

rare parrot
#

That fixed it?

willow junco
#

well "fixed is a weird thing to say" because I think that is the problem

rare parrot
#

Does that change make the issue go away on your full site?

willow junco
#

cardElement.unmount(); cardElement.mount(document.createElement('div'));

#

if you run that it shows the same error

rare parrot
#

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.

willow junco
#

sure, maybe, but this worked until, idk, recently

rare parrot
#

Do you know how recently?

willow junco
#

well it is still working on some of our users machines, so I'm guessing really recently,

rare parrot
#

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.

willow junco
#

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.

rare parrot
#

Could be either of those, yeah... ๐Ÿค”

willow junco
#

it would be nice if the error message could be changed on the stripe side

rare parrot
#

One sec...

willow junco
#

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.

rare parrot
#

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.

willow junco
#

I would definitely buy "the last month" to be an accurate timeline

rare parrot
#

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.

willow junco
#

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.

rare parrot
#

I'm a little confused, to be honest. Card Sources still work just fine. Are you taking non-Card payments?

willow junco
#

no

rare parrot
#

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 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

willow junco
rare parrot
#

Yeah, that's deprecated, but it still works for card payments.

#

You should migrate to something modern, but createSource for card payments works.

willow junco
#

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

rare parrot
#

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.

willow junco
#

yes

rare parrot
#

You can use a standalone Setup Intent without a Subscription or a Customer though.

#

Then attach the resulting Payment Method to a Customer later.

willow junco
#

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

rare parrot
#

Yeah, you can do that.

willow junco
#

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

rare parrot
#

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:

  1. Create a Setup Intent
  2. Pass the Setup Intent's client secret to your frontend
  3. Use the Payment Element to collect payment details and confirm the Setup Intent using confirmSetup
  4. Send the resulting Payment Method ID to your server
  5. Create the Customer, attach the Payment Method, set it as the default for Invoices, create the Subscription, etc.
willow junco
#

I see, so we have to start the flow on the server, where we aren't doing that today

rare parrot
#

Oh, I see...

#

You want to avoid the first server part all together.

willow junco
#

well it isn't necessarily that we want to avoid it, just that, it's completely unnecessary to force us to do that

rare parrot
limber muralBOT
rare parrot
willow junco
#

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?

rare parrot
#

Generally creating the Setup Intent first and then confirming it client-side with the client secret is the most recommended approach.

willow junco
#

can I ask, why is that? Or maybe, is there a page somewhere that details why that is best?

rare parrot
#

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.

willow junco
#

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.

rare parrot
#

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!

hearty kite
#

Hi @willow junco I'm taking over this thread, let me know if you have any follow-up questions.

willow junco
#

will do, Thank you @rare parrot