Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/cashu/src/nuts/nut04.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,11 @@ impl Settings {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MintQuoteCustomRequest {
/// Amount to mint
pub amount: Amount,
///
/// Optional common field. Method-specific NUTs make it required or ignore
/// it as needed (e.g. NUT-23 requires `amount`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub amount: Option<Amount>,
/// Currency unit
pub unit: CurrencyUnit,
/// Optional description
Expand Down
6 changes: 6 additions & 0 deletions crates/cashu/src/nuts/nut05.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,12 @@ pub struct MeltQuoteCustomRequest {
pub request: String,
/// Currency unit
pub unit: CurrencyUnit,
/// Amount the wallet would like to pay
///
/// Optional common field. Method-specific NUTs make it required or ignore
/// it as needed (e.g. NUT-30 requires `amount` for onchain melts).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub amount: Option<Amount>,
/// Extra payment-method-specific fields
///
/// These fields are flattened into the JSON representation, allowing
Expand Down
2 changes: 1 addition & 1 deletion crates/cdk-axum/src/custom_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ mod tests {
.get_mint_quote(cdk::mint::MintQuoteRequest::Custom {
method: PaymentMethod::Custom(method.to_string()),
request: MintQuoteCustomRequest {
amount: Amount::from(amount),
amount: Some(Amount::from(amount)),
unit: CurrencyUnit::Sat,
description: None,
pubkey: None,
Expand Down
1 change: 1 addition & 0 deletions crates/cdk-common/src/melt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ mod tests {
method: "cashapp".to_string(),
unit: CurrencyUnit::Sat,
request: "$tag".to_string(),
amount: None,
extra: serde_json::Value::Null,
};
let req: MeltQuoteRequest = custom_req.into();
Expand Down
2 changes: 1 addition & 1 deletion crates/cdk-common/src/mint_quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl MintQuoteRequest {
Self::Bolt11(request) => Some(request.amount),
Self::Bolt12(request) => request.amount,
Self::Onchain(_) => None,
Self::Custom { request, .. } => Some(request.amount),
Self::Custom { request, .. } => request.amount,
}
}

Expand Down
8 changes: 6 additions & 2 deletions crates/cdk-common/src/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ pub struct CustomIncomingPaymentOptions {
pub method: String,
/// Optional description for the payment request
pub description: Option<String>,
/// Amount for the payment request
pub amount: Amount<CurrencyUnit>,
/// Optional amount for the payment request
pub amount: Option<Amount<CurrencyUnit>>,
/// Optional expiry time as Unix timestamp in seconds
pub unix_expiry: Option<u64>,
/// Extra payment-method-specific fields as JSON string
Expand Down Expand Up @@ -293,6 +293,8 @@ pub struct CustomOutgoingPaymentOptions {
pub method: String,
/// Payment request string (method-specific format)
pub request: String,
/// Optional amount the wallet would like to pay (from the melt quote request)
pub amount: Option<Amount<CurrencyUnit>>,
/// Maximum fee amount allowed for the payment
pub max_fee_amount: Option<Amount<CurrencyUnit>>,
/// Optional timeout in seconds
Expand Down Expand Up @@ -391,6 +393,8 @@ impl OutgoingPaymentOptions {
Box::new(CustomOutgoingPaymentOptions {
method: method.to_string(),
request: request.to_string(),
// Payment is already quoted; correlation is via quote_id.
amount: None,
max_fee_amount: Some(fee_reserve),
timeout_secs: None,
melt_options: melt_quote.options,
Expand Down
6 changes: 4 additions & 2 deletions crates/cdk-fake-wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,9 @@ impl MintPayment for FakeWallet {
(
PaymentIdentifier::CustomId(custom_id),
request,
custom_options.amount,
custom_options
.amount
.unwrap_or_else(|| Amount::new(0, self.unit.clone())),
custom_options.unix_expiry,
)
}
Expand Down Expand Up @@ -1110,7 +1112,7 @@ mod tests {
CustomIncomingPaymentOptions {
method: "venmo".to_string(),
description: None,
amount: Amount::new(10, CurrencyUnit::Sat),
amount: Some(Amount::new(10, CurrencyUnit::Sat)),
unix_expiry: None,
extra_json: None,
},
Expand Down
3 changes: 3 additions & 0 deletions crates/cdk-integration-tests/tests/integration_tests_pure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,7 @@ async fn test_direct_connector_custom_melt_enum_roundtrip() {
method: "paypal".to_string(),
request: "invoice-123".to_string(),
unit: CurrencyUnit::Sat,
amount: None,
extra: serde_json::Value::Null,
});

Expand Down Expand Up @@ -2482,6 +2483,7 @@ async fn test_custom_melt_quote_status_preserves_extra_json() {
method: "test-custom".to_string(),
request: "custom-request".to_string(),
unit: CurrencyUnit::Sat,
amount: None,
extra: serde_json::json!({ "request_metadata": true }),
},
))
Expand Down Expand Up @@ -2543,6 +2545,7 @@ async fn test_custom_melt_quote_id_propagates_to_payment_processor() {
method: "test-custom".to_string(),
request: "custom-request".to_string(),
unit: CurrencyUnit::Sat,
amount: None,
extra: serde_json::json!({ "request_metadata": true }),
},
))
Expand Down
3 changes: 2 additions & 1 deletion crates/cdk-payment-processor/src/proto/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl MintPayment for PaymentProcessorClient {
options: Some(super::incoming_payment_options::Options::Custom(
super::CustomIncomingPaymentOptions {
description: opts.description,
amount: Some(opts.amount.into()),
amount: opts.amount.map(Into::into),
unix_expiry: opts.unix_expiry,
extra_json: opts.extra_json.clone(),
},
Expand Down Expand Up @@ -330,6 +330,7 @@ impl MintPayment for PaymentProcessorClient {
options: Some(super::outgoing_payment_variant::Options::Custom(
super::CustomOutgoingPaymentOptions {
offer: opts.request.to_string(),
amount: opts.amount.map(Into::into),
max_fee_amount: opts.max_fee_amount.into_proto(),
timeout_secs: opts.timeout_secs,
melt_options: opts.melt_options.map(Into::into),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ syntax = "proto3";

package cdk_payment_processor;

service CdkPaymentProcessor {
service CdkPaymentProcessor {
rpc GetSettings(EmptyRequest) returns (SettingsResponse) {}
rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse) {}
rpc GetPaymentQuote(PaymentQuoteRequest) returns (PaymentQuoteResponse) {}
Expand Down Expand Up @@ -232,6 +232,8 @@ message CustomOutgoingPaymentOptions {
string offer = 1;
optional AmountMessage max_fee_amount = 2;
optional uint64 timeout_secs = 3;
// Optional amount the wallet would like to pay (from the melt quote request)
optional AmountMessage amount = 4;
optional MeltOptions melt_options = 5;
// Extra payment-method-specific fields as JSON string
// These fields are flattened into the JSON representation on the client side
Expand Down
21 changes: 16 additions & 5 deletions crates/cdk-payment-processor/src/proto/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,13 @@ impl CdkPaymentProcessor for PaymentProcessorServer {
.ok_or_else(|| Status::invalid_argument("Missing options"))?
{
incoming_payment_options::Options::Custom(opts) => {
let amount = opts
.amount
.ok_or_else(|| Status::invalid_argument("Missing amount"))?
.try_into()
.map_err(|_| Status::invalid_argument("Invalid amount"))?;
let amount: Option<cdk_common::Amount<CurrencyUnit>> = match opts.amount {
Some(a) => Some(
a.try_into()
.map_err(|_| Status::invalid_argument("Invalid amount"))?,
),
None => None,
};
IncomingPaymentOptions::Custom(Box::new(
cdk_common::payment::CustomIncomingPaymentOptions {
method: "".to_string(),
Expand Down Expand Up @@ -332,6 +334,7 @@ impl CdkPaymentProcessor for PaymentProcessorServer {
cdk_common::payment::CustomOutgoingPaymentOptions {
method: String::new(), // Will be set from variant
request: request.request.clone(),
amount: None,
max_fee_amount: None,
timeout_secs: None,
melt_options: request.options.map(TryInto::try_into).transpose()?,
Expand Down Expand Up @@ -450,11 +453,19 @@ impl CdkPaymentProcessor for PaymentProcessorServer {
.try_from_proto()
.map_err(|_| Status::invalid_argument("Invalid max_fee_amount"))?;
let quote_id = parse_quote_id(&opts.quote_id)?;
let amount: Option<cdk_common::Amount<CurrencyUnit>> = match opts.amount {
Some(a) => Some(
a.try_into()
.map_err(|_| Status::invalid_argument("Invalid amount"))?,
),
None => None,
};

cdk_common::payment::OutgoingPaymentOptions::Custom(Box::new(
cdk_common::payment::CustomOutgoingPaymentOptions {
method: String::new(), // Method will be determined from context
request: opts.offer, // Reusing offer field for custom request string
amount,
max_fee_amount,
timeout_secs: opts.timeout_secs,
melt_options: opts.melt_options.map(TryInto::try_into).transpose()?,
Expand Down
2 changes: 1 addition & 1 deletion crates/cdk/src/mint/issue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ impl Mint {
let custom_options = CustomIncomingPaymentOptions {
method: payment_method.to_string(),
description: request.description,
amount: request.amount.with_unit(unit.clone()),
amount: request.amount.map(|a| a.with_unit(unit.clone())),
unix_expiry: Some(quote_expiry),
extra_json,
};
Expand Down
2 changes: 2 additions & 0 deletions crates/cdk/src/mint/melt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ impl Mint {
request,
unit,
method,
amount,
extra,
} = melt_request;

Expand Down Expand Up @@ -720,6 +721,7 @@ impl Mint {
OutgoingPaymentOptions::Custom(Box::new(CustomOutgoingPaymentOptions {
method: method.to_string(),
request: request.clone(),
amount: (*amount).map(|a| a.with_unit(unit.clone())),
max_fee_amount: None,
timeout_secs: None,
melt_options: None,
Expand Down
2 changes: 1 addition & 1 deletion crates/cdk/src/wallet/issue/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl Wallet {
MintQuoteRequest::Custom {
method: method.clone(),
request: cdk_common::nuts::MintQuoteCustomRequest {
amount,
amount: Some(amount),
unit: unit.clone(),
description,
pubkey: Some(secret_key.public_key()),
Expand Down
1 change: 1 addition & 0 deletions crates/cdk/src/wallet/melt/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl Wallet {
method: method.to_string(),
request: request.clone(),
unit: self.unit.clone(),
amount: None,
extra: extra.unwrap_or(serde_json::Value::Null),
};
let quote_res = self
Expand Down
7 changes: 4 additions & 3 deletions crates/cdk/src/wallet/mint_connector/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@ mod tests {
let request = MintQuoteRequest::Custom {
method: PaymentMethod::Custom("paypal".to_string()),
request: MintQuoteCustomRequest {
amount: cdk_common::Amount::from(1000),
amount: Some(cdk_common::Amount::from(1000)),
unit: cdk_common::CurrencyUnit::Sat,
description: None,
pubkey: None,
Expand Down Expand Up @@ -1016,7 +1016,7 @@ mod tests {

// Verify the actual field values round-tripped correctly
let parsed = parsed.expect("already checked");
assert_eq!(parsed.amount, cdk_common::Amount::from(1000));
assert_eq!(parsed.amount, Some(cdk_common::Amount::from(1000)));
assert_eq!(parsed.unit, cdk_common::CurrencyUnit::Sat);
}

Expand All @@ -1033,7 +1033,7 @@ mod tests {
.post_mint_quote(MintQuoteRequest::Custom {
method: invalid_method.clone(),
request: MintQuoteCustomRequest {
amount: cdk_common::Amount::from(1000),
amount: Some(cdk_common::Amount::from(1000)),
unit: cdk_common::CurrencyUnit::Sat,
description: None,
pubkey: None,
Expand Down Expand Up @@ -1088,6 +1088,7 @@ mod tests {
method: "../../v1/swap".to_string(),
request: "custom-payment-request".to_string(),
unit: cdk_common::CurrencyUnit::Sat,
amount: None,
extra: serde_json::Value::Null,
}))
.await;
Expand Down
Loading