nozzlegear / ShopifySharp

ShopifySharp is a .NET library that helps developers easily authenticate with and manage Shopify stores.
https://nozzlegear.com/shopify-development-handbook
MIT License
765 stars 313 forks source link

Annual Subscription Option #707

Open cannunziello opened 2 years ago

cannunziello commented 2 years ago

Hey,

I'm setting up my billing process and as far as I can tell there is no option for changing the interval for recurring charges.

Is this because "Annual subscriptions are not supported on the REST admin API." as stated on https://shopify.dev/apps/billing/subscriptions/annual#limitations

Thanks,

Caleb

nozzlegear commented 2 years ago

@cannunziello Hey! I wasn't actually aware that annual subscriptions were even possible, I had just been creating a regular charge for the price of 12 months and then tracking when my user needs to be charged again. Looking at your link though, it seems that annual subscriptions are possible but only with the Graph API. Most of the methods/classes/API calls in ShopifySharp were written before Shopify's Graph API existed, so by default almost everything is using the REST API.

However, we do have a GraphService that can be used to write Graph API calls! If you wanted to create an annual subscription with ShopifySharp, it would look something like this:

// Note: need to use double curly brackets or else String.Format will get confused and throw an exception
var mutation = @"
    mutation {{
        appSubscriptionCreate(
            name: "{0}"
            returnUrl: "{1}"
            lineItems: [
            {{
                plan: {{
                    appRecurringPricingDetails: {{
                        price: {{ 
                            amount: {2}, 
                            currencyCode: USD 
                        }}
                        interval: ANNUAL
                    }}
                }}
            }}
            ]
        ) {{
            appSubscription {{
                id
            }}
            confirmationUrl
            userErrors {{
                field
                message
            }}
        }}
    }}";

// Format the mutation with your variables
mutation = String.Format(mutation, "My plan name", "https://example.com/return-url", 10.00m);

// Create the subscription
var service = new ShopifySharp.GraphService(shopifyShopDomain, accessToken);
var response = await service.PostAsync(mutation);

// Check for errors
var userErrors = response.Item("userErrors");

if (userErrors != null)
{
    // TODO: handle error here
    var field = userErrors.Value<string>("field");
    var message = userErrors.Value<string>("message");

    throw new Exception("Could not create annual subscription. Shopify returned error '" + field + "': " + message);
}

// Get the subscription id and the confirmation url
var appSubscription = response.Item("appSubscription");
// Graph IDs are strings
string subscriptionId = appSubscription.Value<string>("id");
string confirmationUrl = response.Value<string>("confirmationUrl");

// Redirect your user to the confirmation url
return Redirect(confirmationUrl);

Try that and let me know if it works for you! I haven't tested it myself yet so there might be a bug in there. I'm also considering adding common Graph operations to ShopifySharp, and this seems like a good candidate for that.

Sky-bits commented 11 months ago

@nozzlegear how do check if the Shopify payment is accepted ? when redirecting only the req URL we can't get the any response data.

nozzlegear commented 11 months ago

@Sky-bits When you create the charge, you need to set a return url where Shopify will send the user after they've accepted or declined your charge. When they get sent to the return url, there up be a charge_id value in the query, which you'll need to use to look up the charge.

If the status is "active" then you're good to go and Shopify has activated and applied the charge. I believe there is no more "accepted" status, instead Shopify just activates charges as soon as they're accepted.

dgivoni commented 2 months ago

Would love to have this implemented