Skip to content
This repository was archived by the owner on Oct 7, 2023. It is now read-only.
Open
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
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ Endpoints
* current-user/ (GET)
* subscription/ (GET/POST)
* change-card/ (GET/POST)
* change-card-token/ (POST)
* charges/ (GET)
* invoices/ (GET)
* plans/ (GET)
* events/ (GET)
* webhook/ (POST)
* cancel/ (POST)

**ALL TEMPLATES AND AJAX VIEWS HAS BEEN REMOVED, USE ADDED ENDPOINTS**
**ALL TEMPLATES AND AJAX VIEWS HAVE BEEN REMOVED, USE ADDED ENDPOINTS**

Documentation can be found at http://django-stripe-payments.readthedocs.org
29 changes: 6 additions & 23 deletions payments/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,13 @@
from .utils import get_user_model


def user_search_fields():
def user_search_fields(): # coverage: omit
User = get_user_model()
USERNAME_FIELD = getattr(User, "USERNAME_FIELD", None)
fields = []
if USERNAME_FIELD is not None:
# Using a Django 1.5+ User model
fields = [
"user__{0}".format(USERNAME_FIELD)
]

try:
# get_field_by_name throws FieldDoesNotExist if the field is not
# present on the model
# pylint: disable=W0212,E1103
User._meta.get_field_by_name("email")
fields += ["user__email"]
except FieldDoesNotExist:
pass
else:
# Using a pre-Django 1.5 User model
fields = [
"user__username",
"user__email"
]
fields = [
"user__{0}".format(User.USERNAME_FIELD)
]
if "email" in [f.name for f in User._meta.fields]:
fields += ["user__email"]
return fields


Expand Down
11 changes: 11 additions & 0 deletions payments/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,33 @@
class EventProcessingExceptionSerializer(ModelSerializer):
class Meta:
model = EventProcessingException
fields = '__all__'


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

class Meta:
model = Event
fields = '__all__'


class CurrentSubscriptionSerializer(ModelSerializer):
class Meta:
model = CurrentSubscription
fields = '__all__'


class ChargeSerializer(ModelSerializer):
class Meta:
model = Charge
fields = '__all__'


class InvoiceItemSerializer(ModelSerializer):
class Meta:
model = InvoiceItem
fields = '__all__'


class InvoiceSerializer(ModelSerializer):
Expand All @@ -56,6 +61,7 @@ class InvoiceSerializer(ModelSerializer):

class Meta:
model = Invoice
fields = '__all__'


class CurrentCustomerSerializer(ModelSerializer):
Expand All @@ -64,6 +70,7 @@ class CurrentCustomerSerializer(ModelSerializer):

class Meta:
model = Customer
fields = '__all__'


"""
Expand All @@ -90,6 +97,10 @@ class CardSerializer(Serializer):
address_country = serializers.CharField(required=False, allow_null=True)


class CardTokenSerializer(Serializer):
token = serializers.CharField(required=True, help_text=u'Card token generated by stripe.js, or other api call.')


class CancelSerializer(Serializer):
confirm = serializers.BooleanField(required=True)

Expand Down
9 changes: 5 additions & 4 deletions payments/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from django.conf.urls import patterns, include, url
from django.conf.urls import include, url

import views
from payments.api import views

urlpatterns = patterns('',
urlpatterns = [
url(r'^current-user/$', views.CurrentCustomerDetailView.as_view(), name='stripe-current-customer-detail'),
url(r'^subscription/$', views.SubscriptionView.as_view(), name='stripe-subscription'),
url(r'^change-card/$', views.ChangeCardView.as_view(), name='stripe-change-card'),
url(r'^change-card-token/$', views.ChangeCardTokenView.as_view(), name='stripe-change-card-token'),
url(r'^charges/$', views.ChargeListView.as_view(), name='stripe-charges'),
url(r'^invoices/$', views.InvoiceListView.as_view(), name='stripe-invoices'),
url(r'^plans/$', views.PlanListView.as_view(), name='stripe-plans'),
url(r'^events/$', views.EventListView.as_view(), name='stripe-events'),
url(r'^webhook/$', views.WebhookView.as_view(), name='stripe-webhook'),
url(r'^cancel/$', views.CancelView.as_view(), name='stripe-cancel'),
)
]
37 changes: 37 additions & 0 deletions payments/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CurrentCustomerSerializer,
SubscriptionSerializer,
CardSerializer,
CardTokenSerializer,
CancelSerializer,
ChargeSerializer,
InvoiceSerializer,
Expand Down Expand Up @@ -120,6 +121,42 @@ def post(self, request, *args, **kwargs):
return Response(error_data, status=status.HTTP_400_BAD_REQUEST)


class ChangeCardTokenView(StripeView):
"""
Add or update customer card token

