Skip to main content

Merchant Operations Guide

Learn how to register merchants, manage settings, configure currencies, and query merchant data using the DolphinPay TypeScript SDK.

Overview

The Merchant Module provides methods for:

  • Merchant registration and onboarding
  • Currency management (add/remove supported currencies)
  • Receiving address configuration per currency
  • Status management (activate/deactivate)
  • Query operations for merchant data
  • Dry run testing for all operations

Merchant Registration

Register a New Merchant

import { createClient } from '@dolphinpay/sdk';

const client = createClient({
packageId: '0x9c7ca262d020b005e0e6b6a5d083b329d58716e0d80c07b46804324074468f9c',
network: 'testnet',
});

// Build merchant registration transaction
const txb = client.merchant.buildRegisterMerchant({
name: 'My Online Store',
description: 'E-commerce platform for digital goods',
});

// Execute transaction
const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});

console.log('Merchant registered:', result.digest);

After registration, you'll receive:

  • Merchant Object: The merchant account on-chain
  • MerchantCap: Capability object for managing the merchant

Extract Merchant ID

// Get transaction details
const tx = await suiClient.getTransactionBlock({
digest: result.digest,
options: { showObjectChanges: true },
});

// Find created objects
const createdObjects = tx.objectChanges?.filter(
(change) => change.type === 'created'
);

// Merchant object
const merchantObject = createdObjects?.find((obj) =>
obj.objectType.includes('::merchant::Merchant')
);

// MerchantCap object
const merchantCap = createdObjects?.find((obj) =>
obj.objectType.includes('::merchant::MerchantCap')
);

console.log('Merchant ID:', merchantObject?.objectId);
console.log('MerchantCap ID:', merchantCap?.objectId);

Currency Management

Add Supported Currency

Add a currency that your merchant accepts:

const txb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0x2::sui::SUI', // SUI token
receivingAddress: '0xYOUR_WALLET_ADDRESS',
});

const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});

Add Multiple Currencies

// Add USDC
const usdcTxb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0xUSDC_TOKEN_TYPE',
receivingAddress: '0xYOUR_USDC_ADDRESS',
});

await wallet.signAndExecuteTransactionBlock({ transactionBlock: usdcTxb });

// Add USDT
const usdtTxb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0xUSDT_TOKEN_TYPE',
receivingAddress: '0xYOUR_USDT_ADDRESS',
});

await wallet.signAndExecuteTransactionBlock({ transactionBlock: usdtTxb });

Remove Supported Currency

const txb = client.merchant.buildRemoveSupportedCurrency({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0xUSDC_TOKEN_TYPE',
});

const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});

Update Receiving Address

Change the receiving address for a specific currency:

const txb = client.merchant.buildSetReceivingAddress({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0x2::sui::SUI',
newAddress: '0xNEW_WALLET_ADDRESS',
});

const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});

Merchant Information Management

Update Merchant Info

Update merchant name and description:

const txb = client.merchant.buildUpdateMerchantInfo({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
name: 'My Updated Store Name',
description: 'Updated description with new services',
});

const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});

Toggle Merchant Status

Activate or deactivate your merchant account:

// Deactivate merchant (e.g., for maintenance)
const txb = client.merchant.buildToggleMerchantStatus({
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
});

const result = await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});

// Status is toggled (active -> inactive, or inactive -> active)

Querying Merchant Data

Get Merchant Details

const merchant = await client.merchant.getMerchant('0xMERCHANT_OBJECT_ID');

console.log('Merchant Details:');
console.log(' Name:', merchant.name);
console.log(' Description:', merchant.description);
console.log(' Owner:', merchant.owner);
console.log(' Active:', merchant.is_active);
console.log(' Created:', new Date(Number(merchant.created_at)));

Check Merchant Status

const isActive = await client.merchant.isMerchantActive('0xMERCHANT_OBJECT_ID');

if (isActive) {
console.log('Merchant is active and can accept payments');
} else {
console.log('Merchant is inactive');
}

Get Merchant Fee Configuration

const feeConfig = await client.merchant.getMerchantFeeConfig(
'0xMERCHANT_OBJECT_ID'
);

console.log('Fee Configuration:');
console.log(' Platform Fee (BPS):', feeConfig.platform_fee_bps); // Default: 30 (0.3%)
console.log(' Custom Fee Enabled:', feeConfig.custom_fee_enabled);

if (feeConfig.custom_fee_enabled) {
console.log(' Custom Fee (BPS):', feeConfig.custom_fee_bps);
console.log(' Min Fee:', feeConfig.min_fee);
console.log(' Max Fee:', feeConfig.max_fee);
}

Dry Run Testing

Test Merchant Registration

const result = await client.merchant.dryRunRegisterMerchant(
{
name: 'Test Store',
description: 'Testing registration',
},
'0xYOUR_ADDRESS'
);

if (result.success) {
console.log('✅ Registration will succeed');
console.log('Gas estimate:', result.effects.gasUsed);
console.log('Objects to be created:', result.objectChanges?.length);

// Proceed with actual registration
const txb = client.merchant.buildRegisterMerchant({
name: 'Test Store',
description: 'Testing registration',
});
} else {
console.error('❌ Registration would fail:', result.error);
}

Test Currency Operations

// Test adding currency
const addResult = await client.merchant.dryRunAddSupportedCurrency(
{
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0x2::sui::SUI',
receivingAddress: '0xWALLET_ADDRESS',
},
'0xYOUR_ADDRESS'
);

if (addResult.success) {
console.log('Currency addition will succeed');
}

// Test removing currency
const removeResult = await client.merchant.dryRunRemoveSupportedCurrency(
{
merchantObjectId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0xUSDC_TYPE',
},
'0xYOUR_ADDRESS'
);

Complete Merchant Setup Example

