paypro: through a lot of debugging. Payment sending is working.

This commit is contained in:
Christopher Jeffrey 2014-08-01 19:45:24 -07:00 committed by Manuel Araoz
parent d79dfb20c3
commit d9c72392bd
3 changed files with 180 additions and 20 deletions

View File

@ -111,8 +111,12 @@ angular.module('copayApp.controllers').controller('SendController',
$rootScope.pendingPayment = null;
}
var uri = address.indexOf('bitcoin:') === 0
&& copay.HDPath.parseBitcoinURI(address);
var uri;
if (address.indexOf('bitcoin:') === 0) {
uri = copay.HDPath.parseBitcoinURI(address);
} else if (address.indexOf('Merchant: ') === 0) {
uri = { merchant: address.split(' ')[1] };
}
if (uri && uri.merchant) {
w.createPaymentTx({

View File

@ -14,6 +14,8 @@ angular.module('copayApp.directives')
var validator = function(value) {
var uri = copay.HDPath.parseBitcoinURI(value);
window._rootScope = scope;
// Is this a payment protocol URI (BIP-72)?
if (uri && uri.merchant) {
scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) {
@ -44,6 +46,9 @@ angular.module('copayApp.directives')
// XXX There needs to be a better way to do this:
total = +total / config.unitToSatoshi;
var sendForm = angular.element(
document.getElementsByName('sendForm')[0]);
var address = angular.element(
document.querySelector('input#address'));
@ -75,7 +80,13 @@ angular.module('copayApp.directives')
document.querySelector('[title="Send all funds"]'));
sendall.attr('class', sendall.attr('class') + ' hidden');
address.on('change', function(ev) {
// Reset all the changes from the payment protocol weirdness.
// XXX use ng-change attr instead
//address.attr('ng-change', 'ppChange()');
//scope.ppChange = scope.ppChange || function() {
//address.on('change', function(ev) {
scope.$on('change', function(ev) {
//scope.$watch('address', function(newValue, oldValue) {
var val = address.val();
var uri = copay.HDPath.parseBitcoinURI(val || '');
if (!uri || !uri.merchant) {
@ -85,6 +96,13 @@ angular.module('copayApp.directives')
if (amount.attr('disabled') === false) {
submit.attr('disabled', true);
}
sendto.html(sendto.html().replace(/<br><b>Server:.*$/, ''));
if (!/hidden/.test(tamount.attr('class'))) {
tamount.attr(tamount.attr('class') + ' hidden');
}
if (submit.attr('disabled') === false) {
submit.attr('disabled', true);
}
if (/ hidden$/.test(sendall.attr('class'))) {
sendall.attr('class',
sendall.attr('class').replace(' hidden', ''));
@ -92,9 +110,22 @@ angular.module('copayApp.directives')
}
// TODO: Check paymentRequest expiration,
// delete if beyond expiration date.
//};
//});
});
//scope.$apply(); // scope.$digest();
ctrl.$setValidity('validAddress', true);
scope.sendForm.$valid = true;
scope.sendForm.$invalid = false;
scope.sendForm.$pristine = true;
scope.sendForm.address.$valid = true;
scope.sendForm.address.$invalid = false;
scope.sendForm.address.$pristine = true;
scope.sendForm.amount.$valid = true;
scope.sendForm.amount.$invalid = false;
scope.sendForm.amount.$pristine = true;
});
ctrl.$setValidity('validAddress', true);

View File

@ -25,15 +25,9 @@ var TxProposals = require('./TxProposals');
var PrivateKey = require('./PrivateKey');
var copayConfig = require('../../../config');
if (typeof window !== 'undefined') {
var G = window;
} else {
var G = global;
}
if (typeof angular !== 'undefined') {
G.$http = G.$http || angular.bootstrap().get('$http');
}
var G = typeof window !== 'undefined'
? window
: global;
function Wallet(opts) {
var self = this;
@ -808,8 +802,9 @@ Wallet.prototype.createPaymentTx = function(options, cb) {
headers: {
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE
+ ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
'Content-Type': 'application/octet-stream',
'Content-Length': 0
'Content-Type': 'application/octet-stream'
// XHR does not allow these:
// 'Content-Length': 0
},
responseType: 'arraybuffer'
})
@ -978,13 +973,13 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
if (options.refund_to) {
// pubkey needs to be ripesha'd
options.refund_to = bitcore.sha256ripe160(options.refund_to);
options.refund_to = bitcore.util.sha256ripe160(options.refund_to);
var total = txp.merchant.pr.pd.outputs.reduce(function(total, _, i) {
return total.add(bignum.fromBuffer(tx.outs[i].v, {
endian: 'little',
size: 1
}));
}, bugnum('0', 10));
}, bignum('0', 10));
var rpo = new PayPro();
rpo = rpo.makeOutput();
// XXX Bad - the amount *has* to be a Number in protobufjs
@ -1021,6 +1016,29 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
pay.set('memo', options.memo);
pay = pay.serialize();
this.log(pay);
this.log(pay.toString('hex'));
// https://www.google.com/search?q=angular+%24http+ArrayBuffer+in+body
// https://github.com/feross/buffer/blob/master/index.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays
// var view = new Uint8Array(new ArrayBuffer(pay.length));
// Buffer._augment(view);
// pay = pay.copy(view);
// var view = new Uint8Array(new ArrayBuffer(pay.length));
// view.set(Array.prototype.slice.call(pay), 0);
// pay = view;
var buf = new ArrayBuffer(pay.length);
var view = new Uint8Array(buf);
for (var i = 0; i < pay.length; i++) {
view[i] = pay[i];
}
return $http({
method: 'POST',
url: txp.merchant.pr.pd.payment_url,
@ -1028,11 +1046,16 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
// BIP-71
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE
+ ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE,
'Content-Length': pay.length + '',
'Content-Transfer-Encoding': 'binary'
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE
// XHR does not allow these:
// 'Content-Length': (pay.byteLength || pay.length) + '',
// 'Content-Transfer-Encoding': 'binary'
},
body: pay.serialize(),
// data: pay,
// data: pay,
// data: view,
data: buf, // Technically how this should be done.
// requestType: 'arraybuffer',
responseType: 'arraybuffer'
})
.success(function(data, status, headers, config) {
@ -1060,7 +1083,13 @@ Wallet.prototype.receivePaymentRequestACK = function(tx, txp, ack, cb) {
payment = pay.makePayment(payment);
var tx = payment.message.transactions[0];
if (!tx) {
return cb();
}
if (tx.buffer) {
tx.buffer = new Buffer(new Uint8Array(tx.buffer));
tx.buffer = tx.buffer.slice(tx.offset, tx.limit);
var ptx = new bitcore.Transaction();
ptx.parse(tx.buffer);
@ -1551,4 +1580,100 @@ Wallet.prototype.verifySignedJson = function(senderId, payload, signature) {
return v;
}
// NOTE: Angular $http module does not send ArrayBuffers correctly, so we're
// not going to use it. We'll have to write our own. Otherwise, we could
// hex-encoded our messages and decode them on the other side, but that
// deviates from BIP-70 slightly.
// if (typeof angular !== 'undefined') {
// G.$http = G.$http || angular.bootstrap().get('$http');
// }
G.$http = G.$http || function $http(options, callback) {
if (typeof options === 'string') {
options = { uri: options };
}
options.method = options.method || 'GET';
options.headers = options.headers || {};
var ret = {
success: function(cb) {
this._success = cb;
return this;
},
error: function(cb) {
this._error = cb;
return this;
},
_success: function() {
;
},
_error: function(_, err) {
throw err;
}
};
var method = (options.method || 'GET').toUpperCase();
var uri = options.uri || options.url;
var req = options;
req.headers = req.headers || {};
req.body = req.body || {};
if (typeof XMLHttpRequest !== 'undefined') {
var xhr = new XMLHttpRequest();
xhr.open(method, uri, true);
Object.keys(options.headers).forEach(function(key) {
var val = options.headers[key];
if (key === 'Content-Length') return;
if (key === 'Content-Transfer-Encoding') return;
xhr.setRequestHeader(key, val);
});
// For older browsers (binary data):
// xhr.overrideMimeType('text/plain; charset=x-user-defined');
// Newer browsers (binary data):
// xhr.responseType = 'arraybuffer';
if (options.responseType) {
xhr.responseType = options.responseType;
}
// xhr.onreadystatechange = function() {
// if (xhr.readyState == 4) {
// ;
// }
// };
xhr.onload = function(event) {
var response = xhr.response;
var buf = new Uint8Array(response);
var headers = {};
(xhr.getAllResponseHeaders() || '').replace(
/(?:\r?\n|^)([^:\r\n]+): *([^\r\n]+)/g,
function($0, $1, $2) {
headers[$1.toLowerCase()] = $2;
}
);
return ret._success(buf, xhr.status, headers, options);
};
xhr.onerror = function(event) {
return ret._error(null, new Error(event.message), null, options);
};
if (options.data || options.body) {
xhr.send(options.data || options.body);
} else {
xhr.send(null);
}
return ret;
}
return ret;
};
module.exports = require('soop')(Wallet);