最新版本

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 json
import traceback import traceback
from decimal import Decimal from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse from django.http import JsonResponse
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel from alipay.aop.api.domain.AlipayTradeAppPayModel import AlipayTradeAppPayModel
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from alipay.aop.api.request.AlipayTradeAppPayRequest import AlipayTradeAppPayRequest from alipay.aop.api.request.AlipayTradeAppPayRequest import AlipayTradeAppPayRequest
from .models import Order, User, Plan from .models import Order, User, Plan
from datetime import datetime from datetime import datetime
from alipay.aop.api.domain.AlipayTradeWapPayModel import AlipayTradeWapPayModel from alipay.aop.api.domain.AlipayTradeWapPayModel import AlipayTradeWapPayModel
from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayRequest from alipay.aop.api.request.AlipayTradeWapPayRequest import AlipayTradeWapPayRequest
import requests import requests
from decimal import Decimal, getcontext from decimal import Decimal, getcontext
# 设置 Decimal 的精度 # 设置 Decimal 的精度
getcontext().prec = 28 getcontext().prec = 28
def get_usd_to_cny_rate(): def get_usd_to_cny_rate():
a = 7.2 a = 7.2
return a return a
def get_alipay_client_config(client_type='pc'): def get_alipay_client_config(client_type='pc'):
""" """
根据环境和客户端类型返回支付宝配置 根据环境和客户端类型返回支付宝配置
""" """
alipay_client_config = AlipayClientConfig() alipay_client_config = AlipayClientConfig()
if settings.ALIPAY_DEBUG: if settings.ALIPAY_DEBUG:
print('请求沙箱支付') print('请求沙箱支付')
alipay_client_config.server_url = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do' alipay_client_config.server_url = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do'
if client_type == 'pc': if client_type == 'pc':
alipay_client_config.app_id = settings.ALIPAY_APP_ID_SANDBOX_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.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_SANDBOX_PC
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_SANDBOX_PC alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_SANDBOX_PC
elif client_type == 'mobile': elif client_type == 'mobile':
alipay_client_config.app_id = settings.ALIPAY_APP_ID_SANDBOX_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.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_SANDBOX_MOBILE
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_SANDBOX_MOBILE alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_SANDBOX_MOBILE
else: else:
print('请求生产支付') print('请求生产支付')
alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do' alipay_client_config.server_url = 'https://openapi.alipay.com/gateway.do'
if client_type == 'pc': if client_type == 'pc':
alipay_client_config.app_id = settings.ALIPAY_APP_ID_PRODUCTION_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.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_PRODUCTION_PC
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_PRODUCTION_PC alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_PRODUCTION_PC
elif client_type == 'mobile': elif client_type == 'mobile':
alipay_client_config.app_id = settings.ALIPAY_APP_ID_PRODUCTION_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.app_private_key = settings.ALIPAY_APP_PRIVATE_KEY_PRODUCTION_MOBILE
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_PRODUCTION_MOBILE alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_PRODUCTION_MOBILE
return alipay_client_config return alipay_client_config
def convert_cny_to_usd(price_cny): def convert_cny_to_usd(price_cny):
""" """
将人民币价格转换为美元价格 将人民币价格转换为美元价格
:param price_cny: 人民币价格 :param price_cny: 人民币价格
:return: 美元价格保留两位小数 :return: 美元价格保留两位小数
""" """
# 获取美元对人民币的汇率 # 获取美元对人民币的汇率
usd_to_cny_rate = get_usd_to_cny_rate() usd_to_cny_rate = get_usd_to_cny_rate()
print("原价:", price_cny) print("原价:", price_cny)
print("汇率:", usd_to_cny_rate) print("汇率:", usd_to_cny_rate)
return (Decimal(price_cny) * Decimal(usd_to_cny_rate)).quantize(Decimal('0.00')) return (Decimal(price_cny) * Decimal(usd_to_cny_rate)).quantize(Decimal('0.00'))
@csrf_exempt @csrf_exempt
def create_alipay_order(request): def create_alipay_order(request):
""" """
创建支付宝订单并生成支付链接电脑端 创建支付宝订单并生成支付链接电脑端
""" """
try: try:
# 检查用户是否已登录 # 检查用户是否已登录
if not request.user.is_authenticated: if not request.user.is_authenticated:
print("用户未登录") print("用户未登录")
return JsonResponse({"code": 401, "message": "用户未登录"}) return JsonResponse({"code": 401, "message": "用户未登录"})
# 获取前端传递的数据 # 获取前端传递的数据
data = json.loads(request.body) data = json.loads(request.body)
print(f"接收到的请求数据: {data}") print(f"接收到的请求数据: {data}")
plan_title = data.get('title') plan_title = data.get('title')
price = Decimal(data.get('price')) # 将价格转换为 Decimal 类型 price = Decimal(data.get('price')) # 将价格转换为 Decimal 类型
plan_id = data.get('id') plan_id = data.get('id')
# 从数据库获取对应的套餐信息 # 从数据库获取对应的套餐信息
plan = Plan.objects.get(id=plan_id) plan = Plan.objects.get(id=plan_id)
print(f"数据库中获取的套餐信息: {plan}") print(f"数据库中获取的套餐信息: {plan}")
if not plan: if not plan:
print("套餐信息不存在") print("套餐信息不存在")
return JsonResponse({"code": 400, "message": "套餐信息不存在"}) return JsonResponse({"code": 400, "message": "套餐信息不存在"})
# 检查汇率是否有效 # 检查汇率是否有效
# 获取美元对人民币的汇率 # 获取美元对人民币的汇率
usd_to_cny_rate = get_usd_to_cny_rate() usd_to_cny_rate = get_usd_to_cny_rate()
if not usd_to_cny_rate: if not usd_to_cny_rate:
print("无法进行价格转换,汇率不可用") print("无法进行价格转换,汇率不可用")
return JsonResponse({"code": 500, "message": "汇率不可用,无法创建订单"}) return JsonResponse({"code": 500, "message": "汇率不可用,无法创建订单"})
# 将套餐的人民币价格转换为美元价格 # 将套餐的人民币价格转换为美元价格
plan_price_usd = convert_cny_to_usd(plan.price) plan_price_usd = convert_cny_to_usd(plan.price)
# 将人民币价格转换为美元价格 # 将人民币价格转换为美元价格
price_usd = convert_cny_to_usd(price) price_usd = convert_cny_to_usd(price)
# 比较转换后的美元价格 # 比较转换后的美元价格
if not price_usd or plan_price_usd != price_usd: if not price_usd or plan_price_usd != price_usd:
print(f"{plan_price_usd}套餐信息不匹配或价格不正确{price_usd}") print(f"{plan_price_usd}套餐信息不匹配或价格不正确{price_usd}")
return JsonResponse({"code": 400, "message": "套餐信息不匹配或价格不正确"}) return JsonResponse({"code": 400, "message": "套餐信息不匹配或价格不正确"})
final_price = price_usd final_price = price_usd
# 获取当前用户信息 # 获取当前用户信息
user = request.user user = request.user
# 生成订单 # 生成订单
order_id = f"order_{datetime.now().strftime('%Y%m%d%H%M%S')}_{user.id}" order_id = f"order_{datetime.now().strftime('%Y%m%d%H%M%S')}_{user.id}"
order = Order.objects.create( order = Order.objects.create(
order_id=order_id, order_id=order_id,
user=user, user=user,
plan=plan, plan=plan,
username=user.username, username=user.username,
amount=final_price, amount=final_price,
payment_method='alipay', payment_method='alipay',
status='pending' status='pending'
) )
print(f"创建的订单信息: {order}") print(f"创建的订单信息: {order}")
# 获取支付宝电脑端客户端配置 # 获取支付宝电脑端客户端配置
alipay_client_config = get_alipay_client_config(client_type='pc') alipay_client_config = get_alipay_client_config(client_type='pc')
client = DefaultAlipayClient(alipay_client_config=alipay_client_config) client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
# 构造支付宝支付请求对象 # 构造支付宝支付请求对象
model = AlipayTradePagePayModel() model = AlipayTradePagePayModel()
model.out_trade_no = order_id # 订单ID model.out_trade_no = order_id # 订单ID
model.total_amount = str(final_price) # 订单金额 model.total_amount = str(final_price) # 订单金额
model.subject = plan_title # 商品名称 model.subject = plan_title # 商品名称
model.product_code = "FAST_INSTANT_TRADE_PAY" # 产品码 model.product_code = "FAST_INSTANT_TRADE_PAY" # 产品码
print(f"生成订单请求体: {model}") print(f"生成订单请求体: {model}")
request = AlipayTradePagePayRequest(biz_model=model) request = AlipayTradePagePayRequest(biz_model=model)
request.notify_url = settings.ALIPAY_NOTIFY_URL # 异步通知URL request.notify_url = settings.ALIPAY_NOTIFY_URL # 异步通知URL
request.return_url = settings.ALIPAY_RETURN_URL # 同步返回URL request.return_url = settings.ALIPAY_RETURN_URL # 同步返回URL
print(f"回调地址: {settings.ALIPAY_NOTIFY_URL}") print(f"回调地址: {settings.ALIPAY_NOTIFY_URL}")
response = client.page_execute(request, http_method="GET") response = client.page_execute(request, http_method="GET")
print(f"支付宝请求响应: {response}") print(f"支付宝请求响应: {response}")
# 返回支付URL # 返回支付URL
return JsonResponse({"code": 200, "message": "订单创建成功", "data": {'alipay_url': response}}) return JsonResponse({"code": 200, "message": "订单创建成功", "data": {'alipay_url': response}})
except Exception as e: except Exception as e:
print(f"创建订单时出错: {traceback.format_exc()}") print(f"创建订单时出错: {traceback.format_exc()}")
return JsonResponse({"code": 500, "message": f"创建订单时出错: {str(e)}"}) return JsonResponse({"code": 500, "message": f"创建订单时出错: {str(e)}"})
@csrf_exempt @csrf_exempt
def create_alipay_h5_order(request): def create_alipay_h5_order(request):
""" """
创建支付宝H5订单并生成支付链接 创建支付宝H5订单并生成支付链接
""" """
try: try:
# 检查用户是否已登录 # 检查用户是否已登录
if not request.user.is_authenticated: if not request.user.is_authenticated:
return JsonResponse({"code": 401, "message": "用户未登录"}) return JsonResponse({"code": 401, "message": "用户未登录"})
# 获取前端传递的数据 # 获取前端传递的数据
data = json.loads(request.body) data = json.loads(request.body)
plan_id = data.get('id') plan_id = data.get('id')
plan_title = data.get('title') plan_title = data.get('title')
price = Decimal(data.get('price')) # 将价格转换为 Decimal 类型 price = Decimal(data.get('price')) # 将价格转换为 Decimal 类型
# 获取对应套餐信息 # 获取对应套餐信息
plan = Plan.objects.get(id=plan_id) plan = Plan.objects.get(id=plan_id)
if not plan: if not plan:
return JsonResponse({"code": 400, "message": "套餐信息不存在"}) return JsonResponse({"code": 400, "message": "套餐信息不存在"})
# 检查汇率是否有效 # 检查汇率是否有效
# 获取美元对人民币的汇率 # 获取美元对人民币的汇率
usd_to_cny_rate = get_usd_to_cny_rate() usd_to_cny_rate = get_usd_to_cny_rate()
if not usd_to_cny_rate: if not usd_to_cny_rate:
print("无法进行价格转换,汇率不可用") print("无法进行价格转换,汇率不可用")
return JsonResponse({"code": 500, "message": "汇率不可用,无法创建订单"}) return JsonResponse({"code": 500, "message": "汇率不可用,无法创建订单"})
# 将套餐的人民币价格转换为美元价格 # 将套餐的人民币价格转换为美元价格
plan_price_usd = convert_cny_to_usd(plan.price) plan_price_usd = convert_cny_to_usd(plan.price)
# 将人民币价格转换为美元价格 # 将人民币价格转换为美元价格
price_usd = convert_cny_to_usd(price) price_usd = convert_cny_to_usd(price)
# 比较转换后的美元价格 # 比较转换后的美元价格
if not price_usd or plan_price_usd != price_usd: if not price_usd or plan_price_usd != price_usd:
return JsonResponse({"code": 400, "message": "套餐信息不匹配或价格不正确"}) return JsonResponse({"code": 400, "message": "套餐信息不匹配或价格不正确"})
final_price = price_usd final_price = price_usd
# 创建订单 # 创建订单
user = request.user user = request.user
order_id = f"order_{datetime.now().strftime('%Y%m%d%H%M%S')}_{user.id}" order_id = f"order_{datetime.now().strftime('%Y%m%d%H%M%S')}_{user.id}"
order = Order.objects.create( order = Order.objects.create(
order_id=order_id, order_id=order_id,
user=user, user=user,
plan=plan, plan=plan,
username=user.username, username=user.username,
amount=final_price, amount=final_price,
payment_method='alipay', payment_method='alipay',
status='pending' status='pending'
) )
# 获取支付宝H5客户端配置 # 获取支付宝H5客户端配置
alipay_client_config = get_alipay_client_config(client_type='mobile') alipay_client_config = get_alipay_client_config(client_type='mobile')
client = DefaultAlipayClient(alipay_client_config=alipay_client_config) client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
# 构造支付宝H5支付请求对象 # 构造支付宝H5支付请求对象
model = AlipayTradeWapPayModel() model = AlipayTradeWapPayModel()
model.out_trade_no = order_id model.out_trade_no = order_id
model.total_amount = str(final_price) model.total_amount = str(final_price)
model.subject = plan_title model.subject = plan_title
model.product_code = "QUICK_WAP_WAY" model.product_code = "QUICK_WAP_WAY"
request = AlipayTradeWapPayRequest(biz_model=model) request = AlipayTradeWapPayRequest(biz_model=model)
request.notify_url = settings.ALIPAY_NOTIFY_URL # 异步通知URL request.notify_url = settings.ALIPAY_NOTIFY_URL # 异步通知URL
request.return_url = settings.ALIPAY_RETURN_URL # 同步返回URL request.return_url = settings.ALIPAY_RETURN_URL # 同步返回URL
# 执行请求获取支付宝H5支付跳转链接 # 执行请求获取支付宝H5支付跳转链接
response = client.page_execute(request, http_method="GET") response = client.page_execute(request, http_method="GET")
return JsonResponse({"code": 200, "message": "订单创建成功", "data": {'alipay_url': response}}) return JsonResponse({"code": 200, "message": "订单创建成功", "data": {'alipay_url': response}})
except Exception as e: except Exception as e:
return JsonResponse({"code": 500, "message": f"创建H5订单时出错: {str(e)}"}) return JsonResponse({"code": 500, "message": f"创建H5订单时出错: {str(e)}"})
def update_user_membership(order): def update_user_membership(order):
""" """
更新用户积分信息 更新用户积分信息
""" """
try: try:
# 验证订单状态是否为成功 # 如果订单已经是 'completed' 状态,说明已经处理过,跳过处理
if order.status not in ("completed", "TRADE_SUCCESS", "TRADE_FINISHED"): if order.status == 'completed':
print("订单未成功") print("订单已处理,无需再次更新")
return False return False
# 从订单中获取关联的套餐信息 # 获取套餐信息
plan = order.plan plan = order.plan
# 检查汇率是否有效 # 检查汇率是否有效
# 获取美元对人民币的汇率 usd_to_cny_rate = get_usd_to_cny_rate()
usd_to_cny_rate = get_usd_to_cny_rate() if not usd_to_cny_rate:
if not usd_to_cny_rate: print("无法进行价格转换,汇率不可用")
print("无法进行价格转换,汇率不可用") return False
return False
# 将订单金额从美元转换回人民币 # 确保订单的套餐与实际套餐详情匹配
order_amount_cny = order.amount 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
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 = order.user user.points += plan.credits_per_month
user.save()
# 更新用户的积分
user.points += plan.credits_per_month # 更新订单状态为 'completed'
user.save() order.status = 'completed'
order.save()
print(f"用户 {user.username} 的积分已更新")
return True print(f"用户 {user.username} 的积分已更新--增加{plan.credits_per_month}--现在{user.points}")
return True
except Exception as e:
print(f"更新用户积分信息时出错: {e}") except Exception as e:
return False print(f"更新用户积分信息时出错: {e}")
return False
@csrf_exempt
def alipay_notify(request):
""" @csrf_exempt
支付宝异步通知处理验证支付结果 def alipay_notify(request):
""" """
try: 支付宝异步通知处理验证支付结果
data = request.POST.dict() """
print(f"接收到的异步通知数据: {data}") try:
data = request.POST.dict()
# 直接使用返回的数据处理逻辑 print(f"接收到的异步通知数据: {data}")
order_id = data.get('out_trade_no')
trade_status = data.get('trade_status') # 获取订单ID和交易状态
print(f"订单ID: {order_id}, 支付状态: {trade_status}") order_id = data.get('out_trade_no')
trade_status = data.get('trade_status')
try: print(f"订单ID: {order_id}, 支付状态: {trade_status}")
order = Order.objects.get(order_id=order_id)
except Order.DoesNotExist: try:
print(f"订单 {order_id} 不存在") order = Order.objects.get(order_id=order_id)
return JsonResponse({"code": 400, "message": "订单不存在"}) except Order.DoesNotExist:
print(f"订单 {order_id} 不存在")
if trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"): return JsonResponse({"code": 400, "message": "订单不存在"})
order.status = 'completed'
order.save() # 如果订单状态已经是 'completed',表示已经处理过,无需再次处理
print(f"订单 {order_id} 支付成功") if order.status == 'completed':
update_user_membership(order) print(f"订单 {order_id} 已处理,无需再次更新")
return JsonResponse({"code": 200, "message": "支付成功"}) return JsonResponse({"code": 200, "message": "订单已处理"})
else:
order.status = 'failed' # 如果支付成功或交易完成,更新订单状态并处理会员更新
order.save() if trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
print(f"订单 {order_id} 支付失败或未成功") print(f"订单 {order_id} 支付成功")
return JsonResponse({"code": 400, "message": "支付未成功或失败"}) update_user_membership(order)
return JsonResponse({"code": 200, "message": "支付成功"})
except Exception as e: else:
print(f"处理异步通知时出错: {traceback.format_exc()}") order.status = 'failed'
return JsonResponse({"code": 500, "message": f"处理异步通知时出错: {str(e)}"}) order.save()
print(f"订单 {order_id} 支付失败或未成功")
@csrf_exempt return JsonResponse({"code": 400, "message": "支付未成功或失败"})
def alipay_return(request):
""" except Exception as e:
支付宝同步返回处理用于用户支付后的页面跳转 print(f"处理异步通知时出错: {traceback.format_exc()}")
""" return JsonResponse({"code": 500, "message": f"处理异步通知时出错: {str(e)}"})
try:
data = request.GET.dict() @csrf_exempt
print(f"接收到的同步返回数据: {data}") def alipay_return(request):
"""
# 直接使用返回的数据处理逻辑 支付宝同步返回处理用于用户支付后的页面跳转
order_id = data.get('out_trade_no') """
print(f"订单 {order_id} 支付成功") try:
return JsonResponse({"code": 200, "message": f"支付成功,订单号:{order_id}"}) data = request.GET.dict()
print(f"接收到的同步返回数据: {data}")
except Exception as e:
print(f"处理同步返回时出错: {traceback.format_exc()}") # 直接使用返回的数据处理逻辑
return JsonResponse({"code": 500, "message": f"处理同步返回时出错: {str(e)}"}) 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 json
import time import time
import requests import requests
from django.http import JsonResponse from django.http import JsonResponse
from .models import VideoGeneration from .models import VideoGeneration
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from .base import logger,deduct_points from .base import logger,deduct_points
def generate_video_id(user_id): import http.client
""" import random
根据用户ID和当前时间戳生成唯一的视频ID import urllib
""" import hashlib
timestamp = int(time.time()) from langdetect import detect
video_id = f"{timestamp}_{user_id}" from langdetect.lang_detect_exception import LangDetectException
print(f"生成的视频ID: {video_id}") def generate_video_id(user_id):
return video_id """
根据用户ID和当前时间戳生成唯一的视频ID
def get_dimensions(model, width=None, height=None): """
""" timestamp = int(time.time())
根据模型返回相应的宽高分辨率: video_id = f"{timestamp}_{user_id}"
gen3: 最大1280x768 print(f"生成的视频ID: {video_id}")
gen2: 可传入前端指定的宽高默认最大1920x1080 return video_id
"""
if model == 'gen3': appid = '20240601002067404' # 填写你的appid
return 1280, 768 secretKey = '6pRZ9HCSqGuMqzLO55hB' # 填写你的密钥
elif model == 'gen2':
# 如果前端提供了 width 和 height则使用传入的值否则使用默认值 def translate(text, from_lang='auto', to_lang='en'):
if width and height: myurl = '/api/trans/vip/translate'
return width, height salt = random.randint(32768, 65536)
return 1920, 1080 sign = appid + text + str(salt) + secretKey
else: sign = hashlib.md5(sign.encode()).hexdigest()
raise ValueError(f"不支持的模型: {model}") myurl = (myurl + '?appid=' + appid + '&q=' + urllib.parse.quote(text) +
'&from=' + from_lang + '&to=' + to_lang + '&salt=' + str(salt) +
def get_headers(): '&sign=' + sign)
"""
返回统一的请求头 try:
""" httpClient = http.client.HTTPConnection('api.fanyi.baidu.com')
return { httpClient.request('GET', myurl)
"accept": "application/json",
"content-type": "application/json", # response是HTTPResponse对象
"Authorization": "12e76710fad2047db8c0cc6b25987e2a2" # 替换为你的真实授权密钥 response = httpClient.getresponse()
} result_all = response.read().decode("utf-8")
result = json.loads(result_all)
@csrf_exempt print(f'请求结果{result}')
def text_to_video(request): return result['trans_result'][0]['dst']
"""
文本生成视频接口 except Exception as e:
""" return str(e)
if request.method == "POST":
try: finally:
if not request.user.is_authenticated: if httpClient:
return JsonResponse({'code': 401, 'message': '用户未登录'}) httpClient.close()
user = request.user
data = json.loads(request.body)
def get_dimensions(model, width=None, height=None):
# 获取参数 """
text_prompt = data.get('text_prompt') 根据模型返回相应的宽高分辨率:
style = data.get('style', '') # 选择的风格,默认是 'general' gen3: 最大1280x768
model = data.get('model', 'gen3') # 选择模型,默认是 'gen3' gen2: 可传入前端指定的宽高默认最大1920x1080
time_duration = int(data.get('time', 5)) # 视频时长 """
motion = int(data.get('motion', 5)) # 视频丰富度参数 if model == 'gen3':
width = data.get('width') # 由前端传入的宽度仅限gen2 return 1280, 768
height = data.get('height') # 由前端传入的高度仅限gen2 elif model == 'gen2':
# 如果前端提供了 width 和 height则使用传入的值否则使用默认值
print(f"生成模型: {model}, 时长: {time_duration}, 文本: {text_prompt}, 宽高: {width}x{height}, 画面丰富度: {motion}") if width and height:
return width, height
result = deduct_points(user, "text-to-video") return 1920, 1080
if result is not True: else:
return result # 返回积分不足的响应 raise ValueError(f"不支持的模型: {model}")
# 验证模型和时长限制 def get_headers():
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 的时长'}) return {
"accept": "application/json",
# 生成视频ID "content-type": "application/json",
video_id = generate_video_id(user.id) "Authorization": "12e76710fad2047db8c0cc6b25987e2a2" # 替换为你的真实授权密钥
}
# 根据模型获取宽高
width, height = get_dimensions(model, width, height)
print(f"生成的视频分辨率: {width}x{height}")
@csrf_exempt
# 准备POST请求数据 def text_to_video(request):
payload = { """
"text_prompt": f"{text_prompt}. Style: {style}", 文本生成视频接口
"model": model, """
"width": width, if request.method == "POST":
"height": height, try:
"motion": motion, # 视频画面丰富度 if not request.user.is_authenticated:
"seed": 0, return JsonResponse({'code': 401, 'message': '用户未登录'})
"upscale": False, user = request.user
"interpolate": False, data = json.loads(request.body)
"callback_url": "https://www.typeframes.ai/api/callback/", text_prompt = data.get('text_prompt')
"time": time_duration # 视频时长 print(f'文生视频原内容---{text_prompt}')
}
print(f'text_to_video 请求体: {payload}') # 判断是否需要翻译
try:
# 发送请求到API language = detect(text_prompt)
response = requests.post('https://api.aivideoapi.com/runway/generate/text', json=payload, headers=get_headers()) print(f"检测到的语言: {language}")
print(f"API响应状态码: {response.status_code}") if language == 'en':
print(f"API响应内容: {response.text}") description = text_prompt # 英文无需翻译
else:
# 解析响应数据 description = translate(text_prompt, 'auto', 'en') # 翻译成英文
if response.status_code == 200: print(f'文生视频翻译内容---{description}')
res_data = response.json() except LangDetectException:
uuid = res_data.get('uuid') return JsonResponse({'code': 400, 'message': '无法检测语言,请输入有效文本'})
# 保存生成记录到数据库 # 获取参数
video_generation = VideoGeneration.objects.create( style = data.get('style', '') # 选择的风格,默认是 'general'
video_id=video_id, model = data.get('model', 'gen3') # 选择模型,默认是 'gen3'
user=user, time_duration = int(data.get('time', 5)) # 视频时长
text=text_prompt, motion = int(data.get('motion', 5)) # 视频丰富度参数
status='in_progress', width = data.get('width') # 由前端传入的宽度仅限gen2
pid=uuid, height = data.get('height') # 由前端传入的高度仅限gen2
media_type=f'{model}_text_to_video',
slug=f'text-to-video', print(f"生成模型: {model}, 时长: {time_duration}, 文本: {text_prompt}, 宽高: {width}x{height}, 画面丰富度: {motion}")
ratio=f"{width}:{height}"
) result = deduct_points(user, "text-to-video")
video_generation.save() if result is not True:
return result # 返回积分不足的响应
# 返回成功响应
return JsonResponse({ # 验证模型和时长限制
'code': 200, if model == 'gen3' and time_duration not in [5, 10]:
'message': '请求成功,视频正在生成中', return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'})
'data': { if model == 'gen2' and time_duration != 4:
'video_id': video_generation.video_id, return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'})
'status': video_generation.status,
'pid': uuid # 生成视频ID
} video_id = generate_video_id(user.id)
})
else: # 根据模型获取宽高
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}}) width, height = get_dimensions(model, width, height)
print(f"生成的视频分辨率: {width}x{height}")
except Exception as e:
print(f"发生错误: {str(e)}") # 准备POST请求数据
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}}) payload = {
"text_prompt": f"{description}. Style: {style}",
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}}) "model": model,
"width": width,
"height": height,
"motion": motion, # 视频画面丰富度
@csrf_exempt "seed": 0,
def image_to_video(request): "upscale": True,
""" "interpolate": True,
图片生成视频接口图片宽高自适应 "callback_url": "https://www.typeframes.ai/api/callback/",
""" "time": time_duration # 视频时长
if request.method == "POST": }
try: print(f'text_to_video 请求体: {payload}')
if not request.user.is_authenticated:
return JsonResponse({'code': 401, 'message': '用户未登录'}) # 发送请求到API
user = request.user response = requests.post('https://api.aivideoapi.com/runway/generate/text', json=payload, headers=get_headers())
data = json.loads(request.body) print(f"API响应状态码: {response.status_code}")
print(f"API响应内容: {response.text}")
# 获取参数
text_prompt = data.get('text_prompt') # 解析响应数据
image_url = data.get('image_url') if response.status_code == 200:
model = data.get('model', 'gen3') # 选择模型,默认是 'gen3' res_data = response.json()
time_duration = int(data.get('time', 5)) # 视频时长 uuid = res_data.get('uuid')
motion = int(data.get('motion', 5)) # 视频丰富度
# 保存生成记录到数据库
print(f"生成模型: {model}, 图片地址: {image_url}, 时长: {time_duration}, 画面丰富度: {motion}") video_generation = VideoGeneration.objects.create(
video_id=video_id,
result = deduct_points(user, "img-to-video") user=user,
if result is not True: text=text_prompt,
return result # 返回积分不足的响应 status='in_progress',
pid=uuid,
# 验证模型和时长限制 media_type=f'{model}_text_to_video',
if model == 'gen3' and time_duration not in [5, 10]: slug=f'text-to-video',
return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'}) ratio=f"{width}:{height}"
if model == 'gen2' and time_duration != 4: )
return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'}) video_generation.save()
# 生成视频ID # 返回成功响应
video_id = generate_video_id(user.id) return JsonResponse({
'code': 200,
# 准备POST请求数据宽高不需要传入图片自适应 'message': '请求成功,视频正在生成中',
payload = { 'data': {
"text_prompt": text_prompt, 'video_id': video_generation.video_id,
"model": model, 'status': video_generation.status,
"img_prompt": image_url, 'pid': uuid
"image_as_end_frame": False, # 图片不作为最后一帧 }
"motion": motion, # 视频丰富度 })
"seed": 0, else:
"upscale": False, return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
"interpolate": False,
"callback_url": "https://www.typeframes.ai/api/callback/", except Exception as e:
"time": time_duration # 视频时长 print(f"发生错误: {str(e)}")
} return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
print(f'img_to_video 请求体: {payload}')
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
# 请求头
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}") @csrf_exempt
def image_to_video(request):
# 解析响应数据 """
if response.status_code == 200: 图片生成视频接口图片宽高自适应
res_data = response.json() """
uuid = res_data.get('uuid') if request.method == "POST":
try:
# 保存生成记录到数据库 if not request.user.is_authenticated:
video_generation = VideoGeneration.objects.create( return JsonResponse({'code': 401, 'message': '用户未登录'})
video_id=video_id, user = request.user
user=user, data = json.loads(request.body)
text=text_prompt,
status='in_progress', # 获取参数
pid=uuid, text_prompt = data.get('text_prompt')
media_type=f'{model}_img_to_video', print(f' 图片生视频原内容---{text_prompt}')
slug=f'img-to-video',
ratio="auto" # 图片自适应宽高 # 判断是否需要翻译
) try:
video_generation.save() language = detect(text_prompt)
print(f"检测到的语言: {language}")
# 返回成功响应 if language == 'en':
return JsonResponse({ description = text_prompt # 英文无需翻译
'code': 200, else:
'message': '请求成功,视频正在生成中', description = translate(text_prompt, 'auto', 'en') # 翻译成英文
'data': { print(f'图片生视翻译内容---{description}')
'video_id': video_generation.video_id, except LangDetectException:
'status': video_generation.status, return JsonResponse({'code': 400, 'message': '无法检测语言,请输入有效文本'})
'pid': uuid
} image_url = data.get('image_url')
}) model = data.get('model', 'gen3') # 选择模型,默认是 'gen3'
else: time_duration = int(data.get('time', 5)) # 视频时长
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}}) motion = int(data.get('motion', 5)) # 视频丰富度
except Exception as e: print(f"生成模型: {model}, 图片地址: {image_url}, 时长: {time_duration}, 画面丰富度: {motion}")
print(f"发生错误: {str(e)}")
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}}) result = deduct_points(user, "img-to-video")
if result is not True:
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}}) return result # 返回积分不足的响应
# 验证模型和时长限制
if model == 'gen3' and time_duration not in [5, 10]:
@csrf_exempt return JsonResponse({'code': 400, 'message': 'gen3 模型只支持 5s 或 10s 的时长'})
def extend_video(request): if model == 'gen2' and time_duration != 4:
""" return JsonResponse({'code': 400, 'message': 'gen2 模型只支持 4s 的时长'})
延长视频的函数发送请求到 /runway/extend
""" # 生成视频ID
if request.method == "POST": video_id = generate_video_id(user.id)
try:
if not request.user.is_authenticated: # 准备POST请求数据宽高不需要传入图片自适应
return JsonResponse({'code': 401, 'message': '用户未登录'}) payload = {
user = request.user "text_prompt": description,
data = json.loads(request.body) "model": model,
"img_prompt": image_url,
# 获取参数 "image_as_end_frame": False, # 图片不作为最后一帧
pid = data.get('pid') "motion": motion, # 视频丰富度
motion = int(data.get('motion', 5)) # 视频丰富度 "seed": 0,
"upscale": True,
# 获取当前视频生成记录 "interpolate": True,
video_generation = VideoGeneration.objects.get(pid=pid) "callback_url": "https://www.typeframes.ai/api/callback/",
"time": time_duration # 视频时长
# 通过 media_type 判断模型是 gen3 还是 gen2 }
if 'gen3' in video_generation.media_type: print(f'img_to_video 请求体: {payload}')
model = 'gen3'
# 验证 gen3 的延长逻辑 # 请求头
if video_generation.extension_count >= 1 or video_generation.time_duration >= 10: headers = get_headers()
return JsonResponse({'code': 400, 'message': 'gen3 最大时长为 10s无法继续延长'})
time_extension = 5 # 每次延长 5 秒 # 发送请求到API
response = requests.post('https://api.aivideoapi.com/runway/generate/imageDescription', json=payload, headers=headers)
elif 'gen2' in video_generation.media_type: print(f"API响应状态码: {response.status_code}")
model = 'gen2' print(f"API响应内容: {response.text}")
# 验证 gen2 的延长逻辑
if video_generation.extension_count >= 2 or video_generation.time_duration >= 12: # 解析响应数据
return JsonResponse({'code': 400, 'message': 'gen2 最大时长为 12s无法继续延长'}) if response.status_code == 200:
time_extension = 4 # 每次延长 4 秒 res_data = response.json()
uuid = res_data.get('uuid')
else:
return JsonResponse({'code': 400, 'message': '未知的模型类型'}) # 保存生成记录到数据库
video_generation = VideoGeneration.objects.create(
result = deduct_points(user, video_generation.slug) video_id=video_id,
if result is not True: user=user,
return result # 返回积分不足的响应 text=text_prompt,
status='in_progress',
# 生成新的视频ID pid=uuid,
new_video_id = generate_video_id(user.id) media_type=f'{model}_img_to_video',
slug=f'img-to-video',
# 默认请求体 ratio="auto" # 图片自适应宽高
payload = { )
"uuid": pid, # 视频的 UUID video_generation.save()
"motion": motion, # 视频丰富度
"seed": 0, # 默认 seed 为 0 # 返回成功响应
"upscale": False, # 默认开启 upscale return JsonResponse({
"interpolate": False, # 默认开启 interpolate 'code': 200,
"callback_url": "https://www.typeframes.ai/api/callback/" 'message': '请求成功,视频正在生成中',
} 'data': {
print(f"extend_video 请求体: {payload}") 'video_id': video_generation.video_id,
'status': video_generation.status,
headers = get_headers() 'pid': uuid
}
# 发送 POST 请求 })
response = requests.post('https://api.aivideoapi.com/runway/extend', json=payload, headers=headers) else:
print(f"API响应状态码: {response.status_code}") return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}})
print(f"API响应内容: {response.text}")
except Exception as e:
# 解析响应内容 print(f"发生错误: {str(e)}")
if response.status_code == 200: return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}})
res_data = response.json()
new_pid = res_data.get('uuid') return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}})
# 创建新的延长视频记录,复制上一条视频的参数
new_video_generation = VideoGeneration.objects.create(
video_id=new_video_id, @csrf_exempt
user=user, def extend_video(request):
text=video_generation.text, """
voice_name=video_generation.voice_name, 延长视频的函数发送请求到 /runway/extend
style=video_generation.style, """
rate=video_generation.rate, if request.method == "POST":
media_type=video_generation.media_type, try:
ratio=video_generation.ratio, if not request.user.is_authenticated:
status='in_progress', return JsonResponse({'code': 401, 'message': '用户未登录'})
pid=new_pid, user = request.user
time_duration=video_generation.time_duration + time_extension, # 增加对应模型的时长 data = json.loads(request.body)
extension_count=video_generation.extension_count + 1, # 增加延长次数 print(data)
slug=video_generation.slug # slug 使用中划线 # 获取参数
) pid = data.get('task_id')
print(f"延长视频成功: {res_data}") motion = 6 # 视频丰富度
return JsonResponse({'code': 200, 'message': '视频延长请求成功', 'data': {'new_pid': new_pid}}) # 获取当前视频生成记录
video_generation = VideoGeneration.objects.get(pid=pid)
else:
return JsonResponse({'code': response.status_code, 'message': 'API请求失败', 'data': {}}) # 通过 media_type 判断模型是 gen3 还是 gen2
if 'gen3' in video_generation.media_type:
except Exception as e: model = 'gen3'
print(f"发生错误: {str(e)}") # 验证 gen3 的延长逻辑
return JsonResponse({'code': 500, 'message': f'内部错误: {str(e)}', 'data': {}}) if video_generation.extension_count >= 1 or video_generation.time_duration >= 10:
return JsonResponse({'code': 400, 'message': 'gen3 最大时长为 10s无法继续延长'})
return JsonResponse({'code': 405, 'message': '请求方法错误', 'data': {}}) 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 import requests
from django.conf import settings from django.conf import settings
from django.http import JsonResponse from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from datetime import datetime from datetime import datetime
from .models import Order, Plan, User from .models import Order, Plan, User
import json import json
import logging from .base import logger
logger = logging.getLogger(__name__) def get_paypal_access_token():
"""获取 PayPal 访问令牌"""
def get_paypal_access_token(): print("正在获取 PayPal 访问令牌...")
"""获取 PayPal 访问令牌"""
print("正在获取 PayPal 访问令牌...") # 根据配置文件选择 PayPal URL
url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/oauth2/token" paypal_base_url = "https://api.sandbox.paypal.com" if settings.PAYPAL_MODE == 'sandbox' else "https://api.paypal.com"
auth = ( url = f"{paypal_base_url}/v1/oauth2/token"
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 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" headers = {
} "Accept": "application/json",
data = { "Accept-Language": "en_US"
"grant_type": "client_credentials" }
} data = {
"grant_type": "client_credentials"
response = requests.post(url, headers=headers, data=data, auth=auth) }
if response.status_code == 200: response = requests.post(url, headers=headers, data=data, auth=auth)
access_token = response.json().get('access_token')
print(f"获取的访问令牌: {access_token}") if response.status_code == 200:
return access_token access_token = response.json().get('access_token')
else: logger.info(f"[支付执行] 获取的访问令牌: {access_token}")
print(f"获取 PayPal 访问令牌失败: {response.text}") print(f"获取的访问令牌: {access_token}")
return None return access_token
else:
@csrf_exempt logger.error(f"[支付执行] 获取 PayPal 访问令牌失败: {response.text}")
def create_paypal_payment(request): print(f"获取 PayPal 访问令牌失败: {response.text}")
"""创建 PayPal 支付""" return None
if request.method == 'POST': @csrf_exempt
try: def create_paypal_payment(request):
print("创建 PayPal 支付开始...") """创建 PayPal 支付"""
data = json.loads(request.body) if request.method == 'POST':
plan_id = data.get('plan_id') try:
payment_method = data.get('payment_method', 'paypal') print("创建 PayPal 支付开始...")
data = json.loads(request.body)
# 获取当前用户信息 plan_id = data.get('plan_id')
user = request.user payment_method = data.get('payment_method', 'paypal')
# 验证用户和计划是否存在 # 获取当前用户信息
if not user.is_authenticated: user = request.user
print("用户未认证")
return JsonResponse({"code": 401, "message": "用户未认证", "data": {}}) # 验证用户和计划是否存在
if not user.is_authenticated:
try: print("用户未认证")
plan = Plan.objects.get(id=plan_id) return JsonResponse({"code": 401, "message": "用户未认证", "data": {}})
print(f"找到的套餐: {plan.title}")
except Plan.DoesNotExist: try:
print("套餐不存在") plan = Plan.objects.get(id=plan_id)
return JsonResponse({"code": 400, "message": "套餐不存在", "data": {}}) print(f"找到的套餐: {plan.title}")
except Plan.DoesNotExist:
access_token = get_paypal_access_token() print("套餐不存在")
if not access_token: return JsonResponse({"code": 400, "message": "套餐不存在", "data": {}})
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
access_token = get_paypal_access_token()
# 配置支付请求 if not access_token:
url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/payments/payment" return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
headers = {
"Content-Type": "application/json", # 配置支付请求
"Authorization": f"Bearer {access_token}" url = f"https://api.paypal.com/v1/payments/payment"
} headers = {
payload = { "Content-Type": "application/json",
"intent": "sale", "Authorization": f"Bearer {access_token}"
"payer": {"payment_method": "paypal"}, }
"redirect_urls": { payload = {
"return_url": f"{settings.PAYPAL_RETURN_URL}", "intent": "sale",
"cancel_url": f"{settings.PAYPAL_CANCEL_URL}" "payer": {"payment_method": "paypal"},
}, "redirect_urls": {
"transactions": [{ "return_url": f"{settings.PAYPAL_RETURN_URL}",
"item_list": { "cancel_url": f"{settings.PAYPAL_CANCEL_URL}"
"items": [{ },
"name": plan.title, "transactions": [{
"sku": str(plan.id), "item_list": {
"price": str(plan.price), "items": [{
"currency": "USD", "name": plan.title,
"quantity": 1 "sku": str(plan.id),
}] "price": str(plan.price),
}, "currency": "USD",
"amount": { "quantity": 1
"total": str(plan.price), }]
"currency": "USD" },
}, "amount": {
"description": f"购买 {plan.title} 计划" "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)
print(f"支付请求Payload: {json.dumps(payload, indent=2)}")
if response.status_code == 201:
payment_id = response.json().get('id') response = requests.post(url, headers=headers, json=payload)
approval_url = None
for link in response.json().get('links', []): if response.status_code == 201:
if link.get('rel') == "approval_url": payment_id = response.json().get('id')
approval_url = link.get('href') approval_url = None
break for link in response.json().get('links', []):
if link.get('rel') == "approval_url":
# 检查订单是否已经存在,防止重复创建订单 approval_url = link.get('href')
if Order.objects.filter(order_id=payment_id).exists(): break
return JsonResponse({"code": 400, "message": "订单已存在", "data": {}})
# 检查订单是否已经存在,防止重复创建订单
# 创建订单并使用PayPal返回的paymentId作为order_id if Order.objects.filter(order_id=payment_id).exists():
order = Order.objects.create( return JsonResponse({"code": 400, "message": "订单已存在", "data": {}})
order_id=payment_id, # 使用PayPal的支付ID作为订单ID
user=user, # 创建订单并使用PayPal返回的paymentId作为order_id
username=user.username, order = Order.objects.create(
plan=plan, order_id=payment_id, # 使用PayPal的支付ID作为订单ID
amount=plan.price, user=user,
payment_method=payment_method, username=user.username,
status='pending' plan=plan,
) amount=plan.price,
print(f"订单创建成功订单ID: {order.order_id}") payment_method=payment_method,
status='pending'
if approval_url: )
print(f"支付批准URL: {approval_url}") print(f"订单创建成功订单ID: {order.order_id}")
return JsonResponse({"code": 200, "message": "支付创建成功", "data": {"approval_url": approval_url}})
else: if approval_url:
print("未找到支付批准URL") print(f"支付批准URL: {approval_url}")
return JsonResponse({"code": 400, "message": "未找到支付批准URL", "data": {}}) return JsonResponse({"code": 200, "message": "支付创建成功", "data": {"approval_url": approval_url}})
else: else:
print(f"支付创建失败: {response.status_code}, {response.json()}") print("未找到支付批准URL")
return JsonResponse({"code": 400, "message": "支付创建失败", "data": response.json()}) return JsonResponse({"code": 400, "message": "未找到支付批准URL", "data": {}})
else:
except json.JSONDecodeError: print(f"支付创建失败: {response.status_code}, {response.json()}")
print("请求体不是有效的JSON") return JsonResponse({"code": 400, "message": "支付创建失败", "data": response.json()})
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}})
except Exception as e: except json.JSONDecodeError:
print(f"支付创建过程中发生异常: {str(e)}") print("请求体不是有效的JSON")
return JsonResponse({"code": 500, "message": f"支付创建过程中发生异常: {str(e)}", "data": {}}) return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}})
else: except Exception as e:
return JsonResponse({"code": 405, "message": "方法不允许", "data": {}}) print(f"支付创建过程中发生异常: {str(e)}")
return JsonResponse({"code": 500, "message": f"支付创建过程中发生异常: {str(e)}", "data": {}})
@csrf_exempt else:
def execute_paypal_payment(request): return JsonResponse({"code": 405, "message": "方法不允许", "data": {}})
"""执行 PayPal 支付"""
if request.method == 'POST':
try: @csrf_exempt
data = json.loads(request.body) def execute_paypal_payment(request):
payment_id = data.get('paymentId') """执行 PayPal 支付"""
payer_id = data.get('PayerID') if request.method == 'POST':
try:
if not payment_id or not payer_id: logger.info("[支付执行] 接收到来自前端的 POST 请求,开始处理支付...")
return JsonResponse({"code": 400, "message": "缺少支付ID或付款人ID", "data": {}})
order = Order.objects.get(order_id=payment_id) # 解析请求体
if order.status == 'completed': data = json.loads(request.body)
return JsonResponse({"code": 400, "message": "订单已支付", "data": {}}) payment_id = data.get('paymentId')
# 获取PayPal访问令牌 payer_id = data.get('PayerID')
access_token = get_paypal_access_token()
if not access_token: logger.info(f"[支付执行] 请求内容: paymentId={payment_id}, PayerID={payer_id}")
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}}) print(f"请求内容: paymentId={payment_id}, PayerID={payer_id}")
# 执行支付确认请求 if not payment_id or not payer_id:
url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/payments/payment/{payment_id}/execute" logger.error("[支付执行] 缺少支付ID或付款人ID")
headers = { return JsonResponse({"code": 400, "message": "缺少支付ID或付款人ID", "data": {}})
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}" # 获取订单信息
} try:
payload = { order = Order.objects.get(order_id=payment_id)
"payer_id": payer_id logger.info(f"[支付执行] 获取到的订单信息: {order}")
} except Order.DoesNotExist:
logger.error("[支付执行] 订单不存在")
response = requests.post(url, headers=headers, json=payload) return JsonResponse({"code": 400, "message": "订单不存在", "data": {}})
if response.status_code == 200: # 检查订单是否已经支付
try: if order.status == 'completed':
order.status = 'completed' logger.info(f"[支付执行] 订单 {payment_id} 已支付,无需再次执行")
order.updated_at = datetime.now() print(f"订单 {payment_id} 已支付,无需再次执行")
order.save() return JsonResponse({"code": 400, "message": "订单已支付", "data": {}})
# 更新用户积分 # 获取 PayPal 访问令牌
user = order.user access_token = get_paypal_access_token()
user.points += order.plan.credits_per_month if not access_token:
user.save() logger.error("[支付执行] 无法获取支付访问令牌")
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
return JsonResponse({"code": 200, "message": "支付成功", "data": {}}) logger.info(f"[支付执行] 获取到的 PayPal 访问令牌: {access_token}")
except Order.DoesNotExist:
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) # 设置 PayPal API 请求的 URL 和头部信息
else: paypal_base_url = "https://api.sandbox.paypal.com" if settings.PAYPAL_MODE == 'sandbox' else "https://api.paypal.com"
return JsonResponse({"code": 400, "message": "支付执行失败", "data": response.json()}) url = f"{paypal_base_url}/v1/payments/payment/{payment_id}/execute"
headers = {
except json.JSONDecodeError: "Content-Type": "application/json",
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}}) "Authorization": f"Bearer {access_token}"
except Exception as e: }
return JsonResponse({"code": 500, "message": f"支付执行过程中发生异常: {str(e)}", "data": {}}) payload = {
else: "payer_id": payer_id
return JsonResponse({"code": 405, "message": "方法不允许", "data": {}}) }
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 import requests
from django.conf import settings from django.conf import settings
from django.http import JsonResponse from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from datetime import datetime from datetime import datetime
from .models import Order from .models import Order
import json import json
import logging from .base import logger
logger = logging.getLogger(__name__) def get_paypal_access_token():
"""获取 PayPal 访问令牌"""
def get_paypal_access_token(): print("正在获取 PayPal 访问令牌...")
"""获取 PayPal 访问令牌""" url = f"https://api.paypal.com/v1/oauth2/token"
print("正在获取 PayPal 访问令牌...") auth = (
url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/oauth2/token" settings.PAYPAL_CLIENT_ID_SANDBOX if settings.PAYPAL_MODE == 'sandbox' else settings.PAYPAL_CLIENT_ID_PRODUCTION,
auth = ( settings.PAYPAL_SECRET_KEY_SANDBOX if settings.PAYPAL_MODE == 'sandbox' else settings.PAYPAL_SECRET_KEY_PRODUCTION
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",
headers = { "Accept-Language": "en_US"
"Accept": "application/json", }
"Accept-Language": "en_US" data = {
} "grant_type": "client_credentials"
data = { }
"grant_type": "client_credentials"
} response = requests.post(url, headers=headers, data=data, auth=auth)
response = requests.post(url, headers=headers, data=data, auth=auth) if response.status_code == 200:
access_token = response.json().get('access_token')
if response.status_code == 200: logger.info(f"获取的访问令牌: {access_token}")
access_token = response.json().get('access_token') print(f"获取的访问令牌: {access_token}")
print(f"获取的访问令牌: {access_token}") return access_token
return access_token else:
else: logger.error(f"获取 PayPal 访问令牌失败: {response.text}")
print(f"获取 PayPal 访问令牌失败: {response.text}") print(f"获取 PayPal 访问令牌失败: {response.text}")
return None return None
def verify_paypal_webhook(headers, body):
"""使用 PayPal API 验证 Webhook 签名""" def verify_paypal_webhook(headers, body):
print("验证 PayPal Webhook 签名...") """使用 PayPal API 验证 Webhook 签名"""
verify_url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/notifications/verify-webhook-signature" logger.info("验证 PayPal Webhook 签名...")
auth_token = get_paypal_access_token() print("验证 PayPal Webhook 签名...")
if not auth_token: verify_url = f"https://api.paypal.com/v1/notifications/verify-webhook-signature"
print("无法获取 PayPal 访问令牌,无法验证 Webhook 签名")
return False auth_token = get_paypal_access_token()
if not auth_token:
verify_headers = { logger.error("无法获取 PayPal 访问令牌,无法验证 Webhook 签名")
"Content-Type": "application/json", print("无法获取 PayPal 访问令牌,无法验证 Webhook 签名")
"Authorization": f"Bearer {auth_token}" return False
}
verify_headers = {
verify_payload = { "Content-Type": "application/json",
"auth_algo": headers.get('Paypal-Auth-Algo'), "Authorization": f"Bearer {auth_token}"
"cert_url": headers.get('Paypal-Cert-Url'), }
"transmission_id": headers.get('Paypal-Transmission-Id'),
"transmission_sig": headers.get('Paypal-Transmission-Sig'), verify_payload = {
"transmission_time": headers.get('Paypal-Transmission-Time'), "auth_algo": headers.get('Paypal-Auth-Algo'),
"webhook_id": settings.PAYPAL_WEBHOOK_ID, "cert_url": headers.get('Paypal-Cert-Url'),
"webhook_event": json.loads(body) "transmission_id": headers.get('Paypal-Transmission-Id'),
} "transmission_sig": headers.get('Paypal-Transmission-Sig'),
"transmission_time": headers.get('Paypal-Transmission-Time'),
response = requests.post(verify_url, headers=verify_headers, json=verify_payload) "webhook_id": settings.PAYPAL_WEBHOOK_ID,
if response.status_code == 200 and response.json().get('verification_status') == 'SUCCESS': "webhook_event": json.loads(body)
print("Webhook 签名验证成功") }
return True
else: response = requests.post(verify_url, headers=verify_headers, json=verify_payload)
print(f"Webhook 签名验证失败: {response.text}")
return False if response.status_code == 200 and response.json().get('verification_status') == 'SUCCESS':
logger.info("Webhook 签名验证成功")
@csrf_exempt print("Webhook 签名验证成功")
def paypal_webhook(request): return True
"""PayPal Webhook 处理函数,用于接收和处理 PayPal 异步通知""" else:
try: logger.error(f"Webhook 签名验证失败: {response.text}")
print("接收到 PayPal Webhook 请求...") print(f"Webhook 签名验证失败: {response.text}")
headers = request.headers return False
payload = json.loads(request.body)
# 验证 Webhook 签名 @csrf_exempt
if not verify_paypal_webhook(headers, request.body): def execute_paypal_payment(payment_id, payer_id):
return JsonResponse({"code": 400, "message": "Webhook 签名验证失败", "data": {}}) """执行 PayPal 支付"""
try:
event_type = payload.get('event_type') logger.info(f"回调接收到 开始执行支付订单ID: {payment_id}, 付款人ID: {payer_id}")
order = Order.objects.get(order_id=payment_id)
# 打印接收到的 Webhook payload 以供调试
print(f"接收到 PayPal Webhook 事件: {event_type}") if order.status == 'completed':
print(f"Webhook payload: {json.dumps(payload, indent=2)}") logger.info(f"订单 {payment_id} 已经支付,不需要再次执行支付")
print(f"订单 {payment_id} 已经支付,不需要再次执行支付")
# 处理不同类型的事件 return JsonResponse({"code": 400, "message": "订单已支付", "data": {}})
if event_type == 'PAYMENT.SALE.COMPLETED':
sale_id = payload['resource'].get('id') # 获取 PayPal 访问令牌
parent_payment = payload['resource'].get('parent_payment') # 使用parent_payment作为订单ID access_token = get_paypal_access_token()
if parent_payment: if not access_token:
try: logger.error("无法获取支付访问令牌")
order = Order.objects.get(order_id=parent_payment) return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}})
# 检查订单状态,防止重复更新 # 根据配置文件选择 PayPal URL
if order.status == 'completed': paypal_base_url = "https://api.sandbox.paypal.com" if settings.PAYPAL_MODE == 'sandbox' else "https://api.paypal.com"
print(f"订单 {parent_payment} 已经完成,不重复更新") url = f"{paypal_base_url}/v1/payments/payment/{payment_id}/execute"
return JsonResponse({"code": 200, "message": "订单已处理", "data": {}}) headers = {
"Content-Type": "application/json",
# 再次向 PayPal 查询订单状态,确保支付确实完成 "Authorization": f"Bearer {access_token}"
access_token = get_paypal_access_token() }
if not access_token: payload = {
print("无法获取 PayPal 访问令牌,无法验证订单状态") "payer_id": payer_id
return JsonResponse({"code": 500, "message": "无法获取支付访问令牌", "data": {}}) }
payment_details_url = f"https://api.{settings.PAYPAL_MODE}.paypal.com/v1/payments/payment/{parent_payment}" logger.info(f"执行 PayPal 支付请求, URL: {url}, 请求体: {payload}")
headers = { print(f"执行 PayPal 支付请求, URL: {url}, 请求体: {payload}")
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}" response = requests.post(url, headers=headers, json=payload)
}
payment_details_response = requests.get(payment_details_url, headers=headers) # 处理支付执行结果
if response.status_code == 200:
if payment_details_response.status_code == 200: order.status = 'completed'
payment_details = payment_details_response.json() order.updated_at = datetime.now()
if payment_details['state'] == 'approved': order.save()
order.status = 'completed'
order.updated_at = datetime.now() # 更新用户积分
order.save() user = order.user
user.points += order.plan.credits_per_month
# 更新用户积分 user.save()
user = order.user
user.points += order.plan.credits_per_month logger.info(f"订单 {payment_id} 支付成功,用户积分已更新: {order.plan.credits_per_month}")
user.save() print(f"订单 {payment_id} 支付成功,用户积分已更新: {order.plan.credits_per_month}")
return JsonResponse({"code": 200, "message": "支付成功", "data": {}})
print(f"订单 {parent_payment} 状态更新为 'completed' 并已更新用户积分") else:
else: logger.error(f"支付执行失败: {response.text}")
print(f"支付未完成,状态: {payment_details['state']}") print(f"支付执行失败: {response.text}")
else: return JsonResponse({"code": 400, "message": "支付执行失败", "data": response.json()})
print(f"查询支付状态失败: {payment_details_response.text}")
return JsonResponse({"code": 500, "message": "查询支付状态失败", "data": {}}) except Order.DoesNotExist:
logger.error(f"订单 {payment_id} 不存在")
except Order.DoesNotExist: print(f"订单 {payment_id} 不存在")
print(f"未找到订单 {parent_payment}") return JsonResponse({"code": 400, "message": "订单不存在", "data": {}})
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) except Exception as e:
else: logger.error(f"支付执行过程中发生异常: {str(e)}")
print("Webhook payload 中缺少 parent_payment") print(f"支付执行过程中发生异常: {str(e)}")
return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}}) return JsonResponse({"code": 500, "message": f"支付执行过程中发生异常: {str(e)}", "data": {}})
elif event_type == 'PAYMENT.SALE.DENIED':
sale_id = payload['resource'].get('id') @csrf_exempt
parent_payment = payload['resource'].get('parent_payment') def paypal_webhook(request):
if parent_payment: """PayPal Webhook 处理函数,用于接收和处理 PayPal 异步通知"""
try: try:
order = Order.objects.get(order_id=parent_payment) logger.info("接收到 PayPal Webhook 请求...")
order.status = 'failed' print("接收到 PayPal Webhook 请求...")
order.updated_at = datetime.now()
order.save() headers = request.headers
print(f"订单 {parent_payment} 状态更新为 'failed'") payload = json.loads(request.body)
except Order.DoesNotExist:
print(f"未找到订单 {parent_payment}") # 验证 Webhook 签名
return JsonResponse({"code": 400, "message": "订单不存在", "data": {}}) if not verify_paypal_webhook(headers, request.body):
else: logger.error("Webhook 签名验证失败")
print("Webhook payload 中缺少 parent_payment") return JsonResponse({"code": 400, "message": "Webhook 签名验证失败", "data": {}})
return JsonResponse({"code": 400, "message": "Webhook payload 中缺少 parent_payment", "data": {}})
event_type = payload.get('event_type')
return JsonResponse({"code": 200, "message": "Webhook 处理成功", "data": {}})
# 打印接收到的 Webhook payload 以供调试
except json.JSONDecodeError: logger.info(f"接收到 PayPal Webhook 事件: {event_type}")
print("请求体不是有效的JSON") print(f"接收到 PayPal Webhook 事件: {event_type}")
return JsonResponse({"code": 400, "message": "请求体不是有效的JSON", "data": {}}) logger.info(f"Webhook payload: {json.dumps(payload, indent=2)}")
except Exception as e:
print(f"处理 PayPal Webhook 时发生错误: {e}") # 处理不同类型的事件
return JsonResponse({"code": 500, "message": f"处理 PayPal Webhook 时发生错误: {str(e)}", "data": {}}) 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) @receiver(user_logged_in)
def user_logged_in_handler(request, user, **kwargs): 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: try:
social_account = SocialAccount.objects.get(user=user, provider='google') social_account = SocialAccount.objects.get(user=user, provider='google')
print(social_account.extra_data) # 打印 extra_data 内容以进行调试 print(f'找到 social_account用户ID: {user.id}')
google_id = social_account.extra_data.get('sub')
google_id = social_account.extra_data['sub'] # 使用 'sub' 字段 email = social_account.extra_data.get('email')
email = social_account.extra_data['email'] username = social_account.extra_data.get('name')
username = social_account.extra_data['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: try:
custom_user = User.objects.get(google_id=google_id) custom_user = User.objects.get(google_id=google_id)
print(f'根据 google_id 找到用户: {custom_user.username}')
except User.DoesNotExist: except User.DoesNotExist:
custom_user = User.objects.create( print(f'没有找到 google_id 为 {google_id} 的用户,检查 email: {email}')
username=username, # 进一步检查是否有相同的 email 用户存在
email=email, if User.objects.filter(email=email).exists():
google_id=google_id, custom_user = User.objects.get(email=email)
password_hash='', # Google 登录不需要密码 print(f'找到 email 为 {email} 的用户: {custom_user.username}')
) # 如果用户存在但 google_id 不同,更新 google_id
UserSource.objects.create(user=custom_user, source='Google') custom_user.google_id = google_id
custom_user.save()
# 更新登录次数和最后登录IP 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.login_count += 1
custom_user.last_login_ip = request.META.get('REMOTE_ADDR') custom_user.last_login_ip = ip
custom_user.save() 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: 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积分 REWARD_POINTS = 50 # 例如奖励50积分
def check_and_reward_points(user, openid): def check_and_reward_points(user, openid):
""" """
根据 openid 查询原系统会员状态为原系统的用户增加一天会员时长不论是否为会员 根据 openid 查询原系统会员状态为原系统的用户增加一天会员时长不论是否为会员
@ -151,7 +152,7 @@ def send_verification_email_view(request):
if request.method == 'POST': if request.method == 'POST':
try: try:
body = json.loads(request.body) body = json.loads(request.body)
email = body.get('phone_number') email = body.get('email')
if not email: if not email:
return JsonResponse({'code': 400, 'message': '邮箱地址是必须的'}) return JsonResponse({'code': 400, 'message': '邮箱地址是必须的'})
@ -193,8 +194,8 @@ def send_verification_sms_view(request):
if not phone_number: if not phone_number:
return JsonResponse({'code': 400, 'message': '手机号码是必须的'}) return JsonResponse({'code': 400, 'message': '手机号码是必须的'})
sign_name = "福铭科技" # 替换为实际的签名名称 sign_name = "光映" # 替换为实际的签名名称
template_code = "SMS_299200388" # 替换为实际的短信模板代码 template_code = "SMS_473670066" # 替换为实际的短信模板代码
response = send_verification_sms(phone_number, sign_name, template_code) response = send_verification_sms(phone_number, sign_name, template_code)
return JsonResponse(response) return JsonResponse(response)
@ -379,7 +380,7 @@ def send_verification_code_view(request):
if '@' in identifier: if '@' in identifier:
response = send_verification_email(identifier) response = send_verification_email(identifier)
else: else:
response = send_verification_sms(identifier, "福铭科技", "SMS_299200388") response = send_verification_sms(identifier, "光映", "SMS_473670066")
return JsonResponse(response) return JsonResponse(response)
except json.JSONDecodeError: 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" 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! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = False
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
# settings.py # settings.py
@ -63,7 +63,7 @@ AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend', 'allauth.account.auth_backends.AuthenticationBackend',
) )
LOGIN_REDIRECT_URL = '/user/userinfo/' # 登录后重定向的URL LOGIN_REDIRECT_URL = '/create' # 登录后重定向的URL
LOGIN_URL = '/' LOGIN_URL = '/'
LOGOUT_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/'
@ -159,10 +159,10 @@ DATABASES = {
{ {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎 'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'admin_data', # 数据库名称 '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端口 'PORT': 3306, # mysql端口
'USER': 'admin_data', # 数据库用户名 'USER': 'admin_data', # 数据库用户名
'PASSWORD': 'mPmFz2NeXSYbzrt5', # 数据库密码 'PASSWORD': 'kyRz3c3hGMmDADrW', # 数据库密码
'OPTIONS': { 'OPTIONS': {
'charset': 'utf8mb4', 'charset': 'utf8mb4',
'use_unicode': True, '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 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_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_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_PUBLIC_KEY_PRODUCTION_MOBILE = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomw6g4rBmCr/QoX3NI3DVLyDpkaUytZ2uFhdfQaegIDAuUfZfgpTCASlAtO82t8ISAbSOSyp9CUpwdGV4EYOiCBbLxMYB6taaHPiIjJ1zNT1EakJzWgU53hz1AVeABB9kdAvMqSvjH6KLoVupmqm4Li8ZwDW9M2ANAmyDfKgiF0Lt4aUUnaZktoCrTWTkpmtfRZCHNACj851IllvN2wyC4OL7dJq5UzOFxmn07Dy/2z4UAhaaSAyRVawpOui5AIYJTXZERLYL3KMyRnMuZoFq3xltzVTzRPM06nRa9RfeVNVwWVtGBIe/r8tcg5wyhI57KUszGNOmUIm/se6G2lnAQIDAQAB' # 生产环境移动端支付宝公钥
ALIPAY_NOTIFY_URL = 'https://www.typeframes.com.cn/api/alipay/notify/' 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 # settings.py
# PayPal 配置 # PayPal 配置
PAYPAL_CLIENT_ID_SANDBOX = 'AfwWNEZE92XpbmoHywwdDEXH2x2Elak1Gxbireb5bv-u17OtKiJUuz7CMLnqayAZkqeU2kGtWXHXcXGS' PAYPAL_CLIENT_ID_SANDBOX = 'AfwWNEZE92XpbmoHywwdDEXH2x2Elak1Gxbireb5bv-u17OtKiJUuz7CMLnqayAZkqeU2kGtWXHXcXGS'
PAYPAL_SECRET_KEY_SANDBOX = 'ELmuFW7AspHp4km8amEhlM-NB3OGGx_iuqQbntTQ-2RTnJWbxdLTl6xlET1F1ezxSHbU1CUpK0_8ToI3' PAYPAL_SECRET_KEY_SANDBOX = 'ELmuFW7AspHp4km8amEhlM-NB3OGGx_iuqQbntTQ-2RTnJWbxdLTl6xlET1F1ezxSHbU1CUpK0_8ToI3'
PAYPAL_CLIENT_ID_PRODUCTION = 'YOUR_PRODUCTION_CLIENT_ID' # 替换为实际的生产环境客户端ID PAYPAL_CLIENT_ID_PRODUCTION = 'Aelz9E_u5mlLP_-8nOGuXS14P-osTWYqxPx8HknC1PNBqRtG_RzAtWblryvr0o8pIyqIomI-2KMFagk2' # 替换为实际的生产环境客户端ID
PAYPAL_SECRET_KEY_PRODUCTION = 'YOUR_PRODUCTION_SECRET_KEY' # 替换为实际的生产环境密钥 PAYPAL_SECRET_KEY_PRODUCTION = 'EIA_O_JeOWUjolXK3yi4VC88wpcZaIYgQvAPJ251LX_OTsatYzjt_Y2aFKLjXt5fInRkdTfeIbrwlZEh' # 替换为实际的生产环境密钥
PAYPAL_MODE = 'sandbox' #'sandbox' if DEBUG else 'live' # 根据DEBUG模式选择环境 PAYPAL_MODE = 'sandbox' if DEBUG else 'live' # 根据DEBUG模式选择环境
PAYPAL_RETURN_URL = 'https://www.typeframes.ai/user/plan_list/' # 支付成功后跳转的URL PAYPAL_RETURN_URL = 'https://www.typeframes.ai/create' # 支付成功后跳转的URL
PAYPAL_CANCEL_URL = 'https://www.typeframes.ai/user/plan_list/' # 支付取消后跳转的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] [uwsgi]
chdir = /app # 项目目录
chdir = /www/wwwroot/Ai_Admin
# 指定项目application
module = WebSite.wsgi:application module = WebSite.wsgi:application
# 进程个数
processes = 4 processes = 4
# 线程个数
threads = 2 threads = 2
# 指定启动时的pid文件路径用于停止服务和重启服务请勿删除
pidfile = /www/wwwroot/Ai_Admin/uwsgi.pid
# 指定ip及端口 # 指定ip及端口
http = 0.0.0.0:3001 http=0.0.0.0:3001
# 启动uwsgi的用户名和用户组
uid = root
gid = root
# 启用主进程 # 启用主进程
master = true master = true
@ -14,20 +28,18 @@ master = true
buffer-size = 32768 buffer-size = 32768
http-timeout = 60 http-timeout = 60
post-buffering = 8192 post-buffering = 8192
# 后台运行,并输出日志
daemonize = /www/wwwroot/Ai_Admin/uwsgi.log
# 处理 SIGTERM 信号以便优雅地关闭 # 处理 SIGTERM 信号以便优雅地关闭
die-on-term = true die-on-term = true
# 处理静态和媒体文件 # 添加以下配置以处理静态和媒体文件
static-map = /static=/app/static static-map = /static=/www/wwwroot/Ai_Admin/static
static-map = /media=/app/media static-map = /media=/www/wwwroot/Ai_Admin/media
static-map = /media/avatars=/app/media/avatars static-map = /media/avatars=/www/wwwroot/Ai_Admin/media/avatars
static-map = /media/audios=/app/media/audios static-map = /media/audios=/www/wwwroot/Ai_Admin/media/audios
static-map = /.well-known/acme-challenge/=/app/.well-known/acme-challenge static-map = /.well-known/acme-challenge/=/www/wwwroot/java_node_ssl/.well-known/acme-challenge
# 日志输出到标准输出
logger = stdout
# 启用thunder lock
thunder-lock = true

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