Exemplo n.º 1
0
    def post():
        """Create the payment records."""
        current_app.logger.info('<Payment.post')
        request_json = request.get_json()
        current_app.logger.debug(request_json)
        # Validate the input request
        valid_format, errors = schema_utils.validate(request_json,
                                                     'payment_request')

        if not valid_format:
            return error_to_response(
                Error.INVALID_REQUEST,
                invalid_params=schema_utils.serialize(errors))

        # Check if user is authorized to perform this action
        business_identifier = get_str_by_path(
            request_json, 'businessInfo/businessIdentifier')
        corp_type_code = get_str_by_path(request_json, 'businessInfo/corpType')

        authorization = check_auth(business_identifier=business_identifier,
                                   corp_type_code=corp_type_code,
                                   contains_role=EDIT_ROLE)
        try:
            response, status = PaymentService.create_payment(
                request_json, authorization), HTTPStatus.CREATED
        except (BusinessException, ServiceUnavailableException) as exception:
            return exception.response()
        current_app.logger.debug('>Payment.post')
        return jsonify(response), status
Exemplo n.º 2
0
def _get_payment_method(payment_request: Dict, authorization: Dict):
    payment_method = get_str_by_path(payment_request,
                                     'paymentInfo/methodOfPayment')
    if not payment_method:
        payment_method = get_str_by_path(
            authorization, 'account/paymentPreference/methodOfPayment')
    if not payment_method:
        payment_method = 'CC'
    return payment_method
Exemplo n.º 3
0
 def create_account(self, name: str, account_info: Dict[str, Any],
                    authorization: Dict[str, Any]):
     """Create account."""
     current_app.logger.debug('<create_account')
     bcol_user_id: str = get_str_by_path(
         authorization, 'account/paymentPreference/bcOnlineUserId')
     bcol_account_id: str = get_str_by_path(
         authorization, 'account/paymentPreference/bcOnlineAccountId')
     return {
         'bcol_account_id': bcol_account_id,
         'bcol_user_id': bcol_user_id
     }
Exemplo n.º 4
0
def _get_payment_method(payment_request: Dict, authorization: Dict):
    payment_method = get_str_by_path(payment_request,
                                     'paymentInfo/methodOfPayment')
    if not payment_method:
        payment_method = get_str_by_path(
            authorization, 'account/paymentPreference/methodOfPayment')
    if not payment_method:
        is_direct_pay_enabled = current_app.config.get('DIRECT_PAY_ENABLED')
        if is_direct_pay_enabled:
            payment_method = PaymentMethod.DIRECT_PAY.value
        else:
            payment_method = PaymentMethod.CC.value
    return payment_method
Exemplo n.º 5
0
    def _find_payment_account(cls, authorization):
        # find payment account
        payment_account: PaymentAccount = PaymentAccount.find_account(
            authorization)

        # If there is no payment_account it must be a request with no account (NR, Staff payment etc.)
        # and invoked using a service account or a staff token
        if not payment_account:
            payment_method = get_str_by_path(
                authorization, 'account/paymentInfo/methodOfPayment'
            ) or _get_default_payment()
            payment_account = PaymentAccount.create(
                dict(accountId=get_str_by_path(authorization, 'account/id'),
                     paymentInfo=dict(methodOfPayment=payment_method)))
        return payment_account
Exemplo n.º 6
0
 def create_account(self, name: str, contact_info: Dict[str, Any], authorization: Dict[str, Any], **kwargs):
     """Create account."""
     current_app.logger.debug('<create_account')
     user: UserContext = kwargs['user']
     account_info: Dict[str, Any] = kwargs['account_info']
     if user.is_staff() or user.is_system():
         bcol_user_id: str = None
         bcol_account_id: str = get_str_by_path(account_info, 'bcolAccountNumber')
     else:
         bcol_user_id: str = get_str_by_path(authorization, 'account/paymentPreference/bcOnlineUserId')
         bcol_account_id: str = get_str_by_path(authorization, 'account/paymentPreference/bcOnlineAccountId')
     return {
         'bcol_account_id': bcol_account_id,
         'bcol_user_id': bcol_user_id
     }
Exemplo n.º 7
0
    def create_refund(cls, invoice_id: int, request: Dict[str, str],
                      **kwargs) -> Dict[str, str]:
        """Create refund."""
        current_app.logger.debug(f'Starting refund : {invoice_id}')
        # Do validation by looking up the invoice
        invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id)

        paid_statuses = (InvoiceStatus.PAID.value,
                         InvoiceStatus.APPROVED.value,
                         InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value)

        if invoice.invoice_status_code not in paid_statuses:
            current_app.logger.info(
                f'Cannot process refund as status of {invoice_id} is {invoice.invoice_status_code}'
            )
            raise BusinessException(Error.INVALID_REQUEST)

        refund: RefundService = RefundService()
        refund.invoice_id = invoice_id
        refund.reason = get_str_by_path(request, 'reason')
        refund.requested_by = kwargs['user'].user_name
        refund.requested_date = datetime.now()
        refund.flush()
        pay_system_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
            payment_method=invoice.payment_method_code)
        invoice_status = pay_system_service.process_cfs_refund(invoice)
        message = REFUND_SUCCESS_MESSAGES.get(
            f'{invoice.payment_method_code}.{invoice.invoice_status_code}')
        # set invoice status
        invoice.invoice_status_code = invoice_status or InvoiceStatus.REFUND_REQUESTED.value
        invoice.refund = invoice.total  # no partial refund
        invoice.save()
        current_app.logger.debug(f'Completed refund : {invoice_id}')
        return {'message': message}
Exemplo n.º 8
0
def _create_account(pay_service, business_info, contact_info, account_info,
                    authorization):
    """Create account in pay system and save it in pay db."""
    current_app.logger.debug('Check if payment account exists')
    payment_account: PaymentAccount = PaymentAccount.find_account(
        business_info,
        authorization,
        pay_service.get_payment_system_code(),
    )
    if not payment_account.id:
        current_app.logger.debug('No payment account, creating new')
        name = business_info.get(
            'businessName', get_str_by_path(authorization, 'account/name'))
        pay_system_account = pay_service.create_account(
            name=name,
            contact_info=contact_info,
            account_info=account_info,
            authorization=authorization)

        current_app.logger.debug(
            'Creating payment record for account : {}'.format(
                payment_account.id))
        payment_account = PaymentAccount.create(
            business_info=business_info,
            account_details=pay_system_account,
            payment_system=pay_service.get_payment_system_code(),
            authorization=authorization)
    return payment_account
Exemplo n.º 9
0
    def create_refund(cls, invoice_id: int, request: Dict[str, str],
                      **kwargs) -> None:
        """Create refund."""
        current_app.logger.debug('<create refund')
        # Do validation by looking up the invoice
        invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id)
        # Allow refund only for direct pay payments, and only if the status of invoice is PAID/UPDATE_REVENUE_ACCOUNT
        paid_statuses = (InvoiceStatus.PAID.value,
                         InvoiceStatus.APPROVED.value,
                         InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value)

        if invoice.invoice_status_code not in paid_statuses:
            raise BusinessException(Error.INVALID_REQUEST)

        refund: RefundService = RefundService()
        refund.invoice_id = invoice_id
        refund.reason = get_str_by_path(request, 'reason')
        refund.requested_by = kwargs['user'].user_name
        refund.requested_date = datetime.now()
        refund.flush()

        cls._process_cfs_refund(invoice)

        # set invoice status
        invoice.invoice_status_code = InvoiceStatus.REFUND_REQUESTED.value
        invoice.refund = invoice.total  # no partial refund
        invoice.save()
Exemplo n.º 10
0
    def _save_account(cls,
                      account_request: Dict[str, any],
                      payment_account: PaymentAccountModel,
                      is_sandbox: bool = False):
        """Update and save payment account and CFS account model."""
        # pylint:disable=cyclic-import, import-outside-toplevel
        from pay_api.factory.payment_system_factory import PaymentSystemFactory

        payment_account.auth_account_id = account_request.get('accountId')

        # If the payment method is CC, set the payment_method as DIRECT_PAY
        if payment_method := get_str_by_path(account_request,
                                             'paymentInfo/methodOfPayment'):
            payment_account.payment_method = payment_method
            payment_account.bcol_account = account_request.get(
                'bcolAccountNumber', None)
            payment_account.bcol_user_id = account_request.get(
                'bcolUserId', None)
Exemplo n.º 11
0
 def create_routing_slip_refund(cls, routing_slip_number: str,
                                request: Dict[str, str],
                                **kwargs) -> Dict[str, str]:
     """Create Routing slip refund."""
     current_app.logger.debug('<create Routing slip  refund')
     #
     # check if routing slip exists
     # validate user role -> update status of routing slip
     # check refunds table
     #   if Yes ; update the data [only with whatever is in payload]
     #   if not ; create new entry
     # call cfs
     rs_model = RoutingSlipModel.find_by_number(routing_slip_number)
     if not rs_model:
         raise BusinessException(Error.RS_DOESNT_EXIST)
     reason = get_str_by_path(request, 'reason')
     if (refund_status := get_str_by_path(request, 'status')) is None:
         raise BusinessException(Error.INVALID_REQUEST)
Exemplo n.º 12
0
    def find_account(cls, business_info: Dict[str, Any],
                     authorization: Dict[str, Any],
                     payment_system: str, **kwargs):
        """Find payment account by corp number, corp type and payment system code."""
        current_app.logger.debug('<find_payment_account')
        user: UserContext = kwargs['user']
        auth_account_id: str = get_str_by_path(authorization, 'account/id')
        bcol_user_id: str = get_str_by_path(authorization, 'account/paymentPreference/bcOnlineUserId')
        bcol_account_id: str = get_str_by_path(authorization, 'account/paymentPreference/bcOnlineAccountId')
        corp_number: str = business_info.get('businessIdentifier')
        corp_type: str = business_info.get('corpType')

        account_dao = None

        if payment_system == PaymentSystem.BCOL.value:
            # If BCOL payment and if the payment is inititated by customer check if bcol_user_id is present
            # Staff and system can make payments for users with no bcol account
            if not user.is_system() and not user.is_staff() and bcol_user_id is None:
                raise BusinessException(Error.INCOMPLETE_ACCOUNT_SETUP)

            account_dao: BcolPaymentAccount = BcolPaymentAccount.find_by_bcol_user_id_and_account(
                auth_account_id=auth_account_id,
                bcol_user_id=bcol_user_id,
                bcol_account_id=bcol_account_id
            )
        elif payment_system == PaymentSystem.INTERNAL.value:
            account_dao: InternalPaymentAccount = InternalPaymentAccount. \
                find_by_corp_number_and_corp_type_and_account_id(corp_number=corp_number, corp_type=corp_type,
                                                                 account_id=auth_account_id
                                                                 )
        elif payment_system == PaymentSystem.PAYBC.value:
            if not corp_number and not corp_type:
                raise BusinessException(Error.INVALID_CORP_OR_FILING_TYPE)
            account_dao = CreditPaymentAccount.find_by_corp_number_and_corp_type_and_auth_account_id(
                corp_number=corp_number,
                corp_type=corp_type,
                auth_account_id=auth_account_id
            )
        payment_account = PaymentAccount()
        payment_account.populate(account_dao)  # pylint: disable=protected-access

        current_app.logger.debug('>find_payment_account')
        return payment_account
Exemplo n.º 13
0
def _get_payment_method(payment_request: Dict,
                        payment_account: PaymentAccount):
    # If no methodOfPayment is provided, use the one against the payment account table.
    payment_method = get_str_by_path(payment_request,
                                     'paymentInfo/methodOfPayment')
    if not payment_method:
        payment_method = payment_account.payment_method
    if not payment_method:
        payment_method = _get_default_payment()
    return payment_method
Exemplo n.º 14
0
    def find_account(cls, business_info: Dict[str, Any],
                     authorization: Dict[str, Any], payment_system: str):
        """Find payment account by corp number, corp type and payment system code."""
        current_app.logger.debug('<find_payment_account')
        auth_account_id: str = get_str_by_path(authorization, 'account/id')
        bcol_user_id: str = get_str_by_path(authorization, 'account/paymentPreference/bcOnlineUserId')
        bcol_account_id: str = get_str_by_path(authorization, 'account/paymentPreference/bcOnlineAccountId')
        corp_number: str = business_info.get('businessIdentifier')
        corp_type: str = business_info.get('corpType')

        account_dao = None

        if payment_system == PaymentSystem.BCOL.value:
            if not bcol_user_id:
                raise BusinessException(Error.PAY015)

            account_dao: BcolPaymentAccount = BcolPaymentAccount.find_by_bcol_user_id_and_account(
                auth_account_id=auth_account_id,
                bcol_user_id=bcol_user_id,
                bcol_account_id=bcol_account_id
            )
        elif payment_system == PaymentSystem.INTERNAL.value:
            account_dao: InternalPaymentAccount = InternalPaymentAccount. \
                find_by_corp_number_and_corp_type_and_account_id(corp_number=corp_number, corp_type=corp_type,
                                                                 account_id=auth_account_id
                                                                 )
        elif payment_system == PaymentSystem.PAYBC.value:
            if not corp_number and not corp_type:
                raise BusinessException(Error.PAY004)
            account_dao = CreditPaymentAccount.find_by_corp_number_and_corp_type_and_auth_account_id(
                corp_number=corp_number,
                corp_type=corp_type,
                auth_account_id=auth_account_id
            )
        payment_account = PaymentAccount()
        payment_account.populate(account_dao)  # pylint: disable=protected-access

        current_app.logger.debug('>find_payment_account')
        return payment_account
Exemplo n.º 15
0
    def create(business_info: Dict[str, Any], account_details: Dict[str, str],
               payment_system: str, authorization: Dict[str, Any]):
        """Create Payment account record."""
        current_app.logger.debug('<create')
        auth_account_id = get_str_by_path(authorization, 'account/id')
        payment_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id(auth_account_id)
        new_payment_account: bool = False
        is_premium_payment: bool = False
        if not payment_account:
            payment_account = PaymentAccountModel()
            payment_account.auth_account_id = auth_account_id
            payment_account = payment_account.save()
            new_payment_account = True

        dao = None
        if payment_system == PaymentSystem.BCOL.value:
            dao = BcolPaymentAccount()
            dao.account_id = payment_account.id
            dao.bcol_account_id = account_details.get('bcol_account_id', None)
            dao.bcol_user_id = account_details.get('bcol_user_id', None)
            is_premium_payment = True
        elif payment_system == PaymentSystem.INTERNAL.value:
            dao = InternalPaymentAccount()
            dao.corp_number = business_info.get('businessIdentifier', None)
            dao.corp_type_code = business_info.get('corpType', None)
            dao.account_id = payment_account.id
        elif payment_system == PaymentSystem.PAYBC.value:
            dao = CreditPaymentAccount()
            dao.corp_number = business_info.get('businessIdentifier', None)
            dao.corp_type_code = business_info.get('corpType', None)
            dao.paybc_account = account_details.get('account_number', None)
            dao.paybc_party = account_details.get('party_number', None)
            dao.paybc_site = account_details.get('site_number', None)
            dao.account_id = payment_account.id

        dao = dao.save()

        if new_payment_account and is_premium_payment:
            PaymentAccount._persist_default_statement_frequency(payment_account.id)

        p = PaymentAccount()
        p.populate(dao)  # pylint: disable=protected-access
        current_app.logger.debug('>create')
        return p
