1use std::collections::HashMap;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use url::Url;
6
7use crate::error::NodelessError;
8use crate::serde_utils::{opt_serde_timestamp, opt_serde_url, serde_timestamp, serde_url};
9use crate::Nodeless;
10
11#[derive(Clone, Debug, Deserialize, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct Store {
15 pub id: String,
16 pub name: String,
17 #[serde(with = "opt_serde_url")]
18 pub url: Option<Url>,
19 pub email: Option<String>,
20 #[serde(with = "serde_timestamp")]
21 pub created_at: i64,
22}
23
24#[derive(Clone, Debug, Deserialize, Serialize)]
26#[serde(rename_all = "camelCase")]
27pub struct InvoiceRequest {
28 pub amount: f64,
29 pub currency: String,
30 pub buyer_email: String,
31 #[serde(with = "serde_url")]
32 pub redirect_url: Url,
33 pub metadata: Option<HashMap<String, String>>,
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize)]
38#[serde(rename_all = "camelCase")]
39pub struct Invoice {
40 pub id: Option<String>,
41 #[serde(with = "opt_serde_url")]
42 #[serde(rename = "checkoutLink")]
43 pub checkout_link: Option<Url>,
44 pub sats_amount: u64,
45 pub status: InvoiceStatus,
46 pub buyer_email: String,
47 #[serde(with = "serde_url")]
48 pub redirect_url: Url,
49 pub metadata: Option<HashMap<String, String>>,
50 #[serde(with = "serde_timestamp")]
51 pub created_at: i64,
52 #[serde(with = "opt_serde_timestamp")]
53 pub paid_at: Option<i64>,
54 pub onchain_address: String,
55 pub lightning_invoice: String,
56 pub store: Store,
57 pub qr_codes: QrCodes,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum InvoiceStatus {
63 New,
64 Paid,
65 Expired,
66 Unknown(String),
67}
68
69impl<'de> Deserialize<'de> for InvoiceStatus {
70 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
71 where
72 D: Deserializer<'de>,
73 {
74 let status_str = String::deserialize(deserializer)?;
75 match status_str.as_str() {
76 "new" => Ok(InvoiceStatus::New),
77 "paid" => Ok(InvoiceStatus::Paid),
78 "expired" => Ok(InvoiceStatus::Expired),
79 _ => Ok(InvoiceStatus::Unknown(status_str)),
80 }
81 }
82}
83
84impl Serialize for InvoiceStatus {
85 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86 where
87 S: Serializer,
88 {
89 match self {
90 InvoiceStatus::New => serializer.serialize_str("new"),
91 InvoiceStatus::Paid => serializer.serialize_str("paid"),
92 InvoiceStatus::Expired => serializer.serialize_str("expired"),
93 InvoiceStatus::Unknown(status_str) => serializer.serialize_str(status_str),
94 }
95 }
96}
97
98#[derive(Clone, Debug, Deserialize, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct QrCodes {
102 pub unified: String,
103 pub onchain: String,
104 pub lightning: String,
105}
106
107impl Nodeless {
108 pub async fn get_stores(&self) -> Result<Vec<Store>, NodelessError> {
110 let url = self.base_url.join("api/v1/store")?;
111
112 let res = self.make_get(url).await?;
113 Ok(serde_json::from_value(res["data"].clone())?)
114 }
115
116 pub async fn get_store(&self, id: &str) -> Result<Store, NodelessError> {
118 let url = self.base_url.join(&format!("api/v1/store/{}", id))?;
119
120 let res = self.make_get(url).await?;
121 Ok(serde_json::from_value(res["data"].clone())?)
122 }
123
124 pub async fn create_store_invoice(
126 &self,
127 store_id: &str,
128 invoice: InvoiceRequest,
129 ) -> Result<Invoice, NodelessError> {
130 let url = self
131 .base_url
132 .join(&format!("api/v1/store/{}/invoice", store_id))?;
133
134 let res = self
135 .make_post(url, Some(serde_json::to_value(invoice)?))
136 .await?;
137 Ok(serde_json::from_value(res["data"].to_owned())?)
138 }
139
140 pub async fn get_store_invoice(
142 &self,
143 store_id: &str,
144 invoice_id: &str,
145 ) -> Result<Invoice, NodelessError> {
146 let url = self
147 .base_url
148 .join(&format!("api/v1/store/{}/invoice/{}", store_id, invoice_id))?;
149
150 let res = self.make_get(url).await?;
151 Ok(serde_json::from_value(res["data"].to_owned())?)
152 }
153
154 pub async fn get_store_invoice_status(
156 &self,
157 store_id: &str,
158 invoice_id: &str,
159 ) -> Result<InvoiceStatus, NodelessError> {
160 let url = self.base_url.join(&format!(
161 "api/v1/store/{}/invoice/{}/status",
162 store_id, invoice_id
163 ))?;
164 let res = self.make_get(url).await?;
165 Ok(serde_json::from_value(res["status"].to_owned())?)
166 }
167}