credit payment method

This commit is contained in:
Kyle Spearrin 2019-02-20 20:16:06 -05:00
parent 1f6632146b
commit fb3afbdc76
7 changed files with 48 additions and 29 deletions

2
jslib

@ -1 +1 @@
Subproject commit 36e65f08cad6cebdb5b90c88a68360296e8ff2ec Subproject commit 2b931963cd8dbebdcbdd6a418ac3ef72adb73539

View File

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

View File

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

View File

@ -1,21 +1,26 @@
<div class="mb-4 text-lg" *ngIf="showOptions"> <div class="mb-4 text-lg" *ngIf="showOptions">
<div class="form-check form-check-inline mr-4"> <div class="form-check form-check-inline mr-4">
<input class="form-check-input" type="radio" name="Method" id="method-card" value="card" [(ngModel)]="method" (change)="changeMethod()"> <input class="form-check-input" type="radio" name="Method" id="method-card" [value]="paymentMethodType.Card" [(ngModel)]="method" (change)="changeMethod()">
<label class="form-check-label" for="method-card"> <label class="form-check-label" for="method-card">
<i class="fa fa-fw fa-credit-card"></i> {{'creditCard' | i18n}}</label> <i class="fa fa-fw fa-credit-card"></i> {{'creditCard' | i18n}}</label>
</div> </div>
<div class="form-check form-check-inline mr-4" *ngIf="!hideBank"> <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()"> <input class="form-check-input" type="radio" name="Method" id="method-bank" [value]="paymentMethodType.BankAccount" [(ngModel)]="method" (change)="changeMethod()">
<label class="form-check-label" for="method-bank"> <label class="form-check-label" for="method-bank">
<i class="fa fa-fw fa-university"></i> {{'bankAccount' | i18n}}</label> <i class="fa fa-fw fa-university"></i> {{'bankAccount' | i18n}}</label>
</div> </div>
<div class="form-check form-check-inline" *ngIf="!hidePaypal"> <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()"> <input class="form-check-input" type="radio" name="Method" id="method-paypal" [value]="paymentMethodType.PayPal" [(ngModel)]="method" (change)="changeMethod()">
<label class="form-check-label" for="method-paypal"> <label class="form-check-label" for="method-paypal">
<i class="fa fa-fw fa-paypal"></i> PayPal</label> <i class="fa fa-fw fa-paypal"></i> PayPal</label>
</div> </div>
<div class="form-check form-check-inline" *ngIf="!hideCredit">
<input class="form-check-input" type="radio" name="Method" id="method-credit" [value]="paymentMethodType.Credit" [(ngModel)]="method" (change)="changeMethod()">
<label class="form-check-label" for="method-credit">
<i class="fa fa-fw fa-dollar"></i> {{'accountCredit' | i18n}}</label>
</div>
</div> </div>
<ng-container *ngIf="method === 'card'"> <ng-container *ngIf="method === paymentMethodType.Card">
<div class="row"> <div class="row">
<div class="form-group col-4"> <div class="form-group col-4">
<label for="stripe-card-number-element">{{'number' | i18n}}</label> <label for="stripe-card-number-element">{{'number' | i18n}}</label>
@ -39,13 +44,7 @@
</div> </div>
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="method === 'paypal'"> <ng-container *ngIf="method === paymentMethodType.BankAccount">
<div class="mb-3">
<div id="bt-dropin-container" class="mb-1"></div>
<small class="text-muted">{{'paypalClickSubmit' | i18n}}</small>
</div>
</ng-container>
<ng-container *ngIf="method === 'bank'">
<app-callout type="warning" title="{{'verifyBankAccount' | i18n}}"> <app-callout type="warning" title="{{'verifyBankAccount' | i18n}}">
{{'verifyBankAccountInitialDesc' | i18n}} {{'verifyBankAccountFailureWarning' | i18n}} {{'verifyBankAccountInitialDesc' | i18n}} {{'verifyBankAccountFailureWarning' | i18n}}
</app-callout> </app-callout>
@ -75,3 +74,14 @@
</div> </div>
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="method === paymentMethodType.PayPal">
<div class="mb-3">
<div id="bt-dropin-container" class="mb-1"></div>
<small class="text-muted">{{'paypalClickSubmit' | i18n}}</small>
</div>
</ng-container>
<ng-container *ngIf="method === paymentMethodType.Credit">
<app-callout type="note">
{{'makeSureEnoughCredit' | i18n}}
</app-callout>
</ng-container>

View File