Exemplo n.º 16
0
    def update_invoice(cls, invoice_id: int,
                       payment_request: Tuple[Dict[str, Any]]):
        """Update invoice related records."""
        current_app.logger.debug('<update_invoice')

        invoice: Invoice = Invoice.find_by_id(invoice_id,
                                              skip_auth_check=False)
        payment_method = get_str_by_path(payment_request,
                                         'paymentInfo/methodOfPayment')

        is_not_currently_on_ob = invoice.payment_method_code != PaymentMethod.ONLINE_BANKING.value
        is_not_changing_to_cc = payment_method not in (
            PaymentMethod.CC.value, PaymentMethod.DIRECT_PAY.value)
        # can patch only if the current payment method is OB
        if is_not_currently_on_ob or is_not_changing_to_cc:
            raise BusinessException(Error.INVALID_REQUEST)

        # check if it has any invoice references already created
        # if there is any invoice ref , send them to the invoiced credit card flow

        invoice_reference = InvoiceReference.find_active_reference_by_invoice_id(
            invoice.id)
        if invoice_reference:
            invoice.payment_method_code = PaymentMethod.CC.value
        else:
            pay_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
                PaymentMethod.DIRECT_PAY.value)
            payment_account = PaymentAccount.find_by_id(
                invoice.payment_account_id)
            pay_service.create_invoice(payment_account,
                                       invoice.payment_line_items,
                                       invoice,
                                       corp_type_code=invoice.corp_type_code)

            invoice.payment_method_code = PaymentMethod.DIRECT_PAY.value
        invoice.save()
        current_app.logger.debug('>update_invoice')
        return invoice.asdict()
Exemplo n.º 17
0
    def search_purchase_history(cls,  # pylint:disable=too-many-arguments, too-many-locals, too-many-branches
                                auth_account_id: str, search_filter: Dict,
                                page: int, limit: int, return_all: bool, max_no_records: int = 0):
        """Search for purchase history."""
        # Payment Account Sub Query
        payment_account_sub_query = db.session.query(PaymentAccount).filter(
            PaymentAccount.auth_account_id == auth_account_id).subquery('pay_accnt')

        query = db.session.query(Payment, Invoice) \
            .join(Invoice) \
            .outerjoin(CreditPaymentAccount) \
            .outerjoin(BcolPaymentAccount) \
            .outerjoin(InternalPaymentAccount) \
            .filter(or_(InternalPaymentAccount.account_id == payment_account_sub_query.c.id,
                        BcolPaymentAccount.account_id == payment_account_sub_query.c.id,
                        CreditPaymentAccount.account_id == payment_account_sub_query.c.id))

        if search_filter.get('status', None):
            query = query.filter(Payment.payment_status_code == search_filter.get('status'))
        if search_filter.get('folioNumber', None):
            query = query.filter(Invoice.folio_number == search_filter.get('folioNumber'))
        if search_filter.get('businessIdentifier', None):
            query = query.filter(Invoice.business_identifier == search_filter.get('businessIdentifier'))
        if search_filter.get('createdBy', None):  # pylint: disable=no-member
            query = query.filter(
                Payment.created_name.like('%' + search_filter.get('createdBy') + '%'))  # pylint: disable=no-member

        # Find start and end dates
        created_from: datetime = None
        created_to: datetime = None
        if get_str_by_path(search_filter, 'dateFilter/startDate'):
            created_from = datetime.strptime(get_str_by_path(search_filter, 'dateFilter/startDate'), '%m/%d/%Y')
        if get_str_by_path(search_filter, 'dateFilter/endDate'):
            created_to = datetime.strptime(get_str_by_path(search_filter, 'dateFilter/endDate'), '%m/%d/%Y')
        if get_str_by_path(search_filter, 'weekFilter/index'):
            created_from, created_to = get_week_start_and_end_date(
                int(get_str_by_path(search_filter, 'weekFilter/index')))
        if get_str_by_path(search_filter, 'monthFilter/month') and get_str_by_path(search_filter, 'monthFilter/year'):
            month = int(get_str_by_path(search_filter, 'monthFilter/month'))
            year = int(get_str_by_path(search_filter, 'monthFilter/year'))
            created_from, created_to = get_first_and_last_dates_of_month(month=month, year=year)

        if created_from and created_to:
            # Truncate time for from date and add max time for to date
            tz_name = current_app.config['LEGISLATIVE_TIMEZONE']
            tz_local = pytz.timezone(tz_name)

            created_from = created_from.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(tz_local)
            created_to = created_to.replace(hour=23, minute=59, second=59, microsecond=999999).astimezone(tz_local)
            query = query.filter(
                func.timezone(tz_name, func.timezone('UTC', Payment.created_on)).between(created_from, created_to))

        # Add ordering
        query = query.order_by(Payment.created_on.desc())

        if not return_all:
            # Add pagination
            pagination = query.paginate(per_page=limit, page=page)
            result, count = pagination.items, pagination.total
            # If maximum number of records is provided, return it as total
            if max_no_records > 0:
                count = max_no_records if max_no_records < count else count
        else:
            # If maximum number of records is provided, set the page with that number
            if max_no_records > 0:
                pagination = query.paginate(per_page=max_no_records, page=1)
                result, count = pagination.items, max_no_records
            else:
                result = query.all()
                count = len(result)

        return result, count
