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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,12 @@ jobs:
python_version:
# The last ~5 versions, once we're on schedule
# https://docs.stripe.com/sdks/versioning?lang=python#stripe-sdk-language-version-support-policy
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
- "pypy-3.7"
- "pypy-3.8"
- "pypy-3.9"
- "pypy-3.10"
- "pypy-3.11"
Expand All @@ -117,8 +113,7 @@ jobs:
name: Publish
if: >-
((github.event_name == 'workflow_dispatch') || (github.event_name == 'push')) &&
startsWith(github.ref, 'refs/tags/v') &&
endsWith(github.actor, '-stripe')
startsWith(github.ref, 'refs/tags/v')
needs: [build, test, lint]
runs-on: "ubuntu-24.04"
permissions:
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
[![pypi](https://img.shields.io/pypi/v/stripe.svg)](https://pypi.python.org/pypi/stripe)
[![Build Status](https://github.com/stripe/stripe-python/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/stripe/stripe-python/actions?query=branch%3Amaster)

> [!TIP]
> Want to chat live with Stripe engineers? Join us on our [Discord server](https://stripe.com/go/discord/python).

The Stripe Python library provides convenient access to the Stripe API from
applications written in the Python language. It includes a pre-defined set of
classes for API resources that initialize themselves dynamically from API
Expand All @@ -29,9 +32,9 @@ python -m pip install .

### Requirements

Per our [Language Version Support Policy](https://docs.stripe.com/sdks/versioning?lang=python#stripe-sdk-language-version-support-policy), we currently support **Python 3.7+**.
Per our [Language Version Support Policy](https://docs.stripe.com/sdks/versioning?lang=python#stripe-sdk-language-version-support-policy), we currently support **Python 3.9+**.

Support for Python 3.7 and 3.8 is deprecated and will be removed in an upcoming major version. Read more and see the full schedule in the docs: https://docs.stripe.com/sdks/versioning?lang=python#stripe-sdk-language-version-support-policy
Read more and see the full schedule in the docs: https://docs.stripe.com/sdks/versioning?lang=python#stripe-sdk-language-version-support-policy

#### Extended Support

Expand Down Expand Up @@ -195,7 +198,7 @@ print(customer.last_response.headers)
### How to use undocumented parameters and properties

In some cases, you might encounter parameters on an API request or fields on an API response that aren’t available in the SDKs.
This might happen when they’re undocumented or when they’re in preview and you aren’t using a preview SDK.
This might happen when they’re undocumented or when they’re in preview and you aren’t using a preview SDK.
See [undocumented params and properties](https://docs.stripe.com/sdks/server-side?lang=python#undocumented-params-and-fields) to send those parameters or access those fields.

### Writing a Plugin
Expand Down
10 changes: 3 additions & 7 deletions flake8_stripe/flake8_stripe.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,12 @@ class TypingImportsChecker:
version = "0.1.0"

# Rules:
# * typing_extensions v4.1.1 is the latest that supports Python 3.6
# so don't depend on anything from a more recent version than that.
#
# If we need something newer, maybe we can provide it for users on
# newer versions with a conditional import, but we'll cross that
# bridge when we come to it.

# If a symbol exists in both `typing` and `typing_extensions`, which
# should you use? Prefer `typing_extensions` if the symbol available there.
# in 4.1.1. In typing_extensions 4.7.0, `typing_extensions` started re-exporting
# EVERYTHING from `typing` but this is not the case in v4.1.1.

# now that we're into modern typing_extensions versions, we should probably prefer that over built-in typing _unless_ all of our `typing `needs are present in all supported Python versions. In that case, we could drop `typing_extensions`. See: https://go/j/DEVSDK-3046
allowed_typing_extensions_imports = [
"Literal",
"NoReturn",
Expand All @@ -36,6 +31,7 @@ class TypingImportsChecker:
"Awaitable",
"Never",
"override",
"deprecated",
]

allowed_typing_imports = [
Expand Down
10 changes: 4 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description = "Python bindings for the Stripe API"
authors = [{ name = "Stripe", email = "support@stripe.com" }]
license-files = ["LICENSE"]
keywords = ["stripe", "api", "payments"]
requires-python = ">=3.7"
requires-python = ">=3.9"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
Expand All @@ -27,11 +27,9 @@ classifiers = [
]

dependencies = [
"typing_extensions <= 4.2.0, > 3.7.2; python_version < '3.7'",
# The best typing support comes from 4.5.0+ but we can support down to
# 3.7.2 without throwing exceptions.
"typing_extensions >= 4.5.0; python_version >= '3.7'",
"requests >= 2.20; python_version >= '3.0'",
# this is the version where everything started getting re-exported, so it's a convenient backstop
"typing_extensions >= 4.7.0",
"requests >= 2.20",
]

[project.optional-dependencies]
Expand Down
12 changes: 6 additions & 6 deletions stripe/_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -1982,7 +1982,7 @@ def persons( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"get",
"/v1/accounts/{account}/persons".format(
account=sanitize_id(self.get("id"))
account=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -2037,7 +2037,7 @@ async def persons_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"get",
"/v1/accounts/{account}/persons".format(
account=sanitize_id(self.get("id"))
account=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -2098,7 +2098,7 @@ def reject( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"post",
"/v1/accounts/{account}/reject".format(
account=sanitize_id(self.get("id"))
account=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -2161,7 +2161,7 @@ async def reject_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"post",
"/v1/accounts/{account}/reject".format(
account=sanitize_id(self.get("id"))
account=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -2201,7 +2201,7 @@ def _build_instance_url(cls, sid):
return "%s/%s" % (base, extn)

def instance_url(self):
return self._build_instance_url(self.get("id"))
return self._build_instance_url(self._data.get("id"))

def deauthorize(self, **params):
params["stripe_user_id"] = self.id
Expand All @@ -2211,7 +2211,7 @@ def serialize(self, previous):
params = super(Account, self).serialize(previous)
previous = previous or self._previous or {}

for k, v in iter(self.items()):
for k, v in iter(self._data.items()):
if k == "individual" and isinstance(v, Person) and k not in params:
params[k] = v.serialize(previous.get(k, None))

Expand Down
13 changes: 7 additions & 6 deletions stripe/_api_requestor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import (
Any,
AsyncIterable,
Callable,
Dict,
List,
Mapping,
Expand Down Expand Up @@ -545,14 +546,14 @@ def request_headers(
ua: Dict[str, Union[str, "AppInfo"]] = {
"bindings_version": VERSION,
"lang": "python",
"publisher": "stripe",
"httplib": self._get_http_client().name,
}
for attr, func in [
["lang_version", platform.python_version],
["platform", platform.platform],
["uname", lambda: " ".join(platform.uname())],
]:
attr_funcs: List[Tuple[str, Callable[[], str]]] = [
("lang_version", platform.python_version),
]
if stripe.enable_telemetry:
attr_funcs.append(("platform", platform.platform))
for attr, func in attr_funcs:
try:
val = func()
except Exception:
Expand Down
7 changes: 3 additions & 4 deletions stripe/_api_resource.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from typing_extensions import Literal, Self
from typing_extensions import Literal, Self, deprecated

from stripe._error import InvalidRequestError
from stripe._stripe_object import StripeObject
from stripe._request_options import extract_options_from_dict
from stripe._api_mode import ApiMode
from stripe._base_address import BaseAddress
from stripe._api_requestor import _APIRequestor
from stripe import _util
from urllib.parse import quote_plus
from typing import (
Any,
Expand All @@ -26,7 +25,7 @@ class APIResource(StripeObject, Generic[T]):
OBJECT_NAME: ClassVar[str]

@classmethod
@_util.deprecated(
@deprecated(
"This method is deprecated and will be removed in a future version of stripe-python. Child classes of APIResource should define their own `retrieve` and use APIResource._request directly."
)
def retrieve(cls, id, **params) -> T:
Expand Down Expand Up @@ -55,7 +54,7 @@ def class_url(cls) -> str:
return "/v1/%ss" % (base,)

def instance_url(self) -> str:
id = self.get("id")
id = self._data.get("id")

if not isinstance(id, str):
raise InvalidRequestError(
Expand Down
4 changes: 2 additions & 2 deletions stripe/_application_fee.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def refund( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"post",
"/v1/application_fees/{id}/refunds".format(
id=sanitize_id(self.get("id"))
id=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -321,7 +321,7 @@ async def refund_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"post",
"/v1/application_fees/{id}/refunds".format(
id=sanitize_id(self.get("id"))
id=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down
4 changes: 2 additions & 2 deletions stripe/_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -2634,7 +2634,7 @@ def capture( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"post",
"/v1/charges/{charge}/capture".format(
charge=sanitize_id(self.get("id"))
charge=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -2705,7 +2705,7 @@ async def capture_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"post",
"/v1/charges/{charge}/capture".format(
charge=sanitize_id(self.get("id"))
charge=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down
4 changes: 2 additions & 2 deletions stripe/_credit_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ def void_credit_note( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"post",
"/v1/credit_notes/{id}/void".format(
id=sanitize_id(self.get("id"))
id=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -686,7 +686,7 @@ async def void_credit_note_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"post",
"/v1/credit_notes/{id}/void".format(
id=sanitize_id(self.get("id"))
id=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down
3 changes: 2 additions & 1 deletion stripe/_custom_method.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Optional
from stripe import _util
from urllib.parse import quote_plus
from typing_extensions import deprecated


# TODO(major): 1704.
@_util.deprecated(
@deprecated(
"the custom_method class decorator will be removed in a future version of stripe-python. Define custom methods directly and use StripeObject._static_request within."
)
def custom_method(
Expand Down
20 changes: 10 additions & 10 deletions stripe/_customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ def create_funding_instructions( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"post",
"/v1/customers/{customer}/funding_instructions".format(
customer=sanitize_id(self.get("id"))
customer=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -576,7 +576,7 @@ async def create_funding_instructions_async( # pyright: ignore[reportGeneralTyp
await self._request_async(
"post",
"/v1/customers/{customer}/funding_instructions".format(
customer=sanitize_id(self.get("id"))
customer=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -727,7 +727,7 @@ def delete_discount( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"delete",
"/v1/customers/{customer}/discount".format(
customer=sanitize_id(self.get("id"))
customer=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -782,7 +782,7 @@ async def delete_discount_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"delete",
"/v1/customers/{customer}/discount".format(
customer=sanitize_id(self.get("id"))
customer=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -879,7 +879,7 @@ def list_payment_methods( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"get",
"/v1/customers/{customer}/payment_methods".format(
customer=sanitize_id(self.get("id"))
customer=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -936,7 +936,7 @@ async def list_payment_methods_async( # pyright: ignore[reportGeneralTypeIssues
await self._request_async(
"get",
"/v1/customers/{customer}/payment_methods".format(
customer=sanitize_id(self.get("id"))
customer=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -1061,7 +1061,7 @@ def retrieve_payment_method( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"get",
"/v1/customers/{customer}/payment_methods/{payment_method}".format(
customer=sanitize_id(self.get("id")),
customer=sanitize_id(self._data.get("id")),
payment_method=sanitize_id(payment_method),
),
params=params,
Expand Down Expand Up @@ -1127,7 +1127,7 @@ async def retrieve_payment_method_async( # pyright: ignore[reportGeneralTypeIss
await self._request_async(
"get",
"/v1/customers/{customer}/payment_methods/{payment_method}".format(
customer=sanitize_id(self.get("id")),
customer=sanitize_id(self._data.get("id")),
payment_method=sanitize_id(payment_method),
),
params=params,
Expand Down Expand Up @@ -1916,7 +1916,7 @@ def fund_cash_balance( # pyright: ignore[reportGeneralTypeIssues]
self.resource._request(
"post",
"/v1/test_helpers/customers/{customer}/fund_cash_balance".format(
customer=sanitize_id(self.resource.get("id"))
customer=sanitize_id(self.resource._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -1973,7 +1973,7 @@ async def fund_cash_balance_async( # pyright: ignore[reportGeneralTypeIssues]
await self.resource._request_async(
"post",
"/v1/test_helpers/customers/{customer}/fund_cash_balance".format(
customer=sanitize_id(self.resource.get("id"))
customer=sanitize_id(self.resource._data.get("id"))
),
params=params,
),
Expand Down
4 changes: 2 additions & 2 deletions stripe/_dispute.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ def close( # pyright: ignore[reportGeneralTypeIssues]
self._request(
"post",
"/v1/disputes/{dispute}/close".format(
dispute=sanitize_id(self.get("id"))
dispute=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down Expand Up @@ -634,7 +634,7 @@ async def close_async( # pyright: ignore[reportGeneralTypeIssues]
await self._request_async(
"post",
"/v1/disputes/{dispute}/close".format(
dispute=sanitize_id(self.get("id"))
dispute=sanitize_id(self._data.get("id"))
),
params=params,
),
Expand Down
Loading
Loading