You've already forked AstralRinth
forked from didirus/AstralRinth
Mural Pay integration (#4520)
* 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>
This commit is contained in:
117
packages/muralpay/src/error.rs
Normal file
117
packages/muralpay/src/error.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
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"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user