lingo: start transaction options (#32967)
Based on the backend capabilities.
This commit is contained in:
parent
2eaeeb3670
commit
23f13059d0
|
@ -0,0 +1,69 @@
|
|||
# lingo - basket and payment system
|
||||
# Copyright (C) 2019 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from django import forms
|
||||
|
||||
from .models import Regie
|
||||
|
||||
|
||||
def create_form_fields(parameters, json_field):
|
||||
fields, initial = {}, {}
|
||||
for param in parameters:
|
||||
field_name = param['name']
|
||||
if param['type'] is bool:
|
||||
fields[field_name] = forms.BooleanField(label=param['caption'], required=False)
|
||||
initial[field_name] = param['default']
|
||||
if field_name in json_field:
|
||||
initial[field_name] = json_field[field_name]
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
return fields, initial
|
||||
|
||||
|
||||
def compute_json_field(parameters, cleaned_data):
|
||||
json_field = {}
|
||||
for param in parameters:
|
||||
param_name = param['name']
|
||||
if param_name in cleaned_data:
|
||||
json_field[param_name] = cleaned_data[param_name]
|
||||
return json_field
|
||||
|
||||
|
||||
class RegieForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Regie
|
||||
fields = ['label', 'slug', 'description', 'payment_backend', 'is_default',
|
||||
'webservice_url', 'extra_fees_ws_url', 'payment_min_amount', 'text_on_success']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RegieForm, self).__init__(*args, **kwargs)
|
||||
fields, initial = create_form_fields(
|
||||
self.instance.payment_backend.get_payment().get_parameters(scope='transaction'),
|
||||
self.instance.transaction_options
|
||||
)
|
||||
self.fields.update(fields)
|
||||
self.initial.update(initial)
|
||||
|
||||
def save(self):
|
||||
instance = super(RegieForm, self).save()
|
||||
instance.transaction_options = compute_json_field(
|
||||
self.instance.payment_backend.get_payment().get_parameters(scope='transaction'),
|
||||
self.cleaned_data
|
||||
)
|
||||
instance.save()
|
||||
return instance
|
|
@ -27,31 +27,23 @@ from django.http import HttpResponse
|
|||
|
||||
import eopayment
|
||||
|
||||
from .forms import RegieForm
|
||||
from .models import PaymentBackend, Regie, Transaction
|
||||
|
||||
|
||||
REGIE_FIELDS = ['label', 'slug', 'description', 'payment_backend', 'is_default', 'webservice_url',
|
||||
'extra_fees_ws_url', 'payment_min_amount', 'text_on_success']
|
||||
|
||||
|
||||
class RegieListView(ListView):
|
||||
model = Regie
|
||||
|
||||
|
||||
class RegieCreateView(CreateView):
|
||||
model = Regie
|
||||
fields = REGIE_FIELDS
|
||||
fields = ['label', 'slug', 'description', 'payment_backend']
|
||||
success_url = reverse_lazy('lingo-manager-regie-list')
|
||||
|
||||
def get_initial(self):
|
||||
if self.model.objects.all().count() == 0:
|
||||
return {'is_default': True}
|
||||
return {}
|
||||
|
||||
|
||||
class RegieUpdateView(UpdateView):
|
||||
model = Regie
|
||||
fields = REGIE_FIELDS
|
||||
form_class = RegieForm
|
||||
success_url = reverse_lazy('lingo-manager-regie-list')
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.18 on 2019-05-14 12:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('lingo', '0036_auto_20190426_1202'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='regie',
|
||||
name='transaction_options',
|
||||
field=jsonfield.fields.JSONField(blank=True, default=dict, verbose_name='Transaction Options'),
|
||||
),
|
||||
]
|
|
@ -98,6 +98,9 @@ class PaymentBackend(models.Model):
|
|||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
def get_payment(self):
|
||||
return eopayment.Payment(self.service, self.service_options)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Regie(models.Model):
|
||||
|
@ -117,6 +120,7 @@ class Regie(models.Model):
|
|||
verbose_name=_('Custom text displayed on success'),
|
||||
blank=True, null=True)
|
||||
payment_backend = models.ForeignKey(PaymentBackend, on_delete=models.CASCADE)
|
||||
transaction_options = JSONField(blank=True, verbose_name=_('Transaction Options'))
|
||||
|
||||
def is_remote(self):
|
||||
return self.webservice_url != ''
|
||||
|
|
|
@ -339,6 +339,8 @@ class PayMixin(object):
|
|||
capture_date = items[0].capture_date
|
||||
if capture_date:
|
||||
kwargs['capture_date'] = capture_date
|
||||
if regie.transaction_options:
|
||||
kwargs.update(regie.transaction_options)
|
||||
(order_id, kind, data) = payment.request(total_amount, **kwargs)
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(u'emitted payment request with id %s', smart_text(order_id), extra={
|
||||
|
|
|
@ -47,7 +47,6 @@ def test_add_regie(app, admin_user, payment_backend):
|
|||
resp.forms[0]['slug'] = 'test'
|
||||
resp.forms[0]['description'] = 'description'
|
||||
resp.forms[0]['payment_backend'] = payment_backend.pk
|
||||
assert resp.form['is_default'].checked is True
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.location.endswith('/manage/lingo/regies/')
|
||||
assert Regie.objects.count() == 1
|
||||
|
@ -68,6 +67,61 @@ def test_edit_regie(app, admin_user, payment_backend):
|
|||
regie = Regie.objects.all()[0]
|
||||
assert regie.description == 'other description'
|
||||
|
||||
|
||||
def test_edit_regie_dynamic_backend_fields(app, admin_user):
|
||||
payment_backend = PaymentBackend.objects.create(
|
||||
label='test1', slug='test1', service='systempayv2', service_options={'siret': '1234'})
|
||||
regie = Regie.objects.create(
|
||||
label='test-regie', slug='test-regie', payment_backend=payment_backend)
|
||||
assert regie.transaction_options == {}
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/lingo/regies/%s/edit' % regie.pk, status=200)
|
||||
resp.forms[0]['label'] = 'test-regie'
|
||||
resp.forms[0]['slug'] = 'test-regie'
|
||||
resp.forms[0]['description'] = 'description'
|
||||
resp.forms[0]['payment_backend'] = payment_backend.pk
|
||||
|
||||
assert resp.forms[0]['manual_validation'].checked is False
|
||||
resp.forms[0]['manual_validation'] = True
|
||||
resp = resp.forms[0].submit()
|
||||
|
||||
assert Regie.objects.count() == 1
|
||||
|
||||
regie = Regie.objects.get(slug='test-regie')
|
||||
assert regie.transaction_options['manual_validation']
|
||||
|
||||
# No dynamic fields if no backend capabilities
|
||||
payment_backend_dummy = PaymentBackend.objects.create(
|
||||
label='test1', slug='test-dummy', service='dummy', service_options={'siret': '1234'})
|
||||
regie_dummy = Regie.objects.create(
|
||||
label='test-regie-2', slug='test-regie-2', payment_backend=payment_backend_dummy)
|
||||
|
||||
resp = app.get('/manage/lingo/regies/%s/edit' % regie_dummy.pk, status=200)
|
||||
assert 'manual_validation' not in resp.forms[0].fields
|
||||
resp.forms[0]['label'] = 'Test'
|
||||
resp.forms[0]['slug'] = 'test-regie-2'
|
||||
resp.forms[0]['description'] = 'description'
|
||||
resp.forms[0]['payment_backend'] = payment_backend_dummy.pk
|
||||
resp = resp.forms[0].submit()
|
||||
|
||||
regie_dummy = Regie.objects.get(slug='test-regie-2')
|
||||
assert regie_dummy.transaction_options == {}
|
||||
|
||||
# Change backend
|
||||
resp = app.get('/manage/lingo/regies/%s/edit' % regie.pk, status=200)
|
||||
resp.forms[0]['label'] = 'test-regie'
|
||||
resp.forms[0]['slug'] = 'test-regie'
|
||||
resp.forms[0]['description'] = 'description'
|
||||
resp.forms[0]['payment_backend'] = payment_backend_dummy.pk
|
||||
|
||||
assert resp.forms[0]['manual_validation'].checked is True
|
||||
resp.forms[0]['manual_validation'] = True
|
||||
resp = resp.forms[0].submit()
|
||||
|
||||
regie = Regie.objects.get(slug='test-regie')
|
||||
assert regie.transaction_options == {}
|
||||
|
||||
def test_delete_regie(app, admin_user, payment_backend):
|
||||
test_add_regie(app, admin_user, payment_backend)
|
||||
app = login(app)
|
||||
|
@ -88,7 +142,6 @@ def test_add_second_regie(app, admin_user, payment_backend):
|
|||
resp.forms[0]['slug'] = 'test2'
|
||||
resp.forms[0]['description'] = 'description'
|
||||
resp.forms[0]['payment_backend'] = payment_backend.pk
|
||||
assert resp.form['is_default'].checked is False
|
||||
resp = resp.forms[0].submit()
|
||||
assert resp.location.endswith('/manage/lingo/regies/')
|
||||
|
||||
|
|
|
@ -136,6 +136,32 @@ def test_payment_min_amount(app, basket_page, regie, user):
|
|||
assert resp.status_code == 302
|
||||
|
||||
|
||||
def test_transaction_manual_validation(app, basket_page, user, monkeypatch):
|
||||
pb = PaymentBackend.objects.create(
|
||||
label='test1', slug='test1', service='payzen',
|
||||
service_options={'vads_site_id': '12345678', 'secret_test': 'plkoBdfcx987dhft6'}
|
||||
|
||||
)
|
||||
regie = Regie.objects.create(
|
||||
label='Test', slug='test', description='test', payment_backend=pb,
|
||||
transaction_options={'manual_validation': True})
|
||||
BasketItem.objects.create(
|
||||
user=user, regie=regie, subject='item1', amount='1.5', source_url='/item/1')
|
||||
|
||||
class MockPayment(object):
|
||||
request = mock.Mock(return_value=(9876, 3, {}))
|
||||
|
||||
def get_eopayment_object(*args, **kwargs):
|
||||
return MockPayment
|
||||
|
||||
import combo.apps.lingo.views
|
||||
monkeypatch.setattr(combo.apps.lingo.views, 'get_eopayment_object', get_eopayment_object)
|
||||
|
||||
resp = login(app).get('/test_basket_cell/')
|
||||
resp = resp.form.submit()
|
||||
assert MockPayment.request.call_args.kwargs['manual_validation'] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize('with_payment_backend', [False, True])
|
||||
def test_successfull_items_payment(app, basket_page, regie, user, with_payment_backend):
|
||||
items = {'item1': {'amount': '10.5', 'source_url': 'http://example.org/item/1'},
|
||||
|
|
Loading…
Reference in New Issue