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)
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')
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
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
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
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
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
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
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
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)
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
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
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
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 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
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
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
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