Skip to content

NUT-23: HTTP Payment required #239

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

NUT-23: HTTP Payment required #239

wants to merge 2 commits into from

Conversation

hzrd149
Copy link

@hzrd149 hzrd149 commented Mar 24, 2025

This PR adds NUT-23 that describes how the HTTP 402 status code + an X-Cashu header can be used to require payment for http resources
readable version

The goal for this NUT is to keep it tightly coupled to HTTP and keep all communication in-band to reduce complexity.

@callebtc
Copy link
Contributor

This should reuse the payment request from NUT-18 and specify which fields need to be set #243

@callebtc callebtc added ready Ready to merge needs implementation Needs a reference implementation new nut A new protocol NUT labels Mar 27, 2025
@callebtc
Copy link
Contributor

Looks ready. Please leave your reviews. We usually wait for an implementation before we merge.

@Egge21M
Copy link
Contributor

Egge21M commented Mar 27, 2025

This is great! I have been waiting for this. For anyone building an implementation for this, here is an endpoint to test against: https://cashu402.fly.dev/secret

Repo: https://github.com/Egge21M/cashu402

@positiveblue
Copy link

positiveblue commented Mar 27, 2025

It's nice to see more people building on the HTTP 402 🎉

eCash has something really distinct and it's that "the token is the money" which means that people can use it as "proof of payment" (you send it to me and I claim it instantly). This is different from other payment methods where you can do pull based payments (ex: credit cards) or push based but you need to check if the payment was completed outbound (ex: Bitcoin)

We have been working for a while with a generic flow that supports all kind of payment methods. The trade offs for ecash is

  • Cons:
    • One extra HTTP trip
  • Pros:
    • One integration, many payment methods
    • Support for multiple offers (ex: you need to become a paid user but can be professional, pro or enterprise)
    • Allows solutions that use third party gateways (you pay somewhere else and they send me a webhook that the offer was paid, easy to swap with current solutions like stripe)

I understand why you want to create a NUT for this. Same of why x402 exists for "strictly on chain payments" but even in that case I would try to bake in the "multiple offer support" in.

@trbouma
Copy link

trbouma commented Mar 28, 2025

If you plan to keep a int I suggest that you clarify to specify that it be fractional unit of the currency for a 402 payment required. Otherwise, you'll need a float type, if you are going to specify a fiat currency e.g., 0.01 USD

I prefer sticking to an int so the proposed change would be:

a: The amount required in the specified fractional unit

For background, every currency has a fractional unit and number to base
You can see below for example the Ukranian, hyrvnia has a fractional unit of kopeck and the number to base is 100
Similarly, the USD is the cent and the number to base is 100
Others like the Uganda Shilling is 1:1 (base unit same as fractional unit)
Finally, for BTC the fractional unit is sat, and the number to base is 100000000

I think this would be the best way to express pricing for 402 micropayment - in the fractional unit versus the base unit. A 1 cent per view versus $0.01 per view or 1 sat per view versus 0.000000000001BTC per view

You can peruse my currency table dump below for more examples.

