예제 #1
0
    def build_pay_system_url(payment: Payment, transaction_id: uuid,
                             pay_return_url: str):
        """Build pay system url which will be used to redirect to the payment system."""
        current_app.logger.debug('<build_pay_system_url')
        pay_system_service: PaymentSystemService = PaymentSystemFactory.create_from_system_code(
            payment_system=payment.payment_system_code)
        invoice = InvoiceModel.find_by_payment_id(payment.id)
        invoice_reference = InvoiceReference.find_active_reference_by_invoice_id(
            invoice.id)
        return_url = f'{pay_return_url}/{payment.id}/transaction/{transaction_id}'

        current_app.logger.debug('>build_pay_system_url')
        return pay_system_service.get_payment_system_url(
            Invoice.populate(invoice), invoice_reference, return_url)
예제 #2
0
def test_invoice_get_invoices(session):
    """Assert that get_invoices works."""
    payment_account = factory_payment_account()
    payment = factory_payment()
    payment_account.save()
    payment.save()
    i = factory_invoice(payment_id=payment.id, account_id=payment_account.id)
    i.save()

    invoices = Invoice_service.get_invoices(payment.id)

    assert invoices is not None
    assert len(invoices.get('items')) == 1
    assert not invoices.get('items')[0].get('line_items')
예제 #3
0
def test_invoice_find_by_id(session):
    """Assert that the invoice is saved to the table."""
    payment_account = factory_payment_account()
    payment_account.save()
    i = factory_invoice(payment_account=payment_account)
    i.save()

    invoice = Invoice_service.find_by_id(i.id, skip_auth_check=True)

    assert invoice is not None
    assert invoice.id is not None
    assert invoice.invoice_status_code is not None
    assert invoice.refund is None
    assert invoice.payment_date is None
    assert invoice.total is not None
    assert invoice.paid is None
    assert not invoice.payment_line_items
예제 #4
0
    def asdict(self):
        """Return the payment as a python dict."""
        invoices = []
        for invoice in self._invoices:
            current_invoice = Invoice.populate(invoice)
            if current_invoice.invoice_status_code != Status.CANCELLED.value:
                invoices.append(current_invoice.asdict())

        d = {
            'id': self._id,
            'payment_system_code': self._payment_system_code,
            'payment_method_code': self._payment_method_code,
            'payment_status_code': self._payment_status_code,
            'payment_create_date': self._created_on,
            'payment_create_by': self._created_by,
            'payment_update_date': self._updated_on,
            'payment_update_by': self._updated_by,
            'payment_invoices': invoices
        }
        return d
예제 #5
0
    def post(invoice_id: int = None):
        """Update the payment method for an online banking ."""
        current_app.logger.info('<InvoiceReport.post for invoice : %s',
                                invoice_id)

        try:
            pdf, file_name = InvoiceService.create_invoice_pdf(invoice_id)
            response = Response(pdf, 201)
            response.headers.set('Content-Disposition',
                                 'attachment',
                                 filename=f'{file_name}.pdf')
            response.headers.set('Content-Type', 'application/pdf')
            response.headers.set('Access-Control-Expose-Headers',
                                 'Content-Disposition')
            return response

        except BusinessException as exception:
            return exception.response()
        current_app.logger.debug('>InvoiceReport.post')
        return jsonify(response), 200
