From d162abbd9ebf7056779891bd99386d0ba3c7e778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 20 Mar 2022 01:02:06 +0100 Subject: [PATCH] Add setup intent expire delay issue11253 review390061002 --- CHANGELOG | 1 + payment.py | 41 +++++++++++++++++++++++++++++------------ view/account_form.xml | 3 +++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6dc0e67846..37439fcba9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add setup intent expire delay * Add support for Python 3.10 * Remove support for Python 3.6 diff --git a/payment.py b/payment.py index 55fc5b6956..f97844dd12 100644 --- a/payment.py +++ b/payment.py @@ -1,5 +1,6 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +import datetime as dt import logging import urllib.parse import uuid @@ -890,6 +891,9 @@ class Account(ModelSQL, ModelView): depends=['webhook_identifier'], help="The Stripe's signing secret of the webhook.") last_event = fields.Char("Last Event", readonly=True) + setup_intent_delay = fields.TimeDelta( + "Setup Intent Delay", required=True, + help="The delay before cancelling setup intent not succeeded.") @classmethod def __setup__(cls): @@ -918,6 +922,10 @@ class Account(ModelSQL, ModelView): '/webhook/%(identifier)s' % url_part)) + @classmethod + def default_setup_intent_delay(cls): + return dt.timedelta(days=30) + @classmethod def fetch_events(cls): """Fetch last events of each account without webhook and process them @@ -1631,21 +1639,30 @@ class Customer(CheckoutMixin, DeactivableMixin, ModelSQL, ModelView): # Use clear cache after commit customer = cls(customer.id) setup_intent = customer.stripe_setup_intent - if not setup_intent or setup_intent.status != 'succeeded': + if not setup_intent: + continue + if setup_intent.status not in {'succeeded', 'canceled'}: + delay = customer.stripe_account.setup_intent_delay + expiration = dt.datetime.now() - delay + if dt.datetime.fromtimstamp(setup_intent.created) < expiration: + stripe.SetupIntent.cancel( + customer.stripe_customer_id, + api_key=customer.stripe_account.secret_key) continue customer.lock() try: - if customer.stripe_customer_id: - stripe.PaymentMethod.attach( - setup_intent.payment_method, - customer=customer.stripe_customer_id, - api_key=customer.stripe_account.secret_key) - else: - cu = stripe.Customer.create( - api_key=customer.stripe_account.secret_key, - payment_method=setup_intent.payment_method, - **customer._customer_parameters()) - customer.stripe_customer_id = cu.id + if setup_intent.status == 'succeeded': + if customer.stripe_customer_id: + stripe.PaymentMethod.attach( + setup_intent.payment_method, + customer=customer.stripe_customer_id, + api_key=customer.stripe_account.secret_key) + else: + cu = stripe.Customer.create( + api_key=customer.stripe_account.secret_key, + payment_method=setup_intent.payment_method, + **customer._customer_parameters()) + customer.stripe_customer_id = cu.id except (stripe.error.RateLimitError, stripe.error.APIConnectionError) as e: logger.warning(str(e)) diff --git a/view/account_form.xml b/view/account_form.xml index ab2aff19fc..16d6b3f16c 100644 --- a/view/account_form.xml +++ b/view/account_form.xml @@ -13,4 +13,7 @@ this repository contains the full copyright notices and license terms. -->