Here's a complete example of setting up a new merchant:

import { createClient } from '@dolphinpay/sdk';

async function setupMerchant(wallet: any) {
const client = createClient({
packageId: '0x9c7ca262d020b005e0e6b6a5d083b329d58716e0d80c07b46804324074468f9c',
network: 'testnet',
});

// Step 1: Register merchant
console.log('Step 1: Registering merchant...');
const registerTxb = client.merchant.buildRegisterMerchant({
name: 'Acme Online Store',
description: 'Premium digital goods marketplace',
});

const registerResult = await wallet.signAndExecuteTransactionBlock({
transactionBlock: registerTxb,
});

// Step 2: Extract merchant IDs
console.log('Step 2: Extracting merchant IDs...');
const tx = await suiClient.getTransactionBlock({
digest: registerResult.digest,
options: { showObjectChanges: true },
});

const createdObjects = tx.objectChanges?.filter(
(change) => change.type === 'created'
);

const merchantObject = createdObjects?.find((obj) =>
obj.objectType.includes('::merchant::Merchant')
);

const merchantCap = createdObjects?.find((obj) =>
obj.objectType.includes('::merchant::MerchantCap')
);

const merchantId = merchantObject?.objectId;
const merchantCapId = merchantCap?.objectId;

console.log('Merchant ID:', merchantId);
console.log('MerchantCap ID:', merchantCapId);

// Step 3: Add supported currencies
console.log('Step 3: Adding supported currencies...');

// Add SUI
const suiTxb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: merchantId!,
merchantCapId: merchantCapId!,
currencyType: '0x2::sui::SUI',
receivingAddress: wallet.address,
});

await wallet.signAndExecuteTransactionBlock({ transactionBlock: suiTxb });
console.log(' ✓ SUI added');

// Add USDC (example)
const usdcTxb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: merchantId!,
merchantCapId: merchantCapId!,
currencyType: '0xUSDC_TYPE',
receivingAddress: wallet.address,
});

await wallet.signAndExecuteTransactionBlock({ transactionBlock: usdcTxb });
console.log(' ✓ USDC added');

// Step 4: Verify setup
console.log('Step 4: Verifying merchant setup...');
const merchant = await client.merchant.getMerchant(merchantId!);
const isActive = await client.merchant.isMerchantActive(merchantId!);

console.log('Merchant setup complete!');
console.log(' Name:', merchant.name);
console.log(' Active:', isActive);

return {
merchantId,
merchantCapId,
merchant,
};
}

Validation and Error Handling

Input Validation

import { validateMerchantName, validateDescription } from '@dolphinpay/sdk';

try {
// Validate merchant name (1-100 chars)
validateMerchantName('My Store');

// Validate description (max 500 chars)
validateDescription('A description of my store');

// Proceed with registration
const txb = client.merchant.buildRegisterMerchant({
name: 'My Store',
description: 'A description of my store',
});
} catch (error) {
console.error('Validation error:', error.message);
}

Common Errors

try {
const txb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: '0xMERCHANT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
currencyType: '0x2::sui::SUI',
receivingAddress: '0xWALLET',
});

await wallet.signAndExecuteTransactionBlock({ transactionBlock: txb });
} catch (error) {
if (error.message.includes('E_CURRENCY_ALREADY_SUPPORTED')) {
console.error('Currency is already supported');
} else if (error.message.includes('E_INVALID_ADDRESS')) {
console.error('Invalid receiving address');
} else if (error.message.includes('E_NOT_MERCHANT_OWNER')) {
console.error('Not authorized to modify this merchant');
} else {
console.error('Transaction failed:', error.message);
}
}

Best Practices

1. Store Merchant IDs Securely

// After registration, save merchant IDs for future use
const merchantConfig = {
merchantId: '0xMERCHANT_OBJECT_ID',
merchantCapId: '0xMERCHANT_CAP_ID',
createdAt: Date.now(),
};

// Save to database or local storage
localStorage.setItem('merchantConfig', JSON.stringify(merchantConfig));

2. Verify Before Currency Operations

async function addCurrencySafely(
merchantId: string,
merchantCapId: string,
currencyType: string,
receivingAddress: string
) {
// Check if currency is already supported
const merchant = await client.merchant.getMerchant(merchantId);

if (merchant.supported_currencies?.includes(currencyType)) {
console.log('Currency already supported, updating address instead');

const txb = client.merchant.buildSetReceivingAddress({
merchantObjectId: merchantId,
merchantCapId,
currencyType,
newAddress: receivingAddress,
});

return await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
}

// Add new currency
const txb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: merchantId,
merchantCapId,
currencyType,
receivingAddress,
});

return await wallet.signAndExecuteTransactionBlock({ transactionBlock: txb });
}

3. Test with Dry Run

// Always test before executing
async function safeExecute(txBuilder: () => Transaction, sender: string) {
const dryRun = await client.dryRunTransaction(txBuilder(), sender);

if (dryRun.success) {
const txb = txBuilder();
return await wallet.signAndExecuteTransactionBlock({
transactionBlock: txb,
});
} else {
throw new Error(`Dry run failed: ${dryRun.error}`);
}
}

4. Handle MerchantCap Carefully

// MerchantCap is required for all merchant operations
// Never share or lose this object ID

// Store securely
const MERCHANT_CAP_ID = process.env.MERCHANT_CAP_ID;

// Verify ownership before operations
async function verifyMerchantOwnership(
merchantCapId: string,
expectedOwner: string
) {
const obj = await suiClient.getObject({
id: merchantCapId,
options: { showOwner: true },
});

const owner = (obj.data?.owner as any)?.AddressOwner;

if (owner !== expectedOwner) {
throw new Error('Not the merchant owner');
}
}

Next Steps

API Reference

For complete API documentation, see: