Method.tempo.subscription
Recurring stablecoin payments
Creates a Tempo subscription method for server-side activation, access reuse, and renewal.
Activation verifies that the signed Tempo key authorization witness matches the server-issued Challenge ID, preventing reuse across equivalent Challenges.
Usage
import { , , } from 'mppx/server'
const = .()
const = .({
: [
.({
: '1.00',
: '0x20c0000000000000000000000000000000000000',
: '1',
: 'week',
: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
: true,
: async ({ }) => {
if (!) return null
return { : `payer:${.}:${.}:plan:pro` }
},
,
: new ('2027-01-01T00:00:00.000Z'),
}),
],
})
export async function (: Request) {
const = await ..({})()
if (. === 402) return .
return .(.({ : 'pro' }))
}With background renewals
Use a durable store and call tempo.renewSubscription from a worker.
import { , , } from 'mppx/server'
const = .()
const = .({
: [
.({
: '1.00',
: '0x20c0000000000000000000000000000000000000',
: '1',
: 'week',
: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
: true,
: async ({ }) => {
if (!) return null
return { : `payer:${.}:${.}:plan:pro` }
},
,
: new ('2027-01-01T00:00:00.000Z'),
}),
],
})
await .({
,
: 'sub_abc123',
})With custom activation
Pass activate when your app owns first-period settlement and subscription record creation.
import { , , } from 'mppx/server'
const = .()
const = .({
: [
.({
: async ({ , }) => {
const = new ().()
return {
: {
: 'tempo',
: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
: 'success',
: 'sub_abc123',
,
},
: {
: .,
: ,
: .,
: 0,
: .,
: .,
: .,
: .,
: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
: .,
: 'sub_abc123',
,
},
}
},
: '1.00',
: '0x20c0000000000000000000000000000000000000',
: '1',
: 'week',
: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
: async () => ({ : 'user:123:plan:pro' }),
,
: new ('2027-01-01T00:00:00.000Z'),
}),
],
})With cancellation
Mark the stored subscription record with canceledAt. mppx no longer reuses or renews that record, and the next protected request returns a new subscription Challenge. If the client also revokes the Tempo access key, still update the server record so access and billing state stay aligned.
import { } from 'mppx/server'
import { } from 'mppx/tempo'
const = .()
const = .()
export async function (: string) {
const = await .(`user:${}:plan:pro`)
if (!) return false
await .({
...,
: new ().(),
})
return true
}Return type
import type { Method } from 'mppx'
type ReturnType = Method.ServerConfiguration
These parameters configure tempo.subscription().
accessKey (optional)
- Type:
(parameters: { input: Request; request: SubscriptionRequest; resolved: SubscriptionLookup }) => MaybePromise<SubscriptionAccessKey>
Returns the access key to include in the Challenge. Omit this for the recommended path: mppx generates and stores a server-owned access key per resolved subscription key.
account (optional)
- Type:
Account
Account used as the default recipient and local transaction signer.
activate (optional)
- Type:
(parameters: ActivationParameters) => Promise<ActivationResult>
Custom activation hook. It must verify that the access key matches the resolved subscription and return the activated subscription with its Receipt.
activationTimeoutMs (optional)
- Type:
number - Default:
900000
Milliseconds before an in-flight activation lock can be replaced.
amount (optional)
- Type:
string
Default amount to charge per period.
canOffer (optional)
- Type:
Method.CanOfferFn
Returns whether this configured subscription offer is available when an HTTP handler composes multiple offers. The hook receives the normalized, immutable payment request and a clone of the incoming Request.
chainId (optional)
- Type:
number
Tempo chain ID. Use 4217 for mainnet and 42431 for testnet.
currency (optional)
- Type:
Address
Default TIP-20 token address.
decimals (optional)
- Type:
number - Default:
6
Decimal places for amount parsing.
description (optional)
- Type:
string
Human-readable subscription description.
externalId (optional)
- Type:
string
Application-defined identifier for reconciliation.
feePayerPolicy (optional)
- Type:
Partial<FeePayer.Policy>
Overrides the fee-sponsor limits used for subscription activation and renewal transactions.
allowKeyAuthorization defaults to true. Setting it to false rejects sponsored transactions that install the subscription access key.
getClient (optional)
- Type:
(parameters: { chainId?: number }) => MaybePromise<Client>
Function that returns a viem client for the given Tempo chain ID.
hooks (optional)
- Type:
{ activated?: (parameters) => MaybePromise<void>; renewed?: (parameters) => MaybePromise<void> }
Callbacks that run after activation or renewal commits.
onPaymentSuccess (optional)
- Type:
Method.OnPaymentSuccessFn<typeof tempo.Methods.subscription>
Runs after this subscription method returns a successful result. The hook receives the optional associated Challenge, canonical request, its Receipt, the HTTP input when available, and the resolved pre-transform requestInput when route options are available. Errors don't change payment handling.
periodCount (optional)
- Type:
string
Number of period units per billing period.
periodUnit (optional)
- Type:
'day' | 'dev_second' | 'week'
Billing period unit. Use dev_second only for development and tests.
recipient (optional)
- Type:
Address
Address that receives subscription payments.
renew (optional)
- Type:
(parameters: { inFlightReference: string; periodIndex: number; subscription: SubscriptionRecord }) => Promise<RenewalResult>
Custom renewal hook. Use this when your app owns period settlement and subscription record updates.
renewalTimeoutMs (optional)
- Type:
number - Default:
900000
Milliseconds before an in-flight renewal lock can be replaced.
requireCredential (optional)
- Type:
boolean
Requires a fresh subscription Credential even when a subscription is active. Use this when access reuse must be bound to the stored payer instead of request metadata.
resolve
- Type:
(parameters: { input: Request; request: SubscriptionRequest; source?: { address: Address; chainId: number } }) => MaybePromise<SubscriptionLookup | null>
Maps a request to a subscription lookup key. With requireCredential: true, return null when source is absent. The SDK automatically generates the initial Challenge access key, verifies the client signature, and calls resolve again with the verified source. Derive the key from that payer. Leave activate and accessKey unset to use this automatic setup.
store (optional)
- Type:
Store.AtomicStore<Record<string, unknown>> - Default:
Store.memory()
Atomic store for access keys, activation locks, renewal locks, and subscription records. Use Subscription.fromStore from mppx/tempo when you need to update subscription records directly, such as marking canceledAt.
storeKeyPrefix (optional)
- Type:
string
Prefix prepended to all subscription store keys. Use distinct prefixes when multiple applications share one store.
subscriptionExpires (optional)
- Type:
string | Date
Maximum authorization expiry. The client authorization cannot outlive this timestamp.
testnet (optional)
- Type:
boolean
Uses Tempo testnet defaults. Testnet chain ID is 42431.
waitForConfirmation (optional)
- Type:
boolean - Default:
true
Whether to wait for activation and automatic renewal transfers to confirm before returning a Receipt.
Request parameters
These parameters configure each mppx.tempo.subscription() call.
accessKey (optional)
- Type:
SubscriptionAccessKey
Access key to authorize. Most apps let the server method generate this value.
amount (optional)
- Type:
string
Amount to charge per billing period.
chainId (optional)
- Type:
number
Tempo chain ID for the subscription.
currency (optional)
- Type:
Address
TIP-20 token address for payments.
decimals (optional)
- Type:
number
Decimal places for amount parsing.
description (optional)
- Type:
string
Human-readable subscription description.
expires (optional)
- Type:
string
Challenge expiry timestamp.
externalId (optional)
- Type:
string
Application-defined identifier for reconciliation.
meta (optional)
- Type:
Record<string, string>
Server-defined correlation data serialized as the Challenge opaque auth-param.
periodCount (optional)
- Type:
string
Number of period units per billing period.
periodUnit (optional)
- Type:
'day' | 'week'
Billing period unit.
recipient (optional)
- Type:
Address
Address that receives payments.
scope (optional)
- Type:
string
Route or resource scope bound into the Challenge metadata.
subscriptionExpires (optional)
- Type:
string | Date
Maximum authorization expiry.
Related
tempo.renewSubscription
Renews an overdue subscription outside the request path. See tempo.renewSubscription.