Skip to main content

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
});
tip

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);
warning

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

API Reference

For complete API documentation, see: