Generates an HMAC-SHA256 signature for eSewa payment authentication.
Signature:
pub fn generate_signature(
total_amount: &str,
transaction_uuid: &str,
product_code: &str,
secret_key: &str,
) -> StringParameters:
total_amount: &str- The total payment amount (including tax, fees, etc.)transaction_uuid: &str- Unique transaction identifierproduct_code: &str- eSewa merchant product code (e.g., "EPAYTEST" for sandbox)secret_key: &str- Secret key provided by eSewa merchant account
Returns: String - Base64-encoded HMAC-SHA256 signature
Example:
let sig = generate_signature("110", "id-123-abc", "EPAYTEST", "8gBm/:&EnhH.1/q");
assert!(!sig.is_empty());Notes:
- The signature is computed over the string:
total_amount={amount},transaction_uuid={uuid},product_code={code} - Same inputs will always produce the same signature
- Used internally by
pay_with_esewabut can be called directly if needed
Initiates a payment with eSewa and returns the payment redirect URL.
Signature:
pub async fn pay_with_esewa(
request: EsewaPaymentRequest,
secret_key: &str,
env: EsewaEnvironment,
) -> Result<String, PaymentError>Parameters:
request: EsewaPaymentRequest- Complete payment request detailssecret_key: &str- eSewa merchant secret key
Returns: Result<String, PaymentError>
- Ok(String) - Payment URL to redirect the user to
- Err(PaymentError) - Error if request fails
Example:
let request = EsewaPaymentRequest {
amount: "100".to_string(),
tax_amount: "10".to_string(),
total_amount: "110".to_string(),
transaction_uuid: generate_transaction_uuid(),
product_code: "EPAYTEST".to_string(),
product_service_charge: "0".to_string(),
product_delivery_charge: "0".to_string(),
success_url: "http://example.com/success".to_string(),
failure_url: "http://example.com/failure".to_string(),
signed_field_names: "total_amount,transaction_uuid,product_code".to_string(),
};
let url = pay_with_esewa(request, "8gBm/:&EnhH.1/q", EsewaEnvironment::Sandbox).await?;
// Redirect user to `url`Error Handling:
match pay_with_esewa(request, secret_key, EsewaEnvironment::Sandbox).await {
Ok(url) => {
// Success - redirect user
}
Err(PaymentError::NetworkError(e)) => {
// Network/connection error
}
Err(PaymentError::InvalidResponse(e)) => {
// Invalid API response
}
Err(e) => {
// Other errors
}
}Validates and decodes the payment response from eSewa callback.
Signature:
pub fn validate_esewa_response(
encoded_data: &str,
secret_key: &str,
) -> Result<ValidationResult, PaymentError>Parameters:
encoded_data: &str- Base64-encoded JSON data from eSewa's?data=query parametersecret_key: &str- eSewa merchant secret key
Returns: Result<ValidationResult, PaymentError>
- Ok(ValidationResult) - Decoded response with signature validation status
- Err(PaymentError) - Error if decoding or parsing fails
Example:
// In your success callback handler
let encoded = query_params.get("data").unwrap();
match validate_esewa_response(encoded, "8gBm/:&EnhH.1/q") {
Ok(result) => {
if result.signature_valid {
// Process successful payment
println!("Transaction: {}", result.response.transaction_code);
println!("Amount: {}", result.response.total_amount);
} else {
// Invalid signature - potential fraud
log::warn!("Invalid signature detected!");
}
}
Err(e) => {
// Handle validation error
}
}Important:
- Always check
result.signature_validbefore processing payment - Log all validation failures for security monitoring
- Invalid signatures may indicate tampering or fraud attempts
Generates a unique transaction identifier.
Signature:
pub fn generate_transaction_uuid() -> StringReturns: String - UUID in format id-<milliseconds>-<random_chars>
Example:
let uuid1 = generate_transaction_uuid();
let uuid2 = generate_transaction_uuid();
// Example outputs:
// "id-1763263100223-b26yhc0gy"
// "id-1763263100224-x8kp3md9z"
assert_ne!(uuid1, uuid2);Format Details:
- Prefix: "id-"
- Timestamp: Current time in milliseconds since UNIX epoch
- Random suffix: 9 alphanumeric characters (base36)
Represents the complete payment request data.
pub struct EsewaPaymentRequest {
pub amount: String,
pub tax_amount: String,
pub total_amount: String,
pub transaction_uuid: String,
pub product_code: String,
pub product_service_charge: String,
pub product_delivery_charge: String,
pub success_url: String,
pub failure_url: String,
pub signed_field_names: String,
}Fields:
amount- Base payment amount (without tax or fees)tax_amount- Tax amounttotal_amount- Total amount (amount + tax + service + delivery)transaction_uuid- Unique transaction identifierproduct_code- eSewa merchant product codeproduct_service_charge- Service charge amountproduct_delivery_charge- Delivery charge amountsuccess_url- URL to redirect on successful paymentfailure_url- URL to redirect on failed paymentsigned_field_names- Comma-separated list of fields to sign (typically "total_amount,transaction_uuid,product_code")
Traits: Debug, Clone, Serialize, Deserialize
Represents the decoded payment response from eSewa.
pub struct EsewaPaymentResponse {
pub transaction_code: String,
pub status: String,
pub total_amount: String,
pub transaction_uuid: String,
pub product_code: String,
pub signed_field_names: String,
pub signature: String,
}Fields:
transaction_code- eSewa's transaction reference codestatus- Payment status (e.g., "COMPLETE", "FAILED")total_amount- Total amount paidtransaction_uuid- Your original transaction UUIDproduct_code- Product code usedsigned_field_names- Fields included in signaturesignature- HMAC signature from eSewa
Traits: Debug, Clone, Serialize, Deserialize
Status Values:
"COMPLETE"- Payment successful"FAILED"- Payment failed- Other values may exist - check eSewa documentation
Result of response validation including signature check.
pub struct ValidationResult {
pub signature_valid: bool,
pub response: EsewaPaymentResponse,
}Fields:
signature_valid-trueif signature matches,falseotherwiseresponse- Decoded payment response data
Traits: Debug, Clone, Serialize, Deserialize
Usage Pattern:
let result = validate_esewa_response(encoded, secret_key)?;
if result.signature_valid && result.response.status == "COMPLETE" {
// Process payment
}Error type for payment operations.
pub enum PaymentError {
NetworkError(String),
InvalidResponse(String),
SignatureError(String),
DecodeError(String),
}Variants:
NetworkError- HTTP request failed (network issue, timeout, etc.)InvalidResponse- Server returned unexpected response (non-200 status, etc.)SignatureError- Signature generation or validation failedDecodeError- Base64 decoding or JSON parsing failed
Traits: Debug, Display, Error
Example Handling:
match operation() {
Err(PaymentError::NetworkError(e)) => {
log::error!("Network error: {}", e);
// Retry logic
}
Err(PaymentError::DecodeError(e)) => {
log::error!("Invalid data format: {}", e);
// Don't retry - bad data
}
Err(e) => {
log::error!("Payment error: {}", e);
}
Ok(result) => { /* success */ }
}All functions are thoroughly tested. Run tests with:
cargo testSee tests/integration_tests.rs for comprehensive test examples.
All functions are thread-safe and can be called concurrently.
pay_with_esewa requires an async runtime (e.g., tokio):
[dependencies]
tokio = { version = "1", features = ["full"] }#[tokio::main]
async fn main() {
// Use async functions
}