332 lines
13 KiB
Python
332 lines
13 KiB
Python
![]() |
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)}"})
|