feat: stripe webhook for camp sales
This commit is contained in:
parent
3fe041abae
commit
1056616351
|
@ -49,6 +49,7 @@ jobs:
|
|||
secret_STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
|
||||
secret_STRIPE_PUBLIC_KEY: ${{ secrets.STRIPE_PUBLIC_KEY }}
|
||||
secret_STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
|
||||
secret_STRIPE_CAMP_WEBHOOK_SECRET: ${{ secrets.STRIPE_CAMP_WEBHOOK_SECRET }}
|
||||
secret_COINBASE_WEBHOOK_SECRET: ${{ secrets.COINBASE_WEBHOOK_SECRET }}
|
||||
secret_PATREON_CLIENT_ID: ${{ secrets.PATREON_CLIENT_ID }}
|
||||
secret_PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }}
|
||||
|
|
|
@ -28,6 +28,7 @@ from notifications.webhooks import webhook_event
|
|||
from payments.views.common import membership_expired
|
||||
from payments.api import api_gift_days
|
||||
from payments.views.stripe import pay, done, stripe_webhook, stop_subscription
|
||||
from payments.views.camp import stripe_camp_webhook
|
||||
from payments.views.crypto import crypto, coinbase_webhook
|
||||
from posts.api import md_show_post, api_show_post, json_feed
|
||||
from posts.models.post import Post
|
||||
|
@ -88,6 +89,7 @@ urlpatterns = [
|
|||
path("monies/membership_expired/", membership_expired, name="membership_expired"),
|
||||
path("monies/subscription/<str:subscription_id>/stop/", stop_subscription, name="stop_subscription"),
|
||||
path("monies/stripe/webhook/", stripe_webhook, name="stripe_webhook"),
|
||||
path("monies/stripe/webhook_camp/", stripe_camp_webhook, name="stripe_camp_webhook"),
|
||||
path("monies/coinbase/webhook/", coinbase_webhook, name="coinbase_webhook"),
|
||||
path("monies/gift/<int:days>/<slug:user_slug>.json", api_gift_days, name="api_gift_days"),
|
||||
|
||||
|
|
|
@ -301,6 +301,12 @@ ACHIEVEMENTS = [
|
|||
"image": "https://vas3k.club/static/images/achievements/music_jam_6.png",
|
||||
"style": "background-color: #49D6AC; font-size: 120%;",
|
||||
}),
|
||||
("vas3k_camp_2024", {
|
||||
"name": "Вастрик 🔥 Кэмп 2024",
|
||||
"description": "Участник клубного Кэмпа в Сербии летом 2024",
|
||||
"image": "https://vas3k.club/static/images/achievements/vas3k_camp_2024.jpg",
|
||||
"style": "background-color: #F1DFC5; font-size: 130%;",
|
||||
}),
|
||||
]
|
||||
|
||||
# VIP: https://i.vas3k.club/3cb.png
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
{% extends "emails/layout.html" %}
|
||||
{% load static %}
|
||||
{% load text_filters %}
|
||||
|
||||
{% block css %}
|
||||
<style>
|
||||
p {
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
font-size: 19px;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
🔥 Ждём вас на Вастрик Кэмпе 2024
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<p>
|
||||
👋 Это тестовое письмо пока не будет готово настоящее
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{{ settings.APP_HOST }}{% url "login" %}" class="button">Кнопка входа в чат</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Кек пук
|
||||
</p>
|
||||
|
||||
<br><br><br>
|
||||
{% endblock %}
|
||||
|
||||
{% block unsubscribe %}{% endblock %}
|
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
|
@ -10,6 +10,25 @@ from users.models.user import User
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_stripe_webhook_event(request, webhook_secret):
|
||||
payload = request.body
|
||||
sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")
|
||||
|
||||
if not payload or not sig_header:
|
||||
raise BadRequest(code=400, message="[invalid payload]")
|
||||
|
||||
try:
|
||||
event = stripe.Webhook.construct_event(
|
||||
payload, sig_header, webhook_secret
|
||||
)
|
||||
except ValueError:
|
||||
raise BadRequest(code=400, message="[invalid payload]")
|
||||
except stripe.error.SignatureVerificationError:
|
||||
raise BadRequest(code=400, message="[invalid signature]")
|
||||
|
||||
return event
|
||||
|
||||
|
||||
def cancel_all_stripe_subscriptions(stripe_id: str) -> bool:
|
||||
if not stripe_id:
|
||||
return False
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import logging
|
||||
import os
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.template import loader
|
||||
from django_q.tasks import async_task
|
||||
|
||||
from club.exceptions import BadRequest
|
||||
from notifications.email.sender import send_transactional_email
|
||||
from notifications.signals.achievements import async_create_or_update_achievement
|
||||
|
||||
from payments.helpers import parse_stripe_webhook_event
|
||||
from users.models.achievements import Achievement, UserAchievement
|
||||
from users.models.user import User
|
||||
|
||||
log = logging.getLogger()
|
||||
|
||||
STRIPE_CAMP_WEBHOOK_SECRET = os.getenv("STRIPE_CAMP_WEBHOOK_SECRET")
|
||||
|
||||
|
||||
# TODO: this is a temporary webhook, remove it after June 2024 (when the camp is over)
|
||||
def stripe_camp_webhook(request):
|
||||
try:
|
||||
event = parse_stripe_webhook_event(request, STRIPE_CAMP_WEBHOOK_SECRET)
|
||||
except BadRequest as ex:
|
||||
return HttpResponse(ex.message, status=ex.code)
|
||||
|
||||
if event["type"] == "checkout.session.completed":
|
||||
session = event["data"]["object"]
|
||||
|
||||
user = User.objects.filter(email=session["customer_details"]["email"].lower()).first()
|
||||
|
||||
if user:
|
||||
camp_achievement = Achievement.objects.filter(code="vas3k_camp_2024").first()
|
||||
user_achievement, is_created = UserAchievement.objects.get_or_create(
|
||||
user=user,
|
||||
achievement=camp_achievement,
|
||||
)
|
||||
if is_created:
|
||||
async_create_or_update_achievement(user_achievement)
|
||||
|
||||
# send confirmation email
|
||||
camp_confirmation_template = loader.get_template("emails/camp_confirmation.html")
|
||||
async_task(
|
||||
send_transactional_email,
|
||||
recipient=session["customer_details"]["email"].lower(),
|
||||
subject=f"🔥 Ждём вас на Вастрик Кэмпе 2024",
|
||||
html=camp_confirmation_template.render({"user": user})
|
||||
)
|
||||
|
||||
return HttpResponse("[ok]", status=200)
|
||||
|
||||
return HttpResponse("[unknown event]", status=400)
|
|
@ -6,7 +6,9 @@ from django.http import HttpResponse
|
|||
from django.shortcuts import render
|
||||
|
||||
from authn.decorators.auth import require_auth
|
||||
from club.exceptions import BadRequest
|
||||
from payments.exceptions import PaymentException
|
||||
from payments.helpers import parse_stripe_webhook_event
|
||||
from payments.models import Payment
|
||||
from payments.products import PRODUCTS, find_by_stripe_id, TAX_RATE_VAT
|
||||
from payments.service import stripe
|
||||
|
@ -156,22 +158,10 @@ def stop_subscription(request, subscription_id):
|
|||
|
||||
|
||||
def stripe_webhook(request):
|
||||
payload = request.body
|
||||
sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")
|
||||
|
||||
if not payload or not sig_header:
|
||||
return HttpResponse("[invalid payload]", status=400)
|
||||
|
||||
try:
|
||||
event = stripe.Webhook.construct_event(
|
||||
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
|
||||
)
|
||||
except ValueError:
|
||||
return HttpResponse("[invalid payload]", status=400)
|
||||
except stripe.error.SignatureVerificationError:
|
||||
return HttpResponse("[invalid signature]", status=400)
|
||||
|
||||
log.info("Stripe webhook event: " + event["type"])
|
||||
event = parse_stripe_webhook_event(request, settings.STRIPE_WEBHOOK_SECRET)
|
||||
except BadRequest as ex:
|
||||
return HttpResponse(ex.message, status=ex.code)
|
||||
|
||||
if event["type"] == "checkout.session.completed":
|
||||
session = event["data"]["object"]
|
||||
|
@ -214,3 +204,4 @@ def stripe_webhook(request):
|
|||
return HttpResponse("[ok]", status=200)
|
||||
|
||||
return HttpResponse("[unknown event]", status=400)
|
||||
|
||||
|
|
Loading…
Reference in New Issue