Skip to content

Commit 0b092c7

Browse files
committedMay 29, 2021
First commit
0 parents  commit 0b092c7

File tree

7 files changed

+191
-0
lines changed

7 files changed

+191
-0
lines changed
 

‎.gitignore

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
__pycache__/
2+
*.py[cod]
3+
4+
env/
5+
bin/
6+
build/
7+
_build/
8+
develop-eggs/
9+
dist/
10+
eggs/
11+
lib/
12+
lib64/
13+
parts/
14+
sdist/
15+
var/
16+
*.egg-info/
17+
*.egg
18+
.eggs
19+
.installed.cfg

‎LICENSE.txt

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (c) 2021, Bharat Chauhan
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice, this
7+
list of conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation and/or
11+
other materials provided with the distribution.
12+
13+
3. Neither the name of the copyright holder nor the names of its contributors
14+
may be used to endorse or promote products derived from this software without
15+
specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

‎README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# async-stripe
2+
3+
An asynchronous wrapper around the official stripe library.
4+
5+
/!\ **Note:** This is still a work in progress and doesn't provide async support
6+
for all the resources yet.
7+
8+
## Usage
9+
10+
The usage api is kept very similar to stripe's official library:
11+
12+
```python
13+
from async_stripe import stripe
14+
15+
stripe.api_key = '<stripe-secret-key>'
16+
17+
payment_intent = await stripe.PaymentIntent.create(amount=1000, currency='usd')
18+
19+
print(payment_intent.id)
20+
```
21+
22+
### Sync usage
23+
24+
Currently, only a limited operations have async usage. For those cases, you'll
25+
have to fall back to using sync code (without the `await` keyword).
26+
27+
## Requirements
28+
29+
+ Tornado (used for making async http requests)
30+
+ stripe (official stripe library)
31+
32+
33+
## License
34+
35+
[BSD-3-Clause](LICENSE.txt)

‎async_stripe/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .patched_stripe import stripe

‎async_stripe/patched_stripe.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import stripe as real_stripe
2+
from .resources import (
3+
AsyncPaymentMethod, AsyncPaymentIntent, AsyncCustomer
4+
)
5+
6+
class Stripe:
7+
def __init__(self):
8+
self._api_key = None
9+
self._public_key = None
10+
self._webhook_secret = None
11+
12+
PaymentMethod = AsyncPaymentMethod
13+
PaymentIntent = AsyncPaymentIntent
14+
Customer = AsyncCustomer
15+
16+
@property
17+
def api_key(self):
18+
return self._api_key
19+
20+
@api_key.setter
21+
def api_key(self, value):
22+
self._api_key = value
23+
real_stripe.api_key = value
24+
25+
def __getattr__(self, name):
26+
return getattr(real_stripe, name)
27+
28+
29+
stripe = Stripe()

‎async_stripe/resources/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from .base import AsyncBaseResource, BASE_URL
2+
3+
class AsyncPaymentMethod(AsyncBaseResource):
4+
resource = 'PaymentMethod'
5+
url = BASE_URL + 'payment_methods'
6+
7+
8+
class AsyncPaymentIntent(AsyncBaseResource):
9+
resource = 'PaymentIntent'
10+
url = BASE_URL + 'payment_intents'
11+
12+
13+
class AsyncCustomer(AsyncBaseResource):
14+
resource = 'Customer'
15+
url = BASE_URL + 'customers'
16+

‎async_stripe/resources/base.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import json
2+
from urllib.parse import urlencode
3+
from tornado.httpclient import AsyncHTTPClient, HTTPError
4+
from stripe.api_requestor import _api_encode
5+
from async_stripe.patched_stripe import real_stripe
6+
7+
8+
BASE_URL = 'https://api.stripe.com/v1/'
9+
10+
11+
client = AsyncHTTPClient()
12+
13+
14+
class ResourceMetaclass(type):
15+
def __getattr__(cls, name):
16+
resource = getattr(real_stripe, cls.resource)
17+
return getattr(resource, name)
18+
19+
20+
class AsyncBaseResource(metaclass=ResourceMetaclass):
21+
@classmethod
22+
async def fetch(cls, url, method='GET', data=None):
23+
if data:
24+
body = urlencode(list(_api_encode(data)), doseq=True)
25+
else:
26+
body = None
27+
28+
try:
29+
response = await client.fetch(
30+
url,
31+
method=method,
32+
auth_username=real_stripe.api_key,
33+
body=body
34+
)
35+
except HTTPError as e:
36+
# :TODO: log exception
37+
if hasattr(e, 'response'):
38+
print('ERROR:', e.code)
39+
print(e.response.body)
40+
raise
41+
except Exception as e:
42+
# :TODO: log exception
43+
raise
44+
else:
45+
obj = real_stripe.util.convert_to_stripe_object(json.loads(response.body), real_stripe.api_key)
46+
return obj
47+
48+
@classmethod
49+
async def retrieve(cls, id):
50+
url = cls.url + '/' + id
51+
obj = await cls.fetch(url)
52+
return obj
53+
54+
@classmethod
55+
async def create(cls, **kwargs):
56+
url = cls.url
57+
obj = await cls.fetch(url, method='POST', data=kwargs)
58+
return obj
59+
60+
@classmethod
61+
async def modify(cls, id, **kwargs):
62+
url = cls.url + '/' + id
63+
obj = await cls.fetch(url, method='POST', data=kwargs)
64+
return obj
65+

0 commit comments

Comments
 (0)