[ADD] payment_sips
contains acquirer definition to support Atos Wordline online payments Courtesy of Eezee-It Closes #6684
This commit is contained in:
parent
799fff6897
commit
b2193e6734
|
@ -0,0 +1,2 @@
|
|||
import models
|
||||
import controllers
|
|
@ -0,0 +1,37 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright Eezee-It
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
'name': 'Worldline SIPS Payment Acquiring for online payments',
|
||||
'version': '1.0',
|
||||
'author': 'Eezee-It',
|
||||
'category': 'Hidden',
|
||||
'description': """
|
||||
Worldline SIPS Payment Acquirer for online payments
|
||||
|
||||
Works with Worldline keys version 2.0, contains implementation of
|
||||
payments acquirer using Worldline SIPS.""",
|
||||
'depends': ['payment'],
|
||||
'data': [
|
||||
'views/sips.xml',
|
||||
'views/payment_acquirer.xml',
|
||||
'data/sips.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import main
|
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import logging
|
||||
import werkzeug
|
||||
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SipsController(http.Controller):
|
||||
_notify_url = '/payment/sips/ipn/'
|
||||
_return_url = '/payment/sips/dpn/'
|
||||
|
||||
def _get_return_url(self, **post):
|
||||
""" Extract the return URL from the data coming from sips. """
|
||||
return_url = post.pop('return_url', '')
|
||||
if not return_url:
|
||||
tx_obj = request.registry['payment.transaction']
|
||||
data = tx_obj._sips_data_to_object(post.get('Data'))
|
||||
custom = json.loads(data.pop('returnContext', False) or '{}')
|
||||
return_url = custom.get('return_url', '/')
|
||||
return return_url
|
||||
|
||||
def sips_validate_data(self, **post):
|
||||
res = False
|
||||
env = request.env
|
||||
tx_obj = env['payment.transaction']
|
||||
acquirer_obj = env['payment.acquirer']
|
||||
|
||||
sips = acquirer_obj.search([('provider', '=', 'sips')], limit=1)
|
||||
|
||||
security = sips._sips_generate_shasign(post)
|
||||
if security == post['Seal']:
|
||||
_logger.debug('Sips: validated data')
|
||||
res = tx_obj.sudo().form_feedback(post, 'sips')
|
||||
else:
|
||||
_logger.warning('Sips: data are corrupted')
|
||||
return res
|
||||
|
||||
@http.route([
|
||||
'/payment/sips/ipn/'],
|
||||
type='http', auth='none', methods=['POST'])
|
||||
def sips_ipn(self, **post):
|
||||
""" Sips IPN. """
|
||||
self.sips_validate_data(**post)
|
||||
return ''
|
||||
|
||||
@http.route([
|
||||
'/payment/sips/dpn'], type='http', auth="none", methods=['POST'])
|
||||
def sips_dpn(self, **post):
|
||||
""" Sips DPN """
|
||||
return_url = self._get_return_url(**post)
|
||||
self.sips_validate_data(**post)
|
||||
return werkzeug.utils.redirect(return_url)
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
|
||||
<record id="payment_acquirer_sips" model="payment.acquirer">
|
||||
<field name="name">Sips</field>
|
||||
<field name="provider">sips</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="view_template_id" ref="payment_sips.sips_acquirer_button"/>
|
||||
<field name="environment">test</field>
|
||||
<field name="pre_msg"><![CDATA[
|
||||
<p>You will be redirected to the Sips website after clicking on payment button.</p>]]></field>
|
||||
</record>
|
||||
|
||||
<record id="sequence_payment_transaction_type_sips" model="ir.sequence.type">
|
||||
<field name="name">Sips Payment Transaction</field>
|
||||
<field name="code">sips.payment.transaction</field>
|
||||
</record>
|
||||
|
||||
<record id="sequence_payment_transaction_sips" model="ir.sequence">
|
||||
<field name="name">Sips Payment Transaction</field>
|
||||
<field name="code">sips.payment.transaction</field>
|
||||
<field name="prefix"></field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sips
|
|
@ -0,0 +1,232 @@
|
|||
# -*- coding: utf-'8' "-*-"
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import logging
|
||||
from hashlib import sha256
|
||||
import urlparse
|
||||
import unicodedata
|
||||
|
||||
from openerp import models, fields, api
|
||||
from openerp.tools.float_utils import float_compare
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.payment.models.payment_acquirer import ValidationError
|
||||
from openerp.addons.payment_sips.controllers.main import SipsController
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CURRENCY_CODES = {
|
||||
'EUR': '978',
|
||||
'USD': '840',
|
||||
'CHF': '756',
|
||||
'GBP': '826',
|
||||
'CAD': '124',
|
||||
'JPY': '392',
|
||||
'MXN': '484',
|
||||
'TRY': '949',
|
||||
'AUD': '036',
|
||||
'NZD': '554',
|
||||
'NOK': '578',
|
||||
'BRL': '986',
|
||||
'ARS': '032',
|
||||
'KHR': '116',
|
||||
'TWD': '901',
|
||||
}
|
||||
|
||||
|
||||
class AcquirerSips(models.Model):
|
||||
_inherit = 'payment.acquirer'
|
||||
# Fields
|
||||
sips_merchant_id = fields.Char('SIPS API User Password',
|
||||
required_if_provider='sips')
|
||||
sips_secret = fields.Char('SIPS Secret', size=64, required_if_provider='sips')
|
||||
|
||||
# Methods
|
||||
def _get_sips_urls(self, environment):
|
||||
""" Worldline SIPS URLS """
|
||||
url = {
|
||||
'prod': 'https://payment-webinit.sips-atos.com/paymentInit',
|
||||
'test': 'https://payment-webinit.simu.sips-atos.com/paymentInit', }
|
||||
|
||||
return {'sips_form_url': url.get(environment, url['test']), }
|
||||
|
||||
@api.model
|
||||
def _get_providers(self):
|
||||
providers = super(AcquirerSips, self)._get_providers()
|
||||
providers.append(['sips', 'Sips'])
|
||||
return providers
|
||||
|
||||
def _sips_generate_shasign(self, values):
|
||||
""" Generate the shasign for incoming or outgoing communications.
|
||||
:param dict values: transaction values
|
||||
:return string: shasign
|
||||
"""
|
||||
if self.provider != 'sips':
|
||||
raise ValidationError(_('Incorrect payment acquirer provider'))
|
||||
data = values['Data']
|
||||
|
||||
# Test key provided by Worldine
|
||||
key = u'002001000000001_KEY1'
|
||||
|
||||
if self.environment == 'prod':
|
||||
key = getattr(self, 'sips_secret')
|
||||
|
||||
shasign = sha256(data + key)
|
||||
return shasign.hexdigest()
|
||||
|
||||
@api.multi
|
||||
def sips_form_generate_values(self, partner_values, tx_values):
|
||||
self.ensure_one()
|
||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||
currency = self.env['res.currency'].sudo().browse(tx_values['currency_id'])
|
||||
currency_code = CURRENCY_CODES.get(currency.name, False)
|
||||
if not currency_code:
|
||||
raise ValidationError(_('Currency not supported by Wordline'))
|
||||
amount = int(tx_values.get('amount') * 100)
|
||||
if self.environment == 'prod':
|
||||
# For production environment, key version 2 is required
|
||||
merchant_id = getattr(self, 'sips_merchant_id')
|
||||
key_version = '2'
|
||||
else:
|
||||
# Test key provided by Atos Wordline works only with version 1
|
||||
merchant_id = '002001000000001'
|
||||
key_version = '1'
|
||||
|
||||
sips_tx_values = dict(tx_values)
|
||||
sips_tx_values.update({
|
||||
'Data': u'amount=%s|' % amount +
|
||||
u'currencyCode=%s|' % currency_code +
|
||||
u'merchantId=%s|' % merchant_id +
|
||||
u'normalReturnUrl=%s|' % urlparse.urljoin(base_url, SipsController._return_url) +
|
||||
u'automaticResponseUrl=%s|' % urlparse.urljoin(base_url, SipsController._return_url) +
|
||||
u'transactionReference=%s|' % tx_values['reference'] +
|
||||
u'statementReference=%s|' % tx_values['reference'] +
|
||||
u'keyVersion=%s' % key_version,
|
||||
'InterfaceVersion': 'HP_2.3',
|
||||
})
|
||||
|
||||
return_context = {}
|
||||
if sips_tx_values.get('return_url'):
|
||||
return_context[u'return_url'] = u'%s' % sips_tx_values.pop('return_url')
|
||||
return_context[u'reference'] = u'%s' % sips_tx_values['reference']
|
||||
sips_tx_values['Data'] += u'|returnContext=%s' % (json.dumps(return_context))
|
||||
|
||||
shasign = self._sips_generate_shasign(sips_tx_values)
|
||||
sips_tx_values['Seal'] = shasign
|
||||
return partner_values, sips_tx_values
|
||||
|
||||
@api.multi
|
||||
def sips_get_form_action_url(self):
|
||||
self.ensure_one()
|
||||
return self._get_sips_urls(self.environment)['sips_form_url']
|
||||
|
||||
|
||||
class TxSips(models.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
# sips status
|
||||
_sips_valid_tx_status = ['00']
|
||||
_sips_wait_tx_status = ['90', '99']
|
||||
_sips_refused_tx_status = ['05', '14', '34', '54', '75', '97']
|
||||
_sips_error_tx_status = ['03', '12', '24', '25', '30', '40', '51', '63', '94']
|
||||
_sips_pending_tx_status = ['60']
|
||||
_sips_cancel_tx_status = ['17']
|
||||
|
||||
# --------------------------------------------------
|
||||
# FORM RELATED METHODS
|
||||
# --------------------------------------------------
|
||||
def _sips_data_to_object(self, data):
|
||||
res = {}
|
||||
for element in data.split('|'):
|
||||
element_split = element.split('=')
|
||||
res[element_split[0]] = element_split[1]
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _sips_form_get_tx_from_data(self, data):
|
||||
""" Given a data dict coming from sips, verify it and find the related
|
||||
transaction record. """
|
||||
|
||||
data = self._sips_data_to_object(data.get('Data'))
|
||||
reference = data.get('transactionReference')
|
||||
|
||||
if not reference:
|
||||
custom = json.loads(data.pop('returnContext', False) or '{}')
|
||||
reference = custom.get('reference')
|
||||
|
||||
payment_tx = self.search([('reference', '=', reference)])
|
||||
if not payment_tx or len(payment_tx) > 1:
|
||||
error_msg = _('Sips: received data for reference %s') % reference
|
||||
if not payment_tx:
|
||||
error_msg += _('; no order found')
|
||||
else:
|
||||
error_msg += _('; multiple order found')
|
||||
_logger.error(error_msg)
|
||||
raise ValidationError(error_msg)
|
||||
return payment_tx
|
||||
|
||||
@api.model
|
||||
def _sips_form_get_invalid_parameters(self, tx, data):
|
||||
invalid_parameters = []
|
||||
|
||||
data = self._sips_data_to_object(data.get('Data'))
|
||||
|
||||
# TODO: txn_id: should be false at draft, set afterwards, and verified with txn details
|
||||
if tx.acquirer_reference and data.get('transactionReference') != tx.acquirer_reference:
|
||||
invalid_parameters.append(('transactionReference', data.get('transactionReference'), tx.acquirer_reference))
|
||||
# check what is bought
|
||||
if float_compare(float(data.get('amount', '0.0')) / 100, tx.amount, 2) != 0:
|
||||
invalid_parameters.append(('amount', data.get('amount'), '%.2f' % tx.amount))
|
||||
if tx.partner_reference and data.get('customerId') != tx.partner_reference:
|
||||
invalid_parameters.append(('customerId', data.get('customerId'), tx.partner_reference))
|
||||
|
||||
return invalid_parameters
|
||||
|
||||
@api.model
|
||||
def _sips_form_validate(self, tx, data):
|
||||
data = self._sips_data_to_object(data.get('Data'))
|
||||
status = data.get('responseCode')
|
||||
data = {
|
||||
'acquirer_reference': data.get('transactionReference'),
|
||||
'partner_reference': data.get('customerId'),
|
||||
'date_validate': data.get('transactionDateTime',
|
||||
fields.Datetime.now())
|
||||
}
|
||||
res = False
|
||||
if status in self._sips_valid_tx_status:
|
||||
msg = 'Payment for tx ref: %s, got response [%s], set as done.' % \
|
||||
(tx.reference, status)
|
||||
_logger.info(msg)
|
||||
data.update(state='done', state_message=msg)
|
||||
res = True
|
||||
elif status in self._sips_error_tx_status:
|
||||
msg = 'Payment for tx ref: %s, got response [%s], set as ' \
|
||||
'error.' % (tx.reference, status)
|
||||
data.update(state='error', state_message=msg)
|
||||
elif status in self._sips_wait_tx_status:
|
||||
msg = 'Received wait status for payment ref: %s, got response ' \
|
||||
'[%s], set as error.' % (tx.reference, status)
|
||||
data.update(state='error', state_message=msg)
|
||||
elif status in self._sips_refused_tx_status:
|
||||
msg = 'Received refused status for payment ref: %s, got response' \
|
||||
' [%s], set as error.' % (tx.reference, status)
|
||||
data.update(state='error', state_message=msg)
|
||||
elif status in self._sips_pending_tx_status:
|
||||
msg = 'Payment ref: %s, got response [%s] set as pending.' \
|
||||
% (tx.reference, status)
|
||||
data.update(state='pending', state_message=msg)
|
||||
elif status in self._sips_cancel_tx_status:
|
||||
msg = 'Received notification for payment ref: %s, got response ' \
|
||||
'[%s], set as cancel.' % (tx.reference, status)
|
||||
data.update(state='cancel', state_message=msg)
|
||||
else:
|
||||
msg = 'Received unrecognized status for payment ref: %s, got ' \
|
||||
'response [%s], set as error.' % (tx.reference, status)
|
||||
data.update(state='error', state_message=msg)
|
||||
|
||||
_logger.info(msg)
|
||||
tx.write(data)
|
||||
return res
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="acquirer_form_sips" model="ir.ui.view">
|
||||
<field name="name">acquirer.form.sips</field>
|
||||
<field name="model">payment.acquirer</field>
|
||||
<field name="inherit_id" ref="payment.acquirer_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//group[@name="acquirer_display"]' position='after'>
|
||||
<group attrs="{'invisible': [('provider', '!=', 'sips')]}">
|
||||
<field name="sips_secret"/>
|
||||
<field name="sips_merchant_id"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
|
||||
<template id="sips_acquirer_button">
|
||||
<form t-att-action="tx_url" method="post" target="_self">
|
||||
<input type="hidden" name="Data" t-att-value="tx_values['Data']"/>
|
||||
<input type="hidden" name="InterfaceVersion" t-att-value="tx_values['InterfaceVersion']"/>
|
||||
<input type="hidden" name="Seal" t-att-value="tx_values['Seal']"/>
|
||||
<!-- submit -->
|
||||
<button type="submit" width="100px"
|
||||
t-att-class="submit_class">
|
||||
<img t-if="not submit_txt" src="/payment_sips/static/src/img/sips_icon.png"/>
|
||||
<span t-if="submit_txt"><t t-esc="submit_txt"/> <span class="fa fa-long-arrow-right"/></span>
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
Loading…
Reference in New Issue