Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ venv/
dist
build
checkout_sdk.egg-info
htmlcov
htmlcov
.cursor/rules/
.cursor/skills/
.vscode/settings.json
52 changes: 52 additions & 0 deletions checkout_sdk/accounts/accounts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from enum import Enum
from typing import Dict

from checkout_sdk.common.common import Phone, Address
from checkout_sdk.common.common import ResidentialStatusType, AccountHolderIdentification
Expand Down Expand Up @@ -382,3 +383,54 @@ class UpdateScheduleRequest:

class PaymentInstrumentsQuery:
status: str


class ReserveRuleType(str, Enum):
ROLLING = 'rolling'


class HoldingDuration:
weeks: int


class RollingReserveRule:
percentage: float
holding_duration: HoldingDuration


class ReserveRuleRequest:
type: ReserveRuleType
rolling: RollingReserveRule
valid_from: str


class FilePurpose(str, Enum):
ADDITIONAL_DOCUMENT = 'additional_document'
ARTICLES_OF_ASSOCIATION = 'articles_of_association'
BANK_VERIFICATION = 'bank_verification'
CERTIFIED_AUTHORISED_SIGNATORY = 'certified_authorised_signatory'
COMPANY_OWNERSHIP = 'company_ownership'
IDENTIFICATION = 'identification'
IDENTITY_VERIFICATION = 'identity_verification'
DISPUTE_EVIDENCE = 'dispute_evidence'
COMPANY_VERIFICATION = 'company_verification'
FINANCIAL_VERIFICATION = 'financial_verification'
TAX_VERIFICATION = 'tax_verification'
PROOF_OF_LEGALITY = 'proof_of_legality'
PROOF_OF_PRINCIPAL_ADDRESS = 'proof_of_principal_address'
SHAREHOLDER_STRUCTURE = 'shareholder_structure'
PROOF_OF_RESIDENTIAL_ADDRESS = 'proof_of_residential_address'
PROOF_OF_REGISTRATION = 'proof_of_registration'


class EntityFileRequest:
purpose: FilePurpose


class EtagHeader:
etag: str

def get_header_mappings(self) -> Dict[str, str]:
return {
'etag': 'If-Match'
}
56 changes: 54 additions & 2 deletions checkout_sdk/accounts/accounts_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

from warnings import warn

from checkout_sdk.accounts.accounts import OnboardEntityRequest, UpdateScheduleRequest, AccountsPaymentInstrument, \
PaymentInstrumentRequest, PaymentInstrumentsQuery, UpdatePaymentInstrumentRequest
from checkout_sdk.accounts.accounts import (
EtagHeader, OnboardEntityRequest, UpdateScheduleRequest, AccountsPaymentInstrument,
PaymentInstrumentRequest, PaymentInstrumentsQuery, UpdatePaymentInstrumentRequest,
ReserveRuleRequest, EntityFileRequest
)
from checkout_sdk.api_client import ApiClient
from checkout_sdk.authorization_type import AuthorizationType
from checkout_sdk.checkout_configuration import CheckoutConfiguration
Expand All @@ -19,6 +22,8 @@ class AccountsClient(Client):
__FILES_PATH = 'files'
__PAYOUT_SCHEDULES_PATH = 'payout-schedules'
__PAYMENT_INSTRUMENTS_PATH = 'payment-instruments'
__MEMBERS_PATH = 'members'
__RESERVE_RULES_PATH = 'reserve-rules'

def __init__(self, api_client: ApiClient,
files_client: ApiClient,
Expand Down Expand Up @@ -106,3 +111,50 @@ def update_payout_schedule(self, entity_id: str, currency: Currency,
self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH, entity_id, self.__PAYOUT_SCHEDULES_PATH),
self._sdk_authorization(),
{currency: update_schedule_request})

def get_sub_entity_members(self, entity_id: str):
return self._api_client.get(
self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH, entity_id, self.__MEMBERS_PATH),
self._sdk_authorization())

