Payments#
With hourly, issuing invoices is a breeze. To issue a stripe invoice:
hourly-report invoice=stripe stripe.customer.email=myclient@momandpop.com
See the Instructions for configuring hourly for Stripe.
To issue a BTCPay
invoice connected to your BTCPay Server:
hourly-report invoice=btcpay
See the Instructions for configuring hourly with a BTCPay server.
Currently, only BTCPay
and Stripe
are supported. If you are interested
in adding support for other invoicing platforms, issue a pull request.
If you want to sponsor development for an hourly feature, contact Asher.
Stripe#
Stripe is a popular global payment processing platform for credit cards. They have an invoice API that allows hourly to issue invoices on your behalf in a single command:
hourly-report invoice=stripe stripe.customer.email=my.client@momandpop.com
Stripe Setup#
Step 1 - install the stripe python api:#
pip install --upgrade stripe
Step 2 - Create a stripe account#
You will need an account at Stripe. Be sure to follow the steps for a developer looking to handle one-time payments. You should also set up your invoice template settings.
Step 3 - Set environment variables#
From the Stripe dashboard:
- copy the
Secret key
and set it as an environment variableSTRIPE_API_SECRET_KEY
You can also paste in the Secret key
later as a command-line argument argument to hourly: stripe.secret_key=<your stripe secret key>
Warning
You will probably want to use your test_
API keys first!
Hourly Configuration#
Hourly provides the following default configuration for stripe invoices:
invoice: # see https://stripe.com/docs/billing/invoices/create-invoice#python stripe: secret_key: ${env:STRIPE_API_SECRET_KEY} customer: name: null email: null description: null customer_id: null # skips customer creation if set invoice_item: customer: # overridden by customer_id amount: # overrides earnings currency: # overrides compensation currency description: # overrides <hours worked> from repo.start_date to repo.end_date invoice: customer: # overridden by customer_id collection_method: send_invoice days_until_due: 30 auto_advance: True # auto-finalize this draft after ~1 hour. footer: Time sheet generated by hourly send_invoice: true # sends invoice to customer.email immediately return_status: false logging: 40 # https://docs.python.org/3/library/logging.html#logging-level
The following fields are subsets of stripe's own API, which has additional fields you can use:
Any of the defaults can be overridden, either at command line or by your project's configuration override.
However, stripe.customer.email
is a required field.
Generating Stripe invoices#
To generate a stripe invoice for your repo, you will need to specify an email address. Hourly will prepare an invoice and ask for confirmation. Here is an example of what that looks like when I run hourly on the hourly repo:
hourly-report invoice=stripe repo.start_date="Jan 1, 2020" stripe.customer.email=apembroke+hourly@gmail.com"
1 days 04:02:14, 28.04 hours worked 2803.72 USD Generating stripe invoice for Asher Pembroke creating new customer new customer_id: cus_GVy3BWS792lu4D customer: description: null email: apembroke+hourly@gmail.com name: null customer_id: cus_GVy3BWS792lu4D invoice: auto_advance: false collection_method: send_invoice customer: cus_GVy3BWS792lu4D days_until_due: 30 footer: Time sheet generated by hourly invoice_item: amount: 280373 currency: usd customer: cus_GVy3BWS792lu4D description: 28.04 hours worked from 2020-01-03T18:44:04-05:00 to 2020-01-09T02:04:18-05:00 logging: 40 return_status: false secret_key: ${env:STRIPE_API_SECRET_KEY} send_invoice: true Is this correct? (yes/n): yes Success! Invoice will be sent to apembroke+hourly@gmail.com Invoice may be paid at https://pay.stripe.com/invoice/<---- redacted -----> View your invoice at https://dashboard.stripe.com
Note
I have redacted actual payment URL
The recipient should get an email from stripe to pay by credit card. You can test credit card payment using one of their testing cards. Meanwhile, visiting the url should show you a page like this:
BTCPay#
Background#
BTCPay is a decentralized payment processing platform for accepting cryptocurrency. With BTCpay integration, you can issue invoices and receive crypto payments with maximum privacy and minimal cost. You can use a third-party provider or host it yourself - the only difference will be the domain name used to create the local client.
Setup#
First you will need to register and create a store on a BTCPay server
. There are a few free ones listed on btcpayserver.org, but please use caution when choosing a free service, as there are privacy trade-offs to consider. For maximum privacy and security, host one yourself.
Once you've chosen a server, connect a bitcoin wallet to your new store. This can be done in your store's general settings, under Derivation Scheme
, where you provide your wallet's xpubkey
- BTCPay Server uses this key to generate a unique payment address for every invoice issued.
Warning
A legitimate BTCPay Server should only ask for your wallet's xpubkey
and NEVER YOUR PRIVATE KEY
Info
BTCPay also supports Lightning invoices, which allows for instant settlement. This involves some tradeoffs in security and availability.
Then you will need to install the btcpay-python client
pip install btcpay-python
Pairing with BTCPay server#
Follow these pairing instructions from the kind BTCPay
developers.
Note
These instructions correspond to "The manual way" - we want to be able to create a btcpay client on-demand without storing it in a database.
I'm essentially repeating their instructions below:
Step 1 - Get a pairing code#
- On your BTCPay server, browse to Stores > Store settings > Access tokens > Create new token
- Fill in the form:
- Label:
- Public key: leave blank
- Label:
- Click save and then copy the 7 digit
pairing_code
from the success page
Step 2 - Generate a private key#
This can be done with the following code:
from btcpay import crypto privkey = crypto.generate_privkey() with open('btcpayserver.pem', 'w') as pem: pem.write(privkey)
Here we store the private key in a PEM
file. By default,
hourly will look for btcpayserver.pem
in the top level of your git repo,
but you can use a different name.
Warning
Do not add the pem file to your git repo! List it in your .gitignore so you don't do so by accident.
Step 3 - Create a client#
Create a client using host url of your btcpayserver
(e.g. https://btc.exitpay.org) and private key:
client = BTCPayClient(host=host_url, pem=privkey)
Store your server's host url in the environment variable BTCPAYSERVER_HOST
.
Step 4 - Generate a pairing token#
using the pairing code from Step 1
token = client.pair_client(pairing_code) merchant_token = token['merchant']
Save the merchant_token as an environment variable BTCPAYSERVER_MERCHANT
Step 5 - Recreate the client#
Whenever you like:
client = BTCPayClient( host = host_store, pem = privkey, tokens = token, )
Step 6 - Generate a test invoice#
Assuming you have completed the steps to connect a wallet to your btcpayserver, you should be able to run the following code to generate an invoice.
new_invoice = client.create_invoice({"price": 20, "currency": "USD"}) print(new_invoice['url'])
This should give you a payment url you can email to your client/employer.
Depending on how you set up your BTCPay Server, the invoice will only be valid for a short period of time (default is 15 minutes). There is a trade-off here: a short time period mitigates the risk of currency fluctuation, but requires that the client/employer must act quickly to pay the invoice.
Hourly configuration#
Hourly creates a BTCPayClient
through the following configuration:
# for invoice spec, see https://bitpay.com/api/#rest-api-resources-invoices invoice: btcpay: host: ${env:BTCPAYSERVER_HOST} tokens: merchant: ${env:BTCPAYSERVER_MERCHANT} pem: btcpayserver.pem # file holding btcpayserver private key return_status: false invoice: currency: null # will be honored if set price: null # will be honored if set, else determined by wage orderId: null fullNotifications: True extendedNotifications: True transactionSpeed: medium notificationURL: null # https://mywebhook.com notificationEmail: null # myemail@email.com redirectURL: null # https://yourredirecturl.com buyer: email: null # fox.mulder@trustno.one name: null # Fox Mulder phone: null # 555-123-456 address1: null # 2630 Hegal Place address2: null # Apt 42 locality: null # Alexandria region: # VA postalCode: # 23242 country: # US notify: True itemDesc: null # will be honored if set, else hourly will provide
This allows hourly to access your environment variables and the pem
file you created above.
Any of these parameters can be overridden when you run hourly. Here are some examples.
hourly-report invoice=btcpay btcpay.pem=<private key> hourly-report invoice=btcpay btcpay.pem=/path/to/other/btcpayserver.pem hourly-report invoice=btcpay btcpay.host=https://myprivateserver.com
Hourly Invoicing#
If you configured hourly with BTCPay, you can generate an invoice for your git repo in a given date range. Here is what that looks like when applied to the hourly repo:
hourly-report invoice=btcpay payment=btcpay repo.start_date="Jan 1, 2020" repo.end_date="Jan 6, 2020" Processing timesheet for Asher Pembroke pay period: 2020-01-03 18:44:04-05:00 -> 2020-01-05 18:34:41-05:00 ignoring pro bono TimeIn LogIn email TimeOut LogOut email TimeDelta Hours 0 2020-01-03 18:44:04-05:00 clock-in apembroke@gmail.com 2020-01-03 20:31:57-05:00 clock-out apembroke@gmail.com 01:47:53 1.798056 1 2020-01-03 20:45:54-05:00 clock-in apembroke@gmail.com 2020-01-03 22:40:56-05:00 clock-out apembroke@gmail.com 01:55:02 1.917222 2 2020-01-04 13:16:11-05:00 clock-in apembroke@gmail.com 2020-01-04 14:01:43-05:00 clock-out apembroke@gmail.com 00:45:32 0.758889 3 2020-01-04 14:55:18-05:00 clock-in apembroke@gmail.com 2020-01-04 16:35:04-05:00 clock-out apembroke@gmail.com 01:39:46 1.662778 4 2020-01-04 19:56:53-05:00 clock-in apembroke@gmail.com 2020-01-04 21:06:20-05:00 clock-out apembroke@gmail.com 01:09:27 1.157500 5 2020-01-04 23:59:21-05:00 clock-in apembroke@gmail.com 2020-01-05 03:59:59-05:00 clock-out apembroke@gmail.com 04:00:38 4.010556 6 2020-01-05 16:32:33-05:00 clock-in apembroke@gmail.com 2020-01-05 17:03:22-05:00 clock-out apembroke@gmail.com 00:30:49 0.513611 7 2020-01-05 17:29:01-05:00 clock-in apembroke@gmail.com 2020-01-05 18:34:41-05:00 clock-out apembroke@gmail.com 01:05:40 1.094444 0 days 12:54:47, 12.91 hours worked 1291.31 USD generating invoice for current user Asher Pembroke buyer: address1: null address2: null country: null email: null locality: null name: null notify: true phone: null postalCode: null region: null currency: USD extendedNotifications: true fullNotifications: true itemDesc: 12.91 hours worked from 2020-01-03T18:44:04-05:00 to 2020-01-05T18:34:41-05:00 notificationEmail: null notificationURL: null orderId: null price: 1291.3055555555554 redirectURL: null transactionSpeed: medium Is this correct? (yes/n)yes Success! Your invoice may be paid here: https://btc.exitpay.org/invoice?id=MoSbFujB7AwcrvfMN21gGC
Navigate to the payment url provided: