diff --git a/src/app/pages/cart/cart.component.html b/src/app/pages/cart/cart.component.html index 94f4864..ac35420 100644 --- a/src/app/pages/cart/cart.component.html +++ b/src/app/pages/cart/cart.component.html @@ -147,8 +147,8 @@ diff --git a/src/app/pages/cart/cart.component.ts b/src/app/pages/cart/cart.component.ts index 26ad593..470a9ce 100644 --- a/src/app/pages/cart/cart.component.ts +++ b/src/app/pages/cart/cart.component.ts @@ -149,11 +149,6 @@ export class CartComponent implements OnDestroy { alert(this.i18n.t('cart.acceptTerms')); return; } - // Auth gate: require Telegram login before payment - if (!this.authService.isAuthenticated()) { - this.authService.requestLogin(); - return; - } this.openPaymentPopup(); } @@ -203,10 +198,6 @@ 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, @@ -227,22 +218,17 @@ export class CartComponent implements OnDestroy { ) : of(null); - 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: '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', + ...(partnerQrId && { partnerqrID: partnerQrId }), + qrDescription: this.buildQrDescription(this.generateOrderId()), }; syncCart$ .pipe( - switchMap(() => this.apiService.createPayment(qrPayload, { authorizationKey, userIdValue })) + switchMap(() => this.apiService.createSbpPayment(qrPayload)) ) .subscribe({ next: (response) => { @@ -261,11 +247,6 @@ export class CartComponent implements OnDestroy { this.qrCodeUrl.set(qrUrl); this.paymentUrl.set(paymentLink); - if (paymentLink && typeof window !== 'undefined' && window.innerWidth < 768) { - window.location.href = paymentLink; - return; - } - this.paymentStatus.set('waiting'); this.startPolling(); }, @@ -278,7 +259,7 @@ export class CartComponent implements OnDestroy { startPolling(): void { this.stopPolling(); - if (!this.qrPartnerId || !this.paymentId()) { + if (!this.paymentId()) { this.setPaymentError(); return; } @@ -287,7 +268,7 @@ export class CartComponent implements OnDestroy { .pipe( take(this.maxChecks), // maximum 36 checks (3 minutes) exhaustMap(() => { - return this.apiService.checkPaymentStatus(this.qrPartnerId, this.paymentId()).pipe( + return this.apiService.checkSbpPaymentStatus(this.paymentId()).pipe( timeout(8000), catchError((err) => { console.error('Error checking payment status:', err); @@ -302,10 +283,9 @@ export class CartComponent implements OnDestroy { return; } - const paymentStatus = response.paymentStatus?.toUpperCase(); - const paymentCode = response.code?.toUpperCase(); + const paymentStatus = (response.paymentStatus || response.status || response.code || '').toUpperCase(); - if (paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED' || paymentStatus === 'REJECTED') { + if (paymentStatus === 'FAILED' || paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED' || paymentStatus === 'REJECTED') { this.paymentStatus.set('timeout'); this.stopPolling(); if (this.closeTimeout) clearTimeout(this.closeTimeout); @@ -316,7 +296,7 @@ export class CartComponent implements OnDestroy { } // Check if payment is successful - if (paymentStatus === 'COMPLETED' || paymentStatus === 'APPROVED') { + if (paymentStatus === 'COMPLETED' || paymentStatus === 'APPROVED' || paymentStatus === 'PAID') { this.paymentStatus.set('success'); this.stopPolling(); // Clear cart but don't close popup - wait for email submission @@ -471,6 +451,20 @@ export class CartComponent implements OnDestroy { return `order_${timestamp}_${random}`; } + private buildQrDescription(orderId: string): string { + const items = this.items() + .map((item: CartItem) => { + const name = this.itemName(item).trim() || `Item ${item.itemID}`; + const details = [item.colour, item.size].filter(Boolean).join(', '); + const label = details ? `${name} (${details})` : name; + return `${item.quantity} x ${label}`; + }) + .join('; '); + + const description = `Order ${orderId}; items: ${items}; total: ${this.totalPrice().toFixed(2)} RUB`; + return description.length > 500 ? `${description.slice(0, 497)}...` : description; + } + onPhoneInput(event: Event): void { const input = event.target as HTMLInputElement; let value = input.value.replace(/\D/g, ''); // Remove all non-digits diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 699306d..54af297 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -9,7 +9,7 @@ export interface QrCreateRequest { qrtype: 'QRDynamic'; amount: number; currency: 'RUB'; - partnerqrID: string; + partnerqrID?: string; qrDescription?: string; Userid?: string; Reference?: string; @@ -49,12 +49,19 @@ export interface QrDynamicStatusResponse { qrExpirationDate: string; } +export interface QrPaymentStatusResponse { + status?: string; + paymentStatus?: string; + code?: string; +} + @Injectable({ providedIn: 'root' }) export class ApiService { private readonly baseUrl = environment.apiUrl; private readonly qrBaseUrl = (environment as any).qrApiUrl as string; + private readonly sbpQrUrl = 'https://qr.vitanova.network:567/qr'; private readonly retryConfig = { count: 2, @@ -404,6 +411,15 @@ export class ApiService { return this.http.post(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders }); } + createSbpPayment(payload: QrCreateRequest): Observable { + return this.http.post(this.sbpQrUrl, payload); + } + + checkSbpPaymentStatus(paymentId: string): Observable { + const params = new HttpParams().set('id', paymentId); + return this.http.get(this.sbpQrUrl, { params }); + } + checkPaymentStatus(partnerQrId: string, qrId: string): Observable { return this.http.get( `${this.qrBaseUrl}/qr/dynamic/${encodeURIComponent(partnerQrId)}/${encodeURIComponent(qrId)}`