Skip to content

Commit 69c580d

Browse files
committedAug 21, 2019
Add working bitcoinRpc example. Update packages, update readme to match v2 api, remove request.js dependency, update jsdoc comments
1 parent 93b70fc commit 69c580d

File tree

7 files changed

+706
-1214
lines changed

7 files changed

+706
-1214
lines changed
 

‎.jshintrc

-32
This file was deleted.

‎examples/bitcoinRpc.js

+370-254
Large diffs are not rendered by default.

‎examples/config.example.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
module.exports = {
22
network: 'test',
3-
currency: 'BTC',
43
rpcServer: {
5-
// should set this to match your own bitcoin rpc settings
6-
username: 'fakeUser',
7-
password: 'fakePassword',
8-
ipAddress: '127.0.0.1',
9-
port: '18332'
4+
BTC: {
5+
// should set this to match your own bitcoin-core rpc settings
6+
username: 'fakeUser',
7+
password: 'fakePassword',
8+
ipAddress: '127.0.0.1',
9+
port: '18332'
10+
},
11+
BCH: {
12+
// should set this to match your own bitcoin-abc rpc settings
13+
username: 'fakeUser',
14+
password: 'fakePassword',
15+
ipAddress: '127.0.0.1',
16+
port: '18332'
17+
}
1018
},
1119
trustedKeys: {
1220
// The idea is that you or the wallet provider will populate this with keys that are trusted, we have provided a few possible approaches
@@ -16,8 +24,6 @@ module.exports = {
1624
'mh65MN7drqmwpCRZcEeBEE9ceQCQ95HtZc': {
1725
// This is displayed to the user, somewhat like the organization field on an SSL certificate
1826
owner: 'BitPay (TESTNET ONLY - DO NOT TRUST FOR ACTUAL BITCOIN)',
19-
// Which bitcoin networks is this key valid for (regtest, test, main)
20-
networks: ['test'],
2127
// Which domains this key is valid for
2228
domains: ['test.bitpay.com'],
2329
// The actual public key which should be used to validate the signatures

‎index.js

+194-230
Large diffs are not rendered by default.

‎package-lock.json

+57-606
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
{
22
"name": "json-payment-protocol",
3-
"version": "0.2.0",
3+
"version": "2.0.0",
44
"description": "Simple interface for retrieving JSON payment requests and submitting payments",
55
"main": "index.js",
66
"dependencies": {
7-
"lodash": "^4.17.4",
8-
"request": "^2.83.0",
97
"secp256k1": "^3.5.0"
108
},
119
"devDependencies": {
12-
"async": "^2.6.0",
13-
"bs58": "^4.0.1",
14-
"kbpgp": "^2.0.77",
15-
"promptly": "^2.2.0",
16-
"request-promise": "^4.2.2"
10+
"async": "2.6.0",
11+
"bs58": "4.0.1",
12+
"eslint": "6.2.1",
13+
"kbpgp": "2.0.77",
14+
"promptly": "2.2.0",
15+
"request": "^2.83.0",
16+
"request-promise": "4.2.2"
1717
},
1818
"engines": {
1919
"node": ">=8.0.0"

‎readme.md

+63-76
Original file line numberDiff line numberDiff line change
@@ -6,102 +6,86 @@ This is the second version of the JSON payment protocol interface. If you have q
66

77

88
### Getting Started with v2
9-
This is actively being implemented. The plan is to:
10-
* Update the bitpay side to send the v2 structure
11-
* Change the json-payment-protocol client code to work with v1 and v2
12-
13-
### Getting Started with v1
149

1510
`npm install json-payment-protocol`
1611

17-
### v1 Usage
18-
19-
We support both callbacks and promises. For promises just add Async to the end of the function name. 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.
12+
### v2 Usage
2013

21-
#### Callbacks
14+
This library is now using async await structure for all functions. Be careful to follow the notes about when to broadcast your payment.
15+
Broadcasting a payment before getting a success notification back from the server in most cases will lead to a failed payment for the sender.
16+
The sender will bear the cost of paying transaction fees yet again to get their money back.
17+
18+
#### Example
2219
```js
2320
const JsonPaymentProtocol = require('json-payment-protocol');
24-
const paymentProtocol = new JsonPaymentProtocol();
25-
26-
let requestUrl = 'bitcoin:?r=https://test.bitpay.com/i/Jr629pwsXKdTCneLyZja4t';
2721

28-
paymentProtocol.getRawPaymentRequest(requestUrl, function (err, response) {
29-
if (err) {
30-
return console.log('Error retrieving payment request', err);
22+
// Options such as additional headers, etc which you want to pass to the node https client on every request
23+
const requestOptions = {};
24+
25+
const trustedKeys = {
26+
'mh65MN7drqmwpCRZcEeBEE9ceQCQ95HtZc': {
27+
// This is displayed to the user, somewhat like the organization field on an SSL certificate
28+
owner: 'BitPay (TESTNET ONLY - DO NOT TRUST FOR ACTUAL BITCOIN)',
29+
// Which domains this key is valid for
30+
domains: ['test.bitpay.com'],
31+
// The actual public key which should be used to validate the signatures
32+
publicKey: '03159069584176096f1c89763488b94dbc8d5e1fa7bf91f50b42f4befe4e45295a',
3133
}
32-
paymentProtocol.parsePaymentRequest(response.rawBody, response.headers, function (err, paymentRequest) {
33-
if (err) {
34-
return console.log('Error parsing payment request', err);
35-
}
36-
37-
console.log('Payment request retrieved');
38-
console.log(paymentRequest);
39-
40-
//TODO: Create the rawTransaction and sign it in your wallet instead of this, do NOT broadcast yet
41-
let currency = 'BTC';
42-
43-
// Funded unsigned raw transaction
44-
let unsignedRawTransaction = '02000000016b7bceefa3ff3bf6f3ad39a99cf6def9126a6edf8f49462bd06e4cb74366dab00100000000feffffff0248590095000000001976a9141b4f4e0c5354ce950ea702cc79be34885e7a60af88ac0c430100000000001976a914072053b485736e002f665d5fc65c443fb379256e88ac00000000'
45-
// Signed version of that transaction
46-
let signedRawTransaction = '02000000016b7bceefa3ff3bf6f3ad39a99cf6def9126a6edf8f49462bd06e4cb74366dab0010000006b4830450221008d8852576eb8e505832a53569dd756a1d0c304606c27e81d0ac1a83e78250969022058b2bde3f2e1ea7e6a62e69d99f7219e846f04c1c58ff163e2996669a935c31501210206e855c3cfd24a5e154cf94ff7a214d598dfc2d62966011fd83c360cf229777ffeffffff0248590095000000001976a9141b4f4e0c5354ce950ea702cc79be34885e7a60af88ac0c430100000000001976a914072053b485736e002f665d5fc65c443fb379256e88ac00000000';
47-
// total size of the signed transaction (note the way shown here is incorrect for segwit, see the code in /examples for getting vsize from RPC)
48-
let signedRawTransactionSize = Buffer.from(signedRawTransaction, 'hex').byteLength;
49-
50-
paymentProtocol.sendPaymentForVerification(currency, unsignedRawTransaction, signedRawTransactionSize, paymentRequest.paymentUrl, function(err, response) {
51-
if (err) {
52-
// If server rejects, stop, don't broadcast, show user the error
53-
return console.log('Error verifying payment with server', err);
54-
}
55-
56-
// Execute these in parallel
57-
// Sending payment to server via payment protocol
58-
paymentProtocol.broadcastPayment(currency, signedRawTransaction, paymentRequest.paymentUrl, function(err, response) {
59-
console.log('Ignore any errors here if you already received verified above');
60-
});
61-
// Sending payment to bitcoin p2p network
62-
myWallet.broadcastp2p(signedRawTransaction);
63-
});
64-
});
65-
});
66-
```
34+
};
35+
36+
const client = new JsonPaymentProtocol(requestOptions, trustedKeys);
6737

68-
#### Promises
69-
```js
70-
const JsonPaymentProtocol = require('json-payment-protocol');
71-
const paymentProtocol = new JsonPaymentProtocol();
7238

7339
let requestUrl = 'bitcoin:?r=https://test.bitpay.com/i/Jr629pwsXKdTCneLyZja4t';
74-
let response = await paymentProtocol.getRawPaymentRequestAsync(requestUrl);
75-
let paymentRequest = await paymentProtocol.parsePaymentRequestAsync(response.rawBody, response.headers);
7640

77-
console.log('Payment request retrieved');
78-
console.log(paymentRequest);
41+
const paymentOptions = await client.getPaymentOptions(requestUrl);
42+
43+
// The paymentOptions response will contain one or more currency / chain options. If you are a multi-currency wallet then you should
44+
// display the compatible payment options to the user. If only one option is supported it is
7945

80-
//TODO: Create the rawTransaction and sign it in your wallet instead of this example, do NOT broadcast yet
81-
// Funded unsigned raw transaction
82-
let unsignedRawTransaction = '02000000016b7bceefa3ff3bf6f3ad39a99cf6def9126a6edf8f49462bd06e4cb74366dab00100000000feffffff0248590095000000001976a9141b4f4e0c5354ce950ea702cc79be34885e7a60af88ac0c430100000000001976a914072053b485736e002f665d5fc65c443fb379256e88ac00000000'
83-
// Signed version of that transaction
84-
let signedRawTransaction = '02000000016b7bceefa3ff3bf6f3ad39a99cf6def9126a6edf8f49462bd06e4cb74366dab0010000006b4830450221008d8852576eb8e505832a53569dd756a1d0c304606c27e81d0ac1a83e78250969022058b2bde3f2e1ea7e6a62e69d99f7219e846f04c1c58ff163e2996669a935c31501210206e855c3cfd24a5e154cf94ff7a214d598dfc2d62966011fd83c360cf229777ffeffffff0248590095000000001976a9141b4f4e0c5354ce950ea702cc79be34885e7a60af88ac0c430100000000001976a914072053b485736e002f665d5fc65c443fb379256e88ac00000000';
85-
// total size of the signed transaction (note the way shown here is incorrect for segwit, see the code in /examples for getting vsize from RPC)
86-
let signedRawTransactionSize = Buffer.from(signedRawTransaction, 'hex').byteLength;
46+
const { responseData: paymentRequest } = await client.selectPaymentOption(paymentOptions.requestUrl, userChoice.chain, userChoice.currency);
8747

88-
// This sends the proposed unsigned transaction to the server
48+
// Parse response data instructions and create an appropriate unsigned and signed transaction
49+
// This is pseudocode
50+
let unsignedTransaction = await myWallet.createTransaction(responseData);
51+
let signedTransaction = await myWallet.signTransaction(unsignedTransaction);
52+
53+
// We send the unsigned transaction(s) first with their size to verify if this payment will be accepted
8954
try {
90-
await paymentProtocol.sendPaymentForVerificationAsync(currency, unsignedRawTransaction, signedRawTransactionSize, paymentRequest.paymentUrl);
55+
await client.verifyUnsignedPayment({
56+
paymentUrl: paymentOptions.requestUrl,
57+
chain: userChoice.chain,
58+
// For chains which can support multiple currencies via tokens, a currency code is required to identify which token is being used
59+
currency: userChoice.currency,
60+
unsignedTransactions: [{
61+
tx: unsignedTransaction.rawHex,
62+
// `vsize` for bitcoin core w/ segwit support, `size` for other clients
63+
weightedSize: signedTransaction.vsize || signedTransaction.size
64+
}]
65+
});
9166
} catch (e) {
92-
// Payment was rejected, do not continue, tell the user why they were rejected
93-
return console.log('Proposed payment rejected', e);
67+
// If an error occurs here, it is most likely an issue with the transaction (insufficient fee, rbf, unconfirmed inputs, etc)
68+
// It could also be a network error or the invoice may no longer be accepting payments (already paid or expired)
69+
return console.log('Error verifying payment with server', e);
9470
}
9571

96-
// This sends the fully signed transaction
72+
// If the payment is valid we send the signed payment
9773
try {
98-
await paymentProtocol.sendSignedPaymentAsync(currency, signedRawTransaction, paymentRequest.paymentUrl);
74+
await client.sendSignedPayment({
75+
paymentUrl: paymentOptions.requestUrl,
76+
chain: choice.chain,
77+
currency: choice.currency,
78+
signedTransactions: [{
79+
tx: signedTransaction,
80+
// `vsize` for bitcoin core w/ segwit support, `size` for other clients
81+
weightedSize: signedTransaction.vsize || signedTransaction.size
82+
}]
83+
});
84+
await broadcastP2P(signedTransaction);
85+
console.log('Payment successfully sent');
9986
} catch (e) {
100-
// ignore errors here
87+
console.log('Error sending payment', e);
10188
}
102-
103-
// Broadcast from your wallet to p2p
104-
myWallet.broadcastp2p(signedRawTransaction);
10589
```
10690

10791
### Options
@@ -110,7 +94,10 @@ Options passed to `new JsonPaymentProtocol()` are passed to request, so if you n
11094

11195
```js
11296
new JsonPaymentProtocol({
113-
proxy: 'socks://mySocksProxy.local'
97+
proxy: 'socks://mySocksProxy.local',
98+
headers: {
99+
'user-agent': 'myWallet'
100+
}
114101
})
115102
```
116103

0 commit comments

Comments
 (0)
Please sign in to comment.