Code Monkey home page Code Monkey logo

jsonpaymentprotocol's Introduction

JSON Payment Protocol Interface v2

This is the second version of the JSON payment protocol interface. If you have questions about the v2 specification itself, view the documentation.

If you have questions about the first version of the specification view the documentation.

Getting Started with v2

npm install json-payment-protocol

v2 Usage

This library is now using async await structure for all functions. Be careful to follow the notes about when to broadcast your payment. Broadcasting a payment before getting a success notification back from the server in most cases will lead to a failed payment for the sender. The sender will bear the cost of paying transaction fees yet again to get their money back.

Example

const JsonPaymentProtocol = require('json-payment-protocol');

// Options such as additional headers, etc which you want to pass to the node https client on every request
const requestOptions = {};

const trustedKeys = {
  'mh65MN7drqmwpCRZcEeBEE9ceQCQ95HtZc': {
    // This is displayed to the user, somewhat like the organization field on an SSL certificate
    owner: 'BitPay (TESTNET ONLY - DO NOT TRUST FOR ACTUAL BITCOIN)',
    // Which domains this key is valid for
    domains: ['test.bitpay.com'],
    // The actual public key which should be used to validate the signatures
    publicKey: '03159069584176096f1c89763488b94dbc8d5e1fa7bf91f50b42f4befe4e45295a',
  }
};

const client = new JsonPaymentProtocol(requestOptions, trustedKeys);


let requestUrl = 'bitcoin:?r=https://test.bitpay.com/i/Jr629pwsXKdTCneLyZja4t';

const paymentOptions = await client.getPaymentOptions(requestUrl);

// The paymentOptions response will contain one or more currency / chain options. If you are a multi-currency wallet then you should
// display the compatible payment options to the user. If only one option is supported it is 

const { responseData: paymentRequest } = await client.selectPaymentOption(paymentOptions.requestUrl, userChoice.chain, userChoice.currency);

// Parse response data instructions and create an appropriate unsigned and signed transaction
// This is pseudocode
let unsignedTransaction = await myWallet.createTransaction(responseData);
let signedTransaction = await myWallet.signTransaction(unsignedTransaction);

// We send the unsigned transaction(s) first with their size to verify if this payment will be accepted
try {
  await client.verifyUnsignedPayment({
    paymentUrl: paymentOptions.requestUrl,
    chain: userChoice.chain,
    // For chains which can support multiple currencies via tokens, a currency code is required to identify which token is being used
    currency: userChoice.currency,
    unsignedTransactions: [{
      tx: unsignedTransaction.rawHex,
      // `vsize` for bitcoin core w/ segwit support, `size` for other clients
      weightedSize: signedTransaction.vsize || signedTransaction.size
    }]
  });
} catch (e) {
  // If an error occurs here, it is most likely an issue with the transaction (insufficient fee, rbf, unconfirmed inputs, etc)
  // It could also be a network error or the invoice may no longer be accepting payments (already paid or expired)
  return console.log('Error verifying payment with server', e);
}

// If the payment is valid we send the signed payment
try {
  await client.sendSignedPayment({
    paymentUrl: paymentOptions.requestUrl,
    chain: choice.chain,
    currency: choice.currency,
    signedTransactions: [{
      tx: signedTransaction,
      // `vsize` for bitcoin core w/ segwit support, `size` for other clients
      weightedSize: signedTransaction.vsize || signedTransaction.size
    }]
  });
  await broadcastP2P(signedTransaction);
  console.log('Payment successfully sent');
} catch (e) {
  console.log('Error sending payment', e);
}

Options

Options passed to new JsonPaymentProtocol() are passed to request, so if you need to use a proxy or set any other request.js flags you can do so by including them when instantiating your instance. For example:

new JsonPaymentProtocol({
  proxy: 'socks://mySocksProxy.local',
  headers: {
    'user-agent': 'myWallet'
  }
})

URI Formats

You can provide either the bitcoin:?r=https://bitpay.com/i/invoice format or https://bitpay.com/i/invoice directly.

jsonpaymentprotocol's People

Contributors

bitjson avatar gandrewstone avatar matiu avatar micahriggan avatar nitsujlangston avatar unusualbob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonpaymentprotocol's Issues

Bitpay error message uses inconsistent units

While testing the json payment protocol, I purposely crafted a transaction 1000 satoshi less than the requested amount, to see the error message returned by bitpay. The error message returned was Unhandled rejection Error: The amount on the transaction (0.000252 BTC) does not match the amount requested (26200 BTC). This payment will not be accepted. The second value should be 0.000262 BTC.

