ai_admin/WebAdmin/paypal_webhook.py
2024-09-20 04:29:09 +00:00

166 lines
7.5 KiB
Python
Executable File

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": {}})