api changed

This commit is contained in:
sdarbinyan
2026-06-06 16:16:37 +04:00
parent 14bdd3bcd0
commit 58e0869916
3 changed files with 41 additions and 31 deletions

View File

@@ -147,8 +147,8 @@
<button <button
class="checkout-btn" class="checkout-btn"
(click)="checkout()" (click)="checkout()"
[class.disabled]="!termsAccepted" [class.disabled]="!termsAccepted || !isAuthenticated()"
[disabled]="!termsAccepted" [disabled]="!termsAccepted || !isAuthenticated()"
> >
{{ 'cart.checkout' | translate }} {{ 'cart.checkout' | translate }}
</button> </button>

View File

@@ -149,11 +149,6 @@ export class CartComponent implements OnDestroy {
alert(this.i18n.t('cart.acceptTerms')); alert(this.i18n.t('cart.acceptTerms'));
return; return;
} }
// Auth gate: require Telegram login before payment
if (!this.authService.isAuthenticated()) {
this.authService.requestLogin();
return;
}
this.openPaymentPopup(); this.openPaymentPopup();
} }
@@ -203,10 +198,6 @@ export class CartComponent implements OnDestroy {
createPayment(): void { createPayment(): void {
const sessionId = this.authService.session()?.sessionId || ''; const sessionId = this.authService.session()?.sessionId || '';
const partnerQrId = this.getPartnerQrId(); const partnerQrId = this.getPartnerQrId();
if (!partnerQrId) {
this.setPaymentError();
return;
}
const cartItems = this.items().map((item: CartItem) => ({ const cartItems = this.items().map((item: CartItem) => ({
itemID: item.itemID, itemID: item.itemID,
@@ -227,22 +218,17 @@ export class CartComponent implements OnDestroy {
) )
: of(null); : of(null);
const userIdValue = this.getUrlParam('userid-value') || undefined;
const authorizationKey = this.getUrlParam('authorization-key') || undefined;
const qrPayload = { const qrPayload = {
qrtype: 'QRDynamic' as const, qrtype: 'QRDynamic' as const,
amount: Number(this.totalPrice()), amount: Number(this.totalPrice()),
currency: 'RUB' as const, currency: 'RUB' as const,
partnerqrID: partnerQrId, ...(partnerQrId && { partnerqrID: partnerQrId }),
qrDescription: `Order ${this.generateOrderId()}, total: ${this.totalPrice().toFixed(2)} RUB`, qrDescription: this.buildQrDescription(this.generateOrderId()),
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$ syncCart$
.pipe( .pipe(
switchMap(() => this.apiService.createPayment(qrPayload, { authorizationKey, userIdValue })) switchMap(() => this.apiService.createSbpPayment(qrPayload))
) )
.subscribe({ .subscribe({
next: (response) => { next: (response) => {
@@ -261,11 +247,6 @@ export class CartComponent implements OnDestroy {
this.qrCodeUrl.set(qrUrl); this.qrCodeUrl.set(qrUrl);
this.paymentUrl.set(paymentLink); this.paymentUrl.set(paymentLink);
if (paymentLink && typeof window !== 'undefined' && window.innerWidth < 768) {
window.location.href = paymentLink;
return;
}
this.paymentStatus.set('waiting'); this.paymentStatus.set('waiting');
this.startPolling(); this.startPolling();
}, },
@@ -278,7 +259,7 @@ export class CartComponent implements OnDestroy {
startPolling(): void { startPolling(): void {
this.stopPolling(); this.stopPolling();
if (!this.qrPartnerId || !this.paymentId()) { if (!this.paymentId()) {
this.setPaymentError(); this.setPaymentError();
return; return;
} }
@@ -287,7 +268,7 @@ export class CartComponent implements OnDestroy {
.pipe( .pipe(
take(this.maxChecks), // maximum 36 checks (3 minutes) take(this.maxChecks), // maximum 36 checks (3 minutes)
exhaustMap(() => { exhaustMap(() => {
return this.apiService.checkPaymentStatus(this.qrPartnerId, this.paymentId()).pipe( return this.apiService.checkSbpPaymentStatus(this.paymentId()).pipe(
timeout(8000), timeout(8000),
catchError((err) => { catchError((err) => {
console.error('Error checking payment status:', err); console.error('Error checking payment status:', err);
@@ -302,10 +283,9 @@ export class CartComponent implements OnDestroy {
return; return;
} }
const paymentStatus = response.paymentStatus?.toUpperCase(); const paymentStatus = (response.paymentStatus || response.status || response.code || '').toUpperCase();
const paymentCode = response.code?.toUpperCase();
if (paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED' || paymentStatus === 'REJECTED') { if (paymentStatus === 'FAILED' || paymentStatus === 'EXPIRED' || paymentStatus === 'CANCELLED' || paymentStatus === 'REJECTED') {
this.paymentStatus.set('timeout'); this.paymentStatus.set('timeout');
this.stopPolling(); this.stopPolling();
if (this.closeTimeout) clearTimeout(this.closeTimeout); if (this.closeTimeout) clearTimeout(this.closeTimeout);
@@ -316,7 +296,7 @@ export class CartComponent implements OnDestroy {
} }
// Check if payment is successful // Check if payment is successful
if (paymentStatus === 'COMPLETED' || paymentStatus === 'APPROVED') { if (paymentStatus === 'COMPLETED' || paymentStatus === 'APPROVED' || paymentStatus === 'PAID') {
this.paymentStatus.set('success'); this.paymentStatus.set('success');
this.stopPolling(); this.stopPolling();
// Clear cart but don't close popup - wait for email submission // Clear cart but don't close popup - wait for email submission
@@ -471,6 +451,20 @@ export class CartComponent implements OnDestroy {
return `order_${timestamp}_${random}`; 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 { onPhoneInput(event: Event): void {
const input = event.target as HTMLInputElement; const input = event.target as HTMLInputElement;
let value = input.value.replace(/\D/g, ''); // Remove all non-digits let value = input.value.replace(/\D/g, ''); // Remove all non-digits

View File

@@ -9,7 +9,7 @@ export interface QrCreateRequest {
qrtype: 'QRDynamic'; qrtype: 'QRDynamic';
amount: number; amount: number;
currency: 'RUB'; currency: 'RUB';
partnerqrID: string; partnerqrID?: string;
qrDescription?: string; qrDescription?: string;
Userid?: string; Userid?: string;
Reference?: string; Reference?: string;
@@ -49,12 +49,19 @@ export interface QrDynamicStatusResponse {
qrExpirationDate: string; qrExpirationDate: string;
} }
export interface QrPaymentStatusResponse {
status?: string;
paymentStatus?: string;
code?: string;
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ApiService { export class ApiService {
private readonly baseUrl = environment.apiUrl; private readonly baseUrl = environment.apiUrl;
private readonly qrBaseUrl = (environment as any).qrApiUrl as string; private readonly qrBaseUrl = (environment as any).qrApiUrl as string;
private readonly sbpQrUrl = 'https://qr.vitanova.network:567/qr';
private readonly retryConfig = { private readonly retryConfig = {
count: 2, count: 2,
@@ -404,6 +411,15 @@ export class ApiService {
return this.http.post<QrCreateResponse>(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders }); return this.http.post<QrCreateResponse>(`${this.qrBaseUrl}/qr`, payload, { headers: httpHeaders });
} }
createSbpPayment(payload: QrCreateRequest): Observable<QrCreateResponse> {
return this.http.post<QrCreateResponse>(this.sbpQrUrl, payload);
}
checkSbpPaymentStatus(paymentId: string): Observable<QrPaymentStatusResponse> {
const params = new HttpParams().set('id', paymentId);
return this.http.get<QrPaymentStatusResponse>(this.sbpQrUrl, { params });
}
checkPaymentStatus(partnerQrId: string, qrId: string): Observable<QrDynamicStatusResponse> { checkPaymentStatus(partnerQrId: string, qrId: string): Observable<QrDynamicStatusResponse> {
return this.http.get<QrDynamicStatusResponse>( return this.http.get<QrDynamicStatusResponse>(
`${this.qrBaseUrl}/qr/dynamic/${encodeURIComponent(partnerQrId)}/${encodeURIComponent(qrId)}` `${this.qrBaseUrl}/qr/dynamic/${encodeURIComponent(partnerQrId)}/${encodeURIComponent(qrId)}`