Skip to main content

cdk_payment_processor/proto/
mod.rs

1use std::str::FromStr;
2
3use cdk_common::payment::{
4    CreateIncomingPaymentResponse, MakePaymentResponse as CdkMakePaymentResponse,
5    PaymentIdentifier as CdkPaymentIdentifier, PaymentQuoteResponse as CdkPaymentQuoteResponse,
6    WaitPaymentResponse,
7};
8use cdk_common::{Amount, CurrencyUnit, MeltOptions as CdkMeltOptions};
9
10mod client;
11mod server;
12
13pub use client::PaymentProcessorClient;
14pub use server::PaymentProcessorServer;
15
16tonic::include_proto!("cdk_payment_processor");
17
18impl From<CdkPaymentIdentifier> for PaymentIdentifier {
19    fn from(value: CdkPaymentIdentifier) -> Self {
20        match value {
21            CdkPaymentIdentifier::Label(id) => Self {
22                r#type: PaymentIdentifierType::Label.into(),
23                value: Some(payment_identifier::Value::Id(id)),
24            },
25            CdkPaymentIdentifier::OfferId(id) => Self {
26                r#type: PaymentIdentifierType::OfferId.into(),
27                value: Some(payment_identifier::Value::Id(id)),
28            },
29            CdkPaymentIdentifier::PaymentHash(hash) => Self {
30                r#type: PaymentIdentifierType::PaymentHash.into(),
31                value: Some(payment_identifier::Value::Hash(hex::encode(hash))),
32            },
33            CdkPaymentIdentifier::Bolt12PaymentHash(hash) => Self {
34                r#type: PaymentIdentifierType::Bolt12PaymentHash.into(),
35                value: Some(payment_identifier::Value::Hash(hex::encode(hash))),
36            },
37            CdkPaymentIdentifier::CustomId(id) => Self {
38                r#type: PaymentIdentifierType::CustomId.into(),
39                value: Some(payment_identifier::Value::Id(id)),
40            },
41            CdkPaymentIdentifier::PaymentId(hash) => Self {
42                r#type: PaymentIdentifierType::PaymentId.into(),
43                value: Some(payment_identifier::Value::Hash(hex::encode(hash))),
44            },
45        }
46    }
47}
48
49impl TryFrom<PaymentIdentifier> for CdkPaymentIdentifier {
50    type Error = crate::error::Error;
51
52    fn try_from(value: PaymentIdentifier) -> Result<Self, Self::Error> {
53        match (value.r#type(), value.value) {
54            (PaymentIdentifierType::Label, Some(payment_identifier::Value::Id(id))) => {
55                Ok(CdkPaymentIdentifier::Label(id))
56            }
57            (PaymentIdentifierType::OfferId, Some(payment_identifier::Value::Id(id))) => {
58                Ok(CdkPaymentIdentifier::OfferId(id))
59            }
60            (PaymentIdentifierType::PaymentHash, Some(payment_identifier::Value::Hash(hash))) => {
61                let decoded = hex::decode(hash)?;
62                let hash_array: [u8; 32] = decoded
63                    .try_into()
64                    .map_err(|_| crate::error::Error::InvalidHash)?;
65                Ok(CdkPaymentIdentifier::PaymentHash(hash_array))
66            }
67            (
68                PaymentIdentifierType::Bolt12PaymentHash,
69                Some(payment_identifier::Value::Hash(hash)),
70            ) => {
71                let decoded = hex::decode(hash)?;
72                let hash_array: [u8; 32] = decoded
73                    .try_into()
74                    .map_err(|_| crate::error::Error::InvalidHash)?;
75                Ok(CdkPaymentIdentifier::Bolt12PaymentHash(hash_array))
76            }
77            (PaymentIdentifierType::CustomId, Some(payment_identifier::Value::Id(id))) => {
78                Ok(CdkPaymentIdentifier::CustomId(id))
79            }
80            (PaymentIdentifierType::PaymentId, Some(payment_identifier::Value::Hash(hash))) => {
81                let decoded = hex::decode(hash)?;
82                let hash_array: [u8; 32] = decoded
83                    .try_into()
84                    .map_err(|_| crate::error::Error::InvalidHash)?;
85                Ok(CdkPaymentIdentifier::PaymentId(hash_array))
86            }
87            _ => Err(crate::error::Error::InvalidPaymentIdentifier),
88        }
89    }
90}
91
92impl TryFrom<MakePaymentResponse> for CdkMakePaymentResponse {
93    type Error = crate::error::Error;
94    fn try_from(value: MakePaymentResponse) -> Result<Self, Self::Error> {
95        // Use direct enum conversion instead of parsing string from as_str_name()
96        // as_str_name() returns "QUOTE_STATE_PAID" but MeltQuoteState::from_str expects "PAID"
97        let status: cdk_common::nuts::MeltQuoteState = value.status().into();
98        let payment_proof = value.payment_proof;
99        let unit = CurrencyUnit::from_str(&value.unit)?;
100        let payment_identifier = value
101            .payment_identifier
102            .ok_or(crate::error::Error::InvalidPaymentIdentifier)?;
103        Ok(Self {
104            payment_lookup_id: payment_identifier.try_into()?,
105            payment_proof,
106            status,
107            total_spent: Amount::new(value.total_spent, unit),
108        })
109    }
110}
111
112impl From<CdkMakePaymentResponse> for MakePaymentResponse {
113    fn from(value: CdkMakePaymentResponse) -> Self {
114        Self {
115            payment_identifier: Some(value.payment_lookup_id.into()),
116            payment_proof: value.payment_proof,
117            status: QuoteState::from(value.status).into(),
118            total_spent: value.total_spent.value(),
119            unit: value.total_spent.unit().to_string(),
120            extra_json: None,
121        }
122    }
123}
124
125impl From<CreateIncomingPaymentResponse> for CreatePaymentResponse {
126    fn from(value: CreateIncomingPaymentResponse) -> Self {
127        Self {
128            request_identifier: Some(value.request_lookup_id.into()),
129            request: value.request,
130            expiry: value.expiry,
131            extra_json: None,
132        }
133    }
134}
135
136impl TryFrom<CreatePaymentResponse> for CreateIncomingPaymentResponse {
137    type Error = crate::error::Error;
138
139    fn try_from(value: CreatePaymentResponse) -> Result<Self, Self::Error> {
140        let request_identifier = value
141            .request_identifier
142            .ok_or(crate::error::Error::InvalidPaymentIdentifier)?;
143        Ok(Self {
144            request_lookup_id: request_identifier.try_into()?,
145            request: value.request,
146            expiry: value.expiry,
147            extra_json: Some(
148                serde_json::from_str(value.extra_json.unwrap_or_default().as_str())
149                    .unwrap_or_default(),
150            ),
151        })
152    }
153}
154impl From<CdkPaymentQuoteResponse> for PaymentQuoteResponse {
155    fn from(value: CdkPaymentQuoteResponse) -> Self {
156        Self {
157            request_identifier: value.request_lookup_id.map(|i| i.into()),
158            amount: value.amount.value(),
159            fee: value.fee.value(),
160            unit: value.amount.unit().to_string(),
161            state: QuoteState::from(value.state).into(),
162            extra_json: None,
163        }
164    }
165}
166
167impl From<PaymentQuoteResponse> for CdkPaymentQuoteResponse {
168    fn from(value: PaymentQuoteResponse) -> Self {
169        let state_val = value.state();
170        let request_identifier = value.request_identifier;
171        let unit = CurrencyUnit::from_str(&value.unit).unwrap_or_default();
172
173        Self {
174            request_lookup_id: request_identifier
175                .map(|i| i.try_into().expect("valid request identifier")),
176            amount: Amount::new(value.amount, unit.clone()),
177            fee: Amount::new(value.fee, unit),
178            state: state_val.into(),
179        }
180    }
181}
182
183impl From<MeltOptions> for CdkMeltOptions {
184    fn from(value: MeltOptions) -> Self {
185        match value.options.expect("option defined") {
186            melt_options::Options::Mpp(mpp) => Self::Mpp {
187                mpp: cashu::nuts::nut15::Mpp {
188                    amount: mpp.amount.into(),
189                },
190            },
191            melt_options::Options::Amountless(amountless) => Self::Amountless {
192                amountless: cashu::nuts::nut23::Amountless {
193                    amount_msat: amountless.amount_msat.into(),
194                },
195            },
196        }
197    }
198}
199
200impl From<CdkMeltOptions> for MeltOptions {
201    fn from(value: CdkMeltOptions) -> Self {
202        match value {
203            CdkMeltOptions::Mpp { mpp } => Self {
204                options: Some(melt_options::Options::Mpp(Mpp {
205                    amount: mpp.amount.into(),
206                })),
207            },
208            CdkMeltOptions::Amountless { amountless } => Self {
209                options: Some(melt_options::Options::Amountless(Amountless {
210                    amount_msat: amountless.amount_msat.into(),
211                })),
212            },
213        }
214    }
215}
216
217impl From<QuoteState> for cdk_common::nuts::MeltQuoteState {
218    fn from(value: QuoteState) -> Self {
219        match value {
220            QuoteState::Unpaid => Self::Unpaid,
221            QuoteState::Paid => Self::Paid,
222            QuoteState::Pending => Self::Pending,
223            QuoteState::Unknown => Self::Unknown,
224            QuoteState::Failed => Self::Failed,
225            QuoteState::Issued => Self::Unknown,
226            QuoteState::Unspecified => Self::Unknown,
227        }
228    }
229}
230
231impl From<cdk_common::nuts::MeltQuoteState> for QuoteState {
232    fn from(value: cdk_common::nuts::MeltQuoteState) -> Self {
233        match value {
234            cdk_common::nuts::MeltQuoteState::Unpaid => Self::Unpaid,
235            cdk_common::nuts::MeltQuoteState::Paid => Self::Paid,
236            cdk_common::nuts::MeltQuoteState::Pending => Self::Pending,
237            cdk_common::nuts::MeltQuoteState::Unknown => Self::Unknown,
238            cdk_common::nuts::MeltQuoteState::Failed => Self::Failed,
239        }
240    }
241}
242
243impl From<cdk_common::nuts::MintQuoteState> for QuoteState {
244    fn from(value: cdk_common::nuts::MintQuoteState) -> Self {
245        match value {
246            cdk_common::nuts::MintQuoteState::Unpaid => Self::Unpaid,
247            cdk_common::nuts::MintQuoteState::Paid => Self::Paid,
248            cdk_common::nuts::MintQuoteState::Issued => Self::Issued,
249        }
250    }
251}
252
253impl From<WaitPaymentResponse> for WaitIncomingPaymentResponse {
254    fn from(value: WaitPaymentResponse) -> Self {
255        Self {
256            payment_identifier: Some(value.payment_identifier.into()),
257            payment_amount: value.payment_amount.value(),
258            unit: value.payment_amount.unit().to_string(),
259            payment_id: value.payment_id,
260        }
261    }
262}
263
264impl TryFrom<WaitIncomingPaymentResponse> for WaitPaymentResponse {
265    type Error = crate::error::Error;
266
267    fn try_from(value: WaitIncomingPaymentResponse) -> Result<Self, Self::Error> {
268        let payment_identifier = value
269            .payment_identifier
270            .ok_or(crate::error::Error::InvalidPaymentIdentifier)?
271            .try_into()?;
272        let unit = CurrencyUnit::from_str(&value.unit)?;
273
274        Ok(Self {
275            payment_identifier,
276            payment_amount: Amount::new(value.payment_amount, unit),
277            payment_id: value.payment_id,
278        })
279    }
280}