[Из песочницы] How to make possible micro-payments in your app

This week I spent coding my very first public pet-app based on Telegram chat bot which acts as a Bitcoin wallet and allows to send and receive tips between Telegram users and other so-called «Lightning Apps». I assume that you are familiar with Bitcoin & Telegram in general, i«ll try to post short and without deep jump into details. More resources about bitcoin can be found here and Telegram is simply an instant messenger that allows you to create your custom apps (chat-bots) using their platform.


What are the key points of such app?


  • Allows to rate other users ideas and answers with real value instead of
    «virtual likes». This brings online conversation to completely new level
  • Real example of working micro-payment app which can act with other entities
    over internet using open protocol
  • All the modules are open-source projects and can be easy re-used and adjusted
    for your own project. App does not relay on third-party commercial services.
    Even it falls under e-commerce field, which is currently almost closed, the app
    is based on open solutions.


What are the use-cases?

something like this…

image


or like that…

image

What do numbers mean? These are bitcoin units — satoshis. As for mid-2019 1000 satoshis has value of $0.06. You can spend it online or exchange to your local currency using many available services. So this is not just another «number in a database» or a «coupons you can spend in our partner store», but a real internet money.

You may ask what to do, if you don«t have any bitcoin to use in the app. You simply can receive a tip from existing app user or buy small amount of bitcoin on various exchanges or bitcoin wallets for mobile phone or pc.

imageimageimage

When you got bitcoin, you can start @atomic_tipbot and make a /deposit.

You’ll see an invoice, you can select preferred payment method and send money just scanning the QR code or with button press («open in wallet») on invoice.

imageimageimage

Confirm payment in your mobile wallet, and you will see a confirmation screen almost instantly in Telegram.

So far we touched two separate parts of the app: telegram bot back-end and payment processing system (actually more like it’s front-end).

To create this Telegram bot i«ve used Python Telegram Bot. And of course, the bot is open-source.

As a payment processing system to generate invoices, check status, send notification and take care of UX is used BTCPayServer which documentation can be found here.

If you are looking on how to process payment events directly, you can look at Electrum CLI manual pages for on-chain payments and check out solutions for off-chain (Lightning) payments like Lightning-charge or Sparko.


You said «no 3rd party» and «no cloud services»?!

Yes, that«s true. In my app i am using free shared hosting of BTCPayServer instance — BTCPayJungle, but it is possible to spin up even on your own dedicated server. Just remember that you need then about ~500GB of space to store Bitcoin and Litecoin blockchain. BTCPayServer does not hold any private keys or sensitive information that can allow hackers to control funds on you wallet. For on-chain (standart) payments you need to provide xpub key from you wallet to BTCPayServer so it will be able to generate public addresses. In this way, BTCPay architecture makes possible to process payments in trustless manner even on shared environment. It is very simple to configure if you use proven wallets such as Electrum.


What is Lightning Network?

It is something like an add-on to Bitcoin which adds additional abstraction layer and allows to make instant and almost free bitcoin transactions. All this stuff is possible thanks to cryptographic multi-signature magic, which is often called smart-contract. There are multiple implementations of LN protocol. I am using c-lightning C implementation.

The major difference between on-chain and off-chain (Lightning) payments are in how transactions are stored. All on-chain transactions are broadcasted to the whole network so each running bitcoin node will record this transaction into blockchain, if block containing this transaction will be mined. Hey, BitFury, am i right? ;) That’s why we call such transactions on-chain. Such transactions will be stored in blockchain till the end of its existence. It is possible publicly to track, analyse and monitor any such transaction over the network.

In case of off-chain (Lightning) transaction things happens in other way. Each lightning node has (may be many) on-chain address, from which one all starts. When funds are received on such address, using cryptography techniques node may open so-called lightning channel to another node, what means that these two nodes made agreement about funds availability between them. Later, all the transactions that are made trough channel (own transactions, or it may be routed transaction from another node) are stored only on those two nodes (and intermediate node if there was such). The only transactions which are stored in networks blockchain are channel opening and closing transactions. This is why Lightning transactions we call off-chain. Mostly they exist only in node database-file.


The whole thing is visualized on diagram


image

Short story:

1) User sends message and requests deposit to bot on Telegram

2) Telegram triggers bot’s python app

3) python app sends request to BTCPayServer

