332 lines
13 KiB
Python
Executable File
332 lines
13 KiB
Python
Executable File
import json
|
||
import traceback
|
||
from decimal import Decimal
|
||
from django.conf import settings
|
||
from django.views.decorators.csrf import csrf_exempt
|
||
from django.http import JsonResponse
|
||
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
|
||
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
|
||
from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel
|
||
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
|
||
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
|
||
from alipay.aop.api.request.AlipayTradeAppPayRequest import AlipayTradeAppPayRequest
|
||
from .models import Order, User, Plan
|
||
from datetime import datetime
|
||
from alipay.aop.api.domain.AlipayTradeWapPayModel import AlipayTradeWapPayModel
|
||
from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayRequest
|
||
import requests
|
||
from decimal import Decimal, getcontext
|
||
|
||
# 设置 Decimal 的精度
|
||
getcontext().prec = 28
|
||
|
||
|
||
def get_usd_to_cny_rate():
|
||
a = 7.2
|
||
return a
|
||
|
||
|
||
|
||
|
||
def get_alipay_client_config(client_type='pc'):
|
||
"""
|
||
根据环境和客户端类型返回支付宝配置
|
||
"""
|
||
alipay_client_config = AlipayClientConfig()
|
||
|
||
if settings.ALIPAY_DEBUG:
|
||
print('请求沙箱支付')
|
||
alipay_client_config.server_url = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do'
|
||
if client_type == 'pc':
|
||
alipay_client_config.app_id = settings.ALIPAY_APP_ID_SANDBOX_PC
|
||
alipay_client_config.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_SANDBOX_PC
|
||
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_SANDBOX_PC
|
||
elif client_type == 'mobile':
|
||
alipay_client_config.app_id = settings.ALIPAY_APP_ID_SANDBOX_MOBILE
|
||
alipay_client_config.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_SANDBOX_MOBILE
|
||
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_SANDBOX_MOBILE
|
||
else:
|
||
print('请求生产支付')
|
||
alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
|
||
if client_type == 'pc':
|
||
alipay_client_config.app_id = settings.ALIPAY_APP_ID_PRODUCTION_PC
|
||
alipay_client_config.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_PRODUCTION_PC
|
||
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_PRODUCTION_PC
|
||
elif client_type == 'mobile':
|
||
alipay_client_config.app_id = settings.ALIPAY_APP_ID_PRODUCTION_MOBILE
|
||
alipay_client_config.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_PRODUCTION_MOBILE
|
||
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_PRODUCTION_MOBILE
|
||
|
||
return alipay_client_config
|
||
def convert_cny_to_usd(price_cny):
|
||
"""
|
||
将人民币价格转换为美元价格
|
||
:param price_cny: 人民币价格
|
||
:return: 美元价格,保留两位小数
|
||
"""
|
||
# 获取美元对人民币的汇率
|
||
usd_to_cny_rate = get_usd_to_cny_rate()
|
||
print("原价:", price_cny)
|
||
print("汇率:", usd_to_cny_rate)
|
||
return (Decimal(price_cny) * Decimal(usd_to_cny_rate)).quantize(Decimal('0.00'))
|
||
@csrf_exempt
|
||
def create_alipay_order(request):
|
||
"""
|
||
创建支付宝订单并生成支付链接(电脑端)
|
||
"""
|
||
try:
|
||
# 检查用户是否已登录
|
||
if not request.user.is_authenticated:
|
||
print("用户未登录")
|
||
return JsonResponse({"code": 401, "message": "用户未登录"})
|
||
|
||
# 获取前端传递的数据
|
||
data = json.loads(request.body)
|
||
print(f"接收到的请求数据: {data}")
|
||
plan_title = data.get('title')
|
||
price = Decimal(data.get('price')) # 将价格转换为 Decimal 类型
|
||
plan_id = data.get('id')
|
||
|
||
# 从数据库获取对应的套餐信息
|
||
plan = Plan.objects.get(id=plan_id)
|
||
print(f"数据库中获取的套餐信息: {plan}")
|
||
if not plan:
|
||
print("套餐信息不存在")
|
||
return JsonResponse({"code": 400, "message": "套餐信息不存在"})
|
||
|
||
# 检查汇率是否有效
|
||
# 获取美元对人民币的汇率
|
||
usd_to_cny_rate = get_usd_to_cny_rate()
|
||
if not usd_to_cny_rate:
|
||
print("无法进行价格转换,汇率不可用")
|
||
return JsonResponse({"code": 500, "message": "汇率不可用,无法创建订单"})
|
||
|
||
# 将套餐的人民币价格转换为美元价格
|
||
plan_price_usd = convert_cny_to_usd(plan.price)
|
||
|
||
# 将人民币价格转换为美元价格
|
||
price_usd = convert_cny_to_usd(price)
|
||
|
||
# 比较转换后的美元价格
|
||
if not price_usd or plan_price_usd != price_usd:
|
||
print(f"{plan_price_usd}套餐信息不匹配或价格不正确{price_usd}")
|
||
return JsonResponse({"code": 400, "message": "套餐信息不匹配或价格不正确"})
|
||
|
||
final_price = price_usd
|
||
|
||
# 获取当前用户信息
|
||
user = request.user
|
||
|
||
# 生成订单
|
||
order_id = f"order_{datetime.now().strftime('%Y%m%d%H%M%S')}_{user.id}"
|
||
order = Order.objects.create(
|
||
order_id=order_id,
|
||
user=user,
|
||
plan=plan,
|
||
username=user.username,
|
||
amount=final_price,
|
||
payment_method='alipay',
|
||
status='pending'
|
||
)
|
||
print(f"创建的订单信息: {order}")
|
||
|
||
# 获取支付宝电脑端客户端配置
|
||
alipay_client_config = get_alipay_client_config(client_type='pc')
|
||
client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
|
||
|
||
# 构造支付宝支付请求对象
|
||
model = AlipayTradePagePayModel()
|
||
model.out_trade_no = order_id # 订单ID
|
||
model.total_amount = str(final_price) # 订单金额
|
||
model.subject = plan_title # 商品名称
|
||
model.product_code = "FAST_INSTANT_TRADE_PAY" # 产品码
|
||
print(f"生成订单请求体: {model}")
|
||
|
||
request = AlipayTradePagePayRequest(biz_model=model)
|
||
request.notify_url = settings.ALIPAY_NOTIFY_URL # 异步通知URL
|
||
request.return_url = settings.ALIPAY_RETURN_URL # 同步返回URL
|
||
print(f"回调地址: {settings.ALIPAY_NOTIFY_URL}")
|
||
|
||
response = client.page_execute(request, http_method="GET")
|
||
print(f"支付宝请求响应: {response}")
|
||
|
||
# 返回支付URL
|
||
return JsonResponse({"code": 200, "message": "订单创建成功", "data": {'alipay_url': response}})
|
||
|
||
except Exception as e:
|
||
print(f"创建订单时出错: {traceback.format_exc()}")
|
||
return JsonResponse({"code": 500, "message": f"创建订单时出错: {str(e)}"})
|
||
|
||
@csrf_exempt
|
||
def create_alipay_h5_order(request):
|
||
"""
|
||
创建支付宝H5订单并生成支付链接
|
||
"""
|
||
try:
|
||
# 检查用户是否已登录
|
||
if not request.user.is_authenticated:
|
||
return JsonResponse({"code": 401, "message": "用户未登录"})
|
||
|
||
# 获取前端传递的数据
|
||
data = json.loads(request.body)
|
||
plan_id = data.get('id')
|
||
plan_title = data.get('title')
|
||
price = Decimal(data.get('price')) # 将价格转换为 Decimal 类型
|
||
|
||
# 获取对应套餐信息
|
||
plan = Plan.objects.get(id=plan_id)
|
||
if not plan:
|
||
return JsonResponse({"code": 400, "message": "套餐信息不存在"})
|
||
|
||
# 检查汇率是否有效
|
||
# 获取美元对人民币的汇率
|
||
usd_to_cny_rate = get_usd_to_cny_rate()
|
||
if not usd_to_cny_rate:
|
||
print("无法进行价格转换,汇率不可用")
|
||
return JsonResponse({"code": 500, "message": "汇率不可用,无法创建订单"})
|
||
|
||
# 将套餐的人民币价格转换为美元价格
|
||
plan_price_usd = convert_cny_to_usd(plan.price)
|
||
|
||
# 将人民币价格转换为美元价格
|
||
price_usd = convert_cny_to_usd(price)
|
||
|
||
# 比较转换后的美元价格
|
||
if not price_usd or plan_price_usd != price_usd:
|
||
return JsonResponse({"code": 400, "message": "套餐信息不匹配或价格不正确"})
|
||
|
||
final_price = price_usd
|
||
|
||
# 创建订单
|
||
user = request.user
|
||
order_id = f"order_{datetime.now().strftime('%Y%m%d%H%M%S')}_{user.id}"
|
||
order = Order.objects.create(
|
||
order_id=order_id,
|
||
user=user,
|
||
plan=plan,
|
||
username=user.username,
|
||
amount=final_price,
|
||
payment_method='alipay',
|
||
status='pending'
|
||
)
|
||
|
||
# 获取支付宝H5客户端配置
|
||
alipay_client_config = get_alipay_client_config(client_type='mobile')
|
||
client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
|
||
|
||
# 构造支付宝H5支付请求对象
|
||
model = AlipayTradeWapPayModel()
|
||
model.out_trade_no = order_id
|
||
model.total_amount = str(final_price)
|
||
model.subject = plan_title
|
||
model.product_code = "QUICK_WAP_WAY"
|
||
|
||
request = AlipayTradeWapPayRequest(biz_model=model)
|
||
request.notify_url = settings.ALIPAY_NOTIFY_URL # 异步通知URL
|
||
request.return_url = settings.ALIPAY_RETURN_URL # 同步返回URL
|
||
|
||
# 执行请求,获取支付宝H5支付跳转链接
|
||
response = client.page_execute(request, http_method="GET")
|
||
|
||
return JsonResponse({"code": 200, "message": "订单创建成功", "data": {'alipay_url': response}})
|
||
|
||
except Exception as e:
|
||
return JsonResponse({"code": 500, "message": f"创建H5订单时出错: {str(e)}"})
|
||
|
||
|
||
def update_user_membership(order):
|
||
"""
|
||
更新用户积分信息
|
||
"""
|
||
try:
|
||
# 验证订单状态是否为成功
|
||
if order.status not in ("completed", "TRADE_SUCCESS", "TRADE_FINISHED"):
|
||
print("订单未成功")
|
||
return False
|
||
|
||
# 从订单中获取关联的套餐信息
|
||
plan = order.plan
|
||
|
||
# 检查汇率是否有效
|
||
# 获取美元对人民币的汇率
|
||
usd_to_cny_rate = get_usd_to_cny_rate()
|
||
if not usd_to_cny_rate:
|
||
print("无法进行价格转换,汇率不可用")
|
||
return False
|
||
|
||
# 将订单金额从美元转换回人民币
|
||
order_amount_cny = order.amount
|
||
|
||
# 确保订单的套餐与实际套餐详情匹配
|
||
if not plan or convert_cny_to_usd(plan.price) != order_amount_cny:
|
||
print(f"订单的套餐信息与实际套餐不匹配plan.price{plan.price}---")
|
||
return False
|
||
|
||
# 获取用户信息
|
||
user = order.user
|
||
|
||
# 更新用户的积分
|
||
user.points += plan.credits_per_month
|
||
user.save()
|
||
|
||
print(f"用户 {user.username} 的积分已更新")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"更新用户积分信息时出错: {e}")
|
||
return False
|
||
|
||
@csrf_exempt
|
||
def alipay_notify(request):
|
||
"""
|
||
支付宝异步通知处理,验证支付结果
|
||
"""
|
||
try:
|
||
data = request.POST.dict()
|
||
print(f"接收到的异步通知数据: {data}")
|
||
|
||
# 直接使用返回的数据处理逻辑
|
||
order_id = data.get('out_trade_no')
|
||
trade_status = data.get('trade_status')
|
||
print(f"订单ID: {order_id}, 支付状态: {trade_status}")
|
||
|
||
try:
|
||
order = Order.objects.get(order_id=order_id)
|
||
except Order.DoesNotExist:
|
||
print(f"订单 {order_id} 不存在")
|
||
return JsonResponse({"code": 400, "message": "订单不存在"})
|
||
|
||
if trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
|
||
order.status = 'completed'
|
||
order.save()
|
||
print(f"订单 {order_id} 支付成功")
|
||
update_user_membership(order)
|
||
return JsonResponse({"code": 200, "message": "支付成功"})
|
||
else:
|
||
order.status = 'failed'
|
||
order.save()
|
||
print(f"订单 {order_id} 支付失败或未成功")
|
||
return JsonResponse({"code": 400, "message": "支付未成功或失败"})
|
||
|
||
except Exception as e:
|
||
print(f"处理异步通知时出错: {traceback.format_exc()}")
|
||
return JsonResponse({"code": 500, "message": f"处理异步通知时出错: {str(e)}"})
|
||
|
||
@csrf_exempt
|
||
def alipay_return(request):
|
||
"""
|
||
支付宝同步返回处理,用于用户支付后的页面跳转
|
||
"""
|
||
try:
|
||
data = request.GET.dict()
|
||
print(f"接收到的同步返回数据: {data}")
|
||
|
||
# 直接使用返回的数据处理逻辑
|
||
order_id = data.get('out_trade_no')
|
||
print(f"订单 {order_id} 支付成功")
|
||
return JsonResponse({"code": 200, "message": f"支付成功,订单号:{order_id}"})
|
||
|
||
except Exception as e:
|
||
print(f"处理同步返回时出错: {traceback.format_exc()}")
|
||
return JsonResponse({"code": 500, "message": f"处理同步返回时出错: {str(e)}"})
|