MeridianPricingPage renders your whole plan grid from the plans you built in the plan builder: prices, trials, features, events, discounts, and the subscribe flow.
MeridianProvider ancestor, and it renders Polaris web components, so it must run inside an embedded Shopify app with App Bridge loaded, which every Shopify app template provides. Outside the embedded admin the elements are unregistered and render as unstyled text, with a console warning.
The returnUrl
returnUrl is where Shopify sends the merchant after they approve or decline the charge. It must be an https URL on the shop’s own admin:
Props
What a merchant sees
Only active plans appear; drafts stay hidden until you activate them. Prices are converted to the shop’s currency before they reach the browser. Each card carries the plan name, its price for the selected cycle, its trial badge, and one line per feature and per metered event included in it. The card for the shop’s current plan is outlined, and its button is disabled. A metered event’s line says what usage costs, so a merchant knows it before reaching Shopify’s approval screen:
Amounts are in the shop’s currency, and the rate is the one Meridian states in the usage terms Shopify asks the merchant to approve for the same plan (see What the merchant approves). It is converted exactly and never rounded to the cent: 0.10 USD in a euro store at an FX rate of 0.92 reads 0,092 €, not 0,09 €. A rate under one cent is stated per ten times the units until it reaches a cent, so 0.005 per translation reads “0.04 per 100 translations”. The cap is in cents, the spending limit sent to Shopify.
The words come from the event’s Unit in the catalog, pluralized for any count other than one (“per translation” for a rate on a single unit). An abbreviation such as
GB and a unit that already ends in “s” are left as you typed them. An event with no unit uses its name instead, as written.
A priced usage view states its allowance and its cap but not its rate, because a view’s rate can be a share of money (0.02 is 2% of GMV) rather than a price per unit: “Gross merchandise value: 1,000 included, then billed on usage, up to $200.00 per month”.
The button label reads the shop’s current plan rather than saying “Subscribe” to everyone:
The comparison uses the monthly price, so the label holds when the merchant toggles to yearly.
Monthly and yearly
The Monthly / Yearly toggle appears only when at least one of your plans has a yearly price. A plan without one renders and bills monthly even while the toggle is on yearly; a plan with one shows a badge for the amount saved against twelve monthly payments. A plan priced at zero renders as Free, with no billing period and no trial badge.Discounts
The page handles both kinds of discount without any wiring from you. Automatic discounts are previewed per plan for the selected cycle, so the price shown is the price charged. Codes are entered behind an Add promo code control: the page validates a code against every plan at once and shows the discounted price on the cards it applies to. A typed code takes precedence over an automatic one, and the backend re-validates at subscribe time. An invalid code reports on the field alone, never revealing whether another code exists. Shopify bakes a discount into a subscription when it is created, so one cannot be added to a subscription the shop is already on. On the current plan’s card, a preview is labelled either applied to your subscription or for new subscriptions, so a merchant is never shown an offer they cannot take.Subscribing
Picking a plan callssubscribe, which returns the Shopify confirmation URL the merchant has to approve. The page opens it in the top frame.
A free plan has no approval step: it activates immediately, returns no confirmation URL, and the page refreshes so the new plan shows at once. onSubscribed fires then.
Coming back from Shopify
After the merchant approves the charge, Shopify sends them back to yourreturnUrl with a charge_id in the URL. The page reads it and asks Meridian to confirm the charge with Shopify directly, rather than waiting for Shopify’s subscription webhook, which can land a minute or more later. The merchant sees no new message at any point:
- The plan cards stay on screen. The button of the plan the merchant approved shows its loading state, and every other plan button is disabled, so a second subscribe cannot start. When the page cannot tell which plan was approved yet, all plan buttons are disabled and none shows the loading state.
- As soon as the charge is active, the page refreshes in place and the card reads Current plan. This usually takes a second or two.
- If the merchant declined the charge, or it expired, the buttons come back as they were.
- If nothing is settled after 60 seconds, the buttons are enabled again. The page keeps checking in the background every 15 seconds, still without a message, until 5 minutes after the return, and turns to Current plan by itself if the charge settles meanwhile.
charge_id from the URL, so a reload does not start the check again. Confirming records the subscription exactly as Shopify’s webhook would, so your automations fire once, whichever arrives first.
This only works when your returnUrl points at the route that renders MeridianPricingPage, as in the examples above. Before sending the merchant to Shopify, the page also remembers the chosen plan in the browser’s session storage: that is how it knows which button to show as loading, and it lets the page run one silent check even if the return URL carries no charge_id.
Meridian records the subscription and holds the Shopify billing handshake, so your app never implements the recurring-charge or usage-charge APIs. Once the subscription is active, feature gates and usage allowances resolve against the new plan.
Building your own instead
PassonPlanSelected to intercept the click, or skip the component: read plans from useMeridian and call subscribe yourself. The plan objects carry everything the built-in page renders, including each plan’s features and its events’ included quantity, overage rate and cap.