Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

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

Help Needed with Post-Purchase Extension: CORS Errors When Fetching Shop Configuration

Help Needed with Post-Purchase Extension: CORS Errors When Fetching Shop Configuration

Ekomi-Dev
Shopify Partner
6 0 0

Hello Shopify Community,

 

I'm developing a Post-Purchase extension for my Shopify app, and I'm running into some issues with fetching the shop configuration from my server. I hope someone here can help me figure out what's going wrong.

 

**Background:**

 

- I'm creating a Post-Purchase extension that collects customer feedback after checkout.

- The extension needs to fetch shop-specific configuration data from my server during the `ShouldRender` phase.

- I'm using a Remix app as my server, hosted at `https://page-patio-confusion-heel.trycloudflare.com`.

- I've set up my `shopify.extension.toml` to allow network access to the required URLs.

 

**The Problem:**

 

When I test the extension, I encounter the following errors in the console:

 

```

Error fetching shop configuration: AxiosError {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}

 

Access to XMLHttpRequest at 'https://plugins-dashboard.coddle.de/zaufane-shopify/api/shop-configuration' from origin 'https://cdn.shopify.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

```

 

**What I've Tried:**

 

- Ensured that my server endpoints include the necessary CORS headers (`Access-Control-Allow-Origin`, `Access-Control-Allow-Headers`, etc.).

- Updated my `shopify.extension.toml` file to include the `allowed_urls`:

 

```toml

[capabilities]

network_access = true

 

[network_access]

allowed_urls = [

"https://page-patio-confusion-heel.trycloudflare.com",

"https://plugins-dashboard.coddle.de"

]

```

 

- Verified that the server is accessible and the endpoints work when tested with `curl` and Postman.

- Adjusted my code to handle the possibility of CORS errors gracefully, but I still need to fetch the configuration successfully.

 

**My Code:**

 

Here is the relevant part of my extension code (`index.tsx`):

 

```typescript

import React, { useState } from "react";

import {

extend,

render,

BlockStack,

Button,

CalloutBanner,

Layout,

TextBlock,

TextContainer,

TextField,

InlineStack,

Text,

Bookend,

Banner,

useExtensionApi,

} from "@shopify/post-purchase-ui-extensions-react";

import { translations } from "./utils/langs";

import axios from "axios";

 

const APP_URL = process.env.SHOPIFY_EXTENSION_URL; // Replace with your Remix app URL

 

// Fetch shop configuration

async function fetchShopConfiguration(shopDomain: string) {

const url = `${APP_URL}/api/shop-configuration`;

 

try {

const response = await axios.post(

url,

{ shopURL: shopDomain },

{

headers: {

"Content-Type": "application/json",

"Accept": "application/json",

},

},

);

return response.data;

} catch (error) {

console.error("Error fetching shop configuration:", error);

throw error;

}

}

 

extend(

"Checkout::PostPurchase::ShouldRender",

async ({

inputData: {

initialPurchase: { lineItems },

shop: { domain: shopDomain },

},

storage,

}) => {

try {

const shopConfiguration = await fetchShopConfiguration(shopDomain);

 

await storage.update({

initialData: {

rating: 0,

feedback: "",

shopConfiguration,

orderId: lineItems[0].product.id,

},

});

 

return {

render: shopConfiguration.extensionEnabled === true,

};

} catch {

// If fetching the configuration fails, do not render the extension

return { render: false };

}

},

);

 

render("Checkout::PostPurchase::Render", ({ storage }) => (

<App initialData={storage.initialData} />

));

 

function App({ initialData }) {

const { done } = useExtensionApi();

 

const [rating, setRating] = useState<number>(initialData.rating || 0);

const [feedback, setFeedback] = useState<string>(initialData.feedback || "");

const [loading, setLoading] = useState<boolean>(false);

const [warning, setWarning] = useState<string>("");

 

const locale = navigator.language.split("-")[0];

const t = translations[locale] || translations["en"];

 

const handleSubmit = async () => {

setLoading(true);

setWarning("");

 

if (feedback.trim().length < 3 || rating === 0) {

setWarning("Please fill in all fields");

setLoading(false);

return;

}

 

const url = `${APP_URL}/api/send-feedback`;

 

try {

const response = await axios.post(

url,

{

rating,

feedback,

shopConfiguration: initialData.shopConfiguration,

orderId: initialData.orderId,

},

{

headers: {

"Content-Type": "application/json",

Accept: "application/json",

},

},

);

done(); // Redirect to thank-you page

return response.data;

} catch (error) {

console.error("Error submitting feedback:", error);

// Handle error as needed

} finally {

setLoading(false);

}

};

 

return (

<BlockStack spacing="loose">

<CalloutBanner title={t.title}>{t.feedbackPrompt}</CalloutBanner>

<Layout>

<BlockStack spacing="loose">

<TextContainer>

<TextBlock>

<Text size="large">{t.feedbackInstruction}</Text>

</TextBlock>

</TextContainer>

<BlockStack spacing="tight">

<TextBlock>

<Text size="large">{t.starRatingPrompt}</Text>

</TextBlock>

<InlineStack spacing="xtight">

{[1, 2, 3, 4, 5].map((star) => (

<Button

key={star}

onPress={() => setRating(star)}

subdued={star > rating}

>

<Text size="xlarge">{star <= rating ? "★" : "☆"}</Text>

</Button>

))}

</InlineStack>

<Bookend>

<TextBlock>

<Text size="large">

{t.ratingOutOf} {rating}/5

</Text>

</TextBlock>

</Bookend>

</BlockStack>

<BlockStack spacing="tight">

<TextField

label={t.personalFeedback}

value={feedback}

onChange={setFeedback}

multiline

required

type="text"

/>

</BlockStack>

{warning && <Banner status="critical" title={warning} />}

<Button onPress={handleSubmit} submit loading={loading}>

{t.submitButton}

</Button>

</BlockStack>

</Layout>

</BlockStack>

);

}

 

export default App;

```

 

**My `shopify.extension.toml` Configuration:**

 

```toml

name = "reviewfeedback"

type = "checkout_post_purchase"

handle = "my-post-purchase-extension"

 

[capabilities]

network_access = true

 

[network_access]

allowed_urls = [

"https://page-patio-confusion-heel.trycloudflare.com",

"https://plugins-dashboard.coddle.de"

]

```

 

**Questions:**

 

1. Why am I still getting CORS errors even after configuring the server to include `Access-Control-Allow-Origin` headers?

2. Is there something specific I need to do in my extension or server code to allow cross-origin requests from `https://cdn.shopify.com`?

3. How can I successfully fetch the shop configuration during the `ShouldRender` phase without encountering network errors?

 

**Additional Details:**

 

- My server endpoints correctly respond to `OPTIONS` preflight requests with the necessary CORS headers.

- The network requests work fine when tested outside of the Shopify extension (e.g., using Postman).

- I'm using `axios` for making HTTP requests within the extension.

 

Any help or guidance would be greatly appreciated!

 

Thank you in advance.




Replies 0 (0)