Shopify App Auth's Successfully But Returns Redirect Error on Install

Sangarita
New Member
1 0 0

Hi

When I test my app on development store, I'm redirected to AUTH prompt successfully

When I click to install the app I'm redirected to the install callback: {app}/callback?code={code}&hmac={hmac}&shop={shop}&timestamp={timestamp}

and then I get this error: 

{"error": true, "message": "Request failed with status code 401","data":{}}

 

When I go to the store and try to open the app, I receive this error:

{My App Address} refused to connect.

 

The install callback file looks like this:

 

 

const crypto = require('crypto');
const querystring = require('querystring');
const axios = require('axios');
const { INTERNAL_SERVER_ERROR, HMAC_NOT_MATCH_ERROR } = require('../../../src/constant/errorMessages');
const { findOneShop } = require('../../../src/services/ShopServices');
const manageShopDetails = require('../../../src/helpers/manageShopDetails');

//Application install callback main function
const installCallback = async (req, res, next) => {

    try { 

        const {
            shop,
            code,
        } = req.query;

        //Validate hmac string
        await validateHmac(req.query);

        //Get shop details from database
        const shopData = await getShopDetails(shop);

        //Get access token
        const accessToken = await getAccessToken(shop, code, shopData);
        
        //Manage shop details
        const {
            shopDetails,
            isWebhookCreate
        } = await manageShopDetails(shop, accessToken, shopData, 'installCallback');

        //Check webhook create
        if (isWebhookCreate) {

            //Create webhook create in shopify
            await webhookCreate(shop, accessToken);

        }

        res.redirect(`${config.frontendUrl}?shop=${shop}&token=${shopDetails.token}`);

    } catch (error) {

        //Error send in error handler middleware
        return next(error);

    }

};

//Validate HMAC
const validateHmac = async (query) => {

    const map = Object.assign({}, query);
    delete map['hmac'];
    const message = querystring.stringify(map);

    const generatedHmac = crypto
        .createHmac('sha256', config.shopifySecretKey)
        .update(message)
        .digest('hex');

    if (query.hmac != generatedHmac)
        throw new Error(HMAC_NOT_MATCH_ERROR);

    return true;

};

//Get shop details from database
const getShopDetails = async (shopDomain) => {

    const shopData = await findOneShop({
        shopDomain
    });

    if (shopData.error)
        throw new Error(INTERNAL_SERVER_ERROR);

    return shopData.data;

};

//Get access token
const getAccessToken = async (shop, code, shopData) => {

    let accessToken = null;

    if (shopData && shopData.accessToken) {

        accessToken = shopData.accessToken;

    } else {

        const options = {
            method: 'POST',
            url: `https://${shop}/admin/oauth/access_token`,
            data: {
                client_id: config.shopifyApiKey,
                client_secret: config.shopifySecretKey,
                code: code
            }
        };

        const {
            data,
        } = await axios(options);

        accessToken = data.access_token;

    }

    return accessToken;

};

//Webhook create
const webhookCreate = async (shop, accessToken) => {

    const options = {
        method: 'POST',
        url: `https://${shop}/admin/webhooks.json`,
        headers: {
            'X-Shopify-Access-Token': accessToken
        },
        data: {
            webhook: {
                topic: 'app/uninstalled',
                address: `${config.baseUrl}/v1/app/webhook/callback`,
                format: 'json'
            },
        }
    };

    const {
        data,
    } = await axios(options);

    return data;

};

//Export function
exports.get = installCallback;

 

const axios = require('axios');
const md5 = require('md5');
const { INTERNAL_SERVER_ERROR } = require('../../src/constant/errorMessages');
const { insertShop, findOneAndUpdateShop } = require('../../src/services/ShopServices');
const fetchOrderDetails = require('../../src/helpers/fetchOrderDetails');

//Manage shop details
const manageShopDetails = async (shop, accessToken, shopData, callFrom) => {

    let shopDetails = null;
    let isWebhookCreate = false;

    const options = {
        method: 'GET',
        headers: {
            'X-Shopify-Access-Token': accessToken
        },
        url: `https://${shop}/admin/shop.json`,
    };

    const {
        data,
    } = await axios(options);

    const urlShopData = data.shop;

    const shopObj = {
        shopId: urlShopData.id,
        shopName: urlShopData.name,
        shopDomain: urlShopData.myshopify_domain,
        domain: urlShopData.domain,
        customerEmail: urlShopData.customer_email,
        accessScope: config.scope,
        accessToken: accessToken,
        timezone: urlShopData.iana_timezone,
        currency: urlShopData.currency,
        weightUnit: urlShopData.weight_unit,
        planName: urlShopData.plan_name,
        planDisplayName: urlShopData.plan_display_name,
        shopOwner: urlShopData.shop_owner,
    };

    if (callFrom != 'generateToken') {

        shopObj.token = md5(accessToken);

    }

    if (shopObj.planName === 'basic' || shopObj.planName === 'partner_test') {

        shopObj.planActivate = true;
        shopObj.planActivateAt = new Date();
        shopObj.planStatus = 'active';
        shopObj.planSlug = 'basic';

    }

    if (shopData) {

        if (shopData.uninstalledAt) {

            isWebhookCreate = true;
            shopObj.status = 1;
            shopObj.uninstalledAt = null;

        }

        if (shopObj.planName != 'basic' && shopObj.planName != 'partner_test' && shopObj.planName != shopData.planName) {

            shopObj.planSlug = null;
            shopObj.planShopifyId = null;
            shopObj.planActivate = false;
            shopObj.planStatus = null;

        }

        const updateData = {
            $set: shopObj
        };

        const updateShopData = await findOneAndUpdateShop({
            _id: shopData._id
        }, updateData);

        if (updateShopData.error)
            throw new Error(INTERNAL_SERVER_ERROR);

        shopDetails = updateShopData.data;

    } else {

        isWebhookCreate = true;

        const insertShopData = await insertShop(shopObj);

        if (insertShopData.error)
            throw new Error(INTERNAL_SERVER_ERROR);

        shopDetails = insertShopData.data;

        await fetchOrderDetails(shopDetails.shopId);

    }

    return {
        shopDetails,
        isWebhookCreate,
    };

};

//Export function
module.exports = manageShopDetails;

 

Any idea what's wrong?

0 Likes