api settings
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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": "Անվտանգ կապ"
|
||||||
|
|||||||
@@ -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": "Защищённое соединение"
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user