def reinvite_sub_entity_member(self, entity_id: str, user_id: str):
return self._api_client.put(
self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH, entity_id, self.__MEMBERS_PATH, user_id),
self._sdk_authorization())

def create_reserve_rule(self, entity_id: str, create_request: ReserveRuleRequest):
return self._api_client.post(
self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH, entity_id, self.__RESERVE_RULES_PATH),
self._sdk_authorization(),
create_request)

def get_reserve_rules(self, entity_id: str):
return self._api_client.get(
self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH, entity_id, self.__RESERVE_RULES_PATH),
self._sdk_authorization())

def get_reserve_rule_details(self, entity_id: str, reserve_rule_id: str):
path = self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH,
entity_id, self.__RESERVE_RULES_PATH, reserve_rule_id)
return self._api_client.get(path, self._sdk_authorization())

def update_reserve_rule(self, entity_id: str, reserve_rule_id: str, etag: str, update_request: ReserveRuleRequest):
headers = None
if (etag is not None):
headers = EtagHeader()
headers.etag = etag

path = self.build_path(self.__ACCOUNTS_PATH, self.__ENTITIES_PATH,
entity_id, self.__RESERVE_RULES_PATH, reserve_rule_id)
return self._api_client.put(path, self._sdk_authorization(), update_request, headers=headers)

def upload_entity_file(self, entity_id: str, entity_file_request: EntityFileRequest):
return self.__files_client.post(
self.build_path(self.__ENTITIES_PATH, entity_id, self.__FILES_PATH),
self._sdk_authorization(),
entity_file_request)

def retrieve_entity_file(self, entity_id: str, file_id: str):
return self.__files_client.get(
self.build_path(self.__ENTITIES_PATH, entity_id, self.__FILES_PATH, file_id),
self._sdk_authorization())
Empty file.
92 changes: 92 additions & 0 deletions checkout_sdk/agenticcommerce/agentic_commerce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from __future__ import absolute_import

from enum import Enum
from typing import List, Dict
from datetime import datetime

from checkout_sdk.common.enums import Country, Currency


class DelegatedPaymentMethodType(str, Enum):
CARD = 'card'


class DelegatedCardNumberType(str, Enum):
FPAN = 'fpan'
NETWORK_TOKEN = 'network_token'


class DelegatedPaymentAllowanceReason(str, Enum):
ONE_TIME = 'one_time'


class DelegatedCardFundingType(str, Enum):
CREDIT = 'credit'
DEBIT = 'debit'
PREPAID = 'prepaid'


class DelegatedPaymentMethodCard:
type: DelegatedPaymentMethodType
card_number_type: DelegatedCardNumberType
number: str
exp_month: str = None

Check warning on line 33 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "exp_month"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbq&open=AZ2vqBS-vn5gzXa5SQbq&pullRequest=202
exp_year: str = None

Check warning on line 34 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "exp_year"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbr&open=AZ2vqBS-vn5gzXa5SQbr&pullRequest=202
name: str = None

Check warning on line 35 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "name"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbs&open=AZ2vqBS-vn5gzXa5SQbs&pullRequest=202
cvc: str = None

Check warning on line 36 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "cvc"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbt&open=AZ2vqBS-vn5gzXa5SQbt&pullRequest=202
cryptogram: str = None

Check warning on line 37 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "cryptogram"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbu&open=AZ2vqBS-vn5gzXa5SQbu&pullRequest=202
eci_value: str = None

Check warning on line 38 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "eci_value"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbv&open=AZ2vqBS-vn5gzXa5SQbv&pullRequest=202
checks_performed: List[str] = None

Check warning on line 39 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "list[str]" with "Optional[list[str]]" or don't assign "None" to "checks_performed"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbw&open=AZ2vqBS-vn5gzXa5SQbw&pullRequest=202
iin: str = None

