#paul_ece-react

1 messages ยท Page 1 of 1 (latest)

unique dustBOT
#

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

vocal island
#

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

sterile crestBOT
primal flare
#

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

vocal island
#

yep, 1 second

primal flare
#

did you properly register the domain in your Stripe account?

vocal island
#

yes

primal flare
#

I don't really understand const onReady = ({ availablePaymentMethods }) => { and why you are doing that

vocal island
#

if the ECE hasn't loaded, I am displaying a loading spinner

unique dustBOT
primal flare
#

can you write expressCheckoutElement.on('ready', async (event) => { console.log("on ready!!!: ", JSON.stringify(event)); event.resolve(); }); instead? or similar

vocal island
#

I destructure it because I believe I saw it in the docs

primal flare
#

I assume you are ending up in a world where you have neither GooglePay nor ApplePay nor Paypal working so nothing shows

vocal island
#

yes

#

But for some reason it did show 30 minutes ago

#

And it's confusing

#

(although only google pay)

primal flare
#

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

vocal island
#

I've looked at the git history and I didn't do anything to the ECE

primal flare
#

@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

vocal island
#

Alright, sure

real basalt
#

๐Ÿ‘‹

vocal island
real basalt
#

Taking a look...

vocal island
#

(or not see.. cause that's the problem ๐Ÿ™‚ )

real basalt
#

Apple Pay shows up there for me in Safari...

#

Not seeing Google Pay in Chrome though.

vocal island
#

Hmm

#

interesting

#

I havent tried safari, but GPay indeed doesnt show up

real basalt
#

Hm, looks like something might be broken more broadly, hang on...

vocal island
#

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

};`

real basalt
#

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.

vocal island
#

Got it, thanks!

#

In the meantime

#

I have one more question

#

How can I read the GPay/Apple pay user details in my webhook?

real basalt
#

Which details?

vocal island
#

like address and name

real basalt
#

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.

vocal island
#

Got it, thanks!

#

Regarding the User details, I only want to look at invoicing

real basalt
#

What do you mean by "only want to look at invoicing"?

vocal island
#

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

real basalt
#

Invoices create Payment Intents, so Payment Intents can be "invoicing events".

vocal island
#

ah okay

real basalt
#

Also, you should use invoice.paid, not invoice.payment_succeeded.

vocal island
#

What is the difference?

real basalt
#

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.

vocal island
#

Could you please explain what do you mean by 'paid out of band'

real basalt
vocal island
#

Ah, I see

#

that would never be the same for me

#

Given that, is there any difference between the two events?

#

case for me**

real basalt
#

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. ๐Ÿ™‚

vocal island
#

true true

#

okay, Ill instead just listen to that

#

I assume all the event payload is the same, right?

real basalt
#

Yeah, they both contain an Invoice.

vocal island
#

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*

real basalt
#

Fetch the Payment Intent, then look at the Payment Method, but yep.

vocal island
#

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 }

real basalt
vocal island
#

Okay, was wondering that

real basalt
#

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.

vocal island
#

I see

#

But I found this under Retrieve a PaymentIntent in the docs

#

I assumed it was a successvul one

real basalt
#

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.

vocal island
#

Where can I find a sample response of the one I would receive in case the invoice was paid?

real basalt
#

Easiest way is to make a payment in test mode. Can you use Safari to make an Apple Pay payment?

vocal island
#

Im on Windows with a crowded Disk ๐Ÿ™‚

real basalt
#

Ah. ๐Ÿ˜…

#

Let me check on the status of the Google Pay issue...

vocal island
#

Thanks!

real basalt
#

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.

vocal island
#

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

real basalt
#

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.

vocal island
#

Oh true

real basalt
#

That gives me a "Failed to compile" error.

#

Oh, wait...

#

There it goes.

vocal island
#

does it work now?

real basalt
#

Yeah, one moment...

vocal island
#

Sure

real basalt
#

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!

vocal island
#

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

real basalt
#

Ah, that works

vocal island
#

Okay... a lot more lines that I cant paste here .... ๐Ÿ˜…

#

but i still dont see the customer name anywhere

real basalt
#

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. ๐Ÿ™‚

vocal island
#

Alright, I'll delete it ๐Ÿ™‚

real basalt
#

Are you retrieving the Payment Method? Not the Payment Intent or the Invoice, but the Payment Method?

vocal island
#

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

real basalt
#

Can you give me the Event ID for the Event you're looking at? It starts with evt_.

vocal island
#

Yes

#

It's "id": "evt_1P3hHsJ4ILijjURSK4mlG2n0",

#

evt_1P3hHsJ4ILijjURSK4mlG2n0

#

Sorry no

#

It's evt_1P3gxGJ4ILijjURSIovzTvCq

real basalt
#

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

#

So if you retrieve the Invoice and expand payment_intent.payment_method you'll get all the information you want back.

vocal island
#

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?

real basalt
#

Yeah, but payment_intent.payment_method needs to be a string.

vocal island
#

ah, true

real basalt
#

What do you mean by "session"? Want to make sure we're on the same page before I answer.

vocal island
#

When creating the subscription and it returns a client secret

#

I probably didnt get the term right

real basalt
#

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.

vocal island
#

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!

sterile crestBOT
real basalt
#

Happy to help!

vocal island
#

Thanks again!

#

Have a nice day!