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
743 stars 309 forks source link

Big problem with UninstallAppAsync and webhooks delete #713

Open davidkdb opened 2 years ago

davidkdb commented 2 years ago

Hi,

We are in a Catch-22 situation.

One of our customers has a shop where the status is "Payment required". It seems to be closed.

But they have not uninstalled our app, and we keep getting webhooks from them.

We want to force an app uninstallation (UninstallAppAsync), but ShopifySharp fails with the status "Payment required".

We also tried to remove the webhooks, with the same result.

Any way around this with ShopifySharp? Not an option to wait for webhook deletion because this will take days, and it fills up our error log, and gives so many entries in the webhook failure log in partner Dashboard.

nozzlegear commented 2 years ago

Hmm as far as I know there's no way to make an API call when the shop is in this payment limbo. Unfortunately I don't think there's anything we could do with ShopifySharp to fix this. Your best bet would probably be contacting Shopify support to ask if they have a better solution than just waiting until the webhooks expire, but in my experience the webhooks will probably be expired by the time they've had a chance to respond to your request.

One thing I've done in this situation is create an attribute called [CatchShopifyExceptions] and decorate your webhook controller with it. Then inside that attribute you can check to see what kind of exception was thrown, and if it was a "Payment required" one you could just log a warning, discard the exception and have the controller return a 200 OK response so the webhook won't be called over and over. It would look something like this:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ShopifySharp;

namespace YourApp.Attributes
{
    /// <summary>
    /// A filter attribute that will catch all exceptions thrown by ShopifySharp. 
    /// </summary>
    public class CatchShopifyExceptionsAttribute : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            if (context.ExceptionHandled || !context.HttpContext.Response.Body.CanWrite)
            {
                return;
            }

            if (context.Exception is ShopifyException ex)
            {
               var logger = (ILogger) context.HttpContext.RequestServices.GetService(typeof(ILogger<CatchShopifyExceptionsAttribute>));

                switch ((int) ex.StatusCode)
                {
                    case 402:
                        // Payment required status code
                        logger.LogWarning("Caught payment required exception");
                        context.ExceptionHandled = true;
                        context.Result = new OkResult();
                        break;

                    default:
                        // Let the filter throw the exception
                        context.ExceptionHandled = false;
                        break;
                }
            }
        }
    }
}