[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:
committed by
Yannick Tivisse
co-authored by
Rohan Patel
parent
7a1c8d54b4
commit
f735951d5c
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Executable
+163
@@ -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>
|
||||
Reference in New Issue
Block a user