currency_code,currency_rate,currency_symbol,currency_description,refresh_time,fractional_unit,number_to_base
UAH,999105.81,₴,Ukrainian hryvnia,,kopeck,100
VND,699594225.70,₫,Viet Nam Dong,,hao,10
XOF,17523097.96,R,West African Franc,,centime,100
AWG,52449.9714,f,Aruba florin,,centime,100
BTC,1,B,Bitcoin,,satoshi,100000000
UGX,109976512.00,R,Uganda Shillings,,shilling,1
BTN,2441712.23,Nu,Bhutanese Ngultrum,,chetrum,100
SAT,100000000,≐,satoshis,,msat,1000
NCR,1000000.0,nc,Narnia Crescent,,sat,100
NSL,1000000.0,៛,Narnia Silverleaf,,sat,100
XCD,275724.46,EC$,Eastern Caribbean Dollars,2025-01-28 06:00:00.623873,cent,100
CZK,2472350.09,Kč,Czech Koruna,2025-01-28 06:00:00.623873,heller,100
DKK,735253.67,kr,Danish Krone,2025-01-28 06:00:00.623873,ore,100
BNA,564850,bn,Bananas,,banana,1
HKD,800305.1,$,Hong Kong Dollars,2025-01-28 06:00:00.623873,cent,100
HRK,473845.0,kn,Croatia Kuna,2025-01-28 06:00:00.623873,kuna,1
HUF,40257665.02,Ft,Hungary Forint,2025-01-28 06:00:00.623873,filler,100
INR,8892916.05,R,Indian Rupees,2025-01-28 06:00:00.623873,paisa,100
ISK,13429936.32,kr,Iceland Krona,2025-01-28 06:00:00.623873,eyrir,100
KRW,148500613.63,₩,Korean Won,2025-01-28 06:00:00.623873,jeon,100
NZD,181642.41,$,New Zealand Dollar,2025-01-28 06:00:00.623873,cent,100
PLN,414457.54,zł,Poland Zloty,2025-01-28 06:00:00.623873,grosz,100
RON,490130.83,lei,Romania Leu,2025-01-28 06:00:00.623873,ban,100
RUB,10054395.45,₽,Russian Rubles,2025-01-28 06:00:00.623873,kopeck,100
SGD,138899.68,$,Singapore Dollars,2025-01-28 06:00:00.623873,cent,10
THB,3485525.46,ϯ,Thailand Baht,2025-01-28 06:00:00.623873,satang,100
TRY,3674040.93,₺,Turkey Lira,2025-01-28 06:00:00.623873,kurus,100
TWD,3383202.44,NT$,Taiwan New Dollars,2025-01-28 06:00:00.623873,cent,100
PHP,5941092.62,₱,Philippine Pesos,2025-01-28 06:00:00.623873,sentimo,100
NGN,162571200.0,₦,Nigerian Naira,2025-01-28 06:00:00.623873,kobo,100
NOK,1219284.0,kr,Norway Krone,2025-01-28 06:00:00.623873,ore,100
PKR,28689035.29,₨,Pakistan Rupee,2025-01-28 06:00:00.623873,paisa,100
SAR,375164.31,﷼,Saudia Arabia Riyal,2025-01-28 06:00:00.623873,halala,100
ZAR,1950854.4,R,South Africa Rand,2025-01-28 06:00:00.623873,cent,100
IDR,975427200.0,Rp,Indonesia Rupiah,2025-01-28 06:00:00.623873,sen,100
MXN,2106754.21,$,Mexican Pesos,2025-01-28 06:00:00.623873,centavo,100
QAR,375164.31,﷼,Qatar Riyal,2025-01-28 06:00:00.623873,dirham,100
ARS,107836579.79,$,Argentina Dollars,2025-01-28 06:00:00.623873,centavo,100
BRL,605534.29,R$,Brazilian Real,2025-01-28 06:00:00.623873,centavo,100
CHF,93110.99,CHF,Swiss Franc,2025-01-28 06:00:00.623873,rappen,100
SEK,1083808.0,kr,Sweden Krona,2025-01-28 06:00:00.623873,ore,100
CLP,101427455.56,$,Chilean Peso,2025-01-28 06:00:00.623873,centavo,100
KYD,85100.14,CI$,Cayman Islands Dollars,2025-01-28 06:00:00.623873,cent,100
CAD,146861.86,$,Canadian Dollars,2025-01-29 14:23:50.320343,cent,100
EUR,97809.2,€,Euro,2025-01-29 14:23:50.320588,cent,100
GBP,81857.61,£,British Pounds,2025-01-29 14:23:50.320769,pence,100
JPY,15805071.28,¥,Japanese Yen,2025-01-29 14:23:50.320945,sen,100
USD,101724.26,$,United States Dollars,2025-01-29 14:23:50.321121,cent,100
AUD,163546.06,$,Australian Dollars,2025-01-29 14:23:50.321297,cent,100
CNY,729474.84,¥,Chinese Yuan Renminbi,2025-01-29 14:23:50.321471,jiao,10

