最新版本

This commit is contained in:
root 2024-09-23 09:56:57 +00:00
parent 0fca263911
commit 3f369584fb
78 changed files with 43666 additions and 43305 deletions

4
--ini Executable file
View File

@ -0,0 +1,4 @@
unable to find logger stdout
unable to find logger stdout
unable to find logger stdout
unable to find logger stdout

7652
API日志.log Executable file → Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
2024-09-04 13:54:21,364 - API日志 - INFO - {'frameRate': 60, 'resolution': '720p', 'frameDurationMultiplier': 18, 'webhook': 'https://www.typeframes.cc/api/webhook/?userid=10&videoid=1725429259_10', 'creationParams': {'mediaType': 'stockVideo', 'origin': '/create', 'inputText': '宇宙的故事始于138亿年前的一场大爆炸从这一点爆发出无尽的能量和物质逐渐形成了今天我们所见的银河系、恒星和行星。在这一过程中重力将物质聚集成团恒星点燃行星诞生生命在地球上萌芽。然而这一壮丽的宇宙图景可能终将走向衰亡', 'flowType': 'text-to-video', 'slug': 'create-tiktok-video', 'disableCaptions': True, 'hasToGenerateVoice': False, 'hasToTranscript': False, 'hasToSearchMedia': True, 'hasAvatar': False, 'hasWebsiteRecorder': False, 'hasTextSmallAtBottom': False, 'captionPresetName': 'Wrap 1', 'captionPositionName': 'bottom', 'disableAudio': True, 'ratio': '16 / 9', 'selectedAudio': 'Bladerunner 2049', 'selectedVoice': 'zh-CN-YunxiNeural', 'sourceType': 'contentScraping', 'selectedStoryStyle': {'value': 'custom', 'label': 'Custom'}, 'hasToGenerateVideos': False, 'selectedRecording': 'https://www.typeframes.cc/media/63508bd2-3d2a-418b-900b-4de3457f243c.mp3', 'selectedRecordingType': 'audio', 'generationPreset': 'LEONARDO', 'audioUrl': 'https://cdn.tfrv.xyz/audio/_bladerunner-2049.mp3'}}

164
API日志.log.2024-09-20 Normal file
View File

