figma.payments
These are all defined on figma.payments
.
warning
payments
must be specified in the permissions array in manifest.json
to access this property.
{
"permissions": ["payments"]
}
If your manifest doesn't contain these fields, the payments API methods described below will throw errors if you try to use them.
status: PaymentStatus [readonly]
An object describing the user’s payment status. Right now, the only attribute on this object is whether the user has paid. In the future, we might add more attributes here to provide more information.
type PaymentStatus = {
type: "UNPAID" | "PAID"
}
In development, you’ll be able to test out the entire checkout flow without having to input any actual payment information. Doing so will update your payment status accordingly. Any changes to payment status in development is local and not persisted, and will be reset when restarting your client or using a different machine.
info
To test out your plugin or widget with payment statuses other than UNPAID
while developing, use the setPaymentStatusInDevelopment
function.
For published resources, this always returns PAID
for the creator.
setPaymentStatusInDevelopment(status: PaymentStatus): void
warning
This method can only be used in development.
This sets your payment status to the value of the status
argument in this
method. This is a global setting that will impact your payment status for
all plugins or widgets you run in development.
getUserFirstRanSecondsAgo(): number
When the plugin was first run by the current user.
This is defined as the number of seconds since the current user ran the plugin or widget for the first time. This will return 0 the very first time a user runs your plugin, and will always return 0 when running a plugin in development.
initiateCheckoutAsync(options?: { interstitial?: 'PAID_FEATURE' | 'TRIAL_ENDED' | 'SKIP' }): Promise<void>
This triggers a checkout flow in the Figma UI for the user to purchase your plugin or widget. The user will be prompted to enter their payment information and purchase your resource. This function resolves either when the user has completed the checkout flow, or they’ve dismissed it.
warning
This function will throw an exception in certain cases:
- While in query mode and accepting plugin parameters.
- During widget rendering. Instead, put calls to this function inside your widget event handlers.
See our guide for more information.
This function takes an options
argument that controls the behavior of the
checkout flow.
type CheckoutOptions = {
// This option controls the behavior and copy of the
// interstitial checkout modal.
//
// * PAID_FEATURE: This is the default. Use this option if
// you're asking the user to pay for a
// certain premium feature.
//
// * TRIAL_ENDED: Use this option if the user's free trial
// has ended.
//
// * SKIP: Use this option if you want to skip the
// interstitial entirely. This is useful if
// you have your own upgrade CTA in your
// plugin's UI.
interstitial?: "PAID_FEATURE" | "TRIAL_ENDED" | "SKIP"
}
After initiateCheckoutAsync
resolves, use figma.payments.status
to check
the user’s payment status.
requestCheckout(): void
This is useful for text review plugins. Since these
plugins can only run in query mode, they cannot call
initiateCheckoutAsync
while a user is editing text as that will throw an
exception.
if you are building a text review plugin, call requestCheckout
to
indicate that the user needs to checkout in order to continue using the
plugin. When the user exits text editing, they will be prompted to
checkout. If the user dismisses the checkout flow, the text review plugin
will automatically be disabled.
getPluginPaymentTokenAsync(): Promise<string>
This method generates a token that can be used to securely communicate the
identity of the current user on the current plugin or widget. You can
provide its returned value as the plugin_payment_token
query parameter to
the payments REST API endpoint.
Code Examples
Limiting free usage of the entire plugin to a number of days
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
const secondsSinceFirstRun = figma.payments.getUserFirstRanSecondsAgo()
const daysSinceFirstRun = secondsSinceFirstRun / ONE_DAY_IN_SECONDS
async function checkAndRunPluginFeatureCode() {
if (figma.payments.status.type === "UNPAID" && daysSinceFirstRun > 3) {
await figma.payments.initiateCheckoutAsync({
interstitial: "TRIAL_ENDED"
})
if (figma.payments.status.type === "UNPAID") {
figma.notify("Your free trial has expired, please upgrade to continue.")
return
}
}
// DO STUFF
}
Requiring payment for a feature of the plugin
async function checkAndRunPaidFeatureCode() {
if (figma.payments.status.type === "UNPAID") {
await figma.payments.initiateCheckoutAsync({
interstitial: "PAID_FEATURE"
})
if (figma.payments.status.type === "UNPAID") {
figma.notify("Please upgrade to use this feature.")
return
}
}
// DO STUFF
}
Limiting free usage to a number of runs
async function checkAndRunPluginFeatureCode() {
if (figma.payments.status.type === "UNPAID") {
const usageCount = await figma.clientStorage.getAsync('usage-count') || 0
if (usageCount >= 10) {
await figma.payments.initiateCheckoutAsync({
interstitial: "TRIAL_ENDED"
})
if (figma.payments.status.type === "UNPAID") {
figma.notify("You have run out of free usages of this plugin.")
return
}
} else {
await figma.clientStorage.setAsync('usage-count', usageCount + 1)
}
}
// DO STUFF
}