Cash Pay Server

18 256
Avatar for jimtendo
3 years ago

I began working on a Cash Bounty platform last year, but discontinued active development of this due to the difficulty involved in implementing some form of Payment Handling that could track transactions. One approach I was advised of was to use a different address per transaction - but, given that I wanted the funds to land directly at the recipients address (without me having to act as a mediator at all), I opted for attaching data to OP_RETURN's (via the `?op_return=xxxx` query parameter - which only has very limited support). This was not an elegant solution.

Ultimately, this has stoked me into working on a platform (Cash Pay Server) to ease this whole process via the BIP70 protocol. This has been a bit of a shitshow as wallet support is not great - and, if you implement BIP70, you should also implement the JSONPaymentProtocol (as both use the same URL scheme - and wallets can only really use one or the other). This is what I've attempted to do with Cash Pay Server.

What is Cash Pay Server?

Cash Pay Server is intended to be an open-source, self-hostable BIP70/JSONPaymentProtocol gateway for Bitcoin Cash.

At present, there are two components to this:

  • Cash Pay Server (The Node)

  • Cash Pay Server JS (Javascript Library)

The idea is to make Cash Payment Integration on sites as simple as possible.

Its usage, currently, is something like the following (feedback/suggestions welcome):

<!-- In future, will be a CDN. NPM package will also be available. -->
<script src="https://github.com/developers-cash/cash-pay-server-js/releases/download/v0.1.1/cashpay.min.js"></script>

<!-- Container QR Code will load into -->
<div id="invoice-container" style="width:256px;"></div>

<script>
// Actually create an invoice
var invoice = new CashPay.Invoice();
invoice.addAddress('bitcoincash:qz8dt7dlwkc5n4x9u6gclfwte8lr7n58gyavxt0vmp', 100000)
       .setData('INVOICEID 12345')
       .setWebhook('requested', 'https://webhook.site/5e22c4ae-95c6-4286-9af9-8dbb795e1539')
       .setWebhook('broadcasted', 'https://webhook.site/5e22c4ae-95c6-4286-9af9-8dbb795e1539')
       .create(document.getElementById('invoice-container')); // If argument given, will handle QR rendering, etc.
</script>

The above will:

  • Create an invoice and attach an InvoiceID (setData) to that invoice.

  • Render a QR code on page.

  • Change the QR code to a "green tick" once payment is broadcasted (via BIP70/JSONPaymentProto).

  • Trigger a webhook when the invoice is requested and payment is broadcasted.

    This is important as most platforms would need to inform their backend systems once a payment has been made (i.e. to mark it as "paid" in their databases).

An example of this in action is available at the CodePen link here:

https://codepen.io/jimtendo/pen/LYpXKYe

Questions

Is this ready for Production usage?
No, not yet. I need to finalize the API's for this and implement some additional protections for the Webhooks (e.g. Signature Signing/Validation).

There's also additional features that are needed for this to be really useful (e.g. Confirmed Webhook Notifications, etc).

Will there be a free instance available for those who do not want to self-host?
Yes. The tentative address for this will be pay.infra.cash.

Can this send to multiple addresses at once?
Yes, this can be done by just calling the addAddress method.

invoice.addAddress('bitcoincash:qz8dt7dlwkc5n4x9u6gclfwte8lr7n58gyavxt0vmp', 100000)
       .addAddress('bitcoincash:qreq5h8qj3cjtg3qvu82gla0kl80gu8amc859yhcdy', 100000)

Will this support SLP?
I'm not very well educated on SLP, but my understanding is that it relies upon OP_RETURN outputs, so theoretically this should be achievable. There is an addOutput(...) function available that will accept a raw output script for cases like this. Eventually, depending on whether I can find a lightweight SLP library to compile these outputs, I may integrate this directly into the JS library.
I'm unsure which Wallets (due to poor implementation of BIP70) would support this. For example, I know older versions of the Bitcoin.com Wallet did not.

Will this support OP_RETURN?
Same answer as above currently.

How would I direct the JS Library to my own self-hosted instance?
The constructor accepts a configuration object:

var invoice = new CashPay.Invoice({ endpoint: 'https://your.instance.com' });

