def test_paybc_system_factory(session, public_user_mock): """Assert a paybc service is returned.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case # Test for CC and CP instance = PaymentSystemFactory.create(payment_method='CC', corp_type='CP') assert isinstance(instance, PaybcService) assert isinstance(instance, PaymentSystemService) # Test for CC and CP with zero fees instance = PaymentSystemFactory.create(fees=0, payment_method='CC', corp_type='CP') assert isinstance(instance, InternalPayService) assert isinstance(instance, PaymentSystemService) # Test for PAYBC Service instance = PaymentSystemFactory.create_from_system_code( PaymentSystem.PAYBC.value) assert isinstance(instance, PaybcService) assert isinstance(instance, PaymentSystemService) # Test for Internal Service instance = PaymentSystemFactory.create_from_system_code( PaymentSystem.INTERNAL.value) assert isinstance(instance, InternalPayService) assert isinstance(instance, PaymentSystemService) # Test for BCOL Service instance = PaymentSystemFactory.create_from_system_code( PaymentSystem.BCOL.value) assert isinstance(instance, BcolService) assert isinstance(instance, PaymentSystemService)
def test_paybc_system_factory(session, public_user_mock): """Assert a paybc service is returned.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case # Test for CC and CP instance = PaymentSystemFactory.create(payment_method='CC', corp_type='CP') assert isinstance(instance, PaybcService) assert isinstance(instance, PaymentSystemService) # Test for CC and CP instance = PaymentSystemFactory.create( payment_method=PaymentMethod.DIRECT_PAY.value, corp_type='CP') assert isinstance(instance, DirectPayService) assert isinstance(instance, PaymentSystemService) # Test for CC and CP with zero fees instance = PaymentSystemFactory.create(fees=0, payment_method='CC', corp_type='CP') assert isinstance(instance, InternalPayService) assert isinstance(instance, PaymentSystemService) # Test for PAYBC Service instance = PaymentSystemFactory.create_from_payment_method( PaymentMethod.CC.value) assert isinstance(instance, PaybcService) assert isinstance(instance, PaymentSystemService) # Test for Direct Pay Service instance = PaymentSystemFactory.create_from_payment_method( PaymentMethod.DIRECT_PAY.value) assert isinstance(instance, DirectPayService) assert isinstance(instance, PaymentSystemService) # Test for Internal Service instance = PaymentSystemFactory.create_from_payment_method( PaymentMethod.INTERNAL.value) assert isinstance(instance, InternalPayService) assert isinstance(instance, PaymentSystemService) # Test for BCOL Service instance = PaymentSystemFactory.create_from_payment_method( PaymentMethod.DRAWDOWN.value) assert isinstance(instance, BcolService) assert isinstance(instance, PaymentSystemService) # Test for EFT Service instance = PaymentSystemFactory.create_from_payment_method( PaymentMethod.EFT.value) assert isinstance(instance, EftService) assert isinstance(instance, DepositService) assert isinstance(instance, PaymentSystemService) # Test for WIRE Service instance = PaymentSystemFactory.create_from_payment_method( PaymentMethod.WIRE.value) assert isinstance(instance, WireService) assert isinstance(instance, DepositService) assert isinstance(instance, PaymentSystemService)
def test_bcol_factory_for_system(session, system_user_mock): """Test payment system creation for BCOL payment instances.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case # Try a DRAWDOWN for system user instance = PaymentSystemFactory.create(payment_method='DRAWDOWN') assert isinstance(instance, BcolService) # Create with not specifying a payment_method instance = PaymentSystemFactory.create(account_info={'bcolAccountNumber': '10000'}) assert isinstance(instance, BcolService)
def test_invalid_pay_system(session, public_user_mock): """Test invalid data.""" from pay_api.exceptions import BusinessException from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create(payment_method=None, corp_type=None) assert excinfo.value.code == Error.INVALID_CORP_OR_FILING_TYPE.name with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create(payment_method='XXX', corp_type='XXX') assert excinfo.value.code == Error.INVALID_CORP_OR_FILING_TYPE.name
def test_invalid_pay_system(session): """Test invalid data.""" from pay_api.exceptions import BusinessException with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create(None, None) assert excinfo.value.status == Error.PAY003.status assert excinfo.value.message == Error.PAY003.message assert excinfo.value.code == Error.PAY003.name with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create('XXX', 'XXX') assert excinfo.value.status == Error.PAY003.status assert excinfo.value.message == Error.PAY003.message assert excinfo.value.code == Error.PAY003.name
def test_pad_factory_for_system_fails(session, system_user_mock): """Test payment system creation for PAD payment instances.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case from pay_api.exceptions import BusinessException pad_account = PaymentAccountService.create(get_unlinked_pad_account_payload()) # Try a DRAWDOWN for system user with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create(payment_method='PAD', payment_account=pad_account) assert excinfo.value.code == Error.ACCOUNT_IN_PAD_CONFIRMATION_PERIOD.name time_delay = current_app.config['PAD_CONFIRMATION_PERIOD_IN_DAYS'] with freeze_time(datetime.today() + timedelta(days=time_delay + 1, minutes=1)): instance = PaymentSystemFactory.create(payment_method='PAD', payment_account=pad_account) assert isinstance(instance, PadService)
def test_bcol_factory_for_public(session, public_user_mock): """Test payment system creation for BCOL payment instances.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case instance = PaymentSystemFactory.create(payment_method='DRAWDOWN') assert isinstance(instance, BcolService) assert isinstance(instance, PaymentSystemService)
def test_internal_staff_factory(session, staff_user_mock): """Test payment system creation for staff users.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case # Test for CC and CP with staff role instance = PaymentSystemFactory.create(payment_method='CC') assert isinstance(instance, InternalPayService) assert isinstance(instance, PaymentSystemService)
def build_pay_system_url(payment: Payment, transaction_id: uuid): """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( payment_system=payment.payment_system_code) invoice = InvoiceModel.find_by_payment_id(payment.id) pay_web_transaction_url = current_app.config.get( 'AUTH_WEB_PAY_TRANSACTION_URL') return_url = f'{pay_web_transaction_url}/returnpayment/{payment.id}/transaction/{transaction_id}' current_app.logger.debug('>build_pay_system_url') return pay_system_service.get_payment_system_url( Invoice.populate(invoice), return_url)
def test_invalid_pay_system(session, public_user_mock): """Test invalid data.""" from pay_api.factory.payment_system_factory import PaymentSystemFactory # noqa I001; errors out the test case from pay_api.exceptions import BusinessException with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create(payment_method=None, corp_type=None) assert excinfo.value.status == Error.PAY003.status assert excinfo.value.message == Error.PAY003.message assert excinfo.value.code == Error.PAY003.name with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create(payment_method='XXX', corp_type='XXX') assert excinfo.value.status == Error.PAY003.status assert excinfo.value.message == Error.PAY003.message assert excinfo.value.code == Error.PAY003.name with pytest.raises(BusinessException) as excinfo: PaymentSystemFactory.create_from_system_code('XXX') assert excinfo.value.status == Error.PAY003.status assert excinfo.value.message == Error.PAY003.message assert excinfo.value.code == Error.PAY003.name
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
def test_paybc_system_factory(session): """Assert a paybc service is returned.""" instance = PaymentSystemFactory.create('CC', 'CP') assert isinstance(instance, PaybcService) assert isinstance(instance, PaymentSystemService)
def update_payment(cls, payment_id: int, payment_request: Tuple[Dict[str, Any]], current_user: str = None): # pylint: disable=too-many-locals,too-many-statements """Update payment related records. Does the following; 1. Calculate the fees based on the filing types received. 2. Check if the payment account exists, 3.1 If yes, use the one from database. 3.2 Else create one in payment system and update database. 3. Check PayBC invoice status 1.1 If payment completed, do not update the payment, 1.2 Else continue the process. 4. Get invoice record in database. 5. Invalidate old payment line items and create new payment line items in database and flush. 6. Update 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 7. Update payment record in database and flush. """ current_app.logger.debug('<update_payment') payment_info = payment_request.get('paymentInfo') business_info = payment_request.get('businessInfo') filing_info = payment_request.get('filingInfo') current_app.logger.debug('Creating PaymentSystemService impl') pay_service: PaymentSystemService = PaymentSystemFactory.create( payment_info.get('methodOfPayment', None), business_info.get('corpType', None) ) current_app.logger.debug('Calculate the fees') # Calculate the fees fees = [] for filing_type_info in filing_info.get('filingTypes'): current_app.logger.debug('Getting fees for {} '.format(filing_type_info.get('filingTypeCode'))) fee: FeeSchedule = FeeSchedule.find_by_corp_type_and_filing_type( corp_type=business_info.get('corpType', None), filing_type_code=filing_type_info.get('filingTypeCode', None), valid_date=filing_info.get('date', None), jurisdiction=None, priority=filing_info.get('priority', None), ) if filing_type_info.get('filingDescription'): fee.description = filing_type_info.get('filingDescription') fees.append(fee) current_app.logger.debug('Check if payment account exists') payment: Payment = None try: # get existing payment transaction transaction: PaymentTransaction = PaymentTransaction.find_active_by_payment_id(payment_id) current_app.logger.debug(transaction) if transaction: # check existing payment status in PayBC; PaymentTransaction.update_transaction(payment_id, transaction.id, None) # update transaction function will update the status from PayBC payment: Payment = Payment.find_by_id(payment_id) current_app.logger.debug(payment) if payment.payment_status_code == Status.COMPLETED.value: raise BusinessException(Error.PAY010) if payment.payment_status_code == Status.CANCELLED.value: raise BusinessException(Error.PAY011) current_app.logger.debug('Updating Invoice record for payment {}'.format(payment.id)) invoices = payment.invoices for invoice in invoices: if invoice.invoice_status_code in (Status.DRAFT.value, Status.CREATED.value, Status.PARTIAL.value): payment_line_items = invoice.payment_line_items # Invalidate active payment line items for payment_line_item in payment_line_items: if payment_line_item.line_item_status_code != Status.CANCELLED.value: payment_line_item.line_item_status_code = Status.CANCELLED.value payment_line_item.save() # add new payment line item(s) 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 update invoice') payment_account: PaymentAccount = PaymentAccount.find_by_id(invoice.account_id) # update invoice pay_service.update_invoice( (payment_account.party_number, payment_account.account_number, payment_account.site_number), invoice.invoice_number, ) current_app.logger.debug('Updating invoice record') invoice = Invoice.find_by_id(invoice.id) invoice.updated_on = datetime.now() invoice.updated_by = current_user invoice.total = sum(fee.total for fee in fees) invoice.save() payment.updated_on = datetime.now() payment.updated_by = current_user payment.save() payment.commit() # return payment with updated contents payment = Payment.find_by_id(payment.id) except Exception as e: current_app.logger.error('Rolling back as error occurred!') current_app.logger.error(e) if payment: payment.rollback() raise current_app.logger.debug('>update_payment') return payment.asdict()
def create_payment(cls, payment_request: Tuple[Dict[str, Any]], current_user: str = None): # 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') payment_info = payment_request.get('paymentInfo') business_info = payment_request.get('businessInfo') contact_info = business_info.get('contactInfo') filing_info = payment_request.get('filingInfo') current_app.logger.debug('Creating PaymentSystemService impl') pay_service: PaymentSystemService = PaymentSystemFactory.create( payment_info.get('methodOfPayment', None), business_info.get('corpType', None) ) current_app.logger.debug('Calculate the fees') # Calculate the fees fees = [] for filing_type_info in filing_info.get('filingTypes'): current_app.logger.debug('Getting fees for {} '.format(filing_type_info.get('filingTypeCode'))) fee: FeeSchedule = FeeSchedule.find_by_corp_type_and_filing_type( corp_type=business_info.get('corpType', None), filing_type_code=filing_type_info.get('filingTypeCode', None), valid_date=filing_info.get('date', None), jurisdiction=None, priority=filing_info.get('priority', None), ) if filing_type_info.get('filingDescription'): fee.description = filing_type_info.get('filingDescription') fees.append(fee) current_app.logger.debug('Check if payment account exists') payment_account: PaymentAccount = PaymentAccount.find_account( business_info.get('businessIdentifier', None), business_info.get('corpType', None), pay_service.get_payment_system_code(), ) if not payment_account.id: current_app.logger.debug('No payment account, creating new') party_number, account_number, site_number = pay_service.create_account( business_info.get('businessName'), contact_info ) payment_account = PaymentAccount.create( business_info, (account_number, party_number, site_number), pay_service.get_payment_system_code() ) current_app.logger.debug('Creating payment record for account : {}'.format(payment_account.id)) payment: Payment = None pay_system_invoice: Dict[str, any] = None try: payment: Payment = Payment.create(payment_info, current_user, pay_service.get_payment_system_code()) current_app.logger.debug(payment) current_app.logger.debug('Creating Invoice record for payment {}'.format(payment.id)) invoice = Invoice.create(payment_account, payment.id, fees, current_user) 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.id) current_app.logger.debug('Updating invoice record') invoice = Invoice.find_by_id(invoice.id) invoice.invoice_status_code = Status.CREATED.value invoice.reference_number = pay_system_invoice.get('reference_number', None) invoice.invoice_number = pay_system_invoice.get('invoice_number', None) invoice.save() payment.commit() payment = Payment.find_by_id(payment.id) 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.party_number, payment_account.account_number, payment_account.site_number), pay_system_invoice.get('invoice_number'), ) raise current_app.logger.debug('>create_payment') return payment.asdict()
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()
def update_payment(cls, payment_id: int, payment_request: Tuple[Dict[str, Any]], authorization: Tuple[Dict[str, Any]]): # pylint: disable=too-many-locals,too-many-statements """Update payment related records. Does the following; 1. Calculate the fees based on the filing types received. 2. Check if the payment account exists, 3.1 If yes, use the one from database. 3.2 Else create one in payment system and update database. 3. Check PayBC invoice status 1.1 If payment completed, do not update the payment, 1.2 Else continue the process. 4. Get invoice record in database. 5. Invalidate old payment line items and create new payment line items in database and flush. 6. Update 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 7. Update payment record in database and flush. """ current_app.logger.debug('<update_payment') business_info = payment_request.get('businessInfo') filing_info = payment_request.get('filingInfo') corp_type = business_info.get('corpType', None) payment_method = _get_payment_method(payment_request, authorization) current_app.logger.debug('Calculate the fees') # Calculate the fees fees = _calculate_fees(business_info.get('corpType'), filing_info) 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)) current_app.logger.debug('Check if payment account exists') payment: Payment = None try: # update transaction function will update the status from PayBC _update_active_transactions(payment_id) payment: Payment = Payment.find_by_id(payment_id, skip_auth_check=True) _check_if_payment_is_completed(payment) current_app.logger.debug( 'Updating Invoice record for payment {}'.format(payment.id)) invoices = payment.invoices for invoice in invoices: if invoice.invoice_status_code == InvoiceStatus.CREATED.value: payment_line_items = invoice.payment_line_items # Invalidate active payment line items for payment_line_item in payment_line_items: if payment_line_item.line_item_status_code != LineItemStatus.CANCELLED.value: payment_line_item.line_item_status_code = LineItemStatus.CANCELLED.value payment_line_item.save() # add new payment line item(s) 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 update invoice') # Mark the current active invoice reference as CANCELLED inv_number: str = None for reference in invoice.references: if reference.status_code == InvoiceReferenceStatus.ACTIVE.value: inv_number = reference.invoice_number reference.status_code = InvoiceReferenceStatus.CANCELLED.value reference.flush() # update invoice payment_account: PaymentAccount = 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) pay_system_invoice = pay_service.update_invoice( payment_account, line_items, invoice.id, inv_number, len(invoice.references), 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.total = sum(fee.total for fee in fees) invoice.save() InvoiceReference.create( invoice.id, pay_system_invoice.get('invoice_number', None), pay_system_invoice.get('reference_number', None)) payment.save() payment.commit() _complete_post_payment(pay_service, payment) # return payment with updated contents payment = Payment.find_by_id(payment.id, skip_auth_check=True) except Exception as e: current_app.logger.error('Rolling back as error occurred!') current_app.logger.error(e) if payment: payment.rollback() raise current_app.logger.debug('>update_payment') return payment.asdict()
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)
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