api settings

This commit is contained in:
sdarbinyan
2026-05-13 10:45:18 +04:00
parent 985ec5db7f
commit e9b50078fe
6 changed files with 116 additions and 4 deletions

View File

@@ -109,7 +109,9 @@
"session_failed": "Could not create a session. Please try again.", "session_failed": "Could not create a session. Please try again.",
"payment_failed": "Could not process the payment. Check the code and try again.", "payment_failed": "Could not process the payment. Check the code and try again.",
"invalid_code": "Invalid code. Please check and try again.", "invalid_code": "Invalid code. Please check and try again.",
"invalid_amount": "Please enter a valid amount." "invalid_amount": "Please enter a valid amount.",
"settings_failed": "Could not load settings. You can continue manually.",
"settings_missing_id": "Partner id is missing. You can continue manually."
}, },
"common": { "common": {
"secure": "Secure connection" "secure": "Secure connection"

View File

@@ -109,7 +109,9 @@
"session_failed": "Չհաջողվեց ստեղծել նիստ: Կրկին փորձեք:", "session_failed": "Չհաջողվեց ստեղծել նիստ: Կրկին փորձեք:",
"payment_failed": "Չհաջողվեց մշակել վճարումը: Ստուգեք կոդը և կրկին փորձեք:", "payment_failed": "Չհաջողվեց մշակել վճարումը: Ստուգեք կոդը և կրկին փորձեք:",
"invalid_code": "Սխալ կոդ: Ստուգեք և կրկին մուտքագրեք:", "invalid_code": "Սխալ կոդ: Ստուգեք և կրկին մուտքագրեք:",
"invalid_amount": "Մուտքագրեք ճիշտ գումար:" "invalid_amount": "Մուտքագրեք ճիշտ գումար:",
"settings_failed": "Չհաջողվեց բեռնել կարգավորումները: Կարող եք շարունակել ձեռքով:",
"settings_missing_id": "Գործընկերոջ ID-ն բացակայում է: Կարող եք շարունակել ձեռքով:"
}, },
"common": { "common": {
"secure": "Անվտանգ կապ" "secure": "Անվտանգ կապ"

View File

@@ -109,7 +109,9 @@
"session_failed": "Не удалось создать сессию. Попробуйте ещё раз.", "session_failed": "Не удалось создать сессию. Попробуйте ещё раз.",
"payment_failed": "Не удалось принять платёж. Проверьте код и попробуйте снова.", "payment_failed": "Не удалось принять платёж. Проверьте код и попробуйте снова.",
"invalid_code": "Неверный код. Проверьте и введите снова.", "invalid_code": "Неверный код. Проверьте и введите снова.",
"invalid_amount": "Введите корректную сумму." "invalid_amount": "Введите корректную сумму.",
"settings_failed": "Не удалось загрузить настройки. Можно продолжить вручную.",
"settings_missing_id": "Отсутствует идентификатор партнёра. Можно продолжить вручную."
}, },
"common": { "common": {
"secure": "Защищённое соединение" "secure": "Защищённое соединение"

View File

@@ -9,3 +9,11 @@
export const FASTCHECK_API = isDevMode() export const FASTCHECK_API = isDevMode()
? '/proxy/fastcheck' ? '/proxy/fastcheck'
: 'https://api.fastcheck.store'; : 'https://api.fastcheck.store';
/**
* Base URL for the QR/SBP backend (qr.vitanova.network).
* Used for the /api/settings endpoint that may return active check data on load.
*/
export const QR_VITANOVA_API = isDevMode()
? '/proxy/qr-vitanova/api'
: 'https://qr.vitanova.network/api';

View File

@@ -11,6 +11,10 @@
<div class="card__body"> <div class="card__body">
<!-- @if (settingsError()) {
<p class="field__hint" role="status">{{ settingsError() }}</p>
} -->
<!-- Fastcheck number + new --> <!-- Fastcheck number + new -->
<div class="field"> <div class="field">
<label class="field__label" for="fcNumber"> <label class="field__label" for="fcNumber">

View File

@@ -2,7 +2,7 @@
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { FastcheckService } from '../../fastcheck.service'; import { FastcheckService } from '../../fastcheck.service';
import { FASTCHECK_API } from '../../api'; import { FASTCHECK_API, QR_VITANOVA_API } from '../../api';
import { TranslatePipe } from '../../translate/translate.pipe'; import { TranslatePipe } from '../../translate/translate.pipe';
import { TranslationService } from '../../translate/translation.service'; import { TranslationService } from '../../translate/translation.service';
@@ -26,6 +26,27 @@ interface CheckFastcheckResponse {
firetransactionID: string; firetransactionID: string;
} }
/**
* Response of GET /api/settings?id=<partnerId>.
* Shape is defensive — backend may include min/max limits and/or an
* already-active fastcheck (or QR) for this partner so the page can autofill.
*/
interface SettingsResponse {
minAmount?: number;
maxAmount?: number;
// Possible active fastcheck data — backend may use different casing.
fastcheck?: string;
fastcheckNumber?: string;
code?: string;
amount?: number;
note?: string;
status?: string;
// QR-side info (not rendered on this page yet, but accepted for future use).
qrId?: string;
qrUrl?: string;
payload?: string;
}
@Component({ @Component({
selector: 'app-fastcheck-page', selector: 'app-fastcheck-page',
imports: [FormsModule, TranslatePipe], imports: [FormsModule, TranslatePipe],
@@ -58,6 +79,10 @@ export class FastcheckPage {
return `https://qr.vitanova.network/?id=${encodeURIComponent(id)}`; return `https://qr.vitanova.network/?id=${encodeURIComponent(id)}`;
}); });
// Non-blocking settings hint shown above the form when /settings fails.
settingsError = signal<string>('');
settingsLoaded = signal<boolean>(false);
popupOpen = signal<boolean>(false); popupOpen = signal<boolean>(false);
popupLoading = signal<boolean>(false); popupLoading = signal<boolean>(false);
popupError = signal<string>(''); popupError = signal<string>('');
@@ -120,6 +145,75 @@ export class FastcheckPage {
this.fastcheckNumber.set(masked); this.fastcheckNumber.set(masked);
if (digits.length === 18) this.lookupFastcheck(masked); if (digits.length === 18) this.lookupFastcheck(masked);
} }
// Always call settings on each load — may return active check data to autofill.
this.loadSettings(!!created || !!iidParam);
}
/**
* GET /api/settings?id=<partnerId>. Idempotent — applies response to the
* current UI without creating any new check. Errors are non-blocking.
*
* @param alreadyAutofilled - true when constructor already populated the form
* from nav state / iid; in that case settings must not overwrite it.
*/
private loadSettings(alreadyAutofilled: boolean): void {
const id = this.partnerId();
if (!id) {
this.settingsError.set(this.t('errors.settings_missing_id'));
this.settingsLoaded.set(true);
return;
}
const url = `${QR_VITANOVA_API}/settings?id=${encodeURIComponent(id)}`;
this.http.get<SettingsResponse>(url).subscribe({
next: (res) => {
this.settingsLoaded.set(true);
this.settingsError.set('');
this.applySettings(res ?? {}, alreadyAutofilled);
},
error: () => {
// Non-blocking: keep manual mode available.
this.settingsLoaded.set(true);
this.settingsError.set(this.t('errors.settings_failed'));
}
});
}
/** Apply settings response to UI state without creating any new check. */
private applySettings(res: SettingsResponse, alreadyAutofilled: boolean): void {
// Active check autofill — only if user hasn't already got data on screen.
if (alreadyAutofilled) return;
const rawNumber = res.fastcheck ?? res.fastcheckNumber ?? '';
if (rawNumber) {
const digits = String(rawNumber).replace(/\D/g, '').slice(0, 18);
if (digits.length > 0) {
const groups: string[] = [];
for (let i = 0; i < digits.length; i += 6) groups.push(digits.slice(i, i + 6));
this.fastcheckNumber.set(groups.join('-'));
if (digits.length === 18) {
// Trigger the regular lookup so amount/code-enabled stay consistent.
this.lookupFastcheck(groups.join('-'));
}
}
}
if (typeof res.amount === 'number') {
this.fastcheckAmount.set(res.amount);
}
if (typeof res.code === 'string' && res.code) {
const codeDigits = res.code.replace(/\D/g, '').slice(0, 6);
this.fastcheckCode.set(codeDigits);
this.codeEnabled.set(true);
}
// Final statuses end the flow — no polling, no new requests.
const status = (res.status ?? '').toUpperCase();
if (status === 'COMPLETED' || status === 'APPROVED') {
this.paid.set(true);
}
} }
pay(): void { pay(): void {