File

src/app/providers/services/payment.service.ts

Index

Properties

Properties

url
url: string
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;
  }
}

results matching ""

    No results matching ""