Using django-stripe's built-in webhook support - python

I'm using Django 3.0, dj-stripe 2.0, and the Stripe CLI. dj-stripe provides native support for Stripe webhooks, and their documentation says to include the following in my django project's main urls.py file to expose the webhook endpoint:
url(r"^stripe/", include("djstripe.urls", namespace="djstripe")),
I have done this, but how do I now leverage the endpoint?
As a separate experiment, I created a payments app, setup the URL conf, and successfully called the view when triggering the non dj-stripe webhook endpoint via the Stripe CLI. But it doesn't use any dj-stripe functionality:
# project urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('stripe/', include("djstripe.urls", namespace="djstripe")),
path('payments/', include("payments.urls", namespace="payments")),
]
# payments/urls.py
from django.urls import path
from . import views
app_name="payments"
urlpatterns = [
path("", views.my_handler, name="my-handler"),
]
# payments/views.py
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def my_handler(request, **kwargs):
print(request.body)
return HttpResponse(status=200)
Running stripe listen --forward-to localhost:8000/payments/ and, in a separate window, stripe trigger product.created returns a 200 response.
But stripe listen --forward-to localhost:8000/stripe/webhook/ and stripe trigger product.created returns a 500.
Thank you in advance for your help.
[UPDATE]
I have not modified the default DJSTRIPE_WEBHOOK_URL or DJSTRIPE_WEBHOOK_VALIDATION settings. In settings.py I have:
STRIPE_LIVE_PUBLIC_KEY = os.environ.get("STRIPE_LIVE_PUBLIC_KEY")
STRIPE_LIVE_SECRET_KEY = os.environ.get("STRIPE_LIVE_SECRET_KEY")
STRIPE_TEST_PUBLIC_KEY = os.environ.get("STRIPE_TEST_PUBLIC_KEY")
STRIPE_TEST_SECRET_KEY = os.environ.get("STRIPE_TEST_SECRET_KEY")
STRIPE_LIVE_MODE = os.environ.get("STRIPE_LIVE_MODE")
DJSTRIPE_WEBHOOK_SECRET = "*************************************"
The test keys are pulled from env/bin/activate:
export STRIPE_LIVE_PUBLIC_KEY="pk_live_****...."
export STRIPE_LIVE_SECRET_KEY="sk_live_****...."
export STRIPE_TEST_PUBLIC_KEY="pk_test_****...."
export STRIPE_TEST_SECRET_KEY="sk_test_****...."
export STRIPE_LIVE_MODE="False"
When I run stripe listen --forward-to localhost:8000/stripe/webhook/ and trigger stripe trigger customer.created I get the following 500 error:
stripe.error.InvalidRequestError: Request req_NTtzsTFS8uVdfL: No such customer; a similar object exists in test mode, but a live mode key was used to make this request.
But I don't understand how my keys could be getting mixed up, because the webhooks work fine when I trigger the same event and listen via the /payments/ endpoint.
Thank you again for your time.

In summary, you need to decorate a handler function using the #webhooks.handler decorator.
You have some examples in the webhooks section of the dj-stripe documentation.

Related

how to use django 3 with django-otp to send token sms using SMS service provider for user verification and redirecting to password reset form?

I have only been able to make the following changes in django code:
settings.py:
added along with other apps added in
INSTALLED_APPS = [
.....
'django_otp',
'django_otp.plugins.otp_totp',
]
In additions to other middleware configurations, added:
MIDDLEWARE = [
'django_otp.middleware.OTPMiddleware',
]
urls.py:
from django_otp.admin import OTPAdminSite
from django_otp.plugins.otp_totp.models import TOTPDevice
admin_site = OTPAdmin(name='OTPAdmin')
admin_site.register(User)
admin_site.register(TOTPDevice)
urlpatterns = [
path('admin/', admin_site.urls), #otp app
path('dadmin/', admin.site.urls),
]
Then I ran: $ python3 manage.py migrate otp_totp --fake
and runserver. Created a user and totp device. Scanned the qr code in google authenticator. Then tried logging in using the admin url to login for this new user. It asks for the token generated which I input, it says invalid token though user is authenticated. Seen other posts where the secret code needs to be converted to 32basecode etc, but don't know exactly and where to implement. What more code needs to be added to get this working? I will require detailed code and steps for my use case where i need to change the time for generating the code and send via sms using my service provider api and redirect to password reset form.
Using django 3.1, django-otp 1.0.2
My google authenticator works with my gmail account, so there is no clock time difference either.

