From f5146f5ec75d801141ff2db64850b96bd3c3583f Mon Sep 17 00:00:00 2001 From: david ruiz Date: Mon, 29 Jun 2026 10:57:16 +0200 Subject: [PATCH 1/3] Updated Payments Product Type and SubType + tests --- checkout_sdk/payments/payments.py | 23 +++-- tests/payments/product_serialization_test.py | 99 ++++++++++++++++++++ 2 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 tests/payments/product_serialization_test.py diff --git a/checkout_sdk/payments/payments.py b/checkout_sdk/payments/payments.py index 512e1b3..84b173f 100644 --- a/checkout_sdk/payments/payments.py +++ b/checkout_sdk/payments/payments.py @@ -2,6 +2,7 @@ from datetime import datetime from enum import Enum +from typing import Optional from checkout_sdk.common.common import AccountHolder, BankDetails, MarketplaceData, Address, Phone, CustomerRequest, \ AccountHolderIdentification, QueryFilterDateRange @@ -623,17 +624,23 @@ class ProcessingSettings: scheme_transaction_link_id: str -class ProductSubType (str, Enum): - BLOCKCHAIN = 'BLOCKCHAIN' - CBDC = 'CBDC' - CRYPTOCURRENCY = 'CRYPTOCURRENCY' - NFT = 'NFT' - STABLECOIN = 'STABLECOIN' +class ItemType(str, Enum): + DIGITAL = 'digital' + DISCOUNT = 'discount' + PHYSICAL = 'physical' + + +class ProductSubType(str, Enum): + BLOCKCHAIN = 'blockchain' + CBDC = 'cbdc' + CRYPTOCURRENCY = 'cryptocurrency' + NFT = 'nft' + STABLECOIN = 'stablecoin' class Product: - type: str - sub_type: list # ProductSubType + type: ItemType = None + sub_type: ProductSubType = None name: str quantity: int unit_price: int diff --git a/tests/payments/product_serialization_test.py b/tests/payments/product_serialization_test.py new file mode 100644 index 0000000..cf35580 --- /dev/null +++ b/tests/payments/product_serialization_test.py @@ -0,0 +1,99 @@ +import json + +import pytest + +from checkout_sdk.json_serializer import JsonSerializer +from checkout_sdk.payments.payments import Product, ProductSubType, ItemType + + +def _serialize(obj): + return json.loads(json.dumps(obj, cls=JsonSerializer)) + + +class TestProductSerialization: + + def test_sub_type_serializes_as_lowercase(self): + p = Product() + p.sub_type = ProductSubType.BLOCKCHAIN + assert _serialize(p)['sub_type'] == 'blockchain' + + def test_all_sub_type_values_serialize_lowercase(self): + expected = { + ProductSubType.BLOCKCHAIN: 'blockchain', + ProductSubType.CBDC: 'cbdc', + ProductSubType.CRYPTOCURRENCY: 'cryptocurrency', + ProductSubType.NFT: 'nft', + ProductSubType.STABLECOIN: 'stablecoin', + } + for enum_val, expected_str in expected.items(): + p = Product() + p.sub_type = enum_val + assert _serialize(p)['sub_type'] == expected_str + + def test_all_type_values_serialize_lowercase(self): + expected = { + ItemType.DIGITAL: 'digital', + ItemType.DISCOUNT: 'discount', + ItemType.PHYSICAL: 'physical', + } + for enum_val, expected_str in expected.items(): + p = Product() + p.type = enum_val + assert _serialize(p)['type'] == expected_str + + def test_unset_type_and_sub_type_serialize_as_null(self): + # With = None class-level defaults, unset fields serialize as null (not omitted) + p = Product() + p.name = 'Gold Necklace' + p.quantity = 1 + result = _serialize(p) + assert result['type'] is None + assert result['sub_type'] is None + + def test_type_and_sub_type_accept_none(self): + # Explicitly assigning None is valid and serializes as null + p = Product() + p.type = None + p.sub_type = None + p.name = 'Widget' + result = _serialize(p) + assert result.get('type') is None + assert result.get('sub_type') is None + + def test_type_and_sub_type_round_trip(self): + p = Product() + p.type = ItemType.DIGITAL + p.sub_type = ProductSubType.STABLECOIN + p.name = 'USDC' + p.quantity = 1 + p.unit_price = 10000 + p.reference = '858818ac' + p.commodity_code = 'DEF123' + p.unit_of_measure = 'units' + p.total_amount = 10000 + p.tax_amount = 1000 + p.tax_rate = 2000 + p.discount_amount = 500 + p.wxpay_goods_id = '1001' + p.image_url = 'https://example.com/image.jpg' + p.url = 'https://example.com/product' + p.sku = 'SKU-001' + + result = _serialize(p) + + assert result['type'] == 'digital' + assert result['sub_type'] == 'stablecoin' + assert result['name'] == 'USDC' + assert result['quantity'] == 1 + assert result['unit_price'] == 10000 + assert result['reference'] == '858818ac' + assert result['commodity_code'] == 'DEF123' + assert result['unit_of_measure'] == 'units' + assert result['total_amount'] == 10000 + assert result['tax_amount'] == 1000 + assert result['tax_rate'] == 2000 + assert result['discount_amount'] == 500 + assert result['wxpay_goods_id'] == '1001' + assert result['image_url'] == 'https://example.com/image.jpg' + assert result['url'] == 'https://example.com/product' + assert result['sku'] == 'SKU-001' From 782332676e9a9e4eda89f89b5d4a5168a4a12760 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Mon, 29 Jun 2026 11:14:30 +0200 Subject: [PATCH 2/3] Remove unused Optional import --- checkout_sdk/payments/payments.py | 1 - 1 file changed, 1 deletion(-) diff --git a/checkout_sdk/payments/payments.py b/checkout_sdk/payments/payments.py index 84b173f..ce55532 100644 --- a/checkout_sdk/payments/payments.py +++ b/checkout_sdk/payments/payments.py @@ -2,7 +2,6 @@ from datetime import datetime from enum import Enum -from typing import Optional from checkout_sdk.common.common import AccountHolder, BankDetails, MarketplaceData, Address, Phone, CustomerRequest, \ AccountHolderIdentification, QueryFilterDateRange From 42c7912689dcfd5262f69df4fa501e084088edbd Mon Sep 17 00:00:00 2001 From: david ruiz Date: Mon, 29 Jun 2026 11:20:06 +0200 Subject: [PATCH 3/3] Remove unused pytest import --- tests/payments/product_serialization_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/payments/product_serialization_test.py b/tests/payments/product_serialization_test.py index cf35580..8b8b446 100644 --- a/tests/payments/product_serialization_test.py +++ b/tests/payments/product_serialization_test.py @@ -1,7 +1,5 @@ import json -import pytest - from checkout_sdk.json_serializer import JsonSerializer from checkout_sdk.payments.payments import Product, ProductSubType, ItemType