Skip to content

Commit

Permalink
[WIP0] backport 15 module changes.
Browse files Browse the repository at this point in the history
[FIX] Reordeno para poder comparar con el anterior

[FIX] CLEAN controller code
  • Loading branch information
filoquin committed Aug 25, 2022
1 parent 6b671d0 commit c9dc082
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 30 deletions.
41 changes: 25 additions & 16 deletions payment_mercadopago/controllers/main.py
Expand Up @@ -17,13 +17,6 @@

class MercadoPagoController(http.Controller):

# MercadoPago redirect controller
_success_url = '/payment/mercadopago/success/'
_pending_url = '/payment/mercadopago/pending/'
_failure_url = '/payment/mercadopago/failure/'
_create_preference_url = '/payment/mercadopago/create_preference'
_notify_url = '/payment/mercadopago/notify?source_news=webhooks'

@http.route(['/payment/mercadopago/create_preference'], type='http', auth="none", csrf=False)
def mercadopago_create_preference(self, **post):
# TODO podriamos pasar cada elemento por separado para no necesitar
Expand Down Expand Up @@ -93,21 +86,29 @@ def mercadopago_s2s_otp(self, **kwargs):
request.session.update({'cvv_token': cvv_token})
return {'result': True}

@http.route(['/payment/mercadopago/notification'], type='json', methods=['POST'], auth='public')
def mercadopago_s2s_notification(self, **kwargs):
@http.route(['/payment/mercadopago/notification',
'/payment/mercadopago/notification/<int:acquirer_id>'],
type='json', methods=['POST'], auth='public')
def mercadopago_s2s_notification(self, acquirer_id=False, **kwargs):
querys = parse.urlsplit(request.httprequest.url).query
params = dict(parse.parse_qsl(querys))
if (params and params.get('payment_type') == 'payment' and params.get('data.id')):
acquirer = request.env["payment.acquirer"].search([('provider', '=', 'mercadopago')])
leaf = [('provider', '=', 'mercadopago')]
if acquirer_id:
leaf += [('id', '=', acquirer_id)]
acquirer = request.env["payment.acquirer"].search(leaf)
payment_id = params['data.id']
tx = request.env['payment.transaction'].sudo().search([('acquirer_reference', '=', payment_id)])
tx = request.env['payment.transaction'].sudo().search(
[('acquirer_reference', '=', payment_id)])
MP = MercadoPagoAPI(acquirer)
tree = MP.get_payment(payment_id)
return tx._mercadopago_s2s_validate_tree(tree)
return False

