Skip to main content

Merchant Module API Reference

Complete API documentation for the dolphinpay::merchant module.

Overview

The Merchant module (merchant.move) handles merchant account management:

  • Merchant registration and onboarding
  • Multi-currency support configuration
  • Receiving address management
  • Fee configuration (admin-controlled)
  • Status management (active/inactive)

Package Location: dolphinpay::merchant

Data Structures

Merchant

Main merchant account object.

public struct Merchant has key, store {
id: UID,
owner: address,
name: String,
description: String,
receiving_addresses: VecMap<TypeName, address>,
fee_config: FeeConfig,
settings: MerchantSettings,
created_at: u64,
is_active: bool,
supported_currencies: VecSet<TypeName>,
}

Fields:

  • id - Unique object identifier
  • owner - Owner's address (receives MerchantCap)
  • name - Merchant name (1-100 characters)
  • description - Merchant description (max 500 characters)
  • receiving_addresses - Map of currency type to receiving address
  • fee_config - Fee configuration (admin-controlled)
  • settings - Merchant settings (webhooks, auto-settlement, etc.)
  • created_at - Registration timestamp (milliseconds)
  • is_active - Whether merchant is active
  • supported_currencies - Set of supported currency types

MerchantCap

Capability object proving merchant ownership.

public struct MerchantCap has key {
id: UID,
merchant_id: ID,
}

Fields:

  • id - Unique object identifier
  • merchant_id - ID of the owned merchant

FeeConfig

Fee configuration (admin-controlled).

public struct FeeConfig has store, copy, drop {
platform_fee_bps: u64, // Platform fee in basis points
}

MerchantSettings

Merchant configuration settings.

public struct MerchantSettings has store {
auto_settlement: bool,
settlement_threshold: u64,
webhook_url: String,
api_keys: vector<String>,
require_confirmation: bool,
default_refund_policy: RefundPolicy,
}

RefundPolicy

Refund policy configuration.

public struct RefundPolicy has store, copy, drop {
enabled: bool,
window_days: u64,
require_approval: bool,
auto_approve_threshold: u64,
}

Constants

Limits

const DEFAULT_PLATFORM_FEE_BPS: u64 = 30;  // 0.3% default platform fee
const MAX_NAME_LENGTH: u64 = 100; // Max merchant name length
const MAX_DESCRIPTION_LENGTH: u64 = 500; // Max description length

Error Codes

const E_NOT_AUTHORIZED: u64 = 1;
const E_INVALID_FEE: u64 = 200;
const E_MERCHANT_NOT_FOUND: u64 = 201;
const E_MERCHANT_INACTIVE: u64 = 202;
const E_INVALID_NAME: u64 = 203;
const E_FEE_TOO_HIGH: u64 = 204;
const E_CURRENCY_ALREADY_SUPPORTED: u64 = 205;
const E_CURRENCY_NOT_SUPPORTED: u64 = 206;
const E_INVALID_ADDRESS: u64 = 207;

Public Functions

register_merchant

Registers a new merchant account.

public entry fun register_merchant(
name: String,
description: String,
ctx: &mut TxContext
)

Parameters:

  • name - Merchant name (1-100 characters)
  • description - Merchant description (max 500 characters)
  • ctx - Transaction context

Effects:

  • Creates and shares a Merchant object
  • Creates and transfers MerchantCap to sender
  • Merchant starts in active state
  • Default fee configuration applied

Emits:

  • MerchantRegistered event

Errors:

  • E_INVALID_NAME - Name is empty, too long, or description too long

Example Usage:

// Using SDK
const txb = client.merchant.buildRegisterMerchant({
name: 'Acme Store',
description: 'Premium digital goods marketplace',
});

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

update_merchant_info

Updates merchant name and description.

public entry fun update_merchant_info(
merchant: &mut Merchant,
cap: &MerchantCap,
name: String,
description: String,
ctx: &mut TxContext
)

Parameters:

  • merchant - Mutable reference to merchant object
  • cap - Merchant capability (proves ownership)
  • name - New merchant name
  • description - New description
  • ctx - Transaction context

Effects:

  • Updates merchant name and description

Emits:

  • MerchantSettingsUpdated event

Errors:

  • E_NOT_AUTHORIZED - Cap doesn't match merchant
  • E_INVALID_NAME - Name validation failed

add_supported_currency

Adds a supported currency with receiving address.

public entry fun add_supported_currency(
merchant: &mut Merchant,
cap: &MerchantCap,
currency: TypeName,
receiving_address: address,
ctx: &mut TxContext
)

Parameters:

  • merchant - Mutable reference to merchant
  • cap - Merchant capability
  • currency - Currency type to add (e.g., 0x2::sui::SUI)
  • receiving_address - Address to receive payments in this currency
  • ctx - Transaction context

