How to introduce Wix Stores products via API?

Question:
How to add Wix Stores products via API? I created a page on my website and added some elements to create products in Wix Stores through that page. I wrote the code and followed the instructions in the Stores API documentation. I created a token key in a file on the http-funtions.js website and pasted it into the code structure. When I click the button, no error appears in the log but the product is not added. It seems that blank JSON is returned.

Product:
Editor

To add products to Wix Stores via API, ensure that your request is correctly formatted and authenticated. First, enable Velo (Dev Mode) and generate an API key with the necessary Stores: Products Write permissions. Use wix-fetch or fetch to send a POST request to wix-stores.v2.products.createProduct with the required product details (name, price, description, etc.). Ensure your http-functions.js file correctly processes the request and that the API key is included in the headers. If the JSON response is blank, check your API permissions and verify the payload structure. You can also log the request and response using console.log(response.json()) to debug any issues. Let me know if you need help troubleshooting.

Hello, thank you for your help, after a few hours I leave the json result here. It seems like I’m not being able to capture an image on the ulpload button, the price and cost are displayed as json instead of a number

{
“produto”: {
“name”: “tapete”,
“description”: “Lindo”,
“priceData”: {
“price”: 1000
},
“costAndProfitData”: {
“itemCost”: 520
},
“brand”: “Loja dos Tapetes”,
“productOptions”: ,
“visible”: true,
“productType”: “physical”,
“media”:
},
“colecoes”: [
“All Products”
]
}

com resposta: Erro ao enviar para o backend: SyntaxError: Unexpected token ‘<’, "

I would like to share the code but there are many lines

you can shere the code correctly formatted using the preformatted text icon </> in the reply

(translated by google translator - I’m Portuguese)
Thank you for your help from the bottom of my heart. I’ve been a Wix customer since 2011, but I’m just now learning to explore this platform. I am a beginner. I hope to learn from the best. Thanks

Code page:

import wixData from 'wix-data';
import wixUsers from 'wix-users';

$w.onReady(async () => {
    await carregarDadosLoja();  // Certificando-se que esta função está definida
    await carregarColecoes();

    // Definindo o evento para o carregamento de arquivos
    $w("#media").onChange(onFileUploaded);

    $w("#adicionarproduto").onClick(async () => {
        let nomeLoja = $w("#nomeloja").value.trim();
        let colecoesSelecionadas = [...new Set($w("#collections").value)];

        // Conversão dos campos numéricos
        let price = converterParaNumero($w('#price').value);
        let itemCost = converterParaNumero($w('#cost').value);

        let produto = {
            name: $w('#name').value,
            description: $w('#description').value,
            priceData: {
                price: price  // Usando o valor convertido do preço
            },
            costAndProfitData: {
                itemCost: itemCost  // Usando o valor convertido do custo
            },
            brand: nomeLoja,
            productOptions: [],
            visible: Boolean($w("#visible").value),  // Garantindo que visible seja booleano
            productType: "physical",
            media: []  // Aqui vamos armazenar as URLs ou imageIds
        };

        // Adicionar opções de produto, se houver
        if ($w("#productOptionName1").value.trim() !== "") {
            produto.productOptions.push({
                name: $w("#productOptionName1").value,
                choices: $w("#productOptionDescription1").value.split(",").map(choice => choice.trim()),
                optionType: "DROP_DOWN"
            });
        }

        // Captura de imagens carregadas (até 5 imagens)
        let imagensCarregadas = $w("#media").value || [];
        let mediaFiles = [];

        if (imagensCarregadas.length > 0) {
            for (let i = 0; i < Math.min(imagensCarregadas.length, 5); i++) {
                let uploadedImage = await enviarImagem(imagensCarregadas[i]);
                if (uploadedImage) {
                    mediaFiles.push({ url: uploadedImage });
                }
            }
        }

        // Se houver imagens, adiciona ao objeto produto
        if (mediaFiles.length > 0) {
            produto.media = mediaFiles;
        }

        // Log para verificar os dados antes de enviar
        console.log("Produto antes de enviar:", produto);
        console.log("Coleções selecionadas:", colecoesSelecionadas);

        try {
            console.log("Enviando dados para o backend:", { produto, colecoes: colecoesSelecionadas });

            const response = await fetch('/_functions/criarProduto', {
                method: 'POST',
                body: JSON.stringify({ produto, colecoes: colecoesSelecionadas }),
                headers: {
                    'Content-Type': 'application/json'
                }
            });

            const result = await response.json();
            console.log("Resposta do backend:", result);

            if (result.success) {
                limparCampos();
                $w("#sucessotext").show();
            } else {
                $w("#errotext").show();
            }

        } catch (error) {
            console.error("Erro ao enviar para o backend:", error);
            $w("#errotext").show();
        }
    });
});

