#paul_ece-react
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/1227304487656488980
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
Hey!
Here's how I do it right now
`
const stripePromise = loadStripe(
process.env.NODE_ENV === 'development'
? process.env.NEXT_PUBLIC_TEST_STRIPE_PK_API_KEY
: process.env.NEXT_PUBLIC_PRODUCTION_STRIPE_PK_API_KEY
);
const expressCheckoutOptions = {
layout: {
overflow: "never"
},
buttonHeight: 50,
paymentMethodOrder: ["google_pay", "apple_pay", "paypal"]
};
const options = {
mode: 'subscription',
amount: totalPrice * 100,
currency,
appearance: {
variables: {
borderRadius: '36px',
}
},
setupFutureUsage: "off_session",
};
return (
<Elements stripe={stripePromise} options={options}>
<ElementsConsumer>
{({ stripe }) => {
if (!stripe) {
return <Spinner />;
};
return (
const stripe = useStripe();
const elements = useElements();
const onReady = ({ availablePaymentMethods }) => {
console.log({ availablePaymentMethods });
if (!availablePaymentMethods) {
setShowLoading(true)
} else {
setShowLoading(false);
};
};
<ExpressCheckoutElement
onConfirm={onConfirm}
options={expressCheckoutOptions}
onReady={onReady}
/>
.....`
console.log({ availablePaymentMethods }); logs undefined
paul_ece-react
I think the first step is to load the page and read the console to see what is going on
If you have a live page I'm happy to have a look but hard to say right now without more details bout what the problem is
did you properly register the domain in your Stripe account?
I don't really understand const onReady = ({ availablePaymentMethods }) => { and why you are doing that
if the ECE hasn't loaded, I am displaying a loading spinner
can you write expressCheckoutElement.on('ready', async (event) => { console.log("on ready!!!: ", JSON.stringify(event)); event.resolve(); }); instead? or similar
I destructure it because I believe I saw it in the docs
I assume you are ending up in a world where you have neither GooglePay nor ApplePay nor Paypal working so nothing shows
yes
But for some reason it did show 30 minutes ago
And it's confusing
(although only google pay)
yeah it'd help to drastically simplify your demo to have only ECE and nothing else
right now it's really confusing, it dumps card brands in a weird way, has a full page CardElement, tough to debug
I've looked at the git history and I didn't do anything to the ECE
@real basalt is taking over for me and is going to help you investigate further but if you coul push a really simple demo that only does ECE and nothing else, that will be helpful
Alright, sure
๐
Hey! You can see the ECE-only here: https://didactic-space-couscous-49gx79prv7w35g5q-3000.app.github.dev/payment
Taking a look...
(or not see.. cause that's the problem ๐ )
Apple Pay shows up there for me in Safari...
Not seeing Google Pay in Chrome though.
Hm, looks like something might be broken more broadly, hang on...
Sure
If it helps, this is a minimal version of the code:`
import { loadStripe } from '@stripe/stripe-js';
import { useStripe, useElements, Elements, ElementsConsumer, ExpressCheckoutElement, CardElement } from '@stripe/react-stripe-js';
const stripePromise = loadStripe(
process.env.NODE_ENV === 'development'
? process.env.NEXT_PUBLIC_TEST_STRIPE_PK_API_KEY
: process.env.NEXT_PUBLIC_PRODUCTION_STRIPE_PK_API_KEY
);
const expressCheckoutOptions = {
layout: {
overflow: "never"
},
buttonHeight: 50,
paymentMethodOrder: ["google_pay", "apple_pay", "paypal"]
};
const ExpressCheckout = ({
props
}) => {
const stripe = useStripe();
const elements = useElements();
const [showLoading, setShowLoading] = useState(true);
const [errorMessage, setErrorMessage] = useState(null);
const onReady = ({ availablePaymentMethods }) => {
console.log({ availablePaymentMethods });
if (!availablePaymentMethods) {
setShowLoading(true)
} else {
setShowLoading(false);
};
};
const onConfirm = async ({ paymentMethod }) => {
};
`
` return (
<div className='w-full flex justify-center'>
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.5 }}
className='px-5'>
{
showLoading && <Spinner
color="primary"
labelColor="primary"
size="lg"
className="h-[10vh] w-full flex justify-center items-center font-serif text-2xl mt-5"
>Loading Express Checkout...</Spinner>
}
<ExpressCheckoutElement
onConfirm={onConfirm}
options={expressCheckoutOptions}
onReady={onReady}
/>
{
errorMessage && <ErrorMessage message={errorMessage} />
}
</motion.div>
</div>
);
};
export default function StripeInlineCheckout({
props
}) {
const options = {
mode: 'subscription',
amount: totalPrice * 100,
currency,
appearance: {
variables: {
borderRadius: '36px',
}
},
setupFutureUsage: "off_session",
};
const props={}
return (
<Elements stripe={stripePromise} options={options}>
<ElementsConsumer>
{({ stripe }) => {
if (!stripe) {
return <Spinner />;
};
return (
<>
<ExpressCheckout
{...props}
/>
</>
);
}}
</ElementsConsumer>
</Elements>
);
};`
We think this is an issue on our end, actually. Google Pay also isn't showing up in my test reference integration. We're investigating now.
Got it, thanks!
In the meantime
I have one more question
How can I read the GPay/Apple pay user details in my webhook?
Which details?
like address and name
Those details will be on the Payment Method that's created. You can listen for payment_intent.succeeded and then look at the Payment Method attached.
Regarding Google Pay, we've confirmed the issue is not isolated to you. We're not sure exactly what the problem is, but it's intermittent, and we think it might be due to a gradual rollout. I recommend you ignore Google Pay for now and come back to that piece tomorrow if possible.
I will say that your implementation appears to be fine; if Apple Pay is working Google Pay should also work fine once this underlying issue is sorted out.
What do you mean by "only want to look at invoicing"?
So I just grab the pi Id, and then i assume if I fetch the pi I will get the user data?
only invoicing events
like invoice.payment_succeeded
Invoices create Payment Intents, so Payment Intents can be "invoicing events".
ah okay
Also, you should use invoice.paid, not invoice.payment_succeeded.
What is the difference?
Have a look at this short video, I think it will help you understand how the objects are related to each other: https://stripe.com/docs/payments/tour#payment-objects
invoice.paid will fire whenever an Invoice is paid. invoice.payment_succeeded will only fire when an Invoice is paid via Stripe. So, if you mark an Invoice as paid out of band, and you're only listening for invoice.payment_succeeded, you won't get an Event.
Could you please explain what do you mean by 'paid out of band'
Yeah. Let's say you have a Stripe Invoice for $100, and your customer hands you $100 in cash to pay it. That $100 they physically handed you isn't going through Stripe, but you need to mark the Invoice in Stripe as paid. You can do that by marking it paid out of band: https://docs.stripe.com/api/invoices/pay#pay_invoice-paid_out_of_band
Ah, I see
that would never be the same for me
Given that, is there any difference between the two events?
case for me**
invoice.paid is the newer, generally recommended one. There aren't other differences at the moment, but if there are future differences you'll likely be better off with invoice.paid. It's also eaiser to type. ๐
true true
okay, Ill instead just listen to that
I assume all the event payload is the same, right?
Yeah, they both contain an Invoice.
Okay, got it. So I just grab the payment intent it, fetch the payment intent, and there I find the apple pay user details?
id*
Fetch the Payment Intent, then look at the Payment Method, but yep.
sorry for aksing many questions, but I cant try it out right now (cause GPay doesnt work), but I assume the customer details are in metadata, and the used payment method is payment_method (although is null in the sample response)? where the payload is { "id": "pi_3MtwBwLkdIwHu7ix28a3tqPa", "object": "payment_intent", "amount": 2000, "amount_capturable": 0, "amount_details": { "tip": {} }, "amount_received": 0, "application": null, "application_fee_amount": null, "automatic_payment_methods": { "enabled": true }, "canceled_at": null, "cancellation_reason": null, "capture_method": "automatic", "client_secret": "pi_3MtwBwLkdIwHu7ix28a3tqPa_secret_YrKJUKribcBjcG8HVhfZluoGH", "confirmation_method": "automatic", "created": 1680800504, "currency": "usd", "customer": null, "description": null, "invoice": null, "last_payment_error": null, "latest_charge": null, "livemode": false, "metadata": {}, "next_action": null, "on_behalf_of": null, "payment_method": null, "payment_method_options": { "card": { "installments": null, "mandate_options": null, "network": null, "request_three_d_secure": "automatic" }, "link": { "persistent_token": null } }, "payment_method_types": [ "card", "link" ], "processing": null, "receipt_email": null, "review": null, "setup_future_usage": null, "shipping": null, "source": null, "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "requires_payment_method", "transfer_data": null, "transfer_group": null }
No, metadata is something you set, Stripe doesn't set or use those values: https://docs.stripe.com/api/metadata
Okay, was wondering that
That Payment Intent hasn't succeeded yet, so the payment hasn't been made.
Thus there's no Payment Method.
Note the status of requires_payment_method.
I see
But I found this under Retrieve a PaymentIntent in the docs
I assumed it was a successvul one
Yeah, the Payment Intent exists, but it hasn't been confirmed yet.
The Payment Intent in its current form indicates an intent to pay, but that payment hasn't been carried out yet.
Where can I find a sample response of the one I would receive in case the invoice was paid?
Easiest way is to make a payment in test mode. Can you use Safari to make an Apple Pay payment?
Im on Windows with a crowded Disk ๐
Thanks!
Yeah, no real movement yet, it'll probably be a while unfortunately.
I wouldn't normally do this, but since this is an unusual circumstance with Google Pay being down, I can make a test Apple Pay payment if you point me to where you want me to do that.
1 second, I can get that Github sandbox
Thanks!
Just to make sure I get it the first time, I'll also try to read the payload
1 second please
If not it's not the end of the world, you can retrieve the Event from the API if you need to.
Or view it in the Dashboard.
Oh true
You can vind it here : https://didactic-space-couscous-49gx79prv7w35g5q-3000.app.github.dev/payment
does it work now?
Yeah, one moment...
Sure
Welp, looks like my Apple Pay card isn't working. I don't think I'll be able to do this after all. Sorry about that!
No problem!
I guess I'll just wait
I got an idea, I'll just look into the buried GPay test payments I made a while ago
Ah, that works
Okay... a lot more lines that I cant paste here .... ๐
but i still dont see the customer name anywhere
Just a heads up, this is a public server, so anyone can see your messages. Not sure if you want your email and whatnot out there. ๐
Alright, I'll delete it ๐
Are you retrieving the Payment Method? Not the Payment Intent or the Invoice, but the Payment Method?
Oh!
I was looking at PI
Looking up at the Payment Intent in the dashboard, Under the Payment Method section
You're right
The details are here
But I cannot seem to find the pm ID in the actual webhook payload, or in the payment intent retrieval response
Can you give me the Event ID for the Event you're looking at? It starts with evt_.
Yes
It's "id": "evt_1P3hHsJ4ILijjURSK4mlG2n0",
evt_1P3hHsJ4ILijjURSK4mlG2n0
Sorry no
It's evt_1P3gxGJ4ILijjURSIovzTvCq
That's an invoice.payment_succeeded Event, which only contains the Invoice object. The Invoice has a payment_intent property, and that Payment Intent will have a payment_method property. You can retrieve the Invoice, the Payment Intent, and the Payment Method in a single API request using expansion: https://docs.stripe.com/api/expanding_objects
Note that the payment_intent property on Invoices is marked as "expandable" in the API reference: https://docs.stripe.com/api/invoices/object#invoice_object-payment_intent
The payment_method property on Payment Intents is as well: https://docs.stripe.com/api/payment_intents/object#payment_intent_object-payment_method
So if you retrieve the Invoice and expand payment_intent.payment_method you'll get all the information you want back.
Alright! 1 sec I'm reading the docs to make sure I get it
So something like stripe.invoices.retrieve('webhook_payload.data.object.id (in_)',{expand :[payment_intent.payment_method]})
Or is there to already request an expanded PI when creating the session?
Yeah, but payment_intent.payment_method needs to be a string.
ah, true
What do you mean by "session"? Want to make sure we're on the same page before I answer.
When creating the subscription and it returns a client secret
I probably didnt get the term right
You can, generally speaking, expand things when creating things like Subscriptions. However, at that point the Subscription probably hasn't been paid for yet, so expanding this info wouldn't be useful.
You can't "pre-expand" someting ahead of when it happens/exists.
Ah! makes sense
Alright, I'll give it a try by re-sending a GPay webhook
Yess
Now I got it
Thanks a lot for bearing with me!
Happy to help!