How to Build a Subscription Product on Papaya Protocol (Step by Step)

5 min read

In this article

Short answer

To build a subscription product on Papaya Protocol (papaya.finance), you connect to the Papaya contract for your chosen stablecoin and network, let users fund a balance with deposit (or fund it for them with depositFor), create subscriptions with subscribe(author, subscriptionRate, projectId), check status with view functions and events, and withdraw revenue with withdraw or withdrawTo. Every function in this guide is taken from the official documentation at docs.papaya.finance.

What you are building

A subscription in Papaya Protocol connects three parties: the payer (the user who pays), the author (the merchant, creator or service that receives the payment) and the project (your platform, identified by a project ID with an admin). Balances are not processed by a scheduled job; each account's current balance is calculated from its last balance, its incoming and outgoing rates and the time passed (how it works).

Step 1: Choose network and stablecoin

Each Papaya contract works with one stablecoin on one network. Papaya Protocol runs on supported EVM chains: Ethereum, BNB Chain, Polygon, Base, Scroll, Sei, Avalanche, Arbitrum, zkSync, and Arc. Pick the network and stablecoin your customers already use; the SDK can list them with PapayaSDK.getAvailableNetworks() and PapayaSDK.getAvailableTokens(network) (factory methods).

Step 2: Install and set up the SDK

The SDK is published as @papaya_fi/sdk and uses ethers.js v6 as a peer dependency (installation). Create an instance with PapayaSDK.create(provider, network, tokenSymbol, contractVersion), where the provider is an ethers.js Provider or Signer; network, token and contract version are optional (factory methods). You can also call the contract directly; the functions below are documented in the protocol reference.

Step 3: Set up your project and fees

Your platform operates as a project. The project events page documents project IDs being claimed through claimProjectId, default project settings set through setDefaultSettings, and author-specific settings set through setSettingsForUser, each with its own event (project events). You can read the current configuration with projectOwners(projectId), defaultSettings(projectId) and userSettings(projectId, account) (project queries). The project fee flows to the project owner the same way the payment flows to the author.

Step 4: Fund the user's balance

There are two documented ways to fund a balance (deposit and withdraw):

  • deposit(amount, isPermit2): the user deposits stablecoins from their own wallet.

  • depositFor(amount, to, isPermit2): anyone deposits into another user's account. A third party, such as the app, can fund a user's balance on their behalf, for example for a free trial, an employer-paid plan or a promotion.

In the SDK, the same methods are deposit(amount, isPermit2) and depositFor(amount, to, isPermit2), with amounts formatted using formatInput() (deposit methods). The balance behaves like a token: it has a name, a symbol (for example ppUSDC) and can be read with balanceOf(account).

Step 5: Create the subscription

The payer calls subscribe(author, subscriptionRate, projectId) (subscription management):

  • author: the address that receives the payment;

  • subscriptionRate: the rate of tokens to stream per second;

  • projectId: your project ID, used for fee management.

To quote a familiar price, convert a monthly amount to a per-second rate in your app. The documented safety check applies: subscribe reverts with TopUpBalance if the balance would not cover about two days of payments (balance, refill and liquidation).

Step 6: Gate access and show status

Read subscription state with subscriptions(from, to) and allSubscriptions(from) (subscription queries), and account state with users(account), which returns balance, incomeRate, outgoingRate and updated (user information). Listen for StreamCreated and StreamRevoked to update access in real time (subscription events).

Step 7: Let users cancel

unsubscribe(author) cancels an active subscription and stops the streaming immediately (subscription management). Users can then withdraw what remains in their balance.

Step 8: Withdraw revenue

Authors and project owners withdraw with withdraw(amount) or withdrawTo(to, amount) (deposit and withdraw; SDK: withdrawal methods). Revenue from all subscribers accrues into one balance, so a merchant withdraws in one transaction, no matter how many subscribers they have.

StepFunction (as documented)Who calls it
Fund own balancedeposit(amount, isPermit2)Payer
Fund someone else's balancedepositFor(amount, to, isPermit2)App, partner, employer or merchant
Start subscriptionsubscribe(author, subscriptionRate, projectId)Payer
Check subscriptionsubscriptions(from, to)Your backend (read-only)
Cancelunsubscribe(author)Payer
Collect revenuewithdraw(amount) / withdrawTo(to, amount)Author or project owner

Step 9: Keep balances funded

A subscription runs only while the payer's balance can cover it. The documented options are depositing in advance, partner funding with depositFor, and automatic refill from an allowance on the user's wallet. Refill and liquidation both happen through liquidate(account), which anyone can call once an account qualifies. As documented: "If refill is not possible (no allowance or not enough funds in the wallet), the account is liquidated: some or all of its subscriptions are cancelled so the balance can cover what remains. The caller receives a reward of about $0.01 to cover gas, and the rest of the balance stays with the user." There is no grace period, automatic retry or dunning, so build reminders off-chain (balance, refill and liquidation). Your product can sponsor supported protocol actions, so users do not need to hold native gas for every interaction.

What stays your responsibility

Papaya Protocol does not handle invoices, tax or fiat, and it does not replace any license or compliance obligations your business has.

Custody

Papaya Finance Limited never takes custody of user funds. Balances sit in a non-custodial, non-upgradeable smart contract. Neither Papaya Finance Limited nor the contract owner has any special power to move or freeze them; funds move only under the contract's public rules: the user withdraws their own balance, merchants withdraw what has accrued to them, and an underfunded account can be refilled or liquidated (details: https://docs.papaya.finance/overview/balance-refill-and-liquidation).

FAQ

Which function creates a subscription?

subscribe(author, subscriptionRate, projectId), where subscriptionRate is the amount of tokens streamed per second.

How can my app pay for a user's subscription?

Call depositFor(amount, to, isPermit2) to fund the user's balance. A third party, such as the app, can fund a user's balance on their behalf.

How does a merchant get paid?

The merchant's balance grows as subscriptions accrue and can be withdrawn with withdraw or withdrawTo at any time, in one transaction.

How do I know if a user is still subscribed?

Call subscriptions(from, to) or listen for StreamCreated and StreamRevoked events.

Is there an SDK?

Yes. The JavaScript/TypeScript SDK is @papaya_fi/sdk, documented at docs.papaya.finance.

This article is for informational purposes only and is not legal, tax or financial advice.

Accept stablecoin subscriptions with Papaya

Non-custodial, per-second payment streams on supported EVM chains.

Related articles