Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@mysten/payment-kit

Package Overview
Dependencies
Maintainers
4
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mysten/payment-kit - npm Package Compare versions

Comparing version
0.1.10
to
0.1.11
+256
docs/getting-started.md
# Getting Started
> Install and set up Payment Kit for your Sui application.
> **Note:** This package is in active development and should be used with caution. APIs are experimental and
> subject to breaking changes without notice. We recommend thoroughly testing any implementation
> before using in production environments.
This guide will help you set up and start using the Payment Kit SDK in your Sui application.
## Prerequisites
Before you begin, ensure you have:
- A Sui wallet with Testnet or Mainnet SUI tokens
- Basic understanding of TypeScript and the Sui blockchain
## Installation
Install the Payment Kit SDK and the Sui TypeScript SDK:
```bash npm2yarn
npm install --save @mysten/payment-kit @mysten/sui
```
## Setting up the client
The Payment Kit SDK extends the standard `SuiGrpcClient` to provide payment functionality. Here's
how to set it up:
```ts
// Create a Sui client with Payment Kit extension
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(paymentKit());
```
The client will automatically configure the correct package IDs for the network you're using
(Testnet or Mainnet).
## Your first payment
Let's process a basic registry-based payment using the default payment registry:
```ts
// Create or load your keypair
const keypair = Ed25519Keypair.generate();
const sender = keypair.getPublicKey().toSuiAddress();
// Create the payment transaction
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(), // Unique identifier for this payment
coinType: '0x2::sui::SUI', // Coin type (SUI in this case)
amount: 1n * MIST_PER_SUI, // 1 SUI (in MIST)
receiver,
sender: sender,
});
// Sign and execute
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEffects: true,
showEvents: true,
},
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Payment failed: ${result.FailedTransaction.status.error?.message}`);
}
console.log('Payment processed:', result.Transaction.digest);
```
## Understanding the payment
In this example:
1. **Nonce**: A unique identifier (UUIDv4) that prevents duplicate payments when using a registry
2. **Coin Type**: The type of coin being transferred (SUI token)
3. **Amount**: The payment amount in the smallest unit (MIST for SUI)
4. **Receiver**: The address receiving the payment
5. **Sender**: The address sending the payment (must match the transaction signer)
6. **Registry Name**: The payment registry to use (defaults to `DEFAULT_REGISTRY_NAME`)
## Verifying the payment
After processing a payment, you can query the payment record to verify it exists:
```ts
// Query the payment record
const paymentRecord = await client.paymentKit.getPaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1n * MIST_PER_SUI,
receiver,
});
if (paymentRecord) {
console.log('Payment verified!');
console.log('Transaction:', paymentRecord.paymentTransactionDigest);
console.log('Epoch:', paymentRecord.epochAtTimeOfRecord);
} else {
console.log('Payment not found');
}
```
## Processing an ephemeral payment
If you don't need duplicate prevention or persistent storage, use ephemeral payments:
```ts
const tx = client.paymentKit.tx.processEphemeralPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1n * MIST_PER_SUI,
receiver,
sender: sender,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Ephemeral payment failed: ${result.FailedTransaction.status.error?.message}`);
}
console.log('Ephemeral payment processed:', result.Transaction.digest);
```
Ephemeral payments:
- Don't create a `PaymentRecord`
- Don't prevent duplicates
- Still emit a `PaymentReceipt` event
- Have lower gas costs
## Working with different coin types
Payment Kit supports any Sui coin type. Here's how to process payments with custom coins:
```ts
// Example with a custom coin type
const customCoinType = '0xabcd...::my_coin::MY_COIN';
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: customCoinType,
amount: 5000,
receiver,
sender: sender,
});
// Execute the transaction
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
## Error handling
Always wrap payment operations in try-catch blocks:
```ts
try {
const tx = client.paymentKit.tx.processRegistryPayment({...});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
if (result.$kind === 'FailedTransaction') {
throw new Error(`Payment failed: ${result.FailedTransaction.status.error?.message}`);
}
console.log('Success:', result.Transaction.digest);
} catch (error) {
if (error.message.includes('Duplicate')) {
console.error('Payment already processed');
} else if (error.message.includes('insufficient')) {
console.error('Insufficient balance');
} else {
console.error('Payment failed:', error.message);
}
}
```
## Creating payment URIs
Payment Kit provides helper functions to create and parse Payment Kit transaction URIs. These URIs
are useful for generating payment links, QR codes, or deep links in your application:
```ts
// Create a payment URI
const paymentUri = createPaymentTransactionUri({
receiverAddress: '0x123...abc',
amount: 1000000000n, // 1 SUI in MIST
coinType: '0x2::sui::SUI',
nonce: crypto.randomUUID(),
label: 'Coffee Payment',
message: 'Payment for espresso',
registryName: 'my-registry', // Optional: specify a registry name
// Or use registryId: '0x...' for a specific registry object ID
});
console.log(paymentUri);
// Output: sui:pay?receiver=0x123...abc&amount=1000000000&coinType=0x2%3A%3Asui%3A%3ASUI&nonce=...&label=Coffee%20Payment&message=Payment%20for%20espresso&registry=my-registry
```
The URI parameters include:
- **receiver**: The Sui address receiving the payment (required)
- **amount**: The payment amount as a positive bigint (required)
- **coinType**: The coin type being transferred (required)
- **nonce**: A unique identifier up to 36 characters (required)
- **registry**: Either a registry name or object ID (optional)
- **label**: A short label for the payment (optional)
- **message**: A longer description message (optional)
- **iconUrl**: URL to an icon for display purposes (optional)
You can then parse URIs back into payment parameters:
```ts
// Parse a payment URI
const params = parsePaymentTransactionUri(paymentUri);
// Returns: { receiverAddress, amount, coinType, nonce, label?, message?, iconUrl?, registryName? | registryId? }
// Use the parsed parameters to create a transaction
const tx = client.paymentKit.tx.processRegistryPayment({
receiver: params.receiverAddress,
amount: params.amount,
coinType: params.coinType,
nonce: params.nonce,
registryName: params.registryName,
sender: sender,
});
```
## Next steps
Now that you understand the basics, explore more advanced features:
- [Payment Processing](./payment-processing) - Deep dive into payment models
- [Registry Management](./registry-management) - Create and configure custom registries
- [SDK API Reference](./payment-kit-sdk) - Complete SDK API documentation
# Payment Kit
> Accept payments in any coin type on the Sui blockchain with built-in duplicate prevention.
> **Note:** This package is in active development and should be used with caution. APIs are experimental and
> subject to breaking changes without notice. We recommend thoroughly testing any implementation
> before using in production environments.
The Sui Payment Kit SDK is a TypeScript library that provides a straightforward and secure way to
process blockchain payments on the Sui network. It offers flexible payment processing with built-in
duplicate prevention, configurable payment registries, and comprehensive receipt management.
## Overview
Payment Kit enables developers to integrate secure payment processing into their Sui applications
with minimal setup. The SDK provides two payment models:
- **Registry-based payments**: Persistent payment records with duplicate prevention and configurable
expiration
- **Ephemeral payments**: Lightweight payment processing without persistent storage
All payments emit a `PaymentReceipt` that can be stored offchain for verification and
record-keeping.
## Key features
- **Duplicate Prevention**: Registry-based payments automatically prevent duplicate transactions
- **Flexible Payment Models**: Choose between registry-based or ephemeral payment processing
- **Payment Registries**: Create and manage custom payment registries with configurable policies
- **Receipt Management**: Automatic receipt generation for all payments
- **Multi-coin Support**: Process payments with any Sui coin type
- **Configurable Expiration**: Set custom expiration policies for payment records
- **Fund Management**: Optional registry-managed funds for simplified coin handling
## Installation
```bash npm2yarn
npm install --save @mysten/payment-kit @mysten/sui
```
## Quick start
```ts
// Create a Sui client with a Payment Kit extension
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(paymentKit());
// Process a registry-based payment
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1n * MIST_PER_SUI, // 1 SUI in MIST
receiver: '0x123...abc',
sender: '0x456...def',
});
// Sign and execute the transaction
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Payment failed: ${result.FailedTransaction.status.error?.message}`);
}
```
## Network support
Payment Kit supports the following Sui networks:
- **Mainnet**: Production environment
- **Testnet**: Testing environment
The SDK automatically configures the correct package and object IDs based on the network specified
in your `SuiGrpcClient` configuration.
## Core concepts
### Payment processing
Payment Kit offers two distinct payment processing models to suit different application needs:
1. **Registry-based Payments**: Creates a persistent `PaymentRecord` that prevents duplicate
payments and provides verifiable proof of payment
2. **Ephemeral Payments**: Processes payments without persistent storage, suitable for scenarios
where duplicate prevention is handled externally
### Payment registries
A `PaymentRegistry` manages payment records and configurations. Benefits include:
- Centralized payment tracking
- Configurable expiration policies
- Optional fund management
- Custom naming for easy identification and indexing
### Payment records
When using registry-based payments, a `PaymentRecord` is created as a Dynamic Field on the registry.
Records are identified by a composite key derived from:
- Payment nonce (unique identifier)
- Payment amount
- Coin type
- Receiver address
### Payment receipts
Every payment (registry-based or ephemeral) emits a `PaymentReceipt` event containing:
```ts
type PaymentReceipt = {
paymentType: 'Registry' | 'Ephemeral';
nonce: string;
amount: number;
receiver: string;
coinType: string;
timestampMs: number;
};
```
## Next steps
- [Getting Started Guide](/payment-kit/getting-started) - Detailed setup and usage instructions
- [Payment Processing](/payment-kit/payment-processing) - Learn about different payment models
- [Registry Management](/payment-kit/registry-management) - Create and configure payment registries
- [SDK API Reference](/payment-kit/payment-kit-sdk) - Complete SDK API documentation
# Payment Kit
> Accept payments in any coin type on Sui
- [Payment Kit](..md): Accept payments in any coin type on the Sui blockchain with built-in duplicate prevention.
- [Getting Started](./getting-started.md): Install and set up Payment Kit for your Sui application.
- [Payment Kit SDK](./payment-kit-sdk.md): Payment Kit SDK API reference and configuration for the PaymentKitClient.
- [Payment Processing](./payment-processing.md): Process registry-based and ephemeral payments and handle transaction results on Sui.
- [Registry Management](./registry-management.md): Create, configure, and manage payment registries and accepted coin types on Sui.
# Payment Kit SDK
> Payment Kit SDK API reference and configuration for the PaymentKitClient.
> **Note:** This package is in active development and should be used with caution. APIs are experimental and
> subject to breaking changes without notice. We recommend thoroughly testing any implementation
> before using in production environments.
Complete API reference for the Payment Kit SDK.
## Client setup
### PaymentKitClient
The main client class that provides access to Payment Kit functionality.
#### `paymentKit()`
Function to create a Payment Kit client extension for `SuiGrpcClient`.
```ts
paymentKit<Name extends string = 'paymentKit'>({
name = 'paymentKit' as Name,
}): SuiClientRegistration<PaymentKitCompatibleClient, Name, PaymentKitClient>
```
**Example:**
```ts
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(paymentKit());
// Access Payment Kit functionality
client.paymentKit.tx.processRegistryPayment(/* ... */);
```
**Supported Networks:**
- `testnet`
- `mainnet`
**Throws:**
- `PaymentKitClientError` if network is unsupported
## Transaction methods
Transaction methods create complete, ready-to-sign transactions.
### `client.paymentKit.tx.processRegistryPayment()`
Create a transaction to process a registry-based payment.
```ts
processRegistryPayment(
options: ProcessRegistryPaymentOptions
): Transaction
```
**Parameters:**
```ts
type ProcessRegistryPaymentOptions = {
nonce: string; // Unique payment identifier
coinType: string; // Coin type (e.g., '0x2::sui::SUI')
amount: number | bigint; // Amount in smallest unit
receiver: string; // Recipient address
sender: string; // Sender address (must match signer)
} & Partial<Registry>;
```
**Returns:** `Transaction` - Ready-to-sign transaction
If you prefer to provide the `registryId`, you can do so instead of the `registryName`.
**Example:**
```ts
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
sender: senderAddress,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Payment failed: ${result.FailedTransaction.status.error?.message}`);
}
```
**On-Chain Effects:**
- Transfers coins from sender to receiver
- Creates a `PaymentRecord` dynamic field on the registry
- Emits a `PaymentReceipt` event
**Throws:**
- Error if payment with same key already exists
- Error if sender has insufficient balance
---
### `client.paymentKit.tx.processEphemeralPayment()`
Create a transaction to process an ephemeral payment (no registry storage).
```ts
processEphemeralPayment(
options: ProcessEphemeralPaymentOptions
): Transaction
```
**Parameters:**
```ts
type ProcessEphemeralPaymentOptions = {
nonce: string; // Payment identifier (not enforced for uniqueness)
coinType: string; // Coin type
amount: number | bigint; // Amount in smallest unit
receiver: string; // Recipient address
sender: string; // Sender address (must match signer)
};
```
**Returns:** `Transaction` - Ready-to-sign transaction
**Example:**
```ts
const tx = client.paymentKit.tx.processEphemeralPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 500000000,
receiver,
sender: senderAddress,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Ephemeral payment failed: ${result.FailedTransaction.status.error?.message}`);
}
```
**On-Chain Effects:**
- Transfers coins from sender to receiver
- Emits a `PaymentReceipt` event
- No `PaymentRecord` created (no duplicate prevention)
---
### `client.paymentKit.tx.createRegistry()`
Create a transaction to create a new payment registry.
```ts
createRegistry(options: CreateRegistryOptions): Transaction
```
**Parameters:**
```ts
type CreateRegistryOptions = {
registryName: string; // Unique name for the registry
};
```
**Returns:** `Transaction` - Ready-to-sign transaction
**Example:**
```ts
const tx = client.paymentKit.tx.createRegistry({
registryName: 'my-payment-registry',
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showObjectChanges: true,
},
});
// Extract the RegistryAdminCap from result.Transaction.objectChanges
const adminCap = result.Transaction.objectChanges?.find(
(change) => change.type === 'created' && change.objectType.includes('RegistryAdminCap'),
);
```
**On-Chain Effects:**
- Creates a `PaymentRegistry` object
- Creates a `RegistryAdminCap` owned by sender
- Registry ID is deterministically derived from name
**Important:**
- Registry names must be unique globally
- Save the `RegistryAdminCap` object ID for configuration operations
---
### `client.paymentKit.tx.setConfigEpochExpirationDuration()`
Create a transaction to set the epoch expiration duration for payment records.
```ts
setConfigEpochExpirationDuration(
options: SetEpochExpirationDurationOptions
): Transaction
```
**Parameters:**
```ts
type SetEpochExpirationDurationOptions = {
epochExpirationDuration: number | bigint; // Number of epochs before records expire
adminCapId: string; // Admin capability object ID
} & Partial<Registry>;
```
**Returns:** `Transaction` - Ready-to-sign transaction
**Example:**
```ts
const tx = client.paymentKit.tx.setConfigEpochExpirationDuration({
registryName: 'my-registry',
epochExpirationDuration: 60,
adminCapId: adminCapId,
});
await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
**Requires:**
- Transaction signer must own the admin capability
**Default:** 30 epochs
---
### `client.paymentKit.tx.setConfigRegistryManagedFunds()`
Create a transaction to enable/disable registry-managed funds.
```ts
setConfigRegistryManagedFunds(
options: SetRegistryManagedFundsOptions
): Transaction
```
**Parameters:**
```ts
type SetRegistryManagedFundsOptions = {
registryManagedFunds: boolean; // Enable (true) or disable (false)
adminCapId: string; // Admin capability object ID
} & Partial<Registry>;
```
**Returns:** `Transaction` - Ready-to-sign transaction
**Example:**
```ts
const tx = client.paymentKit.tx.setConfigRegistryManagedFunds({
registryName: 'my-registry',
registryManagedFunds: true,
adminCapId: adminCapId,
});
await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
**When Enabled:**
- Payment receiver must be the registry itself
- Funds accumulate in the registry
- Admin can withdraw funds later
**Default:** Disabled (funds go directly to receivers)
---
### `client.paymentKit.tx.withdrawFromRegistry()`
Create a transaction to withdraw accumulated funds from a registry.
```ts
withdrawFromRegistry(
options: WithdrawFromRegistryOptions
): Transaction
```
**Parameters:**
```ts
type WithdrawFromRegistryOptions = {
coinType: string; // Coin type to withdraw
adminCapId: string; // Admin capability object ID
} & Partial<Registry>;
```
**Returns:** `Transaction` - Ready-to-sign transaction
**Example:**
```ts
const tx = client.paymentKit.tx.withdrawFromRegistry({
coinType: '0x2::sui::SUI',
registryName: 'my-registry',
adminCapId: adminCapId,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
**Requires:**
- Registry must have `registryManagedFunds` enabled
- Transaction signer must own the admin capability
**Effect:**
- Withdraws all coins of specified type to the sender
---
### `client.paymentKit.tx.deletePaymentRecord()`
Create a transaction to delete an expired payment record.
```ts
deletePaymentRecord(
options: DeletePaymentRecordOptions
): Transaction
```
**Parameters:**
```ts
type DeletePaymentRecordOptions = {
nonce: string; // Payment nonce
coinType: string; // Coin type of the payment
amount: number | bigint; // Payment amount
receiver: string; // Payment receiver
} & Partial<Registry>;
```
**Returns:** `Transaction` - Ready-to-sign transaction
**Example:**
```ts
const tx = client.paymentKit.tx.deletePaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
registryName: 'my-registry',
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
**Requires:**
- Payment record must be expired (current epoch >= record epoch + expiration duration)
- Anyone can delete expired records (permissionless)
**Effect:**
- Removes the payment record from the registry
- Sender receives storage rebate
## Query methods
### `client.paymentKit.getPaymentRecord()`
Query the blockchain for a payment record.
```ts
async getPaymentRecord(
options: GetPaymentRecordOptions
): Promise<GetPaymentRecordResponse | null>
```
**Parameters:**
```ts
type GetPaymentRecordOptions = {
nonce: string; // Payment nonce
coinType: string; // Coin type
amount: number | bigint; // Payment amount
receiver: string; // Payment receiver
} & Partial<Registry>;
```
**Returns:**
```ts
type GetPaymentRecordResponse = {
key: string; // Dynamic field object ID
paymentTransactionDigest: string | null; // Transaction that created the record
epochAtTimeOfRecord: string; // Epoch when record was created
} | null; // null if record doesn't exist
```
**Example:**
```ts
const record = await client.paymentKit.getPaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
});
if (record) {
console.log('Payment exists!');
console.log('Transaction:', record.paymentTransactionDigest);
console.log('Created at epoch:', record.epochAtTimeOfRecord);
} else {
console.log('Payment not found');
}
```
**Use Cases:**
- Verify a payment was completed
- Check for duplicate payments before processing
- Retrieve payment details for reconciliation
## Call methods
Call methods return transaction commands that can be composed in custom transactions. Use these when
you need fine-grained control over transaction construction.
### `client.paymentKit.calls.processRegistryPayment()`
Returns a transaction command for registry-based payment.
```ts
processRegistryPayment(
options: ProcessRegistryPaymentOptions
): TransactionObjectArgument
```
**Example:**
```ts
const tx = new Transaction();
// Add custom logic before payment
tx.moveCall({
target: '0xMyPackage::my_module::pre_payment_check',
arguments: [
/* ... */
],
});
// Add payment command
tx.add(
client.paymentKit.calls.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
sender: senderAddress,
}),
);
// Add custom logic after payment
tx.moveCall({
target: '0xMyPackage::my_module::post_payment_action',
arguments: [
/* ... */
],
});
await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
### Other call methods
All transaction methods have corresponding call methods:
- `client.paymentKit.calls.processEphemeralPayment()`
- `client.paymentKit.calls.createRegistry()`
- `client.paymentKit.calls.setConfigEpochExpirationDuration()`
- `client.paymentKit.calls.setConfigRegistryManagedFunds()`
- `client.paymentKit.calls.withdrawFromRegistry()`
- `client.paymentKit.calls.deletePaymentRecord()`
## Types
### PaymentReceipt event
Emitted when a payment is processed (registry or ephemeral).
```ts
type PaymentReceipt = {
payment_type: 'Registry' | 'Ephemeral'; // Payment model used
nonce: string; // Payment identifier
amount: string; // Amount (as string)
receiver: string; // Recipient address
coin_type: string; // Coin type
timestamp_ms: string; // Unix timestamp in ms
};
```
**Extracting from Transaction Result:**
```ts
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEvents: true,
},
});
const receiptEvent = result.Transaction.events?.find((event) =>
event.type.includes('PaymentReceipt'),
);
if (receiptEvent) {
const receipt = receiptEvent.parsedJson as PaymentReceipt;
console.log('Payment Type:', receipt.payment_type);
console.log('Nonce:', receipt.nonce);
console.log('Amount:', receipt.amount);
}
```
## Error handling
### PaymentKitClientError
Custom error class for Payment Kit errors.
```ts
class PaymentKitClientError extends Error {
constructor(message: string);
}
```
## URI utilities
Payment Kit provides utility functions for creating and parsing payment transaction URIs. These URIs
follow the `sui:pay` protocol and can be used to encode payment parameters for QR codes, deep links,
or other sharing mechanisms.
### URI format
Payment URIs follow the `sui:pay` protocol:
```
sui:pay?
receiver=<address>
&amount=<amount>
&coinType=<coinType>
&nonce=<nonce>
&registry=<registryIdOrName>
&label=<label>
&message=<message>
&iconUrl=<url>
```
**Required Parameters:**
| Parameter | Description |
| ---------- | --------------------------------------------------- |
| `receiver` | Recipient Sui address |
| `amount` | Payment amount in smallest unit (for example, MIST) |
| `coinType` | Coin type (for example, `0x2::sui::SUI`) |
| `nonce` | Unique payment identifier (max 36 chars) |
**Optional Parameters:**
| Parameter | Description |
| ---------- | ----------------------------------------------- |
| `registry` | Registry ID or name for registry-based payments |
| `label` | Human-readable label for the payment |
| `message` | Message or memo for the payment |
| `iconUrl` | URL to an icon image for display purposes |
---
### `createPaymentTransactionUri()`
Creates a payment transaction URI from the given parameters.
```ts
createPaymentTransactionUri(params: PaymentUriParams): string
```
**Parameters:**
```ts
type PaymentUriParams = {
receiverAddress: string; // Recipient Sui address
amount: bigint; // Amount in smallest unit
coinType: string; // Coin type (e.g., '0x2::sui::SUI')
nonce: string; // Unique identifier (max 36 chars)
label?: string; // Optional human-readable label
message?: string; // Optional message/memo
iconUrl?: string; // Optional icon URL
} & Partial<Registry>;
```
**Returns:** `string` - The constructed payment URI
**Example:**
```ts
// Create a URI for a registry-based payment
const uri = createPaymentTransactionUri({
receiverAddress: '0x1234...abcd',
amount: 1000000000n, // 1 SUI
coinType: '0x2::sui::SUI',
nonce: crypto.randomUUID(),
registryName: 'my-payment-registry',
label: 'Coffee Purchase',
message: 'Thanks for your order!',
});
// Result:
// sui:pay?
// receiver=0x1234...
// &amount=1000000000
// &coinType=0x2::sui::SUI
// &nonce=abc-123-def
// &registry=my-payment-registry
// &label=Coffee+Purchase
// &message=Thanks+for+your+order!
// Create a URI for an ephemeral payment (no registry)
const ephemeralUri = createPaymentTransactionUri({
receiverAddress: '0x1234...abcd',
amount: 500000000n, // 0.5 SUI
coinType: '0x2::sui::SUI',
nonce: crypto.randomUUID(),
});
```
**Throws:**
- `PaymentKitUriError` if receiver address is invalid
- `PaymentKitUriError` if amount is not positive
- `PaymentKitUriError` if coin type is invalid
- `PaymentKitUriError` if nonce is missing or exceeds 36 characters
- `PaymentKitUriError` if registry ID is not a valid Sui object ID
---
### `parsePaymentTransactionUri()`
Parses a payment transaction URI into its component parameters.
```ts
parsePaymentTransactionUri(uri: string): PaymentUriParams
```
**Parameters:**
| Parameter | Type | Description |
| --------- | -------- | ------------------------ |
| `uri` | `string` | The payment URI to parse |
**Returns:** `PaymentUriParams` - The parsed payment parameters
**Example:**
```ts
const uri =
'sui:pay?receiver=0x1234...&amount=1000000000&coinType=0x2::sui::SUI&nonce=abc123&registry=my-registry';
const params = parsePaymentTransactionUri(uri);
console.log(params.receiverAddress); // '0x1234...'
console.log(params.amount); // 1000000000n (bigint)
console.log(params.coinType); // '0x2::sui::SUI'
console.log(params.nonce); // 'abc123'
console.log(params.registryName); // 'my-registry'
// Use parsed params to process a payment
const tx = client.paymentKit.tx.processRegistryPayment({
receiver: params.receiverAddress,
amount: params.amount,
coinType: params.coinType,
nonce: params.nonce,
registryName: params.registryName!,
sender: senderAddress,
});
```
**Throws:**
- `PaymentKitUriError` if URI doesn't start with `sui:pay?`
- `PaymentKitUriError` if required parameters are missing
- `PaymentKitUriError` if receiver address is invalid
- `PaymentKitUriError` if coin type is invalid
- `PaymentKitUriError` if nonce exceeds 36 characters
- `PaymentKitUriError` if amount is not a positive number
### Future versions
Check the
[CHANGELOG](https://github.com/MystenLabs/ts-sdks/blob/main/packages/payment-kit/CHANGELOG.md) for
updates.
# Payment Processing
> Process registry-based and ephemeral payments and handle transaction results on Sui.
> **Note:** This package is in active development and should be used with caution. APIs are experimental and
> subject to breaking changes without notice. We recommend thoroughly testing any implementation
> before using in production environments.
Payment Kit provides two distinct payment processing models, each designed for different use cases.
This guide explains how each model works and when to use them.
## Payment models overview
### Registry-based payments
Registry-based payments create a persistent `PaymentRecord` stored onchain in a `PaymentRegistry`.
This model provides:
- **Duplicate Prevention**: A payment with the same parameters can only be processed once
- **Verifiable Proof**: Onchain proof that a payment was made
- **Expiration Management**: Records can be deleted after a configurable period
- **Centralized Tracking**: All payments are indexed under a specific registry
**When to Use:**
- E-commerce checkouts
- Subscription payments
- Invoice payments
- Any scenario requiring duplicate prevention
### Ephemeral payments
Ephemeral payments process transfers without creating persistent onchain records. This model
provides:
- **Lower Gas Costs**: No onchain storage means cheaper transactions
- **Flexibility**: No registry configuration required
**When to Use:**
- Tipping/donations
- Recurring payments with external tracking
- High-frequency microtransactions
- Scenarios where duplicate prevention is handled offchain
## How payment keys work
Payment Kit uses a composite key system to uniquely identify payments. Understanding this is crucial
for using the SDK effectively.
### Payment key components
A payment key is derived from four parameters:
```ts
type PaymentKeyArgs = {
nonce: string; // Unique identifier you provide
amount: number; // Payment amount in smallest unit
receiver: string; // Recipient address
coinType: string; // Coin type being transferred
};
```
### Key generation
When you process a registry payment, Payment Kit hashes these parameters to create a unique key:
```ts
// These parameters create a unique payment key
const paymentParams = {
nonce: 'b5e88aec-d88e-4961-9204-6c84e0e1de4e',
amount: 1000000000,
receiver,
coinType: '0x2::sui::SUI',
};
// If you try to process the same payment twice, it will fail
const tx1 = client.paymentKit.tx.processRegistryPayment({
...paymentParams,
sender: senderAddress,
registryName: 'my-registry',
});
// This will fail - same payment key
const tx2 = client.paymentKit.tx.processRegistryPayment({
...paymentParams,
sender: senderAddress,
registryName: 'my-registry',
});
```
### Changing any component creates a new key
Each component affects the payment key. Changing any parameter creates a different payment. This
means, as an example, that a `nonce` can be reused for a different payment, if the `coinType` or
`receiver` differ.
```ts
// Original payment
const originalNonce = crypto.randomUUID();
const payment1 = {
nonce: originalNonce,
amount: 1000000000,
receiver,
coinType: '0x2::sui::SUI',
};
// Different nonce = different payment
const payment2 = {
nonce: crypto.randomUUID(), // Changed
amount: 1000000000,
receiver,
coinType: '0x2::sui::SUI',
};
// Different amount = different payment
const payment3 = {
nonce: originalNonce,
amount: 2000000000, // Changed
receiver,
coinType: '0x2::sui::SUI',
};
// All three are unique payments that can be processed separately
```
## Processing registry-based payments
Let's walk through a complete registry payment workflow:
### Step 1: Create the transaction
```ts
// Define your payment parameters
const paymentParams = {
nonce: crypto.randomUUID(), // Your unique payment ID
coinType: '0x2::sui::SUI', // SUI token
amount: 1000000000, // 1 SUI (in MIST)
receiver,
sender: senderAddress, // Must match signer
};
// Create the transaction
const tx = client.paymentKit.tx.processRegistryPayment(paymentParams);
```
### What happens onchain
When this transaction executes, Payment Kit:
1. **Validates** the payment parameters
2. **Checks** if a payment with this key already exists in the registry
3. **Transfers** the coins from sender to receiver
4. **Creates** a `PaymentRecord` dynamic field on the registry
5. **Emits** a `PaymentReceipt` event
### Step 2: Execute the transaction
```ts
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEffects: true,
showEvents: true,
showObjectChanges: true,
},
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Payment failed: ${result.FailedTransaction.status.error?.message}`);
}
console.log('Transaction digest:', result.Transaction.digest);
```
### Step 3: Extract the receipt
```ts
// Find the PaymentReceipt event
const receiptEvent = result.Transaction.events?.find((event) =>
event.type.includes('PaymentReceipt'),
);
if (receiptEvent) {
const receipt = receiptEvent.parsedJson;
console.log('Payment Receipt:');
console.log(' Type:', receipt.payment_type); // 'Registry'
console.log(' Nonce:', receipt.nonce); // 'crypto.randomUUID()'
console.log(' Amount:', receipt.amount); // 1n * MIST_PER_SUI
console.log(' Receiver:', receipt.receiver); //
console.log(' Coin Type:', receipt.coin_type); // '0x2::sui::SUI'
console.log(' Timestamp:', receipt.timestamp_ms); // Unix timestamp
}
```
### Step 4: Verify the payment record
```ts
// Query the payment record to confirm it exists
const record = await client.paymentKit.getPaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
});
if (record) {
console.log('Payment verified!');
console.log('Record ID:', record.key);
console.log('Transaction:', record.paymentTransactionDigest);
console.log('Epoch:', record.epochAtTimeOfRecord);
}
```
## Processing ephemeral payments
Ephemeral payments follow a simpler flow without creating persistent records:
### Creating an ephemeral payment
```ts
// No registry needed for ephemeral payments
const tx = client.paymentKit.tx.processEphemeralPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 500000000, // 0.5 SUI
receiver,
sender: senderAddress,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEvents: true,
},
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Ephemeral payment failed: ${result.FailedTransaction.status.error?.message}`);
}
```
### What happens onchain
For ephemeral payments, Payment Kit:
1. **Validates** the payment parameters
2. **Transfers** the coins from sender to receiver
3. **Emits** a `PaymentReceipt` event
No `PaymentRecord` is created, so duplicate payments are not prevented.
### Extracting the receipt
```ts
const receiptEvent = result.Transaction.events?.find((event) =>
event.type.includes('PaymentReceipt'),
);
if (receiptEvent) {
const receipt = receiptEvent.parsedJson;
console.log('Payment Type:', receipt.payment_type); // 'Ephemeral'
}
```
## Advanced payment scenarios
### Processing multiple payments in one transaction
You can combine multiple payment operations in a single transaction:
```ts
const tx = new Transaction();
// Add multiple registry payments
tx.add(
client.paymentKit.calls.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
sender: senderAddress,
}),
);
tx.add(
client.paymentKit.calls.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 2000000000,
receiver,
sender: senderAddress,
}),
);
// Execute all payments in one transaction
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
### Working with custom coin types
Payment Kit works with any Sui coin type:
```ts
// Example with a custom token
const CUSTOM_COIN = '0xabc123::my_token::MY_TOKEN';
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: CUSTOM_COIN,
amount: 1000, // Amount in the coin's smallest unit
receiver,
sender: senderAddress,
});
// Make sure the sender has enough of the custom coin
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
## Gas considerations
### Registry payments
Registry payments have higher gas costs because they:
- Create a new dynamic field (first payment with unique key)
- Write data to the registry object
- Perform duplicate checking
### Ephemeral payments
Ephemeral payments have lower gas costs because they:
- Only transfer coins
- Don't create persistent storage
- Skip duplicate checking
## Best practices
1. **Use Meaningful Nonces**: Choose nonces that help you track payments (for example, `order-123`,
`invoice-456`)
2. **Store Receipts Off-Chain**: Save `PaymentReceipt` events in your database for quick lookup
3. **Handle Duplicate Attempts**: Gracefully handle duplicate payment attempts with proper error
messages
4. **Verify Before Delivery**: Always verify payment records before delivering goods or services
5. **Choose the Right Model**:
- Use registry payments for critical transactions requiring duplicate prevention
- Use ephemeral payments for high-frequency, low-value transactions
6. **Monitor Expiration**: If using registry payments, be aware of the expiration policy and query
records before they expire
## Next steps
- [Registry Management](./registry-management) - Learn how to create and configure custom registries
# Registry Management
> Create, configure, and manage payment registries and accepted coin types on Sui.
> **Note:** This package is in active development and should be used with caution. APIs are experimental and
> subject to breaking changes without notice. We recommend thoroughly testing any implementation
> before using in production environments.
Payment registries are the core of Payment Kit's duplicate prevention and payment tracking system.
This guide covers creating, configuring, and managing payment registries.
## Understanding payment registries
A `PaymentRegistry` is an onchain object that:
- Stores `PaymentRecord` dynamic fields for each unique payment
- Manages configuration settings (expiration, fund management)
- Can be owned and administered by a specific account
- Provides namespaced payment tracking
### Default registry
Payment Kit provides a default registry that's ready to use:
```ts
// Use the default registry
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
sender: senderAddress,
});
```
### When to create a custom registry
Consider creating your own registry when:
- You want isolated payment tracking for your application
- You need custom expiration policies
- You want to manage funds centrally
- You need better indexing of your payments
- You want to avoid potential congestion on the default registry
## Creating a registry
### Basic registry creation
Creating a registry is straightforward. You only need to provide a unique name:
```ts
// Create a new registry
const tx = client.paymentKit.tx.createRegistry({
registryName: 'my-app-payments',
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEffects: true,
showObjectChanges: true,
},
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Registry creation failed: ${result.FailedTransaction.status.error?.message}`);
}
console.log('Registry created!');
console.log('Transaction:', result.Transaction.digest);
```
### What gets created
When you create a registry, two objects are created:
1. **PaymentRegistry**: The registry object that stores payment records
2. **RegistryAdminCap**: A capability object that grants admin permissions
### Extracting the admin capability
The `RegistryAdminCap` is crucial for configuring your registry:
```ts
// Find the admin cap in the transaction result
const adminCapObject = result.Transaction.objectChanges?.find(
(change) => change.type === 'created' && change.objectType.includes('RegistryAdminCap'),
);
if (adminCapObject && 'objectId' in adminCapObject) {
const adminCapId = adminCapObject.objectId;
console.log('Admin Capability ID:', adminCapId);
// Store this ID - you'll need it to configure the registry
await database.saveAdminCap({
registryName: 'my-app-payments',
adminCapId: adminCapId,
owner: senderAddress,
});
}
```
### Deriving the registry ID
Payment Kit derives registry IDs deterministically from the registry name:
```ts
// You can compute the registry ID without querying the chain
const registryName = 'random-registry-name';
const registryId = client.paymentKit.getRegistryIdFromName(registryName);
console.log('Registry ID:', registryId);
```
This allows you to reference registries by name throughout your application.
## Registry configuration
Registries have two main configuration options:
### Step 1: Epoch expiration duration
Controls how long payment records persist before they can be deleted.
**Default**: 30 epochs (~30 days on Mainnet)
```ts
// Set expiration to 60 epochs
const tx = client.paymentKit.tx.setConfigEpochExpirationDuration({
registryName: 'my-app-payments',
epochExpirationDuration: 60,
adminCapId: adminCapId,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
// Check transaction status
if (result.$kind === 'FailedTransaction') {
throw new Error(`Configuration update failed: ${result.FailedTransaction.status.error?.message}`);
}
console.log('Expiration set to 60 epochs');
```
**How It Works:**
When a payment is recorded, it stores the current epoch. After the expiration duration has passed,
anyone can delete the record to reclaim storage fees:
```ts
// Current epoch: 1000
// Payment recorded at epoch: 1000
// Expiration duration: 30
// Can be deleted after epoch: 1030
```
**Use Cases:**
- **Short expiration (7-14 epochs)**: High-volume, time-sensitive payments (subscriptions, tickets)
- **Medium expiration (30-60 epochs)**: Standard e-commerce transactions
- **Long expiration (180+ epochs)**: Important financial records requiring long-term verification
### Step 2: Registry managed funds
Controls whether payments must be sent to the registry itself for later withdrawal.
**Default**: Disabled (funds go directly to receivers)
```ts
// Enable registry-managed funds
const tx = client.paymentKit.tx.setConfigRegistryManagedFunds({
registryName: 'my-app-payments',
registryManagedFunds: true,
adminCapId: adminCapId,
});
await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
console.log('Registry now manages funds');
```
**How it works:**
When enabled:
- All payments must specify the registry as the receiver
- Coins are transferred to the registry object
- Registry admin can withdraw accumulated funds later
- Simplifies coin merging for high-throughput scenarios
```ts
// With registry-managed funds enabled
const registryId = getRegistryIdFromName('my-app-payments', namespaceId);
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver: registryId, // Must be the registry itself
sender: senderAddress,
registryName: 'my-app-payments',
});
```
**Use Cases:**
- Platforms collecting payments on behalf of sellers
- Applications that need to batch process payouts
- Scenarios with very high transaction volumes
- Simplified accounting and reconciliation
### Configuring both settings
You can configure both settings independently:
```ts
const tx = new Transaction();
// Set expiration duration
tx.add(
client.paymentKit.calls.setConfigEpochExpirationDuration({
registryName: 'my-app-payments',
epochExpirationDuration: 90,
adminCapId: adminCapId,
}),
);
// Enable managed funds
tx.add(
client.paymentKit.calls.setConfigRegistryManagedFunds({
registryName: 'my-app-payments',
registryManagedFunds: true,
adminCapId: adminCapId,
}),
);
// Apply both configurations in one transaction
await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
## Withdrawing funds from a registry
If you've enabled registry-managed funds, you can withdraw accumulated coins:
```ts
// Withdraw all SUI from the registry
const tx = client.paymentKit.tx.withdrawFromRegistry({
coinType: '0x2::sui::SUI',
registryName: 'my-app-payments',
adminCapId: adminCapId,
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
options: {
showEffects: true,
},
});
console.log('Funds withdrawn!');
```
**Important notes:**
- Only the admin cap owner can withdraw funds
- You must specify the coin type to withdraw
- All coins of that type are withdrawn in one operation
- The withdrawn coins are sent to the transaction sender
### Withdrawing multiple coin types
If your registry accumulates different coin types:
```ts
const tx = new Transaction();
// Withdraw SUI
tx.add(
client.paymentKit.calls.withdrawFromRegistry({
coinType: '0x2::sui::SUI',
registryName: 'my-app-payments',
adminCapId: adminCapId,
}),
);
// Withdraw custom token
tx.add(
client.paymentKit.calls.withdrawFromRegistry({
coinType: '0xabc123::my_token::MY_TOKEN',
registryName: 'my-app-payments',
adminCapId: adminCapId,
}),
);
await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
```
## Deleting expired payment records
After the expiration period, payment records can be deleted to reclaim storage fees:
```ts
// Delete an expired payment record
const tx = client.paymentKit.tx.deletePaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
registryName: 'my-app-payments',
});
const result = await client.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
});
console.log('Record deleted, storage rebate received');
```
**Key points:**
- Records can only be deleted after expiration
- Anyone can delete expired records (permissionless cleanup)
- The deleter receives a small storage rebate
- This incentivizes automatic cleanup of old records
### Checking if a record can be deleted
```ts
// Get the payment record
const record = await client.paymentKit.getPaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
registryName: 'my-app-payments',
});
if (record) {
const recordEpoch = parseInt(record.epochAtTimeOfRecord);
const currentEpoch = await client.core
.getLatestSuiSystemState()
.then((state) => Number(state.epoch));
const expirationDuration = 30; // Your registry's setting
const canDelete = currentEpoch >= recordEpoch + expirationDuration;
console.log('Record epoch:', recordEpoch);
console.log('Current epoch:', currentEpoch);
console.log('Can delete:', canDelete);
}
```
## Using registry ID directly
If you know the registry ID, you can use it instead of the name:
```ts
const registryId = '0x123abc...';
// Process payment using registry ID
const tx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
sender: senderAddress,
registryId: registryId, // Use ID instead of name
});
// Query using registry ID
const record = await client.paymentKit.getPaymentRecord({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver,
registryId: registryId,
});
```
## Complete registry setup example
Here's a complete workflow for setting up a custom registry:
```ts
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(paymentKit());
const keypair = Ed25519Keypair.generate();
const registryName = 'my-marketplace-registry';
// Step 1: Create the registry
console.log('Creating registry...');
const createTx = client.paymentKit.tx.createRegistry({
registryName: registryName,
});
const createResult = await client.signAndExecuteTransaction({
transaction: createTx,
signer: keypair,
options: {
showEffects: true,
showObjectChanges: true,
},
});
// Check transaction status
if (createResult.$kind === 'FailedTransaction') {
throw new Error(
`Registry creation failed: ${createResult.FailedTransaction.status.error?.message}`,
);
}
// Step 2: Extract the admin cap
const adminCapObject = createResult.Transaction.objectChanges?.find(
(change) => change.type === 'created' && change.objectType.includes('RegistryAdminCap'),
);
const adminCapId = adminCapObject && 'objectId' in adminCapObject ? adminCapObject.objectId : '';
console.log('Registry created!');
console.log('Admin Cap ID:', adminCapId);
// Step 3: Configure the registry
console.log('Configuring registry...');
const configTx = new Transaction();
// Set 60-epoch expiration
configTx.add(
client.paymentKit.calls.setConfigEpochExpirationDuration({
registryName: registryName,
epochExpirationDuration: 60,
adminCapId: adminCapId,
}),
);
// Enable managed funds
configTx.add(
client.paymentKit.calls.setConfigRegistryManagedFunds({
registryName: registryName,
registryManagedFunds: true,
adminCapId: adminCapId,
}),
);
const configResult = await client.signAndExecuteTransaction({
transaction: configTx,
signer: keypair,
});
// Check transaction status
if (configResult.$kind === 'FailedTransaction') {
throw new Error(`Configuration failed: ${configResult.FailedTransaction.status.error?.message}`);
}
console.log('Registry configured!');
console.log('Ready to process payments');
// Step 4: Process a payment
const registryId = getRegistryIdFromName(registryName, namespaceId);
const paymentTx = client.paymentKit.tx.processRegistryPayment({
nonce: crypto.randomUUID(),
coinType: '0x2::sui::SUI',
amount: 1000000000,
receiver: registryId, // Funds go to registry
sender: keypair.getPublicKey().toSuiAddress(),
registryName: registryName,
});
const paymentResult = await client.signAndExecuteTransaction({
transaction: paymentTx,
signer: keypair,
});
// Check transaction status
if (paymentResult.$kind === 'FailedTransaction') {
throw new Error(`Payment failed: ${paymentResult.FailedTransaction.status.error?.message}`);
}
console.log('First payment processed:', paymentResult.Transaction.digest);
```
## Best practices
1. **Store admin caps securely**: Treat your admin capability like private keys - they control your
registry
2. **Use descriptive names**: Choose registry names that clearly identify their purpose (for
example, `acme-subscriptions`, `store-123-payments`)
3. **Set appropriate expiration**: Balance storage costs with verification needs
- Short expiration = lower storage costs
- Long expiration = longer verification period
4. **Monitor registry growth**: Track the number of payment records to anticipate storage costs
5. **Plan fund management**: Decide upfront whether to use registry-managed funds based on your
withdrawal patterns
6. **Document your configuration**: Keep records of your registry settings for operational
consistency
7. **Test thoroughly**: Always test registry operations on Testnet before Mainnet deployment
## Next steps
- [SDK API Reference](./payment-kit-sdk) - Complete SDK API documentation
+3
-3
{
"name": "@mysten/payment-kit",
"version": "0.1.10",
"version": "0.1.11",
"private": false,

@@ -35,6 +35,6 @@ "description": "Sui Payment Kit",

"dependencies": {
"@mysten/bcs": "^2.0.4"
"@mysten/bcs": "^2.0.5"
},
"peerDependencies": {
"@mysten/sui": "^2.16.1"
"@mysten/sui": "^2.16.2"
},

@@ -41,0 +41,0 @@ "scripts": {