#_api

1 messages ยท Page 1 of 1 (latest)

chrome falconBOT
#

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

๐Ÿ“ 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.

spare merlin
wanton remnant
#

Pretty much this, if the user is logged into our cart system, I want to send that user id so it comes in with the order. I did try adding meta data to a product but was not able to figure out how to retrieve it. it seemed to be only visible in the dashboard

#

the intent here, is if a customer has an account, with us, to attach the order to the account they are logged in with us on.

spare merlin
#

If you add metadata to the Checkout Session itself it should be included in the checkout.session.completed Event. Do you have an example Checkout Session ID I can take a look at where you tried to do this?

wanton remnant
#

i haven't tried because I really don't know how to implement that part. I can paste what I am doing to build the cart as it is:

spare merlin
#

I can't provide code reviews or tell you what will happen if you run your code.

wanton remnant
#

I have a loop on our cart to do this :

        // create a line item for stripe checkout
        formParams["line_items[#itemIndex#][price_data][currency]"] = "usd";
        formParams["line_items[#itemIndex#][price_data][product_data][name]"] = "#pkProductID#: #productName#";
        for(productPhoto in productPhotos) {
            formParams["line_items[#itemIndex#][price_data][product_data][images][#photoIndex#]"] = productPhoto;
            photoIndex++;
        }
        formParams["line_items[#itemIndex#][price_data][unit_amount]"] = amount;
        formParams["line_items[#itemIndex#][quantity]"] = quantity;
spare merlin
#

The next step would be to try this in test mode and see what happens.

wanton remnant
#

^ no code review required . showing what fields I am passing

spare merlin
#

Those are all line items, nothing to do with metadata there.

wanton remnant
#

right, so metadata is that a root thing at the same level line_items[] are added to the session ?

#

and format is what ?

metadata[mykey]=value ?

spare merlin
#

Correct for both, yep.

wanton remnant
#

so not

metadata[0][mykey]=value
spare merlin
#

It depends on the context/language you're using.

#

Probably not though.

#

It's recommended to use one of our official SDKs if possible.

#

Makes things a lot easier. ๐Ÿ™‚

wanton remnant
#

can't use your sdk because nothing available for coldfusion

#

also, using a beta feature for shipping, so the java jar can't be used

#

curl equivalent requests. so raw form params as the stripe api expects them

#

really wish stripe let me post json, but, alas lol

#

testing quickly with a simple value. one moment.

#

Im trying to add these (think curl)

metadata[coupon]="discountCode"
metadata[gvuser]=0

and getting a problem

spare merlin
#

What's the problem?

wanton remnant
#

since I have you here, maybe you can suggest a better way to handle this. I have some error handling that does some additional checks. for example, when the stripe js makes the call to create the session to our server side script, i will build all the stuff from the cart at that point and check to make sure it is allowed (country restrictions etc)

#

if not allowed, i return null for the session id. which causes the stripe js to bail eventually.

#

(intentional). i also do these checks before displaying the button to checkout with stripe.

#

the checks on the session, are for mitigation reasons only -- potential future problems with attempting to checkout for something they are not allowed to or whatever.

#

the question for this : is there a better json response i can give that is better than { session: null } like, can i give a reason or something ?

#

the reason i am asking, is because there are multiple points where this could fail, and having them all respond with { session: null } for any broken cart/etc issue, is a pita to troubleshoot -- which is what I am running into now

spare merlin
#

I need a bit more context. You said, "for example, when the stripe js makes the call to create the session to our server side script" but I'm not sure what that means. Usually your own code would fetch details from your server like that, not Stripe.js itself. Can you provide more details about that part of the flow? Also, are you using embedded Checkout or are you redirecting to hosted Checkout?

wanton remnant
#

the stripe js, calls a script on our server via XHR, to get the stripe session id

#

in that script is where we build the cart, pass to stripe server to server, and return a response in json that the stripe js then uses for whatever other underlying magic

spare merlin
#

When you say "the stripe js" are you referring to Stripe-related JavaScript you wrote, or are you saying Stripe.js itself is doing this on its own?

wanton remnant
#

