Skip to content

Commit

Permalink
dj stripe payment methods
Browse files Browse the repository at this point in the history
  • Loading branch information
hhartwell committed Dec 19, 2023
1 parent 2d3ce59 commit a11cdbd
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .env_sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
STRIPE_PUBLIC_KEY=
STRIPE_PRIVATE_KEY=
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ INSTALLED_APPS = (
## tests

```bash
$ docker build -t django-ckc . && docker run django-ckc pytest
$ docker build -t django-ckc . && docker run --env-file .env django-ckc pytest
```

## what's in this
Expand Down
49 changes: 49 additions & 0 deletions ckc/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import stripe
from djstripe.models import PaymentMethod, Customer

from rest_framework import serializers

class DefaultCreatedByMixin:
"""This will automatically set `YourModel.created_by` to `request.user`. To override which
attribute the user is written to, add a `user_field` to your classes Meta information
Expand Down Expand Up @@ -25,3 +30,47 @@ def create(self, validated_data):
'and overwrote context?')
validated_data[user_field] = self.context['request'].user
return super().create(validated_data)


class PaymentMethodSerializer(serializers.ModelSerializer):
token = serializers.CharField(write_only=True)

class Meta:
model = PaymentMethod
fields = (
'token',
'id',
'type',

# 'customer',
# 'stripe_id',
# 'card_brand',
# 'card_last4',
# 'card_exp_month',
# 'card_exp_year',
# 'is_default',
# 'created',
# 'modified',
)
read_only_fields = (
'id',
'type',
# 'customer',
# 'stripe_id',
# 'card_brand',
# 'card_last4',
# 'card_exp_month',
# 'card_exp_year',
# 'is_default',
# 'created',
# 'modified',
)

def create(self, validated_data):
customer, created = Customer.get_or_create(subscriber=self.context['request'].user)
try:
payment_method = customer.add_payment_method(validated_data['token'])
except (stripe.error.InvalidRequestError) as e:
raise serializers.ValidationError(e)

return payment_method
15 changes: 15 additions & 0 deletions ckc/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from djstripe.models import PaymentMethod
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

from ckc.serializers import PaymentMethodSerializer


class PaymentMethodViewSet(viewsets.ModelViewSet):
queryset = PaymentMethod.objects.all()
serializer_class = PaymentMethodSerializer
permission_classes = [IsAuthenticated]

def get_queryset(self):
qs = PaymentMethod.objects.filter(customer__subscriber=self.request.user)
return qs
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ pytest-django==4.5.2
flake8==6.0.0

# payment processing
djstripe==2.8.3
dj-stripe==2.8.3
17 changes: 17 additions & 0 deletions testproject/settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

import stripe

DEBUG = True

Expand Down Expand Up @@ -54,3 +55,19 @@
"APP_DIRS": True
}
]

# =============================================================================
# Stripe
# =============================================================================
STRIPE_PUBLIC_KEY = os.environ.get('STRIPE_PUBLIC_KEY')
STRIPE_PRIVATE_KEY = os.environ.get('STRIPE_PRIVATE_KEY')
DJSTRIPE_FOREIGN_KEY_TO_FIELD = "id"
stripe.api_key = STRIPE_PRIVATE_KEY

# =============================================================================
# DJStripe
# =============================================================================
STRIPE_LIVE_SECRET_KEY = STRIPE_PRIVATE_KEY
STRIPE_TEST_SECRET_KEY = STRIPE_PRIVATE_KEY
DJSTRIPE_USE_NATIVE_JSONFIELD = True
STRIPE_LIVE_MODE = False # Change to True in production
6 changes: 0 additions & 6 deletions testproject/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,3 @@ class SnapshottedModelMissingOverride(JsonSnapshotModel, models.Model):
# No _create_json_snapshot here! This is for testing purposes, to confirm we raise
# an assertion when this method is missing
pass


# ----------------------------------------------------------------------------
# For testing DJStripe
# ----------------------------------------------------------------------------
class
2 changes: 2 additions & 0 deletions testproject/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.urls import path
from rest_framework import routers

from ckc.views import PaymentMethodViewSet
from testapp.views import TestExceptionsViewSet
from testapp.viewsets import TestModelWithACreatorViewSet, TestModelWithADifferentNamedCreatorViewSet, BModelViewSet

Expand All @@ -9,6 +10,7 @@
router.register(r'creators', TestModelWithACreatorViewSet)
router.register(r'creators-alternative', TestModelWithADifferentNamedCreatorViewSet)
router.register(r'bmodel', BModelViewSet)
router.register(r'payment-methods', PaymentMethodViewSet, basename='payment-methods')

urlpatterns = router.urls + [
path('test-exceptions/', TestExceptionsViewSet.as_view(), name='test-exceptions'),
Expand Down
25 changes: 25 additions & 0 deletions tests/integration/test_payment_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from django.urls import reverse
from djstripe.models import PaymentMethod, Customer
from rest_framework.test import APITestCase

from django.contrib.auth import get_user_model

User = get_user_model()


class TestExceptions(APITestCase):
def setUp(self):
self.user = User.objects.create_user(username="test", password="test")
self.client.force_authenticate(user=self.user)
return super().setUp()

def test_payment_method(self):
# simulate card being created on the frontend
url = reverse('payment-methods-list')
payload = {"token": "pm_card_visa"}
resp = self.client.post(url, data=payload, format='json')

# assert payment method and customer creation
assert resp.status_code == 201
assert PaymentMethod.objects.count() == 1
assert Customer.objects.count() == 1

0 comments on commit a11cdbd

Please sign in to comment.