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— sharedAdminConfig(validates the allowed currency)merchant— recipient address, must not be@0x0amount— USDC base units;0 < amount ≤ 1_000_000_000_000description— max 500 charsmetadata— max 10 entriesexpiry_seconds—0= 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 matchespayment.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).