303 lines
9.8 KiB
PHP
303 lines
9.8 KiB
PHP
<?php
|
|
|
|
namespace services;
|
|
|
|
require_once('mt/MarketGoods.php');
|
|
require_once('mt/MarketBatch.php');
|
|
require_once('mt/Item.php');
|
|
require_once('mt/BlindBox.php');
|
|
require_once('mt/WhiteList.php');
|
|
require_once('mt/Currency.php');
|
|
require_once('mt/Hero.php');
|
|
require_once('mt/Parameter.php');
|
|
|
|
require_once('models/BoxOrder.php');
|
|
require_once('models/Nft.php');
|
|
require_once('models/BuyRecord.php');
|
|
|
|
require_once('phpcommon/bchelper.php');
|
|
|
|
use phpcommon;
|
|
use phpcommon\SqlHelper;
|
|
use models\BoxOrder;
|
|
use models\Nft;
|
|
use models\BuyRecord;
|
|
|
|
class MarketService extends BaseService {
|
|
|
|
const CURRENCY_DECIMALS = 8;
|
|
const PRICE_PAD = '0000000000';
|
|
|
|
const PRESALE_NOT_STARTED = 0;
|
|
const PRESALE_PREPARE = 1;
|
|
const PRESALE_STARTED = 2;
|
|
const PRESALE_SOLD_OUT = 3;
|
|
|
|
const FUNCID_PRESALE = 1;
|
|
const FUNCID_SHOP = 2;
|
|
|
|
const TOKEN_SALT = 'B8E6BD4F-FD7B-E2B8-6688-80A2D8632064';
|
|
|
|
public static function isTestMode()
|
|
{
|
|
return isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443 && SERVER_ENV == _TEST;
|
|
}
|
|
|
|
public static function isValidToken($account, $token)
|
|
{
|
|
$deToken = base64_decode($token);
|
|
if (empty($deToken)) {
|
|
return false;
|
|
}
|
|
if (empty($account)) {
|
|
return false;
|
|
}
|
|
$data = json_decode($deToken, true);
|
|
$sign = md5(self::TOKEN_SALT .
|
|
$data['account'] .
|
|
$data['rand'] .
|
|
$data['nonce'] .
|
|
$data['createtime']);
|
|
if ($sign == $data['sign']) {
|
|
return phpcommon\isSameAddress($data['account'], $account);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static function buyBoxVerifySignature($buyerAddress,
|
|
$type,
|
|
$paymentTokenAddress,
|
|
$price,
|
|
$nonce,
|
|
$signature)
|
|
|
|
{
|
|
$params = array(
|
|
'c' => 'BcService',
|
|
'a' => 'buyBoxVerifySignature',
|
|
'type' => $type,
|
|
'paymentTokenAddress' => $paymentTokenAddress,
|
|
'price' => $price,
|
|
'nonce' => $nonce,
|
|
'signature' => $signature
|
|
);
|
|
$url = self::getWeb3ServiceUrl();
|
|
$response = '';
|
|
if (!phpcommon\HttpClient::get
|
|
($url,
|
|
$params,
|
|
$response)) {
|
|
phpcommon\sendError(500, 'server internal error');
|
|
die();
|
|
return;
|
|
}
|
|
error_log(json_encode(array(
|
|
'_REQUEST' => $_REQUEST,
|
|
'params' => $params,
|
|
'response' => $response
|
|
)));
|
|
$data = json_decode($response, true);
|
|
if (getXVal($data, 'errcode', 0) != 0) {
|
|
phpcommon\sendError(1, 'Signature verification failed');
|
|
die();
|
|
return;
|
|
} else {
|
|
$recovered = getXVal($data, 'recovered', '');
|
|
if (!phpcommon\isSameAddress($recovered, $buyerAddress)) {
|
|
phpcommon\sendError(1, 'Signature verification failed');
|
|
die();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function auth($account, $tips, $nonce, $signature)
|
|
{
|
|
$params = array(
|
|
'c' => 'BcService',
|
|
'a' => 'authVerifySignature',
|
|
'tips' => $tips,
|
|
'nonce' => $nonce,
|
|
'signature' => $signature
|
|
);
|
|
$url = self::getWeb3ServiceUrl();
|
|
$response = '';
|
|
if (!phpcommon\HttpClient::get
|
|
($url,
|
|
$params,
|
|
$response)) {
|
|
myself()->_rspErr(500, 'server internal error');
|
|
die();
|
|
return;
|
|
}
|
|
error_log(json_encode(array(
|
|
'_REQUEST' => $_REQUEST,
|
|
'params' => $params,
|
|
'response' => $response
|
|
)));
|
|
$data = json_decode($response, true);
|
|
if (getXVal($data, 'errcode', 0) != 0) {
|
|
myself()->_rspErr(1, 'Signature verification failed');
|
|
die();
|
|
return;
|
|
} else {
|
|
$recovered = getXVal($data, 'recovered', '');
|
|
if (!phpcommon\isSameAddress($recovered, $account)) {
|
|
myself()->_rspErr(1, 'Signature verification failed');
|
|
die();
|
|
return;
|
|
} else {
|
|
myself()->_rspData(array(
|
|
'token' => self::genToken($account, $nonce)
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static function getWeb3ServiceUrl()
|
|
{
|
|
$web3ServiceCluster = require_once('../config/web3service.cluster.php');
|
|
return $web3ServiceCluster[rand() % count($web3ServiceCluster)];
|
|
}
|
|
|
|
private static function genToken($account, $nonce)
|
|
{
|
|
$data = array(
|
|
'account' => $account,
|
|
'rand' => uniqid(),
|
|
'nonce' => $nonce,
|
|
'createtime' => myself()->_getNowTime(),
|
|
);
|
|
$data['sign'] = md5(self::TOKEN_SALT .
|
|
$data['account'] .
|
|
$data['rand'] .
|
|
$data['nonce'] .
|
|
$data['createtime']);
|
|
return base64_encode(json_encode($data));
|
|
}
|
|
|
|
|
|
public static function getPreSaleInfo($account)
|
|
{
|
|
$currBatchMeta = mt\MarketBatch::getCurrentBatch();
|
|
if (!$currBatchMeta) {
|
|
return self::getDefaultPresaleInfo();
|
|
}
|
|
|
|
$countdown = mt\MarketBatch::getCountdown($currBatchMeta);
|
|
$soldNum = min(BoxOrder::getSoldNum($currBatchMeta['id']),
|
|
$currBatchMeta['number_of_props']);
|
|
$totalNum = $currBatchMeta['number_of_props'];
|
|
$buyed = self::isTestMode() ?
|
|
0 : BoxOrder::isBuyed($account, $currBatchMeta['id']);
|
|
$title = '';
|
|
$state = self::PRESALE_PREPARE;
|
|
if ($countdown > 0) {
|
|
$state = self::PRESALE_PREPARE;
|
|
} else {
|
|
if ($soldNum >= $totalNum) {
|
|
$title = self::escapeString($currBatchMeta['end_title']);
|
|
$state = self::PRESALE_SOLD_OUT;
|
|
} else {
|
|
$title = self::escapeString($currBatchMeta['begin_title']);
|
|
$state = self::PRESALE_STARTED;
|
|
}
|
|
}
|
|
$presaleInfo = array(
|
|
'batch_id' => $currBatchMeta['batch_id'],
|
|
'countdown' => $countdown,
|
|
'sold_num' => $soldNum,
|
|
'total_num' => $totalNum,
|
|
'state' => $state,
|
|
'title' => $title,
|
|
'hint' => self::escapeString($currBatchMeta['hint']),
|
|
'buyable_list' => array(),
|
|
//'buyed' => $buyed
|
|
);
|
|
|
|
$batchMetas = mt\MarketGoods::getBatchMetas($currBatchMeta['batch_id']);
|
|
if ($batchMetas) {
|
|
foreach ($batchMetas as $meta) {
|
|
$boxId = phpcommon\genBoxId($currBatchMeta['id'],
|
|
$meta['id'],
|
|
$meta['item_id']);
|
|
if (!$buyed) {
|
|
if ($currBatchMeta['white_list'] &&
|
|
!mt\WhiteList::inWhiteList($account)) {
|
|
continue;
|
|
}
|
|
array_push($presaleInfo['buyable_list'],
|
|
array(
|
|
'box_id' => $boxId
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
return $presaleInfo;
|
|
}
|
|
|
|
private static function getDefaultPresaleInfo()
|
|
{
|
|
$title = self::escapeString(
|
|
mt\Parameter::getVal('pre_sale_not_started_title', ''));
|
|
$hint = self::escapeString(
|
|
mt\Parameter::getVal('pre_sale_not_started_hint', ''));
|
|
$presaleInfo = array(
|
|
'batch_id' => 0,
|
|
'countdown' => 0,
|
|
'sold_num' => 0,
|
|
'total_num' => 0,
|
|
'state' => self::PRESALE_NOT_STARTED,
|
|
'title' => $title,
|
|
'hint' => $hint,
|
|
'buyable_list' => array(),
|
|
//'buyed' => 0
|
|
);
|
|
return $presaleInfo;
|
|
}
|
|
|
|
private static function escapeString($str)
|
|
{
|
|
return str_replace("\n", '\n', $str);
|
|
}
|
|
|
|
public static function openBox($itemMeta, &$items)
|
|
{
|
|
if ($itemMeta['type'] == mt\Item::BLIND_BOX_TYPE) {
|
|
//group1:100|group2:100|group3:100
|
|
$groups = StrHelper::parseList($itemMeta['param1'], array('|', ':'));
|
|
foreach ($groups as $tuple) {
|
|
$rndVal = rand() % 100;
|
|
if ($rndVal <= $tuple[1]) {
|
|
$boxMeta = mt\BlindBox::randItem($itemMeta['id'], $tuple[0]);
|
|
if ($boxMeta) {
|
|
$nftMeta = mt\Item::get($boxMeta['item_id']);
|
|
if ($nftMeta) {
|
|
$tokenType = Nft::getTokenType($nftMeta);
|
|
if ($tokenType != Nft::NONE_TYPE) {
|
|
$need = (empty($items) || $tuple[1] >= 100) ? 1 : 0;
|
|
array_push($items, array(
|
|
'need' => $need,
|
|
'item_id' => $nftMeta['id'],
|
|
'token_type' => $tokenType,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$tokenType = Nft::getTokenType($itemMeta);
|
|
if ($tokenType != Nft::NONE_TYPE) {
|
|
array_push($items, array(
|
|
'need' => 1,
|
|
'item_id' => $itemMeta['id'],
|
|
'token_type' => $tokenType,
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|