ok, stripe js by itself does nothing

#

it requires a session

spare merlin
#

Can you share the JavaScript in question so I can better understand what's happening?

wanton remnant
#

to set up the session

#

ok

#
<html>
    <head>
        <script src="https://js.stripe.com/v3/"></script>
        <script type="text/javascript">
            var stripe = Stripe('pk_test_51PGS1b2MMD1HzYgM3aqEYSHM2J3FwPkQS4cQsa1577cFqGSRnWrYuUYS4d0MdsBoBX0MaxbjBJ4fKYXU9rfxXrpz00Jslhzp3I'); // Replace with your actual Stripe public key
            
            async function fetchClientSecret() {
                const response = await fetch("/stripe/session.cfm", { method: "POST" });
                const { clientSecret } = await response.json();
                return clientSecret;
            }
        
            async function onShippingDetailsChange(shippingDetailsChangeEvent) {
                const { checkoutSessionId, shippingDetails } = shippingDetailsChangeEvent;
                const response = await fetch("/stripe/shipping.cfm", {
                    method: "POST",
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ checkout_session_id: checkoutSessionId, shipping_details: shippingDetails }),
                });
                const responseData = await response.json();
                return responseData.type === 'error' ? Promise.resolve({ type: "reject", errorMessage: responseData.message }) : Promise.resolve({ type: "accept" });
            }
        
            async function initialize() {
                const checkout = await stripe.initEmbeddedCheckout({ 
                    fetchClientSecret, 
                    onShippingDetailsChange 
                });
                checkout.mount('#stripecheckout');
            }
        
            // wait for the page to fully load before initializing stripe
            window.addEventListener('load', function() {
                initialize();
            });
        </script>
    </head>
    <body>
        <div id="stripecheckout"></div>
    </body>
</html>
#

the embedded vs non-embedded, only minor differences, still had to pass an async method that fetches the session id from our server for a stripe checkout session

#

more in line with a php integration, than nodejs

spare merlin
#

Okay, gotcha. So that's all code you're expected to modify to suit your own needs and requirements. It's a starting point only. A common approach is to modify both the frontend and backend code to handle things like the scenario you describe by adding additional detail to the response from the server, and additional logic on the frontend to handle that additional information.

wanton remnant
#

html/js client, coldfusion/cfml server

spare merlin
#

For example, your server could return JSON with far more properties and information. It could have an error property with an error code as a value. It could have an errorMessage with a user-facing error. You could then write JS to check for those and, if present, branch off and display the error instead of doing anything Stripe-related at all.

wanton remnant
#

yes, the backend is where we build the stripe checkout session, pass to the stripe api, get back the response and pass that on as the output from /stripe/session.cfm which is a json response that the stripe js library digests

spare merlin
#

Oh, I see what you're saying.

wanton remnant
#

what I am looking to do is probably something like this :

            async function initialize() {
                const checkout = await stripe.initEmbeddedCheckout({ 
                    fetchClientSecret, 
                    onShippingDetailsChange 
                });

                >>>  check here and don't mount or display anything stripe related if check fails <<<<

                >>> else
                checkout.mount('#stripecheckout');
            }
spare merlin
#

Honestly, no. I think what you really want to do is first ask your server to run your validation logic, determine if a Checkout Session should even be created, and respond with ๐Ÿ‘ or ๐Ÿ‘Ž . If it responds with ๐Ÿ‘Ž you never even run stripe.initEmbeddedCheckout.

wanton remnant
#

ugh... doesn't the stripe.init require a promise for the values ?

#

meaning, no real way to intercept and then pass it on.

spare merlin
#

stripe.initEmbeddedCheckout is designed to be called once you're sure you want to display Checkout on your page. If you're not sure yet, you shouldn't call it.

wanton remnant
#

like ideally :

if(fetchClientSecret.id === <somebadvalue>) {
   // die here, show pretty your cart is bad message
} else {
   // do mount stuff
}

as for the meta stuff, it looks like stripe ate it at the base level.

wanton remnant
spare merlin
#

Right, what I'm saying is that you need to split that part up.

#