Check warning on line 40 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "iin"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbx&open=AZ2vqBS-vn5gzXa5SQbx&pullRequest=202
display_card_funding_type: DelegatedCardFundingType = None

Check warning on line 41 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "DelegatedCardFundingType" with "Optional[DelegatedCardFundingType]" or don't assign "None" to "display_card_funding_type"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQby&open=AZ2vqBS-vn5gzXa5SQby&pullRequest=202
display_wallet_type: str = None

Check warning on line 42 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "display_wallet_type"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQbz&open=AZ2vqBS-vn5gzXa5SQbz&pullRequest=202
display_brand: str = None

Check warning on line 43 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "display_brand"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQb0&open=AZ2vqBS-vn5gzXa5SQb0&pullRequest=202
display_last4: str = None

Check warning on line 44 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "display_last4"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQb1&open=AZ2vqBS-vn5gzXa5SQb1&pullRequest=202
metadata: Dict[str, str] = None

Check warning on line 45 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "dict[str, str]" with "Optional[dict[str, str]]" or don't assign "None" to "metadata"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQb2&open=AZ2vqBS-vn5gzXa5SQb2&pullRequest=202

def __init__(self):
self.type = DelegatedPaymentMethodType.CARD


class DelegatedPaymentAllowance:
reason: DelegatedPaymentAllowanceReason
max_amount: int
currency: Currency
merchant_id: str
checkout_session_id: str
expires_at: datetime


class DelegatedPaymentBillingAddress:
name: str
line_one: str
line_two: str = None

Check warning on line 63 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "line_two"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQb3&open=AZ2vqBS-vn5gzXa5SQb3&pullRequest=202
city: str
state: str = None

Check warning on line 65 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "str" with "Optional[str]" or don't assign "None" to "state"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQb4&open=AZ2vqBS-vn5gzXa5SQb4&pullRequest=202
postal_code: str
country: Country


class DelegatedPaymentRiskSignal:
type: str
score: int
action: str


class DelegatedPaymentRequest:
payment_method: DelegatedPaymentMethodCard
allowance: DelegatedPaymentAllowance
billing_address: DelegatedPaymentBillingAddress = None

Check warning on line 79 in checkout_sdk/agenticcommerce/agentic_commerce.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the type hint "DelegatedPaymentBillingAddress" with "Optional[DelegatedPaymentBillingAddress]" or don't assign "None" to "billing_address"

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-python&issues=AZ2vqBS-vn5gzXa5SQb5&open=AZ2vqBS-vn5gzXa5SQb5&pullRequest=202
risk_signals: List[DelegatedPaymentRiskSignal]
metadata: Dict[str, str]


class DelegatedPaymentHeaders:
signature: str
timestamp: str
api_version: str

def get_header_mappings(self) -> Dict[str, str]:
return {
'api_version': 'API-Version'
}
25 changes: 25 additions & 0 deletions checkout_sdk/agenticcommerce/agentic_commerce_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import absolute_import

from checkout_sdk.agenticcommerce.agentic_commerce import DelegatedPaymentRequest, DelegatedPaymentHeaders
from checkout_sdk.api_client import ApiClient
from checkout_sdk.authorization_type import AuthorizationType
from checkout_sdk.checkout_configuration import CheckoutConfiguration
from checkout_sdk.client import Client


class AgenticCommerceClient(Client):
__AGENTIC_COMMERCE_PATH = 'agentic_commerce'
__DELEGATE_PAYMENT_PATH = 'delegate_payment'

def __init__(self, api_client: ApiClient, configuration: CheckoutConfiguration):
super().__init__(api_client=api_client,
configuration=configuration,
authorization_type=AuthorizationType.SECRET_KEY)

