Skip to content
Permalink
Browse files

[ADD] payment_razorpay: added new module for payment integration

Razorpay is a payments solution in India which allows businesses to accept, process and disburse payments.

Related Task ID: 1904990

Co-authored-by: Rohan Patel <rop@odoo.com>
  • Loading branch information...
2 people authored and mba-odoo committed Nov 15, 2018
1 parent 4155bc5 commit a636f8bb84ff9c108c9293e8b4269737bc79b40a
@@ -243,4 +243,28 @@
ref("payment.payment_icon_cc_visa")])]'/>
</record>

<record id="payment_acquirer_razorpay" model="payment.acquirer">
<field name="name">Razorpay</field>
<field name="image" type="base64" file="payment_razorpay/static/description/icon.png"/>
<field name="view_template_id" ref="default_acquirer_button"/>
<field name="module_id" ref="base.module_payment_razorpay"/>
<field name="pending_msg">&lt;i&gt;Pending&lt;/i&gt;... The order will be validated after the payment.</field>
<field name="description" type="html">
<p>
Razorpay is a payments solution in India which allows businesses to accept, process and disburse payments.
</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://razorpay.com/payment-gateway/#methods -->
<field name="payment_icon_ids" eval='[(6, 0, [ref("payment.payment_icon_cc_maestro"),
ref("payment.payment_icon_cc_mastercard"),
ref("payment.payment_icon_cc_rupay"),
ref("payment.payment_icon_cc_diners_club_intl"),
ref("payment.payment_icon_cc_american_express"),
ref("payment.payment_icon_cc_visa")])]'/>
</record>

</odoo>
@@ -49,4 +49,9 @@
<field name="name">Bancontact</field>
<field name="image" type="base64" file="payment/static/img/bancontact.png"/>
</record>

