# Overview

Papaya Subscription Protocol is a streaming payment protocol that enables real-time token streaming between users. The protocol supports subscription-based payments, project management, and advanced features like BySig operations and sponsored calls

### Key Features

* **Streaming Payments**: Real-time token streaming between users
* **Subscription Management**: Create and manage payment subscriptions
* **Project Management**: Multi-project support with custom settings
* **Advanced Operations**: BySig, sponsored calls, and permit operations
* **Liquidation System**: Automated liquidation for underfunded accounts

### Documentation Structure

This documentation is organized into the following sections:

* **Core Functions**: Basic deposit, withdraw, subscription, and payment operations
* **Project Management**: Project settings and ownership management
* **Advanced Features**: BySig operations, sponsored calls, and permit functionality
* **View Functions**: Read-only functions for querying contract state
* **Events**: Contract events for monitoring state changes
* **Errors**: Error codes and their meanings

### Support

For questions, issues or feature requests, please open an issue on our GitHub repository or [contact us](https://t.me/owlhootsgame)

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-screenpal">:screenpal:</i></h4></td><td><strong>Core Functions</strong></td><td></td><td></td><td><a href="/pages/96xSfZHzFuFwSio5BWsT">/pages/96xSfZHzFuFwSio5BWsT</a></td></tr><tr><td><h4><i class="fa-bars-progress">:bars-progress:</i></h4></td><td><strong>Project Management</strong></td><td></td><td></td><td><a href="/pages/V4aKduKLhyRBnncu9r1Y">/pages/V4aKduKLhyRBnncu9r1Y</a></td></tr><tr><td><h4><i class="fa-graduation-cap">:graduation-cap:</i></h4></td><td><strong>Advanced Features</strong></td><td></td><td></td><td><a href="/pages/qSFEmRDc6F7WOS0VU3N5">/pages/qSFEmRDc6F7WOS0VU3N5</a></td></tr><tr><td><h4><i class="fa-telescope">:telescope:</i></h4></td><td><strong>View Functions</strong></td><td></td><td></td><td><a href="/pages/vOTHcODvZZlvqbejtMd9">/pages/vOTHcODvZZlvqbejtMd9</a></td></tr><tr><td><h4><i class="fa-calendar-exclamation">:calendar-exclamation:</i></h4></td><td><strong>Events</strong></td><td></td><td></td><td><a href="/pages/LOQswz7PR8lNRpaqTaNR">/pages/LOQswz7PR8lNRpaqTaNR</a></td></tr><tr><td><h4><i class="fa-triangle-exclamation">:triangle-exclamation:</i></h4></td><td><strong>Errors</strong></td><td></td><td></td><td><a href="/pages/EBbHRGiYH6TXHBLnlaUJ">/pages/EBbHRGiYH6TXHBLnlaUJ</a></td></tr></tbody></table>


# Core Functions


# Deposit & Withdraw

This section covers the core fund management functions for depositing and withdrawing tokens from the Papaya protocol.

### deposit

Deposits tokens into the user's account for streaming payments.

```solidity
function deposit(uint256 amount, bool isPermit2) external
```

#### Parameters

* `amount` (uint256): The amount of tokens to deposit
* `isPermit2` (bool): Whether to use Permit2 for the transfer

#### Description

This function allows users to deposit tokens into their Papaya account. The tokens are transferred from the caller's address to the contract. If `isPermit2` is true, the function uses the Permit2 protocol for the transfer.

#### Example

```javascript
// Deposit 1000 tokens using standard transfer
await papayaContract.deposit(ethers.utils.parseEther("1000"), false);

// Deposit using Permit2
await papayaContract.deposit(ethers.utils.parseEther("1000"), true);
```

### depositFor

Deposits tokens into a specific user's account.

```solidity
function depositFor(uint256 amount, address to, bool isPermit2) external
```

#### Parameters

* `amount` (uint256): The amount of tokens to deposit
* `to` (address): The address to deposit tokens for
* `isPermit2` (bool): Whether to use Permit2 for the transfer

#### Description

This function allows depositing tokens into another user's account. This is useful for applications that want to fund user accounts on their behalf.

#### Example

```javascript
// Deposit 500 tokens for another user
await papayaContract.depositFor(
  ethers.utils.parseEther("500"),
  "0x1234...",
  false
);
```

### withdraw

Withdraws tokens from the user's account.

```solidity
function withdraw(uint256 amount) external
```

#### Parameters

* `amount` (uint256): The amount of tokens to withdraw

#### Description

Allows users to withdraw their deposited tokens back to their address. The tokens are transferred from the contract to the caller's address.

#### Example

```javascript
// Withdraw 100 tokens
await papayaContract.withdraw(ethers.utils.parseEther("100"));
```

### withdrawTo

Withdraws tokens from the user's account to a specific address.

```solidity
function withdrawTo(address to, uint256 amount) external
```

#### Parameters

* `to` (address): The address to withdraw tokens to
* `amount` (uint256): The amount of tokens to withdraw

#### Description

Allows users to withdraw their deposited tokens to a specific address. This is useful for withdrawing to a different wallet or contract.

#### Example

```javascript
// Withdraw 200 tokens to another address
await papayaContract.withdrawTo(
  "0x5678...",
  ethers.utils.parseEther("200")
);
```

### pay

Pays tokens directly to a receiver.

```solidity
function pay(address receiver, uint256 amount) external
```

#### Parameters

* `receiver` (address): The address to pay tokens to
* `amount` (uint256): The amount of tokens to pay

#### Description

Allows users to pay tokens directly to a receiver without creating a subscription. This is useful for one-time payments.

#### Example

```javascript
// Pay 50 tokens to a receiver
await papayaContract.pay(
  "0x9abc...",
  ethers.utils.parseEther("50")
);
```

### rescueFunds

Rescues tokens that may be stuck in the contract.

```solidity
function rescueFunds(contract IERC20 token, uint256 amount) external
```

#### Parameters

* `token` (address): The token address to rescue
* `amount` (uint256): The amount of tokens to rescue

#### Description

Allows the contract owner to rescue tokens that may be stuck in the contract. This is an emergency function for recovering funds.

#### Example

```javascript
// Rescue 1000 tokens (owner only)
await papayaContract.rescueFunds(
  tokenAddress,
  ethers.utils.parseEther("1000")
);
```

### Related Events

* [Refill](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/core-events#refill): Emitted when funds are deposited
* [Transfer](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/core-events#transfer): Emitted when tokens are transferred

### Related Errors

* [InsufficientBalance](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#insufficientbalance): When trying to withdraw more than available balance
* [SafeTransferFailed](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#safetransferfailed): When token transfer fails
* [OwnableUnauthorizedAccount](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#ownableunauthorizedaccount): When non-owner tries to rescue funds


# Subscription Management

This section covers the functions for creating and managing streaming subscriptions between users.

### subscribe

Creates a subscription to stream tokens to an author.

```solidity
function subscribe(address author, uint96 subscriptionRate, uint256 projectId) external
```

#### Parameters

* `author` (address): The address of the content creator to subscribe to
* `subscriptionRate` (uint96): The rate of tokens to stream per second
* `projectId` (uint256): The ID of the project for this subscription

#### Description

Creates a subscription that streams tokens from the caller to the specified author at the given rate. The subscription is associated with a specific project ID for fee management.

#### Example

```javascript
// Subscribe to an author at 0.001 tokens per second for project 1
await papayaContract.subscribe(
  "0xauthor...",
  ethers.utils.parseEther("0.001"),
  1
);
```

### unsubscribe

Cancels a subscription to an author.

```solidity
function unsubscribe(address author) external
```

#### Parameters

* `author` (address): The address of the author to unsubscribe from

#### Description

Cancels an active subscription to the specified author. This stops the token streaming immediately.

#### Example

```javascript
// Unsubscribe from an author
await papayaContract.unsubscribe("0xauthor...");
```

### Related Events

* [StreamCreated](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/subscription-events#streamcreated): Emitted when a subscription is created
* [StreamRevoked](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/subscription-events#streamrevoked): Emitted when a subscription is cancelled

### Related Errors

* [NotSubscribed](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#notsubscribed): When trying to unsubscribe from a non-existent subscription
* [ExcessOfRate](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#excessofrate): When subscription rate exceeds limits
* [ExcessOfSubscriptions](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#excessofsubscriptions): When user has too many active subscriptions


# Payment & Streaming

This section covers the core payment and streaming functionality of the Papaya protocol.

### permitAndCall

Executes a permit and then performs an action in a single transaction.

```solidity
function permitAndCall(bytes permit, bytes action) external payable
```

#### Parameters

* `permit` (bytes): The permit data for token approval
* `action` (bytes): The action to execute after the permit

#### Description

This function allows users to approve tokens and execute an action in a single transaction. This is useful for gasless interactions where users sign permits off-chain.

#### Example

```javascript
// Execute permit and action in one transaction
await papayaContract.permitAndCall(permitData, actionData, {
  value: ethers.utils.parseEther("0.1")
});
```

### Related Events

* [Transfer](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/core-events#transfer): Emitted when tokens are transferred during streaming

### Related Errors

* [WrongSignature](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#wrongsignature): When permit signature is invalid
* [DeadlineExceeded](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#deadlineexceeded): When permit has expired


# Liquidation

This section covers the liquidation functionality for underfunded accounts.

### liquidate

Liquidates an underfunded account.

```solidity
function liquidate(address account) external
```

#### Parameters

* `account` (address): The address of the account to liquidate

#### Description

Allows anyone to liquidate an account that has insufficient funds to cover its streaming obligations. The liquidator receives a reward for performing the liquidation.

#### Example

```javascript
// Liquidate an underfunded account
await papayaContract.liquidate("0xunderfunded...");
```

### Related Events

* [Liquidated](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/core-events#liquidated): Emitted when an account is liquidated

### Related Errors

* [NotLiquidatable](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#notliquidatable): When the account cannot be liquidated
* [InsufficientBalance](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#insufficientbalance): When the account has insufficient balance for liquidation


# Project Management

This section covers the functions for managing project settings and fees.

### claimProjectId

Claims a project ID for a project owner.

```solidity
function claimProjectId(address projectOwner) external
```

#### Parameters

* `projectOwner` (address): The address of the project owner

#### Description

Allows a project owner to claim a unique project ID. This ID is used for managing project-specific settings and fees.

#### Example

```javascript
// Claim project ID for a project owner
await papayaContract.claimProjectId("0xprojectOwner...");
```

### setDefaultSettings

Sets the default settings for a project.

```solidity
function setDefaultSettings(Settings memory settings, uint256 projectId) external
```

#### Parameters

* `settings` (Settings): The settings struct containing:
  * `initialized` (bool): Whether the settings are initialized
  * `projectFee` (uint16): The project fee percentage
* `projectId` (uint256): The ID of the project

#### Description

Sets the default fee settings for a specific project. These settings apply to all users who don't have custom settings for this project.

#### Example

```javascript
// Set default settings for project 1 with 5% fee
await papayaContract.setDefaultSettings(
  {
    initialized: true,
    projectFee: 500 // 5% = 500 basis points
  },
  1
);
```

### setSettingsForUser

Sets custom settings for a specific user in a project.

```solidity
function setSettingsForUser(address user, Settings memory settings, uint256 projectId) external
```

#### Parameters

* `user` (address): The user address to set settings for
* `settings` (Settings): The settings struct containing:
  * `initialized` (bool): Whether the settings are initialized
  * `projectFee` (uint16): The project fee percentage
* `projectId` (uint256): The ID of the project

#### Description

Sets custom fee settings for a specific user in a specific project. These settings override the default project settings for this user.

#### Example

```javascript
// Set custom settings for user in project 1 with 3% fee
await papayaContract.setSettingsForUser(
  "0xuser...",
  {
    initialized: true,
    projectFee: 300 // 3% = 300 basis points
  },
  1
);
```

### Related Events

* [ProjectIdClaimed](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/project-events#projectidclaimed): Emitted when a project ID is claimed
* [SetDefaultSettings](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/project-events#setdefaultsettings): Emitted when default settings are updated
* [SetSettingsForUser](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/events/project-events#setsettingsforuser): Emitted when user settings are updated

### Related Errors

* [InvalidProjectId](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#invalidprojectid): When project ID is invalid
* [AccessDenied](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#accessdenied): When caller doesn't have permission to set settings
* [WrongPercent](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#wrongpercent): When fee percentage is invalid


# Advanced Features


# BySig Operations

This section covers the BySig functionality for gasless operations using signatures.

### bySig

Executes a signed call with a signature.

```solidity
function bySig(address signer, BySig.SignedCall memory sig, bytes signature) external payable returns (bytes memory ret)
```

#### Parameters

* `signer` (address): The address that signed the call
* `sig` (BySig.SignedCall): The signed call data containing:
  * `traits` (uint256): The BySig traits
  * `data` (bytes): The call data
* `signature` (bytes): The signature for the call

#### Description

Executes a call that was signed off-chain. This allows for gasless interactions where users sign messages off-chain and others can execute them on-chain.

#### Example

```javascript
// Execute a signed call
const result = await papayaContract.bySig(
  signerAddress,
  signedCallData,
  signature,
  { value: ethers.utils.parseEther("0.1") }
);
```

### hashBySig

Computes the hash of a BySig call.

```solidity
function hashBySig(BySig.SignedCall memory sig) external view returns (bytes32)
```

#### Parameters

* `sig` (BySig.SignedCall): The signed call data

#### Description

Computes the hash of a BySig call for signature verification.

#### Example

```javascript
// Hash a BySig call
const hash = await papayaContract.hashBySig(signedCallData);
```

### useBySigAccountNonce

Advances the account nonce for BySig operations.

```solidity
function useBySigAccountNonce(uint32 advance) external
```

#### Parameters

* `advance` (uint32): The number of nonces to advance

#### Description

Advances the account nonce used for BySig operations. This is useful for invalidating old signatures.

#### Example

```javascript
// Advance account nonce by 1
await papayaContract.useBySigAccountNonce(1);
```

### useBySigSelectorNonce

Advances the selector nonce for BySig operations.

```solidity
function useBySigSelectorNonce(bytes4 selector, uint32 advance) external
```

#### Parameters

* `selector` (bytes4): The function selector
* `advance` (uint32): The number of nonces to advance

#### Description

Advances the selector-specific nonce used for BySig operations.

#### Example

```javascript
// Advance selector nonce by 1
await papayaContract.useBySigSelectorNonce("0x12345678", 1);
```

### useBySigUniqueNonce

Uses a unique nonce for BySig operations.

```solidity
function useBySigUniqueNonce(uint256 nonce) external
```

#### Parameters

* `nonce` (uint256): The unique nonce to use

#### Description

Uses a specific unique nonce for BySig operations. This allows for precise control over signature replay protection.

#### Example

```javascript
// Use a specific unique nonce
await papayaContract.useBySigUniqueNonce(12345);
```

### Related Errors

* [WrongSignature](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#wrongsignature): When signature is invalid
* [WrongNonce](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#wrongnonce): When nonce is incorrect
* [WrongNonceType](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#wrongnoncetype): When nonce type is invalid


# Sponsored Calls

This section covers the sponsored call functionality for gasless transactions.

### sponsoredCall

Executes a sponsored call with token payment.

```solidity
function sponsoredCall(address token, uint256 amount, bytes data, bytes extraData) external payable returns (bytes memory ret)
```

#### Parameters

* `token` (address): The token address for payment
* `amount` (uint256): The amount of tokens to pay
* `data` (bytes): The call data to execute
* `extraData` (bytes): Additional data for the call

#### Description

Executes a call that is sponsored by the caller. The caller pays for the gas and can optionally pay tokens as part of the transaction.

#### Example

```javascript
// Execute a sponsored call
const result = await papayaContract.sponsoredCall(
  tokenAddress,
  ethers.utils.parseEther("10"),
  callData,
  extraData,
  { value: ethers.utils.parseEther("0.1") }
);
```

### Related Errors

* [FailedCall](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#failedcall): When the sponsored call fails
* [SafeTransferFailed](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#safetransferfailed): When token transfer fails


# Permit Operations

This section covers the permit functionality for gasless token approvals.

### permitAndCall

Executes a permit and then performs an action in a single transaction.

```solidity
function permitAndCall(bytes permit, bytes action) external payable
```

#### Parameters

* `permit` (bytes): The permit data for token approval
* `action` (bytes): The action to execute after the permit

#### Description

This function allows users to approve tokens and execute an action in a single transaction. This is useful for gasless interactions where users sign permits off-chain.

#### Example

```javascript
// Execute permit and action in one transaction
await papayaContract.permitAndCall(permitData, actionData, {
  value: ethers.utils.parseEther("0.1")
});
```

### Related Errors

* [WrongSignature](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#wrongsignature): When permit signature is invalid
* [DeadlineExceeded](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#deadlineexceeded): When permit has expired
* [Permit2TransferAmountTooHigh](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#permit2transferamounttoohigh): When permit2 transfer amount is too high


# Multicall

This section covers the multicall functionality for executing multiple operations in a single transaction.

### multicall

Executes multiple calls in a single transaction.

```solidity
function multicall(bytes[] data) external returns (bytes[] memory results)
```

#### Parameters

* `data` (bytes\[]): Array of encoded function calls

#### Description

Executes multiple function calls in a single transaction. This is useful for batching operations to save gas and ensure atomicity.

#### Example

```javascript
// Execute multiple calls in one transaction
const calls = [
  papayaContract.interface.encodeFunctionData("deposit", [ethers.utils.parseEther("100"), false]),
  papayaContract.interface.encodeFunctionData("subscribe", ["0xauthor...", ethers.utils.parseEther("0.001"), 1])
];

const results = await papayaContract.multicall(calls);
```

### Related Errors

* [FailedCall](https://app.gitbook.com/o/qmYNDgxzLtvTeLBHbPpz/s/crhGDzgi59PyfFaJtlVP/~/changes/67/protocol/error-codes#failedcall): When any of the multicall operations fail


# View Functions

###


# User Information

This section covers the view functions for querying user account information.

### users

Gets detailed information about a user's account.

```solidity
function users(address account) external view returns (int256 balance, int256 incomeRate, int256 outgoingRate, uint256 updated)
```

#### Parameters

* `account` (address): The user's address

#### Returns

* `balance` (int256): The user's current balance
* `incomeRate` (int256): The rate at which tokens are flowing into the account
* `outgoingRate` (int256): The rate at which tokens are flowing out of the account
* `updated` (uint256): The timestamp of the last update

#### Description

Returns comprehensive information about a user's account including their balance and streaming rates.

#### Example

```javascript
// Get user information
const userInfo = await papayaContract.users("0xuser...");
console.log("Balance:", userInfo.balance.toString());
console.log("Income Rate:", userInfo.incomeRate.toString());
console.log("Outgoing Rate:", userInfo.outgoingRate.toString());
console.log("Last Updated:", userInfo.updated.toString());
```

### balanceOf

Gets the balance of a specific account.

```solidity
function balanceOf(address account) external view returns (uint256)
```

#### Parameters

* `account` (address): The account address

#### Returns

* `balance` (uint256): The account balance

#### Description

Returns the current balance of the specified account.

#### Example

```javascript
// Get account balance
const balance = await papayaContract.balanceOf("0xuser...");
console.log("Balance:", ethers.utils.formatEther(balance));
```

### bySigAccountNonces

Gets the account nonce for BySig operations.

```solidity
function bySigAccountNonces(address account) external view returns (uint256)
```

#### Parameters

* `account` (address): The account address

#### Returns

* `nonce` (uint256): The current account nonce

#### Description

Returns the current nonce for BySig operations for a specific account.

#### Example

```javascript
// Get account nonce
const nonce = await papayaContract.bySigAccountNonces("0xuser...");
console.log("Account nonce:", nonce.toString());
```

### bySigSelectorNonces

Gets the selector nonce for BySig operations.

```solidity
function bySigSelectorNonces(address account, bytes4 selector) external view returns (uint256)
```

#### Parameters

* `account` (address): The account address
* `selector` (bytes4): The function selector

#### Returns

* `nonce` (uint256): The current selector nonce

#### Description

Returns the current nonce for a specific function selector in BySig operations.

#### Example

```javascript
// Get selector nonce
const nonce = await papayaContract.bySigSelectorNonces("0xuser...", "0x12345678");
console.log("Selector nonce:", nonce.toString());
```

### bySigUniqueNonces

Checks if a unique nonce has been used.

```solidity
function bySigUniqueNonces(address account, uint256 nonce) external view returns (bool)
```

#### Parameters

* `account` (address): The account address
* `nonce` (uint256): The unique nonce to check

#### Returns

* `used` (bool): Whether the nonce has been used

#### Description

Returns whether a specific unique nonce has been used for BySig operations.

#### Example

```javascript
// Check if nonce is used
const used = await papayaContract.bySigUniqueNonces("0xuser...", 12345);
console.log("Nonce used:", used);
```

### bySigUniqueNoncesSlot

Gets the storage slot for a unique nonce.

```solidity
function bySigUniqueNoncesSlot(address account, uint256 nonce) external view returns (uint256)
```

#### Parameters

* `account` (address): The account address
* `nonce` (uint256): The unique nonce

#### Returns

* `slot` (uint256): The storage slot for the nonce

#### Description

Returns the storage slot used for tracking a specific unique nonce.

#### Example

```javascript
// Get nonce storage slot
const slot = await papayaContract.bySigUniqueNoncesSlot("0xuser...", 12345);
console.log("Nonce slot:", slot.toString());
```

### name

Gets the name of the token.

```solidity
function name() external pure returns (string memory)
```

#### Returns

* `name` (string): The token name

#### Description

Returns the name of the Papaya token.

#### Example

```javascript
// Get token name
const name = await papayaContract.name();
console.log("Token Name:", name);
```

### symbol

Gets the symbol of the token.

```solidity
function symbol() external pure returns (string memory)
```

#### Returns

* `symbol` (string): The token symbol

#### Description

Returns the symbol of the Papaya token.

#### Example

```javascript
// Get token symbol
const symbol = await papayaContract.symbol();
console.log("Token Symbol:", symbol);
```

### decimals

Gets the number of decimals for the token.

```solidity
function decimals() external pure returns (uint8)
```

#### Returns

* `decimals` (uint8): The number of decimals

#### Description

Returns the number of decimals used by the token.

#### Example

```javascript
// Get token decimals
const decimals = await papayaContract.decimals();
console.log("Decimals:", decimals);
```

### totalSupply

Gets the total supply of tokens.

```solidity
function totalSupply() external view returns (uint256)
```

#### Returns

* `totalSupply` (uint256): The total token supply

#### Description

Returns the total supply of Papaya tokens.

#### Example

```javascript
// Get total supply
const totalSupply = await papayaContract.totalSupply();
console.log("Total Supply:", ethers.utils.formatEther(totalSupply));
```


# Subscription Queries

This section covers the view functions for querying subscription information.

### subscriptions

Gets subscription information between two addresses.

```solidity
function subscriptions(address from, address to) external view returns (bool, uint256 encodedRates)
```

#### Parameters

* `from` (address): The subscriber's address
* `to` (address): The author's address

#### Returns

* `exists` (bool): Whether the subscription exists
* `encodedRates` (uint256): The encoded subscription rates

#### Description

Returns information about a subscription between two addresses, including whether it exists and the encoded rates.

#### Example

```javascript
// Get subscription information
const [exists, encodedRates] = await papayaContract.subscriptions("0xsubscriber...", "0xauthor...");
console.log("Subscription exists:", exists);
console.log("Encoded rates:", encodedRates.toString());
```

### allSubscriptions

Gets all subscriptions for a specific address.

```solidity
function allSubscriptions(address from) external view returns (address[] to, uint256[] encodedRates)
```

#### Parameters

* `from` (address): The subscriber's address

#### Returns

* `to` (address\[]): Array of author addresses
* `encodedRates` (uint256\[]): Array of encoded subscription rates

#### Description

Returns all active subscriptions for a specific subscriber address.

#### Example

```javascript
// Get all subscriptions for a user
const [authors, rates] = await papayaContract.allSubscriptions("0xsubscriber...");
console.log("Number of subscriptions:", authors.length);
for (let i = 0; i < authors.length; i++) {
  console.log(`Author ${i}:`, authors[i]);
  console.log(`Rate ${i}:`, rates[i].toString());
}
```


# Project Queries

This section covers the view functions for querying project information.

### projectOwners

Gets the owner of a specific project ID.

```solidity
function projectOwners(uint256) external view returns (address)
```

#### Parameters

* `projectId` (uint256): The project ID

#### Returns

* `owner` (address): The project owner's address

#### Description

Returns the owner address for a specific project ID.

#### Example

```javascript
// Get project owner
const owner = await papayaContract.projectOwners(1);
console.log("Project 1 owner:", owner);
```

### allProjectOwners

Gets all project owners.

```solidity
function allProjectOwners() external view returns (address[])
```

#### Returns

* `owners` (address\[]): Array of all project owner addresses

#### Description

Returns an array of all project owner addresses.

#### Example

```javascript
// Get all project owners
const owners = await papayaContract.allProjectOwners();
console.log("Number of projects:", owners.length);
owners.forEach((owner, index) => {
  console.log(`Project ${index} owner:`, owner);
});
```

### defaultSettings

Gets the default settings for a project.

```solidity
function defaultSettings(uint256 projectId) external view returns (bool initialized, uint16 projectFee)
```

#### Parameters

* `projectId` (uint256): The project ID

#### Returns

* `initialized` (bool): Whether the settings are initialized
* `projectFee` (uint16): The project fee percentage

#### Description

Returns the default settings for a specific project.

#### Example

```javascript
// Get default project settings
const [initialized, projectFee] = await papayaContract.defaultSettings(1);
console.log("Settings initialized:", initialized);
console.log("Project fee:", projectFee, "basis points");
```

### userSettings

Gets the custom settings for a user in a specific project.

```solidity
function userSettings(uint256 projectId, address account) external view returns (bool initialized, uint16 projectFee)
```

#### Parameters

* `projectId` (uint256): The project ID
* `account` (address): The user's address

#### Returns

* `initialized` (bool): Whether the settings are initialized
* `projectFee` (uint16): The project fee percentage

#### Description

Returns the custom settings for a specific user in a specific project.

#### Example

```javascript
// Get user settings for project
const [initialized, projectFee] = await papayaContract.userSettings(1, "0xuser...");
console.log("User settings initialized:", initialized);
console.log("User project fee:", projectFee, "basis points");
```


# System Information

This section covers the view functions for querying system-wide information and constants.

### owner

Gets the contract owner address.

```solidity
function owner() external view returns (address)
```

#### Returns

* `owner` (address): The contract owner's address

#### Description

Returns the address of the contract owner who has administrative privileges.

#### Example

```javascript
// Get contract owner
const owner = await papayaContract.owner();
console.log("Contract owner:", owner);
```

### TOKEN

Gets the token address used by the contract.

```solidity
function TOKEN() external view returns (contract IERC20)
```

#### Returns

* `token` (address): The ERC20 token address

#### Description

Returns the address of the ERC20 token used by the Papaya protocol.

#### Example

```javascript
// Get token address
const tokenAddress = await papayaContract.TOKEN();
console.log("Token address:", tokenAddress);
```

### TOKEN\_PRICE\_FEED

Gets the token price feed address.

```solidity
function TOKEN_PRICE_FEED() external view returns (contract AggregatorV3Interface)
```

#### Returns

* `priceFeed` (address): The token price feed address

#### Description

Returns the address of the Chainlink price feed for the token.

#### Example

```javascript
// Get token price feed
const priceFeed = await papayaContract.TOKEN_PRICE_FEED();
console.log("Token price feed:", priceFeed);
```

### COIN\_PRICE\_FEED

Gets the native coin price feed address.

```solidity
function COIN_PRICE_FEED() external view returns (contract AggregatorV3Interface)
```

#### Returns

* `priceFeed` (address): The native coin price feed address

#### Description

Returns the address of the Chainlink price feed for the native coin (ETH).

#### Example

```javascript
// Get native coin price feed
const priceFeed = await papayaContract.COIN_PRICE_FEED();
console.log("Native coin price feed:", priceFeed);
```

### MAX\_PROTOCOL\_FEE

Gets the maximum protocol fee.

```solidity
function MAX_PROTOCOL_FEE() external view returns (uint256)
```

#### Returns

* `maxFee` (uint256): The maximum protocol fee

#### Description

Returns the maximum protocol fee that can be set.

#### Example

```javascript
// Get max protocol fee
const maxFee = await papayaContract.MAX_PROTOCOL_FEE();
console.log("Max protocol fee:", maxFee.toString());
```

### REFILL\_DAYS

Gets the refill days constant.

```solidity
function REFILL_DAYS() external view returns (uint32)
```

#### Returns

* `refillDays` (uint32): The refill days value

#### Description

Returns the number of days for refill operations.

#### Example

```javascript
// Get refill days
const refillDays = await papayaContract.REFILL_DAYS();
console.log("Refill days:", refillDays.toString());
```

### REFILL\_GAS\_COST

Gets the refill gas cost constant.

```solidity
function REFILL_GAS_COST() external view returns (uint32)
```

#### Returns

* `gasCost` (uint32): The refill gas cost

#### Description

Returns the gas cost for refill operations.

#### Example

```javascript
// Get refill gas cost
const gasCost = await papayaContract.REFILL_GAS_COST();
console.log("Refill gas cost:", gasCost.toString());
```

### APPROX\_LIQUIDATE\_GAS

Gets the approximate gas cost for liquidation.

```solidity
function APPROX_LIQUIDATE_GAS() external view returns (uint256)
```

#### Returns

* `gasCost` (uint256): The approximate liquidation gas cost

#### Description

Returns the approximate gas cost for liquidation operations.

#### Example

```javascript
// Get liquidation gas cost
const gasCost = await papayaContract.APPROX_LIQUIDATE_GAS();
console.log("Liquidation gas cost:", gasCost.toString());
```

### APPROX\_SUBSCRIPTION\_GAS

Gets the approximate gas cost for subscription operations.

```solidity
function APPROX_SUBSCRIPTION_GAS() external view returns (uint256)
```

#### Returns

* `gasCost` (uint256): The approximate subscription gas cost

#### Description

Returns the approximate gas cost for subscription operations.

#### Example

```javascript
// Get subscription gas cost
const gasCost = await papayaContract.APPROX_SUBSCRIPTION_GAS();
console.log("Subscription gas cost:", gasCost.toString());
```

### SUBSCRIPTION\_THRESHOLD

Gets the subscription threshold.

```solidity
function SUBSCRIPTION_THRESHOLD() external view returns (uint8)
```

#### Returns

* `threshold` (uint8): The subscription threshold

#### Description

Returns the threshold value for subscription operations.

#### Example

```javascript
// Get subscription threshold
const threshold = await papayaContract.SUBSCRIPTION_THRESHOLD();
console.log("Subscription threshold:", threshold.toString());
```

### DECIMALS\_SCALE

Gets the decimals scale constant.

```solidity
function DECIMALS_SCALE() external view returns (uint256)
```

#### Returns

* `scale` (uint256): The decimals scale

#### Description

Returns the scale factor used for decimal calculations.

#### Example

```javascript
// Get decimals scale
const scale = await papayaContract.DECIMALS_SCALE();
console.log("Decimals scale:", scale.toString());
```

### FLOOR

Gets the floor value constant.

```solidity
function FLOOR() external view returns (uint256)
```

#### Returns

* `floor` (uint256): The floor value

#### Description

Returns the floor value used in calculations.

#### Example

```javascript
// Get floor value
const floor = await papayaContract.FLOOR();
console.log("Floor value:", floor.toString());
```

### SIGNED\_CALL\_TYPEHASH

Gets the signed call typehash.

```solidity
function SIGNED_CALL_TYPEHASH() external view returns (bytes32)
```

#### Returns

* `typehash` (bytes32): The signed call typehash

#### Description

Returns the typehash used for EIP-712 signed calls.

#### Example

```javascript
// Get signed call typehash
const typehash = await papayaContract.SIGNED_CALL_TYPEHASH();
console.log("Signed call typehash:", typehash);
```

### eip712Domain

Gets the EIP-712 domain information.

```solidity
function eip712Domain() external view returns (bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions)
```

#### Returns

* `fields` (bytes1): The domain fields
* `name` (string): The domain name
* `version` (string): The domain version
* `chainId` (uint256): The chain ID
* `verifyingContract` (address): The verifying contract address
* `salt` (bytes32): The domain salt
* `extensions` (uint256\[]): The domain extensions

#### Description

Returns the EIP-712 domain information used for signature verification.

#### Example

```javascript
// Get EIP-712 domain
const domain = await papayaContract.eip712Domain();
console.log("Domain name:", domain.name);
console.log("Domain version:", domain.version);
console.log("Chain ID:", domain.chainId.toString());
console.log("Verifying contract:", domain.verifyingContract);
```


# Events

###


# Core Events

This section covers the core events emitted by the Papaya protocol.

### Transfer

Emitted when tokens are transferred.

```solidity
event Transfer(address indexed _from, address indexed _to, uint256 _value)
```

#### Parameters

* `_from` (address, indexed): The address tokens are transferred from
* `_to` (address, indexed): The address tokens are transferred to
* `_value` (uint256): The amount of tokens transferred

#### Description

Standard ERC20 transfer event emitted when tokens are moved between addresses.

#### Example

```javascript
// Listen for transfer events
papayaContract.on("Transfer", (from, to, value) => {
  console.log(`Transfer: ${value} tokens from ${from} to ${to}`);
});
```

### Refill

Emitted when funds are deposited into an account.

```solidity
event Refill(address indexed user, uint256 amount)
```

#### Parameters

* `user` (address, indexed): The user who received the funds
* `amount` (uint256): The amount of tokens deposited

#### Description

Emitted when tokens are deposited into a user's account through the `deposit` or `depositFor` functions.

#### Example

```javascript
// Listen for refill events
papayaContract.on("Refill", (user, amount) => {
  console.log(`Refill: ${amount} tokens deposited for ${user}`);
});
```

### Liquidated

Emitted when an account is liquidated.

```solidity
event Liquidated(address indexed user, address indexed liquidator)
```

#### Parameters

* `user` (address, indexed): The address of the liquidated user
* `liquidator` (address, indexed): The address of the liquidator

#### Description

Emitted when an underfunded account is liquidated by the `liquidate` function.

#### Example

```javascript
// Listen for liquidation events
papayaContract.on("Liquidated", (user, liquidator) => {
  console.log(`Liquidated: ${user} was liquidated by ${liquidator}`);
});
```

### OwnershipTransferred

Emitted when contract ownership is transferred.

```solidity
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)
```

#### Parameters

* `previousOwner` (address, indexed): The previous owner's address
* `newOwner` (address, indexed): The new owner's address

#### Description

Emitted when the contract ownership is transferred to a new address.

#### Example

```javascript
// Listen for ownership transfer events
papayaContract.on("OwnershipTransferred", (previousOwner, newOwner) => {
  console.log(`Ownership transferred from ${previousOwner} to ${newOwner}`);
});
```

### EIP712DomainChanged

Emitted when the EIP-712 domain is changed.

```solidity
event EIP712DomainChanged()
```

#### Description

Emitted when the EIP-712 domain information is updated.

#### Example

```javascript
// Listen for EIP-712 domain change events
papayaContract.on("EIP712DomainChanged", () => {
  console.log("EIP-712 domain changed");
});
```


# Subscription Events

This section covers the events related to subscription management.

### StreamCreated

Emitted when a subscription is created.

```solidity
event StreamCreated(address indexed user, address indexed author, uint256 indexed encodedRates)
```

#### Parameters

* `user` (address, indexed): The subscriber's address
* `author` (address, indexed): The author's address
* `encodedRates` (uint256, indexed): The encoded subscription rates

#### Description

Emitted when a new subscription is created through the `subscribe` function.

#### Example

```javascript
// Listen for stream creation events
papayaContract.on("StreamCreated", (user, author, encodedRates) => {
  console.log(`Stream created: ${user} subscribed to ${author} with rates ${encodedRates}`);
});
```

### StreamRevoked

Emitted when a subscription is cancelled.

```solidity
event StreamRevoked(address indexed user, address indexed author, uint256 indexed encodedRates)
```

#### Parameters

* `user` (address, indexed): The subscriber's address
* `author` (address, indexed): The author's address
* `encodedRates` (uint256, indexed): The encoded subscription rates

#### Description

Emitted when a subscription is cancelled through the `unsubscribe` function.

#### Example

```javascript
// Listen for stream revocation events
papayaContract.on("StreamRevoked", (user, author, encodedRates) => {
  console.log(`Stream revoked: ${user} unsubscribed from ${author} with rates ${encodedRates}`);
});
```


# Project Events

This section covers the events related to project management.

### ProjectIdClaimed

Emitted when a project ID is claimed.

```solidity
event ProjectIdClaimed(uint256 projectId, address admin)
```

#### Parameters

* `projectId` (uint256): The claimed project ID
* `admin` (address): The address that claimed the project ID

#### Description

Emitted when a project ID is claimed through the `claimProjectId` function.

#### Example

```javascript
// Listen for project ID claim events
papayaContract.on("ProjectIdClaimed", (projectId, admin) => {
  console.log(`Project ID ${projectId} claimed by ${admin}`);
});
```

### SetDefaultSettings

Emitted when default project settings are updated.

```solidity
event SetDefaultSettings(uint256 indexed projectId, uint16 protocolFee)
```

#### Parameters

* `projectId` (uint256, indexed): The project ID
* `protocolFee` (uint16): The protocol fee percentage

#### Description

Emitted when default settings are updated for a project through the `setDefaultSettings` function.

#### Example

```javascript
// Listen for default settings update events
papayaContract.on("SetDefaultSettings", (projectId, protocolFee) => {
  console.log(`Default settings updated for project ${projectId} with fee ${protocolFee}`);
});
```

### SetSettingsForUser

Emitted when user-specific project settings are updated.

```solidity
event SetSettingsForUser(uint256 indexed projectId, address indexed user, uint16 protocolFee)
```

#### Parameters

* `projectId` (uint256, indexed): The project ID
* `user` (address, indexed): The user's address
* `protocolFee` (uint16): The protocol fee percentage

#### Description

Emitted when user-specific settings are updated for a project through the `setSettingsForUser` function.

#### Example

```javascript
// Listen for user settings update events
papayaContract.on("SetSettingsForUser", (projectId, user, protocolFee) => {
  console.log(`User settings updated for project ${projectId}, user ${user} with fee ${protocolFee}`);
});
```


# Error Codes

This section covers all the error codes that can be thrown by the Papaya protocol.

### AccessDenied

Thrown when access is denied for a specific project.

```solidity
error AccessDenied(uint256 projectId)
```

#### Parameters

* `projectId` (uint256): The project ID for which access was denied

#### Description

Thrown when a caller doesn't have permission to perform an action on a specific project.

### AddressEmptyCode

Thrown when trying to call a contract with empty code.

```solidity
error AddressEmptyCode(address target)
```

#### Parameters

* `target` (address): The target address with empty code

#### Description

Thrown when attempting to call a contract that has no code at the target address.

### DeadlineExceeded

Thrown when a deadline has been exceeded.

```solidity
error DeadlineExceeded()
```

#### Description

Thrown when a permit or other time-sensitive operation has exceeded its deadline.

### EnumerableMapNonexistentKey

Thrown when trying to access a non-existent key in an enumerable map.

```solidity
error EnumerableMapNonexistentKey(bytes32 key)
```

#### Parameters

* `key` (bytes32): The non-existent key

#### Description

Thrown when attempting to access a key that doesn't exist in an enumerable map.

### ExcessOfRate

Thrown when the subscription rate exceeds the maximum allowed rate.

```solidity
error ExcessOfRate()
```

#### Description

Thrown when trying to create a subscription with a rate that exceeds the protocol limits.

### ExcessOfSubscriptions

Thrown when a user has too many active subscriptions.

```solidity
error ExcessOfSubscriptions()
```

#### Description

Thrown when trying to create a subscription but the user has already reached the maximum number of active subscriptions.

### FailedCall

Thrown when a call to another contract fails.

```solidity
error FailedCall()
```

#### Description

Thrown when a call to an external contract fails.

### IndexOutOfBounds

Thrown when trying to access an index that is out of bounds.

```solidity
error IndexOutOfBounds()
```

#### Description

Thrown when attempting to access an array index that doesn't exist.

### InsufficientBalance

Thrown when there are insufficient funds for an operation.

```solidity
error InsufficientBalance(uint256 balance, uint256 needed)
```

#### Parameters

* `balance` (uint256): The current balance
* `needed` (uint256): The amount needed

#### Description

Thrown when trying to perform an operation that requires more funds than are available.

### InsufficialBalance

Thrown when there are insufficient official funds.

```solidity
error InsufficialBalance()
```

#### Description

Thrown when there are insufficient official funds for an operation.

### InvalidProjectId

Thrown when an invalid project ID is provided.

```solidity
error InvalidProjectId(uint256 projectId)
```

#### Parameters

* `projectId` (uint256): The invalid project ID

#### Description

Thrown when trying to use a project ID that doesn't exist or is invalid.

### InvalidShortString

Thrown when a short string is invalid.

```solidity
error InvalidShortString()
```

#### Description

Thrown when a short string doesn't meet the required format.

### NotLegal

Thrown when an operation is not legal.

```solidity
error NotLegal()
```

#### Description

Thrown when an operation violates legal or protocol rules.

### NotLiquidatable

Thrown when an account cannot be liquidated.

```solidity
error NotLiquidatable()
```

#### Description

Thrown when trying to liquidate an account that doesn't meet the liquidation criteria.

### NotSubscribed

Thrown when trying to perform an operation on a non-existent subscription.

```solidity
error NotSubscribed()
```

#### Description

Thrown when trying to unsubscribe from a subscription that doesn't exist.

### OwnableInvalidOwner

Thrown when an invalid owner address is provided.

```solidity
error OwnableInvalidOwner(address owner)
```

#### Parameters

* `owner` (address): The invalid owner address

#### Description

Thrown when trying to set an invalid address as the contract owner.

### OwnableUnauthorizedAccount

Thrown when an unauthorized account tries to perform an owner-only operation.

```solidity
error OwnableUnauthorizedAccount(address account)
```

#### Parameters

* `account` (address): The unauthorized account

#### Description

Thrown when an account without owner privileges tries to perform an owner-only operation.

### Permit2TransferAmountTooHigh

Thrown when the Permit2 transfer amount is too high.

```solidity
error Permit2TransferAmountTooHigh()
```

#### Description

Thrown when trying to transfer more tokens than allowed through Permit2.

### ReduceTheAmount

Thrown when the amount needs to be reduced.

```solidity
error ReduceTheAmount()
```

#### Description

Thrown when the requested amount is too high and needs to be reduced.

### SafeCastOverflowedUintToInt

Thrown when a safe cast from uint to int overflows.

```solidity
error SafeCastOverflowedUintToInt(uint256 value)
```

#### Parameters

* `value` (uint256): The value that caused the overflow

#### Description

Thrown when trying to cast a uint256 to int256 that would cause an overflow.

### SafeTransferFailed

Thrown when a safe transfer operation fails.

```solidity
error SafeTransferFailed()
```

#### Description

Thrown when a safe transfer of tokens fails.

### SafeTransferFromFailed

Thrown when a safe transfer from operation fails.

```solidity
error SafeTransferFromFailed()
```

#### Description

Thrown when a safe transfer from operation fails.

### StringTooLong

Thrown when a string is too long.

```solidity
error StringTooLong(string str)
```

#### Parameters

* `str` (string): The string that is too long

#### Description

Thrown when a string exceeds the maximum allowed length.

### TopUpBalance

Thrown when the balance needs to be topped up.

```solidity
error TopUpBalance()
```

#### Description

Thrown when the account balance is insufficient and needs to be topped up.

### WrongNonce

Thrown when an incorrect nonce is provided.

```solidity
error WrongNonce()
```

#### Description

Thrown when a nonce doesn't match the expected value for BySig operations.

### WrongNonceType

Thrown when an incorrect nonce type is provided.

```solidity
error WrongNonceType()
```

#### Description

Thrown when the nonce type doesn't match the expected type for BySig operations.

### WrongPercent

Thrown when an incorrect percentage is provided.

```solidity
error WrongPercent()
```

#### Description

Thrown when a percentage value is outside the allowed range.

### WrongRelayer

Thrown when an incorrect relayer is provided.

```solidity
error WrongRelayer()
```

#### Description

Thrown when the relayer address doesn't match the expected value.

### WrongSignature

Thrown when a signature is invalid.

```solidity
error WrongSignature()
```

#### Description

Thrown when a signature doesn't match the expected value or is invalid.

### WrongToken

Thrown when an incorrect token is provided.

```solidity
error WrongToken()
```

#### Description

Thrown when trying to use a token that is not supported by the protocol.


# Introduction

<figure><img src="/files/CtDx0gMtW3Fz92C8WyBK" alt=""><figcaption></figcaption></figure>

Papaya is an infrastructure for stablecoin subscriptions. It enables merchants to accept recurring payments from millions of users and collect them in a single transaction, for less than $0.01 in gas. Our subscription protocol enables real-time balance calculations for any user without the need to iterate through all incoming streams. This means you can manage subscriptions for millions of users without enumerating each individual subscription.&#x20;

<figure><img src="/files/svdThFhB06qAHOy26LFa" alt=""><figcaption></figcaption></figure>

### **Key Features**

* **Unlimited Subscriptions**: Handle thousands to millions of subscriptions with minimal transaction costs.
* **Real-Time Payment Streaming**: Enable continuous cash flow for businesses.
* **Multi-Chain Support**: Compatible with major blockchain networks (Ethereum, Polygon, BNB Chain, etc.).
* **Stablecoin Integration**: Native support for USDT, USDC, and PYUSD.
* **Non-Custodial Solution**: Direct wallet integration ensures user control over funds.
* **Complete Transparency**: Monitor transactions in real time with full visibility.
* **Cost-Efficient Collection:** Accept recurring payments from millions of users and collect them in a single transaction for less than $0.01 in gas

### **How It Works**

{% embed url="<https://youtu.be/Z_qZshl9hnY?si=PaArGF7U65l4QhpQ>" %}

### **Why Choose Papaya?**

* **Efficiency**: Save on gas fees by bundling transactions.
* **Scalability**: Designed to support millions of users without compromising performance.
* **User Control**: Transparent, real-time streaming with full control over payments.
* **Cost Savings**: Minimize transaction costs for both businesses and customers.
* **Multi-Chain Ready:** Already live on 9+ blockchains with support for major stablecoins.
* **Trusted Infrastructure:** Audited protocol, backed by founders of 1inch and STON.fi.

{% content-ref url="/pages/T59fWuovoBXOcC1D9mVA" %}
[Getting Started](/getting-started)
{% endcontent-ref %}


# Interaction

Interaction with the contract is facilitated through our SDK.

<figure><img src="/files/uelcuNHiSk8a35FqZWKE" alt=""><figcaption></figcaption></figure>

For user convenience, Papaya supports integration with all existing wallets through the use of standard ERC20 methods: *balanceOf*, *name*, *symbol*, and *decimals*. When funds are deposited into the contract, an abstract token *pp\** (where *\** is the original token's name) will appear in the user's wallet.

{% hint style="info" %}
By abstract, we mean that no new tokens are created; instead, we mimic the token. This allows you to add Papaya as a token to your wallet and track your balance seamlessly.
{% endhint %}

{% content-ref url="/pages/8bvNvaFSoVxgesVk80oz" %}
[Subscription Management](/protocol/core-functions/subscription-management)
{% endcontent-ref %}

{% content-ref url="/pages/7aUFmnCMx9m4smGncsXL" %}
[SDK](/sdk/overview)
{% endcontent-ref %}


# Getting Started

Welcome to Papaya! This guide will help merchants set up and start using Papaya to accept stablecoin subscriptions.

### **For M**erchants

#### **1. Initial Setup**

* Submit Onboarding Form – Fill out the [Google Form](https://forms.gle/oNqsF13ZGefAKWfv6) to share basic details about your project.
* Receive Project ID – After review, you’ll be assigned a unique Project ID.
* Access Integration Options – Use your Project ID with the Papaya SDK or connect directly to our smart contracts.

***

#### **2. Integration Steps**

* SDK Integration – Recommended for most merchants. Add the Papaya SDK to your checkout.
* Smart Contract Integration – For advanced teams needing direct on-chain interaction.
* Configure & Test – Set up subscription tiers, test transactions, and confirm that everything works as expected.

***

#### **3. Go Live**

* Deploy – Launch the integrated solution on your platform.
* Monitor Subscriptions – Track payments and balances in real time.
* Manage Streams – Update or cancel subscriptions directly through the protocol.
* Get Support – Contact the Papaya team for technical or business assistance.

***

### **For Customers**

Here is a quick step-by-step guide for customers using Papaya:

* **Select Papaya at Checkout**\
  When you’re ready to pay or subscribe, choose Papaya as your payment method.

<figure><img src="/files/0CBy8Drd3fedmEjlcyct" alt=""><figcaption></figcaption></figure>

* **Connect Your Wallet**\
  Connect your wallet (e.g., MetaMask, WalletConnect, or another supported option).

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcrhGDzgi59PyfFaJtlVP%2Fuploads%2FGs2LX3v2oP1s6817jvBz%2FWallet%20connect.mp4?alt=media&token=e8b4d22b-91e5-48d9-9a5e-e58e8d558a95>" %}

* **Deposit (Top Up the Protocol)**\
  Deposit funds into the protocol so you have enough balance to cover your subscription payments.

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcrhGDzgi59PyfFaJtlVP%2Fuploads%2F7psG56b1RiIuSJNCyO4u%2FDepositing.mp4?alt=media&token=3330f1f0-7b86-4f66-89c4-aa83f13c907d>" %}

* **Create a Stream (Subscribe)**\
  Create a stream (subscription) by specifying the required parameters (amount, duration, payment frequency, etc.).

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcrhGDzgi59PyfFaJtlVP%2Fuploads%2FhKeLWdSPBl7pIIRV8GvU%2FStream%20creation.mp4?alt=media&token=0c88d3d6-b82d-4d94-9c9f-bab0a5792e42>" %}

* **Stream Changing**\
  If needed, you can modify an existing subscription — update the terms, change the amount, or adjust the schedule.

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcrhGDzgi59PyfFaJtlVP%2Fuploads%2FrdTNwp6IdGuSNfeGHPjj%2FStream%20changing.mp4?alt=media&token=c8dc2ec2-a6e7-4f1f-91a8-e6ae463e8cc0>" %}

* **Cancel Subscription**\
  Should you no longer need the service, cancel your subscription at any time to stop any future charges.

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FcrhGDzgi59PyfFaJtlVP%2Fuploads%2FXWVWlfuyYBAvyprgdN5P%2FStream%20Revoking.mp4?alt=media&token=6937d22f-6357-4e2e-b5aa-148625af8386>" %}

***

By following these steps, businesses can efficiently set up Papaya, and customers can enjoy a smooth and transparent subscription experience.


# Initial Setup

<mark style="color:red;">**TODO: убрать дублирование**</mark> \
This section guides businesses through the initial steps required to start using Papaya.

### **Step 1: Contact the Papaya Team**

* Reach out to the Papaya team <partnerships@papaya.finance> to begin the onboarding process.
* Our team will guide you through the requirements and provide necessary resources.

***

### **Step 2: Complete KYB Verification**

* Submit required documentation for Know Your Business (KYB) verification.
* The KYB process ensures compliance with regulatory standards and secures your account.

***

### **Step 3: Receive Dashboard Access**

* Once your KYB verification is approved, you will receive credentials to access the Papaya business dashboard.
* The dashboard is your central hub for managing subscriptions and payments.

***

### **Step 4: Connect Your Wallet**

* Link your company’s crypto wallet to the Papaya protocol.
* Supported wallets include MetaMask, WalletConnect, and others compatible with major blockchain networks.

***

After completing these steps, you will be ready to integrate Papaya and start processing subscription payments.


# Integration Steps

<mark style="color:red;">**TODO: убрать дублирование**</mark>\
This guide outlines the steps required to integrate Papaya into your business operations.

### **Step 1: Choose an Integration Method**

Select the method that best suits your business needs:

* **Payment Widget**:
  * Use Papaya's pre-built widget for a quick and easy setup.
  * Embed the widget into your checkout page with minimal coding.
* **SDK Integration**:
  * For more control, integrate directly with Papaya's SDK.
  * Suitable for businesses with custom requirements.
* **Custom Solution**:
  * Collaborate with the Papaya team to create a tailored integration solution.

***

### **Step 2: Set Up Payment Streams**

* Define subscription plans and payment frequencies in the Papaya dashboard.
* Configure payment streams for your customers (e.g., every second, daily, weekly, monthly).

***

### **Step 3: Configure the Dashboard**

* Customize your dashboard settings to align with your business processes.
* Use features like analytics, payment tracking, and multi-chain monitoring.

***

### **Step 4: Test Transactions**

* Run test transactions to ensure the integration works as expected.
* Simulate customer subscriptions to verify the setup.

***

By completing these steps, you’ll have Papaya fully integrated into your platform, allowing you to start accepting subscription payments seamlessly.


# Go Live

<mark style="color:red;">**TODO: убрать дублирование**</mark>\
Congratulations on completing the setup and integration of Papaya! This guide will help you prepare for launching your payment system in production.

### **Step 1: Deploy to Production**

* Ensure your integration has been thoroughly tested in a staging environment.
* Move your configuration and integration settings to the production environment.

***

### **Step 2: Monitor Transactions**

* Use the Papaya dashboard to monitor transactions in real-time.
* Keep track of subscription activity, balances, and payment flows.

***

### **Step 3: Manage Payment Streams**

* Access tools in the dashboard to:
  * Pause, resume, or cancel active payment streams.
  * Modify subscription settings for customers as needed.

***

### **Step 4: Access Support**

* Reach out to Papaya’s support team for any assistance during the go-live process.
* Support is available for troubleshooting, optimizing integrations, and addressing customer inquiries.

***

By following these steps, you’ll be ready to launch a smooth and reliable subscription payment experience for your customers.


# Features

Papaya offers powerful features that enable seamless and efficient subscription payment processing. Here's an overview of the core functionalities:

***

### **Real-Time Payment Streaming**

* Enable continuous payment flows from customers to businesses.
* Replace traditional one-time billing with a dynamic pay-as-you-use model.
* Businesses receive a steady cash flow while customers have full control over their subscriptions.

***

### **Multi-Chain Support**

<mark style="color:red;">**TODO: дублируется**</mark>

* Operate on multiple blockchain networks, including:
  * Ethereum
  * Polygon
  * BNB Chain
  * Arbitrum
  * Avalanche
  * Base
  * Scroll
  * zkSync
* Upcoming support for additional chains like TON, TRON and Solana.

***

### **Native Stablecoin Integration**

* Accept payments in widely-used stablecoins:
  * USDT
  * USDC
  * PYUSD
* Secure and stable transactions with no need for conversion.

***

### **Business Dashboard**

<mark style="color:red;">**TODO: посмотреть**</mark>&#x20;

* Manage all payment streams in a centralized dashboard:
  * Real-time tracking of balances and payment flows.
  * Stream management tools to pause, resume, or cancel subscriptions.
  * Transaction analytics for business insights.
  * Multi-chain monitoring for seamless operations across networks.

***

### **Security and Compliance**

<mark style="color:red;">**TODO: интеграция с TRM Labs**</mark>

* Fully audited by Stronghold Security.
* Non-custodial by design, ensuring businesses and customers retain control over funds.
* Compliance with ERC20 standards for token transactions.
* Integrated with <mark style="color:yellow;">TRM Labs</mark> for security monitoring and fraud prevention.
* KYB verification ensures a secure onboarding process for businesses.

***

Papaya’s features make it a robust solution for modern subscription payment needs, providing both efficiency and flexibility.


# Real-Time Payment Streaming

<mark style="color:red;">**TODO: добавить реальные фичи**</mark>\
Papaya's real-time payment streaming revolutionizes the way subscriptions are managed and processed. Here's how it works and why it benefits both businesses and customers:

***

### **How Real-Time Streaming Works**

* Payments are streamed in real-time directly from the subscriber’s wallet to the business.
* Instead of traditional fixed billing cycles (e.g., monthly), funds flow continuously.
* The payment stream stops automatically when the subscription is canceled or the balance is depleted.

***

### **Benefits for Businesses**

1. **Continuous Cash Flow**:
   * Businesses no longer wait for end-of-cycle payments.
   * Receive funds in a steady, uninterrupted manner.
2. **Improved Financial Management**:
   * Predictable revenue streams allow for better forecasting and planning.
   * Real-time tracking ensures visibility over all transactions.
3. **Reduced Overhead**:
   * Consolidated transactions minimize the administrative burden of traditional billing.

***

### **Benefits for Customers**

1. **Full Control**:
   * Customers can pause, resume, or cancel subscriptions at any time.
   * Only pay for what they use in real-time.
2. **Transparency**:
   * Easy-to-track payment streams provide clarity on how funds are spent.
3. **Cost Savings**:
   * Avoid paying for unused subscription time.

***

### **Technical Details**

* Papaya leverages blockchain technology to enable real-time streaming.
* Supported on multiple chains for scalability and compatibility.
* Transactions are secured and verified through non-custodial smart contracts.

***

Real-time payment streaming offers a flexible and efficient alternative to traditional subscription models, aligning payment flows with real-world usage.


# Supported Networks

Papaya is designed to operate seamlessly across multiple blockchain networks, providing flexibility and scalability for businesses and users.

***

### **Currently Supported Networks**

<mark style="color:red;">**TODO: дублируется**</mark>

Papaya is deployed on the following major blockchain networks:

* Ethereum
* Polygon
* BNB Chain
* Arbitrum
* Avalanche
* Base
* Scroll
* zkSync

<figure><img src="/files/X6gV7fen0zZ6bCQWhn3F" alt=""><figcaption></figcaption></figure>

***

### **Coming Soon**

We are continuously expanding our network compatibility. The following chains are currently in development:

* **TON**
* **TRON**
* **Solana**

***

### **Why Multi-Chain Support Matters**

1. **Flexibility**:\
   Businesses and users can choose the network that best suits their needs, based on cost, speed, and scalability.
2. **Lower Costs**:\
   By leveraging networks with lower transaction fees, businesses can save money on operational costs.
3. **Broader Reach**:\
   Supporting multiple networks ensures compatibility with a wide range of wallets and decentralized applications.
4. **Scalability**:\
   Multi-chain architecture allows Papaya to handle a growing number of users and transactions seamlessly.

***

Papaya’s multi-chain support ensures that businesses and users can operate efficiently and effectively in a diverse blockchain ecosystem.


# Business Dashboard

<mark style="color:red;">**TODO: убрать**</mark>\
Papaya's business dashboard is your hub for managing subscriptions and payment streams. It provides powerful tools and insights to help businesses operate efficiently.

***

### **Key Features of the Dashboard**

#### 1. **Real-Time Balance Tracking**

* Monitor your wallet balances across supported blockchain networks.
* Instantly view available funds and ongoing payment streams.

***

#### 2. **Stream Flow Visualization**

* Visualize payment flows in real time.
* Understand the status and performance of all active subscriptions.

***

#### 3. **Transaction Analytics**

* Access detailed analytics for all transactions.
* Gain insights into revenue streams, customer activity, and more.

***

#### 4. **Multi-Chain Monitoring**

* Manage subscriptions and transactions across multiple blockchain networks from a single interface.
* Switch between networks seamlessly.

***

#### 5. **Stream Management Tools**

* Pause, resume, or cancel payment streams directly from the dashboard.
* Adjust subscription settings as needed for individual customers.

***

#### 6. **Wallet Integration Tools**

* Easily link and manage your crypto wallets.
* Ensure secure and seamless operations across networks.

***

### **Benefits of Using the Business Dashboard**

1. **Operational Efficiency**:\
   Manage all aspects of your subscriptions in one place.
2. **Enhanced Transparency**:\
   Gain full visibility into your payment streams and customer behavior.
3. **Simplified Management**:\
   Quickly adapt to changing customer needs and business requirements.
4. **Scalability**:\
   Handle a growing number of subscriptions and transactions with ease.

***

Papaya’s business dashboard empowers businesses with the tools they need to deliver a seamless and transparent subscription experience.


# Use Cases

<mark style="color:red;">**TODO: убрать**</mark>\
Papaya is a versatile payment solution that caters to a wide range of industries and business models. Here are some of the key use cases:

***

### **For Enterprises**

* **International Salary Payments**:\
  Stream salaries to employees across the globe in real-time.
* **Vendor Payments**:\
  Automate and manage recurring payments to vendors and suppliers.
* **Corporate Treasury Operations**:\
  Streamline cash flow management and treasury operations.
* **Large-Scale Subscription Management**:\
  Handle millions of subscriptions efficiently and at low cost.

***

### **For Financial Services**

* **Payment Gateway Integration**:\
  Provide seamless crypto payment options for customers.
* **Cross-Border Transfers**:\
  Enable real-time cross-border payments with minimal fees.
* **Insurance Premium Collection**:\
  Automate the collection of recurring insurance premiums.
* **Banking Solutions**:\
  Integrate real-time payment streaming into modern banking systems.

***

### **For the Digital Economy**

* **Content Creator Monetization**:\
  Allow creators to receive ongoing payments from subscribers in real-time.
* **Gaming Microtransactions**:\
  Enable players to pay only for the time they play, in real-time.
* **SaaS Subscriptions**:\
  Provide flexible subscription plans with continuous payment streaming.
* **Marketplace Payments**:\
  Stream payments between buyers and sellers in online marketplaces.

***

Papaya’s use cases demonstrate its flexibility and scalability, making it an ideal solution for businesses in a variety of industries.


# For Enterprises

<mark style="color:red;">**TODO: удалить / оставить подписки**</mark>

Papaya provides powerful solutions for enterprises to manage payments efficiently and at scale. Here are the key use cases:

***

### **1. International Salary Payments**

* Stream salaries to employees worldwide in real-time.
* Avoid delays and high costs associated with traditional banking systems.
* Ensure transparency and traceability for payroll operations.

***

### **2. Vendor Payments**

* Automate recurring payments to vendors and suppliers.
* Manage multiple payment streams through the Papaya dashboard.
* Reduce administrative overhead and transaction fees.

***

### **3. Corporate Treasury Operations**

* Simplify cash flow management with real-time payment streaming.
* Optimize treasury operations by consolidating multiple payments into single transactions.
* Gain visibility into all transactions for better financial planning.

***

### **4. Large-Scale Subscription Management**

* Handle millions of subscriptions with ease and minimal cost.
* Scale operations efficiently, even with a growing customer base.
* Provide a seamless experience for both customers and the enterprise.

***

Papaya empowers enterprises to modernize their payment processes, offering unmatched efficiency, scalability, and transparency.


# For Financial Services

Papaya revolutionizes payment solutions in the financial sector by enabling seamless and efficient payment processes. Here are the primary use cases:

***

### **1. Payment Gateway Integration**

* Enhance your payment platform by integrating Papaya's real-time payment streaming.
* Offer customers flexible and scalable crypto payment options.
* Reduce operational costs and improve transaction speed.

***

### **2. Cross-Border Transfers**

* Facilitate real-time cross-border payments with minimal fees.
* Eliminate traditional delays and complexities associated with international money transfers.
* Ensure transparency and traceability of transactions for compliance.

***

### **3. Insurance Premium Collection**

* Automate the collection of recurring premiums in real time.
* Provide customers with flexible payment options based on usage.
* Simplify premium management with Papaya’s business dashboard.

***

### **4. Banking Solutions**

* Integrate real-time payment streaming into modern banking services.
* Offer innovative features such as real-time subscription payments and salary streaming.
* Improve customer satisfaction by providing seamless and transparent payment processes.

***

Papaya transforms financial services by enabling secure, scalable, and efficient payment solutions tailored for modern demands.


# For Digital Economy

Papaya empowers businesses in the digital economy to innovate and optimize their payment processes. Here are the key use cases:

***

### **1. Content Creator Monetization**

* Enable creators to receive continuous payments from subscribers.
* Stream payments in real time, eliminating delays in payouts.
* Provide transparency and flexibility for creators and their audiences.

***

### **2. Gaming Microtransactions**

* Introduce real-time payment streaming for pay-as-you-play models.
* Allow gamers to only pay for the time they spend playing.
* Reduce friction in in-game purchases with seamless, low-cost transactions.

***

### **3. SaaS Subscriptions**

* Offer flexible subscription plans with continuous payment streaming.
* Scale subscription management as your customer base grows.
* Improve customer retention by providing a transparent pay-as-you-use model.

***

### **4. Marketplace Payments**

* Facilitate real-time payments between buyers and sellers in digital marketplaces.
* Reduce transaction fees and processing times.
* Ensure secure, transparent, and traceable transactions.

***

Papaya enables businesses in the digital economy to enhance their services with innovative payment solutions that prioritize efficiency, scalability, and user experience.


# FAQ

## **Frequently Asked Questions (FAQ)**

\
Find answers to the most common questions about Papaya.

***

### **Market & Geographic Questions**

#### How does Papaya work in emerging markets or specific regions?

Papaya can be deployed in any market, depending on local regulations and crypto adoption rates. For example:

* In Africa: Useful for currency collection and settlement in USDT.
* In Asia-Pacific: Supports integration with local stablecoins.
* In regulated regions: Works through licensed partners to ensure compliance.

***

### **Integration & Technical Questions**

#### Do merchants need their own crypto custody solution?

No, Papaya provides a dedicated smart contract for each merchant. Funds are stored securely, and only the merchant has access to them. Papaya acts as a non-custodial technology provider.

#### How do merchants handle settlement and off-ramping?

While Papaya focuses on payment infrastructure, it integrates with local providers for:

* Fiat settlement
* Currency conversion
* Bank transfers
* Off-ramping to local currencies

***

### **Regulatory & Compliance**

#### What licenses does Papaya hold?

As a non-custodial technology provider, Papaya is pursuing licenses in key jurisdictions. We work with legal teams to ensure compliance where our technology is deployed.

#### How does Papaya address local regulations?

Papaya adapts to specific regulatory requirements through:

* Partnerships with licensed entities in regulated markets.
* Integration with regulated stablecoins.
* Compliance with jurisdictional laws.

***

### **Business Model & Commercial Terms**

#### How does Papaya's revenue model work?

Papaya offers:

* Revenue-sharing for referral partners.
* White-label solutions for payment providers.
* Transaction-based pricing starting at 1% for volumes under $2M.
* Flexible terms for startups and early-stage partners.

***

### **Implementation Questions**

#### Can Papaya integrate with existing POS systems?

Yes, Papaya provides APIs compatible with:

* Point-of-sale systems
* Payment service providers
* E-commerce platforms
* Banking infrastructure

#### Does Papaya support currency conversion between stablecoins?

Yes, Papaya supports:

* Integration with local stablecoins.
* Cross-currency settlement through partners.
* Currency conversion to meet business needs.

***

If you have additional questions, please reach out to our support team for more details.


# Glossary

This glossary provides definitions of key terms and concepts used in the Papaya documentation.

***

### **Core Protocol Terms**

* **Stream**: Continuous flow of tokens from one address to another at a fixed rate.
* **Flow Rate**: The speed at which tokens are transferred, defined by amount per time period.
* **Liquidation**: Process of automatically revoking streams when a user's balance becomes insufficient for subscription coverage.
* **Author**: Address receiving streaming payments (merchant, business, creator).
* **Subscriber**: Address initiating streaming payments (customer, user).
* **Project ID**: Unique identifier for platforms building on Papaya.
* **Smart Contract**: The underlying technology stack that enables automatic execution of payment streams.

***

This glossary will be updated regularly to include additional terms and concepts as needed.


# Future Development

<mark style="color:red;">**TODO: пересмотреть или убрать**</mark>\
Papaya is committed to continuous innovation and expansion to meet the evolving needs of businesses and users. Here are some of the key areas we are focusing on for future development:

***

### **1. Expanding Network Support**

* Adding new blockchain networks to ensure broader compatibility:
  * **TON (Telegram Open Network)**: To support the growing ecosystem of Telegram-based projects.
  * **Solana**: A high-performance blockchain for fast and cost-effective transactions.
  * Additional layer-2 solutions and emerging networks.

***

### **2. Enhanced Subscription Management**

* Developing advanced features for businesses:
  * Tiered subscription models with dynamic pricing.
  * Support for complex subscription hierarchies.
  * Advanced analytics for subscription trends and customer behavior.

***

### **3. Cross-Chain Payment Solutions**

* Building solutions for seamless cross-chain transactions:
  * Automatic stablecoin conversion across supported networks.
  * Multi-chain streaming support for businesses operating on different blockchains.

***

### **4. Improved User Experience**

* Introducing new tools for both businesses and end-users:
  * A mobile-friendly version of the business dashboard.
  * Simplified onboarding processes for non-crypto-native users.
  * Enhanced reporting and exportable transaction summaries.

***

### **5. Partnerships and Ecosystem Growth**

* Expanding partnerships with:
  * Payment gateways and processors for fiat off-ramping.
  * Stablecoin providers to support localized currencies.
  * Regulatory bodies to ensure compliance in new regions.

***

### **6. Developer Ecosystem**

* Launching resources for developers:
  * Comprehensive API documentation.
  * SDKs for easier integration with popular programming languages.
  * A developer portal for community-driven innovation.

***

### **7. Regulatory Compliance**

* Acquiring necessary licenses in key jurisdictions.
* Collaborating with legal experts to meet global compliance requirements.
* Ensuring the highest security standards for all transactions.

***

Papaya is dedicated to shaping the future of subscription payments with innovative, scalable, and user-centric solutions. Stay tuned for more updates as we continue to grow!


# Introduction

<figure><img src="/files/5qDToZj48wjpJlRZk2ME" alt=""><figcaption></figcaption></figure>

In Web2 payment systems, each subscription must be claimed individually, meaning that you can only determine whether a subscription payment will be successful, such as if there are sufficient funds on the card, at the moment of processing.

Web3 subscriptions, on the other hand, are built on a protocol with a unique operating algorithm, representing an innovation in accounting. It ensures data consistency, as the data cannot be altered in a way that would break the system. Auditors guarantee that the smart contract operates consistently and reliably.

Our goal was to optimize the claiming process by consolidating multiple claims into a single short transaction, allowing us to collect payments from all subscribers at once, rather than processing each one individually.


# Overview

Papaya is a scalable subscriptions protocol that enables per-second payments. Unlike competitors, Papaya does not use token proxies but works with native assets, allowing you to retrieve your funds back to your wallet at any time.

* Each Papaya contract is designed to operate with a single specific token. At present, we exclusively support stablecoins: USDT, USDC, and DAI.
* Each Papaya contract functions independently, operates solely with its predetermined token, and remains unaffected by subscriptions involving other tokens.
* Our contracts provide robust support for the creation, modification, and deletion of subscriptions, ensuring flexibility and control.
* Every subscription is uniquely tied to a specific wallet, guaranteeing that users are accountable only for their expenses and can rely on the security of their funds.
* Our contract architecture supports the collection of payments from an unlimited number of subscribers within a single transaction.
* Distinguishing ourselves from competitors, Papaya adheres strictly to widely accepted ERC20 standards, ensuring compliance with the highest security requirements.

## Computational complexities

***

* `n` - number of subscribers
* `m` - number of products and authors

| Action   | TON Subs   | Papaya Subs |
| -------- | ---------- | ----------- |
| Deposit  | `-`        | `O(n)`      |
| Withdraw | `-`        | `O(n)`      |
| Claim    | `O(n * m)` | `O(m)`      |

## Example

***

Imagine `10,000` products and authors having `1,000,000` of subscribers each. This would lead to `10,000,000,000` of txs per month/week.


# Overview

The Papaya SDK provides a simple and efficient way to interact with the Papaya Protocol - a subscription and payment platform built on various blockchain networks. This SDK abstracts away the complexity of direct blockchain interactions, making it easy to integrate Papaya's functionality into your applications.

### Features

* Simple interface for interacting with the Papaya Protocol
* Support for multiple networks (Polygon, BSC, Avalanche, Ethereum Mainnet, etc.)
* Multiple stablecoin support (USDT, USDC, PYUSD)
* Typed definitions for better development experience
* Multiple contract version support
* BySig methods for gasless transactions
* Comprehensive transaction handling
* Utility functions for rate conversions and data formatting

### Quick Start

```typescript
import { ethers } from 'ethers';
import { PapayaSDK, formatOutput, convertRateToPeriod, RatePeriod } from '@papaya_fi/sdk';

// Create an Ethereum provider
const provider = new ethers.JsonRpcProvider('YOUR_RPC_URL');

// Create a signer if you need to send transactions
const privateKey = 'YOUR_PRIVATE_KEY';
const signer = new ethers.Wallet(privateKey, provider);

// Create a Papaya SDK instance
const papaya = PapayaSDK.create(
  signer,      // Or provider if you only need read-only operations
  'polygon',   // Network name (default is 'polygon')
  'USDT'       // Token symbol (default is 'USDT')
);

// Now you can use the SDK to interact with the Papaya Protocol
async function getBalance() {
  const rawBalance = await papaya.balanceOf();
  // Convert raw balance to readable format
  const readableBalance = formatOutput(BigInt(rawBalance), 18);
  console.log(`Your balance: ${readableBalance} USDT`);
}

// Example subscription
async function subscribeToAuthor() {
  const authorAddress = '0x...';  // The address to subscribe to
  const amountPerMonth = 10;      // Amount in tokens per month
  
  const tx = await papaya.subscribe(authorAddress, amountPerMonth);
  await tx.wait();
  console.log('Successfully subscribed!');
}

// Example getting user info with rate conversion
async function getUserInfo() {
  const userInfo = await papaya.getUserInfo();
  
  // Convert raw blockchain data to human-readable format
  const formattedInfo = {
    balance: formatOutput(BigInt(userInfo.balance), 18),
    // Convert per-second rates to monthly rates
    incomeRate: convertRateToPeriod(Number(formatOutput(userInfo.incomeRate, 18)), RatePeriod.MONTH),
    outgoingRate: convertRateToPeriod(Number(formatOutput(userInfo.outgoingRate, 18)), RatePeriod.MONTH),
    updated: new Date(Number(userInfo.updated) * 1000).toISOString()
  };
  
  console.log(`Balance: ${formattedInfo.balance} USDT`);
  console.log(`Monthly income: ${formattedInfo.incomeRate} USDT`);
  console.log(`Monthly outgoing: ${formattedInfo.outgoingRate} USDT`);
}
```

### Support

For questions, issues or feature requests, please open an issue on our GitHub repository or contact us at [Papaya Community](https://t.me/PapayaCommunity/26037).

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-rocket-launch">:rocket-launch:</i></h4></td><td><strong>Getting Started</strong></td><td></td><td></td><td><a href="/pages/6jt1fvoCH7mthgSxaIRW">/pages/6jt1fvoCH7mthgSxaIRW</a></td></tr><tr><td><h4><i class="fa-webhook">:webhook:</i></h4></td><td><strong>API Reference</strong></td><td></td><td></td><td><a href="/pages/Ev3ckhr66RSjzPYv8Iud">/pages/Ev3ckhr66RSjzPYv8Iud</a></td></tr><tr><td><h4><i class="fa-lightbulb">:lightbulb:</i></h4></td><td><strong>Examples</strong></td><td></td><td></td><td><a href="/pages/qvUfgc2SPgqMpGgPfNi7">/pages/qvUfgc2SPgqMpGgPfNi7</a></td></tr><tr><td><h4><i class="fa-chart-network">:chart-network:</i></h4></td><td><strong>Network Support</strong></td><td></td><td></td><td><a href="/pages/A9zc7RNI13ywoE3UHiEw">/pages/A9zc7RNI13ywoE3UHiEw</a></td></tr><tr><td><h4><i class="fa-shapes">:shapes:</i></h4></td><td><strong>Utilities</strong></td><td></td><td></td><td><a href="/pages/evdOm0urplyH6auuH9UL">/pages/evdOm0urplyH6auuH9UL</a></td></tr></tbody></table>


# Getting Started

####


# Installation

Installation and setup of Papaya SDK

## Installation

Install the Papaya SDK package using npm or yarn:

```bash
# Using npm
npm install @papaya_fi/sdk

# Using yarn
yarn add @papaya_fi/sdk
```

You'll also need to install ethers.js v6 as a peer dependency:

```bash
npm install ethers@^6.0.0
# or
yarn add ethers@^6.0.0
```

### Prerequisites

Before you begin using the Papaya SDK, you'll need:

* A JavaScript/TypeScript development environment
* Node.js (v14 or higher recommended)
* npm or yarn package manager
* Basic knowledge of Ethereum/EVM blockchain concepts
* Access to blockchain RPC nodes (e.g., from Infura, Alchemy, or your own node)


# Basic Setup

Basic setup of Papaya SDK - creating provider, signer and SDK instance

### Creating a Provider and Signer

First, you need to set up an Ethereum provider and (optionally) a signer:

```typescript
import { ethers } from 'ethers';

// Read-only provider
const provider = new ethers.JsonRpcProvider('https://polygon-rpc.com');

// For transactions, you need a signer
// Option 1: Using a private key (server-side)
const privateKey = 'YOUR_PRIVATE_KEY'; // Never hardcode this in production!
const signer = new ethers.Wallet(privateKey, provider);

// Option 2: Using a browser wallet like MetaMask (client-side)
// This assumes window.ethereum is available
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
```

### Creating a Papaya SDK Instance

Once you have a provider or signer, you can create a Papaya SDK instance:

```typescript
import { 
  PapayaSDK, 
  RatePeriod, 
  formatInput, 
  formatOutput, 
  convertRateToPeriod 
} from '@papaya_fi/sdk';

// With a signer (for transactions)
const papaya = PapayaSDK.create(signer, 'polygon', 'USDT');

// Or with just a provider (for read-only operations)
const readOnlyPapaya = PapayaSDK.create(provider, 'polygon', 'USDT');
```

#### Parameters:

1. `provider` or `signer`: An ethers.js Provider or Signer
2. `network`: The blockchain network to use (default: 'polygon')
   * Available networks: 'polygon', 'bsc', 'avalanche', 'base', 'scroll', 'arbitrum', 'mainnet', 'sei', 'zksync'
3. `tokenSymbol`: The stablecoin to use (default: 'USDT')
   * Available tokens: 'USDT', 'USDC', 'PYUSD' (availability varies by network)
4. `contractVersion`: Optional contract version (defaults to the latest version for the selected network)


# API Reference


# Factory Methods

Factory methods of Papaya SDK for creating instances and getting network information

### `PapayaSDK.create()`

Creates a new instance of the Papaya SDK.

```typescript
static create(
  provider: ethers.Provider | ethers.Signer,
  network: NetworkName = 'polygon',
  tokenSymbol: TokenSymbol = 'USDT',
  contractVersion?: string
): PapayaSDK
```

**Parameters:**

* `provider`: An ethers.js Provider or Signer instance
* `network`: (Optional) The blockchain network to use (default: 'polygon')
* `tokenSymbol`: (Optional) The stablecoin to use (default: 'USDT')
* `contractVersion`: (Optional) Specific contract version to use (defaults to the latest version for the selected network)

**Returns:** A new `PapayaSDK` instance.

**Example:**

```typescript
const provider = new ethers.JsonRpcProvider('https://polygon-rpc.com');
const papaya = PapayaSDK.create(provider, 'polygon', 'USDT');
```

### `PapayaSDK.getAvailableNetworks()`

Returns a list of all networks supported by the SDK.

```typescript
static getAvailableNetworks(): NetworkName[]
```

**Returns:** Array of network names.

**Example:**

```typescript
const networks = PapayaSDK.getAvailableNetworks();
console.log(networks); // ['polygon', 'bsc', 'avalanche', ...]
```

### `PapayaSDK.getAvailableTokens()`

Returns a list of tokens available for a specific network.

```typescript
static getAvailableTokens(network: NetworkName): TokenSymbol[]
```

**Parameters:**

* `network`: The network to check for available tokens

**Returns:** Array of token symbols available on the specified network.

**Example:**

```typescript
const tokens = PapayaSDK.getAvailableTokens('polygon');
console.log(tokens); // ['USDT', 'USDC']
```


# Account Methods

Account methods of Papaya SDK for getting balance and user information

### `balanceOf()`

Retrieves the token balance of an account in the Papaya protocol.

```typescript
async balanceOf(account?: string): Promise<number>
```

**Parameters:**

* `account`: (Optional) The address to check the balance of. If not provided, uses the connected signer's address.

**Returns:** The account balance in its raw blockchain format. Use `formatOutput()` to convert to a human-readable number.

**Example:**

```typescript
// Get raw balance
const rawBalance = await papaya.balanceOf();

// Convert to human-readable format
const balance = formatOutput(BigInt(rawBalance), 18);
console.log(`My balance: ${balance} USDT`);
```

### `getUserInfo()`

Gets detailed information about a user's account.

```typescript
async getUserInfo(account?: string): Promise<UserInfo>
```

**Parameters:**

* `account`: (Optional) The address to get info for. If not provided, uses the connected signer's address.

**Returns:** A `UserInfo` object with the following properties:

* `balance`: The account balance in raw blockchain format
* `incomeRate`: The rate at which the account is receiving subscriptions (raw format)
* `outgoingRate`: The rate at which the account is paying subscriptions (raw format)
* `updated`: The timestamp when the account was last updated

{% hint style="info" %}
The `incomeRate` and `outgoingRate` values are rates in their raw blockchain format. You should use the [utility functions](/sdk/utilities) to convert them to human-readable values, typically per month.
{% endhint %}

**Example:**

```typescript
// Get raw user info
const userInfo = await papaya.getUserInfo();

// Convert to human-readable format
const formattedUserInfo = {
  balance: formatOutput(BigInt(userInfo.balance), 18),
  incomeRate: convertRateToPeriod(Number(formatOutput(userInfo.incomeRate, 18)), RatePeriod.MONTH),
  outgoingRate: convertRateToPeriod(Number(formatOutput(userInfo.outgoingRate, 18)), RatePeriod.MONTH),
  updated: new Date(Number(userInfo.updated) * 1000).toLocaleString()
};

console.log(`Balance: ${formattedUserInfo.balance} USDT`);
console.log(`Income rate: ${formattedUserInfo.incomeRate} USDT per month`);
console.log(`Outgoing rate: ${formattedUserInfo.outgoingRate} USDT per month`);
console.log(`Last updated: ${formattedUserInfo.updated}`);
```


# Deposit Methods

Deposit methods of Papaya SDK for depositing tokens into the protocol

### `deposit()`

Deposits tokens into the Papaya protocol.

```typescript
async deposit(amount: bigint | number, isPermit2: boolean = false): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `amount`: The amount of tokens to deposit, should be formatted using `formatInput()`
* `isPermit2`: (Optional) Whether to use Permit2 for the deposit (default: false)

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
// Format the amount correctly (10 USDT with 6 decimals)
const amount = formatInput('10', 6);

// Deposit
const tx = await papaya.deposit(amount);
await tx.wait();
console.log('Deposit successful');
```

### `depositBySig()`

Creates a deposit transaction that can be signed off-chain and executed by anyone.

```typescript
async depositBySig(amount: bigint | number, deadline: number): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `amount`: The amount of tokens to deposit, should be formatted using `formatInput()`
* `deadline`: Timestamp after which the transaction can't be executed

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
// Format the amount correctly (10 USDT with 6 decimals)
const amount = formatInput('10', 6);

// Set deadline to 1 hour from now
const deadline = Math.floor(Date.now() / 1000) + 3600;

// Create the depositBySig transaction
const tx = await papaya.depositBySig(amount, deadline);
await tx.wait();
```

### `depositFor()`

Deposits tokens into another account.

```typescript
async depositFor(amount: bigint | number, to: string, isPermit2: boolean = false): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `amount`: The amount of tokens to deposit, should be formatted using `formatInput()`
* `to`: The recipient address
* `isPermit2`: (Optional) Whether to use Permit2 for the deposit (default: false)

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
// Format the amount correctly (50 USDT with 6 decimals)
const amount = formatInput('50', 6);
const recipientAddress = '0x...';

// Deposit to the recipient
const tx = await papaya.depositFor(amount, recipientAddress);
await tx.wait();
```


# Withdrawal Methods

Withdrawal methods of Papaya SDK for withdrawing tokens from the protocol

### `withdraw()`

Withdraws tokens from the Papaya protocol.

```typescript
async withdraw(amount: bigint | number): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `amount`: The amount of tokens to withdraw, should be formatted using `formatInput()`

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
// Format the amount correctly (50 USDT with 18 decimals)
const amount = formatInput('50', 18);

// Withdraw
const tx = await papaya.withdraw(amount);
await tx.wait();
console.log('Withdrawal successful');
```

### `withdrawBySig()`

Creates a withdrawal transaction that can be signed off-chain and executed by anyone.

```typescript
async withdrawBySig(amount: bigint | number, deadline: number): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `amount`: The amount of tokens to withdraw, should be formatted using `formatInput()`
* `deadline`: Timestamp after which the transaction can't be executed

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
// Format the amount correctly (50 USDT with 18 decimals)
const amount = formatInput('50', 18);

// Set deadline to 1 hour from now
const deadline = Math.floor(Date.now() / 1000) + 3600;

// Create the withdrawBySig transaction
const tx = await papaya.withdrawBySig(amount, deadline);
await tx.wait();
```

### `withdrawTo()`

Withdraws tokens directly to another address.

```typescript
async withdrawTo(to: string, amount: bigint | number): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `to`: The recipient address
* `amount`: The amount of tokens to withdraw, should be formatted using `formatInput()`

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
const recipientAddress = '0x...';
// Format the amount correctly (25 USDT with 18 decimals)
const amount = formatInput('25', 18);

// Withdraw to the recipient
const tx = await papaya.withdrawTo(recipientAddress, amount);
await tx.wait();
```


# Subscription Methods

Subscription methods of Papaya SDK for managing subscriptions to content creators

### `subscribe()`

Creates a new subscription to a creator.

```typescript
async subscribe(
  author: string, 
  amount: number | bigint,
  period: RatePeriod = RatePeriod.MONTH,
  projectId: number
): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `author`: The address of the creator to subscribe to
* `amount`: The amount of tokens for the subscription period
* `period`: (Optional) The subscription period (default: RatePeriod.MONTH)
* `projectId`: The project ID associated with the subscription

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
const creatorAddress = '0x...';
const tx = await papaya.subscribe(creatorAddress, 10, RatePeriod.MONTH, 0);
await tx.wait();
```

### `subscribeBySig()`

Creates a subscription transaction that can be signed off-chain and executed by anyone.

```typescript
async subscribeBySig(
  author: string, 
  amount: number | bigint,
  period: RatePeriod = RatePeriod.MONTH,
  projectId: number,
  deadline: number
): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `author`: The address of the creator to subscribe to
* `amount`: The amount of tokens for the subscription period
* `period`: (Optional) The subscription period (default: RatePeriod.MONTH)
* `projectId`: The project ID associated with the subscription
* `deadline`: Timestamp after which the transaction can't be executed

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
const creatorAddress = '0x...';
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
const tx = await papaya.subscribeBySig(creatorAddress, 10, RatePeriod.MONTH, 0, deadline);
await tx.wait();
```

### `unsubscribe()`

Cancels a subscription to a creator.

```typescript
async unsubscribe(author: string): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `author`: The address of the creator to unsubscribe from

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
const creatorAddress = '0x...';
const tx = await papaya.unsubscribe(creatorAddress);
await tx.wait();
```

### `unsubscribeBySig()`

Creates an unsubscribe transaction that can be signed off-chain and executed by anyone.

```typescript
async unsubscribeBySig(author: string, deadline: number): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `author`: The address of the creator to unsubscribe from
* `deadline`: Timestamp after which the transaction can't be executed

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
const creatorAddress = '0x...';
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
const tx = await papaya.unsubscribeBySig(creatorAddress, deadline);
await tx.wait();
```

### `getSubscriptions()`

Gets all subscriptions for an account.

```typescript
async getSubscriptions(account?: string): Promise<Subscription[]>
```

**Parameters:**

* `account`: (Optional) The address to get subscriptions for. If not provided, uses the connected signer's address.

**Returns:** Array of `Subscription` objects, each containing:

* `recipient`: The address receiving the subscription
* `incomeRate`: The rate at which the recipient is receiving tokens (raw format)
* `outgoingRate`: The rate at which the subscriber is paying tokens (raw format)
* `projectId`: The project ID associated with the subscription

{% hint style="info" %}
The `incomeRate` and `outgoingRate` values are per-second rates in their raw blockchain format. You should use the [utility functions](/sdk/utilities) to convert them to human-readable values.
{% endhint %}

**Example:**

```typescript
// Get raw subscriptions
const subscriptions = await papaya.getSubscriptions();

// Format the subscription rates to human-readable monthly values
const formattedSubscriptions = subscriptions.map(sub => ({
  recipient: sub.recipient,
  incomeRate: convertRateToPeriod(formatOutput(sub.incomeRate, 18), RatePeriod.MONTH),
  outgoingRate: convertRateToPeriod(formatOutput(sub.outgoingRate, 18), RatePeriod.MONTH),
  projectId: sub.projectId
}));
```


# Payment Methods

Methods for making one-time payments in the Papaya protocol

### `pay()`

Makes a one-time payment to a recipient.

```typescript
async pay(receiver: string, amount: bigint | number): Promise<ethers.TransactionResponse>
```

**Parameters:**

* `receiver`: The recipient's address
* `amount`: The amount of tokens to pay, should be formatted using `formatInput()`

**Returns:** An ethers.js `TransactionResponse` object.

**Example:**

```typescript
const recipientAddress = '0x...';
// Format the amount correctly (20 USDT with 6 decimals)
const amount = formatInput('20', 6);

// Make the payment
const tx = await papaya.pay(recipientAddress, amount);
await tx.wait();
```


# Examples


# Browser Integration

Example of integrating Papaya SDK with a browser-based web application

{% hint style="info" %}
This example demonstrates how to integrate the Papaya SDK with a browser-based web application using a browser wallet like MetaMask.
{% endhint %}

```typescript
// App.tsx or App.jsx
import React, { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import { PapayaSDK } from '@papaya_fi/sdk';

function App() {
  const [papaya, setPapaya] = useState(null);
  const [account, setAccount] = useState('');
  const [balance, setBalance] = useState('0');
  const [loading, setLoading] = useState(true);
  
  // Initialize SDK when the component mounts
  useEffect(() => {
    async function initializePapaya() {
      if (window.ethereum) {
        try {
          // Request account access
          await window.ethereum.request({ method: 'eth_requestAccounts' });
          
          const provider = new ethers.BrowserProvider(window.ethereum);
          const signer = await provider.getSigner();
          const userAddress = await signer.getAddress();
          
          // Create Papaya SDK instance
          const papayaInstance = PapayaSDK.create(signer, 'polygon', 'USDT');
          
          // Get user balance
          const userBalance = await papayaInstance.balanceOf();
          
          setPapaya(papayaInstance);
          setAccount(userAddress);
          setBalance(userBalance.toString());
          setLoading(false);
        } catch (error) {
          console.error('Error initializing Papaya SDK:', error);
          setLoading(false);
        }
      } else {
        console.log('Please install MetaMask!');
        setLoading(false);
      }
    }
    
    initializePapaya();
  }, []);
  
  // Function to handle deposits
  async function handleDeposit() {
    if (!papaya) return;
    
    try {
      const tx = await papaya.deposit(10); // Deposit 10 USDT
      await tx.wait();
      
      // Update balance
      const newBalance = await papaya.balanceOf();
      setBalance(newBalance.toString());
      
      alert('Deposit successful!');
    } catch (error) {
      console.error('Error depositing funds:', error);
      alert('Error depositing funds. See console for details.');
    }
  }
  
  return (
    <div className="App">
      <h1>Papaya SDK Demo</h1>
      
      {loading ? (
        <p>Loading...</p>
      ) : (
        <>
          <p>Connected account: {account}</p>
          <p>Your balance: {balance} USDT</p>
          
          <button onClick={handleDeposit}>
            Deposit 10 USDT
          </button>
        </>
      )}
    </div>
  );
}

export default App;
```

### Key Points

1. **Check for MetaMask**: Check for `window.ethereum` before initialization
2. **Request account access**: Use `eth_requestAccounts` to get permission
3. **Create provider**: Use `BrowserProvider` to connect to MetaMask
4. **Error handling**: Wrap all operations in try/catch blocks
5. **Update UI**: Update state after successful transactions


# Advanced Examples

Advanced examples and patterns for Papaya SDK

{% hint style="info" %}
This document covers advanced usage patterns and sophisticated integrations with the Papaya SDK.
{% endhint %}

### Gas Optimization

#### Batching Transactions

If you need to perform multiple operations, consider batching them to minimize gas costs:

```typescript
import { ethers } from 'ethers';
import { PapayaSDK } from '@papaya_fi/sdk';

async function optimizedOperations(signer: ethers.Signer) {
  const papaya = PapayaSDK.create(signer, 'polygon', 'USDT');
  
  // Instead of multiple separate transactions:
  // await papaya.deposit(100);
  // await papaya.subscribe(creator1, 10);
  // await papaya.subscribe(creator2, 5);
  
  // Batch the operations in your UI/UX flow
  // First deposit enough tokens for everything
  const depositTx = await papaya.deposit(115); // 100 + 10 + 5
  await depositTx.wait();
  
  // Then do the subscriptions
  const [tx1, tx2] = await Promise.all([
    papaya.subscribe(creator1, 10),
    papaya.subscribe(creator2, 5)
  ]);
  
  await Promise.all([tx1.wait(), tx2.wait()]);
}
```

#### Using Permit2 for Deposits

The Papaya SDK supports using Permit2 for approving and depositing tokens in a single transaction, which saves gas:

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';

async function depositWithPermit2(papaya: PapayaSDK, amount: number) {
  // Set isPermit2 to true to use Permit2 for approval and deposit in one transaction
  const tx = await papaya.deposit(amount, true);
  await tx.wait();
  console.log('Deposit with Permit2 completed successfully');
}
```

### Working with Custom Contract Versions

The Papaya SDK supports multiple contract versions for each network and token.

#### Specifying a Contract Version

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';

// Using a specific contract version
const papayaV1 = PapayaSDK.create(provider, 'polygon', 'USDT', '1');

// Using the latest version (default)
const papaya = PapayaSDK.create(provider, 'polygon', 'USDT');
```

#### Working with Custom Contracts

You can also specify custom contract and token addresses:

```typescript
import { PapayaSDK, PapayaSDKOptions } from '@papaya_fi/sdk';

// Using custom addresses
const options: PapayaSDKOptions = {
  provider: signer,
  network: 'polygon',
  tokenSymbol: 'USDT',
  contractAddress: '0xCustomContractAddress',
  tokenAddress: '0xCustomTokenAddress'
};

const papaya = new PapayaSDK(options);
```

### Multiple Network Support

#### Working with Multiple Networks Simultaneously

For applications that need to work with multiple networks, you can create multiple SDK instances:

```typescript
import { ethers } from 'ethers';
import { PapayaSDK, NetworkName } from '@papaya_fi/sdk';

// Function to create SDK instances for multiple networks
async function createMultiNetworkSDKs(privateKey: string) {
  const networks: NetworkName[] = ['polygon', 'bsc', 'mainnet'];
  const sdkInstances = {};
  
  for (const network of networks) {
    // Create provider for each network
    const provider = new ethers.JsonRpcProvider(getRpcUrl(network));
    const signer = new ethers.Wallet(privateKey, provider);
    
    // Create SDK instance
    sdkInstances[network] = PapayaSDK.create(signer, network, 'USDT');
  }
  
  return sdkInstances;
}

// Helper function to get RPC URL for a network
function getRpcUrl(network: NetworkName): string {
  const rpcUrls = {
    polygon: 'https://polygon-rpc.com',
    bsc: 'https://bsc-dataseed.binance.org',
    mainnet: 'https://eth.llamarpc.com',
    // Add more networks as needed
  };
  
  return rpcUrls[network] || rpcUrls['polygon']; // Default to polygon
}
```

#### Dynamically Switching Networks

For applications that need to switch networks dynamically:

```typescript
import { ethers } from 'ethers';
import { PapayaSDK, NetworkName, TokenSymbol } from '@papaya_fi/sdk';

class NetworkManager {
  private signer: ethers.Signer;
  private currentSDK: PapayaSDK | null = null;
  private currentNetwork: NetworkName | null = null;
  
  constructor(signer: ethers.Signer) {
    this.signer = signer;
  }
  
  async switchNetwork(network: NetworkName, token: TokenSymbol = 'USDT') {
    if (network === this.currentNetwork) return this.currentSDK;
    
    try {
      // For browser wallet integration, you might need to request network switch
      if (window.ethereum) {
        const chainId = getChainId(network);
        await window.ethereum.request({
          method: 'wallet_switchEthereumChain',
          params: [{ chainId: `0x${chainId.toString(16)}` }],
        });
      }
      
      // Create new SDK instance for the selected network
      this.currentSDK = PapayaSDK.create(this.signer, network, token);
      this.currentNetwork = network;
      
      return this.currentSDK;
    } catch (error) {
      console.error(`Error switching to network ${network}:`, error);
      throw error;
    }
  }
  
  getCurrentSDK(): PapayaSDK {
    if (!this.currentSDK) {
      throw new Error('No network selected. Call switchNetwork first.');
    }
    return this.currentSDK;
  }
}

// Helper function to get chain ID for a network
function getChainId(network: NetworkName): number {
  const chainIds = {
    polygon: 137,
    bsc: 56,
    avalanche: 43114,
    base: 8453,
    scroll: 534352,
    arbitrum: 42161,
    mainnet: 1,
    sei: 32741,
    zksync: 324
  };
  
  return chainIds[network] || 137; // Default to polygon
}
```

### Relayer Services

To fully leverage **BySig methods**, you'll need a relayer service to submit the signed transactions to the blockchain.

#### Building a Simple Relayer

Here's a basic example of how to build a simple relayer service using **Node.js** and **Express**:

```typescript
// relayer.ts
import express from 'express';
import { ethers } from 'ethers';
import { PapayaSDK } from '@papaya_fi/sdk';
import dotenv from 'dotenv';

dotenv.config();

const app = express();
app.use(express.json());

const NETWORKS = {
  polygon: 'https://polygon-rpc.com',
  bsc: 'https://bsc-dataseed.binance.org',
  mainnet: 'https://eth.llamarpc.com',
  // Add more networks as needed
};

// Initialize providers and signers for each network
const providers = {};
const signers = {};
const sdkInstances = {};

Object.entries(NETWORKS).forEach(([network, rpcUrl]) => {
  providers[network] = new ethers.JsonRpcProvider(rpcUrl);
  signers[network] = new ethers.Wallet(process.env.RELAYER_PRIVATE_KEY, providers[network]);
  sdkInstances[network] = PapayaSDK.create(signers[network], network as any, 'USDT');
});

// Endpoint to handle gasless deposits
app.post('/api/relay/deposit', async (req, res) => {
  try {
    const { txData, chainId } = req.body;
    
    // Determine network from chain ID
    const network = getNetworkFromChainId(chainId);
    if (!network || !sdkInstances[network]) {
      return res.status(400).json({ 
        success: false, 
        error: 'Unsupported network' 
      });
    }
    
    // Submit the transaction
    const provider = providers[network];
    const signer = signers[network];
    
    // Here you would extract the necessary data from txData
    // and call the contract's bySig method directly
    
    // For a complete implementation, you would:
    // 1. Extract user address, amount, deadline, and signature from txData
    // 2. Call the contract directly with these parameters
    // 3. Return the transaction hash
    
    // Simplified example (this is not complete and would need adaptation):
    const tx = await signer.sendTransaction(txData);
    const receipt = await tx.wait();
    
    return res.json({
      success: true,
      txHash: receipt.hash
    });
  } catch (error) {
    console.error('Relayer error:', error);
    return res.status(500).json({
      success: false,
      error: 'Relayer error: ' + error.message
    });
  }
});

function getNetworkFromChainId(chainId: number): string | null {
  const chainIdMap = {
    1: 'mainnet',
    56: 'bsc',
    137: 'polygon',
    // Add more as needed
  };
  
  return chainIdMap[chainId] || null;
}

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Relayer service running on port ${PORT}`);
});
```

### Error Handling and Recovery

#### Common Errors and Solutions

| Error                     | Potential Cause                               | Solution                                                      |
| ------------------------- | --------------------------------------------- | ------------------------------------------------------------- |
| "Insufficient allowance"  | Token approval needed                         | Call the token's approve method before depositing             |
| "Insufficient balance"    | User doesn't have enough tokens               | Inform user to get more tokens                                |
| "Transaction underpriced" | Gas price too low                             | Increase gas price or wait for network congestion to decrease |
| "Nonce too low"           | Transaction with same nonce already processed | Reset nonce or use the next available nonce                   |
| "Deadline expired"        | BySig transaction submitted after deadline    | Generate a new signature with a future deadline               |

#### Implementing Robust Error Handling

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';
import { ethers } from 'ethers';

async function robustDeposit(papaya: PapayaSDK, amount: number): Promise<boolean> {
  try {
    const tx = await papaya.deposit(amount);
    await tx.wait();
    return true;
  } catch (error) {
    // Handle specific errors
    if (error.message.includes('insufficient allowance')) {
      console.warn('Token approval needed. Attempting to approve...');
      try {
        // Attempt to approve tokens and retry
        // This would require implementing an approveTokens function
        await approveTokens(papaya, amount);
        
        // Retry deposit
        const tx = await papaya.deposit(amount);
        await tx.wait();
        return true;
      } catch (approvalError) {
        console.error('Failed to approve tokens:', approvalError);
        throw new Error('Token approval failed. Please approve tokens manually.');
      }
    } else if (error.message.includes('insufficient funds')) {
      throw new Error('Insufficient balance. Please add more tokens to your wallet.');
    } else {
      // Generic error handling
      console.error('Deposit error:', error);
      throw error;
    }
  }
}

// Example function to approve tokens (implementation would depend on your setup)
async function approveTokens(papaya: PapayaSDK, amount: number) {
  // Implementation would depend on how you access the token contract
  // This is just a placeholder
  const tokenContract = getTokenContract();
  const tx = await tokenContract.approve(papaya.getContractAddress(), amount);
  await tx.wait();
}
```

#### Transaction Monitoring and Recovery

For critical operations, implement transaction monitoring and recovery:

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';
import { ethers } from 'ethers';

async function monitorTransaction(
  txHash: string,
  provider: ethers.Provider,
  maxAttempts: number = 10
): Promise<ethers.TransactionReceipt> {
  let attempts = 0;
  
  while (attempts < maxAttempts) {
    try {
      const receipt = await provider.getTransactionReceipt(txHash);
      
      if (receipt) {
        // Check if transaction was successful
        if (receipt.status === 1) {
          return receipt;
        } else {
          throw new Error('Transaction failed');
        }
      }
      
      // Wait before checking again
      await new Promise(resolve => setTimeout(resolve, 5000));
      attempts++;
    } catch (error) {
      if (attempts >= maxAttempts) {
        throw error;
      }
      
      // Wait longer before retrying
      await new Promise(resolve => setTimeout(resolve, 5000));
      attempts++;
    }
  }
  
  throw new Error('Transaction not confirmed after maximum attempts');
}

// Usage example
async function safeDeposit(papaya: PapayaSDK, amount: number) {
  try {
    const tx = await papaya.deposit(amount);
    console.log(`Transaction sent: ${tx.hash}`);
    
    // Monitor transaction
    const receipt = await monitorTransaction(tx.hash, papaya.getProvider());
    console.log(`Deposit confirmed in block ${receipt.blockNumber}`);
    
    return receipt;
  } catch (error) {
    console.error('Deposit failed:', error);
    // Implement recovery logic here if needed
    throw error;
  }
}
```

### Performance Optimization

#### Caching Strategies

```typescript
class CachedPapayaSDK {
  private papaya: PapayaSDK;
  private cache: Map<string, { data: any; timestamp: number }> = new Map();
  private cacheTimeout: number = 30000; // 30 seconds
  
  constructor(papaya: PapayaSDK) {
    this.papaya = papaya;
  }
  
  async getCachedBalance(address: string): Promise<string> {
    const cacheKey = `balance_${address}`;
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.data;
    }
    
    const balance = await this.papaya.balanceOf(address);
    this.cache.set(cacheKey, { data: balance, timestamp: Date.now() });
    
    return balance;
  }
  
  async getCachedUserInfo(address: string): Promise<any> {
    const cacheKey = `userInfo_${address}`;
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.data;
    }
    
    const userInfo = await this.papaya.getUserInfo(address);
    this.cache.set(cacheKey, { data: userInfo, timestamp: Date.now() });
    
    return userInfo;
  }
  
  // Clear cache when transactions are made
  clearCache() {
    this.cache.clear();
  }
}
```

*These advanced techniques will help you build robust applications that leverage the full power of the Papaya SDK while ensuring optimal performance and user experience.*


# Network Support


# Supported Networks

Supported blockchain networks in Papaya SDK

The SDK currently supports the following networks:

| Network     | Chain ID | Description                                                 |
| ----------- | -------- | ----------------------------------------------------------- |
| `polygon`   | 137      | Polygon mainnet, a popular Ethereum scaling solution        |
| `bsc`       | 56       | Binance Smart Chain, Binance's EVM-compatible blockchain    |
| `avalanche` | 43114    | Avalanche C-Chain, an EVM-compatible blockchain             |
| `base`      | 8453     | Base, an Ethereum L2 scaling solution by Coinbase           |
| `scroll`    | 534352   | Scroll, an Ethereum L2 zk-rollup scaling solution           |
| `arbitrum`  | 42161    | Arbitrum, an Ethereum L2 optimistic rollup scaling solution |
| `mainnet`   | 1        | Ethereum mainnet                                            |
| `sei`       | 32741    | Sei EVM, a high-performance EVM-compatible blockchain       |
| `zksync`    | 324      | zkSync Era, an Ethereum L2 zk-rollup scaling solution       |

You can get a list of all supported networks programmatically:

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';

const networks = PapayaSDK.getAvailableNetworks();
console.log(networks);
```

### RPC Providers

To use the Papaya SDK, you'll need access to an RPC endpoint for the network you're targeting. Here are some public RPC endpoints for the supported networks:

| Network   | Example RPC URL                         |
| --------- | --------------------------------------- |
| polygon   | `https://polygon-rpc.com`               |
| bsc       | `https://bsc-dataseed.binance.org`      |
| avalanche | `https://api.avax.network/ext/bc/C/rpc` |
| base      | `https://mainnet.base.org`              |
| scroll    | `https://rpc.scroll.io`                 |
| arbitrum  | `https://arb1.arbitrum.io/rpc`          |
| mainnet   | `https://eth.llamarpc.com`              |
| sei       | `https://evm-rpc.sei.io`                |
| zksync    | `https://mainnet.era.zksync.io`         |

*For production use, we recommend using a dedicated RPC provider service like* [*Infura*](https://www.infura.io/)*,* [*Alchemy*](https://www.alchemy.com/)*, or* [*QuickNode*](https://www.quicknode.com/)*.*


# Token Availability

Token availability across different networks in Papaya SDK

{% hint style="info" %}
The SDK supports the following stablecoin tokens:
{% endhint %}

| Token   | Description                                                          |
| ------- | -------------------------------------------------------------------- |
| `USDT`  | Tether USD, a widely used stablecoin                                 |
| `USDC`  | USD Coin, a fully-collateralized stablecoin                          |
| `PYUSD` | PayPal USD, a stablecoin issued by PayPal (only on Ethereum mainnet) |

{% hint style="info" %}
Not all tokens are available on all networks. You can check which tokens are available on a specific network:
{% endhint %}

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';

const polygonTokens = PapayaSDK.getAvailableTokens('polygon');
console.log(polygonTokens); // ['USDT', 'USDC']
```

### Network and Token Availability Matrix

Below is a matrix showing which tokens are available on which networks:

| Network   | USDT | USDC | PYUSD |
| --------- | ---- | ---- | ----- |
| polygon   | ✅    | ✅    | ❌     |
| bsc       | ✅    | ✅    | ❌     |
| avalanche | ✅    | ✅    | ❌     |
| base      | ❌    | ✅    | ❌     |
| scroll    | ✅    | ✅    | ❌     |
| arbitrum  | ✅    | ✅    | ❌     |
| mainnet   | ✅    | ✅    | ✅     |
| sei       | ✅    | ✅    | ❌     |
| zksync    | ✅    | ❌    | ❌     |

### Contract Versions

The SDK supports multiple contract versions for each token on each network. By default, it will use the latest available version, but you can specify a particular version if needed.

*For example, on Polygon, both USDT and USDC have versions "1" and "1.5.3" available.*

When creating a new SDK instance, you can specify a particular version:

```typescript
import { PapayaSDK } from '@papaya_fi/sdk';

// Using the latest version (default)
const papaya = PapayaSDK.create(provider, 'polygon', 'USDT');

// Specifying a particular version
const papayaV1 = PapayaSDK.create(provider, 'polygon', 'USDT', '1');
```


# Utilities


# Rate Conversion

Rate conversion functions of Papaya SDK for working with time periods

{% hint style="info" %}
The Papaya Protocol stores subscription rates as per-second values (raw), but in applications, you'll typically want to display these as more human-readable periods like per month or per year.
{% endhint %}

### Rate Periods

The SDK exports a `RatePeriod` enum to represent different time periods:

```typescript
export enum RatePeriod {
  SECOND = 'second',
  HOUR = 'hour',
  DAY = 'day',
  WEEK = 'week',
  MONTH = 'month',
  YEAR = 'year'
}
```

These period values correspond to the following conversion factors (in seconds):

| Period | Seconds                 |
| ------ | ----------------------- |
| SECOND | 1                       |
| HOUR   | 3,600                   |
| DAY    | 86,400                  |
| WEEK   | 604,800                 |
| MONTH  | 2,628,000 (≈30.42 days) |
| YEAR   | 31,536,000 (365 days)   |

### `convertRatePerSecond()`

Converts an amount for a specific period (e.g., 10 USDT per month) to a per-second rate.

```typescript
function convertRatePerSecond(amount: string, period: RatePeriod): number
```

**Parameters:**

* `amount`: The rate amount as a string
* `period`: The time period for the rate (e.g., `RatePeriod.MONTH`)

**Returns:** The equivalent per-second rate as a number.

**Example:**

```typescript
// Convert 10 USDT per month to a per-second rate
const perSecondRate = convertRatePerSecond('10', RatePeriod.MONTH);
console.log(perSecondRate); // Very small number representing tokens per second
```

### `convertRateToPeriod()`

Converts a per-second rate to a rate for a specified period.

```typescript
function convertRateToPeriod(ratePerSecond: number, period: RatePeriod): number
```

**Parameters:**

* `ratePerSecond`: The per-second rate as a number
* `period`: The target time period (e.g., `RatePeriod.MONTH`)

**Returns:** The rate for the target period as a number.

**Example:**

```typescript
// Convert a per-second rate to a monthly rate
const monthlyRate = convertRateToPeriod(perSecondRate, RatePeriod.MONTH);
console.log(monthlyRate); // Approximately 10 (if perSecondRate was from previous example)
```

### Practical Example

```typescript
import { convertRatePerSecond, convertRateToPeriod, RatePeriod } from '@papaya_fi/sdk';

// User wants to subscribe to 25 USDT per month
const monthlyAmount = '25';
const perSecondRate = convertRatePerSecond(monthlyAmount, RatePeriod.MONTH);

// Use the per-second rate for subscription
await papaya.subscribe(creatorAddress, perSecondRate, RatePeriod.SECOND, projectId);

// Later, when getting subscription info
const userInfo = await papaya.getUserInfo();
const rawIncomeRate = userInfo.incomeRate;

// Convert back to human-readable format
const monthlyIncome = convertRateToPeriod(Number(rawIncomeRate), RatePeriod.MONTH);
console.log(`Monthly income: ${monthlyIncome} USDT`);
```


# Formatting Functions

Formatting functions of Papaya SDK for working with numbers and blockchain data

{% hint style="info" %}
The SDK provides functions for formatting amounts between different representations.
{% endhint %}

### `formatInput()`

Converts a human-readable amount into a bigint for use in blockchain transactions.

```typescript
function formatInput(amount: string, unit?: string | ethers.Numeric): bigint
```

**Parameters:**

* `amount`: The amount as a string
* `unit`: (Optional) The unit of the amount (e.g., "18" for 18 decimal places)

**Returns:** The formatted bigint amount.

**Example:**

```typescript
// Format 10 USDT for transaction (USDT has 6 decimal places)
const amount = formatInput('10', 6);
console.log(amount); // 10000000n

// Format 1.5 ETH for transaction (ETH has 18 decimal places)
const ethAmount = formatInput('1.5', 18);
console.log(ethAmount); // 1500000000000000000n
```

### `formatOutput()`

Converts a bigint amount from blockchain to a human-readable number.

```typescript
function formatOutput(amount: bigint | number, unit?: string | ethers.Numeric): string
```

**Parameters:**

* `amount`: The amount as bigint or number
* `unit`: (Optional) The unit of the amount (e.g., "18" for 18 decimal places)

**Returns:** The formatted amount as a string.

**Example:**

```typescript
// Convert raw USDT balance to readable format
const rawBalance = 10000000n; // 10 USDT in raw format
const readableBalance = formatOutput(rawBalance, 6);
console.log(readableBalance); // "10.0"

// Convert raw ETH balance to readable format
const rawEthBalance = 1500000000000000000n; // 1.5 ETH in raw format
const readableEthBalance = formatOutput(rawEthBalance, 18);
console.log(readableEthBalance); // "1.5"
```

### Practical Examples

#### Working with Balances

```typescript
import { formatInput, formatOutput } from '@papaya_fi/sdk';

// Get user balance
const rawBalance = await papaya.balanceOf();

// Convert to human-readable format for display
const displayBalance = formatOutput(BigInt(rawBalance), 18);
console.log(`Your balance: ${displayBalance} USDT`);

// User wants to withdraw 25 USDT
const withdrawalAmount = formatInput('25', 18);
const tx = await papaya.withdraw(withdrawalAmount);
await tx.wait();
```

#### Working with Different Tokens

```typescript
// USDT has 6 decimal places
const usdtAmount = formatInput('100', 6); // 100000000n

// USDC also has 6 decimal places
const usdcAmount = formatInput('50', 6); // 50000000n

// ETH has 18 decimal places
const ethAmount = formatInput('0.1', 18); // 100000000000000000n
```

#### Error Handling

```typescript
try {
  const amount = formatInput('invalid', 18);
} catch (error) {
  console.error('Formatting error:', error.message);
  // Error: invalid value for BigInt
}

try {
  const amount = formatInput('-10', 18);
} catch (error) {
  console.error('Formatting error:', error.message);
  // Error: negative value for BigInt
}
```


# TypeScript Types

TypeScript types and interfaces of Papaya SDK

{% hint style="info" %}
The Papaya SDK provides full TypeScript typing for better development experience and autocompletion.
{% endhint %}

### Main Types

### `PapayaSDK`

The main SDK class for interacting with the Papaya Protocol.

```typescript
class PapayaSDK {
  static create(
    provider: ethers.Provider | ethers.Signer,
    network?: NetworkName,
    tokenSymbol?: TokenSymbol,
    contractVersion?: string
  ): PapayaSDK;
  
  static getAvailableNetworks(): NetworkName[];
  static getAvailableTokens(network: NetworkName): TokenSymbol[];
}
```

### `NetworkName`

Type for supported network names.

```typescript
type NetworkName = 
  | 'polygon'
  | 'bsc'
  | 'avalanche'
  | 'base'
  | 'scroll'
  | 'arbitrum'
  | 'mainnet'
  | 'sei'
  | 'zksync';
```

### `TokenSymbol`

Type for supported token symbols.

```typescript
type TokenSymbol = 'USDT' | 'USDC' | 'PYUSD';
```

### `RatePeriod`

Enum for time periods of rates.

```typescript
enum RatePeriod {
  SECOND = 'second',
  HOUR = 'hour',
  DAY = 'day',
  WEEK = 'week',
  MONTH = 'month',
  YEAR = 'year'
}
```

### Data Interfaces

### `UserInfo`

Interface for user information.

```typescript
interface UserInfo {
  balance: bigint;
  incomeRate: bigint;
  outgoingRate: bigint;
  updated: bigint;
}
```

### `Subscription`

Interface for subscription information.

```typescript
interface Subscription {
  recipient: string;
  incomeRate: bigint;
  outgoingRate: bigint;
  projectId: number;
}
```

### `SubscriptionInfo`

Interface for detailed subscription information.

```typescript
interface SubscriptionInfo {
  isSubscribed: boolean;
  outgoingRate: bigint;
  projectId: number;
}
```

### Type Usage Examples

#### Creating a Typed Instance

```typescript
import { PapayaSDK, NetworkName, TokenSymbol } from '@papaya_fi/sdk';

// Typed parameters
const network: NetworkName = 'polygon';
const token: TokenSymbol = 'USDT';

const papaya = PapayaSDK.create(provider, network, token);
```

#### Working with User Information

```typescript
import { UserInfo, formatOutput, convertRateToPeriod, RatePeriod } from '@papaya_fi/sdk';

async function displayUserInfo() {
  const userInfo: UserInfo = await papaya.getUserInfo();
  
  const formattedInfo = {
    balance: formatOutput(userInfo.balance, 18),
    incomeRate: convertRateToPeriod(
      Number(formatOutput(userInfo.incomeRate, 18)), 
      RatePeriod.MONTH
    ),
    outgoingRate: convertRateToPeriod(
      Number(formatOutput(userInfo.outgoingRate, 18)), 
      RatePeriod.MONTH
    ),
    updated: new Date(Number(userInfo.updated) * 1000)
  };
  
  return formattedInfo;
}
```

#### Working with Subscriptions

```typescript
import { Subscription, convertRateToPeriod, RatePeriod } from '@papaya_fi/sdk';

async function getFormattedSubscriptions() {
  const subscriptions: Subscription[] = await papaya.getSubscriptions();
  
  return subscriptions.map((sub: Subscription) => ({
    recipient: sub.recipient,
    monthlyRate: convertRateToPeriod(
      Number(formatOutput(sub.outgoingRate, 18)), 
      RatePeriod.MONTH
    ),
    projectId: sub.projectId
  }));
}
```

#### Checking Subscriptions

```typescript
import { SubscriptionInfo } from '@papaya_fi/sdk';

async function checkSubscription(creatorAddress: string) {
  const subInfo: SubscriptionInfo = await papaya.isSubscribed(creatorAddress);
  
  if (subInfo.isSubscribed) {
    const monthlyRate = convertRateToPeriod(
      Number(formatOutput(subInfo.outgoingRate, 18)), 
      RatePeriod.MONTH
    );
    console.log(`Subscribed to ${creatorAddress} with rate ${monthlyRate} USDT/month`);
  } else {
    console.log(`Not subscribed to ${creatorAddress}`);
  }
}
```

### Utility Types

### `BigNumberish`

Type for numbers that can be converted to BigNumber.

```typescript
type BigNumberish = string | number | bigint;
```

### `TransactionResponse`

Type for ethers.js transaction responses.

```typescript
import { TransactionResponse } from 'ethers';

async function sendTransaction(): Promise<TransactionResponse> {
  return await papaya.deposit(amount);
}
```


# Token Symbol

Utility methods of the SDK for getting token information and configuration

### `getTokenSymbol()`

Gets the current token symbol used by the SDK instance.

```typescript
getTokenSymbol(): TokenSymbol
```

**Returns:** The current token symbol.

**Example:**

```typescript
const token = papaya.getTokenSymbol();
console.log(`Using token: ${token}`); // 'USDT'
```


# Main API

Browser-compatible JavaScript API client for Papaya crypto payments, subscriptions, and revenue splits with no axios dependency.

✅ Works in modern browsers

✅ Uses native fetch()

✅ Non-custodial, signer-based transaction execution

✅ Supports USDT/USDC on Ethereum and L2s (Arbitrum, Base, etc.)

***

### 📦 Installation

```
npm install @papaya_fi/api ethers
```

### Features

* Simple interface for interacting with the Papaya Protocol
* Support for multiple networks (Ethereum, Base, Polygon, BSC, Avalanche, etc.)
* Multiple stablecoin support (USDT, USDC, PYUSD)
* Typed definitions for better development experience
* Multiple contract version support
* BySig methods for gasless transactions
* Comprehensive transaction handling
* Utility functions for rate conversions and data formatting

### Quick Start

```typescript
import { ethers } from 'ethers';
import { PapayaSDK, formatOutput, convertRateToPeriod, RatePeriod } from '@papaya_fi/sdk';

// Create an Ethereum provider
const provider = new ethers.JsonRpcProvider('YOUR_RPC_URL');

// Create a signer if you need to send transactions
const privateKey = 'YOUR_PRIVATE_KEY';
const signer = new ethers.Wallet(privateKey, provider);

// Create a Papaya SDK instance
const papaya = PapayaSDK.create(
  signer,      // Or provider if you only need read-only operations
  'polygon',   // Network name (default is 'polygon')
  'USDT'       // Token symbol (default is 'USDT')
);

// Now you can use the SDK to interact with the Papaya Protocol
async function getBalance() {
  const rawBalance = await papaya.balanceOf();
  // Convert raw balance to readable format
  const readableBalance = formatOutput(BigInt(rawBalance), 18);
  console.log(`Your balance: ${readableBalance} USDT`);
}

// Example subscription
async function subscribeToAuthor() {
  const authorAddress = '0x...';  // The address to subscribe to
  const amountPerMonth = 10;      // Amount in tokens per month
  
  const tx = await papaya.subscribe(authorAddress, amountPerMonth);
  await tx.wait();
  console.log('Successfully subscribed!');
}

// Example getting user info with rate conversion
async function getUserInfo() {
  const userInfo = await papaya.getUserInfo();
  
  // Convert raw blockchain data to human-readable format
  const formattedInfo = {
    balance: formatOutput(BigInt(userInfo.balance), 18),
    // Convert per-second rates to monthly rates
    incomeRate: convertRateToPeriod(Number(formatOutput(userInfo.incomeRate, 18)), RatePeriod.MONTH),
    outgoingRate: convertRateToPeriod(Number(formatOutput(userInfo.outgoingRate, 18)), RatePeriod.MONTH),
    updated: new Date(Number(userInfo.updated) * 1000).toISOString()
  };
  
  console.log(`Balance: ${formattedInfo.balance} USDT`);
  console.log(`Monthly income: ${formattedInfo.incomeRate} USDT`);
  console.log(`Monthly outgoing: ${formattedInfo.outgoingRate} USDT`);
}
```

### Support

For questions, issues or feature requests, please open an issue on our GitHub repository or contact us at [Papaya Community](https://t.me/PapayaCommunity/26037).

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-rocket-launch">:rocket-launch:</i></h4></td><td><strong>Getting Started</strong></td><td></td><td></td><td><a href="/pages/6jt1fvoCH7mthgSxaIRW">/pages/6jt1fvoCH7mthgSxaIRW</a></td></tr><tr><td><h4><i class="fa-webhook">:webhook:</i></h4></td><td><strong>API Reference</strong></td><td></td><td></td><td><a href="/pages/Ev3ckhr66RSjzPYv8Iud">/pages/Ev3ckhr66RSjzPYv8Iud</a></td></tr><tr><td><h4><i class="fa-lightbulb">:lightbulb:</i></h4></td><td><strong>Examples</strong></td><td></td><td></td><td><a href="/pages/qvUfgc2SPgqMpGgPfNi7">/pages/qvUfgc2SPgqMpGgPfNi7</a></td></tr><tr><td><h4><i class="fa-chart-network">:chart-network:</i></h4></td><td><strong>Network Support</strong></td><td></td><td></td><td><a href="/pages/A9zc7RNI13ywoE3UHiEw">/pages/A9zc7RNI13ywoE3UHiEw</a></td></tr><tr><td><h4><i class="fa-shapes">:shapes:</i></h4></td><td><strong>Utilities</strong></td><td></td><td></td><td><a href="/pages/evdOm0urplyH6auuH9UL">/pages/evdOm0urplyH6auuH9UL</a></td></tr></tbody></table>


# Meta API

Browser-compatible meta-transaction client for Papaya Gasless UX for subscriptions, payments, deposits & withdrawals – powered by EIP-712 and a relayer network.

> ✅ No axios – uses native fetch()
>
> ✅ Full EIP-712 signing (ethers-compatible)
>
> ✅ Non-custodial: user signs off-chain, relayer broadcasts on-chain
>
> ✅ Works in modern browsers and bundlers (Webpack, Vite, etc.)

***

### 📦 Installation & Initialization

```bash
npm install @papaya_fi/api ethers
```

```typescript
import { PapayaMetaApiClient } from '@papaya_fi/api/browser';

const metaClient = new PapayaMetaApiClient(
  'https://api.papaya.finance', // production endpoint
  'bsc-usdt',                   // 'bsc-usdc' | 'bsc-usdt' | 'pol-usdc'| 'pol-usdt'
  'your-api-key'                // issued after KYB and onboarding
);
```

#### Constructor Parameters

<table><thead><tr><th width="94">Param</th><th width="97">Required</th><th>Description</th></tr></thead><tbody><tr><td>baseUrl</td><td>✅</td><td>API base URL, e.g. 'https://api.papaya.finance'</td></tr><tr><td>contract</td><td>❌</td><td>Contract identifier: 'bsc-usdc' | 'bsc-usdt' | 'pol-usdc'| 'pol-usdt'</td></tr><tr><td>apiKey</td><td>✅</td><td>API key (issued by Papaya during onboarding)</td></tr><tr><td>provider</td><td>❌</td><td>Optional ethers.Provider, can be set later via .setProvider()</td></tr></tbody></table>

> 🔐 All requests automatically include the header: x-api-key: \<your-api-key>

***

### 🔑 Meta-Transaction Concept in Papaya

Meta-transactions allow users to perform operations without paying gas themselves:

1. Client → API: call prepare\*MetaTransaction(...) → get structured transactionData
2. Client → Wallet: call signMetaTransaction(...) → sign EIP-712 message with traits
3. Client → API: call executeMetaTransaction(...) → relayer submits the tx on-chain

Result: the user does not pay gas; they only pay according to your business logic (subscriptions, payments, etc.). Gas is paid by the relayer, which you or Papaya control.

***

### 📚 Method Overview

#### 1. Prepare Meta-Transactions (off-chain)

These methods prepare data for EIP-712 signing and later execution.

| Method                                                   | Params                                        | Returns                |                                                          |
| -------------------------------------------------------- | --------------------------------------------- | ---------------------- | -------------------------------------------------------- |
| prepareSubscriptionMetaTransaction(params)               | { from, author, subscriptionRate, projectId } | { from, method, data } | Prepares subscribe(...) call                             |
| prepareCancelSubscriptionMetaTransaction(author, params) | author, { from }                              | { from, method, data } | Prepares unsubscribe(author) call                        |
| preparePaymentMetaTransaction(params)                    | { from, to, amount }                          | from, method, data }   | Prepares pay(to, amount) call                            |
| prepareDepositMetaTransaction(params)                    | { from, amount, isPermit2? }                  | { from, method, data } | Prepares deposit(amount, isPermit2) call                 |
| prepareWithdrawMetaTransaction(params)                   | { from, amount, to? }                         | { from, method, data } | Prepares withdraw(amount) or withdrawTo(to, amount) call |

{% hint style="info" %}
📌 `amount` and `subscriptionRate` in token smallest units (wei-like). For USDT/USDC (18 decimals), multiply USD amount by 1e18: 50 USDT → '50000000000000000000'.\
\
📌 `amount` in `prepareDepositMetaTransaction()` in token should be same as for native token. \
example: **USDT** on **Polygon** (6 decimals), multiply USD amount by 1e6: 50 USDT → '50000000'.
{% endhint %}

***

#### 2. Signing (client-side, EIP-712)

**signMetaTransaction(transactionData, signer, contractAddress, chainId, relayerAddress)**

Signs prepared meta-transaction using EIP-712.

| Param           | Type          | Description                                            |
| --------------- | ------------- | ------------------------------------------------------ |
| transactionData | object        | Result of any prepare\*MetaTransaction() call          |
| signer          | ethers.Signer | Ethers signer, e.g. provider.getSigner() from MetaMask |
| contractAddress | string        | Papaya contract address (0x...)                        |
| chainId         | number        | Chain ID (e.g. 137 for Polygon)                        |
| relayerAddress  | string        | Address of the relayer (operated by you or Papaya)     |

Returns:

```typescript
{
  from: string;
  data: string;      // JSON string, e.g. { "data": "0x..." }
  nonce: number;
  signature: string; // EIP-712 signature
  deadline: number;  // unix timestamp
}
```

***

#### 3. Execute Meta-Transaction (via relayer)

**executeMetaTransaction(signedTransaction)**

Sends signed meta-transaction payload to the relayer API. The relayer then broadcasts the corresponding on-chain transaction.

Returns (typical):

* txHash: transaction hash after broadcast
* status: 'pending' | 'confirmed' | 'failed'
* On error – HTTP 4xx/5xx with error description

> ⚠️ The relayer must be authorized in the Papaya contract.
>
> Contact Papaya Team if you want to use the shared relayer or run your own.

***

### 🚀 Convenience “All-in-One” Methods

These helpers encapsulate: prepare → sign → execute.

<table><thead><tr><th>Method</th><th></th><th data-hidden></th></tr></thead><tbody><tr><td>createSubscriptionViaMetaTransaction(params, signer, contractAddress, chainId, relayerAddress)</td><td>Gasless subscription creation</td><td></td></tr><tr><td>cancelSubscriptionViaMetaTransaction(author, params, signer, contractAddress, chainId, relayerAddress)</td><td>Gasless subscription cancellation</td><td></td></tr><tr><td>makePaymentViaMetaTransaction(...)</td><td>Gasless one-off payment</td><td></td></tr><tr><td>depositFundsViaMetaTransaction(...)</td><td>Gasless deposit</td><td></td></tr><tr><td>withdrawFundsViaMetaTransaction(...)</td><td>Gasless withdraw</td><td></td></tr></tbody></table>

#### Example: Gasless Subscription

```typescript
import { ethers } from 'ethers';
import { PapayaMetaApiClient } from '@papaya_fi/api/browser';

const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const [account] = await provider.listAccounts();

const metaClient = new PapayaMetaApiClient(
  'https://api.papaya.finance',
  'pol-usdt',
  'your-api-key'
);

const result = await metaClient.createSubscriptionViaMetaTransaction(
  {
    from: account,
    author: '0x742d35Cc6634C0532925a3b8D4C9f2a93A9E3F1A',
    subscriptionRate: '50000000000000000000', // 50 USDT (18 decimals)
    projectId: 0
  },
  signer,
  '0x8F7d7e3F56c3A71E4f3B58A0E77a0A873c52bF05', // Papaya USDT contract
  137,                                          // Polygon
  '0xD9147F139980cFcB552Fcae2f3c9b7B215ACE850'  // Relayer address
);

console.log('Meta tx sent:', result.txHash);
```

***

### 🔁 Additional Methods

| Method                | Description                                                         |
| --------------------- | ------------------------------------------------------------------- |
| getNonce(address)     | Returns the current nonce for given address (for replay protection) |
| setProvider(provider) | Sets/updates ethers.Provider (useful to fetch chainId etc.)         |

***

### ⚠️ Important Notes

1. Relayer security

   Only trusted relayers should be allowed to execute meta-transactions.

   Papaya can provide a shared relayer or help you deploy your own.
2. Signature deadline

   Each meta-transaction has a deadline (unix timestamp). After that time, the contract rejects the signature.
3. Replay protection

   Contracts validate (nonceType, nonce) pairs – each combination is unique and can be used only once.
4. EIP-712 support

   Wallet must support eth\_signTypedData\_v4 (MetaMask, WalletConnect, Coinbase Wallet, etc.).

***

### 🛡️ Security & Compatibility

* ✅ Supports ethers.js v5 and v6
* ✅ Uses native fetch() only (no axios)
* ✅ Compatible with modern bundlers (Webpack, Vite, etc.)
* ✅ Relies on BigInt – runtime will check for availability
* ✅ Default HTTP timeout: 10 seconds for API calls

***

> 💡 Papaya protocol is non-custodial, audited, and stablecoin-native infrastructure for  payments and subscriptions &#x20;


# API Reference

## Get list of available contracts

> Returns a list of all available smart contracts

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"paths":{"/api/v1/contracts/":{"get":{"tags":["contracts","Api"],"summary":"Get list of available contracts","description":"Returns a list of all available smart contracts","operationId":"PapayaApiEndpointsGetContracts","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PapayaApiModelsContractInfo"}}}}}}}}},"components":{"schemas":{"PapayaApiModelsContractInfo":{"type":"object","description":"Information about a smart contract","additionalProperties":false,"properties":{"name":{"type":"string","description":"Name of the smart contract","nullable":true},"address":{"type":"string","description":"Ethereum address of the smart contract","nullable":true}}}}}}
```

## API information and status

> Returns information about the API and the status of smart contracts

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"paths":{"/api/v1/contracts/api-info":{"get":{"tags":["contracts","Api"],"summary":"API information and status","description":"Returns information about the API and the status of smart contracts","operationId":"PapayaApiEndpointsContractsGetApiInfo","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsApiInfoResponse"}}}}}}}},"components":{"schemas":{"PapayaApiModelsApiInfoResponse":{"type":"object","description":"Response model for API information endpoint","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the API information","nullable":true},"contracts":{"type":"object","description":"Dictionary of available contracts and their information","nullable":true,"additionalProperties":{}}}}}}}
```

## Cancel subscription for a specific contract

> Cancel an existing subscription to an author

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/subscriptions/{author}":{"delete":{"tags":["subscriptions","Api"],"summary":"Cancel subscription for a specific contract","description":"Cancel an existing subscription to an author","operationId":"CancelSubscription","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}},{"name":"author","in":"path","required":true,"schema":{"type":"string"}},{"name":"From","in":"query","schema":{"type":"string","nullable":true}},{"name":"Sponsor","in":"query","schema":{"type":"string","nullable":true}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Create new payment stream/subscription for a specific contract

> Create a new subscription/payment stream to an author

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsCreateSubscriptionRequest":{"type":"object","additionalProperties":false,"properties":{"from":{"type":"string"},"author":{"type":"string"},"subscriptionRate":{"type":"string"},"projectId":{"type":"string"},"sponsor":{"type":"string","nullable":true}}},"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/subscriptions/":{"post":{"tags":["subscriptions","Api"],"summary":"Create new payment stream/subscription for a specific contract","description":"Create a new subscription/payment stream to an author","operationId":"CreateSubscription","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsCreateSubscriptionRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## List user's active subscriptions for a specific contract

> Retrieve a list of all active subscriptions for a user

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiEndpointsSubscriptionsListSubscriptionsResponse":{"type":"object","additionalProperties":false,"properties":{"address":{"type":"string"},"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/PapayaApiEndpointsSubscriptionsSubscriptionInfo"}}}},"PapayaApiEndpointsSubscriptionsSubscriptionInfo":{"type":"object","additionalProperties":false,"properties":{"to":{"type":"string"},"incomeRate":{"type":"number","format":"decimal"},"outgoingRate":{"type":"number","format":"decimal"},"incomeRateRaw":{"$ref":"#/components/schemas/SystemNumericsBigInteger"},"outgoingRateRaw":{"$ref":"#/components/schemas/SystemNumericsBigInteger"}}},"SystemNumericsBigInteger":{"type":"object","additionalProperties":false,"properties":{"IsPowerOfTwo":{"type":"boolean"},"IsZero":{"type":"boolean"},"IsOne":{"type":"boolean"},"IsEven":{"type":"boolean"},"Sign":{"type":"integer","format":"int32"},"_sign":{"type":"integer","format":"int32"},"_bits":{"type":"array","nullable":true,"items":{"type":"integer"}}}}}},"paths":{"/api/v1/{contract}/subscriptions/{address}":{"get":{"tags":["subscriptions","Api"],"summary":"List user's active subscriptions for a specific contract","description":"Retrieve a list of all active subscriptions for a user","operationId":"ListSubscriptions","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}},{"name":"address","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiEndpointsSubscriptionsListSubscriptionsResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Deposit funds to user account

> Deposit funds to a user's account

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsDepositFundsRequest":{"type":"object","description":"Request model for depositing funds into the system","additionalProperties":false,"required":["from","amount"],"properties":{"from":{"type":"string","description":"Ethereum address to deposit funds from","minLength":1},"amount":{"type":"string","description":"Amount to deposit","minLength":1},"isPermit2":{"type":"boolean","description":"Whether to use Permit2 for token approval","nullable":true},"sponsor":{"type":"string","description":"Optional sponsor address that pays for gas fees","nullable":true}}},"PapayaApiModelsDepositFundsResponse":{"allOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"},{"type":"object","description":"Response model for depositing funds into the system","additionalProperties":false,"properties":{"from":{"type":"string","description":"Ethereum address that funds were deposited from"},"amount":{"type":"string","description":"Amount that was deposited"},"isPermit2":{"type":"boolean","description":"Whether Permit2 was used for token approval","nullable":true},"sponsor":{"type":"string","description":"Address that paid for gas fees","nullable":true}}}]},"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/payments/deposit":{"post":{"tags":["payments","Api"],"summary":"Deposit funds to user account","description":"Deposit funds to a user's account","operationId":"DepositFunds","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsDepositFundsRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsDepositFundsResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Get user account balance

> Retrieve the balance of a user's account

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiEndpointsPaymentsBalanceResponse":{"type":"object","additionalProperties":false,"properties":{"address":{"type":"string","nullable":true},"balance":{"type":"string","nullable":true}}}}},"paths":{"/api/v1/{contract}/payments/balance/{address}":{"get":{"tags":["payments","Api"],"summary":"Get user account balance","description":"Retrieve the balance of a user's account","operationId":"GetBalance","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}},{"name":"address","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiEndpointsPaymentsBalanceResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Make a one-time payment

> Create a transaction to make a one-time payment to another user. This endpoint supports sponsored transactions where gas fees can be paid by the recipient. The sponsor must be either the sender or receiver of the payment. All addresses must be valid Ethereum addresses (42 characters starting with 0x).

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsMakePaymentRequest":{"type":"object","description":"Request model for making a payment transaction","additionalProperties":false,"required":["from","to","amount"],"properties":{"from":{"type":"string","description":"Ethereum address of the sender","minLength":1},"to":{"type":"string","description":"Ethereum address of the recipient","minLength":1},"amount":{"type":"string","description":"Amount to be transferred (in wei for ETH)","minLength":1},"sponsor":{"type":"string","description":"Optional sponsor address that pays for gas fees. Must be either the sender or receiver.","nullable":true}}},"PapayaApiModelsMakePaymentResponse":{"allOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"},{"type":"object","description":"Response model for making a payment transaction","additionalProperties":false,"properties":{"from":{"type":"string","description":"Ethereum address of the sender"},"to":{"type":"string","description":"Ethereum address of the recipient"},"amount":{"type":"string","description":"Amount to be transferred"},"sponsor":{"type":"string","description":"Address that pays for gas fees","nullable":true}}}]},"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/payments/":{"post":{"tags":["payments","Api"],"summary":"Make a one-time payment","description":"Create a transaction to make a one-time payment to another user. This endpoint supports sponsored transactions where gas fees can be paid by the recipient. The sponsor must be either the sender or receiver of the payment. All addresses must be valid Ethereum addresses (42 characters starting with 0x).","operationId":"MakePayment","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsMakePaymentRequest"}}},"required":true},"responses":{"200":{"description":"Successfully prepared payment transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsMakePaymentResponse"}}}},"400":{"description":"Bad Request - Invalid parameters or addresses"},"401":{"description":"Unauthorized - Invalid API key"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Withdraw funds from user account

> Withdraw funds from a user's account

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsWithdrawFundsRequest":{"type":"object","description":"Request model for withdrawing funds from the system","additionalProperties":false,"required":["from","amount","to"],"properties":{"from":{"type":"string","description":"Ethereum address to withdraw funds from","minLength":1},"amount":{"type":"string","description":"Amount to withdraw","minLength":1},"to":{"type":"string","description":"Ethereum address to withdraw funds to","minLength":1},"sponsor":{"type":"string","description":"Optional sponsor address that pays for gas fees","nullable":true}}},"PapayaApiModelsWithdrawFundsResponse":{"allOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"},{"type":"object","description":"Response model for withdrawing funds from the system","additionalProperties":false,"properties":{"from":{"type":"string","description":"Ethereum address that funds were withdrawn from"},"amount":{"type":"string","description":"Amount that was withdrawn"},"to":{"type":"string","description":"Ethereum address that funds were withdrawn to"},"sponsor":{"type":"string","description":"Address that paid for gas fees","nullable":true}}}]},"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/payments/withdraw":{"post":{"tags":["payments","Api"],"summary":"Withdraw funds from user account","description":"Withdraw funds from a user's account","operationId":"WithdrawFunds","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsWithdrawFundsRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsWithdrawFundsResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Execute a meta transaction using a signature from the user

> Execute a meta transaction that has been signed by the user

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiEndpointsMetaExecuteMetaTransactionRequest":{"type":"object","additionalProperties":false,"required":["from","signature","data","nonce"],"properties":{"from":{"type":"string","minLength":1},"signature":{"type":"string","minLength":1},"data":{"type":"string","minLength":1},"nonce":{"type":"integer","format":"int32"},"sponsor":{"type":"string","nullable":true},"deadline":{"type":"integer","format":"int64","nullable":true}}},"PapayaApiEndpointsMetaExecuteMetaTransactionResponse":{"type":"object","additionalProperties":false,"properties":{"message":{"type":"string"},"from":{"type":"string"},"transactionHash":{"type":"string"},"gasUsed":{"type":"integer","format":"int64"}}}}},"paths":{"/api/v1/{contract}/meta/execute":{"post":{"tags":["meta","Api"],"summary":"Execute a meta transaction using a signature from the user","description":"Execute a meta transaction that has been signed by the user","operationId":"ExecuteMetaTransaction","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiEndpointsMetaExecuteMetaTransactionRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiEndpointsMetaExecuteMetaTransactionResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Get the current nonce for an account

> Retrieve the current nonce value for a specific account

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiEndpointsMetaGetNonceResponse":{"type":"object","additionalProperties":false,"properties":{"account":{"type":"string"},"nonce":{"type":"integer","format":"int32"}}}}},"paths":{"/api/v1/{contract}/meta/nonce/{account}":{"get":{"tags":["meta","Api"],"summary":"Get the current nonce for an account","description":"Retrieve the current nonce value for a specific account","operationId":"GetNonce","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}},{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiEndpointsMetaGetNonceResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Create multiple subscriptions in one sponsored call

> Create multiple subscriptions in a single transaction

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsBatchSubscriptionRequest":{"type":"object","additionalProperties":false,"required":["from","subscriptions"],"properties":{"from":{"type":"string","minLength":1},"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/PapayaApiModelsSubscription"}},"sponsor":{"type":"string","nullable":true}}},"PapayaApiModelsSubscription":{"type":"object","additionalProperties":false,"required":["author","subscriptionRate","projectId"],"properties":{"author":{"type":"string","minLength":1},"subscriptionRate":{"type":"string","minLength":1},"projectId":{"type":"string","minLength":1}}},"PapayaApiModelsBatchSubscriptionResponse":{"allOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"},{"type":"object","additionalProperties":false,"properties":{"from":{"type":"string","nullable":true},"count":{"type":"integer","format":"int32"},"sponsor":{"type":"string","nullable":true}}}]},"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/batch/subscriptions":{"post":{"tags":["batch","Api"],"summary":"Create multiple subscriptions in one sponsored call","description":"Create multiple subscriptions in a single transaction","operationId":"CreateBatchSubscriptions","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsBatchSubscriptionRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsBatchSubscriptionResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```

## Process batch payments to multiple recipients

> Process multiple payments to different recipients in a single transaction

```json
{"openapi":"3.0.0","info":{"title":"Papaya API","version":"v1"},"servers":[{"url":"https://api.papaya.finance"}],"security":[{"ApiKey":[]}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","description":"API Key Authentication. Example: \"X-API-Key: {api_key}\"","name":"X-API-Key","in":"header","scheme":"ApiKey"}},"schemas":{"PapayaApiModelsBatchPaymentRequest":{"type":"object","additionalProperties":false,"required":["from","payments"],"properties":{"from":{"type":"string","minLength":1},"payments":{"type":"array","items":{"$ref":"#/components/schemas/PapayaApiModelsPayment"}},"sponsor":{"type":"string","nullable":true}}},"PapayaApiModelsPayment":{"type":"object","additionalProperties":false,"required":["to","amount"],"properties":{"to":{"type":"string","minLength":1},"amount":{"type":"string","minLength":1}}},"PapayaApiModelsBatchPaymentResponse":{"allOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionResponse"},{"type":"object","additionalProperties":false,"properties":{"from":{"type":"string","nullable":true},"count":{"type":"integer","format":"int32"},"sponsor":{"type":"string","nullable":true}}}]},"PapayaApiModelsTransactionResponse":{"type":"object","description":"Base response model for transaction operations","additionalProperties":false,"properties":{"message":{"type":"string","description":"Message describing the result of the transaction","nullable":true},"transaction":{"description":"Transaction details including gas estimates and other parameters","nullable":true,"oneOf":[{"$ref":"#/components/schemas/PapayaApiModelsTransactionModel"}]}}},"PapayaApiModelsTransactionModel":{"type":"object","description":"Represents a blockchain transaction with its key parameters","additionalProperties":{},"properties":{"from":{"type":"string","description":"The sender address initiating the transaction","nullable":true},"to":{"type":"string","description":"The destination address (typically a smart contract address)","nullable":true},"data":{"type":"string","description":"The encoded function call data","nullable":true},"gas":{"type":"integer","description":"The estimated gas amount for the transaction","format":"int64","nullable":true},"value":{"type":"string","description":"The amount of tokens/ETH to send with the transaction (optional)","nullable":true}}}}},"paths":{"/api/v1/{contract}/batch/payments":{"post":{"tags":["batch","Api"],"summary":"Process batch payments to multiple recipients","description":"Process multiple payments to different recipients in a single transaction","operationId":"ProcessBatchPayments","parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsBatchPaymentRequest"}}},"required":true},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapayaApiModelsBatchPaymentResponse"}}}},"400":{"description":"Bad Request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"500":{"description":"Internal Server Error"}}}}}}
```


# Overview

## What is Papaya Invoice Bot?

Papaya Bot is a Telegram-based cryptocurrency payment processing system that enables businesses and developers to accept recurring cryptocurrency payments through a simple API. The bot provides a seamless interface for users to pay for subscriptions and services using popular stablecoins like USDC and USDT on major blockchain networks such as Ethereum, Base, Polygon, BSC, etc.

## Key Features

* **API-based Invoice Creation**: Developers can create payment invoices programmatically through a REST API
* **Telegram Integration**: Users interact with the payment system directly through Telegram
* **Multi-chain Support**: Supports multiple blockchain networks including Polygon and Binance Smart Chain
* **Cryptocurrency Support**: Accepts popular stablecoins like USDC and USDT
* **Subscription Management**: Handles recurring payment subscriptions with automatic cancellation
* **Webhook Integration**: Sends real-time notifications when payments are processed
* **Security**: Implements HMAC signature verification for webhook security

## How It Works

### For Developers

{% stepper %}
{% step %}

#### Register your application

Register your application through the Telegram bot interface to get an API key.
{% endstep %}

{% step %}

#### Create invoices

Use the API to create payment invoices with specific amounts and billing periods.
{% endstep %}

{% step %}

#### Provide payment links

Share payment links with your users who can pay directly through Telegram.
{% endstep %}

{% step %}

#### Receive webhooks

Get notified when payments are processed through webhook callbacks.
{% endstep %}

{% step %}

#### Manage subscriptions

Track active subscriptions and handle cancellations via the API.
{% endstep %}
{% endstepper %}

### For Users

{% stepper %}
{% step %}

#### Receive a payment link

Get a payment link from a service provider.
{% endstep %}

{% step %}

#### Open in Telegram

Click the link to open the Papaya Bot.
{% endstep %}

{% step %}

#### Select payment method

Choose from supported cryptocurrencies and blockchain networks.
{% endstep %}

{% step %}

#### Connect wallet

Connect your cryptocurrency wallet via WalletConnect.
{% endstep %}

{% step %}

#### Complete payment

Approve the payment and complete the subscription setup.
{% endstep %}
{% endstepper %}

## Main Usage Flows

### Flow: Application Registration

{% stepper %}
{% step %}

#### Open the Telegram bot

User opens the Telegram bot.
{% endstep %}

{% step %}

#### Navigate to registration

Navigate to "Integrations" → "Register Application".
{% endstep %}

{% step %}

#### Provide application name

Provide the application name.
{% endstep %}

{% step %}

#### Set wallet address

Set the wallet address that will receive payments.
{% endstep %}

{% step %}

#### Configure webhook (optional)

Optionally configure a webhook URL for notifications.
{% endstep %}

{% step %}

#### Receive API key

Receive the API key for programmatic access.
{% endstep %}
{% endstepper %}

### Flow: Invoice Creation and Payment

{% stepper %}
{% step %}

#### Create invoice via API

Developer creates an invoice via the API with amount and period.
{% endstep %}

{% step %}

#### Receive invoice details

API returns an invoice ID and payment link.
{% endstep %}

{% step %}

#### Share payment link

Developer shares the payment link with the customer.
{% endstep %}

{% step %}

#### Open bot with pre-filled invoice

Customer clicks the link and opens the bot with a pre-filled invoice.
{% endstep %}

{% step %}

#### Select network and token

Customer selects blockchain network and token.
{% endstep %}

{% step %}

#### Connect wallet

Customer connects wallet via WalletConnect QR code.
{% endstep %}

{% step %}

#### Approve payment

Customer approves payment and completes subscription.
{% endstep %}

{% step %}

#### Webhook on blockchain event

A blockchain event triggers a webhook notification to the developer.
{% endstep %}
{% endstepper %}

### Flow: Subscription Management

{% stepper %}
{% step %}

#### Track subscriptions

Developer can track active subscriptions via the API.
{% endstep %}

{% step %}

#### Users view subscriptions

Users can view their subscriptions in the bot using "My Subscriptions".
{% endstep %}

{% step %}

#### Cancellation

Subscriptions can be canceled by the service provider or automatically when payments stop.
{% endstep %}

{% step %}

#### Webhook updates

Webhook notifications inform about subscription status changes.
{% endstep %}
{% endstepper %}

### Flow: Webhook Notifications

{% stepper %}
{% step %}

#### Identify application

When a payment is processed, the system identifies the associated application.
{% endstep %}

{% step %}

#### Send POST request

If a webhook URL is configured, the system sends a POST request with payment details.
{% endstep %}

{% step %}

#### Include HMAC signature

The request includes an HMAC signature for security verification.
{% endstep %}

{% step %}

#### Developer processes notification

Developer's system processes the notification and updates their records.
{% endstep %}

{% step %}

#### Handle cancellations

In case of subscription cancellation, appropriate actions are triggered.
{% endstep %}
{% endstepper %}

## Supported Cryptocurrencies and Networks

### Networks

* **Polygon**: Major blockchain network with low transaction fees
* **Binance Smart Chain**: High-performance blockchain with fast transactions

### Tokens

* **USDC**: USD Coin stablecoin
* **USDT**: Tether stablecoin

## Billing Periods

Invoices can be created with different billing periods:

* Daily
* Weekly
* Monthly (default)
* Yearly

## Security Features

* **API Key Authentication**: All API calls require a valid API key
* **HMAC Signatures**: Webhook requests include verifiable signatures
* **Wallet Verification**: Payment recipients are verified against registered applications
* **Transaction Tracking**: All payments are tracked with blockchain transaction hashes

## Integration Benefits

### For Developers

* Easy cryptocurrency payment integration without managing blockchain complexity
* Reliable payment processing with webhook notifications
* Flexible billing periods to match business models
* No need to manage cryptocurrency wallets directly

### For Users

* Familiar Telegram interface for payments
* Support for popular cryptocurrencies
* Secure wallet connection via WalletConnect
* Automatic subscription management

## Getting Started

{% stepper %}
{% step %}

#### Register

Start a chat with the Papaya Bot and register your application.
{% endstep %}

{% step %}

#### Configure

Set up your receiving wallet address and optional webhook URL.
{% endstep %}

{% step %}

#### Integrate

Use the API to create invoices in your application.
{% endstep %}

{% step %}

#### Test

Use test mode to verify your integration works correctly.
{% endstep %}

{% step %}

#### Go live

Switch to production mode when ready for real transactions.
{% endstep %}
{% endstepper %}

## Use Cases

* SaaS subscription payments in cryptocurrency
* Digital service payments
* Content access subscriptions
* Membership payments
* Any recurring payment scenario where cryptocurrency is preferred

This system provides a complete solution for businesses wanting to accept cryptocurrency payments without the complexity of direct blockchain integration.


# API Reference

## Create invoice

> Creates a new invoice with the specified rate and period for the authenticated application.

```json
{"openapi":"3.0.1","info":{"title":"Papaya Bot API","version":"v1"},"servers":[{"url":"https://bot-api.papaya.finance"}],"security":[{"X-API-Key":[]}],"components":{"securitySchemes":{"X-API-Key":{"type":"apiKey","description":"API Key needed to access the endpoints","name":"X-API-Key","in":"header"}},"schemas":{"InvoiceRequest":{"type":"object","properties":{"title":{"type":"string","nullable":true},"message":{"type":"string","nullable":true},"rate":{"type":"number","format":"double"},"period":{"$ref":"#/components/schemas/RatePeriod"},"metadata":{"type":"object","nullable":true}},"additionalProperties":false},"RatePeriod":{"enum":["PerDay","PerWeek","PerMonth","PerYear"],"type":"string"},"CreateInvoiceResponse":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"rate":{"type":"number","format":"double"},"createdAt":{"type":"string","format":"date-time"},"botUrl":{"type":"string","nullable":true}},"additionalProperties":false}}},"paths":{"/api/invoice":{"post":{"tags":["Invoice"],"summary":"Create invoice","description":"Creates a new invoice with the specified rate and period for the authenticated application.","operationId":"CreateInvoice","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRequest"}},"text/json":{"schema":{"$ref":"#/components/schemas/InvoiceRequest"}},"application/*+json":{"schema":{"$ref":"#/components/schemas/InvoiceRequest"}}}},"responses":{"200":{"description":"Invoice created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvoiceResponse"}}}},"400":{"description":"Bad request - Invalid parameters"},"401":{"description":"Unauthorized - Invalid or missing API key"},"500":{"description":"Internal server error"}}}}}}
```

## Get invoice by id

> Retrieves a specific invoice by its ID, ensuring it belongs to the authenticated application.

```json
{"openapi":"3.0.1","info":{"title":"Papaya Bot API","version":"v1"},"servers":[{"url":"https://bot-api.papaya.finance"}],"security":[{"X-API-Key":[]}],"components":{"securitySchemes":{"X-API-Key":{"type":"apiKey","description":"API Key needed to access the endpoints","name":"X-API-Key","in":"header"}},"schemas":{"InvoiceDetails":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"recipient":{"type":"string","nullable":true},"rate":{"type":"number","format":"double"},"createdAt":{"type":"string","format":"date-time"},"isPaid":{"type":"boolean"},"metadata":{"type":"object","nullable":true},"botUrl":{"type":"string","nullable":true}},"additionalProperties":false},"ProblemDetails":{"type":"object","properties":{"type":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"status":{"type":"integer","format":"int32","nullable":true},"detail":{"type":"string","nullable":true},"instance":{"type":"string","nullable":true}},"additionalProperties":{}}}},"paths":{"/api/invoice/{id}":{"get":{"tags":["Invoice"],"summary":"Get invoice by id","description":"Retrieves a specific invoice by its ID, ensuring it belongs to the authenticated application.","operationId":"GetInvoiceById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Invoice created successfully","content":{"text/plain":{"schema":{"$ref":"#/components/schemas/InvoiceDetails"}},"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceDetails"}},"text/json":{"schema":{"$ref":"#/components/schemas/InvoiceDetails"}}}},"400":{"description":"Bad request - Invalid parameters","content":{"text/plain":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}},"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}},"text/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"404":{"description":"Not Found","content":{"text/plain":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}},"application/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}},"text/json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error"}}}}}}
```

## Get all subscriptions

> Retrieves all subscriptions for the authenticated application with pagination support.

```json
{"openapi":"3.0.1","info":{"title":"Papaya Bot API","version":"v1"},"servers":[{"url":"https://bot-api.papaya.finance"}],"security":[{"X-API-Key":[]}],"components":{"securitySchemes":{"X-API-Key":{"type":"apiKey","description":"API Key needed to access the endpoints","name":"X-API-Key","in":"header"}},"schemas":{"SubscriptionApiModelPaginatedResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionApiModel"},"description":"The items in the current page","nullable":true},"totalCount":{"type":"integer","description":"Total number of items across all pages","format":"int32"},"page":{"type":"integer","description":"Current page number (1-indexed)","format":"int32"},"limit":{"type":"integer","description":"Number of items per page","format":"int32"},"totalPages":{"type":"integer","description":"Total number of pages","format":"int32"}},"additionalProperties":false,"description":"A paginated response containing items and pagination metadata"},"SubscriptionApiModel":{"type":"object","properties":{"id":{"type":"integer","description":"Unique identifier for the subscription","format":"int64"},"chainId":{"type":"integer","description":"Blockchain chain ID where the subscription is registered","format":"int32"},"tokenName":{"type":"string","description":"Name of the token used for the subscription payments","nullable":true},"tx":{"type":"string","description":"Transaction hash of the subscription payment","nullable":true},"tgUserId":{"type":"integer","description":"Telegram user ID associated with the subscription","format":"int64"},"userAddress":{"type":"string","description":"User's wallet address associated with the subscription","nullable":true},"createdAt":{"type":"string","description":"Creation timestamp of the subscription","format":"date-time"},"isActive":{"type":"boolean","description":"Indicates whether the subscription is currently active","format":"boolean"}},"additionalProperties":false,"description":"Subscription data model for API responses"}}},"paths":{"/api/subscriptions":{"get":{"tags":["Subscription"],"summary":"Get all subscriptions","description":"Retrieves all subscriptions for the authenticated application with pagination support.","operationId":"GetSubscriptions","parameters":[{"name":"page","in":"query","schema":{"type":"integer","format":"int32","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","format":"int32","default":50}}],"responses":{"200":{"description":"Returns paginated list of subscriptions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionApiModelPaginatedResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"500":{"description":"Internal server error"}}}}}}
```

## Get subscription by ID

> Retrieves a specific subscription by its ID, ensuring it belongs to the authenticated application.

```json
{"openapi":"3.0.1","info":{"title":"Papaya Bot API","version":"v1"},"servers":[{"url":"https://bot-api.papaya.finance"}],"security":[{"X-API-Key":[]}],"components":{"securitySchemes":{"X-API-Key":{"type":"apiKey","description":"API Key needed to access the endpoints","name":"X-API-Key","in":"header"}},"schemas":{"SubscriptionApiModel":{"type":"object","properties":{"id":{"type":"integer","description":"Unique identifier for the subscription","format":"int64"},"chainId":{"type":"integer","description":"Blockchain chain ID where the subscription is registered","format":"int32"},"tokenName":{"type":"string","description":"Name of the token used for the subscription payments","nullable":true},"tx":{"type":"string","description":"Transaction hash of the subscription payment","nullable":true},"tgUserId":{"type":"integer","description":"Telegram user ID associated with the subscription","format":"int64"},"userAddress":{"type":"string","description":"User's wallet address associated with the subscription","nullable":true},"createdAt":{"type":"string","description":"Creation timestamp of the subscription","format":"date-time"},"isActive":{"type":"boolean","description":"Indicates whether the subscription is currently active","format":"boolean"}},"additionalProperties":false,"description":"Subscription data model for API responses"}}},"paths":{"/api/subscriptions/{id}":{"get":{"tags":["Subscription"],"summary":"Get subscription by ID","description":"Retrieves a specific subscription by its ID, ensuring it belongs to the authenticated application.","operationId":"GetSubscriptionById","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Returns the requested subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionApiModel"}}}},"401":{"description":"Unauthorized - Invalid or missing API key"},"404":{"description":"Subscription not found"},"500":{"description":"Internal server error"}}}}}}
```


# Webhook

## Overview

Webhooks are automated messages sent from our system to your application when specific events occur in your registered application. These events include successful payment processing, subscription creation, and subscription revocation.

Additionally, you can now retrieve subscription information using our new API endpoints (see API Reference documentation for details). When blockchain events occur for applications without a configured webhook URL, the system will still process the events (such as deactivating subscriptions when streams are revoked) but will not send webhook notifications.

## How Webhooks Work

{% stepper %}
{% step %}

### Register your app and configure webhook

You register your application with our system and configure a webhook URL.
{% endstep %}

{% step %}

### Events trigger HTTP POST

When specific blockchain events occur (like payments or subscription changes), our system sends an HTTP POST request to your configured webhook URL.
{% endstep %}

{% step %}

### Request contains event and signature

The request contains event data and a security signature for verification.
{% endstep %}

{% step %}

### Process the payload

Your application processes the webhook payload and takes appropriate action.
{% endstep %}
{% endstepper %}

## Event Types

The following events trigger webhook calls:

* **stream\_created**: Triggered when a new subscription payment stream is created (i.e., a user successfully pays for a subscription)
* **stream\_revoked**: Triggered when a subscription is revoked or canceled

Webhooks are only sent for events related to registered applications that have a webhook URL configured. Events for applications without a configured webhook URL will still be processed (e.g., subscriptions will be deactivated when streams are revoked), but no webhook will be sent.

## Webhook Configuration

### Setting Up Your Webhook URL

{% stepper %}
{% step %}
Register your application through the Papaya Telegram bot.
{% endstep %}

{% step %}
Navigate to your app details in the bot menu.
{% endstep %}

{% step %}
Select "Изменить Webhook URL" (Change Webhook URL).
{% endstep %}

{% step %}
Enter your webhook endpoint URL.
{% endstep %}

{% step %}
For production apps, HTTPS is required.
{% endstep %}
{% endstepper %}

### Webhook URL Requirements

* **Protocol**: HTTP or HTTPS (HTTPS required for Production mode)
* **Format**: Must be a valid URL (e.g., `https://yourdomain.com/webhook`)
* **Availability**: Must be publicly accessible from the internet
* **Security**: Should validate the HMAC signature in the request header

Note: Webhooks are only sent for blockchain events related to applications that have a webhook URL configured. Events for applications without a configured webhook URL will still be processed (e.g., subscriptions will be deactivated when streams are revoked), but no webhook notification will be sent.

## Webhook Payload Structure

Each webhook request contains a JSON payload with the following structure:

```json
{
  "updateId": "transaction-hash-string",
  "updateType": "stream_created|stream_revoked",
  "requestDate": "2023-12-23T10:00:00.0000000Z",
  "payload": {
    // Invoice object details
  }
}
```

### Payload Fields

* **updateId**: The blockchain transaction hash that triggered the event
* **updateType**: The type of event (`stream_created` or `stream_revoked`)
* **requestDate**: ISO 8601 formatted timestamp of when the webhook was sent
* **payload**: Complete invoice object containing payment details

## Security: Verifying Webhook Signatures

All webhook requests include an HMAC signature in the `X-Pay-Signature` header for security verification.

### Signature Generation

The signature is generated using the invoice's transaction hash as the data and your API key as the secret:

```
Signature = HMAC-SHA256(transaction_hash, api_key)
```

Where:

* `transaction_hash` is the `updateId` field from the webhook payload
* `api_key` is your application's API key

### Signature Verification Process

{% stepper %}
{% step %}
Receive the webhook request body as a raw string.
{% endstep %}

{% step %}
Extract the `updateId` field from the JSON payload.
{% endstep %}

{% step %}
Use your API key as the secret to generate an HMAC-SHA256 hash of the updateId.
{% endstep %}

{% step %}
Compare the generated signature with the one in the `X-Pay-Signature` header.
{% endstep %}
{% endstepper %}

### Examples Verification Code

#### .Net (C#)

```csharp
public class WebhookController : ControllerBase
{
    private readonly string _apiKey; // Your application's API key
    
    [HttpPost("webhook")]
    public async Task<IActionResult> HandleWebhook([FromBody] dynamic payload, [FromHeader] string xPaySignature)
    {
        // Extract the updateId from the payload
        var updateId = payload.updateId?.ToString();
        
        if (string.IsNullOrEmpty(updateId))
        {
            return BadRequest("Invalid payload: updateId is required");
        }
        
        // Generate expected signature using the updateId and your API key
        var expectedSignature = GenerateHmacSignature(updateId, _apiKey);
        
        // Verify the signature
        if (!VerifySignature(expectedSignature, xPaySignature))
        {
            return Unauthorized("Invalid signature");
        }
        
        // Process the webhook
        // ... your processing logic here ...
        
        return Ok();
    }
    
    private string GenerateHmacSignature(string data, string secret)
    {
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        return Convert.ToHexString(hash).ToLower();
    }
    
    private bool VerifySignature(string expected, string actual)
    {
        return expected.Equals(actual, StringComparison.OrdinalIgnoreCase);
    }
}
```

#### Node.js

```javascript
const crypto = require('crypto');

function verifySignature(updateId, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(updateId, 'utf8')
    .digest('hex');
    
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

app.post('/webhook', express.json(), (req, res) => {
  const signature = req.get('X-Pay-Signature');
  const { updateId } = req.body;
  
  if (!updateId) {
    return res.status(400).send('Invalid payload: updateId is required');
  }
  
  if (!verifySignature(updateId, signature, process.env.API_KEY)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process the webhook
  // ... your processing logic here ...
  
  res.status(200).send('OK');
});
```

#### FastAPI (Python)

```python
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
import hashlib
import hmac
import json

app = FastAPI()

API_KEY = "your-api-key-here"  # Your application's API key

@app.post("/webhook")
async def handle_webhook(request: Request):
    # Get the raw body for signature verification
    body = await request.body()
    body_str = body.decode("utf-8")
    
    # Parse the JSON to extract updateId
    payload = json.loads(body_str)
    update_id = payload.get("updateId")
    
    if not update_id:
        raise HTTPException(status_code=400, detail="Invalid payload: updateId is required")
    
    # Get the signature from headers
    signature = request.headers.get("X-Pay-Signature")
    
    if not signature:
        raise HTTPException(status_code=401, detail="Missing signature")
    
    # Verify the signature
    expected_signature = hmac.new(
        API_KEY.encode("utf-8"),
        update_id.encode("utf-8"),
        hashlib.sha256
    ).hexdigest().lower()
    
    if not hmac.compare_digest(expected_signature, signature.lower()):
        raise HTTPException(status_code=401, detail="Invalid signature")
    
    # Process the webhook
    # ... your processing logic here ...
    
    return {"status": "success"}
```

#### Django (Python)

```python
import hashlib
import hmac
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods

API_KEY = "your-api-key-here"  # Your application's API key

@csrf_exempt
@require_http_methods(["POST"])
def webhook_view(request):
    if request.content_type != 'application/json':
        return JsonResponse({'error': 'Invalid content type'}, status=400)
    
    try:
        # Get the raw body for signature verification
        body = request.body.decode('utf-8')
        payload = json.loads(body)
        update_id = payload.get('updateId')
        
        if not update_id:
            return JsonResponse({'error': 'Invalid payload: updateId is required'}, status=400)
        
        # Get the signature from headers
        signature = request.META.get('HTTP_X_PAY_SIGNATURE')
        
        if not signature:
            return JsonResponse({'error': 'Missing signature'}, status=401)
        
        # Verify the signature
        expected_signature = hmac.new(
            API_KEY.encode('utf-8'),
            update_id.encode('utf-8'),
            hashlib.sha256
        ).hexdigest().lower()
        
        if not hmac.compare_digest(expected_signature, signature.lower()):
            return JsonResponse({'error': 'Invalid signature'}, status=401)
        
        # Process the webhook
        # ... your processing logic here ...
        
        return JsonResponse({'status': 'success'})
        
    except json.JSONDecodeError:
        return JsonResponse({'error': 'Invalid JSON'}, status=400)
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)
```

#### Java (Spring Boot)

```java
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

@RestController
public class WebhookController {
    
    private static final String API_KEY = "your-api-key-here"; // Your application's API key
    
    @PostMapping("/webhook")
    public ResponseEntity<String> handleWebhook(@RequestBody Map<String, Object> payload,
                                               @RequestHeader("X-Pay-Signature") String signature) {
        try {
            // Extract updateId from the payload
            Object updateIdObj = payload.get("updateId");
            if (updateIdObj == null) {
                return ResponseEntity.badRequest()
                    .body("{\"error\": \"Invalid payload: updateId is required\"}");
            }
            String updateId = updateIdObj.toString();
            
            // Verify the signature
            String expectedSignature = generateHmacSignature(updateId, API_KEY);
            
            if (!secureCompare(expectedSignature, signature)) {
                return ResponseEntity.status(401)
                    .body("{\"error\": \"Invalid signature\"}");
            }
            
            // Process the webhook
            // ... your processing logic here ...
            
            return ResponseEntity.ok("{\"status\": \"success\"}");
            
        } catch (Exception e) {
            return ResponseEntity.status(500)
                .body("{\"error\": \"" + e.getMessage() + "\"}");
        }
    }
    
    private String generateHmacSignature(String data, String secret) 
            throws NoSuchAlgorithmException, InvalidKeyException {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(
            secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        mac.init(secretKeySpec);
        byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        
        // Convert to hex string and convert to lowercase
        StringBuilder result = new StringBuilder();
        for (byte b : hash) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
    
    private boolean secureCompare(String expected, String actual) {
        return expected.equalsIgnoreCase(actual);
    }
}
```

## Example Webhook Payloads

### Stream Created Event

```json
{
  "updateId": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "updateType": "stream_created",
  "requestDate": "2023-12-23T10:00:00.0000000Z",
  "payload": {
    "id": 123,
    "recipient": "0x742d35Cc6634C0532925a3b844Bc454e443867b4",
    "rate": "10.50",
    "isPaid": true,
    "tx": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "createdAt": "2023-12-23T09:30:00.0000000Z",
    "metadata": {
      "userId": 12345,
      "planId": "premium"
    }
  }
}
```

### Stream Revoked Event

```json
{
  "updateId": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
  "updateType": "stream_revoked",
  "requestDate": "2023-12-23T11:00:00.0000000Z",
  "payload": {
    "id": 123,
    "recipient": "0x742d35Cc6634C0532925a3b844Bc454e443867b4",
    "rate": "10.50",
    "isPaid": true,
    "tx": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "createdAt": "2023-12-23T09:30:00.0000000Z",
    "metadata": {
      "userId": 12345,
      "planId": "premium"
    }
  }
}
```

## Error Handling and Retries

Our system implements retry logic for webhook delivery:

* If your webhook endpoint returns a non-successful HTTP status code (not 2xx), the system will retry the request
* Retry attempts: 3 attempts with 1-minute intervals between attempts
* If all retries fail, the webhook will not be resent

When a `stream_revoked` event is received but no webhook URL is configured for the application, the system will automatically deactivate the corresponding subscription in the database without sending a webhook.

## Testing Webhooks

### Test Mode vs Production Mode

* **Test Mode**: Webhook URLs can use HTTP or HTTPS protocols
* **Production Mode**: Webhook URLs must use HTTPS protocol

### Testing Your Webhook

{% stepper %}
{% step %}
Register your application in the Telegram bot.
{% endstep %}

{% step %}
Set your webhook URL to a testing endpoint (like RequestBin or webhook.site).
{% endstep %}

{% step %}
Perform a test transaction.
{% endstep %}

{% step %}
Monitor your testing endpoint to see the webhook payload.
{% endstep %}
{% endstepper %}

## Troubleshooting

### Common Issues

{% stepper %}
{% step %}
Webhook not being received:

* Verify your URL is publicly accessible
* Check that your server is properly configured to handle POST requests
* Ensure your webhook URL is correctly set in the bot
  {% endstep %}

{% step %}
Signature verification failing:

* Confirm you're using the correct API key
* Ensure you're hashing the raw request body, not a parsed version
* Check that your HMAC implementation matches the expected format
  {% endstep %}

{% step %}
Webhook returning 404:

* Verify your endpoint path is correct
* Ensure your server is running and accessible
  {% endstep %}
  {% endstepper %}

### Debugging Tips

* Use logging to capture incoming webhook requests and their headers
* Verify that your endpoint accepts POST requests
* Check that your server handles JSON content type properly
* Monitor your server logs for any errors during webhook processing

## Best Practices

{% stepper %}
{% step %}
Always verify signatures to ensure webhook authenticity.
{% endstep %}

{% step %}
Respond quickly to webhook requests (within 10 seconds).
{% endstep %}

{% step %}
Use HTTPS endpoints in production environments.
{% endstep %}

{% step %}
Log webhook events for debugging and monitoring.
{% endstep %}

{% step %}
Handle duplicate events gracefully (the same event might be sent multiple times).
{% endstep %}

{% step %}
Validate payload data before processing.
{% endstep %}

{% step %}
Implement idempotency to prevent duplicate processing of the same event.
{% endstep %}
{% endstepper %}


