You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

214 lines
7.3 KiB

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))