This commit is contained in:
sdarbinyan
2026-06-29 00:06:18 +04:00
parent 3cf0ef87f8
commit 0977f302a4
8 changed files with 84 additions and 52 deletions

View File

@@ -78,14 +78,14 @@ export const en: Translations = {
privacyPolicy: 'privacy policy', privacyPolicy: 'privacy policy',
and: 'and', and: 'and',
checkout: 'Place order', checkout: 'Place order',
payViaQr: 'Pay via QR',
payViaCard: 'Pay via card',
close: 'Close', close: 'Close',
creatingPayment: 'Creating payment...', creatingPayment: 'Creating payment...',
waitFewSeconds: 'Please wait a few seconds', waitFewSeconds: 'Please wait a few seconds',
scanQr: 'Scan the QR code to pay', scanQr: 'Scan the QR code to pay',
choosePaymentMethod: 'Choose a payment method',
amountToPay: 'Amount due:', amountToPay: 'Amount due:',
waitingPayment: 'Waiting for payment...', waitingPayment: 'Waiting for payment...',
payByBankCard: 'Pay by bank card',
copied: '✓ Copied', copied: '✓ Copied',
copyLink: 'Copy link', copyLink: 'Copy link',
openNewTab: 'Open in new tab', openNewTab: 'Open in new tab',

View File

@@ -78,14 +78,14 @@ export const hy: Translations = {
privacyPolicy: 'գաղտնիության քաղաքականության', privacyPolicy: 'գաղտնիության քաղաքականության',
and: 'և', and: 'և',
checkout: 'Ձևակերպել պատվերը', checkout: 'Ձևակերպել պատվերը',
payViaQr: 'Վճարել QR-ով',
payViaCard: 'Վճարել քարտով',
close: 'Փակել', close: 'Փակել',
creatingPayment: 'Վճարման ստեղծում...', creatingPayment: 'Վճարման ստեղծում...',
waitFewSeconds: 'Խնդրում ենք սպասել մի քանի վայրկյան', waitFewSeconds: 'Խնդրում ենք սպասել մի քանի վայրկյան',
scanQr: 'Սքանավորեք QR կոդը վճարման համար', scanQr: 'Սքանավորեք QR կոդը վճարման համար',
choosePaymentMethod: 'Ընտրեք վճարման եղանակը',
amountToPay: 'Վճարման գումար՝', amountToPay: 'Վճարման գումար՝',
waitingPayment: 'Սպասում ենք վճարմանը...', waitingPayment: 'Սպասում ենք վճարմանը...',
payByBankCard: 'Վճարել բանկային քարտով',
copied: '✓ Պատճենված է', copied: '✓ Պատճենված է',
copyLink: 'Պատճենել հղումը', copyLink: 'Պատճենել հղումը',
openNewTab: 'Բացել նոր ներդիրում', openNewTab: 'Բացել նոր ներդիրում',

View File

@@ -78,14 +78,14 @@ export const ru: Translations = {
privacyPolicy: 'политикой конфиденциальности', privacyPolicy: 'политикой конфиденциальности',
and: 'и', and: 'и',
checkout: 'Оформить заказ', checkout: 'Оформить заказ',
payViaQr: 'Оплатить через QR',
payViaCard: 'Оплатить картой',
close: 'Закрыть', close: 'Закрыть',
creatingPayment: 'Создание платежа...', creatingPayment: 'Создание платежа...',
waitFewSeconds: 'Подождите несколько секунд', waitFewSeconds: 'Подождите несколько секунд',
scanQr: 'Сканируйте QR-код для оплаты', scanQr: 'Сканируйте QR-код для оплаты',
choosePaymentMethod: 'Выберите способ оплаты',
amountToPay: 'Сумма к оплате:', amountToPay: 'Сумма к оплате:',
waitingPayment: 'Ожидание оплаты...', waitingPayment: 'Ожидание оплаты...',
payByBankCard: 'Оплатить банковской картой',
copied: '✓ Скопировано', copied: '✓ Скопировано',
copyLink: 'Скопировать ссылку', copyLink: 'Скопировать ссылку',
openNewTab: 'Открыть в новой вкладке', openNewTab: 'Открыть в новой вкладке',

View File

@@ -76,14 +76,14 @@ export interface Translations {
privacyPolicy: string; privacyPolicy: string;
and: string; and: string;
checkout: string; checkout: string;
payViaQr: string;
payViaCard: string;
close: string; close: string;
creatingPayment: string; creatingPayment: string;
waitFewSeconds: string; waitFewSeconds: string;
scanQr: string; scanQr: string;
choosePaymentMethod: string;
amountToPay: string; amountToPay: string;
waitingPayment: string; waitingPayment: string;
payByBankCard: string;
copied: string; copied: string;
copyLink: string; copyLink: string;
openNewTab: string; openNewTab: string;

View File

@@ -157,14 +157,25 @@
</label> </label>
</div> </div>
<button <div class="payment-method-actions">
class="checkout-btn" <button
(click)="checkout()" class="checkout-btn qr-payment-btn"
[class.disabled]="isCheckoutDisabled" (click)="checkout('qr')"
[disabled]="isCheckoutDisabled" [class.disabled]="isCheckoutDisabled"
> [disabled]="isCheckoutDisabled"
{{ 'cart.checkout' | translate }} >
</button> {{ 'cart.payViaQr' | translate }}
</button>
<button
class="checkout-btn card-payment-btn"
(click)="checkout('card')"
[class.disabled]="isCheckoutDisabled"
[disabled]="isCheckoutDisabled"
>
{{ 'cart.payViaCard' | translate }}
</button>
</div>
@if (!isAuthenticated()) { @if (!isAuthenticated()) {
<div class="cart-login-gate"> <div class="cart-login-gate">
@@ -208,7 +219,7 @@
@if (paymentStatus() === 'waiting') { @if (paymentStatus() === 'waiting') {
<div class="payment-active"> <div class="payment-active">
<h2>{{ qrCodeUrl() ? ('cart.scanQr' | translate) : ('cart.choosePaymentMethod' | translate) }}</h2> <h2>{{ selectedPaymentMethod() === 'card' ? ('cart.waitingPayment' | translate) : ('cart.scanQr' | translate) }}</h2>
@if (qrCodeUrl()) { @if (qrCodeUrl()) {
<div class="qr-section"> <div class="qr-section">
@@ -231,12 +242,6 @@
</div> </div>
</div> </div>
@if (bankPaymentUrl()) {
<button class="bank-payment-btn" (click)="openBankPaymentPopup()">
{{ 'cart.payByBankCard' | translate }}
</button>
}
<div class="payment-actions"> <div class="payment-actions">
@if (paymentUrl()) { @if (paymentUrl()) {
<button class="copy-btn" (click)="copyPaymentLink()"> <button class="copy-btn" (click)="copyPaymentLink()">

View File

@@ -724,6 +724,13 @@
} }
} }
.payment-method-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 8px;
}
.checkout-btn { .checkout-btn {
width: 100%; width: 100%;
padding: 16px; padding: 16px;
@@ -736,7 +743,6 @@
font-weight: 700; font-weight: 700;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
margin-top: 8px;
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15); box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15);
&:hover:not(:disabled) { &:hover:not(:disabled) {
@@ -810,6 +816,15 @@
} }
} }
} }
.card-payment-btn {
background: #1f2937;
&:hover:not(:disabled) {
background: #111827;
box-shadow: 0 6px 16px rgba(31, 41, 55, 0.28);
}
}
} }
// Novo Cart Summary - Green Modern // Novo Cart Summary - Green Modern
@@ -868,6 +883,13 @@
} }
} }
.payment-method-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 12px;
}
.checkout-btn { .checkout-btn {
width: 100%; width: 100%;
padding: 16px; padding: 16px;
@@ -879,7 +901,6 @@
font-weight: 700; font-weight: 700;
cursor: pointer; cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
margin-top: 12px;
box-shadow: 0 4px 16px rgba(16, 185, 129, 0.3); box-shadow: 0 4px 16px rgba(16, 185, 129, 0.3);
&:hover:not(:disabled) { &:hover:not(:disabled) {
@@ -897,6 +918,15 @@
} }
} }
.card-payment-btn {
background: linear-gradient(135deg, #1f2937 0%, #111827 100%);
box-shadow: 0 4px 16px rgba(31, 41, 55, 0.22);
&:hover:not(:disabled) {
box-shadow: 0 8px 24px rgba(31, 41, 55, 0.3);
}
}
.cart-login-gate { .cart-login-gate {
margin-top: 20px; margin-top: 20px;
padding: 24px; padding: 24px;
@@ -1446,24 +1476,6 @@
} }
} }
.bank-payment-btn {
width: 100%;
padding: 14px 18px;
border: none;
border-radius: 10px;
background: #1f2937;
color: white;
font-weight: 700;
font-size: 1rem;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: #111827;
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(31, 41, 55, 0.22);
}
}
} }
.bank-payment-modal-overlay { .bank-payment-modal-overlay {
@@ -1671,6 +1683,10 @@
} }
} }
.payment-method-actions {
grid-template-columns: 1fr;
}
.bank-payment-modal-overlay { .bank-payment-modal-overlay {
padding: 12px; padding: 12px;
} }

View File

@@ -17,6 +17,8 @@ import { TranslatePipe } from '../../i18n/translate.pipe';
import { TranslateService } from '../../i18n/translate.service'; import { TranslateService } from '../../i18n/translate.service';
import { PAYMENT_POLL_INTERVAL_MS, PAYMENT_MAX_CHECKS, PAYMENT_TIMEOUT_CLOSE_MS, LINK_COPIED_DURATION_MS } from '../../config/constants'; import { PAYMENT_POLL_INTERVAL_MS, PAYMENT_MAX_CHECKS, PAYMENT_TIMEOUT_CLOSE_MS, LINK_COPIED_DURATION_MS } from '../../config/constants';
type PaymentMethod = 'qr' | 'card';
@Component({ @Component({
selector: 'app-cart', selector: 'app-cart',
imports: [DecimalPipe, RouterLink, FormsModule, EmptyCartIconComponent, DeliverySelectorComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe], imports: [DecimalPipe, RouterLink, FormsModule, EmptyCartIconComponent, DeliverySelectorComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe],
@@ -51,6 +53,7 @@ export class CartComponent implements OnDestroy {
bankPaymentUrl = signal<string>(''); bankPaymentUrl = signal<string>('');
bankPaymentFrameUrl = signal<SafeResourceUrl | null>(null); bankPaymentFrameUrl = signal<SafeResourceUrl | null>(null);
showBankPaymentPopup = signal<boolean>(false); showBankPaymentPopup = signal<boolean>(false);
selectedPaymentMethod = signal<PaymentMethod>('qr');
paymentId = signal<string>(''); paymentId = signal<string>('');
linkCopied = signal<boolean>(false); linkCopied = signal<boolean>(false);
@@ -161,7 +164,7 @@ export class CartComponent implements OnDestroy {
this.cartService.setSelectedDelivery(itemID, selectedDelivery); this.cartService.setSelectedDelivery(itemID, selectedDelivery);
} }
checkout(): void { checkout(paymentMethod: PaymentMethod): void {
if (!this.allRequiredDeliveriesSelected()) { if (!this.allRequiredDeliveriesSelected()) {
alert(this.i18n.t('cart.deliveryRequired')); alert(this.i18n.t('cart.deliveryRequired'));
return; return;
@@ -171,11 +174,12 @@ export class CartComponent implements OnDestroy {
alert(this.i18n.t('cart.acceptTerms')); alert(this.i18n.t('cart.acceptTerms'));
return; return;
} }
this.openPaymentPopup(); this.openPaymentPopup(paymentMethod);
} }
openPaymentPopup(): void { openPaymentPopup(paymentMethod: PaymentMethod): void {
this.showPaymentPopup.set(true); this.showPaymentPopup.set(true);
this.selectedPaymentMethod.set(paymentMethod);
this.paymentStatus.set('creating'); this.paymentStatus.set('creating');
this.paymentId.set(''); this.paymentId.set('');
this.qrCodeUrl.set(''); this.qrCodeUrl.set('');
@@ -192,7 +196,7 @@ export class CartComponent implements OnDestroy {
this.phoneError.set(''); this.phoneError.set('');
this.emailSubmitting.set(false); this.emailSubmitting.set(false);
this.paidItems = [...this.items()]; this.paidItems = [...this.items()];
this.createPayment(); this.createPayment(paymentMethod);
} }
closePaymentPopup(): void { closePaymentPopup(): void {
@@ -219,10 +223,10 @@ export class CartComponent implements OnDestroy {
this.bankPaymentFrameUrl.set(null); this.bankPaymentFrameUrl.set(null);
this.showBankPaymentPopup.set(false); this.showBankPaymentPopup.set(false);
this.linkCopied.set(false); this.linkCopied.set(false);
this.createPayment(); this.createPayment(this.selectedPaymentMethod());
} }
createPayment(): void { createPayment(paymentMethod: PaymentMethod): void {
const orderId = this.generateOrderId(); const orderId = this.generateOrderId();
const paymentPayload = { const paymentPayload = {
amount: Number(this.totalWithDelivery()), amount: Number(this.totalWithDelivery()),
@@ -231,6 +235,7 @@ export class CartComponent implements OnDestroy {
siteorderID: orderId, siteorderID: orderId,
redirectUrl: '', redirectUrl: '',
telegramUsername: this.getTelegramUsername(), telegramUsername: this.getTelegramUsername(),
paymentMethod,
items: this.buildPaymentItems(), items: this.buildPaymentItems(),
}; };
@@ -242,7 +247,7 @@ export class CartComponent implements OnDestroy {
const paymentLink = this.apiService.resolvePaymentLink(response); const paymentLink = this.apiService.resolvePaymentLink(response);
const bankUrl = this.apiService.resolveBankPaymentUrl(response); const bankUrl = this.apiService.resolveBankPaymentUrl(response);
if (!qrId || (!qrUrl && !bankUrl)) { if (!qrId || (paymentMethod === 'qr' && !qrUrl) || (paymentMethod === 'card' && !bankUrl)) {
console.error('Payment response missing payment fields:', response); console.error('Payment response missing payment fields:', response);
this.setPaymentError(); this.setPaymentError();
return; return;
@@ -255,6 +260,9 @@ export class CartComponent implements OnDestroy {
this.paymentStatus.set('waiting'); this.paymentStatus.set('waiting');
this.startPolling(); this.startPolling();
if (paymentMethod === 'card') {
this.openBankPaymentPopup();
}
}, },
error: (err) => { error: (err) => {
console.error('Error creating payment:', err); console.error('Error creating payment:', err);

View File

@@ -23,6 +23,8 @@ export interface QrCreateResponse {
nspkID?: string; nspkID?: string;
nspkId?: string; nspkId?: string;
nspkurl?: string; nspkurl?: string;
orderID?: string;
url?: string;
bankUrl?: string; bankUrl?: string;
status?: string; status?: string;
qrStatus?: string; qrStatus?: string;
@@ -43,6 +45,7 @@ export interface CartPaymentRequest {
siteorderID: string; siteorderID: string;
redirectUrl: string; redirectUrl: string;
telegramUsername: string; telegramUsername: string;
paymentMethod: 'qr' | 'card';
items: Array<{ itemID: number; price: number; name: string; quantity?: number; delivery?: DeliveryOption[] }>; items: Array<{ itemID: number; price: number; name: string; quantity?: number; delivery?: DeliveryOption[] }>;
} }
@@ -537,7 +540,7 @@ export class ApiService {
} }
resolvePaymentQrId(response: QrCreateResponse): string { resolvePaymentQrId(response: QrCreateResponse): string {
return response.qrId ?? response.qrID ?? response.nspkID ?? response.nspkId ?? ''; return response.qrId ?? response.qrID ?? response.nspkID ?? response.nspkId ?? response.orderID ?? '';
} }
resolvePaymentLink(response: QrCreateResponse): string { resolvePaymentLink(response: QrCreateResponse): string {
@@ -545,7 +548,7 @@ export class ApiService {
} }
resolveBankPaymentUrl(response: QrCreateResponse): string { resolveBankPaymentUrl(response: QrCreateResponse): string {
return response.bankUrl ?? ''; return response.bankUrl ?? response.url ?? '';
} }
resolvePaymentQrUrl(response: QrCreateResponse): string { resolvePaymentQrUrl(response: QrCreateResponse): string {