@ -35,9 +35,10 @@ const StripeElementClasses = {
}) })
export class PaymentComponent implements OnInit { export class PaymentComponent implements OnInit {
@Input() showOptions = true; @Input() showOptions = true;
@Input() method: 'card' | 'paypal' | 'bank' = 'card'; @Input() method = PaymentMethodType.Card;
@Input() hideBank = false; @Input() hideBank = false;
@Input() hidePaypal = false; @Input() hidePaypal = false;
@Input() hideCredit = false;
bank: any = { bank: any = {
routing_number: null, routing_number: null,
@ -48,6 +49,8 @@ export class PaymentComponent implements OnInit {
country: 'US', country: 'US',
}; };
paymentMethodType = PaymentMethodType;
private btScript: HTMLScriptElement; private btScript: HTMLScriptElement;
private btInstance: any = null; private btInstance: any = null;
private stripeScript: HTMLScriptElement; private stripeScript: HTMLScriptElement;
@ -74,8 +77,9 @@ export class PaymentComponent implements OnInit {
ngOnInit() { ngOnInit() {
if (!this.showOptions) { if (!this.showOptions) {
this.hidePaypal = this.method !== 'paypal'; this.hidePaypal = this.method !== PaymentMethodType.PayPal;
this.hideBank = this.method !== 'bank'; this.hideBank = this.method !== PaymentMethodType.BankAccount;
this.hideCredit = this.method !== PaymentMethodType.Credit;
} }
window.document.head.appendChild(this.stripeScript); window.document.head.appendChild(this.stripeScript);
if (!this.hidePaypal) { if (!this.hidePaypal) {
@ -93,7 +97,7 @@ export class PaymentComponent implements OnInit {
} catch { } } catch { }
} }
}); });
}, 200); }, 500);
if (!this.hidePaypal) { if (!this.hidePaypal) {
window.document.head.removeChild(this.btScript); window.document.head.removeChild(this.btScript);
window.setTimeout(() => { window.setTimeout(() => {
@ -110,14 +114,14 @@ export class PaymentComponent implements OnInit {
window.document.head.removeChild(btStylesheet); window.document.head.removeChild(btStylesheet);
} catch { } } catch { }
} }
}, 200); }, 500);
} }
} }
changeMethod() { changeMethod() {
this.btInstance = null; this.btInstance = null;
if (this.method === 'paypal') { if (this.method === PaymentMethodType.PayPal) {
window.setTimeout(() => { window.setTimeout(() => {
(window as any).braintree.dropin.create({ (window as any).braintree.dropin.create({
authorization: this.platformUtilsService.isDev() ? authorization: this.platformUtilsService.isDev() ?
@ -149,20 +153,18 @@ export class PaymentComponent implements OnInit {
createPaymentToken(): Promise<[string, PaymentMethodType]> { createPaymentToken(): Promise<[string, PaymentMethodType]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.method === 'paypal') { if (this.method === PaymentMethodType.PayPal) {
this.btInstance.requestPaymentMethod().then((payload: any) => { this.btInstance.requestPaymentMethod().then((payload: any) => {
resolve([payload.nonce, PaymentMethodType.PayPal]); resolve([payload.nonce, this.method]);
}).catch((err: any) => { }).catch((err: any) => {
reject(err.message); reject(err.message);
}); });
} else if (this.method === 'card' || this.method === 'bank') { } else if (this.method === PaymentMethodType.Card || this.method === PaymentMethodType.BankAccount) {
let type = PaymentMethodType.Card;
let sourceObj: any = null; let sourceObj: any = null;
let createObj: any = null; let createObj: any = null;
if (this.method === 'card') { if (this.method === PaymentMethodType.Card) {
sourceObj = this.stripeCardNumberElement; sourceObj = this.stripeCardNumberElement;
} else { } else {
type = PaymentMethodType.BankAccount;
sourceObj = 'bank_account'; sourceObj = 'bank_account';
createObj = this.bank; createObj = this.bank;
} }
@ -170,7 +172,7 @@ export class PaymentComponent implements OnInit {
if (result.error) { if (result.error) {
reject(result.error.message); reject(result.error.message);
} else if (result.token && result.token.id != null) { } else if (result.token && result.token.id != null) {
resolve([result.token.id, type]); resolve([result.token.id, this.method]);
} else { } else {
reject(); reject();
} }
@ -181,7 +183,7 @@ export class PaymentComponent implements OnInit {
private setStripeElement() { private setStripeElement() {
window.setTimeout(() => { window.setTimeout(() => {
if (this.method === 'card') { if (this.method === PaymentMethodType.Card) {
if (this.stripeCardNumberElement == null) { if (this.stripeCardNumberElement == null) {
this.stripeCardNumberElement = this.stripeElements.create('cardNumber', { this.stripeCardNumberElement = this.stripeElements.create('cardNumber', {
style: StripeElementStyle, style: StripeElementStyle,

View File

@ -9,7 +9,8 @@
<i class="fa fa-spinner fa-spin text-muted" *ngIf="!firstLoaded && loading" title="{{'loading' | i18n}}"></i> <i class="fa fa-spinner fa-spin text-muted" *ngIf="!firstLoaded && loading" title="{{'loading' | i18n}}"></i>
<ng-container *ngIf="billing"> <ng-container *ngIf="billing">
<h2>{{(isCreditBalance ? 'accountCredit' : 'accountBalance') | i18n}}</h2> <h2>{{(isCreditBalance ? 'accountCredit' : 'accountBalance') | i18n}}</h2>
<p>{{creditOrBalance | currency:'$'}}</p> <p><strong>{{creditOrBalance | currency:'$'}}</strong></p>
<p>{{'creditAppliedDesc' | i18n}}</p>
<button type="button" class="btn btn-outline-secondary" (click)="addCredit()" *ngIf="!showAddCredit"> <button type="button" class="btn btn-outline-secondary" (click)="addCredit()" *ngIf="!showAddCredit">
{{'addCredit' | i18n}} {{'addCredit' | i18n}}
</button> </button>

View File

@ -1506,6 +1506,12 @@
"message": "Amount", "message": "Amount",
"description": "Dollar amount, or quantity." "description": "Dollar amount, or quantity."
}, },
"makeSureEnoughCredit": {
"message": "Please make sure that your account has enough credit available for this purchase. If your account does not have enough credit available, your default payment method on file will be used for the difference. You can add credit to your account from the Billing page."
},
"creditAppliedDesc": {
"message": "Your account's credit can be used to make purchases. Any available credit will be automatically applied towards invoices generated for this account."
},
"goPremium": { "goPremium": {
"message": "Go Premium", "message": "Go Premium",
"description": "Another way of saying \"Get a premium membership\"" "description": "Another way of saying \"Get a premium membership\""