2024-09-23 17:56:57 +08:00
|
|
|
|
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": {}})
|