Is there an ETA on how long until this is usable?
I don't want to be over-optimistic, but am hoping I can have this ready within two months. This was a side-project done inbetween a full-time job so I didn't have a lot of time to invest in this. Now, I have a bit more time.
If someone wants to send me shekels, that'll incentivize me to get this done quicker.

Why BIP70?
BIP70 has a bad reputation. In my opinion, this is undeserved. It is not perfect, but most of the issues with it were to do with poor wallet implementation. Additionally, BitPay's JSONPaymentProtocol actually complicated things in that they both use the exact same URL schema. Therefore, anyone wishing to implement BIP70 also has to support JSONPaymentProtocol to ensure maximum support - as the wallets can only really support one or the other. Very disrespectful.
If anyone would like more details on how BIP70 vs JSONPaymentProto should be handled, happy to do a quick write-up on this. In general though, you want a "switchboard" type approach depending on the "accept" and "content-type" HTTP headers.

Can I render my own QR Code and not rely on the default container?
Yes, the container is more of a convenience, but the idea is to have event hooks in the JS library to allow this. In terms of the default container appearance, if any designers want to shoot something more attractive my way (or assist with that code itself, it'd be much welcomed). At the moment, that code is very messy and the default container isn't very attractive.

Other feedback or suggestions for features to accommodate other use-cases are also most welcome.

1
$ 2.26
$ 0.50 from @quest
$ 0.50 from @dagur
$ 0.50 from @im_uname
+ 6
Avatar for jimtendo
3 years ago

Comments

Great work!

$ 0.00
3 years ago

"This is a remarkable project in the BCH ecosystem as it will boost the mass adoption of BCH if well implemented" Kudos to you!

$ 0.00
3 years ago

Great project , keep up the good work, I hope it will get a good support form the BCH community.

$ 0.00
3 years ago

Looking forward to learning more about this, kudos!

$ 0.00
3 years ago

Well, good to know about it man ! :D This post is pretty interesting, hope to see something new from you soon ! God bless you !

$ 0.00
3 years ago

Semi technical post. Wasn't aware of this. Good for people with BCH websites for commerce.

$ 0.00
3 years ago

This looks great. Would the following use case work with BIP70? With the current API, I don't think it's possible, but in theory...

  • Create a invoice with static fiat amount
  • When user scans QR code, server side calculates conversions and presents the correct amount in BCH
  • The invoice can be reused.

This would for example allow for printing out a QR code on paper for an invoice and putting it on the office fridge. A primitive self-snack/beer-checkout solution.

$ 0.00
3 years ago

That's actually pretty clever. I explicitly disallow reuse, but maybe there could be a flag there. I think this could be implemented, but wouldn't have "confirmed" webhooks under current design (but may be able to get broadcast/requested). Would require modification of schema to do that. Let me sit on it though. I do like the idea.

$ 0.00
3 years ago

I'll test it out at the office if you manage to incorporate the idea ;-)

$ 0.00
3 years ago

I do really really like this idea. Great for high-trust environments (i.e. places where people aren't thieving douchebags) and could see this being used at People's Markets, etc.

Unfortunately, I cannot see a way to tie this into the current implementation without cluttering up the code (as it's not really how BIP70/JSONPaymentProto is intended to work). There are a few difficulties, but one main one is that when we receive the payment, we verify that the outputs match 'exactly' that of when the invoice is first created. The problem is that if we attempt to calculate the conversion on the Payment Request (as opposed to invoice creation), we can end up with wobbly conversions (due to volatility) - meaning the outputs would not always match up when it comes to the PaymentAck. To try to mitigate this would require some confusing conditionals (e.g. acceptable percentage margins on outputs at PaymentReq vs PaymentAck), which would complicate the code-base, I think. A simple solution might be to "reset" the amounts at PaymentReq, but if two users attempted to scan at the same time, one of them might fail (hence hindering reliability.

That said, once I've completed this project though, I'm considering either implementing this as a separate endpoint - or, alternatively, a separate service as I really do like this concept and think it'd double as great promotional material.

On the note of the converting USD, etc, I actually implemented this last night to make implementation easier. If a string is passed as amount during invoice creation (with currency code suffix), the server will perform the conversion on the fly.

var invoice = new CashPay.Invoice({ endpoint: 'https://pay.dev.infra.cash' });
invoice.addAddress('bitcoincash:qz8dt7dlwkc5n4x9u6gclfwte8lr7n58gyavxt0vmp', "1.50USD") // String + Currency suffix (uses Coinbase rates)
       .setData('INVOICEID 12345')
       .setWebhook('requested', 'https://webhook.site/5e22c4ae-95c6-4286-9af9-8dbb795e1539')
       .setWebhook('broadcasted', 'https://webhook.site/5e22c4ae-95c6-4286-9af9-8dbb795e1539')
       .create(document.getElementById('invoice-container')); // Argument given here will load into 
$ 0.00
3 years ago

Can you think of a use-case for limited re-use? For example, something like "quantity" of beers, limiting re-use to 100 times or something like that? Just trying to think what the best way to support this would be.

$ 0.00
3 years ago

Not really, maybe "timeout after a month" rather than quantity? For the self-snack use case, it's better to just open the fridge and look rather than relying on predetermined quantity.

$ 0.00
3 years ago

JSONPaymentProtocol (as both use the same URL scheme - and wallets can only really use one or the other)

would you mind explaining this? I really don't understand the difference.

as the wallets can only really support one or the other. Very disrespectful.

it appears as both of these were created by BitPay, so I'm confused as to how this is "disrespectful"

thanks!

$ 0.00
3 years ago

BIP70 proto was actually authored by Gavin Andresen and Mike Hearn. BitPay was just the first big service to force BIP70's usage (afaik). https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki

would you mind explaining this? I really don't understand the difference.

To disclaim, my understanding of the history here might not be perfect. But JSONPaymentProtocol was created due to difficulties devs had in implementing BIP70 (I'm assuming the two reasons were: 1) ProtoBuf and 2) x501 Signed Requests).

The URL scheme for a BIP70 request looks as follows: bitcoin:?r=https://merchant.com/pay.php?h%3D2a8628fc2fbe

When JSONPaymentProtocol was introduced, they used the exact same URL scheme format: bitcoin:?r=https://merchant.com/pay.php?h%3D2a8628fc2fbe

The problem with this is that there is no explicit way to tell a wallet to use BIP70 or JSONPaymentProtocol. The wallet chooses which will be used as they "contact us first" (there's no way, afaik, to indicate to use BIP70 versus JSONPaymentProto in the URL itself).

For example, if I had a service where I "only" supported BIP70 (and not JSONPaymentProto) and a user attempted to pay with a wallet that used JSONPaymentProto, the URL would be the same, but the interaction would obviously fail because my service only supports BIP70 - and wouldn't know how to read the JSONPaymentProto's format.

This means, to accommodate both cases, if we're supporting BIP70, we should also support JSONPaymentProto (otherwise, we'll run into issues like that above).

Fortunately, we can actually do this using the HTTP Headers. For example: BIP70 accept header: application/bitcoincash-paymentrequest JSON accept header: application/payment-request

The way I've handled this for this particular service is to use a "switchboard" of sorts whereby we generate the correct response for the wallet based on those headers. This irons out a few issues with bad wallet implementations also. For example, Bitcoin.com Wallet, in the past, would actually accept the PaymentRequest as BIP70 and then send the PaymentAck as JSONPaymentProto. So, that particular wallet implementation (since corrected) was a mix-n-match of BIP70 and JSONPaymentProto - but with this switch-board approach, we're able to handle it okay because we are supporting both now.

The source for this is here: https://github.com/developers-cash/cash-pay-server/blob/master/routes/pay/index.js ... under the GET and POST routes /pay/:invoiceId if you want a practical example.

$ 0.50
3 years ago

ahhh, I see, thanks for the detailed explanation..

But JSONPaymentProtocol was created due to difficulties devs had in implementing BIP70 (I'm assuming the two reasons were: 1) ProtoBuf and 2) x501 Signed Requests).

yes! this was a problem for me as well, actually the moment BitPay implemented it I hated it, which is why I'm happy they pushed for the JSON format, but I see how it's totally confusing from the wallet's perspective (don't know why they would do that).

For example: BIP70 accept header: application/bitcoincash-paymentrequest JSON accept header: application/payment-request

so is this formal, or this this part of your proposal? why not application/bip70-paymentrequest and application/json-paymentrequest? just wondering.. 🤔

i'll definitely check out the repo in the near future 👍

$ 0.00
3 years ago