diff --git a/README.md b/README.md index 24dfa085..1fa307c5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Apple App Store Server Python Library -The [Python](https://github.com/apple/app-store-server-library-python) server library for the [App Store Server API](https://developer.apple.com/documentation/appstoreserverapi), [App Store Server Notifications](https://developer.apple.com/documentation/appstoreservernotifications), and [Retention Messaging API](https://developer.apple.com/documentation/retentionmessaging). Also available in [Swift](https://github.com/apple/app-store-server-library-swift), [Node.js](https://github.com/apple/app-store-server-library-node), and [Java](https://github.com/apple/app-store-server-library-java). +The [Python](https://github.com/apple/app-store-server-library-python) server library for the [App Store Server API](https://developer.apple.com/documentation/appstoreserverapi), [App Store Server Notifications](https://developer.apple.com/documentation/appstoreservernotifications), [Retention Messaging API](https://developer.apple.com/documentation/retentionmessaging), and [Advanced Commerce API](https://developer.apple.com/documentation/AdvancedCommerceAPI). Also available in [Swift](https://github.com/apple/app-store-server-library-swift), [Node.js](https://github.com/apple/app-store-server-library-node), and [Java](https://github.com/apple/app-store-server-library-java). ## Table of Contents 1. [Installation](#installation) diff --git a/appstoreserverlibrary/models/AbstractAdvancedCommerceBaseItem.py b/appstoreserverlibrary/models/AbstractAdvancedCommerceBaseItem.py new file mode 100644 index 00000000..b6b4dd50 --- /dev/null +++ b/appstoreserverlibrary/models/AbstractAdvancedCommerceBaseItem.py @@ -0,0 +1,18 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from abc import ABC + +from attr import define +import attr + +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils +from .LibraryUtility import AttrsRawValueAware + +@define +class AbstractAdvancedCommerceBaseItem(AttrsRawValueAware, ABC): + SKU: str = attr.ib(validator=AdvancedCommerceValidationUtils.sku_validator) + """ + The product identifier of an in-app purchase product you manage in your own system. + + https://developer.apple.com/documentation/advancedcommerceapi/sku + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AbstractAdvancedCommerceInAppRequest.py b/appstoreserverlibrary/models/AbstractAdvancedCommerceInAppRequest.py new file mode 100644 index 00000000..01295251 --- /dev/null +++ b/appstoreserverlibrary/models/AbstractAdvancedCommerceInAppRequest.py @@ -0,0 +1,14 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from abc import ABC + +from attr import define +import attr + +from .AdvancedCommerceRequest import AdvancedCommerceRequest +from ..jws_signature_creator import AdvancedCommerceAPIInAppRequest + +@define +class AbstractAdvancedCommerceInAppRequest(AdvancedCommerceRequest, AdvancedCommerceAPIInAppRequest, ABC): + operation: str = attr.ib() + version: str = attr.ib() \ No newline at end of file diff --git a/appstoreserverlibrary/models/AbstractAdvancedCommerceItem.py b/appstoreserverlibrary/models/AbstractAdvancedCommerceItem.py new file mode 100644 index 00000000..9013bad1 --- /dev/null +++ b/appstoreserverlibrary/models/AbstractAdvancedCommerceItem.py @@ -0,0 +1,23 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +import attr + +from .AbstractAdvancedCommerceBaseItem import AbstractAdvancedCommerceBaseItem +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AbstractAdvancedCommerceItem(AbstractAdvancedCommerceBaseItem): + description: str = attr.ib(validator=AdvancedCommerceValidationUtils.description_validator) + """ + A string you provide that describes a SKU. + + https://developer.apple.com/documentation/advancedcommerceapi/description + """ + + displayName: str = attr.ib(validator=AdvancedCommerceValidationUtils.display_name_validator) + """ + A string with a product name that you can localize and is suitable for display to customers. + + https://developer.apple.com/documentation/advancedcommerceapi/displayname + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AbstractAdvancedCommerceResponse.py b/appstoreserverlibrary/models/AbstractAdvancedCommerceResponse.py new file mode 100644 index 00000000..6f6125d2 --- /dev/null +++ b/appstoreserverlibrary/models/AbstractAdvancedCommerceResponse.py @@ -0,0 +1,23 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from abc import ABC +from typing import Optional + +from attr import define +import attr + +@define +class AbstractAdvancedCommerceResponse(ABC): + signedRenewalInfo: Optional[str] = attr.ib(default=None) + """ + Subscription renewal information, signed by the App Store, in JSON Web Signature (JWS) format. + + https://developer.apple.com/documentation/appstoreserverapi/jwsrenewalinfo + """ + + signedTransactionInfo: Optional[str] = attr.ib(default=None) + """ + Transaction information signed by the App Store, in JSON Web Signature (JWS) Compact Serialization format. + + https://developer.apple.com/documentation/appstoreserverapi/jwstransaction + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceDescriptors.py b/appstoreserverlibrary/models/AdvancedCommerceDescriptors.py new file mode 100644 index 00000000..9ff9354f --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceDescriptors.py @@ -0,0 +1,27 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +import attr + +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceDescriptors: + """ + The display name and description of a subscription product. + + https://developer.apple.com/documentation/advancedcommerceapi/descriptors + """ + description: str = attr.ib(validator=AdvancedCommerceValidationUtils.description_validator) + """ + A string you provide that describes a SKU. + + https://developer.apple.com/documentation/advancedcommerceapi/description + """ + + displayName: str = attr.ib(validator=AdvancedCommerceValidationUtils.display_name_validator) + """ + A string with a product name that you can localize and is suitable for display to customers. + + https://developer.apple.com/documentation/advancedcommerceapi/displayname + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceEffective.py b/appstoreserverlibrary/models/AdvancedCommerceEffective.py new file mode 100644 index 00000000..b694fa74 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceEffective.py @@ -0,0 +1,14 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommerceEffective(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + A string value that indicates when a requested change to an auto-renewable subscription goes into effect. + + https://developer.apple.com/documentation/advancedcommerceapi/effective + """ + IMMEDIATELY = "IMMEDIATELY" + NEXT_BILL_CYCLE = "NEXT_BILL_CYCLE" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceOffer.py b/appstoreserverlibrary/models/AdvancedCommerceOffer.py new file mode 100644 index 00000000..50f7d092 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceOffer.py @@ -0,0 +1,50 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. +from typing import Optional + +from attr import define +import attr + +from .AdvancedCommerceOfferPeriod import AdvancedCommerceOfferPeriod +from .AdvancedCommerceOfferReason import AdvancedCommerceOfferReason +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils +from .LibraryUtility import AttrsRawValueAware + +@define +class AdvancedCommerceOffer(AttrsRawValueAware): + """ + A discount offer for an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/offer + """ + + periodCount: int = attr.ib(validator=AdvancedCommerceValidationUtils.period_count_validator) + """ + The number of periods the offer is active. + """ + + price: int = attr.ib() + """ + The offer price, in milliunits. + + https://developer.apple.com/documentation/advancedcommerceapi/price + """ + + period: AdvancedCommerceOfferPeriod = AdvancedCommerceOfferPeriod.create_main_attr('rawPeriod', raw_required=True) + """ + The period of the offer. + """ + + rawPeriod: str = AdvancedCommerceOfferPeriod.create_raw_attr('period', required=True) + """ + See period + """ + + reason: AdvancedCommerceOfferReason = AdvancedCommerceOfferReason.create_main_attr('rawReason', raw_required=True) + """ + The reason for the offer. + """ + + rawReason: str = AdvancedCommerceOfferReason.create_raw_attr('reason', required=True) + """ + See reason + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceOfferPeriod.py b/appstoreserverlibrary/models/AdvancedCommerceOfferPeriod.py new file mode 100644 index 00000000..0946b48f --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceOfferPeriod.py @@ -0,0 +1,21 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommerceOfferPeriod(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + The period of the offer. + + https://developer.apple.com/documentation/advancedcommerceapi/offer + """ + P3D = "P3D" + P1W = "P1W" + P2W = "P2W" + P1M = "P1M" + P2M = "P2M" + P3M = "P3M" + P6M = "P6M" + P9M = "P9M" + P1Y = "P1Y" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceOfferReason.py b/appstoreserverlibrary/models/AdvancedCommerceOfferReason.py new file mode 100644 index 00000000..0d4286e3 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceOfferReason.py @@ -0,0 +1,15 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommerceOfferReason(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + The reason for the offer. + + https://developer.apple.com/documentation/advancedcommerceapi/offer + """ + ACQUISITION = "ACQUISITION" + WIN_BACK = "WIN_BACK" + RETENTION = "RETENTION" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceOneTimeChargeCreateRequest.py b/appstoreserverlibrary/models/AdvancedCommerceOneTimeChargeCreateRequest.py new file mode 100644 index 00000000..6c30527a --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceOneTimeChargeCreateRequest.py @@ -0,0 +1,55 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional + +from attr import define +import attr + +from .AbstractAdvancedCommerceInAppRequest import AbstractAdvancedCommerceInAppRequest +from .AdvancedCommerceOneTimeChargeItem import AdvancedCommerceOneTimeChargeItem + +@define +class AdvancedCommerceOneTimeChargeCreateRequest(AbstractAdvancedCommerceInAppRequest): + """ + The request data your app provides when a customer purchases a one-time-charge product. + + https://developer.apple.com/documentation/advancedcommerceapi/onetimechargecreaterequest + """ + + currency: str = attr.ib() + """ + The currency of the price of the product. + + https://developer.apple.com/documentation/advancedcommerceapi/currency + """ + + item: AdvancedCommerceOneTimeChargeItem = attr.ib() + """ + The details of the product for purchase. + + https://developer.apple.com/documentation/advancedcommerceapi/onetimechargeitem + """ + + taxCode: str = attr.ib() + """ + The tax code for this product. + + https://developer.apple.com/documentation/advancedcommerceapi/taxCode + """ + + operation: str = attr.ib(init=False, default="CREATE_ONE_TIME_CHARGE", on_setattr=attr.setters.frozen) + """ + The constant that represents the operation of this request. + """ + + version: str = attr.ib(init=False, default="1", on_setattr=attr.setters.frozen) + """ + The version number of the API. + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + The storefront for the transaction. + + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceOneTimeChargeItem.py b/appstoreserverlibrary/models/AdvancedCommerceOneTimeChargeItem.py new file mode 100644 index 00000000..514e8771 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceOneTimeChargeItem.py @@ -0,0 +1,21 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +import attr + +from .AbstractAdvancedCommerceItem import AbstractAdvancedCommerceItem + +@define +class AdvancedCommerceOneTimeChargeItem(AbstractAdvancedCommerceItem): + """ + The details of a one-time charge product, including its display name, price, SKU, and metadata. + + https://developer.apple.com/documentation/advancedcommerceapi/onetimechargeitem + """ + + price: int = attr.ib() + """ + The price, in milliunits of the currency, of the one-time charge product. + + https://developer.apple.com/documentation/advancedcommerceapi/price + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommercePeriod.py b/appstoreserverlibrary/models/AdvancedCommercePeriod.py new file mode 100644 index 00000000..4b0a623e --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommercePeriod.py @@ -0,0 +1,19 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommercePeriod(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + The duration of a single cycle of an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/period + """ + + P1W = "P1W" + P1M = "P1M" + P2M = "P2M" + P3M = "P3M" + P6M = "P6M" + P1Y = "P1Y" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceReason.py b/appstoreserverlibrary/models/AdvancedCommerceReason.py new file mode 100644 index 00000000..cb41c2c1 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceReason.py @@ -0,0 +1,15 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommerceReason(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + The data your app provides to change an item of an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifychangeitem + """ + UPGRADE = "UPGRADE" + DOWNGRADE = "DOWNGRADE" + APPLY_OFFER = "APPLY_OFFER" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRefundReason.py b/appstoreserverlibrary/models/AdvancedCommerceRefundReason.py new file mode 100644 index 00000000..975db70d --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRefundReason.py @@ -0,0 +1,20 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommerceRefundReason(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + A reason to request a refund. + + https://developer.apple.com/documentation/advancedcommerceapi/refundreason + """ + + UNINTENDED_PURCHASE = "UNINTENDED_PURCHASE" + FULFILLMENT_ISSUE = "FULFILLMENT_ISSUE" + UNSATISFIED_WITH_PURCHASE = "UNSATISFIED_WITH_PURCHASE" + LEGAL = "LEGAL" + OTHER = "OTHER" + MODIFY_ITEMS_REFUND = "MODIFY_ITEMS_REFUND" + SIMULATE_REFUND_DECLINE = "SIMULATE_REFUND_DECLINE" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRefundType.py b/appstoreserverlibrary/models/AdvancedCommerceRefundType.py new file mode 100644 index 00000000..445a72d9 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRefundType.py @@ -0,0 +1,15 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from enum import Enum + +from .LibraryUtility import AppStoreServerLibraryEnumMeta + +class AdvancedCommerceRefundType(str, Enum, metaclass=AppStoreServerLibraryEnumMeta): + """ + Information about the refund request for an item, such as its SKU, the refund amount, reason, and type. + + https://developer.apple.com/documentation/advancedcommerceapi/requestrefunditem + """ + FULL = "FULL" + PRORATED = "PRORATED" + CUSTOM = "CUSTOM" \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRequest.py b/appstoreserverlibrary/models/AdvancedCommerceRequest.py new file mode 100644 index 00000000..fbcf66b7 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRequest.py @@ -0,0 +1,18 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from abc import ABC + +from attr import define +import attr + +from .AdvancedCommerceRequestInfo import AdvancedCommerceRequestInfo +from .LibraryUtility import AttrsRawValueAware + +@define +class AdvancedCommerceRequest(AttrsRawValueAware, ABC): + requestInfo: AdvancedCommerceRequestInfo = attr.ib() + """ + The metadata to include in server requests. + + https://developer.apple.com/documentation/advancedcommerceapi/requestinfo + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRequestInfo.py b/appstoreserverlibrary/models/AdvancedCommerceRequestInfo.py new file mode 100644 index 00000000..98386e18 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRequestInfo.py @@ -0,0 +1,32 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional +from uuid import UUID + +from attr import define +import attr + +@define +class AdvancedCommerceRequestInfo: + """ + The metadata to include in server requests. + + https://developer.apple.com/documentation/advancedcommerceapi/requestinfo + """ + + requestReferenceId: UUID = attr.ib() + """ + A UUID that you provide to uniquely identify each request. If the request times out, you can use the same requestReferenceId value to retry the request. Otherwise, provide a unique value. + """ + + appAccountToken: Optional[UUID] = attr.ib(default=None) + """ + A UUID that represents an app account token, to associate with the transaction in the request. + """ + + consistencyToken: Optional[str] = attr.ib(default=None) + """ + The value of the advancedCommerceConsistencyToken that you receive in the JWSRenewalInfo renewal information for a subscription. Don’t generate this value. + + https://developer.apple.com/documentation/AppStoreServerAPI/advancedCommerceConsistencyToken + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRequestRefundItem.py b/appstoreserverlibrary/models/AdvancedCommerceRequestRefundItem.py new file mode 100644 index 00000000..07a62205 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRequestRefundItem.py @@ -0,0 +1,49 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional + +from attr import define +import attr + +from .AbstractAdvancedCommerceBaseItem import AbstractAdvancedCommerceBaseItem +from .AdvancedCommerceRefundReason import AdvancedCommerceRefundReason +from .AdvancedCommerceRefundType import AdvancedCommerceRefundType + +@define +class AdvancedCommerceRequestRefundItem(AbstractAdvancedCommerceBaseItem): + """ + Information about the refund request for an item, such as its SKU, the refund amount, reason, and type. + + https://developer.apple.com/documentation/advancedcommerceapi/requestrefunditem + """ + + revoke: bool = attr.ib() + + refundReason: AdvancedCommerceRefundReason = AdvancedCommerceRefundReason.create_main_attr('rawRefundReason', raw_required=True) + """ + The reason for the refund request. + + https://developer.apple.com/documentation/advancedcommerceapi/refundreason + """ + + rawRefundReason: str = AdvancedCommerceRefundReason.create_raw_attr('refundReason', required=True) + """ + See refundReason + """ + + refundType: AdvancedCommerceRefundType = AdvancedCommerceRefundType.create_main_attr('rawRefundType', raw_required=True) + """ + The type of refund requested. + """ + + rawRefundType: str = AdvancedCommerceRefundType.create_raw_attr('refundType', required=True) + """ + See refundType + """ + + refundAmount: Optional[int] = attr.ib(default=None) + """ + The refund amount you're requesting for the SKU, in milliunits of the currency. + + https://developer.apple.com/documentation/advancedcommerceapi/refundamount + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRequestRefundRequest.py b/appstoreserverlibrary/models/AdvancedCommerceRequestRefundRequest.py new file mode 100644 index 00000000..6236d38b --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRequestRefundRequest.py @@ -0,0 +1,40 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional, List + +from attr import define +import attr + +from .AdvancedCommerceRequest import AdvancedCommerceRequest +from .AdvancedCommerceRequestRefundItem import AdvancedCommerceRequestRefundItem +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceRequestRefundRequest(AdvancedCommerceRequest): + """ + The request body for requesting a refund for a transaction. + + https://developer.apple.com/documentation/advancedcommerceapi/requestrefundrequest + """ + + items: List[AdvancedCommerceRequestRefundItem] = attr.ib(validator=AdvancedCommerceValidationUtils.items_validator) + """ + https://developer.apple.com/documentation/advancedcommerceapi/requestrefunditem + """ + + refundRiskingPreference: bool = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/refundriskingpreference + """ + + currency: Optional[str] = attr.ib(default=None) + """ + The currency of the transaction. + + https://developer.apple.com/documentation/advancedcommerceapi/currency + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceRequestRefundResponse.py b/appstoreserverlibrary/models/AdvancedCommerceRequestRefundResponse.py new file mode 100644 index 00000000..03e9a1ff --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceRequestRefundResponse.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from .AbstractAdvancedCommerceResponse import AbstractAdvancedCommerceResponse + +class AdvancedCommerceRequestRefundResponse(AbstractAdvancedCommerceResponse): + """ + The response body for a transaction refund request. + + https://developer.apple.com/documentation/advancedcommerceapi/requestrefundresponse + """ + + def __init__(self, signedTransactionInfo: str): + super().__init__(signedRenewalInfo=None, signedTransactionInfo=signedTransactionInfo) \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCancelRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCancelRequest.py new file mode 100644 index 00000000..db6e8317 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCancelRequest.py @@ -0,0 +1,21 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional + +from attr import define +import attr + +from .AdvancedCommerceRequest import AdvancedCommerceRequest + +@define +class AdvancedCommerceSubscriptionCancelRequest(AdvancedCommerceRequest): + """ + The request body for turning off automatic renewal of a subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptioncancelrequest + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCancelResponse.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCancelResponse.py new file mode 100644 index 00000000..b0f7a246 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCancelResponse.py @@ -0,0 +1,14 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define + +from .AbstractAdvancedCommerceResponse import AbstractAdvancedCommerceResponse + +@define +class AdvancedCommerceSubscriptionCancelResponse(AbstractAdvancedCommerceResponse): + """ + The response body for a successful subscription cancellation. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptioncancelresponse + """ + pass \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataDescriptors.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataDescriptors.py new file mode 100644 index 00000000..1f960d4d --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataDescriptors.py @@ -0,0 +1,48 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. +from typing import Optional + +from attr import define +import attr + +from .AdvancedCommerceEffective import AdvancedCommerceEffective +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceSubscriptionChangeMetadataDescriptors(): + """ + The subscription metadata to change, specifically the description and display name. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionchangemetadatadescriptors + """ + + effective: AdvancedCommerceEffective = AdvancedCommerceEffective.create_main_attr('rawEffective', raw_required=True) + """ + The string that determines when the metadata change goes into effect. + + https://developer.apple.com/documentation/advancedcommerceapi/effective + """ + + rawEffective: str = AdvancedCommerceEffective.create_raw_attr('effective', required=True) + """ + See effective + """ + + description: Optional[str] = attr.ib( + default=None, + validator=attr.validators.optional(AdvancedCommerceValidationUtils.description_validator) + ) + """ + The new description for the subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/description + """ + + displayName: Optional[str] = attr.ib( + default=None, + validator=attr.validators.optional(AdvancedCommerceValidationUtils.display_name_validator) + ) + """ + The new display name for the subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/displayname + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataItem.py new file mode 100644 index 00000000..2c5a829b --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataItem.py @@ -0,0 +1,54 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. +from typing import Optional + +from attr import define +import attr + +from .AdvancedCommerceEffective import AdvancedCommerceEffective +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceSubscriptionChangeMetadataItem(): + """ + The metadata to change for an item, specifically its SKU, description, and display name. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionchangemetadataitem + """ + + currentSKU: str = attr.ib(validator=AdvancedCommerceValidationUtils.sku_validator) + """ + The original SKU of the item. + """ + + effective: AdvancedCommerceEffective = AdvancedCommerceEffective.create_main_attr('rawEffective', raw_required=True) + """ + The string that determines when the metadata change goes into effect. + + https://developer.apple.com/documentation/advancedcommerceapi/effective + """ + + rawEffective: str = AdvancedCommerceEffective.create_raw_attr('effective', required=True) + """ + See effective + """ + + description: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.description_validator)) + """ + The new description for the item. + + https://developer.apple.com/documentation/advancedcommerceapi/description + """ + + displayName: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.display_name_validator)) + """ + The new display name for the item. + + https://developer.apple.com/documentation/advancedcommerceapi/displayname + """ + + SKU: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.sku_validator)) + """ + The new SKU of the item. + + https://developer.apple.com/documentation/advancedcommerceapi/sku + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataRequest.py new file mode 100644 index 00000000..19d8c1dc --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataRequest.py @@ -0,0 +1,39 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional, List + +from attr import define +import attr + +from .AdvancedCommerceRequest import AdvancedCommerceRequest +from .AdvancedCommerceSubscriptionChangeMetadataDescriptors import AdvancedCommerceSubscriptionChangeMetadataDescriptors +from .AdvancedCommerceSubscriptionChangeMetadataItem import AdvancedCommerceSubscriptionChangeMetadataItem +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceSubscriptionChangeMetadataRequest(AdvancedCommerceRequest): + """ + The request body you provide to change the metadata of a subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionchangemetadatarequest + """ + + descriptors: Optional[AdvancedCommerceSubscriptionChangeMetadataDescriptors] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionchangemetadatadescriptors + """ + + items: Optional[List[AdvancedCommerceSubscriptionChangeMetadataItem]] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.items_validator)) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionchangemetadataitem + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ + + taxCode: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/taxcode + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataResponse.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataResponse.py new file mode 100644 index 00000000..b7c157d6 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionChangeMetadataResponse.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from .AbstractAdvancedCommerceResponse import AbstractAdvancedCommerceResponse + +class AdvancedCommerceSubscriptionChangeMetadataResponse(AbstractAdvancedCommerceResponse): + """ + The response body for a successful subscription metadata change. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionchangemetadataresponse + """ + + def __init__(self, signedRenewalInfo: str, signedTransactionInfo: str): + super().__init__(signedRenewalInfo=signedRenewalInfo, signedTransactionInfo=signedTransactionInfo) \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCreateItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCreateItem.py new file mode 100644 index 00000000..aab39cd5 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCreateItem.py @@ -0,0 +1,26 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional +import attr +from attr import define +from .AbstractAdvancedCommerceItem import AbstractAdvancedCommerceItem +from .AdvancedCommerceOffer import AdvancedCommerceOffer + + +@define +class AdvancedCommerceSubscriptionCreateItem(AbstractAdvancedCommerceItem): + """ + The data that describes a subscription item. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptioncreateitem + """ + + price: int = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/price + """ + + offer: Optional[AdvancedCommerceOffer] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/offer + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCreateRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCreateRequest.py new file mode 100644 index 00000000..85bb8d6a --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionCreateRequest.py @@ -0,0 +1,62 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional, List +import attr +from attr import define +from .AbstractAdvancedCommerceInAppRequest import AbstractAdvancedCommerceInAppRequest +from .AdvancedCommerceDescriptors import AdvancedCommerceDescriptors +from .AdvancedCommerceSubscriptionCreateItem import AdvancedCommerceSubscriptionCreateItem +from .AdvancedCommercePeriod import AdvancedCommercePeriod + + +@define +class AdvancedCommerceSubscriptionCreateRequest(AbstractAdvancedCommerceInAppRequest): + """ + The request data your app provides when a customer purchases an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptioncreaterequest + """ + + operation: str = attr.ib(init=False, default="CREATE_SUBSCRIPTION", on_setattr=attr.setters.frozen) + + version: str = attr.ib(init=False, default="1", on_setattr=attr.setters.frozen) + + currency: str = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/currency + """ + + descriptors: AdvancedCommerceDescriptors = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/descriptors + """ + + items: List[AdvancedCommerceSubscriptionCreateItem] = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptioncreateitem + """ + + taxCode: str = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/taxCode + """ + + period: AdvancedCommercePeriod = AdvancedCommercePeriod.create_main_attr('rawPeriod', raw_required=True) + """ + https://developer.apple.com/documentation/advancedcommerceapi/period + """ + + rawPeriod: str = AdvancedCommercePeriod.create_raw_attr('period', required=True) + """ + See period + """ + + previousTransactionId: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/transactionid + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateDescriptors.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateDescriptors.py new file mode 100644 index 00000000..1402cc27 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateDescriptors.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from .AdvancedCommerceDescriptors import AdvancedCommerceDescriptors + + +@define +class AdvancedCommerceSubscriptionMigrateDescriptors(AdvancedCommerceDescriptors): + """ + The description and display name of the subscription to migrate to that you manage. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigratedescriptors + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateItem.py new file mode 100644 index 00000000..49838a41 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateItem.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from .AbstractAdvancedCommerceItem import AbstractAdvancedCommerceItem + + +@define +class AdvancedCommerceSubscriptionMigrateItem(AbstractAdvancedCommerceItem): + """ + The SKU, description, and display name to use for a migrated subscription item. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigrateitem + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateRenewalItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateRenewalItem.py new file mode 100644 index 00000000..0c4af397 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateRenewalItem.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from .AbstractAdvancedCommerceItem import AbstractAdvancedCommerceItem + + +@define +class AdvancedCommerceSubscriptionMigrateRenewalItem(AbstractAdvancedCommerceItem): + """ + The item information that replaces a migrated subscription item when the subscription renews. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigraterenewalitem + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateRequest.py new file mode 100644 index 00000000..aafb3622 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateRequest.py @@ -0,0 +1,54 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional, List +import attr +from attr import define +from .AdvancedCommerceRequest import AdvancedCommerceRequest +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils +from .AdvancedCommerceSubscriptionMigrateDescriptors import AdvancedCommerceSubscriptionMigrateDescriptors +from .AdvancedCommerceSubscriptionMigrateItem import AdvancedCommerceSubscriptionMigrateItem +from .AdvancedCommerceSubscriptionMigrateRenewalItem import AdvancedCommerceSubscriptionMigrateRenewalItem + +@define +class AdvancedCommerceSubscriptionMigrateRequest(AdvancedCommerceRequest): + """ + The subscription details you provide to migrate a subscription from In-App Purchase to the Advanced Commerce API, such as descriptors, items, storefront, and more. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigraterequest + """ + + descriptors: AdvancedCommerceSubscriptionMigrateDescriptors = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigratedescriptors + """ + + items: List[AdvancedCommerceSubscriptionMigrateItem] = attr.ib(validator=AdvancedCommerceValidationUtils.items_validator) + """ + An array of one or more SKUs, along with descriptions and display names, that are included in the subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigrateitem + """ + + targetProductId: str = attr.ib() + """ + Your generic product ID for an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/targetproductid + """ + + taxCode: str = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/taxcode + """ + + renewalItems: Optional[List[AdvancedCommerceSubscriptionMigrateRenewalItem]] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.items_validator)) + """ + An optional array of subscription items that represents the items that renew at the next renewal period, if they differ from items. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigraterenewalitem + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateResponse.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateResponse.py new file mode 100644 index 00000000..152b0d22 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionMigrateResponse.py @@ -0,0 +1,15 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from .AbstractAdvancedCommerceResponse import AbstractAdvancedCommerceResponse + + +@define +class AdvancedCommerceSubscriptionMigrateResponse(AbstractAdvancedCommerceResponse): + """ + A response that contains signed renewal and transaction information after a subscription successfully migrates to the Advanced Commerce API. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmigrateresponse + """ + def __init__(self, signedRenewalInfo: str, signedTransactionInfo: str): + super().__init__(signedRenewalInfo=signedRenewalInfo, signedTransactionInfo=signedTransactionInfo) \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyAddItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyAddItem.py new file mode 100644 index 00000000..a1470659 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyAddItem.py @@ -0,0 +1,33 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional +import attr +from attr import define +from .AbstractAdvancedCommerceItem import AbstractAdvancedCommerceItem +from .AdvancedCommerceOffer import AdvancedCommerceOffer + + +@define +class AdvancedCommerceSubscriptionModifyAddItem(AbstractAdvancedCommerceItem): + """ + The data your app provides to add items when it makes changes to an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyadditem + """ + + price: int = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/price + """ + + offer: Optional[AdvancedCommerceOffer] = attr.ib(default=None) + """ + A discount offer for an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/offer + """ + + proratedPrice: Optional[int] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/proratedprice + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyChangeItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyChangeItem.py new file mode 100644 index 00000000..dbc5228e --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyChangeItem.py @@ -0,0 +1,56 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional +import attr +from attr import define +from .AbstractAdvancedCommerceItem import AbstractAdvancedCommerceItem +from .AdvancedCommerceEffective import AdvancedCommerceEffective +from .AdvancedCommerceOffer import AdvancedCommerceOffer +from .AdvancedCommerceReason import AdvancedCommerceReason +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + + +@define +class AdvancedCommerceSubscriptionModifyChangeItem(AbstractAdvancedCommerceItem): + """ + The data your app provides to change an item of an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifychangeitem + """ + + currentSKU: str = attr.ib(validator=AdvancedCommerceValidationUtils.sku_validator) + """ + https://developer.apple.com/documentation/advancedcommerceapi/sku + """ + + price: int = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/price + """ + + reason: AdvancedCommerceReason = AdvancedCommerceReason.create_main_attr('rawReason', raw_required=True) + + rawReason: str = AdvancedCommerceReason.create_raw_attr('reason', required=True) + """ + See reason + """ + + effective: AdvancedCommerceEffective = AdvancedCommerceEffective.create_main_attr('rawEffective', raw_required=True) + """ + https://developer.apple.com/documentation/advancedcommerceapi/effective + """ + + rawEffective: str = AdvancedCommerceEffective.create_raw_attr('effective', required=True) + """ + See effective + """ + + offer: Optional[AdvancedCommerceOffer] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/offer + """ + + proratedPrice: Optional[int] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/proratedprice + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyDescriptors.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyDescriptors.py new file mode 100644 index 00000000..d44d4990 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyDescriptors.py @@ -0,0 +1,36 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. +from typing import Optional + +import attr +from attr import define +from .AdvancedCommerceEffective import AdvancedCommerceEffective +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceSubscriptionModifyDescriptors(): + """ + The data your app provides to change the description and display name of an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifydescriptors + """ + + effective: AdvancedCommerceEffective = AdvancedCommerceEffective.create_main_attr('rawEffective', raw_required=True) + """ + https://developer.apple.com/documentation/advancedcommerceapi/effective + """ + + rawEffective: str = AdvancedCommerceEffective.create_raw_attr('effective', required=True) + """ + See effective + """ + + description: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.description_validator)) + """ + https://developer.apple.com/documentation/advancedcommerceapi/description + """ + + displayName: Optional[str] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.display_name_validator) + ) + """ + https://developer.apple.com/documentation/advancedcommerceapi/displayname + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyInAppRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyInAppRequest.py new file mode 100644 index 00000000..bec1d218 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyInAppRequest.py @@ -0,0 +1,75 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional, List +import attr +from attr import define +from .AbstractAdvancedCommerceInAppRequest import AbstractAdvancedCommerceInAppRequest +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils +from .AdvancedCommerceSubscriptionModifyAddItem import AdvancedCommerceSubscriptionModifyAddItem +from .AdvancedCommerceSubscriptionModifyChangeItem import AdvancedCommerceSubscriptionModifyChangeItem +from .AdvancedCommerceSubscriptionModifyDescriptors import AdvancedCommerceSubscriptionModifyDescriptors +from .AdvancedCommerceSubscriptionModifyPeriodChange import AdvancedCommerceSubscriptionModifyPeriodChange +from .AdvancedCommerceSubscriptionModifyRemoveItem import AdvancedCommerceSubscriptionModifyRemoveItem + + +@define +class AdvancedCommerceSubscriptionModifyInAppRequest(AbstractAdvancedCommerceInAppRequest): + """ + The request data your app provides to make changes to an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyinapprequest + """ + + operation: str = attr.ib(default="MODIFY_SUBSCRIPTION", init=False, on_setattr=attr.setters.frozen) + + version: str = attr.ib(default="1", init=False, on_setattr=attr.setters.frozen) + + transactionId: str = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/transactionid + """ + + retainBillingCycle: bool = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/retainbillingcycle + """ + + addItems: Optional[List[AdvancedCommerceSubscriptionModifyAddItem]] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.items_validator)) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyadditem + """ + + changeItems: Optional[List[AdvancedCommerceSubscriptionModifyChangeItem]] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.items_validator)) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifychangeitem + """ + + currency: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/currency + """ + + descriptors: Optional[AdvancedCommerceSubscriptionModifyDescriptors] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifydescriptors + """ + + periodChange: Optional[AdvancedCommerceSubscriptionModifyPeriodChange] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyperiodchange + """ + + removeItems: Optional[List[AdvancedCommerceSubscriptionModifyRemoveItem]] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyremoveitem + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ + + taxCode: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/taxcode + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyPeriodChange.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyPeriodChange.py new file mode 100644 index 00000000..a2f33177 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyPeriodChange.py @@ -0,0 +1,36 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +import attr +from attr import define +from .AdvancedCommerceEffective import AdvancedCommerceEffective +from .AdvancedCommercePeriod import AdvancedCommercePeriod +from .LibraryUtility import AttrsRawValueAware + + +@define +class AdvancedCommerceSubscriptionModifyPeriodChange(AttrsRawValueAware): + """ + The data your app provides to change the period of an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyperiodchange + """ + + effective: AdvancedCommerceEffective = AdvancedCommerceEffective.create_main_attr('rawEffective', raw_required=True) + """ + https://developer.apple.com/documentation/advancedcommerceapi/effective + """ + + rawEffective: str = AdvancedCommerceEffective.create_raw_attr('effective', required=True) + """ + See effective + """ + + period: AdvancedCommercePeriod = AdvancedCommercePeriod.create_main_attr('rawPeriod', raw_required=True) + """ + https://developer.apple.com/documentation/advancedcommerceapi/period + """ + + rawPeriod: str = AdvancedCommercePeriod.create_raw_attr('period', required=True) + """ + See period + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyRemoveItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyRemoveItem.py new file mode 100644 index 00000000..f978e7d3 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionModifyRemoveItem.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from .AbstractAdvancedCommerceBaseItem import AbstractAdvancedCommerceBaseItem + + +@define +class AdvancedCommerceSubscriptionModifyRemoveItem(AbstractAdvancedCommerceBaseItem): + """ + The data your app provides to remove an item from an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionmodifyremoveitem + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeItem.py new file mode 100644 index 00000000..cc2212e2 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeItem.py @@ -0,0 +1,25 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +import attr +from attr import define +from typing import List, Optional +from .AbstractAdvancedCommerceBaseItem import AbstractAdvancedCommerceBaseItem +from .AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils + +@define +class AdvancedCommerceSubscriptionPriceChangeItem(AbstractAdvancedCommerceBaseItem): + """ + The data your app provides to change a subscription price. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionpricechangeitem + """ + + price: int = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/price + """ + + dependentSKUs: Optional[List[str]] = attr.ib(default=None, validator=attr.validators.optional(AdvancedCommerceValidationUtils.dependent_skus_validator)) + """ + https://developer.apple.com/documentation/advancedcommerceapi/dependentsku + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeRequest.py new file mode 100644 index 00000000..13612b9a --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeRequest.py @@ -0,0 +1,36 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import Optional, List +import attr +from attr import define +from .AdvancedCommerceRequest import AdvancedCommerceRequest +from .AdvancedCommerceSubscriptionPriceChangeItem import AdvancedCommerceSubscriptionPriceChangeItem + +@define +class AdvancedCommerceSubscriptionPriceChangeRequest(AdvancedCommerceRequest): + """ + The request body you use to change the price of an auto-renewable subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionpricechangerequest + """ + + items: List[AdvancedCommerceSubscriptionPriceChangeItem] = attr.ib() + """ + An array that contains one or more SKUs and the changed price for each SKU. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionpricechangeitem + """ + + currency: Optional[str] = attr.ib(default=None) + """ + The currency of the prices. + + https://developer.apple.com/documentation/advancedcommerceapi/currency + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + The App Store storefront of the subscription. + + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeResponse.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeResponse.py new file mode 100644 index 00000000..264b1d67 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionPriceChangeResponse.py @@ -0,0 +1,15 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from .AbstractAdvancedCommerceResponse import AbstractAdvancedCommerceResponse + + +@define +class AdvancedCommerceSubscriptionPriceChangeResponse(AbstractAdvancedCommerceResponse): + """ + A response that contains signed JWS renewal and JWS transaction information after a subscription price change request. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionpricechangeresponse + """ + def __init__(self, signedRenewalInfo: str, signedTransactionInfo: str): + super().__init__(signedRenewalInfo=signedRenewalInfo, signedTransactionInfo=signedTransactionInfo) \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionReactivateInAppRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionReactivateInAppRequest.py new file mode 100644 index 00000000..ee7cdb78 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionReactivateInAppRequest.py @@ -0,0 +1,33 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from __future__ import annotations +from typing import List, Optional +import attr +from appstoreserverlibrary.models.AbstractAdvancedCommerceInAppRequest import AbstractAdvancedCommerceInAppRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionReactivateItem import AdvancedCommerceSubscriptionReactivateItem + +@attr.define +class AdvancedCommerceSubscriptionReactivateInAppRequest(AbstractAdvancedCommerceInAppRequest): + """ + The request your app provides to reactivate a subscription that has automatic renewal turned off. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionreactivateinapprequest + """ + operation: str = attr.ib(init=False, default="REACTIVATE_SUBSCRIPTION", on_setattr=attr.setters.frozen) + + version: str = attr.ib(init=False, default="1", on_setattr=attr.setters.frozen) + + transactionId: str = attr.ib() + """ + https://developer.apple.com/documentation/appstoreserverapi/transactionid + """ + + items: Optional[List[AdvancedCommerceSubscriptionReactivateItem]] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionreactivateitem + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionReactivateItem.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionReactivateItem.py new file mode 100644 index 00000000..7561a994 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionReactivateItem.py @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from appstoreserverlibrary.models.AbstractAdvancedCommerceBaseItem import AbstractAdvancedCommerceBaseItem + + +@define +class AdvancedCommerceSubscriptionReactivateItem(AbstractAdvancedCommerceBaseItem): + """ + An item in a subscription to reactive. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionreactivateitem + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionRevokeRequest.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionRevokeRequest.py new file mode 100644 index 00000000..f8d32f70 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionRevokeRequest.py @@ -0,0 +1,44 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from __future__ import annotations +from typing import Optional +import attr +from appstoreserverlibrary.models.AdvancedCommerceRequest import AdvancedCommerceRequest +from appstoreserverlibrary.models.AdvancedCommerceRefundReason import AdvancedCommerceRefundReason +from appstoreserverlibrary.models.AdvancedCommerceRefundType import AdvancedCommerceRefundType + + +@attr.define +class AdvancedCommerceSubscriptionRevokeRequest(AdvancedCommerceRequest): + """ + The request body you provide to terminate a subscription and all its items immediately. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionrevokerequest + """ + + refundRiskingPreference: bool = attr.ib() + """ + https://developer.apple.com/documentation/advancedcommerceapi/refundriskingpreference + """ + + refundType: AdvancedCommerceRefundType = AdvancedCommerceRefundType.create_main_attr('rawRefundType', raw_required=True) + + rawRefundType: str = AdvancedCommerceRefundType.create_raw_attr('refundType', required=True) + """ + See refundType + """ + + refundReason: AdvancedCommerceRefundReason = AdvancedCommerceRefundReason.create_main_attr('rawRefundReason', raw_required=True) + """ + https://developer.apple.com/documentation/advancedcommerceapi/refundreason + """ + + rawRefundReason: str = AdvancedCommerceRefundReason.create_raw_attr('refundReason', required=True) + """ + See refundReason + """ + + storefront: Optional[str] = attr.ib(default=None) + """ + https://developer.apple.com/documentation/advancedcommerceapi/storefront + """ \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceSubscriptionRevokeResponse.py b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionRevokeResponse.py new file mode 100644 index 00000000..e7066078 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceSubscriptionRevokeResponse.py @@ -0,0 +1,15 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from attr import define +from appstoreserverlibrary.models.AbstractAdvancedCommerceResponse import AbstractAdvancedCommerceResponse + + +@define +class AdvancedCommerceSubscriptionRevokeResponse(AbstractAdvancedCommerceResponse): + """ + The response body for a successful revoke-subscription request. + + https://developer.apple.com/documentation/advancedcommerceapi/subscriptionrevokeresponse + """ + def __init__(self, signedRenewalInfo: str, signedTransactionInfo: str): + super().__init__(signedRenewalInfo=signedRenewalInfo, signedTransactionInfo=signedTransactionInfo) \ No newline at end of file diff --git a/appstoreserverlibrary/models/AdvancedCommerceValidationUtils.py b/appstoreserverlibrary/models/AdvancedCommerceValidationUtils.py new file mode 100644 index 00000000..5b1ea638 --- /dev/null +++ b/appstoreserverlibrary/models/AdvancedCommerceValidationUtils.py @@ -0,0 +1,101 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +from typing import TypeVar + +T = TypeVar('T') + + +class AdvancedCommerceValidationUtils: + MAXIMUM_DESCRIPTION_LENGTH = 45 + MAXIMUM_DISPLAY_NAME_LENGTH = 30 + MAXIMUM_SKU_LENGTH = 128 + MIN_PERIOD = 1 + MAX_PERIOD = 12 + + @staticmethod + def description_validator(instance, attribute, value): + """ + Validates description is not None and does not exceed maximum length. + + Raises: + ValueError: If description exceeds maximum length + """ + if len(value) > AdvancedCommerceValidationUtils.MAXIMUM_DESCRIPTION_LENGTH: + raise ValueError( + f"Description length cannot exceed " + f"{AdvancedCommerceValidationUtils.MAXIMUM_DESCRIPTION_LENGTH} characters" + ) + + @staticmethod + def display_name_validator(instance, attribute, value): + """ + Validates display name is not None and does not exceed maximum length. + + Raises: + ValueError: If display name exceeds maximum length + """ + if len(value) > AdvancedCommerceValidationUtils.MAXIMUM_DISPLAY_NAME_LENGTH: + raise ValueError( + f"Display name length cannot exceed " + f"{AdvancedCommerceValidationUtils.MAXIMUM_DISPLAY_NAME_LENGTH} characters" + ) + + @staticmethod + def sku_validator(instance, attribute, value): + """ + Validates SKU does not exceed maximum length. + + Raises: + ValueError: If SKU exceeds maximum length + """ + if len(value) > AdvancedCommerceValidationUtils.MAXIMUM_SKU_LENGTH: + raise ValueError( + f"SKU length cannot exceed " + f"{AdvancedCommerceValidationUtils.MAXIMUM_SKU_LENGTH} characters" + ) + + @staticmethod + def period_count_validator(instance, attribute, value): + """ + Validates periodCount is not None and between `MIN_PERIOD` and `MAX_PERIOD` inclusive. + + Raises: + ValueError: If period_count is out of range + """ + if (value < AdvancedCommerceValidationUtils.MIN_PERIOD or + value > AdvancedCommerceValidationUtils.MAX_PERIOD): + raise ValueError( + f"Period count must be between " + f"{AdvancedCommerceValidationUtils.MIN_PERIOD} and " + f"{AdvancedCommerceValidationUtils.MAX_PERIOD}" + ) + + @staticmethod + def items_validator(instance, attribute, value): + """ + Validates a list of items is not None, not empty, and contains no None elements. + + Raises: + ValueError: If list is empty or contains None elements + """ + if not value: + raise ValueError("Items list cannot be empty") + + for i, item in enumerate(value): + if item is None: + raise ValueError(f"Item at index {i} in the list cannot be None") + + @staticmethod + def dependent_skus_validator(instance, attribute, value): + """ + Validates that each SKU in the dependentSKUs list does not exceed maximum length. + + Raises: + ValueError: If any SKU exceeds maximum length + """ + for sku in value: + if len(sku) > AdvancedCommerceValidationUtils.MAXIMUM_SKU_LENGTH: + raise ValueError( + f"SKU length cannot exceed " + f"{AdvancedCommerceValidationUtils.MAXIMUM_SKU_LENGTH} characters" + ) diff --git a/tests/resources/models/advancedCommerceDescriptors.json b/tests/resources/models/advancedCommerceDescriptors.json new file mode 100644 index 00000000..5fe14d4c --- /dev/null +++ b/tests/resources/models/advancedCommerceDescriptors.json @@ -0,0 +1,4 @@ +{ + "description": "description", + "displayName": "display name" +} diff --git a/tests/resources/models/advancedCommerceOffer.json b/tests/resources/models/advancedCommerceOffer.json new file mode 100644 index 00000000..83a413fb --- /dev/null +++ b/tests/resources/models/advancedCommerceOffer.json @@ -0,0 +1,6 @@ +{ + "period": "P1W", + "periodCount": 3, + "price": 5000, + "reason": "WIN_BACK" +} diff --git a/tests/resources/models/advancedCommerceOneTimeChargeCreateRequest.json b/tests/resources/models/advancedCommerceOneTimeChargeCreateRequest.json new file mode 100644 index 00000000..d9874797 --- /dev/null +++ b/tests/resources/models/advancedCommerceOneTimeChargeCreateRequest.json @@ -0,0 +1,16 @@ +{ + "currency": "USD", + "item": { + "description": "description", + "displayName": "display name", + "SKU": "sku", + "price": 10000 + }, + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440000" + }, + "taxCode": "taxCode", + "storefront": "USA", + "operation": "CREATE", + "version": "1.0" +} diff --git a/tests/resources/models/advancedCommerceOneTimeChargeItem.json b/tests/resources/models/advancedCommerceOneTimeChargeItem.json new file mode 100644 index 00000000..0849bf28 --- /dev/null +++ b/tests/resources/models/advancedCommerceOneTimeChargeItem.json @@ -0,0 +1,6 @@ +{ + "description": "description", + "displayName": "display name", + "SKU": "sku", + "price": 15000 +} diff --git a/tests/resources/models/advancedCommerceRequestInfo.json b/tests/resources/models/advancedCommerceRequestInfo.json new file mode 100644 index 00000000..a57cbdca --- /dev/null +++ b/tests/resources/models/advancedCommerceRequestInfo.json @@ -0,0 +1,5 @@ +{ + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440010", + "appAccountToken": "660e8400-e29b-41d4-a716-446655440011", + "consistencyToken": "consistency_token_value" +} diff --git a/tests/resources/models/advancedCommerceRequestRefundItem.json b/tests/resources/models/advancedCommerceRequestRefundItem.json new file mode 100644 index 00000000..875107fa --- /dev/null +++ b/tests/resources/models/advancedCommerceRequestRefundItem.json @@ -0,0 +1,7 @@ +{ + "SKU": "sku", + "refundReason": "LEGAL", + "refundType": "FULL", + "revoke": true, + "refundAmount": 5000 +} diff --git a/tests/resources/models/advancedCommerceRequestRefundRequest.json b/tests/resources/models/advancedCommerceRequestRefundRequest.json new file mode 100644 index 00000000..6a58aa7a --- /dev/null +++ b/tests/resources/models/advancedCommerceRequestRefundRequest.json @@ -0,0 +1,22 @@ +{ + "items": [ + { + "SKU": "sku", + "refundReason": "LEGAL", + "refundType": "FULL", + "revoke": true + }, + { + "SKU": "sku", + "refundReason": "OTHER", + "refundType": "PRORATED", + "revoke": false + } + ], + "refundRiskingPreference": true, + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440002" + }, + "currency": "USD", + "storefront": "USA" +} diff --git a/tests/resources/models/advancedCommerceRequestRefundResponse.json b/tests/resources/models/advancedCommerceRequestRefundResponse.json new file mode 100644 index 00000000..0e4f409c --- /dev/null +++ b/tests/resources/models/advancedCommerceRequestRefundResponse.json @@ -0,0 +1,3 @@ +{ + "signedTransactionInfo": "signed_transaction_info_value" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionCancelRequest.json b/tests/resources/models/advancedCommerceSubscriptionCancelRequest.json new file mode 100644 index 00000000..9cbe08c5 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionCancelRequest.json @@ -0,0 +1,6 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440003" + }, + "storefront": "USA" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionCancelResponse.json b/tests/resources/models/advancedCommerceSubscriptionCancelResponse.json new file mode 100644 index 00000000..6f14b309 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionCancelResponse.json @@ -0,0 +1,4 @@ +{ + "signedRenewalInfo": "signed_renewal_info", + "signedTransactionInfo": "signed_transaction_info" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionChangeMetadataDescriptors.json b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataDescriptors.json new file mode 100644 index 00000000..b07d7400 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataDescriptors.json @@ -0,0 +1,5 @@ +{ + "effective": "IMMEDIATELY", + "description": "description", + "displayName": "displayName" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionChangeMetadataItem.json b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataItem.json new file mode 100644 index 00000000..c498b6e5 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataItem.json @@ -0,0 +1,7 @@ +{ + "currentSKU": "currentSku", + "effective": "NEXT_BILL_CYCLE", + "description": "description", + "displayName": "displayName", + "SKU": "sku" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionChangeMetadataRequest.json b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataRequest.json new file mode 100644 index 00000000..20a1e396 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataRequest.json @@ -0,0 +1,14 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440009" + }, + "items": [ + { + "currentSKU": "currentSKU", + "effective": "IMMEDIATELY", + "description": "description", + "displayName": "display name", + "SKU": "sku" + } + ] +} diff --git a/tests/resources/models/advancedCommerceSubscriptionChangeMetadataResponse.json b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataResponse.json new file mode 100644 index 00000000..6f14b309 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionChangeMetadataResponse.json @@ -0,0 +1,4 @@ +{ + "signedRenewalInfo": "signed_renewal_info", + "signedTransactionInfo": "signed_transaction_info" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionCreateItem.json b/tests/resources/models/advancedCommerceSubscriptionCreateItem.json new file mode 100644 index 00000000..f1b00ea2 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionCreateItem.json @@ -0,0 +1,6 @@ +{ + "description": "description", + "displayName": "display name", + "SKU": "sku", + "price": 20000 +} diff --git a/tests/resources/models/advancedCommerceSubscriptionCreateRequest.json b/tests/resources/models/advancedCommerceSubscriptionCreateRequest.json new file mode 100644 index 00000000..2b7a132c --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionCreateRequest.json @@ -0,0 +1,28 @@ +{ + "currency": "USD", + "descriptors": { + "description": "description", + "displayName": "display name" + }, + "items": [ + { + "SKU": "sku", + "description": "description", + "displayName": "display name", + "price": 20000 + }, + { + "SKU": "sku", + "description": "description", + "displayName": "display name", + "price": 30000 + } + ], + "period": "P1M", + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440001" + }, + "taxCode": "taxCode", + "storefront": "USA", + "previousTransactionId": "transactionId" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionMigrateDescriptors.json b/tests/resources/models/advancedCommerceSubscriptionMigrateDescriptors.json new file mode 100644 index 00000000..6d0bd6ee --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionMigrateDescriptors.json @@ -0,0 +1,4 @@ +{ + "description": "description", + "displayName": "displayName" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionMigrateItem.json b/tests/resources/models/advancedCommerceSubscriptionMigrateItem.json new file mode 100644 index 00000000..00fce710 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionMigrateItem.json @@ -0,0 +1,5 @@ +{ + "SKU": "sku", + "description": "description", + "displayName": "displayName" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionMigrateRenewalItem.json b/tests/resources/models/advancedCommerceSubscriptionMigrateRenewalItem.json new file mode 100644 index 00000000..00fce710 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionMigrateRenewalItem.json @@ -0,0 +1,5 @@ +{ + "SKU": "sku", + "description": "description", + "displayName": "displayName" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionMigrateRequest.json b/tests/resources/models/advancedCommerceSubscriptionMigrateRequest.json new file mode 100644 index 00000000..d670da51 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionMigrateRequest.json @@ -0,0 +1,18 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440006" + }, + "descriptors": { + "description": "description", + "displayName": "display name" + }, + "items": [ + { + "SKU": "sku", + "description": "description", + "displayName": "display name" + } + ], + "targetProductId": "targetProductId", + "taxCode": "taxCode" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionMigrateResponse.json b/tests/resources/models/advancedCommerceSubscriptionMigrateResponse.json new file mode 100644 index 00000000..2b12972a --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionMigrateResponse.json @@ -0,0 +1,4 @@ +{ + "signedRenewalInfo": "signed_renewal_info_value", + "signedTransactionInfo": "signed_transaction_info_value" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionModifyAddItem.json b/tests/resources/models/advancedCommerceSubscriptionModifyAddItem.json new file mode 100644 index 00000000..935ffb42 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionModifyAddItem.json @@ -0,0 +1,6 @@ +{ + "SKU": "sku", + "description": "description", + "displayName": "displayName", + "price": 12000 +} diff --git a/tests/resources/models/advancedCommerceSubscriptionModifyChangeItem.json b/tests/resources/models/advancedCommerceSubscriptionModifyChangeItem.json new file mode 100644 index 00000000..ac33673d --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionModifyChangeItem.json @@ -0,0 +1,9 @@ +{ + "currentSKU": "currentSku", + "description": "description", + "displayName": "displayName", + "effective": "IMMEDIATELY", + "price": 13000, + "reason": "UPGRADE", + "SKU": "sku" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionModifyDescriptors.json b/tests/resources/models/advancedCommerceSubscriptionModifyDescriptors.json new file mode 100644 index 00000000..b07d7400 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionModifyDescriptors.json @@ -0,0 +1,5 @@ +{ + "effective": "IMMEDIATELY", + "description": "description", + "displayName": "displayName" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionModifyInAppRequest.json b/tests/resources/models/advancedCommerceSubscriptionModifyInAppRequest.json new file mode 100644 index 00000000..0934cb8c --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionModifyInAppRequest.json @@ -0,0 +1,14 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440007" + }, + "transactionId": "transactionId", + "retainBillingCycle": true, + "descriptors": { + "effective": "IMMEDIATELY", + "description": "description", + "displayName": "display name" + }, + "taxCode": "taxCode", + "currency": "USD" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionModifyPeriodChange.json b/tests/resources/models/advancedCommerceSubscriptionModifyPeriodChange.json new file mode 100644 index 00000000..b46ebb58 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionModifyPeriodChange.json @@ -0,0 +1,4 @@ +{ + "effective": "IMMEDIATELY", + "period": "P3M" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionModifyRemoveItem.json b/tests/resources/models/advancedCommerceSubscriptionModifyRemoveItem.json new file mode 100644 index 00000000..c9767fb0 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionModifyRemoveItem.json @@ -0,0 +1,3 @@ +{ + "SKU": "sku" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionPriceChangeItem.json b/tests/resources/models/advancedCommerceSubscriptionPriceChangeItem.json new file mode 100644 index 00000000..8a4b90f0 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionPriceChangeItem.json @@ -0,0 +1,5 @@ +{ + "SKU": "sku", + "price": 16000, + "dependentSKUs": ["dependentSKU"] +} diff --git a/tests/resources/models/advancedCommerceSubscriptionPriceChangeRequest.json b/tests/resources/models/advancedCommerceSubscriptionPriceChangeRequest.json new file mode 100644 index 00000000..fba8aded --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionPriceChangeRequest.json @@ -0,0 +1,12 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440005" + }, + "items": [ + { + "SKU": "sku123", + "price": 15000 + } + ], + "currency": "USD" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionPriceChangeResponse.json b/tests/resources/models/advancedCommerceSubscriptionPriceChangeResponse.json new file mode 100644 index 00000000..6f14b309 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionPriceChangeResponse.json @@ -0,0 +1,4 @@ +{ + "signedRenewalInfo": "signed_renewal_info", + "signedTransactionInfo": "signed_transaction_info" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionReactivateInAppRequest.json b/tests/resources/models/advancedCommerceSubscriptionReactivateInAppRequest.json new file mode 100644 index 00000000..408a99b3 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionReactivateInAppRequest.json @@ -0,0 +1,11 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440008" + }, + "transactionId": "transactionId", + "items": [ + { + "SKU": "sku" + } + ] +} diff --git a/tests/resources/models/advancedCommerceSubscriptionReactivateItem.json b/tests/resources/models/advancedCommerceSubscriptionReactivateItem.json new file mode 100644 index 00000000..c9767fb0 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionReactivateItem.json @@ -0,0 +1,3 @@ +{ + "SKU": "sku" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionRevokeRequest.json b/tests/resources/models/advancedCommerceSubscriptionRevokeRequest.json new file mode 100644 index 00000000..d723be45 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionRevokeRequest.json @@ -0,0 +1,9 @@ +{ + "requestInfo": { + "requestReferenceId": "550e8400-e29b-41d4-a716-446655440004" + }, + "refundRiskingPreference": true, + "refundReason": "LEGAL", + "refundType": "FULL", + "storefront": "USA" +} diff --git a/tests/resources/models/advancedCommerceSubscriptionRevokeResponse.json b/tests/resources/models/advancedCommerceSubscriptionRevokeResponse.json new file mode 100644 index 00000000..6f14b309 --- /dev/null +++ b/tests/resources/models/advancedCommerceSubscriptionRevokeResponse.json @@ -0,0 +1,4 @@ +{ + "signedRenewalInfo": "signed_renewal_info", + "signedTransactionInfo": "signed_transaction_info" +} diff --git a/tests/test_advanced_commerce_models.py b/tests/test_advanced_commerce_models.py new file mode 100644 index 00000000..660bf7f0 --- /dev/null +++ b/tests/test_advanced_commerce_models.py @@ -0,0 +1,611 @@ +# Copyright (c) 2026 Apple Inc. Licensed under MIT License. + +import json +import unittest + +from appstoreserverlibrary.models.AdvancedCommerceDescriptors import AdvancedCommerceDescriptors +from appstoreserverlibrary.models.AdvancedCommerceEffective import AdvancedCommerceEffective +from appstoreserverlibrary.models.AdvancedCommerceOffer import AdvancedCommerceOffer +from appstoreserverlibrary.models.AdvancedCommerceOfferPeriod import AdvancedCommerceOfferPeriod +from appstoreserverlibrary.models.AdvancedCommerceOfferReason import AdvancedCommerceOfferReason +from appstoreserverlibrary.models.AdvancedCommerceOneTimeChargeCreateRequest import AdvancedCommerceOneTimeChargeCreateRequest +from appstoreserverlibrary.models.AdvancedCommerceOneTimeChargeItem import AdvancedCommerceOneTimeChargeItem +from appstoreserverlibrary.models.AdvancedCommercePeriod import AdvancedCommercePeriod +from appstoreserverlibrary.models.AdvancedCommerceReason import AdvancedCommerceReason +from appstoreserverlibrary.models.AdvancedCommerceRefundReason import AdvancedCommerceRefundReason +from appstoreserverlibrary.models.AdvancedCommerceRefundType import AdvancedCommerceRefundType +from appstoreserverlibrary.models.AdvancedCommerceRequestInfo import AdvancedCommerceRequestInfo +from appstoreserverlibrary.models.AdvancedCommerceRequestRefundItem import AdvancedCommerceRequestRefundItem +from appstoreserverlibrary.models.AdvancedCommerceRequestRefundRequest import AdvancedCommerceRequestRefundRequest +from appstoreserverlibrary.models.AdvancedCommerceRequestRefundResponse import AdvancedCommerceRequestRefundResponse +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionCancelRequest import AdvancedCommerceSubscriptionCancelRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionCancelResponse import AdvancedCommerceSubscriptionCancelResponse +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionChangeMetadataDescriptors import AdvancedCommerceSubscriptionChangeMetadataDescriptors +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionChangeMetadataItem import AdvancedCommerceSubscriptionChangeMetadataItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionChangeMetadataRequest import AdvancedCommerceSubscriptionChangeMetadataRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionChangeMetadataResponse import AdvancedCommerceSubscriptionChangeMetadataResponse +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionCreateItem import AdvancedCommerceSubscriptionCreateItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionCreateRequest import AdvancedCommerceSubscriptionCreateRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionMigrateDescriptors import AdvancedCommerceSubscriptionMigrateDescriptors +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionMigrateItem import AdvancedCommerceSubscriptionMigrateItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionMigrateRenewalItem import AdvancedCommerceSubscriptionMigrateRenewalItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionMigrateRequest import AdvancedCommerceSubscriptionMigrateRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionMigrateResponse import AdvancedCommerceSubscriptionMigrateResponse +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionModifyAddItem import AdvancedCommerceSubscriptionModifyAddItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionModifyChangeItem import AdvancedCommerceSubscriptionModifyChangeItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionModifyDescriptors import AdvancedCommerceSubscriptionModifyDescriptors +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionModifyInAppRequest import AdvancedCommerceSubscriptionModifyInAppRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionModifyPeriodChange import AdvancedCommerceSubscriptionModifyPeriodChange +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionModifyRemoveItem import AdvancedCommerceSubscriptionModifyRemoveItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionPriceChangeItem import AdvancedCommerceSubscriptionPriceChangeItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionPriceChangeRequest import AdvancedCommerceSubscriptionPriceChangeRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionPriceChangeResponse import AdvancedCommerceSubscriptionPriceChangeResponse +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionReactivateInAppRequest import AdvancedCommerceSubscriptionReactivateInAppRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionReactivateItem import AdvancedCommerceSubscriptionReactivateItem +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionRevokeRequest import AdvancedCommerceSubscriptionRevokeRequest +from appstoreserverlibrary.models.AdvancedCommerceSubscriptionRevokeResponse import AdvancedCommerceSubscriptionRevokeResponse +from appstoreserverlibrary.models.AdvancedCommerceValidationUtils import AdvancedCommerceValidationUtils +from appstoreserverlibrary.models.LibraryUtility import _get_cattrs_converter +from tests.util import read_data_from_file + + +class AdvancedCommerceModelsTest(unittest.TestCase): + def test_advanced_commerce_period(self): + self.assertEqual("P1W", AdvancedCommercePeriod.P1W.value) + self.assertEqual("P1M", AdvancedCommercePeriod.P1M.value) + self.assertEqual("P2M", AdvancedCommercePeriod.P2M.value) + self.assertEqual("P3M", AdvancedCommercePeriod.P3M.value) + self.assertEqual("P6M", AdvancedCommercePeriod.P6M.value) + self.assertEqual("P1Y", AdvancedCommercePeriod.P1Y.value) + + self.assertEqual(AdvancedCommercePeriod.P1W, AdvancedCommercePeriod("P1W")) + self.assertEqual(AdvancedCommercePeriod.P1M, AdvancedCommercePeriod("P1M")) + self.assertEqual(AdvancedCommercePeriod.P1Y, AdvancedCommercePeriod("P1Y")) + self.assertFalse("INVALID" in AdvancedCommercePeriod) + + self.assertEqual("P1W", AdvancedCommercePeriod.P1W.value) + + def test_advanced_commerce_reason(self): + self.assertEqual("UPGRADE", AdvancedCommerceReason.UPGRADE.value) + self.assertEqual("DOWNGRADE", AdvancedCommerceReason.DOWNGRADE.value) + self.assertEqual("APPLY_OFFER", AdvancedCommerceReason.APPLY_OFFER.value) + + self.assertEqual(AdvancedCommerceReason.UPGRADE, AdvancedCommerceReason("UPGRADE")) + self.assertEqual(AdvancedCommerceReason.DOWNGRADE, AdvancedCommerceReason("DOWNGRADE")) + self.assertEqual(AdvancedCommerceReason.APPLY_OFFER, AdvancedCommerceReason("APPLY_OFFER")) + self.assertFalse("INVALID" in AdvancedCommerceReason) + + self.assertEqual("UPGRADE", AdvancedCommerceReason.UPGRADE.value) + + def test_advanced_commerce_refund_reason(self): + self.assertEqual("UNINTENDED_PURCHASE", AdvancedCommerceRefundReason.UNINTENDED_PURCHASE.value) + self.assertEqual("FULFILLMENT_ISSUE", AdvancedCommerceRefundReason.FULFILLMENT_ISSUE.value) + self.assertEqual("UNSATISFIED_WITH_PURCHASE", AdvancedCommerceRefundReason.UNSATISFIED_WITH_PURCHASE.value) + self.assertEqual("LEGAL", AdvancedCommerceRefundReason.LEGAL.value) + self.assertEqual("OTHER", AdvancedCommerceRefundReason.OTHER.value) + self.assertEqual("MODIFY_ITEMS_REFUND", AdvancedCommerceRefundReason.MODIFY_ITEMS_REFUND.value) + self.assertEqual("SIMULATE_REFUND_DECLINE", AdvancedCommerceRefundReason.SIMULATE_REFUND_DECLINE.value) + + self.assertEqual(AdvancedCommerceRefundReason.LEGAL, AdvancedCommerceRefundReason("LEGAL")) + self.assertEqual(AdvancedCommerceRefundReason.OTHER, AdvancedCommerceRefundReason("OTHER")) + self.assertFalse("INVALID" in AdvancedCommerceRefundReason) + + self.assertEqual("LEGAL", AdvancedCommerceRefundReason.LEGAL.value) + + def test_advanced_commerce_refund_type(self): + self.assertEqual("FULL", AdvancedCommerceRefundType.FULL.value) + self.assertEqual("PRORATED", AdvancedCommerceRefundType.PRORATED.value) + self.assertEqual("CUSTOM", AdvancedCommerceRefundType.CUSTOM.value) + + self.assertEqual(AdvancedCommerceRefundType.FULL, AdvancedCommerceRefundType("FULL")) + self.assertEqual(AdvancedCommerceRefundType.PRORATED, AdvancedCommerceRefundType("PRORATED")) + self.assertEqual(AdvancedCommerceRefundType.CUSTOM, AdvancedCommerceRefundType("CUSTOM")) + self.assertFalse("INVALID" in AdvancedCommerceRefundType) + + self.assertEqual("FULL", AdvancedCommerceRefundType.FULL.value) + + def test_advanced_commerce_offer_period(self): + self.assertEqual("P3D", AdvancedCommerceOfferPeriod.P3D.value) + self.assertEqual("P1W", AdvancedCommerceOfferPeriod.P1W.value) + self.assertEqual("P2W", AdvancedCommerceOfferPeriod.P2W.value) + self.assertEqual("P1M", AdvancedCommerceOfferPeriod.P1M.value) + self.assertEqual("P2M", AdvancedCommerceOfferPeriod.P2M.value) + self.assertEqual("P3M", AdvancedCommerceOfferPeriod.P3M.value) + + self.assertEqual(AdvancedCommerceOfferPeriod.P1W, AdvancedCommerceOfferPeriod("P1W")) + self.assertEqual(AdvancedCommerceOfferPeriod.P1M, AdvancedCommerceOfferPeriod("P1M")) + self.assertEqual(AdvancedCommerceOfferPeriod.P3D, AdvancedCommerceOfferPeriod("P3D")) + self.assertFalse("INVALID" in AdvancedCommerceOfferPeriod) + + self.assertEqual("P1W", AdvancedCommerceOfferPeriod.P1W.value) + + def test_advanced_commerce_offer_reason(self): + self.assertEqual("ACQUISITION", AdvancedCommerceOfferReason.ACQUISITION.value) + self.assertEqual("WIN_BACK", AdvancedCommerceOfferReason.WIN_BACK.value) + self.assertEqual("RETENTION", AdvancedCommerceOfferReason.RETENTION.value) + + self.assertEqual(AdvancedCommerceOfferReason.ACQUISITION, AdvancedCommerceOfferReason("ACQUISITION")) + self.assertEqual(AdvancedCommerceOfferReason.WIN_BACK, AdvancedCommerceOfferReason("WIN_BACK")) + self.assertEqual(AdvancedCommerceOfferReason.RETENTION, AdvancedCommerceOfferReason("RETENTION")) + self.assertFalse("INVALID" in AdvancedCommerceOfferReason) + + self.assertEqual("WIN_BACK", AdvancedCommerceOfferReason.WIN_BACK.value) + + def test_advanced_commerce_effective(self): + self.assertEqual("IMMEDIATELY", AdvancedCommerceEffective.IMMEDIATELY.value) + self.assertEqual("NEXT_BILL_CYCLE", AdvancedCommerceEffective.NEXT_BILL_CYCLE.value) + + self.assertEqual(AdvancedCommerceEffective.IMMEDIATELY, AdvancedCommerceEffective("IMMEDIATELY")) + self.assertEqual(AdvancedCommerceEffective.NEXT_BILL_CYCLE, AdvancedCommerceEffective("NEXT_BILL_CYCLE")) + self.assertFalse("INVALID" in AdvancedCommerceEffective) + + self.assertEqual("IMMEDIATELY", AdvancedCommerceEffective.IMMEDIATELY.value) + + def test_validation_utils_description(self): + valid_description = "Valid description" + AdvancedCommerceValidationUtils.description_validator(None, None, valid_description) + + max_length_description = "A" * 45 + AdvancedCommerceValidationUtils.description_validator(None, None, max_length_description) + + too_long_description = "A" * 46 + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.description_validator(None, None, too_long_description) + + def test_validation_utils_display_name(self): + valid_display_name = "Valid Name" + AdvancedCommerceValidationUtils.display_name_validator(None, None, valid_display_name) + + max_length_display_name = "A" * 30 + AdvancedCommerceValidationUtils.display_name_validator(None, None, max_length_display_name) + + too_long_display_name = "A" * 31 + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.display_name_validator(None, None, too_long_display_name) + + def test_validation_utils_sku(self): + valid_sku = "valid.sku.123" + AdvancedCommerceValidationUtils.sku_validator(None, None, valid_sku) + + max_length_sku = "A" * 128 + AdvancedCommerceValidationUtils.sku_validator(None, None, max_length_sku) + + too_long_sku = "A" * 129 + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.sku_validator(None, None, too_long_sku) + + def test_validation_utils_period_count(self): + AdvancedCommerceValidationUtils.period_count_validator(None, None, 1) + AdvancedCommerceValidationUtils.period_count_validator(None, None, 6) + AdvancedCommerceValidationUtils.period_count_validator(None, None, 12) + + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.period_count_validator(None, None, 0) + + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.period_count_validator(None, None, 13) + + def test_validation_utils_items(self): + valid_list = [ + AdvancedCommerceOneTimeChargeItem( + description="desc", + displayName="name", + SKU="sku1", + price=1000 + ) + ] + AdvancedCommerceValidationUtils.items_validator(None, None, valid_list) + + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.items_validator(None, None, None) + + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.items_validator(None, None, []) + + list_with_none = [None] + with self.assertRaises(ValueError): + AdvancedCommerceValidationUtils.items_validator(None, None, list_with_none) + + def test_advanced_commerce_descriptors_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceDescriptors.json') + + descriptors_dict = json.loads(json_data) + descriptors = _get_cattrs_converter(AdvancedCommerceDescriptors).structure(descriptors_dict, AdvancedCommerceDescriptors) + + self.assertEqual("description", descriptors.description) + self.assertEqual("display name", descriptors.displayName) + + def test_advanced_commerce_one_time_charge_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceOneTimeChargeItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceOneTimeChargeItem).structure(item_dict, AdvancedCommerceOneTimeChargeItem) + + self.assertEqual("description", item.description) + self.assertEqual("display name", item.displayName) + self.assertEqual("sku", item.SKU) + self.assertEqual(15000, item.price) + + def test_advanced_commerce_subscription_create_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionCreateItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionCreateItem).structure(item_dict, AdvancedCommerceSubscriptionCreateItem) + + self.assertEqual("description", item.description) + self.assertEqual("display name", item.displayName) + self.assertEqual("sku", item.SKU) + self.assertEqual(20000, item.price) + + def test_advanced_commerce_request_refund_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceRequestRefundItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceRequestRefundItem).structure(item_dict, AdvancedCommerceRequestRefundItem) + + self.assertEqual("sku", item.SKU) + self.assertEqual(AdvancedCommerceRefundReason.LEGAL, item.refundReason) + self.assertEqual("LEGAL", item.rawRefundReason) + self.assertEqual(AdvancedCommerceRefundType.FULL, item.refundType) + self.assertEqual("FULL", item.rawRefundType) + self.assertTrue(item.revoke) + self.assertEqual(5000, item.refundAmount) + + def test_advanced_commerce_offer_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceOffer.json') + + offer_dict = json.loads(json_data) + offer = _get_cattrs_converter(AdvancedCommerceOffer).structure(offer_dict, AdvancedCommerceOffer) + + self.assertEqual(AdvancedCommerceOfferPeriod.P1W, offer.period) + self.assertEqual("P1W", offer.rawPeriod) + self.assertEqual(3, offer.periodCount) + self.assertEqual(5000, offer.price) + self.assertEqual(AdvancedCommerceOfferReason.WIN_BACK, offer.reason) + self.assertEqual("WIN_BACK", offer.rawReason) + + def test_advanced_commerce_one_time_charge_create_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceOneTimeChargeCreateRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceOneTimeChargeCreateRequest).structure(request_dict, AdvancedCommerceOneTimeChargeCreateRequest) + + self.assertEqual("USD", request.currency) + self.assertIsNotNone(request.item) + self.assertEqual("description", request.item.description) + self.assertEqual("display name", request.item.displayName) + self.assertEqual("sku", request.item.SKU) + self.assertEqual(10000, request.item.price) + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440000", str(request.requestInfo.requestReferenceId)) + self.assertEqual("taxCode", request.taxCode) + self.assertEqual("USA", request.storefront) + self.assertEqual("CREATE_ONE_TIME_CHARGE", request.operation) + self.assertEqual("1", request.version) + + def test_advanced_commerce_subscription_create_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionCreateRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionCreateRequest).structure(request_dict, AdvancedCommerceSubscriptionCreateRequest) + + self.assertEqual("USD", request.currency) + self.assertIsNotNone(request.descriptors) + self.assertEqual("description", request.descriptors.description) + self.assertEqual("display name", request.descriptors.displayName) + self.assertEqual(2, len(request.items)) + self.assertEqual("sku", request.items[0].SKU) + self.assertEqual(20000, request.items[0].price) + self.assertEqual("sku", request.items[1].SKU) + self.assertEqual(30000, request.items[1].price) + self.assertEqual("P1M", request.period) + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440001", str(request.requestInfo.requestReferenceId)) + self.assertEqual("taxCode", request.taxCode) + self.assertEqual("USA", request.storefront) + self.assertEqual("transactionId", request.previousTransactionId) + + def test_advanced_commerce_request_refund_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceRequestRefundRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceRequestRefundRequest).structure(request_dict, AdvancedCommerceRequestRefundRequest) + + self.assertEqual(2, len(request.items)) + self.assertEqual("sku", request.items[0].SKU) + self.assertEqual(AdvancedCommerceRefundReason.LEGAL, request.items[0].refundReason) + self.assertEqual(AdvancedCommerceRefundType.FULL, request.items[0].refundType) + self.assertTrue(request.items[0].revoke) + self.assertEqual("sku", request.items[1].SKU) + self.assertEqual(AdvancedCommerceRefundReason.OTHER, request.items[1].refundReason) + self.assertEqual(AdvancedCommerceRefundType.PRORATED, request.items[1].refundType) + self.assertFalse(request.items[1].revoke) + self.assertTrue(request.refundRiskingPreference) + self.assertEqual("550e8400-e29b-41d4-a716-446655440002", str(request.requestInfo.requestReferenceId)) + self.assertEqual("USD", request.currency) + self.assertEqual("USA", request.storefront) + + def test_advanced_commerce_subscription_cancel_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionCancelRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionCancelRequest).structure(request_dict, AdvancedCommerceSubscriptionCancelRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440003", str(request.requestInfo.requestReferenceId)) + self.assertEqual("USA", request.storefront) + + def test_advanced_commerce_subscription_revoke_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionRevokeRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionRevokeRequest).structure(request_dict, AdvancedCommerceSubscriptionRevokeRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440004", str(request.requestInfo.requestReferenceId)) + self.assertTrue(request.refundRiskingPreference) + self.assertEqual("USA", request.storefront) + + def test_advanced_commerce_subscription_price_change_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionPriceChangeRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionPriceChangeRequest).structure(request_dict, AdvancedCommerceSubscriptionPriceChangeRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440005", str(request.requestInfo.requestReferenceId)) + self.assertEqual(1, len(request.items)) + self.assertEqual("sku123", request.items[0].SKU) + self.assertEqual(15000, request.items[0].price) + self.assertEqual("USD", request.currency) + + def test_advanced_commerce_request_refund_response_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceRequestRefundResponse.json') + + response_dict = json.loads(json_data) + response = _get_cattrs_converter(AdvancedCommerceRequestRefundResponse).structure(response_dict, AdvancedCommerceRequestRefundResponse) + + self.assertEqual("signed_transaction_info_value", response.signedTransactionInfo) + + def test_advanced_commerce_subscription_cancel_response_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionCancelResponse.json') + + response_dict = json.loads(json_data) + response = _get_cattrs_converter(AdvancedCommerceSubscriptionCancelResponse).structure(response_dict, AdvancedCommerceSubscriptionCancelResponse) + + self.assertEqual("signed_renewal_info", response.signedRenewalInfo) + self.assertEqual("signed_transaction_info", response.signedTransactionInfo) + + def test_advanced_commerce_subscription_revoke_response_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionRevokeResponse.json') + + response_dict = json.loads(json_data) + response = _get_cattrs_converter(AdvancedCommerceSubscriptionRevokeResponse).structure(response_dict, AdvancedCommerceSubscriptionRevokeResponse) + + self.assertEqual("signed_renewal_info", response.signedRenewalInfo) + self.assertEqual("signed_transaction_info", response.signedTransactionInfo) + + def test_advanced_commerce_subscription_price_change_response_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionPriceChangeResponse.json') + + response_dict = json.loads(json_data) + response = _get_cattrs_converter(AdvancedCommerceSubscriptionPriceChangeResponse).structure(response_dict, AdvancedCommerceSubscriptionPriceChangeResponse) + + self.assertEqual("signed_renewal_info", response.signedRenewalInfo) + self.assertEqual("signed_transaction_info", response.signedTransactionInfo) + + def test_advanced_commerce_subscription_change_metadata_response_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionChangeMetadataResponse.json') + + response_dict = json.loads(json_data) + response = _get_cattrs_converter(AdvancedCommerceSubscriptionChangeMetadataResponse).structure(response_dict, AdvancedCommerceSubscriptionChangeMetadataResponse) + + self.assertEqual("signed_renewal_info", response.signedRenewalInfo) + self.assertEqual("signed_transaction_info", response.signedTransactionInfo) + + + def test_advanced_commerce_subscription_migrate_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionMigrateRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionMigrateRequest).structure(request_dict, AdvancedCommerceSubscriptionMigrateRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440006", str(request.requestInfo.requestReferenceId)) + self.assertIsNotNone(request.descriptors) + self.assertEqual("description", request.descriptors.description) + self.assertEqual("display name", request.descriptors.displayName) + self.assertEqual(1, len(request.items)) + self.assertEqual("sku", request.items[0].SKU) + self.assertEqual("targetProductId", request.targetProductId) + self.assertEqual("taxCode", request.taxCode) + + def test_advanced_commerce_subscription_modify_in_app_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionModifyInAppRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionModifyInAppRequest).structure(request_dict, AdvancedCommerceSubscriptionModifyInAppRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440007", str(request.requestInfo.requestReferenceId)) + self.assertEqual("transactionId", request.transactionId) + self.assertTrue(request.retainBillingCycle) + self.assertIsNotNone(request.descriptors) + self.assertEqual("description", request.descriptors.description) + self.assertEqual("display name", request.descriptors.displayName) + self.assertEqual("taxCode", request.taxCode) + self.assertEqual("USD", request.currency) + + def test_advanced_commerce_subscription_reactivate_in_app_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionReactivateInAppRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionReactivateInAppRequest).structure(request_dict, AdvancedCommerceSubscriptionReactivateInAppRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440008", str(request.requestInfo.requestReferenceId)) + self.assertEqual("transactionId", request.transactionId) + self.assertEqual(1, len(request.items)) + self.assertEqual("sku", request.items[0].SKU) + + def test_advanced_commerce_subscription_change_metadata_request_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionChangeMetadataRequest.json') + + request_dict = json.loads(json_data) + request = _get_cattrs_converter(AdvancedCommerceSubscriptionChangeMetadataRequest).structure(request_dict, AdvancedCommerceSubscriptionChangeMetadataRequest) + + self.assertIsNotNone(request.requestInfo) + self.assertEqual("550e8400-e29b-41d4-a716-446655440009", str(request.requestInfo.requestReferenceId)) + self.assertEqual(1, len(request.items)) + self.assertEqual("currentSKU", request.items[0].currentSKU) + self.assertEqual("sku", request.items[0].SKU) + + def test_advanced_commerce_subscription_migrate_descriptors_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionMigrateDescriptors.json') + + descriptors_dict = json.loads(json_data) + descriptors = _get_cattrs_converter(AdvancedCommerceSubscriptionMigrateDescriptors).structure(descriptors_dict, AdvancedCommerceSubscriptionMigrateDescriptors) + + self.assertEqual("description", descriptors.description) + self.assertEqual("displayName", descriptors.displayName) + + def test_advanced_commerce_subscription_modify_descriptors_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionModifyDescriptors.json') + + descriptors_dict = json.loads(json_data) + descriptors = _get_cattrs_converter(AdvancedCommerceSubscriptionModifyDescriptors).structure(descriptors_dict, AdvancedCommerceSubscriptionModifyDescriptors) + + self.assertEqual("description", descriptors.description) + self.assertEqual("displayName", descriptors.displayName) + + def test_advanced_commerce_subscription_change_metadata_descriptors_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionChangeMetadataDescriptors.json') + + descriptors_dict = json.loads(json_data) + descriptors = _get_cattrs_converter(AdvancedCommerceSubscriptionChangeMetadataDescriptors).structure(descriptors_dict, AdvancedCommerceSubscriptionChangeMetadataDescriptors) + + self.assertEqual("description", descriptors.description) + self.assertEqual("displayName", descriptors.displayName) + + def test_advanced_commerce_subscription_change_metadata_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionChangeMetadataItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionChangeMetadataItem).structure(item_dict, AdvancedCommerceSubscriptionChangeMetadataItem) + + self.assertEqual("currentSku", item.currentSKU) + self.assertEqual("sku", item.SKU) + self.assertEqual("description", item.description) + self.assertEqual("displayName", item.displayName) + + def test_advanced_commerce_subscription_migrate_renewal_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionMigrateRenewalItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionMigrateRenewalItem).structure(item_dict, AdvancedCommerceSubscriptionMigrateRenewalItem) + + self.assertEqual("sku", item.SKU) + self.assertEqual("description", item.description) + self.assertEqual("displayName", item.displayName) + + def test_advanced_commerce_subscription_modify_add_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionModifyAddItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionModifyAddItem).structure(item_dict, AdvancedCommerceSubscriptionModifyAddItem) + + self.assertEqual("sku", item.SKU) + self.assertEqual("description", item.description) + self.assertEqual("displayName", item.displayName) + self.assertEqual(12000, item.price) + + def test_advanced_commerce_subscription_modify_change_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionModifyChangeItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionModifyChangeItem).structure(item_dict, AdvancedCommerceSubscriptionModifyChangeItem) + + self.assertEqual("currentSku", item.currentSKU) + self.assertEqual("sku", item.SKU) + self.assertEqual("description", item.description) + self.assertEqual("displayName", item.displayName) + self.assertEqual(13000, item.price) + + def test_advanced_commerce_subscription_modify_remove_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionModifyRemoveItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionModifyRemoveItem).structure(item_dict, AdvancedCommerceSubscriptionModifyRemoveItem) + + self.assertEqual("sku", item.SKU) + + def test_advanced_commerce_subscription_modify_period_change_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionModifyPeriodChange.json') + + change_dict = json.loads(json_data) + change = _get_cattrs_converter(AdvancedCommerceSubscriptionModifyPeriodChange).structure(change_dict, AdvancedCommerceSubscriptionModifyPeriodChange) + + self.assertEqual("P3M", change.period) + + def test_advanced_commerce_subscription_price_change_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionPriceChangeItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionPriceChangeItem).structure(item_dict, AdvancedCommerceSubscriptionPriceChangeItem) + + self.assertEqual("sku", item.SKU) + self.assertEqual(16000, item.price) + self.assertEqual("dependentSKU", item.dependentSKUs[0]) + + def test_advanced_commerce_subscription_price_change_item_dependent_sku_validation(self): + valid_sku = "A" * 128 + too_long_sku = "A" * 129 + + # Valid SKU in dependentSKUs is accepted + item = AdvancedCommerceSubscriptionPriceChangeItem(SKU="sku", price=1000, dependentSKUs=[valid_sku]) + self.assertEqual(valid_sku, item.dependentSKUs[0]) + + # Too-long SKU in dependentSKUs raises ValueError + with self.assertRaises(ValueError): + AdvancedCommerceSubscriptionPriceChangeItem(SKU="sku", price=1000, dependentSKUs=[too_long_sku]) + + # None list is allowed (field is optional) + item_none = AdvancedCommerceSubscriptionPriceChangeItem(SKU="sku", price=1000, dependentSKUs=None) + self.assertIsNone(item_none.dependentSKUs) + + def test_advanced_commerce_subscription_reactivate_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionReactivateItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionReactivateItem).structure(item_dict, AdvancedCommerceSubscriptionReactivateItem) + + self.assertEqual("sku", item.SKU) + + def test_advanced_commerce_request_info_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceRequestInfo.json') + + info_dict = json.loads(json_data) + info = _get_cattrs_converter(AdvancedCommerceRequestInfo).structure(info_dict, AdvancedCommerceRequestInfo) + + self.assertEqual("550e8400-e29b-41d4-a716-446655440010", str(info.requestReferenceId)) + self.assertEqual("660e8400-e29b-41d4-a716-446655440011", str(info.appAccountToken)) + self.assertEqual("consistency_token_value", info.consistencyToken) + + def test_advanced_commerce_subscription_migrate_item_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionMigrateItem.json') + + item_dict = json.loads(json_data) + item = _get_cattrs_converter(AdvancedCommerceSubscriptionMigrateItem).structure(item_dict, AdvancedCommerceSubscriptionMigrateItem) + + self.assertEqual("sku", item.SKU) + self.assertEqual("description", item.description) + self.assertEqual("displayName", item.displayName) + + def test_advanced_commerce_subscription_migrate_response_deserialization(self): + json_data = read_data_from_file('tests/resources/models/advancedCommerceSubscriptionMigrateResponse.json') + + response_dict = json.loads(json_data) + response = _get_cattrs_converter(AdvancedCommerceSubscriptionMigrateResponse).structure(response_dict, AdvancedCommerceSubscriptionMigrateResponse) + + self.assertEqual("signed_renewal_info_value", response.signedRenewalInfo) + self.assertEqual("signed_transaction_info_value", response.signedTransactionInfo)