Payment Kit 표준
Sui Payment Kit은 Sui에서 안전하고 유연한 payment processing을 위한 framework이다. 이는 persistent 및 ephemeral payment option, event-driven architecture, 그리고 built-in duplicate prevention을 제공한다.
Payment Kit은 Sui에서 payment processing을 표준화하여, developer가 common payment verification 및 receipt management 로직을 다시 구현하지 않고도 견고한 payment flow를 구축할 수 있게 한다. Payment Kit을 사용하는 application은 battle-tested security pattern과 ecosystem 전반에서 일관된 payment handling의 이점을 누린다.
Key features
Payment Kit은 다음의 핵심 capability를 제공한다:
- Secure payment processing: payment amount를 검증하고 coin을 안전하게 전송한다.
- Payment registries: duplicate detection이 포함된 payment receipt를 위한 optional persistent storage이다.
- Flexible receipt management: payment tracking 및 verification을 위한 receipt를 생성한다.
- Event-driven architecture: off-chain tracking 및 integration을 위한 event를 emit한다.
- Multi-coin support: 어떤 Sui coin type과도 함께 동작한다.
- Transaction URIs: user friendly payment flow를 위한 encoded link를 생성할 수 있도록 표준화된 URI format이다.
Architecture components
Payment Kit은 다음의 주요 component로 구성된다:
Payment processing core
coin transfer, payment validation, 그리고 receipt generation을 처리한다. core는 다음을 검증한다:
- payment amount가 expected value와 일치한다
- coin이 충분한 balance를 가진다
- transfer가 성공적으로 완료된다
- receipt에 정확한 payment 정보가 포함된다
Registry system
payment history를 추적하고 duplicate payment를 방지하는 optional persistent storage이다. registry는 다음을 제공한다:
- composite key를 사용한 payment record storage
- payment record를 위한 configurable expiration policy
- 누적된 fund에 대한 withdrawal capability
- capability를 통한 administrative control
Core concepts
Payment modes
Payment Kit은 두 가 지 payment processing mode를 지원한다:
1. Registry payments: duplicate prevention과 persistent receipt가 포함된 PaymentRegistry를 통해 payment를 처리한다. 다음의 경우 이 mode를 사용한다:
- duplicate payment를 방지해야 한다
- payment history를 추적해야 한다
- compliance 또는 auditing에 payment record가 필요하다
- fund가 관리되는 registry에 누적되어야 한다
2. Ephemeral payments: persistent storage 없이 one-time payment를 처리한다. 다음의 경우 이 mode를 사용한다:
- duplicate prevention이 강제되지 않는다
Duplicate prevention
duplicate prevention은 PaymentRegistry를 통해 payment를 처리할 때 강제된다. 시스템은 다음으로부터 derived된 composite PaymentKey를 사용한다:
- Nonce: 각 payment에 대한 unique identifier이다
(UUIDv4). - Amount: coin unit으로 나타낸 payment value이다.
- Coin type: 특정 coin type이다.
- Receiver address: payment destination address이다.
이 composite key는 amount 또는 receiver와 같은 개별 구성 요소가 서로 다른 payment에서 재사용되더라도, 동일한 payment가 두 번 처리될 수 없도록 보장한다.
Payment receipts
처리된 모든 payment는 다음을 포함하는 PaymentReceipt object를 생성한다:
- reference를 위한 payment nonce
- 지급된 amount
- 사용된 coin type
- receiver address
- payment timestamp
- registry 정보(registry payment에만 해당하며 ephemeral payment에는 적용되지 않는다)
receipt는 payment proof 역할을 하며, off-chain verification, accounting, 또는 다른 시스템과의 integration에 사용할 수 있다.
Payment records
payment가 PaymentRegistry를 통해 처리되면, 시스템은 payment history를 추적하고 duplicate prevention을 가능하게 하기 위해 내부적으로 PaymentRecord object를 생성하고 저장한다.
payment record는 핵심적인 측면에서 payment receipt와 다르다:
PaymentRecords vs PaymentReceipts:
PaymentRecords: duplicate detection을 위해 payment metadata를 지속하는 internal registry storage structure이다. 이는 registry의 internal table에 저장되며 object로 직접 접근할 수 없다.PaymentReceipts: payment processing 후 반환되는 user-facing object로, payment proof 역할을 한다. 이는 저장, 전송, 또는 off-chain verification에 사용할 수 있다.
PaymentRecord lifecycle:
- Creation:
process_registry_payment가 호출되면, compositePaymentKey를 사용해 registry에PaymentRecord가 생성되고 저장된다. - Storage: record는 unique payment key로 index된 registry internal table에 지속된다.
- Expiration: record는 registry에 구성된
epoch_expiration_duration이 지난 후 deletion 대상이 된다. - Deletion: expired record는
delete_payment_record를 사용해 제거하여 storage를 회수하고 gas cost를 줄일 수 있다.
PaymentRecord expiration:
PaymentRecord에는 payment 생성 시점에 계산된 expiration epoch가 포함된다. 이 expiration 메커니즘은 다음을 가능하게 한다:
- registry에서 storage가 무기한 증가하는 것을 방지한다
- 과거 payment 데이터의 eventual cleanup을 허용한다
- storage 효율성과 duplicate prevention 요구 사이의 균형을 맞춘다
set_config_epoch_expiration_duration을 통해 registry별로 구성할 수 있다
PaymentRecord는 expiration epoch 이전에는 삭제할 수 없어, duplicate detection을 위한 최소 retention period를 보장한다. expiration 이후에는 administrator 또는 user가 storage를 해제하기 위해 record를 삭제할 수 있지만, 삭제는 optional이다.
Working with payment registries
Creating a registry
새 payment registry를 생성하려면, registry address를 derive하는 데 사용되는 ASCII-based string인 name을 제공해야 한다. name 외에도 package Namespace object도 제공해야 한다. Namespace는 여러 payment registry를 관리하기 위한 higher-order organizational structure를 제공한다.
module sui::payment_kit;
public fun create_registry(
namespace: &mut Namespace,
name: String,
ctx: &mut TxContext
)
이 함수는 administrative control을 위한 PaymentRegistry와 RegistryAdminCap을 생성한다. RegistryAdminCap은 초기에 creator가 소유하며, 필요에 따라 공유하거나 전송할 수 있다.
Namespace objects
mainnet: 0xccd3e4c7802921991cd9ce488c4ca0b51334ba75483702744242284ccf3ae7c2
testnet: 0xa5016862fdccba7cc576b56cc5a391eda6775200aaa03a6b3c97d512312878db
Processing registry payments
duplicate prevention이 포함된 registry를 통해 payment를 처리한다:
module sui::payment_kit;
public fun process_registry_payment<T>(
registry: &mut PaymentRegistry,
nonce: String,
payment_amount: u64,
coin: Coin<T>,
receiver: Option<address>,
clock: &Clock,
ctx: &mut TxContext
)
Parameters:
registry: payment registry에 대한 mutable reference이다.nonce: unique payment identifier이다(duplicate를 방지한다).payment_amount: coin unit으로 나타낸 expected payment amount이다.coin: payment coin object이다.receiver: optional receiver address이다(None이면 fund는 registry에 남는다).clock: timestamping을 위한 Sui clock object이다.ctx: transaction context이다.
이 함수는 다음을 수행한다:
- payment amount가 coin value와 일치하는지 검증한다
- composite key를 사용해 duplicate payment를 확인한다
- registry에 payment를 기록한다
- configuration에 따라
receiver또는 registry로 fund를 전송한다 PaymentReceipt를 생성하고 반환한다- payment event를 emit한다
Error conditions:
EDuplicatePayment: 동일한 composite key를 가진 payment가 이미 존재한다.EPaymentAmountMismatch: coin value가 expected amount와 일치하지 않는다.
Managing payment records
storage를 회수하기 위해 expired PaymentRecord를 삭제한다:
module sui::payment_kit;
public fun delete_payment_record<T>(
registry: &mut PaymentRegistry,
payment_key: PaymentKey<T>,
ctx: &mut TxContext
)
record는 registry에 구성된 expiration duration에 따라 expire된 이후에만 삭제할 수 있다. 원래 payment parameter로 create_payment_key 함수를 사용해 PaymentKey를 생성한다.
Configuring registries
registry administrator는 RegistryAdminCap을 사용해 configuration setting을 업데이트할 수 있 다:
Set expiration duration:
module sui::payment_kit;
public fun set_config_epoch_expiration_duration(
registry: &mut PaymentRegistry,
cap: &RegistryAdminCap,
epoch_expiration_duration: u64,
ctx: &mut TxContext
)
Set PaymentRegistry to receive funds:
module sui::payment_kit;
public fun set_config_registry_managed_funds(
registry: &mut PaymentRegistry,
cap: &RegistryAdminCap,
registry_managed_funds: bool,
ctx: &mut TxContext
)
registry_managed_funds가 true이면, payment는 나중에 withdraw하기 위해 registry에 누적된다. false이면, payment는 receiver에게 즉시 전송된다.
Withdrawing from a registry
PaymentRegistry가 fund를 관리하도록 설정된 경우, administrator는 누적된 fund를 인출할 수 있다:
module sui::payment_kit;
public fun withdraw_from_registry<T>(
registry: &mut PaymentRegistry,
cap: &RegistryAdminCap,
ctx: &mut TxContext
)
이 함수는 RegistryAdminCap을 요구하며, registry에서 타입 T의 누적된 모든 coin을 반환한다. registry가 fund를 유지하도록 구성된 경우에만 이를 사용한다(registry_managed_funds configuration setting이 이를 제어한다).
Processing ephemeral payments
duplicate prevention 또는 persistent record가 필요하지 않은 시나리오에서는 ephemeral payment를 사용한다:
module sui::payment_kit;
public fun process_ephemeral_payment<T>(
nonce: String,
payment_amount: u64,
coin: Coin<T>,
receiver: address,
clock: &Clock,
ctx: &mut TxContext
)
ephemeral payment는 다음과 같다:
- duplicate를 확인하지 않는다
- on-chain에 payment record를 저장하지 않는다
- fund를 receiver로 즉시 전송한다
- transaction에 대한 receipt를 생성한다
- off-chain tracking을 위한 payment event를 emit한다
- registry payment보다 gas cost가 낮다
이 mode는 다음의 경우에 적합하다:
- duplicate prevention이 필요하지 않다
- application이 external payment tracking system을 사용한다
Transaction URIs
Payment Kit은 payment request를 encoding하기 위한 표준 URI format을 정의한다. Transaction URI를 사용하면 application이 wallet과 다른 client가 parse하고 실행할 수 있는 payment link를 생성할 수 있다.
URI format
Payment Kit Transaction URI는 다음 format을 사용한다:
sui:pay?receiver=<receiverAddress>
&amount=<amount>
&coinType=<coinType>
&nonce=<nonce>
&label=<label>
&iconUrl=<iconUrl>
&message=<message>
®istry=<registry>
URI parameters
receiver
payment fund를 수신하는 destination address이다. 유효한 Sui address여야 한다.
amount
amount field는 required query parameter이다. value는 positive u64여야 한다. 이 field는 지정된 coin type의 native amount를 나타낸다(예를 들어, 100000000 MISTS는 0.1 SUI를 나타낸다).
nonce
nonce field는 required query parameter이다. 이는 이 payment에 대한 unique ASCII-based identifier를 나타낸다. duplicate payment를 방지하기 위해 registry 내에서 unique해야 한다. 권장 format은 UUIDv4이다. 36자를 초과할 수 없다.
coinType
coinType field는 required query parameter이다. 전송할 coin의 full type identifier이다(예를 들어, 0x2::sui::SUI 또는 0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI).
label
payment를 수신하는 merchant 또는 application에 대한 human-readable name이다.
iconUrl
merchant 또는 application을 위한 icon image를 가리키는 URL이다. payment confirmation 중 user에게 표시될 수 있다.
message
payment에 대한 description 또는 context이다. purpose를 설명하기 위해 user에게 표시될 수 있다.
registry
payment processing에 사용할 PaymentRegistry의 object ID 또는 ASCII-represented name이다. 제공되면, payment는 duplicate prevention이 포함된 registry를 통해 처리된다. 생략되면, payment는 ephemeral payment로 처리된다.
Example URIs
Basic SUI payment:
sui:pay?receiver=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
&amount=1000000000
&coinType=0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI
&nonce=550e8400-e29b-41d4-a716-446655440000
Payment with display metadata:
sui:pay?receiver=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
&amount=1000000000
&coinType=0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI
&nonce=550e8400-e29b-41d4-a716-446655440000
&label=Coffee%20Shop
&iconUrl=https://example.com/icon.png
&message=Espresso%20and%20croissant
Registry-based payment (registry object ID):
sui:pay?receiver=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
&amount=1000000000
&coinType=0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI
&nonce=550e8400-e29b-41d4-a716-446655440000
®istry=0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
Registry-based payment (registry ASCII name):
sui:pay?receiver=0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
&amount=1000000000
&coinType=0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI
&nonce=550e8400-e29b-41d4-a716-446655440000
®istry=default-payment-registry
URI encoding
모든 parameter value는 RFC 3986에 따라 적절히 URL-encoded되어야 한다. parameter value의 special character(예: space, colon, slash)는 percent-encoded되어야 한다. 예시는 다음과 같다:
- Space:
%20 - Colon:
%3A - Slash:
%2F - Double colon (
::):%3A%3A
Implementation notes
transaction URI support를 구현할 때:
- Validate all parameters: address가 유효한 Sui address인지, amount가 positive
u64value인지, nonce가 36자를 초과하지 않는지, 그리고 coin type이 올바른 format을 따르는지 확인한다. - Generate unique nonces: accidental duplicate를 방지하기 위해 UUIDv4 또는 다른 cryptographically secure random identifier를 사용한다.
- Display metadata to users: context 제공을 위해 payment confirmation 중
label,iconUrl, 그리고messageparameter를 user에게 표시한다. - Route to correct payment function:
registry가 제공되면process_registry_payment를 사용하고, 그렇지 않으면process_ephemeral_payment를 사용한다.
Key structures
Namespace
payment registry를 organization하기 위한 higher-order namespace를 나타낸다:
public struct Namespace has key, store {
id: UID,
}
PaymentRegistry
duplicate prevention을 포함하여 payment와 receipt를 추적한다:
public struct PaymentRegistry has key {
id: UID,
cap_id: ID,
config: VecMap<String, Value>,
version: u16,
}
RegistryAdminCap
특정 registry에 대한 administrative capability를 제공한다:
public struct RegistryAdminCap has key, store {
id: UID,
registry_id: ID,
}
PaymentType
payment type을 나타내는 enum이다: Ephemeral(one-time) 또는 Registry(registry에서 추적됨)이다.
public enum PaymentType has copy, drop, store {
Ephemeral,
Registry(ID),
}
PaymentReceipt
처리된 payment의 detail을 포함한다:
public struct PaymentReceipt has key, store {
payment_type: PaymentType,
nonce: String,
payment_amount: u64,
receiver: address,
coin_type: String,
timestamp_ms: u64,
}
PaymentKey
payment record를 식별하기 위한 unique key이다:
public struct PaymentKey<phantom T> has copy, drop, store {
nonce: String,
payment_amount: u64,
receiver: address,
}
PaymentRecord
payment record 정보를 저장하는 internal structure이다:
public struct PaymentRecord has store {
epoch_at_time_of_record: u64,
}
Events
Payment Kit은 off-chain tracking 및 integration을 위한 event를 emit한다. payment processing 함수는 다음을 포함하는 event를 emit한다:
- Payment nonce
- Payment amount
- Coin type
- Receiver address
- Timestamp
- Registry information (for registry payments)
이 event를 사용하여 다음을 수행한다:
- off-chain으로 payment history를 추적한다
- external workflow를 trigger한다
- application state를 업데이트한다
- report 및 analytics를 생성한다
- accounting system과 통합한다
Error codes
Payment Kit은 다음 error condition을 정의한다:
- Duplicate payment detection: 동일한 composite key를 가진 payment가 이미 처리되었다.
- Payment amount mismatch: coin value가 expected payment amount와 일치하지 않는다.
- Payment record not found: 존재하지 않는 payment record에 액세스하려고 했다.
- Payment record not expired: expiration 전에 record를 삭제하려고 했다.
- Unauthorized admin: 작업에
RegistryAdminCap이 필요하다. - Registry already exists: namespace에 duplicate registry를 생성하려고 했다.
- Invalid registry name: registry name이 requirement를 충족하지 않는다.