Ensure to have an idempotency key by default

issue7083
review43061002
This commit is contained in:
Cédric Krier committed 2018-02-14 00:30:14 +01:00
1 parent b9ee6ac434
commit 54e5cb4096
2 files changed
+44 -1

No files matched your search

+41 -1
View File
@@ -9,6 +9,7 @@ from operator import attrgetter
import stripe
from trytond import backend
from trytond.model import ModelSQL, ModelView, Workflow, fields
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Bool
@@ -113,6 +114,8 @@ class Payment:
'invisible': ~Eval('stripe_journal') | ~Eval('stripe_token'),
},
depends=['stripe_journal', 'stripe_token'])
stripe_idempotency_key = fields.Char(
"Stripe Idempotency_key", readonly=True)
stripe_error_message = fields.Char("Stripe Error Message", readonly=True,
states={
'invisible': ~Eval('stripe_error_message'),
@@ -199,6 +202,21 @@ class Payment:
# the readonly attribute must be unset.
cls.stripe_customer_source_selection._field.readonly = False
@classmethod
def __register__(cls, module_name):
TableHandler = backend.get('TableHandler')
cursor = Transaction().connection.cursor()
sql_table = cls.__table__()
table = TableHandler(cls, module_name)
idempotency_key_exist = table.column_exist('stripe_idempotency_key')
super(Payment, cls).__register__(module_name)
# Migration from 4.6: do not set the same key to all existing payments
if not idempotency_key_exist:
cursor.execute(*sql_table.update(
[sql_table.stripe_idempotency_key], [None]))
@classmethod
def default_stripe_capture(cls):
return True
@@ -211,6 +229,10 @@ class Payment:
def default_stripe_chargeable(cls):
return False
@classmethod
def default_stripe_idempotency_key(cls):
return uuid.uuid4().hex
@fields.depends('journal')
def on_change_with_stripe_journal(self, name=None):
if self.journal:
@@ -288,6 +310,16 @@ class Payment:
'payment': self.rec_name,
})
@classmethod
def create(cls, vlist):
vlist = [v.copy() for v in vlist]
for values in vlist:
# Ensure to get a different key for each record
# default methods are called only once
values.setdefault('stripe_idempotency_key',
cls.default_stripe_idempotency_key())
return super(Payment, cls).create(vlist)
@classmethod
def copy(cls, payments, default=None):
if default is None:
@@ -297,6 +329,7 @@ class Payment:
default['stripe_checkout_id'] = None
default['stripe_charge_id'] = None
default['stripe_token'] = None
default.setdefault('stripe_idempotency_key')
default.setdefault('stripe_error_message')
default.setdefault('stripe_error_code')
default.setdefault('stripe_error_param')
@@ -375,6 +408,9 @@ class Payment:
source = self.stripe_customer_source
if self.stripe_customer:
customer = self.stripe_customer.stripe_customer_id
idempotency_key = None
if self.stripe_idempotency_key:
idempotency_key = 'charge-%s' % self.stripe_idempotency_key
return {
'api_key': self.journal.stripe_account.secret_key,
'amount': self.stripe_amount,
@@ -383,7 +419,7 @@ class Payment:
'description': self.description,
'customer': customer,
'source': source,
'idempotency_key': self.stripe_checkout_id,
'idempotency_key': idempotency_key,
}
@classmethod
@@ -427,8 +463,12 @@ class Payment:
Transaction().commit()
def _capture_parameters(self):
idempotency_key = None
if self.stripe_idempotency_key:
idempotency_key = 'capture-%s' % self.stripe_idempotency_key
return {
'amount': self.stripe_amount,
'idempotency_key': idempotency_key,
}
@@ -119,7 +119,10 @@ Run cron::
Create failing payment::
>>> previous_idempotency_key = payment.stripe_idempotency_key
>>> payment, = payment.duplicate()
>>> payment.stripe_idempotency_key != previous_idempotency_key
True
>>> payment.click('approve')
>>> payment.state
u'approved'