From cd09fe080186f5b7d3d3d07d4ed0accc23492d80 Mon Sep 17 00:00:00 2001 From: silverpill Date: Wed, 17 Aug 2022 15:36:27 +0000 Subject: [PATCH] Add API method for generating invoices --- docs/openapi.yaml | 46 +++++++++++++++++++++++++ src/mastodon_api/subscriptions/types.rs | 30 +++++++++++++++- src/mastodon_api/subscriptions/views.rs | 40 +++++++++++++++++++-- src/models/invoices/mod.rs | 2 +- src/monero/wallet.rs | 17 +++++++++ 5 files changed, 131 insertions(+), 4 deletions(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 14cf0dc..f19ce5a 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -684,6 +684,34 @@ paths: description: User's wallet address is not known or not verified 418: description: Blockchain integration is not enabled + /api/v1/subscriptions/invoices: + post: + summary: Create invoice + requestBody: + content: + application/json: + schema: + type: object + properties: + sender: + description: Sender actor address (webfinger account URI). + type: string + example: user@example.org + recipient: + description: Recipient ID. + type: string + format: uuid + responses: + 200: + description: Invoice created. + content: + application/json: + schema: + $ref: '#/components/schemas/Invoice' + 404: + description: Sender or recipient not found. + 418: + description: Blockchain integration is not enabled. /api/v1/timelines/public: get: summary: View local public posts. @@ -924,6 +952,24 @@ components: description: IPFS gateway URL. type: string nullable: true + Invoice: + type: object + properties: + id: + description: Invoice ID. + type: string + format: uuid + sender_id: + description: The profile ID of the sender. + type: string + format: uuid + recipient_id: + description: The profile ID of the recipient. + type: string + format: uuid + payment_address: + description: Payment address. + type: string Mention: type: object properties: diff --git a/src/mastodon_api/subscriptions/types.rs b/src/mastodon_api/subscriptions/types.rs index f469aa6..fadafca 100644 --- a/src/mastodon_api/subscriptions/types.rs +++ b/src/mastodon_api/subscriptions/types.rs @@ -1,4 +1,32 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::models::invoices::types::DbInvoice; + +#[derive(Deserialize)] +pub struct InvoiceData { + pub sender: String, // acct + pub recipient: Uuid, +} + +#[derive(Serialize)] +pub struct Invoice { + pub id: Uuid, + pub sender_id: Uuid, + pub recipient_id: Uuid, + pub payment_address: String, +} + +impl From for Invoice { + fn from(value: DbInvoice) -> Self { + Self { + id: value.id, + sender_id: value.sender_id, + recipient_id: value.recipient_id, + payment_address: value.payment_address, + } + } +} #[derive(Deserialize)] pub struct SubscriptionQueryParams { diff --git a/src/mastodon_api/subscriptions/views.rs b/src/mastodon_api/subscriptions/views.rs index 3bf1ada..7ca7d9b 100644 --- a/src/mastodon_api/subscriptions/views.rs +++ b/src/mastodon_api/subscriptions/views.rs @@ -1,4 +1,4 @@ -use actix_web::{web, HttpResponse, Scope}; +use actix_web::{post, web, HttpResponse, Scope}; use actix_web_httpauth::extractors::bearer::BearerAuth; use crate::activitypub::builders::update_person::prepare_update_person; @@ -12,15 +12,23 @@ use crate::ethereum::subscriptions::{ }; use crate::mastodon_api::accounts::types::Account; use crate::mastodon_api::oauth::auth::get_current_user; -use crate::models::profiles::queries::update_profile; +use crate::models::invoices::queries::create_invoice; +use crate::models::profiles::queries::{ + get_profile_by_acct, + update_profile, +}; use crate::models::profiles::types::{ MoneroSubscription, PaymentOption, PaymentType, ProfileUpdateData, }; +use crate::models::users::queries::get_user_by_id; +use crate::monero::wallet::create_monero_address; use crate::utils::currencies::Currency; use super::types::{ + Invoice, + InvoiceData, SubscriptionQueryParams, SubscriptionSettings, }; @@ -123,8 +131,36 @@ pub async fn subscriptions_enabled( Ok(HttpResponse::Ok().json(account)) } +#[post("/invoices")] +async fn create_invoice_view( + config: web::Data, + db_pool: web::Data, + invoice_data: web::Json, +) -> Result { + let monero_config = config.blockchain() + .ok_or(HttpError::NotSupported)? + .monero_config() + .ok_or(HttpError::NotSupported)?; + let db_client = &**get_database_client(&db_pool).await?; + let sender = get_profile_by_acct(db_client, &invoice_data.sender).await?; + let recipient = get_user_by_id(db_client, &invoice_data.recipient).await?; + 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)) +} + pub fn subscription_api_scope() -> Scope { web::scope("/api/v1/subscriptions") .route("/authorize", web::get().to(authorize_subscription)) .route("/enable", web::post().to(subscriptions_enabled)) + .service(create_invoice_view) } diff --git a/src/models/invoices/mod.rs b/src/models/invoices/mod.rs index ec98503..0333ab5 100644 --- a/src/models/invoices/mod.rs +++ b/src/models/invoices/mod.rs @@ -1,2 +1,2 @@ pub mod queries; -mod types; +pub mod types; diff --git a/src/monero/wallet.rs b/src/monero/wallet.rs index a50fd6a..70ee05b 100644 --- a/src/monero/wallet.rs +++ b/src/monero/wallet.rs @@ -1,7 +1,10 @@ use monero_rpc::RpcClient; +use monero_rpc::monero::Address; use crate::config::MoneroConfig; +const DEFAULT_ACCOUNT: u32 = 0; + #[derive(thiserror::Error, Debug)] pub enum MoneroError { #[error(transparent)] @@ -19,3 +22,17 @@ pub async fn create_monero_wallet( wallet_client.create_wallet(name, password, language).await?; Ok(()) } + +pub async fn create_monero_address( + config: &MoneroConfig, +) -> Result { + let wallet_client = RpcClient::new(config.wallet_url.clone()).wallet(); + wallet_client.open_wallet( + config.wallet_name.clone(), + config.wallet_password.clone(), + ).await?; + let (address, address_index) = + wallet_client.create_address(DEFAULT_ACCOUNT, None).await?; + log::info!("created monero address {}/{}", DEFAULT_ACCOUNT, address_index); + Ok(address) +}