// Função acionada quando um arquivo é enviado
function onFileUploaded(event) {
    let file = event.file;  // Obtém o arquivo carregado
    console.log("Arquivo carregado:", file);

    // Aqui você pode processar o arquivo (ex: adicionar a lista de mídia)
    let uploadedImage = file.url;
    $w("#media").value.push(uploadedImage);  // Adiciona a URL do arquivo carregado ao campo de mídia

    // Exemplo de como exibir a imagem carregada
    console.log("URL da imagem carregada:", uploadedImage);
}

// Função para enviar a imagem para o servidor (mantido sem alterações)
async function enviarImagem(imagem) {
    try {
        return imagem.fileUrl;
    } catch (error) {
        console.error("Erro ao processar imagem:", error);
        return null;
    }
}

// Função para carregar os dados da loja
async function carregarDadosLoja() {
    try {
        let loja = await wixData.query("Todasaslojas").eq("_id", wixUsers.currentUser.id).find();
        console.log("Dados da loja carregados:", loja);
        if (loja.items.length > 0) {
            let lojaData = loja.items[0];
            $w("#nomeloja").value = lojaData.title;
            $w("#nomeresponsavel").value = lojaData.nomeresponsavel;
            $w("#contactoemail").value = lojaData.contactoemail;
            $w("#contactotelefonico").value = lojaData.contactotelefonico;
            $w("#moradaprofissional").value = lojaData.moradaprofissional;
            $w("#apresentacao").value = lojaData.apresentacao;
            $w("#valormedio").value = lojaData.valormedio;
            $w("#categoria").value = lojaData.categoria || [];
            $w("#cobertura").value = lojaData.cobertura || [];
        }
    } catch (error) {
        console.error("Erro ao carregar dados da loja:", error);
    }
}

// Função para carregar as coleções
async function carregarColecoes() {
    try {
        let collections = await wixData.query("Stores/Collections").find();
        let uniqueCollections = new Set();
        let options = collections.items
            .filter(collection => {
                if (!uniqueCollections.has(collection._id)) {
                    uniqueCollections.add(collection._id);
                    return true;
                }
                return false;
            })
            .map(collection => ({ label: collection.name, value: collection._id }));

        if (!uniqueCollections.has("All Products")) {
            options.unshift({ label: "All Products", value: "All Products" });
        }

        $w("#collections").options = options;
        $w("#collections").value = ["All Products"];
    } catch (error) {
        console.error("Erro ao carregar coleções:", error);
    }
}

// Função para converter o valor para número (se não for um número válido, retorna 0)
function converterParaNumero(valor) {
    let numero = parseFloat(valor);
    return isNaN(numero) ? 0 : numero;  // Retorna 0 se não for um número válido
}

// Função para limpar os campos do formulário
function limparCampos() {
    $w("#name").value = "";
    $w("#description").value = "";
    $w("#price").value = "";
    $w("#cost").value = "";
    $w("#productOptionName1").value = "";
    $w("#productOptionDescription1").value = "";
    $w("#collections").value = ["All Products"];
}

Code Http:

import { fetch } from 'wix-fetch';

