Join us for an upcoming Shopify Partner webinar on February 27, 2024. Discover the latest Checkout Extensibility features, and deep dive on improvements to Shopify Functions and Web Pixels. Register now for either the 10am EST or 2pm EST sessions.
Solved

How to properly restrict access to my Shopify app based on payments ?

Not applicable

After following this tutorial: https://shopify.dev/tutorials/build-a-shopify-app-with-node-and-react , I can install my app, click cancel on the payment page, and still use the app as if nothing happened.

I did not find in the docs how to properly restrict access based on (non-)payment.

Could you please point me to the correct resource if there is one ?

Accepted Solutions (3)
leighb4rnes
Shopify Partner
18 2 7

This is an accepted solution.

Hiya,

I tend to setup a slightly different onboarding flow ( and use GQL for most of the checks ) 

1. On install I show a grid of plan/packages that a user can select from. 

2. When the user selects one it takes them to payment 

2.1 if they close the window or cancel it just takes them back to the same screen when they try and access the app. 

No webhooks needed!!!

Alternatley
2.2 they accept the charge they are redirected back to the url you specify ( including a query string ) and a charge ID I think ( woul dneed to check ) 
2.2.1 I then look up the charge check its active and let them in. No delays and pretty secure. 

On additonal entries into the app I just do a billing check when they land on the dashboard or whatever silently int he background ( I tend to have a timeout on that so it only runs once a day ) 

----

Might be worthwhile taking a look at your flow, not auto redirecting to the payment screen and having an access wall in your app. 



View solution in original post

Not applicable

This is an accepted solution.

Found and implemented what I was looking for thx to the help of Jack McCracken in the Shopify Partners Slack.

Here is my code in case it helps someone:

 

 

function checkPro(ctx, subscriptionID) {


    return new Promise((resolve, reject) => {
        const { shop, accessToken } = ctx.session;

        console.log("SHOP: "+shop);
        console.log("SUBSCRIPTION ID: "+subscriptionID);

        const querySub = JSON.stringify({
            query: `{
                appInstallation {
                    activeSubscriptions {
                        status,
                        id
                    }
                }
            }`
        });
        return fetch(`https://${shop}/admin/api/2019-10/graphql.json`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                "X-Shopify-Access-Token": accessToken,
            },
            body: querySub
        }).then(response => response.json()).then(responseSub => {

            const subs = responseSub.data.appInstallation.activeSubscriptions.filter(sub => sub.id.split("/AppSubscription/")[1] == subscriptionID);

            console.log("SUBS: "+JSON.stringify(subs));

            if (subs.length > 0 && subs[0].status == "ACTIVE") {
                global.db.collection("Domains").doc(shop).update({pro: true}).then(function() {
                    console.log("TURNING PRO ON");
                    resolve("IT IS PRO");
                });
            }
            else {
                console.log("PRO STAY OFF");
                resolve("IT IS NOT PRO");
            }
        });
    });
    
}

 

 

 

 

 

 

 

View solution in original post

Not applicable

This is an accepted solution.

Found what I was looking for thx to the help of Jack McCracken ^^

Maybe it helps someone else :

 

        const querySub = JSON.stringify({
            query: `{
                appInstallation {
                    activeSubscriptions {
                        status,
                        id
                    }
                }
            }`
        });
        return fetch(`https://${shop}/admin/api/2019-10/graphql.json`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                "X-Shopify-Access-Token": accessToken,
            },
            body: querySub
        }).then(response => response.json()).then(responseSub => {

 

View solution in original post

Replies 8 (8)

Not applicable
Not applicable

After registering a webhook for the "APP_SUBSCRIPTIONS_UPDATE" and granting access when the event indicating that the subscription is "ACTIVE" arrives, everything works fine except for one thing:

It takes about 2min and 30 seconds for the event to arrive.

The customer is stuck waiting 2min and 30 seconds. This is not optimal to say the least. 😕 Would anyone have a suggestion ?

leighb4rnes
Shopify Partner
18 2 7

This is an accepted solution.

Hiya,

I tend to setup a slightly different onboarding flow ( and use GQL for most of the checks ) 

1. On install I show a grid of plan/packages that a user can select from. 

2. When the user selects one it takes them to payment 

2.1 if they close the window or cancel it just takes them back to the same screen when they try and access the app. 

No webhooks needed!!!

Alternatley
2.2 they accept the charge they are redirected back to the url you specify ( including a query string ) and a charge ID I think ( woul dneed to check ) 
2.2.1 I then look up the charge check its active and let them in. No delays and pretty secure. 

On additonal entries into the app I just do a billing check when they land on the dashboard or whatever silently int he background ( I tend to have a timeout on that so it only runs once a day ) 

----

Might be worthwhile taking a look at your flow, not auto redirecting to the payment screen and having an access wall in your app. 



Not applicable

Thank you for your answer ^^

Not applicable

@leighb4rnes 

 

1) "I tend to setup a slightly different onboarding flow ( and use GQL for most of the checks ) "

What GraphQL endpoints do you query to check that the payment went through ?

2)  " if they close the window or cancel it just takes them back to the same screen when they try and access the app. "

But where in your code (and how) do you check that the subscription was indeed confirmed  (Do you use a GraphQL endpoint or is there a simple callback ) ?

3) "2.2 they accept the charge they are redirected back to the url you specify ( including a query string ) and a charge ID I think ( woul dneed to check ) 
2.2.1 I then look up the charge check its active and let them in. No delays and pretty secure. "

How ?

 

Not applicable

This is an accepted solution.

Found and implemented what I was looking for thx to the help of Jack McCracken in the Shopify Partners Slack.

Here is my code in case it helps someone:

 

 

function checkPro(ctx, subscriptionID) {


    return new Promise((resolve, reject) => {
        const { shop, accessToken } = ctx.session;

        console.log("SHOP: "+shop);
        console.log("SUBSCRIPTION ID: "+subscriptionID);

        const querySub = JSON.stringify({
            query: `{
                appInstallation {
                    activeSubscriptions {
                        status,
                        id
                    }
                }
            }`
        });
        return fetch(`https://${shop}/admin/api/2019-10/graphql.json`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                "X-Shopify-Access-Token": accessToken,
            },
            body: querySub
        }).then(response => response.json()).then(responseSub => {

            const subs = responseSub.data.appInstallation.activeSubscriptions.filter(sub => sub.id.split("/AppSubscription/")[1] == subscriptionID);

            console.log("SUBS: "+JSON.stringify(subs));

            if (subs.length > 0 && subs[0].status == "ACTIVE") {
                global.db.collection("Domains").doc(shop).update({pro: true}).then(function() {
                    console.log("TURNING PRO ON");
                    resolve("IT IS PRO");
                });
            }
            else {
                console.log("PRO STAY OFF");
                resolve("IT IS NOT PRO");
            }
        });
    });
    
}

 

 

 

 

 

 

 

Not applicable

This is an accepted solution.

Found what I was looking for thx to the help of Jack McCracken ^^

Maybe it helps someone else :

 

        const querySub = JSON.stringify({
            query: `{
                appInstallation {
                    activeSubscriptions {
                        status,
                        id
                    }
                }
            }`
        });
        return fetch(`https://${shop}/admin/api/2019-10/graphql.json`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                "X-Shopify-Access-Token": accessToken,
            },
            body: querySub
        }).then(response => response.json()).then(responseSub => {

 

DaviAreias
Shopify Partner
35 0 6

I found out that this solution will not hide app blocks in the theme editor.

 

Most app creators don't use app blocks anyway, but I don't understand why they don't explain this, since in my view app blocks are better for everyone.