Skip to main content

Payment Module API Reference

API reference for dolphinpay::payment (contract/sources/core/payment.move).

The module handles payment creation, execution, and cancellation. All amounts are USDC base units (6 decimals) — 1 USDC = 1_000_000. Every create/execute call validates the coin type against the shared AdminConfig (native Circle USDC only), and the fee rate is read from AdminConfig (0 bps by policy).

Data structures

Payment

public struct Payment has key, store {
id: UID,
merchant: address,
payer: Option<address>,
amount: u64, // USDC base units
currency: TypeName,
created_at: u64, // ms
completed_at: Option<u64>, // ms
expires_at: u64, // ms
status: u8,
metadata: VecMap<String, String>,
description: String,
refundable: bool, // always false (ADR-004)
refund_window: u64, // always 0 (ADR-004)
}

refundable/refund_window are fixed legacy layout fields. There are no on-chain refunds and no function to change them.

Constants

Status codes

const STATUS_PENDING: u8 = 0;
const STATUS_SUCCESS: u8 = 1;
const STATUS_FAILED: u8 = 2; // reserved; not set by current code
const STATUS_EXPIRED: u8 = 3; // declared; current expiry abort rolls it back
const STATUS_CANCELLED: u8 = 4;

Limits

const DEFAULT_EXPIRY_MS: u64 = 3600000; // 1 hour default
const MAX_EXPIRY_SECONDS: u64 = 31536000; // 1 year maximum
const MAX_PAYMENT_AMOUNT: u64 = 1_000_000_000_000; // 1,000,000 USDC
const MAX_DESCRIPTION_LENGTH: u64 = 500;
const MAX_METADATA_ENTRIES: u64 = 10;

There is no fee constant in this module — the fee comes from AdminConfig only.

Error codes

const E_NOT_AUTHORIZED: u64 = 1;
const E_INVALID_AMOUNT: u64 = 100;
const E_PAYMENT_EXPIRED: u64 = 101;
const E_PAYMENT_ALREADY_COMPLETED: u64 = 102;
const E_INVALID_STATUS: u64 = 103;
const E_AMOUNT_MISMATCH: u64 = 104;
const E_CURRENCY_MISMATCH: u64 = 105;
const E_INVALID_MERCHANT: u64 = 106;
const E_INVALID_EXPIRY: u64 = 107;
const E_AMOUNT_TOO_LARGE: u64 = 108;
const E_DESCRIPTION_TOO_LONG: u64 = 109;
const E_METADATA_TOO_LARGE: u64 = 110;
const E_MERCHANT_INACTIVE: u64 = 111;
const E_MERCHANT_MISMATCH: u64 = 112;

Currency-policy aborts (E_CURRENCY_NOT_SET = 103, E_CURRENCY_NOT_ALLOWED = 104) originate in dolphinpay::admin.

Public functions

create_payment

public fun create_payment<T>(
config: &AdminConfig,
merchant: address,
amount: u64,
description: String,
metadata: VecMap<String, String>,
expiry_seconds: u64,
ctx: &mut TxContext
): Payment

Creates a payment in PENDING status. T must be the platform's allowed currency (native Circle USDC); the call aborts otherwise.

Parameters:

  • config — shared AdminConfig (validates the allowed currency)
  • merchant — recipient address, must not be @0x0
  • amount — USDC base units; 0 < amount ≤ 1_000_000_000_000
  • description — max 500 chars
  • metadata — max 10 entries
  • expiry_seconds0 = default 1 hour; max 1 year

Emits: PaymentCreated

Aborts: admin::E_CURRENCY_NOT_SET / E_CURRENCY_NOT_ALLOWED, E_INVALID_MERCHANT, E_INVALID_AMOUNT, E_AMOUNT_TOO_LARGE, E_DESCRIPTION_TOO_LONG, E_METADATA_TOO_LARGE, E_INVALID_EXPIRY

SDK example:

import { usdcToUnits, USDC_TYPES } from '@dolphinpay/sdk';

// 10 USDC = 10_000_000 base units
const txb = client.payment.buildCreatePayment({
merchant: '0xMERCHANT_ADDRESS',
amount: usdcToUnits('10'),
currencyType: USDC_TYPES.testnet,
description: 'Order #12345',
expirySeconds: 3600,
});

create_payment_with_merchant

