Payment Operations Guide
Learn how to create, execute, cancel, and query payments using the DolphinPay TypeScript SDK.
Overview
The Payment Module provides comprehensive methods for managing the entire payment lifecycle:
- Create payments with customizable parameters
- Execute payments with automatic coin selection
- Cancel payments when needed
- Query payment data and status
- Dry run testing before actual execution
Creating Payments
Basic Payment Creation
Create a simple payment request:
import { createClient, suiToMist } from '@dolphinpay/sdk';
const client = createClient({
packageId: '0x9c7ca262d020b005e0e6b6a5d083b329d58716e0d80c07b46804324074468f9c',
network: 'testnet',
});
// Build payment creation transaction
const txb = client.payment.buildCreatePayment({
merchant: '0xMERCHANT_ADDRESS',
amount: suiToMist(10), // 10 SUI
currency: 'SUI',
description: 'Premium subscription',
expirySeconds: 3600, // 1 hour
});
// Sign and execute with your wallet
const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
console.log('Payment created:', result.digest);
Payment with Metadata
Add custom metadata to payments for tracking:
const txb = client.payment.buildCreatePayment({
merchant: '0xMERCHANT_ADDRESS',
amount: suiToMist(99.99),
currency: 'SUI',
description: 'Order #12345',
metadata: {
orderId: 'ORD-12345',
customerId: 'CUST-789',
productId: 'PROD-456',
},
expirySeconds: 7200, // 2 hours
});
Metadata is limited to 10 key-value pairs. Use it for essential tracking information only.
Payment with Merchant Validation
Create payment with merchant verification:
const txb = client.payment.buildCreatePaymentWithMerchant({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
amount: suiToMist(50),
currency: 'SUI',
description: 'Service payment',
expirySeconds: 1800, // 30 minutes
});
This method validates:
- Merchant exists and is active
- Currency is supported by merchant
- Merchant fee configuration is valid
Executing Payments
Basic Payment Execution
Execute a pending payment:
import { SUI_TYPES } from '@dolphinpay/sdk';
const txb = client.payment.buildExecutePayment(
{
paymentId: '0xPAYMENT_OBJECT_ID',
coinObjectId: '0xCOIN_OBJECT_ID', // Your SUI coin
},
SUI_TYPES.SUI // Currency type
);
const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
Payment Execution with Merchant
Execute through merchant for custom fee handling:
const txb = client.payment.buildExecutePaymentWithMerchant(
{
paymentId: '0xPAYMENT_OBJECT_ID',
merchantObjectId: '0xMERCHANT_OBJECT_ID',
coinObjectId: '0xCOIN_OBJECT_ID',
},
SUI_TYPES.SUI
);
const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
Automatic Coin Selection
The frontend can automatically select the best coin:
import { useSuiClient } from '@mysten/dapp-kit';
async function selectCoinForPayment(
paymentAmount: bigint,
currentAddress: string
) {
const suiClient = useSuiClient();
// Get all SUI coins for the user
const coins = await suiClient.getCoins({
owner: currentAddress,
coinType: '0x2::sui::SUI',
});
if (coins.data.length === 0) {
throw new Error('No SUI coins found');
}
// Find a coin with sufficient balance
let selectedCoin = coins.data.find(
(coin) => BigInt(coin.balance) >= paymentAmount
);
// If no single coin has enough, use the largest one
if (!selectedCoin) {
selectedCoin = coins.data.reduce((largest, coin) =>
BigInt(coin.balance) > BigInt(largest.balance) ? coin : largest
);
}
return selectedCoin;
}
Cancelling Payments
Cancel a Payment
Merchants can cancel pending payments:
const txb = client.payment.buildCancelPayment({
paymentId: '0xPAYMENT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
});
const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
console.log('Payment cancelled:', result.digest);
Only pending payments can be cancelled. Completed or expired payments cannot be cancelled.
Querying Payment Data
Get Payment Details
Query full payment information:
const payment = await client.payment.getPayment('0xPAYMENT_OBJECT_ID');
console.log('Payment Details:');
console.log(' Amount:', payment.amount);
console.log(' Currency:', payment.currency);
console.log(' Merchant:', payment.merchant);
console.log(' Status:', payment.status);
console.log(' Created:', new Date(Number(payment.created_at)));
console.log(' Expires:', new Date(Number(payment.expiry_time)));
if (payment.payer) {
console.log(' Payer:', payment.payer);
}
Check Payment Status
const status = await client.payment.getPaymentStatus('0xPAYMENT_OBJECT_ID');
// Status values:
// 0 = Pending
// 1 = Completed
// 2 = Failed
// 3 = Expired
// 4 = Cancelled
if (status === 0) {
console.log('Payment is pending');
} else if (status === 1) {
console.log('Payment completed successfully');
}
Check if Payment Expired
const isExpired = await client.payment.isPaymentExpired('0xPAYMENT_OBJECT_ID');
if (isExpired) {
console.log('Payment has expired');
}
Dry Run Testing
Test transactions before execution to catch errors and estimate gas:
Dry Run Payment Creation
const result = await client.payment.dryRunCreatePayment(
{
merchant: '0xMERCHANT_ADDRESS',
amount: suiToMist(10),
currency: 'SUI',
description: 'Test payment',
expirySeconds: 3600,
},
'0xYOUR_ADDRESS' // Sender address for dry run
);
if (result.success) {
console.log('✅ Transaction will succeed!');
console.log('Gas estimate:', result.effects.gasUsed);
console.log('Objects created:', result.objectChanges?.length);
// Now execute the real transaction
const txb = client.payment.buildCreatePayment({
// ... same parameters
});
} else {
console.error('❌ Transaction would fail:', result.error);
}
Dry Run Payment Execution
const result = await client.payment.dryRunExecutePayment(
{
paymentId: '0xPAYMENT_OBJECT_ID',
coinObjectId: '0xCOIN_OBJECT_ID',
},
SUI_TYPES.SUI,
'0xYOUR_ADDRESS'
);
if (result.success) {
console.log('Payment execution will succeed');
console.log('Gas cost:', result.effects.gasUsed);
} else {
console.error('Execution would fail:', result.error);
}
Fee Calculation
Calculate Payment Fees
import { calculateFeeBreakdown } from '@dolphinpay/sdk';
const amount = suiToMist(100); // 100 SUI
// Calculate with platform fee only (0.3%)
const fees = calculateFeeBreakdown(amount);
console.log('Amount:', fees.amount);
console.log('Platform Fee:', fees.platformFee); // 0.3 SUI
console.log('Net Amount:', fees.netAmount); // 99.7 SUI
// Calculate with custom merchant fee
const feesWithMerchant = calculateFeeBreakdown(amount, 50); // +0.5% merchant fee
console.log('Platform Fee:', feesWithMerchant.platformFee); // 0.3 SUI
console.log('Merchant Fee:', feesWithMerchant.merchantFee); // 0.5 SUI
console.log('Total Fee:', feesWithMerchant.totalFee); // 0.8 SUI
console.log('Net Amount:', feesWithMerchant.netAmount); // 99.2 SUI
Error Handling
Common Error Patterns
try {
const txb = client.payment.buildCreatePayment({
merchant: '0xMERCHANT_ADDRESS',
amount: suiToMist(10),
currency: 'SUI',
description: 'Test payment',
expirySeconds: 3600,
});
const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
console.log('Success:', result.digest);
} catch (error) {
if (error.message.includes('Insufficient funds')) {
console.error('Not enough SUI in wallet');
} else if (error.message.includes('E_INVALID_AMOUNT')) {
console.error('Invalid payment amount');
} else if (error.message.includes('E_MERCHANT_INACTIVE')) {
console.error('Merchant is not active');
} else {
console.error('Transaction failed:', error.message);
}
}
Validation Before Submission
import { validatePaymentAmount, validateExpiry } from '@dolphinpay/sdk';
try {
// Validate amount
validatePaymentAmount(suiToMist(10));
// Validate expiry
validateExpiry(3600); // 1 hour
// Create payment if validation passes
const txb = client.payment.buildCreatePayment({
// ... parameters
});
} catch (error) {
console.error('Validation failed:', error.message);
}
Best Practices
1. Always Use Dry Run First
// Test first
const dryRun = await client.payment.dryRunCreatePayment(params, senderAddress);
if (dryRun.success) {
// Execute real transaction
const txb = client.payment.buildCreatePayment(params);
await wallet.signAndExecuteTransactionBlock({ transactionBlock: txb });
}
2. Handle Payment Expiry
// Check expiry before execution
const isExpired = await client.payment.isPaymentExpired(paymentId);
if (isExpired) {
console.error('Payment has expired, create a new one');
return;
}
// Execute payment
const txb = client.payment.buildExecutePayment(/* ... */);
3. Monitor Transaction Status
async function waitForTransaction(digest: string) {
const suiClient = useSuiClient();
let attempts = 0;
const maxAttempts = 10;
while (attempts < maxAttempts) {
try {
const tx = await suiClient.getTransactionBlock({
digest,
options: { showEffects: true },
});
if (tx.effects?.status.status === 'success') {
return tx;
} else if (tx.effects?.status.status === 'failure') {
throw new Error('Transaction failed');
}
} catch (error) {
// Transaction not found yet, wait and retry
await new Promise((resolve) => setTimeout(resolve, 1000));
attempts++;
}
}
throw new Error('Transaction timeout');
}
4. Use Metadata Wisely
// Good: Essential tracking info
const metadata = {
orderId: 'ORD-123',
customerId: 'CUST-456',
};
// Bad: Too much data (limit: 10 entries)
const badMetadata = {
field1: 'value1',
field2: 'value2',
// ... 15 more fields
};
Next Steps
- Merchant Operations - Learn merchant management
- Event Querying - Query payment history
- Basic Payment Example - See complete integration example
API Reference
For complete API documentation, see: