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:
- Head over to gadget.dev and create your Shopify app
- Set up your app with the basic Shopify integration
2. Add the app to Mantle
- In your Mantle dashboard, navigate to Apps
- Click Add app to create your new app
- Once added, go to the app Settings and create a new API key
- Add the app ID and API key to the Gadget environment variables:
GADGET_PUBLIC_MANTLE_APP_ID(withGADGET_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
- Open
package.jsonin Gadget - Add
@heymantle/polaristo the dependency list:"dependencies": { "@heymantle/polaris": "^12" } - 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
- In Gadget, navigate to API/models/shopifyShop/schema
- Click the plus button to add a new field
- 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:
- Create a new file at
API/services/mantle.jswith 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:
- Open each file in
API/models/shopifyShop/actions:install.js,reinstall.js, andupdate.js - Import the Mantle service at the top of each file:
import { identifyShop } from '../../../services/mantle' - Replace the existing
onSuccessfunction 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:
- Open
WEB/components/App.jsx - Add the Mantle imports:
import { MantleProvider } from "@heymantle/react"; import { useFindFirst } from "@gadgetinc/react"; - Modify the
EmbeddedAppcomponent 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:
- Go to your Mantle dashboard and select your app
- Navigate to Plans and click Add plan
- Create at least one plan
Now, create a plans page in your Gadget app:
- Create a new file at
WEB/routes/plans.jsxwith 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:
- Update
App.jsxto 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].
Updated over 1 year ago