author: lsunsi
author: luizParreira
author: lorenzolfm
This document describes an extension to the payRequest base specification that allows the WALLET
to send money to a SERVICE
denominating the amount in a different currency. The features proposed enable many use cases ranging from denominating an invoice in a foreign currency to a remittance-like experience.
The main features provided by this extension are:
SERVICE
MUST informWALLET
what currencies it supportsWALLET
MAY request an invoice with an amount denominated in one of the currenciesWALLET
MAY request to the payment to be converted into one of the currencies
The extension is opt-in and backward compatible. Further, a supporting WALLET
can always tell if a SERVICE
is also supporting beforehand so the communication is never ambiguous.
The first request is unchanged from the base specification.
SERVICE
must alter its JSON response to the first request to include a currencies
field, as follows:
type BaseResponse = {
tag: "payRequest",
metadata: string,
callback: string,
maxSendable: number,
minSendable: number
}
type Currency = {
code: string, // Code of the currency, used as an ID for it. E.g.: BRL
name: string, // Name of the currency. E.g.: Reais
symbol: string, // Symbol of the currency. E.g.: R$
decimals: number, // Integer; Number of decimal places. E.g.: 2
multiplier: number, // Double; Number of millisatoshis per smallest unit of currency. E.g.: 5405.405
convertible?: bool // Whether the payment can be converted into the currency
}
type ExtendedResponse = BaseResponse & {
currencies: Currency[]
}
{
"tag": "payRequest",
"metadata": '[["text/plain","$kenu ⚡ bipa.app"]]',
"callback": "https://api.bipa.app/ln/request/invoice/kenu",
"maxSendable": 1000000000,
"minSendable": 1000,
+ "currencies": [
+ {
+ "code": "BRL",
+ "name": "Real",
+ "symbol": "R$",
+ "decimals": 2,
+ "multiplier": 5404.405,
+ "convertible": true
+ }
+ ]
}
- The inclusion of the
currencies
field implies the support of this extension - The inclusion of a
currency
implies it can be used for denomination of an amount - The inclusion of a
convertible currency
implies theSERVICE
can quote and guarantee a price for a given currency - The
multiplier
is not guaranteed by theSERVICE
and is subject to change - The
code
of acurrency
will be used as an identifier for the next request and must be unique - The
code
must be according to ISO-4217 if possible - The order of the
currencies
may be interpreted by theWALLET
as the receiving user preference for a currency
Upon receiving the currencies
field on the response, the WALLET
shows the user it has the option of denominating the amount in one of the currencies
or for the payment to be creditted as a different currency
for the receiver.
The inputs that must be gathered from the user are:
- An optional denominating currency and amount (
CURRENCY_D
andAMOUNT_D
) - An optional
convert
currency (CURRENCY_C
)
The most general case has all the parameters set.
It will generate an invoice with the amount equivalent to AMOUNT_D
CURRENCY_D
, which will be converted into CURRENCY_C
by the SERVICE
upon payment.
<callback><?|&>amount=<AMOUNT_D>.<CURRENCY_D>&convert=<CURRENCY_C>
Each combination of parameters is valid and generates a different use case.
- Omitting the
amount
denomination implies the invoice is for millisatoshis (base spec) - Omitting the
convert
implies the receiver will get BTC from the payment, no matter theamount
denomination
Note that the amount provided in all requests is always an integer number interpreted as the smallest unit of the selected currency
. The smallest unit needs to be according to the decimals
parameter, so the WALLET
has all the needed information to receive input and show output properly.
Upon receiving a currency-denominated request from WALLET
, the SERVICE
must return an invoice with an amount matching the converted rate for the amount in that currency. The rate used does not need to match the multiplier
first informed.
If the WALLET
requested an actual conversion, the SERVICE
must provide an additional field alongside the invoice informing the guaranteed converted
amount that will be creditted to the receiver upon payment. The converted
amount, and therefore the conversion rate, must be guaranteed by the SERVICE
for as long as the invoice is not expired. The converted
amount must be denominated in the smallest unit of the currency, just like the amount
parameter.
type BaseResponse = {
pr: string,
routes: [],
}
type ExtendedResponse = BaseResponse & {
converted?: number, // Integer; Present if and only if `convert` was received.
}
{
"pr": "lnbc1230n1pjknkl...ju36m3lyytlwv42fee8gpt6vd2v",
"routes": [],
+ "converted": 123
}
These examples show all the possible uses of this extension by a supporting WALLET
and SERVICE
.
GET <service>/.well-known/lnurlp/<identifier>
{
"tag": "payRequest",
"callback": "bipa.app/callback",
"metadata": "...",
"minSendable": 1000,
"maxSendable": 1000000,
"currencies": [
{
"code": "BRL",
"name": "Reais",
"symbol": "R$",
"decimals": 2,
"multiplier": 5405.405,
"convertible": true
},
{
"code": "USDT",
"name": "Tether",
"symbol": "₮",
"decimals": 6,
"multiplier": 26315.789
}
]
}
// GET <callback>?amount=538000
{ "pr": "(invoice of 538 sats)" }
// GET <callback>?amount=100.BRL
{ "pr": "(invoice of 538 sats)" }
// GET <callback>?amount=538000&convert=BRL
{ "converted": 100, "pr": "(invoice of 538 sats)" }
// GET <callback>?amount=100.BRL&convert=USDT
{ "converted": 200000, "pr": "(invoice of 538 sats)" }
// GET <callback>?amount=100.BRL
{ "status": "ERROR", "reason": "..." }
-
Some of the ideas included in this PR were taken from the implementation and discussion on this PR. Most precisely, @ethanrose (author) and @callebtc (contributor).
-
Some early ideas for this including some other aspects of it were hashed out (but not pull-requested) in this earlier draft too. Thanks, @luizParreira (author), @joosjager (contributor), @za-kk (contributor).