Handle errors. Cancel gift card

This commit is contained in:
Gustavo Maximiliano Cortez 2016-05-19 17:29:24 -03:00
commit b0d24c6814
No known key found for this signature in database
GPG key ID: 15EDAD8D9F2EB1AF
6 changed files with 238 additions and 63 deletions

View file

@ -40,7 +40,7 @@
<div ng-init="amazon.init()">
<div class="p20t text-center">
<div class="p20t text-center" ng-click="amazon.init()">
<img src="img/amazon-gift-card.png" alt="Amazon Gift Card" width="200">
</div>
@ -60,8 +60,9 @@
{{item.cardInfo.value.amount | currency : item.cardInfo.value.currencyCode + ' ' : 2}}
</div>
<div class="large-5 medium-5 small-5 columns text-right">
<span class="text-warning" ng-if="item.status == 'ERROR'">Error</span>
<span class="text-light" ng-if="item.status != 'ERROR'">{{item.date * 1000 | amTimeAgo}}</span>
<span class="text-warning" ng-if="item.status == 'FAILURE'">Error</span>
<span class="text-secondary" ng-if="item.status == 'RESEND'">Resend is required</span>
<span class="text-light" ng-if="item.status == 'SUCCESS'">{{item.date * 1000 | amTimeAgo}}</span>
</div>
<div class="large-1 medium-1 small-1 columns text-right">
<i class="icon-arrow-right3 size-18"></i>

View file

@ -27,6 +27,17 @@
<span class="text-warning">
{{buy.error}}
</span>
<div class="m10t size-12" ng-show="buy.errorInfo">
There was an error when trying to buy gift card, but the funds were sent to BitPay Invoice. Please, contact
BitPay to refund your bitcoin
<div class="p10 m10t">
Amount: {{buy.errorInfo.amount}} {{buy.errorInfo.currencyCode}}<br>
BitPay Invoice ID: {{buy.errorInfo.bitpayInvoiceId}}.
</div>
<div class="text-center">
<a ng-click="$root.openExternalLink(buy.errorInfo.bitpayInvoiceUrl)">Open invoice</a>
</div>
</div>
</div>
<div class="text-center">
@ -73,7 +84,27 @@
</div>
<div class="m20t row" ng-show="buy.giftCard">
<div class="columns">
<div class="columns" ng-show="buy.giftCard.status != 'SUCCESS'">
<h1 class="text-center">Amazon.com Gift Card could not be created</h1>
<div class="box-notification m20b">
<span class="text-warning">
There was an error when trying to create the Amazon.com Gift Card. Status: {{buy.giftCard.status}}
</span>
</div>
<div class="text-gray size-12 m20t">
<span ng-show="buy.giftCard.status == 'RESEND'">
This is a temporary/recoverable system failure that can be
resolved retrying the request from your list of cards
</span>
<span ng-show="buy.giftCard.status == 'FAILURE'">
This failure could not be recoverable. Request your refund from your list of cards
</span>
<button class="m20t button outline round dark-gray expand" ng-click="$root.go('amazon')">
Back
</button>
</div>
</div>
<div class="columns" ng-show="buy.giftCard.status == 'SUCCESS'">
<h1 class="text-center">Gift card ready to redeem!</h1>
<div class="size-12 text-center">

View file

