2022-09-02 18:28:27 +00:00
|
|
|
use actix_web::{get, post, web, HttpResponse, Scope};
|
2022-08-25 15:21:59 +00:00
|
|
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
2022-09-07 10:43:50 +00:00
|
|
|
use uuid::Uuid;
|
2022-08-25 15:21:59 +00:00
|
|
|
|
|
|
|
use crate::activitypub::builders::update_person::prepare_update_person;
|
|
|
|
use crate::config::Config;
|
|
|
|
use crate::database::{Pool, get_database_client};
|
|
|
|
use crate::errors::{HttpError, ValidationError};
|
|
|
|
use crate::ethereum::contracts::ContractSet;
|
|
|
|
use crate::ethereum::subscriptions::{
|
|
|
|
create_subscription_signature,
|
|
|
|
is_registered_recipient,
|
|
|
|
};
|
|
|
|
use crate::mastodon_api::accounts::types::Account;
|
|
|
|
use crate::mastodon_api::oauth::auth::get_current_user;
|
2022-09-07 10:43:50 +00:00
|
|
|
use crate::models::invoices::queries::{create_invoice, get_invoice_by_id};
|
2022-08-17 15:36:27 +00:00
|
|
|
use crate::models::profiles::queries::{
|
2022-09-06 15:44:30 +00:00
|
|
|
get_profile_by_id,
|
2022-08-17 15:36:27 +00:00
|
|
|
update_profile,
|
|
|
|
};
|
2022-08-25 23:01:08 +00:00
|
|
|
use crate::models::profiles::types::{
|
2022-08-25 21:51:18 +00:00
|
|
|
MoneroSubscription,
|
2022-08-25 23:01:08 +00:00
|
|
|
PaymentOption,
|
|
|
|
PaymentType,
|
|
|
|
ProfileUpdateData,
|
|
|
|
};
|
2022-09-05 22:08:38 +00:00
|
|
|
use crate::models::subscriptions::queries::get_subscription_by_participants;
|
2022-08-17 15:36:27 +00:00
|
|
|
use crate::models::users::queries::get_user_by_id;
|
|
|
|
use crate::monero::wallet::create_monero_address;
|
2022-08-25 23:01:08 +00:00
|
|
|
use crate::utils::currencies::Currency;
|
|
|
|
use super::types::{
|
2022-08-17 15:36:27 +00:00
|
|
|
Invoice,
|
|
|
|
InvoiceData,
|
2022-09-05 22:08:38 +00:00
|
|
|
SubscriptionAuthorizationQueryParams,
|
|
|
|
SubscriptionDetails,
|
2022-09-02 18:28:27 +00:00
|
|
|
SubscriptionOption,
|
2022-09-05 22:08:38 +00:00
|
|
|
SubscriptionQueryParams,
|
2022-08-25 23:01:08 +00:00
|
|
|
};
|
2022-08-25 15:21:59 +00:00
|
|
|
|
2022-09-08 09:58:00 +00:00
|
|
|
#[get("/authorize")]
|
2022-08-25 15:21:59 +00:00
|
|
|
pub async fn authorize_subscription(
|
|
|
|
auth: BearerAuth,
|
|
|
|
config: web::Data<Config>,
|
|
|
|
db_pool: web::Data<Pool>,
|
2022-09-05 22:08:38 +00:00
|
|
|
query_params: web::Query<SubscriptionAuthorizationQueryParams>,
|
2022-08-25 15:21:59 +00:00
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
2022-08-30 16:13:58 +00:00
|
|
|
let ethereum_config = config.blockchain()
|
2022-08-25 15:21:59 +00:00
|
|
|
.ok_or(HttpError::NotSupported)?
|
|
|
|
.ethereum_config()
|
|
|
|
.ok_or(HttpError::NotSupported)?;
|
2022-08-28 18:49:23 +00:00
|
|
|
// The user must have a public ethereum address,
|
2022-08-25 15:21:59 +00:00
|
|
|
// because subscribers should be able
|
|
|
|
// to verify that payments are actually sent to the recipient.
|
|
|
|
let wallet_address = current_user
|
2022-08-28 18:49:23 +00:00
|
|
|
.public_wallet_address(&Currency::Ethereum)
|
2022-08-25 15:21:59 +00:00
|
|
|
.ok_or(HttpError::PermissionError)?;
|
|
|
|
let signature = create_subscription_signature(
|
|
|
|
ethereum_config,
|
|
|
|
&wallet_address,
|
|
|
|
query_params.price,
|
|
|
|
).map_err(|_| HttpError::InternalError)?;
|
|
|
|
Ok(HttpResponse::Ok().json(signature))
|
|
|
|
}
|
|
|
|
|
2022-09-02 18:28:27 +00:00
|
|
|
#[get("/options")]
|
|
|
|
async fn get_subscription_options(
|
|
|
|
auth: BearerAuth,
|
|
|
|
db_pool: web::Data<Pool>,
|
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
|
|
|
let options: Vec<SubscriptionOption> = current_user.profile
|
|
|
|
.payment_options.into_inner().into_iter()
|
|
|
|
.filter_map(SubscriptionOption::from_payment_option)
|
|
|
|
.collect();
|
|
|
|
Ok(HttpResponse::Ok().json(options))
|
|
|
|
}
|
|
|
|
|
2022-09-08 10:05:34 +00:00
|
|
|
#[post("/options")]
|
2022-09-15 20:18:14 +00:00
|
|
|
pub async fn register_subscription_option(
|
2022-08-25 15:21:59 +00:00
|
|
|
auth: BearerAuth,
|
|
|
|
config: web::Data<Config>,
|
|
|
|
db_pool: web::Data<Pool>,
|
|
|
|
maybe_blockchain: web::Data<Option<ContractSet>>,
|
2022-09-02 18:28:27 +00:00
|
|
|
subscription_option: web::Json<SubscriptionOption>,
|
2022-08-25 15:21:59 +00:00
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
|
|
|
|
2022-09-15 20:18:14 +00:00
|
|
|
let maybe_payment_option = match subscription_option.into_inner() {
|
2022-09-02 18:28:27 +00:00
|
|
|
SubscriptionOption::Ethereum => {
|
2022-08-30 16:13:58 +00:00
|
|
|
let ethereum_config = config.blockchain()
|
2022-08-29 19:17:16 +00:00
|
|
|
.and_then(|conf| conf.ethereum_config())
|
|
|
|
.ok_or(HttpError::NotSupported)?;
|
2022-08-25 23:01:08 +00:00
|
|
|
let contract_set = maybe_blockchain.as_ref().as_ref()
|
|
|
|
.ok_or(HttpError::NotSupported)?;
|
|
|
|
let wallet_address = current_user
|
|
|
|
.public_wallet_address(&Currency::Ethereum)
|
|
|
|
.ok_or(HttpError::PermissionError)?;
|
2022-09-15 20:18:14 +00:00
|
|
|
if current_user.profile.payment_options
|
2022-08-25 23:01:08 +00:00
|
|
|
.any(PaymentType::EthereumSubscription)
|
|
|
|
{
|
2022-09-15 20:18:14 +00:00
|
|
|
// Ignore attempts to update payment option
|
|
|
|
None
|
|
|
|
} else {
|
2022-08-25 23:01:08 +00:00
|
|
|
let is_registered = is_registered_recipient(
|
|
|
|
contract_set,
|
|
|
|
&wallet_address,
|
|
|
|
).await.map_err(|_| HttpError::InternalError)?;
|
|
|
|
if !is_registered {
|
|
|
|
return Err(ValidationError("recipient is not registered").into());
|
|
|
|
};
|
2022-09-15 20:18:14 +00:00
|
|
|
Some(PaymentOption::ethereum_subscription(
|
2022-08-29 19:17:16 +00:00
|
|
|
ethereum_config.chain_id.clone(),
|
2022-09-15 20:18:14 +00:00
|
|
|
))
|
|
|
|
}
|
2022-08-25 23:01:08 +00:00
|
|
|
},
|
2022-09-02 18:28:27 +00:00
|
|
|
SubscriptionOption::Monero { price, payout_address } => {
|
2022-08-25 21:51:18 +00:00
|
|
|
let monero_config = config.blockchain()
|
|
|
|
.and_then(|conf| conf.monero_config())
|
|
|
|
.ok_or(HttpError::NotSupported)?;
|
2022-09-15 20:18:14 +00:00
|
|
|
let payment_info = MoneroSubscription {
|
|
|
|
chain_id: monero_config.chain_id.clone(),
|
|
|
|
price,
|
|
|
|
payout_address,
|
2022-08-25 21:51:18 +00:00
|
|
|
};
|
2022-09-15 20:18:14 +00:00
|
|
|
Some(PaymentOption::MoneroSubscription(payment_info))
|
2022-08-25 23:01:08 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
if let Some(payment_option) = maybe_payment_option {
|
2022-08-25 15:21:59 +00:00
|
|
|
let mut profile_data = ProfileUpdateData::from(¤t_user.profile);
|
2022-09-15 20:18:14 +00:00
|
|
|
profile_data.add_payment_option(payment_option);
|
2022-08-25 15:21:59 +00:00
|
|
|
current_user.profile = update_profile(
|
|
|
|
db_client,
|
|
|
|
¤t_user.id,
|
|
|
|
profile_data,
|
|
|
|
).await?;
|
|
|
|
|
|
|
|
// Federate
|
|
|
|
prepare_update_person(db_client, config.instance(), ¤t_user)
|
|
|
|
.await?.spawn_deliver();
|
|
|
|
};
|
|
|
|
|
|
|
|
let account = Account::from_user(current_user, &config.instance_url());
|
|
|
|
Ok(HttpResponse::Ok().json(account))
|
|
|
|
}
|
|
|
|
|
2022-09-05 22:08:38 +00:00
|
|
|
#[get("/find")]
|
|
|
|
async fn find_subscription(
|
|
|
|
db_pool: web::Data<Pool>,
|
|
|
|
query_params: web::Query<SubscriptionQueryParams>,
|
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let subscription = get_subscription_by_participants(
|
|
|
|
db_client,
|
|
|
|
&query_params.sender_id,
|
|
|
|
&query_params.recipient_id,
|
|
|
|
).await?;
|
|
|
|
let details = SubscriptionDetails {
|
|
|
|
id: subscription.id,
|
|
|
|
expires_at: subscription.expires_at,
|
|
|
|
};
|
|
|
|
Ok(HttpResponse::Ok().json(details))
|
|
|
|
}
|
|
|
|
|
2022-08-17 15:36:27 +00:00
|
|
|
#[post("/invoices")]
|
|
|
|
async fn create_invoice_view(
|
|
|
|
config: web::Data<Config>,
|
|
|
|
db_pool: web::Data<Pool>,
|
|
|
|
invoice_data: web::Json<InvoiceData>,
|
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let monero_config = config.blockchain()
|
|
|
|
.ok_or(HttpError::NotSupported)?
|
|
|
|
.monero_config()
|
|
|
|
.ok_or(HttpError::NotSupported)?;
|
2022-09-07 11:57:17 +00:00
|
|
|
if invoice_data.sender_id == invoice_data.recipient_id {
|
|
|
|
return Err(ValidationError("sender must be different from recipient").into());
|
|
|
|
};
|
2022-08-17 15:36:27 +00:00
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
2022-09-06 15:44:30 +00:00
|
|
|
let sender = get_profile_by_id(db_client, &invoice_data.sender_id).await?;
|
|
|
|
let recipient = get_user_by_id(db_client, &invoice_data.recipient_id).await?;
|
2022-08-17 15:36:27 +00:00
|
|
|
let payment_address = create_monero_address(monero_config).await
|
|
|
|
.map_err(|_| HttpError::InternalError)?
|
|
|
|
.to_string();
|
|
|
|
let db_invoice = create_invoice(
|
|
|
|
db_client,
|
|
|
|
&sender.id,
|
|
|
|
&recipient.id,
|
|
|
|
&monero_config.chain_id,
|
|
|
|
&payment_address,
|
|
|
|
).await?;
|
|
|
|
let invoice = Invoice::from(db_invoice);
|
|
|
|
Ok(HttpResponse::Ok().json(invoice))
|
|
|
|
}
|
|
|
|
|
2022-09-07 10:43:50 +00:00
|
|
|
#[get("/invoices/{invoice_id}")]
|
|
|
|
async fn get_invoice(
|
|
|
|
db_pool: web::Data<Pool>,
|
|
|
|
invoice_id: web::Path<Uuid>,
|
|
|
|
) -> Result<HttpResponse, HttpError> {
|
|
|
|
let db_client = &**get_database_client(&db_pool).await?;
|
|
|
|
let db_invoice = get_invoice_by_id(db_client, &invoice_id).await?;
|
|
|
|
let invoice = Invoice::from(db_invoice);
|
|
|
|
Ok(HttpResponse::Ok().json(invoice))
|
|
|
|
}
|
|
|
|
|
2022-08-25 15:21:59 +00:00
|
|
|
pub fn subscription_api_scope() -> Scope {
|
|
|
|
web::scope("/api/v1/subscriptions")
|
2022-09-08 09:58:00 +00:00
|
|
|
.service(authorize_subscription)
|
2022-09-02 18:28:27 +00:00
|
|
|
.service(get_subscription_options)
|
2022-09-15 20:18:14 +00:00
|
|
|
.service(register_subscription_option)
|
2022-09-05 22:08:38 +00:00
|
|
|
.service(find_subscription)
|
2022-08-17 15:36:27 +00:00
|
|
|
.service(create_invoice_view)
|
2022-09-07 10:43:50 +00:00
|
|
|
.service(get_invoice)
|
2022-08-25 15:21:59 +00:00
|
|
|
}
|