Skip to content

Commit

Permalink
Merge d5838e2 into ae66dc1
Browse files Browse the repository at this point in the history
  • Loading branch information
remik committed Nov 3, 2020
2 parents ae66dc1 + d5838e2 commit 4b7f41b
Show file tree
Hide file tree
Showing 16 changed files with 409 additions and 223 deletions.
18 changes: 6 additions & 12 deletions .travis.yml
@@ -1,18 +1,12 @@
language: python
matrix:
include:
- python: 3.5
dist: trusty
sudo: false
- python: 3.6
dist: xenial
sudo: false
- python: 3.7
dist: xenial
sudo: true
cache: pip
python:
- "3.6"
- "3.7"
- "3.8"

install:
- pip install tox tox-travis coverage coveralls
- pip install tox-travis coverage coveralls

script:
- tox
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.txt
@@ -1,6 +1,14 @@
# Change Log
All notable changes to this project will be documented in this file.

##[0.10.0]
### Removed
- Added support for python 3.5
### Added
- Added support for python 3.8
- Added support for Django 3.0 and 3.1


##[0.9.7]
### Changed
- Handle refunds when part of the charge has already been manually refunded.
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Expand Up @@ -201,8 +201,8 @@ Another way of updating the credit card information is to run the `refresh_custo

Support
=======
* Django 1.11, Django 2.2
* Python 3.5-3.7
* Django 2.2-3.1
* Python 3.6-3.8

.. |travis| image:: https://secure.travis-ci.org/HealthByRo/aa-stripe.svg?branch=master
.. _travis: http://travis-ci.org/HealthByRo/aa-stripe
Expand Down
2 changes: 1 addition & 1 deletion aa_stripe/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
__title__ = "Ro Stripe"
__version__ = "0.9.7"
__version__ = "0.10.0"
__author__ = "Remigiusz Dymecki"
__license__ = "MIT"
__copyright__ = "Copyright 2019 Ro"
Expand Down
45 changes: 32 additions & 13 deletions aa_stripe/models.py
Expand Up @@ -6,7 +6,6 @@
from time import sleep

import simplejson as json
import six
import stripe
from dateutil.relativedelta import relativedelta
from django import dispatch
Expand Down Expand Up @@ -232,7 +231,8 @@ class StripeCoupon(StripeBasicModel):
),
)
livemode = models.BooleanField(
default=False, help_text=_("Flag indicating whether the object exists in live mode or test mode.")
default=False,
help_text=_("Flag indicating whether the object exists in live mode or test mode."),
)
max_redemptions = models.PositiveIntegerField(
blank=True,
Expand All @@ -255,10 +255,13 @@ class StripeCoupon(StripeBasicModel):
),
)
redeem_by = models.DateTimeField(
blank=True, null=True, help_text=_("Date after which the coupon can no longer be redeemed.")
blank=True,
null=True,
help_text=_("Date after which the coupon can no longer be redeemed."),
)
times_redeemed = models.PositiveIntegerField(
default=0, help_text=_("Number of times this coupon has been applied to a customer.")
default=0,
help_text=_("Number of times this coupon has been applied to a customer."),
)
valid = models.BooleanField(
default=False,
Expand Down Expand Up @@ -294,7 +297,7 @@ def update_from_stripe_data(self, stripe_coupon, exclude_fields=None, commit=Tru
update_data["amount_off"] = Decimal(update_data["amount_off"]) / 100

# also make sure the object is up to date (without the need to call database)
for key, value in six.iteritems(update_data):
for key, value in update_data.items():
setattr(self, key, value)

if commit:
Expand Down Expand Up @@ -337,7 +340,10 @@ def save(self, force_retrieve=False, *args, **kwargs):
coupon.is_deleted = True
super(StripeCoupon, coupon).save() # use super save() to call pre/post save signals
# update all fields in the local object in case someone tried to change them
self.update_from_stripe_data(stripe_coupon, exclude_fields=["metadata"] if not force_retrieve else [])
self.update_from_stripe_data(
stripe_coupon,
exclude_fields=["metadata"] if not force_retrieve else [],
)
self.stripe_response = stripe_coupon
except stripe.error.InvalidRequestError:
if force_retrieve:
Expand Down Expand Up @@ -400,7 +406,10 @@ def charge(self, idempotency_key=None):
customer = StripeCustomer.get_latest_active_customer_for_user(self.user)
self.customer = customer
if customer:
metadata = {"object_id": self.object_id, "content_type_id": self.content_type_id}
metadata = {
"object_id": self.object_id,
"content_type_id": self.content_type_id,
}
idempotency_key = "{}-{}-{}".format(metadata["object_id"], metadata["content_type_id"], idempotency_key)

params = {
Expand Down Expand Up @@ -470,13 +479,17 @@ def refund(self, amount_to_refund=None, retry_on_error=True):
if (amount_to_refund + self.amount_refunded) > self.amount:
raise StripeMethodNotAllowed("Refunds exceed charge")

idempotency_key = "{}-{}-{}-{}".format(self.object_id, self.content_type_id,
self.amount_refunded, amount_to_refund)
idempotency_key = "{}-{}-{}-{}".format(
self.object_id, self.content_type_id, self.amount_refunded, amount_to_refund
)

refund_id = ""
try:
stripe_refund = stripe.Refund.create(idempotency_key=idempotency_key,
charge=self.stripe_charge_id, amount=amount_to_refund)
stripe_refund = stripe.Refund.create(
idempotency_key=idempotency_key,
charge=self.stripe_charge_id,
amount=amount_to_refund,
)
refund_id = stripe_refund["id"]
except stripe.error.InvalidRequestError as e:
# retry if we're not already retrying (prevent infinite loop)
Expand All @@ -488,7 +501,9 @@ def refund(self, amount_to_refund=None, retry_on_error=True):
pass
else:
stripe_charge = stripe.Charge.retrieve(self.stripe_charge_id)

if stripe_charge.amount_refunded != self.amount_refunded and e.code != "charge_already_refunded":

# refresh data and retry
self.amount_refunded = stripe_charge.amount_refunded
# set amount_to_refund to None to request maximum refund
Expand Down Expand Up @@ -523,7 +538,8 @@ class StripeSubscriptionPlan(StripeBasicModel):
default="USD",
)
name = models.CharField(
max_length=255, help_text=_("Name of the plan, to be displayed on invoices and in the web interface.")
max_length=255,
help_text=_("Name of the plan, to be displayed on invoices and in the web interface."),
)
interval = models.CharField(
max_length=10,
Expand Down Expand Up @@ -607,7 +623,10 @@ class StripeSubscription(StripeBasicModel):
blank=True,
choices=STATUS_CHOICES,
)
metadata = JSONField(blank=True, help_text="https://stripe.com/docs/api/python#create_subscription-metadata")
metadata = JSONField(
blank=True,
help_text="https://stripe.com/docs/api/python#create_subscription-metadata",
)
tax_percent = models.DecimalField(
default=0,
validators=[MinValueValidator(0), MaxValueValidator(100)],
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
@@ -1,3 +1,4 @@
-r requirements/requirements-base.txt
-r requirements/requirements-django.txt
-r requirements/requirements-testing.txt
-r requirements/requirements-codestyle.txt
2 changes: 0 additions & 2 deletions requirements/requirements-base.txt
@@ -1,6 +1,4 @@
django>=2.2.14,<3
django-extensions>=2.1.3
stripe>=2.35.1,<3.0.0
djangorestframework>=3.6.0
simplejson>=3.10.0
six>=1.10.0
1 change: 1 addition & 0 deletions requirements/requirements-django.txt
@@ -0,0 +1 @@
django>=2.2.14,<3.2
2 changes: 1 addition & 1 deletion run_isort
@@ -1 +1 @@
isort --recursive -p aa_stripe -sd THIRDPARTY -m 0 -w 120 -y -s venv -s .tox
isort . --recursive -p aa_stripe -sd THIRDPARTY -m 0 -w 120 -s venv -s .tox
28 changes: 21 additions & 7 deletions runtests.py
Expand Up @@ -32,8 +32,26 @@ def flake8_main(args):

def isort_main():
print("Running isort code checking")
ret = subprocess.call(["isort", "--recursive", "-p", "aa_stripe", "-sd", "THIRDPARTY", "-m", "0", "-w", "120", "-y",
"-s", "venv", "-s", ".tox", "--check-only"])
ret = subprocess.call(
[
"isort",
".",
"--recursive",
"-p",
"aa_stripe",
"-sd",
"THIRDPARTY",
"-m",
"0",
"-w",
"120",
"-s",
"venv",
"-s",
".tox",
"--check-only",
]
)

if ret:
print("isort failed: Some modules have incorrectly ordered imports. Fix by running `./run_isort`")
Expand Down Expand Up @@ -94,11 +112,7 @@ def is_class(string):
except ValueError:
pass
else:
pytest_args = [
"--cov-report",
"xml",
"--cov",
"aa_stripe"] + pytest_args
pytest_args = ["--cov-report", "xml", "--cov", "aa_stripe"] + pytest_args

if first_arg.startswith("-"):
# `runtests.py [flags]`
Expand Down
26 changes: 16 additions & 10 deletions setup.py
Expand Up @@ -27,24 +27,27 @@ def get_packages(package):
"""
Return root package and all sub-packages.
"""
return [dirpath
for dirpath, dirnames, filenames in os.walk(package)
if os.path.exists(os.path.join(dirpath, "__init__.py"))]
return [
dirpath
for dirpath, dirnames, filenames in os.walk(package)
if os.path.exists(os.path.join(dirpath, "__init__.py"))
]


def get_package_data(package):
"""
Return all files under the root package, that are not in a
package themselves.
"""
walk = [(dirpath.replace(package + os.sep, "", 1), filenames)
for dirpath, dirnames, filenames in os.walk(package)
if not os.path.exists(os.path.join(dirpath, "__init__.py"))]
walk = [
(dirpath.replace(package + os.sep, "", 1), filenames)
for dirpath, dirnames, filenames in os.walk(package)
if not os.path.exists(os.path.join(dirpath, "__init__.py"))
]

filepaths = []
for base, filenames in walk:
filepaths.extend([os.path.join(base, filename)
for filename in filenames])
filepaths.extend([os.path.join(base, filename) for filename in filenames])
return {package: filepaths}


Expand All @@ -54,6 +57,9 @@ def get_package_data(package):
requirements = local_open("requirements/requirements-base.txt")
required_to_install = [dist.strip() for dist in requirements.readlines()]

requirements_django = local_open("requirements/requirements-django.txt")
required_to_install += [dist.strip() for dist in requirements_django.readlines()]


setup(
name="aa_stripe",
Expand All @@ -75,9 +81,9 @@ def get_package_data(package):
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Topic :: Internet :: WWW/HTTP",
]
],
)

0 comments on commit 4b7f41b

Please sign in to comment.