You've already forked AstralRinth
forked from didirus/AstralRinth
* wip: muralpay integration * Basic Mural Pay API bindings * Fix clippy * use dotenvy in muralpay example * Refactor payout creation code * wip: muralpay payout requests * Mural Pay payouts work * Fix clippy * add mural pay fees API * Work on payout fee API * Fees API for more payment methods * Fix CI * Temporarily disable Venmo and PayPal methods from frontend * wip: counterparties * Start on counterparties and payment methods API * Mural Pay multiple methods when fetching * Don't send supported_countries to frontend * Add countries to muralpay fiat methods * Compile fix * Add exchange rate info to fees endpoint * Add fees to premium Tremendous options * Add delivery email field to Tremendous payouts * Add Tremendous product category to payout methods * Add bank details API to muralpay * Fix CI * Fix CI * Remove prepaid visa, compute fees properly for Tremendous methods * Add more details to Tremendous errors * Add fees to Mural * Payout history route and bank details * Re-add legacy PayPal/Venmo options for US * move the mural bank details route * Add utoipa support to payout endpoints * address some PR comments * add CORS to new utoipa routes * Immediately approve mural payouts * Add currency support to Tremendous payouts * Currency forex * add forex to tremendous fee request * Add Mural balance to bank balance info * Add more Tremendous currencies support * Transaction payouts available use the correct date * Address my own review comment * Address PR comments * Change Mural withdrawal limit to 3k * maybe fix tremendous gift cards * Change how Mural minimum withdrawals are calculated * Tweak min/max withdrawal values --------- Co-authored-by: Calum H. <contact@cal.engineer> Co-authored-by: Alejandro González <me@alegon.dev>
118 lines
3.1 KiB
Rust
118 lines
3.1 KiB
Rust
use std::{collections::HashMap, fmt};
|
|
|
|
use bytes::Bytes;
|
|
use derive_more::{Display, Error, From};
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug, Display, Error, From)]
|
|
pub enum MuralError {
|
|
#[display("API error")]
|
|
Api(ApiError),
|
|
#[display("request error")]
|
|
Request(reqwest::Error),
|
|
#[display("failed to decode response\n{json:?}")]
|
|
#[from(skip)]
|
|
Decode {
|
|
source: serde_json::Error,
|
|
json: Bytes,
|
|
},
|
|
#[display("failed to decode error response\n{json:?}")]
|
|
#[from(skip)]
|
|
DecodeError {
|
|
source: serde_json::Error,
|
|
json: Bytes,
|
|
},
|
|
}
|
|
|
|
pub type Result<T, E = MuralError> = std::result::Result<T, E>;
|
|
|
|
#[derive(Debug, Display, Error, From)]
|
|
pub enum TransferError {
|
|
#[display("no transfer API key")]
|
|
NoTransferKey,
|
|
#[display("API error")]
|
|
Api(Box<ApiError>),
|
|
#[display("request error")]
|
|
Request(reqwest::Error),
|
|
#[display("failed to decode response\n{json:?}")]
|
|
#[from(skip)]
|
|
Decode {
|
|
source: serde_json::Error,
|
|
json: Bytes,
|
|
},
|
|
#[display("failed to decode error response\n{json:?}")]
|
|
#[from(skip)]
|
|
DecodeError {
|
|
source: serde_json::Error,
|
|
json: Bytes,
|
|
},
|
|
}
|
|
|
|
impl From<MuralError> for TransferError {
|
|
fn from(value: MuralError) -> Self {
|
|
match value {
|
|
MuralError::Api(x) => Self::Api(Box::new(x)),
|
|
MuralError::Request(x) => Self::Request(x),
|
|
MuralError::Decode { source, json } => {
|
|
Self::Decode { source, json }
|
|
}
|
|
MuralError::DecodeError { source, json } => {
|
|
Self::DecodeError { source, json }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Error)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ApiError {
|
|
pub error_instance_id: Uuid,
|
|
pub name: String,
|
|
pub message: String,
|
|
#[serde(deserialize_with = "one_or_many")]
|
|
#[serde(default)]
|
|
pub details: Vec<String>,
|
|
#[serde(default)]
|
|
pub params: HashMap<String, serde_json::Value>,
|
|
}
|
|
|
|
fn one_or_many<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
#[derive(Deserialize)]
|
|
#[serde(untagged)]
|
|
enum OneOrMany {
|
|
One(String),
|
|
Many(Vec<String>),
|
|
}
|
|
|
|
match OneOrMany::deserialize(deserializer)? {
|
|
OneOrMany::One(s) => Ok(vec![s]),
|
|
OneOrMany::Many(v) => Ok(v),
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for ApiError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let mut lines = vec![self.message.to_string()];
|
|
|
|
if !self.details.is_empty() {
|
|
lines.push("details:".into());
|
|
lines.extend(self.details.iter().map(|s| format!("- {s}")));
|
|
}
|
|
|
|
if !self.params.is_empty() {
|
|
lines.push("params:".into());
|
|
lines
|
|
.extend(self.params.iter().map(|(k, v)| format!("- {k}: {v}")));
|
|
}
|
|
|
|
lines.push(format!("error name: {}", self.name));
|
|
lines.push(format!("error instance id: {}", self.error_instance_id));
|
|
|
|
write!(f, "{}", lines.join("\n"))
|
|
}
|
|
}
|