changes
This commit is contained in:
@@ -92,13 +92,20 @@ class PaymentTransaction(models.Model):
|
||||
|
||||
poynt_data = self._poynt_create_order_and_authorize()
|
||||
if poynt_data:
|
||||
status = poynt_data.get('status', 'AUTHORIZED')
|
||||
payment_data = {
|
||||
'reference': self.reference,
|
||||
'poynt_order_id': poynt_data.get('order_id'),
|
||||
'poynt_transaction_id': poynt_data.get('transaction_id'),
|
||||
'poynt_status': poynt_data.get('status', 'AUTHORIZED'),
|
||||
'poynt_status': status,
|
||||
'funding_source': poynt_data.get('funding_source', {}),
|
||||
}
|
||||
if status in ('DECLINED', 'FAILED', 'REFUND_FAILED'):
|
||||
self._set_error(
|
||||
_("Payment was %(status)s by the processor.",
|
||||
status=status.lower())
|
||||
)
|
||||
return
|
||||
self._process('poynt', payment_data)
|
||||
|
||||
def _poynt_create_order_and_authorize(self):
|
||||
@@ -148,6 +155,103 @@ class PaymentTransaction(models.Model):
|
||||
self._set_error(str(e))
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def _detect_card_brand_from_details(payment_details):
|
||||
"""Detect card brand from the payment_details string on a token.
|
||||
|
||||
Tokens store details like "VISA ending in 1234" or
|
||||
"AMERICAN_EXPRESS ending in 5678".
|
||||
"""
|
||||
details = (payment_details or '').upper()
|
||||
if 'AMEX' in details or 'AMERICAN_EXPRESS' in details:
|
||||
return 'amex'
|
||||
if 'VISA' in details:
|
||||
return 'visa'
|
||||
if 'MASTER' in details:
|
||||
return 'mastercard'
|
||||
return 'other'
|
||||
|
||||
def _apply_token_surcharge(self):
|
||||
"""Apply surcharge to the linked invoice for token-based payments.
|
||||
|
||||
Checks if surcharge is enabled, detects card brand from the token,
|
||||
adds a surcharge line to the invoice, and updates the transaction
|
||||
amount. Skips rental orders (recurring charges should not get
|
||||
surcharge), invoices with no linked records, or invoices where
|
||||
surcharge is already applied.
|
||||
"""
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
if ICP.get_param('fusion_poynt.surcharge_enabled', 'False') != 'True':
|
||||
return
|
||||
|
||||
if not self.token_id or not self.invoice_ids:
|
||||
return
|
||||
|
||||
for inv in self.invoice_ids:
|
||||
sale_orders = inv.mapped('line_ids.sale_line_ids.order_id')
|
||||
for so in sale_orders:
|
||||
if getattr(so, 'is_rental_order', False):
|
||||
if not getattr(so, 'rental_apply_cc_fee', True):
|
||||
return
|
||||
|
||||
card_type = self._detect_card_brand_from_details(
|
||||
self.token_id.payment_details,
|
||||
)
|
||||
rate_key = {
|
||||
'visa': 'fusion_poynt.surcharge_visa_rate',
|
||||
'mastercard': 'fusion_poynt.surcharge_mastercard_rate',
|
||||
'amex': 'fusion_poynt.surcharge_amex_rate',
|
||||
'debit': 'fusion_poynt.surcharge_debit_rate',
|
||||
}.get(card_type, 'fusion_poynt.surcharge_other_rate')
|
||||
rate = float(ICP.get_param(rate_key, '0') or 0)
|
||||
if rate <= 0:
|
||||
return
|
||||
|
||||
product_id = int(ICP.get_param('fusion_poynt.surcharge_product_id', '0') or 0)
|
||||
product = self.env['product.product'].sudo().browse(product_id).exists()
|
||||
if not product:
|
||||
product = self.env.ref(
|
||||
'fusion_poynt.product_cc_processing_fee', raise_if_not_found=False,
|
||||
)
|
||||
if not product:
|
||||
_logger.warning("Surcharge product not configured; skipping token surcharge")
|
||||
return
|
||||
|
||||
total_fee = 0.0
|
||||
for invoice in self.invoice_ids.sudo():
|
||||
already_has = invoice.invoice_line_ids.filtered(
|
||||
lambda l: l.product_id.id == product.id
|
||||
)
|
||||
if already_has:
|
||||
continue
|
||||
|
||||
fee_amount = round(invoice.amount_residual * rate / 100.0, 2)
|
||||
if fee_amount <= 0:
|
||||
continue
|
||||
|
||||
was_posted = invoice.state == 'posted'
|
||||
if was_posted:
|
||||
invoice.button_draft()
|
||||
|
||||
description = "Credit Card Processing Fee (%.2f%% surcharge)" % rate
|
||||
invoice.write({
|
||||
'invoice_line_ids': [(0, 0, {
|
||||
'product_id': product.id,
|
||||
'name': description,
|
||||
'quantity': 1,
|
||||
'price_unit': fee_amount,
|
||||
'tax_ids': [(5, 0, 0)],
|
||||
})],
|
||||
})
|
||||
|
||||
if was_posted:
|
||||
invoice.action_post()
|
||||
|
||||
total_fee += fee_amount
|
||||
|
||||
if total_fee > 0:
|
||||
self.amount += total_fee
|
||||
|
||||
def _poynt_process_token_payment(self):
|
||||
"""Process a payment using a stored token (card on file).
|
||||
|
||||
@@ -156,6 +260,8 @@ class PaymentTransaction(models.Model):
|
||||
were created before the JWT migration.
|
||||
"""
|
||||
try:
|
||||
self._apply_token_surcharge()
|
||||
|
||||
provider = self._get_provider_sudo()
|
||||
action = 'AUTHORIZE' if provider.capture_manually else 'SALE'
|
||||
payment_jwt = self.token_id.poynt_payment_token
|
||||
@@ -213,13 +319,28 @@ class PaymentTransaction(models.Model):
|
||||
if order_id:
|
||||
self.poynt_order_id = order_id
|
||||
|
||||
status = txn_result.get('status', '')
|
||||
payment_data = {
|
||||
'reference': self.reference,
|
||||
'poynt_order_id': order_id,
|
||||
'poynt_transaction_id': transaction_id,
|
||||
'poynt_status': txn_result.get('status', ''),
|
||||
'poynt_status': status,
|
||||
'funding_source': txn_result.get('fundingSource', {}),
|
||||
}
|
||||
|
||||
if status in ('DECLINED', 'FAILED', 'REFUND_FAILED'):
|
||||
processor = txn_result.get('processorResponse', {})
|
||||
decline_msg = (
|
||||
processor.get('statusMessage')
|
||||
or processor.get('message')
|
||||
or status.lower()
|
||||
)
|
||||
self._set_error(
|
||||
_("Payment %(status)s: %(reason)s",
|
||||
status=status.lower(), reason=decline_msg)
|
||||
)
|
||||
return
|
||||
|
||||
self._process('poynt', payment_data)
|
||||
except ValidationError as e:
|
||||
self._set_error(str(e))
|
||||
|
||||
Reference in New Issue
Block a user