Files
odoo_source/addons/pos_stripe/models/pos_payment_method.py
T
lejeune quentin f4a95639d4 [FIX] pos_stripe: Improves error handling
Currently if an error occurs on the server side it is not
reported to the POS interface and therefore to the user.

With this commit all responses to requests to the Stripe
server are managed in the POS so that the cashier is correctly
informed of any errors that may occur

closes odoo/odoo#104870

X-original-commit: a9c794ed31cfe1527f0e480729212859338bca60
Signed-off-by: Trinh Jacky (trj) <trj@odoo.com>
2022-11-03 16:43:36 +01:00

125 lines
5.2 KiB
Python

# coding: utf-8
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import requests
import werkzeug
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError, UserError, AccessError
_logger = logging.getLogger(__name__)
TIMEOUT = 10
class PosPaymentMethod(models.Model):
_inherit = 'pos.payment.method'
def _get_payment_terminal_selection(self):
return super()._get_payment_terminal_selection() + [('stripe', 'Stripe')]
# Stripe
stripe_serial_number = fields.Char(help='[Serial number of the stripe terminal], for example: WSC513105011295', copy=False)
@api.constrains('stripe_serial_number')
def _check_stripe_serial_number(self):
for payment_method in self:
if not payment_method.stripe_serial_number:
continue
existing_payment_method = self.search([('id', '!=', payment_method.id),
('stripe_serial_number', '=', payment_method.stripe_serial_number)],
limit=1)
if existing_payment_method:
raise ValidationError(_('Terminal %s is already used on payment method %s.',\
payment_method.stripe_serial_number, existing_payment_method.display_name))
@api.model
def _get_stripe_secret_key(self):
stripe_secret_key = self.env['payment.provider'].search([('code', '=', 'stripe')], limit=1).stripe_secret_key
if not stripe_secret_key:
raise ValidationError(_('Complete the Stripe onboarding.'))
return stripe_secret_key
@api.model
def stripe_connection_token(self):
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Do not have access to fetch token from Stripe"))
endpoint = 'https://api.stripe.com/v1/terminal/connection_tokens'
try:
resp = requests.post(endpoint, auth=(self.sudo()._get_stripe_secret_key(), ''), timeout=TIMEOUT)
except requests.exceptions.RequestException:
_logger.exception("Failed to call stripe_connection_token endpoint")
raise UserError(_("There are some issues between us and Stripe, try again later."))
return resp.json()
def _stripe_calculate_amount(self, amount):
currency = self.journal_id.currency_id or self.company_id.currency_id
return int(amount/currency.rounding)
def stripe_payment_intent(self, amount):
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Do not have access to fetch token from Stripe"))
# For Terminal payments, the 'payment_method_types' parameter must include
# 'card_present' and the 'capture_method' must be set to 'manual'
endpoint = 'https://api.stripe.com/v1/payment_intents'
currency = self.journal_id.currency_id or self.company_id.currency_id
try:
data = werkzeug.urls.url_encode({
"currency": currency.name,
"amount": self._stripe_calculate_amount(amount),
"payment_method_types[]": "card_present",
"capture_method": "manual",
})
resp = requests.post(endpoint, data=data, auth=(self.sudo()._get_stripe_secret_key(), ''), timeout=TIMEOUT)
except requests.exceptions.RequestException:
_logger.exception("Failed to call stripe_payment_intent endpoint")
raise UserError(_("There are some issues between us and Stripe, try again later."))
return resp.json()
@api.model
def stripe_capture_payment(self, paymentIntentId, amount=None):
"""Captures the payment identified by paymentIntentId.
:param paymentIntentId: the id of the payment to capture
:param amount: without this parameter the entire authorized
amount is captured. Specifying a larger amount allows
overcapturing to support tips.
"""
if not self.env.user.has_group('point_of_sale.group_pos_user'):
raise AccessError(_("Do not have access to fetch token from Stripe"))
endpoint = ('https://api.stripe.com/v1/payment_intents/%s/capture') % \
(werkzeug.urls.url_quote(paymentIntentId))
data = None
if amount is not None:
data = {
"amount_to_capture": self._stripe_calculate_amount(amount),
}
try:
resp = requests.post(endpoint, data=data, auth=(self.sudo()._get_stripe_secret_key(), ''), timeout=TIMEOUT)
except requests.exceptions.RequestException:
_logger.exception("Failed to call stripe_capture_payment endpoint")
raise UserError(_("There are some issues between us and Stripe, try again later."))
return resp.json()
def action_stripe_key(self):
res_id = self.env['payment.provider'].search([('code', '=', 'stripe')], limit=1).id
# Redirect
return {
'name': _('Stripe'),
'res_model': 'payment.provider',
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_id': res_id,
}