Unoff supports two mutually exclusive payment systems. The template includes code for both — you must choose one and configure it before shipping. Mixing the two is not supported.
Uses the native figma.payments Canvas API. Figma handles the checkout UI — zero friction for users.
"payments" must be present in manifest.json:
{
"permissions": ["payments", "currentuser"]
}
Read the current user's payment status from the Canvas context:
figma.payments?.status.type
// 'PAID' | 'NOT_PAID' | 'UNKNOWN'
'UNKNOWN' means the status could not be determined (e.g. offline). Treat it as unpaid.
src/bridges/plans/payProPlan.ts handles checkout initiation:
const payProPlan = async (
interstitial: 'SKIP' | 'PAID_FEATURE' | 'UPGRADE' | 'TRIAL_ENDED' = 'SKIP'
) => {
await figma.payments
?.initiateCheckoutAsync({ interstitial })
.then(() => {
if (figma.payments?.status.type === 'PAID')
figma.ui.postMessage({
type: 'WELCOME_TO_PRO',
data: {
status: figma.payments.status.type,
id: figma.currentUser?.id,
},
})
})
}
'SKIP' — Goes directly to checkout. Use when triggered by an explicit "Upgrade" button click.'PAID_FEATURE' — Shows an upsell modal explaining the feature is paid. Use when the user tries to use a locked feature.'UPGRADE' — Shows an upgrade modal for seat-based plans.'TRIAL_ENDED' — Shows a trial-ended modal before checkout. Use after checkTrialStatus returns 'EXPIRED'.