<record id="payment_icon_cc_rupay" model="payment.icon">
<field name="name">Rupay</field>
<field name="image" type="base64" file="payment/static/img/rupay.png"/>
</record>
</odoo>
Binary file not shown.
@@ -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': 'Razorpay Payment Acquirer',
'category': 'Accounting',
'summary': 'Payment Acquirer: Razorpay Implementation',
'version': '1.0',
'description': """Razorpay Payment Acquirer""",
'depends': ['payment'],
'data': [
'views/payment_views.xml',
'views/payment_razorpay_templates.xml',
'data/payment_acquirer_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,23 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import logging
import pprint

from odoo import http
from odoo.http import request

_logger = logging.getLogger(__name__)


class RazorpayController(http.Controller):

@http.route(['/payment/razorpay/capture'], type='http', auth='public', csrf=False)
def razorpay_capture(self, **kwargs):
payment_id = kwargs.get('payment_id')
if payment_id:
response = request.env['payment.transaction'].sudo()._create_razorpay_capture(kwargs)
if response.get('id'):
_logger.info('Razorpay: entering form_feedback with post data %s', pprint.pformat(response))
request.env['payment.transaction'].sudo().form_feedback(response, 'razorpay')
return '/payment/process'
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="payment.payment_acquirer_razorpay" model="payment.acquirer">
<field name="provider">razorpay</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="razorpay_form"/>
<field name="environment">test</field>
<field name="pre_msg"><![CDATA[
<p>You will be prompted with Razorpay Payment page for payment information.</p>]]>
</field>
<field name="razorpay_key_id">dummy</field>
<field name="razorpay_key_secret">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
@@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import logging
import requests

from odoo import api, fields, models, _
from odoo.tools.float_utils import float_compare, float_repr, float_round
from odoo.addons.payment.models.payment_acquirer import ValidationError

_logger = logging.getLogger(__name__)


class PaymentAcquirer(models.Model):
_inherit = 'payment.acquirer'

provider = fields.Selection(selection_add=[('razorpay', 'Razorpay')])
razorpay_key_id = fields.Char(string='Key ID', required_if_provider='razorpay', groups='base.group_user')
razorpay_key_secret = fields.Char(string='Key Secret', required_if_provider='razorpay', groups='base.group_user')

@api.multi
def razorpay_form_generate_values(self, values):
self.ensure_one()
currency = self.env['res.currency'].sudo().browse(values['currency_id'])
if currency != self.env.ref('base.INR'):
raise ValidationError(_('Currency not supported by Razorpay'))
values.update({
'key': self.razorpay_key_id,
'amount': float_repr(float_round(values.get('amount'), 2) * 100, 0),
'name': values.get('partner_name'),
'contact': values.get('partner_phone'),
'email': values.get('partner_email'),
'order_id': values.get('reference'),
})
return values


class PaymentTransaction(models.Model):
_inherit = 'payment.transaction'

@api.model
def _create_razorpay_capture(self, data):
payment_acquirer = self.env['payment.acquirer'].search([('provider', '=', 'razorpay')], limit=1)
payment_url = "https://%s:%s@api.razorpay.com/v1/payments/%s" % (payment_acquirer.razorpay_key_id, payment_acquirer.razorpay_key_secret, data.get('payment_id'))
try:
payment_response = requests.get(payment_url)
payment_response = payment_response.json()
except Exception as e:
raise e
reference = payment_response.get('notes', {}).get('order_id', False)
if reference:
transaction = self.search([('reference', '=', reference)])
capture_url = "https://%s:%s@api.razorpay.com/v1/payments/%s/capture" % (payment_acquirer.razorpay_key_id, payment_acquirer.razorpay_key_secret, data.get('payment_id'))
charge_data = {'amount': int(transaction.amount * 100)}
try:
payment_response = requests.post(capture_url, data=charge_data)
payment_response = payment_response.json()
except Exception as e:
raise e
return payment_response

@api.model
def _razorpay_form_get_tx_from_data(self, data):
reference, txn_id = data.get('notes', {}).get('order_id'), data.get('id')
if not reference or not txn_id:
error_msg = _('Razorpay: received data with missing reference (%s) or txn_id (%s)') % (reference, txn_id)
_logger.info(error_msg)
raise ValidationError(error_msg)

txs = self.env['payment.transaction'].search([('reference', '=', reference)])
if not txs or len(txs) > 1:
error_msg = 'Razorpay: received data for reference %s' % (reference)
if not txs:
error_msg += '; no order found'
else:
error_msg += '; multiple order found'
_logger.info(error_msg)
raise ValidationError(error_msg)
return txs[0]

@api.multi
def _razorpay_form_get_invalid_parameters(self, data):
invalid_parameters = []
if float_compare(float(data.get('amount', '0.0')) / 100, self.amount, precision_digits=2) != 0:
invalid_parameters.append(('amount', data.get('amount'), '%.2f' % self.amount))
return invalid_parameters

@api.multi
def _razorpay_form_validate(self, data):
status = data.get('status')
if status == 'captured':
_logger.info('Validated Razorpay payment for tx %s: set as done' % (self.reference))
self.write({'acquirer_reference': data.get('id')})
self._set_transaction_done()
return True
if status == 'authorized':
_logger.info('Validated Razorpay payment for tx %s: set as authorized' % (self.reference))
self.write({'acquirer_reference': data.get('id')})
self._set_transaction_authorized()
return True
else:
error = 'Received unrecognized status for Razorpay payment %s: %s, set as error' % (self.reference, status)
_logger.info(error)
self.write({'acquirer_reference': data.get('id'), 'state_message': data.get('error')})
self._set_transaction_cancel()
return False
Binary file not shown.
@@ -0,0 +1,83 @@
odoo.define('payment_razorpay.razorpay', function(require) {
"use strict";

var ajax = require('web.ajax');
var core = require('web.core');
var _t = core._t;
var qweb = core.qweb;
ajax.loadXML('/payment_razorpay/static/src/xml/payment_razorpay_templates.xml', qweb);

require('web.dom_ready');
if (!$('.o_payment_form').length) {
return $.Deferred().reject("DOM doesn't contain '.o_payment_form'");
}

var observer = new MutationObserver(function(mutations, observer) {
for(var i=0; i<mutations.length; ++i) {
for(var j=0; j<mutations[i].addedNodes.length; ++j) {
if(mutations[i].addedNodes[j].tagName.toLowerCase() === "form" && mutations[i].addedNodes[j].getAttribute('provider') == 'razorpay') {
display_razorpay_form($(mutations[i].addedNodes[j]));
}
}
}
});

function razorpay_show_error(msg) {
var wizard = $(qweb.render('razorpay.error', {'msg': msg || _t('Payment error')}));
wizard.appendTo($('body')).modal({'keyboard': true});
};

function razorpay_handler(resp) {
if (resp.razorpay_payment_id) {
$.post('/payment/razorpay/capture',{
payment_id: resp.razorpay_payment_id,
}).done(function (data) {
window.location.href = data;
}).fail(function (data) {
razorpay_show_error(data && data.data && data.data.message);
});
}
};

function display_razorpay_form(provider_form) {
// Open Checkout with further options
var payment_form = $('.o_payment_form');
if(!payment_form.find('i').length)
payment_form.append('<i class="fa fa-spinner fa-spin"/>');
payment_form.attr('disabled','disabled');

var get_input_value = function (name) {
return provider_form.find('input[name="' + name + '"]').val();
}
var primaryColor = getComputedStyle(document.body).getPropertyValue('--primary');
var options = {
"key": get_input_value('key'),
"amount": get_input_value('amount'),
"name": get_input_value('merchant_name'),
"description": get_input_value('description'),
"handler": razorpay_handler,
"modal": {
"ondismiss": function() { window.location.reload(); },
'backdropclose': function() { window.location.reload(); }
},
'prefill': {
'name': get_input_value('name'),
'contact': get_input_value('contact'),
'email': get_input_value('email')
},
'notes': {
'order_id': get_input_value('order_id'),
},
"theme": {
"color": primaryColor
},
}
var rzp1 = new Razorpay(options);
rzp1.open();
};

$.getScript("https://checkout.razorpay.com/v1/checkout.js", function (data, textStatus, jqxhr) {
observer.observe(document.body, {childList: true});
display_razorpay_form($('form[provider="razorpay"]'));
});
});
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="razorpay.error">
<div role="dialog" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<header class="modal-header">
<h4 class="modal-title">Error</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">&amp;times;</button>
</header>
<main class="modal-body">
<t t-esc="msg"></t>
</main>
<footer class="modal-footer">
<a role="button" href="#" class="btn btn-link btn-sm" data-dismiss="modal">Close</a>
</footer>
</div>
</div>
</div>
</t>
</templates>
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import test_razorpay
Oops, something went wrong.

0 comments on commit a636f8b

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.