From 59963a62cdb78c6e24bd34382d8f085535d4d2c6 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 3 Oct 2025 16:40:41 -0400 Subject: [PATCH 1/3] Add email notifications for economically unviable refunds Enhanced the refund email system to properly notify users when their cryptocurrency refunds cannot be processed due to being economically unviable (too small to cover network transaction fees). Changes: - Updated send_refund_email() to differentiate between "no refund address" and "economically unviable" scenarios with appropriate messaging - Added email notifications in crypto watcher for all NOT_REFUNDED status transitions due to economic viability: * Duplicate payment processing (line ~1770) * Expired payment processing (line ~2145) * Underpayment processing (line ~2905) * Passive monitoring refund retries (line ~3280) - Added proper error handling for email sending to prevent disruption - All economically unviable refund tests pass with no regressions Users now receive clear explanations when refunds are too small to send rather than being left without notification. --- make_post_sell/lib/crypto_watcher/__init__.py | 88 +++++++++++++++++++ make_post_sell/lib/mail.py | 9 +- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/make_post_sell/lib/crypto_watcher/__init__.py b/make_post_sell/lib/crypto_watcher/__init__.py index 70e41d0..dae0326 100644 --- a/make_post_sell/lib/crypto_watcher/__init__.py +++ b/make_post_sell/lib/crypto_watcher/__init__.py @@ -1767,6 +1767,30 @@ def process_payment( refund_details["refund_amount"] * coin_config["atomic_units"] ) + + # Send refund email notification + if crypto_payment.invoice and crypto_payment.invoice.user: + try: + # Create shop context request + email_request = create_shop_context_request( + env_request, crypto_payment + ) + send_refund_email( + email_request, + crypto_payment.invoice.user.email, + crypto_payment, + refund_details, + ) + log.payment_info( + crypto_payment, + "Sent refund email for economically unviable duplicate payment", + ) + except Exception as e: + log.payment_error( + crypto_payment, + f"Failed to send refund email for economically unviable duplicate payment: {e}", + ) + env_request.dbsession.add(crypto_payment) env_request.dbsession.flush() return # Exit early - no refund to process @@ -2116,6 +2140,26 @@ def process_payment( refund_details["refund_amount"] * coin_config["atomic_units"] ) + + # Send refund email notification + if crypto_payment.invoice and crypto_payment.invoice.user: + try: + send_refund_email( + env_request, + crypto_payment.invoice.user.email, + crypto_payment, + refund_details, + ) + log.payment_info( + crypto_payment, + "Sent refund email for economically unviable expired payment", + ) + except Exception as e: + log.payment_error( + crypto_payment, + f"Failed to send refund email for economically unviable expired payment: {e}", + ) + # Delete invoice for terminal state delete_invoice_for_terminal_state( env_request.dbsession, crypto_payment @@ -2856,6 +2900,26 @@ def process_payment( refund_details["refund_amount"] * coin_config["atomic_units"] ) + + # Send refund email notification + if crypto_payment.invoice and crypto_payment.invoice.user: + try: + send_refund_email( + env_request, + crypto_payment.invoice.user.email, + crypto_payment, + refund_details, + ) + log.payment_info( + crypto_payment, + "Sent refund email for economically unviable underpayment", + ) + except Exception as e: + log.payment_error( + crypto_payment, + f"Failed to send refund email for economically unviable underpayment: {e}", + ) + # Delete invoice for terminal state delete_invoice_for_terminal_state( env_request.dbsession, crypto_payment @@ -3211,6 +3275,30 @@ def process_refund_confirmations(request, settings): "economically unviable refund", ) payment.refund_reason = f"Underpayment - refund economically unviable ({refund_details['refund_amount']} {payment.coin_type})" + + # Send refund email notification + if payment.invoice and payment.invoice.user: + try: + # Create a basic request object for email context + from pyramid.testing import DummyRequest + email_request = DummyRequest() + email_request.registry = request.registry + send_refund_email( + email_request, + payment.invoice.user.email, + payment, + refund_details, + ) + log.payment_info( + payment, + "Sent refund email for economically unviable underpayment (passive monitoring)", + ) + except Exception as e: + log.payment_error( + payment, + f"Failed to send refund email for economically unviable underpayment (passive monitoring): {e}", + ) + db.add(payment) continue diff --git a/make_post_sell/lib/mail.py b/make_post_sell/lib/mail.py index 9c0ebcd..61ac61c 100644 --- a/make_post_sell/lib/mail.py +++ b/make_post_sell/lib/mail.py @@ -389,8 +389,13 @@ def send_refund_email(request, to_email, crypto_payment, refund_details): CryptoPayment.STATUS_OUT_OF_STOCK_NOT_REFUNDED, CryptoPayment.STATUS_DOUBLEPAY_NOT_REFUNDED, ]: - subject = f"Payment Issue - No Refund Address - {crypto_payment.coin_type}" - explanation = "We were unable to process a refund for your payment because no refund address was configured. Please contact support if you need assistance." + # Check if it's economically unviable vs no refund address + if crypto_payment.refund_reason and "economically unviable" in crypto_payment.refund_reason: + subject = f"Payment Issue - Refund Too Small - {crypto_payment.coin_type}" + explanation = f"Your payment of {received_amount} {crypto_payment.coin_type} results in a refund amount too small to cover network transaction fees. The refund would cost more to send than its value. Please contact support if you have questions." + else: + subject = f"Payment Issue - No Refund Address - {crypto_payment.coin_type}" + explanation = "We were unable to process a refund for your payment because no refund address was configured. Please contact support if you need assistance." has_fee = False # No refund means no fee calculation else: From 125d8726bb73330d9d00e6d934d6634a263fa0f5 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 3 Oct 2025 16:44:46 -0400 Subject: [PATCH 2/3] Remove support contact message from economically unviable refund emails The message 'Please contact support if you have questions' has been removed since no support is offered for refunds. --- make_post_sell/lib/mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make_post_sell/lib/mail.py b/make_post_sell/lib/mail.py index 61ac61c..6b8545e 100644 --- a/make_post_sell/lib/mail.py +++ b/make_post_sell/lib/mail.py @@ -392,7 +392,7 @@ def send_refund_email(request, to_email, crypto_payment, refund_details): # Check if it's economically unviable vs no refund address if crypto_payment.refund_reason and "economically unviable" in crypto_payment.refund_reason: subject = f"Payment Issue - Refund Too Small - {crypto_payment.coin_type}" - explanation = f"Your payment of {received_amount} {crypto_payment.coin_type} results in a refund amount too small to cover network transaction fees. The refund would cost more to send than its value. Please contact support if you have questions." + explanation = f"Your payment of {received_amount} {crypto_payment.coin_type} results in a refund amount too small to cover network transaction fees. The refund would cost more to send than its value." else: subject = f"Payment Issue - No Refund Address - {crypto_payment.coin_type}" explanation = "We were unable to process a refund for your payment because no refund address was configured. Please contact support if you need assistance." From 7ff744e0df7b083296a17ec49882830add5a59ce Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 3 Oct 2025 16:46:09 -0400 Subject: [PATCH 3/3] Remove all 'contact support' references from refund emails Removed support contact messages from: - No refund address scenario emails - Both text and HTML email templates This is consistent with the policy that no support is offered for refunds. --- make_post_sell/lib/mail.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/make_post_sell/lib/mail.py b/make_post_sell/lib/mail.py index 6b8545e..2076062 100644 --- a/make_post_sell/lib/mail.py +++ b/make_post_sell/lib/mail.py @@ -395,7 +395,7 @@ def send_refund_email(request, to_email, crypto_payment, refund_details): explanation = f"Your payment of {received_amount} {crypto_payment.coin_type} results in a refund amount too small to cover network transaction fees. The refund would cost more to send than its value." else: subject = f"Payment Issue - No Refund Address - {crypto_payment.coin_type}" - explanation = "We were unable to process a refund for your payment because no refund address was configured. Please contact support if you need assistance." + explanation = "We were unable to process a refund for your payment because no refund address was configured." has_fee = False # No refund means no fee calculation else: @@ -422,8 +422,6 @@ Refund Details: {fee_note} Please allow up to 10 confirmations for the refund to be fully processed. - -If you have any questions, please contact support with your payment ID: {crypto_payment.id} """ # Build the HTML message @@ -462,10 +460,6 @@ If you have any questions, please contact support with your payment ID: {crypto_

Please allow up to 10 confirmations for the refund to be fully processed.

- -

- If you have any questions, please contact support with your payment ID: {crypto_payment.id} -

"""