support for paypal through braintree

This commit is contained in:
Kyle Spearrin 2017-07-28 14:29:25 -04:00
parent 84554174ac
commit 96b8467859
15 changed files with 836 additions and 688 deletions

View File

@ -2,6 +2,7 @@
"appSettings": {
"apiUri": "https://preview-api.bitwarden.com",
"identityUri": "https://preview-identity.bitwarden.com",
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD"
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD",
"braintreeKey": "sandbox_r72q8jq6_9pnxkwm75f87sdc2"
}
}

View File

@ -2,6 +2,7 @@
"appSettings": {
"apiUri": "https://api.bitwarden.com",
"identityUri": "https://identity.bitwarden.com",
"stripeKey": "pk_live_bpN0P37nMxrMQkcaHXtAybJk"
"stripeKey": "pk_live_bpN0P37nMxrMQkcaHXtAybJk",
"braintreeKey": "TODO"
}
}

View File

@ -2,6 +2,7 @@
"appSettings": {
"apiUri": "http://localhost:4000",
"identityUri": "http://localhost:33656",
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD"
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD",
"braintreeKey": "sandbox_r72q8jq6_9pnxkwm75f87sdc2"
}
}

View File

@ -5,6 +5,9 @@
$analytics, toastr, existingPaymentMethod) {
$analytics.eventTrack('organizationBillingChangePaymentController', { category: 'Modal' });
$scope.existingPaymentMethod = existingPaymentMethod;
$scope.paymentMethod = 'card';
$scope.showPaymentOptions = false;
$scope.card = {};
$scope.submit = function () {
$scope.submitPromise = stripe.card.createToken($scope.card).then(function (response) {

View File

@ -137,9 +137,8 @@
trialEndDate: org.Subscription.TrialEndDate,
cancelledDate: org.Subscription.CancelledDate,
status: org.Subscription.Status,
cancelled: org.Subscription.Status === 'cancelled',
markedForCancel: (org.Subscription.Status === 'active' || org.Subscription.Status === 'trialing') &&
org.Subscription.CancelledDate
cancelled: org.Subscription.Cancelled,
markedForCancel: !org.Subscription.Cancelled && org.Subscription.CancelAtEndDate
};
}

View File

@ -141,7 +141,7 @@
</div>
<div ng-show="!loading && paymentSource">
<i class="fa" ng-class="{'fa-credit-card': paymentSource.type === 0,
'fa-university': paymentSource.type === 1}"></i>
'fa-university': paymentSource.type === 1, 'fa-paypal fa-fw text-blue': paymentSource.type === 2}"></i>
{{paymentSource.description}}
</div>
</div>

View File

@ -1,2 +1,2 @@
angular.module("bit")
.constant("appSettings", {"apiUri":"https://api.bitwarden.com","identityUri":"https://identity.bitwarden.com","stripeKey":"pk_live_bpN0P37nMxrMQkcaHXtAybJk","version":"1.14.0","environment":"Production"});
.constant("appSettings", {"apiUri":"https://api.bitwarden.com","identityUri":"https://identity.bitwarden.com","stripeKey":"pk_live_bpN0P37nMxrMQkcaHXtAybJk","braintreeKey":"TODO","version":"1.14.0","environment":"Production"});

View File