예제 #6
0
    def create_transaction_for_invoice(
            invoice_id: int, request_json: Dict) -> PaymentTransaction:
        """Create transaction record for invoice, by creating a payment record if doesn't exist."""
        current_app.logger.debug('<create transaction')
        # Lookup invoice record
        invoice: Invoice = Invoice.find_by_id(invoice_id, skip_auth_check=True)
        if not invoice.id:
            raise BusinessException(Error.INVALID_INVOICE_ID)
        if invoice.payment_method_code == PaymentMethod.PAD.value:  # No transaction needed for PAD invoices.
            raise BusinessException(Error.INVALID_TRANSACTION)

        pay_system_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
            payment_method=invoice.payment_method_code)
        # Check if return url is valid
        PaymentTransaction._validate_redirect_url_and_throw_error(
            invoice.payment_method_code, request_json.get('clientSystemUrl'))

        # Check if there is a payment created. If not, create a payment record with status CREATED
        payment: Payment = Payment.find_payment_for_invoice(invoice_id)
        if not payment:
            # Transaction is against payment, so create a payment if not present.
            invoice_reference = InvoiceReference.find_active_reference_by_invoice_id(
                invoice.id)

            # Create a payment record
            payment = Payment.create(
                payment_method=pay_system_service.get_payment_method_code(),
                payment_system=pay_system_service.get_payment_system_code(),
                payment_status=pay_system_service.get_default_payment_status(),
                invoice_number=invoice_reference.invoice_number,
                invoice_amount=invoice.total,
                payment_account_id=invoice.payment_account_id)

        transaction = PaymentTransaction._create_transaction(payment,
                                                             request_json,
                                                             invoice=invoice)
        current_app.logger.debug('>create transaction')

        return transaction
예제 #7
0
def test_invoice_saved_from_new(session):
    """Assert that the invoice is saved to the table."""
    payment_account = factory_payment_account()
    payment = factory_payment()
    payment_account.save()
    payment.save()
    i = factory_invoice(payment_account=payment_account)
    i.save()
    fee_schedule = FeeSchedule.find_by_filing_type_and_corp_type('CP', 'OTANN')
    line = factory_payment_line_item(i.id, fee_schedule_id=fee_schedule.fee_schedule_id)
    line.save()
    invoice = Invoice_service.find_by_id(i.id, skip_auth_check=True)

    assert invoice is not None
    assert invoice.id is not None
    assert invoice.invoice_status_code is not None
    assert invoice.refund is None
    assert invoice.payment_date is None
    assert invoice.total is not None
    assert invoice.paid is None
    assert invoice.payment_line_items is not None
    assert invoice.folio_number is not None
    assert invoice.business_identifier is not None
예제 #8
0
def test_invoice_with_temproary_business_identifier(session):
    """Assert that the invoice dictionary is not include temproary business identifier."""
    payment_account = factory_payment_account()
    payment = factory_payment()
    payment_account.save()
    payment.save()
    i = factory_invoice(payment_account=payment_account, business_identifier='Tzxcasd')
    i.save()
    fee_schedule = FeeSchedule.find_by_filing_type_and_corp_type('CP', 'OTANN')
    line = factory_payment_line_item(i.id, fee_schedule_id=fee_schedule.fee_schedule_id)
    line.save()
    invoice = Invoice_service.find_by_id(i.id, skip_auth_check=True)
    assert invoice is not None
    assert invoice.id is not None
    assert invoice.invoice_status_code is not None
    assert invoice.refund is None
    assert invoice.payment_date is None
    assert invoice.total is not None
    assert invoice.paid is None
    assert invoice.payment_line_items is not None
    assert invoice.folio_number is not None
    assert invoice.business_identifier is not None
    invoice_dict = invoice.asdict()
    assert invoice_dict.get('business_identifier') is None
