alert(' . json_encode(t('shop.payment_processing')) . ');';
}
// Import from config:
$shop = $config['shop'];
if ($shop['loginToView'] === true) protect_page();
$loggedin = user_logged_in();
function shop_db_offer_columns(): array {
$columns = array();
$rows = db()->fetchAll("SHOW COLUMNS FROM `znote_shop_offers`;");
if (is_array($rows)) {
foreach ($rows as $row) {
if (!empty($row['Field'])) {
$columns[(string)$row['Field']] = true;
}
}
}
return $columns;
}
function shop_load_db_offers(): array {
$columns = shop_db_offer_columns();
$where = !empty($columns['active']) ? "WHERE `active` = 1" : "";
$order = !empty($columns['sort_order'])
? "ORDER BY `sort_order` ASC, `id` ASC"
: "ORDER BY `id` ASC";
$rows = db()->fetchAll("
SELECT `id`, `type`, `itemid`, `count`, `description`, `points`
FROM `znote_shop_offers`
{$where}
{$order};
");
if (!is_array($rows)) {
return array();
}
$offers = array();
foreach ($rows as $row) {
$itemid = (int)$row['itemid'];
if ((int)$row['type'] === 5 && $itemid > 0) {
$male = (int)floor($itemid / 10000);
$female = (int)($itemid % 10000);
$itemid = array($male, $female);
}
$offers[(int)$row['id']] = array(
'type' => (int)$row['type'],
'itemid' => $itemid,
'count' => (int)$row['count'],
'description' => (string)$row['description'],
'points' => (int)$row['points'],
);
}
return $offers;
}
$shop_list = shop_load_db_offers();
if ($loggedin === true) {
$postedShopSession = (string)($_POST['session'] ?? '');
$storedShopSession = (string)($_SESSION['shop_session'] ?? '');
if (!empty($_POST['buy']) && $postedShopSession !== '' && $storedShopSession !== '' && hash_equals($storedShopSession, $postedShopSession)) {
unset($_SESSION['shop_session']);
$time = time();
$cid = (int)$user_data['id'];
// Sanitizing post, setting default buy value
$buy = false;
$post = (int)$_POST['buy'];
foreach ($shop_list as $key => $value) {
if ($key === $post) {
$buy = $value;
}
}
if ($buy === false) die("Error: Shop offer ID mismatch.");
// Plugins may adjust what this offer costs - a discount code, a happy
// hour, a loyalty rebate. The filtered value is what gets checked,
// charged and logged, so the three can never disagree.
$buy['points'] = max(0, (int)znote_hook_filter('shop.price', (int)$buy['points'], array(
'account_id' => $cid,
'offer_id' => $post,
'offer' => $buy,
)));
// If this is an outfit offer, convert array into an integer.
if ($buy['type'] == 5) {
if (is_array($buy['itemid'])) {
if (COUNT($buy['itemid']) == 2) $buy['itemid'] = ($buy['itemid'][0] * 1000) + $buy['itemid'][1];
else $buy['itemid'] = $buy['itemid'][0];
}
}
$db = db();
if (!$db->beginTransaction()) {
die("Failed to start shop transaction.");
}
$data = $db->fetchOne("SELECT `points` FROM `znote_accounts` WHERE `account_id` = ? LIMIT 1 FOR UPDATE;", [$cid]);
if (!$data) {
$db->rollback();
die("0: Account is not converted to work with Znote AAC");
}
$old_points = (int)$data['points'];
if ($old_points < $buy['points']) {
$db->rollback();
echo 'You need more points, this offer cost '.$buy['points'].' points.';
} else {
$expense_points = (int)$buy['points'];
$orderReady = true;
if (!$db->execute(
"UPDATE `znote_accounts` SET `points` = `points` - ? WHERE `account_id` = ? AND `points` >= ?;",
[$expense_points, $cid, $expense_points]
)) {
$orderReady = false;
}
// Do the magic (insert into db, or change sex etc)
// If type is 2 or 3
if ($orderReady && $buy['type'] == 2) {
// Add premium days to account
$orderReady = user_account_add_premdays($cid, $buy['count']);
$successMessage = 'You now have '.$buy['count'].' additional days of premium membership.';
} else if ($orderReady) {
$orderReady = $db->execute(
"INSERT INTO `znote_shop_orders` (`account_id`, `type`, `itemid`, `count`, `time`) VALUES (?, ?, ?, ?, ?);",
[$cid, (int)$buy['type'], (int)$buy['itemid'], (int)$buy['count'], $time]
);
if ($buy['type'] == 3) {
$successMessage = ''. t('shop.gender_unlocked') .'';
} else if ($buy['type'] == 4) {
$successMessage = ''. t('shop.name_unlocked') .'';
} else {
$successMessage = 'Your order is ready to be delivered. Write this command in-game to get it: [!shop].
Make sure you are in depot and can carry it before executing the command!';
}
}
if ($orderReady) {
$orderReady = $db->execute(
"INSERT INTO `znote_shop_logs` (`account_id`, `player_id`, `type`, `itemid`, `count`, `points`, `time`) VALUES (?, 0, ?, ?, ?, ?, ?);",
[$cid, (int)$buy['type'], (int)$buy['itemid'], (int)$buy['count'], (int)$buy['points'], $time]
);
}
if ($orderReady) {
$db->commit();
$user_znote_data['points'] = $old_points - $expense_points;
echo $successMessage;
// Plugins can react to a purchase - a coupon ledger, a Discord message,
// a loyalty counter. They cannot change what was bought; this is a
// notification, fired after the points have already been taken.
znote_hook('shop.purchased', array(
'account_id' => $cid,
'offer_id' => $post,
'type' => $buy['type'],
'itemid' => $buy['itemid'],
'count' => $buy['count'],
'points' => $buy['points'],
));
$buy['points'] = 0;
} else {
$db->rollback();
echo 'Shop purchase failed. Please try again or contact staff.';
}
}
//var_dump($buy);
//echo ''. $_POST['buy'] .'';
}
}
view('shop');
theme_close();