Ai_Admin/WebAdmin/paypal_payment.py
2024-09-23 09:56:57 +00:00

266 lines
12 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, Plan, User
import json
from .base import logger
def get_paypal_access_token():
"""获取 PayPal 访问令牌"""
print("正在获取 PayPal 访问令牌...")
# 根据配置文件选择 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/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
@csrf_exempt
def create_paypal_payment(request):
"""创建 PayPal 支付"""
if request.method == 'POST':
try:
print("创建 PayPal 支付开始...")
data = json.loads(request.body)
plan_id = data.get('plan_id')
payment_method = data.get('payment_method', 'paypal')
# 获取当前用户信息
user = request.user
# 验证用户和计划是否存在
if not user.is_authenticated:
print("用户未认证")
return JsonResponse({"code": 401, "message": "用户未认证", "data": {}})
try:
plan = Plan.objects.get(id=plan_id)
print(f"找到的套餐: {plan.title}")
except Plan.DoesNotExist:
print("套餐不存在")
return JsonResponse({"code": 400, "message": "套餐不存在", "data": {}})
access_token = get_paypal_access_token()
if not access_token:
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
# 配置支付请求
url = f"https://api.paypal.com/v1/payments/payment"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}"
}
payload = {
"intent": "sale",
"payer": {"payment_method": "paypal"},
"redirect_urls": {
"return_url": f"{settings.PAYPAL_RETURN_URL}",
"cancel_url": f"{settings.PAYPAL_CANCEL_URL}"
},
"transactions": [{
"item_list": {
"items": [{
"name": plan.title,
"sku": str(plan.id),
"price": str(plan.price),
"currency": "USD",
"quantity": 1
}]
},
"amount": {
"total": str(plan.price),
"currency": "USD"
},
"description": f"购买 {plan.title} 计划"
}]
}
print(f"支付请求Payload: {json.dumps(payload, indent=2)}")
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 201:
payment_id = response.json().get('id')
approval_url = None
for link in response.json().get('links', []):
if link.get('rel') == "approval_url":
approval_url = link.get('href')
break
# 检查订单是否已经存在,防止重复创建订单
if Order.objects.filter(order_id=payment_id).exists():
return JsonResponse({"code": 400, "message": "订单已存在", "data": {}})
# 创建订单并使用PayPal返回的paymentId作为order_id
order = Order.objects.create(
order_id=payment_id, # 使用PayPal的支付ID作为订单ID
user=user,
username=user.username,
plan=plan,
amount=plan.price,
payment_method=payment_method,
status='pending'
)
print(f"订单创建成功订单ID: {order.order_id}")
if approval_url:
print(f"支付批准URL: {approval_url}")
return JsonResponse({"code": 200, "message": "支付创建成功", "data": {"approval_url": approval_url}})
else:
print("未找到支付批准URL")
return JsonResponse({"code": 400, "message": "未找到支付批准URL", "data": {}})
else:
print(f"支付创建失败: {response.status_code}, {response.json()}")
return JsonResponse({"code": 400, "message": "支付创建失败", "data": response.json()})
except json.JSONDecodeError:
print("请求体不是有效的JSON")
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}})
except Exception as e:
print(f"支付创建过程中发生异常: {str(e)}")
return JsonResponse({"code": 500, "message": f"支付创建过程中发生异常: {str(e)}", "data": {}})
else:
return JsonResponse({"code": 405, "message": "方法不允许", "data": {}})
@csrf_exempt
def execute_paypal_payment(request):
"""执行 PayPal 支付"""
if request.method == 'POST':
try:
logger.info("[支付执行] 接收到来自前端的 POST 请求,开始处理支付...")
# 解析请求体
data = json.loads(request.body)
payment_id = data.get('paymentId')
payer_id = data.get('PayerID')
logger.info(f"[支付执行] 请求内容: paymentId={payment_id}, PayerID={payer_id}")
print(f"请求内容: paymentId={payment_id}, PayerID={payer_id}")
if not payment_id or not payer_id:
logger.error("[支付执行] 缺少支付ID或付款人ID")
return JsonResponse({"code": 400, "message": "缺少支付ID或付款人ID", "data": {}})
# 获取订单信息
try:
order = Order.objects.get(order_id=payment_id)
logger.info(f"[支付执行] 获取到的订单信息: {order}")
except Order.DoesNotExist:
logger.error("[支付执行] 订单不存在")
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}})
# 检查订单是否已经支付
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": {}})
logger.info(f"[支付执行] 获取到的 PayPal 访问令牌: {access_token}")
# 设置 PayPal API 请求的 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"[支付执行] 发送支付请求URL: {url}, 请求头: {headers}, 请求体: {payload}")
print(f"发送支付请求URL: {url}, 请求体: {payload}")
# 执行 PayPal 支付请求
response = requests.post(url, headers=headers, json=payload)
# 打印 PayPal API 请求的响应
logger.info(f"[支付执行] PayPal API 响应状态码: {response.status_code}")
print(f"PayPal API 响应状态码: {response.status_code}")
logger.info(f"[支付执行] PayPal API 响应内容: {response.json()}")
print(f"PayPal API 响应内容: {response.json()}")
# 判断支付是否成功
if response.status_code == 200:
payment_data = response.json()
if payment_data['state'] == 'approved':
logger.info(f"[支付执行] 支付成功,订单状态更新为 'approved'")
# 确保订单未完成过
if order.status != 'completed':
order.status = 'completed'
order.updated_at = datetime.now()
order.save()
# 获取用户当前积分并进行累加
user = order.user
current_points = user.points if user.points else 0 # 防止积分为 None
added_points = order.plan.credits_per_month
user.points = current_points + added_points
user.save()
# 打印用户当前积分、购买的套餐和增加的积分
logger.info(f"[支付执行] 用户 {user.username} 当前积分: {current_points}")
logger.info(f"[支付执行] 购买的套餐: {order.plan.title}")
logger.info(f"[支付执行] 增加的积分: {added_points}")
logger.info(f"[支付执行] 用户新的总积分: {user.points}")
print(f"用户 {user.username} 当前积分: {current_points}")
print(f"购买的套餐: {order.plan.title}")
print(f"增加的积分: {added_points}")
print(f"新的总积分: {user.points}")
return JsonResponse({"code": 200, "message": "支付成功,积分已更新", "data": {}})
else:
logger.info(f"[支付执行] 订单 {payment_id} 已支付,无需再次更新积分")
return JsonResponse({"code": 400, "message": "订单已支付,积分不重复增加", "data": {}})
else:
logger.error(f"[支付执行] 支付未完成,状态为: {payment_data['state']}")
print(f"支付未完成,状态为: {payment_data['state']}")
return JsonResponse({"code": 400, "message": "支付状态未完成", "data": payment_data})
else:
logger.error(f"[支付执行] 支付执行失败,状态码: {response.status_code}")
print(f"支付执行失败,状态码: {response.status_code}")
return JsonResponse({"code": 400, "message": "支付执行失败", "data": response.json()})
except json.JSONDecodeError:
logger.error("[支付执行] 请求体不是有效的 JSON 格式")
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}})
except Exception as e:
logger.error(f"[支付执行] 支付执行过程中发生异常: {str(e)}")
print(f"支付执行过程中发生异常: {str(e)}")
return JsonResponse({"code": 500, "message": f"支付执行过程中发生异常: {str(e)}", "data": {}})
else:
logger.error(f"[支付执行] 请求方法 {request.method} 不被允许")
return JsonResponse({"code": 405, "message": "方法不允许", "data": {}})