custom payment methods#382
Conversation
robwoodgate
left a comment
There was a problem hiding this comment.
I think this is a fantastic step forward for wallets to be able to better support unknown methods - I've added some suggested tweaks to the base structs to make life even better.
|
|
||
| ### Websocket Notifications | ||
|
|
||
| Custom methods **MAY** support websocket subscriptions via [NUT-17][17] with kind `"{method}_mint_quote"`. |
|
|
||
| ### Websocket Notifications | ||
|
|
||
| Custom methods **MAY** support websocket subscriptions via [NUT-17][17] with kind `"{method}_melt_quote"`. |
| { | ||
| "unit": <str_enum[UNIT]> | ||
| // Additional method-specific fields may be required | ||
| } |
There was a problem hiding this comment.
To better support custom, I think we should expand the base request something like:
| { | |
| "unit": <str_enum[UNIT]>, | |
| "amount": <int>, // Optional | |
| "description": <str>, // Optional | |
| "pubkey": <str> // Optional (NUT-20) | |
| } |
Then NUT specific methods can enforce / ignore as needed - eg: NUT-23 makes amount required., NUT-25 makes pubkey required.
| { | ||
| "quote": <str>, | ||
| "request": <str>, | ||
| "unit": <str_enum[UNIT]>, | ||
| // Additional method-specific fields will be included | ||
| } |
There was a problem hiding this comment.
Custom would be better supported if the base reply is more like:
| { | |
| "quote": <str>, | |
| "request": <str>, | |
| "unit": <str_enum[UNIT]>, | |
| "expiry": <int|null>, | |
| "pubkey": <str>, // Optional (NUT-20) | |
| "amount_paid": <int>, | |
| "amount_issued": <int> | |
| // Additional method-specific fields will be included | |
| } |
| { | ||
| "request": <str>, | ||
| "unit": <str_enum[UNIT]> | ||
| // Additional method-specific fields will be required | ||
| } |
There was a problem hiding this comment.
Melt base suggestion for better custom handling:
| { | |
| "request": <str>, | |
| "unit": <str_enum[UNIT]>, | |
| "amount": <int>, // Optional | |
| // Additional method-specific fields will be required | |
| } |
| { | ||
| "quote": <str>, | ||
| "amount": <int>, | ||
| "unit": <str_enum[UNIT]>, | ||
| "state": <str_enum[STATE]>, | ||
| "expiry": <int> | ||
| // Additional method-specific fields will be included | ||
| } | ||
| ``` | ||
|
|
||
| Where `quote` is the quote ID, `amount` and `unit` the amount and unit that need to be provided, and `expiry` is the Unix timestamp until which the melt quote is valid. |
There was a problem hiding this comment.
Melt response base suggestion for better custom method handling:
| { | |
| "quote": <str>, | |
| "request": <str>, | |
| "amount": <int>, | |
| "unit": <str_enum[UNIT]>, | |
| "fee_reserve": <int>, // Optional | |
| "state": <str_enum[STATE]>, | |
| "expiry": <int> | |
| // Additional method-specific fields will be included | |
| } | |
| Where `quote` is the quote ID, `amount` and `unit` the amount and unit that need to be provided, and `expiry` is the Unix timestamp until which the melt quote is valid. | |
| `request` contains the method-specific payment routing instructions (eg bolt11 Lightning invoice, Bitcoin onchain address, bank account details, paypal email etc). | |
| `fee_reserve` is the additional fee for using the method. The mint expects the wallet to include `Proofs` of _at least_ `total_amount = amount + fee_reserve + fee` where `fee` is calculated from the keyset's `input_fee_ppk` as described in [NUT-02][02]. |
|
Sorry @asmogo - I questioned an earlier draft that included So maybe we DO need to include |
|
Related #377 which adds the |
robwoodgate
left a comment
There was a problem hiding this comment.
Have implemented custom method support in cashu-ts on top of this + #377 + #387 (cashubtc/cashu-ts#693) and it works nicely. Wallets can quote, mint and melt methods they don't fully understand using just the base structs.
A few tweaks below that came out of implementing. Note the suggestions assume #377 merges first.
Related to my base struct suggestions above: expiry really wants to be in the common mint quote response - every method has one, and a wallet showing a quote for an unknown method needs to know when to stop polling. With #377 in, it's the only field from the suggested base response still left as method-specific.
(method in the quote structs is now covered by #387, which also resolves the WS routing question from #378.)
Can also confirm the optional fee_reserve in the melt base works in practice - onchain can't make it required (it uses fee_options), and bolt11/12 tighten it to required in their own NUTs.
|
|
||
| For a custom `{method}`, the wallet sends a request following the common mint quote request format (see [General Flow](#general-flow)). Method-specific fields (e.g., an `amount` of tokens to mint, a `description`, or a `pubkey` for [NUT-20][20] locks) are defined by the method-specific NUT. | ||
|
|
||
| The mint responds with the common mint quote response format. The `request` field contains the method-specific payment request (e.g., a payment URL, an on-chain address, an account identifier). Additional method-specific fields (such as `amount` or `expiry`) are defined by the method-specific NUT. |
There was a problem hiding this comment.
Wallets can derive the accounting fields from state for methods they know, but for an unknown method there's no fallback, so these need to be explicit. Suggest:
| The mint responds with the common mint quote response format. The `request` field contains the method-specific payment request (e.g., a payment URL, an on-chain address, an account identifier). Additional method-specific fields (such as `amount` or `expiry`) are defined by the method-specific NUT. | |
| The mint responds with the common mint quote response format, and **MUST** include the `amount_paid`, `amount_issued` and `updated_at` fields. The `request` field contains the method-specific payment request (e.g., a payment URL, an on-chain address, an account identifier). Additional method-specific fields (such as `amount` or `expiry`) are defined by the method-specific NUT. |
|
|
||
| For a custom `{method}`, the wallet sends a request following the common melt quote request format (see [General Flow](#general-flow)). The `request` field is the method-specific payment target (e.g., a bank account identifier, an on-chain address, a payment processor reference). `unit` is the unit the wallet would like to pay with. | ||
|
|
||
| The mint responds with the common melt quote response format. Method-specific fields are defined by the method-specific NUT. |
There was a problem hiding this comment.
Wallets can't interpret a custom state machine for a method they don't understand, so custom melts need the standard states. Suggest:
| The mint responds with the common melt quote response format. Method-specific fields are defined by the method-specific NUT. | |
| The mint responds with the common melt quote response format, using the standard `state` values (`"UNPAID"`, `"PENDING"`, `"PAID"`). Method-specific fields are defined by the method-specific NUT. |
Implements the base-shape harmonization proposed on cashubtc/nuts#382: - MintQuoteBaseRequest gains optional amount and description; method NUTs tighten as needed (bolt11 requires amount). - MintQuoteBaseResponse gains expiry (normalized to null when unset), subsuming the per-method expiry fields. - MeltQuoteBaseRequest gains optional amount (onchain requires it). - MeltQuoteBaseResponse gains request and optional fee_reserve; bolt methods keep fee_reserve required. - Melt base validation now requires the payment request for every method, and normalizes fee_reserve when present. - New MintQuoteGenericResponse/MeltQuoteGenericResponse passthrough types are the defaults on the generic quote methods, so custom-method fields are reachable without casting. BREAKING CHANGE: melt quote responses without a request field now throw; generic quote methods default to the Generic response types.
Implements the base-shape harmonization proposed on cashubtc/nuts#382: - MintQuoteBaseRequest gains optional amount and description; method NUTs tighten as needed (bolt11 requires amount). - MintQuoteBaseResponse gains expiry (normalized to null when unset), subsuming the per-method expiry fields. - MeltQuoteBaseRequest gains optional amount (onchain requires it). - MeltQuoteBaseResponse gains request and optional fee_reserve; bolt methods keep fee_reserve required. - Melt base validation now requires the payment request for every method, and normalizes fee_reserve when present. - New MintQuoteGenericResponse/MeltQuoteGenericResponse passthrough types are the defaults on the generic quote methods, so custom-method fields are reachable without casting. BREAKING CHANGE: melt quote responses without a request field now throw; generic quote methods default to the Generic response types.
support custom payment methods not defined in a dedicated NUT.