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