Effects:

  • Adds currency to supported currencies set
  • Maps currency to receiving address

Emits:

  • MerchantSettingsUpdated event

Errors:

  • E_NOT_AUTHORIZED - Cap doesn't match merchant
  • E_CURRENCY_ALREADY_SUPPORTED - Currency already added
  • E_INVALID_ADDRESS - Receiving address is 0x0

Example:

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

remove_supported_currency

Removes a supported currency.

public entry fun remove_supported_currency(
merchant: &mut Merchant,
cap: &MerchantCap,
currency: TypeName,
ctx: &mut TxContext
)

Parameters:

  • merchant - Mutable reference to merchant
  • cap - Merchant capability
  • currency - Currency type to remove
  • ctx - Transaction context

Effects:

  • Removes currency from supported currencies
  • Removes receiving address mapping

Emits:

  • MerchantSettingsUpdated event

Errors:

  • E_NOT_AUTHORIZED - Cap doesn't match merchant
  • E_CURRENCY_NOT_SUPPORTED - Currency not in supported list

set_receiving_address

Updates receiving address for a currency.

public entry fun set_receiving_address(
merchant: &mut Merchant,
cap: &MerchantCap,
currency: TypeName,
new_address: address,
ctx: &mut TxContext
)

Parameters:

  • merchant - Mutable reference to merchant
  • cap - Merchant capability
  • currency - Currency type to update
  • new_address - New receiving address
  • ctx - Transaction context

Effects:

  • Updates receiving address for specified currency

Emits:

  • MerchantSettingsUpdated event

Errors:

  • E_NOT_AUTHORIZED - Cap doesn't match merchant
  • E_CURRENCY_NOT_SUPPORTED - Currency not supported
  • E_INVALID_ADDRESS - New address is 0x0

Example:

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

toggle_merchant_status

Toggles merchant active status.

public entry fun toggle_merchant_status(
merchant: &mut Merchant,
cap: &MerchantCap,
ctx: &mut TxContext
)

Parameters:

  • merchant - Mutable reference to merchant
  • cap - Merchant capability
  • ctx - Transaction context

Effects:

  • Toggles is_active field (true ↔ false)
  • Inactive merchants cannot receive new payments

Emits:

  • MerchantStatusChanged event

Errors:

  • E_NOT_AUTHORIZED - Cap doesn't match merchant

Query Functions

is_active

Checks if merchant is active.

public fun is_active(merchant: &Merchant): bool

Returns:

  • bool - true if merchant is active

get_owner

Returns merchant owner address.

public fun get_owner(merchant: &Merchant): address

Returns:

  • address - Owner's address

get_name

Returns merchant name.

public fun get_name(merchant: &Merchant): String

Returns:

  • String - Merchant name

get_description

Returns merchant description.

public fun get_description(merchant: &Merchant): String

Returns:

  • String - Merchant description

get_receiving_address

Gets receiving address for a currency.

public fun get_receiving_address(
merchant: &Merchant,
currency: TypeName
): Option<address>

Parameters:

  • merchant - Reference to merchant
  • currency - Currency type to query

Returns:

  • Option<address> - Receiving address (Some if currency supported)

supports_currency

Checks if merchant supports a currency.

public fun supports_currency(
merchant: &Merchant,
currency: TypeName
): bool

Parameters:

  • merchant - Reference to merchant
  • currency - Currency type to check

Returns:

  • bool - true if currency is supported

get_fee_config

Returns merchant's fee configuration.

public fun get_fee_config(merchant: &Merchant): &FeeConfig

Returns:

  • &FeeConfig - Reference to fee configuration

get_platform_fee_bps

Gets platform fee in basis points.

public fun get_platform_fee_bps(merchant: &Merchant): u64

Returns:

  • u64 - Platform fee in basis points (default: 30 = 0.3%)

Admin Functions

These functions are only callable by admin (with AdminCap).

set_merchant_fee

Sets merchant-specific fee (admin only).

public entry fun set_merchant_fee(
merchant: &mut Merchant,
admin_cap: &AdminCap,
platform_fee_bps: u64,
ctx: &mut TxContext
)

Parameters:

  • merchant - Mutable reference to merchant
  • admin_cap - Admin capability
  • platform_fee_bps - New platform fee in basis points
  • ctx - Transaction context

Effects:

  • Updates merchant's platform fee
  • Allows custom fees per merchant

Note: Only admin can modify fees. Merchants cannot set their own fees.


Events

MerchantRegistered

Emitted when a merchant is registered.

public struct MerchantRegistered has copy, drop {
merchant_id: ID,
owner: address,
name: String,
timestamp: u64,
}

