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 from .base import logger def get_paypal_access_token(): """获取 PayPal 访问令牌""" print("正在获取 PayPal 访问令牌...") url = f"https://api.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') logger.info(f"获取的访问令牌: {access_token}") print(f"获取的访问令牌: {access_token}") return access_token else: logger.error(f"获取 PayPal 访问令牌失败: {response.text}") print(f"获取 PayPal 访问令牌失败: {response.text}") return None def verify_paypal_webhook(headers, body): """使用 PayPal API 验证 Webhook 签名""" logger.info("验证 PayPal Webhook 签名...") print("验证 PayPal Webhook 签名...") verify_url = f"https://api.paypal.com/v1/notifications/verify-webhook-signature" auth_token = get_paypal_access_token() if not auth_token: logger.error("无法获取 PayPal 访问令牌,无法验证 Webhook 签名") 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': logger.info("Webhook 签名验证成功") print("Webhook 签名验证成功") return True else: logger.error(f"Webhook 签名验证失败: {response.text}") print(f"Webhook 签名验证失败: {response.text}") return False @csrf_exempt def execute_paypal_payment(payment_id, payer_id): """执行 PayPal 支付""" try: logger.info(f"回调接收到 开始执行支付,订单ID: {payment_id}, 付款人ID: {payer_id}") order = Order.objects.get(order_id=payment_id) if order.status == 'completed': logger.info(f"订单 {payment_id} 已经支付,不需要再次执行支付") print(f"订单 {payment_id} 已经支付,不需要再次执行支付") return JsonResponse({"code": 400, "message": "订单已支付", "data": {}}) # 获取 PayPal 访问令牌 access_token = get_paypal_access_token() if not access_token: logger.error("无法获取支付访问令牌") return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}}) # 根据配置文件选择 PayPal URL paypal_base_url = "https://api.sandbox.paypal.com" if settings.PAYPAL_MODE == 'sandbox' else "https://api.paypal.com" url = f"{paypal_base_url}/v1/payments/payment/{payment_id}/execute" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {access_token}" } payload = { "payer_id": payer_id } logger.info(f"执行 PayPal 支付请求, URL: {url}, 请求体: {payload}") print(f"执行 PayPal 支付请求, URL: {url}, 请求体: {payload}") response = requests.post(url, headers=headers, json=payload) # 处理支付执行结果 if response.status_code == 200: order.status = 'completed' order.updated_at = datetime.now() order.save() # 更新用户积分 user = order.user user.points += order.plan.credits_per_month user.save() logger.info(f"订单 {payment_id} 支付成功,用户积分已更新: {order.plan.credits_per_month}") print(f"订单 {payment_id} 支付成功,用户积分已更新: {order.plan.credits_per_month}") return JsonResponse({"code": 200, "message": "支付成功", "data": {}}) else: logger.error(f"支付执行失败: {response.text}") print(f"支付执行失败: {response.text}") return JsonResponse({"code": 400, "message": "支付执行失败", "data": response.json()}) except Order.DoesNotExist: logger.error(f"订单 {payment_id} 不存在") print(f"订单 {payment_id} 不存在") return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) except Exception as e: logger.error(f"支付执行过程中发生异常: {str(e)}") print(f"支付执行过程中发生异常: {str(e)}") return JsonResponse({"code": 500, "message": f"支付执行过程中发生异常: {str(e)}", "data": {}}) @csrf_exempt def paypal_webhook(request): """PayPal Webhook 处理函数,用于接收和处理 PayPal 异步通知""" try: logger.info("接收到 PayPal Webhook 请求...") print("接收到 PayPal Webhook 请求...") headers = request.headers payload = json.loads(request.body) # 验证 Webhook 签名 if not verify_paypal_webhook(headers, request.body): logger.error("Webhook 签名验证失败") return JsonResponse({"code": 400, "message": "Webhook 签名验证失败", "data": {}}) event_type = payload.get('event_type') # 打印接收到的 Webhook payload 以供调试 logger.info(f"接收到 PayPal Webhook 事件: {event_type}") print(f"接收到 PayPal Webhook 事件: {event_type}") logger.info(f"Webhook payload: {json.dumps(payload, indent=2)}") # 处理不同类型的事件 if event_type == 'PAYMENT.SALE.COMPLETED': parent_payment = payload['resource'].get('parent_payment') # 使用 parent_payment 作为订单 ID state = payload['resource'].get('state') # 获取支付状态 if parent_payment: try: order = Order.objects.get(order_id=parent_payment) # 通过 state 判断支付是否完成 if state == 'completed': if order.status == 'completed': logger.info(f"订单 {parent_payment} 已经完成,不重复更新") print(f"订单 {parent_payment} 已经完成,不重复更新") return JsonResponse({"code": 200, "message": "订单已处理", "data": {}}) # 更新订单状态为已完成 order.status = 'completed' order.updated_at = datetime.now() order.save() # 更新用户积分 user = order.user user.points += order.plan.credits_per_month user.save() logger.info(f"订单 {parent_payment} 状态更新为 'completed' 并已更新用户积分") print(f"订单 {parent_payment} 状态更新为 'completed' 并已更新用户积分") return JsonResponse({"code": 200, "message": "支付完成,订单更新成功", "data": {}}) else: logger.error(f"支付状态为 {state},未完成支付") print(f"支付状态为 {state},未完成支付") return JsonResponse({"code": 400, "message": f"支付状态为 {state},未完成支付", "data": {}}) except Order.DoesNotExist: logger.error(f"未找到订单 {parent_payment}") print(f"未找到订单 {parent_payment}") return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) else: logger.error("Webhook payload 中缺少 parent_payment") print("Webhook payload 中缺少 parent_payment") return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}}) elif event_type == 'PAYMENTS.PAYMENT.CREATED': # 支付创建时的逻辑,可以执行支付 payment_id = payload['resource']['id'] payer_id = payload['resource']['payer']['payer_info']['payer_id'] execute_paypal_payment(payment_id, payer_id) elif event_type == 'PAYMENT.SALE.DENIED': 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() logger.info(f"订单 {parent_payment} 状态更新为 'failed'") print(f"订单 {parent_payment} 状态更新为 'failed'") except Order.DoesNotExist: logger.error(f"未找到订单 {parent_payment}") print(f"未找到订单 {parent_payment}") return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) return JsonResponse({"code": 200, "message": "Webhook 处理成功", "data": {}}) except json.JSONDecodeError: logger.error("请求体不是有效的JSON") print("请求体不是有效的JSON") return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}}) except Exception as e: logger.error(f"处理 PayPal Webhook 时发生错误: {str(e)}") print(f"处理 PayPal Webhook 时发生错误: {str(e)}") return JsonResponse({"code": 500, "message": f"处理 PayPal Webhook 时发生错误: {str(e)}", "data": {}})