@ -2,14 +2,51 @@
.module('bit.organization')
.controller('settingsBillingChangePaymentController', function ($scope, $state, $uibModalInstance, apiService, stripe,
$analytics, toastr, existingPaymentMethod) {
$analytics, toastr, existingPaymentMethod, appSettings, $timeout) {
$analytics.eventTrack('settingsBillingChangePaymentController', { category: 'Modal' });
$scope.existingPaymentMethod = existingPaymentMethod;
$scope.paymentMethod = 'card';
$scope.dropinLoaded = false;
$scope.showPaymentOptions = true;
$scope.card = {};
var btInstance = null;
$scope.changePaymentMethod = function (val) {
$scope.paymentMethod = val;
if ($scope.paymentMethod !== 'paypal') {
return;
}
braintree.dropin.create({
authorization: appSettings.braintreeKey,
container: '#bt-dropin-container',
paymentOptionPriority: ['paypal'],
paypal: {
flow: 'vault',
buttonStyle: {
label: 'pay',
size: 'medium',
shape: 'pill',
color: 'blue'
}
}
}, function (createErr, instance) {
if (createErr) {
console.error(createErr);
return;
}
btInstance = instance;
$timeout(function () {
$scope.dropinLoaded = true;
});
});
};
$scope.submit = function () {
$scope.submitPromise = stripe.card.createToken($scope.card).then(function (response) {
$scope.submitPromise = getPaymentToken($scope.card).then(function (token) {
var request = {
paymentToken: response.id
paymentToken: token
};
return apiService.accounts.putPayment(null, request).$promise;
@ -31,4 +68,17 @@
$scope.close = function () {
$uibModalInstance.dismiss('cancel');
};
function getPaymentToken(card) {
if ($scope.paymentMethod === 'paypal') {
return btInstance.requestPaymentMethod().then(function (payload) {
return payload.nonce;
});
}
else {
return stripe.card.createToken(card).then(function (response) {
return response.id;
});
}
}
});

View File

@ -104,8 +104,8 @@
trialEndDate: billing.Subscription.TrialEndDate,
cancelledDate: billing.Subscription.CancelledDate,
status: billing.Subscription.Status,
cancelled: billing.Subscription.Status === 'cancelled',
markedForCancel: billing.Subscription.Status === 'active' && billing.Subscription.CancelledDate
cancelled: billing.Subscription.Cancelled,
markedForCancel: !billing.Subscription.Cancelled && billing.Subscription.CancelAtEndDate
};
}

View File

@ -2,28 +2,62 @@
.module('bit.settings')
.controller('settingsPremiumController', function ($scope, $state, apiService, toastr, $analytics, authService, stripe,
constants) {
constants, $timeout, appSettings) {
authService.getUserProfile().then(function (profile) {
if (profile.premium) {
return $state.go('backend.user.settingsBilling');
}
});
var btInstance = null;
$scope.storageGbPrice = constants.storageGb.yearlyPrice;
$scope.premiumPrice = constants.premium.price;
$scope.paymentMethod = 'card';
$scope.dropinLoaded = false;
$scope.model = {
additionalStorageGb: null
};
$scope.changePaymentMethod = function () {
if ($scope.paymentMethod !== 'paypal') {
return;
}
braintree.dropin.create({
authorization: appSettings.braintreeKey,
container: '#bt-dropin-container',
paymentOptionPriority: ['paypal'],
paypal: {
flow: 'vault',
buttonStyle: {
label: 'pay',
size: 'medium',
shape: 'pill',
color: 'blue'
}
}
}, function (createErr, instance) {
if (createErr) {
console.error(createErr);
return;
}
btInstance = instance;
$timeout(function () {
$scope.dropinLoaded = true;
});
});
};
$scope.totalPrice = function () {
return $scope.premiumPrice + (($scope.model.additionalStorageGb || 0) * $scope.storageGbPrice);
};
$scope.submit = function (model) {
$scope.submitPromise = stripe.card.createToken(model.card).then(function (response) {
$scope.submitPromise = getPaymentToken(model).then(function (token) {
var request = {
paymentToken: response.id,
paymentToken: token,
additionalStorageGb: model.additionalStorageGb
};
@ -39,4 +73,17 @@
toastr.success('Premium upgrade complete.', 'Success');
});
};
function getPaymentToken(model) {
if ($scope.paymentMethod === 'paypal') {
return btInstance.requestPaymentMethod().then(function (payload) {
return payload.nonce;
});
}
else {
return stripe.card.createToken(model.card).then(function (response) {
return response.id;
});
}
}
});

View File

@ -54,7 +54,7 @@
</div>
</div>
</div>
<div class="box-footer">
<div class="box-footer" ng-if="!subscription.cancelled || subscription.markedForCancel">
<button type="button" class="btn btn-default btn-flat" ng-click="cancel()"
ng-if="!subscription.cancelled && !subscription.markedForCancel">
Cancel
@ -82,7 +82,7 @@
</div>
</div>
</div>
<div class="box-footer">
<div class="box-footer" ng-if="!subscription.cancelled">
<button type="button" class="btn btn-default btn-flat" ng-click="adjustStorage(true)">
Add Storage
</button>
@ -104,7 +104,7 @@
</div>
<div ng-show="!loading && paymentSource">
<i class="fa" ng-class="{'fa-credit-card': paymentSource.type === 0,
'fa-university': paymentSource.type === 1}"></i>
'fa-university': paymentSource.type === 1, 'fa-paypal fa-fw text-blue': paymentSource.type === 2}"></i>
{{paymentSource.description}}
</div>
</div>

View File

@ -13,6 +13,21 @@
<li ng-repeat="e in form.$errors">{{e}}</li>
</ul>
</div>
<div ng-if="showPaymentOptions">
<label class="radio-inline">
<input type="radio" name="PaymentMethod" value="card" ng-model="paymentMethod"
ng-change="changePaymentMethod('card')"> Credit Card
</label>
<label class="radio-inline">
<input type="radio" name="PaymentMethod" value="paypal" ng-model="paymentMethod"
ng-change="changePaymentMethod('paypal')"> PayPal
</label>
<hr />
</div>
<div ng-if="paymentMethod === 'paypal'">
<div id="bt-dropin-container"></div>
</div>
<div ng-if="paymentMethod === 'card'">
<div class="row">
<div class="col-md-6">
<div class="form-group" show-errors>
@ -355,6 +370,7 @@
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Submit

View File

@ -89,6 +89,19 @@
<h3 class="box-title">Payment Information</h3>
</div>
<div class="box-body">
<label class="radio-inline">
<input type="radio" name="PaymentMethod" value="card" ng-model="paymentMethod"
ng-change="changePaymentMethod()"> Credit Card
</label>
<label class="radio-inline">
<input type="radio" name="PaymentMethod" value="paypal" ng-model="paymentMethod"
ng-change="changePaymentMethod()"> PayPal
</label>
<hr />
<div ng-if="paymentMethod === 'paypal'">
<div id="bt-dropin-container"></div>
</div>
<div ng-if="paymentMethod === 'card'">
<div class="row">
<div class="col-md-5">
<div class="form-group" show-errors>
@ -434,6 +447,7 @@
</div>
</div>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Submit

View File

@ -7,18 +7,25 @@
'self';
script-src
'self'
'sha256-ryoU+5+IUZTuUyTElqkrQGBJXr1brEv6r2CA62WUw8w='
https://www.google-analytics.com
https://js.stripe.com
https://js.braintreegateway.com
https://www.paypalobjects.com
https://maxcdn.bootstrapcdn.com
https://ajax.googleapis.com;
style-src
'self'
'unsafe-inline'
https://maxcdn.bootstrapcdn.com
https://assets.braintreegateway.com
https://*.paypal.com
https://fonts.googleapis.com;
img-src
'self'
data:
https://*.paypal.com
https://www.paypalobjects.com
https://q.stripe.com
https://haveibeenpwned.com
https://chart.googleapis.com
@ -30,10 +37,14 @@
child-src
'self'
https://js.stripe.com
https://assets.braintreegateway.com
https://*.paypal.com
https://*.duosecurity.com;
frame-src
'self'
https://js.stripe.com
https://assets.braintreegateway.com
https://*.paypal.com
https://*.duosecurity.com;
connect-src
*;">
@ -43,6 +54,7 @@
<title page-title>bitwarden.com Password Manager</title>
<script src="https://js.stripe.com/v2/"></script>
<script src="https://js.braintreegateway.com/web/dropin/1.4.0/js/dropin.min.js"></script>
<!-- @if true !>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

View File

@ -620,6 +620,10 @@ h1, h2, h3, h4, h5, h6 {
}
}
.braintree-placeholder {
display: none;
}
.clickable {
cursor: pointer;
}