NoReverseMatch exception when creating integration test for REST API with Python Django

I've created a REST API accepting a POST request in Django. The request itself works great when calling from a React front end, there should be nothing wrong with the API itself.
I want to create integration test for the API.
I've written the below test, which is basically copy/paste from the Django Documentation adapted to my API:
from django.urls import reverse, path
from rest_framework import status
from rest_framework.test import APITestCase
class SendLoginEmailTest(APITestCase):
def test_post_withValidData_loginEmailIsSent(self):
url = reverse('api_private:send_login_email')
data = {'email': 'validemail#email.com', 'token': '4d4ca980-cb0c-425d-8d2c-7e38cb33f38e'}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
I run the test using the command:
python3 manage.py test
The test fails with the following exception:
django.urls.exceptions.NoReverseMatch: Reverse for 'send_login_email' not found. 'send_login_email' is not a valid view function or pattern name.
Any ideas what I'm doing wrong?
== Update ==
Content of urls.py.
from django.urls import path
from server.api_private.views import SendLoginEmail, ValidateEmail, IsEmailValidated, ProjectSave
app_name = "api_private"
urlpatterns = [
path('send_login_email/', SendLoginEmail.as_view()),
path('validate_email/', ValidateEmail.as_view()),
path('is_email_validated/', IsEmailValidated.as_view()),
path('project_save/', ProjectSave.as_view())
]
You didn't add names to your urls. reverse functions is looking for names. Add names to all URLs that you are going to use with reverse function.
urlpatterns = [
path('send_login_email/', SendLoginEmail.as_view(), name='send_logic_email'),
...,
]

Loading URL patterns without activating them?

My Django project has two applications in it: a web tool and a REST interface.
I run the REST interface on my database system (e.g. db.myhost.com). This interface only has URL patterns that correspond to the various REST endpoints:
app_name = "rest"
urlpatterns = [
url(r'^report/id/(?P<rid>[0-9]+)/$', views.ReportByID.as_view()),
url(r'^report/slug/(?P<slug>[a-z0-9-]+)/$', views.ReportBySlug.as_view()),
]
Part of the data that these views ultimately show need links to the other application in my project (which I host on a separate system). That application also has URL patterns:
app_name = "mytool"
urlpatterns = [
url(r'^some/cool/path/$', views.some_cool_path),
]
The REST interface only enables the REST URL patterns, since I only want to serve REST endpoints via that host:
# On my REST system
ROOT_URL = "myproject.rest_urls"
Is there a way that I can get the REST application to load the mytool URL patterns without activating them? I don't want a user to be able to browse to db.myhost.com/some/cool/path/ and get an error because that path isn't served on that host, it's served by the web tool server instead. It would be helpful, however, to be able to use reverse() to get the mytool URLs, even if they are just relative fragments (i.e. /some/cool/path ... I could always prepend the server name, which is unlikely to ever change).
I could hard-code the necessary paths, but I'd like to avoid having to do that in case they need to change in the future.
We can do it using django test utils override_settings decorator. It will use temporary settings so, it will not have any effect on the live site.
settings.py
INSTALLED_APPS = [
# .....
'mytool',
# .....
]
ROOT_URL = "myproject.rest_urls"
mytool/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('news/<slug:slug>/', views.NewsDetailView.as_view(), name='news_detail'),
]
mytool/utils.py
from django.test.utils import override_settings
from django.urls import reverse
def temp_reverse(url_conf, url_name, url_args=(), url_kwargs={}):
#override_settings(ROOT_URLCONF=url_conf)
def get_reverse(url_name, *args, **kwargs):
return reverse(url_name, args=args, kwargs=kwargs)
return get_reverse(url_name, *url_args, **url_kwargs)
accessing the reverse for unregistered urls
from mytool.urils import temp_reverse
url = temp_reverse('mytool.urls', 'news_detail', url_kwargs={'slug': 'django-awesome'})
print(url)
# output: /news/django-awesome/

