api changed
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)}`
|
||||||
|
|||||||
Reference in New Issue
Block a user