paypro: through a lot of debugging. Payment sending is working.
This commit is contained in:
parent
d79dfb20c3
commit
d9c72392bd
3 changed files with 180 additions and 20 deletions
|
|
@ -111,8 +111,12 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
$rootScope.pendingPayment = null;
|
$rootScope.pendingPayment = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uri = address.indexOf('bitcoin:') === 0
|
var uri;
|
||||||
&& copay.HDPath.parseBitcoinURI(address);
|
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) {
|
if (uri && uri.merchant) {
|
||||||
w.createPaymentTx({
|
w.createPaymentTx({
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ angular.module('copayApp.directives')
|
||||||
var validator = function(value) {
|
var validator = function(value) {
|
||||||
var uri = copay.HDPath.parseBitcoinURI(value);
|
var uri = copay.HDPath.parseBitcoinURI(value);
|
||||||
|
|
||||||
|
window._rootScope = scope;
|
||||||
|
|
||||||
// Is this a payment protocol URI (BIP-72)?
|
// Is this a payment protocol URI (BIP-72)?
|
||||||
if (uri && uri.merchant) {
|
if (uri && uri.merchant) {
|
||||||
scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) {
|
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:
|
// XXX There needs to be a better way to do this:
|
||||||
total = +total / config.unitToSatoshi;
|
total = +total / config.unitToSatoshi;
|
||||||
|
|
||||||
|
var sendForm = angular.element(
|
||||||
|
document.getElementsByName('sendForm')[0]);
|
||||||
|
|
||||||
var address = angular.element(
|
var address = angular.element(
|
||||||
document.querySelector('input#address'));
|
document.querySelector('input#address'));
|
||||||
|
|
||||||
|
|
@ -75,7 +80,13 @@ angular.module('copayApp.directives')
|
||||||
document.querySelector('[title="Send all funds"]'));
|
document.querySelector('[title="Send all funds"]'));
|
||||||
sendall.attr('class', sendall.attr('class') + ' hidden');
|
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 val = address.val();
|
||||||
var uri = copay.HDPath.parseBitcoinURI(val || '');
|
var uri = copay.HDPath.parseBitcoinURI(val || '');
|
||||||
if (!uri || !uri.merchant) {
|
if (!uri || !uri.merchant) {
|
||||||
|
|
@ -85,6 +96,13 @@ angular.module('copayApp.directives')
|
||||||
if (amount.attr('disabled') === false) {
|
if (amount.attr('disabled') === false) {
|
||||||
submit.attr('disabled', true);
|
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'))) {
|
if (/ hidden$/.test(sendall.attr('class'))) {
|
||||||
sendall.attr('class',
|
sendall.attr('class',
|
||||||
sendall.attr('class').replace(' hidden', ''));
|
sendall.attr('class').replace(' hidden', ''));
|
||||||
|
|
@ -92,9 +110,22 @@ angular.module('copayApp.directives')
|
||||||
}
|
}
|
||||||
// TODO: Check paymentRequest expiration,
|
// TODO: Check paymentRequest expiration,
|
||||||
// delete if beyond expiration date.
|
// delete if beyond expiration date.
|
||||||
|
//};
|
||||||
|
//});
|
||||||
});
|
});
|
||||||
|
//scope.$apply(); // scope.$digest();
|
||||||
|
|
||||||
ctrl.$setValidity('validAddress', true);
|
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);
|
ctrl.$setValidity('validAddress', true);
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,9 @@ var TxProposals = require('./TxProposals');
|
||||||
var PrivateKey = require('./PrivateKey');
|
var PrivateKey = require('./PrivateKey');
|
||||||
var copayConfig = require('../../../config');
|
var copayConfig = require('../../../config');
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
var G = typeof window !== 'undefined'
|
||||||
var G = window;
|
? window
|
||||||
} else {
|
: global;
|
||||||
var G = global;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof angular !== 'undefined') {
|
|
||||||
G.$http = G.$http || angular.bootstrap().get('$http');
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wallet(opts) {
|
function Wallet(opts) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
@ -808,8 +802,9 @@ Wallet.prototype.createPaymentTx = function(options, cb) {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE
|
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE
|
||||||
+ ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
+ ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
||||||
'Content-Type': 'application/octet-stream',
|
'Content-Type': 'application/octet-stream'
|
||||||
'Content-Length': 0
|
// XHR does not allow these:
|
||||||
|
// 'Content-Length': 0
|
||||||
},
|
},
|
||||||
responseType: 'arraybuffer'
|
responseType: 'arraybuffer'
|
||||||
})
|
})
|
||||||
|
|
@ -978,13 +973,13 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
|
||||||
|
|
||||||
if (options.refund_to) {
|
if (options.refund_to) {
|
||||||
// pubkey needs to be ripesha'd
|
// 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) {
|
var total = txp.merchant.pr.pd.outputs.reduce(function(total, _, i) {
|
||||||
return total.add(bignum.fromBuffer(tx.outs[i].v, {
|
return total.add(bignum.fromBuffer(tx.outs[i].v, {
|
||||||
endian: 'little',
|
endian: 'little',
|
||||||
size: 1
|
size: 1
|
||||||
}));
|
}));
|
||||||
}, bugnum('0', 10));
|
}, bignum('0', 10));
|
||||||
var rpo = new PayPro();
|
var rpo = new PayPro();
|
||||||
rpo = rpo.makeOutput();
|
rpo = rpo.makeOutput();
|
||||||
// XXX Bad - the amount *has* to be a Number in protobufjs
|
// 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.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({
|
return $http({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: txp.merchant.pr.pd.payment_url,
|
url: txp.merchant.pr.pd.payment_url,
|
||||||
|
|
@ -1028,11 +1046,16 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
|
||||||
// BIP-71
|
// BIP-71
|
||||||
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE
|
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE
|
||||||
+ ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
+ ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
||||||
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE,
|
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE
|
||||||
'Content-Length': pay.length + '',
|
// XHR does not allow these:
|
||||||
'Content-Transfer-Encoding': 'binary'
|
// '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'
|
responseType: 'arraybuffer'
|
||||||
})
|
})
|
||||||
.success(function(data, status, headers, config) {
|
.success(function(data, status, headers, config) {
|
||||||
|
|
@ -1060,7 +1083,13 @@ Wallet.prototype.receivePaymentRequestACK = function(tx, txp, ack, cb) {
|
||||||
payment = pay.makePayment(payment);
|
payment = pay.makePayment(payment);
|
||||||
|
|
||||||
var tx = payment.message.transactions[0];
|
var tx = payment.message.transactions[0];
|
||||||
|
|
||||||
|
if (!tx) {
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
|
||||||
if (tx.buffer) {
|
if (tx.buffer) {
|
||||||
|
tx.buffer = new Buffer(new Uint8Array(tx.buffer));
|
||||||
tx.buffer = tx.buffer.slice(tx.offset, tx.limit);
|
tx.buffer = tx.buffer.slice(tx.offset, tx.limit);
|
||||||
var ptx = new bitcore.Transaction();
|
var ptx = new bitcore.Transaction();
|
||||||
ptx.parse(tx.buffer);
|
ptx.parse(tx.buffer);
|
||||||
|
|
@ -1551,4 +1580,100 @@ Wallet.prototype.verifySignedJson = function(senderId, payload, signature) {
|
||||||
return v;
|
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);
|
module.exports = require('soop')(Wallet);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue