Как обрабатывать файл customer.subscription.created, если организация (клиент) еще не существует в Stripe webhook?
Я использую Django и Stripe для мультитенантной SaaS-системы. Я регистрирую пользователей и организации вручную из view и создаю клиента и подписку с помощью Stripe API перед сохранением в базе данных.
Вот мой веб-сайт:
class StripeWebhookView(APIView):
permission_classes = [AllowAny]
def post(self, request, *args, **kwargs):
payload = request.body
sig_header = request.headers.get('stripe-signature')
try:
event = stripe.Webhook.construct_event(
payload=payload,
sig_header=sig_header,
secret=settings.STRIPE_SIGNING_SECRET
)
except Exception:
return Response(status=400)
event_type = event['type']
data_object = event['data']['object']
if event_type == 'invoice.payment_succeeded':
billing_reason = data_object.get('billing_reason')
organization = OrganizationModel.objects.get(
stripe_customer_id='cus_Sr7NCr3urlBdws'
)
if billing_reason == 'subscription_create':
print(data_object)
elif event_type == 'customer.subscription.created':
subscription = data_object
customer_id = subscription.get("customer")
try:
organization = OrganizationModel.objects.get(
stripe_customer_id=customer_id
)
# handle subscription creation logic here
except OrganizationModel.DoesNotExist:
# I don't know what to do here if my tenant doesn't exist yet
return Response(status=200)
Моя проблема
Иногда webhook customer.subscription.created поступает до того, как организация (и схема клиента) будет создана в базе данных.
Поскольку Stripe не гарантирует порядок в webhook, это нарушает логику. Событие не может быть обработано, поскольку организации еще нет. Если я отвечу 200 "ОК", Stripe не будет повторять попытку. Если я верну значение 400, Stripe повторит попытку слишком много раз, и это все равно может не сработать, пока организация не будет создана.
Мой вопрос
В соответствии с рекомендациями Stripe:
- Как я должен безопасно обрабатывать такие события, как customer.subscription.created, когда клиент/организация, возможно, еще не существует?
- Рекомендуется ли временно сохранять события (в таблице, подобной WebhookEventModel), и обрабатывать их позже, как только клиент будет создан?
- Или есть лучший способ отложить обработку этих событий?
- Как команды обычно справляются с такими условиями гонки?
Заранее благодарю.
Первое предложение
Я просмотрел функцию post
в вашем TestRegisterView
несколько раз, и мне все еще непонятно, почему вам нужно отложить создание организации до тех пор, пока вы не создадите клиента и подписку с помощью Stripe SDK.
Итак, моим первым предложением было бы отложить создание любых объектов Stripe до тех пор, пока после необходимые объекты не будут созданы в вашей базе данных. Я предполагаю, что у вас есть на то причина, и предлагаю другое предложение, но я думаю, вам следует серьезно подумать над этим. Возможно, это более элегантный способ, которым вы сейчас это делаете, но я все же думаю, что вам следует отложить создание клиентов и подписки.
Второе предложение
Другой подход, который может решить эту проблему, заключается в добавлении Сельдерея в ваш проект. В любом случае, наличие асинхронной очереди задач для обработки веб-запросов Stripe - хорошая идея, особенно по мере роста вашей базы данных и увеличения времени выполнения запросов.
В частности, в @shared_task
декораторе есть функция, которая может здесь сработать. Когда вы оформляете функцию задачи, вы можете указать количество повторных попыток и использовать стратегию отмены. В вашем случае вы могли бы указать повторные попытки с экспоненциальной задержкой. Это позволило бы выполнить обработку полезной нагрузки события webhook с ошибкой один раз (или более), подождать некоторое время, в течение которого организация будет создана, а затем повторить операцию.
Такой декоратор может выглядеть следующим образом
@app.task(autoretry_for=(OrganizationModel.DoesNotExist,), retry_backoff=True)
def task:
# your code for handling the webhook event goes here
просто напишите логику опроса для вашего приложения, возможно, планируя опрос каждые 30 минут для всех вызовов stripe api