paypro: rework flow of payment protocol.

This commit is contained in:
Christopher Jeffrey 2014-07-27 22:56:57 -07:00 committed by Manuel Araoz
commit ebf3137946

View file

@ -733,10 +733,10 @@ Wallet.prototype.sign = function(ntxid, cb) {
Wallet.prototype.sendTx = function(ntxid, cb) { Wallet.prototype.sendTx = function(ntxid, cb) {
var txp = this.txProposals.get(ntxid); var txp = this.txProposals.get(ntxid);
if (txp.merchant) { // if (txp.merchant) {
return this.sendTxToMerchant(ntxid, // return this.createPaymentTx(ntxid,
{ uri: txp.merchant, txp: txp }, cb); // { uri: txp.merchant, txp: txp }, cb);
} // }
var tx = txp.builder.build(); var tx = txp.builder.build();
if (!tx.isComplete()) if (!tx.isComplete())
@ -773,25 +773,13 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
// because the tx outputs are given to us by the server in the payment details. // because the tx outputs are given to us by the server in the payment details.
// This is just preliminary code, not meant to be used. // This is just preliminary code, not meant to be used.
Wallet.prototype.sendTxToMerchant = function(ntxid, options, cb) { Wallet.prototype.createPaymentTx = function(ntxid, options, cb) {
var self = this; var self = this;
if (typeof options === 'string') { if (typeof options === 'string') {
options = { uri: options }; options = { uri: options };
} }
var txp = this.txProposals.txps[ntxid];
if (!txp) return;
var tx = txp.builder.build();
if (!tx.isComplete()) return;
this.log('Sending transaction to merchant');
tx = tx.serialize();
var txHex = tx.toString('hex');
this.log('Raw transaction: ', txHex);
return $http({ return $http({
method: options.method || 'POST', method: options.method || 'POST',
url: options.uri || options.url, url: options.uri || options.url,
@ -807,14 +795,14 @@ Wallet.prototype.sendTxToMerchant = function(ntxid, options, cb) {
data = PayPro.PaymentRequest.decode(data); data = PayPro.PaymentRequest.decode(data);
var pr = new PayPro(); var pr = new PayPro();
pr = pr.makePaymentRequest(data); pr = pr.makePaymentRequest(data);
return self._receivePaymentRequest(tx, options, pr, cb); return self.receivePaymentRequest(tx, options, pr, cb);
}) })
.error(function(data, status, headers, config) { .error(function(data, status, headers, config) {
return cb(new Error('Status: ' + JSON.stringify(status))); return cb(new Error('Status: ' + JSON.stringify(status)));
}); });
}; };
Wallet.prototype._receivePaymentRequest = function(tx, options, pr, cb) { Wallet.prototype.receivePaymentRequest = function(tx, options, pr, cb) {
var self = this; var self = this;
var ver = pr.get('payment_details_version'); var ver = pr.get('payment_details_version');
@ -856,24 +844,48 @@ Wallet.prototype._receivePaymentRequest = function(tx, options, pr, cb) {
var payment_url = pd.get('payment_url'); var payment_url = pd.get('payment_url');
var merchant_data = pd.get('merchant_data'); var merchant_data = pd.get('merchant_data');
return this._createPaymentTx(outputs, function(err, tx) { return this.getUnspent(function(err, unpsent) {
if (err) return cb(err); var ntxid = self.createPaymentTxSync(options, outputs, unspent);
if (ntxid) {
self.sendIndexes();
self.sendTxProposal(ntxid);
self.store();
self.emit('txProposalsUpdated');
}
self.log('You are currently on this BTC network:'); self.log('You are currently on this BTC network:');
self.log(network); self.log(network);
self.log('The server sent you a message:'); self.log('The server sent you a message:');
self.log(memo); self.log(memo);
return cb(ntxid);
});
};
Wallet.prototype.sendPaymentTx = function(ntxid, options, pay, cb) {
var self = this;
var txp = this.txProposals.txps[ntxid];
if (!txp) return;
var tx = txp.builder.build();
if (!tx.isComplete()) return;
this.log('Sending Transaction');
var refund_outputs = []; var refund_outputs = [];
options.refund_to = options.refund_to || self.getAddresses()[0]; options.refund_to = options.refund_to || self.getAddresses()[0];
if (options.refund_to) { if (options.refund_to) {
var total = 0;
for (var i = 0; i < tx.outs.length - 1; i++) {
total += tx.outs[i].v;
}
var rpo = new PayPro(); var rpo = new PayPro();
rpo = rpo.makeOutput(); rpo = rpo.makeOutput();
rpo.set('amount', 0); rpo.set('amount', total);
rpo.set('script', rpo.set('script',
Buffer.concat( Buffer.concat([
new Buffer([ new Buffer([
118, // OP_DUP 118, // OP_DUP
169, // OP_HASH160 169, // OP_HASH160
@ -885,7 +897,7 @@ Wallet.prototype._receivePaymentRequest = function(tx, options, pr, cb) {
136, // OP_EQUALVERIFY 136, // OP_EQUALVERIFY
172 // OP_CHECKSIG 172 // OP_CHECKSIG
]) ])
) ])
); );
refund_outputs.push(rpo.message); refund_outputs.push(rpo.message);
} }
@ -904,12 +916,6 @@ Wallet.prototype._receivePaymentRequest = function(tx, options, pr, cb) {
options.payment_url = options.payment_url || payment_url; options.payment_url = options.payment_url || payment_url;
return self._sendPayment(tx, options, pay, cb);
});
};
Wallet.prototype._sendPayment = function(tx, options, pay, cb) {
var self = this;
return $http({ return $http({
method: 'POST', method: 'POST',
url: options.payment_url, url: options.payment_url,
@ -929,14 +935,14 @@ Wallet.prototype._sendPayment = function(tx, options, pay, cb) {
data = PayPro.PaymentACK.decode(data); data = PayPro.PaymentACK.decode(data);
var ack = new PayPro(); var ack = new PayPro();
ack = ack.makePaymentACK(data); ack = ack.makePaymentACK(data);
return self._receivePaymentRequestACK(tx, options, ack, cb); return self.receivePaymentRequestACK(tx, options, ack, cb);
}) })
.error(function(data, status, headers, config) { .error(function(data, status, headers, config) {
return cb(new Error('Status: ' + JSON.stringify(status))); return cb(new Error('Status: ' + JSON.stringify(status)));
}); });
}; };
Wallet.prototype._receivePaymentRequestACK = function(tx, options, ack, cb) { Wallet.prototype.receivePaymentRequestACK = function(tx, options, ack, cb) {
var self = this; var self = this;
var payment = ack.get('payment'); var payment = ack.get('payment');
var memo = ack.get('memo'); var memo = ack.get('memo');
@ -956,10 +962,8 @@ Wallet.prototype._receivePaymentRequestACK = function(tx, options, ack, cb) {
return cb(txid, ca); return cb(txid, ca);
}; };
Wallet.prototype._createPaymentTx = function(outputs, cb) { Wallet.prototype.createPaymentTxSync = function(options, outputs, unspent) {
var self = this; var self = this;
return this.getUnspent(function(err, unspent) {
if (err) return cb(err);
var outs = []; var outs = [];
outputs.forEach(function(output) { outputs.forEach(function(output) {
@ -971,27 +975,26 @@ Wallet.prototype._createPaymentTx = function(outputs, cb) {
var opts = { var opts = {
remainderOut: { remainderOut: {
address: self._doGenerateAddress(true).toString() address: this._doGenerateAddress(true).toString()
} }
}; };
var tx = new Builder(opts) var b = new Builder(opts)
.setUnspent(unspent) .setUnspent(unspent)
.setOutputs(outs); .setOutputs(outs);
var priv = self.privateKey; var priv = this.privateKey;
var pkr = self.publicKeyRing; var pkr = this.publicKeyRing;
var selectedUtxos = tx.getSelectedUnspent(); var selectedUtxos = b.getSelectedUnspent();
var inputChainPaths = selectedUtxos.map(function(utxo) { var inputChainPaths = selectedUtxos.map(function(utxo) {
return pkr.pathForAddress(utxo.address); return pkr.pathForAddress(utxo.address);
}); });
if (priv) { if (priv) {
var keys = priv.getForPaths(inputChainPaths); var keys = priv.getForPaths(inputChainPaths);
var signed = tx.sign(keys); var signed = b.sign(keys);
} }
tx = tx.sign(keys); b = b.sign(keys);
tx = tx.build();
outputs.forEach(function(output, i) { outputs.forEach(function(output, i) {
var value = output.get('amount'); var value = output.get('amount');
@ -1006,17 +1009,38 @@ Wallet.prototype._createPaymentTx = function(outputs, cb) {
v[6] = (value.high >> 16) & 0xff; v[6] = (value.high >> 16) & 0xff;
v[7] = (value.high >> 24) & 0xff; v[7] = (value.high >> 24) & 0xff;
var s = script.buffer.slice(script.offset, script.limit); var s = script.buffer.slice(script.offset, script.limit);
tx.outs[i].v = v; b.tx.outs[i].v = v;
tx.outs[i].s = s; b.tx.outs[i].s = s;
}); });
self.log(''); this.log('');
self.log('Created transaction:'); this.log('Created transaction:');
self.log(tx.getStandardizedObject()); this.log(tx.getStandardizedObject());
self.log(''); this.log('');
return cb(null, tx); var myId = this.getMyCopayerId();
}); var now = Date.now();
var me = {};
var tx = b.build();
if (priv && tx.countInputSignatures(0)) me[myId] = now;
var meSeen = {};
if (priv) meSeen[myId] = now;
var data = {
inputChainPaths: inputChainPaths,
signedBy: me,
seenBy: meSeen,
creator: myId,
createdTs: now,
builder: b,
comment: options.memo
};
var ntxid = this.txProposals.add(data);
return ntxid;
}; };
Wallet.prototype.addSeenToTxProposals = function() { Wallet.prototype.addSeenToTxProposals = function() {