Exemplo n.º 18
0
 def find_account(cls, authorization: Dict[str, Any]) -> PaymentAccount:
     """Find payment account by corp number, corp type and payment system code."""
     current_app.logger.debug('<find_payment_account')
     auth_account_id: str = get_str_by_path(authorization, 'account/id')
     return PaymentAccount.find_by_auth_account_id(auth_account_id)
Exemplo n.º 19
0
    def search_purchase_history(
            cls,  # pylint:disable=too-many-arguments, too-many-locals, too-many-branches
            auth_account_id: str,
            search_filter: Dict,
            page: int,
            limit: int,
            return_all: bool,
            max_no_records: int = 0,
            **kwargs):
        """Search for purchase history."""
        user: UserContext = kwargs['user']
        product_code = user.product_code

        query = db.session.query(Invoice) \
            .outerjoin(PaymentAccount, Invoice.payment_account_id == PaymentAccount.id) \
            .filter(PaymentAccount.auth_account_id == auth_account_id)
        # If a product code is present in token (service account), then filter only that product's invoices.
        if product_code:
            query = query.join(CorpType, CorpType.code == Invoice.corp_type_code)\
                .filter(CorpType.product == product_code)
        if search_filter.get('status', None):
            query = query.filter(
                Invoice.invoice_status_code == search_filter.get('status'))
        if search_filter.get('folioNumber', None):
            query = query.filter(
                Invoice.folio_number == search_filter.get('folioNumber'))
        if search_filter.get('businessIdentifier', None):
            query = query.filter(Invoice.business_identifier ==
                                 search_filter.get('businessIdentifier'))
        if search_filter.get('createdBy', None):  # pylint: disable=no-member
            query = query.filter(
                Invoice.created_name.ilike('%' +
                                           search_filter.get('createdBy') +
                                           '%'))  # pylint: disable=no-member

        # Find start and end dates
        created_from: datetime = None
        created_to: datetime = None
        if get_str_by_path(search_filter, 'dateFilter/startDate'):
            created_from = datetime.strptime(
                get_str_by_path(search_filter, 'dateFilter/startDate'),
                DT_SHORT_FORMAT)
        if get_str_by_path(search_filter, 'dateFilter/endDate'):
            created_to = datetime.strptime(
                get_str_by_path(search_filter, 'dateFilter/endDate'),
                DT_SHORT_FORMAT)
        if get_str_by_path(search_filter, 'weekFilter/index'):
            created_from, created_to = get_week_start_and_end_date(
                int(get_str_by_path(search_filter, 'weekFilter/index')))
        if get_str_by_path(search_filter,
                           'monthFilter/month') and get_str_by_path(
                               search_filter, 'monthFilter/year'):
            month = int(get_str_by_path(search_filter, 'monthFilter/month'))
            year = int(get_str_by_path(search_filter, 'monthFilter/year'))
            created_from, created_to = get_first_and_last_dates_of_month(
                month=month, year=year)

        if created_from and created_to:
            # Truncate time for from date and add max time for to date
            tz_name = current_app.config['LEGISLATIVE_TIMEZONE']
            tz_local = pytz.timezone(tz_name)

            created_from = created_from.replace(
                hour=0, minute=0, second=0, microsecond=0).astimezone(tz_local)
            created_to = created_to.replace(
                hour=23, minute=59, second=59,
                microsecond=999999).astimezone(tz_local)
            query = query.filter(
                func.timezone(tz_name,
                              func.timezone('UTC',
                                            Invoice.created_on)).between(
                                                created_from, created_to))

        # Add ordering
        query = query.order_by(Invoice.created_on.desc())

        if not return_all:
            # Add pagination
            pagination = query.paginate(per_page=limit, page=page)
            result, count = pagination.items, pagination.total
            # If maximum number of records is provided, return it as total
            if max_no_records > 0:
                count = max_no_records if max_no_records < count else count
        else:
            # If maximum number of records is provided, set the page with that number
            if max_no_records > 0:
                pagination = query.paginate(per_page=max_no_records, page=1)
                result, count = pagination.items, max_no_records
            else:
                result = query.all()
                count = len(result)

        return result, count
