Integrate your Gadget-hosted Shopify app with Mantle
1. Create your Shopify app on Gadget
Head over to gadget.dev and create your Shopify app.
In order to use the Shopify Billing API, apps must have a public distribution setting. Go to your Shopify partner dashboard, select your app, select the Choose Distribution button and then select Public distribution.
2. Add the app to Mantle
In your Mantle dashboard, under Apps hit the Add app button to add this new app. Once added, go to Settings for the app and create a new API key.
Add the app ID and API key to GADGET_PUBLIC_MANTLE_APP_ID
and MANTLE_API_KEY
variables in Gadget Settings -> Environment Variables. We’re using GADGET_PUBLIC_
in front of the app ID environment variable because we want it available on the frontend, do not use this in front the of the API key since it should be kept secret.
3. Add Mantle package
In Gadget, open package.json
and add @heymantle/polaris
to the dependency list:
"@heymantle/polaris": "^12"
This includes @heymantle/react
and @heymantle/client
as dependencies.
Once you’ve added the Mantle dependency, select Run yarn to update dependencies in Gadget.
4. Add mantleApiToken to the shopifyShop model
In Gadget, under API/models/shopifyShop/schema
, hit the plus button to add a string field called mantleApiToken
.
5. Identify shops to Mantle when they install the app
First, add a helper file API/services/mantle.js
:
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, under API/models/shopifyShop/actions
for each file install.js
, reinstall.js
, update.js
we want to identify the shop to Mantle:
import { identifyShop } from '../../../services/mantle'
// replace onSuccess with this
export async function onSuccess({ params, record, logger, api, connections }) {
await identifyShop({
shop: record,
api,
});
};
Shops will now be identified to Mantle and you’ll have the Mantle customer API token that you can use in frontend requests.
6. Add MantleProvider context to your frontend
Next, we’ll need to add the MantleProvider
context to your react frontend to be able to access mantle customer information and performance actions such as subscribe
. In WEB/components/App.jsx
you’ll need to load the current authenticated shopifyShop object in the Embedded
components to get the Mantle customer API token mantleApiToken
from that object. With this you’ll surround the returned components in Embedded
ith MantleProvider
and include the appropriate props:
import { MantleProvider } from "@heymantle/react";
import { useFindFirst } from "@gadgetinc/react";
// ......
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
Next you’ll want to add a couple plans so there’s something to show on this page, hop on back to your Mantle dashboard, select your test app you created in Step 2, under Plans hit the Add plan button and add a couple test plans.
Now, to show these plans to your customers in your app, add a new file WEB/routes/plans.jsx
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>
);
}
Or if you’re using Hosted Billing, you can display your plans or account page with the following code:
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 '';
}
You’ll also need to add the route in App.jsx
import PlansPage from "../routes/plans";
// in function App()
<Route path="/plans" element={<PlansPage />} />
// in NavMenu
<Link to="/plans">Plans</Link>
Wrapping it up
That’s it! You now have a Gadget-hosted Shopify app with Mantle integrated, and a plans page. As always, reach out to us if you have any questions!