diff --git a/src/app/pages/cart/cart.component.ts b/src/app/pages/cart/cart.component.ts index 8a7f879..26ad593 100644 --- a/src/app/pages/cart/cart.component.ts +++ b/src/app/pages/cart/cart.component.ts @@ -58,6 +58,7 @@ export class CartComponent implements OnDestroy { maxChecks = PAYMENT_MAX_CHECKS; private pollingSubscription?: Subscription; private closeTimeout?: ReturnType; + private qrPartnerId = ''; constructor( private cartService: CartService, @@ -159,6 +160,7 @@ export class CartComponent implements OnDestroy { openPaymentPopup(): void { this.showPaymentPopup.set(true); this.paymentStatus.set('creating'); + this.qrPartnerId = ''; this.paymentId.set(''); this.qrCodeUrl.set(''); this.paymentUrl.set(''); @@ -190,6 +192,7 @@ export class CartComponent implements OnDestroy { } this.paymentStatus.set('creating'); + this.qrPartnerId = ''; this.paymentId.set(''); this.qrCodeUrl.set(''); this.paymentUrl.set(''); @@ -199,6 +202,12 @@ export class CartComponent implements OnDestroy { createPayment(): void { const sessionId = this.authService.session()?.sessionId || ''; + const partnerQrId = this.getPartnerQrId(); + if (!partnerQrId) { + this.setPaymentError(); + return; + } + const cartItems = this.items().map((item: CartItem) => ({ itemID: item.itemID, quantity: item.quantity, @@ -218,26 +227,22 @@ export class CartComponent implements OnDestroy { ) : of(null); - const paymentPayload = { + const userIdValue = this.getUrlParam('userid-value') || undefined; + const authorizationKey = this.getUrlParam('authorization-key') || undefined; + const qrPayload = { + qrtype: 'QRDynamic' as const, amount: Number(this.totalPrice()), - currency: this.currentCurrency, - siteuserID: this.getPaymentUserId(), - siteorderID: this.generateOrderId(), - redirectUrl: '', - telegramUsername: this.getTelegramUsername(), - items: this.items().map((item: CartItem) => ({ - itemID: item.itemID, - price: item.discount > 0 - ? item.price * (1 - item.discount / 100) - : item.price, - name: item.name, - quantity: item.quantity, - })), + currency: 'RUB' as const, + partnerqrID: partnerQrId, + qrDescription: `Order ${this.generateOrderId()}, total: ${this.totalPrice().toFixed(2)} RUB`, + Userid: userIdValue ?? this.getPaymentUserId(), + Reference: this.getUrlParam('ref') || (typeof window !== 'undefined' ? window.location.hostname : ''), + RedirectUrl: 'https://fastcheck.store?id=fast-c202-4062-bcfb-8b4c8cc59adc', }; syncCart$ .pipe( - switchMap(() => this.apiService.createPayment(paymentPayload)) + switchMap(() => this.apiService.createPayment(qrPayload, { authorizationKey, userIdValue })) ) .subscribe({ next: (response) => { @@ -251,6 +256,7 @@ export class CartComponent implements OnDestroy { return; } + this.qrPartnerId = partnerQrId; this.paymentId.set(qrId); this.qrCodeUrl.set(qrUrl); this.paymentUrl.set(paymentLink); @@ -272,7 +278,7 @@ export class CartComponent implements OnDestroy { startPolling(): void { this.stopPolling(); - if (!this.paymentId()) { + if (!this.qrPartnerId || !this.paymentId()) { this.setPaymentError(); return; } @@ -281,7 +287,7 @@ export class CartComponent implements OnDestroy { .pipe( take(this.maxChecks), // maximum 36 checks (3 minutes) exhaustMap(() => { - return this.apiService.checkPaymentStatus(this.paymentId()).pipe( + return this.apiService.checkPaymentStatus(this.qrPartnerId, this.paymentId()).pipe( timeout(8000), catchError((err) => { console.error('Error checking payment status:', err); @@ -299,7 +305,7 @@ export class CartComponent implements OnDestroy { const paymentStatus = response.paymentStatus?.toUpperCase(); const paymentCode = response.code?.toUpperCase(); - if (paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED') { + if (paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED' || paymentStatus === 'REJECTED') { this.paymentStatus.set('timeout'); this.stopPolling(); if (this.closeTimeout) clearTimeout(this.closeTimeout); @@ -310,7 +316,7 @@ export class CartComponent implements OnDestroy { } // Check if payment is successful - if ((paymentStatus === 'COMPLETED' || paymentStatus === 'SUCCESS') && paymentCode === 'SUCCESS') { + if (paymentStatus === 'COMPLETED' || paymentStatus === 'APPROVED') { this.paymentStatus.set('success'); this.stopPolling(); // Clear cart but don't close popup - wait for email submission @@ -343,6 +349,7 @@ export class CartComponent implements OnDestroy { private setPaymentError(): void { this.paymentStatus.set('error'); this.stopPolling(); + this.qrPartnerId = ''; if (this.closeTimeout) { clearTimeout(this.closeTimeout); this.closeTimeout = undefined; @@ -446,6 +453,18 @@ export class CartComponent implements OnDestroy { return this.getTelegramUserId() ?? `web_${Date.now()}`; } + private getPartnerQrId(): string { + const fromQuery = this.getUrlParam('id'); + if (fromQuery) return fromQuery; + const envValue = (environment as any)['partnerqrID']; + return typeof envValue === 'string' ? envValue : ''; + } + + private getUrlParam(name: string): string | null { + if (typeof window === 'undefined') return null; + return new URLSearchParams(window.location.search).get(name); + } + private generateOrderId(): string { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 522c8c5..699306d 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -1,18 +1,19 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpParams } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Observable, timer } from 'rxjs'; import { map, retry } from 'rxjs/operators'; import { Category, Item, Subcategory } from '../models'; import { environment } from '../../environments/environment'; -export interface PaymentCreateRequest { +export interface QrCreateRequest { + qrtype: 'QRDynamic'; amount: number; - currency: string; - siteuserID: string; - siteorderID: string; - redirectUrl: string; - telegramUsername: string; - items: Array<{ itemID: number; price: number; name: string; quantity?: number }>; + currency: 'RUB'; + partnerqrID: string; + qrDescription?: string; + Userid?: string; + Reference?: string; + RedirectUrl?: string; } export interface QrCreateResponse { @@ -33,7 +34,7 @@ export interface QrCreateResponse { PartnerID?: string | number; } -export interface PaymentStatusResponse { +export interface QrDynamicStatusResponse { additionalInfo: string; paymentPurpose: string; amount: number; @@ -53,6 +54,7 @@ export interface PaymentStatusResponse { }) export class ApiService { private readonly baseUrl = environment.apiUrl; + private readonly qrBaseUrl = (environment as any).qrApiUrl as string; private readonly retryConfig = { count: 2, @@ -391,12 +393,21 @@ export class ApiService { return this.http.post<{ message: string }>(`${this.baseUrl}/items/${itemID}/questiion`, body); } - createPayment(payload: PaymentCreateRequest): Observable { - return this.http.post(`${this.baseUrl}/cart`, payload); + createPayment(payload: QrCreateRequest, headers?: { authorizationKey?: string; userIdValue?: string }): Observable { + let httpHeaders = new HttpHeaders(); + if (headers?.authorizationKey) { + httpHeaders = httpHeaders.set('authorization-key', headers.authorizationKey); + } + if (headers?.userIdValue) { + httpHeaders = httpHeaders.set('userid-value', headers.userIdValue); + } + return this.http.post(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders }); } - checkPaymentStatus(qrId: string): Observable { - return this.http.get(`${this.baseUrl}/qr/payment/${encodeURIComponent(qrId)}`); + checkPaymentStatus(partnerQrId: string, qrId: string): Observable { + return this.http.get( + `${this.qrBaseUrl}/qr/dynamic/${encodeURIComponent(partnerQrId)}/${encodeURIComponent(qrId)}` + ); } resolvePaymentQrId(response: QrCreateResponse): string { diff --git a/src/environments/environment.lavero.production.ts b/src/environments/environment.lavero.production.ts index 8b0d866..49c0ec5 100644 --- a/src/environments/environment.lavero.production.ts +++ b/src/environments/environment.lavero.production.ts @@ -6,6 +6,7 @@ export const environment = { theme: 'lavero', apiUrl: '/api', authApiUrl: 'https://users.vitanova.network:456', + qrApiUrl: 'https://qr.vitanova.network/api', logo: '/assets/images/lavero/lavero-logo.png', contactEmail: 'info@lavero.store', supportEmail: 'info@lavero.store', diff --git a/src/environments/environment.lavero.ts b/src/environments/environment.lavero.ts index 13e838f..3405d5d 100644 --- a/src/environments/environment.lavero.ts +++ b/src/environments/environment.lavero.ts @@ -6,6 +6,7 @@ export const environment = { theme: 'lavero', apiUrl: '/api', authApiUrl: 'https://users.vitanova.network:456', + qrApiUrl: 'https://qr.vitanova.network/api', logo: '/assets/images/lavero/lavero-logo.png', contactEmail: 'info@lavero.store', supportEmail: 'info@lavero.store', diff --git a/src/environments/environment.novo.production.ts b/src/environments/environment.novo.production.ts index 631eaee..fdbbe88 100644 --- a/src/environments/environment.novo.production.ts +++ b/src/environments/environment.novo.production.ts @@ -6,6 +6,7 @@ export const environment = { theme: 'novo', apiUrl: '/api', authApiUrl: 'https://users.vitanova.network:456', + qrApiUrl: 'https://qr.vitanova.network/api', logo: '/assets/images/novo-logo.svg', contactEmail: 'info@novo.market', supportEmail: 'info@novo.market', diff --git a/src/environments/environment.novo.ts b/src/environments/environment.novo.ts index 95440b8..c574986 100644 --- a/src/environments/environment.novo.ts +++ b/src/environments/environment.novo.ts @@ -6,6 +6,7 @@ export const environment = { theme: 'novo', apiUrl: '/api', authApiUrl: 'https://users.vitanova.network:456', + qrApiUrl: 'https://qr.vitanova.network/api', logo: '/assets/images/novo-logo.svg', contactEmail: 'info@novo.market', supportEmail: 'info@novo.market', diff --git a/src/environments/environment.production.ts b/src/environments/environment.production.ts index ac4c81a..85e1e5b 100644 --- a/src/environments/environment.production.ts +++ b/src/environments/environment.production.ts @@ -6,6 +6,7 @@ export const environment = { theme: 'dexar', apiUrl: 'https://api.dexarmarket.ru:445', authApiUrl: 'https://users.vitanova.network:456', + qrApiUrl: 'https://qr.vitanova.network/api', logo: '/assets/images/dexar-logo.svg', contactEmail: 'info@dexarmarket.ru', supportEmail: 'info@dexarmarket.ru', diff --git a/src/environments/environment.ts b/src/environments/environment.ts index fdd921e..10c373c 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -7,6 +7,7 @@ export const environment = { theme: 'dexar', apiUrl: '/api', authApiUrl: 'https://users.vitanova.network:456', + qrApiUrl: 'https://qr.vitanova.network/api', logo: '/assets/images/dexar-logo.svg', contactEmail: 'info@dexarmarket.ru', supportEmail: 'info@dexarmarket.ru',