@ -17,28 +17,55 @@
ng-swipe-right="cancel()">
<div class="header-modal bg-gray text-center">
<img src="img/amazon-card.png" alt="Amazon" width="200">
<img src="img/amazon-card.png" alt="Amazon" width="200" ng-click="refreshGiftCard()">
<div class="size-24 text-bold m10t">
{{card.cardInfo.value.amount | currency : card.cardInfo.value.currencyCode + ' ' : 2}}
</div>
</div>
<div class="box-notification m20b" ng-show="error" ng-click="error = null">
<span class="text-warning">
{{error}}
</span>
</div>
<div class="text-center size-12" ng-show="card.status != 'SUCCESS' && !loading">
<div ng-show="card.status == 'RESEND'">
<div class="text-secondary m10h">
There was a temporary/recoverable system failure that can be resolved by retrying the request
</div>
<button class="m10t button outline round dark-gray tiny" ng-click="refreshGiftCard()">
Try again
</button>
</div>
<div class="text-warning m10" ng-show="card.status == 'FAILURE'">
There was a failure to the create gift card that could not be recoverable. Please, contact BitPay to refund your bitcoin
</div>
</div>
<ul class="no-bullet size-14">
<li class="line-b p10 oh pointer" ng-click="$root.openExternalLink()">
<li class="line-b p10 oh pointer" ng-show="card.gcClaimCode" ng-click="$root.openExternalLink()">
<i class="icon-arrow-right3 size-24 right text-bold"></i>
<span class="text-gray">Claim code</span>
<span class="text-bold right enable_text_select">{{card.gcClaimCode}}</span>
</li>
<li class="line-b p10 oh">
<span class="text-gray">Created at</span>
<span class="test-gray right enable_text_select">{{card.date * 1000 | amDateFormat:'MM/DD/YYYY HH:mm a'}}</span>
<span class="text-gray">Card status</span>
<span class="text-success right" ng-if="card.cardInfo.cardStatus == 'Fulfilled'">Fulfilled</span>
<span class="text-secondary right" ng-if="card.cardInfo.cardStatus == 'RefundedToPurchaser'">Refunded to purchaser</span>
<span class="text-warning right" ng-if="card.cardInfo.cardStatus == 'Expired'">Expired</span>
</li>
<li class="line-b p10 oh">
<span class="text-gray">Status</span>
<span class="text-success right" ng-if="card.status == 'SUCCESS'">Completed</span>
<span class="text-info right" ng-if="card.status == 'PENDING'">Pending</span>
<span class="text-warning right" ng-if="card.status == 'ERROR'">Error</span>
<span class="text-gray">Created at</span>
<span class="test-gray right">{{card.date * 1000 | amDateFormat:'MM/DD/YYYY HH:mm a'}}</span>
</li>
<li class="line-b p10 oh">
<span class="text-gray">Creation status</span>
<span class="text-success right" ng-if="card.status == 'SUCCESS'">Success</span>
<span class="text-secondary right" ng-if="card.status == 'RESEND'">Resend is required</span>
<span class="text-warning right" ng-if="card.status == 'FAILURE'">Error</span>
<span class="text-warning right" ng-if="card.status == 'REFUND'">Refunded</span>
</li>
<li class="line-b p10 oh pointer" ng-click="$root.openExternalLink(card.bitpayInvoiceUrl)">
<i class="icon-arrow-right3 size-24 right text-gray"></i>
@ -47,5 +74,33 @@
</li>
</ul>
<div class="row m20t" ng-show="card.status == 'FAILURE'">
<div class="columns text-center">
<p class="size-12 text-gray">
This action will remove the gift card
</p>
<button class="button outline round dark-gray tiny"
ng-click="remove();"
ng-disabled="loading">
<i class="fi-x"></i>
Remove
</button>
</div>
</div>
<div class="row m20t" ng-show="card.status == 'SUCCESS' && card.cardInfo.cardStatus == 'Fulfilled'">
<div class="columns text-center">
<p class="size-12 text-gray">
This action will cancel the gift card
</p>
<button class="button outline round dark-gray tiny"
ng-click="cancelGiftCard()"
ng-disabled="loading">
<i class="fi-x"></i>
Cancel
</button>
</div>
</div>
<div class="extra-margin-bottom"></div>
</div>

View file

@ -15,7 +15,6 @@ angular.module('copayApp.controllers').controller('amazonController',
return;
}
self.giftCards = gcds;
});
};
@ -26,8 +25,55 @@ angular.module('copayApp.controllers').controller('amazonController',
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.card = card;
$scope.cancelGiftCard = function() {
$scope.refresh = true;
var dataSrc = {
creationRequestId: $scope.card.creationRequestId,
gcId: $scope.card.gcId,
bitpayInvoiceId: $scope.card.bitpayInvoiceId,
bitpayInvoiceUrl: $scope.card.bitpayInvoiceUrl,
date: $scope.card.date
};
$scope.loading = true;
amazonService.cancelGiftCard(dataSrc, function(err, data) {
$scope.loading = null;
if (err || data.status != 'SUCCESS') {
$scope.error = err || data.status;
return;
}
$scope.refreshGiftCard();
});
};
$scope.remove = function() {
amazonService.saveGiftCard($scope.card, {remove: true}, function(err) {
$modalInstance.close(true);
});
};
$scope.refreshGiftCard = function() {
$scope.refresh = true;
var dataSrc = {
creationRequestId: $scope.card.creationRequestId,
amount: $scope.card.cardInfo.value.amount,
currencyCode: $scope.card.cardInfo.value.currencyCode,
bitpayInvoiceId: $scope.card.bitpayInvoiceId,
bitpayInvoiceUrl: $scope.card.bitpayInvoiceUrl,
date: $scope.card.date
};
$scope.loading = true;
amazonService.createGiftCard(dataSrc, function(err, data) {
$scope.loading = null;
if (err) {
$scope.error = err;
return;
}
$scope.card = data;
});
};
$scope.cancel = lodash.debounce(function() {
$modalInstance.dismiss('cancel');
$modalInstance.close($scope.refresh);
}, 0, 1000);
};
@ -39,7 +85,7 @@ angular.module('copayApp.controllers').controller('amazonController',
});
var disableCloseModal = $rootScope.$on('closeModal', function() {
modalInstance.dismiss('cancel');
modalInstance.close($scope.refresh);
});
modalInstance.result.finally(function() {
@ -48,6 +94,10 @@ angular.module('copayApp.controllers').controller('amazonController',
var m = angular.element(document.getElementsByClassName('reveal-modal'));
m.addClass(animationService.modalAnimated.slideOutRight);
});
modalInstance.result.then(function(refresh) {
if (refresh) self.init();
}, function() {});
};
});

