types.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. //! Type definitions for NpubCash API
  2. use std::str::FromStr;
  3. use cashu::nut00::KnownMethod;
  4. use cashu::PaymentMethod;
  5. use cdk_common::mint_url::MintUrl;
  6. use cdk_common::nuts::{CurrencyUnit, MintQuoteState};
  7. use cdk_common::wallet::MintQuote;
  8. use cdk_common::Amount;
  9. use serde::{Deserialize, Serialize};
  10. /// Default mint URL used when quote doesn't specify one
  11. const DEFAULT_MINT_URL: &str = "http://localhost:3338";
  12. /// A quote from the NpubCash service
  13. #[derive(Debug, Clone, Serialize, Deserialize)]
  14. #[serde(rename_all = "camelCase")]
  15. pub struct Quote {
  16. /// Unique identifier for the quote
  17. #[serde(rename = "quoteId")]
  18. pub id: String,
  19. /// Amount in the specified unit
  20. pub amount: u64,
  21. /// Currency or unit for the amount (optional, defaults to "sat")
  22. #[serde(default = "default_unit")]
  23. pub unit: String,
  24. /// Unix timestamp when the quote was created
  25. #[serde(default)]
  26. pub created_at: u64,
  27. /// Unix timestamp when the quote was paid (optional)
  28. #[serde(skip_serializing_if = "Option::is_none")]
  29. pub paid_at: Option<u64>,
  30. /// Unix timestamp when the quote expires (optional)
  31. #[serde(skip_serializing_if = "Option::is_none")]
  32. pub expires_at: Option<u64>,
  33. /// Mint URL associated with the quote (optional)
  34. #[serde(skip_serializing_if = "Option::is_none")]
  35. pub mint_url: Option<String>,
  36. /// Lightning invoice request (optional)
  37. #[serde(skip_serializing_if = "Option::is_none")]
  38. pub request: Option<String>,
  39. /// Quote state (e.g., "PAID", "PENDING") (optional)
  40. #[serde(skip_serializing_if = "Option::is_none")]
  41. pub state: Option<String>,
  42. /// Whether the quote is locked (optional)
  43. #[serde(skip_serializing_if = "Option::is_none")]
  44. pub locked: Option<bool>,
  45. }
  46. fn default_unit() -> String {
  47. "sat".to_string()
  48. }
  49. /// Response containing a list of quotes with pagination metadata
  50. #[derive(Debug, Clone, Serialize, Deserialize)]
  51. pub struct QuotesResponse {
  52. /// Quote data
  53. pub data: QuotesData,
  54. /// Pagination metadata
  55. pub metadata: Metadata,
  56. }
  57. /// Container for quote data
  58. #[derive(Debug, Clone, Serialize, Deserialize)]
  59. pub struct QuotesData {
  60. /// List of quotes
  61. pub quotes: Vec<Quote>,
  62. }
  63. /// Pagination metadata
  64. #[derive(Debug, Clone, Serialize, Deserialize)]
  65. pub struct Metadata {
  66. /// Total number of available items
  67. pub total: usize,
  68. /// Current offset (optional, may not be present in all responses)
  69. #[serde(skip_serializing_if = "Option::is_none")]
  70. pub offset: Option<usize>,
  71. /// Items per page
  72. pub limit: usize,
  73. /// Since timestamp (optional, present when querying with since parameter)
  74. #[serde(skip_serializing_if = "Option::is_none")]
  75. pub since: Option<u64>,
  76. }
  77. /// Response containing user settings
  78. #[derive(Debug, Clone, Serialize, Deserialize)]
  79. #[serde(rename_all = "camelCase")]
  80. pub struct UserResponse {
  81. /// Whether the request resulted in an error
  82. #[serde(default)]
  83. pub error: bool,
  84. /// User data container
  85. pub data: UserDataContainer,
  86. }
  87. /// Container for user data
  88. #[derive(Debug, Clone, Serialize, Deserialize)]
  89. pub struct UserDataContainer {
  90. /// User settings
  91. pub user: UserData,
  92. }
  93. /// User settings data
  94. #[derive(Debug, Clone, Serialize, Deserialize)]
  95. #[serde(rename_all = "camelCase")]
  96. pub struct UserData {
  97. /// User's public key
  98. pub pubkey: String,
  99. /// Configured mint URL
  100. #[serde(skip_serializing_if = "Option::is_none")]
  101. pub mint_url: Option<String>,
  102. /// Whether quotes are locked
  103. #[serde(default)]
  104. pub lock_quote: bool,
  105. }
  106. /// NIP-98 authentication response
  107. #[derive(Debug, Clone, Serialize, Deserialize)]
  108. pub struct Nip98Response {
  109. /// NIP-98 response data
  110. pub data: Nip98Data,
  111. }
  112. /// NIP-98 token data
  113. #[derive(Debug, Clone, Serialize, Deserialize)]
  114. pub struct Nip98Data {
  115. /// JWT token
  116. pub token: String,
  117. }
  118. impl From<Quote> for MintQuote {
  119. fn from(quote: Quote) -> Self {
  120. let mint_url = quote
  121. .mint_url
  122. .and_then(|url| MintUrl::from_str(&url).ok())
  123. .unwrap_or_else(|| {
  124. MintUrl::from_str(DEFAULT_MINT_URL).expect("default mint URL should be valid")
  125. });
  126. let unit = CurrencyUnit::from_str(&quote.unit).unwrap_or(CurrencyUnit::Sat);
  127. let state = match quote.state.as_deref() {
  128. Some("PAID") => MintQuoteState::Paid,
  129. Some("ISSUED") => MintQuoteState::Issued,
  130. _ => MintQuoteState::Unpaid,
  131. };
  132. let expiry = quote.expires_at.unwrap_or(quote.created_at + 86400);
  133. Self {
  134. id: quote.id,
  135. mint_url,
  136. payment_method: PaymentMethod::Known(KnownMethod::Bolt11),
  137. amount: Some(Amount::from(quote.amount)),
  138. unit,
  139. request: quote.request.unwrap_or_default(),
  140. state,
  141. expiry,
  142. secret_key: None,
  143. amount_issued: Amount::ZERO,
  144. amount_paid: if quote.paid_at.is_some() {
  145. Amount::from(quote.amount)
  146. } else {
  147. Amount::ZERO
  148. },
  149. used_by_operation: None,
  150. version: 0,
  151. }
  152. }
  153. }