예제 #9
0
    def update_transaction(
            transaction_id: uuid,  # pylint: disable=too-many-locals
            pay_response_url: str):
        """Update transaction record.

        Does the following:
        1. Find the payment record with the id
        2. Find the invoice record using the payment identifier
        3. Call the pay system service and get the receipt details
        4. Save the receipt record
        5. Change the status of Invoice
        6. Change the status of Payment
        7. Update the transaction record
        """
        #  TODO for now assumption is this def will be called only for credit card, bcol and internal payments.
        #  When start to look into the PAD and Online Banking may need to refactor here
        transaction_dao: PaymentTransactionModel = PaymentTransactionModel.find_by_id(
            transaction_id)
        if not transaction_dao:
            raise BusinessException(Error.INVALID_TRANSACTION_ID)
        if transaction_dao.status_code == TransactionStatus.COMPLETED.value:
            raise BusinessException(Error.INVALID_TRANSACTION)

        payment: Payment = Payment.find_by_id(transaction_dao.payment_id)
        payment_account: PaymentAccount = PaymentAccount.find_by_id(
            payment.payment_account_id)

        # For transactions other than Credit Card, there could be more than one invoice per payment.
        invoices: [Invoice] = Invoice.find_invoices_for_payment(
            transaction_dao.payment_id)

        if payment.payment_status_code == PaymentStatus.COMPLETED.value:
            # if the transaction status is EVENT_FAILED then publish to queue and return, else raise error
            if transaction_dao.status_code == TransactionStatus.EVENT_FAILED.value:
                # Publish status to Queue
                for invoice in invoices:
                    PaymentTransaction.publish_status(transaction_dao, invoice)

                transaction_dao.status_code = TransactionStatus.COMPLETED.value
                return PaymentTransaction.__wrap_dao(transaction_dao.save())

            raise BusinessException(Error.COMPLETED_PAYMENT)

        pay_system_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
            payment_method=payment.payment_method_code)

        invoice_reference = InvoiceReference.find_any_active_reference_by_invoice_number(
            payment.invoice_number)
        try:
            receipt_details = pay_system_service.get_receipt(
                payment_account, pay_response_url, invoice_reference)
            txn_reason_code = None
        except ServiceUnavailableException as exc:
            txn_reason_code = exc.status
            transaction_dao.pay_system_reason_code = txn_reason_code
            receipt_details = None

        if receipt_details:
            PaymentTransaction._update_receipt_details(invoices, payment,
                                                       receipt_details,
                                                       transaction_dao)
        else:
            transaction_dao.status_code = TransactionStatus.FAILED.value

        # check if the pay_response_url contains any failure status
        if not txn_reason_code:
            pay_system_reason_code = pay_system_service.get_pay_system_reason_code(
                pay_response_url)
            transaction_dao.pay_system_reason_code = pay_system_reason_code

        # Save response URL
        transaction_dao.transaction_end_time = datetime.now()
        transaction_dao.pay_response_url = pay_response_url
        transaction_dao = transaction_dao.save()

        # Publish message to unlock account if account is locked.
        if payment.payment_status_code == PaymentStatus.COMPLETED.value:
            active_failed_payments = Payment.get_failed_payments(
                auth_account_id=payment_account.auth_account_id)
            current_app.logger.info('active_failed_payments %s',
                                    active_failed_payments)
            if not active_failed_payments:
                PaymentAccount.unlock_frozen_accounts(
                    payment.payment_account_id)

        transaction = PaymentTransaction.__wrap_dao(transaction_dao)

        current_app.logger.debug('>update_transaction')
        return transaction
