238 lines
11 KiB
Python
Executable File
238 lines
11 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
|
||
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": {}})
|