Skip to content

Commit

Permalink
Merge branch 'async/v2.3.0' of https://github.com/zt50tz/yookassa-sdk…
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeevnick committed Apr 11, 2022
2 parents bed9c7b + 9da37b0 commit 5afd1a3
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 55 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ Russian | [English](https://github.com/yoomoney/yookassa-sdk-python/blob/master/
Клиент для работы с платежами по [API ЮKassa](https://yookassa.ru/developers/api)
Подходит тем, у кого способ подключения к ЮKassa называется API.

Асинхронная версия.

## Требования

1. Python 2.7 or Python 3.x
1. Python 3.x
2. pip

## Установка
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
httpx
requests
setuptools
urllib3
Expand Down
61 changes: 47 additions & 14 deletions src/yookassa/client.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
# -*- coding: utf-8 -*-
import requests
from requests.adapters import HTTPAdapter
import typing
import httpx
import time
from requests.auth import _basic_auth_str
from urllib3 import Retry

from yookassa import Configuration
from yookassa.domain.common import RequestObject, UserAgent
from yookassa.domain.exceptions import ApiError, BadRequestError, ForbiddenError, NotFoundError, \
ResponseProcessingError, TooManyRequestsError, UnauthorizedError


class RetryTransport(httpx.HTTPTransport):
""" Адаптация urllib3.Retry для HTTPX """

def __init__(self, *args, total: int = 3, backoff_factor: float = 1, method_whitelist: typing.Optional[list] = None, status_forcelist: typing.Optional[list] = None, **kwargs):
super(RetryTransport, self).__init__(*args, **kwargs)
self.total = total
self.backoff_factor = backoff_factor
self.method_whitelist = method_whitelist
self.status_forcelist = status_forcelist

def handle_request(
self,
request: httpx.Request,
) -> httpx.Response:
retry = 0
resp = None
retry_active = not self.method_whitelist or request.method in self.method_whitelist
while retry < self.total:
retry += 1
if retry > 2:
time.sleep(self.backoff_factor)
try:
if resp is not None:
resp.close()
resp = super().handle_request(request)
except Exception:
if not retry_active:
raise
continue
if self.status_forcelist and resp.status_code in self.status_forcelist:
continue
break
return resp


class ApiClient:
endpoint = Configuration.api_endpoint()

Expand All @@ -29,35 +64,33 @@ def __init__(self):
if self.configuration.agent_module:
self.user_agent.module = self.configuration.agent_module

def request(self, method="", path="", query_params=None, headers=None, body=None):
async def request(self, method="", path="", query_params=None, headers=None, body=None):
if isinstance(body, RequestObject):
body.validate()
body = dict(body)

request_headers = self.prepare_request_headers(headers)
raw_response = self.execute(body, method, path, query_params, request_headers)
raw_response = await self.execute(body, method, path, query_params, request_headers)

if raw_response.status_code != 200:
self.__handle_error(raw_response)

return raw_response.json()

def execute(self, body, method, path, query_params, request_headers):
async def execute(self, body, method, path, query_params, request_headers):
session = self.get_session()
raw_response = session.request(method,
self.endpoint + path,
f'{self.endpoint}{path}',
params=query_params,
headers=request_headers,
json=body)
return raw_response

def get_session(self):
session = requests.Session()
retries = Retry(total=self.max_attempts,
backoff_factor=self.timeout / 1000,
method_whitelist=['POST'],
status_forcelist=[202])
session.mount('https://', HTTPAdapter(max_retries=retries))
def get_session(self) -> httpx.Client:
session = httpx.Client(
timeout=httpx.Timeout(self.timeout / 1000, connect=self.timeout / 1000),
transport=RetryTransport()
)
return session

def prepare_request_headers(self, headers):
Expand Down
12 changes: 6 additions & 6 deletions src/yookassa/deal.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self):
self.client = ApiClient()

@classmethod
def find_one(cls, deal_id):
async def find_one(cls, deal_id):
"""
Get receipt information
Expand All @@ -26,11 +26,11 @@ def find_one(cls, deal_id):
raise ValueError('Invalid payment_id value')

path = instance.base_path + '/' + deal_id
response = instance.client.request(HttpVerb.GET, path)
response = await instance.client.request(HttpVerb.GET, path)
return DealResponse(response)

@classmethod
def create(cls, params, idempotency_key=None):
async def create(cls, params, idempotency_key=None):
"""
Create receipt
Expand All @@ -55,13 +55,13 @@ def create(cls, params, idempotency_key=None):
else:
raise TypeError('Invalid params value type')

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return DealResponse(response)

@classmethod
def list(cls, params):
async def list(cls, params):
instance = cls()
path = cls.base_path

response = instance.client.request(HttpVerb.GET, path, params)
response = await instance.client.request(HttpVerb.GET, path, params)
return DealListResponse(response)
20 changes: 10 additions & 10 deletions src/yookassa/payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self):
self.client = ApiClient()