예제 #10
0
    def update_failed_distributions(cls):  # pylint:disable=too-many-locals
        """Update failed distributions.

        Steps:
        1. Get all invoices with status UPDATE_REVENUE_ACCOUNT.
        2. Find the completed invoice reference for the invoice.
        3. Call the paybc GET service and check if there is any revenue not processed.
        4. If yes, update the revenue details.
        5. Update the invoice status as PAID and save.
        """
        gl_updated_invoices = InvoiceModel.query.filter_by(
            invoice_status_code=InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value).all()
        current_app.logger.debug(f'Found {len(gl_updated_invoices)} invoices to update revenue details.')

        if len(gl_updated_invoices) > 0:  # pylint:disable=too-many-nested-blocks
            access_token: str = cls._get_token().json().get('access_token')
            paybc_ref_number: str = current_app.config.get('PAYBC_DIRECT_PAY_REF_NUMBER')
            paybc_svc_base_url = current_app.config.get('PAYBC_DIRECT_PAY_BASE_URL')
            for gl_updated_invoice in gl_updated_invoices:
                payment: PaymentModel = PaymentModel.find_payment_for_invoice(gl_updated_invoice.id)
                # For now handle only GL updates for Direct Pay, more to come in future
                if payment.payment_method_code != PaymentMethod.DIRECT_PAY.value:
                    cls._update_invoice_status(gl_updated_invoice, InvoiceStatus.PAID.value)
                else:
                    active_reference = list(
                        filter(lambda reference: (reference.status_code == InvoiceReferenceStatus.COMPLETED.value),
                               gl_updated_invoice.references))[0]
                    payment_url: str = \
                        f'{paybc_svc_base_url}/paybc/payment/{paybc_ref_number}/{active_reference.invoice_number}'

                    payment_details: dict = cls.get_payment_details(payment_url, access_token)
                    if payment_details and payment_details.get('paymentstatus') == STATUS_PAID:
                        has_gl_completed: bool = True
                        for revenue in payment_details.get('revenue'):
                            if revenue.get('glstatus') in STATUS_NOT_PROCESSED:
                                has_gl_completed = False

                        if not has_gl_completed:
                            post_revenue_payload = {
                                'revenue': []
                            }

                            invoice: InvoiceService = InvoiceService.find_by_id(identifier=gl_updated_invoice.id,
                                                                                skip_auth_check=True)

                            payment_line_items = PaymentLineItemModel.find_by_invoice_ids([invoice.id])
                            index: int = 0

                            for payment_line_item in payment_line_items:
                                distribution_code = DistributionCodeModel.find_by_id(
                                    payment_line_item.fee_distribution_id)
                                index = index + 1
                                post_revenue_payload['revenue'].append(
                                    cls.get_revenue_details(index, distribution_code, payment_line_item.total))

                                if payment_line_item.service_fees is not None and payment_line_item.service_fees > 0:
                                    index = index + 1
                                    post_revenue_payload['revenue'].append(
                                        cls.get_revenue_details(index, distribution_code,
                                                                payment_line_item.service_fees,
                                                                is_service_fee=True))

                            OAuthService.post(payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON,
                                              post_revenue_payload)

                        cls._update_invoice_status(gl_updated_invoice, InvoiceStatus.PAID.value)
예제 #11
0
def test_invoice_invalid_lookup(session):
    """Test invalid lookup."""
    with pytest.raises(BusinessException) as excinfo:
        Invoice_service.find_by_id(999, skip_auth_check=True)
    assert excinfo.type == BusinessException
예제 #12
0
    def update_transaction(
            payment_identifier: int,
            transaction_id: uuid,  # pylint: disable=too-many-locals
            receipt_number: str,
            jwt: JwtManager = None,
            skip_auth_check: bool = False):
        """Update transaction record.

        Does the following:
        1. Find the payment record with the id
        2. Find the invoice record using the payment identifier
        3. Call the pay system service and get the receipt details
        4. Save the receipt record
        5. Change the status of Invoice
        6. Change the status of Payment
        7. Update the transaction record
        """
        transaction_dao: PaymentTransactionModel = PaymentTransactionModel.find_by_id_and_payment_id(
            transaction_id, payment_identifier)
        if not transaction_dao:
            raise BusinessException(Error.PAY008)
        if transaction_dao.status_code == Status.COMPLETED.value:
            raise BusinessException(Error.PAY006)

        payment: Payment = Payment.find_by_id(payment_identifier,
                                              jwt=jwt,
                                              one_of_roles=[EDIT_ROLE],
                                              skip_auth_check=skip_auth_check)

        pay_system_service: PaymentSystemService = PaymentSystemFactory.create(
            payment_system=payment.payment_system_code)

        invoice = Invoice.find_by_payment_identifier(payment_identifier,
                                                     jwt=jwt,
                                                     skip_auth_check=True)

        payment_account = PaymentAccount.find_by_id(invoice.account_id)

        try:
            receipt_details = pay_system_service.get_receipt(
                payment_account, receipt_number, invoice.invoice_number)
            txn_reason_code = None
        except ServiceUnavailableException as exc:
            txn_reason_code = exc.status_code
            receipt_details = None

        if receipt_details:
            # Find if a receipt exists with same receipt_number for the invoice
            receipt: Receipt = Receipt.find_by_invoice_id_and_receipt_number(
                invoice.id, receipt_details[0])
            if not receipt.id:
                receipt: Receipt = Receipt()
                receipt.receipt_number = receipt_details[0]
                receipt.receipt_date = receipt_details[1]
                receipt.receipt_amount = receipt_details[2]
                receipt.invoice_id = invoice.id
            else:
                receipt.receipt_date = receipt_details[1]
                receipt.receipt_amount = receipt_details[2]
            # Save receipt details to DB.
            receipt.save()

            invoice.paid = receipt.receipt_amount
            if invoice.paid == invoice.total:
                invoice.invoice_status_code = Status.COMPLETED.value
                payment.payment_status_code = Status.COMPLETED.value
                payment.save()
            elif 0 < invoice.paid < invoice.total:
                invoice.invoice_status_code = Status.PARTIAL.value
            invoice.save()
            transaction_dao.status_code = Status.COMPLETED.value
        else:
            transaction_dao.status_code = Status.FAILED.value

        transaction_dao.transaction_end_time = datetime.now()

        # Publish status to Queue
        PaymentTransaction.publish_status(transaction_dao, payment)

        transaction_dao = transaction_dao.save()

        transaction = PaymentTransaction()
        transaction._dao = transaction_dao  # pylint: disable=protected-access
        transaction.pay_system_reason_code = txn_reason_code

        current_app.logger.debug('>update_transaction')
        return transaction
