tipi payment form cell (#14519)
This commit is contained in:
parent
a5309c31af
commit
970fadf03b
|
@ -0,0 +1,40 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0001_initial'),
|
||||
('data', '0022_auto_20170214_2006'),
|
||||
('lingo', '0027_auto_20170214_2006'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TipiPaymentFormCell',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('placeholder', models.CharField(max_length=20)),
|
||||
('order', models.PositiveIntegerField()),
|
||||
('slug', models.SlugField(verbose_name='Slug', blank=True)),
|
||||
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)),
|
||||
('public', models.BooleanField(default=True, verbose_name='Public')),
|
||||
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
|
||||
('last_update_timestamp', models.DateTimeField(auto_now=True)),
|
||||
('title', models.CharField(max_length=150, verbose_name='Title', blank=True)),
|
||||
('url', models.URLField(default=b'https://www.tipi.budget.gouv.fr/tpa/paiement.web', verbose_name='TIPI payment service URL')),
|
||||
('regies', models.CharField(help_text='separated by commas', max_length=256, verbose_name='Regies')),
|
||||
('control_protocol', models.CharField(default=b'1', max_length=8, verbose_name='Control protocol', choices=[(b'pesv2', 'Indigo/PES v2'), (b'rolmre', 'ROLMRE')])),
|
||||
('test_mode', models.BooleanField(default=False, verbose_name='Test mode')),
|
||||
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
|
||||
('page', models.ForeignKey(to='data.Page')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'TIPI Payment Form',
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
|
@ -500,3 +500,38 @@ class SelfDeclaredInvoicePayment(Items):
|
|||
context['synchronous'] = True
|
||||
context['page_path'] = context['request'].path
|
||||
return super(Items, self).render(context)
|
||||
|
||||
|
||||
TIPI_CONTROL_PROCOTOLS = (
|
||||
('pesv2', _('Indigo/PES v2')),
|
||||
('rolmre', _('ROLMRE')),
|
||||
)
|
||||
|
||||
@register_cell_class
|
||||
class TipiPaymentFormCell(CellBase):
|
||||
title = models.CharField(_('Title'), max_length=150, blank=True)
|
||||
url = models.URLField(_('TIPI payment service URL'), default='https://www.tipi.budget.gouv.fr/tpa/paiement.web')
|
||||
regies = models.CharField(_('Regies'), help_text=_('separated by commas'), max_length=256)
|
||||
control_protocol = models.CharField(_('Control protocol'), max_length=8, choices=TIPI_CONTROL_PROCOTOLS, default='1')
|
||||
test_mode = models.BooleanField(_('Test mode'), default=False)
|
||||
template_name = 'lingo/tipi_form.html'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('TIPI Payment Form')
|
||||
|
||||
class Media:
|
||||
js = ('js/tipi.js',)
|
||||
|
||||
def get_cell_extra_context(self, context):
|
||||
extra_context = super(TipiPaymentFormCell, self).get_cell_extra_context(context)
|
||||
context['title'] = self.title
|
||||
context['url'] = self.url
|
||||
context['mode'] = 'T' if self.test_mode else 'M'
|
||||
context['pesv2'] = (self.control_protocol == 'pesv2')
|
||||
context['regies'] = []
|
||||
for regie in self.regies.split(','):
|
||||
regie_id = regie.strip()
|
||||
if not regie_id:
|
||||
continue
|
||||
context['regies'].append(regie_id)
|
||||
return extra_context
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{% load i18n %}
|
||||
<h2>{{ title }}</h2>
|
||||
<div>
|
||||
<form name="tipi_form" id="tipi_form" data-url="{{ url }}" data-saisie="{{ mode }}" data-pesv2="{{ pesv2 }}">
|
||||
<ul class="errorlist" id="popup_warning" style="display: none">
|
||||
<li>{% trans "Your browser is blocking popups but they are required to start the payment, make sure they are allowed for this site." %}</li>
|
||||
</ul>
|
||||
<p><label>{% trans "Community identifier" %}</label>
|
||||
<select id="numcli">
|
||||
{% for id in regies %}
|
||||
<option value="{{ id }}">{{ id }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
<ul class="errorlist" id="refdet_error" style="display: none">
|
||||
<li>{% trans "invalid reference" %}</li>
|
||||
</ul>
|
||||
<p>
|
||||
<label>{% trans "Reference" %}</label>
|
||||
<input type="text" id="exer" maxlength="4" size="4" placeholder="0000" /> -
|
||||
{% if pesv2 %}
|
||||
<input type="text" id="idpce" maxlength="8" size="8" placeholder="00000000" /> -
|
||||
<input type="text" id="idligne" maxlength="6" size="6" placeholder="000000" />
|
||||
{% else %}
|
||||
<input type="text" id="rolrec" maxlength="2" size="2" placeholder="00" /> -
|
||||
<input type="text" id="roldeb" maxlength="2" size="2" placeholder="00" /> -
|
||||
<input type="text" id="roldet" maxlength="13" size="13" placeholder="0000000000000" />
|
||||
{% endif %}
|
||||
</p>
|
||||
<ul class="errorlist" id="montant_error" style="display: none">
|
||||
<li>{% trans "invalid amount" %}</li>
|
||||
</ul>
|
||||
<p>
|
||||
<label>{% trans "Amount" %}</label>
|
||||
<input type="text" id="montant_euros" size="4" maxlength="4" placeholder="0000" /> ,
|
||||
<input type="text" id="montant_cents" size="2" maxlength="2" placeholder="00" value="00" /> €
|
||||
</p>
|
||||
<ul class="errorlist" id="mel_error" style="display: none">
|
||||
<li>{% trans "invalid email" %}</li>
|
||||
</ul>
|
||||
<p>
|
||||
<label>{% trans "Email" %}</label>
|
||||
<input type="email" id="mel" {% if request.user.email %}value="{{ request.user.email }}" {% endif %} placeholder="{% trans "user@example.net" %}" />
|
||||
</p>
|
||||
<p>
|
||||
<button>{% trans "Pay" %}</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,129 @@
|
|||
$(function() {
|
||||
var tipi_popup;
|
||||
var tipi_timer;
|
||||
function reinit() {
|
||||
$('#refdet_error').hide();
|
||||
$('#montant_error').hide();
|
||||
$('#mel_error').hide();
|
||||
}
|
||||
|
||||
function checkpopup() {
|
||||
// checks if TIPI is still open and disable form widgets
|
||||
if (tipi_popup.closed) {
|
||||
$('#tipi_form input, #tipi_form select, #tipi_form button').attr('disabled', false);
|
||||
document.forms['tipi_form'].reset();
|
||||
clearInterval(tipi_timer);
|
||||
}
|
||||
}
|
||||
|
||||
function refdet_valid_value(element) {
|
||||
var value = element.val().trim();
|
||||
if (!value || isNaN(value)) {
|
||||
$('#refdet_error').show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function fill_padding(element) {
|
||||
// if element is shorter than required length it should be padded with zeros
|
||||
var padding = '';
|
||||
var value = element.val().trim();
|
||||
for (var i=0; i<element.attr('maxLength')-value.length; i++) {
|
||||
padding += '0';
|
||||
}
|
||||
return padding + value;
|
||||
}
|
||||
|
||||
$("form#tipi_form").on("submit", function() {
|
||||
var url = $("form#tipi_form").data("url");
|
||||
var saisie = $("form#tipi_form").data("saisie");
|
||||
var pesv2 = $("form#tipi_form").data("pesv2") == 'True';
|
||||
var params = {'refdet': function(id) {
|
||||
var exer = params.exer('exer');
|
||||
if (pesv2) {
|
||||
// if PESv2 then refdet is composed by exer + idpce + idligne
|
||||
var idpce = $('#idpce');
|
||||
var idligne = $('#idligne');
|
||||
if (refdet_valid_value(idpce) && refdet_valid_value(idligne)) {
|
||||
refdet = fill_padding(idpce) + fill_padding(idligne);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// if ROLMRE then refdet is composed by exer + rolrec + roldeb + roldet
|
||||
var rolrec = $('#rolrec');
|
||||
var roldeb = $('#roldeb');
|
||||
var roldet = $('#roldet');
|
||||
if (refdet_valid_value(rolrec) && refdet_valid_value(roldeb) && refdet_valid_value(roldet)) {
|
||||
refdet = fill_padding(rolrec) + fill_padding(roldeb) + fill_padding(roldet);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (saisie == 'T') {
|
||||
// in test mode the refdet should be 999900000000999999
|
||||
return "999900000000999999";
|
||||
} else {
|
||||
return exer + refdet;
|
||||
}
|
||||
}, 'montant': function(id) {
|
||||
var euros = $('#' + id + '_euros').val().trim();
|
||||
var cents = $('#' + id + '_cents').val().trim();
|
||||
if (euros && cents) {
|
||||
if (isNaN(euros) || isNaN(cents)) {
|
||||
$('#' + id + '_error').show();
|
||||
return false;
|
||||
}
|
||||
return parseInt(euros) * 100 + parseInt(cents);
|
||||
}
|
||||
$('#' + id + '_error').show();
|
||||
return false;
|
||||
}, 'mel': function(id) {
|
||||
var email = $('#' + id).val().trim();
|
||||
if (!email) {
|
||||
$('#' + id +'_error').show();
|
||||
}
|
||||
return email;
|
||||
}, 'exer': function(id) {
|
||||
if (saisie == 'T') {
|
||||
// in test mode exer should be 9999
|
||||
return "9999";
|
||||
}
|
||||
var exer = $('#' + id);
|
||||
if (refdet_valid_value(exer)) {
|
||||
return exer.val().trim();
|
||||
}
|
||||
return false;
|
||||
}};
|
||||
var tipi_url = url + '?saisie=' + saisie + '&numcli=' + $('#numcli').val();
|
||||
var url_params = '&';
|
||||
reinit();
|
||||
|
||||
for (var field in params) {
|
||||
var valid = params[field](field);
|
||||
if (!valid) {
|
||||
return false;
|
||||
} else {
|
||||
var field_error = $('#' + field + '_error');
|
||||
if (field_error) {
|
||||
field_error.hide();
|
||||
}
|
||||
url_params += field + '=' + valid + '&';
|
||||
}
|
||||
}
|
||||
var url = tipi_url;
|
||||
url = tipi_url + encodeURI(url_params);
|
||||
// TIPI popup requires specific params
|
||||
tipi_popup = window.open(url, 'tipi', 'height=800, width=900, toolbar=no, menubar=no, scrollbars=no, resizable=yes, location=no, directories=no, status=no');
|
||||
if (tipi_popup) {
|
||||
$('#tipi_form input, #tipi_form select, #tipi_form button').attr('disabled', true);
|
||||
tipi_timer = setInterval(checkpopup, 400);
|
||||
$('#popup_warning').hide();
|
||||
} else {
|
||||
$('#popup_warning').show();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
|
@ -11,7 +11,7 @@ from django.utils import timezone
|
|||
from combo.data.models import Page
|
||||
from combo.apps.lingo.models import Regie, BasketItem, Transaction
|
||||
from combo.apps.lingo.models import (LingoBasketCell,
|
||||
LingoRecentTransactionsCell, LingoBasketLinkCell)
|
||||
LingoRecentTransactionsCell, LingoBasketLinkCell, TipiPaymentFormCell)
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
@ -152,3 +152,40 @@ def test_basket_link_cell(regie, user):
|
|||
content = cell.render(context)
|
||||
assert '12345' in content
|
||||
assert page.get_online_url() in content
|
||||
|
||||
def test_tipi_cell():
|
||||
page = Page(title='example page', slug='example-page')
|
||||
page.save()
|
||||
cell = TipiPaymentFormCell()
|
||||
cell.page = page
|
||||
cell.title = 'TIPI Payment'
|
||||
cell.regies = "test regie"
|
||||
cell.order = 0
|
||||
cell.save()
|
||||
assert cell.control_protocol == '1'
|
||||
assert cell.url == 'https://www.tipi.budget.gouv.fr/tpa/paiement.web'
|
||||
assert cell.template_name == 'lingo/tipi_form.html'
|
||||
html = cell.render(Context({}))
|
||||
assert "<h2>TIPI Payment</h2>" in html
|
||||
assert "Community identifier" in html
|
||||
assert 'id="exer"' in html
|
||||
assert 'id="idpce"' in html
|
||||
assert 'id="idligne"' in html
|
||||
assert 'id="rolrec"' not in html
|
||||
assert 'id="roldeb"' not in html
|
||||
assert 'id="roldet"' not in html
|
||||
assert 'data-saisie="M"' in html
|
||||
|
||||
cell.control_protocol = '2'
|
||||
cell.test_mode = True
|
||||
cell.save()
|
||||
html = cell.render(Context({}))
|
||||
assert 'id="rolrec"' in html
|
||||
assert 'id="roldeb"' in html
|
||||
assert 'id="roldet"' in html
|
||||
assert 'id="idpce"' not in html
|
||||
assert 'id="idligne"' not in html
|
||||
assert 'data-saisie="T"' in html
|
||||
|
||||
cell_media = str(cell.media)
|
||||
assert "js/tipi.js" in cell_media
|
||||
|
|
Loading…
Reference in New Issue