1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import hashlib
import hmac
import logging
import pprint
import requests
from werkzeug.urls import url_join
from odoo import _, fields, models
from odoo.exceptions import ValidationError
from odoo.addons.payment_razorpay import const
_logger = logging.getLogger(__name__)
class PaymentProvider(models.Model):
_inherit = 'payment.provider'
code = fields.Selection(
selection_add=[('razorpay', "Razorpay")], ondelete={'razorpay': 'set default'}
)
razorpay_key_id = fields.Char(
string="Razorpay Key Id",
help="The key solely used to identify the account with Razorpay.",
required_if_provider='razorpay',
)
razorpay_key_secret = fields.Char(
string="Razorpay Key Secret",
required_if_provider='razorpay',
groups='base.group_system',
)
razorpay_webhook_secret = fields.Char(
string="Razorpay Webhook Secret",
required_if_provider='razorpay',
groups='base.group_system',
)
#=== COMPUTE METHODS ===#
def _compute_feature_support_fields(self):
""" Override of `payment` to enable additional features. """
super()._compute_feature_support_fields()
self.filtered(lambda p: p.code == 'razorpay').update({
'support_manual_capture': 'full_only',
'support_refund': 'partial',
'support_tokenization': True,
})
# === BUSINESS METHODS ===#
def _get_supported_currencies(self):
""" Override of `payment` to return the supported currencies. """
supported_currencies = super()._get_supported_currencies()
if self.code == 'razorpay':
supported_currencies = supported_currencies.filtered(
lambda c: c.name in const.SUPPORTED_CURRENCIES
)
return supported_currencies
def _razorpay_make_request(self, endpoint, payload=None, method='POST'):
""" Make a request to Razorpay API at the specified endpoint.
Note: self.ensure_one()
:param str endpoint: The endpoint to be reached by the request.
:param dict payload: The payload of the request.
:param str method: The HTTP method of the request.
:return The JSON-formatted content of the response.
:rtype: dict
:raise ValidationError: If an HTTP error occurs.
"""
self.ensure_one()
url = url_join('https://api.razorpay.com/v1/', endpoint)
auth = (self.razorpay_key_id, self.razorpay_key_secret)
try:
if method == 'GET':
response = requests.get(url, params=payload, auth=auth, timeout=10)
else:
response = requests.post(url, json=payload, auth=auth, timeout=10)
try:
response.raise_for_status()
except requests.exceptions.HTTPError:
_logger.exception(
"Invalid API request at %s with data:\n%s", url, pprint.pformat(payload),
)
raise ValidationError("Razorpay: " + _(
"Razorpay gave us the following information: '%s'",
response.json().get('error', {}).get('description')
))
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
_logger.exception("Unable to reach endpoint at %s", url)
raise ValidationError(
"Razorpay: " + _("Could not establish the connection to the API.")
)
return response.json()
def _razorpay_calculate_signature(self, data):
""" Compute the signature for the request's data according to the Razorpay documentation.
See https://razorpay.com/docs/webhooks/validate-test#validate-webhooks.
:param bytes data: The data to sign.
:return: The calculated signature.
:rtype: str
"""
secret = self.razorpay_webhook_secret
return hmac.new(secret.encode(), msg=data, digestmod=hashlib.sha256).hexdigest()
def _get_default_payment_method_codes(self):
""" Override of `payment` to return the default payment method codes. """
default_codes = super()._get_default_payment_method_codes()
if self.code != 'razorpay':
return default_codes
return const.DEFAULT_PAYMENT_METHOD_CODES
def _get_validation_amount(self):
""" Override of `payment` to return the amount for Razorpay validation operations.
:return: The validation amount.
:rtype: float
"""
res = super()._get_validation_amount()
if self.code != 'razorpay':
return res
return 1.0
|