예제 #13
0
    def update_transaction(payment_identifier: int, transaction_id: uuid,  # pylint: disable=too-many-locals
                           receipt_number: str):
        """Update transaction record.

        Does the following:
        1. Find the payment record with the id
        2. Find the invoice record using the payment identifier
        3. Call the pay system service and get the receipt details
        4. Save the receipt record
        5. Change the status of Invoice
        6. Change the status of Payment
        7. Update the transaction record
        """
        transaction_dao: PaymentTransactionModel = PaymentTransactionModel.find_by_id_and_payment_id(
            transaction_id, payment_identifier
        )
        if not transaction_dao:
            raise BusinessException(Error.INVALID_TRANSACTION_ID)
        if transaction_dao.status_code == TransactionStatus.COMPLETED.value:
            raise BusinessException(Error.INVALID_TRANSACTION)

        payment: Payment = Payment.find_by_id(payment_identifier, skip_auth_check=True)

        if payment.payment_status_code == PaymentStatus.COMPLETED.value:
            raise BusinessException(Error.COMPLETED_PAYMENT)

        pay_system_service: PaymentSystemService = PaymentSystemFactory.create_from_system_code(
            payment_system=payment.payment_system_code
        )

        invoice = Invoice.find_by_payment_identifier(payment_identifier, skip_auth_check=True)
        invoice_reference = InvoiceReference.find_active_reference_by_invoice_id(invoice.id)
        payment_account = PaymentAccount.find_by_pay_system_id(
            credit_account_id=invoice.credit_account_id,
            internal_account_id=invoice.internal_account_id,
            bcol_account_id=invoice.bcol_account_id)

        try:
            receipt_details = pay_system_service.get_receipt(payment_account, receipt_number, invoice_reference)
            txn_reason_code = None
        except ServiceUnavailableException as exc:
            txn_reason_code = exc.status
            receipt_details = None

        if receipt_details:
            # Find if a receipt exists with same receipt_number for the invoice
            receipt = PaymentTransaction.__save_receipt(invoice, receipt_details)

            invoice.paid = receipt.receipt_amount

            if invoice.paid == invoice.total:
                invoice.invoice_status_code = InvoiceStatus.PAID.value
                payment.payment_status_code = PaymentStatus.COMPLETED.value
                payment.save()

                invoice_reference.status_code = InvoiceReferenceStatus.COMPLETED.value
                invoice_reference.save()

            invoice.save()

            transaction_dao.status_code = TransactionStatus.COMPLETED.value
        else:
            transaction_dao.status_code = TransactionStatus.FAILED.value

        transaction_dao.transaction_end_time = datetime.now()

        # Publish status to Queue
        PaymentTransaction.publish_status(transaction_dao, payment, invoice.filing_id)

        transaction_dao = transaction_dao.save()

        transaction = PaymentTransaction()
        transaction._dao = transaction_dao  # pylint: disable=protected-access
        transaction.pay_system_reason_code = txn_reason_code

        current_app.logger.debug('>update_transaction')
        return transaction