View file

@ -45,11 +45,12 @@ angular.module('copayApp.controllers').controller('buyAmazonController',
});
} catch (e) {
$log.debug(e);
};
};
};
$scope.openWalletsModal = function(wallets) {
self.error = null;
self.errorInfo = null;
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.type = 'SELL';
$scope.wallets = wallets;
@ -104,6 +105,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController',
this.createTx = function() {
self.error = null;
self.errorInfo = null;
var currency_code = configService.getSync().amazon.testnet ? window.amazon_sandbox_currency_code : window.amazon_currency_code;
var dataSrc = {
@ -178,6 +180,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController',
self.loading = null;
if (err) {
self.error = err;
self.errorInfo = gift;
return;
}
self.giftCard = giftCard;

View file

@ -43,6 +43,52 @@ angular.module('copayApp.services').factory('amazonService', function($http, $lo
return kSigning;
}
var _getHeaders = function(data, method, endpoint, amz_target) {
var content_type = 'application/json';
var accept = 'application/json';
var amz_date = moment.utc().format('YYYYMMDD[T]HHmmss[Z]');
var date_stamp = moment.utc().format('YYYYMMDD');
var canonical_querystring = '';
/************* TASK 1: CREATE A CANONICAL REQUEST *************/
var canonical_headers =
'accept:' + accept + '\n' +
'content-type:' + content_type + '\n' +
'host:' + credentials.AMAZON_ENDPOINT.replace('https://', '') + '\n' +
'x-amz-date:' + amz_date + '\n' +
'x-amz-target:' + amz_target + '\n';
var signed_headers = 'accept;content-type;host;x-amz-date;x-amz-target';
data = JSON.stringify(data);
var payload_hash = CryptoJS.SHA256(data).toString(CryptoJS.enc.Hex);
var canonical_request = method + '\n' + endpoint + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
/************* TASK 2: CREATE THE STRING TO SIGN *************/
var algorithm = 'AWS4-HMAC-SHA256';
var credential_scope = date_stamp + '/' + credentials.AMAZON_REGION + '/' + credentials.AMAZON_SERVICE_NAME + '/' + 'aws4_request';
var hashed_canonical_request = CryptoJS.SHA256(canonical_request).toString(CryptoJS.enc.Hex);
var string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashed_canonical_request;
/************* TASK 3: CALCULATE THE SIGNATURE *************/
var signing_key = _getSignatureKey();
var signature = CryptoJS.HmacSHA256(string_to_sign, signing_key).toString(CryptoJS.enc.Hex)
var authorization_header = algorithm + ' ' + 'Credential=' + credentials.AMAZON_ACCESS_KEY + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature;
/************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************/
return {
'Content-Type': content_type,
'Accept': accept,
'X-Amz-Date': amz_date,
'X-Amz-Target': amz_target,
'Authorization': authorization_header
};
};
var _getBitPay = function(endpoint) {
return {
method: 'GET',
@ -125,7 +171,7 @@ angular.module('copayApp.services').factory('amazonService', function($http, $lo
root.createGiftCard = function(dataSrc, cb) {
var sandbox = credentials.AMAZON_SANDBOX ? 'T' : 'P'; // T: test - P: production
var now = moment().unix();
var requestId = credentials.AMAZON_PARTNER_ID + sandbox + now;
var requestId = dataSrc.creationRequestId || credentials.AMAZON_PARTNER_ID + sandbox + now;
var data = {
'creationRequestId': requestId,
@ -136,66 +182,55 @@ angular.module('copayApp.services').factory('amazonService', function($http, $lo
}
};
var content_type = 'application/json';
var accept = 'application/json';
var amz_date = moment.utc().format('YYYYMMDD[T]HHmmss[Z]');
var date_stamp = moment.utc().format('YYYYMMDD');
var method = 'POST';
var endpoint = '/CreateGiftCard';
var amz_target = 'com.amazonaws.agcod.AGCODService.CreateGiftCard';
var canonical_querystring = '';
/************* TASK 1: CREATE A CANONICAL REQUEST *************/
var canonical_headers =
'accept:' + accept + '\n' +
'content-type:' + content_type + '\n' +
'host:' + credentials.AMAZON_ENDPOINT.replace('https://', '') + '\n' +
'x-amz-date:' + amz_date + '\n' +
'x-amz-target:' + amz_target + '\n';
var signed_headers = 'accept;content-type;host;x-amz-date;x-amz-target';
data = JSON.stringify(data);
var payload_hash = CryptoJS.SHA256(data).toString(CryptoJS.enc.Hex);
var canonical_request = 'POST' + '\n' + '/CreateGiftCard' + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
/************* TASK 2: CREATE THE STRING TO SIGN *************/
var algorithm = 'AWS4-HMAC-SHA256';
var credential_scope = date_stamp + '/' + credentials.AMAZON_REGION + '/' + credentials.AMAZON_SERVICE_NAME + '/' + 'aws4_request';
var hashed_canonical_request = CryptoJS.SHA256(canonical_request).toString(CryptoJS.enc.Hex);
var string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashed_canonical_request;
/************* TASK 3: CALCULATE THE SIGNATURE *************/
var signing_key = _getSignatureKey();
var signature = CryptoJS.HmacSHA256(string_to_sign, signing_key).toString(CryptoJS.enc.Hex)
var authorization_header = algorithm + ' ' + 'Credential=' + credentials.AMAZON_ACCESS_KEY + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature;
/************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************/
var headers = {
'Content-Type': content_type,
'Accept': accept,
'X-Amz-Date': amz_date,
'X-Amz-Target': amz_target,
'Authorization': authorization_header
};
var headers = _getHeaders(data, method, endpoint, amz_target);
$http({
'method': 'POST',
'url': credentials.AMAZON_ENDPOINT + '/CreateGiftCard',
'data': data,
'method': method,
'url': credentials.AMAZON_ENDPOINT + endpoint,
'data': JSON.stringify(data),
'headers': headers
}).then(function(data) {
$log.info('Amazon Create Gift Card: SUCCESS');
$log.info('Amazon.com Gift Card Create/Update: SUCCESS');
var newData = data.data;
newData['bitpayInvoiceId'] = dataSrc.bitpayInvoiceId;
newData['bitpayInvoiceUrl'] = dataSrc.bitpayInvoiceUrl;
newData['date'] = now;
newData['date'] = dataSrc.date || now;
root.saveGiftCard(newData, null, function(err) {
return cb(null, newData);
});
}, function(data) {
$log.error('Amazon Create Gift Card: ERROR ' + data.statusText);
$log.error('Amazon.com Gift Card Create/Update: ERROR ' + data.statusText);
return cb(data.statusText);
});
};
root.cancelGiftCard = function(dataSrc, cb) {
var data = {
'creationRequestId': dataSrc.creationRequestId,
'partnerId': credentials.AMAZON_PARTNER_ID,
'gcId': dataSrc.gcId,
};
var method = 'POST';
var endpoint = '/CancelGiftCard';
var amz_target = 'com.amazonaws.agcod.AGCODService.CancelGiftCard';
var headers = _getHeaders(data, method, endpoint, amz_target);
$http({
'method': method,
'url': credentials.AMAZON_ENDPOINT + endpoint,
'data': JSON.stringify(data),
'headers': headers
}).then(function(data) {
$log.info('Amazon.com Gift Card Cancel: SUCCESS');
return cb(null, data.data);
}, function(data) {
$log.error('Amazon.com Gift Card Cancel: ERROR ' + data.statusText);
return cb(data.statusText);
});
};