Awesome
paypercall
Charge for HTTP APIs on a pay-per-call basis with Bitcoin and Lightning.
Want to require a payment when users take some programmatic action, such as sending an SMS or running imaging processing?
That's what paypercall
is for.
Available as an express middleware (where a node.js app makes charges directly) or as a reverse proxy (where a reverse proxy requires payment before providing access to an app).
Powered by :zap: Lightning Charge.
Install
$ npm install paypercall
Server Setup
As a middleware
paypercall
can be used as an express middleware to charge payments directly in your node.js apps.
Below is an example app that charges 0.1 USD to send SMS messages:
app.js
const pay = require('paypercall')({ chargeUrl: ..., chargeToken: ... })
, twi = require('twilio')(twilioSid, twilioToken)
, app = require('express')()
app.use(require('body-parser').urlencoded())
app.post('/sms', pay(0.1, 'USD'), (req, res, next) =>
twi.messages.create({ from: 'paypercall', to: req.body.to, body: req.body.message })
.then(msg => res.send({ success: true, sid: msg.sid }))
.catch(next))
app.listen(4000, _ => console.log('HTTP server running on localhost:4000'))
See ifpaytt
for a more full-fledged application using paypercall
as a middleware.
As a reverse proxy
Alternatively, you can develop your HTTP server with no payments awareness
and use paypercall
as a reverse proxy to handle payments.
Below is an example using a python app:
app.py
from flask import Flask, request
from twilio.rest import Client as Twilio
app = Flask(__name__)
twi = Twilio(twilioSid, twilioToken)
@app.route("/sms", methods=['POST'])
def sms():
msg = twi.messages.create(from='paypercall', to=request.form['to'], body=request.form['message'])
return { 'success': True, 'sid': msg.sid }
@app.run(Port=4001)
Run the python app and the paypercall
proxy:
$ FLASK_APP=app.py flask run
* Running on http://localhost:4001/
$ paypercall --charge-token mySecretToken --upstream-url http://localhost:4001 \
--port 4000 --rates-yaml '{ POST /sms: 0.1 USD }'
HTTP reverse proxy running on http://localhost:4000, proxying to http://localhost:4001
You will now have the python app running on port 4001 (providing API calls free of charge)
and the paypercall
reverse proxy running on port 4000 (charging on a per-call basis).
Paying for API calls
Users can access paypercall
-enabled API endpoints in three steps:
-
Send an empty request (no body) to the
paypercall
-enabled endpoint to get the BOLT11 payment request and theX-Token
header:$ curl -i -X POST http://localhost:4000/sms HTTP/1.1 402 Payment Required Content-Type: application/vnd.lightning.bolt11 X-Token: lmbdmJeoSQ0ZCB5egtnph.af1eupleFBVuhN2vrbRuDLTlsnnUPYRzDWdL5HtWykY lnbcrt8925560p1pdfh7n2pp54g5avyupe70l988h30u0hy8agpj2z7qsveu7ejhys97j98rgez0...
-
Make the payment:
$ lightning-cli pay lnbcrt8925560p1pdfh7n2pp54g5avyupe70l988h30u0hy8agpj2z7qsveu7ejhys97j98rgez0...
-
Send the request again, this time with the request body and with the
X-Token
header echoed back:$ curl -i -X POST http://localhost:4000/sms \ -H 'X-Token: lmbdmJeoSQ0ZCB5egtnph.af1eupleFBVuhN2vrbRuDLTlsnnUPYRzDWdL5HtWykY' \ -t to=+972-789456123 \ -d message='I got lightning working and all I got was this sms!' HTTP/1.1 200 OK Content-Type: application/json {"success":true,"sid":"SMf34fe622a8fe7565fc15be3ce8bc437e"}
Documentation
Middleware
const pay = require('paypercall')(options)
Returns a new payment middleware factory. options
can contain the following fields:
chargeUrl
: Lightning Charge server URL (optional, defaults tohttp://localhost:9112
)chargeToken
: Lightning Charge access token (required)dbPath
: Path to sqlite database (optional, defaults to./paypercall.db
)currency
: Default currency if none is specified (optional, defaults toBTC
)secret
: Secret key used for HMAC tokens (optional, generated based onchargeToken
by default)invoiceExp
: How long should invoices be payable for (optional, defaults to 1 hour)accessExp
: How long should paid access tokens remain valid for (optional, defaults to 1 hour)
const payware = pay(amount[, currency])
Returns an express middleware that requires a payment of amount
units of currency
(or the default currency if none provided) before letting requests pass through.
Can be used as following:
const pay = require('paypercall')({ chargeToken: 'myToken', currency: 'EUR' })
, app = require('express')
// charge requests to a specific route
app.post('/sms', pay(0.15), (req, res) => { /* send SMS */ })
// charge all requests to /paid-apis/*
app.use('/paid-apis', pay(0.25))
// dynamic pricing (should only be based on the method and path)
app.post('/ocr/:type', (req, res, next) => {
pay(getPriceForType(req.params.type))(req, res, (err) => {
if (err) return next(err)
// payment succesfull, run OCR
// (the paid invoice is accessible at `req.invoice`)
})
})
Reverse proxy
$ paypercall --help
Charge for HTTP APIs on a pay-per-call basis with Bitcoin and Lightning
Usage
$ paypercall [options]
Options
-c, --charge-url <url> lightning charge server url [default: http://localhost:9112]
-t, --charge-token <token> lightning charge access token [required]
-u, --upstream-url <url> the upstream server to reverse proxy [required]
-r, --rates-path <path> path to YAML file mapping from endpoints to rates [default: ./rates.yaml]
-y, --rates-yaml <yaml> YAML string to use instead of reading from {rates-path}
-x, --currency <name> default rate currency if none is specified [default: BTC]
-d, --db-path <path> path to store sqlite database [default: ./payperclick.db]
--invoice-expiry <sec> how long should invoices be payable for [default: 1 hour]
--access-expiry <sec> how long should paid access tokens remain valid for [default: 1 hour]
--token-secret <secret> secret key used for HMAC tokens [default: generated based on {charge-token}]
-p, --port <port> http server port [default: 4000]
-i, --host <host> http server listen address [default: 127.0.0.1]
-e, --node-env <env> nodejs environment mode [default: production]
-h, --help output usage information
-v, --version output version number
Example
$ payperclick -t myAccessToken -u http://upstream-server.com/ \
-y '{ POST /sms: 0.0001 BTC, PUT /page/:id: 0.0002 BTC }'
License
MIT