예제 #14
0
    def update_transaction(payment_identifier: int, transaction_id: uuid,
                           receipt_number: str):
        """Update transaction record.

        Does the following:
        1. Find the payment record with the id
        2. Find the invoice record using the payment identifier
        3. Call the pay system service and get the receipt details
        4. Save the receipt record
        5. Change the status of Invoice
        6. Change the status of Payment
        7. Update the transaction record
        """
        transaction_dao: PaymentTransactionModel = PaymentTransactionModel.find_by_id_and_payment_id(
            transaction_id, payment_identifier)
        if not transaction_dao:
            raise BusinessException(Error.PAY008)
        if transaction_dao.status_code == Status.COMPLETED.value:
            raise BusinessException(Error.PAY006)

        payment: Payment = Payment.find_by_id(payment_identifier)

        pay_system_service: PaymentSystemService = PaymentSystemFactory.create(
            payment_system=payment.payment_system_code)

        invoice = Invoice.find_by_payment_identifier(payment_identifier)

        payment_account = PaymentAccount.find_by_id(invoice.account_id)

        receipt_details = pay_system_service.get_receipt(
            payment_account, receipt_number, invoice.invoice_number)
        if receipt_details:
            # Find if a receipt exists with same receipt_number for the invoice
            receipt: Receipt = Receipt.find_by_invoice_id_and_receipt_number(
                invoice.id, receipt_details[0])
            if not receipt.id:
                receipt: Receipt = Receipt()
                receipt.receipt_number = receipt_details[0]
                receipt.receipt_date = receipt_details[1]
                receipt.receipt_amount = receipt_details[2]
                receipt.invoice_id = invoice.id
            else:
                receipt.receipt_date = receipt_details[1]
                receipt.receipt_amount = receipt_details[2]
            # Save receipt details to DB.
            receipt.save()

            invoice.paid = receipt.receipt_amount
            if invoice.paid == invoice.total:
                invoice.invoice_status_code = Status.COMPLETED.value
                payment.payment_status_code = Status.COMPLETED.value
                payment.save()
            elif 0 < invoice.paid < invoice.total:
                invoice.invoice_status_code = Status.PARTIAL.value
            invoice.save()

        transaction_dao.transaction_end_time = datetime.now()
        transaction_dao.status_code = Status.COMPLETED.value
        transaction_dao = transaction_dao.save()

        transaction = PaymentTransaction()
        transaction._dao = transaction_dao  # pylint: disable=protected-access

        current_app.logger.debug('>update_transaction')
        return transaction
예제 #15
0
 def get_receipt(self, payment_account: PaymentAccount, receipt_number: str, invoice_reference: InvoiceReference):
     """Get receipt from bcol for the receipt number or get receipt against invoice number."""
     current_app.logger.debug('<get_receipt')
     invoice = Invoice.find_by_id(invoice_reference.invoice_id, skip_auth_check=True)
     return f'RCPT_{invoice_reference.invoice_number}', datetime.now(), invoice.total
예제 #16
0
def test_invoice_find_by_invalid_payment_id(session):
    """Test invalid lookup."""
    invoice = Invoice_service.find_by_payment_identifier(999)

    assert invoice is not None
    assert invoice.id is None
예제 #17
0
def test_invoice_invalid_lookup(session):
    """Test invalid lookup."""
    invoice = Invoice_service.find_by_id(999)

    assert invoice is not None
    assert invoice.id is None
예제 #18
0
 def get_receipt(self, payment_account: PaymentAccount, pay_response_url: str, invoice_reference: InvoiceReference):
     """Create a static receipt."""
     # Find the invoice using the invoice_number
     invoice = Invoice.find_by_id(invoice_reference.invoice_id, skip_auth_check=True)
     return f'{invoice_reference.invoice_number}', datetime.now(), invoice.total