137 lines
5.3 KiB
Python
137 lines
5.3 KiB
Python
import pprint
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
from odoo.addons.payment.logging import get_payment_logger
|
|
|
|
|
|
_logger = get_payment_logger(__name__)
|
|
|
|
|
|
class PaymentTransaction(models.Model):
|
|
_inherit = 'payment.transaction'
|
|
|
|
btcpay_invoiceId = fields.Char("Invoice Id")
|
|
btcpay_txid = fields.Char("Transaction Id")
|
|
btcpay_status = fields.Char("Transaction Status")
|
|
|
|
def _get_specific_rendering_values(self, processing_values):
|
|
""" Override of payment to return BTCPay-specific rendering values.
|
|
|
|
The redirect form only needs to carry the transaction reference: the
|
|
BTCPay invoice is built server-side (see the checkout controller) from
|
|
the transaction, so no amount, currency or buyer data is sent through
|
|
the browser.
|
|
|
|
Note: self.ensure_one() from `_get_processing_values`
|
|
|
|
:param dict processing_values: The generic and specific processing values of the transaction
|
|
:return: The dict of provider-specific processing values
|
|
:rtype: dict
|
|
"""
|
|
res = super()._get_specific_rendering_values(processing_values)
|
|
if self.provider_code != 'btcpayserver':
|
|
return res
|
|
|
|
return {
|
|
'api_url': '/btcpay/checkout',
|
|
'reference': self.reference,
|
|
# Keep the pre-19.0.1.1 key during rolling deployments. QWeb
|
|
# templates live in the database, so an instance restarted before
|
|
# the module is upgraded can still render the previous template.
|
|
'item_number': self.reference,
|
|
}
|
|
|
|
@api.model
|
|
def _extract_reference(self, provider_code, payment_data):
|
|
""" Override of payment to extract the transaction reference from BTCPay data.
|
|
|
|
:param str provider_code: The code of the provider handling the transaction.
|
|
:param dict payment_data: The payment data sent by the provider.
|
|
:return: The transaction reference.
|
|
:rtype: str
|
|
"""
|
|
if provider_code != 'btcpayserver':
|
|
return super()._extract_reference(provider_code, payment_data)
|
|
|
|
return payment_data.get('reference')
|
|
|
|
def _extract_amount_data(self, payment_data):
|
|
""" Override of payment to return the settled amount and currency.
|
|
|
|
The amount and currency are read back from BTCPay on notification and
|
|
returned here so that the generic amount validation compares them
|
|
against the transaction. This prevents a tampered or mismatched invoice
|
|
from confirming the transaction (and thus the order).
|
|
|
|
:param dict payment_data: The payment data sent by the provider.
|
|
:return: The settled amount data, or ``None`` for other providers.
|
|
:rtype: dict|None
|
|
"""
|
|
if self.provider_code != 'btcpayserver':
|
|
return super()._extract_amount_data(payment_data)
|
|
|
|
try:
|
|
amount = float(payment_data.get('amount'))
|
|
except (TypeError, ValueError):
|
|
amount = None
|
|
return {
|
|
'amount': amount,
|
|
'currency_code': payment_data.get('currency'),
|
|
}
|
|
|
|
def _apply_updates(self, payment_data):
|
|
""" Override of payment to process the transaction based on BTCPay data.
|
|
|
|
Note: self.ensure_one() from `_process`
|
|
|
|
:param dict payment_data: The payment data sent by the provider.
|
|
:return: None
|
|
"""
|
|
if self.provider_code != 'btcpayserver':
|
|
return super()._apply_updates(payment_data)
|
|
|
|
_logger.info("BTCPay _apply_updates: %s", pprint.pformat(payment_data))
|
|
|
|
if not payment_data.get('reference'):
|
|
raise ValidationError(
|
|
"BTCPay: " + _("Received payment data with missing reference."))
|
|
|
|
# Keep both external-reference fields bound to the authenticated
|
|
# BTCPay invoice. The controller validates this identity before
|
|
# entering `_process`.
|
|
if invoice_id := payment_data.get('invoiceID'):
|
|
self.btcpay_invoiceId = invoice_id
|
|
self.provider_reference = invoice_id
|
|
if payment_data.get('txid'):
|
|
self.btcpay_txid = payment_data['txid']
|
|
status = payment_data.get('status')
|
|
self.btcpay_status = status
|
|
|
|
if status in ('paid', 'processing'):
|
|
self._set_pending(state_message=payment_data.get('pending_reason'))
|
|
elif status in ('confirmed', 'complete'):
|
|
self._set_done()
|
|
elif status == 'new':
|
|
pass # Invoice created on BTCPay, waiting for the buyer to pay.
|
|
elif status in ('expired', 'cancel', 'cancelled'):
|
|
self._set_canceled(
|
|
state_message="BTCPay: " + _("Invoice status: %s.", status))
|
|
elif status == 'invalid':
|
|
_logger.warning(
|
|
"Received data with invalid payment status (%s) for transaction with reference %s",
|
|
status, self.reference
|
|
)
|
|
self._set_error(
|
|
"BTCPay: " + _("Received data with invalid payment status: %s.", status)
|
|
)
|
|
else:
|
|
_logger.warning(
|
|
"Received data with unknown payment status (%s) for transaction with reference %s",
|
|
status, self.reference
|
|
)
|
|
self._set_error(
|
|
"BTCPay: " + _("Received data with unknown payment status: %s.", status)
|
|
)
|