@ -0,0 +1,164 @@
2024-09-21 14:02:29,146 - API日志 - INFO - [支付执行] 获取的访问令牌: A21AAIVwWsRmSYRX_McyZN1RISbr4rs-amzjrc5R8H2rYRMZfH_Ia3QNs4HvEFVKxK2VAEMVe2dWfciYCJ5yhzGlbJgMBDuYQ
2024-09-21 14:02:50,451 - API日志 - INFO - [支付执行] 接收到来自前端的 POST 请求,开始处理支付...
2024-09-21 14:02:50,452 - API日志 - INFO - [支付执行] 请求内容: paymentId=PAYID-M3XGC5I9U46968696522382D, PayerID=8RSF8T3UK8NT2
2024-09-21 14:02:50,454 - API日志 - INFO - [支付执行] 获取到的订单信息: Order PAYID-M3XGC5I9U46968696522382D - pending
2024-09-21 14:02:50,527 - API日志 - INFO - [支付执行] 获取的访问令牌: A21AAIVwWsRmSYRX_McyZN1RISbr4rs-amzjrc5R8H2rYRMZfH_Ia3QNs4HvEFVKxK2VAEMVe2dWfciYCJ5yhzGlbJgMBDuYQ
2024-09-21 14:02:50,527 - API日志 - INFO - [支付执行] 获取到的 PayPal 访问令牌: A21AAIVwWsRmSYRX_McyZN1RISbr4rs-amzjrc5R8H2rYRMZfH_Ia3QNs4HvEFVKxK2VAEMVe2dWfciYCJ5yhzGlbJgMBDuYQ
2024-09-21 14:02:50,528 - API日志 - INFO - [支付执行] 发送支付请求URL: https://api.sandbox.paypal.com/v1/payments/payment/PAYID-M3XGC5I9U46968696522382D/execute, 请求头: {'Content-Type': 'application/json', 'Authorization': 'Bearer A21AAIVwWsRmSYRX_McyZN1RISbr4rs-amzjrc5R8H2rYRMZfH_Ia3QNs4HvEFVKxK2VAEMVe2dWfciYCJ5yhzGlbJgMBDuYQ'}, 请求体: {'payer_id': '8RSF8T3UK8NT2'}
2024-09-21 14:02:51,294 - API日志 - INFO - [支付执行] PayPal API 响应状态码: 200
2024-09-21 14:02:51,295 - API日志 - INFO - [支付执行] PayPal API 响应内容: {'id': 'PAYID-M3XGC5I9U46968696522382D', 'intent': 'sale', 'state': 'approved', 'cart': '2Y943631F86154047', 'payer': {'payment_method': 'paypal', 'status': 'VERIFIED', 'payer_info': {'email': 'sb-pztpq32086919@personal.example.com', 'first_name': 'John', 'last_name': 'Doe', 'payer_id': '8RSF8T3UK8NT2', 'shipping_address': {'recipient_name': 'Doe John', 'line1': 'NO 1 Nan Jin Road', 'city': 'Shanghai', 'state': 'Shanghai', 'postal_code': '200000', 'country_code': 'C2'}, 'country_code': 'C2'}}, 'transactions': [{'amount': {'total': '1.00', 'currency': 'USD', 'details': {'subtotal': '1.00', 'shipping': '0.00', 'insurance': '0.00', 'handling_fee': '0.00', 'shipping_discount': '0.00', 'discount': '0.00'}}, 'payee': {'merchant_id': 'AQJTM59Z4T3EG', 'email': 'sb-05ob032098751@business.example.com'}, 'description': '购买 Hobby 计划', 'item_list': {'items': [{'name': 'Hobby', 'sku': '1', 'price': '1.00', 'currency': 'USD', 'tax': '0.00', 'quantity': 1, 'image_url': ''}], 'shipping_address': {'recipient_name': 'Doe John', 'line1': 'NO 1 Nan Jin Road', 'city': 'Shanghai', 'state': 'Shanghai', 'postal_code': '200000', 'country_code': 'C2'}}, 'related_resources': [{'sale': {'id': '40C15537SF235414R', 'state': 'completed', 'amount': {'total': '1.00', 'currency': 'USD', 'details': {'subtotal': '1.00', 'shipping': '0.00', 'insurance': '0.00', 'handling_fee': '0.00', 'shipping_discount': '0.00', 'discount': '0.00'}}, 'payment_mode': 'INSTANT_TRANSFER', 'protection_eligibility': 'ELIGIBLE', 'protection_eligibility_type': 'ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE', 'transaction_fee': {'value': '0.33', 'currency': 'USD'}, 'parent_payment': 'PAYID-M3XGC5I9U46968696522382D', 'create_time': '2024-09-21T06:02:50Z', 'update_time': '2024-09-21T06:02:50Z', 'links': [{'href': 'https://api.sandbox.paypal.com/v1/payments/sale/40C15537SF235414R', 'rel': 'self', 'method': 'GET'}, {'href': 'https://api.sandbox.paypal.com/v1/payments/sale/40C15537SF235414R/refund', 'rel': 'refund', 'method': 'POST'}, {'href': 'https://api.sandbox.paypal.com/v1/payments/payment/PAYID-M3XGC5I9U46968696522382D', 'rel': 'parent_payment', 'method': 'GET'}]}}]}], 'failed_transactions': [], 'create_time': '2024-09-21T06:02:29Z', 'update_time': '2024-09-21T06:02:50Z', 'links': [{'href': 'https://api.sandbox.paypal.com/v1/payments/payment/PAYID-M3XGC5I9U46968696522382D', 'rel': 'self', 'method': 'GET'}]}
2024-09-21 14:02:51,295 - API日志 - INFO - [支付执行] 支付成功,订单状态更新为 'approved'
2024-09-21 14:02:51,352 - API日志 - INFO - [支付执行] 用户 19978615506 当前积分: 50
2024-09-21 14:02:51,352 - API日志 - ERROR - [支付执行] 支付执行过程中发生异常: 'Plan' object has no attribute 'name'
2024-09-21 14:03:02,480 - API日志 - INFO - 接收到 PayPal Webhook 请求...
2024-09-21 14:03:02,481 - API日志 - INFO - 验证 PayPal Webhook 签名...
2024-09-21 14:03:02,661 - API日志 - INFO - 获取的访问令牌: A21AAIVwWsRmSYRX_McyZN1RISbr4rs-amzjrc5R8H2rYRMZfH_Ia3QNs4HvEFVKxK2VAEMVe2dWfciYCJ5yhzGlbJgMBDuYQ
2024-09-21 14:03:02,784 - API日志 - INFO - Webhook 签名验证成功
2024-09-21 14:03:02,784 - API日志 - INFO - 接收到 PayPal Webhook 事件: PAYMENTS.PAYMENT.CREATED
2024-09-21 14:03:02,785 - API日志 - INFO - Webhook payload: {
"id": "WH-53X19961TT501671H-9G343651RY447041V",
"event_version": "1.0",
"create_time": "2024-09-21T06:02:53.663Z",
"resource_type": "payment",
"event_type": "PAYMENTS.PAYMENT.CREATED",
"summary": "Checkout payment is created and approved by buyer",
"resource": {
"update_time": "2024-09-21T06:02:50Z",
"create_time": "2024-09-21T06:02:29Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAYID-M3XGC5I9U46968696522382D",
"rel": "self",
"method": "GET"
}
],
"id": "PAYID-M3XGC5I9U46968696522382D",
"state": "approved",
"transactions": [
{
"amount": {
"total": "1.00",
"currency": "USD",
"details": {
"subtotal": "1.00",
"shipping": "0.00",
"insurance": "0.00",
"handling_fee": "0.00",
"shipping_discount": "0.00",
"discount": "0.00"
}
},
"payee": {
"merchant_id": "AQJTM59Z4T3EG",
"email": "sb-05ob032098751@business.example.com"
},
"description": "\u8d2d\u4e70 Hobby \u8ba1\u5212",
"item_list": {
"items": [
{
"name": "Hobby",
"sku": "1",
"price": "1.00",
"currency": "USD",
"tax": "0.00",
"quantity": 1,
"image_url": ""
}
],
"shipping_address": {
"recipient_name": "John Doe",
"line1": "NO 1 Nan Jin Road",
"city": "Shanghai",
"state": "Shanghai",
"postal_code": "200000",
"country_code": "C2"
}
},
"related_resources": [
{
"sale": {
"id": "40C15537SF235414R",
"state": "completed",
"amount": {
"total": "1.00",
"currency": "USD",
"details": {
"subtotal": "1.00",
"shipping": "0.00",
"insurance": "0.00",
"handling_fee": "0.00",
"shipping_discount": "0.00",
"discount": "0.00"
}
},
"payment_mode": "INSTANT_TRANSFER",
"protection_eligibility": "ELIGIBLE",
"protection_eligibility_type": "ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE",
"transaction_fee": {
"value": "0.33",
"currency": "USD"
},
"parent_payment": "PAYID-M3XGC5I9U46968696522382D",
"create_time": "2024-09-21T06:02:50Z",
"update_time": "2024-09-21T06:02:50Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/40C15537SF235414R",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/sale/40C15537SF235414R/refund",
"rel": "refund",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAYID-M3XGC5I9U46968696522382D",
"rel": "parent_payment",
"method": "GET"
}
]
}
}
]
}
],
"intent": "sale",
"payer": {
"payment_method": "paypal",
"status": "VERIFIED",
"payer_info": {
"email": "sb-pztpq32086919@personal.example.com",
"first_name": "John",
"last_name": "Doe",
"payer_id": "8RSF8T3UK8NT2",
"shipping_address": {
"recipient_name": "John Doe",
"line1": "NO 1 Nan Jin Road",
"city": "Shanghai",
"state": "Shanghai",
"postal_code": "200000",
"country_code": "C2"
},
"phone": "2192589204",
"country_code": "C2"
}
},
"cart": "2Y943631F86154047"
},
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-53X19961TT501671H-9G343651RY447041V",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-53X19961TT501671H-9G343651RY447041V/resend",
"rel": "resend",
"method": "POST"
}
]
}
2024-09-21 14:03:02,785 - API日志 - INFO - 回调接收到 开始执行支付订单ID: PAYID-M3XGC5I9U46968696522382D, 付款人ID: 8RSF8T3UK8NT2
2024-09-21 14:03:02,795 - API日志 - INFO - 订单 PAYID-M3XGC5I9U46968696522382D 已经支付,不需要再次执行支付
2024-09-21 14:06:05,605 - API日志 - INFO - [支付执行] 获取的访问令牌: A21AAIVwWsRmSYRX_McyZN1RISbr4rs-amzjrc5R8H2rYRMZfH_Ia3QNs4HvEFVKxK2VAEMVe2dWfciYCJ5yhzGlbJgMBDuYQ

7
API日志.log.2024-09-21 Normal file
View File