@Egge21M
Copy link
Contributor

Egge21M commented Mar 28, 2025

If you plan to keep a int I suggest that you clarify to specify that it be fractional unit of the currency for a 402 payment required.

There is no such thing in Cashu. Amounts are always integers of the specified unit

@trbouma
Copy link

trbouma commented Mar 28, 2025

If you plan to keep a int I suggest that you clarify to specify that it be fractional unit of the currency for a 402 payment required.

There is no such thing in Cashu. Amounts are always integers of the specified unit

So, if "usd" can only specify whole dollars?

@Egge21M
Copy link
Contributor

Egge21M commented Mar 28, 2025

So, if "usd" can only specify whole dollars?

A single unit of "usd" represents a dollar cent. There are no floats in the Cashu protocol.

@trbouma
Copy link

trbouma commented Mar 28, 2025

So, if "usd" can only specify whole dollars?

A single unit of "usd" represents a dollar cent. There are no floats in the Cashu protocol.

That's ok - if it's clarified in the spec. Currency is usually expressed as a main (base) unit and a fractional unit. I read the spec as the main unit, but it's actually the fractional unit. It wasn't apparent to me. Just wanting to reduce the potential confusion.

FWIW

From wikipedia

Each currency typically has a main currency unit (the dollar, for example, or the euro) and a fractional unit, often defined as 1⁄100 of the main unit: 100 cents = 1 dollar, 100 centimes = 1 franc, 100 pence = 1 pound, although units of 1⁄10 or 1⁄1000 occasionally also occur. Some currencies do not have any smaller units at all, such as the Icelandic króna and the Japanese yen.

@Egge21M
Copy link
Contributor

Egge21M commented Mar 28, 2025

I think this is out of scope for this NUT / PR. Cashu does not dictate anything about what "unit" can be. It can be a currency or something completely different. Also two mints can have key sets for different things that still share a name. The unit and "what it means" is only the mints responsibility and the spec can not specify anything about that.

@thesimplekid
Copy link
Collaborator

implemented in athenut thesimplekid/athenut-mint@723f8cd

- `a`: The amount required in the specified unit
- `u`: The currency unit (e.g., "sat", "usd", "api")
- `m`: Array of mint URLs that the server accepts tokens from
- `p2pk`: Optional P2PK requirements
Copy link
Contributor

Choose a reason for hiding this comment

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

Is P2PKOption defined anywhere?

@positiveblue
Copy link

Bump

I would try to bake the "multiple offer support" in

@gudnuf
Copy link
Collaborator

gudnuf commented Apr 16, 2025

Seems like this new nut is unnecessary with the change in #243 that makes the transport field optional. If the transport field in a payment request is undefined then the client can know to respond with payment in band.

This PR does define the x-cashu behavior which is useful, but I wonder if it deserves a new nut?

@hzrd149
Copy link
Author

hzrd149 commented Apr 16, 2025

Seems like this new nut is unnecessary with the change in #243 that makes the transport field optional. If the transport field in a payment request is undefined then the client can know to respond with payment in band.

It helps to have the HTTP 402 process clearly spelled out in a NUT so that all servers and clients implementing it don't have to worry about 4+ different implementations ( which is usually what happens without clear instructions )

This PR does define the x-cashu behavior which is useful, but I wonder if it deserves a new nut?

I don't know if it needs it own NUT, but it might make NUT-18 more crowded if it was merged into that

@Egge21M
Copy link
Contributor

Egge21M commented Apr 17, 2025

I think it is worth keeping this in a separate NUT, as it defines the header structure and protocol (both are clearly not part of NUT-18 scope).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs implementation Needs a reference implementation new nut A new protocol NUT ready Ready to merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants