support for paypal through braintree
This commit is contained in:
parent
84554174ac
commit
96b8467859
|
@ -2,6 +2,7 @@
|
||||||
"appSettings": {
|
"appSettings": {
|
||||||
"apiUri": "https://preview-api.bitwarden.com",
|
"apiUri": "https://preview-api.bitwarden.com",
|
||||||
"identityUri": "https://preview-identity.bitwarden.com",
|
"identityUri": "https://preview-identity.bitwarden.com",
|
||||||
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD"
|
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD",
|
||||||
|
"braintreeKey": "sandbox_r72q8jq6_9pnxkwm75f87sdc2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"appSettings": {
|
"appSettings": {
|
||||||
"apiUri": "https://api.bitwarden.com",
|
"apiUri": "https://api.bitwarden.com",
|
||||||
"identityUri": "https://identity.bitwarden.com",
|
"identityUri": "https://identity.bitwarden.com",
|
||||||
"stripeKey": "pk_live_bpN0P37nMxrMQkcaHXtAybJk"
|
"stripeKey": "pk_live_bpN0P37nMxrMQkcaHXtAybJk",
|
||||||
|
"braintreeKey": "TODO"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"appSettings": {
|
"appSettings": {
|
||||||
"apiUri": "http://localhost:4000",
|
"apiUri": "http://localhost:4000",
|
||||||
"identityUri": "http://localhost:33656",
|
"identityUri": "http://localhost:33656",
|
||||||
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD"
|
"stripeKey": "pk_test_KPoCfZXu7mznb9uSCPZ2JpTD",
|
||||||
|
"braintreeKey": "sandbox_r72q8jq6_9pnxkwm75f87sdc2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
$analytics, toastr, existingPaymentMethod) {
|
$analytics, toastr, existingPaymentMethod) {
|
||||||
$analytics.eventTrack('organizationBillingChangePaymentController', { category: 'Modal' });
|
$analytics.eventTrack('organizationBillingChangePaymentController', { category: 'Modal' });
|
||||||
$scope.existingPaymentMethod = existingPaymentMethod;
|
$scope.existingPaymentMethod = existingPaymentMethod;
|
||||||
|
$scope.paymentMethod = 'card';
|
||||||
|
$scope.showPaymentOptions = false;
|
||||||
|
$scope.card = {};
|
||||||
|
|
||||||
$scope.submit = function () {
|
$scope.submit = function () {
|
||||||
$scope.submitPromise = stripe.card.createToken($scope.card).then(function (response) {
|
$scope.submitPromise = stripe.card.createToken($scope.card).then(function (response) {
|
||||||
|
|
|
@ -137,9 +137,8 @@
|
||||||
trialEndDate: org.Subscription.TrialEndDate,
|
trialEndDate: org.Subscription.TrialEndDate,
|
||||||
cancelledDate: org.Subscription.CancelledDate,
|
cancelledDate: org.Subscription.CancelledDate,
|
||||||
status: org.Subscription.Status,
|
status: org.Subscription.Status,
|
||||||
cancelled: org.Subscription.Status === 'cancelled',
|
cancelled: org.Subscription.Cancelled,
|
||||||
markedForCancel: (org.Subscription.Status === 'active' || org.Subscription.Status === 'trialing') &&
|
markedForCancel: !org.Subscription.Cancelled && org.Subscription.CancelAtEndDate
|
||||||
org.Subscription.CancelledDate
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!loading && paymentSource">
|
<div ng-show="!loading && paymentSource">
|
||||||
<i class="fa" ng-class="{'fa-credit-card': paymentSource.type === 0,
|
<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}}
|
{{paymentSource.description}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
angular.module("bit")
|
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"});
|
||||||
|
|
|
@ -2,14 +2,51 @@
|
||||||
.module('bit.organization')
|
.module('bit.organization')
|
||||||
|
|
||||||
.controller('settingsBillingChangePaymentController', function ($scope, $state, $uibModalInstance, apiService, stripe,
|
.controller('settingsBillingChangePaymentController', function ($scope, $state, $uibModalInstance, apiService, stripe,
|
||||||
$analytics, toastr, existingPaymentMethod) {
|
$analytics, toastr, existingPaymentMethod, appSettings, $timeout) {
|
||||||
$analytics.eventTrack('settingsBillingChangePaymentController', { category: 'Modal' });
|
$analytics.eventTrack('settingsBillingChangePaymentController', { category: 'Modal' });
|
||||||
$scope.existingPaymentMethod = existingPaymentMethod;
|
$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.submit = function () {
|
||||||
$scope.submitPromise = stripe.card.createToken($scope.card).then(function (response) {
|
$scope.submitPromise = getPaymentToken($scope.card).then(function (token) {
|
||||||
var request = {
|
var request = {
|
||||||
paymentToken: response.id
|
paymentToken: token
|
||||||
};
|
};
|
||||||
|
|
||||||
return apiService.accounts.putPayment(null, request).$promise;
|
return apiService.accounts.putPayment(null, request).$promise;
|
||||||
|
@ -31,4 +68,17 @@
|
||||||
$scope.close = function () {
|
$scope.close = function () {
|
||||||
$uibModalInstance.dismiss('cancel');
|
$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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -104,8 +104,8 @@
|
||||||
trialEndDate: billing.Subscription.TrialEndDate,
|
trialEndDate: billing.Subscription.TrialEndDate,
|
||||||
cancelledDate: billing.Subscription.CancelledDate,
|
cancelledDate: billing.Subscription.CancelledDate,
|
||||||
status: billing.Subscription.Status,
|
status: billing.Subscription.Status,
|
||||||
cancelled: billing.Subscription.Status === 'cancelled',
|
cancelled: billing.Subscription.Cancelled,
|
||||||
markedForCancel: billing.Subscription.Status === 'active' && billing.Subscription.CancelledDate
|
markedForCancel: !billing.Subscription.Cancelled && billing.Subscription.CancelAtEndDate
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,62 @@
|
||||||
.module('bit.settings')
|
.module('bit.settings')
|
||||||
|
|
||||||
.controller('settingsPremiumController', function ($scope, $state, apiService, toastr, $analytics, authService, stripe,
|
.controller('settingsPremiumController', function ($scope, $state, apiService, toastr, $analytics, authService, stripe,
|
||||||
constants) {
|
constants, $timeout, appSettings) {
|
||||||
authService.getUserProfile().then(function (profile) {
|
authService.getUserProfile().then(function (profile) {
|
||||||
if (profile.premium) {
|
if (profile.premium) {
|
||||||
return $state.go('backend.user.settingsBilling');
|
return $state.go('backend.user.settingsBilling');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var btInstance = null;
|
||||||
$scope.storageGbPrice = constants.storageGb.yearlyPrice;
|
$scope.storageGbPrice = constants.storageGb.yearlyPrice;
|
||||||
$scope.premiumPrice = constants.premium.price;
|
$scope.premiumPrice = constants.premium.price;
|
||||||
|
$scope.paymentMethod = 'card';
|
||||||
|
$scope.dropinLoaded = false;
|
||||||
|
|
||||||
$scope.model = {
|
$scope.model = {
|
||||||
additionalStorageGb: null
|
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 () {
|
$scope.totalPrice = function () {
|
||||||
return $scope.premiumPrice + (($scope.model.additionalStorageGb || 0) * $scope.storageGbPrice);
|
return $scope.premiumPrice + (($scope.model.additionalStorageGb || 0) * $scope.storageGbPrice);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.submit = function (model) {
|
$scope.submit = function (model) {
|
||||||
$scope.submitPromise = stripe.card.createToken(model.card).then(function (response) {
|
$scope.submitPromise = getPaymentToken(model).then(function (token) {
|
||||||
var request = {
|
var request = {
|
||||||
paymentToken: response.id,
|
paymentToken: token,
|
||||||
additionalStorageGb: model.additionalStorageGb
|
additionalStorageGb: model.additionalStorageGb
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,4 +73,17 @@
|
||||||
toastr.success('Premium upgrade complete.', 'Success');
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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()"
|
<button type="button" class="btn btn-default btn-flat" ng-click="cancel()"
|
||||||
ng-if="!subscription.cancelled && !subscription.markedForCancel">
|
ng-if="!subscription.cancelled && !subscription.markedForCancel">
|
||||||
Cancel
|
Cancel
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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)">
|
<button type="button" class="btn btn-default btn-flat" ng-click="adjustStorage(true)">
|
||||||
Add Storage
|
Add Storage
|
||||||
</button>
|
</button>
|
||||||
|
@ -104,7 +104,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!loading && paymentSource">
|
<div ng-show="!loading && paymentSource">
|
||||||
<i class="fa" ng-class="{'fa-credit-card': paymentSource.type === 0,
|
<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}}
|
{{paymentSource.description}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,21 @@
|
||||||
<li ng-repeat="e in form.$errors">{{e}}</li>
|
<li ng-repeat="e in form.$errors">{{e}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group" show-errors>
|
<div class="form-group" show-errors>
|
||||||
|
@ -355,6 +370,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
|
<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
|
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Submit
|
||||||
|
|
|
@ -89,6 +89,19 @@
|
||||||
<h3 class="box-title">Payment Information</h3>
|
<h3 class="box-title">Payment Information</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<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="row">
|
||||||
<div class="col-md-5">
|
<div class="col-md-5">
|
||||||
<div class="form-group" show-errors>
|
<div class="form-group" show-errors>
|
||||||
|
@ -434,6 +447,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="form.$loading">
|
<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
|
<i class="fa fa-refresh fa-spin loading-icon" ng-show="form.$loading"></i>Submit
|
||||||
|
|
|
@ -7,18 +7,25 @@
|
||||||
'self';
|
'self';
|
||||||
script-src
|
script-src
|
||||||
'self'
|
'self'
|
||||||
|
'sha256-ryoU+5+IUZTuUyTElqkrQGBJXr1brEv6r2CA62WUw8w='
|
||||||
https://www.google-analytics.com
|
https://www.google-analytics.com
|
||||||
https://js.stripe.com
|
https://js.stripe.com
|
||||||
|
https://js.braintreegateway.com
|
||||||
|
https://www.paypalobjects.com
|
||||||
https://maxcdn.bootstrapcdn.com
|
https://maxcdn.bootstrapcdn.com
|
||||||
https://ajax.googleapis.com;
|
https://ajax.googleapis.com;
|
||||||
style-src
|
style-src
|
||||||
'self'
|
'self'
|
||||||
'unsafe-inline'
|
'unsafe-inline'
|
||||||
https://maxcdn.bootstrapcdn.com
|
https://maxcdn.bootstrapcdn.com
|
||||||
|
https://assets.braintreegateway.com
|
||||||
|
https://*.paypal.com
|
||||||
https://fonts.googleapis.com;
|
https://fonts.googleapis.com;
|
||||||
img-src
|
img-src
|
||||||
'self'
|
'self'
|
||||||
data:
|
data:
|
||||||
|
https://*.paypal.com
|
||||||
|
https://www.paypalobjects.com
|
||||||
https://q.stripe.com
|
https://q.stripe.com
|
||||||
https://haveibeenpwned.com
|
https://haveibeenpwned.com
|
||||||
https://chart.googleapis.com
|
https://chart.googleapis.com
|
||||||
|
@ -30,10 +37,14 @@
|
||||||
child-src
|
child-src
|
||||||
'self'
|
'self'
|
||||||
https://js.stripe.com
|
https://js.stripe.com
|
||||||
|
https://assets.braintreegateway.com
|
||||||
|
https://*.paypal.com
|
||||||
https://*.duosecurity.com;
|
https://*.duosecurity.com;
|
||||||
frame-src
|
frame-src
|
||||||
'self'
|
'self'
|
||||||
https://js.stripe.com
|
https://js.stripe.com
|
||||||
|
https://assets.braintreegateway.com
|
||||||
|
https://*.paypal.com
|
||||||
https://*.duosecurity.com;
|
https://*.duosecurity.com;
|
||||||
connect-src
|
connect-src
|
||||||
*;">
|
*;">
|
||||||
|
@ -43,6 +54,7 @@
|
||||||
<title page-title>bitwarden.com Password Manager</title>
|
<title page-title>bitwarden.com Password Manager</title>
|
||||||
|
|
||||||
<script src="https://js.stripe.com/v2/"></script>
|
<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 !>
|
<!-- @if true !>
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
|
||||||
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||||
|
|
|
@ -620,6 +620,10 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.braintree-placeholder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue