Improve webhook handling and validation logic

Supporting multiple webhook secrets and enhanced Connect account
validation. Added more detailed logging and improved error handling for
payment processing.
main
Razmig Sarkissian 4 weeks ago
parent 908c0b7dc8
commit 7c1c37746c
  1. 97
      tournaments/services/payment_service.py

@ -593,31 +593,75 @@ class PaymentService:
payload = request.body payload = request.body
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE') sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
# Check if this is a Connect account webhook # Check if this is a Connect account webhook (header method - for direct API calls)
stripe_account = request.META.get('HTTP_STRIPE_ACCOUNT') stripe_account_header = request.META.get('HTTP_STRIPE_ACCOUNT')
print("=== WEBHOOK DEBUG ===") print("=== WEBHOOK DEBUG ===")
print(f"Signature: {sig_header}") print(f"Signature: {sig_header}")
print(f"Connect Account: {stripe_account}") print(f"Connect Account Header: {stripe_account_header}")
if stripe_account: # First, try to construct the event with any available webhook secret to inspect the payload
# This is a connected account (regular umpire tournament) webhook_secrets = []
webhook_secret = settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET if hasattr(settings, 'XLR_STRIPE_WEBHOOK_SECRET') and settings.XLR_STRIPE_WEBHOOK_SECRET:
print(f"Using umpire webhook secret for connected account: {stripe_account}") webhook_secrets.append(('XLR', settings.XLR_STRIPE_WEBHOOK_SECRET))
if hasattr(settings, 'TOURNAMENT_STRIPE_WEBHOOK_SECRET') and settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET:
webhook_secrets.append(('TOURNAMENT', settings.TOURNAMENT_STRIPE_WEBHOOK_SECRET))
if hasattr(settings, 'SHOP_STRIPE_WEBHOOK_SECRET') and settings.SHOP_STRIPE_WEBHOOK_SECRET:
webhook_secrets.append(('SHOP', settings.SHOP_STRIPE_WEBHOOK_SECRET))
print(f"Available webhook secrets: {[name for name, _ in webhook_secrets]}")
event = None
used_secret = None
# Try to verify with each secret to get the event payload
for secret_name, secret_value in webhook_secrets:
try:
print(f"Trying {secret_name} webhook secret...")
event = stripe.Webhook.construct_event(payload, sig_header, secret_value)
used_secret = secret_name
print(f"SUCCESS: Webhook verified with {secret_name} secret")
break
except stripe.error.SignatureVerificationError as e:
print(f"Failed with {secret_name} secret: {str(e)}")
continue
if not event:
print("ERROR: No webhook secret worked")
return HttpResponse("Webhook signature verification failed", status=400)
# Now check if this is a Connect webhook by looking at the payload
connect_account_id = event.get('account') # This is how Connect webhooks are identified
print(f"Connect Account ID from payload: {connect_account_id}")
# Log webhook details
print(f"Event ID: {event.get('id')}")
print(f"Event Type: {event.get('type')}")
print(f"Live Mode: {event.get('livemode')}")
# Determine if this should have used a different webhook secret based on the account
if connect_account_id:
print(f"This is a Connect webhook from account: {connect_account_id}")
# Check if the account matches the expected tournament account
if connect_account_id == "acct_1S0jbSAs9xuFLROy":
print("This matches the expected tournament Connect account")
if used_secret != 'TOURNAMENT':
print(f"WARNING: Used {used_secret} secret but should probably use TOURNAMENT secret")
else:
print(f"Unknown Connect account: {connect_account_id}")
else: else:
# This is platform account (corporate tournament) print("This is a platform/direct webhook (no Connect account)")
webhook_secret = settings.XLR_STRIPE_WEBHOOK_SECRET if used_secret != 'XLR':
print("Using XLR company webhook secret") print(f"WARNING: Used {used_secret} secret but should probably use XLR secret")
try: try:
event = stripe.Webhook.construct_event(payload, sig_header, webhook_secret) # Process the webhook event
print(f"Tournament webhook event type: {event['type']}")
# Debug metadata
stripe_object = event['data']['object'] stripe_object = event['data']['object']
metadata = stripe_object.get('metadata', {}) metadata = stripe_object.get('metadata', {})
print(f"is_corporate_tournament: {metadata.get('is_corporate_tournament', 'unknown')}") print(f"is_corporate_tournament: {metadata.get('is_corporate_tournament', 'unknown')}")
print(f"payment_source: {metadata.get('payment_source', 'unknown')}") print(f"payment_source: {metadata.get('payment_source', 'unknown')}")
print(f"stripe_account_type: {metadata.get('stripe_account_type', 'unknown')}")
print(f"stripe_account_id: {metadata.get('stripe_account_id', 'unknown')}")
if event['type'] == 'checkout.session.completed': if event['type'] == 'checkout.session.completed':
success = PaymentService.process_direct_payment(stripe_object) success = PaymentService.process_direct_payment(stripe_object)
@ -628,33 +672,20 @@ class PaymentService:
print(f"Failed to process completed checkout session") print(f"Failed to process completed checkout session")
return HttpResponse(status=400) return HttpResponse(status=400)
elif event['type'] == 'payment_intent.payment_failed': # Handle other event types if needed
success = PaymentService.process_failed_payment_intent(stripe_object) elif event['type'] == 'payment_intent.succeeded':
if success: print(f"Payment intent succeeded - you might want to handle this")
print(f"Successfully processed failed payment intent") return HttpResponse(status=200)
return HttpResponse(status=200)
else:
print(f"Failed to process failed payment intent")
return HttpResponse(status=400)
elif event['type'] == 'checkout.session.expired':
success = PaymentService.process_expired_checkout_session(stripe_object)
if success:
print(f"Successfully processed expired checkout session")
return HttpResponse(status=200)
else:
print(f"Failed to process expired checkout session")
return HttpResponse(status=400)
else: else:
print(f"Unhandled event type: {event['type']}") print(f"Unhandled event type: {event['type']}")
return HttpResponse(status=200) return HttpResponse(status=200)
except Exception as e: except Exception as e:
print(f"Webhook error: {str(e)}") print(f"Error processing webhook: {str(e)}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
return HttpResponse(status=400) return HttpResponse("Webhook processing failed", status=500)
@staticmethod @staticmethod
def create_payment_link(team_registration_id): def create_payment_link(team_registration_id):

Loading…
Cancel
Save