Fix misleading overpayment log for exact payments

- Only log 'Overpayment within 5% threshold' when received > expected
- Skip the log message for exact payment matches
- Prevents confusion when payment amount exactly matches expected amount
This commit is contained in:
Russell Ballestrini 2025-10-01 11:27:34 -04:00
parent 8326c7401f
commit 3ae1b9bed9
4 changed files with 37 additions and 16 deletions

View file

@ -351,10 +351,7 @@ def _should_process_late_payment(payment, tx):
return True
# Edge Case: First payment to a fresh quote (normal processing should handle this, but just in case)
if payment.received_amount == 0 and payment.status in [
CryptoPayment.STATUS_PENDING,
CryptoPayment.STATUS_CONFIRMED,
]:
if payment.received_amount == 0 and payment.status == CryptoPayment.STATUS_PENDING:
log.payment_info(payment, "Accepting first payment to fresh quote")
return True
@ -680,8 +677,10 @@ def process_confirmed_payment(env_request, crypto_payment, client, payment_rescu
results["final_status"] = CryptoPayment.STATUS_CONFIRMED_OVERPAY
return results
else:
# Overpayment within threshold - will confirm normally at end
log.payment_info(crypto_payment, "Overpayment within 5% threshold")
# Check if there's actually an overpayment (not exact match)
if received_crypto > expected_crypto:
# Overpayment within threshold - will confirm normally at end
log.payment_info(crypto_payment, "Overpayment within 5% threshold")
else:
# Normal payment - will confirm at end
pass
@ -3632,9 +3631,18 @@ def scan_wallet_for_double_or_late_payments(request, settings):
)
else:
# First payment to this address - process normally
# Determine if this is a late payment or double payment
if payment.status in [
CryptoPayment.STATUS_EXPIRED,
CryptoPayment.STATUS_CANCELLED,
]:
payment_type = "late payment"
else:
payment_type = "double payment"
log.payment_info(
payment,
f"Found late payment to {payment.status} quote: "
f"Found {payment_type} to {payment.status} quote: "
f"{tx.get('amount', 0) / 1e12:.12f} XMR, "
f"{tx.get('confirmations', 0)} confirmations, height {tx.get('height', 0)}",
)
@ -3771,9 +3779,18 @@ def scan_wallet_for_double_or_late_payments(request, settings):
if payment and _should_process_late_payment(payment, tx):
late_payments_found += 1
# Determine if this is a late payment or double payment
if payment.status in [
CryptoPayment.STATUS_EXPIRED,
CryptoPayment.STATUS_CANCELLED,
]:
payment_type = "late payment"
else:
payment_type = "double payment"
log.payment_info(
payment,
f"Found late payment to {payment.status} quote: "
f"Found {payment_type} to {payment.status} quote: "
f"{tx.get('amount', 0)} {coin_type}, "
f"{tx.get('confirmations', 0)} confirmations",
)

View file

@ -858,15 +858,17 @@ class CryptoWatcherIntegrationTests(unittest.TestCase):
# Check that payment_error was called for duplicate detection
error_calls = mock_log.payment_error.call_args_list
duplicate_detection_calls = [
call for call in error_calls
call
for call in error_calls
if "DUPLICATE PAYMENT DETECTED" in str(call[0][1])
]
self.assertTrue(len(duplicate_detection_calls) >= 1)
# Verify duplicate payment record creation was logged
info_calls = mock_log.payment_info.call_args_list
duplicate_creation_calls = [
call for call in info_calls
call
for call in info_calls
if "Created duplicate payment" in str(call[0][1])
]
self.assertEqual(len(duplicate_creation_calls), 1)

View file

@ -180,9 +180,9 @@ class TestInvoiceDeletion(unittest.TestCase):
self.assertEqual(self.mock_crypto_payment.invoice, self.mock_invoice)
@patch("make_post_sell.lib.crypto_watcher.delete_invoice_by_id")
@patch("make_post_sell.lib.crypto_watcher.logger")
@patch("make_post_sell.lib.crypto_watcher.log")
def test_handle_invoice_deletion_returns_false(
self, mock_logger, mock_delete_invoice
self, mock_log, mock_delete_invoice
):
"""Test that function handles when delete_invoice_by_id returns failure."""
mock_delete_invoice.return_value = {
@ -194,8 +194,8 @@ class TestInvoiceDeletion(unittest.TestCase):
delete_invoice_for_terminal_state(self.mock_dbsession, self.mock_crypto_payment)
# Verify warning was logged
mock_logger.warning.assert_called_once()
# Verify error was logged
mock_log.error_with_context.assert_called_once()
# Verify invoice reference was NOT cleared since deletion failed
self.assertEqual(self.mock_crypto_payment.invoice_id, "test-invoice-123")

View file

@ -1415,7 +1415,9 @@ class TestCryptoPayment(unittest.TestCase):
self.assertIn(self.payment.uuid_str[:8], result)
self.assertIn("[pending]", result) # status shown as-is
self.assertIn("XMR", result)
self.assertIn("0/1", result) # received/expected in XMR (trailing zeros stripped)
self.assertIn(
"0/1", result
) # received/expected in XMR (trailing zeros stripped)
self.assertIn("conf:0/10", result)
self.assertIn("addr:", result)
self.assertIn("user:Alice", result)