Permalink
Please sign in to comment.
Browse files
[ADD] added new payment acquirer PayUlatam
This commit is related to Task ID 1826303
- Loading branch information...
Showing
with
444 additions
and 0 deletions.
- +22 −0 addons/payment/data/payment_acquirer_data.xml
- +5 −0 addons/payment/data/payment_icon_data.xml
- BIN addons/payment/static/img/codensa_easy_credit.png
- +6 −0 addons/payment_payulatam/__init__.py
- +16 −0 addons/payment_payulatam/__manifest__.py
- +4 −0 addons/payment_payulatam/controllers/__init__.py
- +22 −0 addons/payment_payulatam/controllers/main.py
- +17 −0 addons/payment_payulatam/data/payment_acquirer_data.xml
- +4 −0 addons/payment_payulatam/models/__init__.py
- +148 −0 addons/payment_payulatam/models/payment.py
- BIN addons/payment_payulatam/static/src/img/payulatam_icon.png
- +4 −0 addons/payment_payulatam/tests/__init__.py
- +148 −0 addons/payment_payulatam/tests/test_payulatam.py
- +20 −0 addons/payment_payulatam/views/payment_payulatam_templates.xml
- +28 −0 addons/payment_payulatam/views/payment_views.xml
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,16 @@ | |||
# -*- coding: utf-8 -*- | |||
# Part of Odoo. See LICENSE file for full copyright and licensing details. | |||
|
|||
{ | |||
'name': 'PayuLatam Payment Acquirer', | |||
'category': 'Payment Acquirer', | |||
'summary': 'Payment Acquirer: PayuLatam Implementation', | |||
'description': """Payulatam payment acquirer""", | |||
'depends': ['payment'], | |||
'data': [ | |||
'views/payment_views.xml', | |||
'views/payment_payulatam_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,22 @@ | |||
# -*- 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 PayuLatamController(http.Controller): | |||
|
|||
@http.route('/payment/payulatam/response', type='http', auth='public', csrf=False) | |||
def payulatam_response(self, **post): | |||
""" PayUlatam.""" | |||
_logger.info('PayUlatam: entering form_feedback with post response data %s', pprint.pformat(post)) | |||
if post: | |||
request.env['payment.transaction'].sudo().form_feedback(post, 'payulatam') | |||
return werkzeug.utils.redirect('/payment/process') |
@@ -0,0 +1,17 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<odoo> | |||
<data noupdate="1"> | |||
<record id="payment.payment_acquirer_payulatam" model="payment.acquirer"> | |||
<field name="provider">payulatam</field> | |||
<field name="company_id" ref="base.main_company"/> | |||
<field name="view_template_id" ref="payulatam_form"/> | |||
<field name="environment">test</field> | |||
<field name="pre_msg" type="html"> | |||
<p>You will be redirected to the PayUlatam website after clicking on the payment button.</p> | |||
</field> | |||
<field name="payulatam_account_id">dummy</field> | |||
<field name="payulatam_merchant_id">dummy</field> | |||
<field name="payulatam_api_key">dummy</field> | |||
</record> | |||
</data> | |||
</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,148 @@ | |||
# -*- coding: utf-8 -*- | |||
# Part of Odoo. See LICENSE file for full copyright and licensing details. | |||
|
|||
import logging | |||
import uuid | |||
|
|||
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.tools.float_utils import float_compare | |||
|
|||
|
|||
_logger = logging.getLogger(__name__) | |||
|
|||
|
|||
class PaymentAcquirerPayulatam(models.Model): | |||
_inherit = 'payment.acquirer' | |||
|
|||
provider = fields.Selection(selection_add=[('payulatam', 'PayUlatam')]) | |||
payulatam_merchant_id = fields.Char(string="PayUlatam Merchant ID", required_if_provider='payulatam', groups='base.group_user') | |||
payulatam_account_id = fields.Char(string="PayUlatam Account ID", required_if_provider='payulatam', groups='base.group_user') | |||
payulatam_api_key = fields.Char(string="PayUlatam API Key", required_if_provider='payulatam', groups='base.group_user') | |||
|
|||
def _get_payulatam_urls(self, environment): | |||
""" PayUlatam URLs""" | |||
if environment == 'prod': | |||
return 'https://checkout.payulatam.com/ppp-web-gateway-payu/' | |||
return 'https://sandbox.checkout.payulatam.com/ppp-web-gateway-payu/' | |||
|
|||
def _payulatam_generate_sign(self, inout, values): | |||
if inout not in ('in', 'out'): | |||
raise Exception("Type must be 'in' or 'out'") | |||
|
|||
if inout == 'in': | |||
data_string = ('~').join((self.payulatam_api_key, self.payulatam_merchant_id, values['referenceCode'], | |||
str(values['amount']), values['currency'])) | |||
else: | |||
data_string = ('~').join((self.payulatam_api_key, self.payulatam_merchant_id, values['referenceCode'], | |||
str(float(values.get('TX_VALUE'))), values['currency'], values.get('transactionState'))) | |||
return md5(data_string.encode('utf-8')).hexdigest() | |||
|
|||
@api.multi | |||
def payulatam_form_generate_values(self, values): | |||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') | |||
tx = self.env['payment.transaction'].search([('reference', '=', values.get('reference'))]) | |||
# payulatam will not allow any payment twise even if payment was failed last time. | |||
# so, replace reference code if payment is not done or pending. | |||
if tx.state not in ['done', 'pending']: | |||
tx.payulatam_reference_code = str(uuid.uuid4()) | |||
payulatam_values = dict( | |||
values, | |||
merchantId=self.payulatam_merchant_id, | |||
accountId=self.payulatam_account_id, | |||
description=values.get('reference'), | |||
referenceCode=tx.payulatam_reference_code, | |||
amount=values['amount'], | |||
tax='0', # This is the transaction VAT. If VAT zero is sent the system, 19% will be applied automatically. It can contain two decimals. Eg 19000.00. In the where you do not charge VAT, it should should be set as 0. | |||
taxReturnBase='0', | |||
currency=values['currency'].name, | |||
buyerEmail=values['partner_email'], | |||
responseUrl=urls.url_join(base_url, '/payment/payulatam/response'), | |||
) | |||
payulatam_values['signature'] = self._payulatam_generate_sign("in", payulatam_values) | |||
return payulatam_values | |||
|
|||
@api.multi | |||
def payulatam_get_form_action_url(self): | |||
self.ensure_one() | |||
return self._get_payulatam_urls(self.environment) | |||
|
|||
|
|||
class PaymentTransactionPayulatam(models.Model): | |||
_inherit = 'payment.transaction' | |||
|
|||
# payulatam will not allow any payment twice with same reference even if payment was failed last time | |||
# so need to change the reference number each time so instead of changing order reference added new field | |||
payulatam_reference_code = fields.Char('Reference Code', readonly=True) | |||
|
|||
@api.model | |||
def _payulatam_form_get_tx_from_data(self, data): | |||
""" Given a data dict coming from payulatam, verify it and find the related | |||
transaction record. """ | |||
reference, txnid, sign = data.get('referenceCode'), data.get('transactionId'), data.get('signature') | |||
if not reference or not txnid or not sign: | |||
raise ValidationError(_('PayUlatam: received data with missing reference (%s) or transaction id (%s) or sign (%s)') % (reference, txnid, sign)) | |||
|
|||
transaction = self.search([('payulatam_reference_code', '=', reference)]) | |||
|
|||
if not transaction: | |||
error_msg = (_('PayUlatam: received data for reference %s; no order found') % (reference)) | |||
raise ValidationError(error_msg) | |||
elif len(transaction) > 1: | |||
error_msg = (_('PayUlatam: received data for reference %s; multiple orders found') % (reference)) | |||
raise ValidationError(error_msg) | |||
|
|||
# verify shasign | |||
sign_check = transaction.acquirer_id._payulatam_generate_sign('out', data) | |||
if sign_check.upper() != sign.upper(): | |||
raise ValidationError(('PayUlatam: invalid sign, received %s, computed %s, for data %s') % (sign, sign_check, data)) | |||
return transaction | |||
|
|||
@api.multi | |||
def _payulatam_form_get_invalid_parameters(self, data): | |||
invalid_parameters = [] | |||
|
|||
if self.payulatam_reference_code and data.get('referenceCode') != self.payulatam_reference_code: | |||
invalid_parameters.append(('Reference code', data.get('referenceCode'), self.payulatam_reference_code)) | |||
if float_compare(float(data.get('TX_VALUE', '0.0')), self.amount, 2) != 0: | |||
invalid_parameters.append(('Amount', data.get('TX_VALUE'), '%.2f' % self.amount)) | |||
if data.get('merchantId') != self.acquirer_id.payulatam_merchant_id: | |||
invalid_parameters.append(('Merchant Id', data.get('merchantId'), self.acquirer_id.payulatam_merchant_id)) | |||
return invalid_parameters | |||
|
|||
@api.multi | |||
def _payulatam_form_validate(self, data): | |||
self.ensure_one() | |||
|
|||
status = data.get('lapTransactionState') or data.find('transactionResponse').find('state').text | |||
res = { | |||
'acquirer_reference': data.get('transactionId') or data.find('transactionResponse').find('transactionId').text, | |||
'state_message': data.get('message') or "" | |||
} | |||
|
|||
if status == 'APPROVED': | |||
_logger.info('Validated PayUlatam payment for tx %s: set as done' % (self.reference)) | |||
res.update(state='done', date=fields.Datetime.now()) | |||
self._set_transaction_done() | |||
self.write(res) | |||
self.execute_callback() | |||
return True | |||
elif status == 'PENDING': | |||
_logger.info('Received notification for PayUlatam payment %s: set as pending' % (self.reference)) | |||
res.update(state='pending') | |||
self._set_transaction_pending() | |||
return self.write(res) | |||
elif status in ['EXPIRED', 'DECLINED']: | |||
_logger.info('Received notification for PayUlatam payment %s: set as Cancel' % (self.reference)) | |||
res.update(state='cancel') | |||
self._set_transaction_cancel() | |||
return self.write(res) | |||
else: | |||
error = 'Received unrecognized status for PayUlatam payment %s: %s, set as error' % (self.reference, status) | |||
_logger.info(error) | |||
res.update(state='cancel', state_message=error) | |||
self._set_transaction_cancel() | |||
return self.write(res) |
Binary file not shown.
@@ -0,0 +1,4 @@ | |||
# -*- coding: utf-8 -*- | |||
# Part of Odoo. See LICENSE file for full copyright and licensing details. | |||
|
|||
from . import test_payulatam |

Oops, something went wrong.
0 comments on commit
67add26