You need to first check to see if Checkout is going to happen or not, then separately after that (and only if Checkout is going to happen) initialize Checkout.

wanton remnant
#

would love to, but i don't know how i can take the promise out , if the method requires a promise

spare merlin
#

Sorry, I think we might be talking past each other, I'm not saying you should remove a promise.

wanton remnant
#

move ?

spare merlin
#

You said, "but i don't know how i can take the promise out" and I said. "I'm not saying you should remove a promise" in response.

wanton remnant
#

i would like the check the validity of the response of that promise, and/or pass something in that promise response to stripe to say "hey, something went wrong"

spare merlin
#

You can't do that. That's what I'm trying to say; your approach will not work.

#

You're trying to use this incorrectly.

wanton remnant
#

so what you are saying, is i should check the stuff twice --- because i will always check the data in the script the data is being manipulated for output somewhere

#

but you are saying, i should duplicate those checks somewhere else, on a separate XHR, make those requests to see if valid, and THEN --- respond

spare merlin
#

Before you call stripe.initEmbeddedCheckout you first need to determine if you even need to use Checkout or not. That means your first step is to check with your server and validate whatever you need to validate first. Then, if your server says everything is good to go, then you call stripe.initEmbeddedCheckout and then it will fetch the Checkout Session ID from your server. If Checkout isn't going to happen you shuold never call stripe.initEmbeddedCheckout.

wanton remnant
#

what happens when stripe returns "null" -- which it has

#

that is where i got that idea from, passing a null value for stripe checkout id

#

checkout is intended to happen

#

but stuff goes wrong

spare merlin
#

stripe.initEmbeddedCheckout isn't expecting fetchClientSecret to get null or anything like that. If it does it will fail, as you've found.

#

It usually displays an error in the area where Checkout would normally be embedded.

wanton remnant
#

maybe a user added something mid transaction on another tab, and we need to account for that at the moment the session is being requested from stripe

#

so what you are saying, is that initEmbeddedCheckout has no error handling

spare merlin
#

Not in the way you're describing, no.

wanton remnant
#

so if something breaks between our server, stripe and the response back, then there is no way to account for that ?

#

to get the session id, we need to build the form data to hand off, and params, etc.

#

then call the stripe checkout api posting that data

spare merlin
#

An error will be displayed on the page if something goes wrong.

wanton remnant
#

stripe checkout api response with either a session, or null for the session id

#

that is passed down the pipe through the promise (in this case fetchClientSecret).

#

in this process, stuff can go wrong

#

i have dealt with trapping and sending us debug notices/etc when it does, however the user experience really sucks when (for example) we get a null for checkout session

spare merlin
#

Yes, and you can either rely on the error displayed by Checkout or add additional logic to handle errors inside your fetchClientSecret function.

wanton remnant
#

whether it be from stripe servers, or our end

#

what error ?

#

it's always the same, because the only bad response is "null"

spare merlin
#

Errors like getting back null instead of a client secret.

wanton remnant
#

sorry, and if clientsecret is missing completely from the response (that happened)

#

i was sending the wrong init for checkout session to stripe api and got back a valid session id, but no clientsecret which is what was required for the beta shipping feature

chrome falconBOT
spare merlin
#

The main issue is that you have an incorrect mental model for how stripe.initEmbeddedCheckout is designed. The idea is that you never call stripe.initEmbeddedCheckout unless you're certain you want to display Checkout. If something goes wrong with the fetch of the client secret that means something actually went wrong, like a network error, or an unexpected server error. You're trying to also shoehorn in validation into this process, but it's not designed for that.

#

You need to do your validation prior to this entire process.

wanton remnant
#

and here, i am certain i want to display checkout

#

the problem is in the response from checkout api

#

i want to handle that when it breaks

spare merlin
#

No, you're not. You're not certain because your server not returning a client secret is an expected outcome.

wanton remnant
#

yes, it is an expected outcome.

spare merlin
#

If your server not returning a client secret is an expected outcome then you shouldn't be calling stripe.initEmbeddedCheckout.

#

You should instead be doing other work/validation/whatever before that to get to the point where the only expected outcome is that your server will return a client secret.

wanton remnant
#

