From e02d19c4342ba475fc36d190bd9589ed563d1c3f Mon Sep 17 00:00:00 2001 From: David Grayston Date: Fri, 3 Oct 2025 22:30:20 +0100 Subject: [PATCH] feat: Add support for non-catalog discounts --- CHANGELOG.md | 6 + .../Operations/CreateTransaction.py | 4 + .../Discount/TransactionNonCatalogDiscount.py | 17 ++ .../Operations/Discount/__init__.py | 3 + .../Operations/PreviewTransaction.py | 4 + .../Operations/PreviewTransactionByAddress.py | 4 + .../PreviewTransactionByCustomer.py | 4 + .../Operations/PreviewTransactionByIP.py | 4 + .../Operations/UpdateTransaction.py | 4 + .../create_with_noncatalog_discount_full.json | 22 ++ ...eate_with_noncatalog_discount_minimal.json | 13 + ...address_with_noncatalog_discount_full.json | 27 ++ ...ress_with_noncatalog_discount_minimal.json | 18 ++ ...ustomer_with_noncatalog_discount_full.json | 27 ++ ...omer_with_noncatalog_discount_minimal.json | 16 ++ ...w_by_ip_with_noncatalog_discount_full.json | 26 ++ ...y_ip_with_noncatalog_discount_minimal.json | 15 + ...preview_with_noncatalog_discount_full.json | 23 ++ ...view_with_noncatalog_discount_minimal.json | 14 + .../update_with_noncatalog_discount_full.json | 16 ++ ...date_with_noncatalog_discount_minimal.json | 7 + .../Transactions/test_TransactionsClient.py | 270 ++++++++++++++++++ 22 files changed, 544 insertions(+) create mode 100644 paddle_billing/Resources/Transactions/Operations/Discount/TransactionNonCatalogDiscount.py create mode 100644 paddle_billing/Resources/Transactions/Operations/Discount/__init__.py create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_full.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_minimal.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_full.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_minimal.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_full.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_minimal.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_full.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_minimal.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_full.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_minimal.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_full.json create mode 100644 tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_minimal.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9934039c..4726dbb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-python-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. +## [Unreleased] + +### Added + +- Non-catalog discounts on Transactions, see [changelog](https://developer.paddle.com/changelog/2025/custom-discounts?utm_source=dx&utm_medium=paddle-python-sdk) + ## 1.10.0 - 2025-08-15 ### Added diff --git a/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py b/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py index d96d5af1..70c765e9 100644 --- a/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py +++ b/paddle_billing/Resources/Transactions/Operations/CreateTransaction.py @@ -15,6 +15,9 @@ TransactionCreateItemWithPrice, CreateBillingDetails, ) +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) @dataclass @@ -31,3 +34,4 @@ class CreateTransaction(Operation): billing_details: CreateBillingDetails | None | Undefined = Undefined() billing_period: TimePeriod | None | Undefined = Undefined() checkout: Checkout | None | Undefined = Undefined() + discount: TransactionNonCatalogDiscount | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/Discount/TransactionNonCatalogDiscount.py b/paddle_billing/Resources/Transactions/Operations/Discount/TransactionNonCatalogDiscount.py new file mode 100644 index 00000000..fed004e4 --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/Discount/TransactionNonCatalogDiscount.py @@ -0,0 +1,17 @@ +from __future__ import annotations +from dataclasses import dataclass + +from paddle_billing.Undefined import Undefined +from paddle_billing.Entities.Shared.CustomData import CustomData +from paddle_billing.Entities.Discounts.DiscountType import DiscountType + + +@dataclass +class TransactionNonCatalogDiscount: + amount: str + description: str + type: DiscountType + recur: bool | Undefined = Undefined() + maximum_recurring_intervals: int | None | Undefined = Undefined() + custom_data: CustomData | None | Undefined = Undefined() + restrict_to: list[str] | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/Discount/__init__.py b/paddle_billing/Resources/Transactions/Operations/Discount/__init__.py new file mode 100644 index 00000000..0963c947 --- /dev/null +++ b/paddle_billing/Resources/Transactions/Operations/Discount/__init__.py @@ -0,0 +1,3 @@ +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py index a8d90e39..2fecd0bb 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransaction.py @@ -7,6 +7,9 @@ TransactionItemPreviewWithPriceId, TransactionItemPreviewWithNonCatalogPrice, ) +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) @dataclass @@ -16,3 +19,4 @@ class PreviewTransaction(Operation): currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() + discount: TransactionNonCatalogDiscount | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py index 8358869e..773e960f 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByAddress.py @@ -7,6 +7,9 @@ TransactionItemPreviewWithPriceId, TransactionItemPreviewWithNonCatalogPrice, ) +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) @dataclass @@ -17,3 +20,4 @@ class PreviewTransactionByAddress(Operation): currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() + discount: TransactionNonCatalogDiscount | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py index 87990877..fab2e8e4 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByCustomer.py @@ -7,6 +7,9 @@ TransactionItemPreviewWithPriceId, TransactionItemPreviewWithNonCatalogPrice, ) +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) @dataclass @@ -18,3 +21,4 @@ class PreviewTransactionByCustomer(Operation): currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() + discount: TransactionNonCatalogDiscount | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py index bc92c2c6..754a10dc 100644 --- a/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py +++ b/paddle_billing/Resources/Transactions/Operations/PreviewTransactionByIP.py @@ -7,6 +7,9 @@ TransactionItemPreviewWithPriceId, TransactionItemPreviewWithNonCatalogPrice, ) +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) @dataclass @@ -17,3 +20,4 @@ class PreviewTransactionByIP(Operation): currency_code: CurrencyCode | None | Undefined = Undefined() discount_id: str | None | Undefined = Undefined() ignore_trials: bool | Undefined = Undefined() + discount: TransactionNonCatalogDiscount | None | Undefined = Undefined() diff --git a/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py b/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py index 32fdc27a..de7b3c3b 100644 --- a/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py +++ b/paddle_billing/Resources/Transactions/Operations/UpdateTransaction.py @@ -15,6 +15,9 @@ TransactionUpdateItemWithPrice, UpdateBillingDetails, ) +from paddle_billing.Resources.Transactions.Operations.Discount.TransactionNonCatalogDiscount import ( + TransactionNonCatalogDiscount, +) @dataclass @@ -31,3 +34,4 @@ class UpdateTransaction(Operation): billing_details: UpdateBillingDetails | None | Undefined = Undefined() billing_period: TimePeriod | None | Undefined = Undefined() checkout: Checkout | None | Undefined = Undefined() + discount: TransactionNonCatalogDiscount | None | Undefined = Undefined() diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_full.json new file mode 100644 index 00000000..bf6a91cb --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_full.json @@ -0,0 +1,22 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv" + } + ], + "discount": { + "amount": "500", + "description": "Black Friday", + "type": "flat", + "recur": true, + "maximum_recurring_intervals": 3, + "custom_data": { + "source": "bf2025" + }, + "restrict_to": [ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb" + ] + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_minimal.json b/tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_minimal.json new file mode 100644 index 00000000..a813662d --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/create_with_noncatalog_discount_minimal.json @@ -0,0 +1,13 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv" + } + ], + "discount": { + "amount": "10", + "description": "Promo", + "type": "percentage" + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_full.json new file mode 100644 index 00000000..6daa6180 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_full.json @@ -0,0 +1,27 @@ +{ + "address": { + "postal_code": "12345", + "country_code": "US" + }, + "items": [ + { + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "quantity": 1, + "include_in_totals": true + } + ], + "discount": { + "amount": "500", + "description": "Black Friday", + "type": "flat", + "recur": true, + "maximum_recurring_intervals": 3, + "custom_data": { + "source": "bf2025" + }, + "restrict_to": [ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb" + ] + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_minimal.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_minimal.json new file mode 100644 index 00000000..609c2daf --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_address_with_noncatalog_discount_minimal.json @@ -0,0 +1,18 @@ +{ + "address": { + "postal_code": "12345", + "country_code": "US" + }, + "items": [ + { + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "quantity": 1, + "include_in_totals": true + } + ], + "discount": { + "amount": "5", + "description": "Black Friday", + "type": "percentage" + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_full.json new file mode 100644 index 00000000..9f792867 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_full.json @@ -0,0 +1,27 @@ +{ + "address_id": "add_01hv8h6jj90jjz0d71m6hj4r9z", + "customer_id": "ctm_01h844q4mznqpgqgm6evgw1w63", + "business_id": "biz_01hv8j0z17hv4ew8teebwjmfcb", + "currency_code": "USD", + "items": [ + { + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "quantity": 1, + "include_in_totals": true + } + ], + "discount": { + "amount": "500", + "description": "Black Friday", + "type": "flat", + "recur": true, + "maximum_recurring_intervals": 3, + "custom_data": { + "source": "bf2025" + }, + "restrict_to": [ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb" + ] + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_minimal.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_minimal.json new file mode 100644 index 00000000..f1e30633 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_customer_with_noncatalog_discount_minimal.json @@ -0,0 +1,16 @@ +{ + "address_id": "add_01hv8h6jj90jjz0d71m6hj4r9z", + "customer_id": "ctm_01h844q4mznqpgqgm6evgw1w63", + "items": [ + { + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "quantity": 1, + "include_in_totals": true + } + ], + "discount": { + "amount": "5", + "description": "Black Friday", + "type": "percentage" + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_full.json new file mode 100644 index 00000000..ad7bb64b --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_full.json @@ -0,0 +1,26 @@ +{ + "customer_ip_address": "203.0.113.0", + "customer_id": "ctm_01h844q4mznqpgqgm6evgw1w63", + "currency_code": "USD", + "items": [ + { + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "quantity": 1, + "include_in_totals": true + } + ], + "discount": { + "amount": "500", + "description": "Black Friday", + "type": "flat", + "recur": true, + "maximum_recurring_intervals": 3, + "custom_data": { + "source": "bf2025" + }, + "restrict_to": [ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb" + ] + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_minimal.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_minimal.json new file mode 100644 index 00000000..d99147ac --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_by_ip_with_noncatalog_discount_minimal.json @@ -0,0 +1,15 @@ +{ + "customer_ip_address": "203.0.113.0", + "items": [ + { + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "quantity": 1, + "include_in_totals": true + } + ], + "discount": { + "amount": "5", + "description": "Black Friday", + "type": "percentage" + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_full.json new file mode 100644 index 00000000..a10a47f1 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_full.json @@ -0,0 +1,23 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "include_in_totals": true + } + ], + "discount": { + "amount": "500", + "description": "Black Friday", + "type": "flat", + "recur": true, + "maximum_recurring_intervals": 3, + "custom_data": { + "source": "bf2025" + }, + "restrict_to": [ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb" + ] + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_minimal.json b/tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_minimal.json new file mode 100644 index 00000000..e7e19f30 --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/preview_with_noncatalog_discount_minimal.json @@ -0,0 +1,14 @@ +{ + "items": [ + { + "quantity": 1, + "price_id": "pri_01he5kxqey1k8ankgef29cj4bv", + "include_in_totals": true + } + ], + "discount": { + "amount": "5", + "description": "Black Friday", + "type": "percentage" + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_full.json b/tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_full.json new file mode 100644 index 00000000..4c668a3b --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_full.json @@ -0,0 +1,16 @@ +{ + "discount": { + "amount": "500", + "description": "Black Friday", + "type": "flat", + "recur": true, + "maximum_recurring_intervals": 3, + "custom_data": { + "source": "bf2025" + }, + "restrict_to": [ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb" + ] + } +} diff --git a/tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_minimal.json b/tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_minimal.json new file mode 100644 index 00000000..eb3d1abe --- /dev/null +++ b/tests/Functional/Resources/Transactions/_fixtures/request/update_with_noncatalog_discount_minimal.json @@ -0,0 +1,7 @@ +{ + "discount": { + "amount": "10", + "description": "Promo", + "type": "percentage" + } +} diff --git a/tests/Functional/Resources/Transactions/test_TransactionsClient.py b/tests/Functional/Resources/Transactions/test_TransactionsClient.py index 8f7c8efa..0f1b81a0 100644 --- a/tests/Functional/Resources/Transactions/test_TransactionsClient.py +++ b/tests/Functional/Resources/Transactions/test_TransactionsClient.py @@ -9,6 +9,7 @@ from paddle_billing.Entities.TransactionPreview import TransactionPreview from paddle_billing.Resources.Shared.Operations import Comparator, DateComparison, Pager from paddle_billing.Entities.Discount import Discount, DiscountStatus +from paddle_billing.Entities.Discounts.DiscountType import DiscountType from paddle_billing.Entities.Shared import ( AddressPreview, @@ -57,6 +58,7 @@ TransactionCreateItem, TransactionCreateItemWithPrice, ) +from paddle_billing.Resources.Transactions.Operations.Discount import TransactionNonCatalogDiscount from paddle_billing.Resources.Transactions.Operations.Update import ( UpdateBillingDetails, TransactionUpdateItem, @@ -256,6 +258,37 @@ def test_list_transaction_can_paginate(self, test_client, mock_requests, operati ReadsFixtures.read_raw_json_fixture("response/full_entity"), "/transactions", ), + ( + CreateTransaction( + items=[TransactionCreateItem(price_id="pri_01he5kxqey1k8ankgef29cj4bv", quantity=1)], + discount=TransactionNonCatalogDiscount(amount="10", description="Promo", type="percentage"), + ), + ReadsFixtures.read_raw_json_fixture("request/create_with_noncatalog_discount_minimal"), + 200, + ReadsFixtures.read_raw_json_fixture("response/minimal_entity"), + "/transactions", + ), + ( + CreateTransaction( + items=[TransactionCreateItem(price_id="pri_01he5kxqey1k8ankgef29cj4bv", quantity=1)], + discount=TransactionNonCatalogDiscount( + amount="500", + description="Black Friday", + type="flat", + recur=True, + maximum_recurring_intervals=3, + custom_data=CustomData({"source": "bf2025"}), + restrict_to=[ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb", + ], + ), + ), + ReadsFixtures.read_raw_json_fixture("request/create_with_noncatalog_discount_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/minimal_entity"), + "/transactions", + ), ], ids=[ "Create transaction with basic data", @@ -263,6 +296,8 @@ def test_list_transaction_can_paginate(self, test_client, mock_requests, operati "Create transaction with manual collection mode", "Create transaction with minimal billing details", "Create transaction with full billing details", + "Create transaction with non-catalog discount minimal", + "Create transaction with non-catalog discount full", ], ) def test_create_transaction_uses_expected_payload( @@ -455,6 +490,41 @@ def test_create_transaction_with_includes_returns_expected_payload( ReadsFixtures.read_raw_json_fixture("response/full_entity"), "/transactions/txn_01h7zcgmdc6tmwtjehp3sh7azf", ), + ( + "txn_01h7zcgmdc6tmwtjehp3sh7azf", + UpdateTransaction( + discount=TransactionNonCatalogDiscount( + amount="10", + description="Promo", + type=DiscountType.Percentage, + ) + ), + ReadsFixtures.read_raw_json_fixture("request/update_with_noncatalog_discount_minimal"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/transactions/txn_01h7zcgmdc6tmwtjehp3sh7azf", + ), + ( + "txn_01h7zcgmdc6tmwtjehp3sh7azf", + UpdateTransaction( + discount=TransactionNonCatalogDiscount( + amount="500", + description="Black Friday", + type=DiscountType.Flat, + recur=True, + maximum_recurring_intervals=3, + custom_data=CustomData({"source": "bf2025"}), + restrict_to=[ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb", + ], + ) + ), + ReadsFixtures.read_raw_json_fixture("request/update_with_noncatalog_discount_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/full_entity"), + "/transactions/txn_01h7zcgmdc6tmwtjehp3sh7azf", + ), ], ids=[ "Update transaction with single new value", @@ -462,6 +532,8 @@ def test_create_transaction_with_includes_returns_expected_payload( "Create transaction with minimal billing details", "Create transaction with full billing details", "Create transaction with items", + "Update with non-catalog discount minimal", + "Update with non-catalog discount full", ], ) def test_update_transaction_uses_expected_payload( @@ -1136,6 +1208,196 @@ def test_get_transaction_with_and_without_payment_method_id( ReadsFixtures.read_raw_json_fixture("response/preview_entity"), "/transactions/preview", ), + ( + PreviewTransactionByAddress( + address=AddressPreview( + postal_code="12345", + country_code=CountryCode.US, + ), + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount(amount="5", description="Black Friday", type="percentage"), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_by_address_with_noncatalog_discount_minimal"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransactionByAddress( + address=AddressPreview( + postal_code="12345", + country_code=CountryCode.US, + ), + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount( + amount="500", + description="Black Friday", + type="flat", + recur=True, + maximum_recurring_intervals=3, + custom_data=CustomData({"source": "bf2025"}), + restrict_to=[ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb", + ], + ), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_by_address_with_noncatalog_discount_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransactionByCustomer( + address_id="add_01hv8h6jj90jjz0d71m6hj4r9z", + customer_id="ctm_01h844q4mznqpgqgm6evgw1w63", + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount(amount="5", description="Black Friday", type="percentage"), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_by_customer_with_noncatalog_discount_minimal"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransactionByCustomer( + address_id="add_01hv8h6jj90jjz0d71m6hj4r9z", + customer_id="ctm_01h844q4mznqpgqgm6evgw1w63", + business_id="biz_01hv8j0z17hv4ew8teebwjmfcb", + currency_code=CurrencyCode.USD, + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount( + amount="500", + description="Black Friday", + type="flat", + recur=True, + maximum_recurring_intervals=3, + custom_data=CustomData({"source": "bf2025"}), + restrict_to=[ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb", + ], + ), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_by_customer_with_noncatalog_discount_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransactionByIP( + customer_ip_address="203.0.113.0", + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount(amount="5", description="Black Friday", type="percentage"), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_by_ip_with_noncatalog_discount_minimal"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransactionByIP( + customer_ip_address="203.0.113.0", + customer_id="ctm_01h844q4mznqpgqgm6evgw1w63", + currency_code=CurrencyCode.USD, + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount( + amount="500", + description="Black Friday", + type="flat", + recur=True, + maximum_recurring_intervals=3, + custom_data=CustomData({"source": "bf2025"}), + restrict_to=[ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb", + ], + ), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_by_ip_with_noncatalog_discount_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransaction( + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount(amount="5", description="Black Friday", type="percentage"), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_with_noncatalog_discount_minimal"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), + ( + PreviewTransaction( + items=[ + TransactionItemPreviewWithPriceId( + price_id="pri_01he5kxqey1k8ankgef29cj4bv", + quantity=1, + include_in_totals=True, + ) + ], + discount=TransactionNonCatalogDiscount( + amount="500", + description="Black Friday", + type="flat", + recur=True, + maximum_recurring_intervals=3, + custom_data=CustomData({"source": "bf2025"}), + restrict_to=[ + "pro_01gsz4t5hdjse780zja8vvr7jg", + "pro_01gsz4s0w61y0pp88528f1wvvb", + ], + ), + ), + ReadsFixtures.read_raw_json_fixture("request/preview_with_noncatalog_discount_full"), + 200, + ReadsFixtures.read_raw_json_fixture("response/preview_entity"), + "/transactions/preview", + ), ( PreviewTransactionByIP( customer_ip_address="203.0.113.0", @@ -1221,6 +1483,14 @@ def test_get_transaction_with_and_without_payment_method_id( "Full transaction preview by customer", "Basic transaction preview by IP", "Full transaction preview by IP", + "Preview by address with non-catalog discount minimal", + "Preview by address with non-catalog discount full", + "Preview by customer with non-catalog discount minimal", + "Preview by customer with non-catalog discount full", + "Preview by IP with non-catalog discount minimal", + "Preview by IP with non-catalog discount full", + "Preview transaction with non-catalog discount minimal", + "Preview transaction with non-catalog discount full", ], ) def test_preview_transaction_returns_expected_response(