public fun create_payment_with_merchant<T>(
config: &AdminConfig,
merchant_obj: &Merchant,
amount: u64,
description: String,
metadata: VecMap<String, String>,
expiry_seconds: u64,
ctx: &mut TxContext
): Payment

Same as create_payment, but takes the shared Merchant object, verifies it is active, and uses its owner address as the recipient.

Additional abort: E_MERCHANT_INACTIVE


execute_payment

public entry fun execute_payment<T>(
payment: &mut Payment,
config: &AdminConfig,
mut coin: Coin<T>,
ctx: &mut TxContext
)

Executes a PENDING payment. The coin's value must equal payment.amount exactly and T must match both the allowed currency and the payment's recorded currency.

Effects:

  • Reads the fee rate from AdminConfig (0 bps by policy). If it were non-zero, the fee portion is split off and sent to the treasury — never to the merchant.
  • Transfers the (net) coin to payment.merchant.
  • Sets status to SUCCESS and records payer and completion time — state is updated before any transfer (checks-effects-interactions).
  • If already past expires_at, the function attempts to mark the payment EXPIRED and then aborts. The abort rolls that write back, so the stored payment remains PENDING and no expiry event is persisted.

Emits: PaymentCompleted (with fee_amount and net_amount)

Aborts: admin currency-policy errors, E_PAYMENT_ALREADY_COMPLETED, E_PAYMENT_EXPIRED, E_AMOUNT_MISMATCH, E_CURRENCY_MISMATCH

CLI example:

# Pay 10 USDC with a Coin<USDC> object worth exactly 10_000_000 base units
sui client call \
--package <PACKAGE_ID> --module payment --function execute_payment \
--type-args <USDC_TYPE> \
--args <PAYMENT_ID> <ADMIN_CONFIG_ID> <USDC_COIN_ID>

execute_payment_with_merchant

public entry fun execute_payment_with_merchant<T>(
payment: &mut Payment,
config: &AdminConfig,
merchant_obj: &Merchant,
mut coin: Coin<T>,
ctx: &mut TxContext
)

Same fee behavior as execute_payment (fee read from AdminConfig; merchants have no fee configuration). Additionally:

  • Verifies merchant_obj's owner matches payment.merchant (E_MERCHANT_MISMATCH) and that the merchant is active (E_MERCHANT_INACTIVE).
  • Sends funds to the merchant's currency-specific receiving address if one is registered for T, otherwise to the merchant owner address.

cancel_payment

public entry fun cancel_payment(
payment: &mut Payment,
reason: String,
ctx: &TxContext
)

Cancels a PENDING payment. Only the transaction sender equal to payment.merchant may cancel — no capability object is involved.

Emits: PaymentCancelled

Aborts: E_NOT_AUTHORIZED (sender is not the merchant), E_INVALID_STATUS (payment not PENDING)

Query functions

public fun get_status(payment: &Payment): u8
public fun get_amount(payment: &Payment): u64 // USDC base units
public fun get_merchant(payment: &Payment): address
public fun get_payer(payment: &Payment): Option<address>
public fun get_currency(payment: &Payment): TypeName
public fun get_description(payment: &Payment): String
public fun get_created_at(payment: &Payment): u64 // ms
public fun get_expires_at(payment: &Payment): u64 // ms
public fun is_expired(payment: &Payment, ctx: &TxContext): bool
public fun is_refundable(payment: &Payment): bool // always false
public fun get_metadata(payment: &Payment): &VecMap<String, String>

Fee calculation (internal)

fun calculate_fee_with_bps(amount: u64, fee_bps: u64): u64

fee = amount * fee_bps / 10000, computed in u128 to avoid overflow. With the current policy of 0 bps, the fee is always 0 and the merchant receives the full amount. Non-zero fees (max 1000 bps = 10%) require an admin-set treasury; see the Admin API.

Events

Emitted via dolphinpay::events:

  • PaymentCreated { payment_id, merchant, amount, currency, description, timestamp }
  • PaymentCompleted { payment_id, merchant, payer, amount, currency, fee_amount, net_amount, tx_hash, timestamp }
  • PaymentCancelled { payment_id, reason, timestamp }

PaymentFailed and PaymentExpired are defined in the events module but are not emitted by any current payment flow (expiry is reflected in the payment's status).

Next steps