diff --git a/checkout_sdk/payments/payments.py b/checkout_sdk/payments/payments.py index 8be749b..142adb9 100644 --- a/checkout_sdk/payments/payments.py +++ b/checkout_sdk/payments/payments.py @@ -57,6 +57,7 @@ class Exemption(str, Enum): LOW_VALUE = 'low_value' SECURE_CORPORATE_PAYMENT = 'secure_corporate_payment' TRUSTED_LISTING = 'trusted_listing' + TRUSTED_LISTING_PROMPT = 'trusted_listing_prompt' TRANSACTION_RISK_ASSESSMENT = 'transaction_risk_assessment' THREE_DS_OUTAGE = '3ds_outage' SCA_DELEGATION = 'sca_delegation' @@ -124,10 +125,14 @@ class BillingPlanType(str, Enum): PRE_APPROVED_PAYMENTS = 'PRE_APPROVED_PAYMENTS' +class PanPreference(str, Enum): + FPAN = 'fpan' + DPAN = 'dpan' + + class BillingDescriptor: name: str city: str - # Not available on previous reference: str @@ -324,7 +329,6 @@ class ThreeDsRequest: exemption: Exemption challenge_indicator: ChallengeIndicator allow_upgrade: bool - # Not available on Previous status: str authentication_date: datetime authentication_amount: int @@ -380,6 +384,41 @@ class SenderInformation: sourceOfFunds: str +class PartnerCustomerRiskData: + key: str + value: str + + +class AccommodationAddress: + address_line1: str + zip: str + + +class AccommodationGuest: + first_name: str + last_name: str + date_of_birth: str + + +class AccommodationRoom: + rate: str + number_of_nights_at_room_rate: str + + +class AccommodationData: + name: str + booking_reference: str + check_in_date: str + check_out_date: str + address: AccommodationAddress + state: str + country: str + city: str + number_of_rooms: int + guests: list # AccommodationGuest + room: list # AccommodationRoom + + class ProcessingSettings: order_id: str tax_amount: int @@ -414,6 +453,11 @@ class ProcessingSettings: dlocal: DLocalProcessingSettings senderInformation: SenderInformation purpose: str + partner_customer_risk_data: list # PartnerCustomerRiskData + accommodation_data: list # AccommodationData + surcharge_amount: int + pan_preference: PanPreference + provision_network_token: bool class Product: diff --git a/checkout_sdk/payments/sessions/sessions.py b/checkout_sdk/payments/sessions/sessions.py index c9cfd22..924d1e3 100644 --- a/checkout_sdk/payments/sessions/sessions.py +++ b/checkout_sdk/payments/sessions/sessions.py @@ -1,74 +1,270 @@ from datetime import datetime from enum import Enum -from checkout_sdk.common.common import Address, CustomerRequest from checkout_sdk.common.enums import Currency from checkout_sdk.payments.payments import PaymentType, BillingDescriptor, PaymentCustomerRequest, ShippingDetails, \ - PaymentRecipient, ProcessingSettings, RiskRequest, PaymentRetryRequest, ThreeDsRequest, PaymentSender -from checkout_sdk.payments.payments_previous import BillingInformation + PaymentRecipient, ProcessingSettings, RiskRequest, ThreeDsRequest, PaymentSender class PaymentMethodsType(str, Enum): + ALIPAY_CN = 'alipay_cn' + ALIPAY_HK = 'alipay_hk' + ALMA = 'alma' APPLEPAY = 'applepay' BANCONTACT = 'bancontact' + BENEFIT = 'benefit' + BIZUM = 'bizum' CARD = 'card' + DANA = 'dana' EPS = 'eps' - GIROPAY = 'giropay' + GCASH = 'gcash' GOOGLEPAY = 'googlepay' IDEAL = 'ideal' + KAKAOPAY = 'kakaopay' + KLARNA = 'klarna' KNET = 'knet' + MBWAY = 'mbway' + MOBILEPAY = 'mobilepay' MULTIBANCO = 'multibanco' - PRZELEWY24 = 'p24' + P24 = 'p24' PAYPAL = 'paypal' - SOFORT = 'sofort' + PLAID = 'plaid' + QPAY = 'qpay' + REMEMBER_ME = 'remember_me' + SEPA = 'sepa' + STCPAY = 'stcpay' + STORED_CARD = 'stored_card' + TABBY = 'tabby' + TAMARA = 'tamara' + TNG = 'tng' + TRUEMONEY = 'truemoney' + TWINT = 'twint' + VIPPS = 'vipps' + + +class Locale(str, Enum): + AR = 'ar' + DA_DK = 'da-DK' + DE_DE = 'de-DE' + EL = 'el' + EN_GB = 'en-GB' + ES_ES = 'es-ES' + FI_FI = 'fi-FI' + FIL_PH = 'fil-PH' + FR_FR = 'fr-FR' + HI_IN = 'hi-IN' + ID_ID = 'id-ID' + IT_IT = 'it-IT' + JA_JP = 'ja-JP' + KO_KR = 'ko-KR' + MS_MY = 'ms-MY' + NB_NO = 'nb-NO' + NL_NL = 'nl-NL' + PT_PT = 'pt-PT' + SV_SE = 'sv-SE' + TH_TH = 'th-TH' + VI_VN = 'vi-VN' + ZH_CN = 'zh-CN' + ZH_HK = 'zh-HK' + ZH_TW = 'zh-TW' class StorePaymentDetails(str, Enum): DISABLED = 'disabled' ENABLED = 'enabled' + COLLECT_CONSENT = 'collect_consent' + + +class InstructionPurpose(str, Enum): + DONATIONS = 'donations' + EDUCATION = 'education' + EMERGENCY_NEED = 'emergency_need' + EXPATRIATION = 'expatriation' + FAMILY_SUPPORT = 'family_support' + FINANCIAL_SERVICES = 'financial_services' + GIFTS = 'gifts' + INCOME = 'income' + INSURANCE = 'insurance' + INVESTMENT = 'investment' + IT_SERVICES = 'it_services' + LEISURE = 'leisure' + LOAN_PAYMENT = 'loan_payment' + MEDICAL_TREATMENT = 'medical_treatment' + OTHER = 'other' + PENSION = 'pension' + ROYALTIES = 'royalties' + SAVINGS = 'savings' + TRAVEL_AND_TOURISM = 'travel_and_tourism' + + +class Instruction: + purpose: InstructionPurpose + + +class CustomerRetry: + max_attempts: int + + +class AccountHolderType(str, Enum): + INDIVIDUAL = 'individual' + CORPORATE = 'corporate' + GOVERNMENT = 'government' + + +class BillingAddress: + country: str + address_line1: str + address_line2: str + city: str + state: str + zip: str + + +class BillingPhone: + country_code: str + number: str + + +class SessionBilling: + address: BillingAddress + phone: BillingPhone + +class AccountHolder: + type: AccountHolderType -class Billing: - address: Address + def __init__(self, type_p: AccountHolderType): + self.type = type_p -class Card: +class IndividualAccountHolder(AccountHolder): + first_name: str + last_name: str + middle_name: str + account_name_inquiry: bool + + def __init__(self): + super().__init__(AccountHolderType.INDIVIDUAL) + + +class CorporateAccountHolder(AccountHolder): + company_name: str + account_name_inquiry: bool + + def __init__(self): + super().__init__(AccountHolderType.CORPORATE) + + +class ApplePayConfiguration: + store_payment_details: StorePaymentDetails + account_holder: AccountHolder + + +class CardConfiguration: store_payment_details: StorePaymentDetails + account_holder: AccountHolder -class PaymentMethodConfiguration: - card: Card +class GooglePayConfiguration: + store_payment_details: StorePaymentDetails + account_holder: AccountHolder + + +class StoredCardConfiguration: + customer_id: str + instrument_ids: list # list of strings + default_instrument_id: str + + +class SessionPaymentMethodConfiguration: + applepay: ApplePayConfiguration + card: CardConfiguration + googlepay: GooglePayConfiguration + stored_card: StoredCardConfiguration + + +class Item: + name: str + quantity: int + unit_price: int + reference: str + commodity_code: str + unit_of_measure: str + total_amount: int + tax_amount: int + discount_amount: int + url: str + image_url: str class PaymentSessionsRequest: amount: int currency: Currency + billing: SessionBilling + success_url: str + failure_url: str payment_type: PaymentType - billing: BillingInformation billing_descriptor: BillingDescriptor reference: str description: str customer: PaymentCustomerRequest - customer: CustomerRequest shipping: ShippingDetails recipient: PaymentRecipient processing: ProcessingSettings + instruction: Instruction processing_channel_id: str + payment_method_configuration: SessionPaymentMethodConfiguration + items: list # Item + amount_allocations: list # AmountAllocations + risk: RiskRequest + display_name: str + metadata: dict + locale: str + three_ds: ThreeDsRequest + sender: PaymentSender + capture: bool + capture_on: datetime expires_on: datetime - payment_method_configuration: PaymentMethodConfiguration enabled_payment_methods: list # PaymentMethodsType disabled_payment_methods: list # PaymentMethodsType - items: list # payments.Product - amount_allocations: list # values of AmountAllocations - risk: RiskRequest - customer_retry: PaymentRetryRequest - display_name: str + customer_retry: CustomerRetry + ip_address: str + + +class PaymentSessionWithPaymentRequest: + session_data: str + amount: int + currency: Currency + billing: SessionBilling success_url: str failure_url: str + payment_type: PaymentType + billing_descriptor: BillingDescriptor + reference: str + description: str + customer: PaymentCustomerRequest + shipping: ShippingDetails + recipient: PaymentRecipient + processing: ProcessingSettings + instruction: Instruction + processing_channel_id: str + payment_method_configuration: SessionPaymentMethodConfiguration + items: list # Item + amount_allocations: list # AmountAllocations + risk: RiskRequest + display_name: str metadata: dict locale: str three_ds: ThreeDsRequest sender: PaymentSender capture: bool capture_on: datetime + + +class SubmitPaymentSessionRequest: + session_data: str + amount: int + reference: str + items: list # Item + three_ds: ThreeDsRequest ip_address: str diff --git a/checkout_sdk/payments/sessions/sessions_client.py b/checkout_sdk/payments/sessions/sessions_client.py index e06a16a..41b3fa8 100644 --- a/checkout_sdk/payments/sessions/sessions_client.py +++ b/checkout_sdk/payments/sessions/sessions_client.py @@ -4,7 +4,9 @@ from checkout_sdk.authorization_type import AuthorizationType from checkout_sdk.checkout_configuration import CheckoutConfiguration from checkout_sdk.client import Client -from checkout_sdk.payments.sessions.sessions import PaymentSessionsRequest +from checkout_sdk.payments.sessions.sessions import ( + PaymentSessionsRequest, PaymentSessionWithPaymentRequest, SubmitPaymentSessionRequest +) class PaymentSessionsClient(Client): @@ -18,3 +20,14 @@ def __init__(self, api_client: ApiClient, configuration: CheckoutConfiguration): def create_payment_sessions(self, payment_sessions_request: PaymentSessionsRequest): return self._api_client.post(self.__PAYMENT_SESSIONS_PATH, self._sdk_authorization(), payment_sessions_request) + + def create_payment_session_with_payment( + self, payment_session_with_payment_request: PaymentSessionWithPaymentRequest): + return self._api_client.post(self.__PAYMENT_SESSIONS_PATH + '/complete', self._sdk_authorization(), + payment_session_with_payment_request) + + def submit_payment_session(self, session_id: str, + submit_payment_session_request: SubmitPaymentSessionRequest): + return self._api_client.post(self.__PAYMENT_SESSIONS_PATH + '/' + session_id + '/submit', + self._sdk_authorization(), + submit_payment_session_request) diff --git a/tests/payments/request_apm_payments_integration_test.py b/tests/payments/request_apm_payments_integration_test.py index e5bad76..445d562 100644 --- a/tests/payments/request_apm_payments_integration_test.py +++ b/tests/payments/request_apm_payments_integration_test.py @@ -513,12 +513,18 @@ def test_should_request_eps_payment(default_api): def test_should_make_bizum_payment(default_api): + customer_request = PaymentCustomerRequest() + customer_request.email = random_email() + customer_request.name = "Test Customer" + customer_request.phone = phone() + payment_request = PaymentRequest() payment_request.source = RequestBizumSource() payment_request.source.mobile_number = '+447700900986' payment_request.amount = 10 payment_request.currency = Currency.EUR payment_request.capture = True + payment_request.customer = customer_request payment_request.success_url = SUCCESS_URL payment_request.failure_url = FAILURE_URL diff --git a/tests/payments/sessions/payment_sessions_client_test.py b/tests/payments/sessions/payment_sessions_client_test.py index f81735c..7ac67af 100644 --- a/tests/payments/sessions/payment_sessions_client_test.py +++ b/tests/payments/sessions/payment_sessions_client_test.py @@ -1,6 +1,8 @@ import pytest -from checkout_sdk.payments.sessions.sessions import PaymentSessionsRequest +from checkout_sdk.payments.sessions.sessions import ( + PaymentSessionsRequest, PaymentSessionWithPaymentRequest, SubmitPaymentSessionRequest +) from checkout_sdk.payments.sessions.sessions_client import PaymentSessionsClient @@ -14,3 +16,12 @@ class TestPaymentSessionsClient: def test_should_create_payment_sessions(self, mocker, client: PaymentSessionsClient): mocker.patch('checkout_sdk.api_client.ApiClient.post', return_value='response') assert client.create_payment_sessions(PaymentSessionsRequest()) == 'response' + + def test_should_create_payment_session_with_payment(self, mocker, client: PaymentSessionsClient): + mocker.patch('checkout_sdk.api_client.ApiClient.post', return_value='response') + assert client.create_payment_session_with_payment(PaymentSessionWithPaymentRequest()) == 'response' + + def test_should_submit_payment_session(self, mocker, client: PaymentSessionsClient): + mocker.patch('checkout_sdk.api_client.ApiClient.post', return_value='response') + session_id = 'ps_test_session_id' + assert client.submit_payment_session(session_id, SubmitPaymentSessionRequest()) == 'response' diff --git a/tests/payments/sessions/payment_sessions_integration_test.py b/tests/payments/sessions/payment_sessions_integration_test.py index a6eaf9d..ba33fd6 100644 --- a/tests/payments/sessions/payment_sessions_integration_test.py +++ b/tests/payments/sessions/payment_sessions_integration_test.py @@ -1,10 +1,20 @@ from __future__ import absolute_import +import pytest + from checkout_sdk.common.common import CustomerRequest from checkout_sdk.common.enums import Currency -from checkout_sdk.payments.payments_previous import BillingInformation -from checkout_sdk.payments.sessions.sessions import PaymentSessionsRequest -from tests.checkout_test_utils import assert_response, address +from checkout_sdk.payments.sessions.sessions import ( + PaymentSessionsRequest, + PaymentSessionWithPaymentRequest, + SubmitPaymentSessionRequest, + SessionBilling, + BillingAddress, + BillingPhone, + Item +) +from checkout_sdk.payments.payments import ThreeDsRequest +from tests.checkout_test_utils import assert_response def test_should_create_payment_sessions(default_api): @@ -18,9 +28,52 @@ def test_should_create_payment_sessions(default_api): '_links.self') +@pytest.mark.skip(reason='use on demand') +def test_should_create_payment_session_with_payment(default_api): + request = create_payment_session_with_payment_request() + + response = default_api.payment_sessions.create_payment_session_with_payment(request) + + assert_response(response, + 'id', + 'status', + 'amount', + 'currency', + '_links') + + +@pytest.mark.skip(reason='use on demand') +def test_should_submit_payment_session(default_api): + # First create a payment session + payment_session_request = create_payment_sessions_request() + payment_session_response = default_api.payment_sessions.create_payment_sessions(payment_session_request) + + # Then submit a payment for that session + submit_request = create_submit_payment_session_request() + session_id = payment_session_response.id + + response = default_api.payment_sessions.submit_payment_session(session_id, submit_request) + + assert_response(response, + 'id', + 'status', + '_links') + + def create_payment_sessions_request(): - billing = BillingInformation() - billing.address = address() + billing_address = BillingAddress() + billing_address.country = 'GB' + billing_address.address_line1 = '123 High Street' + billing_address.city = 'London' + billing_address.zip = 'W1K 1LB' + + billing_phone = BillingPhone() + billing_phone.country_code = '44' + billing_phone.number = '7123456789' + + billing = SessionBilling() + billing.address = billing_address + billing.phone = billing_phone customer = CustomerRequest() customer.name = 'John Smith' @@ -36,3 +89,56 @@ def create_payment_sessions_request(): request.failure_url = 'https://example.com/payments/failure' return request + + +def create_payment_session_with_payment_request(): + billing_address = BillingAddress() + billing_address.country = 'GB' + billing_address.address_line1 = '123 High Street' + billing_address.city = 'London' + billing_address.zip = 'W1K 1LB' + + billing_phone = BillingPhone() + billing_phone.country_code = '44' + billing_phone.number = '7123456789' + + billing = SessionBilling() + billing.address = billing_address + billing.phone = billing_phone + + customer = CustomerRequest() + customer.name = 'John Smith' + customer.email = 'john.smith@example.com' + + request = PaymentSessionWithPaymentRequest() + request.session_data = 'session_data_token_example' + request.amount = 2000 + request.currency = Currency.GBP + request.reference = 'ORD-123B' + request.billing = billing + request.customer = customer + request.success_url = 'https://example.com/payments/success' + request.failure_url = 'https://example.com/payments/failure' + + return request + + +def create_submit_payment_session_request(): + three_ds = ThreeDsRequest() + three_ds.enabled = True + three_ds.attempt_n3d = False + + item = Item() + item.name = 'Test Product' + item.quantity = 1 + item.unit_price = 2000 + + request = SubmitPaymentSessionRequest() + request.session_data = 'session_data_token_example' + request.amount = 2000 + request.reference = 'ORD-123C' + request.items = [item] + request.three_ds = three_ds + request.ip_address = '90.197.169.245' + + return request