Exemplo n.º 20
0
    def _save_account(cls, account_request: Dict[str, any],
                      payment_account: PaymentAccountModel):
        """Update and save payment account and CFS account model."""
        # pylint:disable=cyclic-import, import-outside-toplevel
        from pay_api.factory.payment_system_factory import PaymentSystemFactory
        # If the payment method is CC, set the payment_method as DIRECT_PAY
        payment_method: str = get_str_by_path(account_request,
                                              'paymentInfo/methodOfPayment')
        if not payment_method or payment_method == PaymentMethod.CC.value:
            payment_method = PaymentMethod.DIRECT_PAY.value

        payment_account.payment_method = payment_method
        payment_account.auth_account_id = account_request.get('accountId')
        payment_account.auth_account_name = account_request.get(
            'accountName', None)
        payment_account.bcol_account = account_request.get(
            'bcolAccountNumber', None)
        payment_account.bcol_user_id = account_request.get('bcolUserId', None)
        payment_account.pad_tos_accepted_by = account_request.get(
            'padTosAcceptedBy', None)
        payment_account.pad_tos_accepted_date = datetime.now()

        payment_info = account_request.get('paymentInfo')
        billable = payment_info.get('billable', True)
        payment_account.billable = billable

        # Steps to decide on creating CFS Account or updating CFS bank account.
        # Updating CFS account apart from bank details not in scope now.
        # Create CFS Account IF:
        # 1. New payment account
        # 2. Existing payment account:
        # -  If the account was on DIRECT_PAY and switching to Online Banking, and active CFS account is not present.
        # -  If the account was on DRAWDOWN and switching to PAD, and active CFS account is not present
        cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) \
            if payment_account.id else None
        pay_system = PaymentSystemFactory.create_from_payment_method(
            payment_method=payment_method)
        if pay_system.get_payment_system_code() == PaymentSystem.PAYBC.value:
            if cfs_account is None:
                cfs_account = pay_system.create_account(
                    name=payment_account.auth_account_name,
                    contact_info=account_request.get('contactInfo'),
                    payment_info=account_request.get('paymentInfo'))
                if cfs_account:
                    cfs_account.payment_account = payment_account
                    cfs_account.flush()
            # If the account is PAD and bank details changed, then update bank details
            else:
                # Update details in CFS
                pay_system.update_account(
                    name=payment_account.auth_account_name,
                    cfs_account=cfs_account,
                    payment_info=payment_info)

        elif cfs_account is not None:
            # if its not PAYBC ,it means switching to either drawdown or internal ,deactivate the cfs account
            cfs_account.status = CfsAccountStatus.INACTIVE.value
            cfs_account.flush()

        is_pad = payment_method == PaymentMethod.PAD.value
        if is_pad:
            # override payment method for since pad has 3 days wait period
            effective_pay_method, activation_date = PaymentAccount._get_payment_based_on_pad_activation(
                payment_account)
            payment_account.pad_activation_date = activation_date
            payment_account.payment_method = effective_pay_method

        payment_account.save()
Exemplo n.º 21
0
    def _save_account(cls, account_request: Dict[str, any],
                      payment_account: PaymentAccountModel):
        """Update and save payment account and CFS account model."""
        # pylint:disable=cyclic-import, import-outside-toplevel
        from pay_api.factory.payment_system_factory import PaymentSystemFactory

        # If the payment method is CC, set the payment_method as DIRECT_PAY
        payment_method: str = get_str_by_path(account_request,
                                              'paymentInfo/methodOfPayment')
        if not payment_method or payment_method == PaymentMethod.CC.value:
            payment_method = PaymentMethod.DIRECT_PAY.value

        payment_account.payment_method = payment_method
        payment_account.auth_account_id = account_request.get('accountId')
        payment_account.auth_account_name = account_request.get(
            'accountName', None)
        payment_account.bcol_account = account_request.get(
            'bcolAccountNumber', None)
        payment_account.bcol_user_id = account_request.get('bcolUserId', None)
        payment_account.pad_tos_accepted_by = account_request.get(
            'padTosAcceptedBy', None)
        if payment_account.pad_tos_accepted_by is not None:
            payment_account.pad_tos_accepted_date = datetime.now()

        payment_info = account_request.get('paymentInfo')
        billable = payment_info.get('billable', True)
        payment_account.billable = billable
        payment_account.flush()

        # Steps to decide on creating CFS Account or updating CFS bank account.
        # Updating CFS account apart from bank details not in scope now.
        # Create CFS Account IF:
        # 1. New payment account
        # 2. Existing payment account:
        # -  If the account was on DIRECT_PAY and switching to Online Banking, and active CFS account is not present.
        # -  If the account was on DRAWDOWN and switching to PAD, and active CFS account is not present
        cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(payment_account.id) \
            if payment_account.id else None
        pay_system = PaymentSystemFactory.create_from_payment_method(
            payment_method=payment_method)
        if pay_system.get_payment_system_code() == PaymentSystem.PAYBC.value:
            if cfs_account is None:
                cfs_account = pay_system.create_account(
                    name=payment_account.auth_account_name,
                    contact_info=account_request.get('contactInfo'),
                    payment_info=account_request.get('paymentInfo'))
                if cfs_account:
                    cfs_account.payment_account = payment_account
                    cfs_account.flush()
            # If the account is PAD and bank details changed, then update bank details
            else:
                # Update details in CFS
                pay_system.update_account(
                    name=payment_account.auth_account_name,
                    cfs_account=cfs_account,
                    payment_info=payment_info)
            is_pad = payment_method == PaymentMethod.PAD.value
            if is_pad:
                # override payment method for since pad has 3 days wait period
                effective_pay_method, activation_date = PaymentAccount._get_payment_based_on_pad_activation(
                    payment_account)
                payment_account.pad_activation_date = activation_date
                payment_account.payment_method = effective_pay_method

        elif pay_system.get_payment_system_code() == PaymentSystem.CGI.value:
            # if distribution code exists, put an end date as previous day and create new.
            dist_code_svc: DistributionCode = DistributionCode.find_active_by_account_id(
                payment_account.id)
            if dist_code_svc and dist_code_svc.distribution_code_id:
                end_date: datetime = datetime.now() - timedelta(days=1)
                dist_code_svc.end_date = end_date.date()
                dist_code_svc.save()

            # Create distribution code details.
            if revenue_account := payment_info.get('revenueAccount'):
                revenue_account.update(
                    dict(
                        accountId=payment_account.id,
                        name=payment_account.auth_account_name,
                    ))
                DistributionCode.save_or_update(revenue_account)
