Compare commits
21 Commits
957321ae1e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a52fd07273 | |||
| fe7fea151a | |||
| e9acbd4898 | |||
| 4841cdf90d | |||
| c2a0675c79 | |||
| e62afe07eb | |||
| 926afc5691 | |||
| 45769ca817 | |||
| 02a33e9b14 | |||
| 9c96370235 | |||
| 9cbb6660f8 | |||
| b1ffd577c5 | |||
| bee56afedc | |||
| ce2c9c42fe | |||
| 17dfad5eaa | |||
| abb4f7b849 | |||
| 097064281a | |||
| 0330e0a212 | |||
| 5147d05ea2 | |||
| 14d9642568 | |||
| 6e7527cf1e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,8 +5,10 @@
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
/dist
|
||||
|
||||
# Local-only docs and scratch (not for publishing)
|
||||
/docs/
|
||||
changes.txt
|
||||
api.txt
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
|
||||
<div class="card__header">
|
||||
<div class="sbp-logo">
|
||||
<img src="https://sbp.nspk.ru/storage/settings/common/logo/0645d335-8b62-43a1-9a33-0d4c9d1dc0e0.svg" alt="СБП" />
|
||||
<img src="public/sbp.svg" alt="СБП" />
|
||||
</div>
|
||||
<h1 class="card__title">Оплата через СБП</h1>
|
||||
<p class="card__subtitle">Система быстрых платежей</p>
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
eFastcheck.store
|
||||
General Information
|
||||
Information exchange with the Fastcheck server is realized via RESTful API. All requests to the server must be executed via HTTPS using GET||POST||PUT||DELETE requests to the given ROOT address. Body of requests must be in JSON format. All not public requests must be signed by the client and the public key must be sent to the server for client identification and sign checking.
|
||||
|
||||
|
||||
|
||||
|
||||
Check if server is available
|
||||
Client needs to periodically check if the server is available by sending “ping” to the client. On error corresponding message must be shown.
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type GET
|
||||
Path /ping
|
||||
Request Parameters:
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
"message": "pong",
|
||||
}
|
||||
________________
|
||||
|
||||
|
||||
Create new websession
|
||||
Creates a new websession for qr generation. By timeout a new websession must be requested, after the user shows some activity (click on qr).
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type GET
|
||||
Path /websession
|
||||
Request Parameters:
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
"sessionId": “1AF3781BF6B94604B771AEA1D44FA63A”
|
||||
"userId" : "",
|
||||
"expires" : "sessionId",
|
||||
"userSessionId": "",
|
||||
"Status": false
|
||||
}
|
||||
________________
|
||||
|
||||
|
||||
Check websession status
|
||||
Check if the user is already logged in. a new websession for qr generation. By timeout a new websession must be requested, after the user shows some activity (click on qr).
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type GET
|
||||
Path /websession/:webSessionID
|
||||
Request Parameters:
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
"sessionId": “1AF3781BF6B94604B771AEA1D44FA63A”,
|
||||
"userId" : "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo",
|
||||
"expires" : "sessionId",
|
||||
"userSessionId": "8A94EFEFD003426A9B456C48CAC99BE6",
|
||||
"Status": true
|
||||
}
|
||||
________________
|
||||
Delete websession status
|
||||
Delete the session to log out from the system.
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type DELETE
|
||||
Path /websession/:webSessionID
|
||||
Request Parameters:
|
||||
{
|
||||
"sessionId": “1AF3781BF6B94604B771AEA1D44FA63A”
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
}
|
||||
________________
|
||||
|
||||
|
||||
Check Fastcheck status
|
||||
Check if fastcheck exists and get the amount assigned to check.
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type GET
|
||||
Path /fastcheck
|
||||
|
||||
|
||||
Request Parameters:
|
||||
{
|
||||
"fastcheck": “1234-5678-0001”,
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
"fastcheck": "1234-5678-0001",
|
||||
"expiration": 2021-07-07T09:08:18Z ,
|
||||
"Status": true
|
||||
}
|
||||
________________
|
||||
New Fastcheck
|
||||
Create a fastcheck for a given amount. The Users must have a sufficient amount on the balance.
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type POST
|
||||
Path /fastcheck
|
||||
HEADER: Authorization - {"sessionID": "1AF3781BF6B94604B771AEA1D44FA63A"}
|
||||
Request Parameters:
|
||||
{
|
||||
"amount": 158000,
|
||||
"currency": "RUB"
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
"fastcheck": "1234-5678-0001",
|
||||
"expiration": 2021-07-07T09:08:18Z ,
|
||||
"code": "5864",
|
||||
"Status": true
|
||||
}
|
||||
________________
|
||||
Accept Fastcheck
|
||||
Accept fastcheck to the user balance.
|
||||
Protocol: https
|
||||
Root Path: api.Fastcheck.store
|
||||
Type POST
|
||||
Path /fastcheck
|
||||
HEADER: Authorization - {"sessionID": "1AF3781BF6B94604B771AEA1D44FA63A"}
|
||||
Request Parameters:
|
||||
{
|
||||
"fastcheck": "1234-5678-0001",
|
||||
"code": "5864"
|
||||
}
|
||||
Response (404-ERROR):
|
||||
{
|
||||
"message": "not authorized"
|
||||
}
|
||||
Response (200-OK):
|
||||
{
|
||||
"message": "ok"
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
General Information
|
||||
Information exchange with the SBP server is realized via RESTful API. All requests to the server must be executed via HTTPS using GET||POST||PUT||DELETE requests to the given ROOT address. Body of requests must be in JSON format. All not public requests must be signed by the client and the public key must be sent to the server for client identification and sign checking.
|
||||
|
||||
|
||||
Header:
|
||||
“Authorization”: {JSON WITH KEY AND PARTNERID}
|
||||
|
||||
|
||||
Check if server is available
|
||||
Client needs to periodically check if the server is available by sending “ping” to the client. On error corresponding message must be shown.
|
||||
Protocol: https
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type GET
|
||||
Path /ping
|
||||
Request Parameters:
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
Response (Error):
|
||||
{
|
||||
"message": "pong",
|
||||
"status": "Wrong Header"
|
||||
}
|
||||
Response (OK):
|
||||
{
|
||||
"message": "pong",
|
||||
"status": "Correct Header"
|
||||
}
|
||||
________________
|
||||
|
||||
|
||||
Create New QR code
|
||||
Create New QR for payment via SBP
|
||||
Protocol: https
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type POST
|
||||
Path /qr
|
||||
Request Parameters:
|
||||
{
|
||||
"amount": 10.00, //amount from 10Rub to 499.000 Rub
|
||||
"qrDescription": "Item description",
|
||||
"order": "540", //orderid at partner’s platform
|
||||
"partnerID": 102 //same as in header
|
||||
"Phonemask": 79xxxx66265 //User phone number mask, needed only for crypto based operations. Payment will be accepted only from phone numbers corresponding to the mask
|
||||
"Namelastname": Hakxx Sargxxxx /Mask for User name, lastname in cyrilic, needed only for crypto based operations. Payment will be accepted only from the user corresponding to that mask.
|
||||
}
|
||||
|
||||
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "wrong key"
|
||||
}
|
||||
Response =200(OK):
|
||||
{
|
||||
"qrId": "BD10002CI1V3JP1T8QR8TIQ8K35RBVQB",
|
||||
"qrStatus": "NEW",
|
||||
"qrExpirationDate": "2025-11-20T10:10:44Z",
|
||||
"Payload": "https://qr.nspk.ru/BD10002CI1V3JP1T8QR8TIQ8K35RBVQB?type=02&bank=100000000007&sum=1000&cur=RUB&crc=8ACC",
|
||||
"qrUrl": "https://e-commerce.raiffeisen.ru/api/sbp/v1/qr/BD10002CI1V3JP1T8QR8TIQ8K35RBVQB/image"
|
||||
}
|
||||
________________
|
||||
|
||||
|
||||
Check Dynamic QR code
|
||||
Check QR status
|
||||
Protocol: https
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type GET
|
||||
Path /qr/dynamic/{qrId}
|
||||
|
||||
|
||||
Request Parameters:
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "Error from the bank "
|
||||
}
|
||||
Response =200(OK):
|
||||
{
|
||||
`json:"nspkID"` //": "AD100060JFQF8FSB9Q28FFL88IH6SST0" `json:"amount"` // "1235"
|
||||
`json:"currency" // "RUB"
|
||||
`json:"order"` // "126" partner order id PaymentDetails
|
||||
`json:"paymentDetails"` // "Назначение платежа 2",
|
||||
`json:"qrType"` //"QRDynamic",
|
||||
`json:"qrExpirationDate"` //: "2025-11-22T09:14:38+03:00" `json:"sbpBank"` // "raiffeisen"
|
||||
`json:"sbpMerchant"` //"Dexar"
|
||||
`json:"sbpMerchantId"` //"", uint64
|
||||
`json:"sbpOperationId"` //0 Status
|
||||
`json:"status"` //": "NEW", "APPROVED", "REJECTED", "COMPLETED"
|
||||
`json:"nspkurl"` //"https://qr.nspk.ru/AD100060JFQF8FSB9Q28FFL88IH6SST0
|
||||
`json:"statusurl"` // "https://partner.com/1234321/status" url for checking QR `json:"redirectUrl"` //"https://fastcheck.store/"
|
||||
`json:"qrDescription"` //"QR для оплаты заказа"
|
||||
`json:"additionalInfo"` // TTL
|
||||
`json:"TTL"` //10 timeout in minutes
|
||||
`json:"callbackUrl"` // https://partner.com/1234321 callback after QR get paid
|
||||
`json:"retry"` //0 retry count for calling partner
|
||||
`json:"partnerID"` //103 Partner created QR PartnerqrID `json:"partnerqrID"` //QR ID in partner system RequestIP
|
||||
`json:"requestIP"` //IP address of client requested QR
|
||||
}
|
||||
________________
|
||||
|
||||
|
||||
Check Static QR code
|
||||
Get all qr-s paid by static QR for today, skipping already read qr codes
|
||||
Protocol: https
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type GET
|
||||
Path /qr/static/{qrId}?skip=25
|
||||
Request Parameters:
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Response =200(OK):
|
||||
[{
|
||||
`json:"nspkID"` //": "AD100060JFQF8FSB9Q28FFL88IH6SST0" `json:"amount"` // "1235"
|
||||
`json:"currency" // "RUB"
|
||||
`json:"order"` // "126" partner order id PaymentDetails
|
||||
`json:"paymentDetails"` // "Назначение платежа 2",
|
||||
`json:"qrType"` //"QRDynamic",
|
||||
`json:"qrExpirationDate"` //: "2025-11-22T09:14:38+03:00" `json:"sbpBank"` // "raiffeisen"
|
||||
`json:"sbpMerchant"` //"Dexar"
|
||||
`json:"sbpMerchantId"` //"", uint64
|
||||
`json:"sbpOperationId"` //0 Status
|
||||
`json:"status"` //": "NEW", "APPROVED", "REJECTED", "COMPLETED"
|
||||
`json:"nspkurl"` //"https://qr.nspk.ru/AD100060JFQF8FSB9Q28FFL88IH6SST0
|
||||
`json:"statusurl"` // "https://partner.com/1234321/status" url for checking QR `json:"redirectUrl"` //"https://fastcheck.store/"
|
||||
`json:"qrDescription"` //"QR для оплаты заказа"
|
||||
`json:"additionalInfo"` // TTL
|
||||
`json:"TTL"` //10 timeout in minutes
|
||||
`json:"callbackUrl"` // https://partner.com/1234321 callback after QR get paid
|
||||
`json:"retry"` //0 retry count for calling partner
|
||||
`json:"partnerID"` //103 Partner created QR PartnerqrID `json:"partnerqrID"` //QR ID in partner system RequestIP
|
||||
`json:"requestIP"` //IP address of client requested QR
|
||||
}]
|
||||
|
||||
|
||||
________________
|
||||
|
||||
|
||||
Delete QR
|
||||
Delete unused QR. If QR is not paid until expiration time, it will be automatically deleted.
|
||||
Protocol: https
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type DELETE
|
||||
Path /qr/{qrId}
|
||||
Request Parameters:
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "Error from the bank "
|
||||
|
||||
|
||||
}
|
||||
Response =200(OK):
|
||||
{
|
||||
}
|
||||
________________
|
||||
Check Partner
|
||||
Returns partner status, with balance and transactions. Each transaction id is QR code, which can be checked additionally.
|
||||
Root Path: API.VITANOVA.NETWORK
|
||||
Type Get
|
||||
Path /partners/{partnerID}
|
||||
Request Parameters:
|
||||
{
|
||||
}
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "Not authorized "
|
||||
}
|
||||
Response =200(OK):
|
||||
{
|
||||
"telegram_id": 8285633,
|
||||
"username": "ZZZ",
|
||||
"first_name": "АMAN",
|
||||
"last_name": "",
|
||||
"balance": 22,
|
||||
"transaction": [
|
||||
{
|
||||
"additionalInfo": "Ручка",
|
||||
"paymentPurpose": "Ручка",
|
||||
"amount": 22,
|
||||
"code": "SUCCESS",
|
||||
"createDate": "2025-11-22T15:57:40.925104+03:00",
|
||||
"currency": "RUB",
|
||||
"order": "8285633735_301",
|
||||
"paymentStatus": "SUCCESS",
|
||||
"qrId": "AD10004C1K9N71MN907RD56UOA0BHIBR",
|
||||
"transactionDate": "2025-11-22T15:58:14.814187+03:00",
|
||||
"transactionId": 771515533,
|
||||
"qrExpirationDate": "2025-11-22T16:12:40+03:00"
|
||||
}
|
||||
],
|
||||
"inn": 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
________________
|
||||
|
||||
|
||||
Withdraw
|
||||
Get amount from balance and Creates fastcheck, which then can be for buying usdt, transferring to bank account and to bank card. Fastcheck can be checked on site or via API only by Id, but can be used only with code.
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type POST
|
||||
Path/partners/withdraw/{partnerID}
|
||||
Request Parameters:
|
||||
{
|
||||
“amount”: 10600.00
|
||||
“currency”: “RUB”
|
||||
“partnerId: “1023454”
|
||||
“wallet”: “TBia4uHnb3oSSZm5isP284cA7Np1v15Vhi”
|
||||
“”
|
||||
“rate”:79.50
|
||||
}
|
||||
|
||||
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "Not enough amount on balance "
|
||||
}
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "Rate is not correct "
|
||||
}
|
||||
Response =200(OK):
|
||||
{
|
||||
“trxID”:”T5Mv2v8n9L7jY4k1pW3QhUoZfE9R1X3s7rY6tB0pA2C4D6E8F5H”
|
||||
}
|
||||
________________
|
||||
RATE
|
||||
Get currency exchange rate.
|
||||
Root Path: QR.VITANOVA.NETWORK
|
||||
Type GET
|
||||
Path/partners/rate
|
||||
Request Parameters:
|
||||
|
||||
|
||||
|
||||
|
||||
Response !=200(Error):
|
||||
{
|
||||
"error": "Not Authorized "
|
||||
}
|
||||
|
||||
|
||||
Response =200(OK):
|
||||
|
||||
|
||||
{
|
||||
"rate": 78.5
|
||||
}
|
||||
@@ -148,7 +148,7 @@
|
||||
|
||||
<div class="card__header">
|
||||
<div class="sbp-logo">
|
||||
<img src="https://sbp.nspk.ru/storage/settings/common/logo/0645d335-8b62-43a1-9a33-0d4c9d1dc0e0.svg" alt="СБП" />
|
||||
<img src="sbp.svg" alt="СБП" />
|
||||
</div>
|
||||
<h1 class="card__title">Оплата через СБП</h1>
|
||||
<p class="card__subtitle">Система быстрых платежей</p>
|
||||
|
||||
1
public/sbp.svg
Normal file
1
public/sbp.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 10 KiB |
@@ -1,5 +1,5 @@
|
||||
<app-site-header />
|
||||
<!-- <app-site-header /> -->
|
||||
<main class="app-main">
|
||||
<router-outlet />
|
||||
</main>
|
||||
<app-site-footer />
|
||||
<!-- <app-site-footer /> -->
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { SiteHeader } from './site-header/site-header';
|
||||
import { SiteFooter } from './site-footer/site-footer';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, SiteHeader, SiteFooter],
|
||||
imports: [RouterOutlet],
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss'
|
||||
})
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
<button type="button" class="method" [class.method--active]="payment() === 'sbp'"
|
||||
(click)="selectPayment('sbp', true)" aria-label="СБП">
|
||||
<img class="method__logo"
|
||||
src="https://sbp.nspk.ru/storage/settings/common/logo/0645d335-8b62-43a1-9a33-0d4c9d1dc0e0.svg"
|
||||
src="/sbp.svg"
|
||||
alt="СБП" />
|
||||
</button>
|
||||
<button type="button" class="method method--disabled" disabled aria-label="WeChat Pay">
|
||||
<img class="method__logo" src="/wechat-pay.svg" alt="WeChat Pay" />
|
||||
</button>
|
||||
<button type="button" class="method method--disabled" disabled aria-label="Alipay">
|
||||
<button type="button" class="method method--disabled" disabled aria-label="Alipay">
|
||||
<img class="method__logo" src="/alipay.svg" alt="Alipay" />
|
||||
</button>
|
||||
<button type="button" class="method method--disabled" disabled aria-label="Visa">
|
||||
|
||||
@@ -234,11 +234,17 @@
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 11px !important;
|
||||
font-weight: 500;
|
||||
color: #475569;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
margin: 0 auto 8px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__img {
|
||||
|
||||
@@ -9,9 +9,19 @@ type PaymentMethod = 'sbp';
|
||||
type Currency = 'RUB';
|
||||
|
||||
interface SettingsResponse {
|
||||
sbp?: boolean;
|
||||
wechat?: boolean;
|
||||
visa?: boolean;
|
||||
mastercard?: boolean;
|
||||
alipay?: boolean;
|
||||
rubles?: boolean;
|
||||
usd?: boolean;
|
||||
euro?: boolean;
|
||||
cny?: boolean;
|
||||
dram?: boolean;
|
||||
minAmount?: number;
|
||||
maxAmount?: number;
|
||||
[key: string]: unknown;
|
||||
qrTTL?: number;
|
||||
}
|
||||
|
||||
interface CreateQrResponse {
|
||||
@@ -21,13 +31,10 @@ interface CreateQrResponse {
|
||||
nspkurl?: string; // actual field name in real responses
|
||||
qrUrl?: string;
|
||||
status?: string; // e.g. "REGISTERED"
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface QrStatusResponse {
|
||||
status?: string; // "REGISTERED" | "NEW" | "APPROVED" | "REJECTED" | "COMPLETED"
|
||||
nspkurl?: string;
|
||||
nspkID?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@@ -40,6 +47,9 @@ interface QrStatusResponse {
|
||||
export class CreatePage {
|
||||
private http = inject(HttpClient);
|
||||
private i18n = inject(TranslationService);
|
||||
private readonly sites: Record<string, string> = {
|
||||
'51': 'fastcheck.store'
|
||||
};
|
||||
|
||||
private t(key: string): string { return this.i18n.translate(key); }
|
||||
|
||||
@@ -51,7 +61,6 @@ export class CreatePage {
|
||||
note = signal<string>('');
|
||||
error = signal<string>('');
|
||||
loading = signal<boolean>(false);
|
||||
settingsLoaded = signal<boolean>(false);
|
||||
|
||||
currency = signal<Currency>('RUB');
|
||||
payment = signal<PaymentMethod>('sbp');
|
||||
@@ -72,7 +81,6 @@ export class CreatePage {
|
||||
qrStatus = signal<string>('');
|
||||
paymentDone = signal<boolean>(false);
|
||||
private pollHandle: ReturnType<typeof setInterval> | null = null;
|
||||
private activeQrId = '';
|
||||
|
||||
/** Auth credentials passed by the host page as URL params. */
|
||||
private get authKey(): string {
|
||||
@@ -87,6 +95,9 @@ export class CreatePage {
|
||||
private get partnerqrID(): string {
|
||||
return new URLSearchParams(window.location.search).get('id') ?? '';
|
||||
}
|
||||
private get fromSite(): string {
|
||||
return new URLSearchParams(window.location.search).get('from') ?? '';
|
||||
}
|
||||
|
||||
get isMobile(): boolean {
|
||||
return window.innerWidth < 768;
|
||||
@@ -97,21 +108,13 @@ export class CreatePage {
|
||||
}
|
||||
|
||||
private loadSettings(): void {
|
||||
// The `id` query param is the user's id. Fetch per-user amount limits.
|
||||
// If the call fails or omits a value, keep current defaults.
|
||||
const userId = this.partnerqrID;
|
||||
if (!userId) {
|
||||
this.settingsLoaded.set(true);
|
||||
return;
|
||||
}
|
||||
const url = `${QR_VITANOVA_API}/settings?id=${encodeURIComponent(userId)}`;
|
||||
// Fetch limits from /qr/settings. If the call fails, keep defaults.
|
||||
const url = `${QR_VITANOVA_API}/qr/settings`;
|
||||
this.http.get<SettingsResponse>(url).subscribe({
|
||||
next: (s) => {
|
||||
if (typeof s?.minAmount === 'number') this.minAmount.set(s.minAmount);
|
||||
if (typeof s?.maxAmount === 'number') this.maxAmount.set(s.maxAmount);
|
||||
this.settingsLoaded.set(true);
|
||||
},
|
||||
error: () => this.settingsLoaded.set(true) // proceed with current defaults
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -149,7 +152,8 @@ export class CreatePage {
|
||||
partnerqrID,
|
||||
qrDescription: this.note().trim(),
|
||||
Userid: this.userId,
|
||||
Reference: this.reference
|
||||
Reference: this.reference,
|
||||
RedirectUrl: `https://fastcheck.store?id=fast-c202-4062-bcfb-8b4c8cc59adc`
|
||||
},
|
||||
{ headers }
|
||||
)
|
||||
@@ -167,7 +171,6 @@ export class CreatePage {
|
||||
}
|
||||
|
||||
if (qrId || nspkUrl) {
|
||||
this.activeQrId = qrId;
|
||||
const qrData = nspkUrl
|
||||
? `https://api.qrserver.com/v1/create-qr-code/?size=256x256&margin=8&data=${encodeURIComponent(nspkUrl)}`
|
||||
: (res.qrUrl ?? null);
|
||||
@@ -189,14 +192,13 @@ export class CreatePage {
|
||||
this.stopPolling();
|
||||
this.qrPolling.set(true);
|
||||
this.pollHandle = setInterval(() => {
|
||||
this.http.get<QrStatusResponse>(`${QR_VITANOVA_API}/qr/dynamic/${qrId}`)
|
||||
this.http.get<QrStatusResponse>(`${QR_VITANOVA_API}/qr/dynamic/${encodeURIComponent(this.partnerqrID)}/${qrId}`)
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
const st = res?.status ?? '';
|
||||
this.qrStatus.set(st);
|
||||
if (st === 'COMPLETED' || st === 'APPROVED') {
|
||||
this.stopPolling();
|
||||
this.paymentDone.set(true);
|
||||
this.handlePaymentSuccess(res);
|
||||
} else if (st === 'REJECTED') {
|
||||
this.stopPolling();
|
||||
this.error.set(this.t('errors.payment_failed'));
|
||||
@@ -204,7 +206,10 @@ export class CreatePage {
|
||||
}
|
||||
// REGISTERED / NEW / '' — keep polling
|
||||
},
|
||||
error: () => undefined
|
||||
error: () => {
|
||||
this.closeQr();
|
||||
this.error.set('оплата не прошла');
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
@@ -226,14 +231,52 @@ export class CreatePage {
|
||||
this.note.set(value);
|
||||
}
|
||||
|
||||
closeQr(): void {
|
||||
private handlePaymentSuccess(paidQr: QrStatusResponse): void {
|
||||
this.stopPolling();
|
||||
this.qrImageUrl.set(null);
|
||||
this.qrPolling.set(false);
|
||||
this.qrStatus.set('');
|
||||
this.paymentDone.set(false);
|
||||
if (this.pollHandle !== null) {
|
||||
clearInterval(this.pollHandle);
|
||||
this.pollHandle = null;
|
||||
this.paymentDone.set(true);
|
||||
|
||||
const id = this.partnerqrID;
|
||||
if (!id) {
|
||||
this.redirectToSource();
|
||||
return;
|
||||
}
|
||||
|
||||
this.http
|
||||
.post(`https://fastcheck.store/api/fastcheck/settings/${encodeURIComponent(id)}`, paidQr)
|
||||
.subscribe({
|
||||
next: () => this.redirectToSource(id),
|
||||
error: () => this.redirectToSource(id)
|
||||
});
|
||||
}
|
||||
|
||||
private redirectToSource(id?: string): void {
|
||||
const withId = (target: string): string => {
|
||||
if (!id) return target;
|
||||
|
||||
const normalizedTarget = /^https?:\/\//i.test(target) ? target : `https://${target}`;
|
||||
const url = new URL(normalizedTarget);
|
||||
url.searchParams.set('id', id);
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
const from = this.fromSite.trim();
|
||||
const target = this.sites[from];
|
||||
if (target) {
|
||||
window.location.href = withId(target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.history.length > 1) {
|
||||
window.history.back();
|
||||
}
|
||||
}
|
||||
|
||||
closeQr(): void {
|
||||
this.stopPolling();
|
||||
this.qrImageUrl.set(null);
|
||||
this.qrStatus.set('');
|
||||
this.paymentDone.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>fastCHECK</title>
|
||||
<title>QR Vitanova</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="theme-color" content="#2563eb">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user