@ -0,0 +1,7 @@
2024-09-22 18:04:02,758 - API日志 - INFO - [支付执行] 获取的访问令牌: A21AAMX8udSqwN2hgx455qnPAztVrGR8JEIEml8vS52RV_fA0EhIk78KooWj4TccndnZ2M7RFuVn8fnxikBQF8Qhmy7giu35w
2024-09-22 18:09:52,408 - API日志 - INFO - [支付执行] 获取的访问令牌: A21AAMX8udSqwN2hgx455qnPAztVrGR8JEIEml8vS52RV_fA0EhIk78KooWj4TccndnZ2M7RFuVn8fnxikBQF8Qhmy7giu35w
2024-09-22 18:18:20,687 - API日志 - INFO - 接收到 PayPal Webhook 请求...
2024-09-22 18:18:20,688 - API日志 - INFO - 验证 PayPal Webhook 签名...
2024-09-22 18:18:20,768 - API日志 - INFO - 获取的访问令牌: A21AAMX8udSqwN2hgx455qnPAztVrGR8JEIEml8vS52RV_fA0EhIk78KooWj4TccndnZ2M7RFuVn8fnxikBQF8Qhmy7giu35w
2024-09-22 18:18:20,943 - API日志 - ERROR - Webhook 签名验证失败: {"verification_status":"FAILURE"}
2024-09-22 18:18:20,944 - API日志 - ERROR - Webhook 签名验证失败

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,331 +1,337 @@
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)}"})
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:
# 如果订单已经是 'completed' 状态,说明已经处理过,跳过处理
if order.status == 'completed':
print("订单已处理,无需再次更新")
return False
# 获取套餐信息
plan = order.plan
# 检查汇率是否有效
usd_to_cny_rate = get_usd_to_cny_rate()
if not usd_to_cny_rate:
print("无法进行价格转换,汇率不可用")
return False
# 确保订单的套餐与实际套餐详情匹配
if not plan or convert_cny_to_usd(plan.price) != order.amount:
print(f"订单的套餐信息与实际套餐不匹配convert_cny_to_usd(plan.price){convert_cny_to_usd(plan.price)}--order_amount_cny{order.amount}")
return False
# 获取用户信息
user = order.user
# 更新用户的积分
user.points += plan.credits_per_month
user.save()
# 更新订单状态为 'completed'
order.status = 'completed'
order.save()
print(f"用户 {user.username} 的积分已更新--增加{plan.credits_per_month}--现在{user.points}")
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}")
# 获取订单ID和交易状态
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": "订单不存在"})
# 如果订单状态已经是 'completed',表示已经处理过,无需再次处理
if order.status == 'completed':
print(f"订单 {order_id} 已处理,无需再次更新")
return JsonResponse({"code": 200, "message": "订单已处理"})
# 如果支付成功或交易完成,更新订单状态并处理会员更新
if trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
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)}"})

File diff suppressed because it is too large Load Diff

View File

@ -1,332 +1,401 @@
import json
import time
import requests
from django.http import JsonResponse
from .models import VideoGeneration
from django.views.decorators.csrf import csrf_exempt
from .base import logger,deduct_points
def generate_video_id(user_id):
"""
根据用户ID和当前时间戳生成唯一的视频ID
"""
timestamp = int(time.time())
video_id = f"{timestamp}_{user_id}"
print(f"生成的视频ID: {video_id}")
return video_id
def get_dimensions(model, width=None, height=None):
"""
根据模型返回相应的宽高分辨率:
gen3: 最大1280x768
gen2: 可传入前端指定的宽高默认最大1920x1080
"""
if model == 'gen3':
return 1280, 768
elif model == 'gen2':
# 如果前端提供了 width 和 height则使用传入的值否则使用默认值
if width and height:
return width, height
return 1920, 1080
else:
raise ValueError(f"不支持的模型: {model}")
def get_headers():
"""
返回统一的请求头
"""
return {
"accept": "application/json",
"content-type": "application/json",
"Authorization": "12e76710fad2047db8c0cc6b25987e2a2" # 替换为你的真实授权密钥
}
@csrf_exempt
def text_to_video(request):
"""
文本生成视频接口
"""
if request.method == "POST":
try:
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
user = request.user
data = json.loads(request.body)
# 获取参数
text_prompt = data.get('text_prompt')
style = data.get('style', '') # 选择的风格,默认是 'general'
model = data.get('model', 'gen3') # 选择模型,默认是 'gen3'
time_duration = int(data.get('time', 5)) # 视频时长
motion = int(data.get('motion', 5)) # 视频丰富度参数
width = data.get('width') # 由前端传入的宽度仅限gen2
height = data.get('height') # 由前端传入的高度仅限gen2
print(f"生成模型: {model}, 时长: {time_duration}, 文本: {text_prompt}, 宽高: {width}x{height}, 画面丰富度: {motion}")
result = deduct_points(user, "text-to-video")
if result is not True:
return result # 返回积分不足的响应
# 验证模型和时长限制
if model == 'gen3' and time_duration not in [5, 10]:
return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'})
if model == 'gen2' and time_duration != 4:
return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'})
# 生成视频ID
video_id = generate_video_id(user.id)
# 根据模型获取宽高
width, height = get_dimensions(model, width, height)
print(f"生成的视频分辨率: {width}x{height}")
# 准备POST请求数据
payload = {
"text_prompt": f"{text_prompt}. Style: {style}",
"model": model,
"width": width,
"height": height,
"motion": motion, # 视频画面丰富度
"seed": 0,
"upscale": False,
"interpolate": False,
"callback_url": "https://www.typeframes.ai/api/callback/",
"time": time_duration # 视频时长
}
print(f'text_to_video 请求体: {payload}')
# 发送请求到API
response = requests.post('https://api.aivideoapi.com/runway/generate/text', json=payload, headers=get_headers())
print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 解析响应数据
if response.status_code == 200:
res_data = response.json()
uuid = res_data.get('uuid')
# 保存生成记录到数据库
video_generation = VideoGeneration.objects.create(
video_id=video_id,
user=user,
text=text_prompt,
status='in_progress',
pid=uuid,
media_type=f'{model}_text_to_video',
slug=f'text-to-video',
ratio=f"{width}:{height}"
)
video_generation.save()
# 返回成功响应
return JsonResponse({
'code': 200,
'message': '请求成功,视频正在生成中',
'data': {
'video_id': video_generation.video_id,
'status': video_generation.status,
'pid': uuid
}
})
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
except Exception as e:
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
@csrf_exempt
def image_to_video(request):
"""
图片生成视频接口图片宽高自适应
"""
if request.method == "POST":
try:
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
user = request.user
data = json.loads(request.body)
# 获取参数
text_prompt = data.get('text_prompt')
image_url = data.get('image_url')
model = data.get('model', 'gen3') # 选择模型,默认是 'gen3'
time_duration = int(data.get('time', 5)) # 视频时长
motion = int(data.get('motion', 5)) # 视频丰富度
print(f"生成模型: {model}, 图片地址: {image_url}, 时长: {time_duration}, 画面丰富度: {motion}")
result = deduct_points(user, "img-to-video")
if result is not True:
return result # 返回积分不足的响应
# 验证模型和时长限制
if model == 'gen3' and time_duration not in [5, 10]:
return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'})
if model == 'gen2' and time_duration != 4:
return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'})
# 生成视频ID
video_id = generate_video_id(user.id)
# 准备POST请求数据宽高不需要传入图片自适应
payload = {
"text_prompt": text_prompt,
"model": model,
"img_prompt": image_url,
"image_as_end_frame": False, # 图片不作为最后一帧
"motion": motion, # 视频丰富度
"seed": 0,
"upscale": False,
"interpolate": False,
"callback_url": "https://www.typeframes.ai/api/callback/",
"time": time_duration # 视频时长
}
print(f'img_to_video 请求体: {payload}')
# 请求头
headers = get_headers()
# 发送请求到API
response = requests.post('https://api.aivideoapi.com/runway/generate/imageDescription', json=payload, headers=headers)
print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 解析响应数据
if response.status_code == 200:
res_data = response.json()
uuid = res_data.get('uuid')
# 保存生成记录到数据库
video_generation = VideoGeneration.objects.create(
video_id=video_id,
user=user,
text=text_prompt,
status='in_progress',
pid=uuid,
media_type=f'{model}_img_to_video',
slug=f'img-to-video',
ratio="auto" # 图片自适应宽高
)
video_generation.save()
# 返回成功响应
return JsonResponse({
'code': 200,
'message': '请求成功,视频正在生成中',
'data': {
'video_id': video_generation.video_id,
'status': video_generation.status,
'pid': uuid
}
})
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
except Exception as e:
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
@csrf_exempt
def extend_video(request):
"""
延长视频的函数发送请求到 /runway/extend
"""
if request.method == "POST":
try:
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
user = request.user
data = json.loads(request.body)
# 获取参数
pid = data.get('pid')
motion = int(data.get('motion', 5)) # 视频丰富度
# 获取当前视频生成记录
video_generation = VideoGeneration.objects.get(pid=pid)
# 通过 media_type 判断模型是 gen3 还是 gen2
if 'gen3' in video_generation.media_type:
model = 'gen3'
# 验证 gen3 的延长逻辑
if video_generation.extension_count >= 1 or video_generation.time_duration >= 10:
return JsonResponse({'code': 400, 'message': 'gen3 最大时长为 10s无法继续延长'})
time_extension = 5 # 每次延长 5 秒
elif 'gen2' in video_generation.media_type:
model = 'gen2'
# 验证 gen2 的延长逻辑
if video_generation.extension_count >= 2 or video_generation.time_duration >= 12:
return JsonResponse({'code': 400, 'message': 'gen2 最大时长为 12s无法继续延长'})
time_extension = 4 # 每次延长 4 秒
else:
return JsonResponse({'code': 400, 'message': '未知的模型类型'})
result = deduct_points(user, video_generation.slug)
if result is not True:
return result # 返回积分不足的响应
# 生成新的视频ID
new_video_id = generate_video_id(user.id)
# 默认请求体
payload = {
"uuid": pid, # 视频的 UUID
"motion": motion, # 视频丰富度
"seed": 0, # 默认 seed 为 0
"upscale": False, # 默认开启 upscale
"interpolate": False, # 默认开启 interpolate
"callback_url": "https://www.typeframes.ai/api/callback/"
}
print(f"extend_video 请求体: {payload}")
headers = get_headers()
# 发送 POST 请求
response = requests.post('https://api.aivideoapi.com/runway/extend', json=payload, headers=headers)
print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 解析响应内容
if response.status_code == 200:
res_data = response.json()
new_pid = res_data.get('uuid')
# 创建新的延长视频记录,复制上一条视频的参数
new_video_generation = VideoGeneration.objects.create(
video_id=new_video_id,
user=user,
text=video_generation.text,
voice_name=video_generation.voice_name,
style=video_generation.style,
rate=video_generation.rate,
media_type=video_generation.media_type,
ratio=video_generation.ratio,
status='in_progress',
pid=new_pid,
time_duration=video_generation.time_duration + time_extension, # 增加对应模型的时长
extension_count=video_generation.extension_count + 1, # 增加延长次数
slug=video_generation.slug # slug 使用中划线
)
print(f"延长视频成功: {res_data}")
return JsonResponse({'code': 200, 'message': '视频延长请求成功', 'data': {'new_pid': new_pid}})
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
except Exception as e:
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
import json
import time
import requests
from django.http import JsonResponse
from .models import VideoGeneration
from django.views.decorators.csrf import csrf_exempt
from .base import logger,deduct_points
import http.client
import random
import urllib
import hashlib
from langdetect import detect
from langdetect.lang_detect_exception import LangDetectException
def generate_video_id(user_id):
"""
根据用户ID和当前时间戳生成唯一的视频ID
"""
timestamp = int(time.time())
video_id = f"{timestamp}_{user_id}"
print(f"生成的视频ID: {video_id}")
return video_id
appid = '20240601002067404' # 填写你的appid
secretKey = '6pRZ9HCSqGuMqzLO55hB' # 填写你的密钥
def translate(text, from_lang='auto', to_lang='en'):
myurl = '/api/trans/vip/translate'
salt = random.randint(32768, 65536)
sign = appid + text + str(salt) + secretKey
sign = hashlib.md5(sign.encode()).hexdigest()
myurl = (myurl + '?appid=' + appid + '&q=' + urllib.parse.quote(text) +
'&from=' + from_lang + '&to=' + to_lang + '&salt=' + str(salt) +
'&sign=' + sign)
try:
httpClient = http.client.HTTPConnection('api.fanyi.baidu.com')
httpClient.request('GET', myurl)
# response是HTTPResponse对象
response = httpClient.getresponse()
result_all = response.read().decode("utf-8")
result = json.loads(result_all)
print(f'请求结果{result}')
return result['trans_result'][0]['dst']
except Exception as e:
return str(e)
finally:
if httpClient:
httpClient.close()
def get_dimensions(model, width=None, height=None):
"""
根据模型返回相应的宽高分辨率:
gen3: 最大1280x768
gen2: 可传入前端指定的宽高默认最大1920x1080
"""
if model == 'gen3':
return 1280, 768
elif model == 'gen2':
# 如果前端提供了 width 和 height则使用传入的值否则使用默认值
if width and height:
return width, height
return 1920, 1080
else:
raise ValueError(f"不支持的模型: {model}")
def get_headers():
"""
返回统一的请求头
"""
return {
"accept": "application/json",
"content-type": "application/json",
"Authorization": "12e76710fad2047db8c0cc6b25987e2a2" # 替换为你的真实授权密钥
}
@csrf_exempt
def text_to_video(request):
"""
文本生成视频接口
"""
if request.method == "POST":
try:
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
user = request.user
data = json.loads(request.body)
text_prompt = data.get('text_prompt')
print(f'文生视频原内容---{text_prompt}')
# 判断是否需要翻译
try:
language = detect(text_prompt)
print(f"检测到的语言: {language}")
if language == 'en':
description = text_prompt # 英文无需翻译
else:
description = translate(text_prompt, 'auto', 'en') # 翻译成英文
print(f'文生视频翻译内容---{description}')
except LangDetectException:
return JsonResponse({'code': 400, 'message': '无法检测语言,请输入有效文本'})
# 获取参数
style = data.get('style', '') # 选择的风格,默认是 'general'
model = data.get('model', 'gen3') # 选择模型,默认是 'gen3'
time_duration = int(data.get('time', 5)) # 视频时长
motion = int(data.get('motion', 5)) # 视频丰富度参数
width = data.get('width') # 由前端传入的宽度仅限gen2
height = data.get('height') # 由前端传入的高度仅限gen2
print(f"生成模型: {model}, 时长: {time_duration}, 文本: {text_prompt}, 宽高: {width}x{height}, 画面丰富度: {motion}")
result = deduct_points(user, "text-to-video")
if result is not True:
return result # 返回积分不足的响应
# 验证模型和时长限制
if model == 'gen3' and time_duration not in [5, 10]:
return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'})
if model == 'gen2' and time_duration != 4:
return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'})
# 生成视频ID
video_id = generate_video_id(user.id)
# 根据模型获取宽高
width, height = get_dimensions(model, width, height)
print(f"生成的视频分辨率: {width}x{height}")
# 准备POST请求数据
payload = {
"text_prompt": f"{description}. Style: {style}",
"model": model,
"width": width,
"height": height,
"motion": motion, # 视频画面丰富度
"seed": 0,
"upscale": True,
"interpolate": True,
"callback_url": "https://www.typeframes.ai/api/callback/",
"time": time_duration # 视频时长
}
print(f'text_to_video 请求体: {payload}')
# 发送请求到API
response = requests.post('https://api.aivideoapi.com/runway/generate/text', json=payload, headers=get_headers())
print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 解析响应数据
if response.status_code == 200:
res_data = response.json()
uuid = res_data.get('uuid')
# 保存生成记录到数据库
video_generation = VideoGeneration.objects.create(
video_id=video_id,
user=user,
text=text_prompt,
status='in_progress',
pid=uuid,
media_type=f'{model}_text_to_video',
slug=f'text-to-video',
ratio=f"{width}:{height}"
)
video_generation.save()
# 返回成功响应
return JsonResponse({
'code': 200,
'message': '请求成功,视频正在生成中',
'data': {
'video_id': video_generation.video_id,
'status': video_generation.status,
'pid': uuid
}
})
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
except Exception as e:
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
@csrf_exempt
def image_to_video(request):
"""
图片生成视频接口图片宽高自适应
"""
if request.method == "POST":
try:
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
user = request.user
data = json.loads(request.body)
# 获取参数
text_prompt = data.get('text_prompt')
print(f' 图片生视频原内容---{text_prompt}')
# 判断是否需要翻译
try:
language = detect(text_prompt)
print(f"检测到的语言: {language}")
if language == 'en':
description = text_prompt # 英文无需翻译
else:
description = translate(text_prompt, 'auto', 'en') # 翻译成英文
print(f'图片生视翻译内容---{description}')
except LangDetectException:
return JsonResponse({'code': 400, 'message': '无法检测语言,请输入有效文本'})
image_url = data.get('image_url')
model = data.get('model', 'gen3') # 选择模型,默认是 'gen3'
time_duration = int(data.get('time', 5)) # 视频时长
motion = int(data.get('motion', 5)) # 视频丰富度
print(f"生成模型: {model}, 图片地址: {image_url}, 时长: {time_duration}, 画面丰富度: {motion}")
result = deduct_points(user, "img-to-video")
if result is not True:
return result # 返回积分不足的响应
# 验证模型和时长限制
if model == 'gen3' and time_duration not in [5, 10]:
return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'})
if model == 'gen2' and time_duration != 4:
return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'})
# 生成视频ID
video_id = generate_video_id(user.id)
# 准备POST请求数据宽高不需要传入图片自适应
payload = {
"text_prompt": description,
"model": model,
"img_prompt": image_url,
"image_as_end_frame": False, # 图片不作为最后一帧
"motion": motion, # 视频丰富度
"seed": 0,
"upscale": True,
"interpolate": True,
"callback_url": "https://www.typeframes.ai/api/callback/",
"time": time_duration # 视频时长
}
print(f'img_to_video 请求体: {payload}')
# 请求头
headers = get_headers()
# 发送请求到API
response = requests.post('https://api.aivideoapi.com/runway/generate/imageDescription', json=payload, headers=headers)
print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 解析响应数据
if response.status_code == 200:
res_data = response.json()
uuid = res_data.get('uuid')
# 保存生成记录到数据库
video_generation = VideoGeneration.objects.create(
video_id=video_id,
user=user,
text=text_prompt,
status='in_progress',
pid=uuid,
media_type=f'{model}_img_to_video',
slug=f'img-to-video',
ratio="auto" # 图片自适应宽高
)
video_generation.save()
# 返回成功响应
return JsonResponse({
'code': 200,
'message': '请求成功,视频正在生成中',
'data': {
'video_id': video_generation.video_id,
'status': video_generation.status,
'pid': uuid
}
})
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
except Exception as e:
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
@csrf_exempt
def extend_video(request):
"""
延长视频的函数发送请求到 /runway/extend
"""
if request.method == "POST":
try:
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'})
user = request.user
data = json.loads(request.body)
print(data)
# 获取参数
pid = data.get('task_id')
motion = 6 # 视频丰富度
# 获取当前视频生成记录
video_generation = VideoGeneration.objects.get(pid=pid)
# 通过 media_type 判断模型是 gen3 还是 gen2
if 'gen3' in video_generation.media_type:
model = 'gen3'
# 验证 gen3 的延长逻辑
if video_generation.extension_count >= 1 or video_generation.time_duration >= 10:
return JsonResponse({'code': 400, 'message': 'gen3 最大时长为 10s无法继续延长'})
time_extension = 5 # 每次延长 5 秒
elif 'gen2' in video_generation.media_type:
model = 'gen2'
# 验证 gen2 的延长逻辑
if video_generation.extension_count >= 2 or video_generation.time_duration >= 12:
return JsonResponse({'code': 400, 'message': 'gen2 最大时长为 12s无法继续延长'})
time_extension = 4 # 每次延长 4 秒
else:
return JsonResponse({'code': 400, 'message': '未知的模型类型'})
result = deduct_points(user, video_generation.slug)
if result is not True:
return result # 返回积分不足的响应
# 生成新的视频ID
new_video_id = generate_video_id(user.id)
# 默认请求体
payload = {
"uuid": pid, # 视频的 UUID
"motion": motion, # 视频丰富度
"seed": 0, # 默认 seed 为 0
"upscale": True, # 默认开启 upscale
"interpolate": True, # 默认开启 interpolate
"callback_url": "https://www.typeframes.ai/api/callback/"
}
print(f"extend_video 请求体: {payload}")
headers = get_headers()
# 发送 POST 请求
response = requests.post('https://api.aivideoapi.com/runway/extend', json=payload, headers=headers)
print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 解析响应内容
if response.status_code == 200:
res_data = response.json()
new_pid = res_data.get('uuid')
# 创建新的延长视频记录,复制上一条视频的参数
new_video_generation = VideoGeneration.objects.create(
video_id=new_video_id,
user=user,
text=video_generation.text,
voice_name=video_generation.voice_name,
style=video_generation.style,
rate=video_generation.rate,
media_type=video_generation.media_type,
ratio=video_generation.ratio,
status='in_progress',
pid=new_pid,
time_duration=video_generation.time_duration + time_extension, # 增加对应模型的时长
extension_count=video_generation.extension_count + 1, # 增加延长次数
slug=video_generation.slug # slug 使用中划线
)
print(f"延长视频成功: {res_data}")
return JsonResponse({'code': 200, 'message': '视频延长请求成功', 'data': {'new_pid': new_pid}})
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
except Exception as e:
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})