@http.route('/payment/mercadopago/notify', type='json', auth='none')
def mercadopago_notification(self):
@http.route(['/payment/mercadopago/notify',
'/payment/mercadopago/notify/<int:acquirer_id>'],
type='json', auth='none')
def mercadopago_notification(self,acquirer_id=False):
""" Process the data sent by MercadoPago to the webhook based on the event code.
:return: Status 200 to acknowledge the notification
:rtype: Response
Expand All @@ -120,14 +121,22 @@ def mercadopago_notification(self):
payment_id = data['data']['id']

# Get payment data from MercadoPago
acquirer = request.env["payment.acquirer"].sudo().search([('provider', '=', 'mercadopago')], limit=1)
leaf = [('provider', '=', 'mercadopago')]
if acquirer_id:
leaf += [('id', '=', acquirer_id)]

acquirer = request.env["payment.acquirer"].sudo().search(leaf, limit=1)
MP = MercadoPagoAPI(acquirer)
tree = MP.get_payment(payment_id)
tx = request.env['payment.transaction'].sudo().search([('acquirer_reference', '=', payment_id)])
tx = request.env['payment.transaction'].sudo().search(
[('acquirer_reference', '=', payment_id),
('acquirer_id', '=', acquirer.id)]
)
tx._mercadopago_s2s_validate_tree(tree)

except Exception: # Acknowledge the notification to avoid getting spammed
_logger.exception("Unable to handle the notification data; skipping to acknowledge")
_logger.exception(
"Unable to handle the notification data; skipping to acknowledge")

# Acknowledge the notification
return Response('success', status=200)
112 changes: 108 additions & 4 deletions payment_mercadopago/models/mercadopago_request.py
Expand Up @@ -5,6 +5,10 @@
from odoo.exceptions import UserError
from werkzeug import urls
from babel.dates import format_datetime
import requests

MP_URL = "https://api.mercadopago.com/"


_logger = logging.getLogger(__name__)

Expand All @@ -23,6 +27,7 @@ def __init__(self, acquirer):
request_options = RequestOptions(acquirer.mercadopago_access_token, platform_id="BVH38T5N7QOK0PPDGC2G")
self.mp = mercadopago.SDK(acquirer.mercadopago_access_token, request_options=request_options)
self.sandbox = not acquirer.state == "enabled"
self.mercadopago_access_token = acquirer.mercadopago_access_token

def check_response(self, resp):
if resp['status'] in [200, 201]:
Expand All @@ -43,6 +48,46 @@ def check_response(self, resp):
'err_msg': "Server Error"
}

def check_api_response(self, resp):
resp_json = resp.json()
if resp.ok:
return resp_json
elif resp_json.get('cause'):
return {
'err_code': resp_json['cause'][0].get('code'),
'err_msg': resp_json['cause'][0].get('description')
}
elif resp_json.get('error'):
return {
'err_code': resp_json.get('status', 0),
'err_msg': resp_json.get('error')
}
else:
return {
'err_code': 500,
'err_msg': "Server Error"
}

def unlink_card_token(self, customer_id, card_id):

api_url = MP_URL + "v1/customers/%s/cards/%s" % (customer_id, card_id)
headers = {"Authorization": "Bearer %s" % self.mercadopago_access_token}
response = requests.delete(api_url, headers=headers)


#create Test User
def create_test_user(self):
api_url = MP_URL + "users/test_user"
headers = {"Authorization": "Bearer %s" % self.mercadopago_access_token}
request_data = {"site_id":"MLA"}
response = requests.post(api_url, headers=headers, json=request_data)
resp = self.check_api_response(response)

if resp.get('err_code'):
raise UserError(_("MercadoPago Error:\nCode: %s\nMessage: %s" % (resp.get('err_code'), resp.get('err_msg'))))
else:
return resp

# Preference
def create_preference(self, preference):
resp = self.mp.preference().create(preference)
Expand Down Expand Up @@ -118,6 +163,8 @@ def payment(self, tx, amount, capture=True, cvv_token=None):
"""
MercadoPago payment
"""
capture, validation_capture_method = self.validation_capture_method(tx)

values = {
"token": cvv_token or self.get_card_token(tx.payment_token_id.token),
"installments": tx.payment_token_id.installments,
Expand All @@ -132,9 +179,9 @@ def payment(self, tx, amount, capture=True, cvv_token=None):
},
"additional_info": {
"items": [{
"id": 'Venta de ecommerce',
"title": 'Venta de ecommerce',
"description": 'Venta de ecommerce',
"id": '001',
"title": _('Venta de ecommerce'),
"description": _('Venta de ecommerce'),
"category_id": 'others',
"quantity": 1,
"unit_price": amount,
Expand All @@ -151,9 +198,15 @@ def payment(self, tx, amount, capture=True, cvv_token=None):
"registration_date": format_datetime(tx.partner_id.create_date),
},
},
"notification_url": urls.url_join(tx.acquirer_id.get_base_url(), '/payment/mercadopago/notify?source_news=webhooks'),
"notification_url": urls.url_join(tx.acquirer_id.get_base_url(), '/payment/mercadopago/notify/%s?source_news=webhooks' % tx.acquirer_id.id),
"capture": capture
}
if hasattr(tx.partner_id, 'l10n_latam_identification_type_id'):
values['payer']['identification'] = {
"number": tx.partner_id.vat,
"type": tx.partner_id.l10n_latam_identification_type_id.name,
}

if tx.payment_token_id.issuer:
values.update(issuer_id=tx.payment_token_id.issuer)

Expand All @@ -169,6 +222,9 @@ def payment(self, tx, amount, capture=True, cvv_token=None):
if resp.get('err_code'):
raise UserError(_("MercadoPago Error:\nCode: %s\nMessage: %s" % (resp.get('err_code'), resp.get('err_msg'))))
else:
if validation_capture_method == 'refund_payment':
_logger.info(_('Refund validation payment id: %s ' % resp['id']))
self.payment_refund(resp['id'])
return resp

def payment_cancel(self, payment_id):
Expand All @@ -194,3 +250,51 @@ def get_payment(self, payment_id):
raise UserError(_("MercadoPago Error:\nCode: %s\nMessage: %s" % (resp.get('err_code'), resp.get('err_msg'))))
else:
return resp

def payment_refund(self, payment_id, amount = 0):
"""
MercadoPago refund payment
"""
if amount:
values = {
"amount": amount
}
resp = self.mp.refund().create(payment_id, values)
else:
resp = self.mp.refund().create(payment_id)

resp = self.check_response(resp)
if resp.get('err_code'):
raise UserError(_("MercadoPago Error:\nCode: %s\nMessage: %s" % (resp.get('err_code'), resp.get('err_msg'))))
else:
return resp

def payment_can_deferred_capture(self, payment_method_id):

resp = self.mp.payment_methods().list_all()
resp = self.check_response(resp)
if type(resp) is dict and resp.get('err_code'):
_logger.error(_("MercadoPago Error:\nCode: %s\nMessage: %s" % (resp.get('err_code'), resp.get('err_msg'))))
return False
payment = [d for d in resp if d['id'] == payment_method_id and d['status' ]=='active']
if len(payment):
return payment[0]['deferred_capture'] == 'supported'
return False

def validation_capture_method(self,tx):
"""
Validation capture method
If transaction type is a validation,
Return a strategy to no capture the payment or refund it after validation.
Return two values
- Capture: if the transaction should be captured
- Method: If a refund should be made.
"""
if tx.type != 'validation':
return True , None

payment_method_id = tx.payment_token_id.acquirer_ref
if self.payment_can_deferred_capture(payment_method_id):
return False , 'deferred_capture'
else:
return True , 'refund_payment'
44 changes: 35 additions & 9 deletions payment_mercadopago/models/payment.py
Expand Up @@ -5,6 +5,7 @@
import werkzeug

from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.http import request
from ..controllers.main import MercadoPagoController
Expand Down Expand Up @@ -250,6 +251,7 @@ def _mercadopago_form_validate(self, data):
self._set_transaction_error(error)
return True


# --------------------------------------------------
# SERVER2SERVER RELATED METHODS
# --------------------------------------------------
Expand Down Expand Up @@ -280,7 +282,7 @@ def _mercadopago_s2s_validate_tree(self, tree):
if status_code in ["approved", "authorized"]:
init_state = self.state
self.write({
'acquirer_reference': tree.get('id'),
'acquirer_reference': str(tree.get('id')),
'date': fields.Datetime.now(),
})
self._set_transaction_done()
Expand Down Expand Up @@ -335,30 +337,44 @@ def mercadopago_s2s_do_refund(self, **data):
Free the captured amount
'''
MP = MercadoPagoAPI(self.acquirer_id)
MP.payment_cancel(self.acquirer_reference)
MP.payment_cancel(int(self.acquirer_reference))

def get_tx_info_from_mercadopago(self):
self.ensure_one()
if self.acquirer_id.provider != 'mercadopago':
raise UserError(_('acquirer not is Mercadopago'))
MP = MercadoPagoAPI(self.acquirer_id)
payment = MP.get_payment(int(self.acquirer_reference))
raise UserError("%s" % payment)


class PaymentToken(models.Model):
_inherit = 'payment.token'

email = fields.Char('Email', readonly=True)
issuer = fields.Char('Issuer', readonly=True)
customer_id = fields.Char('MercadoPago Customer', readonly=True)
save_token = fields.Boolean('Save Token', default=True, readonly=True)
token = fields.Char('Token', readonly=True)
installments = fields.Integer('Installments', readonly=True)

@api.model
def mercadopago_create(self, values):
if values.get('token') and values.get('payment_method_id'):

#mercadopago_API = MercadoPagoAPI(self.acquirer_id)
#customer_id = mercadopago_API.get_customer_profile(self.partner_id.email)

# create the token
return {
'name': "MercadoPago card token",
'acquirer_ref': values.get('payment_method_id'),
'email': values.get('email'),
'issuer': values.get('issuer'),
'installments': int(values.get('installments', 1)),
'save_token': values.get('save_token') == "on",
'token': values.get('token'),
'name': "MercadoPago card token",
'acquirer_ref': values.get('payment_method_id'),
'email': values.get('email'),
#'customer_id': customer_id,
'issuer': values.get('issuer'),
'installments': int(values.get('installments', 1)),
'save_token': values.get('save_token') == "on",
'token': values.get('token'),
}
else:
raise ValidationError(_('The Token creation in MercadoPago failed.'))
Expand All @@ -382,3 +398,13 @@ def mercadopago_update(self, acquirer):
def hide_email(self, email):
username = email.split("@")[0]
return(email.replace(username, username[:3] + "***"))

def unlink(self):
for token in self:
if token.acquirer_id.provider == 'mercadopago':
mercado_pago = MercadoPagoAPI(token.acquirer_id)
customer_id = mercado_pago.get_customer_profile(self.partner_id.email)

mercado_pago.unlink_card_token(customer_id, token.token)

return super().unlink()
2 changes: 1 addition & 1 deletion payment_mercadopago/static/src/js/payment_form.js
Expand Up @@ -6,7 +6,7 @@ odoo.define('payment_mercadopago.payment_form', function(require) {
var PaymentForm = require('payment.payment_form');
var _t = core._t;
var error_messages = {
'205': 'El número de la tarjeta de no puede ser vacío.',
'205': 'El número de la tarjeta de no puede estar vacío.',
'208': 'La fecha de vencimiento no puede esta vacío.',
'209': 'La fecha de vencimiento no puede esta vacío.',
'212': 'El tipo de documento no puede ser vacío.',
Expand Down
9 changes: 9 additions & 0 deletions payment_mercadopago/views/payment_views.xml
Expand Up @@ -44,4 +44,13 @@
</xpath>
</field>
</record>
<record id="action_info_from_mercadopago" model="ir.actions.server">
<field name="name">Check in Mercadopago</field>
<field name="model_id" ref="payment.model_payment_transaction"/>
<field name="state">code</field>
<field name="code">action = record.get_tx_info_from_mercadopago()</field>
<field name="binding_model_id" ref="payment.model_payment_transaction"/>
<field name="binding_type">action</field>
</record>

</odoo>

0 comments on commit c9dc082

Please sign in to comment.