Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
457455edf0 | ||
|
|
c3d32a04ce | ||
|
|
2a470afc5d | ||
|
|
cab44f0847 | ||
|
|
303bfce067 | ||
|
|
023e4060c5 | ||
|
|
9f3f702c9f | ||
|
|
94eab634ca | ||
|
|
225d4c33c5 |
No files matched your search
@@ -1,12 +1,12 @@
|
|||||||
# BTCPay Server payment gateway for Odoo 16, 17 and 18
|
# BTCPay Server payment gateway for Odoo 16, 17, 18 and 19
|
||||||
|
|
||||||
## Accept bitcoin payments on Odoo 16, 17 and 18
|
## Accept bitcoin payments on Odoo 16, 17, 18 and 19
|
||||||
This odoo addon allows you to accept bitcoin and Lightning Network (and other cryptocurrency) payments in your Odoo e-commerce store.
|
This odoo addon allows you to accept bitcoin and Lightning Network (and other cryptocurrency) payments in your Odoo e-commerce store.
|
||||||

|

|
||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
Please find the installation instructions here:
|
Please find the installation instructions here:
|
||||||
- [Odoo 18 documentation](https://github.com/btcpayserver/odoo/blob/18.0/payment_btcpayserver/README.md)
|
- [Odoo 18 + 19 documentation](https://github.com/btcpayserver/odoo/blob/19.0/payment_btcpayserver/README.md)
|
||||||
- [Odoo 17 documentation](https://github.com/btcpayserver/odoo/blob/17.0/payment_btcpayserver/README.md)
|
- [Odoo 17 documentation](https://github.com/btcpayserver/odoo/blob/17.0/payment_btcpayserver/README.md)
|
||||||
- [Odoo 16 documentation](https://github.com/btcpayserver/odoo/blob/16.0/payment_btcpayserver/README.md)
|
- [Odoo 16 documentation](https://github.com/btcpayserver/odoo/blob/16.0/payment_btcpayserver/README.md)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# BTCPay Server payment gateway for Odoo 18
|
# BTCPay Server payment gateway for Odoo 18 / 19
|
||||||
|
|
||||||
## This is the module to connect Odoo 18 and BTCPay Server
|
## This is the module to connect Odoo 18 / 19 and BTCPay Server
|
||||||
This module allows you to accept bitcoin (and other cryptocurrency) payments in your Odoo e-commerce store.
|
This module allows you to accept bitcoin (and other cryptocurrency) payments in your Odoo e-commerce store.
|
||||||

|

|
||||||
|
|
||||||
@@ -8,9 +8,13 @@ This module allows you to accept bitcoin (and other cryptocurrency) payments in
|
|||||||
If you use Odoo 16 you can find the documentation [here](https://github.com/btcpayserver/odoo/blob/16.0/payment_btcpayserver/README.md) and for Odoo 17 [here](https://github.com/btcpayserver/odoo/blob/17.0/payment_btcpayserver/README.md).
|
If you use Odoo 16 you can find the documentation [here](https://github.com/btcpayserver/odoo/blob/16.0/payment_btcpayserver/README.md) and for Odoo 17 [here](https://github.com/btcpayserver/odoo/blob/17.0/payment_btcpayserver/README.md).
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
The following instructions work for Odoo 18 and 19. You just need to make sure to download the matching branch release or use the matching branch (18.0 or 19.0) mentioned in the steps below.
|
||||||
|
:::
|
||||||
|
|
||||||
## Install the module
|
## Install the module
|
||||||
* Clone our [repository](https://github.com/btcpayserver/odoo) or download the .zip from the [releases page](https://github.com/btcpayserver/odoo/releases)
|
* Clone our [repository](https://github.com/btcpayserver/odoo) or download the .zip from the [releases page](https://github.com/btcpayserver/odoo/releases)
|
||||||
* Make sure you are on branch `18.0` or downloaded a release tagged with version v18.x
|
* Make sure you are on branch `19.0` (or `18.0`) or downloaded a [release](https://github.com/btcpayserver/odoo/releases) tagged with version v19.x (or v18.x)
|
||||||
* Place the `payment_btcpayserver` directory in your Odoo addons directory
|
* Place the `payment_btcpayserver` directory in your Odoo addons directory
|
||||||
* Install dependencies by running `pip install -r requirements.txt` (from inside the `payment_btcpayserver` directory)
|
* Install dependencies by running `pip install -r requirements.txt` (from inside the `payment_btcpayserver` directory)
|
||||||
* Restart Odoo
|
* Restart Odoo
|
||||||
@@ -25,15 +29,16 @@ If you use Odoo 16 you can find the documentation [here](https://github.com/btcp
|
|||||||
In the BTCPay settings form, tab "Credentials":
|
In the BTCPay settings form, tab "Credentials":
|
||||||
* Set field "State" to enabled
|
* Set field "State" to enabled
|
||||||
* Set field "BTCPay Server URL" as test or live URL including https://. Example URL: https://testnet.demo.btcpayserver.org
|
* Set field "BTCPay Server URL" as test or live URL including https://. Example URL: https://testnet.demo.btcpayserver.org
|
||||||
* Get a pairing code from your BTCPay Server store: Settings -> Access Tokens
|
* Get a pairing code from your BTCPay Server store:
|
||||||
|
* Go to: Settings -> Access Tokens
|
||||||
* Click on "Create Token" button
|
* Click on "Create Token" button
|
||||||
* Label: enter e.g. "My odoo store"
|
* Label: enter e.g. "My odoo store"
|
||||||
* Public Key: leave empty
|
* Public Key: leave empty
|
||||||
* Click on "Request Pairing" button, on next page click "Approve" button
|
* Click on "Request Pairing" button, on next page click "Approve" button
|
||||||
* At the top copy the code next to "Server initiated pairing code", e.g. "hg7z8wN"
|
* At the top copy the code next to "Server initiated pairing code", e.g. "hg7z8wN"
|
||||||
* Back in Odoo, paste the code into "Pairing Code" field
|
* Back in Odoo: paste the code into "Pairing Code" field
|
||||||
* Hit Tab key on your keyboard (or click on another field) and the pairing process will start automatically
|
* Hit Tab key on your keyboard (or click on another field) and the pairing process will start automatically
|
||||||
* When the pairing is successful the "Token" and "Private Key" field will be filled automatically
|
* When the pairing is successful the "Token" and "Private Key" field will be filled automatically (see screenshot below)
|
||||||
* Field Facade, keep default 'merchant'.
|
* Field Facade, keep default 'merchant'.
|
||||||
|
|
||||||
On the tab "Configuration":
|
On the tab "Configuration":
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Payment Provider: BTCPay Server',
|
'name': 'Payment Provider: BTCPay Server',
|
||||||
'summary': 'This module integrates BTCPAY - pay with Bitcoin - with Odoo v18.0',
|
'summary': 'This module integrates BTCPAY - pay with Bitcoin - with Odoo v19.0',
|
||||||
'author': 'BTCPay Server team and contributors',
|
'author': 'BTCPay Server team and contributors',
|
||||||
'website': 'https://github.com/btcpayserver/odoo',
|
'website': 'https://github.com/btcpayserver/odoo',
|
||||||
'category': 'Accounting/Payment Providers',
|
'category': 'Accounting/Payment Providers',
|
||||||
'version': '18.0.1.1',
|
'version': '19.0.1.1',
|
||||||
'license': 'GPL-3',
|
'license': 'GPL-3',
|
||||||
'currency': 'USD',
|
'currency': 'USD',
|
||||||
'application': False,
|
'application': False,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
# ******************************************************************************
|
# ******************************************************************************
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
|
|
||||||
from werkzeug import urls
|
from werkzeug import urls
|
||||||
|
|
||||||
@@ -28,9 +27,11 @@ from odoo import _, http
|
|||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
|
from odoo.addons.payment.logging import get_payment_logger
|
||||||
from ..models.libs.client import BTCPayClient
|
from ..models.libs.client import BTCPayClient
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
_logger = get_payment_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BTCPayController(http.Controller):
|
class BTCPayController(http.Controller):
|
||||||
@@ -46,6 +47,13 @@ class BTCPayController(http.Controller):
|
|||||||
tokens={provider.btcpay_facade: provider.btcpay_token},
|
tokens={provider.btcpay_facade: provider.btcpay_token},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _find_transaction(reference):
|
||||||
|
return request.env['payment.transaction'].sudo().search([
|
||||||
|
('reference', '=', reference),
|
||||||
|
('provider_code', '=', 'btcpayserver'),
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _lock_transaction(tx_sudo):
|
def _lock_transaction(tx_sudo):
|
||||||
"""Lock a transaction until the current database transaction ends.
|
"""Lock a transaction until the current database transaction ends.
|
||||||
@@ -176,8 +184,11 @@ class BTCPayController(http.Controller):
|
|||||||
data, so a buyer cannot have an invoice created for a tampered amount.
|
data, so a buyer cannot have an invoice created for a tampered amount.
|
||||||
"""
|
"""
|
||||||
_logger.info("BTCPay: checkout request for transaction %s", data.get('reference'))
|
_logger.info("BTCPay: checkout request for transaction %s", data.get('reference'))
|
||||||
tx_sudo = request.env['payment.transaction'].sudo()._get_tx_from_notification_data(
|
reference = data.get('reference')
|
||||||
'btcpayserver', data)
|
tx_sudo = self._find_transaction(reference)
|
||||||
|
if not tx_sudo:
|
||||||
|
raise ValidationError(
|
||||||
|
_("BTCPay: No transaction found matching reference %s.", reference))
|
||||||
self._lock_transaction(tx_sudo)
|
self._lock_transaction(tx_sudo)
|
||||||
if tx_sudo.state != 'draft':
|
if tx_sudo.state != 'draft':
|
||||||
# Already processed (e.g. double submission): fall back to the
|
# Already processed (e.g. double submission): fall back to the
|
||||||
@@ -239,7 +250,7 @@ class BTCPayController(http.Controller):
|
|||||||
invoice['id'], tx_sudo.reference)
|
invoice['id'], tx_sudo.reference)
|
||||||
return request.redirect(invoice['url'], local=False)
|
return request.redirect(invoice['url'], local=False)
|
||||||
|
|
||||||
@http.route(_notify_url, type='json', auth='public', methods=['POST'], csrf=False)
|
@http.route(_notify_url, type='jsonrpc', auth='public', methods=['POST'], csrf=False)
|
||||||
def btcpay_ipn(self, **post):
|
def btcpay_ipn(self, **post):
|
||||||
""" BTCPay IPN. """
|
""" BTCPay IPN. """
|
||||||
_logger.info('BTCPay: IPN received')
|
_logger.info('BTCPay: IPN received')
|
||||||
@@ -274,30 +285,25 @@ class BTCPayController(http.Controller):
|
|||||||
fetched_invoice.get('id'), fetched_invoice.get('status'), tx_sudo.reference,
|
fetched_invoice.get('id'), fetched_invoice.get('status'), tx_sudo.reference,
|
||||||
)
|
)
|
||||||
|
|
||||||
notification_data = {
|
payment_data = {
|
||||||
"reference": tx_sudo.reference,
|
"reference": tx_sudo.reference,
|
||||||
"status": fetched_invoice.get('status'),
|
"status": fetched_invoice.get('status'),
|
||||||
"invoiceID": incoming_invoice_id,
|
"invoiceID": incoming_invoice_id,
|
||||||
"txid": fetched_invoice.get('url'),
|
"txid": fetched_invoice.get('url'),
|
||||||
# Settled amount and currency, verified against the transaction
|
# Settled amount and currency, validated against the transaction
|
||||||
# before it is set done.
|
# by the payment framework before it is set done.
|
||||||
"amount": fetched_invoice.get('price'),
|
"amount": fetched_invoice.get('price'),
|
||||||
"currency": fetched_invoice.get('currency'),
|
"currency": fetched_invoice.get('currency'),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Handle the notification data
|
tx_sudo._process('btcpayserver', payment_data)
|
||||||
tx_sudo._handle_notification_data('btcpayserver', notification_data)
|
except ValidationError as error:
|
||||||
except ValidationError as error: # Acknowledge rejected notifications without retries.
|
|
||||||
_logger.warning("BTCPay: rejected notification: %s", error)
|
_logger.warning("BTCPay: rejected notification: %s", error)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@http.route(_return_url, type='http', auth="public", methods=['GET'],
|
@http.route(_return_url, type='http', auth="public", methods=['GET'],
|
||||||
csrf=False, save_session=False)
|
csrf=False, save_session=False)
|
||||||
def btcpay_return_from_redirect(self, **data):
|
def btcpay_return_from_redirect(self, **data):
|
||||||
""" BTCPay return
|
""" BTCPay return """
|
||||||
|
|
||||||
We could process and check the invoice status here but there is no need to as the status get's updated via
|
|
||||||
IPN anyway, so just show the user the order confirmation / payment status page.
|
|
||||||
"""
|
|
||||||
_logger.info("BTCPay: user returned to shop after payment")
|
_logger.info("BTCPay: user returned to shop after payment")
|
||||||
return request.redirect('/payment/status')
|
return request.redirect('/payment/status')
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from .libs.client import BTCPayClient
|
from .libs.client import BTCPayClient
|
||||||
from .libs import crypto
|
from .libs import crypto
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
from odoo.addons.payment.logging import get_payment_logger
|
||||||
|
|
||||||
|
|
||||||
|
_logger = get_payment_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PaymentProvider(models.Model):
|
class PaymentProvider(models.Model):
|
||||||
@@ -23,7 +24,6 @@ class PaymentProvider(models.Model):
|
|||||||
@api.onchange('btcpay_pairingCode')
|
@api.onchange('btcpay_pairingCode')
|
||||||
def _onchange_pairingCode(self):
|
def _onchange_pairingCode(self):
|
||||||
if not self.btcpay_token and self.code == 'btcpayserver' and self.btcpay_pairingCode:
|
if not self.btcpay_token and self.code == 'btcpayserver' and self.btcpay_pairingCode:
|
||||||
#_logger.info("ONCHANGE PAIRING CODE***SELF: %s %s %s", self.btcpay_location, self.btcpay_privateKey, self.btcpay_pairingCode)
|
|
||||||
self.btcpay_privateKey = crypto.generate_privkey()
|
self.btcpay_privateKey = crypto.generate_privkey()
|
||||||
client = BTCPayClient(host=self.btcpay_location, pem=self.btcpay_privateKey)
|
client = BTCPayClient(host=self.btcpay_location, pem=self.btcpay_privateKey)
|
||||||
token = client.pair_client(self.btcpay_pairingCode)
|
token = client.pair_client(self.btcpay_pairingCode)
|
||||||
@@ -33,13 +33,10 @@ class PaymentProvider(models.Model):
|
|||||||
def _onchange_token(self):
|
def _onchange_token(self):
|
||||||
if self.code == 'btcpayserver':
|
if self.code == 'btcpayserver':
|
||||||
self.btcpay_pairingCode = ''
|
self.btcpay_pairingCode = ''
|
||||||
#_logger.info("ONCHANGE TOKEN")
|
|
||||||
|
|
||||||
|
|
||||||
@api.onchange('btcpay_location')
|
@api.onchange('btcpay_location')
|
||||||
def _onchange_location(self):
|
def _onchange_location(self):
|
||||||
if self.code == 'btcpayserver':
|
if self.code == 'btcpayserver':
|
||||||
self.btcpay_token = ''
|
self.btcpay_token = ''
|
||||||
#_logger.info("ONCHANGE LOCATION ***SELF: %s %s %s", self.btcpay_location, self.btcpay_privateKey, self.btcpay_pairingCode)
|
|
||||||
self.btcpay_privateKey = ''
|
self.btcpay_privateKey = ''
|
||||||
self.btcpay_pairingCode = ''
|
self.btcpay_pairingCode = ''
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import logging
|
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
from odoo import _, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
from odoo.addons.payment.logging import get_payment_logger
|
||||||
|
|
||||||
|
|
||||||
|
_logger = get_payment_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PaymentTransaction(models.Model):
|
class PaymentTransaction(models.Model):
|
||||||
@@ -35,66 +37,82 @@ class PaymentTransaction(models.Model):
|
|||||||
return {
|
return {
|
||||||
'api_url': '/btcpay/checkout',
|
'api_url': '/btcpay/checkout',
|
||||||
'reference': self.reference,
|
'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,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_tx_from_notification_data(self, provider_code, notification_data):
|
@api.model
|
||||||
""" Override of payment to find the transaction based on BTCPay data.
|
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 that handled the transaction
|
:param str provider_code: The code of the provider handling the transaction.
|
||||||
:param dict notification_data: The notification data sent by the provider
|
:param dict payment_data: The payment data sent by the provider.
|
||||||
:return: The transaction if found
|
:return: The transaction reference.
|
||||||
:rtype: recordset of `payment.transaction`
|
:rtype: str
|
||||||
:raise: ValidationError if the data match no transaction
|
|
||||||
"""
|
"""
|
||||||
tx = super()._get_tx_from_notification_data(provider_code, notification_data)
|
if provider_code != 'btcpayserver':
|
||||||
if provider_code != 'btcpayserver' or len(tx) == 1:
|
return super()._extract_reference(provider_code, payment_data)
|
||||||
return tx
|
|
||||||
|
|
||||||
reference = notification_data.get('reference')
|
return payment_data.get('reference')
|
||||||
tx = self.search([('reference', '=', reference), ('provider_code', '=', 'btcpayserver')])
|
|
||||||
if not tx:
|
|
||||||
raise ValidationError(
|
|
||||||
"BTCPay: " + _("No transaction found matching reference %s.", reference)
|
|
||||||
)
|
|
||||||
return tx
|
|
||||||
|
|
||||||
def _process_notification_data(self, notification_data):
|
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.
|
""" Override of payment to process the transaction based on BTCPay data.
|
||||||
|
|
||||||
Only the transaction state is updated here; confirming the related
|
Note: self.ensure_one() from `_process`
|
||||||
sale order, creating the payment and sending the confirmation email are
|
|
||||||
handled by the generic post-processing of the `payment` framework.
|
|
||||||
|
|
||||||
Note: self.ensure_one()
|
:param dict payment_data: The payment data sent by the provider.
|
||||||
|
|
||||||
:param dict notification_data: The notification data sent by the provider
|
|
||||||
:return: None
|
:return: None
|
||||||
:raise: ValidationError if inconsistent data were received
|
|
||||||
"""
|
"""
|
||||||
super()._process_notification_data(notification_data)
|
|
||||||
if self.provider_code != 'btcpayserver':
|
if self.provider_code != 'btcpayserver':
|
||||||
return
|
return super()._apply_updates(payment_data)
|
||||||
|
|
||||||
_logger.info(
|
_logger.info("BTCPay _apply_updates: %s", pprint.pformat(payment_data))
|
||||||
"processing notification data for BTCPay transaction with reference %s:\n%s",
|
|
||||||
self.reference, pprint.pformat(notification_data))
|
if not payment_data.get('reference'):
|
||||||
if not notification_data.get('reference'):
|
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
"BTCPay: " + _("Received notification data with missing reference."))
|
"BTCPay: " + _("Received payment data with missing reference."))
|
||||||
|
|
||||||
if notification_data.get('invoiceID'):
|
# Keep both external-reference fields bound to the authenticated
|
||||||
self.btcpay_invoiceId = notification_data['invoiceID']
|
# BTCPay invoice. The controller validates this identity before
|
||||||
self.provider_reference = notification_data['invoiceID']
|
# entering `_process`.
|
||||||
if notification_data.get('txid'):
|
if invoice_id := payment_data.get('invoiceID'):
|
||||||
self.btcpay_txid = notification_data['txid']
|
self.btcpay_invoiceId = invoice_id
|
||||||
status = notification_data.get('status')
|
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
|
self.btcpay_status = status
|
||||||
|
|
||||||
if status in ('paid', 'processing'):
|
if status in ('paid', 'processing'):
|
||||||
self._set_pending(state_message=notification_data.get('pending_reason'))
|
self._set_pending(state_message=payment_data.get('pending_reason'))
|
||||||
elif status in ('confirmed', 'complete'):
|
elif status in ('confirmed', 'complete'):
|
||||||
if self._btcpay_verify_amount(notification_data):
|
self._set_done()
|
||||||
self._set_done()
|
|
||||||
elif status == 'new':
|
elif status == 'new':
|
||||||
pass # Invoice created on BTCPay, waiting for the buyer to pay.
|
pass # Invoice created on BTCPay, waiting for the buyer to pay.
|
||||||
elif status in ('expired', 'cancel', 'cancelled'):
|
elif status in ('expired', 'cancel', 'cancelled'):
|
||||||
@@ -102,42 +120,17 @@ class PaymentTransaction(models.Model):
|
|||||||
state_message="BTCPay: " + _("Invoice status: %s.", status))
|
state_message="BTCPay: " + _("Invoice status: %s.", status))
|
||||||
elif status == 'invalid':
|
elif status == 'invalid':
|
||||||
_logger.warning(
|
_logger.warning(
|
||||||
"received invalid payment status for transaction with reference %s", self.reference)
|
"Received data with invalid payment status (%s) for transaction with reference %s",
|
||||||
|
status, self.reference
|
||||||
|
)
|
||||||
self._set_error(
|
self._set_error(
|
||||||
"BTCPay: " + _("Received data with invalid payment status: %s.", status))
|
"BTCPay: " + _("Received data with invalid payment status: %s.", status)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
_logger.warning(
|
_logger.warning(
|
||||||
"received unrecognized payment status (%s) for transaction with reference %s",
|
"Received data with unknown payment status (%s) for transaction with reference %s",
|
||||||
status, self.reference)
|
status, self.reference
|
||||||
|
)
|
||||||
self._set_error(
|
self._set_error(
|
||||||
"BTCPay: " + _("Received data with unknown payment status: %s.", status))
|
"BTCPay: " + _("Received data with unknown payment status: %s.", status)
|
||||||
|
)
|
||||||
def _btcpay_verify_amount(self, notification_data):
|
|
||||||
""" Check that the settled BTCPay invoice matches the transaction.
|
|
||||||
|
|
||||||
The BTCPay invoice is created from the transaction, but its settled
|
|
||||||
amount and currency are read back from BTCPay on notification and
|
|
||||||
compared here so that a tampered or mismatched invoice can never
|
|
||||||
confirm the transaction (and thus the order). On mismatch the
|
|
||||||
transaction is set in error and ``False`` is returned.
|
|
||||||
|
|
||||||
:param dict notification_data: The notification data sent by the provider
|
|
||||||
:return: Whether the settled amount and currency match the transaction
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
self.ensure_one()
|
|
||||||
currency = notification_data.get('currency')
|
|
||||||
try:
|
|
||||||
amount = float(notification_data.get('amount'))
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
amount = None
|
|
||||||
if (amount is None
|
|
||||||
or currency != self.currency_id.name
|
|
||||||
or self.currency_id.compare_amounts(amount, self.amount) != 0):
|
|
||||||
self._set_error("BTCPay: " + _(
|
|
||||||
"The settled amount (%(amount)s %(currency)s) does not match the "
|
|
||||||
"expected amount (%(expected)s %(expected_currency)s).",
|
|
||||||
amount=notification_data.get('amount'), currency=currency,
|
|
||||||
expected=self.amount, expected_currency=self.currency_id.name))
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||