diff --git a/jobs/payment-jobs/config.py b/jobs/payment-jobs/config.py index 938dc1bd9..ca57c2530 100644 --- a/jobs/payment-jobs/config.py +++ b/jobs/payment-jobs/config.py @@ -200,6 +200,8 @@ class _Config(object): # pylint: disable=too-few-public-methods EFT_HOLDING_GL = os.getenv("EFT_HOLDING_GL", "") EFT_TRANSFER_DESC = os.getenv("EFT_TRANSFER_DESC", "BCREGISTRIES {} {} EFT TRANSFER") EFT_OVERDUE_NOTIFY_EMAILS = os.getenv("EFT_OVERDUE_NOTIFY_EMAILS", "") + # Reverse Proxy secret + PAY_CONNECTOR_SECRET = os.getenv("PAY_CONNECTOR_SECRET", "") class DevConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/jobs/payment-jobs/tasks/direct_pay_automated_refund_task.py b/jobs/payment-jobs/tasks/direct_pay_automated_refund_task.py index f64e7891a..6003f9e4c 100644 --- a/jobs/payment-jobs/tasks/direct_pay_automated_refund_task.py +++ b/jobs/payment-jobs/tasks/direct_pay_automated_refund_task.py @@ -151,7 +151,13 @@ def _query_order_status(cls, invoice: Invoice): ) )[0] payment_url: str = f"{paybc_svc_base_url}/paybc/payment/{paybc_ref_number}/{completed_reference.invoice_number}" - payment_response = OAuthService.get(payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON).json() + payment_response = OAuthService.get( + payment_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ).json() return payment_response @classmethod diff --git a/jobs/payment-jobs/tasks/distribution_task.py b/jobs/payment-jobs/tasks/distribution_task.py index 22cd224ce..53845a5fa 100644 --- a/jobs/payment-jobs/tasks/distribution_task.py +++ b/jobs/payment-jobs/tasks/distribution_task.py @@ -106,6 +106,7 @@ def update_revenue_lines(cls, invoice: InvoiceModel, payment_url: str, access_to AuthHeaderType.BEARER, ContentType.JSON, post_revenue_payload, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) @classmethod @@ -144,7 +145,13 @@ def generate_post_revenue_payload(cls, invoice: InvoiceModel): @classmethod def get_payment_details(cls, payment_url: str, access_token: str): """Get the receipt details by calling PayBC web service.""" - payment_response = OAuthService.get(payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON).json() + payment_response = OAuthService.get( + payment_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ).json() return payment_response @classmethod diff --git a/jobs/payment-jobs/tests/jobs/mocks.py b/jobs/payment-jobs/tests/jobs/mocks.py index b2d2fdc2f..9ea2ffea5 100644 --- a/jobs/payment-jobs/tests/jobs/mocks.py +++ b/jobs/payment-jobs/tests/jobs/mocks.py @@ -27,7 +27,7 @@ def paybc_token_response(cls, *args): # pylint: disable=unused-argument; mocks ) -def refund_payload_response(cls, *args): # pylint: disable=unused-argument; mocks of library methods +def refund_payload_response(cls, *args, **kwargs): # pylint: disable=unused-argument; mocks of library methods """Mock refund payload response.""" return Mock( status_code=201, diff --git a/pay-api/src/pay_api/config.py b/pay-api/src/pay_api/config.py index c72837f91..9e9e969ad 100755 --- a/pay-api/src/pay_api/config.py +++ b/pay-api/src/pay_api/config.py @@ -199,6 +199,9 @@ class _Config: # pylint: disable=too-few-public-methods # Used for DEV/TEST/SANDBOX only. If True, will skip payment and return success and send queue message. ALLOW_SKIP_PAYMENT = os.getenv("ALLOW_SKIP_PAYMENT", "False").lower() == "true" + # Reverse Proxy secret + PAY_CONNECTOR_SECRET = os.getenv("PAY_CONNECTOR_SECRET", "") + TESTING = False DEBUG = True diff --git a/pay-api/src/pay_api/models/credit.py b/pay-api/src/pay_api/models/credit.py index 420413a2e..af4167b3d 100644 --- a/pay-api/src/pay_api/models/credit.py +++ b/pay-api/src/pay_api/models/credit.py @@ -72,11 +72,13 @@ def find_by_cfs_identifier(cls, cfs_identifier: str, credit_memo: bool = False): @classmethod def find_remaining_by_account_id(cls, account_id: int) -> Decimal: """Find Credit by account id.""" - return Decimal(str( - cls.query.with_entities(func.coalesce(func.sum(Credit.remaining_amount), 0)) - .filter_by(account_id=account_id) - .scalar() - )) + return Decimal( + str( + cls.query.with_entities(func.coalesce(func.sum(Credit.remaining_amount), 0)) + .filter_by(account_id=account_id) + .scalar() + ) + ) class CreditSchema(ma.SQLAlchemyAutoSchema): # pylint: disable=too-many-ancestors diff --git a/pay-api/src/pay_api/resources/v1/account.py b/pay-api/src/pay_api/resources/v1/account.py index 2ccda50ac..b3feb5c08 100644 --- a/pay-api/src/pay_api/resources/v1/account.py +++ b/pay-api/src/pay_api/resources/v1/account.py @@ -259,7 +259,7 @@ def post_search_purchase_history(account_number: str): """Search purchase history.""" current_app.logger.info(" Dict[str, any]: f"{cfs_base}/cfs/parties/{cfs_account.cfs_party}/accs/{cfs_account.cfs_account}/" f"sites/{cfs_account.cfs_site}/" ) - site_response = OAuthService.get(site_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) + site_response = OAuthService.get( + site_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) return site_response.json() @staticmethod @@ -119,6 +125,7 @@ def update_site_receipt_method(cfs_account: CfsAccountModel, receipt_method: str ContentType.JSON, pad_stop_payload, is_put=True, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) return site_update_response.json() @@ -145,6 +152,7 @@ def validate_bank_account(bank_details: Tuple[Dict[str, Any]]) -> Dict[str, str] ContentType.JSON, bank_details, raise_for_error=False, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) if bank_validation_response_obj.status_code in ( @@ -190,7 +198,14 @@ def _create_party(access_token: str = None, party_name: str = None): party_url = current_app.config.get("CFS_BASE_URL") + "/cfs/parties/" party: Dict[str, Any] = {"customer_name": party_name} - party_response = OAuthService.post(party_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, party) + party_response = OAuthService.post( + party_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + party, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) current_app.logger.debug(">Creating party Record") return party_response.json() @@ -214,7 +229,12 @@ def _create_paybc_account(access_token, party, is_fas: bool): } account_response = OAuthService.post( - account_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, account + account_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + account, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) current_app.logger.debug(">Creating CFS account") return account_response.json() @@ -254,13 +274,24 @@ def _create_site( # pylint: disable=too-many-arguments try: site_response = OAuthService.post( - site_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, site + site_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + site, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ).json() except HTTPError as e: # If the site creation fails with 400, query and return site if e.response.status_code == 400: site_response = ( - OAuthService.get(site_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) + OAuthService.get( + site_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) .json() .get("items")[0] ) @@ -305,6 +336,7 @@ def _save_bank_details( # pylint: disable=too-many-arguments AuthHeaderType.BEARER, ContentType.JSON, payment_details, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ).json() payment_details = { @@ -328,7 +360,13 @@ def get_invoice(cls, cfs_account: CfsAccountModel, inv_number: str): f"sites/{cfs_account.cfs_site}/invs/{inv_number}/" ) - invoice_response = CFSService.get(invoice_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) + invoice_response = CFSService.get( + invoice_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) return invoice_response.json() @classmethod @@ -348,7 +386,14 @@ def reverse_rs_receipt_in_cfs(cls, cfs_account, receipt_number, operation: Rever ), "reversal_comment": cls._build_reversal_comment(operation), } - return CFSService.post(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, payload) + return CFSService.post( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + payload, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) @classmethod def apply_receipt(cls, cfs_account: CfsAccountModel, receipt_number: str, invoice_number: str) -> Dict[str, any]: @@ -374,7 +419,14 @@ def _modify_rs_receipt_in_cfs(cls, cfs_account, invoice_number, receipt_number, payload = { "invoice_number": invoice_number, } - return CFSService.post(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, payload) + return CFSService.post( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + payload, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) @classmethod def update_bank_details( # pylint: disable=too-many-arguments @@ -413,6 +465,7 @@ def get_token(payment_system=PaymentSystem.PAYBC): AuthHeaderType.BASIC, ContentType.FORM_URL_ENCODED, data, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) current_app.logger.debug(">Getting token") return token_response @@ -451,6 +504,7 @@ def create_account_invoice( AuthHeaderType.BEARER, ContentType.JSON, invoice_payload, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) return invoice_response.json() @@ -566,7 +620,14 @@ def reverse_invoice(inv_number: str): cfs_base: str = current_app.config.get("CFS_BASE_URL") invoice_url = f"{cfs_base}/cfs/parties/invs/{inv_number}/creditbalance/" - CFSService.post(invoice_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, {}) + CFSService.post( + invoice_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + {}, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) @classmethod def add_nsf_adjustment(cls, cfs_account: CfsAccountModel, inv_number: str, amount: float): @@ -597,6 +658,7 @@ def add_nsf_adjustment(cls, cfs_account: CfsAccountModel, inv_number: str, amoun AuthHeaderType.BEARER, ContentType.JSON, adjustment, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) current_app.logger.debug(">Created CFS Invoice NSF Adjustment") @@ -647,6 +709,7 @@ def adjust_invoice( AuthHeaderType.BEARER, ContentType.JSON, adjustment, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) current_app.logger.debug(">Created Invoice Adjustment") @@ -685,7 +748,14 @@ def create_cfs_receipt( # pylint: disable=too-many-arguments "comments": "", } current_app.logger.debug(">create_cfs_receipt") - return CFSService.post(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, payload).json() + return CFSService.post( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + payload, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ).json() @classmethod def get_receipt(cls, cfs_account: CfsAccountModel, receipt_number: str) -> Dict[str, any]: @@ -699,7 +769,13 @@ def get_receipt(cls, cfs_account: CfsAccountModel, receipt_number: str) -> Dict[ ) current_app.logger.debug("Receipt URL %s", receipt_url) - receipt_response = cls.get(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) + receipt_response = cls.get( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) current_app.logger.debug(">Received receipt response") return receipt_response.json() @@ -716,7 +792,13 @@ def get_cms(cls, cfs_account: CfsAccountModel, cms_number: str) -> Dict[str, any ) current_app.logger.debug("CMS URL %s", cms_url) - cms_response = cls.get(cms_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) + cms_response = cls.get( + cms_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) current_app.logger.debug(">Received CMS response") return cms_response.json() @@ -745,7 +827,14 @@ def create_cms(cls, line_items: List[PaymentLineItemModel], cfs_account: CfsAcco "lines": cls.build_lines(line_items, negate=True), } - cms_response = CFSService.post(cms_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, cms_payload) + cms_response = CFSService.post( + cms_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + cms_payload, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) current_app.logger.debug(">Received CMS response") return cms_response.json() @@ -767,7 +856,13 @@ def adjust_receipt_to_zero(cls, cfs_account: CfsAccountModel, receipt_number: st adjustment_url = f"{receipt_url}adjustment" current_app.logger.debug("Receipt Adjustment URL %s", adjustment_url) - receipt_response = cls.get(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) + receipt_response = cls.get( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) current_app.logger.info(f"Balance on {receipt_number} - {receipt_response.json().get('unapplied_amount')}") if (unapplied_amount := float(receipt_response.json().get("unapplied_amount", 0))) > 0: adjustment = { @@ -781,8 +876,15 @@ def adjust_receipt_to_zero(cls, cfs_account: CfsAccountModel, receipt_number: st AuthHeaderType.BEARER, ContentType.JSON, adjustment, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ) + receipt_response = cls.get( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) - receipt_response = cls.get(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON) current_app.logger.info(f"Balance on {receipt_number} - {receipt_response.json().get('unapplied_amount')}") current_app.logger.debug(">adjust_receipt_to_zero") diff --git a/pay-api/src/pay_api/services/direct_pay_service.py b/pay-api/src/pay_api/services/direct_pay_service.py index eb9c17c85..cc9169ca4 100644 --- a/pay-api/src/pay_api/services/direct_pay_service.py +++ b/pay-api/src/pay_api/services/direct_pay_service.py @@ -343,6 +343,7 @@ def get_token(self): AuthHeaderType.BASIC, ContentType.FORM_URL_ENCODED, data, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ) current_app.logger.debug(">Getting token") return token_response @@ -454,7 +455,13 @@ def query_order_status( ) )[0] payment_url: str = f"{paybc_svc_base_url}/paybc/payment/{paybc_ref_number}/{inv_reference.invoice_number}" - payment_response = cls.get(payment_url, access_token, AuthHeaderType.BEARER, ContentType.JSON).json() + payment_response = cls.get( + payment_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ).json() return Converter().structure(payment_response, OrderStatus) @classmethod diff --git a/pay-api/src/pay_api/services/paybc_service.py b/pay-api/src/pay_api/services/paybc_service.py index c83afe9b6..7c387c4de 100644 --- a/pay-api/src/pay_api/services/paybc_service.py +++ b/pay-api/src/pay_api/services/paybc_service.py @@ -148,6 +148,7 @@ def get_receipt( AuthHeaderType.BEARER, ContentType.JSON, retry_on_failure=True, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, ).json() for receipt in receipts_response.get("items"): expanded_receipt = self._get_receipt_by_number(access_token, receipt_url, receipt.get("receipt_number")) @@ -179,7 +180,14 @@ def _get_receipt_by_number( ): """Get receipt details by receipt number.""" receipt_url = receipt_url + f"{receipt_number}/" - return self.get(receipt_url, access_token, AuthHeaderType.BEARER, ContentType.JSON, True).json() + return self.get( + receipt_url, + access_token, + AuthHeaderType.BEARER, + ContentType.JSON, + True, + additional_headers={"Pay-Connector": current_app.config.get("PAY_CONNECTOR_SECRET")}, + ).json() def process_cfs_refund( self, diff --git a/pay-api/tests/unit/api/test_partial_refund.py b/pay-api/tests/unit/api/test_partial_refund.py index 34b30d710..c01570d13 100644 --- a/pay-api/tests/unit/api/test_partial_refund.py +++ b/pay-api/tests/unit/api/test_partial_refund.py @@ -200,7 +200,7 @@ def test_refund_validation_for_disbursements(session, client, jwt, app, monkeypa inv_id = rv.json.get("id") invoice: InvoiceModel = InvoiceModel.find_by_id(inv_id) invoice.invoice_status_code = InvoiceStatus.PAID.value - invoice.corp_type_code = 'VS' + invoice.corp_type_code = "VS" invoice.save() token = jwt.create_jwt(get_claims(app_request=app, role=Role.SYSTEM.value), token_header)