For discussing the development and usage of Checkout UI extensions, post-purchase extensions, web pixels, Customer Accounts UI extensions, and POS UI extensions
We're moving the community! Starting July 7, the current community will be read-only for approx. 2 weeks. You can browse content, but posting will be temporarily unavailable. Learn more
We are implementing a Checkout UI extension in our existing app, and we need to communicate with our backend. For this communication, we use App Proxy. In general, App Proxy is configured correctly and is working because we use the same request on the order thank you page. At this moment, we want to move this functionality inside a checkout flow.
Here is our extension configuration:
[capabilities]
network_access = true
block_progress = true
api_access = true
And here is an example code in the extension:
import React, { useState, useEffect } from 'react'
import {
render,
BlockStack,
useShippingAddress,
useExtensionApi,
Banner,
Select,
} from '@shopify/checkout-ui-extensions-react'
render('Checkout::ShippingMethods::RenderAfter', () => <App/>)
function App () {
const { shop } = useExtensionApi()
const [warehouses, setWarehouses] = useState()
const address = useShippingAddress()
const destination = {
shippingCity: address.city,
shippingZip: address.zip,
}
useEffect(() => {
const url = shop.storefrontUrl.replace(/\/$/, "")
fetch(
`${url}/apps/np/warehouses-list`,
{
credentials: 'include',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(destination),
},
).
then((response) => response.json()).
then(({ warehouses, isSaved }) => {
console.log(warehouses)
setWarehouses(warehouses)
}).
catch(console.error)
}, [shop])
return (
<BlockStack>
<Banner>
List of warehouses
</Banner>
<Select
label="Warehouse"
value="2"
autocomplete={true}
options={[]}
/>
</BlockStack>
)
}
This is a proof of concept, and we are trying it on a development store. The error we encounter is:
"Access to fetch at 'https://store.myshopify.com/apps/np/warehouses-list' from origin 'https://cdn.shopify.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
If we rewrite this example to, for example, make a request to the Storefront API, everything works as expected.
Solved! Go to the solution
This is an accepted solution.
We've explored this further and believe the problem is that UI extensions make an HTTP OPTIONS request, which is not supported by App Proxy. The resulting 405 response is interpreted by the browser as a CORS error. For now the solution is to make direct calls to your own app server without App Proxy as an intermediary. We will investigate fixing this but are still undecided on whether we want to support this combination of App Proxy and Checkout UI extensions.
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Forgot to mention we have observed that adding "credentials: include" to the fetch call does not include credentials in the "OPTIONS" request. However, we have found that when making a similar request to the Storefront API have all cookies in the request.
Hi @Eltrino,
While we don't currently provide direct support for Functions in this forum, the public [Shopify/function-examples GitHub repository] has an active [discussion board] that might be worth checking out.
Hope you have a great day
Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
This is not a Shopify Function from what I can tell - this is a Checkout UI extension
its not about functions
I have the same issue. Will appreciate if anyone can share a solution here.
Did you get a solution to this issue? Im having the same problem right now. You can bypass by adding a no-cors mode to the fetch but then the response becomes opaque and you cant get the answer haha. Any other solutions?
Having the exact same issue/question...
Im not sure how to solve this for POST calls but for GET calls I just made fetch call with no attributes sent through the fetch function and the call passed like normal. For now you could "fake" the POST into a GET call with the parameters through the url as long as there are no sensitive data you are sending could be a patch solution. Otherwise I would also be awaiting for the clean solution haha.
Network calls from Checkout UI Extensions do not require proxying through a Shopify endpoint. App Proxy is designed for apps integrating into storefront, rather than checkout. Your app backend will need to specify the `Access-Control-Allow-Origin` response header to allow the customer's browser to make the network call. For more detailed guidance on network calls from Checkout UI Extensions, including setting access control headers, please see this help document.
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Hi @jarthorn,
Apologies for any confusion. As I understand it, the App Proxy feature is intended to help us developers conceal the actual URL of our apps, making it appear as if the requests are to the same domain as the shop. Additionally, using the App Proxy provides a kind of authentication before our apps because all requests will be signed.
However, are you suggesting that we need to make direct calls to our app servers via their actual URLs?
Also, I wanted to clarify whether it's not the correct way to use the proxy if we add a similar dropdown to the thank you page (via script tags) and use the proxy url to fetch data there as well. Could you please confirm?
However, are you suggesting that we need to make direct calls to our app servers via their actual URLs?
I mainly wanted to say that App Proxy is not required for Checkout UI Extensions. You can make direct calls to your own app servers from a Checkout UI Extension. However I'm not aware of a reason why App Proxy could not be used if you wanted to. I suggest start by ensuring you are setting the appropriate `Access-Control-Allow-Origin` header on responses from your app's endpoint to ensure it can be used from within an extension. If the proxy doesn't interfere with that you should see the header come through on the browser side of the request. If you find evience the proxy itself is stripping or altering that header we can investigate further.
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
what is required from us for further investigation? because we are setting correct cors headers and still it does not work.
This is an accepted solution.
We've explored this further and believe the problem is that UI extensions make an HTTP OPTIONS request, which is not supported by App Proxy. The resulting 405 response is interpreted by the browser as a CORS error. For now the solution is to make direct calls to your own app server without App Proxy as an intermediary. We will investigate fixing this but are still undecided on whether we want to support this combination of App Proxy and Checkout UI extensions.
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Hi Jarthorn,
Thank you for the clarification and the update.
Hi @jarthorn ,
Thanks for clarifying this. I was stuck until I read your response.
Is there a way to get the App URL dynamically from within the App itself? Otherwise I have to manually update the URL each time I run it in a dev environment. Looks like a few others have the same question:
https://community.shopify.com/c/extensions/obtain-my-app-s-url-from-a-checkout-ui-extension/td-p/210...
Hi. I'm facing the same issue: is this still like that or do you eventually started support a combination of App Proxy and Checkout ui?
Follow up note: This problem related to HTTP OPTIONS requests was fixed on July 13, 2023. While we still do not recommend using an app proxy with Checkout UI Extensions, we have verified they do work, with some exceptions that are documented here (see app proxy section).
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
This answer still doesn't help.
I tried to make a request to our server without application proxy.
await fetch(url, { // url - is direct link to our server
method: 'POST',
body: JSON.stringify(formData),
headers: {
'Access-Control-Allow-Origin': '*',
'Content-type': 'application/json',
},
})
.then(resp => {
return resp.json();
})
.then(receivedJson => {
console.log(receivedJson);
});
In my server I added special headers
header('Access-Control-Allow-Origin: *');
But in console, I am getting this error: "…has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response."
In network, I see OPTIONS request which return code 200, then my main request which gets “CORS failed” code(.
If I add
mode: 'no-cors',
in fetch – this works, but I can't read the response from my server, and in your documentation, I see
Required CORS headers
so this is one of the must-haves. I would appreciate any help on this issue. Since we need to send a POST request to our server and this request must be secure.
`access-control-allow-origin` is an HTTP response header, not a request header. Remove this header from your fetch request, but leave it in the server response.
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
It does not help. I removed this headers from fetch.
await fetch(url, {
method: 'POST',
body: JSON.stringify(formData),
headers: {
'Content-type': 'application/json',
},
})
.then(resp => {
return resp.json();
})
.then(receivedJson => {
console.log(receivedJson);
});
And in my PHP server
header('Access-Control-Allow-Origin: *');//To be sure, but I also pass the header in the response itself below.
Route::post('/test-extension', function (){
return response()->json(['status'=>'success'])->withHeaders([
"Access-Control-Allow-Origin"=> "*",
]);
});
But I still get error "CORS error".
The problem was in the level settings above. I added this.
$response->headers->set('Access-Control-Allow-Origin' , '*');
$response->headers->set('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE');
$response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With, Application','ip');
at the beginning of all responses from our server—and it worked for me. Thank you
Hello, I hope you are doing well.
I am also facing similar kind of problem in my checkout extention where we want to use api of own backend (custom shopify) app. When we are using the fetch method in extention its showing cors error. But api is working good in postman.
Error :
Fetch Method Code :
Postman Response :
As shopify updated its doc in remix freamwork recently so I am not able to find like how to enable cors at my backend.
Little help would be greatly appreciated.
Thanks
This error indicates that your app server's response is missing the "Access-Control-Allow-Origin" header. Please add this header as described in the documentation.
Jarthorn | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
Thanks, I am also having an issue with this. My Remix headers from Postman are as follows:
But when I send a POST request to the same url through a subscription extension, Shopify gives a CORS error in the browser even though the backend registers a 200 response and everything with the endpoint works fine. This makes it so that I can't see the body of the POST response and can't debug or provide good error messages to the client if I want to send some error from the backend. How should I resolve this?
Nevermind, this has been resolved. Apparently I needed to remove the "allowed headers" from the backend response, or this error would show:
"Access to fetch at 'url' from origin 'https://cdn.shopify.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, https://cdn.shopify.com', but only one is allowed. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
Anyone found the solution?
Hi, a query from a UI extension via an app proxy url has two main stumbling blocks:
I have just written a small UI extension with which I determine the order number and payment method for tracking on the Thankyou page.
Here is the working code.
import { useEffect } from "react";
import {
reactExtension,
useApi,
useCheckoutToken,
useShop
} from '@shopify/ui-extensions-react/checkout';
export const thankyouRender = reactExtension(
'purchase.thank-you.footer.render-after',
() => <Extension />,
);
function Extension() {
const { analytics, sessionToken } = useApi();
const checkoutToken = useCheckoutToken();
const shop = useShop();
const url = shop.storefrontUrl + "apps/orders/api/get-order-by-checkout-token";
useEffect(() => {
async function addThankyouInfo () {
try {
const token = await sessionToken.get();
console.log('sessionToken.get()', token);
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Bearer ${token}`,
},
body: `checkout_token='${checkoutToken}'`,
});
const data = await response.json();
console.log("get-order-by-checkout-token", data);
const orderData = data?.result ?? null;
let order_name = null;
let payment_method = null;
if (orderData.name && orderData.transactions) {
order_name = orderData.name;
payment_method = orderData.transactions
.filter(
(t) =>
["success", "pending"].includes(t.status.toLowerCase()) &&
["capture", "sale"].includes(t.kind.toLowerCase())
)
.sort(
(t1, t2) =>
parseFloat(t2.amountSet.presentmentMoney.amount) -
parseFloat(t1.amountSet.presentmentMoney.amount)
)[0]?.paymentIcon?.altText ?? null;
}
analytics.publish('thankyou-add-info', {
order_name: order_name,
payment_method: payment_method
});
} catch (error) {
console.log(error);
}
}
addThankyouInfo();
}, []);
return null;
}
I am having the same issue with my checkou ui extension, I am accessing the app server url directly and I can send get requests without issues buit when I try to post it gives me cors errors. But I have installed the same app on a test store and it works perfectly; not sure what is going on.
I found a solution for when the checkout extension is sending information to the api, I authenticate it and send the cors like this:
const { cors, sessionToken } = await authenticate.public.checkout(request)
return cors(json({ message: "success" }))