diff --git a/checkout_sdk/payments/setups/setups.py b/checkout_sdk/payments/setups/setups.py index 3b9bb6c1..c8ccbdcc 100644 --- a/checkout_sdk/payments/setups/setups.py +++ b/checkout_sdk/payments/setups/setups.py @@ -1303,8 +1303,11 @@ class PaymentMethods: # The card-present payment method. Not present in the current Payment Setup schema. # [Optional] card_present: CardPresent - # The Pay by Bank (Open Banking) payment method's details and configuration. The spec - # names this key paybybank. + # The Pay by Bank (Open Banking) payment method's details and configuration. + # [Optional] + paybybank: PayByBank + # Deprecated: the API does not recognise the pay_by_bank key and ignores it without + # error, so a value set here is never applied. Use paybybank instead. # [Optional] pay_by_bank: PayByBank # The Stablecoin payment method's details and configuration. @@ -1315,6 +1318,14 @@ class PaymentMethods: cashapp: CashApp +# Risk entity +class PaymentSetupRisk: + """Risk assessment settings for the Payment Setup.""" + # Indicates whether a risk assessment should be performed. Defaults to true. + # [Optional] + enabled: bool + + # Settings entity class Settings: """Settings for the Payment Setup.""" @@ -1876,6 +1887,9 @@ class PaymentSetupsRequest: # Settings for the Payment Setup. # [Optional] settings: Settings + # Risk assessment settings for the Payment Setup. + # [Optional] + risk: PaymentSetupRisk # The customer's details. # [Optional] customer: Customer diff --git a/checkout_sdk/payments/setups/setups_client.py b/checkout_sdk/payments/setups/setups_client.py index a8461a49..e0a026ae 100644 --- a/checkout_sdk/payments/setups/setups_client.py +++ b/checkout_sdk/payments/setups/setups_client.py @@ -23,10 +23,13 @@ def create_payment_setup(self, payment_setups_request: PaymentSetupsRequest): Args: payment_setups_request: The Payment Setup request body. To offer Cash App Pay, set payment_methods.cashapp (initialization, customer_profile_sharing) and - customer.device.client. + customer.device.client. To offer Pay by Bank, set payment_methods.paybybank + (the deprecated pay_by_bank attribute is ignored by the API). Set risk.enabled + to control whether a risk assessment is performed (defaults to true). Returns: ResponseWrapper with the Payment Setup. For Cash App Pay, the customer is sent to - payment_methods.cashapp.action.redirect_url to authorize the payment. + payment_methods.cashapp.action.redirect_url to authorize the payment. For Pay by + Bank, the banks to choose from are in payment_methods.paybybank.action.banks. """ return self._api_client.post( self.build_path(self.__PAYMENTS_PATH, self.__SETUPS_PATH), @@ -39,7 +42,8 @@ def update_payment_setup(self, setup_id: str, payment_setups_request: PaymentSet Args: setup_id: The unique identifier of the Payment Setup to update. - payment_setups_request: The Payment Setup request body. + payment_setups_request: The Payment Setup request body. Pay by Bank goes in + payment_methods.paybybank and risk settings in risk.enabled. Returns: ResponseWrapper with the updated Payment Setup. """ diff --git a/tests/payments/setups/payment_setups_serialization_test.py b/tests/payments/setups/payment_setups_serialization_test.py index 62acda72..076d5fcd 100644 --- a/tests/payments/setups/payment_setups_serialization_test.py +++ b/tests/payments/setups/payment_setups_serialization_test.py @@ -25,7 +25,7 @@ MerchantAccount, OrderSubMerchant, PaymentSetupsRequest, Customer, CustomerEmail, CustomerDevice, CustomerDeviceClient, PaymentMethodInitialization, CashApp, CashAppAction, CashAppActionType, CashAppAddress, - CashAppCustomerProfile, + CashAppCustomerProfile, PaymentSetupRisk, ) @@ -962,3 +962,76 @@ def test_customer_spec_example_reads(self): assert read.merchant_account.last_transaction_date == '2025-03-28T00:00:00.0000000' assert read.merchant_account.total_order_count == 6 assert read.merchant_account.last_payment_amount == 55.99 + + +class TestPayByBankKeyAndRiskSerialization: + + def _request(self): + request = PaymentSetupsRequest() + request.processing_channel_id = 'pc_aaaaaaaaaaaaaaaaaaaaaaaaaa' + request.amount = 1000 + request.currency = Currency.GBP + pay_by_bank = PayByBank() + pay_by_bank.bank_id = 'ob-natwest' + methods = PaymentMethods() + methods.paybybank = pay_by_bank + request.payment_methods = methods + risk = PaymentSetupRisk() + risk.enabled = False + request.risk = risk + return request + + def test_request_sends_the_literal_paybybank_key(self): + body = json.dumps(self._request(), cls=JsonSerializer) + + assert '"paybybank"' in body + assert 'pay_by_bank' not in body + assert json.loads(body)['payment_methods'] == {'paybybank': {'bank_id': 'ob-natwest'}} + + def test_request_sends_risk_enabled_false(self): + assert _serialize(self._request())['risk'] == {'enabled': False} + + def test_risk_enabled_true_serializes(self): + risk = PaymentSetupRisk() + risk.enabled = True + + assert _serialize(risk) == {'enabled': True} + + def test_request_round_trip_keeps_paybybank_and_risk(self): + read = ResponseWrapper(None, _serialize(self._request())) + + assert read.payment_methods.paybybank.bank_id == 'ob-natwest' + assert read.risk.enabled is False + assert not hasattr(read.payment_methods, 'pay_by_bank') + + def test_response_reads_paybybank_action(self): + response = ResponseWrapper(None, { + 'id': 'pset_123', + 'risk': {'enabled': True}, + 'payment_methods': { + 'paybybank': { + 'status': 'action_required', + 'flags': [], + 'action': { + 'type': 'select_bank', + 'banks': [{ + 'bank_id': 'ob-natwest', + 'display_name': 'NatWest', + 'logo_url': 'https://example.com/natwest.png', + 'available': True, + }], + }, + }, + }, + }) + + method = response.payment_methods.paybybank + assert method.status == 'action_required' + assert method.flags == [] + assert method.action.type == 'select_bank' + bank = method.action.banks[0] + assert bank.bank_id == 'ob-natwest' + assert bank.display_name == 'NatWest' + assert bank.logo_url == 'https://example.com/natwest.png' + assert bank.available is True + assert response.risk.enabled is True