Why has required fee rate for BCH changed to 1.001?

It doesn't make sense for the required minimum BCH fee to be 1001 Sats/kB when nodes accept 1000 Sats/kB by default. For most transactions, this change actually is not even a whole Satoshi, but implementations that use 1 Sat/byte will have their BitPay payments fail.

Is there some good reason to have implemented this change in JSON Payment Protocol?

Second clause of if statement cannot hit

if (typeof weightedSize !== 'number' || parseInt(weightedSize) !== weightedSize) {

If the first clause of this statement fails, then the second clause will be impossible with !==

If the goal is to make sure the parameter is a number, I think we should remove the second clause or change it to

parseInt(weightedSize.toString()) !== weightedSize

support unconfirmed chains

There is error messages for spending unconfirmed inputs, this breaks rapid payments and user experiences.

While acceptable in some cases, it would be better if you supported chaining unconfirmed transactions but place stronger requirements on them (higher fees, for example).

If supported, new error codes for failing these more stringent requirements would be needed.

Various issues

Note: I am a wallet developer on a wallet that support BIP70, but I'm commenting here in a personal capacity.

While I think the goal of simplifying/improving BIP70 is laudable, this proposal has some issues I think need addressing.

  1. Lack of standard process

Please follow the standard process if you wish to gain consensus for this proposal. That would in my view include announcing/discussing on bitcoin-dev and following the BIP request/review process.

  1. "requiredFeeRate - The minimum fee per byte required on this transaction. Payment will be rejected if fee rate included for the transaction is not at least this value. May be fractional value ie 0.123 sat/byte"

No. Users pay mining fees (or pay out of band/mine themselves), not you. Once you have given me an address to send to, and I have sent payment to it (and it confirms), if you want to 'reject' that payment then a) your incentives are broken and b) the merchant can expect trouble from the user since they have failed to provide a good/service which was just paid for. This is like you telling me I must pay my credit card provider a certain fee or you will ignore my payment - frankly its nuts, and its ripe for abuse.

If I assume benevolent intent then I must assume you believe you can calculate fees more accurately or cheaply than the user can. A less charitable interpretation of this is that you can make your own life easier by forcing exorbitant costs on the user, or continue to promote shitcoins by artificially inflating the fees on chains you wish to degrade (In a similar manner to your testnet site showing an $83 'network fee' for bitcoin and a $0.02 fee for bcash). Further, if you dictate the fees then this incentives collusion between you and miners to increase fees and split the difference with them.

No sane wallet is going to allow you to abuse/coerce their users in this manner.

  1. "In general, the payment should not be broadcast by the client. If at any time the payment is rejected by the server your client must not broadcast the payment."

This is just not going to happen. If I have created and sent you a signed tx that pays to the address you already showed me, and is valid by the networks consensus rules, then I don't care that you 'reject' it for any other reason. The network decides if my tx is valid, not you.

Your own current handling of RBF with BIP70 is a case in point - If I broadcast an opt-in RBF transaction as a payment to the network, your systems process it correctly, but you currently refuse to return a payment ack for the same tx when I send the tx to you to broadcast on my behalf. Is that technical incompetence, or a political power play? Regardless, you process the payment because the alternative is a support request or legal action against the merchant, neither of which are good for your business. That doesn't change regardless of what you say in this specification.

No user is going to (or should be asked to) trust you to not send a tx you received because you decide you don't like it, and no sane wallet is going to pre-emptively double spend an output to ensure you don't process the payment anyway.

Auth is a regression over current Bip70

As I understand the way the user is expected to authenticate is to manually search the internet for the GPG key, somehow download it from a trusted source (in practice this will almost always amount to downloading in the clear over the internet), and then manually import in the wallet.

This is a clear regression over Bip70. The point of the X509 certs, while cumbersome, was to remove the need to do the above and to allow people to validate the cert against a known certificate authority without searching for a known trusted source.

If you want to make authentication easier you could simply require SSL and require that the SSL cert used to serve the payment request be the identity the merchant desires to use.

This would make identity handling pretty easy. For example:

resp, _ := http.Get("https://bitpay.com")
org := resp.TLS.PeerCertificates[0].Subject.Organization[0]
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
fmt.Printf("Do you want to send a payment to %s(%s)?", org, cn)

But the current proposal to manually find verify GPG keys would be a non-starter for me. The result would be everyone just hardcodes bitpay's GPG key and the payment protocol is only usable to pay bitpay and nobody else.

Signature is invalid

When I retrieve and invoice like this https://bitpay.com/i/9NS2N7jueaGGAc8qHSJbz7

