Hi everyone,
I’m currently building a Shopify public app using Remix and have implemented basic billing logic. Here’s a simplified version of my action function for creating a usage record:
import { ActionFunctionArgs } from "@remix-run/node";
import { authenticate, MONTHLY_PLAN } from "../shopify.server";
export const action = async ({ request }: ActionFunctionArgs) => {
const { billing } = await authenticate.admin(request);
const chargeBilling = await billing.createUsageRecord({
description: "Usage record for product creation",
price: {
amount: 1,
currencyCode: "USD",
},
isTest: true,
});
console.log(chargeBilling);
// App logic
};
I would appreciate guidance on:
- Best practices for implementing usage-based billing, including setting usage limits or thresholds.
- Whether time-based billing (e.g., charging based on active time or intervals) is supported, and if so, how best to structure it.
- Any specific recommendations or gotchas when using
billing.createUsageRecord() with the Shopify Billing API.
- How to tie these billing events to specific user actions (e.g., creating a product) while staying compliant with Shopify’s billing policies.
Thanks in advance for your help!
The code is actually missing a required parameter: subscriptionLineItemId. Before you can call createUsageRecord(), you need an active subscription in place, and the line item ID from that subscription has to be passed on every call. The flow is: merchant approves your usage plan via billing.request() or billing.require(), you get back the AppSubscriptionLineItem.id, and that ID is what links each record to the right billing cycle. Without it the call will fail in production even if it appears to run in test mode (Create usage-based subscriptions).
For usage caps, set cappedAmount when you create the subscription, not on the usage record itself. Shopify enforces it automatically and records that would exceed the cap are silently not charged with no error thrown, which is easy to miss when debugging. Subscribe to APP_SUBSCRIPTIONS_APPROACHING_CAPPED_AMOUNT at 90% and APP_SUBSCRIPTIONS_UPDATE for when merchants change the cap from their admin. On time-based billing: the only supported model is a flat recurring fee combined with usage records, 30 day intervals only. There is no native per hour or per session billing natively (Combine time and usage-based subscriptions).
For tying records to user actions, keep it server side in your action function, which you already are, and always pass an idempotencyKey. A hash of the shop domain plus the specific event ID and a timestamp works well to prevent double billing on retries. I work with a lot of Shopify integrations at Stacksync and the missing idempotencyKey is probably the second most common billing bug after the subscriptionLineItemId issue. One more thing on isTest: true: cap enforcement still applies in test mode, so if you run many test records against a low cap on a test subscription, some will silently not fire.
Hope this helps!