// Substitua com suas informações de API e ID do site
const API_KEY =’’my token’;
const SITE_ID = ‘my site id’;

export async function post_criarProduto(request) {
    try {
        // Recebe os dados do front-end
        const { produto, colecoes } = await request.body.json();

        // Dados de preço e custo
        const price = parseFloat(produto.priceData.price);
        const itemCost = parseFloat(produto.costAndProfitData.itemCost);

        // Estruturação do corpo da requisição para a API Wix Stores V2
        const bodyData = {
            name: produto.name,
            description: produto.description || '', // Descrição do produto (pode ser vazio)
            price: price, // Preço do produto
            itemCost: itemCost, // Custo do produto
            sku: produto.sku || '', // SKU (se aplicável)
            visibility: produto.visibility || 'VISIBLE', // Visibilidade
            productOptions: produto.productOptions || [], // Opções de produto (se houver)
            media: produto.media || [] // Mídias do produto
        };

        // Enviar para a API Wix Stores V2 para criar o produto
        const apiResponse = await fetch('https://www.wixapis.com/stores/v2/products', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${API_KEY}`,
                'wix-site-id': SITE_ID,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(bodyData) // Enviar o corpo estruturado
        });

        // Verifica se a resposta da API Wix Stores foi bem-sucedida
        if (!apiResponse.ok) {
            throw new Error(`Erro na chamada à API Wix Stores: ${apiResponse.statusText}`);
        }

        const apiData = await apiResponse.json();
        const produtoWixId = apiData.productId; // Supondo que a API retorna um id do produto Wix

        // **Adicionar Mídias ao Produto**, se houver
        if (produto.media && produto.media.length > 0) {
            await addProductMedia(produtoWixId, produto.media);
        }

        // **Adicionar o Produto às Coleções**, se houver
        if (colecoes && colecoes.length > 0) {
            await addProductsToCollection(produtoWixId, colecoes);
        }

        return {
            status: 200,
            body: { success: true, produtoId: produtoWixId }
        };

    } catch (error) {
        console.error("Erro ao criar produto:", error);
        return { status: 500, body: { success: false, error: error.message } };
    }
}

// Função para adicionar mídias ao produto
async function addProductMedia(produtoId, media) {
    try {
        const mediaData = media.map((mediaItem) => ({
            mediaId: mediaItem.mediaId, // ID da mídia
            mediaType: mediaItem.mediaType || 'IMAGE', // Tipo de mídia (ex: IMAGE, VIDEO)
            url: mediaItem.url // URL da mídia
        }));

        // Chamada para adicionar mídias ao produto
        const mediaResponse = await fetch(`https://www.wixapis.com/stores/v2/products/${produtoId}/media`, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${API_KEY}`,
                'wix-site-id': SITE_ID,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ media: mediaData })
        });

        if (!mediaResponse.ok) {
            throw new Error(`Erro ao adicionar mídias ao produto: ${mediaResponse.statusText}`);
        }
        console.log("Mídias adicionadas com sucesso!");
    } catch (error) {
        console.error("Erro ao adicionar mídias:", error);
        throw error;
    }
}

// Função para adicionar o produto às coleções
async function addProductsToCollection(produtoId, colecoes) {
    try {
        const collectionData = colecoes.map((colecaoId) => ({
            collectionId: colecaoId // ID da coleção
        }));

        // Chamada para adicionar o produto à coleção
        const collectionResponse = await fetch(`https://www.wixapis.com/stores/v2/products/${produtoId}/collections`, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${API_KEY}`,
                'wix-site-id': SITE_ID,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ collections: collectionData })
        });

        if (!collectionResponse.ok) {
            throw new Error(`Erro ao adicionar o produto às coleções: ${collectionResponse.statusText}`);
        }
        console.log("Produto adicionado às coleções com sucesso!");
    } catch (error) {
        console.error("Erro ao adicionar o produto às coleções:", error);
        throw error;
    }
}