fix paypro broadcast. add merchant info

This commit is contained in:
Matias Alejo Garcia 2014-11-20 01:06:30 -03:00
parent 33dd93ad6a
commit 3a0a1a2f23
10 changed files with 155 additions and 90 deletions

View File

@ -124,8 +124,7 @@ module.exports = function(grunt) {
'lib/socket.io-client/socket.io.js',
'lib/sjcl.js',
'lib/ios-imagefile-megapixel/megapix-image.js',
'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js',
'lib/zeroclipboard/ZeroClipboard.min.js'
'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js'
],
dest: 'lib/vendors.js'
},

View File

@ -20,7 +20,6 @@
"angular-moment": "~0.7.1",
"socket.io-client": ">=1.0.0",
"mousetrap": "1.4.6",
"zeroclipboard": "~1.3.5",
"ng-idle": "*",
"inherits": "~0.0.1",
"angular-load": "0.2.0",

View File

@ -5,6 +5,24 @@ var Address = bitcore.Address;
var bignum = bitcore.Bignum;
var preconditions = require('preconditions').singleton();
function selectText(element) {
var doc = document;
if (doc.body.createTextRange) { // ms
var range = doc.body.createTextRange();
range.moveToElementText(element);
range.select();
} else if (window.getSelection) {
var selection = window.getSelection();
var range = doc.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
angular.module('copayApp.directives')
.directive('validAddress', ['$rootScope',
@ -162,10 +180,14 @@ angular.module('copayApp.directives')
var contact = scope.wallet.addressBook[address];
if (contact && !contact.hidden) {
element.append(contact.label);
attrs['tooltip'] = attrs.address;
element.attr('tooltip',attrs.address);
} else {
element.append(address);
}
element.bind('click', function() {
selectText(elm[0]);
});
}
};
})
@ -296,39 +318,19 @@ angular.module('copayApp.directives')
};
})
.directive('clipCopy', function() {
ZeroClipboard.config({
moviePath: './lib/zeroclipboard/ZeroClipboard.swf',
trustedDomains: ['*'],
allowScriptAccess: 'always',
forceHandCursor: true
});
return {
restric: 'A',
scope: {
clipCopy: '=clipCopy'
},
link: function(scope, elm) {
var client = new ZeroClipboard(elm);
// TODO this does not work (FIXME)
elm.attr('tooltip','Press Ctrl+C to Copy');
elm.attr('tooltip-placement','top');
client.on('load', function(client) {
client.on('datarequested', function(client) {
client.setText(scope.clipCopy);
});
client.on('complete', function(client, args) {
elm.removeClass('btn-copy').addClass('btn-copied').html('Copied!');
setTimeout(function() {
elm.addClass('btn-copy').removeClass('btn-copied').html('');
}, 1000);
});
elm.bind('click', function() {
selectText(elm[0]);
});
client.on('wrongflash noflash', function() {
elm.removeClass('btn-copy').html('');
ZeroClipboard.destroy();
});
}
};
});

View File

@ -256,8 +256,8 @@ Insight.prototype.broadcast = function(rawtx, cb) {
this.requestPost('/api/tx/send', {
rawtx: rawtx
}, function(err, res, body) {
if (err || res.statusCode != 200) cb(err || body);
cb(null, body ? body.txid : null);
if (err || res.statusCode != 200) return cb(err || body);
return cb(null, body ? body.txid : null);
});
};

View File

@ -12,7 +12,7 @@ var buffertools = bitcore.buffertools;
var preconditions = require('preconditions').instance();
var VERSION = 1;
var CORE_FIELDS = ['builderObj', 'inputChainPaths', 'version', 'comment'];
var CORE_FIELDS = ['builderObj', 'inputChainPaths', 'version', 'comment', 'paymentProtocolURL'];
function TxProposal(opts) {
@ -37,6 +37,7 @@ function TxProposal(opts) {
this.comment = opts.comment || null;
this.readonly = opts.readonly || null;
this.merchant = opts.merchant || null;
this.paymentProtocolURL = opts.paymentProtocolURL || null;
this._sync();
}

View File

@ -426,14 +426,79 @@ Wallet.prototype._getKeyMap = function(txp) {
Wallet.prototype._checkSentTx = function(ntxid, cb) {
var txp = this.txProposals.get(ntxid);
var tx = txp.builder.build();
var txid = bitcore.util.formatHashFull(tx.getHash());
var txHex = tx.serialize().toString('hex');
//Use calcHash NOT getHash which could be cached.
var txid = bitcore.util.formatHashFull(tx.calcHash());
this.blockchain.getTransaction(txid, function(err, tx) {
if (err) return cb(false);
return cb(txid);
});
};
Wallet.prototype._processTxProposalSeen = function(ntxid) {
var txp = this.txProposals.get(ntxid);
if (!txp.getSeen(this.getMyCopayerId())) {
txp.setSeen(this.getMyCopayerId());
this.sendSeen(ntxid);
}
};
Wallet.prototype._processTxProposalSent = function(ntxid, cb) {
var self = this;
var txp = this.txProposals.get(ntxid);
this._checkSentTx(ntxid, function(txid) {
if (txid) {
if (!txp.getSent()) {
txp.setSent(txid);
}
}
self.emitAndKeepAlive('txProposalsUpdated');
if (cb) return cb(null, txid);
});
};
Wallet.prototype._processTxProposalPayPro = function(mergeInfo, cb) {
var self = this;
var txp = mergeInfo.txp;
var isNew = mergeInfo.new;
var ntxid = mergeInfo.ntxid;
if (!isNew || !txp.paymentProtocolURL)
return cb();
log.info('Received a Payment Protocol TX Proposal');
self.fetchPaymentTx(txp.paymentProtocolURL, function(err, merchantData) {
if (err) return cb(err);
txp.merchant = merchantData;
return cb();
});
};
Wallet.prototype._processIncomingTxProposal = function(mergeInfo, cb) {
if (!mergeInfo) return cb();
var self = this;
self._processTxProposalPayPro(mergeInfo, function(err) {
if (err) return cb(err);
self._processTxProposalSeen(mergeInfo.ntxid);
var tx = mergeInfo.txp.builder.build();
if (tx.isComplete())
self._processTxProposalSent(mergeInfo.ntxid);
else if (mergeInfo.hasChanged) {
self.sendTxProposal(mergeInfo.ntxid);
self.emitAndKeepAlive('txProposalsUpdated');
}
return cb();
});
};
/**
* @desc
* Handles a 'TXPROPOSAL' network message
@ -454,34 +519,20 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
m.newCopayer = m.txp.setCopayers(senderId, keyMap);
} catch (e) {
log.error('Corrupt TX proposal received from:', senderId, e.toString());
if (m && m.ntxid)
this.txProposals.deleteOne(m.ntxid);
m = null;
}
if (m) {
if (!m.txp.getSeen(this.getMyCopayerId())) {
m.txp.setSeen(this.getMyCopayerId());
this.sendSeen(m.ntxid);
self._processIncomingTxProposal(m, function(err) {
if (err) {
log.error('Corrupt TX proposal received from:', senderId, err.toString());
if (m && m.ntxid)
self.txProposals.deleteOne(m.ntxid);
m = null;
}
var tx = m.txp.builder.build();
if (tx.isComplete()) {
this._checkSentTx(m.ntxid, function(txid) {
if (txid) {
if (!m.txp.getSent()) {
m.txp.setSent(txid);
self.emitAndKeepAlive('txProposalsUpdated');
}
}
});
} else {
if (m.hasChanged) {
this.sendTxProposal(m.ntxid);
}
}
this.emitAndKeepAlive('txProposalsUpdated');
}
this._processProposalEvents(senderId, m);
self._processProposalEvents(senderId, m);
});
};
/**
@ -1420,16 +1471,18 @@ Wallet.prototype.sign = function(ntxid) {
Wallet.prototype.sendTx = function(ntxid, cb) {
var txp = this.txProposals.get(ntxid);
if (txp.merchant) {
return this.sendPaymentTx(ntxid, cb);
}
var tx = txp.builder.build();
if (!tx.isComplete())
throw new Error('Tx is not complete. Can not broadcast');
log.debug('Wallet:' + this.id + ' Broadcasting Transaction');
if (txp.merchant) {
return this.sendPaymentTx(ntxid, cb);
}
var scriptSig = tx.ins[0].getScript();
var size = scriptSig.serialize().length;
var txHex = tx.serialize().toString('hex');
log.debug('Wallet:' + this.id + ' Raw transaction: ', txHex);
@ -1446,10 +1499,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
return cb(txid);
} else {
log.info('Wallet:' + self.getName() + '. Sent failed. Checking if the TX was sent already');
self._checkSentTx(ntxid, function(txid) {
if (txid)
self.emitAndKeepAlive('txProposalsUpdated');
self._processTxProposalSent(ntxid, function(err, txid) {
return cb(txid);
});
}
@ -1640,6 +1690,7 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
if (!unspent || !unspent.length) {
return cb(new Error('No unspent outputs available.'));
}
try {
self.createPaymentTxSync(options, merchantData, safeUnspent);
} catch (e) {
@ -1765,7 +1816,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
view[i] = pay[i];
}
return Wallet.request({
var postInfo = {
method: 'POST',
url: txp.merchant.pr.pd.payment_url,
headers: {
@ -1780,7 +1831,9 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
// be the ArrayBuffer, now you send the View instead).
data: view,
responseType: 'arraybuffer'
})
};
return Wallet.request(postInfo)
.success(function(data, status, headers, config) {
data = PayPro.PaymentACK.decode(data);
var ack = new PayPro();
@ -1790,9 +1843,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
.error(function(data, status, headers, config) {
log.debug('Sending to server was not met with a returned tx.');
log.debug('XHR status: ' + status);
return self._checkSentTx(ntxid, function(txid) {
if (txid)
self.emitAndKeepAlive('txProposalsUpdated');
self._processTxProposalSent(ntxid, function(err, txid) {
return cb(txid, txp.merchant);
});
});
@ -1822,10 +1873,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
tx = payment.message.transactions[0];
if (!tx) {
log.debug('Sending to server was not met with a returned tx.');
return this._checkSentTx(ntxid, function(txid) {
return this._processTxProposalSeen(ntxid, function(err, txid) {
log.debug('[Wallet.js.1613:txid:%s]', txid);
if (txid)
self.emitAndKeepAlive('txProposalUpdated');
return cb(txid, txp.merchant);
});
}
@ -1841,7 +1890,7 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
log.debug('It is not returning a copy of the transaction.');
}
var txid = tx.getHash().toString('hex');
var txid = tx.calcHash().toString('hex');
var txHex = tx.serialize().toString('hex');
log.debug('Raw transaction: ', txHex);
@ -1855,11 +1904,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
self.emitAndKeepAlive('txProposalsUpdated');
return cb(txid, txp.merchant);
} else {
log.debug('Sent failed. Checking if the TX was sent already');
self._checkSentTx(ntxid, function(txid) {
if (txid)
self.emitAndKeepAlive('txProposalsUpdated');
log.debug('PayPro Sent failed. Checking if the TX was sent already');
self._processTxProposalSent(ntxid, function(err, txid) {
return cb(txid, txp.merchant);
});
}
@ -1951,6 +1997,10 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
});
var selectedUtxos = b.getSelectedUnspent();
if (selectedUtxos.length > TX_MAX_INS)
throw new Error('BIG: Resulting TX is too big:' + selectedUtxos.length + ' inputs. Aborting');
var inputChainPaths = selectedUtxos.map(function(utxo) {
return pkr.pathForAddress(utxo.address);
});
@ -1971,6 +2021,12 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
if (!tx.countInputSignatures(0))
throw new Error('Could not sign generated tx');
var txSize = tx.getSize();
if (txSize / 1024 > TX_MAX_SIZE_KB)
throw new Error('BIG: Resulting TX is too big ' + txSize + ' bytes. Aborting');
var me = {};
me[myId] = now;
var meSeen = {};
@ -1984,8 +2040,10 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
createdTs: now,
builder: b,
comment: options.memo,
merchant: merchantData
merchant: merchantData,
paymentProtocolURL: options.uri,
}));
return ntxid;
};
@ -2187,7 +2245,7 @@ Wallet.prototype.subscribeToAddresses = function() {
var addrInfo = this.publicKeyRing.getAddressesInfo();
this.blockchain.subscribe(_.pluck(addrInfo, 'addressStr'));
log.debug('Subscribed to ' + addrInfo.length + ' addresses'); //TODO
log.debug('Subscribed to ' + addrInfo.length + ' addresses');
};
/**
@ -2386,7 +2444,7 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb)
var ntxid;
try {
ntxid = self.createTxSync(toAddress, amountSatStr, comment, safeUnspent, opts);
log.debug('TX Created: ntxid', ntxid); //TODO
log.debug('TX Created: ntxid', ntxid);
} catch (e) {
return cb(e);
}

View File

@ -1533,6 +1533,8 @@ describe('Wallet model', function() {
},
};
w.txProposals.get = sinon.stub().returns(txp);
w.txProposals.merge = sinon.stub().returns({
ntxid: 1,
txp: txp,
@ -1574,6 +1576,7 @@ describe('Wallet model', function() {
},
};
w.txProposals.get = sinon.stub().returns(txp);
w.txProposals.merge = sinon.stub().returns({
ntxid: 1,
txp: txp,
@ -1616,6 +1619,7 @@ describe('Wallet model', function() {
},
};
w.txProposals.get = sinon.stub().returns(txp);
w.txProposals.merge = sinon.stub().returns({
ntxid: 1,
txp: txp,
@ -1649,6 +1653,7 @@ describe('Wallet model', function() {
},
};
w.txProposals.get = sinon.stub().returns(txp);
w.txProposals.merge = sinon.stub().returns({
ntxid: 1,
txp: txp,
@ -1682,6 +1687,7 @@ describe('Wallet model', function() {
},
};
w.txProposals.get = sinon.stub().returns(txp);
w.txProposals.merge = sinon.stub().returns({
ntxid: 1,
txp: txp,
@ -1712,6 +1718,7 @@ describe('Wallet model', function() {
},
};
w.txProposals.get = sinon.stub().returns(txp);
w.txProposals.merge = sinon.stub().returns({
ntxid: 1,
txp: txp,

View File

@ -46,7 +46,12 @@
You can import your current wallets after
<a class="text-white" href="#!/createProfile">creating your profile</a>
</div>
<div class="box-setup">
<h4 class="size-12" tooltip="HOLA">http://HOLA 1234 </h4>
<h4 class="size-12" clip-copy="HOLA">http://HOLA 1234 </h4>
<h1><span translate>Sign in to</span> <b>Copay</b></h1>
<form name="loginForm" ng-submit="openProfile(loginForm)" novalidate>
<p class="text-warning size-12"

View File

@ -40,15 +40,10 @@
<qrcode size="220" data="bitcoin:{{$root.addrInfos[0].addressStr}}"></qrcode>
<div class="m10t">
<h4 class="size-12">{{$root.addrInfos[0].addressStr}} <span class="btn-copy" clip-copy="$root.addrInfos[0].addressStr"></span></h4>
<h4 class="size-12" clip-copy>{{$root.addrInfos[0].addressStr}} </h4>
<span ng-if="$root.updatingBalance">
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
</span>
<div class="small-10 columns small-centered">
<button class="m15t button secondary hide-for-large-up" ng-show="isMobile" ng-click="mobileCopy($root.addrInfos[0].addressStr)">
<i class="fi-link">&nbsp;</i> <span translate>Copy to clipboard</span>
</button>
</div>
</div>
</div>
</div>

View File

@ -12,9 +12,8 @@
<div class="large-7 medium-9 columns">
<div class="list-addr">
<span>
<contact address="{{addr.address}}" tooltip-popup-delay="500" tooltip tooltip-placement="right" />
<contact address="{{addr.address}}" tooltip-popup-delay="500" tooltip tooltip-placement="right">
</span>
<span class="btn-copy" clip-copy="addr.address"> </span>
<small translate class="label" ng-if="addr.isChange">change</small>
</div>
</div>