def create_delegated_payment_token(self, request: DelegatedPaymentRequest, headers: DelegatedPaymentHeaders):
return self._api_client.post(
self.build_path(self.__AGENTIC_COMMERCE_PATH, self.__DELEGATE_PAYMENT_PATH),
self._sdk_authorization(),
request,
headers=headers
)
80 changes: 57 additions & 23 deletions checkout_sdk/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,24 @@ def post(self,
path,
authorization: SdkAuthorization,
request=None,
idempotency_key: str = None):
idempotency_key: str = None,
headers=None):
return self.invoke(method='POST', path=path, authorization=authorization, body=request,
idempotency_key=idempotency_key)
idempotency_key=idempotency_key, headers=headers)

def put(self,
path,
authorization: SdkAuthorization,
request=None):
return self.invoke(method='PUT', path=path, authorization=authorization, body=request)
request=None,
headers=None):
return self.invoke(method='PUT', path=path, authorization=authorization, body=request, headers=headers)

def patch(self,
path,
authorization: SdkAuthorization,
request=None):
return self.invoke(method='PATCH', path=path, authorization=authorization, body=request)
request=None,
headers=None):
return self.invoke(method='PATCH', path=path, authorization=authorization, body=request, headers=headers)

def delete(self,
path,
Expand All @@ -80,37 +83,33 @@ def invoke(self,
idempotency_key: str = None,
params=None,
file_request: FileRequest = None,
multipart_file=None):
multipart_file=None,
headers=None):

headers = {
request_headers = {
'User-Agent': 'checkout-sdk-python/' + VERSION,
'Accept': 'application/json',
'Authorization': authorization.get_authorization_header(),
'Content-Type': 'application/json'}

if idempotency_key is not None:
headers['Cko-Idempotency-Key'] = idempotency_key
request_headers['Cko-Idempotency-Key'] = idempotency_key

if headers is not None:
custom_headers = self._process_custom_headers(headers)
request_headers.update(custom_headers)

base_uri = self._base_uri + path

try:
json_body = None
params_dict = None
files = None

if body is not None:
json_body = json.dumps(body, cls=JsonSerializer)
elif params is not None:
params_dict = json.loads(json.dumps(params, cls=JsonSerializer))
elif file_request is not None:
headers.pop('Content-Type')
files, json_body = get_file_request(file_request, multipart_file)
json_body, params_dict, files = self._prepare_request_payload(
request_headers, body, params, file_request, multipart_file)

self._logger.info(method + ' ' + path)

response = self._http_client.request(method=method,
url=base_uri,
headers=headers,
headers=request_headers,
params=params_dict,
data=json_body,
files=files)
Expand All @@ -130,5 +129,40 @@ def invoke(self,
else:
contents = response.text
return ResponseWrapper(http_metadata, contents)
else:
return ResponseWrapper(http_metadata)
return ResponseWrapper(http_metadata)

def _prepare_request_payload(self, request_headers, body, params, file_request, multipart_file):
json_body = None
params_dict = None
files = None
if body is not None:
json_body = json.dumps(body, cls=JsonSerializer)
elif params is not None:
params_dict = json.loads(json.dumps(params, cls=JsonSerializer))
elif file_request is not None:
request_headers.pop('Content-Type')
files, json_body = get_file_request(file_request, multipart_file)
return json_body, params_dict, files

def _process_custom_headers(self, custom_headers):
if custom_headers is None:
return {}

headers = {}
custom_mappings = {}
if hasattr(custom_headers, 'get_header_mappings'):
custom_mappings = custom_headers.get_header_mappings()

for attr_name in dir(custom_headers):
if attr_name.startswith('_') or callable(getattr(custom_headers, attr_name)):
continue

value = getattr(custom_headers, attr_name)
if value is not None and value != '':
header_name = custom_mappings.get(attr_name, self._convert_property_to_header(attr_name))
headers[header_name] = str(value)

return headers

def _convert_property_to_header(self, property_name):
return '-'.join(word.capitalize() for word in property_name.split('_'))
Loading
Loading