1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-23 19:53:43 +00:00

org billing settings setup

This commit is contained in:
Kyle Spearrin
2018-07-16 17:17:07 -04:00
parent 786f6953e7
commit 0650cafb28
17 changed files with 501 additions and 26 deletions

View File

@@ -10,7 +10,7 @@
<form #form class="card" (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="canChange">
<div class="card-body">
<h3 class="card-body-header">{{(currentType != null ? 'changePaymentMethod' : 'addPaymentMethod') | i18n}}</h3>
<app-payment [showOptions]="currentType == null"></app-payment>
<app-payment [showOptions]="!organizationId || currentType == null" [hidePaypal]="true" [hideBank]="!organizationId"></app-payment>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin"></i>
<span>{{'submit' | i18n}}</span>

View File

@@ -26,7 +26,7 @@ export class AdjustPaymentComponent {
@ViewChild(PaymentComponent) paymentComponent: PaymentComponent;
@Input() currentType?: PaymentMethodType;
@Input() user = true;
@Input() organizationId: string;
@Output() onAdjusted = new EventEmitter();
@Output() onCanceled = new EventEmitter();
@@ -41,8 +41,10 @@ export class AdjustPaymentComponent {
const request = new PaymentRequest();
this.formPromise = this.paymentComponent.createPaymentToken().then((token) => {
request.paymentToken = token;
if (this.user) {
if (this.organizationId == null) {
return this.apiService.postAccountPayment(request);
} else {
return this.apiService.postOrganizationPayment(this.organizationId, request);
}
});
await this.formPromise;

View File

@@ -20,7 +20,7 @@ import { StorageRequest } from 'jslib/models/request/storageRequest';
export class AdjustStorageComponent {
@Input() storageGbPrice = 0;
@Input() add = true;
@Input() user = true;
@Input() organizationId: string;
@Input() interval = 'year';
@Output() onAdjusted = new EventEmitter<number>();
@Output() onCanceled = new EventEmitter();
@@ -39,8 +39,10 @@ export class AdjustStorageComponent {
request.storageGbAdjustment *= -1;
}
if (this.user) {
if (this.organizationId == null) {
this.formPromise = this.apiService.postAccountStorage(request);
} else {
this.formPromise = this.apiService.postOrganizationStorage(this.organizationId, request);
}
await this.formPromise;
this.analytics.eventTrack.next({ action: this.add ? 'Added Storage' : 'Removed Storage' });

View File

@@ -169,7 +169,7 @@
</div>
<small class="text-muted font-italic">{{'paymentChargedWithTrial' | i18n : (interval | i18n) }}</small>
<h2 class="spaced-header mb-4">{{'paymentInformation' | i18n}}</h2>
<app-payment></app-payment>
<app-payment [hidePaypal]="true"></app-payment>
</ng-container>
<div [ngClass]="{'mt-4': plans[plan].noPayment}">
<button type="submit" class="btn btn-primary btn-submit" appBlurClick [disabled]="form.loading">

View File

@@ -32,7 +32,7 @@ export class DeleteAccountComponent {
const request = new PasswordVerificationRequest();
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
try {
this.formPromise = this.apiService.postDeleteAccount(request);
this.formPromise = this.apiService.deleteAccount(request);
await this.formPromise;
this.analytics.eventTrack.next({ action: 'Deleted Account' });
this.toasterService.popAsync('success', this.i18nService.t('accountDeleted'),

View File

@@ -4,7 +4,12 @@
<label class="form-check-label" for="method-card">
<i class="fa fa-fw fa-credit-card"></i> {{'creditCard' | i18n}}</label>
</div>
<div class="form-check form-check-inline">
<div class="form-check form-check-inline mr-4" *ngIf="!hideBank">
<input class="form-check-input" type="radio" name="Method" id="method-bank" value="bank" [(ngModel)]="method" (change)="changeMethod()">
<label class="form-check-label" for="method-bank">
<i class="fa fa-fw fa-university"></i> {{'bankAccount' | i18n}}</label>
</div>
<div class="form-check form-check-inline" *ngIf="!hidePaypal">
<input class="form-check-input" type="radio" name="Method" id="method-paypal" value="paypal" [(ngModel)]="method" (change)="changeMethod()">
<label class="form-check-label" for="method-paypal">
<i class="fa fa-fw fa-paypal"></i> PayPal</label>
@@ -309,3 +314,31 @@
<small class="text-muted">{{'paypalClickSubmit' | i18n}}</small>
</div>
</ng-container>
<ng-container *ngIf="method === 'bank'">
<app-callout type="warning" title="{{'verifyBankAccount' | i18n}}">
{{'verifyBankAccountInitialDesc' | i18n}} {{'verifyBankAccountFailureWarning' | i18n}}
</app-callout>
<div class="row">
<div class="form-group col-6">
<label for="routing_number">{{'routingNumber' | i18n}}</label>
<input id="routing_number" class="form-control" type="text" name="routing_number" [(ngModel)]="bank.routing_number" required>
</div>
<div class="form-group col-6">
<label for="account_number">{{'accountNumber' | i18n}}</label>
<input id="account_number" class="form-control" type="text" name="account_number" [(ngModel)]="bank.account_number" required>
</div>
<div class="form-group col-6">
<label for="account_holder_name">{{'accountHolderName' | i18n}}</label>
<input id="account_holder_name" class="form-control" type="text" name="account_holder_name" [(ngModel)]="bank.account_holder_name"
required>
</div>
<div class="form-group col-6">
<label for="account_holder_type">{{'bankAccountType' | i18n}}</label>
<select id="account_holder_type" class="form-control" name="account_holder_type" [(ngModel)]="bank.account_holder_type" required>
<option value="">-- {{'select' | i18n}} --</option>
<option value="company">{{'bankAccountTypeCompany' | i18n}}</option>
<option value="individual">{{'bankAccountTypeIndividual' | i18n}}</option>
</select>
</div>
</div>
</ng-container>

View File

@@ -20,8 +20,10 @@ const Keys = {
})
export class PaymentComponent implements OnInit {
@Input() showOptions = true;
@Input() method = 'card';
@Input() hideBank = false;
@Input() hidePaypal = false;
method = 'card';
card: any = {
number: null,
exp_month: null,
@@ -29,6 +31,14 @@ export class PaymentComponent implements OnInit {
address_country: '',
address_zip: null,
};
bank: any = {
routing_number: null,
account_number: null,
account_holder_name: null,
account_holder_type: '',
currency: 'USD',
country: 'US',
};
cardExpMonthOptions: any[];
cardExpYearOptions: any[];
@@ -68,24 +78,32 @@ export class PaymentComponent implements OnInit {
{ name: '-- ' + i18nService.t('select') + ' --', value: null },
];
const year = (new Date()).getFullYear();
for (let i = year; i < (year + 10); i++) {
for (let i = year; i < (year + 15); i++) {
this.cardExpYearOptions.push({ name: i.toString(), value: i.toString().slice(-2) });
}
}
ngOnInit() {
if (!this.showOptions) {
this.hidePaypal = this.method !== 'paypal';
this.hideBank = this.method !== 'bank';
}
window.document.head.appendChild(this.stripeScript);
window.document.head.appendChild(this.btScript);
if (!this.hidePaypal) {
window.document.head.appendChild(this.btScript);
}
}
ngOnDestroy() {
window.document.head.removeChild(this.stripeScript);
window.document.head.removeChild(this.btScript);
Array.from(window.document.querySelectorAll('iframe')).forEach((el) => {
if (el.src != null && el.src.indexOf('stripe') > -1) {
window.document.body.removeChild(el);
}
});
if (!this.hidePaypal) {
window.document.head.removeChild(this.btScript);
}
}
changeMethod() {
@@ -127,7 +145,7 @@ export class PaymentComponent implements OnInit {
}).catch((err: any) => {
reject(err.message);
});
} else {
} else if (this.method === 'card') {
(window as any).Stripe.card.createToken(this.card, (status: number, response: any) => {
if (status === 200 && response.id != null) {
resolve(response.id);
@@ -137,6 +155,16 @@ export class PaymentComponent implements OnInit {
reject();
}
});
} else if (this.method === 'bank') {
(window as any).Stripe.bankAccount.createToken(this.bank, (status: number, response: any) => {
if (status === 200 && response.id != null) {
resolve(response.id);
} else if (response.error != null) {
reject(response.error.message);
} else {
reject();
}
});
}
});
}

View File

@@ -65,7 +65,7 @@
</div>
<small class="text-muted font-italic">{{'paymentChargedAnnually' | i18n}}</small>
<h2 class="spaced-header mb-4">{{'paymentInformation' | i18n}}</h2>
<app-payment></app-payment>
<app-payment [hideBank]="true"></app-payment>
<button type="submit" class="btn btn-primary btn-submit" appBlurClick [disabled]="form.loading">
<i class="fa fa-spinner fa-spin"></i>
<span>{{'submit' | i18n}}</span>

View File

@@ -36,7 +36,7 @@
</dd>
</dl>
</div>
<div class="col-8">
<div class="col-8" *ngIf="subscription">
<strong class="d-block mb-1">{{'details' | i18n}}</strong>
<table class="table">
<tbody>
@@ -82,22 +82,22 @@
</div>
</ng-container>
<h2 class="spaced-header">{{'storage' | i18n}}</h2>
<p>{{'subscriptionStorage' | i18n : billing.maxStorageGb : billing.storageName || '0 MB'}}</p>
<p>{{'subscriptionStorage' | i18n : billing.maxStorageGb || 0 : billing.storageName || '0 MB'}}</p>
<div class="progress">
<div class="progress-bar bg-success" role="progressbar" [ngStyle]="{width: storagePercentage + '%' }" [attr.aria-valuenow]="storagePercentage"
<div class="progress-bar bg-success" role="progressbar" [ngStyle]="{width: storageProgressWidth + '%' }" [attr.aria-valuenow]="storagePercentage"
aria-valuemin="0" aria-valuemax="100">{{(storagePercentage / 100) | percent}}</div>
</div>
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel && paymentSource">
<div class="mt-3">
<ng-container *ngIf="!showAdjustStorage">
<div class="d-flex" *ngIf="!showAdjustStorage">
<button type="button" class="btn btn-outline-secondary" (click)="adjustStorage(true)">
{{'addStorage' | i18n}}
</button>
<button type="button" class="btn btn-outline-secondary" (click)="adjustStorage(false)">
<button type="button" class="ml-1 btn btn-outline-secondary" (click)="adjustStorage(false)">
{{'removeStorage' | i18n}}
</button>
</ng-container>
<app-adjust-storage [storageGbPrice]="4" [add]="adjustStorageAdd" [user]="true" (onAdjusted)="closeStorage(true)" (onCanceled)="closeStorage(false)"
</div>
<app-adjust-storage [storageGbPrice]="4" [add]="adjustStorageAdd" (onAdjusted)="closeStorage(true)" (onCanceled)="closeStorage(false)"
*ngIf="showAdjustStorage"></app-adjust-storage>
</div>
</ng-container>
@@ -112,8 +112,8 @@
<button type="button" class="btn btn-outline-secondary" (click)="changePayment()" *ngIf="!showAdjustPayment">
{{(paymentSource ? 'changePaymentMethod' : 'addPaymentMethod') | i18n}}
</button>
<app-adjust-payment [currentType]="paymentSource != null ? paymentSource.type : null" [user]="true" (onAdjusted)="closePayment(true)"
(onCanceled)="closePayment(false)" *ngIf="showAdjustPayment">
<app-adjust-payment [currentType]="paymentSource != null ? paymentSource.type : null" (onAdjusted)="closePayment(true)" (onCanceled)="closePayment(false)"
*ngIf="showAdjustPayment">
</app-adjust-payment>
<h2 class="spaced-header">{{'charges' | i18n}}</h2>
<p *ngIf="!charges || !charges.length">{{'noCharges' | i18n}}</p>

View File

@@ -170,6 +170,11 @@ export class UserBillingComponent implements OnInit {
}
get storagePercentage() {
return this.billing != null ? +(100 * (this.billing.storageGb / this.billing.maxStorageGb)).toFixed(2) : 0;
return this.billing != null && this.billing.maxStorageGb ?
+(100 * (this.billing.storageGb / this.billing.maxStorageGb)).toFixed(2) : 0;
}
get storageProgressWidth() {
return this.storagePercentage < 5 ? 5 : 0;
}
}