Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUT-18: Payment Requests #124

Merged
merged 27 commits into from
Oct 18, 2024
Merged

NUT-18: Payment Requests #124

merged 27 commits into from
Oct 18, 2024

Conversation

Egge21M
Copy link
Contributor

@Egge21M Egge21M commented May 26, 2024

Cashu transactions are initiated by the sender and can be pushed to the receiver without any additional help. However for many use cases requesting a payment may be more desirable (e.g. point-of-sales). This proposal introduces a standardised format for payment requests, that supply a sending wallet with all information necessary to complete the transaction.

xx.md Outdated Show resolved Hide resolved
xx.md Outdated
```go
type PaymentRequest struct {
A int `amount (optional)`
U string `unit`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the unit also be optional and default to sats?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that as well, however I can receivers run into compatibility issues quickly if they let users choose the unit.

xx.md Outdated
- unit: The unit of the requested token (e.g. sats)
- mint: The mint to use to mint the token
- description: A human readable discription that the sending wallet will display after scanning the request
- transport: The method of transport chosen to transmit the created token to the sender (can be multiple, sorted by preference)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a use case where transport is empty? Or should there be a minimum of one element in the list?

@thesimplekid
Copy link
Collaborator

What do you think of an optional condition that could specify spending conditions. For example if the receiver wants it locked using P2PK?

@Egge21M
Copy link
Contributor Author

Egge21M commented May 26, 2024

What do you think of an optional condition that could specify spending conditions. For example if the receiver wants it locked using P2PK?

Yes, this should be included. I will add it later doday

@Egge21M Egge21M marked this pull request as ready for review May 27, 2024 05:00
@callebtc
Copy link
Contributor

callebtc commented May 27, 2024

What do you think of an optional condition that could specify spending conditions. For example if the receiver wants it locked using P2PK?

Very cool. The receiver could specify the secret as well.

Edit: maybe even better: Y – the sender doesn't need to know the secret.

Edit 2: Problem: it would require one Y per proof you expect, which is not likely to be known upfront in most cases.

@Egge21M
Copy link
Contributor Author

Egge21M commented May 27, 2024

Very cool. The receiver could specify the secret as well.

I added l for specifying a locking script. However specifying secret / Y is overkill I believe. It's nice to have, but introduces a lot of complexity and will bloat the request size quite a bit too.

xx.md Outdated Show resolved Hide resolved
xx.md Outdated Show resolved Hide resolved
xx.md Outdated
"r": str <optional>,
"d": str <optional>,
"m": str <optional>,
"l": str <optional>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the format of the lock script?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking to just make it the well known secret format. Basically what will become Proof.secret.

However thinking about it, that will not work because of the nonce. Do you have something in mind? Otherwise we just have to come up with another Type

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just the well known secret without a nonce and the sender can add the nonce?

xx.md Outdated
_Example_

```sh
cashrqAaVhQRVhVWNzYXRhTXgiaHR0cHM6Ly9taW50Lm1pbmliaXRzLmNhc2gvQml0Y29pbmFEeCNQbGVzYXNlIHBheSB0aGUgdmVyeSBmaXJzdCBjYXNodSBwcmFUgaJhVGVub3N0cmJUYXhGbnByb2ZpbGUxcXFzZG11cDZlMno2bWNwZXVlNno2a2wwOGhlNDloY2VuNXhucmMzdG5wdncwbWRndGplbWgwc3V4YTBrag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a cashureq prefix?

can we add a version byte like with keysets? It would be good to add a paragraph explaining the payment request serialization like with tokenv3:

cashureq[version][request object]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it'd be better to use a prefix that doesn't look so visually similar to a token: creq?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good point. The first byte after the prefix is indeed a version byte. I'll update the text to be more clear about that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a slightly different notation that makes it clear what exactly is concatianated and encoded when. Let me know what you think

xx.md Outdated Show resolved Hide resolved
xx.md Outdated Show resolved Hide resolved
@callebtc callebtc changed the title Payment Requests NUT-XX: Payment Requests Aug 29, 2024
xx.md Outdated
- d: A human readable description that the sending wallet will display after scanning the request
- m: The memo of the requested token (can be used for accounting purposes)
- l: The lock script of the requested token
- t: The method of transport chosen to transmit the created token to the sender (can be multiple, sorted by preference)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having multiple transports and letting the user choose which one they prefer makes sense to me. The json example you provided does not allow providing multiple transports. I think it should be an array of Transport

"t": <Array[Transport]>

xx.md Outdated
- t: type of Transport
- a: target of Transport

There can be many transport layers, but here are some recommendations:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to have a specific list of transport-types cashu supports in general instead of allowing everything. This list can be upgraded in the future without breaking the api. Otherwise a receiver could just add a new transport-type the wallet does not know how to handle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wouldn't be an issue though. If the PR does not include a transport that the wallet can handle, the wallet can not pay it and will let the user know.

I think we should curate a list of recommended / common transports though.

xx.md Outdated
- nostr
- type: "nostr"
- target: "<nprofile...>"
- post
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the transport-type post? Do you mean http with the method post? If so I would prefer the term http as the transport-type. Post could be misinterpreted as snailmail

@ngutech21
Copy link
Collaborator

Does this nut provide any information through the info-endpoint other than the supported-flag?
It could make sense to provide the supported currencies and min/max amounts like we do in nut-4 and nut-5. Or is this nut using the same values as nut-5?

xx.md Outdated
Regardless of the transport layer the payload should always (unless specified differently) be a JSON serialised object as follows:

```json
{ "token": "cashuAy..." }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just the plaintext?

Sending JSON makes sense only for HTTP but for all other transports (e.g. SMS or E-mail) it is awkward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I thought it would be good to define a reasonable standard that could work in any transport.

I an definitely open for suggestions

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I favor removing this requirement and allowing the plaintext encoded token to be used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to use a structured object because:

  1. We will break things once we decide to add stuff later
  2. There is a new payment id field that the requester can ask for in the payment payload.

xx.md Outdated
{
"a": int <optional>,
"u": str,
"r": str <optional>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should more than 1 mint be allowed?

xx.md Outdated

- nostr
- type: "nostr"
- target: "<nprofile...>"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the nostr transport follow NIP-61?

xx.md Outdated
Regardless of the transport layer the payload should always (unless specified differently) be a JSON serialised object as follows:

```json
{ "token": "cashuAy..." }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I favor removing this requirement and allowing the plaintext encoded token to be used.

xx.md Outdated

Cashu Payment Requests can be encoded and displayed/sent as string or qr code. They underlying object is serialised using CBOR and finally base64 encoded and prefixed with a version byte and standard prefix.

`"creq" + base64(versionByte + cbor(PaymentRequest))`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion, but should we consider keeping the cashu prefix and using the version byte to differentiate a token and a payment request?

Also, NUT-00 doesn't encode the version byte in the base64; it is included in the prefix. We should keep the spec consistent with this treatment.

@Egge21M
Copy link
Contributor Author

Egge21M commented Oct 15, 2024

@callebtc thanks for adding the suggestions. This does indeed LGTM!

xx.md Outdated
- `i`: Payment id to be included in the payment payload
- `a`: The amount of the requested payment
- `u`: The unit of the requested payment (MUST be set if `a` is set)
- `s`: Whether the payment request is for single use

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be r to match above

xx.md Outdated

## Payment payload

Regardless of the transport layer, the payload sent to the receiver is a JSON serialized object as follows:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would this work with NIP-61, where the nostr event is the payment, which has its own format and should be signed by the sender?

@Egge21M
Copy link
Contributor Author

Egge21M commented Oct 17, 2024

Are we ready to remove NIP-04, because the support for NIP-17 might be too limited still. Afaik nostr-tools does not have it yet.

But I agree that we probably should be looking forward and not carry stuff around that's already considered harmful

Copy link
Contributor

@callebtc callebtc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

18.md Outdated Show resolved Hide resolved

This NUT introduces a standardised format for payment requests, that supply a sending wallet with all information necessary to complete the transaction. This enables many use-cases where a transaction is better initiated by the receiver (e.g. point of sale).

## Flow
Copy link
Contributor

@ok300 ok300 Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it would be helpful to indicate in the Flow at which point user interaction or confirmation is needed (from the payer side), and if there is anything the app must / should show to the user before confirmation, similar to LNURL-pay / LNURL-withdraw.

18.md Show resolved Hide resolved
18.md Show resolved Hide resolved
18.md Outdated

### Transport types

The supported transport types are:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which are the transport types? Looks like a list was intended here, but missing.

@callebtc callebtc changed the title NUT-XX: Payment Requests NUT-18: Payment Requests Oct 18, 2024
@callebtc callebtc merged commit ebed5ba into cashubtc:main Oct 18, 2024
1 check passed
@prusnak
Copy link
Collaborator

prusnak commented Oct 18, 2024

typo fix in #178

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants