diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cd143423..7b2acf820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,46 @@ This release changes the pinned API version to 2026-06-24.preview. * Add support for error codes `anomalous_money_movement_request`, `failed_tax_calculation`, `financial_account_balance_does_not_support_currency`, `financial_account_capability_not_enabled`, and `financial_account_capability_restricted` on `QuotePreviewInvoice.LastFinalizationError` * Add support for error code `default_us_bank_account_cannot_be_archived` on `CannotProceedError` +## 15.3.0 - 2026-06-24 +This release changes the pinned API version to 2026-06-24.dahlia. + +* [#1836](https://github.com/stripe/stripe-python/pull/1836) Update generated code + * Add support for `release_details` on `Reserve.Hold` + * ⚠️ Add support for new value `tax_fund` on enum `BalanceTransaction.type` + * Change `Billing.CreditGrant.priority` to be required + * Add support for `buyer_id` on `Charge.PaymentMethodDetail.Bizum`, `ConfirmationToken.PaymentMethodPreview.Bizum`, `ConfirmationToken.PaymentMethodPreview.Blik`, `PaymentAttemptRecord.PaymentMethodDetail.Bizum`, `PaymentMethod.Bizum`, `PaymentMethod.Blik`, and `PaymentRecord.PaymentMethodDetail.Bizum` + * Add support for `transaction_link_id` on `Charge.PaymentMethodDetail.Card` + * ⚠️ Add support for new value `sui` on enums `Charge.PaymentMethodDetail.Crypto.network`, `PaymentAttemptRecord.PaymentMethodDetail.Crypto.network`, and `PaymentRecord.PaymentMethodDetail.Crypto.network` + * ⚠️ Add support for new value `usdsui` on enums `Charge.PaymentMethodDetail.Crypto.token_currency`, `PaymentAttemptRecord.PaymentMethodDetail.Crypto.token_currency`, and `PaymentRecord.PaymentMethodDetail.Crypto.token_currency` + * Add support for `fingerprint` on `Charge.PaymentMethodDetail.Pix`, `ConfirmationToken.PaymentMethodPreview.Pix`, `PaymentMethod.Pix`, and `SetupAttempt.PaymentMethodDetail.Pix` + * Add support for `sunbit` on `Checkout.Session.PaymentMethodOption`, `PaymentIntent.PaymentMethodOption`, `PaymentIntentConfirmParamsPaymentMethodOption`, `PaymentIntentCreateParamsPaymentMethodOption`, `PaymentIntentModifyParamsPaymentMethodOption`, and `checkout.SessionCreateParamsPaymentMethodOption` + * Add support for `billing_cycle_anchor_config` on `checkout.SessionCreateParamsSubscriptionDatum` + * Add support for `wechat_pay` on `Checkout.Session.PaymentMethodOption` + * Add support for `mastercard_compliance` on `Dispute.Evidence.EnhancedEvidence`, `Dispute.EvidenceDetail.EnhancedEligibility`, and `DisputeModifyParamsEvidenceEnhancedEvidence` + * ⚠️ Add support for new value `mastercard_compliance` on enum `Dispute.enhanced_eligibility_types` + * Add support for `status_details` on `FinancialConnections.Account` + * ⚠️ Add support for new value `validated` on enum `Identity.VerificationSession.Redaction.status` + * Add support for new value `satispay` on enums `InvoiceCreateParamsPaymentSetting.payment_method_types`, `InvoiceModifyParamsPaymentSetting.payment_method_types`, `SubscriptionCreateParamsPaymentSetting.payment_method_types`, and `SubscriptionModifyParamsPaymentSetting.payment_method_types` + * ⚠️ Add support for new value `satispay` on enums `Invoice.PaymentSetting.payment_method_types` and `Subscription.PaymentSetting.payment_method_types` + * ⚠️ Remove support for `stored_credential_usage` on `PaymentAttemptRecord.PaymentMethodDetail.Card` and `PaymentRecord.PaymentMethodDetail.Card` + * ⚠️ Change `PaymentAttemptRecord.PaymentMethodDetail.Card.description` and `PaymentRecord.PaymentMethodDetail.Card.description` to be optional + * ⚠️ Change `PaymentAttemptRecord.PaymentMethodDetail.Card.iin` and `PaymentRecord.PaymentMethodDetail.Card.iin` to be optional + * ⚠️ Change `PaymentAttemptRecord.PaymentMethodDetail.Card.issuer` and `PaymentRecord.PaymentMethodDetail.Card.issuer` to be optional + * Add support for `setup_future_usage` on `PaymentIntent.PaymentMethodOption.Satispay`, `PaymentIntentConfirmParamsPaymentMethodOptionSatispay`, `PaymentIntentCreateParamsPaymentMethodOptionSatispay`, and `PaymentIntentModifyParamsPaymentMethodOptionSatispay` + * Change `PaymentRecordReportRefundParams.refunded` to be optional + * Add support for `satispay` on `SetupAttempt.PaymentMethodDetail` + * Add support for `custom_fields`, `description`, and `footer` on `Subscription.InvoiceSetting`, `SubscriptionCreateParamsInvoiceSetting`, and `SubscriptionModifyParamsInvoiceSetting` + * Add support for `payment_method_options` and `payment_method` on `TopupCreateParams` + * Add support for new value `2026-06-24.dahlia` on enum `WebhookEndpointCreateParams.api_version` + * Add support for `mode` on `V2.Commerce.ProductCatalogImport` + * ⚠️ Add support for new value `promotion` on enum `V2.Commerce.ProductCatalogImport.feed_type` + * Add support for `sunbit_payments` on `V2.Core.Account.Configuration.Merchant.Capability`, `v2.core.AccountCreateParamsConfigurationMerchantCapability`, and `v2.core.AccountModifyParamsConfigurationMerchantCapability` + * Add support for `crypto_money_manager` and `money_manager` on `v2.core.AccountModifyParamsIdentityAttestationTermsOfService` + * ⚠️ Remove support for `crypto_storer` and `storer` on `v2.core.AccountModifyParamsIdentityAttestationTermsOfService` + * Add support for new value `promotion` on enum `v2.commerce.ProductCatalogImportCreateParams.feed_type` + * ⚠️ Add support for new value `sunbit_payments` on enum `EventsV2CoreAccountIncludingConfigurationMerchantCapabilityStatusUpdatedEvent.updated_capability` + * Add support for error codes `anomalous_money_movement_request`, `failed_tax_calculation`, `financial_account_balance_does_not_support_currency`, `financial_account_capability_not_enabled`, and `financial_account_capability_restricted` on `Invoice.LastFinalizationError`, `PaymentIntent.LastPaymentError`, `SetupAttempt.SetupError`, `SetupIntent.LastSetupError`, `StripeError`, and `Terminal.Reader.Action.ApiError` + ## 15.3.0b1 - 2026-05-27 This release changes the pinned API version to 2026-05-27.private. diff --git a/CODEGEN_VERSION b/CODEGEN_VERSION index 983636f7b..04898d96a 100644 --- a/CODEGEN_VERSION +++ b/CODEGEN_VERSION @@ -1 +1 @@ -6012b623b1c09ad54d466947da04511a042ee45a \ No newline at end of file +fff10f396daccd2c07ae48db99eb6b774bfc8430 \ No newline at end of file diff --git a/stripe/_api_requestor.py b/stripe/_api_requestor.py index 3d0ceb641..2c0a2afe7 100644 --- a/stripe/_api_requestor.py +++ b/stripe/_api_requestor.py @@ -24,7 +24,6 @@ NoReturn, Unpack, ) -import uuid from urllib.parse import urlsplit, urlunsplit, parse_qs # breaking circular dependency @@ -100,6 +99,11 @@ def is_v2_delete_resp(method: str, api_mode: ApiMode) -> bool: return method == "delete" and api_mode == "V2" +def _generate_idempotency_key() -> str: + b = os.urandom(16) + return f"{b[0:4].hex()}-{b[4:6].hex()}-{b[6:8].hex()}-{b[8:10].hex()}-{b[10:].hex()}" + + class _APIRequestor(object): _instance: ClassVar["_APIRequestor|None"] = None @@ -616,7 +620,7 @@ def request_headers( # IKs should be set for all POST requests and v2 delete requests if method == "post" or (api_mode == "V2" and method == "delete"): - headers.setdefault("Idempotency-Key", str(uuid.uuid4())) + headers.setdefault("Idempotency-Key", _generate_idempotency_key()) if method == "post": if api_mode == "V2": diff --git a/stripe/_http_client.py b/stripe/_http_client.py index b09b4984f..3f69f5a91 100644 --- a/stripe/_http_client.py +++ b/stripe/_http_client.py @@ -65,61 +65,11 @@ def _now_ms(): def new_default_http_client(*args: Any, **kwargs: Any) -> "HTTPClient": - """ - This method creates and returns a new HTTPClient based on what libraries are available. It uses the following precedence rules: - - 1. Urlfetch (this is provided by Google App Engine, so if it's present you probably want it) - 2. Requests (popular library, the top priority for all environments outside Google App Engine, but not always present) - 3. Pycurl (another library, not always present, not as preferred as Requests but at least it verifies SSL certs) - 4. urllib with a warning (basically always present, a reasonable final default) - - For performance, it only imports what it's actually going to use. But, it re-calculates every time its called, so probably save its result instead of calling it multiple times. - """ - try: - from google.appengine.api import urlfetch # type: ignore # noqa: F401 - except ImportError: - pass - else: - return UrlFetchClient(*args, **kwargs) - - try: - import requests # noqa: F401 - except ImportError: - pass - else: - return RequestsClient(*args, **kwargs) - - try: - import pycurl # type: ignore # noqa: F401 - except ImportError: - pass - else: - return PycurlClient(*args, **kwargs) - - return UrllibClient(*args, **kwargs) + return _default_sync_client(*args, **kwargs) def new_http_client_async_fallback(*args: Any, **kwargs: Any) -> "HTTPClient": - """ - Similar to `new_default_http_client` above, this returns a client that can handle async HTTP requests, if available. - """ - - try: - import httpx # noqa: F401 - import anyio # noqa: F401 - except ImportError: - pass - else: - return HTTPXClient(*args, **kwargs) - - try: - import aiohttp # noqa: F401 - except ImportError: - pass - else: - return AIOHTTPClient(*args, **kwargs) - - return NoImportFoundAsyncClient(*args, **kwargs) + return _default_async_client(*args, **kwargs) class HTTPClient(object): @@ -1550,3 +1500,76 @@ async def request_stream_async( async def close_async(self): self.raise_async_client_import_error() + + +# --- Client resolution --- +# Detect available HTTP libraries at module load time so the expensive imports +# (e.g. requests, httpx) happen during Python's init phase rather than when +# StripeClient() is constructed. This matters in environments like AWS Lambda +# where module loading has a generous timeout (10s) but handler invocation +# does not (often 3s). +# +# Sync client precedence: +# 1. Urlfetch (Google App Engine — if present, you probably want it) +# 2. Requests (popular, top priority outside GAE) +# 3. Pycurl (verifies SSL certs, but less preferred than Requests) +# 4. urllib (stdlib fallback, basically always present) +# +# Async client precedence: +# 1. httpx + anyio (both required) +# 2. aiohttp +# 3. NoImportFoundAsyncClient (raises on use) +# +# To add a new client: define the class above, then add it to the appropriate +# cascade below. The resolved class is stored directly — new_default_http_client() +# and new_http_client_async_fallback() just call it. + + +def _resolve_sync_client(): + try: + from google.appengine.api import urlfetch # type: ignore # noqa: F401 + + return UrlFetchClient + except ImportError: + pass + + try: + import requests # noqa: F401 + + return RequestsClient + except ImportError: + pass + + try: + import pycurl # type: ignore # noqa: F401 + + return PycurlClient + except ImportError: + pass + + return UrllibClient + + +def _resolve_async_client(): + try: + import httpx # noqa: F401 + import anyio # noqa: F401 + + return HTTPXClient + except ImportError: + pass + + try: + import aiohttp # noqa: F401 + + return AIOHTTPClient + except ImportError: + pass + + return NoImportFoundAsyncClient + + +# Called at module load time so HTTP library imports happen during Python's init +# phase rather than inside StripeClient.__init__(). +_default_sync_client = _resolve_sync_client() +_default_async_client = _resolve_async_client() diff --git a/tests/test_http_client.py b/tests/test_http_client.py index be0f8400f..ea74cf7d9 100644 --- a/tests/test_http_client.py +++ b/tests/test_http_client.py @@ -101,8 +101,8 @@ def test_default_httpclient_from_imports( with patch("builtins.__import__") as mocked_import_fn: mocked_import_fn.side_effect = mock_import(available_libs) - client = _http_client.new_default_http_client() - assert isinstance(client, expected) + resolved_class = _http_client._resolve_sync_client() + assert resolved_class is expected @pytest.mark.parametrize( ["available_libs", "expected"], @@ -123,8 +123,8 @@ def test_default_async_httpclient_from_imports( with patch("builtins.__import__") as mocked_import_fn: mocked_import_fn.side_effect = mock_import(available_libs) - client = _http_client.new_http_client_async_fallback() - assert isinstance(client, expected) + resolved_class = _http_client._resolve_async_client() + assert resolved_class is expected MakeReqFunc = Callable[[str, str, Dict[str, str], Optional[str]], Any]