4) BTCPayServer generates incoming addresses for BTC and LTC, as well contacting lightning-charge, which one is getting data from c-lightning daemon. In a result we get well-handled HTML frame for payment processing

5) User pays invoice and will see confirmation

6) BTCPayServer sends invoice payment notification (IPN) to specified callback url, in our case it
will go to callbacks.py (another python app to listen for callbacks)

7) According to complete payment detail user balance is changed and user gets notification

… if user decides to withdraw funds (tips he received) …

8) Bot’s python app listens to text or image of QR, when it gets invoice data (long string of characters) then it sends request to pay that invoice to another python app paylightning.py*, which initializes pay-outs using c-lightning RPC.


image

*To be fair, I need to mention that my pay-wrapper paylightning.py is incomplete and may return false-negative in some specific cases. You should use well-tested payment wrapper for c-lightning in case of production-like use.

Users are able to send tips to each other on group chats and send/receive value without any limits. But that«s not all. Thanks to Lightning Network protocol, which is in some kind of unified and simplified payment network you are able to send same balance coins to completely different apps. There is another bitcoin tip-bot on telegram — @lntxbot. User can request a deposit to this bot

imageimage

Simply forward data from @lntxbot to @atomic_tipbot and voul«a!

This is not anymore «number swap in database» but payment between two separate, non-related «databases». Bot already has few 'LApps' integrated: sat2.io and lnsms.world.

You may ask, what exactly means LApp integration?

Each Lightning Network node acts as a bitcoin wallet. It has both on-chain and off-chain balance. The on-chain balance usually is used to open channels and store funds from closed channels, in short — nodes maintenance tasks. The off-chain balance is what exactly is used to exchange funds between nodes. The core functions of each node is ability to send or receive funds, so node as a software provides pay and invoice methods. I’ll show in short how it happens:

user@ln-node:/ % lightning-cli invoice 1000000000 internal_description external_description
{
   "payment_hash" : "64c0c8f5f8f708b08487ad1376f3d256f92ccb9606987ba395c2b4193efde5a0",
   "expires_at" : 1558709834,
   "bolt11" : "lnbc10m1pwdaj72pp5vnqv3a0c7uytppy845fhdu7j2mujejukq6v8hgu4c26pj0hauksqdpqv4u8getjdeskchmyv4ekxunfwp6xjmmwxqyjw5qcqp2rzjqw3qcrp2u3ggke56wrjlstcg76drmfw680cvjum88sl7ja7mpas7xzxz8sqqfecqqyqqqqlgqqqqqqgqjqn8e3ml733dkms2txxldnuwsllwhhkldss73268hdka3e7f083vjhjqurwqrndqd2rcd85pw7vkywnr4dq7yfd59r0g2mw4wzztwr6dcprxzej6"
}

The BOLT11 invoice is generated in this case, which is unified format for payment requests in Bitcoin (as well as Litecoin) network. This string includes data about invoice recipient, amount, timestamp, expiration etc. To decode this string c-lightning provides decodepay method:

user@ln-node:/ % lightning-cli decodepay lnbc10m1pwdaj72pp5vnqv3a0c7uytppy845fhdu7j2mujejukq6v8hgu4c26pj0hauksqdpqv4u8getjdeskchmyv4ekxunfwp6xjmmwxqyjw5qcqp2rzjqw3qcrp2u3ggke56wrjlstcg76drmfw680cvjum88sl7ja7mpas7xzxz8sqqfecqqyqqqqlgqqqqqqgqjqn8e3ml733dkms2txxldnuwsllwhhkldss73268hdka3e7f083vjhjqurwqrndqd2rcd85pw7vkywnr4dq7yfd59r0g2mw4wzztwr6dcprxzej6
{
   "currency" : "bc",
   "created_at" : 1558105034,
   "expiry" : 604800,
   "payee" : "025a14b8ed40583d67aec92da19453e0b2d1fbbf75f96f85d3dd0ff61a51ee0490",
   "msatoshi" : 1000000000,
   "amount_msat" : "1000000000msat",
   "description" : "external_description",
   "min_final_cltv_expiry" : 10,
   "routes" : [
      [
         {
            "pubkey" : "03a20c0c2ae4508b669a70e5f82f08f69a3da5da3bf0c973673c3fe977db0f61e3",
            "short_channel_id" : "574012x1255x1",
            "fee_base_msat" : 1000,
            "fee_proportional_millionths" : 1,
            "cltv_expiry_delta" : 144
         }
      ]
   ],
   "payment_hash" : "64c0c8f5f8f708b08487ad1376f3d256f92ccb9606987ba395c2b4193efde5a0",
   "signature" : "304502210099f31dffd18b6db8296637db3e3a1ffbaf7b7db087a2ad1eedb7639f25e78b25022079038370073681aa1e1a7a05de6588e98ead078896d0a37a15b755c212dc3d37"
}