@classmethod
def find_one(cls, payment_id):
async def find_one(cls, payment_id):
"""
Get information about payment
Expand All @@ -26,11 +26,11 @@ def find_one(cls, payment_id):
raise ValueError('Invalid payment_id value')

path = instance.base_path + '/' + payment_id
response = instance.client.request(HttpVerb.GET, path)
response = await instance.client.request(HttpVerb.GET, path)
return PaymentResponse(response)

@classmethod
def create(cls, params, idempotency_key=None):
async def create(cls, params, idempotency_key=None):
"""
Create payment
Expand All @@ -55,11 +55,11 @@ def create(cls, params, idempotency_key=None):
else:
raise TypeError('Invalid params value type')

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return PaymentResponse(response)

@classmethod
def capture(cls, payment_id, params=None, idempotency_key=None):
async def capture(cls, payment_id, params=None, idempotency_key=None):
"""
Capture payment
Expand Down Expand Up @@ -88,11 +88,11 @@ def capture(cls, payment_id, params=None, idempotency_key=None):
else:
params_object = None

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return PaymentResponse(response)

@classmethod
def cancel(cls, payment_id, idempotency_key=None):
async def cancel(cls, payment_id, idempotency_key=None):
"""
Cancel payment
Expand All @@ -111,13 +111,13 @@ def cancel(cls, payment_id, idempotency_key=None):
headers = {
'Idempotence-Key': str(idempotency_key)
}
response = instance.client.request(HttpVerb.POST, path, None, headers)
response = await instance.client.request(HttpVerb.POST, path, None, headers)
return PaymentResponse(response)

@classmethod
def list(cls, params):
async def list(cls, params):
instance = cls()
path = cls.base_path

response = instance.client.request(HttpVerb.GET, path, params)
response = await instance.client.request(HttpVerb.GET, path, params)
return PaymentListResponse(response)
8 changes: 4 additions & 4 deletions src/yookassa/payout.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self):
self.client = ApiClient()

@classmethod
def find_one(cls, payout_id):
async def find_one(cls, payout_id):
"""
Get receipt information
Expand All @@ -26,11 +26,11 @@ def find_one(cls, payout_id):
raise ValueError('Invalid payment_id value')

path = instance.base_path + '/' + payout_id
response = instance.client.request(HttpVerb.GET, path)
response = await instance.client.request(HttpVerb.GET, path)
return PayoutResponse(response)

@classmethod
def create(cls, params, idempotency_key=None):
async def create(cls, params, idempotency_key=None):
"""
Create receipt
Expand All @@ -55,5 +55,5 @@ def create(cls, params, idempotency_key=None):
else:
raise TypeError('Invalid params value type')

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return PayoutResponse(response)
12 changes: 6 additions & 6 deletions src/yookassa/receipt.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self):
self.client = ApiClient()

