Skip to content

Commit

Permalink
finish forwarding prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
joksas committed Aug 19, 2024
1 parent 1f67a5c commit 91737de
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
authors = ["RSS Blue", "Dovydas Joksas"]
name = "v4v"
version = "0.4.6"
version = "0.5.0"
edition = "2021"
description = "Value-for-value helper utilities for Podcasting 2.0"
license = "MIT OR Apache-2.0"
Expand Down
48 changes: 48 additions & 0 deletions src/alby/webhooks.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use serde_json::Value;

use crate::podcasting::tlv::Record;
pub use crate::svix::webhooks::{HeaderMap, WebhookError as Error};

/// Verifies Alby webhook requests.
Expand Down Expand Up @@ -47,3 +50,48 @@ pub fn verify_signature<HM: HeaderMap>(
) -> Result<(), Error> {
crate::svix::webhooks::Webhook::new(secret)?.verify(payload, headers)
}

/// Alby invoice obtained via webhook request.
#[derive(Debug, serde::Deserialize)]
pub struct AlbyInvoice {
/// 24 alphanumeric characters
pub identifier: String,

/// e.g., "incoming" or "outgoing"
#[serde(rename = "type")]
pub type_: String,

/// Description.
#[serde(default)]
pub memo: Option<String>,

/// State of the invoice, e.g., "SETTLED">
pub state: String,

/// Arbitrary data added during the invoice creation.
#[serde(default)]
pub metadata: Value,

/// Payer name.
#[serde(default)]
pub payer_name: Option<String>,

/// Amount in sats.
#[serde(rename = "amount")]
pub num_sats: u64,

/// When the invoice was created.
pub created_at: chrono::DateTime<chrono::Utc>,

/// bLIP-10 TLV record.
#[serde(
default,
deserialize_with = "crate::podcasting::tlv::deserialize_untrusted_tlv_record"
)]
pub boostagram: Option<Record>,
}

/// Extracts an Alby invoice from a webhook request body.
pub fn extract_alby_invoice(body: &Value) -> Result<AlbyInvoice, String> {
serde_json::from_value(body.clone()).map_err(|e| e.to_string())
}
35 changes: 30 additions & 5 deletions src/podcasting/forwarding.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::alby::api::{
invoices::{CreateInvoiceArgs as AlbyCreateInvoiceArgs, CreateInvoiceResponse},
RequestError,
pub use super::payments::MakePaymentArgs as ForwardPaymentArgs;
use super::payments::{make_payment, PaymentInfo, PaymentRecipientInfo};
use crate::alby::{
api::{
invoices::{CreateInvoiceArgs as AlbyCreateInvoiceArgs, CreateInvoiceResponse},
RequestError,
},
webhooks::AlbyInvoice,
};

use super::payments::{PaymentInfo, PaymentRecipientInfo};

/// Arguments for creating an invoice for forwarding payments to multiple Podcasting 2.0
/// recipients.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
Expand Down Expand Up @@ -32,6 +35,16 @@ struct CreateInvoiceMetadata {
pub forwarding_data: CreateInvoiceMetadataForwardingStruct,
}

impl TryFrom<AlbyInvoice> for CreateInvoiceMetadata {
type Error = serde_json::Error;

fn try_from(invoice: AlbyInvoice) -> Result<Self, Self::Error> {
let forwarding_data = serde_json::from_value(invoice.metadata)?;

Ok(Self { forwarding_data })
}
}

/// Creates an invoice for forwarding payments to multiple Podcasting 2.0 recipients.
pub async fn create_invoice(
args: CreateInvoiceArgs<'_>,
Expand Down Expand Up @@ -61,3 +74,15 @@ pub async fn create_invoice(

crate::alby::api::invoices::create_invoice(invoice_args).await
}

/// Forwards payments to multiple Podcasting 2.0 recipients.
pub async fn forward_payments(args: ForwardPaymentArgs<'_>) -> Result<(), RequestError> {
make_payment(ForwardPaymentArgs {
user_agent: args.user_agent,
token: args.token,
payment_info: args.payment_info.clone(),
recipients: args.recipients.clone(),
})
.await
.map(|_| ())
}
20 changes: 19 additions & 1 deletion src/podcasting/tlv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use uuid::Uuid;
/// <https://github.com/Podcastindex-org/podcast-namespace/blob/main/value/blip-0010.md#fields>
/// standard so will make it as generic as possible.
#[derive(Debug, serde::Deserialize)]
pub struct UntrustedRecord {
struct UntrustedRecord {
/// ACTION
/// "boost", "stream" or "auto"
action: Value,
Expand Down Expand Up @@ -448,3 +448,21 @@ impl From<UntrustedRecord> for Record {
}
}
}

/// Deserialize a bLIP-10 TLV record from an untrusted source.
pub fn deserialize_untrusted_tlv_record<'de, D>(deserializer: D) -> Result<Option<Record>, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = match serde::Deserialize::deserialize(deserializer) {
Ok(value) => value,
Err(_) => return Ok(None),
};

let untrusted_record: UntrustedRecord = match serde_json::from_value(value) {
Ok(untrusted_record) => untrusted_record,
Err(_) => return Ok(None),
};

Ok(Some(untrusted_record.into()))
}

0 comments on commit 91737de

Please sign in to comment.