SHA256
Wallet-session pairing и browser plugin wallet, оплаты пока не работают
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import { importPkcs8Ed25519, signBase64 } from './crypto-utils.js';
|
||||
import { WsJsonClient } from './ws-client.js';
|
||||
|
||||
const SESSION_TYPE_WALLET = 50;
|
||||
|
||||
function normalizeServerUrl(url) {
|
||||
const value = String(url || '').trim();
|
||||
if (!value) return 'wss://shineup.me/ws';
|
||||
if (value.startsWith('ws://') || value.startsWith('wss://')) return value;
|
||||
if (value.startsWith('http://') || value.startsWith('https://')) {
|
||||
const parsed = new URL(value);
|
||||
parsed.protocol = parsed.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
if (!parsed.pathname || parsed.pathname === '/') parsed.pathname = '/ws';
|
||||
return parsed.toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function opError(op, response) {
|
||||
const payload = response?.payload || {};
|
||||
const message = payload?.message || response?.message || payload?.error || response?.error || 'Unknown server error';
|
||||
const code = String(payload?.code || response?.code || payload?.error || response?.error || 'UNKNOWN').toUpperCase();
|
||||
const error = new Error(`${op}: ${message} (${code})`);
|
||||
error.op = op;
|
||||
error.code = code;
|
||||
error.status = response?.status || 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
export class ShineApiClient {
|
||||
constructor(serverUrl) {
|
||||
this.serverUrl = normalizeServerUrl(serverUrl);
|
||||
this.ws = new WsJsonClient(this.serverUrl);
|
||||
}
|
||||
|
||||
async getUser(login) {
|
||||
const response = await this.ws.request('GetUser', { login: String(login || '').trim() });
|
||||
if (response.status !== 200) throw opError('GetUser', response);
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async startEspPairing({ login, passwordHash, requesterSessionKey, payloadType = 1 }) {
|
||||
const response = await this.ws.request('StartEspPairing', {
|
||||
login: String(login || '').trim(),
|
||||
passwordHash: String(passwordHash || '').trim(),
|
||||
requesterSessionKey: String(requesterSessionKey || '').trim(),
|
||||
requesterSessionType: SESSION_TYPE_WALLET,
|
||||
requesterClientPlatform: 'Chrome Extension Wallet',
|
||||
payloadType: Number(payloadType) || 1,
|
||||
});
|
||||
if (response.status !== 200) throw opError('StartEspPairing', response);
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async getEspPairingStatus(pairingId) {
|
||||
const response = await this.ws.request('GetEspPairingStatus', {
|
||||
pairingId: String(pairingId || '').trim(),
|
||||
});
|
||||
if (response.status !== 200) throw opError('GetEspPairingStatus', response);
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async cancelEspPairing(pairingId, requesterSessionKey) {
|
||||
const response = await this.ws.request('CancelEspPairing', {
|
||||
pairingId: String(pairingId || '').trim(),
|
||||
requesterSessionKey: String(requesterSessionKey || '').trim(),
|
||||
});
|
||||
if (response.status !== 200) throw opError('CancelEspPairing', response);
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async resumeSession(sessionRecord) {
|
||||
const login = String(sessionRecord?.login || '').trim();
|
||||
const sessionId = String(sessionRecord?.sessionId || '').trim();
|
||||
const sessionKey = String(sessionRecord?.sessionKey || '').trim();
|
||||
const sessionPrivPkcs8 = String(sessionRecord?.sessionPrivPkcs8 || '').trim();
|
||||
if (!login || !sessionId || !sessionKey || !sessionPrivPkcs8) {
|
||||
throw new Error('Сохранённая wallet-session неполная');
|
||||
}
|
||||
|
||||
const privateKey = await importPkcs8Ed25519(sessionPrivPkcs8);
|
||||
const challengeResp = await this.ws.request('SessionChallenge', { sessionId });
|
||||
if (challengeResp.status !== 200) throw opError('SessionChallenge', challengeResp);
|
||||
const nonce = challengeResp?.payload?.nonce;
|
||||
if (!nonce) throw new Error('SessionChallenge: сервер не вернул nonce');
|
||||
|
||||
const timeMs = Date.now();
|
||||
const preimage = `SESSION_LOGIN:${sessionId}:${timeMs}:${nonce}`;
|
||||
const signatureB64 = await signBase64(privateKey, preimage);
|
||||
const loginResp = await this.ws.request('SessionLogin', {
|
||||
sessionId,
|
||||
sessionKey,
|
||||
timeMs,
|
||||
signatureB64,
|
||||
sessionType: Number(sessionRecord?.sessionType || SESSION_TYPE_WALLET) || SESSION_TYPE_WALLET,
|
||||
clientPlatform: 'Chrome Extension Wallet',
|
||||
clientInfo: 'SHiNE Browser Plugin Wallet',
|
||||
});
|
||||
if (loginResp.status !== 200) throw opError('SessionLogin', loginResp);
|
||||
|
||||
return {
|
||||
login,
|
||||
sessionId,
|
||||
storagePwd: String(loginResp?.payload?.storagePwd || '').trim(),
|
||||
};
|
||||
}
|
||||
|
||||
close() {
|
||||
this.ws.close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user