just passing the data on and forward to the client from what we receive from stripe

spare merlin
#

Can network or other unexpected errors happen at that point? Sure. But the only expected outcome when you call stripe.initEmbeddedCheckout is that your server will return a client secret for a Checkout Session.

wanton remnant
#

i did use that later to do other things, like some checks, as should be done when building the data to send to stripe -- pretty simple really

#

but things fail

#

yes

#

network errors/etc. i want to account for these

#

i suppose i could probably do a promise of a promise

spare merlin
#

You can account for those inside your fetchClientSecret function.

#

For example, you can display your own error message/instructions on your page if you don't get a client secret back.

wanton remnant
#

get the response from one promise, check the data, then pass that promise response to another promise as a result of a promise method that can be passed to the stripe init

spare merlin
#

You could instruct your customer to reload the page, for example.

wanton remnant
#

the fetchClientSecret() -- i have not tried, but typically, when something is inside something being called as a hook on a 3rd party library, there really is no ui/ux control at that point which doesn't get overridden by the final response from the promise

#

entirely dependent on how that library handles the promise

spare merlin
#

You're too focused on the Promise. This isn't about the Promise. Inside fetchClientSecret you can do whatever you want, you can write whatever code in there. You can make updates to your UI, you can make multiple fetch requests, you can write any logic. You are expected to return a client secret at the end, but beyond that you're free to do whatever you want.

#

For example, you can fetch from your server, then write code to check for any errors or unexpected stuff in the response, then update your UI to show an error, etc.

wanton remnant
#

right, and that end response, is handled by the stripe library, and stripe library will display /alter the ui based on that

#

here is a better question

spare merlin
#

Yes. If it gets a client secret it will display Checkout. if it doesn't it won't. That's it.

wanton remnant
#

can i just pass a string ?

#

or a variable containing a string ?

spare merlin
#

You can pass whatever you want, but if you pass anything other than a valid client secret Checkout will display an error.

wanton remnant
#

so it doesn't have to be a promise ?

spare merlin
#

Give it a try.

wanton remnant
#

^ true

#

nope

#

Invalid initEmbeddedCheckout(options) parameter: fetchClientSecretValue is not an accepted parameter.

#

must be a promise

#

ya ๐Ÿ™‚

spare merlin
wanton remnant
#

well, i will work a way around it. will do some hacky stuff i guess to get the response, then parse, then if it's good, set a value in a faux promise that returns that scoping nightmare value, as a parameter to the embedded checkout.

#

in the future, would be nice if maybe there were some options say

init( { promise, promise }, { options: { failPromise } } )

#

maybe it already exists. but from our conversation, i get the feeling it is just assumed it is working at that point.

#

which is fair, nothing "should" go wrong.... but.....

queen tulip
#

I don't believe there's something that exists today. You can file a feature request via
https://support.stripe.com/?contact=true

wanton remnant
#

true. in my case, the problem i first encountered and didn't realize, there was a connectivity issue that was periodically giving back "unauthorized" with http code 0, and/or corrupting response/data sent resulting in no session created

#

the user experience, was the pre-render loading images for the stripe embed, then eventually a non-specific error in the middle of the screen

#

it wasn't a stripe problem, but it was rough to nail down. my other concern is this -- a user can call the file stripe is requesting to generate the session

#

they can then use that session to alter their cart -- theoretically, which is why (even if you believe the code is perfect), taking that into account by checking at the final point (in addition to checks before), is a good idea. tbh, people that do this, i don't care if they get a crap message or bad user experience

queen tulip
#

Yup but if you'd like that level of granularity then you should look into using PaymentElement instead.

wanton remnant
#

my concern here was if something else legitimate messed up

#

client requires :

  • custom shipping
  • 1 - click checkout
  • coupon support using existing system

client doesn't want to collect address/etc except after checkout is completed.

#

i went down the paymentelement road, and it was nice, full control.

#

it's one of those "we want full control" but "we don't want to handle any of the data until after" situations lol.. making it work

queen tulip
#

I see

wanton remnant
#

thank you guys for the help
again. really appreciate your responses here โค๏ธ