Where to instantiate a client for an external service in Django?

What is the accepted practice in Django for creating an instance of a client for an external service (e.g. Zookeeper, Cassandra, Redis, etc.)?
I need this client to perform a "set key" operation from inside a view and I don't want to create the client on each request due to high overhead.
Currently I've declared it as a global variable in views.py, but that's not really ok, because it gets instantiated when I do python manage.py makemigrations too.
A really stripped down example of the issue:
urls.py
from django.conf.urls import url
from app.views import MyView
urlpatterns = [
url(r'^', MyView.as_view()),
]
views.py
from django.shortcuts import render
from django.views import View
from django.http import HttpResponse
from kazoo.client import KazooClient
import logging
logging.basicConfig(level=logging.DEBUG)
zk = KazooClient(hosts="127.0.0.1:2181")
zk.start()
# Create your views here.
class MyView(View):
def get(self, request):
value, _ = zk.get("/my_zk_key")
return HttpResponse("value: {}".format(value))
The problem is that the zookeeper client gets created and connects in cases other than runserver, like this:
$ python manage.py makemigrations
INFO:kazoo.client:Connecting to 127.0.0.1:2181
DEBUG:kazoo.client:Sending request(xid=None): Connect(protocol_version=0, last_zxid_seen=0, time_out=10000, session_id=0, passwd=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', read_only=None)
INFO:kazoo.client:Zookeeper connection established, state: CONNECTED
No changes detected
$
I'm looking for an accepted practice in Django for dealing with such cases.
If your problem can be reduced to the last point, you can avoid setting your global variable during the migration with:
import sys
if 'makemigrations' not in sys.argv and 'migrate' not in sys.argv:
# variable setting here

django-subdomains config localhost

I'm having trouble getting mine configured correctly.
Have sites enabled and site domain for "SITE_ID = 1" (db object 1 domain) set too "mysite.app"
I have these subdomains setup
ROOT_URLCONF = 'mysite.urls'
SUBDOMAIN_URLCONFS = {
None: 'frontend.urls', # no subdomain, e.g. ``example.com``
'www': 'frontend.urls',
'api': 'api.urls',
}
etc/hosts file
127.0.0.1 api.mysite.app
127.0.0.1 www.mysite.app
127.0.0.1 mysite.app
api/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index),
]
api/views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return HttpResponse('API')
The frontend app urls and views is identical except for returning string "FRONTEND" in the HttpResponse object.
I can tell django-subdomains is working becuase it does go to the "frontend" app when I hit "mysite.app:8000" vs the mysite.urls as seen in the root_url_conf. It displays "FRONTEND"
But no matter what I do I can't get "api.mysite.app:8000" to hit the api urls file to display "API"
Am I missing something? I'm very new to django. Any help is appreciated.
Thanks.
Simply had to restart the dev server. All was configured correctly.
Try http://api.127.0.0.1.xip.io:8000/ with your dev server running at 127.0.0.1:8000. Since 127.0.0.1 isn't really a domain but an IP, it can't have subdomains. And since your hosts file redirects to 127.0.0.1 not 127.0.0.1.xip.io or similar (you don't have to do it for testing either) you won't be connected.
I haven't used the library that you mentioned but from experience with self-written snippets for subdomains, I'd say this is the case.

Categories