@classmethod
def find_one(cls, receipt_id):
async def find_one(cls, receipt_id):
"""
Get receipt information
Expand All @@ -27,11 +27,11 @@ def find_one(cls, receipt_id):
raise ValueError('Invalid payment_id value')

path = instance.base_path + '/' + receipt_id
response = instance.client.request(HttpVerb.GET, path)
response = await instance.client.request(HttpVerb.GET, path)
return ReceiptResponse(response)

@classmethod
def create(cls, params, idempotency_key=None):
async def create(cls, params, idempotency_key=None):
"""
Create receipt
Expand All @@ -56,13 +56,13 @@ def create(cls, params, idempotency_key=None):
else:
raise TypeError('Invalid params value type')

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return ReceiptResponse(response)

@classmethod
def list(cls, params):
async def list(cls, params):
instance = cls()
path = cls.base_path

response = instance.client.request(HttpVerb.GET, path, params)
response = await instance.client.request(HttpVerb.GET, path, params)
return ReceiptListResponse(response)
12 changes: 6 additions & 6 deletions src/yookassa/refund.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self):
self.client = ApiClient()

@classmethod
def create(cls, params, idempotency_key=None):
async def create(cls, params, idempotency_key=None):
"""
Create refund
Expand All @@ -38,11 +38,11 @@ def create(cls, params, idempotency_key=None):
else:
raise TypeError('Invalid params value type')

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return RefundResponse(response)

@classmethod
def find_one(cls, refund_id):
async def find_one(cls, refund_id):
"""
Get refund information
Expand All @@ -53,13 +53,13 @@ def find_one(cls, refund_id):
if not isinstance(refund_id, str) or not refund_id:
raise ValueError('Invalid payment_id value')
path = instance.base_path + '/' + refund_id
response = instance.client.request(HttpVerb.GET, path)
response = await instance.client.request(HttpVerb.GET, path)
return RefundResponse(response)

@classmethod
def list(cls, params):
async def list(cls, params):
instance = cls()
path = cls.base_path

response = instance.client.request(HttpVerb.GET, path, params)
response = await instance.client.request(HttpVerb.GET, path, params)
return RefundListResponse(response)
4 changes: 2 additions & 2 deletions src/yookassa/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def __init__(self):
self.client = ApiClient()

@classmethod
def get_account_settings(cls, params=None):
async def get_account_settings(cls, params=None):
"""
Shop Info
Expand All @@ -21,5 +21,5 @@ def get_account_settings(cls, params=None):
instance = cls()
path = cls.base_path

response = instance.client.request(HttpVerb.GET, path, params)
response = await instance.client.request(HttpVerb.GET, path, params)
return response
12 changes: 6 additions & 6 deletions src/yookassa/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ def __init__(self):
:return: WebhookList
"""
@classmethod
def list(cls):
async def list(cls):
instance = cls()
path = cls.base_path

response = instance.client.request(HttpVerb.GET, path)
response = await instance.client.request(HttpVerb.GET, path)
return WebhookList(response)

"""
Expand All @@ -34,7 +34,7 @@ def list(cls):
:return: WebhookResponse
"""
@classmethod
def add(cls, params, idempotency_key=None):
async def add(cls, params, idempotency_key=None):
instance = cls()
path = cls.base_path
if not idempotency_key:
Expand All @@ -50,7 +50,7 @@ def add(cls, params, idempotency_key=None):
else:
raise TypeError('Invalid params value type')

response = instance.client.request(HttpVerb.POST, path, None, headers, params_object)
response = await instance.client.request(HttpVerb.POST, path, None, headers, params_object)
return WebhookResponse(response)

"""
Expand All @@ -61,7 +61,7 @@ def add(cls, params, idempotency_key=None):
:return: WebhookResponse
"""
@classmethod
def remove(cls, webhook_id, idempotency_key=None):
async def remove(cls, webhook_id, idempotency_key=None):
instance = cls()
path = cls.base_path + '/' + webhook_id
if not idempotency_key:
Expand All @@ -70,5 +70,5 @@ def remove(cls, webhook_id, idempotency_key=None):
'Idempotence-Key': str(idempotency_key)
}

response = instance.client.request(HttpVerb.DELETE, path, None, headers)
response = await instance.client.request(HttpVerb.DELETE, path, None, headers)
return WebhookResponse(response)

0 comments on commit 5afd1a3

Please sign in to comment.