Card matching query does not exist on Customer.add_card from stripe token from stripe checkout.js
See original GitHub issueDescribe the bug
I’m running an app which uses stripe checkout in the front end (Source imported from here https://checkout.stripe.com/v2/checkout.js ), in which the user enters their credit card information and then Stripe provides a token which is passed to our API for further processing. On our server we use DJstripe to create a customer and then call add_card
with the token from the stripe checkout to add the user’s card. Since upgrading to DJstripe 2.6.2 (Previously 2.5.1), we sometimes run into a djstripe.models.payment_methods.Card.DoesNotExist: Card matching query does not exist
error when attempting to add the card. Before the upgrade our system has processed thousands of payments with this same set up with no issues. Typically we are seeing this problem in about 1 in 5 payment attempts.
From our server logs, I can see that when the error occurs, a number of webhooks are received and processed between the time that the add_card
action is started and when the error trace is printed out. One of the webhooks being processed is started before the error trace, and completes after the error trace, so it really seems like some sort of race condition problem related to the webhooks. In some cases, the webhooks all complete without errors, and in other cases, the webhook handler also raises a similar Card.DoesNotExist
error
Full trace:
[2022-10-31T01:49:00.948455283Z] app[web.1]: ERROR 2022-10-30 21:49:00,412 log 302 140720089622336 Internal Server Error: /subscribe/
[2022-10-31T01:49:00.948487892Z] app[web.1]: Traceback (most recent call last):
[2022-10-31T01:49:00.948493567Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/base.py", line 583, in _create_from_stripe_object
[2022-10-31T01:49:00.948498572Z] app[web.1]: instance = cls.stripe_objects.get(id=id_)
[2022-10-31T01:49:00.948503185Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
[2022-10-31T01:49:00.948507907Z] app[web.1]: return getattr(self.get_queryset(), name)(*args, **kwargs)
[2022-10-31T01:49:00.948512467Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/query.py", line 435, in get
[2022-10-31T01:49:00.948516964Z] app[web.1]: raise self.model.DoesNotExist(
[2022-10-31T01:49:00.948521326Z] app[web.1]: djstripe.models.payment_methods.Card.DoesNotExist: Card matching query does not exist.
[2022-10-31T01:49:00.948525947Z] app[web.1]:
[2022-10-31T01:49:00.948530448Z] app[web.1]: During handling of the above exception, another exception occurred:
[2022-10-31T01:49:00.948535195Z] app[web.1]:
[2022-10-31T01:49:00.948539640Z] app[web.1]: Traceback (most recent call last):
[2022-10-31T01:49:00.948544260Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 84, in _execute
[2022-10-31T01:49:00.948549387Z] app[web.1]: return self.cursor.execute(sql, params)
[2022-10-31T01:49:00.948553967Z] app[web.1]: psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "djstripe_card_stripe_id_1dd55072_uniq"
[2022-10-31T01:49:00.948558639Z] app[web.1]: DETAIL: Key (id)=(card_1Lyn5mGRvRnAJwTxHV5AQTS7) already exists.
[2022-10-31T01:49:00.948563054Z] app[web.1]:
[2022-10-31T01:49:00.948567452Z] app[web.1]:
[2022-10-31T01:49:00.948571724Z] app[web.1]: The above exception was the direct cause of the following exception:
[2022-10-31T01:49:00.948576137Z] app[web.1]:
[2022-10-31T01:49:00.948580371Z] app[web.1]: Traceback (most recent call last):
[2022-10-31T01:49:00.948584843Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/base.py", line 704, in _get_or_create_from_stripe_object
[2022-10-31T01:49:00.948589488Z] app[web.1]: cls._create_from_stripe_object(
[2022-10-31T01:49:00.948594015Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/base.py", line 596, in _create_from_stripe_object
[2022-10-31T01:49:00.948598603Z] app[web.1]: instance.save()
[2022-10-31T01:49:00.948603013Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/base.py", line 739, in save
[2022-10-31T01:49:00.948607581Z] app[web.1]: self.save_base(using=using, force_insert=force_insert,
[2022-10-31T01:49:00.948629782Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/base.py", line 776, in save_base
[2022-10-31T01:49:00.948635021Z] app[web.1]: updated = self._save_table(
[2022-10-31T01:49:00.948639363Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/base.py", line 881, in _save_table
[2022-10-31T01:49:00.948644971Z] app[web.1]: results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
[2022-10-31T01:49:00.948649643Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/base.py", line 919, in _do_insert
[2022-10-31T01:49:00.948654197Z] app[web.1]: return manager._insert(
[2022-10-31T01:49:00.948658540Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
[2022-10-31T01:49:00.948663127Z] app[web.1]: return getattr(self.get_queryset(), name)(*args, **kwargs)
[2022-10-31T01:49:00.948667512Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/query.py", line 1270, in _insert
[2022-10-31T01:49:00.948672061Z] app[web.1]: return query.get_compiler(using=using).execute_sql(returning_fields)
[2022-10-31T01:49:00.948676482Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/cachalot/monkey_patch.py", line 37, in inner
[2022-10-31T01:49:00.948681022Z] app[web.1]: return original(compiler, *args, **kwargs)
[2022-10-31T01:49:00.948685392Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/cachalot/monkey_patch.py", line 113, in inner
[2022-10-31T01:49:00.948689919Z] app[web.1]: return original(write_compiler, *args, **kwargs)
[2022-10-31T01:49:00.948694317Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/sql/compiler.py", line 1416, in execute_sql
[2022-10-31T01:49:00.948698989Z] app[web.1]: cursor.execute(sql, params)
[2022-10-31T01:49:00.948703350Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/raven/contrib/django/client.py", line 127, in execute
[2022-10-31T01:49:00.948707898Z] app[web.1]: return real_execute(self, sql, params)
[2022-10-31T01:49:00.948712411Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/cachalot/monkey_patch.py", line 137, in inner
[2022-10-31T01:49:00.948717005Z] app[web.1]: return original(cursor, sql, *args, **kwargs)
[2022-10-31T01:49:00.948721458Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 66, in execute
[2022-10-31T01:49:00.948725997Z] app[web.1]: return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
[2022-10-31T01:49:00.948730549Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
[2022-10-31T01:49:00.948735119Z] app[web.1]: return executor(sql, params, many, context)
[2022-10-31T01:49:00.948739492Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 79, in _execute
[2022-10-31T01:49:00.948744007Z] app[web.1]: with self.db.wrap_database_errors:
[2022-10-31T01:49:00.948748442Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/utils.py", line 90, in __exit__
[2022-10-31T01:49:00.948757510Z] app[web.1]: raise dj_exc_value.with_traceback(traceback) from exc_value
[2022-10-31T01:49:00.948762415Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 84, in _execute
[2022-10-31T01:49:00.948893559Z] app[web.1]: return self.cursor.execute(sql, params)
[2022-10-31T01:49:00.948912659Z] app[web.1]: django.db.utils.IntegrityError: duplicate key value violates unique constraint "djstripe_card_stripe_id_1dd55072_uniq"
[2022-10-31T01:49:00.948918328Z] app[web.1]: DETAIL: Key (id)=(card_1Lyn5mGRvRnAJwTxHV5AQTS7) already exists.
[2022-10-31T01:49:00.948923122Z] app[web.1]:
[2022-10-31T01:49:00.948927415Z] app[web.1]:
[2022-10-31T01:49:00.948931693Z] app[web.1]: During handling of the above exception, another exception occurred:
[2022-10-31T01:49:00.948936130Z] app[web.1]:
[2022-10-31T01:49:00.948940620Z] app[web.1]: Traceback (most recent call last):
[2022-10-31T01:49:00.948945372Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/core/handlers/exception.py", line 47, in inner
[2022-10-31T01:49:00.948950138Z] app[web.1]: response = get_response(request)
[2022-10-31T01:49:00.948954602Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/core/handlers/base.py", line 181, in _get_response
[2022-10-31T01:49:00.948959230Z] app[web.1]: response = wrapped_callback(request, *callback_args, **callback_kwargs)
[2022-10-31T01:49:00.948963812Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/views/generic/base.py", line 70, in view
[2022-10-31T01:49:00.948968417Z] app[web.1]: return self.dispatch(request, *args, **kwargs)
[2022-10-31T01:49:00.948972788Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/views/generic/base.py", line 98, in dispatch
[2022-10-31T01:49:00.948977427Z] app[web.1]: return handler(request, *args, **kwargs)
[2022-10-31T01:49:00.948981813Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/views/generic/edit.py", line 142, in post
[2022-10-31T01:49:00.948986376Z] app[web.1]: return self.form_valid(form)
[2022-10-31T01:49:00.948990764Z] app[web.1]: File "/app/payment/views.py", line 534, in form_valid
[2022-10-31T01:49:00.948995282Z] app[web.1]: user = form.create_customer()
[2022-10-31T01:49:00.948999722Z] app[web.1]: File "/app/payment/forms.py", line 175, in create_customer
[2022-10-31T01:49:00.949004196Z] app[web.1]: customer.add_card(self.cleaned_data.get('stripe_token'))
[2022-10-31T01:49:00.949008633Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/core.py", line 1029, in add_card
[2022-10-31T01:49:00.949013292Z] app[web.1]: new_payment_method = DjstripePaymentMethod.from_stripe_object(
[2022-10-31T01:49:00.949017753Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/payment_methods.py", line 42, in from_stripe_object
[2022-10-31T01:49:00.949022310Z] app[web.1]: model.sync_from_stripe_data(data)
[2022-10-31T01:49:00.949026708Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/base.py", line 946, in sync_from_stripe_data
[2022-10-31T01:49:00.949063242Z] app[web.1]: instance, created = cls._get_or_create_from_stripe_object(
[2022-10-31T01:49:00.949069927Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/djstripe/models/base.py", line 719, in _get_or_create_from_stripe_object
[2022-10-31T01:49:00.949074588Z] app[web.1]: return cls.stripe_objects.get(id=id_), False
[2022-10-31T01:49:00.949079763Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
[2022-10-31T01:49:00.949084473Z] app[web.1]: return getattr(self.get_queryset(), name)(*args, **kwargs)
[2022-10-31T01:49:00.949088916Z] app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/db/models/query.py", line 435, in get
[2022-10-31T01:49:00.949093398Z] app[web.1]: raise self.model.DoesNotExist(
[2022-10-31T01:49:00.949097784Z] app[web.1]: djstripe.models.payment_methods.Card.DoesNotExist: Card matching query does not exist.
To Reproduce I have been unable to reliably reproduce
Software versions
- Dj-Stripe version: 2.6.2
- Python version: 3.10.6
- Django version: 3.2.15
- Stripe API version: 2020-08-27
- Database type and version: PostgreSQL 10.21
Issue Analytics
- State:
- Created a year ago
- Comments:13 (5 by maintainers)
Top GitHub Comments
@martinmain93 I think that’s the best you can do at the moment. But please test thoroughly to ensure there are no unwarranted side effects.
@jleclanche Perhaps we can add a default retry mechanism for all methods using idempotency keys?
Here’s the traceback. In this case, I’ve added a workaround by detecting if this specific error has been raised, and then trying to detect if the customer has a valid source. If the user has a valid source (after retrying a few times), then we ignore the error.
There must have been something that changed between Djstripe 2.5 and 2.6. Our system seems to be hacked-together working for now, with lots of error catching and retrying, but our server logs are crammed with Webhook error messages every time anyone does anything stripe related.