In case we need to pay some invoice, we should use pay method of c-lightning:

user@ln-node:/ % lightning-cli pay lnbc2u1pwdana3pp5c0nyfgq974hr8huflt9uutyalj4maaw3q5594xp89jkvw74jme3sdql2pshjgr5dus8q6r0dejjqampd3kx2aqcqzpgwjgn45gy80jwjhgm3tpsxg33j6h6pehdus0mnjerrad943cz3vs83g30lyhlhfjxtqvtl76vttkuhs5jekuxpsqmf98l8265pwmm76gp4e7z6j
{
   "id" : 163,
   "payment_hash" : "c3e644a005f56e33df89facbce2c9dfcabbef5d105285a98272cacc77ab2de63",
   "destination" : "03021c5f5f57322740e4ee6936452add19dc7ea7ccf90635f95119ab82a62ae268",
   "msatoshi" : 200000,
   "amount_msat" : "200000msat",
   "msatoshi_sent" : 200003,
   "amount_sent_msat" : "200003msat",
   "created_at" : 1558106072,
   "status" : "complete",
   "payment_preimage" : "1a9552b9f4e9199e26839353f870b12cc85b7674a2fb134e78aa370032611019",
   "bolt11" : "lnbc2u1pwdana3pp5c0nyfgq974hr8huflt9uutyalj4maaw3q5594xp89jkvw74jme3sdql2pshjgr5dus8q6r0dejjqampd3kx2aqcqzpgwjgn45gy80jwjhgm3tpsxg33j6h6pehdus0mnjerrad943cz3vs83g30lyhlhfjxtqvtl76vttkuhs5jekuxpsqmf98l8265pwmm76gp4e7z6j"
}

All the statuses of payments are handled by the whole network and you own node. You don’t have a need (but have a possibility to do so) to communicate at all with receiving-side app developers or their API, some card processing provider or any bank to get confirmation about your payment or to release funds from your own account. There are no charge-backs or any other kind of external influence on you accounts balance. All you need is a software which acts by decentralized network rules. This is a solution to exchange a value over internet, without a need of any kind of arbiter in the middle who decides about your payment compatibility with some rules, sometimes even previously unknown to all parties. There are no «internal» rules in bitcoin, all the rules your app needs to complete with are open and known.

This makes possible direct, almost free and instant value exchange over internet between people or even software itself in truly autonomous way.

If there is a way to get payment data from other app then you are able to act with it in financial sense. As in our example, lnsms.world is a web-app that allows you to send sms to any number and pay for it with bitcoin. To integrate such option in our app we simply need to pass required data from web-form to lnsms.world:

payload = {                                                                                                                           
        'number': phone_number,
        'text': text,
        'force_unicode': 0
    }

    send_req = requests.post('https://lnsms.world/invoice', data=payload)
    if send_req.status_code == 201:
        plain_invoice = str(send_req.text)

I don’t know for what reason, but lnsms responds with 201 HTTP status code and returns BOLT11 invoice. Well, that’s enough to pay this invoice and our users will send sms shortly after payment is received. We can charge some fee on top or not, that is our free choice.

Now, imagine, that your bot is actually some more useful app and pays not another bot, but some other entity, connected to internet. Or, those may be payments between software, machines, stores, service providers on regular basis repeating each few seconds? Do you feel the power of micro-payments? That is how the Internet of Things will look like. Already now there are such projects as https://althea.org/ and in near future we will face great changes in field what we call e-commerce.

If you are interested in further reading about micro-payments, bitcoin and related software solutions from technical-only perspective feel free to note this in comments.

If you like this post and would try bitcoin payments yourself you can leave me a tip on my tipping page.

© Habrahabr.ru