changeset 0852decc2eb9 in modules/account_payment_stripe:default
details:
https://hg.tryton.org/modules/account_payment_stripe?cmd=changeset;node=0852decc2eb9
description:
Add wizard to delete customer source
issue9501
review323711002
diffstat:
CHANGELOG | 1 +
__init__.py | 2 +
payment.py | 72 ++++++++++++++++++++++++++++++-
payment.xml | 17 +++++++
tests/scenario_account_payment_stripe.rst | 13 +++++
view/customer_form.xml | 5 +-
view/customer_source_detach_ask_form.xml | 9 +++
7 files changed, 117 insertions(+), 2 deletions(-)
diffs (210 lines):
diff -r b17ec8f1c68c -r 0852decc2eb9 CHANGELOG
--- a/CHANGELOG Mon Jul 13 20:54:31 2020 +0200
+++ b/CHANGELOG Tue Aug 11 21:47:51 2020 +0200
@@ -1,3 +1,4 @@
+* Add wizard to delete customer source
* Add webhook for payment intent canceled
Version 5.6.0 - 2020-05-04
diff -r b17ec8f1c68c -r 0852decc2eb9 __init__.py
--- a/__init__.py Mon Jul 13 20:54:31 2020 +0200
+++ b/__init__.py Tue Aug 11 21:47:51 2020 +0200
@@ -19,11 +19,13 @@
payment.Journal,
payment.Group,
payment.Payment,
+ payment.CustomerSourceDetachAsk,
party.Party,
ir.Cron,
module='account_payment_stripe', type_='model')
Pool.register(
payment.Checkout,
+ payment.CustomerSourceDetach,
party.Replace,
module='account_payment_stripe', type_='wizard')
Pool.register(
diff -r b17ec8f1c68c -r 0852decc2eb9 payment.py
--- a/payment.py Mon Jul 13 20:54:31 2020 +0200
+++ b/payment.py Tue Aug 11 21:47:51 2020 +0200
@@ -22,7 +22,8 @@
from trytond.sendmail import sendmail_transactional
from trytond.transaction import Transaction
from trytond.url import http_host
-from trytond.wizard import Wizard, StateAction
+from trytond.wizard import (
+ Wizard, StateAction, StateView, StateTransition, Button)
from trytond.modules.account_payment.exceptions import (
ProcessError, PaymentValidationError)
@@ -1449,6 +1450,10 @@
'invisible': ~Eval('stripe_checkout_needed', False),
'depends': ['stripe_checkout_needed'],
},
+ 'detach_source': {
+ 'invisible': ~Eval('stripe_customer_id'),
+ 'depends': ['stripe_customer_id'],
+ },
})
def get_stripe_checkout_needed(self, name):
@@ -1628,6 +1633,30 @@
name = '****' + source.sepa_debit.last4
return name
+ @classmethod
+ @ModelView.button_action(
+ 'account_payment_stripe.wizard_customer_source_detach')
+ def detach_source(cls, customers):
+ pass
+
+ def delete_source(self, source):
+ try:
+ if source in dict(self.payment_methods()):
+ stripe.PaymentMethod.detach(
+ source,
+ api_key=self.stripe_account.secret_key)
+ else:
+ stripe.Customer.delete_source(
+ self.stripe_customer_id,
+ source,
+ api_key=self.stripe_account.secret_key)
+ except (stripe.error.RateLimitError,
+ stripe.error.APIConnectionError) as e:
+ logger.warning(str(e))
+ raise
+ self._sources_cache.clear()
+ self._payment_methods_cache.clear()
+
def payment_methods(self):
methods = self._payment_methods_cache.get(self.id)
if methods is not None:
@@ -1742,3 +1771,44 @@
class CheckoutPage(Report):
"Stripe Checkout"
__name__ = 'account.payment.stripe.checkout'
+
+
+class CustomerSourceDetach(Wizard):
+ "Detach Customer Source"
+ __name__ = 'account.payment.stripe.customer.source.detach'
+ start_state = 'ask'
+ ask = StateView(
+ 'account.payment.stripe.customer.source.detach.ask',
+ 'account_payment_stripe.customer_source_detach_ask_view_form', [
+ Button("Cancel", 'end', 'tryton-cancel'),
+ Button("Detach", 'detach', 'tryton-ok', default=True),
+ ])
+ detach = StateTransition()
+
+ def default_ask(self, fields):
+ default = {}
+ if 'customer' in fields:
+ default['customer'] = self.record.id
+ return default
+
+ def transition_detach(self):
+ self.record.delete_source(self.ask.source)
+ return 'end'
+
+
+class CustomerSourceDetachAsk(ModelView):
+ "Detach Customer Source"
+ __name__ = 'account.payment.stripe.customer.source.detach.ask'
+
+ customer = fields.Many2One(
+ 'account.payment.stripe.customer', "Customer", readonly=True)
+ source = fields.Selection('get_sources', "Source", required=True)
+
+ @fields.depends('customer')
+ def get_sources(self):
+ sources = [('', '')]
+ if self.customer:
+ sources.extend(
+ dict(set(self.customer.sources())
+ | set(self.customer.payment_methods())).items())
+ return sources
diff -r b17ec8f1c68c -r 0852decc2eb9 payment.xml
--- a/payment.xml Mon Jul 13 20:54:31 2020 +0200
+++ b/payment.xml Tue Aug 11 21:47:51 2020 +0200
@@ -302,6 +302,11 @@
<field name="model"
search="[('model', '=', 'account.payment.stripe.customer')]"/>
</record>
+ <record model="ir.model.button" id="customer_source_detach_button">
+ <field name="name">detach_source</field>
+ <field name="string">Detach Source</field>
+ <field name="model" search="[('model', '=',
'account.payment.stripe.customer')]"/>
+ </record>
<record model="ir.action.wizard" id="wizard_checkout">
<field name="name">Stripe Checkout</field>
@@ -325,6 +330,18 @@
<field name="template_extension">html</field>
</record>
+ <record model="ir.action.wizard" id="wizard_customer_source_detach">
+ <field name="name">Detach Source</field>
+ <field
name="wiz_name">account.payment.stripe.customer.source.detach</field>
+ <field name="model">account.payment.stripe.customer</field>
+ </record>
+
+ <record model="ir.ui.view" id="customer_source_detach_ask_view_form">
+ <field
name="model">account.payment.stripe.customer.source.detach.ask</field>
+ <field name="type">form</field>
+ <field name="name">customer_source_detach_ask_form</field>
+ </record>
+
<record model="ir.cron" id="cron_charge">
<field name="method">account.payment|stripe_charge</field>
<field name="interval_number" eval="15"/>
diff -r b17ec8f1c68c -r 0852decc2eb9 tests/scenario_account_payment_stripe.rst
--- a/tests/scenario_account_payment_stripe.rst Mon Jul 13 20:54:31 2020 +0200
+++ b/tests/scenario_account_payment_stripe.rst Tue Aug 11 21:47:51 2020 +0200
@@ -219,6 +219,19 @@
>>> payment.state
'succeeded'
+Detach source::
+
+ >>> detach = Wizard(
+ ... 'account.payment.stripe.customer.source.detach', [stripe_customer])
+ >>> detach.form.source = source_id
+ >>> detach.execute('detach')
+
+ >>> cus = stripe.Customer.retrieve(stripe_customer.stripe_customer_id)
+ >>> len(cus.sources)
+ 0
+ >>> len(stripe.PaymentMethod.list(customer=cus.id, type='card'))
+ 0
+
Delete customer::
>>> stripe_customer.delete()
diff -r b17ec8f1c68c -r 0852decc2eb9 view/customer_form.xml
--- a/view/customer_form.xml Mon Jul 13 20:54:31 2020 +0200
+++ b/view/customer_form.xml Tue Aug 11 21:47:51 2020 +0200
@@ -11,7 +11,10 @@
<newline/>
<label name="stripe_customer_id"/>
<field name="stripe_customer_id"/>
- <button name="stripe_checkout" colspan="2"/>
+ <group id="buttons" col="-1" colspan="4">
+ <button name="stripe_checkout"/>
+ <button name="detach_source"/>
+ </group>
<label name="stripe_error_message"/>
<field name="stripe_error_message"/>
<newline/>
diff -r b17ec8f1c68c -r 0852decc2eb9 view/customer_source_detach_ask_form.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/view/customer_source_detach_ask_form.xml Tue Aug 11 21:47:51 2020 +0200
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<form col="2">
+ <label name="customer"/>
+ <field name="customer"/>
+ <label name="source"/>
+ <field name="source"/>
+</form>