developing
This commit is contained in:
parent
31a0b7ff4d
commit
9421f798db
|
@ -1 +1 @@
|
||||||
from tronscan import Tronscan
|
from .tronscan import Tronscan
|
||||||
|
|
|
@ -1,58 +1,96 @@
|
||||||
from flask import Flask, request, jsonify
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from flask import Flask, request, jsonify, make_response
|
||||||
|
from flask_cors import CORS
|
||||||
|
|
||||||
from config import get_config
|
from config import get_config
|
||||||
from services.order import OrderService
|
from services.order import OrderService
|
||||||
from services.user import UserService
|
from services.user import UserService
|
||||||
|
from utils.datetime import parse_time_string, to_milliseconds
|
||||||
|
|
||||||
|
app = Flask('app')
|
||||||
|
CORS(app, resources={r"/*": {"origins": "http://localhost:8080"}})
|
||||||
config = get_config()
|
config = get_config()
|
||||||
|
config.order.lifetime = to_milliseconds(**parse_time_string(config.order.lifetime))
|
||||||
|
order_service = OrderService(config)
|
||||||
|
user_service = UserService(config)
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
order_service = OrderService()
|
@app.after_request
|
||||||
user_service = UserService()
|
def add_cors_headers(response):
|
||||||
|
response.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8080' # 具体的来源
|
||||||
|
# response.headers['Access-Control-Allow-Credentials'] = 'true' # 如果需要凭据支持
|
||||||
|
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||||
|
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/createOrder', methods=['POST'])
|
@app.route('/createOrder', methods=['POST'])
|
||||||
def create_order():
|
def create_order():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
name = data.get('name', None)
|
payment_method = data.get('paymentMethod', None)
|
||||||
phone = data.get('phone', None)
|
|
||||||
email = data.get('email', None)
|
|
||||||
payment_method = data.get('payment_method', None)
|
|
||||||
if payment_method not in config['PaymentAddresses']:
|
if payment_method not in config['PaymentAddresses']:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"message": "Unsupported payment method. Currently, only USDT payments are supported."
|
"message": "wrong payment method"
|
||||||
|
}), 400
|
||||||
|
quant = data.get('amount', 0)
|
||||||
|
if quant < config.order.min_quant:
|
||||||
|
return jsonify({
|
||||||
|
"message": "Amount below minimum limit."
|
||||||
}), 400
|
}), 400
|
||||||
address = data.get('address', None)
|
|
||||||
|
|
||||||
if address is None:
|
wallet_address = data.get('wallet_address', None)
|
||||||
addresses = user_service.get_addresses(name, phone, email)
|
if wallet_address is None:
|
||||||
|
name = data.get('name', None)
|
||||||
|
phone = data.get('phone', None)
|
||||||
|
email = data.get('email', None)
|
||||||
|
wallet_addresses = user_service.get_wallet_addresses(name, phone, email)
|
||||||
|
|
||||||
if not addresses:
|
if not wallet_addresses:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"message": "No payment address associated with you was found. Please provide a payment address."
|
"message": "empty wallet address"
|
||||||
}), 400
|
}), 400
|
||||||
if len(addresses) > 1:
|
if len(wallet_addresses) > 1:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"message": "Please select an address to place your order.",
|
"message": "Please select an address to place your order.",
|
||||||
"addresses": addresses
|
"wallet_addresses": wallet_addresses
|
||||||
}), 200
|
}), 200
|
||||||
# 单个地址
|
# 单个地址
|
||||||
address = addresses[0]
|
wallet_address = wallet_addresses[0]
|
||||||
|
|
||||||
order_id = order_service.create_order(address)
|
order_id, create_timestamp = order_service.create_order(quant, wallet_address)
|
||||||
return jsonify({"order_id": order_id}), 200
|
|
||||||
|
return jsonify({"orderID": order_id,
|
||||||
|
"amount": quant,
|
||||||
|
"orderCreateTimestamp": create_timestamp,
|
||||||
|
"orderExpirationTime": config.order.lifetime}), 200
|
||||||
|
|
||||||
|
|
||||||
order_status = config.order.order_status
|
order_status = config.order.order_status
|
||||||
|
|
||||||
|
|
||||||
@app.route('/finishOrder', methods=['GET'])
|
@app.route('/finishOrder', methods=['GET'])
|
||||||
def finish_order():
|
def finish_order():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
order_id = data.get('order_id', None)
|
order_id = data.get('orderID', None)
|
||||||
status = order_service.finish_order(order_id)
|
status = order_service.finish_order(order_id)
|
||||||
return jsonify({"order_id": order_id,
|
return jsonify({"order_id": order_id,
|
||||||
"status": status,
|
"status": status,
|
||||||
"msg": order_status[status]}), 200
|
"msg": order_status[status]}), 200
|
||||||
|
|
||||||
|
|
||||||
|
def run_gunicorn():
|
||||||
|
# Gunicorn 的命令和参数
|
||||||
|
command = [sys.executable, '-m', 'gunicorn', 'app:app']
|
||||||
|
|
||||||
|
# 启动 Gunicorn
|
||||||
|
subprocess.run(command)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True)
|
if os.name == 'nt':
|
||||||
|
app.run(debug=True, port=5000, host='0.0.0.0')
|
||||||
|
elif os.name == 'posix':
|
||||||
|
run_gunicorn()
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
[APIKey]
|
[APIKey]
|
||||||
tronscan=cc87d361-7cd6-4f69-a57b-f0a77a213355
|
tronscan = cc87d361-7cd6-4f69-a57b-f0a77a213355
|
||||||
|
|
||||||
[PaymentAddresses]
|
[PaymentAddresses]
|
||||||
usdt=TB592A5QwHvvcJoCmvALmzT3S9Pux91Gub
|
usdt = TB592A5QwHvvcJoCmvALmzT3S9Pux91Gub
|
||||||
|
;usdt=TNrzRLi2ArZhiMx51zusBEHHh1qyB9Ldq2
|
||||||
|
|
||||||
[MYSQL]
|
;[MYSQL]
|
||||||
user: 'your_mysql_username'
|
;user = wystan
|
||||||
password: 'your_mysql_password'
|
;password = Wystan14Brown!
|
||||||
host: 'localhost'
|
;host = 100.64.0.1
|
||||||
database: 'your_database_name'
|
;port = 3306
|
||||||
autocommit: false
|
;database = payment
|
||||||
allow_multi_statements: True
|
;autocommit = 0
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
order:
|
order:
|
||||||
|
min_quant: 30
|
||||||
lifetime: 10min
|
lifetime: 10min
|
||||||
order_status:
|
order_status:
|
||||||
- Timed Out
|
- Timed Out
|
||||||
|
@ -6,3 +7,10 @@ order:
|
||||||
- Unpaid
|
- Unpaid
|
||||||
- Wrong Amount
|
- Wrong Amount
|
||||||
- Pending
|
- Pending
|
||||||
|
mysql:
|
||||||
|
user: wystan
|
||||||
|
password: Wystan14Brown!
|
||||||
|
host: 100.64.0.1
|
||||||
|
port: 3306
|
||||||
|
database: payment
|
||||||
|
autocommit: false
|
|
@ -15,6 +15,7 @@ from argparse import Namespace
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
import yaml
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
random_seed = 20240717
|
random_seed = 20240717
|
||||||
|
@ -36,7 +37,7 @@ setup_seed(random_seed)
|
||||||
|
|
||||||
class Setting:
|
class Setting:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
settings:dict = None,
|
settings: dict = None,
|
||||||
config_parser: ConfigParser = None,
|
config_parser: ConfigParser = None,
|
||||||
argument_parser: Namespace = None,
|
argument_parser: Namespace = None,
|
||||||
_visited=None,
|
_visited=None,
|
||||||
|
@ -44,7 +45,8 @@ class Setting:
|
||||||
**kwargs):
|
**kwargs):
|
||||||
self._parent = _parent
|
self._parent = _parent
|
||||||
|
|
||||||
self.update(_visited=_visited, **settings)
|
if settings is not None:
|
||||||
|
self.update(_visited=_visited, **settings)
|
||||||
|
|
||||||
if config_parser:
|
if config_parser:
|
||||||
self.update_config_parser(config_parser)
|
self.update_config_parser(config_parser)
|
||||||
|
@ -61,6 +63,8 @@ class Setting:
|
||||||
cls_attr = getattr(self.__class__, k, None)
|
cls_attr = getattr(self.__class__, k, None)
|
||||||
if callable(cls_attr):
|
if callable(cls_attr):
|
||||||
raise KeyError(f"The key '{k}' conflicts with an existing class method. you can use {k}_ instead.")
|
raise KeyError(f"The key '{k}' conflicts with an existing class method. you can use {k}_ instead.")
|
||||||
|
if k.startswith('_'):
|
||||||
|
raise KeyError(f"The key '{k}' is a private attribute.")
|
||||||
if isinstance(v, dict):
|
if isinstance(v, dict):
|
||||||
obj_id = id(v)
|
obj_id = id(v)
|
||||||
if obj_id in _visited:
|
if obj_id in _visited:
|
||||||
|
@ -69,22 +73,22 @@ class Setting:
|
||||||
else:
|
else:
|
||||||
v = Setting(_visited=_visited, _parent=self, **v)
|
v = Setting(_visited=_visited, _parent=self, **v)
|
||||||
_visited[obj_id] = v
|
_visited[obj_id] = v
|
||||||
self.__dict__[k] = v
|
self[k] = v
|
||||||
|
|
||||||
def update_config_parser(self, config_parser: ConfigParser):
|
def update_config_parser(self, config_parser: ConfigParser):
|
||||||
for section in config_parser.sections():
|
for section in config_parser.sections():
|
||||||
section_config = Setting(_parent=self)
|
section_config = Setting(_parent=self)
|
||||||
for option in config_parser.options(section):
|
for option in config_parser.options(section):
|
||||||
section_config[option] = config_parser.get(section, option)
|
section_config[option] = config_parser.get(section, option)
|
||||||
self.__dict__[section] = section_config
|
self[section] = section_config
|
||||||
|
|
||||||
def update_argument_parser(self, argument_parser: Namespace):
|
def update_argument_parser(self, argument_parser: Namespace):
|
||||||
for k in vars(argument_parser):
|
for k in vars(argument_parser):
|
||||||
self.__dict__[k] = getattr(argument_parser, k)
|
self[k] = getattr(argument_parser, k)
|
||||||
|
|
||||||
def get(self, item, default=None):
|
def get(self, item, default=None):
|
||||||
if item not in self.__dict__ and self._parent is not None:
|
if item not in self.__dict__ and self._parent is not None:
|
||||||
return self._parent.get(item, default)
|
return self._parent.get(item.lower(), default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def get_int(self, item):
|
def get_int(self, item):
|
||||||
|
@ -100,21 +104,28 @@ class Setting:
|
||||||
self.__dict__[item] = value
|
self.__dict__[item] = value
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return self.__dict__[item]
|
return self.__dict__[item.lower()]
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
self.__dict__[key] = value
|
self.__dict__[key.lower()] = value
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
return self.get(key)
|
return self[key]
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return set(k for k in self.__dict__.keys() if not k.startswith('_'))
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for k, v in self.__dict__.items():
|
||||||
|
yield k, v
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
def _str_helper(config, indent=0, visited=None):
|
def _str_helper(settings, visited=None, indent_count=0):
|
||||||
if visited is None:
|
if visited is None:
|
||||||
visited = set()
|
visited = set()
|
||||||
lines = []
|
lines = []
|
||||||
indent_str = ' ' * indent
|
indent_str = ' ' * indent_count
|
||||||
for key, value in config.__dict__.items():
|
for key, value in settings.__dict__.items():
|
||||||
if key.startswith('_'):
|
if key.startswith('_'):
|
||||||
continue
|
continue
|
||||||
if isinstance(value, Setting):
|
if isinstance(value, Setting):
|
||||||
|
@ -123,11 +134,29 @@ class Setting:
|
||||||
else:
|
else:
|
||||||
visited.add(id(value))
|
visited.add(id(value))
|
||||||
lines.append(f"{indent_str}{key}:")
|
lines.append(f"{indent_str}{key}:")
|
||||||
lines.append(_str_helper(value, indent + 1, visited))
|
lines.append(_str_helper(value, visited=visited, indent_count=indent_count + 1))
|
||||||
else:
|
else:
|
||||||
lines.append(f"{indent_str}{key}: {value}")
|
lines.append(f"{indent_str}{key}: {value}")
|
||||||
return '\n'.join(lines)
|
return '\n'.join(lines)
|
||||||
|
return _str_helper(self)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
def _str_helper(settings, visited=None, indent_count=0):
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
lines = []
|
||||||
|
for key, value in settings.__dict__.items():
|
||||||
|
if key.startswith('_'):
|
||||||
|
continue
|
||||||
|
if isinstance(value, Setting):
|
||||||
|
if id(value) in visited:
|
||||||
|
lines.append(f"{key}=<Circular Reference>")
|
||||||
|
else:
|
||||||
|
visited.add(id(value))
|
||||||
|
lines.append(f"{key}={_str_helper(value, visited=visited, indent_count=indent_count + 1)}")
|
||||||
|
else:
|
||||||
|
lines.append(f"{key}={value}")
|
||||||
|
return f'Setting({", ".join(lines)})'
|
||||||
return _str_helper(self)
|
return _str_helper(self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,7 +183,7 @@ def get_config(yaml_file=fr'{ROOT_DIR}/config/param.yaml', ini_file=fr'{ROOT_DIR
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
config.update_argument_parser(args)
|
config.update_argument_parser(args)
|
||||||
if os.path.exists(yaml_file):
|
if os.path.exists(yaml_file):
|
||||||
with open('config.yaml', 'r') as file:
|
with open(yaml_file, 'r') as file:
|
||||||
data = yaml.safe_load(file)
|
data = yaml.safe_load(file)
|
||||||
config.update(data)
|
config.update(**data)
|
||||||
return config
|
return config
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from mysql.connector import connect, Error, OperationalError
|
from mysql.connector import connect as connect_mysql, Error, OperationalError
|
||||||
from mysql.connector import errors as db_errors
|
from mysql.connector import errors as db_errors
|
||||||
|
|
||||||
from custom_decorators import singleton
|
from custom_decorators import singleton
|
||||||
|
@ -15,9 +15,12 @@ class Database:
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"""Establish a new database connection."""
|
"""Establish a new database connection."""
|
||||||
try:
|
try:
|
||||||
self.connection = connect(**self.config)
|
self.connection = connect_mysql(**self.config)
|
||||||
if self.connection.is_connected():
|
if self.connection.is_connected():
|
||||||
logger.info("Connected to MySQL database")
|
logger.info("Connected to MySQL database")
|
||||||
|
except AttributeError as e:
|
||||||
|
e.args = (f'{e.args[0]} in MYSQL configuration',)
|
||||||
|
raise
|
||||||
except Error as e:
|
except Error as e:
|
||||||
logger.info(f"Error while connecting to MySQL: {e}")
|
logger.info(f"Error while connecting to MySQL: {e}")
|
||||||
self.connection = None
|
self.connection = None
|
||||||
|
|
|
@ -2,31 +2,31 @@ from utils.database import pack_params
|
||||||
|
|
||||||
|
|
||||||
class User:
|
class User:
|
||||||
def __init__(self, id_=None, name=None, phone=None, email=None, address=None, payment_method=None):
|
def __init__(self, id_=None, name=None, phone=None, email=None, wallet_address=None, payment_method=None):
|
||||||
self.id = id_
|
self.id = id_
|
||||||
self.name = name
|
self.name = name
|
||||||
self.phone = phone
|
self.phone = phone
|
||||||
self.email = email
|
self.email = email
|
||||||
self.address = address
|
self.wallet_address = wallet_address
|
||||||
self.payment_method = payment_method
|
self.payment_method = payment_method
|
||||||
|
|
||||||
def insert_sql(self, params_format="list"):
|
def insert_sql(self, params_format="list"):
|
||||||
params_sql, params = pack_params(params_format=params_format, param_sql="{param}", join_str=",",
|
params_sql, params = pack_params(params_format=params_format, param_sql="{param}", join_str=",",
|
||||||
name=self.name, phone=self.phone, email=self.email, address=self.address,
|
name=self.name, phone=self.phone, email=self.email, wallet_address=self.wallet_address,
|
||||||
payment_method=self.payment_method)
|
payment_method=self.payment_method)
|
||||||
return f"INSERT INTO user ({params_sql}) VALUES ({','.join('%s' for _ in params)})", params
|
return f"INSERT INTO users ({params_sql}) VALUES ({','.join('%s' for _ in params)})", params
|
||||||
|
|
||||||
def select_sql(self, condition="AND", params_format="list"):
|
def select_sql(self, condition="AND", params_format="list"):
|
||||||
params_sql, params = pack_params(params_format=params_format, param_sql="{param}=%s", join_str=f" {condition} ",
|
params_sql, params = pack_params(params_format=params_format, param_sql="{param}=%s", join_str=f" {condition} ",
|
||||||
name=self.name, phone=self.phone, email=self.email, address=self.address,
|
name=self.name, phone=self.phone, email=self.email, wallet_address=self.wallet_address,
|
||||||
payment_method=self.payment_method)
|
payment_method=self.payment_method)
|
||||||
return f"SELECT id, name, phone, email, address, payment_method FROM user WHERE {params_sql}", params
|
return f"SELECT id, name, phone, email, wallet_address, payment_method FROM users WHERE {params_sql}", params
|
||||||
|
|
||||||
def exists_sql(self, condition="AND", params_format="list"):
|
def exists_sql(self, condition="AND", params_format="list"):
|
||||||
params_sql, params = pack_params(params_format=params_format, param_sql="{param}=%s", join_str=f" {condition} ",
|
params_sql, params = pack_params(params_format=params_format, param_sql="{param}=%s", join_str=f" {condition} ",
|
||||||
name=self.name, phone=self.phone, email=self.email, address=self.address,
|
name=self.name, phone=self.phone, email=self.email, wallet_address=self.wallet_address,
|
||||||
payment_method=self.payment_method)
|
payment_method=self.payment_method)
|
||||||
return f"SELECT id FROM user WHERE {params_sql} LIMIT 1", params
|
return f"SELECT id FROM users WHERE {params_sql} LIMIT 1", params
|
||||||
|
|
||||||
def params(self, format="dict"):
|
def params(self, format="dict"):
|
||||||
if format == "list":
|
if format == "list":
|
||||||
|
@ -50,11 +50,11 @@ class User:
|
||||||
params.append(self.email)
|
params.append(self.email)
|
||||||
elif format == "dict":
|
elif format == "dict":
|
||||||
params["email"] = self.email
|
params["email"] = self.email
|
||||||
if self.address:
|
if self.wallet_address:
|
||||||
if format == "list":
|
if format == "list":
|
||||||
params.append(self.address)
|
params.append(self.wallet_address)
|
||||||
elif format == "dict":
|
elif format == "dict":
|
||||||
params["address"] = self.address
|
params["wallet_address"] = self.wallet_address
|
||||||
if self.payment_method:
|
if self.payment_method:
|
||||||
if format == "list":
|
if format == "list":
|
||||||
params.append(self.payment_method)
|
params.append(self.payment_method)
|
||||||
|
@ -70,17 +70,17 @@ class User:
|
||||||
different_attrs["phone"] = (self.phone, other_user.phone)
|
different_attrs["phone"] = (self.phone, other_user.phone)
|
||||||
if self.email != other_user.email:
|
if self.email != other_user.email:
|
||||||
different_attrs["email"] = (self.email, other_user.email)
|
different_attrs["email"] = (self.email, other_user.email)
|
||||||
if self.address != other_user.address:
|
if self.wallet_address != other_user.wallet_address:
|
||||||
different_attrs["address"] = (self.address, other_user.address)
|
different_attrs["wallet_address"] = (self.wallet_address, other_user.wallet_address)
|
||||||
if self.payment_method != other_user.payment_method:
|
if self.payment_method != other_user.payment_method:
|
||||||
different_attrs["payment_method"] = (self.payment_method, other_user.payment_method)
|
different_attrs["payment_method"] = (self.payment_method, other_user.payment_method)
|
||||||
return different_attrs
|
return different_attrs
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, User):
|
if isinstance(other, User):
|
||||||
return ((self.name, self.phone, self.email, self.address, self.payment_method)
|
return ((self.name, self.phone, self.email, self.wallet_address, self.payment_method)
|
||||||
== (other.name, other.phone, other.email, other.address, other.payment_method))
|
== (other.name, other.phone, other.email, other.wallet_address, other.payment_method))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.name, self.phone, self.email, self.address, self.payment_method))
|
return hash((self.name, self.phone, self.email, self.wallet_address, self.payment_method))
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from ruamel_yaml.util import create_timestamp
|
|
||||||
|
|
||||||
from custom_decorators import singleton
|
from custom_decorators import singleton
|
||||||
from database import Database
|
from database import Database
|
||||||
from utils.datetime import current_timestamp
|
from utils.datetime import current_timestamp
|
||||||
|
@ -10,15 +8,16 @@ class OrderRepository:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.db = Database(config['MYSQL'])
|
self.db = Database(config['MYSQL'])
|
||||||
|
|
||||||
def create(self, order_id, from_address, to_address):
|
def create(self, order_id, quant, from_address, to_address):
|
||||||
cur_time = current_timestamp()
|
timestamp = current_timestamp()
|
||||||
try:
|
try:
|
||||||
self.db.execute_query(
|
self.db.execute_query(
|
||||||
"INSERT INTO orders (order_id, from_address, to_address, create_timestamp, update_timestamp) "
|
"INSERT INTO orders (order_id, quant, from_address, to_address, create_timestamp, update_timestamp) "
|
||||||
"VALUES (%s, %s, %s, %s, %s)",
|
"VALUES (%s, %s, %s, %s, %s, %s)",
|
||||||
[order_id, from_address, to_address, cur_time, cur_time]
|
[order_id, quant, from_address, to_address, timestamp, timestamp]
|
||||||
)
|
)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
return timestamp
|
||||||
except Exception:
|
except Exception:
|
||||||
self.db.rollback()
|
self.db.rollback()
|
||||||
raise
|
raise
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
loguru==0.7.2
|
loguru==0.7.2
|
||||||
mysql-connector-python==9.1.0
|
mysql-connector-python==9.1.0
|
||||||
Requests==2.32.3
|
Requests==2.32.3
|
||||||
|
flask==3.1.0
|
||||||
|
gunicorn==23.0.0
|
||||||
|
pyyaml==6.0.2
|
||||||
|
flask-cors==5.0.0
|
|
@ -3,37 +3,38 @@ import uuid
|
||||||
from custom_decorators import singleton
|
from custom_decorators import singleton
|
||||||
from repositories.order import OrderRepository
|
from repositories.order import OrderRepository
|
||||||
from services.payment import PaymentService
|
from services.payment import PaymentService
|
||||||
from utils.datetime import current, current_timestamp, is_time_difference_greater_than, parse_time_string
|
from utils.datetime import current, current_timestamp, is_time_difference_greater_than
|
||||||
|
|
||||||
|
|
||||||
@singleton
|
@singleton
|
||||||
class OrderService:
|
class OrderService:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.payment_service = PaymentService()
|
self.payment_service = PaymentService(config.APIKey.tronscan)
|
||||||
self.order_repo = OrderRepository(config)
|
self.order_repo = OrderRepository(config)
|
||||||
|
|
||||||
def create_order(self, address=None):
|
def create_order(self, quant, address):
|
||||||
date_str = current().strftime('%Y%m%d%H%M%S')
|
date_str = current().strftime('%Y%m%d%H%M%S')
|
||||||
unique_id = str(uuid.uuid4()).split('-')[0]
|
unique_id = str(uuid.uuid4()).split('-')[0]
|
||||||
order_id = f"{date_str}-{unique_id}"
|
order_id = f"{date_str}-{unique_id}"
|
||||||
|
|
||||||
self.order_repo.create(order_id, address,
|
create_timestamp = self.order_repo.create(order_id, quant,
|
||||||
self.config['PaymentAddresses'])
|
address, self.config['PaymentAddresses'])
|
||||||
return order_id
|
return order_id, create_timestamp
|
||||||
|
|
||||||
def finish_order(self, order_id):
|
def finish_order(self, order_id):
|
||||||
# 判断支付时间是否超过订单存活时间
|
# 判断支付时间是否超过订单存活时间
|
||||||
status = 2
|
status = 2
|
||||||
now = current_timestamp()
|
now = current_timestamp()
|
||||||
quant, from_address, to_address, create_timestamp = self.order_repo.get_order_info(order_id)
|
quant, from_address, to_address, create_timestamp = self.order_repo.get_order_info(order_id)
|
||||||
if is_time_difference_greater_than(create_timestamp, now, **parse_time_string(self.config.order.lifetime)):
|
if is_time_difference_greater_than(create_timestamp, now, milliseconds=self.config.order.lifetime):
|
||||||
# 订单超时
|
# 订单超时
|
||||||
status = 0
|
status = 0
|
||||||
else:
|
else:
|
||||||
correct_quant, confirmed = self.payment_service.check_payment(quant,
|
correct_quant, confirmed = self.payment_service.check_payment(quant,
|
||||||
from_address, to_address,
|
from_address, to_address,
|
||||||
create_timestamp, now)
|
# 减去十秒, 避免网络延迟导致的订单创建时间太晚
|
||||||
|
create_timestamp - 10, now)
|
||||||
if correct_quant and confirmed:
|
if correct_quant and confirmed:
|
||||||
# 支付成功
|
# 支付成功
|
||||||
status = 1
|
status = 1
|
||||||
|
|
|
@ -9,7 +9,7 @@ class UserService:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.user_repo = UserRepository(config)
|
self.user_repo = UserRepository(config)
|
||||||
|
|
||||||
def get_addresses(self, name=None, phone=None, email=None):
|
def get_wallet_addresses(self, name=None, phone=None, email=None):
|
||||||
if phone or email:
|
if phone or email:
|
||||||
users = self.user_repo.get_or_create(User(name=name, phone=phone, email=email))
|
users = self.user_repo.get_or_create(User(name=name, phone=phone, email=email))
|
||||||
addresses = set(user.address for user in users if user.address)
|
addresses = set(user.address for user in users if user.address)
|
||||||
|
|
|
@ -7,10 +7,10 @@ def current():
|
||||||
|
|
||||||
|
|
||||||
def current_timestamp():
|
def current_timestamp():
|
||||||
datetime.datetime.now().timestamp()
|
return int(datetime.datetime.now().timestamp() * 1000)
|
||||||
|
|
||||||
|
|
||||||
def is_time_difference_greater_than(timestamp1, timestamp2, hours=0, minutes=0, seconds=0):
|
def is_time_difference_greater_than(timestamp1, timestamp2, hours=0, minutes=0, seconds=0, milliseconds=0):
|
||||||
"""
|
"""
|
||||||
判断两个时间戳的时间差是否大于指定的小时、分钟和秒数
|
判断两个时间戳的时间差是否大于指定的小时、分钟和秒数
|
||||||
|
|
||||||
|
@ -25,14 +25,14 @@ def is_time_difference_greater_than(timestamp1, timestamp2, hours=0, minutes=0,
|
||||||
bool: 如果时间差大于指定的小时、分钟和秒数返回True,否则返回False
|
bool: 如果时间差大于指定的小时、分钟和秒数返回True,否则返回False
|
||||||
"""
|
"""
|
||||||
# 将时间戳转换为 datetime 对象
|
# 将时间戳转换为 datetime 对象
|
||||||
time1 = datetime.fromtimestamp(timestamp1)
|
time1 = datetime.fromtimestamp(timestamp1) / 1000.0
|
||||||
time2 = datetime.fromtimestamp(timestamp2)
|
time2 = datetime.fromtimestamp(timestamp2) / 1000.0
|
||||||
|
|
||||||
# 计算时间差
|
# 计算时间差
|
||||||
time_difference = abs(time2 - time1)
|
time_difference = abs(time2 - time1)
|
||||||
|
|
||||||
# 计算指定的时间差值
|
# 计算指定的时间差值
|
||||||
threshold = datetime.timedelta(hours=hours, minutes=minutes, seconds=seconds)
|
threshold = datetime.timedelta(hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds)
|
||||||
|
|
||||||
# 判断时间差是否大于指定的时间
|
# 判断时间差是否大于指定的时间
|
||||||
return time_difference > threshold
|
return time_difference > threshold
|
||||||
|
@ -44,7 +44,8 @@ def parse_time_string(time_str):
|
||||||
'd': 'days',
|
'd': 'days',
|
||||||
'h': 'hours',
|
'h': 'hours',
|
||||||
'min': 'minutes',
|
'min': 'minutes',
|
||||||
's': 'seconds'
|
's': 'seconds',
|
||||||
|
'ms': 'milliseconds'
|
||||||
}
|
}
|
||||||
|
|
||||||
# 使用正则表达式匹配数字和单位的模式
|
# 使用正则表达式匹配数字和单位的模式
|
||||||
|
@ -61,3 +62,6 @@ def parse_time_string(time_str):
|
||||||
raise ValueError(f"Unsupported unit: {unit}")
|
raise ValueError(f"Unsupported unit: {unit}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def to_milliseconds(days=0, hours=0, minutes=0, seconds=0, milliseconds=0):
|
||||||
|
return days * 24 * 60 * 60 * 1000 + hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + milliseconds
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
def convert_to_tronscan_timestamp(timestamp):
|
def convert_to_tronscan_timestamp(timestamp):
|
||||||
return int(timestamp * 1000)
|
# return int(timestamp * 1000)
|
||||||
|
return timestamp
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
.idea
|
||||||
|
|
||||||
# Python:
|
# Python:
|
||||||
*.ipynb
|
*.ipynb
|
||||||
*/__pycache__
|
*/__pycache__
|
||||||
|
@ -12,7 +14,6 @@ docs/build
|
||||||
venv
|
venv
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
.idea
|
|
||||||
htmlcov/
|
htmlcov/
|
||||||
.coverage
|
.coverage
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
.el-card[data-v-0f66bda2] .el-card__header{padding:calc(var(--el-card-padding) - 2px) var(--el-card-padding);border-bottom:1px solid var(--el-card-border-color)}.description[data-v-0f66bda2]{margin-bottom:15px;padding:10px 20px 12px;color:#0d236d;-webkit-text-size-adjust:none;background:rgba(52,100,255,.1);border:1px solid rgba(52,100,255,.3);font:12px Microsoft YaHei,Arial}.description p.title[data-v-0f66bda2]{line-height:30px}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,238 @@
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f4f7fa;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
max-width: 500px;
|
||||||
|
padding: 20px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
background-color: #eef3ff;
|
||||||
|
border-left: 4px solid #5b8def;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special-notice {
|
||||||
|
background-color: #d6e4ff;
|
||||||
|
padding: 1vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input[type="text"],
|
||||||
|
.form-group input[type="email"],
|
||||||
|
.form-group input[type="number"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
border-color: #5b8def;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 5px rgba(91, 141, 239, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input[type="radio"] {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.3s, background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-option.selected {
|
||||||
|
border: 2px solid #5b8def;
|
||||||
|
background-color: rgba(91, 141, 239, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-option input[type="radio"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-option span {
|
||||||
|
margin-left: 5px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-option img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #5b8def;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-option.selected .dot {
|
||||||
|
background-color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #5b8def;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #487acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
--color: inherit;
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
line-height: 1em;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
fill: currentColor;
|
||||||
|
color: var(--color);
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
display: none; /* 初始隐藏 */
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 46vh;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
color: #aaa;
|
||||||
|
float: right;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover,
|
||||||
|
.close-btn:focus {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header h2 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* 标题居中 */
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
max-height: 45vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex; /* 添加 Flexbox */
|
||||||
|
flex-direction: column; /* 垂直排列子元素 */
|
||||||
|
align-items: center; /* 水平居中 */
|
||||||
|
justify-content: center; /* 垂直居中 */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
margin-top: 15px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-address {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
word-break: break-all;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-option {
|
||||||
|
padding: 10px;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-option:hover {
|
||||||
|
background: #f1f1f1;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -1,131 +1,153 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>创建订单</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<style>
|
<title>Recharge Page</title>
|
||||||
/* 简单的样式 */
|
<link href="/css/modules.fdcec670.css" rel="stylesheet"/>
|
||||||
.hidden { display: none; }
|
<link rel="stylesheet" href="/css/styles.css"/>
|
||||||
.error { color: red; }
|
<link rel="preload" href="/index.js" as="script">
|
||||||
</style>
|
<script src="/index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>创建订单</h1>
|
<div class="container">
|
||||||
<form id="order-form">
|
<h1>Recharge</h1>
|
||||||
<label for="phone">手机号:</label>
|
<div class="tips">
|
||||||
<input type="text" id="phone" name="phone"><br><br>
|
<p>Kind Reminder:</p>
|
||||||
|
<ol>
|
||||||
<label for="email">邮箱:</label>
|
<li>Currently, only USDT deposits are supported, and balances can be withdrawn. Other deposit methods are under development. Please stay tuned for announcements regarding their release.</li>
|
||||||
<input type="email" id="email" name="email"><br><br>
|
<li>The deposit amount will be credited within 1 minute after payment. If it does not arrive within 5 minutes, please contact customer support and provide your U address, deposit screenshot, transaction ID, and account name.</li>
|
||||||
|
<li>The actual amount credited for USDT deposits must match the amount displayed on the USDT deposit payment page; otherwise, the deposit will not be credited. Please be informed.</li>
|
||||||
<label for="address">地址:</label>
|
</ol>
|
||||||
<input type="text" id="address" name="address"><br><br>
|
<div class="special-notice">
|
||||||
|
<strong>Special Notice:</strong> Please ensure the recipient address is correct when making a transfer!
|
||||||
<button type="submit">提交订单</button>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
<form>
|
||||||
<div id="message" class="hidden"></div>
|
<div class="form-group">
|
||||||
<div id="addresses" class="hidden">
|
<label>Payment method:</label>
|
||||||
<h2>请选择一个地址:</h2>
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
<ul id="address-list"></ul>
|
<div class="el-form-item__content">
|
||||||
|
<i class="el-icon"
|
||||||
|
data-v-0f66bda2=""
|
||||||
|
style="font-size: 30px">
|
||||||
|
<svg
|
||||||
|
t="1693061019040"
|
||||||
|
class="icon"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="4072"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
data-v-0f66bda2="">
|
||||||
|
<path
|
||||||
|
d="M1023.082985 511.821692c0 281.370746-228.08199 509.452736-509.452736 509.452736-281.360557 0-509.452736-228.08199-509.452737-509.452736 0-281.365652 228.092179-509.452736 509.452737-509.452737 281.370746 0 509.452736 228.087085 509.452736 509.452737"
|
||||||
|
fill="#1BA27A"
|
||||||
|
p-id="4073"></path>
|
||||||
|
<path
|
||||||
|
d="M752.731701 259.265592h-482.400796v116.460896h182.969951v171.176119h116.460895v-171.176119h182.96995z"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
p-id="4074"></path>
|
||||||
|
<path
|
||||||
|
d="M512.636816 565.13592c-151.358408 0-274.070289-23.954468-274.070289-53.50782 0-29.548259 122.706786-53.507821 274.070289-53.507821 151.358408 0 274.065194 23.959562 274.065194 53.507821 0 29.553353-122.706786 53.507821-274.065194 53.50782m307.734925-44.587303c0-38.107065-137.776398-68.995184-307.734925-68.995184-169.953433 0-307.74002 30.888119-307.74002 68.995184 0 33.557652 106.837333 61.516418 248.409154 67.711363v245.729433h116.450707v-245.632637c142.66205-6.001353 250.615085-34.077294 250.615084-67.808159"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
p-id="4075"></path>
|
||||||
|
</svg>
|
||||||
|
</i>
|
||||||
|
<div data-v-0f66bda2="" style="margin-left: 20px">
|
||||||
|
<div class="el-radio-group" role="radiogroup" aria-labelledby="el-id-7163-5" data-v-0f66bda2=""
|
||||||
|
id="el-id-7163-12">
|
||||||
|
<label class="el-radio is-bordered is-checked el-radio--default" data-v-0f66bda2="">
|
||||||
|
<span class="el-radio__input is-checked">
|
||||||
|
<input class="el-radio__original" name="el-id-7163-6" type="radio" value="30"
|
||||||
|
onclick="setAmountValue(this.value)"/>
|
||||||
|
<span class="el-radio__inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="el-radio__label">30U</span>
|
||||||
|
</label>
|
||||||
|
<label class="el-radio is-bordered el-radio--default" data-v-0f66bda2="">
|
||||||
|
<span class="el-radio__input">
|
||||||
|
<input class="el-radio__original" name="el-id-7163-6" type="radio" value="90"
|
||||||
|
onclick="setAmountValue(this.value)"/>
|
||||||
|
<span class="el-radio__inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="el-radio__label">90U</span>
|
||||||
|
</label>
|
||||||
|
<label class="el-radio is-bordered el-radio--default" data-v-0f66bda2="">
|
||||||
|
<span class="el-radio__input">
|
||||||
|
<input class="el-radio__original" name="el-id-7163-6" type="radio"
|
||||||
|
value="150" onclick="setAmountValue(this.value)"/>
|
||||||
|
<span class="el-radio__inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="el-radio__label">150U</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="amount">Amount:</label>
|
||||||
|
<input type="number" id="amount" value="30" min="30" step="any" tabindex="0" autocomplete="off"
|
||||||
|
style="width: 18%;"> (USDT)
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nickname">Name:</label>
|
||||||
|
<input type="text" id="nickname" placeholder="Please enter your Nickname.">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="phone">Phone:</label>
|
||||||
|
<input type="text" id="phone" placeholder="Please enter your phone number.">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email:</label>
|
||||||
|
<input type="email" id="email" placeholder="Please enter your email.">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="wallet">Wallet address:</label>
|
||||||
|
<input type="text" id="wallet" placeholder="Enter your wallet address.">
|
||||||
|
</div>
|
||||||
|
<button type="button" onclick="handleSubmit()">Gain Points Now!!!</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="error" class="error hidden"></div>
|
|
||||||
|
|
||||||
|
<div id="addressModal" class="modal" style="display: none;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Select a wallet address</h2>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="addressList"></div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="closeAddress">Enter a new wallet address.</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="paymentModal" class="modal" style="display: none;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<span id="closePayment" class="close-btn">×</span>
|
||||||
|
<div class="modal-body">
|
||||||
|
<h2>Payment Information</h2>
|
||||||
|
<div id="orderInfo"></div>
|
||||||
|
<img src="/img/wallet_QRCode_detail.png" alt="付款二维码" class="qr-code">
|
||||||
|
<div class="payment-address">Wallet address: TNrzRLi2ArZhiMx51zusBEHHh1qyB9Ldq2
|
||||||
|
<button onclick="copyText('TNrzRLi2ArZhiMx51zusBEHHh1qyB9Ldq2')" id="copyAddress"
|
||||||
|
style="all: unset; cursor: pointer; margin-left: 10px; background-color: #00bfff; color: #fff; border: none; padding: 5px 10px;border-radius: 3px;">
|
||||||
|
copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="completeBtn" style="width: 11vh">Paid</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script>
|
<script>
|
||||||
async function createOrder(phone = "", email = "", address = "") {
|
function copyText(text) {
|
||||||
try {
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
const response = await fetch('/create_order', {
|
document.getElementById('copyAddress').textContent = 'Copied';
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ phone, email, address })
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
if (data.order_id) {
|
|
||||||
handleOrderSuccess(data.order_id);
|
|
||||||
} else if (data.addresses && Array.isArray(data.addresses)) {
|
|
||||||
handleMultipleAddresses(data.addresses);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (data.message === "地址不能为空,请输入地址。") {
|
|
||||||
promptUserForAddress();
|
|
||||||
} else {
|
|
||||||
handleError(data.message || '发生未知错误。');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('网络或服务器错误:', error);
|
|
||||||
handleError('网络或服务器错误,请稍后再试。');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleOrderSuccess(orderId) {
|
|
||||||
document.getElementById('message').innerText = `订单创建成功!您的订单号是:${orderId}`;
|
|
||||||
document.getElementById('message').classList.remove('hidden');
|
|
||||||
document.getElementById('addresses').classList.add('hidden');
|
|
||||||
document.getElementById('error').classList.add('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMultipleAddresses(addresses) {
|
|
||||||
const addressList = document.getElementById('address-list');
|
|
||||||
addressList.innerHTML = ''; // 清空现有列表
|
|
||||||
|
|
||||||
addresses.forEach((addr, index) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.innerText = `选择地址 ${index + 1}`;
|
|
||||||
button.addEventListener('click', () => {
|
|
||||||
// 选择地址后重新提交订单
|
|
||||||
createOrder(null, null, addr);
|
|
||||||
});
|
|
||||||
li.innerText = addr + ' ';
|
|
||||||
li.appendChild(button);
|
|
||||||
addressList.appendChild(li);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('addresses').classList.remove('hidden');
|
|
||||||
document.getElementById('message').classList.add('hidden');
|
|
||||||
document.getElementById('error').classList.add('hidden');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function promptUserForAddress() {
|
|
||||||
const address = prompt('地址不能为空,请输入您的地址:');
|
|
||||||
if (address && address.trim() !== "") {
|
|
||||||
createOrder(null, null, address.trim());
|
|
||||||
} else {
|
|
||||||
handleError('地址输入为空,无法创建订单。');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleError(message) {
|
|
||||||
const errorDiv = document.getElementById('error');
|
|
||||||
errorDiv.innerText = `错误:${message}`;
|
|
||||||
errorDiv.classList.remove('hidden');
|
|
||||||
document.getElementById('message').classList.add('hidden');
|
|
||||||
document.getElementById('addresses').classList.add('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绑定表单提交事件
|
|
||||||
document.getElementById('order-form').addEventListener('submit', function(event) {
|
|
||||||
event.preventDefault(); // 阻止表单默认提交行为
|
|
||||||
|
|
||||||
// 获取表单输入值
|
|
||||||
const phone = document.getElementById('phone').value.trim();
|
|
||||||
const email = document.getElementById('email').value.trim();
|
|
||||||
const address = document.getElementById('address').value.trim();
|
|
||||||
|
|
||||||
// 清除之前的消息
|
|
||||||
document.getElementById('message').classList.add('hidden');
|
|
||||||
document.getElementById('addresses').classList.add('hidden');
|
|
||||||
document.getElementById('error').classList.add('hidden');
|
|
||||||
|
|
||||||
// 调用createOrder函数
|
|
||||||
createOrder(phone, email, address);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1 +1,250 @@
|
||||||
console.log('Happy developing ✨')
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const options = document.querySelectorAll('.el-radio');
|
||||||
|
options.forEach(option => {
|
||||||
|
option.addEventListener('click', selectAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectAmount(event) {
|
||||||
|
const options = document.querySelectorAll('.el-radio.is-checked');
|
||||||
|
options.forEach(option => {
|
||||||
|
option.classList.remove('is-checked');
|
||||||
|
option.querySelector('.el-radio__input').classList.remove('is-checked');
|
||||||
|
});
|
||||||
|
|
||||||
|
event.currentTarget.classList.add('is-checked');
|
||||||
|
event.currentTarget.querySelector('.el-radio__input').classList.add('is-checked');
|
||||||
|
|
||||||
|
// 获取选中的输入值并更新到 id="amount" 的元素
|
||||||
|
document.getElementById('amount').value = event.currentTarget.querySelector('input').value; // 显示金额
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal functionality
|
||||||
|
const closeAddress = document.getElementById('closeAddress');
|
||||||
|
closeAddress.addEventListener('click', () => {
|
||||||
|
document.getElementById('wallet').focus()
|
||||||
|
document.getElementById('addressModal').style.display = 'none';
|
||||||
|
});
|
||||||
|
const closePayment = document.getElementById('closePayment');
|
||||||
|
closePayment.addEventListener('click', () => {
|
||||||
|
document.getElementById('paymentModal').style.display = 'none';
|
||||||
|
});
|
||||||
|
const completeBtn = document.getElementById('completeBtn');
|
||||||
|
completeBtn.addEventListener('click', () => {
|
||||||
|
closePaymentModal();
|
||||||
|
finishOrder();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close modal when clicking outside the modal content
|
||||||
|
document.getElementById('addressModal').addEventListener('click', closeModalOnClickOutside);
|
||||||
|
document.getElementById('paymentModal').addEventListener('click', closeModalOnClickOutside);
|
||||||
|
});
|
||||||
|
|
||||||
|
function finishOrder() {
|
||||||
|
const orderID = localStorage.getItem('currentOrderID'); // 获取订单 ID
|
||||||
|
if (!orderID) {
|
||||||
|
console.error('Order ID is not available.');
|
||||||
|
handleError('Order ID is not available. Please create an order first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `/finishOrder?orderID=${orderID}`;
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Network response was not ok: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
console.log('Response data:', data);
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('There was a problem with the GET request:', error);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleOrderSuccess(orderId, amount, orderCreateTimestamp, orderExpirationTime) {
|
||||||
|
openPaymentModal(orderId, amount, orderCreateTimestamp, orderExpirationTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to show the modal with addresses
|
||||||
|
function handleMultipleAddresses(walletAddresses) {
|
||||||
|
// Find the modal and the modal body
|
||||||
|
const addressList = document.getElementById('addressList');
|
||||||
|
addressList.innerHTML = ''; // 清空当前内容
|
||||||
|
|
||||||
|
// Create a list of wallet addresses
|
||||||
|
walletAddresses.forEach(address => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'address-option';
|
||||||
|
div.textContent = address;
|
||||||
|
div.addEventListener('click', () => {
|
||||||
|
document.getElementById('wallet').value = address;
|
||||||
|
closeAddressModal();
|
||||||
|
handleSubmit();
|
||||||
|
});
|
||||||
|
addressList.appendChild(div);
|
||||||
|
});
|
||||||
|
|
||||||
|
openAddressModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to show the modal with an image
|
||||||
|
function showImageModal(imageUrl, walletAddress) {
|
||||||
|
// Find the modal and modal body
|
||||||
|
const modal = document.getElementById('modal');
|
||||||
|
const modalBody = document.getElementById('modalBody');
|
||||||
|
|
||||||
|
// Clear any previous content
|
||||||
|
modalBody.innerHTML = '';
|
||||||
|
|
||||||
|
// Create an image element
|
||||||
|
const imageElement = document.createElement('img');
|
||||||
|
imageElement.src = imageUrl;
|
||||||
|
imageElement.alt = 'Image';
|
||||||
|
imageElement.style.maxWidth = '100%';
|
||||||
|
imageElement.style.maxHeight = '80vh'; // Limit the height for better UX
|
||||||
|
|
||||||
|
// Create a div to display the wallet address
|
||||||
|
const addressDiv = document.createElement('div');
|
||||||
|
addressDiv.textContent = walletAddress;
|
||||||
|
addressDiv.style.marginTop = '10px';
|
||||||
|
addressDiv.style.fontSize = '16px';
|
||||||
|
addressDiv.style.color = '#333';
|
||||||
|
|
||||||
|
// Add image and address to modal body
|
||||||
|
modalBody.appendChild(imageElement);
|
||||||
|
modalBody.appendChild(addressDiv);
|
||||||
|
|
||||||
|
openAddressModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPaymentModal(orderId, amount, orderCreateTimestamp, orderExpirationTime) {
|
||||||
|
document.getElementById('paymentModal').style.display = 'flex';
|
||||||
|
|
||||||
|
// 显示订单ID
|
||||||
|
const orderInfoElement = document.getElementById('orderInfo');
|
||||||
|
orderInfoElement.innerHTML = `<p>Order ID: ${orderId}</p>`;
|
||||||
|
|
||||||
|
// 计算剩余支付时间 (以秒为单位)
|
||||||
|
const expirationTime = orderCreateTimestamp + orderExpirationTime; // 10 分钟后的时间戳(单位:毫秒)
|
||||||
|
const updateRemainingTime = () => {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const remainingTime = Math.max(0, expirationTime - now);
|
||||||
|
const minutes = Math.floor((remainingTime / 1000 / 60) % 60);
|
||||||
|
const seconds = Math.floor((remainingTime / 1000) % 60);
|
||||||
|
|
||||||
|
// 更新剩余时间
|
||||||
|
orderInfoElement.innerHTML = `<p>Order ID: ${orderId}, You need to pay <span style="color: red">${amount}</span>USDT.</p>`;
|
||||||
|
orderInfoElement.innerHTML += `<p style="color: red">Time remaining: ${minutes} minutes ${seconds} seconds</p>`;
|
||||||
|
|
||||||
|
// 如果时间到了,停止更新
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
clearInterval(interval);
|
||||||
|
orderInfoElement.innerHTML = `<p style="color: red">Order ${orderId} has expired, please place a new order.</p>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始倒计时
|
||||||
|
const interval = setInterval(updateRemainingTime, 1000);
|
||||||
|
updateRemainingTime(); // 立即更新一次
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function closePaymentModal() {
|
||||||
|
document.getElementById('paymentModal').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAddressModal() {
|
||||||
|
document.getElementById('addressModal').style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeAddressModal() {
|
||||||
|
document.getElementById('addressModal').style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModalOnClickOutside(event) {
|
||||||
|
const modals = document.querySelectorAll('.modal');
|
||||||
|
modals.forEach(modal => {
|
||||||
|
const modalContent = modal.querySelector('.modal-content');
|
||||||
|
if (!modalContent.contains(event.target) && modal.style.display === 'flex') {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptUserForPaymentMethod() {
|
||||||
|
handleError('Unsupported payment method. Currently, only USDT payments are supported.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(message) {
|
||||||
|
const errorDiv = document.getElementById('error');
|
||||||
|
errorDiv.innerText = `错误:${message}`;
|
||||||
|
errorDiv.classList.remove('hidden');
|
||||||
|
document.getElementById('message').classList.add('hidden');
|
||||||
|
document.getElementById('addresses').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptUserForAddress() {
|
||||||
|
const address = prompt('No payment address associated with this phone number was found. Please enter a payment address or provide additional information.');
|
||||||
|
if (address && address.trim() !== "") {
|
||||||
|
createOrder(null, null, address.trim());
|
||||||
|
} else {
|
||||||
|
handleError('No payment address associated with you was found. Please provide a payment address.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createOrder(amount, name, phone, email, wallet_address, paymentMethod) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://127.0.0.1:5000/createOrder', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({amount, name, phone, email, wallet_address, paymentMethod})
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json(); // 确保正确解析响应
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
if (data.orderID) {
|
||||||
|
localStorage.setItem('currentOrderID', data.orderID); // 保存订单 ID 到 localStorage
|
||||||
|
handleOrderSuccess(data.orderID, amount, data.orderCreateTimestamp, data.orderExpirationTime);
|
||||||
|
} else if (data.wallet_addresses && Array.isArray(data.wallet_addresses)) {
|
||||||
|
handleMultipleAddresses(data.wallet_addresses);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data.message === "wrong payment method") {
|
||||||
|
promptUserForPaymentMethod();
|
||||||
|
} else if (data.message === "empty wallet address") {
|
||||||
|
promptUserForAddress();
|
||||||
|
} else {
|
||||||
|
handleError(data.message || 'Unknown error.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Network or server error:', error);
|
||||||
|
handleError('Network or server error. Please try again later.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定表单提交事件
|
||||||
|
function handleSubmit() {
|
||||||
|
const amount = document.getElementById('amount').value.trim();
|
||||||
|
const name = document.getElementById('nickname').value.trim();
|
||||||
|
const phone = document.getElementById('phone').value.trim();
|
||||||
|
const email = document.getElementById('email').value.trim();
|
||||||
|
const payment_address = document.getElementById('wallet').value.trim();
|
||||||
|
|
||||||
|
// 清除之前的消息
|
||||||
|
// document.getElementById('message').classList.add('hidden');
|
||||||
|
|
||||||
|
// 调用createOrder函数
|
||||||
|
createOrder(amount, name, phone, email, payment_address, "USDT");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
<html lang="en" class=" ">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<title>余额充值 - API管理平台</title>
|
||||||
|
<link href="/css/modules.fdcec670.css" rel="stylesheet" />
|
||||||
|
<link href="/css/app.f2b97341.css" rel="stylesheet" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/admin-system-adminMoneyRecharge.5cf4644c.css"
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/styles.css"/>
|
||||||
|
<link rel="preload" href="/index.js" as="script">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body inmaintabuse="1" data-layout="default">
|
||||||
|
<div id="app" class="aminui" data-v-app="">
|
||||||
|
<section class="aminui-wrapper">
|
||||||
|
<div class="aminui-body el-container">
|
||||||
|
<div class="adminui-main" id="adminui-main">
|
||||||
|
<main class="el-main" data-v-0f66bda2="">
|
||||||
|
<div class="el-card is-always-shadow" data-v-0f66bda2="">
|
||||||
|
<div class="el-card__header">
|
||||||
|
<div class="card-header" data-v-0f66bda2="">
|
||||||
|
<span data-v-0f66bda2="">充值</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-card__body" style="">
|
||||||
|
<div class="el-scrollbar table-form-scrollbar" data-v-0f66bda2="">
|
||||||
|
<div class="el-scrollbar__wrap el-scrollbar__wrap--hidden-default">
|
||||||
|
<div class="el-scrollbar__view" style="">
|
||||||
|
<div data-v-0f66bda2="">
|
||||||
|
<section class="description" data-v-0f66bda2="">
|
||||||
|
<p class="title" data-v-0f66bda2="">温馨提示:</p>
|
||||||
|
<p data-v-0f66bda2="">
|
||||||
|
1、目前仅支持USDT充值,充值的金额不支持提现
|
||||||
|
</p>
|
||||||
|
<p data-v-0f66bda2="">
|
||||||
|
2、金额会在支付后1分钟内到账,如果5分钟未到账请联系客服
|
||||||
|
</p>
|
||||||
|
<p data-v-0f66bda2="" style="color: red">
|
||||||
|
3、实际转账金额必须与支付页面显示金额一致,否则无法到账
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
|
<label id="el-id-7163-5" for="el-id-7163-12" class="el-form-item__label">Payment method:</label>
|
||||||
|
<div class="el-form-item__content">
|
||||||
|
<i class="el-icon"
|
||||||
|
data-v-0f66bda2=""
|
||||||
|
style="font-size: 30px">
|
||||||
|
<svg
|
||||||
|
t="1693061019040"
|
||||||
|
class="icon"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="4072"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
data-v-0f66bda2="">
|
||||||
|
<path
|
||||||
|
d="M1023.082985 511.821692c0 281.370746-228.08199 509.452736-509.452736 509.452736-281.360557 0-509.452736-228.08199-509.452737-509.452736 0-281.365652 228.092179-509.452736 509.452737-509.452737 281.370746 0 509.452736 228.087085 509.452736 509.452737"
|
||||||
|
fill="#1BA27A"
|
||||||
|
p-id="4073"></path>
|
||||||
|
<path
|
||||||
|
d="M752.731701 259.265592h-482.400796v116.460896h182.969951v171.176119h116.460895v-171.176119h182.96995z"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
p-id="4074"></path>
|
||||||
|
<path
|
||||||
|
d="M512.636816 565.13592c-151.358408 0-274.070289-23.954468-274.070289-53.50782 0-29.548259 122.706786-53.507821 274.070289-53.507821 151.358408 0 274.065194 23.959562 274.065194 53.507821 0 29.553353-122.706786 53.507821-274.065194 53.50782m307.734925-44.587303c0-38.107065-137.776398-68.995184-307.734925-68.995184-169.953433 0-307.74002 30.888119-307.74002 68.995184 0 33.557652 106.837333 61.516418 248.409154 67.711363v245.729433h116.450707v-245.632637c142.66205-6.001353 250.615085-34.077294 250.615084-67.808159"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
p-id="4075"></path>
|
||||||
|
</svg>
|
||||||
|
</i>
|
||||||
|
<div data-v-0f66bda2="" style="margin-left: 20px">
|
||||||
|
<div class="el-radio-group" role="radiogroup" aria-labelledby="el-id-7163-5" data-v-0f66bda2="" id="el-id-7163-12">
|
||||||
|
<label class="el-radio is-bordered is-checked el-radio--default" data-v-0f66bda2="">
|
||||||
|
<span class="el-radio__input is-checked">
|
||||||
|
<input class="el-radio__original" name="el-id-7163-6" type="radio" value="30" onclick="setAmountValue(this.value)"/>
|
||||||
|
<span class="el-radio__inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="el-radio__label">30$</span>
|
||||||
|
</label>
|
||||||
|
<label class="el-radio is-bordered el-radio--default" data-v-0f66bda2="">
|
||||||
|
<span class="el-radio__input">
|
||||||
|
<input class="el-radio__original" name="el-id-7163-6" type="radio" value="90" onclick="setAmountValue(this.value)"/>
|
||||||
|
<span class="el-radio__inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="el-radio__label">90$</span>
|
||||||
|
</label>
|
||||||
|
<label class="el-radio is-bordered el-radio--default" data-v-0f66bda2="">
|
||||||
|
<span class="el-radio__input">
|
||||||
|
<input class="el-radio__original" name="el-id-7163-6" type="radio" value="150" onclick="setAmountValue(this.value)"/>
|
||||||
|
<span class="el-radio__inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="el-radio__label">150$</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
|
<label id="amount_label" for="amount_input" class="el-form-item__label">Amount:</label>
|
||||||
|
<div class="el-form-item__content">
|
||||||
|
<div class="el-input el-input--default" data-v-0f66bda2="" style="width: 7em">
|
||||||
|
<div class="el-input__wrapper">
|
||||||
|
<input class="el-input__inner" type="number" min="30" step="any" autocomplete="off" tabindex="0"
|
||||||
|
id="amount_input" value="30"/>
|
||||||
|
</div>
|
||||||
|
</div>(USDT)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
|
<label id="name_label" for="name_input" class="el-form-item__label">Name:</label>
|
||||||
|
<div class="el-form-item__content">
|
||||||
|
<div class="el-input el-input--default" data-xv-0f66bda2="">
|
||||||
|
<div class="el-input__wrapper">
|
||||||
|
<input class="el-input__inner" type="text" autocomplete="off" tabindex="0" placeholder="Please enter your Nickname." id="name_input"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
|
<label id="phone_label" for="phone_input" class="el-form-item__label">Phone:</label>
|
||||||
|
<div class="el-form-item__content">
|
||||||
|
<div class="el-input el-input--default" data-v-0f66bda2="">
|
||||||
|
<div class="el-input__wrapper">
|
||||||
|
<input class="el-input__inner" type="tel" autocomplete="off" tabindex="0" placeholder="Please enter your phone number." id="phone_input"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
|
<label id="email_label" for="email_input" class="el-form-item__label">Email:</label>
|
||||||
|
<div class="el-form-item__content">
|
||||||
|
<div class="el-input el-input--default" data-v-0f66bda2="">
|
||||||
|
<div class="el-input__wrapper">
|
||||||
|
<input class="el-input__inner" type="email" autocomplete="off" tabindex="0" placeholder="Please enter your email." id="email_input"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-form-item el-form-item--default" data-v-0f66bda2="">
|
||||||
|
<label id="address_label" for="address_input" class="el-form-item__label">Wallet address:</label>
|
||||||
|
<div class="el-form-item__content">
|
||||||
|
<div class="el-input el-input--default" data-v-0f66bda2="">
|
||||||
|
<div class="el-input__wrapper">
|
||||||
|
<input class="el-input__inner" type="text" autocomplete="off" tabindex="0" placeholder="Please enter your wallet address." id="address_input"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="el-button el-button--primary el-button--default" type="button" data-v-0f66bda2="" onclick="handleSubmit()">
|
||||||
|
<span class="">Gain Points Now!!!</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-scrollbar__bar is-horizontal" style="display: none">
|
||||||
|
<div class="el-scrollbar__thumb" style="transform: translateX(0%)"></div>
|
||||||
|
</div>
|
||||||
|
<div class="el-scrollbar__bar is-vertical" style="display: none">
|
||||||
|
<div class="el-scrollbar__thumb" style="transform: translateY(0%)"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="el-overlay" style="z-index: 2002; display: none">
|
||||||
|
<div role="dialog" aria-modal="true" aria-label="支付" aria-describedby="el-id-7163-9" class="el-overlay-dialog"></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Loading…
Reference in New Issue