[ADD] payment_ccavenue: Add new payment acquirer for India

CCAvenue is the most popular payment provider in india.

Co-authored-by: Rohan Patel <rop@odoo.com>

Related Task ID : 58741
This commit is contained in:
Jinal Patel
2019-02-13 06:12:39 +00:00
committed by Yannick Tivisse
co-authored by Rohan Patel
parent 7a1c8d54b4
commit f735951d5c
12 changed files with 282 additions and 0 deletions
@@ -265,4 +265,26 @@
ref("payment.payment_icon_cc_codensa_easy_credit")])]'/>
</record>
<record id="payment_acquirer_ccavenue" model="payment.acquirer">
<field name="name">CCAvenue</field>
<field name="image" type="base64" file="payment_ccavenue/static/src/img/ccavenue_icon.png"/>
<field name="view_template_id" ref="default_acquirer_button"/>
<field name="module_id" ref="base.module_payment_ccavenue"/>
<field name="description" type="html">
<p>
CCAvenue provides complete, simple and secure online payment gateway services and e-business solutions to Indian websites, with real time Credit Card, Debit Card and Net Banking.
</p>
<ul class="list-inline">
<li class="list-inline-item"><i class="fa fa-check"/>Online Payment</li>
<li class="list-inline-item"><i class="fa fa-check"/>Payment Status Tracking</li>
</ul>
</field>
<!-- https://www.ccavenue.com/widest_range_of_payment_options.jsp# -->
<field name="payment_icon_ids" eval='[(6, 0, [ref("payment.payment_icon_cc_mastercard"),
ref("payment.payment_icon_cc_diners_club_intl"),
ref("payment.payment_icon_cc_jcb"),
ref("payment.payment_icon_cc_american_express"),
ref("payment.payment_icon_cc_visa")])]'/>
</record>
</odoo>
+6
View File
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
from . import models
from odoo.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers
+17
View File
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'CCAvenue Payment Acquirer',
'category': 'Accounting',
'summary': 'Payment Acquirer: CCAvenue Implementation',
'description': """CCAvenue Payment Acquirer""",
'depends': ['payment'],
'external_dependencies': {'python': ['Crypto']},
'data': [
'views/payment_acquirer.xml',
'views/payment_ccavenue_templates.xml',
'data/payment_ccavenue_data.xml',
],
'post_init_hook': 'create_missing_journal_for_acquirers',
}
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import pprint
import werkzeug
from odoo import http
from odoo.http import request
_logger = logging.getLogger(__name__)
class CCAvenueController(http.Controller):
_return_url = '/payment/ccavenue/return/'
_cancel_url = '/payment/ccavenue/cancel/'
@http.route(['/payment/ccavenue/return', '/payment/ccavenue/cancel'], type='http', auth='public', csrf=False)
def ccavenue_return(self, **post):
_logger.info('CCAvenue: Entering form_feedback with post data %s', pprint.pformat(post))
if post:
request.env['payment.transaction'].sudo().form_feedback(post, 'ccavenue')
return werkzeug.utils.redirect('/payment/process')
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="payment.payment_acquirer_ccavenue" model="payment.acquirer">
<field name="provider">ccavenue</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="ccavenue_form"/>
<field name="environment">test</field>
<field name="pre_msg"><![CDATA[
<p>You will be redirected to the CCAvenue website after clicking on the payment button.</p>]]>
</field>
<field name="ccavenue_merchant_id">dummy</field>
<field name="ccavenue_access_code">dummy</field>
<field name="ccavenue_working_key">dummy</field>
</record>
</odoo>
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import payment
+163
View File
@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from binascii import hexlify, unhexlify
from hashlib import md5
from werkzeug import urls
from odoo import api, fields, models, _
from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.addons.payment_ccavenue.controllers.main import CCAvenueController
from odoo.tools.float_utils import float_compare
_logger = logging.getLogger(__name__)
try:
from Crypto.Cipher import AES
except ImportError:
_logger.info('pyCrypto library not found. If you plan to use payment_ccavenue, please install the library from https://pypi.python.org/pypi/pyCrypto')
class PaymentAcquirer(models.Model):
_inherit = 'payment.acquirer'
provider = fields.Selection(selection_add=[('ccavenue', 'CCAvenue')])
ccavenue_merchant_id = fields.Char(string='CCAvenue Merchant ID', required_if_provider='ccavenue', groups='base.group_user')
ccavenue_access_code = fields.Char(string='Access Code', required_if_provider='ccavenue', groups='base.group_user')
ccavenue_working_key = fields.Char(string='Working Key', required_if_provider='ccavenue', groups='base.group_user')
def _get_ccavenue_urls(self, environment):
""" CCAvenue URLs"""
if environment == 'prod':
return {'ccavenue_form_url': 'https://secure.ccavenue.com/transaction/transaction.do?command=initiateTransaction'}
else:
return {'ccavenue_form_url': 'https://test.ccavenue.com/transaction/transaction.do?command=initiateTransaction'}
def _ccavenue_pad(self, data):
length = 16 - (len(data) % 16)
data += chr(length) * length
return data
@api.multi
def _ccavenue_encrypt_text(self, plaintext):
self.ensure_one()
iv = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
plaintext = self._ccavenue_pad(plaintext)
dec_digest = md5(self.ccavenue_working_key.encode('utf-8')).digest()
enc_cipher = AES.new(dec_digest, AES.MODE_CBC, iv)
encrypted_text = hexlify(enc_cipher.encrypt(plaintext)).decode('utf-8')
return encrypted_text
@api.multi
def _ccavenue_decrypt_text(self, ciphertext):
self.ensure_one()
iv = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
dec_digest = md5(self.ccavenue_working_key.encode('utf-8')).digest()
dec_cipher = AES.new(dec_digest, AES.MODE_CBC, iv)
decrypted_text = dec_cipher.decrypt(unhexlify(ciphertext)).decode('utf-8')
return decrypted_text
@api.multi
def ccavenue_form_generate_values(self, values):
self.ensure_one()
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
ccavenue_values = dict(access_code=self.ccavenue_access_code,
merchant_id=self.ccavenue_merchant_id,
order_id=values.get('reference'),
currency=values.get('currency').name,
amount=values.get('amount'),
redirect_url=urls.url_join(base_url, CCAvenueController._return_url),
cancel_url=urls.url_join(base_url, CCAvenueController._cancel_url),
language='EN',
customer_identifier=values.get('partner_email'),
delivery_name=values.get('partner_name'),
delivery_address=values.get('partner_address'),
delivery_city=values.get('partner_city'),
delivery_state=values.get('partner_state').name,
delivery_zip=values.get('partner_zip'),
delivery_country=values.get('partner_country').name,
delivery_tel=values.get('partner_phone'),
billing_name=values.get('billing_partner_name'),
billing_address=values.get('billing_partner_address'),
billing_city=values.get('billing_partner_city'),
billing_state=values.get('billing_partner_state').name,
billing_zip=values.get('billing_partner_zip'),
billing_country=values.get('billing_partner_country').name,
billing_tel=values.get('billing_partner_phone'),
billing_email=values.get('billing_partner_email'),
)
ccavenue_values['encRequest'] = self._ccavenue_encrypt_text(urls.url_encode(ccavenue_values))
return ccavenue_values
@api.multi
def ccavenue_get_form_action_url(self):
self.ensure_one()
return self._get_ccavenue_urls(self.environment)['ccavenue_form_url']
class PaymentTransaction(models.Model):
_inherit = 'payment.transaction'
# Override form_feedback
@api.model
def form_feedback(self, data, acquirer_name):
if acquirer_name == 'ccavenue':
acquirer = self.env['payment.acquirer'].search([('provider', '=', 'ccavenue')], limit=1)
data = acquirer._ccavenue_decrypt_text(data.get('encResp'))
data = urls.url_decode(data)
return super(PaymentTransaction, self).form_feedback(data=data, acquirer_name=acquirer_name)
@api.model
def _ccavenue_form_get_tx_from_data(self, data):
""" Given a data dict coming from ccavenue, verify it and find the related
transaction record. """
reference = data.get('order_id')
if not reference:
raise ValidationError(_('CCAvenue: received data with missing reference (%s)') % (reference))
transaction = self.search([('reference', '=', reference)])
if not transaction or len(transaction) > 1:
error_msg = _('CCAvenue: received data for reference %s') % (reference)
if not transaction:
error_msg += _('; no order found')
else:
error_msg += _('; multiple order found')
raise ValidationError(error_msg)
return transaction
@api.model
def _ccavenue_form_get_invalid_parameters(self, data):
invalid_parameters = []
if self.acquirer_reference and data.get('order_id') != self.acquirer_reference:
invalid_parameters.append(
('Transaction Id', data.get('order_id'), self.acquirer_reference))
# check what is buyed
if float_compare(float(data.get('amount', '0.0')), self.amount, precision_digits=2) != 0:
invalid_parameters.append(('Amount', data.get('amount'), '%.2f' % self.amount))
return invalid_parameters
@api.model
def _ccavenue_form_validate(self, data):
if self.state == 'done':
_logger.warning('CCAvenue: trying to validate an already validated tx (ref %s)' % self.reference)
return True
status_code = data.get('order_status')
if status_code == "Success":
_logger.info('Validated CCAvenue payment for tx %s: set as done' % (self.reference))
self.write({'acquirer_reference': data.get('tracking_id')})
self._set_transaction_done()
return True
elif status_code == "Aborted":
_logger.info('Aborted CCAvenue payment for tx %s: set as cancel' % (self.reference))
self.write({'acquirer_reference': data.get('tracking_id'), 'state_message': data.get('status_message')})
self._set_transaction_cancel()
return False
else:
error = data.get('failure_message') or data.get('status_message')
_logger.info(error)
self.write({'acquirer_reference': data.get('tracking_id')})
self._set_transaction_error(error)
return False
Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="payment_acquirer_form_ccavenue" model="ir.ui.view">
<field name="name">payment.acquirer.form.inherit</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"]' position='after'>
<group attrs="{'invisible': [('provider', '!=', 'ccavenue')]}">
<field name="ccavenue_merchant_id"/>
<field name="ccavenue_access_code" password="True"/>
<field name="ccavenue_working_key" password="True"/>
</group>
</xpath>
</field>
</record>
</odoo>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="ccavenue_form">
<div>
<input type="hidden" name="data_set" t-att-data-action-url="tx_url" data-remove-me=""/>
<input type="hidden" name="encRequest" t-att-value="encRequest"/>
<input type="hidden" name="access_code" t-att-value="access_code"/>
</div>
</template>
</odoo>