Skip to content
LogoLogo

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.Server

Configuration

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.

tempo.renewSubscription

Renews an overdue subscription outside the request path. See tempo.renewSubscription.