#nykka
1 messages ยท Page 1 of 1 (latest)
Hello! Do you have a screenshot of the error you can share here?
Interesting. And then they click on the pay button again and it works the second time?
Yes
Can you share the code that's surfacing the "Please fill in your card details" message?
What do you mean with the code that's surfacing the message?
The "Please fill in your card details" is being displayed by your integration, correct? I don't believe that's part of the Payment Element.
I'm using the payment element, I'm pretty sure we are not validating the inputs but I'll check it and come back to you
Yeah, if you could share your Payment Element code, especially the error handling part, that would be helpful.
Here's the code
<form
id="payment-form"
onSubmit={handleSubmit}
aria-labelledby="payment-message"
>
<p className="font-adieu text-xs uppercase">payment method</p>
<PaymentElement
id="payment-element"
onChange={(e) => handlePaymentChange(e)}
/>
{/* Show any error or success messages */}
{message && (
<div
id="payment-message"
aria-live="polite"
className={`${messageColor[messageType]} mb-4 text-center`}
>
{message}
</div>
)}
<Button
type="submit"
className="my-4"
id="button"
isDisabled={isLoading || shouldBeDisabled || !paymentIsComplete}
appearance="primary"
>
{isLoading ? "Processing..." : `Pay $${total}`}
</Button>
<PendingPayment isOpen={isLoading} />
</form>
Here is how we handle errors
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true);
if (!stripe || !elements) {
// Stripe.js has not yet loaded.
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
const { error } = await stripe.confirmSetup({
elements,
confirmParams: {
return_url: `${window.location.origin}/confirm-payment/thank-you`,
},
});
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
setMessage(error.message);
setMessageType("error");
setIsLoading(false);
Bugsnag.notify(JSON.stringify(error));
} else {
setMessage("An unexpected error occurred.");
setMessageType("error");
setIsLoading(false);
Bugsnag.notify(
JSON.stringify("An unexpected error occurred with Stripe."),
);
}
}; ```
Can you also share the handlePaymentChange() function code?
Sure
const handlePaymentChange = (e: StripePaymentElementChangeEvent) => {
setPaymentIsComplete(e.complete);
setIsLoading(false);
}; ```
Are you able to reproduce this issue on demand?
I think so, let me check
I'm trying with the iOS simulator because I don't have an Iphone ๐ฅฒ
If you can reproduce I would be interested to know what the full error object coming from stripe.confirmSetup looks like.
I think I can check the logs on bugsnag
Just to confirm, you don't have multiple Payment Elements on the page or anything like that, right?
Nope, just one
When this happens, in the payment sheet, does it change from "Amount Pending" to show the amount before it dismisses, or does it stay on "Amount Pending" the whole time?
Hm. Can you share the code you're using to initialize Stripe Elements?
Sure!
{checkoutState && checkoutState.data && checkoutState.data.intent_client_secret && ( <Elements options={{ clientSecret: checkoutState.data.intent_client_secret, appearance, }} stripe={stripePromise} > <div> {/* STRIPE ADDRESS ELEMENT */} <AddressForm handleShippingAddress={handleShippingAddress} initialValues={initialValues} isFetchingAddress={isFetchingAddress} submitAddressError={submitAddressError} cats={catsInStoreValues} lineItems={checkoutData.line_items} totalPrice={checkoutData.total_price} checkoutId={checkoutData.id} handleNavigateToPortal={handleNavigateToPortal} /> </div> {/* BILLING STEP */} <div> {/* STRIPE PAYMENT ELEMENT */} <CheckoutForm total={checkoutData.total_price} returnUrl={checkoutData.return_url} initialValues={initialValues} shouldBeDisabled={currentStep === CheckoutStep.Shipping} /> </div> </Elements> )}
The thing that's really throwing me for a loop is that it works the second time. ๐
The fact that it's throwing the "Please fill in your card details" error when Apple Pay is selected is also very strange. That should only come up when Card is selected.
Not sure how to proceed :/
Yeah, I'm looking at your code again for anything I might have missed...
Oh, you didn't find anything useful in BugSnag?
I couldn't find any record but I'm asking a co worker (who owns an Iphone) to reproduce the error so I can check on today's logs
Do you happen to have one of the Setup Intent IDs involved when the error happened?
Let me see if I can get it
Thanks! I'm heading out for now, but there's someone else jumping in who will hopefully see something I missed. ๐
seti_1MwQlHIm407W2EQ5uKvzJPYg
Thank you for the ID. Checking in to that
Also to be clear was this ever working on iOS safari or has it been like this since it has been used?
Very helpful to know. Can you get a diff of this code between your prod and staging environments?
The code should be exactly the same, but let me double check
Can you try logging your client secret before it is confirmed? My colleague has a theory that these may be two different setup intents being confirmed between the two times
I'm adding @primal gale to the thread, he's the backend dev for this project
How can I do that?
I mean, logging the client's secret
btw, I made a diff between main (prod) and develop (staging) and they are exactly the same
Sorry, now I get what you meant
Yes, and it seems to be always the same. Have in mind i'm testing it in my local env with a credit card. I'm thinking about using the ipad to test it with Apple Pay but I need to do some steps before I can do it.
Yeah if it is always the same then that is likely the issue here
Let's back up a second.
It looks like you are creating a Subscription with a trial here, correct?
I'm charging the customer with a trial and then creating a subcription for the next upcoming boxes
Hmm what do you mean exactly by "charging the customer with a trial"?
Like the trial is part of the Subscription, yes?
Based on the SetupIntent you provided above, it looks to me like you create a SetupIntent and then create a trialing Subscription.
Is that correct?
Yes
Okay yeah so somewhere in there is the root cause of your issue (having two SetupIntents). The design with a trialing Subscription is that it returns you a pending_setup_intent to be used to set up the customer's payment method. See: https://stripe.com/docs/api/subscriptions/object#subscription_object-pending_setup_intent
So you don't actually want to be creating a SetupIntent ahead of time here
You want to just create the Subscription and expand the pending_setup_intent
Then you pass that SetupIntent's client_secret to your frontend to render Payment Element
What we do is we change the customer for a trial/sampler for the first time, and after they get that trial/sampler they are automatically subscribed to the service. The trial is not part of the subscription, because it's just a sampler of our products.
Does that make sense?
Hmm so you are just collecting a payment method (and providing a sample to the customer when you do that), then later on you create a Subscription?
Exactly
Why not just create a trialing Subscription when you collect the payment method?
Well, I'm sure the back end dev had their reasons to make it like that
We have been sharing the complete flow with Stripe via email and they agreed it was ok for our business needs.
Gotcha
Well in that case you want to check on the client secret that you use to render Payment Element
Thanks! What should I be checking on?
You want to log out the actual client secret that was used to confirm when you saw the error
Ok, i'll do that but It will take me some time to do it lol
I'll get back to you as soon as I get something
๐
Thank you all!