Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Contracts/payment-processing-contract/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub enum PaymentError {
ExceedsOriginalAmount = 34,
InvalidRefundStatus = 35,
InsufficientBalance = 36,
InvalidPaginationParams = 40,
QueryLimitExceeded = 41,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

impl fmt::Display for PaymentError {
Expand All @@ -64,6 +66,7 @@ impl fmt::Display for PaymentError {
PaymentError::OrderExpired => write!(f, "Payment order has expired"),
PaymentError::InvalidToken => write!(f, "Token not supported by merchant"),


// Merchant profile validation errors
PaymentError::InvalidName => write!(f, "Invalid merchant name (must be 1-100 characters)"),
PaymentError::InvalidDescription => write!(f, "Invalid description (max 500 characters)"),
Expand Down Expand Up @@ -100,6 +103,8 @@ impl fmt::Display for PaymentError {
PaymentError::ExceedsOriginalAmount => write!(f, "Refund exceeds original amount"),
PaymentError::InvalidRefundStatus => write!(f, "Invalid refund status transition"),
PaymentError::InsufficientBalance => write!(f, "Insufficient balance for refund"),
PaymentError::InvalidPaginationParams => write!(f, "Invalid pagination parameters"),
PaymentError::QueryLimitExceeded => write!(f, "Query limit exceeded"),
}
}
}
Expand Down
292 changes: 290 additions & 2 deletions Contracts/payment-processing-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ use crate::{
MerchantCategory, ProfileUpdateData, MerchantRegisteredEvent, ProfileUpdatedEvent,
MerchantDeactivatedEvent, LimitsUpdatedEvent, merchant_registered_topic,
profile_updated_topic, merchant_deactivated_topic, limits_updated_topic,
RefundRequest, RefundStatus, MultiSigPaymentRecord
RefundRequest, RefundStatus, MultiSigPaymentRecord,
// NEW IMPORTS
PaymentQueryParams, PaymentStats, MerchantPaymentSummary, PayerPaymentSummary,
PaymentIndexEntry, PaymentBucket, CompressedPaymentRecord,
},
storage::Storage,
helper::{validate_name, validate_description, validate_contact_info,
Expand Down Expand Up @@ -156,6 +159,62 @@ pub trait PaymentProcessingTrait {
fn reject_refund(env: Env, caller: Address, refund_id: String) -> Result<(), PaymentError>;
fn execute_refund(env: Env, refund_id: String) -> Result<(), PaymentError>;
fn get_refund_status(env: Env, refund_id: String) -> Result<RefundStatus, PaymentError>;

// Payment History Query and Management Functions
fn get_merchant_payment_history(
env: Env,
merchant: Address,
limit: u32,
offset: u32,
) -> Result<Vec<PaymentRecord>, PaymentError>;

fn get_payer_payment_history(
env: Env,
payer: Address,
limit: u32,
offset: u32,
) -> Result<Vec<PaymentRecord>, PaymentError>;

fn get_payment_by_order_id(
env: Env,
order_id: String,
) -> Result<PaymentRecord, PaymentError>;

fn query_payments(
env: Env,
params: PaymentQueryParams,
) -> Result<Vec<PaymentRecord>, PaymentError>;

fn get_merchant_payment_stats(
env: Env,
merchant: Address,
) -> Result<MerchantPaymentSummary, PaymentError>;

fn get_payer_payment_stats(
env: Env,
payer: Address,
) -> Result<PayerPaymentSummary, PaymentError>;

fn get_global_payment_stats(env: Env) -> Result<PaymentStats, PaymentError>;

fn get_payments_by_time_range(
env: Env,
start_time: u64,
end_time: u64,
) -> Result<Vec<PaymentBucket>, PaymentError>;

fn get_payments_by_token(
env: Env,
token: Address,
limit: u32,
offset: u32,
) -> Result<Vec<PaymentRecord>, PaymentError>;

fn archive_old_payments(
env: Env,
admin: Address,
cutoff_time: u64,
) -> Result<(), PaymentError>;
}

#[contract]
Expand Down Expand Up @@ -518,6 +577,9 @@ impl PaymentProcessingTrait for PaymentProcessingContract {
refunded_amount: 0,
};
storage.save_payment(&payment_record);

// Index the payment for efficient querying
storage.index_payment(&payment_record);

Ok(())
}
Expand Down Expand Up @@ -1213,11 +1275,237 @@ impl PaymentProcessingTrait for PaymentProcessingContract {
Ok(())
}

fn get_refund_status(env: Env, refund_id: String) -> Result<RefundStatus, PaymentError> {
fn get_refund_status(env: Env, refund_id: String) -> Result<RefundStatus, PaymentError> {
let storage = Storage::new(&env);
let req = storage.get_refund(&refund_id)?;
Ok(req.status)
}

// Payment History Query and Management Functions
fn get_merchant_payment_history(
env: Env,
merchant: Address,
limit: u32,
offset: u32,
) -> Result<Vec<PaymentRecord>, PaymentError> {
let storage = Storage::new(&env);
// Validate pagination parameters
if limit == 0 || limit > 100 {
return Err(PaymentError::InvalidPaginationParams);
}
Comment thread
ritik4ever marked this conversation as resolved.
let payments = storage.get_merchant_payments(&merchant, limit, offset);
Ok(payments)
}

fn get_payer_payment_history(
env: Env,
payer: Address,
limit: u32,
offset: u32,
) -> Result<Vec<PaymentRecord>, PaymentError> {
let storage = Storage::new(&env);
// Validate pagination parameters
if limit == 0 || limit > 100 {
return Err(PaymentError::InvalidPaginationParams);
}
let payments = storage.get_payer_payments(&payer, limit, offset);
Ok(payments)
}

fn get_payment_by_order_id(
env: Env,
order_id: String,
) -> Result<PaymentRecord, PaymentError> {
let storage = Storage::new(&env);
storage.get_payment(&order_id)
}

fn query_payments(
env: Env,
params: PaymentQueryParams,
) -> Result<Vec<PaymentRecord>, PaymentError> {
let storage = Storage::new(&env);
// Validate pagination parameters
if params.limit == 0 || params.limit > 100 {
return Err(PaymentError::InvalidPaginationParams);
}
// Get all payments from storage
let all_payments = storage.get_payments_map();
let mut filtered_payments = Vec::new(&env);

let mut count = 0u32;
let mut skipped = 0u32;

for (_order_id, payment) in all_payments.iter() {
// Apply filters
let mut matches = true;

// Time range filter
if let Some(start_time) = params.start_time {
if payment.paid_at < start_time {
matches = false;
}
}
if let Some(end_time) = params.end_time {
if payment.paid_at > end_time {
matches = false;
}
}

// Amount range filter
if let Some(min_amount) = params.min_amount {
if payment.amount < min_amount {
matches = false;
}
}
if let Some(max_amount) = params.max_amount {
if payment.amount > max_amount {
matches = false;
}
}

// Token filter
if let Some(ref token) = params.token {
if payment.token != *token {
matches = false;
}
}

if matches {
// Handle offset
if skipped < params.offset {
skipped += 1;
continue;
}

// Add to results
filtered_payments.push_back(payment);
count += 1;

// Check limit
if count >= params.limit {
break;
}
}
}

Ok(filtered_payments)
}

fn get_merchant_payment_stats(
env: Env,
merchant: Address,
) -> Result<MerchantPaymentSummary, PaymentError> {
let storage = Storage::new(&env);
storage.get_merchant_stats(&merchant)
.ok_or(PaymentError::MerchantNotFound)
}

fn get_payer_payment_stats(
env: Env,
payer: Address,
) -> Result<PayerPaymentSummary, PaymentError> {
let storage = Storage::new(&env);
storage.get_payer_stats(&payer)
.ok_or(PaymentError::PaymentNotFound)
Comment thread
ritik4ever marked this conversation as resolved.
}

fn get_global_payment_stats(env: Env) -> Result<PaymentStats, PaymentError> {
let storage = Storage::new(&env);
storage.get_global_stats()
.ok_or(PaymentError::PaymentNotFound)
}

fn get_payments_by_time_range(
env: Env,
start_time: u64,
end_time: u64,
) -> Result<Vec<PaymentBucket>, PaymentError> {
// Validate time range
if start_time >= end_time {
return Err(PaymentError::InvalidAmount);
Comment thread
ritik4ever marked this conversation as resolved.
}

let storage = Storage::new(&env);
let buckets = storage.get_payments_by_time_range(start_time, end_time);
Ok(buckets)
}

fn get_payments_by_token(
env: Env,
token: Address,
limit: u32,
offset: u32,
) -> Result<Vec<PaymentRecord>, PaymentError> {
// Validate pagination parameters
if limit == 0 || limit > 100 {
return Err(PaymentError::InvalidPaginationParams);
}
let storage = Storage::new(&env);
// Retrieve token index
let token_index: Map<Address, soroban_sdk::Vec<soroban_sdk::String>> = env
.storage()
.persistent()
.get(&DataKey::TokenBasedIndex.as_symbol(&env))
.unwrap_or_else(|| Map::new(&env));
let order_ids = token_index
Comment thread
ritik4ever marked this conversation as resolved.
.get(token)
.unwrap_or_else(|| soroban_sdk::Vec::new(&env));
let mut results = Vec::new(&env);
let mut count = 0u32;
let mut skipped = 0u32;
for i in 0..order_ids.len() {
if let Some(order_id) = order_ids.get(i) {
// Handle offset
if skipped < offset {
skipped += 1;
continue;
}
if let Ok(payment) = storage.get_payment(&order_id) {
results.push_back(payment);
count += 1;
// Check limit
if count >= limit {
break;
}
}
}
}
Ok(results)
}

fn archive_old_payments(
env: Env,
admin: Address,
cutoff_time: u64,
) -> Result<(), PaymentError> {
// Require admin authorization
admin.require_auth();

let storage = Storage::new(&env);

// Verify admin
let stored_admin = storage.get_admin()
.ok_or(PaymentError::AdminNotFound)?;

if stored_admin != admin {
return Err(PaymentError::NotAuthorized);
}

// Validate cutoff time (must be in the past)
if cutoff_time >= env.ledger().timestamp() {
return Err(PaymentError::InvalidAmount);
}

// Perform compression and archival (batch size of 100)
let batch_size = 100u32;
storage.compress_old_payments(cutoff_time, batch_size);

// Emit event
log!(&env, "PaymentsArchived: cutoff_time={}", cutoff_time);

Ok(())
}
}

/// Optimized message creation for signature verification
Expand Down
Loading