| Name | fiat_on_ramp |
|---|---|
| Version | 0.1 |
| Status | Draft |
The goal of this specification is to provide standardized API for applications to purchase bitcoin. This will make integrations simple and providers compatible, enabling wider adoption.
To ensure user's ownership of the withdrawing wallet user must sign a message. Service provides the user with a message and user returns that message or part of the predefined message which the user then returns signed with his public key: Example:
"node_pubkey": "02765a281bd188e80a89e6ea5092dcb8ebaaa5c5da341e64327e3fadbadcbc686c",
"message": "I confirm my bitcoin wallet. [7v7t4Fmb]",
"signature": "rywfek6717yuqpfpjmghyf173obgswr9uw5wbfsfhc8exjomftm71st4cyxprzkx5juxiokhbxm8rzkxoz8e3zmmpa644tudgt119s91",
This can be part of another API call not a standalone step.
Order scenarios:
Steps:
- service needs to provide a quote for the user
- user confirms the order
- service provides payment methods
- user needs to transfer fiat to the service (within specific amount of time?)
- user withdraws the proceeds (amount based on agreed quote)
Steps:
- services provides an estimate to the user
- user confirms the order
- service provides payment methods
- user needs to transfer fiat to the service
- user withdraws the proceeds (amount based on execution at the time)
Service provides lnurlw to the user that user can claim at their convenience. Control of the payout is enforcable so only the pubkey who previously signed the wallet ownership verification message can be the recipient of the funds.
Using nurlw instead of invoices provided by the users addresses multiple potential issues:
- expired invoices and thus failed payments
- upfront commitment to payout amounts which means long lived quotes
Alternative options:
- user provides an invoice for the quoted amount
- user provides pubkey and provider opens a channel and pushes the amount (can be used as a backup for 1.)
| Name | function | status | type |
|---|---|---|---|
| /verify | get secret to verify wallet ownership | required | GET |
| /quote | place order | required | POST |
| /order | place order | required | POST |
| /orders | get order status | required | GET |
| /withdrawal | get lnurlw | required | POST |
| /payout | get payout options | optional | GET |
| /payment-options | get supported payment options and currencies | required | GET |
Request a token to be signed by the reciever node as proof of ownership
Request:
POST /verify
Response:
{
"id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"token": "yyq6qpj2a",
"expires_on": "2023-09-20T00:25:11.123Z"
}
tokenrandom string from the provider that needs to be signed with the node pubkey
Get a an quote or estimate from the provider based on amount of fiat you want to spend
Request:
POST /quote
{
"amount_fiat": 1000,
"currency_id":1
}
amount_fiatwhat the client wants to spend- must be greater than 0 and less than 1000
currency_idis the fiat currency the client wants to be quoted in and will be used as payment- must one of the supported currencies from
/payment-options
- must one of the supported currencies from
Response:
{
"id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"amount_fiat": "1000",
"currency_id": 1,
"amount_sats" : 800000 ,
"expires_on": "2023-09-20T00:25:11.123Z"
}
amount_satsthe amount of bitcoin the client will return for the fiat amount specified in the quoteexpires_onuntil when the order needs to arrive for the quote to be honored
Confirm an order from quote and get payment information in return
Request:
POST /order
{
"quote_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"signature": "c4275e29ed8efecc0162a223403149b60fb38a3adc5c3d435f7071e8e4e0face"
}
Response:
{
"order_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"order_status": "placed"
"expires_on": "2023-09-20T00:25:11.123Z"
"payment_info": {
"
}
}
order_id must be valid UUID 4
order_status can be placed, filled, finished or refunded
payment_info returns the payment processing details
expires_on until when the payment needs to arrive for the order to be honored
Get order status
Request:
POST /order
{
"order_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6"
}
Response:
{
"order_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"order_status": "finished"
"order_status_date": "2023-09-20T00:25:11.123Z"
}
order_id must be valid UUID 4
order_status: can be
placed- status upon user confirmation of the quote / pending paymentfilled- status when order is executed and user can withdraw itfinished- status when user successfully withdrew the fundsrefunded- status when fiat payment was refunded
order_status_date datetime of when the status was made
Request lnurlw from the provider. User can provide optional fallback onchain address which will be used if the withdrawal is not claimed before the expiration date.
Request:
POST /withdrawal
{
"order_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"failback_onchain": "bc1qcmu7kcwrndyke09zzyl0wv3dqxwlzqkma248kj" #optional
}
Response:
{
"order_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"withdrawal_expiration_date": "2023-09-20T00:25:11.123Z"
"lnurlw": "LNURL..."
}
order_id must be valid UUID 4
failback_onchain valid bitcoin address
withdrawal_expiration_date datetime when the lnurlw will expire and in case of fallback provided funds will be sent onchain
Get a list of supported currencies and their payment options
Request:
POST /payment-options
{
"currency_code": "<>" # optional (chf,eur, usd etc)
}
Response:
If no currency_code is specified in request:
{
"currencies": [
{
"eur": {
"currency_id": 1,
"currency_code": "EUR",
"payment_options": [
{
"option": "SEPA",
"id": 1,
"fee_rate": 0.005,
"min_amount": 10,
"max_amount": 1000
},
{
"option": "SEPA Instant",
"id": 2,
"fee_rate": 0.01,
"min_amount": 10,
"max_amount": 1000
},
{
"option": "Credit card",
"id": 3,
"fee_rate": 0.05,
"min_amount": 10,
"max_amount": 1000
}
]
}
},
{
"chf": {
"currency_id": 2,
"currency_code": "CHF",
"payment_options": [
{
"option": "SEPA Instant",
"id": 2,
"fee_rate": 0.01,
"min_amount": 10,
"max_amount": 1000
},
{
"option": "Credit card",
"id": 3,
"fee_rate": 0.05,
"min_amount": 10,
"max_amount": 1000
}
]
}
}
]
}
If currency_code (in this case EUR) is specified in request:
{
"currencies": [
{
"eur": {
"currency_id": 1,
"currency_code": "EUR",
"payment_options": [
{
"option": "Revolut",
"id": 1,
"fee_rate": 0.01,
"min_amount": 10,
"max_amount": 1000
},
{
"option": "Sepa Instant",
"id": 2,
"fee_rate": 0.01,
"min_amount": 10,
"max_amount": 1000
},
{
"option": "Credit card",
"id": 3,
"fee_rate": 0.01,
"min_amount": 10,
"max_amount": 1000
}
]
}
}
]
}