import requests from django.conf import settings from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from datetime import datetime from .models import Order import json import logging logger = logging.getLogger(__name__) def get_paypal_access_token(): """获取 PayPal 访问令牌""" print("正在获取 PayPal 访问令牌...") url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/oauth2/token" auth = ( settings.PAYPAL_CLIENT_ID_SANDBOX if settings.PAYPAL_MODE == 'sandbox' else settings.PAYPAL_CLIENT_ID_PRODUCTION, settings.PAYPAL_SECRET_KEY_SANDBOX if settings.PAYPAL_MODE == 'sandbox' else settings.PAYPAL_SECRET_KEY_PRODUCTION ) headers = { "Accept": "application/json", "Accept-Language": "en_US" } data = { "grant_type": "client_credentials" } response = requests.post(url, headers=headers, data=data, auth=auth) if response.status_code == 200: access_token = response.json().get('access_token') print(f"获取的访问令牌: {access_token}") return access_token else: print(f"获取 PayPal 访问令牌失败: {response.text}") return None def verify_paypal_webhook(headers, body): """使用 PayPal API 验证 Webhook 签名""" print("验证 PayPal Webhook 签名...") verify_url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/notifications/verify-webhook-signature" auth_token = get_paypal_access_token() if not auth_token: print("无法获取 PayPal 访问令牌,无法验证 Webhook 签名") return False verify_headers = { "Content-Type": "application/json", "Authorization": f"Bearer {auth_token}" } verify_payload = { "auth_algo": headers.get('Paypal-Auth-Algo'), "cert_url": headers.get('Paypal-Cert-Url'), "transmission_id": headers.get('Paypal-Transmission-Id'), "transmission_sig": headers.get('Paypal-Transmission-Sig'), "transmission_time": headers.get('Paypal-Transmission-Time'), "webhook_id": settings.PAYPAL_WEBHOOK_ID, "webhook_event": json.loads(body) } response = requests.post(verify_url, headers=verify_headers, json=verify_payload) if response.status_code == 200 and response.json().get('verification_status') == 'SUCCESS': print("Webhook 签名验证成功") return True else: print(f"Webhook 签名验证失败: {response.text}") return False @csrf_exempt def paypal_webhook(request): """PayPal Webhook 处理函数,用于接收和处理 PayPal 异步通知""" try: print("接收到 PayPal Webhook 请求...") headers = request.headers payload = json.loads(request.body) # 验证 Webhook 签名 if not verify_paypal_webhook(headers, request.body): return JsonResponse({"code": 400, "message": "Webhook 签名验证失败", "data": {}}) event_type = payload.get('event_type') # 打印接收到的 Webhook payload 以供调试 print(f"接收到 PayPal Webhook 事件: {event_type}") print(f"Webhook payload: {json.dumps(payload, indent=2)}") # 处理不同类型的事件 if event_type == 'PAYMENT.SALE.COMPLETED': sale_id = payload['resource'].get('id') parent_payment = payload['resource'].get('parent_payment') # 使用parent_payment作为订单ID if parent_payment: try: order = Order.objects.get(order_id=parent_payment) # 检查订单状态,防止重复更新 if order.status == 'completed': print(f"订单 {parent_payment} 已经完成,不重复更新") return JsonResponse({"code": 200, "message": "订单已处理", "data": {}}) # 再次向 PayPal 查询订单状态,确保支付确实完成 access_token = get_paypal_access_token() if not access_token: print("无法获取 PayPal 访问令牌,无法验证订单状态") return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}}) payment_details_url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/payments/payment/{parent_payment}" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {access_token}" } payment_details_response = requests.get(payment_details_url, headers=headers) if payment_details_response.status_code == 200: payment_details = payment_details_response.json() if payment_details['state'] == 'approved': order.status = 'completed' order.updated_at = datetime.now() order.save() # 更新用户积分 user = order.user user.points += order.plan.credits_per_month user.save() print(f"订单 {parent_payment} 状态更新为 'completed' 并已更新用户积分") else: print(f"支付未完成,状态: {payment_details['state']}") else: print(f"查询支付状态失败: {payment_details_response.text}") return JsonResponse({"code": 500, "message": "查询支付状态失败", "data": {}}) except Order.DoesNotExist: print(f"未找到订单 {parent_payment}") return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) else: print("Webhook payload 中缺少 parent_payment") return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}}) elif event_type == 'PAYMENT.SALE.DENIED': sale_id = payload['resource'].get('id') parent_payment = payload['resource'].get('parent_payment') if parent_payment: try: order = Order.objects.get(order_id=parent_payment) order.status = 'failed' order.updated_at = datetime.now() order.save() print(f"订单 {parent_payment} 状态更新为 'failed'") except Order.DoesNotExist: print(f"未找到订单 {parent_payment}") return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) else: print("Webhook payload 中缺少 parent_payment") return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}}) return JsonResponse({"code": 200, "message": "Webhook 处理成功", "data": {}}) except json.JSONDecodeError: print("请求体不是有效的JSON") return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}}) except Exception as e: print(f"处理 PayPal Webhook 时发生错误: {e}") return JsonResponse({"code": 500, "message": f"处理 PayPal Webhook 时发生错误: {str(e)}", "data": {}})