#bk_paymentelement-googlepay
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/1263179597625954395
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.
- bk_code, 20 hours ago, 36 messages
bk_paymentelement-googlepay
Hello! I'm happy to try and help though I would like to focus on the exact error you get and which part of your code is causing that error
Going to outline the steps for our implementation:
- we setup the payment element + our additional form fields (name, address, payment cadence = subscription or one time payment)
- on submit we 1) do the elements.submit call, 2) create a confirmation token 3) hits our API endpoint 4) then tries to use either the subscription or payment client secret to submit the payment
- our api endpoint creates a customer, then if it is a one time payment, it sets up a paymentIntent and if it is a Subscription, it will create the subscription before returning the client secret to the frontend
Exact error I get is: "Failed to execute 'postMessage' on 'Window': Delegation is not allowed without transient user activation."
And this only happens when trying to use Google Pay
And it happens when we try to confirm the payment:
elements,
clientSecret,
confirmParams: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you',
},
});```
Perfect. So GooglePay and ApplePay are strict as to when you can trigger the UI to appear. And that error usually happens when you try to show the UI/modal without an explicit click from the customer. Often it's because you call confirmPayment() in a callback/promise after a call to your server for example
right. I think I read something like that, but I am not sure how you can use the payment element and also submit your business logic to your API and still utilize google/apple pay
There are many different ways to do this. It depends what you call "submit your business logic" but really that's the issue. You need to ensure that the way you build your flow calls confirmPayment() or createPaymentMethod() as a result of a customer action
So does that include being as a result of other things happening? Right now, I have 1 submit event that does several things in succession:
e.preventDefault();
const {error: submitError} = await elements.submit();
if (submitError) {
console.error(submitError);
return;
}
// Create the ConfirmationToken using the details collected by the Payment Element
// and additional shipping information
const {error, confirmationToken} = await stripe.createConfirmationToken({
elements,
params: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you'
}
});
if (error) {
// This point is only reached if there's an immediate error when
// creating the ConfirmationToken. Show the error to your customer (for example, payment details incomplete)
console.error(error);
return;
}
const { body: data } = await processStripeFinalCall(confirmationToken.id);
console.log('intent');
console.log(data);
console.log(data.data.donate.intent.clientSecret);
const clientSecret = data.data.donate.intent.clientSecret;
const { error: confirmError } = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you',
},
});
}```
That processStripeFinalCall is where we hit our API which setups or gets the Stripe Customer, creates the payment intent or subscription and returns the client secret
Wait you're using createConfirmationToken()? So that is the function throwing the error or something else? I'm confused now
I believe it is the confirmPayment because all my logs show before the error
yeah that doesn't make sense to me that it would be since createConfirmationToken() is what controls showing the Google Pay UI.
Like you agree that when you call that method you get the GooglePay UI right?
Yea. On submit is when it pops up and you can choose your payment method and such.
And then it errors after you try to submit the payment
Can you share your code where you create the PaymentIntent?
Here's a preview link if that is helpful: https://seedcompanydev.wpengine.com/test/
Yea, but we are creating that on the server
sure can you share the exact code please?
customerId,
confirmationToken,
paymentMethodId,
savePaymentMethod,
amount,
metadata,
statementDescriptor,
}: {
customerId: string;
paymentMethodId?: string;
savePaymentMethod?: boolean;
} & CamelCasedProperties<
Pick<
Stripe.PaymentIntentCreateParams,
'confirmation_token' | 'amount' | 'metadata' | 'statement_descriptor'
>
>) {
// Stripe makes 75 = $0.75 so multiplying the whole number by 100 gets us the whole $ amount
const stripeAmount = amount * 100;
return await this.stripe.paymentIntents.create({
customer: customerId,
payment_method: paymentMethodId,
confirm: !confirmationToken,
setup_future_usage: savePaymentMethod ? 'off_session' : undefined,
// this setting allows for the payment methods in the dashboard to be used by default
automatic_payment_methods: { enabled: true, allow_redirects: 'always' },
amount: stripeAmount,
currency: 'usd',
metadata,
statement_descriptor: statementDescriptor,
});
}
payment_method: paymentMethodId, what is this? That doesn't exist at all in this specific flow right?
You also have confirm: !confirmationToken, which seems... abnormal to me so I don't get it. This smells like the result of many different rewrites
paymentMethodId essentially only gets used if there is a saved payment (which, my team lead wanted to keep in the code even though we don't use saved payments anywhere)
it would help to temporarily start fresh while you're debugging things so that you're not bogged down by months of rewrites/other features. It's hard to debug the issue with so much unrelated code
and I think there is some logic where if there is a confirmationToken passed, we confirm so it auto confirms.
I'm still really confused why confirmPayment() would show the GooglePay UI again it doesn't make sense to me
I don't think it shows it again, but I see all the console logs that happen before that point and then an error
I actually don't have a good handle on where the error actually is.
yeah that picture doesn't really tell me anything
yea. That's all I have to go off of currently.
I mean you can add clear logs before and after every part of the code to pinpoint which exact method throws that error
and try what I said above: comment out the code to your server and hardcode a client_secret
Like just any string or is there somewhere I can grab a client secret outside of this flow?
Oh I got one from the UI. Nevermind. Let me try it
ok. EVen with the client secret hard codes and my server call out of there I still get the same error
Okay can you share an exact URL with this and exact logs before and after every call so I can have a look?
It's live here. https://seedcompanydev.wpengine.com/test/
e.preventDefault();
const {error: submitError} = await elements.submit();
if (submitError) {
console.error(submitError);
return;
}
// Create the ConfirmationToken using the details collected by the Payment Element
// and additional shipping information
const {error, confirmationToken} = await stripe.createConfirmationToken({
elements,
params: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you'
}
});
if (error) {
// This point is only reached if there's an immediate error when
// creating the ConfirmationToken. Show the error to your customer (for example, payment details incomplete)
console.error(error);
return;
}
// const { body: data } = await processStripeFinalCall(confirmationToken.id);
// console.log('intent');
// console.log(data);
// console.log(data.data.donate.intent.clientSecret);
const clientSecret = 'pi_3PdaeODXchwDHdiC1d0jzCFK_secret_KgXG4GrQv7oX2FCAD8Z7TyvJT';
const { error: confirmError } = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you',
},
});
}```
That's what runs on submit right now after removing the API call and setting up the client secret as static
ah yeah I'm dumb I can't test it since it's GooglePay and that will give you my real address
Unfortunately you didn't seem to add any of the logs I mentioned. Can you please cleanly add the logs before and after every call?
e.preventDefault();
console.log('before element submit')
const {error: submitError} = await elements.submit();
if (submitError) {
console.error(submitError);
return;
}
console.log('after element submit')
// Create the ConfirmationToken using the details collected by the Payment Element
// and additional shipping information
console.log('before confirmation token creation')
const {error, confirmationToken} = await stripe.createConfirmationToken({
elements,
params: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you'
}
});
if (error) {
// This point is only reached if there's an immediate error when
// creating the ConfirmationToken. Show the error to your customer (for example, payment details incomplete)
console.error(error);
return;
}
console.log('after confirmation token creation')
// const { body: data } = await processStripeFinalCall(confirmationToken.id);
// console.log('intent');
// console.log(data);
// console.log(data.data.donate.intent.clientSecret);
const clientSecret = 'pi_3PdaeODXchwDHdiC1d0jzCFK_secret_KgXG4GrQv7oX2FCAD8Z7TyvJT';
console.log('before confirm payment')
const { error: confirmError } = await stripe.confirmPayment({
elements,
clientSecret,
confirmParams: {
return_url: 'https://seedcompanydev.wpengine.com/thank-you',
},
});
console.log('after confirm payment')
}```
okay so what happened between the ConfirmationToken related logs. Did you see GooglePay?
Yes. I have seen and submitted google pay from the beginning.
then
then when I hit pay it errors
And even with my API call in the code, it still has shown this part.
(sorry Discord is busy so catching up on other threads)
Yes. I have seen and submitted google pay from the beginning.
what does that sentence mean? I'm sorry I'm not sitting next to you watching you. I need to understand exactly which bit of your code shows that GooglePay UI
The 2nd screenshot above has been showing whether or not I have had my API call or not and then once I hit "pay" it will error
I don't get what that sentence means ๐
you're saying elements.submit() is what shows the GooglePay UI?
No. I have the one submit functions that I have shared that runs on submit. If Google Pay is the selected option, then it will show the Google Pay UI where I can hit "pay"
๐ I'm asking what shows that UI. Can you please tell me which exact line of your current code shows the GooglePay modal?
I have no idea which part actually spins up the Google Pay piece
Can you try and debug please? That would really help as I'm worried we're talking a bit past each other right now
Comment all the code but the elements.submit() and its logs and confirm you get the modal to appear
Yea. Looks like it is the elements.submit that makes the google pay ui pop up
perfect, so now uncommon the ConfirmationToken part, but keep the rest commented. Can you confirm you properly get the ConfirmationToken created?
Yup. Seems like it all worked just fine
Okay, so it really looks like the confirmPayment() part is the issue here I just don't get why. I'm trying to reproduce but will take me some time as I'm helping 5 other people in parallel
Okay I think I got it. My code is completely different from yours. And if I pick your approach I get a similar error ๐คฆโโ๏ธ
What I do is
- Confirm server-side by passing the ConfirmationToken id
- Client-side, if anything extra is needed such as 3DS (won't happen for GooglePay I think) I use
stripe.handleNextAction()instead
Now let me try to tweak your code to make it work instead
I think this will require me to update my API code a bit. I had it architected this way at one point and my team lead didn't like us having to check for next actions on the frontend vs having the server submit whatever it could and return the next actions
Okay I got it
it's so obvious now but I realize I had never integrated that way so I didn't notice your "bug"
when you call confirmPayment() with elements in your code, we don't "remember" that a ConfirmationToken was created, so to us it looks like you want to just confirm again
Yea. I think he and I made an assumption that if we did it this way, the next actions would just handle themselves.
What you are supposed to do is explicitly give us the ConfirmationToken id and not pass elements
clientSecret: serverResult.client_secret,
confirmParams: {
return_url: 'https://google.com',
confirmation_token: confirmationToken.id,
},
redirect: 'if_required',
});```
this is what my code looks like. So for you you need to do this const { error: confirmError } = await stripe.confirmPayment({ clientSecret, confirmParams: { return_url: 'https://seedcompanydev.wpengine.com/thank-you', confirmation_token: confirmationToken.id, }, });
It's what is documented here: https://docs.stripe.com/payments/build-a-two-step-confirmation#submit-the-payment
So wait... I will or will not need to return the next_actions piece from my API call?
๐ stepping in as koopajah needs to step away
You are attempting to confirm server-side with a Confirmation Token and then figuring out how to handle 3DS if required, correct?
Sorry I made this quite messy so it's not that at all no
Our desired flow was that we would return an unconfirmed payment intent to the client and confirm on the client.
That's how we built the entire solution anyway.
Client-side they create a ConfirmationToken. Then they go to their server and do stuff. After that they create a PaymentIntent and they do not confirm it at all.
Then they come back client-side and they were calling confirmPayment() incorrectly. They were passing the elements when they were supposed to pass the ConfirmationToken id.
The only fix needed is what I showed above in the code example
Cool. let me update and check that out
YAY! That got me taken care of. Thanks @lavish gazelle !!!
I'm so sorry I should have caught it at the beginning. all my examples confirm server-side and I kept going to the wrong doc that did this instead of finding that last one, that's my bad