Building with Gadget

Create a Shopify App with Mantle x Gadget

This guide will walk you through creating a Gadget-hosted Shopify app with Mantle integration for customer and billing management.

1. Create your Shopify app on Gadget

Start by creating a new Shopify app on Gadget:

  1. Head over to gadget.dev and create your Shopify app
  2. Set up your app with the basic Shopify integration

2. Add the app to Mantle

  1. In your Mantle dashboard, navigate to Apps
  2. Click Add app to create your new app
  3. Once added, go to the app Settings and create a new API key
  4. Add the app ID and API key to the Gadget environment variables:
    • GADGET_PUBLIC_MANTLE_APP_ID (with GADGET_PUBLIC_ prefix to make it available on frontend)
    • MANTLE_API_KEY (without the prefix since it should be kept secret)

To add these in Gadget, go to Settings -> Environment Variables.

3. Add Mantle package

  1. Open package.json in Gadget
  2. Add @heymantle/polaris to the dependency list:
    "dependencies": {
      "@heymantle/polaris": "^12"
    }
  3. Click Run yarn in Gadget to update dependencies

This package includes both @heymantle/react and @heymantle/client as dependencies.

4. Add mantleApiToken to the shopifyShop model

  1. In Gadget, navigate to API/models/shopifyShop/schema
  2. Click the plus button to add a new field
  3. Create a string field called mantleApiToken

5. Identify shops to Mantle when they install the app

First, create a helper file to handle Mantle API interactions:

  1. Create a new file at API/services/mantle.js with the following content:
import { MantleClient } from "@heymantle/client";

const mantleClient = new MantleClient({
  appId: process.env.GADGET_PUBLIC_MANTLE_APP_ID,
  apiKey: process.env.MANTLE_API_KEY,
});

const identifyShop = async ({ shop, api }) => {
  const { id, name, email, myshopifyDomain, accessToken } = shop;
  const result = await mantleClient.identify({
    platform: 'shopify',
    platformId: id,
    myshopifyDomain,
    accessToken,
    name,
    email,
  });
  await api.internal.shopifyShop.update(shop.id, {
    shopifyShop: {
      mantleApiToken: result.apiToken
    },
  });
};

module.exports = {
  identifyShop,
  mantleClient,
};

Now, add identification to your shop action files:

  1. Open each file in API/models/shopifyShop/actions: install.js, reinstall.js, and update.js
  2. Import the Mantle service at the top of each file:
    import { identifyShop } from '../../../services/mantle'
  3. Replace the existing onSuccess function with:
    export async function onSuccess({ params, record, logger, api, connections }) {
      await identifyShop({
        shop: record,
        api,
      });
    };

Now shops will be identified to Mantle whenever they're installed, reinstalled, or updated.

6. Add MantleProvider context to your frontend

Update your frontend to integrate the Mantle provider:

  1. Open WEB/components/App.jsx
  2. Add the Mantle imports:
    import { MantleProvider } from "@heymantle/react";
    import { useFindFirst } from "@gadgetinc/react";
  3. Modify the EmbeddedApp component to include the MantleProvider:
function EmbeddedApp() {
  const [{ data, fetching }] = useFindFirst(api.shopifyShop);

  if (fetching) {
    return (
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          height: "100%",
          width: "100%",
        }}
      >
        <Spinner accessibilityLabel="Spinner example" size="large" />
      </div>
    );
  }

  return (
    <MantleProvider
      appId={process.env.GADGET_PUBLIC_MANTLE_APP_ID}
      customerApiToken={data?.mantleApiToken}
    >
      <Outlet />
      <NavMenu>
        <Link to="/" rel="home">Shop Information</Link>
        <Link to="/about">About</Link>
      </NavMenu>
    </MantleProvider>
  );
}

7. Add plans page

Before adding a plans page to your app, set up the plans in Mantle:

  1. Go to your Mantle dashboard and select your app
  2. Navigate to Plans and click Add plan
  3. Create at least one plan

Now, create a plans page in your Gadget app:

  1. Create a new file at WEB/routes/plans.jsx with the following content:
import { Page, Layout } from "@shopify/polaris";
import { TitleBar } from "@shopify/app-bridge-react";
import { useNavigate } from "react-router-dom";
import { useMantle } from '@heymantle/react';
import { PlanCardStack, PlanCardType } from '@heymantle/polaris';

export default function () {
  const navigate = useNavigate();

  const { customer, plans, subscribe } = useMantle();

  return (
    <Page
      title="Plans"
      backAction={{
        content: "Shop Information",
        onAction: () => navigate("/"),
      }}
    >
      <TitleBar title="Select a plan" />
      <Layout>
        <Layout.Section>
          <PlanCardStack
            cardType={PlanCardType.Highlighted}
            customer={customer}
            plans={plans}
            onSelectPlan={async ({ plan, discount }) => {
              const subscription = await subscribe({ planId: plan.id, discountId: discount?.id, returnUrl: '/plans' });
              if (subscription.error) {
                console.error('Unable to subscribe: ', subscription.error);
              } else {
                open(subscription.confirmationUrl, "_top");
              }
            }}
          />
        </Layout.Section>
      </Layout>
    </Page>
  );
}

If you're using Mantle's Hosted Billing, you can use this simplified version instead:

import { useMantle } from "@heymantle/react";
import { useEffect } from 'react';

export default function () {
  const { createHostedSession } = useMantle();
  useEffect(() => {
    (async () => {
      const session = await createHostedSession({ type: 'plans' });
      open(session.url, "_self");
    })();
  }, []);
  return '';
}

To toggle between displaying the plans and account pages in Hosted Billing, simply set the type parameter to either plans or account.

Finally, add the route to your app:

  1. Update App.jsx to include the plans route:
    import PlansPage from "../routes/plans";
    
    // in function App()
    <Route path="/plans" element={<PlansPage />} />
    
    // in NavMenu
    <Link to="/plans">Plans</Link>

Wrapping it up

Congratulations! You now have a Gadget-hosted Shopify app with Mantle integration and a functional plans page. Your app can:

  • Identify customers to Mantle during installation
  • Display available plans to customers
  • Allow customers to subscribe to plans
  • Track customer data and subscriptions

This integration enables you to manage billing, track usage, and analyze customer behavior all through the Mantle dashboard. If you have questions or need assistance, reach out to the Mantle team at [email protected].


Did this page help you?