This is useful if you are planing to use strip.js to
retrieve the card token. This isolates the full credit
card number from your server.
"""
serializer_class = CardTokenSerializer

def post(self, request, *args, **kwargs):
try:
serializer = self.serializer_class(data=request.data)

if serializer.is_valid():
validated_data = serializer.validated_data

customer = self.get_customer()

token = validated_data['token']
customer.update_card(token)
send_invoice = customer.card_fingerprint == ""

if send_invoice:
customer.send_invoice()
customer.retry_unpaid_invoices()

return Response(validated_data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

except stripe.StripeError as e:
error_data = {u'error': smart_str(e) or u'Unknown error'}
return Response(error_data, status=status.HTTP_400_BAD_REQUEST)


class CancelView(StripeView):
""" Cancel customer subscription """
serializer_class = CancelSerializer
Expand Down
210 changes: 210 additions & 0 deletions payments/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2016-12-20 16:32
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import jsonfield.fields


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Charge',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=255, unique=True)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('card_last_4', models.CharField(blank=True, max_length=4)),
('card_kind', models.CharField(blank=True, max_length=50)),
('currency', models.CharField(default='usd', max_length=10)),
('amount', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('amount_refunded', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('description', models.TextField(blank=True)),
('paid', models.NullBooleanField()),
('disputed', models.NullBooleanField()),
('refunded', models.NullBooleanField()),
('captured', models.NullBooleanField()),
('fee', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('receipt_sent', models.BooleanField(default=False)),
('charge_created', models.DateTimeField(blank=True, null=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='CurrentSubscription',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('plan', models.CharField(max_length=100)),
('quantity', models.IntegerField()),
('start', models.DateTimeField()),
('status', models.CharField(max_length=25)),
('cancel_at_period_end', models.BooleanField(default=False)),
('canceled_at', models.DateTimeField(blank=True, null=True)),
('current_period_end', models.DateTimeField(blank=True, null=True)),
('current_period_start', models.DateTimeField(blank=True, null=True)),
('ended_at', models.DateTimeField(blank=True, null=True)),
('trial_end', models.DateTimeField(blank=True, null=True)),
('trial_start', models.DateTimeField(blank=True, null=True)),
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
('currency', models.CharField(default='usd', max_length=10)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
],
),
migrations.CreateModel(
name='Customer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=255, unique=True)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('card_fingerprint', models.CharField(blank=True, max_length=200)),
('card_last_4', models.CharField(blank=True, max_length=4)),
('card_kind', models.CharField(blank=True, max_length=50)),
('date_purged', models.DateTimeField(editable=False, null=True)),
('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=255, unique=True)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('kind', models.CharField(max_length=250)),
('livemode', models.BooleanField(default=False)),
('webhook_message', jsonfield.fields.JSONField()),
('validated_message', jsonfield.fields.JSONField(null=True)),
('valid', models.NullBooleanField()),
('processed', models.BooleanField(default=False)),
('customer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payments.Customer')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='EventProcessingException',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('data', models.TextField()),
('message', models.CharField(max_length=500)),
('traceback', models.TextField()),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('event', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payments.Event')),
],
),
migrations.CreateModel(
name='Invoice',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=255)),
('attempted', models.NullBooleanField()),
('attempts', models.PositiveIntegerField(null=True)),
('closed', models.BooleanField(default=False)),
('paid', models.BooleanField(default=False)),
('period_end', models.DateTimeField()),
('period_start', models.DateTimeField()),
('subtotal', models.DecimalField(decimal_places=2, max_digits=9)),
('total', models.DecimalField(decimal_places=2, max_digits=9)),
('currency', models.CharField(default='usd', max_length=10)),
('date', models.DateTimeField()),
('charge', models.CharField(blank=True, max_length=50)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoices', to='payments.Customer')),
],
options={
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='InvoiceItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=255)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
('currency', models.CharField(default='usd', max_length=10)),
('period_start', models.DateTimeField()),
('period_end', models.DateTimeField()),
('proration', models.BooleanField(default=False)),
('line_type', models.CharField(max_length=50)),
('description', models.CharField(blank=True, max_length=200)),
('plan', models.CharField(blank=True, max_length=100)),
('quantity', models.IntegerField(null=True)),
('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='payments.Invoice')),
],
),
migrations.CreateModel(
name='Transfer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=255, unique=True)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
('currency', models.CharField(default='usd', max_length=25)),
('status', models.CharField(max_length=25)),
('date', models.DateTimeField()),
('description', models.TextField(blank=True, null=True)),
('adjustment_count', models.IntegerField(null=True)),
('adjustment_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('adjustment_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('charge_count', models.IntegerField(null=True)),
('charge_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('charge_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('collected_fee_count', models.IntegerField(null=True)),
('collected_fee_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('net', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('refund_count', models.IntegerField(null=True)),
('refund_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('refund_gross', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('validation_count', models.IntegerField(null=True)),
('validation_fees', models.DecimalField(decimal_places=2, max_digits=9, null=True)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transfers', to='payments.Event')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='TransferChargeFee',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount', models.DecimalField(decimal_places=2, max_digits=9)),
('currency', models.CharField(default='usd', max_length=10)),
('application', models.TextField(blank=True, null=True)),
('description', models.TextField(blank=True, null=True)),
('kind', models.CharField(max_length=150)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('transfer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='charge_fee_details', to='payments.Transfer')),
],
),
migrations.AddField(
model_name='currentsubscription',
name='customer',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='current_subscription', to='payments.Customer'),
),
migrations.AddField(
model_name='charge',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='charges', to='payments.Customer'),
),
migrations.AddField(
model_name='charge',
name='invoice',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='charges', to='payments.Invoice'),
),
]
Empty file added payments/migrations/__init__.py
Empty file.
Loading