View File

@ -1,199 +1,265 @@
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
import logging
logger = logging.getLogger(__name__)
def get_paypal_access_token():
"""获取 PayPal 访问令牌"""
print("正在获取 PayPal 访问令牌...")
url = f"https://api.{settings.PAYPAL_MODE}.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')
print(f"获取的访问令牌: {access_token}")
return access_token
else:
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.{settings.PAYPAL_MODE}.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:
data = json.loads(request.body)
payment_id = data.get('paymentId')
payer_id = data.get('PayerID')
if not payment_id or not payer_id:
return JsonResponse({"code": 400, "message": "缺少支付ID或付款人ID", "data": {}})
order = Order.objects.get(order_id=payment_id)
if order.status == 'completed':
return JsonResponse({"code": 400, "message": "订单已支付", "data": {}})
# 获取PayPal访问令牌
access_token = get_paypal_access_token()
if not access_token:
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
# 执行支付确认请求
url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/payments/payment/{payment_id}/execute"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}"
}
payload = {
"payer_id": payer_id
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
try:
order.status = 'completed'
order.updated_at = datetime.now()
order.save()
# 更新用户积分
user = order.user
user.points += order.plan.credits_per_month
user.save()
return JsonResponse({"code": 200, "message": "支付成功", "data": {}})
except Order.DoesNotExist:
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}})
else:
return JsonResponse({"code": 400, "message": "支付执行失败", "data": response.json()})
except json.JSONDecodeError:
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}})
except Exception as e:
return JsonResponse({"code": 500, "message": f"支付执行过程中发生异常: {str(e)}", "data": {}})
else:
return JsonResponse({"code": 405, "message": "方法不允许", "data": {}})
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": {}})

