From 581f2b224477e00549f6211864a771208aa80965 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 24 Sep 2025 20:28:59 -0400 Subject: [PATCH] Fix crypto payment refund system with proper account balance handling - Fix AttributeError by changing payment.crypto_type to payment.coin_type - Fix Decimal * float error by using Decimal("1e12") instead of 1e12 - Add account-specific balance checking using payment.account_index - Add comprehensive logging for refund attempts and wallet balance checks - Update execute_refund to accept payment object for account context - Ensure refunds are sent from correct account that received the funds This resolves GroupR's underpayment refund issue and improves the reliability of the crypto payment rescue system. --- make_post_sell/lib/crypto_payment_rescue.py | 68 ++++++++++++++++++--- make_post_sell/lib/crypto_watcher.py | 10 ++- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/make_post_sell/lib/crypto_payment_rescue.py b/make_post_sell/lib/crypto_payment_rescue.py index ef720c0..2dbfd15 100644 --- a/make_post_sell/lib/crypto_payment_rescue.py +++ b/make_post_sell/lib/crypto_payment_rescue.py @@ -149,28 +149,75 @@ class PaymentRescue: "reason": "Payment received after quote expiration", } - def execute_refund(self, refund_details): + def execute_refund(self, refund_details, payment=None): """ Execute the actual refund transaction. Args: refund_details: dict with refund information + payment: CryptoPayment object (needed for account_index) Returns: dict with transaction details or raises exception """ + import logging + + logger = logging.getLogger(__name__) + + refund_amount_xmr = refund_details["refund_amount"] + refund_amount_piconero = int(refund_amount_xmr * Decimal("1e12")) + + logger.info(f"Attempting to refund payment {refund_details['payment_id']}") + logger.info( + f"Refund amount: {refund_amount_xmr} XMR ({refund_amount_piconero} piconero)" + ) + logger.info(f"Refund address: {refund_details['refund_address']}") + logger.info(f"Refund reason: {refund_details['reason']}") + + # Check wallet balance for the specific account before attempting refund + account_index = payment.account_index if payment else 0 + logger.info(f"Checking balance for account {account_index}") + try: - # Create the refund transaction - tx_result = self.crypto_client.transfer( - destinations=[ + balance_result = self.crypto_client._call( + "get_balance", {"account_index": account_index} + ) + unlocked_balance = balance_result.get("unlocked_balance", 0) + total_balance = balance_result.get("balance", 0) + logger.info( + f"Account {account_index} balance - Total: {total_balance} piconero ({Decimal(total_balance) / Decimal('1e12')} XMR)" + ) + logger.info( + f"Account {account_index} balance - Unlocked: {unlocked_balance} piconero ({Decimal(unlocked_balance) / Decimal('1e12')} XMR)" + ) + + if unlocked_balance < refund_amount_piconero: + logger.warning( + f"Insufficient unlocked balance in account {account_index}: need {refund_amount_piconero} but only have {unlocked_balance} unlocked" + ) + except Exception as balance_error: + logger.warning( + f"Could not check wallet balance for account {account_index}: {balance_error}" + ) + + try: + # Create the refund transaction from the specific account that received the payment + transfer_params = { + "destinations": [ { "address": refund_details["refund_address"], - "amount": int( - refund_details["refund_amount"] * 1e12 - ), # Convert to piconero + "amount": refund_amount_piconero, } - ] - ) + ], + "account_index": account_index, # Specify the account that received the funds + "get_tx_key": True, + "do_not_relay": False, + "priority": 1, # Use low priority to reduce fees + } + + logger.info(f"Calling transfer with params: {transfer_params}") + tx_result = self.crypto_client._call("transfer", transfer_params) + logger.info(f"Transfer successful: {tx_result}") return { "success": True, @@ -180,4 +227,7 @@ class PaymentRescue: } except Exception as e: + logger.error( + f"Refund failed for payment {refund_details['payment_id']}: {e}" + ) return {"success": False, "error": str(e), "refund_details": refund_details} diff --git a/make_post_sell/lib/crypto_watcher.py b/make_post_sell/lib/crypto_watcher.py index da60a79..691441b 100644 --- a/make_post_sell/lib/crypto_watcher.py +++ b/make_post_sell/lib/crypto_watcher.py @@ -884,7 +884,9 @@ def process_payment( logger.info( f"Expired payment {crypto_payment.id} eligible for refund: {refund_details}" ) - result = payment_rescue.execute_refund(refund_details) + result = payment_rescue.execute_refund( + refund_details, crypto_payment + ) if result["success"]: logger.info( f"Refund executed for expired payment {crypto_payment.id}: TX {result['tx_hash']}" @@ -1083,7 +1085,7 @@ def process_payment( logger.info( f"Overpayment detected for {crypto_payment.id}: {refund_details}" ) - result = payment_rescue.execute_refund(refund_details) + result = payment_rescue.execute_refund(refund_details, crypto_payment) if result["success"]: logger.info( f"Excess refunded for payment {crypto_payment.id}: TX {result['tx_hash']}" @@ -1145,7 +1147,9 @@ def process_payment( logger.info( f"Underpayment {crypto_payment.id} eligible for refund: {refund_details}" ) - result = payment_rescue.execute_refund(refund_details) + result = payment_rescue.execute_refund( + refund_details, crypto_payment + ) if result["success"]: logger.info( f"Refund executed for underpayment {crypto_payment.id}: TX {result['tx_hash']}"