Exemplo n.º 22
0
    def create_payment(cls, payment_request: Tuple[Dict[str, Any]],
                       authorization: Tuple[Dict[str, Any]]):
        # pylint: disable=too-many-locals, too-many-statements
        """Create payment related records.

        Does the following;
        1. Calculate the fees based on the filing types received.
        2. Check if the payment account exists,
            2.1 If yes, use the one from database.
            2.2 Else create one in payment system and update database.
        3. Create payment record in database and flush.
        4. Create invoice record in database and flush.
        5. Create payment line items in database and flush.
        6. Create invoice in payment system;
            6.1 If successful update the invoice table with references from payment system.
                6.1.1 If failed adjust the invoice to zero and roll back the transaction.
            6.2 If fails rollback the transaction
        """
        current_app.logger.debug('<create_payment')
        business_info = payment_request.get('businessInfo')
        contact_info = business_info.get('contactInfo')
        filing_info = payment_request.get('filingInfo')
        account_info = payment_request.get('accountInfo', None)
        filing_id = filing_info.get('filingIdentifier', None)
        folio_number = filing_info.get(
            'folioNumber',
            get_str_by_path(authorization, 'business/folioNumber'))

        corp_type = business_info.get('corpType', None)
        payment_method = _get_payment_method(payment_request, authorization)

        # Calculate the fees
        current_app.logger.debug('Calculate the fees')
        fees = _calculate_fees(corp_type, filing_info)

        # Create payment system instance from factory
        current_app.logger.debug('Creating PaymentSystemService impl')
        pay_service: PaymentSystemService = PaymentSystemFactory.create(
            payment_method=payment_method,
            corp_type=corp_type,
            fees=sum(fee.total for fee in fees),
            account_info=account_info)

        # Create payment account
        payment_account = _create_account(pay_service, business_info,
                                          contact_info, account_info,
                                          authorization)
        payment: Payment = None
        pay_system_invoice: Dict[str, any] = None

        try:
            payment: Payment = Payment.create(
                payment_method, pay_service.get_payment_system_code())

            current_app.logger.debug(
                'Creating Invoice record for payment {}'.format(payment.id))
            invoice = Invoice.create(
                payment_account,
                payment.id,
                fees,
                corp_type,
                routing_slip=get_str_by_path(account_info, 'routingSlip'),
                dat_number=get_str_by_path(account_info, 'datNumber'),
                filing_id=filing_id,
                folio_number=folio_number,
                business_identifier=business_info.get('businessIdentifier'))

            line_items = []
            for fee in fees:
                current_app.logger.debug('Creating line items')
                line_items.append(PaymentLineItem.create(invoice.id, fee))
            current_app.logger.debug(
                'Handing off to payment system to create invoice')
            pay_system_invoice = pay_service.create_invoice(
                payment_account,
                line_items,
                invoice,
                corp_type_code=invoice.corp_type_code)

            current_app.logger.debug('Updating invoice record')
            invoice = Invoice.find_by_id(invoice.id, skip_auth_check=True)
            invoice.invoice_status_code = InvoiceStatus.CREATED.value
            invoice.save()
            InvoiceReference.create(
                invoice.id, pay_system_invoice.get('invoice_number', None),
                pay_system_invoice.get('reference_number', None))

            payment.commit()
            _complete_post_payment(pay_service, payment)
            payment = Payment.find_by_id(payment.id, skip_auth_check=True)

        except Exception as e:
            current_app.logger.error('Rolling back as error occured!')
            current_app.logger.error(e)
            if payment:
                payment.rollback()
            if pay_system_invoice:
                pay_service.cancel_invoice(
                    payment_account,
                    pay_system_invoice.get('invoice_number'),
                )
            raise

        current_app.logger.debug('>create_payment')

        return payment.asdict()
Exemplo n.º 23
0
    def update_invoice(cls,
                       invoice_id: int,
                       payment_request: Tuple[Dict[str, Any]],
                       is_apply_credit: bool = False):
        """Update invoice related records."""
        current_app.logger.debug('<update_invoice')

        invoice: Invoice = Invoice.find_by_id(invoice_id,
                                              skip_auth_check=False)
        # If the call is to apply credit, apply credit and release records.
        if is_apply_credit:
            credit_balance: float = 0
            payment_account: PaymentAccount = PaymentAccount.find_by_id(
                invoice.payment_account_id)
            invoice_balance = invoice.total - (invoice.paid or 0)
            if (payment_account.credit or 0) >= invoice_balance:
                pay_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
                    invoice.payment_method_code)
                # Only release records, as the actual status change should happen during reconciliation.
                pay_service.apply_credit(invoice)
                credit_balance = payment_account.credit - invoice_balance
                invoice.paid = invoice.total
                invoice.save()
            elif (payment_account.credit or 0) <= invoice_balance:
                invoice.paid = (invoice.paid or 0) + payment_account.credit
                invoice.save()

            payment_account.credit = credit_balance
            payment_account.save()
        else:
            payment_method = get_str_by_path(payment_request,
                                             'paymentInfo/methodOfPayment')

            is_not_currently_on_ob = invoice.payment_method_code != PaymentMethod.ONLINE_BANKING.value
            is_not_changing_to_cc = payment_method not in (
                PaymentMethod.CC.value, PaymentMethod.DIRECT_PAY.value)
            # can patch only if the current payment method is OB
            if is_not_currently_on_ob or is_not_changing_to_cc:
                raise BusinessException(Error.INVALID_REQUEST)

            # check if it has any invoice references already created
            # if there is any invoice ref , send them to the invoiced credit card flow

            invoice_reference = InvoiceReference.find_active_reference_by_invoice_id(
                invoice.id)
            if invoice_reference:
                invoice.payment_method_code = PaymentMethod.CC.value
            else:
                pay_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
                    PaymentMethod.DIRECT_PAY.value)
                payment_account = PaymentAccount.find_by_id(
                    invoice.payment_account_id)
                pay_service.create_invoice(
                    payment_account,
                    invoice.payment_line_items,
                    invoice,
                    corp_type_code=invoice.corp_type_code)

                invoice.payment_method_code = PaymentMethod.DIRECT_PAY.value
            invoice.save()
        current_app.logger.debug('>update_invoice')
        return invoice.asdict()