HTTP/1.1 200 OK
Date: Fri, 01 Mar 2019 11:32:42 GMT
Content-Type: application/payment-request; charset=utf-8
Content-Length: 405
Connection: close
Strict-Transport-Security: max-age=31536000
x-signature-type: ecc
x-identity: 1DbY94wCcLRM1Y6RGFg457JyqBbsYxzfiN
signature: 38ca09c742a325d9c38a87de621cd896a5fabb4d01942fc0126b58a8a366f7d634164a31910e2aef07e008c784af3725477610bb831a97c4620b862289c92c34
digest: SHA-256=222611bea82fb44ab45b9f598f0867adaec923c66d5f2b39eabc2d1b48f4f4ef

{"network":"main","currency":"BTC","requiredFeeRate":15.086,"outputs":[{"amount":502700,"address":"169s8UaMUtYwfPqnLsyJNbP7sZAfLYVaYQ"}],"time":"2019-03-01T11:32:22.871Z","expires":"2019-03-01T11:47:22.871Z","memo":"Payment request for BitPay invoice 9NS2N7jueaGGAc8qHSJbz7 for merchant Wikimedia Foundation","paymentUrl":"https://bitpay.com/i/9NS2N7jueaGGAc8qHSJbz7","paymentId":"9NS2N7jueaGGAc8qHSJbz7"}

and I try to validate signature with bitcore-lib, it says that signature is invalid

> bitcore.crypto.Signature.fromString("38ca09c742a325d9c38a87de621cd896a5fabb4d01942fc0126b58a8a366f7d634164a31910e2aef07e008c784af3725477610bb831a97c4620b862289c92c34")
Invalid Argument: Error: Header byte should be 0x30

Why is wrong signature ?

npm latest version is not updated

When I do: npm install json-payment-protocol I get an outdated version of the library. I had to clone this repo myself and put it manually inside the node_modules. It works locally jus fine, but the problem is that it causes problems in the runtime when deployed.
Can you please upgrade the npm registry?

medium agnostic

Reading the specification I notice that the payment uri is, well, a uri.

How is that defined and could it be made medium agnostic so that

Payment payload missing refund address

One of the best features of Bip70 was to allow the buyer to include a refund address with the payment message. This seems to be missing from this spec. Not sure if the removal was intentional or an oversight.

separate broadcast paymebt failures.

Error broadcasting payment to network lists multiple error conditions. The different errors have different user responses (wait if you are disconnected, use different inputs if you report doublespend etc).

Question: Why not separate Authentication-only spec?

It seems that specification.md can be separated into two parts

  1. Authentication with x-identity,x-signature-type and x-signature and safe pubkey distribution
  1. Spec for payment request/response.

But since the simple transaction style payment is not the only way wallets works (e.g. commitment to Assurance Contracts, LN), It seems to me that separating Authentication-specific part and payment-specific part makes more sense. Am I missing something here?

Suggestion for extension of JSON protocol.

I think we could bring the bitcoin/bitcoin cash checkout experience up to paypal-levels of ease of use with one simple extension to this protocol.

Allow the merchant to specify in the Payment Request other metadata they would like from the user.

For example:

{{name}}, {{email}}, {{shipping_address}} (possibly broken into street, street2, city, state, postal_code, country), {{phone}}, {{photo_url}}, maybe even {{twitter_username}}, {{facebook_username}} or {{youtube_username}}.

Wallets implementing this could ask for (and then save) this information from the user, and then the next time they check out have it already pre-filled (with the option to edit). It would just be like:

Bitpay is requesting .01 BCH for "EFF Donation", they also are requesting your:

email: [[email protected]]
phone: [+1-234-567-8900]

[Make Payment!]

(Kind of like logging in with facebook and it says "this app would like to see your email and list of friends.")

If this extension is added, we've basically made it possible for shopping with crypto online AND in retail stores as easy as Amazon's one-click checkout.

`requiredFeeRate` is ambiguous

There are two ways to calculate transaction sizes on the Bitcoin (BTC) chain. The literal way simply takes the total transaction size, but the weighted way discounts bytes in the segwit area by 4x. Both metrics are in units of "bytes", so it is not clear which calculation approach this specification intends.

The Bitcoin Core software gives suggested fees in weighted bytes (see the estimatesmartfee method). Full nodes and miners also use weighted sizes in their block-size calculations. Therefore, it would make the most sense for this specification to clarify that the requiredFeeRate is in weighted bytes.

Bitcoin Cash doesn't have this distinction, of course. This is only an issue for segwit-enabled coins.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.