move repos into monorepo
This commit is contained in:
10
lib/osiris/core/objects/money/mod.rs
Normal file
10
lib/osiris/core/objects/money/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
/// Money Module
|
||||
///
|
||||
/// Financial objects including accounts, assets, transactions, and payment providers.
|
||||
|
||||
pub mod models;
|
||||
pub mod rhai;
|
||||
pub mod payments;
|
||||
|
||||
pub use models::{Account, Asset, Transaction, AccountStatus, TransactionType, Signature, AccountPolicyItem};
|
||||
pub use payments::{PaymentClient, PaymentRequest, PaymentResponse, PaymentStatus};
|
||||
498
lib/osiris/core/objects/money/models.rs
Normal file
498
lib/osiris/core/objects/money/models.rs
Normal file
@@ -0,0 +1,498 @@
|
||||
use crate::store::{BaseData, IndexKey, Object};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Represents the status of an account
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum AccountStatus {
|
||||
Active,
|
||||
Inactive,
|
||||
Suspended,
|
||||
Archived,
|
||||
}
|
||||
|
||||
impl Default for AccountStatus {
|
||||
fn default() -> Self {
|
||||
AccountStatus::Active
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the type of transaction
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TransactionType {
|
||||
Transfer,
|
||||
Clawback,
|
||||
Freeze,
|
||||
Unfreeze,
|
||||
Issue,
|
||||
Burn,
|
||||
}
|
||||
|
||||
impl Default for TransactionType {
|
||||
fn default() -> Self {
|
||||
TransactionType::Transfer
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a signature for transactions
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Signature {
|
||||
pub signer_id: u32,
|
||||
pub signature: String,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
signer_id: 0,
|
||||
signature: String::new(),
|
||||
timestamp: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signer_id(mut self, signer_id: u32) -> Self {
|
||||
self.signer_id = signer_id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn signature(mut self, signature: impl ToString) -> Self {
|
||||
self.signature = signature.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn timestamp(mut self, timestamp: u64) -> Self {
|
||||
self.timestamp = timestamp;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Policy item for account operations
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub struct AccountPolicyItem {
|
||||
pub signers: Vec<u32>,
|
||||
pub min_signatures: u32,
|
||||
pub enabled: bool,
|
||||
pub threshold: f64,
|
||||
pub recipient: u32,
|
||||
}
|
||||
|
||||
impl AccountPolicyItem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
signers: Vec::new(),
|
||||
min_signatures: 0,
|
||||
enabled: false,
|
||||
threshold: 0.0,
|
||||
recipient: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_signer(mut self, signer_id: u32) -> Self {
|
||||
self.signers.push(signer_id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn signers(mut self, signers: Vec<u32>) -> Self {
|
||||
self.signers = signers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn min_signatures(mut self, min_signatures: u32) -> Self {
|
||||
self.min_signatures = min_signatures;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn enabled(mut self, enabled: bool) -> Self {
|
||||
self.enabled = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn threshold(mut self, threshold: f64) -> Self {
|
||||
self.threshold = threshold;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn recipient(mut self, recipient: u32) -> Self {
|
||||
self.recipient = recipient;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an account in the financial system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, crate::DeriveObject)]
|
||||
pub struct Account {
|
||||
/// Base model data
|
||||
pub base_data: BaseData,
|
||||
pub owner_id: u32,
|
||||
#[index]
|
||||
pub address: String,
|
||||
pub balance: f64,
|
||||
pub currency: String,
|
||||
pub assetid: u32,
|
||||
pub last_activity: u64,
|
||||
pub administrators: Vec<u32>,
|
||||
pub accountpolicy: u32,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
/// Create a new account instance
|
||||
pub fn new(id: u32) -> Self {
|
||||
let mut base_data = BaseData::new();
|
||||
Self {
|
||||
base_data,
|
||||
owner_id: 0,
|
||||
address: String::new(),
|
||||
balance: 0.0,
|
||||
currency: String::new(),
|
||||
assetid: 0,
|
||||
last_activity: 0,
|
||||
administrators: Vec::new(),
|
||||
accountpolicy: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the owner ID (fluent)
|
||||
pub fn owner_id(mut self, owner_id: u32) -> Self {
|
||||
self.owner_id = owner_id;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the blockchain address (fluent)
|
||||
pub fn address(mut self, address: impl ToString) -> Self {
|
||||
self.address = address.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the balance (fluent)
|
||||
pub fn balance(mut self, balance: f64) -> Self {
|
||||
self.balance = balance;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the currency (fluent)
|
||||
pub fn currency(mut self, currency: impl ToString) -> Self {
|
||||
self.currency = currency.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the asset ID (fluent)
|
||||
pub fn assetid(mut self, assetid: u32) -> Self {
|
||||
self.assetid = assetid;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the last activity timestamp (fluent)
|
||||
pub fn last_activity(mut self, last_activity: u64) -> Self {
|
||||
self.last_activity = last_activity;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an administrator (fluent)
|
||||
pub fn add_administrator(mut self, admin_id: u32) -> Self {
|
||||
self.administrators.push(admin_id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set all administrators (fluent)
|
||||
pub fn administrators(mut self, administrators: Vec<u32>) -> Self {
|
||||
self.administrators = administrators;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the account policy ID (fluent)
|
||||
pub fn accountpolicy(mut self, accountpolicy: u32) -> Self {
|
||||
self.accountpolicy = accountpolicy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the final account instance
|
||||
pub fn build(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an asset in the financial system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, crate::DeriveObject)]
|
||||
pub struct Asset {
|
||||
/// Base model data
|
||||
pub base_data: BaseData,
|
||||
#[index]
|
||||
pub address: String,
|
||||
pub assetid: u32,
|
||||
pub asset_type: String,
|
||||
pub issuer: u32,
|
||||
pub supply: f64,
|
||||
pub decimals: u8,
|
||||
pub is_frozen: bool,
|
||||
pub metadata: HashMap<String, String>,
|
||||
pub administrators: Vec<u32>,
|
||||
pub min_signatures: u32,
|
||||
}
|
||||
|
||||
impl Asset {
|
||||
/// Create a new asset instance
|
||||
pub fn new(id: u32) -> Self {
|
||||
let mut base_data = BaseData::new();
|
||||
Self {
|
||||
base_data,
|
||||
address: String::new(),
|
||||
assetid: 0,
|
||||
asset_type: String::new(),
|
||||
issuer: 0,
|
||||
supply: 0.0,
|
||||
decimals: 0,
|
||||
is_frozen: false,
|
||||
metadata: HashMap::new(),
|
||||
administrators: Vec::new(),
|
||||
min_signatures: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the blockchain address (fluent)
|
||||
pub fn address(mut self, address: impl ToString) -> Self {
|
||||
self.address = address.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the asset ID (fluent)
|
||||
pub fn assetid(mut self, assetid: u32) -> Self {
|
||||
self.assetid = assetid;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the asset type (fluent)
|
||||
pub fn asset_type(mut self, asset_type: impl ToString) -> Self {
|
||||
self.asset_type = asset_type.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the issuer (fluent)
|
||||
pub fn issuer(mut self, issuer: u32) -> Self {
|
||||
self.issuer = issuer;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the supply (fluent)
|
||||
pub fn supply(mut self, supply: f64) -> Self {
|
||||
self.supply = supply;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the decimals (fluent)
|
||||
pub fn decimals(mut self, decimals: u8) -> Self {
|
||||
self.decimals = decimals;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the frozen status (fluent)
|
||||
pub fn is_frozen(mut self, is_frozen: bool) -> Self {
|
||||
self.is_frozen = is_frozen;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add metadata entry (fluent)
|
||||
pub fn add_metadata(mut self, key: impl ToString, value: impl ToString) -> Self {
|
||||
self.metadata.insert(key.to_string(), value.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set all metadata (fluent)
|
||||
pub fn metadata(mut self, metadata: HashMap<String, String>) -> Self {
|
||||
self.metadata = metadata;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an administrator (fluent)
|
||||
pub fn add_administrator(mut self, admin_id: u32) -> Self {
|
||||
self.administrators.push(admin_id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set all administrators (fluent)
|
||||
pub fn administrators(mut self, administrators: Vec<u32>) -> Self {
|
||||
self.administrators = administrators;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set minimum signatures required (fluent)
|
||||
pub fn min_signatures(mut self, min_signatures: u32) -> Self {
|
||||
self.min_signatures = min_signatures;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the final asset instance
|
||||
pub fn build(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents account policies for various operations
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, crate::DeriveObject)]
|
||||
pub struct AccountPolicy {
|
||||
/// Base model data
|
||||
pub base_data: BaseData,
|
||||
pub transferpolicy: AccountPolicyItem,
|
||||
pub adminpolicy: AccountPolicyItem,
|
||||
pub clawbackpolicy: AccountPolicyItem,
|
||||
pub freezepolicy: AccountPolicyItem,
|
||||
}
|
||||
|
||||
impl AccountPolicy {
|
||||
/// Create a new account policy instance
|
||||
pub fn new(id: u32) -> Self {
|
||||
let mut base_data = BaseData::new();
|
||||
Self {
|
||||
base_data,
|
||||
transferpolicy: AccountPolicyItem::new(),
|
||||
adminpolicy: AccountPolicyItem::new(),
|
||||
clawbackpolicy: AccountPolicyItem::new(),
|
||||
freezepolicy: AccountPolicyItem::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the transfer policy (fluent)
|
||||
pub fn transferpolicy(mut self, transferpolicy: AccountPolicyItem) -> Self {
|
||||
self.transferpolicy = transferpolicy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the admin policy (fluent)
|
||||
pub fn adminpolicy(mut self, adminpolicy: AccountPolicyItem) -> Self {
|
||||
self.adminpolicy = adminpolicy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the clawback policy (fluent)
|
||||
pub fn clawbackpolicy(mut self, clawbackpolicy: AccountPolicyItem) -> Self {
|
||||
self.clawbackpolicy = clawbackpolicy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the freeze policy (fluent)
|
||||
pub fn freezepolicy(mut self, freezepolicy: AccountPolicyItem) -> Self {
|
||||
self.freezepolicy = freezepolicy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the final account policy instance
|
||||
pub fn build(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a financial transaction
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, crate::DeriveObject)]
|
||||
pub struct Transaction {
|
||||
/// Base model data
|
||||
pub base_data: BaseData,
|
||||
pub txid: u32,
|
||||
pub source: u32,
|
||||
pub destination: u32,
|
||||
pub assetid: u32,
|
||||
pub amount: f64,
|
||||
pub timestamp: u64,
|
||||
pub status: String,
|
||||
pub memo: String,
|
||||
pub tx_type: TransactionType,
|
||||
pub signatures: Vec<Signature>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
/// Create a new transaction instance
|
||||
pub fn new(id: u32) -> Self {
|
||||
let mut base_data = BaseData::new();
|
||||
Self {
|
||||
base_data,
|
||||
txid: 0,
|
||||
source: 0,
|
||||
destination: 0,
|
||||
assetid: 0,
|
||||
amount: 0.0,
|
||||
timestamp: 0,
|
||||
status: String::new(),
|
||||
memo: String::new(),
|
||||
tx_type: TransactionType::default(),
|
||||
signatures: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the transaction ID (fluent)
|
||||
pub fn txid(mut self, txid: u32) -> Self {
|
||||
self.txid = txid;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the source account (fluent)
|
||||
pub fn source(mut self, source: u32) -> Self {
|
||||
self.source = source;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the destination account (fluent)
|
||||
pub fn destination(mut self, destination: u32) -> Self {
|
||||
self.destination = destination;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the asset ID (fluent)
|
||||
pub fn assetid(mut self, assetid: u32) -> Self {
|
||||
self.assetid = assetid;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the amount (fluent)
|
||||
pub fn amount(mut self, amount: f64) -> Self {
|
||||
self.amount = amount;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the timestamp (fluent)
|
||||
pub fn timestamp(mut self, timestamp: u64) -> Self {
|
||||
self.timestamp = timestamp;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the status (fluent)
|
||||
pub fn status(mut self, status: impl ToString) -> Self {
|
||||
self.status = status.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the memo (fluent)
|
||||
pub fn memo(mut self, memo: impl ToString) -> Self {
|
||||
self.memo = memo.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the transaction type (fluent)
|
||||
pub fn tx_type(mut self, tx_type: TransactionType) -> Self {
|
||||
self.tx_type = tx_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a signature (fluent)
|
||||
pub fn add_signature(mut self, signature: Signature) -> Self {
|
||||
self.signatures.push(signature);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set all signatures (fluent)
|
||||
pub fn signatures(mut self, signatures: Vec<Signature>) -> Self {
|
||||
self.signatures = signatures;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the final transaction instance
|
||||
pub fn build(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
457
lib/osiris/core/objects/money/payments.rs
Normal file
457
lib/osiris/core/objects/money/payments.rs
Normal file
@@ -0,0 +1,457 @@
|
||||
/// Payment Provider Client
|
||||
///
|
||||
/// Generic payment provider API client supporting multiple payment gateways.
|
||||
/// Currently implements Pesapal API but designed to be extensible for other providers.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::store::{BaseData, IndexKey, Object};
|
||||
|
||||
// Helper to run async code synchronously
|
||||
fn run_async<F, T>(future: F) -> T
|
||||
where
|
||||
F: std::future::Future<Output = T> + Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
// Try to use current runtime handle if available
|
||||
if tokio::runtime::Handle::try_current().is_ok() {
|
||||
// We're in a runtime, spawn a blocking thread with its own runtime
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(future)
|
||||
}).join().unwrap()
|
||||
})
|
||||
} else {
|
||||
// No runtime, create one
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(future)
|
||||
}
|
||||
}
|
||||
|
||||
/// Payment Provider Client for making API calls to payment gateways
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PaymentClient {
|
||||
/// Base data for object storage
|
||||
pub base_data: BaseData,
|
||||
|
||||
/// Provider name (e.g., "pesapal", "stripe", "paypal", "flutterwave")
|
||||
pub provider: String,
|
||||
|
||||
/// Consumer key / API key
|
||||
pub consumer_key: String,
|
||||
|
||||
/// Consumer secret / API secret
|
||||
pub consumer_secret: String,
|
||||
|
||||
/// Base URL for API (optional, uses provider default if not set)
|
||||
pub base_url: Option<String>,
|
||||
|
||||
/// Sandbox mode (for testing)
|
||||
pub sandbox: bool,
|
||||
}
|
||||
|
||||
/// Payment request details
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PaymentRequest {
|
||||
/// Unique merchant reference
|
||||
pub merchant_reference: String,
|
||||
|
||||
/// Amount to charge
|
||||
pub amount: f64,
|
||||
|
||||
/// Currency code (e.g., "USD", "KES", "UGX")
|
||||
pub currency: String,
|
||||
|
||||
/// Description of the payment
|
||||
pub description: String,
|
||||
|
||||
/// Callback URL for payment notifications
|
||||
pub callback_url: String,
|
||||
|
||||
/// Redirect URL after payment (optional)
|
||||
pub redirect_url: Option<String>,
|
||||
|
||||
/// Cancel URL (optional)
|
||||
pub cancel_url: Option<String>,
|
||||
|
||||
/// Customer email
|
||||
pub customer_email: Option<String>,
|
||||
|
||||
/// Customer phone
|
||||
pub customer_phone: Option<String>,
|
||||
|
||||
/// Customer first name
|
||||
pub customer_first_name: Option<String>,
|
||||
|
||||
/// Customer last name
|
||||
pub customer_last_name: Option<String>,
|
||||
}
|
||||
|
||||
/// Payment response from provider
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PaymentResponse {
|
||||
/// Payment link URL
|
||||
pub payment_url: String,
|
||||
|
||||
/// Order tracking ID from provider
|
||||
pub order_tracking_id: String,
|
||||
|
||||
/// Merchant reference
|
||||
pub merchant_reference: String,
|
||||
|
||||
/// Status message
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
/// Payment status query result
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PaymentStatus {
|
||||
/// Order tracking ID
|
||||
pub order_tracking_id: String,
|
||||
|
||||
/// Merchant reference
|
||||
pub merchant_reference: String,
|
||||
|
||||
/// Payment status (e.g., "PENDING", "COMPLETED", "FAILED")
|
||||
pub status: String,
|
||||
|
||||
/// Amount
|
||||
pub amount: f64,
|
||||
|
||||
/// Currency
|
||||
pub currency: String,
|
||||
|
||||
/// Payment method used
|
||||
pub payment_method: Option<String>,
|
||||
|
||||
/// Transaction ID
|
||||
pub transaction_id: Option<String>,
|
||||
}
|
||||
|
||||
// Pesapal-specific structures
|
||||
#[derive(Debug, Serialize)]
|
||||
struct PesapalAuthRequest {
|
||||
consumer_key: String,
|
||||
consumer_secret: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PesapalAuthResponse {
|
||||
token: String,
|
||||
#[serde(rename = "expiryDate")]
|
||||
expiry_date: Option<serde_json::Value>,
|
||||
error: Option<String>,
|
||||
status: Option<String>,
|
||||
message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct PesapalSubmitOrderRequest {
|
||||
id: String,
|
||||
currency: String,
|
||||
amount: f64,
|
||||
description: String,
|
||||
callback_url: String,
|
||||
redirect_mode: String,
|
||||
notification_id: String,
|
||||
billing_address: Option<PesapalBillingAddress>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct PesapalBillingAddress {
|
||||
email_address: Option<String>,
|
||||
phone_number: Option<String>,
|
||||
first_name: Option<String>,
|
||||
last_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PesapalSubmitOrderResponse {
|
||||
order_tracking_id: Option<String>,
|
||||
merchant_reference: Option<String>,
|
||||
redirect_url: Option<String>,
|
||||
error: Option<serde_json::Value>,
|
||||
status: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct PesapalTransactionStatusResponse {
|
||||
payment_method: Option<String>,
|
||||
amount: f64,
|
||||
created_date: String,
|
||||
confirmation_code: Option<String>,
|
||||
payment_status_description: String,
|
||||
description: String,
|
||||
message: String,
|
||||
payment_account: Option<String>,
|
||||
call_back_url: String,
|
||||
status_code: i32,
|
||||
merchant_reference: String,
|
||||
payment_status_code: String,
|
||||
currency: String,
|
||||
error: Option<String>,
|
||||
status: String,
|
||||
}
|
||||
|
||||
impl PaymentClient {
|
||||
/// Create a new payment client
|
||||
pub fn new(id: u32, provider: String, consumer_key: String, consumer_secret: String) -> Self {
|
||||
let base_data = BaseData::with_id(id, String::new());
|
||||
Self {
|
||||
base_data,
|
||||
provider,
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
base_url: None,
|
||||
sandbox: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Pesapal client
|
||||
pub fn pesapal(id: u32, consumer_key: String, consumer_secret: String) -> Self {
|
||||
let base_data = BaseData::with_id(id, String::new());
|
||||
Self {
|
||||
base_data,
|
||||
provider: "pesapal".to_string(),
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
base_url: Some("https://pay.pesapal.com/v3".to_string()),
|
||||
sandbox: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Pesapal sandbox client
|
||||
pub fn pesapal_sandbox(id: u32, consumer_key: String, consumer_secret: String) -> Self {
|
||||
let base_data = BaseData::with_id(id, String::new());
|
||||
Self {
|
||||
base_data,
|
||||
provider: "pesapal".to_string(),
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
base_url: Some("https://cybqa.pesapal.com/pesapalv3".to_string()),
|
||||
sandbox: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set custom base URL
|
||||
pub fn with_base_url(mut self, base_url: String) -> Self {
|
||||
self.base_url = Some(base_url);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable sandbox mode
|
||||
pub fn with_sandbox(mut self, sandbox: bool) -> Self {
|
||||
self.sandbox = sandbox;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the base URL for the provider
|
||||
fn get_base_url(&self) -> String {
|
||||
if let Some(url) = &self.base_url {
|
||||
return url.clone();
|
||||
}
|
||||
|
||||
match self.provider.as_str() {
|
||||
"pesapal" => {
|
||||
if self.sandbox {
|
||||
"https://cybqa.pesapal.com/pesapalv3".to_string()
|
||||
} else {
|
||||
"https://pay.pesapal.com/v3".to_string()
|
||||
}
|
||||
}
|
||||
"stripe" => "https://api.stripe.com/v1".to_string(),
|
||||
"paypal" => "https://api.paypal.com/v2".to_string(),
|
||||
"flutterwave" => "https://api.flutterwave.com/v3".to_string(),
|
||||
_ => panic!("Unknown provider: {}", self.provider),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a payment link
|
||||
pub fn create_payment_link(
|
||||
&self,
|
||||
request: &PaymentRequest,
|
||||
) -> Result<PaymentResponse, String> {
|
||||
match self.provider.as_str() {
|
||||
"pesapal" => self.create_pesapal_payment(request),
|
||||
_ => Err(format!("Provider {} not yet implemented", self.provider)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get payment status
|
||||
pub fn get_payment_status(
|
||||
&self,
|
||||
order_tracking_id: &str,
|
||||
) -> Result<PaymentStatus, String> {
|
||||
match self.provider.as_str() {
|
||||
"pesapal" => self.get_pesapal_status(order_tracking_id),
|
||||
_ => Err(format!("Provider {} not yet implemented", self.provider)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Authenticate with Pesapal and get access token
|
||||
fn pesapal_authenticate(&self) -> Result<String, String> {
|
||||
let url = format!("{}/api/Auth/RequestToken", self.get_base_url());
|
||||
|
||||
let auth_request = PesapalAuthRequest {
|
||||
consumer_key: self.consumer_key.clone(),
|
||||
consumer_secret: self.consumer_secret.clone(),
|
||||
};
|
||||
|
||||
run_async(async move {
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.post(&url)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.json(&auth_request)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send auth request: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let error_text = response.text().await.unwrap_or_default();
|
||||
return Err(format!("Pesapal auth failed ({}): {}", status, error_text));
|
||||
}
|
||||
|
||||
// Debug: print raw response
|
||||
let response_text = response.text().await
|
||||
.map_err(|e| format!("Failed to read auth response: {}", e))?;
|
||||
println!("=== PESAPAL AUTH RESPONSE ===");
|
||||
println!("{}", response_text);
|
||||
println!("==============================");
|
||||
|
||||
let auth_response: PesapalAuthResponse = serde_json::from_str(&response_text)
|
||||
.map_err(|e| format!("Failed to parse auth response: {}", e))?;
|
||||
|
||||
if let Some(error) = auth_response.error {
|
||||
return Err(format!("Pesapal auth error: {}", error));
|
||||
}
|
||||
|
||||
Ok(auth_response.token)
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a Pesapal payment
|
||||
fn create_pesapal_payment(
|
||||
&self,
|
||||
request: &PaymentRequest,
|
||||
) -> Result<PaymentResponse, String> {
|
||||
// Get auth token
|
||||
let token = self.pesapal_authenticate()?;
|
||||
|
||||
let url = format!("{}/api/Transactions/SubmitOrderRequest", self.get_base_url());
|
||||
|
||||
let pesapal_request = PesapalSubmitOrderRequest {
|
||||
id: request.merchant_reference.clone(),
|
||||
currency: request.currency.clone(),
|
||||
amount: request.amount,
|
||||
description: request.description.clone(),
|
||||
callback_url: request.callback_url.clone(),
|
||||
redirect_mode: String::new(),
|
||||
notification_id: String::new(),
|
||||
billing_address: Some(PesapalBillingAddress {
|
||||
email_address: request.customer_email.clone(),
|
||||
phone_number: request.customer_phone.clone(),
|
||||
first_name: request.customer_first_name.clone(),
|
||||
last_name: request.customer_last_name.clone(),
|
||||
}),
|
||||
};
|
||||
|
||||
run_async(async move {
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.post(&url)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Accept", "application/json")
|
||||
.bearer_auth(&token)
|
||||
.json(&pesapal_request)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send payment request: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let error_text = response.text().await.unwrap_or_default();
|
||||
return Err(format!("Pesapal payment request failed ({}): {}", status, error_text));
|
||||
}
|
||||
|
||||
// Debug: print raw response
|
||||
let response_text = response.text().await
|
||||
.map_err(|e| format!("Failed to read payment response: {}", e))?;
|
||||
println!("=== PESAPAL PAYMENT RESPONSE ===");
|
||||
println!("{}", response_text);
|
||||
println!("=================================");
|
||||
|
||||
let pesapal_response: PesapalSubmitOrderResponse = serde_json::from_str(&response_text)
|
||||
.map_err(|e| format!("Failed to parse payment response: {}", e))?;
|
||||
|
||||
if let Some(error) = pesapal_response.error {
|
||||
return Err(format!("Pesapal payment error: {}", error));
|
||||
}
|
||||
|
||||
Ok(PaymentResponse {
|
||||
payment_url: pesapal_response.redirect_url.unwrap_or_default(),
|
||||
order_tracking_id: pesapal_response.order_tracking_id.unwrap_or_default(),
|
||||
merchant_reference: pesapal_response.merchant_reference.unwrap_or_default(),
|
||||
status: pesapal_response.status.unwrap_or_default(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get Pesapal payment status
|
||||
fn get_pesapal_status(
|
||||
&self,
|
||||
order_tracking_id: &str,
|
||||
) -> Result<PaymentStatus, String> {
|
||||
let token = self.pesapal_authenticate()?;
|
||||
let order_tracking_id = order_tracking_id.to_string();
|
||||
|
||||
let url = format!(
|
||||
"{}/api/Transactions/GetTransactionStatus?orderTrackingId={}",
|
||||
self.get_base_url(),
|
||||
order_tracking_id
|
||||
);
|
||||
|
||||
run_async(async move {
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(&url)
|
||||
.header("Accept", "application/json")
|
||||
.bearer_auth(&token)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send status request: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let error_text = response.text().await.unwrap_or_default();
|
||||
return Err(format!("Pesapal status request failed ({}): {}", status, error_text));
|
||||
}
|
||||
|
||||
// Debug: print raw response
|
||||
let response_text = response.text().await
|
||||
.map_err(|e| format!("Failed to read status response: {}", e))?;
|
||||
println!("=== PESAPAL STATUS RESPONSE ===");
|
||||
println!("{}", response_text);
|
||||
println!("================================");
|
||||
|
||||
let status_response: PesapalTransactionStatusResponse = serde_json::from_str(&response_text)
|
||||
.map_err(|e| format!("Failed to parse status response: {}", e))?;
|
||||
|
||||
if let Some(error) = status_response.error {
|
||||
return Err(format!("Pesapal status error: {}", error));
|
||||
}
|
||||
|
||||
Ok(PaymentStatus {
|
||||
order_tracking_id: order_tracking_id.to_string(),
|
||||
merchant_reference: status_response.merchant_reference,
|
||||
status: status_response.payment_status_description,
|
||||
amount: status_response.amount,
|
||||
currency: status_response.currency,
|
||||
payment_method: status_response.payment_method,
|
||||
transaction_id: status_response.confirmation_code,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
630
lib/osiris/core/objects/money/rhai.rs
Normal file
630
lib/osiris/core/objects/money/rhai.rs
Normal file
@@ -0,0 +1,630 @@
|
||||
/// Rhai bindings for Money objects (Account, Asset, Transaction, PaymentClient)
|
||||
|
||||
use ::rhai::plugin::*;
|
||||
use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder};
|
||||
|
||||
use super::models::{Account, Asset, Transaction};
|
||||
use super::payments::{PaymentClient, PaymentRequest, PaymentResponse, PaymentStatus};
|
||||
|
||||
// ============================================================================
|
||||
// Account Module
|
||||
// ============================================================================
|
||||
|
||||
type RhaiAccount = Account;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_account_module {
|
||||
use super::RhaiAccount;
|
||||
|
||||
#[rhai_fn(name = "new_account", return_raw)]
|
||||
pub fn new_account() -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
Ok(Account::new(0))
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "owner_id", return_raw)]
|
||||
pub fn set_owner_id(
|
||||
account: &mut RhaiAccount,
|
||||
owner_id: i64,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(account);
|
||||
*account = owned.owner_id(owner_id as u32);
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "address", return_raw)]
|
||||
pub fn set_address(
|
||||
account: &mut RhaiAccount,
|
||||
address: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(account);
|
||||
*account = owned.address(address);
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "balance", return_raw)]
|
||||
pub fn set_balance(
|
||||
account: &mut RhaiAccount,
|
||||
balance: f64,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(account);
|
||||
*account = owned.balance(balance);
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "currency", return_raw)]
|
||||
pub fn set_currency(
|
||||
account: &mut RhaiAccount,
|
||||
currency: String,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(account);
|
||||
*account = owned.currency(currency);
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "assetid", return_raw)]
|
||||
pub fn set_assetid(
|
||||
account: &mut RhaiAccount,
|
||||
assetid: i64,
|
||||
) -> Result<RhaiAccount, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(account);
|
||||
*account = owned.assetid(assetid as u32);
|
||||
Ok(account.clone())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(name = "get_id")]
|
||||
pub fn get_id(account: &mut RhaiAccount) -> i64 {
|
||||
account.base_data.id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_owner_id")]
|
||||
pub fn get_owner_id(account: &mut RhaiAccount) -> i64 {
|
||||
account.owner_id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_address")]
|
||||
pub fn get_address(account: &mut RhaiAccount) -> String {
|
||||
account.address.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_balance")]
|
||||
pub fn get_balance(account: &mut RhaiAccount) -> f64 {
|
||||
account.balance
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_currency")]
|
||||
pub fn get_currency(account: &mut RhaiAccount) -> String {
|
||||
account.currency.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Asset Module
|
||||
// ============================================================================
|
||||
|
||||
type RhaiAsset = Asset;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_asset_module {
|
||||
use super::RhaiAsset;
|
||||
|
||||
#[rhai_fn(name = "new_asset", return_raw)]
|
||||
pub fn new_asset() -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
Ok(Asset::new(0))
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "address", return_raw)]
|
||||
pub fn set_address(
|
||||
asset: &mut RhaiAsset,
|
||||
address: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(asset);
|
||||
*asset = owned.address(address);
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "asset_type", return_raw)]
|
||||
pub fn set_asset_type(
|
||||
asset: &mut RhaiAsset,
|
||||
asset_type: String,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(asset);
|
||||
*asset = owned.asset_type(asset_type);
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "issuer", return_raw)]
|
||||
pub fn set_issuer(
|
||||
asset: &mut RhaiAsset,
|
||||
issuer: i64,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(asset);
|
||||
*asset = owned.issuer(issuer as u32);
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "supply", return_raw)]
|
||||
pub fn set_supply(
|
||||
asset: &mut RhaiAsset,
|
||||
supply: f64,
|
||||
) -> Result<RhaiAsset, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(asset);
|
||||
*asset = owned.supply(supply);
|
||||
Ok(asset.clone())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(name = "get_id")]
|
||||
pub fn get_id(asset: &mut RhaiAsset) -> i64 {
|
||||
asset.base_data.id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_address")]
|
||||
pub fn get_address(asset: &mut RhaiAsset) -> String {
|
||||
asset.address.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_asset_type")]
|
||||
pub fn get_asset_type(asset: &mut RhaiAsset) -> String {
|
||||
asset.asset_type.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_supply")]
|
||||
pub fn get_supply(asset: &mut RhaiAsset) -> f64 {
|
||||
asset.supply
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Transaction Module
|
||||
// ============================================================================
|
||||
|
||||
type RhaiTransaction = Transaction;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_transaction_module {
|
||||
use super::RhaiTransaction;
|
||||
|
||||
#[rhai_fn(name = "new_transaction", return_raw)]
|
||||
pub fn new_transaction() -> Result<RhaiTransaction, Box<EvalAltResult>> {
|
||||
Ok(Transaction::new(0))
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "source", return_raw)]
|
||||
pub fn set_source(
|
||||
tx: &mut RhaiTransaction,
|
||||
source: i64,
|
||||
) -> Result<RhaiTransaction, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(tx);
|
||||
*tx = owned.source(source as u32);
|
||||
Ok(tx.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "destination", return_raw)]
|
||||
pub fn set_destination(
|
||||
tx: &mut RhaiTransaction,
|
||||
destination: i64,
|
||||
) -> Result<RhaiTransaction, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(tx);
|
||||
*tx = owned.destination(destination as u32);
|
||||
Ok(tx.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "amount", return_raw)]
|
||||
pub fn set_amount(
|
||||
tx: &mut RhaiTransaction,
|
||||
amount: f64,
|
||||
) -> Result<RhaiTransaction, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(tx);
|
||||
*tx = owned.amount(amount);
|
||||
Ok(tx.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "assetid", return_raw)]
|
||||
pub fn set_assetid(
|
||||
tx: &mut RhaiTransaction,
|
||||
assetid: i64,
|
||||
) -> Result<RhaiTransaction, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(tx);
|
||||
*tx = owned.assetid(assetid as u32);
|
||||
Ok(tx.clone())
|
||||
}
|
||||
|
||||
// Getters
|
||||
#[rhai_fn(name = "get_id")]
|
||||
pub fn get_id(tx: &mut RhaiTransaction) -> i64 {
|
||||
tx.base_data.id as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_source")]
|
||||
pub fn get_source(tx: &mut RhaiTransaction) -> i64 {
|
||||
tx.source as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_destination")]
|
||||
pub fn get_destination(tx: &mut RhaiTransaction) -> i64 {
|
||||
tx.destination as i64
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_amount")]
|
||||
pub fn get_amount(tx: &mut RhaiTransaction) -> f64 {
|
||||
tx.amount
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_assetid")]
|
||||
pub fn get_assetid(tx: &mut RhaiTransaction) -> i64 {
|
||||
tx.assetid as i64
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Registration Functions
|
||||
// ============================================================================
|
||||
|
||||
/// Register money modules with the Rhai engine
|
||||
pub fn register_money_modules(parent_module: &mut Module) {
|
||||
// Register custom types
|
||||
parent_module.set_custom_type::<Account>("Account");
|
||||
parent_module.set_custom_type::<Asset>("Asset");
|
||||
parent_module.set_custom_type::<Transaction>("Transaction");
|
||||
parent_module.set_custom_type::<PaymentClient>("PaymentClient");
|
||||
parent_module.set_custom_type::<PaymentRequest>("PaymentRequest");
|
||||
parent_module.set_custom_type::<PaymentResponse>("PaymentResponse");
|
||||
parent_module.set_custom_type::<PaymentStatus>("PaymentStatus");
|
||||
|
||||
// Merge account functions
|
||||
let account_module = exported_module!(rhai_account_module);
|
||||
parent_module.merge(&account_module);
|
||||
|
||||
// Merge asset functions
|
||||
let asset_module = exported_module!(rhai_asset_module);
|
||||
parent_module.merge(&asset_module);
|
||||
|
||||
// Merge transaction functions
|
||||
let transaction_module = exported_module!(rhai_transaction_module);
|
||||
parent_module.merge(&transaction_module);
|
||||
|
||||
// Merge payment client functions
|
||||
let payment_module = exported_module!(rhai_payment_module);
|
||||
parent_module.merge(&payment_module);
|
||||
|
||||
// Merge ethereum wallet functions
|
||||
let eth_module = exported_module!(rhai_ethereum_module);
|
||||
parent_module.merge(ð_module);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Payment Provider Module
|
||||
// ============================================================================
|
||||
|
||||
type RhaiPaymentClient = PaymentClient;
|
||||
type RhaiPaymentRequest = PaymentRequest;
|
||||
type RhaiPaymentResponse = PaymentResponse;
|
||||
type RhaiPaymentStatus = PaymentStatus;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_payment_module {
|
||||
use super::{RhaiPaymentClient, RhaiPaymentRequest, RhaiPaymentResponse, RhaiPaymentStatus};
|
||||
use super::super::payments::{PaymentClient, PaymentRequest, PaymentResponse, PaymentStatus};
|
||||
use ::rhai::EvalAltResult;
|
||||
|
||||
// PaymentClient constructors
|
||||
#[rhai_fn(name = "new_payment_client_pesapal", return_raw)]
|
||||
pub fn new_pesapal_client(
|
||||
id: i64,
|
||||
consumer_key: String,
|
||||
consumer_secret: String,
|
||||
) -> Result<RhaiPaymentClient, Box<EvalAltResult>> {
|
||||
Ok(PaymentClient::pesapal(id as u32, consumer_key, consumer_secret))
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "new_payment_client_pesapal_sandbox", return_raw)]
|
||||
pub fn new_pesapal_sandbox_client(
|
||||
id: i64,
|
||||
consumer_key: String,
|
||||
consumer_secret: String,
|
||||
) -> Result<RhaiPaymentClient, Box<EvalAltResult>> {
|
||||
Ok(PaymentClient::pesapal_sandbox(id as u32, consumer_key, consumer_secret))
|
||||
}
|
||||
|
||||
// PaymentRequest constructor and builder methods
|
||||
#[rhai_fn(name = "new_payment_request", return_raw)]
|
||||
pub fn new_payment_request() -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
Ok(PaymentRequest {
|
||||
merchant_reference: String::new(),
|
||||
amount: 0.0,
|
||||
currency: String::from("USD"),
|
||||
description: String::new(),
|
||||
callback_url: String::new(),
|
||||
redirect_url: None,
|
||||
cancel_url: None,
|
||||
customer_email: None,
|
||||
customer_phone: None,
|
||||
customer_first_name: None,
|
||||
customer_last_name: None,
|
||||
})
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "amount", return_raw)]
|
||||
pub fn set_amount(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
amount: f64,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.amount = amount;
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "currency", return_raw)]
|
||||
pub fn set_currency(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
currency: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.currency = currency;
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "description", return_raw)]
|
||||
pub fn set_description(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
description: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.description = description;
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "callback_url", return_raw)]
|
||||
pub fn set_callback_url(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
url: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.callback_url = url;
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "merchant_reference", return_raw)]
|
||||
pub fn set_merchant_reference(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
reference: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.merchant_reference = reference;
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "customer_email", return_raw)]
|
||||
pub fn set_customer_email(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
email: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.customer_email = Some(email);
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "customer_phone", return_raw)]
|
||||
pub fn set_customer_phone(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
phone: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.customer_phone = Some(phone);
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "customer_name", return_raw)]
|
||||
pub fn set_customer_name(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
first_name: String,
|
||||
last_name: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.customer_first_name = Some(first_name);
|
||||
request.customer_last_name = Some(last_name);
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "redirect_url", return_raw)]
|
||||
pub fn set_redirect_url(
|
||||
request: &mut RhaiPaymentRequest,
|
||||
url: String,
|
||||
) -> Result<RhaiPaymentRequest, Box<EvalAltResult>> {
|
||||
request.redirect_url = Some(url);
|
||||
Ok(request.clone())
|
||||
}
|
||||
|
||||
// PaymentClient methods
|
||||
#[rhai_fn(name = "create_payment_link", return_raw)]
|
||||
pub fn create_payment_link(
|
||||
client: &mut RhaiPaymentClient,
|
||||
request: RhaiPaymentRequest,
|
||||
) -> Result<RhaiPaymentResponse, Box<EvalAltResult>> {
|
||||
client.create_payment_link(&request)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_payment_status", return_raw)]
|
||||
pub fn get_payment_status(
|
||||
client: &mut RhaiPaymentClient,
|
||||
order_tracking_id: String,
|
||||
) -> Result<RhaiPaymentStatus, Box<EvalAltResult>> {
|
||||
client.get_payment_status(&order_tracking_id)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
// PaymentResponse getters
|
||||
#[rhai_fn(name = "get_payment_url", pure)]
|
||||
pub fn get_payment_url(response: &mut RhaiPaymentResponse) -> String {
|
||||
response.payment_url.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_order_tracking_id", pure)]
|
||||
pub fn get_order_tracking_id(response: &mut RhaiPaymentResponse) -> String {
|
||||
response.order_tracking_id.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_merchant_reference", pure)]
|
||||
pub fn get_merchant_reference(response: &mut RhaiPaymentResponse) -> String {
|
||||
response.merchant_reference.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_status", pure)]
|
||||
pub fn get_response_status(response: &mut RhaiPaymentResponse) -> String {
|
||||
response.status.clone()
|
||||
}
|
||||
|
||||
// PaymentStatus getters
|
||||
#[rhai_fn(name = "get_status", pure)]
|
||||
pub fn get_payment_status_value(status: &mut RhaiPaymentStatus) -> String {
|
||||
status.status.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_amount", pure)]
|
||||
pub fn get_amount(status: &mut RhaiPaymentStatus) -> f64 {
|
||||
status.amount
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_currency", pure)]
|
||||
pub fn get_currency(status: &mut RhaiPaymentStatus) -> String {
|
||||
status.currency.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_payment_method", pure)]
|
||||
pub fn get_payment_method(status: &mut RhaiPaymentStatus) -> String {
|
||||
status.payment_method.clone().unwrap_or_default()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_transaction_id", pure)]
|
||||
pub fn get_transaction_id(status: &mut RhaiPaymentStatus) -> String {
|
||||
status.transaction_id.clone().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CustomType Implementations
|
||||
// ============================================================================
|
||||
|
||||
impl CustomType for Account {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("Account");
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for Asset {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("Asset");
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for Transaction {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("Transaction");
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for PaymentClient {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("PaymentClient");
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for PaymentRequest {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("PaymentRequest");
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for PaymentResponse {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("PaymentResponse");
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for PaymentStatus {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("PaymentStatus");
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Ethereum Wallet Module (Stub Implementation)
|
||||
// ============================================================================
|
||||
|
||||
/// Simple Ethereum wallet representation
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EthereumWallet {
|
||||
pub owner_id: u32,
|
||||
pub address: String,
|
||||
pub network: String,
|
||||
}
|
||||
|
||||
impl EthereumWallet {
|
||||
pub fn new() -> Self {
|
||||
// Generate a mock Ethereum address (in production, use ethers-rs or similar)
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let mock_address = format!("0x{:040x}", timestamp as u128);
|
||||
Self {
|
||||
owner_id: 0,
|
||||
address: mock_address,
|
||||
network: String::from("mainnet"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn owner_id(mut self, id: u32) -> Self {
|
||||
self.owner_id = id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn network(mut self, network: impl ToString) -> Self {
|
||||
self.network = network.to_string();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
type RhaiEthereumWallet = EthereumWallet;
|
||||
|
||||
#[export_module]
|
||||
mod rhai_ethereum_module {
|
||||
use super::RhaiEthereumWallet;
|
||||
use ::rhai::EvalAltResult;
|
||||
|
||||
#[rhai_fn(name = "new_ethereum_wallet", return_raw)]
|
||||
pub fn new_ethereum_wallet() -> Result<RhaiEthereumWallet, Box<EvalAltResult>> {
|
||||
Ok(EthereumWallet::new())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "owner_id", return_raw)]
|
||||
pub fn set_owner_id(
|
||||
wallet: &mut RhaiEthereumWallet,
|
||||
owner_id: i64,
|
||||
) -> Result<RhaiEthereumWallet, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(wallet);
|
||||
*wallet = owned.owner_id(owner_id as u32);
|
||||
Ok(wallet.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "network", return_raw)]
|
||||
pub fn set_network(
|
||||
wallet: &mut RhaiEthereumWallet,
|
||||
network: String,
|
||||
) -> Result<RhaiEthereumWallet, Box<EvalAltResult>> {
|
||||
let owned = std::mem::take(wallet);
|
||||
*wallet = owned.network(network);
|
||||
Ok(wallet.clone())
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_address")]
|
||||
pub fn get_address(wallet: &mut RhaiEthereumWallet) -> String {
|
||||
wallet.address.clone()
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "get_network")]
|
||||
pub fn get_network(wallet: &mut RhaiEthereumWallet) -> String {
|
||||
wallet.network.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomType for EthereumWallet {
|
||||
fn build(mut builder: TypeBuilder<Self>) {
|
||||
builder.with_name("EthereumWallet");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user