-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
122 lines (104 loc) · 3.6 KB
/
Copy pathlib.rs
File metadata and controls
122 lines (104 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#![no_std]
use soroban_sdk::{
contract, contractimpl, contracttype, symbol_short, Env, String,
};
// ── Storage types ──────────────────────────────────────────────────────────────
#[contracttype]
#[derive(Clone)]
pub struct PaymentRecord {
/// Laundry order ID (e.g. "ORD-20260521-001")
pub order_id: String,
/// Amount in stroops (1 XLM = 10_000_000 stroops)
pub amount_stroops: i128,
/// Stellar address of the payer (as string for simplicity)
pub payer: String,
/// Horizon transaction hash that confirmed the payment
pub tx_hash: String,
/// Stellar ledger timestamp when recorded
pub recorded_at: u64,
/// Network label: "testnet" or "mainnet"
pub network: String,
}
#[contracttype]
pub enum DataKey {
/// Individual payment record keyed by order ID
Payment(String),
/// Total payment count
Count,
}
// ── Contract ───────────────────────────────────────────────────────────────────
#[contract]
pub struct StellarPayRegistry;
#[contractimpl]
impl StellarPayRegistry {
/// Record a confirmed payment on-chain.
/// Called by the LaundromatAI backend after Horizon confirms the XLM transfer.
/// Returns false if the order was already recorded (idempotent).
pub fn record(
env: Env,
order_id: String,
amount_stroops: i128,
payer: String,
tx_hash: String,
network: String,
) -> bool {
let key = DataKey::Payment(order_id.clone());
// Idempotency: do not overwrite an existing confirmed payment
if env.storage().persistent().has(&key) {
return false;
}
let record = PaymentRecord {
order_id,
amount_stroops,
payer,
tx_hash,
recorded_at: env.ledger().timestamp(),
network,
};
env.storage().persistent().set(&key, &record);
// Extend TTL so the record survives long-term
env.storage().persistent().extend_ttl(&key, 500_000, 500_000);
// Bump total count
let count: u32 = env
.storage()
.instance()
.get(&DataKey::Count)
.unwrap_or(0);
env.storage()
.instance()
.set(&DataKey::Count, &(count + 1));
env.storage().instance().extend_ttl(1_000_000, 1_000_000);
env.events().publish(
(symbol_short!("PAY"),),
count + 1,
);
true
}
/// Retrieve a payment record by order ID.
pub fn get(env: Env, order_id: String) -> Option<PaymentRecord> {
env.storage()
.persistent()
.get(&DataKey::Payment(order_id))
}
/// Total number of payments recorded on this contract.
pub fn count(env: Env) -> u32 {
env.storage()
.instance()
.get(&DataKey::Count)
.unwrap_or(0)
}
pub fn log_payment(
env: Env,
payment_hash: soroban_sdk::BytesN<32>,
amount: i128,
sender: soroban_sdk::Address,
receiver: soroban_sdk::Address,
) {
let key = (sender, payment_hash);
let value = (amount, receiver, env.ledger().timestamp());
env.storage().persistent().set(&key, &value);
}
pub fn register_merchant(env: Env, merchant: soroban_sdk::Address) {
env.storage().persistent().set(&merchant, &true);
}
}