Exemplo n.º 24
0
    def create_invoice(cls, payment_request: Tuple[Dict[str, Any]],
                       authorization: Tuple[Dict[str, Any]]) -> Dict:
        # pylint: disable=too-many-locals, too-many-statements
        """Create payment related records.

        Does the following;
        1. Calculate the fees based on the filing types received.
        2. Check if the payment account exists,
            2.1 If yes, use the one from database.
            2.2 Else create one in payment system and update database.
        3. Create payment record in database and flush.
        4. Create invoice record in database and flush.
        5. Create payment line items in database and flush.
        6. Create invoice in payment system;
            6.1 If successful update the invoice table with references from payment system.
                6.1.1 If failed adjust the invoice to zero and roll back the transaction.
            6.2 If fails rollback the transaction
        """
        current_app.logger.debug('<create_invoice', payment_request)
        business_info = payment_request.get('businessInfo')
        filing_info = payment_request.get('filingInfo')
        account_info = payment_request.get('accountInfo', None)
        filing_id = filing_info.get('filingIdentifier', None)
        folio_number = filing_info.get(
            'folioNumber',
            get_str_by_path(authorization, 'business/folioNumber'))
        corp_type = business_info.get('corpType', None)

        payment_account = cls._find_payment_account(authorization)

        payment_method = _get_payment_method(payment_request, payment_account)

        bcol_account = cls._get_bcol_account(account_info, payment_account)

        # Calculate the fees
        current_app.logger.debug('Calculate the fees')
        fees = _calculate_fees(corp_type, filing_info)

        # Create payment system instance from factory
        current_app.logger.debug('Creating PaymentSystemService impl')
        pay_service: PaymentSystemService = PaymentSystemFactory.create(
            payment_method=payment_method,
            corp_type=corp_type,
            fees=sum(fee.total for fee in fees),
            account_info=account_info,
            payment_account=payment_account)

        pay_system_invoice: Dict[str, any] = None
        invoice: Invoice = None

        try:
            current_app.logger.debug('Creating Invoice record')
            invoice = Invoice()
            invoice.bcol_account = bcol_account
            invoice.payment_account_id = payment_account.id
            invoice.cfs_account_id = payment_account.cfs_account_id
            invoice.invoice_status_code = pay_service.get_default_invoice_status(
            )
            #   TODO Change based on decision, whether to apply service fees for each line or not.
            #   For now add up the service fee on each fee schedule
            invoice.service_fees = sum(fee.service_fees
                                       for fee in fees) if fees else 0
            invoice.total = sum(fee.total for fee in fees) if fees else 0
            invoice.paid = 0
            invoice.refund = 0
            invoice.routing_slip = get_str_by_path(account_info, 'routingSlip')
            invoice.filing_id = filing_id
            invoice.dat_number = get_str_by_path(account_info, 'datNumber')
            invoice.folio_number = folio_number
            invoice.business_identifier = business_info.get(
                'businessIdentifier')
            invoice.payment_method_code = pay_service.get_payment_method_code()
            invoice.corp_type_code = corp_type
            invoice = invoice.flush()

            line_items = []
            for fee in fees:
                current_app.logger.debug('Creating line items')
                line_items.append(PaymentLineItem.create(invoice.id, fee))

            current_app.logger.debug(
                'Handing off to payment system to create invoice')
            invoice_reference = pay_service.create_invoice(
                payment_account,
                line_items,
                invoice,
                corp_type_code=invoice.corp_type_code)

            current_app.logger.debug('Updating invoice record')

            invoice.commit()

            pay_service.complete_post_invoice(invoice, invoice_reference)

            invoice = Invoice.find_by_id(invoice.id, skip_auth_check=True)

        except Exception as e:  # NOQA pylint: disable=broad-except
            current_app.logger.error('Rolling back as error occured!')
            current_app.logger.error(e)
            if invoice:
                invoice.rollback()
            if pay_system_invoice:
                pay_service.cancel_invoice(
                    payment_account,
                    pay_system_invoice.get('invoice_number'),
                )
            raise

        current_app.logger.debug('>create_invoice')

        return invoice.asdict(include_dynamic_fields=True)