Skip to content

Commit cc851c9

Browse files
authored
Support Python 3 and some updates to support with latest version of Libraries (Fueled#1)
* Remove Site Dependency from the site. * remove django importlib dependecy and switch to python's import lib * remove django importlib dependecy and switch to python's import lib * Add migrations folder * Update Url files * fix(serializers): Fix all the models serializers to include fields * add json field dependency * Update customer create to take metadata as well
1 parent 7733fdf commit cc851c9

File tree

9 files changed

+236
-40
lines changed

9 files changed

+236
-40
lines changed

payments/admin.py

+6-23
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,13 @@
1414
from .utils import get_user_model
1515

1616

17-
def user_search_fields():
17+
def user_search_fields(): # coverage: omit
1818
User = get_user_model()
19-
USERNAME_FIELD = getattr(User, "USERNAME_FIELD", None)
20-
fields = []
21-
if USERNAME_FIELD is not None:
22-
# Using a Django 1.5+ User model
23-
fields = [
24-
"user__{0}".format(USERNAME_FIELD)
25-
]
26-
27-
try:
28-
# get_field_by_name throws FieldDoesNotExist if the field is not
29-
# present on the model
30-
# pylint: disable=W0212,E1103
31-
User._meta.get_field_by_name("email")
32-
fields += ["user__email"]
33-
except FieldDoesNotExist:
34-
pass
35-
else:
36-
# Using a pre-Django 1.5 User model
37-
fields = [
38-
"user__username",
39-
"user__email"
40-
]
19+
fields = [
20+
"user__{0}".format(User.USERNAME_FIELD)
21+
]
22+
if "email" in [f.name for f in User._meta.fields]:
23+
fields += ["user__email"]
4124
return fields
4225

4326

payments/api/serializers.py

+7
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,33 @@
2626
class EventProcessingExceptionSerializer(ModelSerializer):
2727
class Meta:
2828
model = EventProcessingException
29+
fields = '__all__'
2930

3031

3132
class EventSerializer(ModelSerializer):
3233
event_processing_exceptions = EventProcessingExceptionSerializer(source='event_processing_exception_serializer_set', many=True, read_only=True)
3334

3435
class Meta:
3536
model = Event
37+
fields = '__all__'
3638

3739

3840
class CurrentSubscriptionSerializer(ModelSerializer):
3941
class Meta:
4042
model = CurrentSubscription
43+
fields = '__all__'
4144

4245

4346
class ChargeSerializer(ModelSerializer):
4447
class Meta:
4548
model = Charge
49+
fields = '__all__'
4650

4751

4852
class InvoiceItemSerializer(ModelSerializer):
4953
class Meta:
5054
model = InvoiceItem
55+
fields = '__all__'
5156

5257

5358
class InvoiceSerializer(ModelSerializer):
@@ -56,6 +61,7 @@ class InvoiceSerializer(ModelSerializer):
5661

5762
class Meta:
5863
model = Invoice
64+
fields = '__all__'
5965

6066

6167
class CurrentCustomerSerializer(ModelSerializer):
@@ -64,6 +70,7 @@ class CurrentCustomerSerializer(ModelSerializer):
6470

6571
class Meta:
6672
model = Customer
73+
fields = '__all__'
6774

6875

6976
"""

payments/api/urls.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from django.conf.urls import patterns, include, url
1+
from django.conf.urls import include, url
22

3-
import views
3+
from payments.api import views
44

5-
urlpatterns = patterns('',
5+
urlpatterns = [
66
url(r'^current-user/$', views.CurrentCustomerDetailView.as_view(), name='stripe-current-customer-detail'),
77
url(r'^subscription/$', views.SubscriptionView.as_view(), name='stripe-subscription'),
88
url(r'^change-card/$', views.ChangeCardView.as_view(), name='stripe-change-card'),
@@ -12,4 +12,4 @@
1212
url(r'^events/$', views.EventListView.as_view(), name='stripe-events'),
1313
url(r'^webhook/$', views.WebhookView.as_view(), name='stripe-webhook'),
1414
url(r'^cancel/$', views.CancelView.as_view(), name='stripe-cancel'),
15-
)
15+
]

payments/migrations/0001_initial.py

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.10.3 on 2016-12-20 16:32
3+
from __future__ import unicode_literals
4+
5+
from django.conf import settings
6+
from django.db import migrations, models
7+
import django.db.models.deletion
8+
import django.utils.timezone
9+
import jsonfield.fields
10+
11+
12+
class Migration(migrations.Migration):
13+
14+
initial = True
15+
16+
dependencies = [
17+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
18+
]
19+
20+
operations = [
21+
migrations.CreateModel(
22+
name='Charge',
23+
fields=[
24+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
25+
('stripe_id', models.CharField(max_length=255, unique=True)),
26+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
27+
('card_last_4', models.CharField(blank=True, max_length=4)),
28+
('card_kind', models.CharField(blank=True, max_length=50)),
29+
('currency', models.CharField(default='usd', max_length=10)),
30+
('amount', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
31+
('amount_refunded', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
32+
('description', models.TextField(blank=True)),
33+
('paid', models.NullBooleanField()),
34+
('disputed', models.NullBooleanField()),
35+
('refunded', models.NullBooleanField()),
36+
('captured', models.NullBooleanField()),
37+
('fee', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
38+
('receipt_sent', models.BooleanField(default=False)),
39+
('charge_created', models.DateTimeField(blank=True, null=True)),
40+
],
41+
options={
42+
'abstract': False,
43+
},
44+
),
45+
migrations.CreateModel(
46+
name='CurrentSubscription',
47+
fields=[
48+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
49+
('plan', models.CharField(max_length=100)),
50+
('quantity', models.IntegerField()),
51+
('start', models.DateTimeField()),
52+
('status', models.CharField(max_length=25)),
53+
('cancel_at_period_end', models.BooleanField(default=False)),
54+
('canceled_at', models.DateTimeField(blank=True, null=True)),
55+
('current_period_end', models.DateTimeField(blank=True, null=True)),
56+
('current_period_start', models.DateTimeField(blank=True, null=True)),
57+
('ended_at', models.DateTimeField(blank=True, null=True)),
58+
('trial_end', models.DateTimeField(blank=True, null=True)),
59+
('trial_start', models.DateTimeField(blank=True, null=True)),
60+
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
61+
('currency', models.CharField(default='usd', max_length=10)),
62+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
63+
],
64+
),
65+
migrations.CreateModel(
66+
name='Customer',
67+
fields=[
68+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
69+
('stripe_id', models.CharField(max_length=255, unique=True)),
70+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
71+
('card_fingerprint', models.CharField(blank=True, max_length=200)),
72+
('card_last_4', models.CharField(blank=True, max_length=4)),
73+
('card_kind', models.CharField(blank=True, max_length=50)),
74+
('date_purged', models.DateTimeField(editable=False, null=True)),
75+
('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
76+
],
77+
options={
78+
'abstract': False,
79+
},
80+
),
81+
migrations.CreateModel(
82+
name='Event',
83+
fields=[
84+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
85+
('stripe_id', models.CharField(max_length=255, unique=True)),
86+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
87+
('kind', models.CharField(max_length=250)),
88+
('livemode', models.BooleanField(default=False)),
89+
('webhook_message', jsonfield.fields.JSONField()),
90+
('validated_message', jsonfield.fields.JSONField(null=True)),
91+
('valid', models.NullBooleanField()),
92+
('processed', models.BooleanField(default=False)),
93+
('customer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payments.Customer')),
94+
],
95+
options={
96+
'abstract': False,
97+
},
98+
),
99+
migrations.CreateModel(
100+
name='EventProcessingException',
101+
fields=[
102+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
103+
('data', models.TextField()),
104+
('message', models.CharField(max_length=500)),
105+
('traceback', models.TextField()),
106+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
107+
('event', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payments.Event')),
108+
],
109+
),
110+
migrations.CreateModel(
111+
name='Invoice',
112+
fields=[
113+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
114+
('stripe_id', models.CharField(max_length=255)),
115+
('attempted', models.NullBooleanField()),
116+
('attempts', models.PositiveIntegerField(null=True)),
117+
('closed', models.BooleanField(default=False)),
118+
('paid', models.BooleanField(default=False)),
119+
('period_end', models.DateTimeField()),
120+
('period_start', models.DateTimeField()),
121+
('subtotal', models.DecimalField(decimal_places=2, max_digits=9)),
122+
('total', models.DecimalField(decimal_places=2, max_digits=9)),
123+
('currency', models.CharField(default='usd', max_length=10)),
124+
('date', models.DateTimeField()),
125+
('charge', models.CharField(blank=True, max_length=50)),
126+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
127+
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoices', to='payments.Customer')),
128+
],
129+
options={
130+
'ordering': ['-date'],
131+
},
132+
),
133+
migrations.CreateModel(
134+
name='InvoiceItem',
135+
fields=[
136+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
137+
('stripe_id', models.CharField(max_length=255)),
138+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
139+
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
140+
('currency', models.CharField(default='usd', max_length=10)),
141+
('period_start', models.DateTimeField()),
142+
('period_end', models.DateTimeField()),
143+
('proration', models.BooleanField(default=False)),
144+
('line_type', models.CharField(max_length=50)),
145+
('description', models.CharField(blank=True, max_length=200)),
146+
('plan', models.CharField(blank=True, max_length=100)),
147+
('quantity', models.IntegerField(null=True)),
148+
('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='payments.Invoice')),
149+
],
150+
),
151+
migrations.CreateModel(
152+
name='Transfer',
153+
fields=[
154+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
155+
('stripe_id', models.CharField(max_length=255, unique=True)),
156+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
157+
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
158+
('currency', models.CharField(default='usd', max_length=25)),
159+
('status', models.CharField(max_length=25)),
160+
('date', models.DateTimeField()),
161+
('description', models.TextField(blank=True, null=True)),
162+
('adjustment_count', models.IntegerField(null=True)),
163+
('adjustment_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
164+
('adjustment_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
165+
('charge_count', models.IntegerField(null=True)),
166+
('charge_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
167+
('charge_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
168+
('collected_fee_count', models.IntegerField(null=True)),
169+
('collected_fee_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
170+
('net', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
171+
('refund_count', models.IntegerField(null=True)),
172+
('refund_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
173+
('refund_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
174+
('validation_count', models.IntegerField(null=True)),
175+
('validation_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
176+
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers', to='payments.Event')),
177+
],
178+
options={
179+
'abstract': False,
180+
},
181+
),
182+
migrations.CreateModel(
183+
name='TransferChargeFee',
184+
fields=[
185+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
186+
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
187+
('currency', models.CharField(default='usd', max_length=10)),
188+
('application', models.TextField(blank=True, null=True)),
189+
('description', models.TextField(blank=True, null=True)),
190+
('kind', models.CharField(max_length=150)),
191+
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
192+
('transfer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='charge_fee_details', to='payments.Transfer')),
193+
],
194+
),
195+
migrations.AddField(
196+
model_name='currentsubscription',
197+
name='customer',
198+
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='current_subscription', to='payments.Customer'),
199+
),
200+
migrations.AddField(
201+
model_name='charge',
202+
name='customer',
203+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='charges', to='payments.Customer'),
204+
),
205+
migrations.AddField(
206+
model_name='charge',
207+
name='invoice',
208+
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='charges', to='payments.Invoice'),
209+
),
210+
]

payments/migrations/__init__.py

Whitespace-only changes.

payments/models.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
from django.utils.encoding import smart_str
1212
from django.template.loader import render_to_string
1313

14-
from django.contrib.sites.models import Site
15-
1614
import stripe
1715

1816
from jsonfield.fields import JSONField
@@ -370,7 +368,7 @@ def cancel(self, at_period_end=True):
370368
cancelled.send(sender=self, stripe_response=sub)
371369

372370
@classmethod
373-
def create(cls, user, card=None, plan=None, charge_immediately=True):
371+
def create(cls, user, card=None, plan=None, charge_immediately=True, metadata={}):
374372

375373
if card and plan:
376374
plan = PAYMENTS_PLANS[plan]["stripe_plan_id"]
@@ -390,7 +388,8 @@ def create(cls, user, card=None, plan=None, charge_immediately=True):
390388
email=user.email,
391389
card=card,
392390
plan=plan or DEFAULT_PLAN,
393-
trial_end=trial_end
391+
trial_end=trial_end,
392+
metadata=metadata
394393
)
395394

396395
if stripe_customer.active_card:
@@ -882,11 +881,9 @@ def sync_from_stripe_data(cls, data):
882881

883882
def send_receipt(self):
884883
if not self.receipt_sent:
885-
site = Site.objects.get_current()
886884
protocol = getattr(settings, "DEFAULT_HTTP_PROTOCOL", "http")
887885
ctx = {
888886
"charge": self,
889-
"site": site,
890887
"protocol": protocol,
891888
}
892889
subject = render_to_string("payments/email/subject.txt", ctx)

payments/settings.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,5 @@ def plan_from_stripe_id(stripe_id):
4949

5050

5151
def get_api_key():
52-
if settings.DEBUG:
53-
api_key = settings.STRIPE_PUBLIC_KEY
54-
else:
55-
api_key = settings.STRIPE_SECRET_KEY
56-
52+
api_key = settings.STRIPE_SECRET_KEY
5753
return api_key

payments/utils.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import datetime
22
import decimal
3+
from importlib import import_module
4+
35

46
from django.core.exceptions import ImproperlyConfigured
5-
from django.utils import importlib, timezone
7+
from django.utils import timezone
68

79

810
def convert_tstamp(response, field_name=None):
@@ -36,7 +38,7 @@ def load_path_attr(path): # pragma: no cover
3638
i = path.rfind(".")
3739
module, attr = path[:i], path[i + 1:]
3840
try:
39-
mod = importlib.import_module(module)
41+
mod = import_module(module)
4042
except ImportError as e:
4143
raise ImproperlyConfigured("Error importing {0}: '{1}'".format(module, e))
4244
try:

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def read(*parts):
4444
"pytz",
4545
"six",
4646
"djangorestframework>=3.1.1",
47+
"jsonfield>=1.0.3",
4748
],
4849
test_suite="runtests.runtests",
4950
tests_require=[

0 commit comments

Comments
 (0)