View File

@ -1,165 +1,237 @@
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
import logging
logger = logging.getLogger(__name__)
def get_paypal_access_token():
"""获取 PayPal 访问令牌"""
print("正在获取 PayPal 访问令牌...")
url = f"https://api.{settings.PAYPAL_MODE}.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')
print(f"获取的访问令牌: {access_token}")
return access_token
else:
print(f"获取 PayPal 访问令牌失败: {response.text}")
return None
def verify_paypal_webhook(headers, body):
"""使用 PayPal API 验证 Webhook 签名"""
print("验证 PayPal Webhook 签名...")
verify_url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/notifications/verify-webhook-signature"
auth_token = get_paypal_access_token()
if not auth_token:
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':
print("Webhook 签名验证成功")
return True
else:
print(f"Webhook 签名验证失败: {response.text}")
return False
@csrf_exempt
def paypal_webhook(request):
"""PayPal Webhook 处理函数,用于接收和处理 PayPal 异步通知"""
try:
print("接收到 PayPal Webhook 请求...")
headers = request.headers
payload = json.loads(request.body)
# 验证 Webhook 签名
if not verify_paypal_webhook(headers, request.body):
return JsonResponse({"code": 400, "message": "Webhook 签名验证失败", "data": {}})
event_type = payload.get('event_type')
# 打印接收到的 Webhook payload 以供调试
print(f"接收到 PayPal Webhook 事件: {event_type}")
print(f"Webhook payload: {json.dumps(payload, indent=2)}")
# 处理不同类型的事件
if event_type == 'PAYMENT.SALE.COMPLETED':
sale_id = payload['resource'].get('id')
parent_payment = payload['resource'].get('parent_payment') # 使用parent_payment作为订单ID
if parent_payment:
try:
order = Order.objects.get(order_id=parent_payment)
# 检查订单状态,防止重复更新
if order.status == 'completed':
print(f"订单 {parent_payment} 已经完成,不重复更新")
return JsonResponse({"code": 200, "message": "订单已处理", "data": {}})
# 再次向 PayPal 查询订单状态,确保支付确实完成
access_token = get_paypal_access_token()
if not access_token:
print("无法获取 PayPal 访问令牌,无法验证订单状态")
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
payment_details_url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/payments/payment/{parent_payment}"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}"
}
payment_details_response = requests.get(payment_details_url, headers=headers)
if payment_details_response.status_code == 200:
payment_details = payment_details_response.json()
if payment_details['state'] == 'approved':
order.status = 'completed'
order.updated_at = datetime.now()
order.save()
# 更新用户积分
user = order.user
user.points += order.plan.credits_per_month
user.save()
print(f"订单 {parent_payment} 状态更新为 'completed' 并已更新用户积分")
else:
print(f"支付未完成,状态: {payment_details['state']}")
else:
print(f"查询支付状态失败: {payment_details_response.text}")
return JsonResponse({"code": 500, "message": "查询支付状态失败", "data": {}})
except Order.DoesNotExist:
print(f"未找到订单 {parent_payment}")
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}})
else:
print("Webhook payload 中缺少 parent_payment")
return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}})
elif event_type == 'PAYMENT.SALE.DENIED':
sale_id = payload['resource'].get('id')
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()
print(f"订单 {parent_payment} 状态更新为 'failed'")
except Order.DoesNotExist:
print(f"未找到订单 {parent_payment}")
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}})
else:
print("Webhook payload 中缺少 parent_payment")
return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}})
return JsonResponse({"code": 200, "message": "Webhook 处理成功", "data": {}})
except json.JSONDecodeError:
print("请求体不是有效的JSON")
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}})
except Exception as e:
print(f"处理 PayPal Webhook 时发生错误: {e}")
return JsonResponse({"code": 500, "message": f"处理 PayPal Webhook 时发生错误: {str(e)}", "data": {}})
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": {}})

View File

@ -5,33 +5,63 @@ from .models import User, UserSource
@receiver(user_logged_in)
def user_logged_in_handler(request, user, **kwargs):
print('1111111111111111111111111')
print('信号处理开始: user_logged_in_handler 被触发')
# 防止信号被重复处理
if hasattr(request, '_google_user_logged_in_handled'):
print('重复信号,跳过处理')
return
request._google_user_logged_in_handled = True
print('处理信号标志设置完成')
try:
social_account = SocialAccount.objects.get(user=user, provider='google')
print(social_account.extra_data) # 打印 extra_data 内容以进行调试
google_id = social_account.extra_data['sub'] # 使用 'sub' 字段
email = social_account.extra_data['email']
username = social_account.extra_data['name']
print(f'找到 social_account用户ID: {user.id}')
google_id = social_account.extra_data.get('sub')
email = social_account.extra_data.get('email')
username = social_account.extra_data.get('name')
print(f'从 social_account 获取的 google_id: {google_id}, email: {email}, username: {username}')
if not google_id:
# 如果 google_id 为空,不要继续创建用户,记录日志或处理错误
print('google_id 为空,终止处理')
return
# 优化:根据 google_id 和 email 双重检查用户是否已存在
try:
custom_user = User.objects.get(google_id=google_id)
print(f'根据 google_id 找到用户: {custom_user.username}')
except User.DoesNotExist:
custom_user = User.objects.create(
username=username,
email=email,
google_id=google_id,
password_hash='', # Google 登录不需要密码
)
UserSource.objects.create(user=custom_user, source='Google')
# 更新登录次数和最后登录IP
print(f'没有找到 google_id 为 {google_id} 的用户,检查 email: {email}')
# 进一步检查是否有相同的 email 用户存在
if User.objects.filter(email=email).exists():
custom_user = User.objects.get(email=email)
print(f'找到 email 为 {email} 的用户: {custom_user.username}')
# 如果用户存在但 google_id 不同,更新 google_id
custom_user.google_id = google_id
custom_user.save()
print(f'更新用户的 google_id 为: {google_id}')
else:
custom_user = User.objects.create(
username=username,
email=email,
google_id=google_id,
# password_hash='', # Google 登录不需要密码
)
print(f'创建新用户: {custom_user.username}google_id: {google_id}')
UserSource.objects.create(user=custom_user, source='Google')
print(f'创建 UserSource 记录,来源为 Google')
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0] # 可能有多个 IP 地址,取第一个
else:
ip = request.META.get('REMOTE_ADDR') # 使用 REMOTE_ADDR 作为备用
# 更新登录次数和 IP 地址
custom_user.login_count += 1
custom_user.last_login_ip = request.META.get('REMOTE_ADDR')
custom_user.last_login_ip = ip
custom_user.save()
print(f'更新用户信息: 登录次数 {custom_user.login_count}, IP 地址 {custom_user.last_login_ip}')
# 确保 Django 用户同步
user.username = custom_user.username
user.save()
except SocialAccount.DoesNotExist:
pass
print('未找到与用户关联的 Google social_account')

