import pymysql import sys import json import random import threading import logging import stripe from sqlalchemy import func from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime from typing import List, Dict, Union, Any from stripe_payment_processor import StripePaymentProcessor from config import Config from app import create_app, db from models import Payments, PaymentBatch, SinglePayments, PaymentPlans from splynx import Splynx, SPLYNX_URL, SPLYNX_KEY, SPLYNX_SECRET api_key = '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' stripe.api_key = api_key def issue_refund_for_payment(payment_record, amount=None, reason=None): """ Issue a refund for a payment record using its Stripe_Charge_ID Args: payment_record: The Payments model instance amount: Optional - amount to refund in cents. If None, refunds full amount reason: Optional - reason for refund ('duplicate', 'fraudulent', 'requested_by_customer') Returns: dict: Success status and refund details """ try: # Check if payment has a charge ID if not payment_record.Stripe_Charge_ID: return { 'success': False, 'error': 'No Stripe Charge ID found for this payment', 'payment_id': payment_record.id } # Check if already refunded if payment_record.Refund: return { 'success': False, 'error': 'Payment already marked as refunded', 'payment_id': payment_record.id } # Create refund parameters refund_params = { 'charge': payment_record.Stripe_Charge_ID } if amount: refund_params['amount'] = amount if reason: refund_params['reason'] = reason # Issue the refund refund = stripe.Refund.create(**refund_params) if refund['status'] == "succeeded": # Update the payment record payment_record.Refund = True payment_record.Stripe_Refund_ID = refund.id payment_record.Stripe_Refund_Created = datetime.fromtimestamp(refund.created) payment_record.Refund_JSON = json.dumps(refund) db.session.commit() return { 'success': True, 'refund_id': refund.id, 'amount_refunded': refund.amount, 'payment_id': payment_record.id, 'refund': refund } else: payment_record.Refund = False # Commit the changes db.session.commit() return { 'success': False, 'payment_id': payment_record.id, 'refund': refund } except stripe.error.InvalidRequestError as e: if "has already been refunded" in str(e) and payment_record.Refund != True: payment_record.Refund = True payment_record.Stripe_Refund_Created = datetime.now() db.session.commit() return { 'success': False, 'error': f'Stripe error: {str(e)}', 'payment_id': payment_record.id } except Exception as e: db.session.rollback() return { 'success': False, 'error': f'Unexpected error: {str(e)}', 'payment_id': payment_record.id } if __name__ == "__main__": app = create_app() with app.app_context(): # Find customer_ids that appear more than once duplicate_customer_ids = db.session.query( Payments.Stripe_Customer_ID ).group_by( Payments.Stripe_Customer_ID ).having( func.count(Payments.Stripe_Customer_ID) > 1 ).all() # Get the actual duplicate records duplicate_records = db.session.query(Payments).filter( Payments.Stripe_Customer_ID.in_([row[0] for row in duplicate_customer_ids]) ).order_by(Payments.Stripe_Customer_ID).all() i = 0 has_charge = 0 for a in duplicate_records: i += 1 #print(a.Stripe_Customer_ID) if a.Stripe_Charge_ID != None: has_charge += 1 print(i, i/2, has_charge, has_charge/2) from sqlalchemy import func, and_ # Step 1: Find customer_ids that appear more than once duplicate_customer_ids = db.session.query( Payments.Stripe_Customer_ID ).group_by( Payments.Stripe_Customer_ID ).having( func.count(Payments.Stripe_Customer_ID) > 1 ).all() # Step 2: Find customer_ids that already have any refunded payments customers_with_refunds = db.session.query( Payments.Stripe_Customer_ID ).filter( and_( Payments.Stripe_Customer_ID.in_([row[0] for row in duplicate_customer_ids]), Payments.Refund == True ) ).distinct().all() # Step 3: Get customer_ids to process (duplicates minus those with existing refunds) customers_to_refund = [ customer_id for customer_id in [row[0] for row in duplicate_customer_ids] if customer_id not in [row[0] for row in customers_with_refunds] ] print(f"Total duplicate customers: {len(duplicate_customer_ids)}") print(f"Customers already with refunds: {len(customers_with_refunds)}") print(f"Customers eligible for refund: {len(customers_to_refund)}") # Step 4: Get the first record for each eligible customer if customers_to_refund: ranked_duplicates = db.session.query( Payments.id, func.row_number().over( partition_by=Payments.Stripe_Customer_ID, order_by=Payments.id ).label('rn') ).filter( Payments.Stripe_Customer_ID.in_(customers_to_refund) ).subquery() # Get only the first record (rn = 1) for each customer_id first_duplicates = db.session.query(Payments).join( ranked_duplicates, Payments.id == ranked_duplicates.c.id ).filter(ranked_duplicates.c.rn == 1).order_by(Payments.Stripe_Customer_ID).all() else: first_duplicates = [] # Now process refunds safely results = [] for payment in first_duplicates: if payment.Stripe_Charge_ID: print(f"Safe to refund - Payment ID: {payment.id}, Customer: {payment.Stripe_Customer_ID}, Charge: {payment.Stripe_Charge_ID}") #result = issue_refund_for_payment( # payment, # reason='duplicate' #) #results.append(result) #if result['success']: # print(f"✓ Refund successful: {result['refund_id']}") #else: # print(f"✗ Refund failed: {result['error']}") else: print(f"Skipping Payment ID {payment.id} - No Stripe Charge ID") print("\n\n\n") print("="*60) print("\n RESULTS\n\n") print(json.dumps(results,indent=2))