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