Multiple issues with Managed App Pricing (Remix and not only)

Hello, I would like to use this post to report our findings and the issues encountered while integrating managed app pricing with our app.

  1. Incorrect object structure returned for billing

We were able to replicate this once during the first charge created for this app. All subsequent charges or plan changes were unaffected, and we were unable to reproduce it again. The issue was that billing.check() returned an object with an incorrect structure, differing from both a) subsequent results and b) TypeScript types defined in @shopify/shopify-api. This was the structure observed for the first plan charge:

{
  hasActivePayment: true,
  oneTimePurchases: [],
  appSubscriptions: [
    {
      id: "gid://shopify/AppSubscription/4242234365767624",
      name: "Starter",
      test: true,
      status: "ACTIVE",
      lineItems: [
        {
          id: "323322?V=1&index=0",
          plan: {
            pricingDetails: {
              pricing: {
                interval: "EVERY_30_DAYS",
                discount: null,
                amount: "11.0",
                currencyCode: "USD",
              },
            },
          },
        },
      ],
    },
  ],
};

The problem is that interval and discount were located inside the pricing object, whereas in the type and all subsequent invocations (after changing the plan) had the correct structure, with these fields under pricingDetails.

  1. Type mismatch for the amount field between types in the @shopify/shopify-api package and the actual values returned: amount is typed as a number but appears as a string in the response.

  2. It is unclear how to test an expired subscription and/or understand how the lineItems would change in the case of a failed or expired payment. Additionally, the role of hasActivePayment is unclear — does it reflect an active payment method (e.g., an expired card) or just the subscription status? What about the status field?

  3. Currently, the only way for a store to cancel an app subscription with managed pricing is by uninstalling the app. Will this be an issue during app submission/review? We would like to follow best practices and integrate with the new managed pricing approach, but this scenario is not mentioned anywhere in the docs.

We would appreciate it if someone from the Shopify team could clarify these points and flag type/object mismatches to the development team.

Hi @edcode

  1. Would you mind sharing the reproduction steps that make billing.check() returns an object with incorrect structure? I’ve been trying with a new Remix app but was unable to reproduce it.
  2. The amount should be string because according to the GraphQL schema, it’s returning decimal and serialized into string. We should follow the schema as close as possible.
  3. hasActivePayment indicates that there’s a subscription matching certain plans if a an array of plans is passed in the .check() function shopify-app-js/packages/apps/shopify-api/docs/migrating-to-v12.md at main · Shopify/shopify-app-js · GitHub . It’s more of a utility method, you can also run any checks manually if you don’t want to use the property.

I’ll follow up more on how lineItems would change in case of failed or expired payment.

  1. I’ll follow up with the team for an answer.
1 Like

Hi @Si_Le, I appreciate your response and reviewing the points:

  1. We can no longer reproduce it, but we have enabled logging. If we manage to capture it again, I will inform here.
  2. I suppose that’s a bug in the @shopify/shopify-api package in this case, as that’s how it’s defined there:
export interface Money {
    amount: number;
    currencyCode: string;
}

Hey @edcode ,

If merchant have an active subscription and their payment fails, their AppSubscription’s status will turn to FROZEN. Then, you’ll need to get allSubscriptions to get the FROZEN subscription.

There isn’t a way to test this failed payment flow right now. The only way to cancel the subscription is to uninstall the app currently and we’re aware of this limitation.

1 Like