UNPKG

spworlds

Version:

Library for SPWorlds API

155 lines (154 loc) 6.87 kB
"use strict"; // Copyright (c) 2022 Matvey Ryabchikov // Copyright (c) 2024-2025 Mihandr // MIT License Object.defineProperty(exports, "__esModule", { value: true }); exports.SPWorlds = void 0; const crypto_1 = require("crypto"); const DEFAULT_API_ENDPOINT = 'https://spworlds.ru'; class SPWorlds { authorization; token; apiTimeout; APIEndpoint; requestAPI = async (method, path, body) => { try { const res = await fetch(`${this.APIEndpoint}/api/public/${path}`, { method, body: body ? JSON.stringify(body) : null, headers: { Authorization: this.authorization, 'Content-Type': 'application/json' }, signal: this.apiTimeout ? AbortSignal.timeout(this.apiTimeout) : undefined }); if (![200, 201, 404].includes(res.status)) throw new Error(`Ошибка при запросе к API ${res.status} ${res.statusText}`); return await res.json(); } catch (err) { if (err.name === 'AbortError') { throw new Error(`Превышено время ожидания запроса (${this.apiTimeout}мс)`); } throw err; } }; /** * Класс для работы с SPWorlds API * @param id ID карты * @param token Токен карты * @param apiTimeout Таймаут запроса к API * @param APIEndpoint Вы можете указать другой API сервер (например https://spworlds.org). По умолчанию: https://spworlds.ru */ constructor({ id, token, apiTimeout, APIEndpoint = DEFAULT_API_ENDPOINT }) { this.authorization = `Bearer ${Buffer.from(`${id}:${token}`).toString('base64')}`; this.token = token; this.apiTimeout = apiTimeout; this.APIEndpoint = APIEndpoint; } /** * Проверка доступности SPWorlds API * @returns Состояние API */ ping = async () => { return !!(await this.getCardInfo().catch(() => false)); }; /** * Создание банковского перевода * @param receiver Номер карты получателя * @param amount Сумма перевода * @param comment Комментарий к переводу * @returns Баланс карты */ createTransaction = async ({ receiver, amount, comment }) => { return this.requestAPI('POST', 'transactions', { receiver, amount, comment }); }; /** * Получение информации о карте * @returns Баланс и подключенный webhook */ getCardInfo = async () => { return this.requestAPI('GET', 'card', null); }; /** * Получение информации о владельце токена * @returns Никнейм, статус, роли, город, банковские карты и время создания */ getCardOwner = async () => { return this.requestAPI('GET', 'accounts/me', null); }; /** * Получение карт пользователя * @param Nickname Никнейм пользователя * @returns Массив с банковскими картами */ getCards = async (nickname) => { return this.requestAPI('GET', `accounts/${nickname}/cards`, null); }; /** * Получение ника игрока * @param discordId ID пользователя Discord * @returns Никнейм и Minecraft UUID игрока */ findUser = async (discordId) => { return this.requestAPI('GET', `users/${discordId}`, null).then((res) => { if (res.username || res.uuid) return res; return null; }); }; /** * Изменение вебхука карты * На вебхук будут отправляться все новые транзакции связанные с картой * @param url Ссылка на вебхук * @returns ID карты и обновленный webhook */ setWebhook = async (url) => { return this.requestAPI('PUT', 'card/webhook', { url: url }); }; /** * Создание запроса на оплату * @param items Товары транзакции * @param redirectUrl URL страницы, на которую попадет пользователь после оплаты * @param webhookUrl URL, куда наш сервер направит запрос, чтобы оповестить ваш сервер об успешной оплате * @param data Cюда можно поместить любые полезные данных. Ограничение - 100 символов. * @returns Ссылка на страницу оплаты, на которую нужно перенаправить пользователя */ initPayment = async ({ items, redirectUrl, webhookUrl, data }) => { if (data && data.length > 100) throw new Error('Data не может быть длиннее 100 символов'); for (const item of items) { if (item.name.length < 3 || item.name.length > 64) throw new Error('Название товара не может быть меньше 3 и больше 64 символов'); if (item.count < 1 || item.count > 9999) throw new Error('Количество товара не может быть меньше 1 и больше 9999'); if (item.price < 1 || item.price > 1728) throw new Error('Цена товара не может быть меньше 1 и больше 1728'); if (item.comment && item.comment.length > 100) throw new Error('Комментарий не может быть длиннее 100 символов'); } return this.requestAPI('POST', 'payments', { items, redirectUrl, webhookUrl, data }); }; /** * Проверка валидности вебхука * @param body Все данные, пришедшие по вебхуку * @param hashHeader Значение хедера `X-Body-Hash` */ validateHash = (body, hashHeader) => { return (0, crypto_1.timingSafeEqual)(Buffer.from((0, crypto_1.createHmac)('sha256', this.token) .update(typeof body === 'string' ? body : JSON.stringify(body)) .digest('base64')), Buffer.from(hashHeader)); }; } exports.SPWorlds = SPWorlds;