src/app/providers/services/payment.service.ts
Properties |
ids |
ids:
|
Type : string[]
|
import { DuplicateInfo } from './../../models/duplicate-info';
import { DatePipe } from '@angular/common';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { catchError, filter, tap } from 'rxjs/operators';
import { map } from 'rxjs/operators/map';
import { ERROR_MESSAGE, LOCALE, PAYMENT_API, STATUS_TYPES } from '../../app.constants';
import { CreatePaymentRequest } from '../../models/create-payment-request.model';
import { IPaymentUpdate } from '../../models/i-payment-update.model';
import { Payment, PaymentInFlightResponse } from '../../models/payment.model';
import { Template } from '../../models/template.model';
import { AppStateStore } from '../stores/app-state.store';
import { ErrorStore } from '../stores/error.store';
import { SessionStore } from '../stores/session.store';
import { DeliveryDateService } from './delivery-date.service';
import { combineLatest } from 'rxjs/observable/combineLatest';
interface GetPaymentsResponse {
Data: any[];
}
interface CreatePaymentsResponse {
ids: string[];
}
export interface IExportUrl {
url: string;
}
@Injectable()
export class PaymentService {
private headers = new HttpHeaders();
private datePipe = new DatePipe(LOCALE);
private payments: Payment[] = null;
private publisher = new BehaviorSubject<Payment[]>(null);
public payments$ = this.publisher.asObservable();
inflightSendOnDate: string;
inflightDeliveryDate: string;
constructor(
private http: HttpClient,
private errorService: ErrorStore,
private app: AppStateStore,
public sessionStore: SessionStore,
private deliveryDateService: DeliveryDateService
) {
this.headers.append('Cache-control', 'no-cache');
this.headers.append('Pragma', 'no-cache');
this.headers.append('Expires', '0');
this.deliveryDateService.earliestStandardElectronicScheduleForDate$.subscribe(expectedSendOnDate => {
this.inflightSendOnDate = expectedSendOnDate;
});
this.deliveryDateService.expectedDeliveryDate$.subscribe(expectedDeliveryDate => {
this.inflightDeliveryDate = expectedDeliveryDate;
});
}
private publish() {
this.publisher.next(this.payments);
}
cachePayment(payment: Payment): void {
const pmt = payment;
pmt.paymentDateFormatted = this.datePipe.transform(new Date(), 'shortDate');
this.payments.push(pmt);
this.payments = this.payments.sort((pmt1: Payment, pmt2: Payment) => {
return this.sortByDateThenName(pmt1, pmt2);
});
this.publish();
}
cancelPayment(id: string): Observable<boolean> {
const url = `${PAYMENT_API}/${id}`;
return this.http.delete(url, { headers: this.headers }).pipe(
map((res) => {
this.payments = this.payments.filter((p) => p.id !== id);
this.publish();
return true;
}),
catchError((error) => {
const title = 'Failed To Cancel Payment';
return this.handleError(error, title);
})
);
}
dismissPaymentErrorMessage(hash: string): Observable<boolean> {
const url = `${PAYMENT_API}/${hash}`;
return this.http.delete(url, { headers: this.headers }).pipe(
map((res) => {
return true;
}),
catchError((error) => {
const title = 'Failed To Dismiss Payment Error Message';
return this.handleError(error, title);
})
);
}
createPayment(
amount: number,
memo: string,
debitTemplate: Template,
creditTemplate: Template
): Observable<Payment> {
const request = {
creditTemplateId: creditTemplate.id,
debitTemplateId: debitTemplate.id,
amount: amount,
memo: memo,
payFrom: debitTemplate.displayName,
payFromAccount: debitTemplate.accountNumber,
payToName: creditTemplate.displayName,
payToBankAccount: creditTemplate.accountNumber,
} as CreatePaymentRequest;
if (this.app.acknowledgeDuplicate) {
request.duplicateOf = !!this.app.duplicateInfo.paymentId
? this.app.duplicateInfo.paymentId
: `${this.app.duplicateInfo.key}|${this.app.duplicateInfo.hash}`;
}
const url = `${PAYMENT_API}/create`;
return this.http.post<PaymentInFlightResponse>(url, request).pipe(
map((response) => {
const payment = new Payment();
payment.inFlightKey = response.key;
payment.hash = response.hash;
payment.amount = request.amount;
payment.memo = request.memo;
payment.status = 'InFlight'; // todo get text from product
payment.payFrom = request.payFrom;
payment.payFromAccountDisplayName = request.payFrom;
payment.payFromAccount = request.payFromAccount;
payment.payToName = request.payToName;
payment.payToBankAccount = request.payToBankAccount;
// todo store inflight payment dto into session storage for on refresh / leave & return use cases
this.cachePayment(payment);
this.payments = this.payments.sort((pmt1: Payment, pmt2: Payment) => {
return this.sortByDateThenName(pmt1, pmt2);
});
this.publish();
return payment;
}),
catchError((error) => {
const title = 'Failed To Create Payment';
return this.handleError(error, title);
})
);
}
updatePayment(id: string, payload: IPaymentUpdate): Observable<boolean> {
const url = `${PAYMENT_API}/${id}`;
return this.http.patch(url, payload).pipe(
map((res) => {
this.payments = this.payments.map((p) => {
if (p.id === id) {
p.amount = payload.amount;
}
return p;
});
this.publish();
return true;
}),
catchError((response) => {
const values = !!response && !!response.error && response.error['$values'];
const message = !!values && values.length > 0 ? values[0]['Message'] : null;
const title = message.toLowerCase().includes('limit')
? 'Limit Exceeded'
: 'Failed To Update Payment';
return this.handleError(null, title, message);
})
);
}
getPaymentById(id: string): Observable<Payment> {
try {
return this.payments$.pipe(
filter((payments) => !!payments),
map(payments => payments.filter(p => p.id === id)),
map((payments) => {
const foundTemplate = payments.length > 0 ? payments[0] : null;
return foundTemplate;
})
);
} catch (error) {
const title = 'Failed To Retrieve Payment';
return this.handleError(error, title);
}
}
findDuplicate(): Observable<Payment> {
try {
return combineLatest(this.payments$, this.deliveryDateService.earliestStandardElectronicScheduleForDate$).pipe(
filter(([payments, nextBusinessDate]) => !!payments && !!nextBusinessDate),
map(([payments, nextBusinessDate]) => {
const foundPayment = payments
.filter((p) => {
if (p.error && p.error.length > 0) {
return false;
}
if (p.status.toLowerCase() === 'inflight') {
return true;
}
const date1 = new Date(p.paymentDateFormatted);
const date2 = new Date(nextBusinessDate);
return date1.getTime() === date2.getTime(); // use date as ms to compare date strings even if formatted differently s
})
.filter((p) => p.amount === this.app.amount)
.filter((p) => p.payFromAccount === this.app.debitTemplate.accountNumber)
.filter((p) => p.payToBankAccount === this.app.creditTemplate.accountNumber)
.reduce((x, p) => {
console.log('matched', p);
return p;
}, null); // return only item in the array instead of returning an array
return foundPayment;
})
);
} catch (error) {
const title = 'Failed To Retrieve Payment';
return this.handleError(error, title);
}
}
loadPayments(isScheduled: boolean, orderby: string, orderbyDirection: string): Observable<Payment[]> {
const url = `${PAYMENT_API}?isScheduled=${isScheduled}&orderby=${orderby}&orderbyDirection=${orderbyDirection}`;
return this.http
.get<Payment[]>(url, { headers: this.headers })
.pipe(
map((payments) => {
this.payments = payments.map((p) => {
if (p.status === STATUS_TYPES.INFLIGHT) {
p.payFromAccountDisplayName = p.payFrom;
p.expectedSendOnDate = this.inflightSendOnDate;
p.expectedDeliveryDate = this.inflightDeliveryDate;
}
isScheduled === true ? p.canCancel = true : p.canCancel = false;
return p;
});
this.publish();
return payments;
}),
catchError((error) => {
const title = 'Failed To Retrieve Payments';
return this.handleError(error, title);
})
);
}
getExportUrl(type: string, startDate: string, endDate: string): Observable<string> {
return this.http
.get<IExportUrl>(
`${PAYMENT_API}/exporturl?type=${type}&startDate=${startDate}&endDate=${endDate}`
)
.pipe(
map((response) => {
if (!response || !response.url || response.url.length === 0) {
return null;
}
return response.url;
}),
catchError((error) => {
const title = 'Failed To Retrieve Export URL';
return this.handleError(error, title);
})
);
}
acknowledgeDuplicate(customerId: string, payment: Payment) {
const info: DuplicateInfo = {};
if (payment.inFlightKey && payment.inFlightKey.length > 0) {
info.hash = payment.hash;
info.key = payment.inFlightKey;
} else {
info.paymentId = payment.id;
}
console.log('acknowleded duplicate of', info);
this.app.acknowledgeDuplicate = true;
this.app.duplicateInfo = info;
}
private handleError(error: any, title: string, msg: string = null) {
let message = ERROR_MESSAGE;
if (!!msg) {
message = msg;
}
if (error && error.error && error.error.length > 0 && error.error[0].message) {
message = error.error[0].message;
if (message.toLowerCase().includes('payment totals are over daily limit')) {
title = 'Limit Exceeded';
}
}
this.errorService.showError(title, message);
return Observable.of(null);
}
private sortByDateThenName(pmt1: Payment, pmt2: Payment): number {
let result = this.sortByDateAsc(pmt1, pmt2);
if (result === 0) {
result = this.sortByNameAsc(pmt1, pmt2);
}
return result;
}
private sortByDateAsc(pmt1: Payment, pmt2: Payment): number {
const dt1 = new Date(pmt1.paymentDate);
const dt2 = new Date(pmt2.paymentDate);
if (dt1 > dt2) {
return 1;
}
if (dt1 < dt2) {
return -1;
}
return 0;
}
private sortByNameAsc(pmt1: Payment, pmt2: Payment): number {
const name1 = pmt1.payToName;
const name2 = pmt2.payToName;
if (name1 > name2) {
return 1;
}
if (name1 < name2) {
return -1;
}
return 0;
}
}