View File

@ -22,6 +22,7 @@ from .aliyun_sms import send_verification_sms, verify_sms_code
# 定义积分奖励值
REWARD_POINTS = 50 # 例如奖励50积分
def check_and_reward_points(user, openid):
"""
根据 openid 查询原系统会员状态为原系统的用户增加一天会员时长不论是否为会员
@ -151,7 +152,7 @@ def send_verification_email_view(request):
if request.method == 'POST':
try:
body = json.loads(request.body)
email = body.get('phone_number')
email = body.get('email')
if not email:
return JsonResponse({'code': 400, 'message': '邮箱地址是必须的'})
@ -193,8 +194,8 @@ def send_verification_sms_view(request):
if not phone_number:
return JsonResponse({'code': 400, 'message': '手机号码是必须的'})
sign_name = "福铭科技" # 替换为实际的签名名称
template_code = "SMS_299200388" # 替换为实际的短信模板代码
sign_name = "光映" # 替换为实际的签名名称
template_code = "SMS_473670066" # 替换为实际的短信模板代码
response = send_verification_sms(phone_number, sign_name, template_code)
return JsonResponse(response)
@ -379,7 +380,7 @@ def send_verification_code_view(request):
if '@' in identifier:
response = send_verification_email(identifier)
else:
response = send_verification_sms(identifier, "福铭科技", "SMS_299200388")
response = send_verification_sms(identifier, "光映", "SMS_473670066")
return JsonResponse(response)
except json.JSONDecodeError:

Binary file not shown.

View File

@ -23,7 +23,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = "django-insecure-!4a*kl(!=s*76xngra89q*86d&e8ya=l49!bibepo6p1(50+$s"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = False
ALLOWED_HOSTS = ['*']
# settings.py
@ -63,7 +63,7 @@ AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)
LOGIN_REDIRECT_URL = '/user/userinfo/' # 登录后重定向的URL
LOGIN_REDIRECT_URL = '/create' # 登录后重定向的URL
LOGIN_URL = '/'
LOGOUT_REDIRECT_URL = '/'
@ -159,10 +159,10 @@ DATABASES = {
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'admin_data', # 数据库名称
'HOST': '1Panel-mysql-61G1', # 数据库地址,本机 ip 地址 127.0.0.1
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # mysql端口
'USER': 'admin_data', # 数据库用户名
'PASSWORD': 'mPmFz2NeXSYbzrt5', # 数据库密码
'PASSWORD': 'kyRz3c3hGMmDADrW', # 数据库密码
'OPTIONS': {
'charset': 'utf8mb4',
'use_unicode': True,
@ -231,7 +231,7 @@ AZURE_CONTAINER_NAME = 'audio'
#支付宝电脑版支付配置
# 支付宝配置
ALIPAY_DEBUG = True # True 表示沙箱环境False 表示生产环境
ALIPAY_DEBUG = False # True 表示沙箱环境False 表示生产环境
# 沙箱环境配 电脑支付宝
ALIPAY_APP_ID_SANDBOX_PC = '9021000139657593' # 沙箱环境电脑端应用ID
@ -250,20 +250,19 @@ ALIPAY_PUBLIC_KEY_PRODUCTION_PC = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo
ALIPAY_APP_ID_PRODUCTION_MOBILE = '2021004169658094' # 生产环境移动端应用ID
ALIPAY_APP_PRIVATE_KEY_PRODUCTION_MOBILE = 'MIIEowIBAAKCAQEAlkLen9IPB0OhhRnDw0Ks1yX74oXSLJ4hEaqQmJqXI8qfNFL+DiuEWs+yAZRad8mDOUqERka1YrMI9Y7iVyGWhcFRHe91+oNNDZlrXp1K/SzPWtA1tPbkBrgxV5lgj/fGKJrMlJhlfF7Bio8b9jOfyMtaqolP777tD0zMAdIbKYCW1wmUgUeRsNplDO/8GIDoJV+qGxTEd2TN3eJfC9MBIkCW/b1QHTXukJIFzKGufNRNJIWbPi/wmyMaUd+lyemz//sNhD2+CpiuB4pi/v3c8tQsPJeOslRpo9+T0lg9wLlDgMMeajf+PMzjErRmiOxI1ZwNn8xkaE6ZfeDhh18v1wIDAQABAoIBAQCQ225bjNpIGn8AHdKHzyNpVoj53CC+OsOOkGxNcdr6+j945fleF73E/i2RMD/28yG6fYf/Z/M9b3PXpyO09AB0eeWFgnCuTR5fWeQUlnbQk/5igg8Eom85uyB2XrqkkC1Mv88yAaj0uTTEKyq9sw77OG/zr+SML/fBpDuYFcFxw7JnD+TxKBeqT6gd1RHcfeyta1MWiEsQeX7y2PGicn5HjbGLCsJxvJVGyvaQFPBN5eoIesSm74DEFL25ZdR1vpaWYGm7XZgAY6dgzPbijFx2nLMRAM9e+M6I0u75k0mDSVb9y+1TXqEKPSG7FegIbzhylj20ZcwWYb0SgJ8sHuvBAoGBAOcRBlbtkq4uGqFyTY1y1WedxSMJf0bMfYum3CvT7YAOVffVxye2I3s4v1Os1NPR5M+om7eEtgN+9Zl2DIagPlTB6CrYRg26hwnDuNhLqLZHo8IW0IK2xY8QSPzvRJcx1sSvTqLXG/MdsFDV9SGNxYazYpIqlL2Q2cUHImc6+JqZAoGBAKZ5rnVPlOQ20aXCe/0nn7K6PIFCQhBHayBjXmqaNi7ZGVgM1CXrvFffpGrc7e60gVfbOkZjzUy0XJAmagYTNZ8+HDh52y6YRoMR3XjPKgrs4+hdraG1vBVSmxRpzUfYqqq+k9T2dnK3pjkYIrDZiiwsT57o2EispQBWRfNmKZPvAoGAeclBXCPPgbqPErTaJ0l1LS28r+DbkodJTIff18HVlf74VK36T/Xx8YpRXVrkudgRD1pA6JMsE9+gtwe/Rl3DPI0PPzXKhoXfksfz7xzBiYFs0GmR8LNAoUzmC1pY/o+oMPZtLerOsm7ddV2v28WLbJmzxHJo/xkPjodaHKq6SVECgYAxGLHa5x2CMKijujfW8Vin3UT62g+hvW5anvO5fMF+D4jp7t6iUGO7hrl2yrFhNNrwjjchi02A0bB60tlx8ThfPnIUNdvuYfpprVsjxfUgQ7zzSq1qg650m2ghHRLtnXi34ny20fkc3AypyfRxM7cMUAUrWIBw7nm7H7aBkh+xAQKBgH4Zaxjcn/OsF4QPCJqzf02+zHh7FGGLnsjZXmtVoNDmmFJFXj5ey05jfcNyoRdtffl0Fa/mbkiL1YcAUoBmkyS7MA4mrrM5vG5ZKtLcul1qeC9JrJ2FEcZBmny2fdbfzFFuBdhz0jbOGoDOKl5KWM7EQoxk377DQVRQ884B0Cc+' # 生产环境移动端应用私钥
ALIPAY_PUBLIC_KEY_PRODUCTION_MOBILE = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomw6g4rBmCr/QoX3NI3DVLyDpkaUytZ2uFhdfQaegIDAuUfZfgpTCASlAtO82t8ISAbSOSyp9CUpwdGV4EYOiCBbLxMYB6taaHPiIjJ1zNT1EakJzWgU53hz1AVeABB9kdAvMqSvjH6KLoVupmqm4Li8ZwDW9M2ANAmyDfKgiF0Lt4aUUnaZktoCrTWTkpmtfRZCHNACj851IllvN2wyC4OL7dJq5UzOFxmn07Dy/2z4UAhaaSAyRVawpOui5AIYJTXZERLYL3KMyRnMuZoFq3xltzVTzRPM06nRa9RfeVNVwWVtGBIe/r8tcg5wyhI57KUszGNOmUIm/se6G2lnAQIDAQAB' # 生产环境移动端支付宝公钥
ALIPAY_NOTIFY_URL = 'https://www.typeframes.com.cn/api/alipay/notify/'
ALIPAY_RETURN_URL = 'https://www.typeframes.com.cn/user/userinfo/'
ALIPAY_RETURN_URL = 'https://www.typeframes.com.cn/create'
# settings.py
# PayPal 配置
PAYPAL_CLIENT_ID_SANDBOX = 'AfwWNEZE92XpbmoHywwdDEXH2x2Elak1Gxbireb5bv-u17OtKiJUuz7CMLnqayAZkqeU2kGtWXHXcXGS'
PAYPAL_SECRET_KEY_SANDBOX = 'ELmuFW7AspHp4km8amEhlM-NB3OGGx_iuqQbntTQ-2RTnJWbxdLTl6xlET1F1ezxSHbU1CUpK0_8ToI3'
PAYPAL_CLIENT_ID_PRODUCTION = 'YOUR_PRODUCTION_CLIENT_ID' # 替换为实际的生产环境客户端ID
PAYPAL_SECRET_KEY_PRODUCTION = 'YOUR_PRODUCTION_SECRET_KEY' # 替换为实际的生产环境密钥
PAYPAL_CLIENT_ID_PRODUCTION = 'Aelz9E_u5mlLP_-8nOGuXS14P-osTWYqxPx8HknC1PNBqRtG_RzAtWblryvr0o8pIyqIomI-2KMFagk2' # 替换为实际的生产环境客户端ID
PAYPAL_SECRET_KEY_PRODUCTION = 'EIA_O_JeOWUjolXK3yi4VC88wpcZaIYgQvAPJ251LX_OTsatYzjt_Y2aFKLjXt5fInRkdTfeIbrwlZEh' # 替换为实际的生产环境密钥
PAYPAL_MODE = 'sandbox' #'sandbox' if DEBUG else 'live' # 根据DEBUG模式选择环境
PAYPAL_RETURN_URL = 'https://www.typeframes.ai/user/plan_list/' # 支付成功后跳转的URL
PAYPAL_CANCEL_URL = 'https://www.typeframes.ai/user/plan_list/' # 支付取消后跳转的URL
PAYPAL_MODE = 'sandbox' if DEBUG else 'live' # 根据DEBUG模式选择环境
PAYPAL_RETURN_URL = 'https://www.typeframes.ai/create' # 支付成功后跳转的URL
PAYPAL_CANCEL_URL = 'https://www.typeframes.ai/create' # 支付取消后跳转的URL
PAYPAL_WEBHOOK_ID = '5L31025588094031T' # 替换为你的实际 Webhook ID
PAYPAL_WEBHOOK_ID = '1Y325150YP471005S' # 替换为你的实际 Webhook ID

0
gunicorn_acess.log Executable file
View File

0
gunicorn_error.log Executable file
View File

View File

@ -1,11 +1,25 @@
[uwsgi]
chdir = /app
# 项目目录
chdir = /www/wwwroot/Ai_Admin
# 指定项目application
module = WebSite.wsgi:application
# 进程个数
processes = 4
# 线程个数
threads = 2
# 指定启动时的pid文件路径用于停止服务和重启服务请勿删除
pidfile = /www/wwwroot/Ai_Admin/uwsgi.pid
# 指定ip及端口
http = 0.0.0.0:3001
http=0.0.0.0:3001
# 启动uwsgi的用户名和用户组
uid = root
gid = root
# 启用主进程
master = true
@ -14,20 +28,18 @@ master = true
buffer-size = 32768
http-timeout = 60
post-buffering = 8192
# 后台运行,并输出日志
daemonize = /www/wwwroot/Ai_Admin/uwsgi.log
# 处理 SIGTERM 信号以便优雅地关闭
die-on-term = true
# 处理静态和媒体文件
static-map = /static=/app/static
static-map = /media=/app/media
static-map = /media/avatars=/app/media/avatars
static-map = /media/audios=/app/media/audios
static-map = /.well-known/acme-challenge/=/app/.well-known/acme-challenge
# 日志输出到标准输出
logger = stdout
# 启用thunder lock
thunder-lock = true
# 添加以下配置以处理静态和媒体文件
static-map = /static=/www/wwwroot/Ai_Admin/static
static-map = /media=/www/wwwroot/Ai_Admin/media
static-map = /media/avatars=/www/wwwroot/Ai_Admin/media/avatars
static-map = /media/audios=/www/wwwroot/Ai_Admin/media/audios
static-map = /.well-known/acme-challenge/=/www/wwwroot/java_node_ssl/.well-known/acme-challenge

1209
uwsgi.log Executable file

File diff suppressed because it is too large Load Diff

1
uwsgi.pid Executable file
View File

@ -0,0 +1 @@
145523