MerchantSettingsUpdated

Emitted when merchant settings are updated.

public struct MerchantSettingsUpdated has copy, drop {
merchant_id: ID,
updated_by: address,
timestamp: u64,
}

MerchantStatusChanged

Emitted when merchant status changes.

public struct MerchantStatusChanged has copy, drop {
merchant_id: ID,
is_active: bool,
changed_by: address,
timestamp: u64,
}

Usage Examples

Complete Merchant Setup

// 1. Register merchant
merchant::register_merchant(
string::utf8(b"Acme Store"),
string::utf8(b"Premium digital goods"),
ctx
);

// 2. Add supported currencies
merchant::add_supported_currency(
&mut merchant,
&merchant_cap,
type_name::get<SUI>(),
wallet_address,
ctx
);

merchant::add_supported_currency(
&mut merchant,
&merchant_cap,
type_name::get<USDC>(),
usdc_wallet_address,
ctx
);

// 3. Verify setup
assert!(merchant::is_active(&merchant), 0);
assert!(merchant::supports_currency(&merchant, type_name::get<SUI>()), 1);

Currency Management

// Add new currency
merchant::add_supported_currency(
&mut merchant,
&cap,
currency_type,
receiving_addr,
ctx
);

// Update receiving address
merchant::set_receiving_address(
&mut merchant,
&cap,
currency_type,
new_addr,
ctx
);

// Remove currency
merchant::remove_supported_currency(
&mut merchant,
&cap,
currency_type,
ctx
);

Status Management

// Deactivate merchant (e.g., for maintenance)
merchant::toggle_merchant_status(&mut merchant, &cap, ctx);
assert!(!merchant::is_active(&merchant), 0);

// Reactivate merchant
merchant::toggle_merchant_status(&mut merchant, &cap, ctx);
assert!(merchant::is_active(&merchant), 0);

Security Considerations

Capability-Based Authorization

All merchant operations require MerchantCap:

// MerchantCap proves ownership
public struct MerchantCap has key {
merchant_id: ID, // Links to specific merchant
}

// Operations verify cap matches merchant
assert!(object::id(merchant) == cap.merchant_id, E_NOT_AUTHORIZED);

Fee Control

  • ✅ Fees are admin-controlled only
  • ✅ Merchants cannot set their own fees
  • ✅ Prevents fee manipulation
  • ✅ Platform maintains fee consistency

Address Validation

All receiving addresses are validated:

  • ✅ Cannot be 0x0
  • ✅ Checked before adding currency
  • ✅ Verified on address updates

Best Practices

1. Store MerchantCap Securely

// After registration
const merchantCap = extractObjectId(result, 'MerchantCap');

// Store securely (database, environment variable, etc.)
process.env.MERCHANT_CAP_ID = merchantCap;

// Never share or expose publicly

2. Validate Before Operations

// Check merchant status before accepting payments
const isActive = await client.merchant.isMerchantActive(merchantId);
if (!isActive) {
throw new Error('Merchant is inactive');
}

// Verify currency support
const merchant = await client.merchant.getMerchant(merchantId);
if (!merchant.supported_currencies.includes(currencyType)) {
throw new Error('Currency not supported');
}

3. Handle Multiple Currencies

// Add all currencies at setup
const currencies = [
{ type: '0x2::sui::SUI', address: suiWallet },
{ type: '0x...::usdc::USDC', address: usdcWallet },
{ type: '0x...::usdt::USDT', address: usdtWallet },
];

for (const currency of currencies) {
const txb = client.merchant.buildAddSupportedCurrency({
merchantObjectId: merchantId,
merchantCapId: capId,
currencyType: currency.type,
receivingAddress: currency.address,
});

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

4. Use Separate Wallets

// Recommended: Use different wallets for different currencies
const wallets = {
SUI: '0xSUI_WALLET_ADDRESS',
USDC: '0xUSDC_WALLET_ADDRESS',
USDT: '0xUSDT_WALLET_ADDRESS',
};

// Better security and accounting

Architecture Notes

Admin-Controlled Fees

┌─────────────────────────────────────┐
│ Fee Management Architecture │
├─────────────────────────────────────┤
│ │
│ Admin (AdminCap) │
│ │ │
│ ├─► Set Platform Fee │
│ │ (Global Default) │
│ │ │
│ └─► Set Merchant Fee │
│ (Per-Merchant Override) │
│ │
│ Merchant (MerchantCap) │
│ │ │
│ ├─► Read Fee Config ✓ │
│ └─► Modify Fee ✗ (Not Allowed) │
│ │
└─────────────────────────────────────┘

For details, see the Core Modules documentation.

Next Steps