@bloonio/lokotro-pay 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, EventEmitter, Output, Input, Component, forwardRef, InjectionToken, NgModule } from '@angular/core';
3
- import * as i2 from '@angular/common';
4
- import { CommonModule } from '@angular/common';
5
- import * as i1 from '@angular/common/http';
6
- import { HttpHeaders, HttpErrorResponse, HttpClientModule } from '@angular/common/http';
7
3
  import * as i1$1 from '@angular/forms';
8
4
  import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators } from '@angular/forms';
9
5
  import { BehaviorSubject, of, throwError, Subject, interval } from 'rxjs';
10
- import { timeout, map, catchError, tap, shareReplay, takeUntil } from 'rxjs/operators';
6
+ import { UpperCasePipe } from '@angular/common';
7
+ import { timeout, map, catchError, tap, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
8
+ import * as i1 from '@angular/common/http';
9
+ import { HttpHeaders, HttpErrorResponse } from '@angular/common/http';
11
10
 
12
11
  /**
13
12
  * Lokotro Pay - Environment Configuration
@@ -18,7 +17,8 @@ import { timeout, map, catchError, tap, shareReplay, takeUntil } from 'rxjs/oper
18
17
  */
19
18
  const LOKOTRO_DEV_ENV = {
20
19
  environment: 'development',
21
- apiBaseUrl: 'https://dev.apps.api.bloonio.com',
20
+ apiBaseUrl: 'http://10.37.31.218:6495',
21
+ // apiBaseUrl: 'https://dev.app.api.gtwy.lokotro.com',
22
22
  paymentApiVersion: 'v1',
23
23
  debugMode: true,
24
24
  logLevel: 'debug',
@@ -29,7 +29,7 @@ const LOKOTRO_DEV_ENV = {
29
29
  */
30
30
  const LOKOTRO_PROD_ENV = {
31
31
  environment: 'production',
32
- apiBaseUrl: 'https://apps.api.bloonio.com',
32
+ apiBaseUrl: 'https://app.api.gtwy.lokotro.com',
33
33
  paymentApiVersion: 'v1',
34
34
  debugMode: false,
35
35
  logLevel: 'error',
@@ -126,6 +126,7 @@ class LokotroPayEnv {
126
126
  return {
127
127
  collect: '/payments/collect',
128
128
  transaction: '/payments/transaction',
129
+ mobileMoneyStatus: '/payments/mobile-money/status',
129
130
  submit: '/payments/submit',
130
131
  verifyOtp: '/payments/verify-otp',
131
132
  resendOtp: '/payments/resend-otp',
@@ -458,12 +459,8 @@ var LokotroPayLanguage;
458
459
  (function (LokotroPayLanguage) {
459
460
  LokotroPayLanguage["English"] = "en";
460
461
  LokotroPayLanguage["French"] = "fr";
461
- LokotroPayLanguage["German"] = "de";
462
462
  LokotroPayLanguage["Spanish"] = "es";
463
- LokotroPayLanguage["Italian"] = "it";
464
463
  LokotroPayLanguage["Russian"] = "ru";
465
- LokotroPayLanguage["Hindi"] = "hi";
466
- LokotroPayLanguage["Japanese"] = "ja";
467
464
  LokotroPayLanguage["Chinese"] = "zh";
468
465
  LokotroPayLanguage["Lingala"] = "ln";
469
466
  })(LokotroPayLanguage || (LokotroPayLanguage = {}));
@@ -473,12 +470,8 @@ var LokotroPayLanguage;
473
470
  const LokotroPayLanguageInfo = {
474
471
  [LokotroPayLanguage.English]: { displayName: 'English', nativeName: 'English', flagAsset: 'assets/flags/england.svg' },
475
472
  [LokotroPayLanguage.French]: { displayName: 'French', nativeName: 'Français', flagAsset: 'assets/flags/france.svg' },
476
- [LokotroPayLanguage.German]: { displayName: 'German', nativeName: 'Deutsch', flagAsset: 'assets/flags/german.svg' },
477
473
  [LokotroPayLanguage.Spanish]: { displayName: 'Spanish', nativeName: 'Español', flagAsset: 'assets/flags/spain.svg' },
478
- [LokotroPayLanguage.Italian]: { displayName: 'Italian', nativeName: 'Italiano', flagAsset: 'assets/flags/italy.svg' },
479
474
  [LokotroPayLanguage.Russian]: { displayName: 'Russian', nativeName: 'Русский', flagAsset: 'assets/flags/russia.svg' },
480
- [LokotroPayLanguage.Hindi]: { displayName: 'Hindi', nativeName: 'हिंदी', flagAsset: 'assets/flags/india.svg' },
481
- [LokotroPayLanguage.Japanese]: { displayName: 'Japanese', nativeName: '日本語', flagAsset: 'assets/flags/japanese.svg' },
482
475
  [LokotroPayLanguage.Chinese]: { displayName: 'Chinese', nativeName: '中文(普通话)', flagAsset: 'assets/flags/china.svg' },
483
476
  [LokotroPayLanguage.Lingala]: { displayName: 'Lingala', nativeName: 'Lingala', flagAsset: 'assets/flags/drc.svg' }
484
477
  };
@@ -490,12 +483,8 @@ const LokotroPayLanguageHelper = {
490
483
  const languageMap = {
491
484
  'en': LokotroPayLanguage.English,
492
485
  'fr': LokotroPayLanguage.French,
493
- 'de': LokotroPayLanguage.German,
494
486
  'es': LokotroPayLanguage.Spanish,
495
- 'it': LokotroPayLanguage.Italian,
496
487
  'ru': LokotroPayLanguage.Russian,
497
- 'hi': LokotroPayLanguage.Hindi,
498
- 'ja': LokotroPayLanguage.Japanese,
499
488
  'zh': LokotroPayLanguage.Chinese,
500
489
  'ln': LokotroPayLanguage.Lingala
501
490
  };
@@ -639,7 +628,10 @@ const EN_TRANSLATIONS = {
639
628
  bankAccountSummary: 'Account Details',
640
629
  accountLabel: 'Account Label',
641
630
  bankTransferProofInstructions: 'A payment link will be sent to your email to upload the proof of transfer.',
642
- confirmBankTransfer: 'Confirm Transfer'
631
+ confirmBankTransfer: 'Confirm Transfer',
632
+ // Auto-redirect countdown
633
+ redirectingIn: 'Redirecting in {seconds}s',
634
+ tapToCancel: 'Tap to cancel'
643
635
  };
644
636
  /**
645
637
  * French translations
@@ -744,7 +736,10 @@ const FR_TRANSLATIONS = {
744
736
  bankAccountSummary: 'Détails du compte',
745
737
  accountLabel: 'Libellé du compte',
746
738
  bankTransferProofInstructions: 'Un lien de paiement sera envoyé à votre adresse e-mail pour télécharger la preuve de virement.',
747
- confirmBankTransfer: 'Confirmer le virement'
739
+ confirmBankTransfer: 'Confirmer le virement',
740
+ // Auto-redirect countdown
741
+ redirectingIn: 'Redirection dans {seconds}s',
742
+ tapToCancel: 'Appuyez pour annuler'
748
743
  };
749
744
  /**
750
745
  * Spanish translations
@@ -849,7 +844,10 @@ const ES_TRANSLATIONS = {
849
844
  bankAccountSummary: 'Detalles de la cuenta',
850
845
  accountLabel: 'Etiqueta de la cuenta',
851
846
  bankTransferProofInstructions: 'Se enviará un enlace de pago a su correo electrónico para cargar el comprobante de transferencia.',
852
- confirmBankTransfer: 'Confirmar transferencia'
847
+ confirmBankTransfer: 'Confirmar transferencia',
848
+ // Auto-redirect countdown
849
+ redirectingIn: 'Redirigiendo en {seconds}s',
850
+ tapToCancel: 'Toque para cancelar'
853
851
  };
854
852
  /**
855
853
  * All translations map
@@ -858,11 +856,7 @@ const TRANSLATIONS = {
858
856
  [LokotroPayLanguage.English]: EN_TRANSLATIONS,
859
857
  [LokotroPayLanguage.French]: FR_TRANSLATIONS,
860
858
  [LokotroPayLanguage.Spanish]: ES_TRANSLATIONS,
861
- [LokotroPayLanguage.German]: EN_TRANSLATIONS, // Fallback to English
862
- [LokotroPayLanguage.Italian]: EN_TRANSLATIONS, // Fallback to English
863
859
  [LokotroPayLanguage.Russian]: EN_TRANSLATIONS, // Fallback to English
864
- [LokotroPayLanguage.Hindi]: EN_TRANSLATIONS, // Fallback to English
865
- [LokotroPayLanguage.Japanese]: EN_TRANSLATIONS, // Fallback to English
866
860
  [LokotroPayLanguage.Chinese]: EN_TRANSLATIONS, // Fallback to English
867
861
  [LokotroPayLanguage.Lingala]: FR_TRANSLATIONS // Fallback to French
868
862
  };
@@ -908,10 +902,19 @@ class LokotroLocalizationService {
908
902
  }
909
903
  }
910
904
  /**
911
- * Get translation by key
905
+ * Get translation by key with optional interpolation
906
+ * @param key - Translation key
907
+ * @param params - Optional parameters for interpolation (e.g., { seconds: 3 })
912
908
  */
913
- translate(key) {
914
- return this.translations[key] || key;
909
+ translate(key, params) {
910
+ let translation = this.translations[key] || key;
911
+ // Handle interpolation if params provided
912
+ if (params) {
913
+ Object.entries(params).forEach(([paramKey, paramValue]) => {
914
+ translation = translation.replace(new RegExp(`\\{${paramKey}\\}`, 'g'), String(paramValue));
915
+ });
916
+ }
917
+ return translation;
915
918
  }
916
919
  /**
917
920
  * Get all supported languages
@@ -946,10 +949,10 @@ class LokotroLocalizationService {
946
949
  this.translationsSubject.next(TRANSLATIONS[language]);
947
950
  }
948
951
  }
949
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLocalizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
950
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLocalizationService, providedIn: 'root' }); }
952
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLocalizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
953
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLocalizationService, providedIn: 'root' }); }
951
954
  }
952
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLocalizationService, decorators: [{
955
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLocalizationService, decorators: [{
953
956
  type: Injectable,
954
957
  args: [{
955
958
  providedIn: 'root'
@@ -1011,104 +1014,114 @@ class LokotroPaymentMethodSelectionComponent {
1011
1014
  parent.appendChild(placeholder);
1012
1015
  }
1013
1016
  }
1014
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1015
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroPaymentMethodSelectionComponent, isStandalone: true, selector: "lokotro-payment-method-selection", inputs: { paymentMethods: "paymentMethods", selectedMethod: "selectedMethod" }, outputs: { methodSelected: "methodSelected" }, ngImport: i0, template: `
1017
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1018
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroPaymentMethodSelectionComponent, isStandalone: true, selector: "lokotro-payment-method-selection", inputs: { paymentMethods: "paymentMethods", selectedMethod: "selectedMethod" }, outputs: { methodSelected: "methodSelected" }, ngImport: i0, template: `
1016
1019
  <div class="lokotro-method-selection">
1017
1020
  <h3 class="lokotro-section-title">{{ localization.translate('selectPaymentMethod') }}</h3>
1018
-
1021
+
1019
1022
  <div class="lokotro-methods-grid">
1020
- <button
1021
- *ngFor="let method of paymentMethods"
1022
- class="lokotro-method-card"
1023
- [class.selected]="selectedMethod?.id === method.id"
1024
- [class.disabled]="!method.isEnabled"
1025
- [disabled]="!method.isEnabled"
1026
- (click)="selectMethod(method)">
1027
-
1028
- <div class="lokotro-method-icon">
1029
- <img
1030
- *ngIf="method.iconUrl"
1031
- [src]="method.iconUrl"
1032
- [alt]="method.displayName"
1033
- (error)="onImageError($event, method.channel)">
1034
- <div *ngIf="!method.iconUrl" class="lokotro-method-icon-placeholder">
1035
- {{ getMethodIcon(method.channel) }}
1023
+ @for (method of paymentMethods; track method) {
1024
+ <button
1025
+ class="lokotro-method-card"
1026
+ [class.selected]="selectedMethod?.id === method.id"
1027
+ [class.disabled]="!method.isEnabled"
1028
+ [disabled]="!method.isEnabled"
1029
+ (click)="selectMethod(method)">
1030
+ <div class="lokotro-method-icon">
1031
+ @if (method.iconUrl) {
1032
+ <img
1033
+ [src]="method.iconUrl"
1034
+ [alt]="method.displayName"
1035
+ (error)="onImageError($event, method.channel)">
1036
+ }
1037
+ @if (!method.iconUrl) {
1038
+ <div class="lokotro-method-icon-placeholder">
1039
+ {{ getMethodIcon(method.channel) }}
1040
+ </div>
1041
+ }
1036
1042
  </div>
1037
- </div>
1038
-
1039
- <div class="lokotro-method-info">
1040
- <span class="lokotro-method-name">{{ method.displayName }}</span>
1041
- <span class="lokotro-method-description" *ngIf="getMethodDescription(method.channel)">
1042
- {{ getMethodDescription(method.channel) }}
1043
- </span>
1044
- </div>
1045
-
1046
- <div class="lokotro-method-check" *ngIf="selectedMethod?.id === method.id">
1047
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1048
- <path d="M20 6L9 17l-5-5"/>
1049
- </svg>
1050
- </div>
1051
- </button>
1043
+ <div class="lokotro-method-info">
1044
+ <span class="lokotro-method-name">{{ method.displayName }}</span>
1045
+ @if (getMethodDescription(method.channel)) {
1046
+ <span class="lokotro-method-description">
1047
+ {{ getMethodDescription(method.channel) }}
1048
+ </span>
1049
+ }
1050
+ </div>
1051
+ @if (selectedMethod?.id === method.id) {
1052
+ <div class="lokotro-method-check">
1053
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1054
+ <path d="M20 6L9 17l-5-5"/>
1055
+ </svg>
1056
+ </div>
1057
+ }
1058
+ </button>
1059
+ }
1052
1060
  </div>
1053
-
1054
- <button
1061
+
1062
+ <button
1055
1063
  class="lokotro-continue-btn"
1056
1064
  [disabled]="!selectedMethod"
1057
1065
  (click)="onContinue()">
1058
1066
  {{ localization.translate('continue') }}
1059
1067
  </button>
1060
1068
  </div>
1061
- `, isInline: true, styles: [".lokotro-method-selection{display:flex;flex-direction:column;gap:24px}.lokotro-section-title{font-size:20px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-methods-grid{display:flex;flex-direction:column;gap:12px}.lokotro-method-card{display:flex;align-items:center;gap:16px;padding:16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:16px;cursor:pointer;transition:all .2s ease;text-align:left}.lokotro-method-card:hover:not(.disabled){border-color:var(--lokotro-accent, #3BFBDA);transform:translateY(-2px)}.lokotro-method-card.selected{border-color:var(--lokotro-accent, #3BFBDA);background:linear-gradient(135deg,var(--lokotro-card, #3A4840),var(--lokotro-surface, #2A3832))}.lokotro-method-card.disabled{opacity:.5;cursor:not-allowed}.lokotro-method-icon{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-surface, #2A3832);border-radius:12px;overflow:hidden}.lokotro-method-icon img{width:100%;height:100%;object-fit:contain}.lokotro-method-icon-placeholder{font-size:24px}.lokotro-method-info{flex:1;display:flex;flex-direction:column;gap:4px}.lokotro-method-name{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-method-description{font-size:13px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-method-check{width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:var(--lokotro-accent, #3BFBDA)}.lokotro-continue-btn{width:100%;padding:16px;background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-continue-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-continue-btn:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
1069
+ `, isInline: true, styles: [".lokotro-method-selection{display:flex;flex-direction:column;gap:24px}.lokotro-section-title{font-size:20px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-methods-grid{display:flex;flex-direction:column;gap:12px}.lokotro-method-card{display:flex;align-items:center;gap:16px;padding:16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:16px;cursor:pointer;transition:all .2s ease;text-align:left}.lokotro-method-card:hover:not(.disabled){border-color:var(--lokotro-accent, #3BFBDA);transform:translateY(-2px)}.lokotro-method-card.selected{border-color:var(--lokotro-accent, #3BFBDA);background:linear-gradient(135deg,var(--lokotro-card, #3A4840),var(--lokotro-surface, #2A3832))}.lokotro-method-card.disabled{opacity:.5;cursor:not-allowed}.lokotro-method-icon{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-surface, #2A3832);border-radius:12px;overflow:hidden}.lokotro-method-icon img{width:100%;height:100%;object-fit:contain}.lokotro-method-icon-placeholder{font-size:24px}.lokotro-method-info{flex:1;display:flex;flex-direction:column;gap:4px}.lokotro-method-name{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-method-description{font-size:13px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-method-check{width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:var(--lokotro-accent, #3BFBDA)}.lokotro-continue-btn{width:100%;padding:16px;background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-continue-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-continue-btn:disabled{opacity:.5;cursor:not-allowed}\n"] }); }
1062
1070
  }
1063
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, decorators: [{
1071
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, decorators: [{
1064
1072
  type: Component,
1065
- args: [{ selector: 'lokotro-payment-method-selection', standalone: true, imports: [CommonModule], template: `
1073
+ args: [{ selector: 'lokotro-payment-method-selection', standalone: true, imports: [], template: `
1066
1074
  <div class="lokotro-method-selection">
1067
1075
  <h3 class="lokotro-section-title">{{ localization.translate('selectPaymentMethod') }}</h3>
1068
-
1076
+
1069
1077
  <div class="lokotro-methods-grid">
1070
- <button
1071
- *ngFor="let method of paymentMethods"
1072
- class="lokotro-method-card"
1073
- [class.selected]="selectedMethod?.id === method.id"
1074
- [class.disabled]="!method.isEnabled"
1075
- [disabled]="!method.isEnabled"
1076
- (click)="selectMethod(method)">
1077
-
1078
- <div class="lokotro-method-icon">
1079
- <img
1080
- *ngIf="method.iconUrl"
1081
- [src]="method.iconUrl"
1082
- [alt]="method.displayName"
1083
- (error)="onImageError($event, method.channel)">
1084
- <div *ngIf="!method.iconUrl" class="lokotro-method-icon-placeholder">
1085
- {{ getMethodIcon(method.channel) }}
1078
+ @for (method of paymentMethods; track method) {
1079
+ <button
1080
+ class="lokotro-method-card"
1081
+ [class.selected]="selectedMethod?.id === method.id"
1082
+ [class.disabled]="!method.isEnabled"
1083
+ [disabled]="!method.isEnabled"
1084
+ (click)="selectMethod(method)">
1085
+ <div class="lokotro-method-icon">
1086
+ @if (method.iconUrl) {
1087
+ <img
1088
+ [src]="method.iconUrl"
1089
+ [alt]="method.displayName"
1090
+ (error)="onImageError($event, method.channel)">
1091
+ }
1092
+ @if (!method.iconUrl) {
1093
+ <div class="lokotro-method-icon-placeholder">
1094
+ {{ getMethodIcon(method.channel) }}
1095
+ </div>
1096
+ }
1086
1097
  </div>
1087
- </div>
1088
-
1089
- <div class="lokotro-method-info">
1090
- <span class="lokotro-method-name">{{ method.displayName }}</span>
1091
- <span class="lokotro-method-description" *ngIf="getMethodDescription(method.channel)">
1092
- {{ getMethodDescription(method.channel) }}
1093
- </span>
1094
- </div>
1095
-
1096
- <div class="lokotro-method-check" *ngIf="selectedMethod?.id === method.id">
1097
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1098
- <path d="M20 6L9 17l-5-5"/>
1099
- </svg>
1100
- </div>
1101
- </button>
1098
+ <div class="lokotro-method-info">
1099
+ <span class="lokotro-method-name">{{ method.displayName }}</span>
1100
+ @if (getMethodDescription(method.channel)) {
1101
+ <span class="lokotro-method-description">
1102
+ {{ getMethodDescription(method.channel) }}
1103
+ </span>
1104
+ }
1105
+ </div>
1106
+ @if (selectedMethod?.id === method.id) {
1107
+ <div class="lokotro-method-check">
1108
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1109
+ <path d="M20 6L9 17l-5-5"/>
1110
+ </svg>
1111
+ </div>
1112
+ }
1113
+ </button>
1114
+ }
1102
1115
  </div>
1103
-
1104
- <button
1116
+
1117
+ <button
1105
1118
  class="lokotro-continue-btn"
1106
1119
  [disabled]="!selectedMethod"
1107
1120
  (click)="onContinue()">
1108
1121
  {{ localization.translate('continue') }}
1109
1122
  </button>
1110
1123
  </div>
1111
- `, styles: [".lokotro-method-selection{display:flex;flex-direction:column;gap:24px}.lokotro-section-title{font-size:20px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-methods-grid{display:flex;flex-direction:column;gap:12px}.lokotro-method-card{display:flex;align-items:center;gap:16px;padding:16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:16px;cursor:pointer;transition:all .2s ease;text-align:left}.lokotro-method-card:hover:not(.disabled){border-color:var(--lokotro-accent, #3BFBDA);transform:translateY(-2px)}.lokotro-method-card.selected{border-color:var(--lokotro-accent, #3BFBDA);background:linear-gradient(135deg,var(--lokotro-card, #3A4840),var(--lokotro-surface, #2A3832))}.lokotro-method-card.disabled{opacity:.5;cursor:not-allowed}.lokotro-method-icon{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-surface, #2A3832);border-radius:12px;overflow:hidden}.lokotro-method-icon img{width:100%;height:100%;object-fit:contain}.lokotro-method-icon-placeholder{font-size:24px}.lokotro-method-info{flex:1;display:flex;flex-direction:column;gap:4px}.lokotro-method-name{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-method-description{font-size:13px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-method-check{width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:var(--lokotro-accent, #3BFBDA)}.lokotro-continue-btn{width:100%;padding:16px;background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-continue-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-continue-btn:disabled{opacity:.5;cursor:not-allowed}\n"] }]
1124
+ `, styles: [".lokotro-method-selection{display:flex;flex-direction:column;gap:24px}.lokotro-section-title{font-size:20px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-methods-grid{display:flex;flex-direction:column;gap:12px}.lokotro-method-card{display:flex;align-items:center;gap:16px;padding:16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:16px;cursor:pointer;transition:all .2s ease;text-align:left}.lokotro-method-card:hover:not(.disabled){border-color:var(--lokotro-accent, #3BFBDA);transform:translateY(-2px)}.lokotro-method-card.selected{border-color:var(--lokotro-accent, #3BFBDA);background:linear-gradient(135deg,var(--lokotro-card, #3A4840),var(--lokotro-surface, #2A3832))}.lokotro-method-card.disabled{opacity:.5;cursor:not-allowed}.lokotro-method-icon{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-surface, #2A3832);border-radius:12px;overflow:hidden}.lokotro-method-icon img{width:100%;height:100%;object-fit:contain}.lokotro-method-icon-placeholder{font-size:24px}.lokotro-method-info{flex:1;display:flex;flex-direction:column;gap:4px}.lokotro-method-name{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-method-description{font-size:13px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-method-check{width:24px;height:24px;display:flex;align-items:center;justify-content:center;color:var(--lokotro-accent, #3BFBDA)}.lokotro-continue-btn{width:100%;padding:16px;background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-continue-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-continue-btn:disabled{opacity:.5;cursor:not-allowed}\n"] }]
1112
1125
  }], ctorParameters: () => [{ type: LokotroLocalizationService }], propDecorators: { paymentMethods: [{
1113
1126
  type: Input
1114
1127
  }], selectedMethod: [{
@@ -1234,10 +1247,13 @@ class LokotroCountryUtils {
1234
1247
  * Enhanced HTTP client for Lokotro Pay with modern error handling and logging
1235
1248
  */
1236
1249
  class LokotroHttpClientService {
1250
+ static { this.instanceCounter = 0; }
1237
1251
  constructor(http) {
1238
1252
  this.http = http;
1239
1253
  this.acceptLanguage = 'fr';
1240
1254
  this.customHeaders = {};
1255
+ this.instanceId = ++LokotroHttpClientService.instanceCounter;
1256
+ console.log(`[Lokotro HTTP] Instance #${this.instanceId} created`);
1241
1257
  }
1242
1258
  /**
1243
1259
  * Configure the HTTP client
@@ -1257,7 +1273,9 @@ class LokotroHttpClientService {
1257
1273
  * Set app-key for Lokotro Gateway authentication
1258
1274
  */
1259
1275
  setAppKey(appKey) {
1276
+ console.log(`[Lokotro HTTP #${this.instanceId}] setAppKey called with:`, appKey?.substring(0, 20) + '...');
1260
1277
  this.appKey = appKey;
1278
+ console.log(`[Lokotro HTTP #${this.instanceId}] appKey now set to:`, this.appKey?.substring(0, 20) + '...');
1261
1279
  }
1262
1280
  /**
1263
1281
  * Remove app-key
@@ -1281,6 +1299,7 @@ class LokotroHttpClientService {
1281
1299
  * Build request headers
1282
1300
  */
1283
1301
  buildHeaders() {
1302
+ console.log(`[Lokotro HTTP #${this.instanceId}] buildHeaders - appKey value:`, this.appKey?.substring(0, 20) + '...');
1284
1303
  let headers = new HttpHeaders({
1285
1304
  'Content-Type': 'application/json',
1286
1305
  'Accept': 'application/json',
@@ -1290,6 +1309,10 @@ class LokotroHttpClientService {
1290
1309
  });
1291
1310
  if (this.appKey) {
1292
1311
  headers = headers.set('app-key', this.appKey);
1312
+ console.log(`[Lokotro HTTP #${this.instanceId}] app-key header added`);
1313
+ }
1314
+ else {
1315
+ console.warn(`[Lokotro HTTP #${this.instanceId}] WARNING: appKey is not set, app-key header NOT added`);
1293
1316
  }
1294
1317
  // Add custom headers
1295
1318
  Object.entries(this.customHeaders).forEach(([key, value]) => {
@@ -1375,9 +1398,28 @@ class LokotroHttpClientService {
1375
1398
  if (error instanceof HttpErrorResponse) {
1376
1399
  // Server error
1377
1400
  const serverError = error.error;
1401
+ let errorMessage = '';
1402
+ if (serverError?.['message'] && typeof serverError['message'] === 'string') {
1403
+ errorMessage = serverError['message'];
1404
+ }
1405
+ else if (serverError?.['detail']) {
1406
+ const detail = serverError['detail'];
1407
+ if (Array.isArray(detail)) {
1408
+ errorMessage = detail.map((d) => d.msg || d.message || JSON.stringify(d)).join('; ');
1409
+ }
1410
+ else if (typeof detail === 'string') {
1411
+ errorMessage = detail;
1412
+ }
1413
+ else {
1414
+ errorMessage = JSON.stringify(detail);
1415
+ }
1416
+ }
1417
+ else {
1418
+ errorMessage = error.message || 'An error occurred';
1419
+ }
1378
1420
  errorResponse = {
1379
1421
  data: undefined,
1380
- message: serverError?.['message'] || error.message || 'An error occurred',
1422
+ message: errorMessage,
1381
1423
  statusCode: error.status,
1382
1424
  isSuccess: false,
1383
1425
  apiResponseCode: serverError?.['api_response_code']
@@ -1432,10 +1474,10 @@ class LokotroHttpClientService {
1432
1474
  return LokotroPayApiResponseCode.LOK001; // General error
1433
1475
  }
1434
1476
  }
1435
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroHttpClientService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
1436
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroHttpClientService, providedIn: 'root' }); }
1477
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroHttpClientService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
1478
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroHttpClientService, providedIn: 'root' }); }
1437
1479
  }
1438
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroHttpClientService, decorators: [{
1480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroHttpClientService, decorators: [{
1439
1481
  type: Injectable,
1440
1482
  args: [{
1441
1483
  providedIn: 'root'
@@ -1544,10 +1586,10 @@ class LokotroCountryService {
1544
1586
  this.countriesCache = undefined;
1545
1587
  this.countriesSubject.next([]);
1546
1588
  }
1547
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroCountryService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1548
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroCountryService, providedIn: 'root' }); }
1589
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroCountryService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1590
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroCountryService, providedIn: 'root' }); }
1549
1591
  }
1550
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroCountryService, decorators: [{
1592
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroCountryService, decorators: [{
1551
1593
  type: Injectable,
1552
1594
  args: [{
1553
1595
  providedIn: 'root'
@@ -1709,8 +1751,8 @@ class LokotroMobileMoneyPhoneInputComponent {
1709
1751
  }
1710
1752
  return null;
1711
1753
  }
1712
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, deps: [{ token: LokotroCountryService }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1713
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroMobileMoneyPhoneInputComponent, isStandalone: true, selector: "lokotro-mobile-money-phone-input", inputs: { label: "label", placeholder: "placeholder", initialCountryCode: "initialCountryCode", showPrefixHints: "showPrefixHints", disabled: "disabled" }, outputs: { countryChanged: "countryChanged", phoneChanged: "phoneChanged" }, providers: [
1754
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, deps: [{ token: LokotroCountryService }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1755
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroMobileMoneyPhoneInputComponent, isStandalone: true, selector: "lokotro-mobile-money-phone-input", inputs: { label: "label", placeholder: "placeholder", initialCountryCode: "initialCountryCode", showPrefixHints: "showPrefixHints", disabled: "disabled" }, outputs: { countryChanged: "countryChanged", phoneChanged: "phoneChanged" }, providers: [
1714
1756
  {
1715
1757
  provide: NG_VALUE_ACCESSOR,
1716
1758
  useExisting: forwardRef(() => LokotroMobileMoneyPhoneInputComponent),
@@ -1724,26 +1766,34 @@ class LokotroMobileMoneyPhoneInputComponent {
1724
1766
  ], ngImport: i0, template: `
1725
1767
  <div class="lokotro-phone-input-container">
1726
1768
  <!-- Label -->
1727
- <label class="lokotro-label" *ngIf="label">{{ label }}</label>
1728
-
1769
+ @if (label) {
1770
+ <label class="lokotro-label">{{ label }}</label>
1771
+ }
1772
+
1729
1773
  <!-- Input Row -->
1730
1774
  <div class="lokotro-phone-input-row">
1731
1775
  <!-- Country Selector -->
1732
- <button
1776
+ <button
1733
1777
  type="button"
1734
1778
  class="lokotro-country-selector"
1735
1779
  [disabled]="disabled || isLoading"
1736
1780
  (click)="toggleCountryPicker()">
1737
- <span class="lokotro-flag" *ngIf="selectedCountry && !isLoading">
1738
- {{ getFlag(selectedCountry) }}
1739
- </span>
1740
- <span class="lokotro-loading-spinner" *ngIf="isLoading"></span>
1741
- <span class="lokotro-country-code" *ngIf="selectedCountry">
1742
- +{{ getPrimaryCode(selectedCountry) }}
1743
- </span>
1781
+ @if (selectedCountry && !isLoading) {
1782
+ <span class="lokotro-flag">
1783
+ {{ getFlag(selectedCountry) }}
1784
+ </span>
1785
+ }
1786
+ @if (isLoading) {
1787
+ <span class="lokotro-loading-spinner"></span>
1788
+ }
1789
+ @if (selectedCountry) {
1790
+ <span class="lokotro-country-code">
1791
+ +{{ getPrimaryCode(selectedCountry) }}
1792
+ </span>
1793
+ }
1744
1794
  <span class="lokotro-dropdown-arrow">▼</span>
1745
1795
  </button>
1746
-
1796
+
1747
1797
  <!-- Phone Input -->
1748
1798
  <input
1749
1799
  type="tel"
@@ -1756,60 +1806,74 @@ class LokotroMobileMoneyPhoneInputComponent {
1756
1806
  (input)="onPhoneInput($event)"
1757
1807
  (blur)="onTouched()">
1758
1808
  </div>
1759
-
1809
+
1760
1810
  <!-- Prefix Error -->
1761
- <div class="lokotro-prefix-error" *ngIf="prefixError">
1762
- {{ prefixError }}
1763
- </div>
1764
-
1811
+ @if (prefixError) {
1812
+ <div class="lokotro-prefix-error">
1813
+ {{ prefixError }}
1814
+ </div>
1815
+ }
1816
+
1765
1817
  <!-- Valid Prefixes Hint -->
1766
- <div class="lokotro-prefix-hints" *ngIf="selectedCountry && showPrefixHints">
1767
- <span class="lokotro-hint-label">{{ localization.translate('validPrefixes') }}</span>
1768
- <span
1769
- class="lokotro-prefix-chip"
1770
- *ngFor="let prefix of getVisiblePrefixes(selectedCountry)">
1771
- {{ prefix }}
1772
- </span>
1773
- <span
1774
- class="lokotro-more-prefixes"
1775
- *ngIf="getValidPrefixes(selectedCountry).length > 6">
1776
- +{{ getValidPrefixes(selectedCountry).length - 6 }} {{ localization.translate('more') }}
1777
- </span>
1778
- </div>
1779
-
1780
- <!-- Country Picker Dropdown -->
1781
- <div class="lokotro-country-picker" *ngIf="showCountryPicker">
1782
- <div class="lokotro-picker-header">
1783
- <span class="lokotro-picker-title">{{ localization.translate('selectCountry') }}</span>
1784
- <button type="button" class="lokotro-picker-close" (click)="closeCountryPicker()">×</button>
1818
+ @if (selectedCountry && showPrefixHints) {
1819
+ <div class="lokotro-prefix-hints">
1820
+ <span class="lokotro-hint-label">{{ localization.translate('validPrefixes') }}</span>
1821
+ @for (prefix of getVisiblePrefixes(selectedCountry); track prefix) {
1822
+ <span
1823
+ class="lokotro-prefix-chip"
1824
+ >
1825
+ {{ prefix }}
1826
+ </span>
1827
+ }
1828
+ @if (getValidPrefixes(selectedCountry).length > 6) {
1829
+ <span
1830
+ class="lokotro-more-prefixes"
1831
+ >
1832
+ +{{ getValidPrefixes(selectedCountry).length - 6 }} {{ localization.translate('more') }}
1833
+ </span>
1834
+ }
1785
1835
  </div>
1786
- <div class="lokotro-picker-list">
1787
- <button
1788
- type="button"
1789
- class="lokotro-country-option"
1790
- *ngFor="let country of countries"
1791
- [class.selected]="selectedCountry?.refCountry?.id === country.refCountry.id"
1792
- (click)="selectCountry(country)">
1793
- <span class="lokotro-option-flag">{{ getFlag(country) }}</span>
1794
- <span class="lokotro-option-name">{{ country.refCountry.name | uppercase }}</span>
1795
- <span class="lokotro-option-code">+{{ getPrimaryCode(country) }}</span>
1796
- <span class="lokotro-option-check" *ngIf="selectedCountry?.refCountry?.id === country.refCountry.id">✓</span>
1797
- </button>
1836
+ }
1837
+
1838
+ <!-- Country Picker Dropdown -->
1839
+ @if (showCountryPicker) {
1840
+ <div class="lokotro-country-picker">
1841
+ <div class="lokotro-picker-header">
1842
+ <span class="lokotro-picker-title">{{ localization.translate('selectCountry') }}</span>
1843
+ <button type="button" class="lokotro-picker-close" (click)="closeCountryPicker()">×</button>
1844
+ </div>
1845
+ <div class="lokotro-picker-list">
1846
+ @for (country of countries; track country) {
1847
+ <button
1848
+ type="button"
1849
+ class="lokotro-country-option"
1850
+ [class.selected]="selectedCountry?.refCountry?.id === country.refCountry.id"
1851
+ (click)="selectCountry(country)">
1852
+ <span class="lokotro-option-flag">{{ getFlag(country) }}</span>
1853
+ <span class="lokotro-option-name">{{ country.refCountry.name | uppercase }}</span>
1854
+ <span class="lokotro-option-code">+{{ getPrimaryCode(country) }}</span>
1855
+ @if (selectedCountry?.refCountry?.id === country.refCountry.id) {
1856
+ <span class="lokotro-option-check">✓</span>
1857
+ }
1858
+ </button>
1859
+ }
1860
+ </div>
1798
1861
  </div>
1799
- </div>
1800
-
1862
+ }
1863
+
1801
1864
  <!-- Backdrop for picker -->
1802
- <div
1803
- class="lokotro-picker-backdrop"
1804
- *ngIf="showCountryPicker"
1805
- (click)="closeCountryPicker()">
1806
- </div>
1865
+ @if (showCountryPicker) {
1866
+ <div
1867
+ class="lokotro-picker-backdrop"
1868
+ (click)="closeCountryPicker()">
1869
+ </div>
1870
+ }
1807
1871
  </div>
1808
- `, isInline: true, styles: [".lokotro-phone-input-container{position:relative;width:100%}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-phone-input-row{display:flex;gap:12px;align-items:stretch}.lokotro-country-selector{display:flex;align-items:center;gap:8px;padding:12px 14px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;transition:border-color .2s;white-space:nowrap}.lokotro-country-selector:hover:not(:disabled){border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-country-selector:disabled{opacity:.6;cursor:not-allowed}.lokotro-flag{font-size:24px;line-height:1}.lokotro-country-code{font-size:16px;font-weight:600}.lokotro-dropdown-arrow{font-size:10px;opacity:.6}.lokotro-loading-spinner{width:20px;height:20px;border:2px solid var(--lokotro-border, #3A473F);border-top-color:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.lokotro-phone-input{flex:1;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;min-width:0}.lokotro-phone-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-phone-input.error{border-color:var(--lokotro-error, #D97652)}.lokotro-phone-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-phone-input:disabled{opacity:.6;cursor:not-allowed}.lokotro-prefix-error{margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-prefix-hints{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:8px}.lokotro-hint-label{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-prefix-chip{padding:2px 8px;background:#3bfbda1a;border-radius:4px;font-size:12px;font-weight:600;color:var(--lokotro-accent, #3BFBDA)}.lokotro-more-prefixes{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-country-picker{position:absolute;top:100%;left:0;right:0;max-height:300px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;margin-top:4px;z-index:1000;overflow:hidden;display:flex;flex-direction:column}.lokotro-picker-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-picker-title{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-picker-close{background:none;border:none;font-size:24px;color:var(--lokotro-text-secondary, #D5D3B8);cursor:pointer;padding:0;line-height:1}.lokotro-picker-list{overflow-y:auto;max-height:250px}.lokotro-country-option{display:flex;align-items:center;gap:12px;width:100%;padding:12px 16px;background:none;border:none;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;text-align:left;transition:background-color .2s}.lokotro-country-option:hover{background:var(--lokotro-surface, #2A3832)}.lokotro-country-option.selected{background:#3bfbda1a}.lokotro-option-flag{font-size:28px}.lokotro-option-name{flex:1;font-size:14px;font-weight:500}.lokotro-option-code{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-option-check{color:var(--lokotro-accent, #3BFBDA);font-weight:700}.lokotro-picker-backdrop{position:fixed;inset:0;z-index:999}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "pipe", type: i2.UpperCasePipe, name: "uppercase" }] }); }
1872
+ `, isInline: true, styles: [".lokotro-phone-input-container{position:relative;width:100%}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-phone-input-row{display:flex;gap:12px;align-items:stretch}.lokotro-country-selector{display:flex;align-items:center;gap:8px;padding:12px 14px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;transition:border-color .2s;white-space:nowrap}.lokotro-country-selector:hover:not(:disabled){border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-country-selector:disabled{opacity:.6;cursor:not-allowed}.lokotro-flag{font-size:24px;line-height:1}.lokotro-country-code{font-size:16px;font-weight:600}.lokotro-dropdown-arrow{font-size:10px;opacity:.6}.lokotro-loading-spinner{width:20px;height:20px;border:2px solid var(--lokotro-border, #3A473F);border-top-color:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.lokotro-phone-input{flex:1;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;min-width:0}.lokotro-phone-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-phone-input.error{border-color:var(--lokotro-error, #D97652)}.lokotro-phone-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-phone-input:disabled{opacity:.6;cursor:not-allowed}.lokotro-prefix-error{margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-prefix-hints{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:8px}.lokotro-hint-label{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-prefix-chip{padding:2px 8px;background:#3bfbda1a;border-radius:4px;font-size:12px;font-weight:600;color:var(--lokotro-accent, #3BFBDA)}.lokotro-more-prefixes{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-country-picker{position:absolute;top:100%;left:0;right:0;max-height:300px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;margin-top:4px;z-index:1000;overflow:hidden;display:flex;flex-direction:column}.lokotro-picker-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-picker-title{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-picker-close{background:none;border:none;font-size:24px;color:var(--lokotro-text-secondary, #D5D3B8);cursor:pointer;padding:0;line-height:1}.lokotro-picker-list{overflow-y:auto;max-height:250px}.lokotro-country-option{display:flex;align-items:center;gap:12px;width:100%;padding:12px 16px;background:none;border:none;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;text-align:left;transition:background-color .2s}.lokotro-country-option:hover{background:var(--lokotro-surface, #2A3832)}.lokotro-country-option.selected{background:#3bfbda1a}.lokotro-option-flag{font-size:28px}.lokotro-option-name{flex:1;font-size:14px;font-weight:500}.lokotro-option-code{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-option-check{color:var(--lokotro-accent, #3BFBDA);font-weight:700}.lokotro-picker-backdrop{position:fixed;inset:0;z-index:999}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "pipe", type: UpperCasePipe, name: "uppercase" }] }); }
1809
1873
  }
1810
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, decorators: [{
1874
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, decorators: [{
1811
1875
  type: Component,
1812
- args: [{ selector: 'lokotro-mobile-money-phone-input', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule], providers: [
1876
+ args: [{ selector: 'lokotro-mobile-money-phone-input', standalone: true, imports: [FormsModule, ReactiveFormsModule, UpperCasePipe], providers: [
1813
1877
  {
1814
1878
  provide: NG_VALUE_ACCESSOR,
1815
1879
  useExisting: forwardRef(() => LokotroMobileMoneyPhoneInputComponent),
@@ -1823,26 +1887,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1823
1887
  ], template: `
1824
1888
  <div class="lokotro-phone-input-container">
1825
1889
  <!-- Label -->
1826
- <label class="lokotro-label" *ngIf="label">{{ label }}</label>
1827
-
1890
+ @if (label) {
1891
+ <label class="lokotro-label">{{ label }}</label>
1892
+ }
1893
+
1828
1894
  <!-- Input Row -->
1829
1895
  <div class="lokotro-phone-input-row">
1830
1896
  <!-- Country Selector -->
1831
- <button
1897
+ <button
1832
1898
  type="button"
1833
1899
  class="lokotro-country-selector"
1834
1900
  [disabled]="disabled || isLoading"
1835
1901
  (click)="toggleCountryPicker()">
1836
- <span class="lokotro-flag" *ngIf="selectedCountry && !isLoading">
1837
- {{ getFlag(selectedCountry) }}
1838
- </span>
1839
- <span class="lokotro-loading-spinner" *ngIf="isLoading"></span>
1840
- <span class="lokotro-country-code" *ngIf="selectedCountry">
1841
- +{{ getPrimaryCode(selectedCountry) }}
1842
- </span>
1902
+ @if (selectedCountry && !isLoading) {
1903
+ <span class="lokotro-flag">
1904
+ {{ getFlag(selectedCountry) }}
1905
+ </span>
1906
+ }
1907
+ @if (isLoading) {
1908
+ <span class="lokotro-loading-spinner"></span>
1909
+ }
1910
+ @if (selectedCountry) {
1911
+ <span class="lokotro-country-code">
1912
+ +{{ getPrimaryCode(selectedCountry) }}
1913
+ </span>
1914
+ }
1843
1915
  <span class="lokotro-dropdown-arrow">▼</span>
1844
1916
  </button>
1845
-
1917
+
1846
1918
  <!-- Phone Input -->
1847
1919
  <input
1848
1920
  type="tel"
@@ -1855,56 +1927,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1855
1927
  (input)="onPhoneInput($event)"
1856
1928
  (blur)="onTouched()">
1857
1929
  </div>
1858
-
1930
+
1859
1931
  <!-- Prefix Error -->
1860
- <div class="lokotro-prefix-error" *ngIf="prefixError">
1861
- {{ prefixError }}
1862
- </div>
1863
-
1932
+ @if (prefixError) {
1933
+ <div class="lokotro-prefix-error">
1934
+ {{ prefixError }}
1935
+ </div>
1936
+ }
1937
+
1864
1938
  <!-- Valid Prefixes Hint -->
1865
- <div class="lokotro-prefix-hints" *ngIf="selectedCountry && showPrefixHints">
1866
- <span class="lokotro-hint-label">{{ localization.translate('validPrefixes') }}</span>
1867
- <span
1868
- class="lokotro-prefix-chip"
1869
- *ngFor="let prefix of getVisiblePrefixes(selectedCountry)">
1870
- {{ prefix }}
1871
- </span>
1872
- <span
1873
- class="lokotro-more-prefixes"
1874
- *ngIf="getValidPrefixes(selectedCountry).length > 6">
1875
- +{{ getValidPrefixes(selectedCountry).length - 6 }} {{ localization.translate('more') }}
1876
- </span>
1877
- </div>
1878
-
1879
- <!-- Country Picker Dropdown -->
1880
- <div class="lokotro-country-picker" *ngIf="showCountryPicker">
1881
- <div class="lokotro-picker-header">
1882
- <span class="lokotro-picker-title">{{ localization.translate('selectCountry') }}</span>
1883
- <button type="button" class="lokotro-picker-close" (click)="closeCountryPicker()">×</button>
1939
+ @if (selectedCountry && showPrefixHints) {
1940
+ <div class="lokotro-prefix-hints">
1941
+ <span class="lokotro-hint-label">{{ localization.translate('validPrefixes') }}</span>
1942
+ @for (prefix of getVisiblePrefixes(selectedCountry); track prefix) {
1943
+ <span
1944
+ class="lokotro-prefix-chip"
1945
+ >
1946
+ {{ prefix }}
1947
+ </span>
1948
+ }
1949
+ @if (getValidPrefixes(selectedCountry).length > 6) {
1950
+ <span
1951
+ class="lokotro-more-prefixes"
1952
+ >
1953
+ +{{ getValidPrefixes(selectedCountry).length - 6 }} {{ localization.translate('more') }}
1954
+ </span>
1955
+ }
1884
1956
  </div>
1885
- <div class="lokotro-picker-list">
1886
- <button
1887
- type="button"
1888
- class="lokotro-country-option"
1889
- *ngFor="let country of countries"
1890
- [class.selected]="selectedCountry?.refCountry?.id === country.refCountry.id"
1891
- (click)="selectCountry(country)">
1892
- <span class="lokotro-option-flag">{{ getFlag(country) }}</span>
1893
- <span class="lokotro-option-name">{{ country.refCountry.name | uppercase }}</span>
1894
- <span class="lokotro-option-code">+{{ getPrimaryCode(country) }}</span>
1895
- <span class="lokotro-option-check" *ngIf="selectedCountry?.refCountry?.id === country.refCountry.id">✓</span>
1896
- </button>
1957
+ }
1958
+
1959
+ <!-- Country Picker Dropdown -->
1960
+ @if (showCountryPicker) {
1961
+ <div class="lokotro-country-picker">
1962
+ <div class="lokotro-picker-header">
1963
+ <span class="lokotro-picker-title">{{ localization.translate('selectCountry') }}</span>
1964
+ <button type="button" class="lokotro-picker-close" (click)="closeCountryPicker()">×</button>
1965
+ </div>
1966
+ <div class="lokotro-picker-list">
1967
+ @for (country of countries; track country) {
1968
+ <button
1969
+ type="button"
1970
+ class="lokotro-country-option"
1971
+ [class.selected]="selectedCountry?.refCountry?.id === country.refCountry.id"
1972
+ (click)="selectCountry(country)">
1973
+ <span class="lokotro-option-flag">{{ getFlag(country) }}</span>
1974
+ <span class="lokotro-option-name">{{ country.refCountry.name | uppercase }}</span>
1975
+ <span class="lokotro-option-code">+{{ getPrimaryCode(country) }}</span>
1976
+ @if (selectedCountry?.refCountry?.id === country.refCountry.id) {
1977
+ <span class="lokotro-option-check">✓</span>
1978
+ }
1979
+ </button>
1980
+ }
1981
+ </div>
1897
1982
  </div>
1898
- </div>
1899
-
1983
+ }
1984
+
1900
1985
  <!-- Backdrop for picker -->
1901
- <div
1902
- class="lokotro-picker-backdrop"
1903
- *ngIf="showCountryPicker"
1904
- (click)="closeCountryPicker()">
1905
- </div>
1986
+ @if (showCountryPicker) {
1987
+ <div
1988
+ class="lokotro-picker-backdrop"
1989
+ (click)="closeCountryPicker()">
1990
+ </div>
1991
+ }
1906
1992
  </div>
1907
- `, styles: [".lokotro-phone-input-container{position:relative;width:100%}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-phone-input-row{display:flex;gap:12px;align-items:stretch}.lokotro-country-selector{display:flex;align-items:center;gap:8px;padding:12px 14px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;transition:border-color .2s;white-space:nowrap}.lokotro-country-selector:hover:not(:disabled){border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-country-selector:disabled{opacity:.6;cursor:not-allowed}.lokotro-flag{font-size:24px;line-height:1}.lokotro-country-code{font-size:16px;font-weight:600}.lokotro-dropdown-arrow{font-size:10px;opacity:.6}.lokotro-loading-spinner{width:20px;height:20px;border:2px solid var(--lokotro-border, #3A473F);border-top-color:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.lokotro-phone-input{flex:1;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;min-width:0}.lokotro-phone-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-phone-input.error{border-color:var(--lokotro-error, #D97652)}.lokotro-phone-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-phone-input:disabled{opacity:.6;cursor:not-allowed}.lokotro-prefix-error{margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-prefix-hints{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:8px}.lokotro-hint-label{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-prefix-chip{padding:2px 8px;background:#3bfbda1a;border-radius:4px;font-size:12px;font-weight:600;color:var(--lokotro-accent, #3BFBDA)}.lokotro-more-prefixes{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-country-picker{position:absolute;top:100%;left:0;right:0;max-height:300px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;margin-top:4px;z-index:1000;overflow:hidden;display:flex;flex-direction:column}.lokotro-picker-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-picker-title{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-picker-close{background:none;border:none;font-size:24px;color:var(--lokotro-text-secondary, #D5D3B8);cursor:pointer;padding:0;line-height:1}.lokotro-picker-list{overflow-y:auto;max-height:250px}.lokotro-country-option{display:flex;align-items:center;gap:12px;width:100%;padding:12px 16px;background:none;border:none;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;text-align:left;transition:background-color .2s}.lokotro-country-option:hover{background:var(--lokotro-surface, #2A3832)}.lokotro-country-option.selected{background:#3bfbda1a}.lokotro-option-flag{font-size:28px}.lokotro-option-name{flex:1;font-size:14px;font-weight:500}.lokotro-option-code{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-option-check{color:var(--lokotro-accent, #3BFBDA);font-weight:700}.lokotro-picker-backdrop{position:fixed;inset:0;z-index:999}\n"] }]
1993
+ `, styles: [".lokotro-phone-input-container{position:relative;width:100%}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-phone-input-row{display:flex;gap:12px;align-items:stretch}.lokotro-country-selector{display:flex;align-items:center;gap:8px;padding:12px 14px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;transition:border-color .2s;white-space:nowrap}.lokotro-country-selector:hover:not(:disabled){border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-country-selector:disabled{opacity:.6;cursor:not-allowed}.lokotro-flag{font-size:24px;line-height:1}.lokotro-country-code{font-size:16px;font-weight:600}.lokotro-dropdown-arrow{font-size:10px;opacity:.6}.lokotro-loading-spinner{width:20px;height:20px;border:2px solid var(--lokotro-border, #3A473F);border-top-color:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.lokotro-phone-input{flex:1;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;min-width:0}.lokotro-phone-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-phone-input.error{border-color:var(--lokotro-error, #D97652)}.lokotro-phone-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-phone-input:disabled{opacity:.6;cursor:not-allowed}.lokotro-prefix-error{margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-prefix-hints{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:8px}.lokotro-hint-label{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-prefix-chip{padding:2px 8px;background:#3bfbda1a;border-radius:4px;font-size:12px;font-weight:600;color:var(--lokotro-accent, #3BFBDA)}.lokotro-more-prefixes{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-country-picker{position:absolute;top:100%;left:0;right:0;max-height:300px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;margin-top:4px;z-index:1000;overflow:hidden;display:flex;flex-direction:column}.lokotro-picker-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-picker-title{font-size:16px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-picker-close{background:none;border:none;font-size:24px;color:var(--lokotro-text-secondary, #D5D3B8);cursor:pointer;padding:0;line-height:1}.lokotro-picker-list{overflow-y:auto;max-height:250px}.lokotro-country-option{display:flex;align-items:center;gap:12px;width:100%;padding:12px 16px;background:none;border:none;color:var(--lokotro-text-primary, #F2F0D5);cursor:pointer;text-align:left;transition:background-color .2s}.lokotro-country-option:hover{background:var(--lokotro-surface, #2A3832)}.lokotro-country-option.selected{background:#3bfbda1a}.lokotro-option-flag{font-size:28px}.lokotro-option-name{flex:1;font-size:14px;font-weight:500}.lokotro-option-code{font-size:12px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-option-check{color:var(--lokotro-accent, #3BFBDA);font-weight:700}.lokotro-picker-backdrop{position:fixed;inset:0;z-index:999}\n"] }]
1908
1994
  }], ctorParameters: () => [{ type: LokotroCountryService }, { type: LokotroLocalizationService }], propDecorators: { label: [{
1909
1995
  type: Input
1910
1996
  }], placeholder: [{
@@ -2049,507 +2135,549 @@ class LokotroPaymentFormComponent {
2049
2135
  onCancel() {
2050
2136
  this.cancel.emit();
2051
2137
  }
2052
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentFormComponent, deps: [{ token: i1$1.FormBuilder }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2053
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroPaymentFormComponent, isStandalone: true, selector: "lokotro-payment-form", inputs: { channel: "channel", transactionId: "transactionId", showUserInfoForm: "showUserInfoForm" }, outputs: { formSubmitted: "formSubmitted", cancel: "cancel" }, ngImport: i0, template: `
2138
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentFormComponent, deps: [{ token: i1$1.FormBuilder }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2139
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroPaymentFormComponent, isStandalone: true, selector: "lokotro-payment-form", inputs: { channel: "channel", transactionId: "transactionId", showUserInfoForm: "showUserInfoForm" }, outputs: { formSubmitted: "formSubmitted", cancel: "cancel" }, ngImport: i0, template: `
2054
2140
  <div class="lokotro-payment-form">
2055
2141
  <!-- E-Wallet Form -->
2056
- <form *ngIf="isEWalletForm" [formGroup]="ewalletForm" (ngSubmit)="submitEWalletForm()">
2057
- <h3 class="lokotro-form-title">{{ localization.translate('eWallet') }}</h3>
2058
-
2059
- <div class="lokotro-form-group">
2060
- <label class="lokotro-label">{{ localization.translate('walletNumber') }}</label>
2061
- <input
2062
- type="text"
2063
- class="lokotro-input"
2064
- formControlName="walletNumber"
2065
- placeholder="Enter wallet number">
2066
- <span class="lokotro-error" *ngIf="ewalletForm.get('walletNumber')?.touched && ewalletForm.get('walletNumber')?.invalid">
2067
- {{ localization.translate('required') }}
2068
- </span>
2069
- </div>
2070
-
2071
- <div class="lokotro-form-group">
2072
- <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2073
- <input
2074
- type="password"
2075
- class="lokotro-input"
2076
- formControlName="walletPin"
2077
- placeholder="Enter PIN"
2078
- maxlength="6">
2079
- <span class="lokotro-error" *ngIf="ewalletForm.get('walletPin')?.touched && ewalletForm.get('walletPin')?.invalid">
2080
- {{ localization.translate('required') }}
2081
- </span>
2082
- </div>
2083
-
2084
- <div class="lokotro-form-actions">
2085
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2086
- {{ localization.translate('cancel') }}
2087
- </button>
2088
- <button type="submit" class="lokotro-btn-primary" [disabled]="ewalletForm.invalid">
2089
- {{ localization.translate('confirm') }}
2090
- </button>
2091
- </div>
2092
- </form>
2093
-
2094
- <!-- Mobile Money Form -->
2095
- <form *ngIf="isMobileMoneyForm" [formGroup]="mobileMoneyForm" (ngSubmit)="submitMobileMoneyForm()">
2096
- <h3 class="lokotro-form-title">{{ localization.translate('mobileMoneyPayment') }}</h3>
2097
-
2098
- <!-- Personal Information Section - shown when showUserInfoForm is true -->
2099
- <div class="lokotro-user-info-section" *ngIf="showUserInfoForm">
2100
- <h4 class="lokotro-section-subtitle">{{ localization.translate('personalInfo') }}</h4>
2101
-
2102
- <div class="lokotro-form-row">
2103
- <div class="lokotro-form-group">
2104
- <label class="lokotro-label">{{ localization.translate('firstName') }}</label>
2105
- <input
2106
- type="text"
2107
- class="lokotro-input"
2108
- formControlName="firstName"
2109
- placeholder="John">
2110
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('firstName')?.touched && mobileMoneyForm.get('firstName')?.invalid">
2142
+ @if (isEWalletForm) {
2143
+ <form [formGroup]="ewalletForm" (ngSubmit)="submitEWalletForm()">
2144
+ <h3 class="lokotro-form-title">{{ localization.translate('eWallet') }}</h3>
2145
+ <div class="lokotro-form-group">
2146
+ <label class="lokotro-label">{{ localization.translate('walletNumber') }}</label>
2147
+ <input
2148
+ type="text"
2149
+ class="lokotro-input"
2150
+ formControlName="walletNumber"
2151
+ placeholder="Enter wallet number">
2152
+ @if (ewalletForm.get('walletNumber')?.touched && ewalletForm.get('walletNumber')?.invalid) {
2153
+ <span class="lokotro-error">
2111
2154
  {{ localization.translate('required') }}
2112
2155
  </span>
2113
- </div>
2114
-
2115
- <div class="lokotro-form-group">
2116
- <label class="lokotro-label">{{ localization.translate('lastName') }}</label>
2117
- <input
2118
- type="text"
2119
- class="lokotro-input"
2120
- formControlName="lastName"
2121
- placeholder="Doe">
2122
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('lastName')?.touched && mobileMoneyForm.get('lastName')?.invalid">
2156
+ }
2157
+ </div>
2158
+ <div class="lokotro-form-group">
2159
+ <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2160
+ <input
2161
+ type="password"
2162
+ class="lokotro-input"
2163
+ formControlName="walletPin"
2164
+ placeholder="Enter PIN"
2165
+ maxlength="6">
2166
+ @if (ewalletForm.get('walletPin')?.touched && ewalletForm.get('walletPin')?.invalid) {
2167
+ <span class="lokotro-error">
2123
2168
  {{ localization.translate('required') }}
2124
2169
  </span>
2125
- </div>
2170
+ }
2126
2171
  </div>
2127
-
2128
- <div class="lokotro-form-group">
2129
- <label class="lokotro-label">{{ localization.translate('email') }}</label>
2130
- <input
2131
- type="email"
2132
- class="lokotro-input"
2133
- formControlName="email"
2134
- placeholder="john.doe@example.com">
2135
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('email')?.touched && mobileMoneyForm.get('email')?.invalid">
2136
- {{ localization.translate('invalidEmail') }}
2137
- </span>
2172
+ <div class="lokotro-form-actions">
2173
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2174
+ {{ localization.translate('cancel') }}
2175
+ </button>
2176
+ <button type="submit" class="lokotro-btn-primary" [disabled]="ewalletForm.invalid">
2177
+ {{ localization.translate('confirm') }}
2178
+ </button>
2138
2179
  </div>
2139
-
2140
- <div class="lokotro-form-group">
2141
- <label class="lokotro-label">{{ localization.translate('phoneNumber') }} ({{ localization.translate('personalPhone') }})</label>
2142
- <input
2143
- type="tel"
2144
- class="lokotro-input"
2145
- formControlName="personalPhone"
2146
- placeholder="+243 XXX XXX XXX">
2147
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('personalPhone')?.touched && mobileMoneyForm.get('personalPhone')?.invalid">
2148
- {{ localization.translate('invalidPhoneNumber') }}
2180
+ </form>
2181
+ }
2182
+
2183
+ <!-- Mobile Money Form -->
2184
+ @if (isMobileMoneyForm) {
2185
+ <form [formGroup]="mobileMoneyForm" (ngSubmit)="submitMobileMoneyForm()">
2186
+ <h3 class="lokotro-form-title">{{ localization.translate('mobileMoneyPayment') }}</h3>
2187
+ <!-- Personal Information Section - shown when showUserInfoForm is true -->
2188
+ @if (showUserInfoForm) {
2189
+ <div class="lokotro-user-info-section">
2190
+ <h4 class="lokotro-section-subtitle">{{ localization.translate('personalInfo') }}</h4>
2191
+ <div class="lokotro-form-row">
2192
+ <div class="lokotro-form-group">
2193
+ <label class="lokotro-label">{{ localization.translate('firstName') }}</label>
2194
+ <input
2195
+ type="text"
2196
+ class="lokotro-input"
2197
+ formControlName="firstName"
2198
+ placeholder="John">
2199
+ @if (mobileMoneyForm.get('firstName')?.touched && mobileMoneyForm.get('firstName')?.invalid) {
2200
+ <span class="lokotro-error">
2201
+ {{ localization.translate('required') }}
2202
+ </span>
2203
+ }
2204
+ </div>
2205
+ <div class="lokotro-form-group">
2206
+ <label class="lokotro-label">{{ localization.translate('lastName') }}</label>
2207
+ <input
2208
+ type="text"
2209
+ class="lokotro-input"
2210
+ formControlName="lastName"
2211
+ placeholder="Doe">
2212
+ @if (mobileMoneyForm.get('lastName')?.touched && mobileMoneyForm.get('lastName')?.invalid) {
2213
+ <span class="lokotro-error">
2214
+ {{ localization.translate('required') }}
2215
+ </span>
2216
+ }
2217
+ </div>
2218
+ </div>
2219
+ <div class="lokotro-form-group">
2220
+ <label class="lokotro-label">{{ localization.translate('email') }}</label>
2221
+ <input
2222
+ type="email"
2223
+ class="lokotro-input"
2224
+ formControlName="email"
2225
+ placeholder="john.doe@example.com">
2226
+ @if (mobileMoneyForm.get('email')?.touched && mobileMoneyForm.get('email')?.invalid) {
2227
+ <span class="lokotro-error">
2228
+ {{ localization.translate('invalidEmail') }}
2229
+ </span>
2230
+ }
2231
+ </div>
2232
+ <div class="lokotro-form-group">
2233
+ <label class="lokotro-label">{{ localization.translate('phoneNumber') }} ({{ localization.translate('personalPhone') }})</label>
2234
+ <input
2235
+ type="tel"
2236
+ class="lokotro-input"
2237
+ formControlName="personalPhone"
2238
+ placeholder="+243 XXX XXX XXX">
2239
+ @if (mobileMoneyForm.get('personalPhone')?.touched && mobileMoneyForm.get('personalPhone')?.invalid) {
2240
+ <span class="lokotro-error">
2241
+ {{ localization.translate('invalidPhoneNumber') }}
2242
+ </span>
2243
+ }
2244
+ </div>
2245
+ <hr class="lokotro-divider">
2246
+ </div>
2247
+ }
2248
+ <!-- Mobile Money Phone Input -->
2249
+ @if (showUserInfoForm) {
2250
+ <h4 class="lokotro-section-subtitle">{{ localization.translate('mobileMoneyDetails') }}</h4>
2251
+ }
2252
+ <lokotro-mobile-money-phone-input
2253
+ [label]="localization.translate('mobileMoneyPhone')"
2254
+ [initialCountryCode]="'243'"
2255
+ [showPrefixHints]="true"
2256
+ (countryChanged)="onCountryChanged($event)"
2257
+ (phoneChanged)="onPhoneChanged($event)"
2258
+ formControlName="phoneNumber">
2259
+ </lokotro-mobile-money-phone-input>
2260
+ @if (mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('invalidPrefix')) {
2261
+ <span class="lokotro-error">
2262
+ {{ localization.translate('invalidPhonePrefix') }}
2149
2263
  </span>
2264
+ }
2265
+ @if (mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooShort')) {
2266
+ <span class="lokotro-error">
2267
+ {{ localization.translate('phoneTooShort') }}
2268
+ </span>
2269
+ }
2270
+ @if (mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooLong')) {
2271
+ <span class="lokotro-error">
2272
+ {{ localization.translate('phoneTooLong') }}
2273
+ </span>
2274
+ }
2275
+ <div class="lokotro-form-actions">
2276
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2277
+ {{ localization.translate('cancel') }}
2278
+ </button>
2279
+ <button type="submit" class="lokotro-btn-primary" [disabled]="mobileMoneyForm.invalid">
2280
+ {{ localization.translate('confirm') }}
2281
+ </button>
2150
2282
  </div>
2151
-
2152
- <hr class="lokotro-divider">
2153
- </div>
2154
-
2155
- <!-- Mobile Money Phone Input -->
2156
- <h4 class="lokotro-section-subtitle" *ngIf="showUserInfoForm">{{ localization.translate('mobileMoneyDetails') }}</h4>
2157
-
2158
- <lokotro-mobile-money-phone-input
2159
- [label]="localization.translate('mobileMoneyPhone')"
2160
- [initialCountryCode]="'243'"
2161
- [showPrefixHints]="true"
2162
- (countryChanged)="onCountryChanged($event)"
2163
- (phoneChanged)="onPhoneChanged($event)"
2164
- formControlName="phoneNumber">
2165
- </lokotro-mobile-money-phone-input>
2166
-
2167
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('invalidPrefix')">
2168
- {{ localization.translate('invalidPhonePrefix') }}
2169
- </span>
2170
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooShort')">
2171
- {{ localization.translate('phoneTooShort') }}
2172
- </span>
2173
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooLong')">
2174
- {{ localization.translate('phoneTooLong') }}
2175
- </span>
2176
-
2177
- <div class="lokotro-form-actions">
2178
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2179
- {{ localization.translate('cancel') }}
2180
- </button>
2181
- <button type="submit" class="lokotro-btn-primary" [disabled]="mobileMoneyForm.invalid">
2182
- {{ localization.translate('confirm') }}
2183
- </button>
2184
- </div>
2185
- </form>
2186
-
2283
+ </form>
2284
+ }
2285
+
2187
2286
  <!-- Card Form -->
2188
- <form *ngIf="isCardForm" [formGroup]="cardForm" (ngSubmit)="submitCardForm()">
2189
- <h3 class="lokotro-form-title">{{ localization.translate('card') }}</h3>
2190
-
2191
- <div class="lokotro-form-group">
2192
- <label class="lokotro-label">{{ localization.translate('cardNumber') }}</label>
2193
- <input
2194
- type="text"
2195
- class="lokotro-input"
2196
- formControlName="cardNumber"
2197
- placeholder="1234 5678 9012 3456"
2198
- maxlength="19"
2199
- (input)="formatCardNumber($event)">
2200
- <span class="lokotro-error" *ngIf="cardForm.get('cardNumber')?.touched && cardForm.get('cardNumber')?.invalid">
2201
- {{ localization.translate('invalidCardNumber') }}
2202
- </span>
2203
- </div>
2204
-
2205
- <div class="lokotro-form-group">
2206
- <label class="lokotro-label">{{ localization.translate('cardHolderName') }}</label>
2207
- <input
2208
- type="text"
2209
- class="lokotro-input"
2210
- formControlName="cardHolderName"
2211
- placeholder="John Doe">
2212
- <span class="lokotro-error" *ngIf="cardForm.get('cardHolderName')?.touched && cardForm.get('cardHolderName')?.invalid">
2213
- {{ localization.translate('required') }}
2214
- </span>
2215
- </div>
2216
-
2217
- <div class="lokotro-form-row">
2287
+ @if (isCardForm) {
2288
+ <form [formGroup]="cardForm" (ngSubmit)="submitCardForm()">
2289
+ <h3 class="lokotro-form-title">{{ localization.translate('card') }}</h3>
2218
2290
  <div class="lokotro-form-group">
2219
- <label class="lokotro-label">{{ localization.translate('expiryDate') }}</label>
2220
- <input
2221
- type="text"
2222
- class="lokotro-input"
2223
- formControlName="expiryDate"
2224
- placeholder="MM/YY"
2225
- maxlength="5"
2226
- (input)="formatExpiryDate($event)">
2227
- <span class="lokotro-error" *ngIf="cardForm.get('expiryDate')?.touched && cardForm.get('expiryDate')?.invalid">
2228
- {{ localization.translate('invalidExpiryDate') }}
2229
- </span>
2291
+ <label class="lokotro-label">{{ localization.translate('cardNumber') }}</label>
2292
+ <input
2293
+ type="text"
2294
+ class="lokotro-input"
2295
+ formControlName="cardNumber"
2296
+ placeholder="1234 5678 9012 3456"
2297
+ maxlength="19"
2298
+ (input)="formatCardNumber($event)">
2299
+ @if (cardForm.get('cardNumber')?.touched && cardForm.get('cardNumber')?.invalid) {
2300
+ <span class="lokotro-error">
2301
+ {{ localization.translate('invalidCardNumber') }}
2302
+ </span>
2303
+ }
2230
2304
  </div>
2231
-
2232
2305
  <div class="lokotro-form-group">
2233
- <label class="lokotro-label">{{ localization.translate('cvv') }}</label>
2234
- <input
2235
- type="password"
2236
- class="lokotro-input"
2237
- formControlName="cvv"
2238
- placeholder="123"
2239
- maxlength="4">
2240
- <span class="lokotro-error" *ngIf="cardForm.get('cvv')?.touched && cardForm.get('cvv')?.invalid">
2241
- {{ localization.translate('invalidCvv') }}
2242
- </span>
2306
+ <label class="lokotro-label">{{ localization.translate('cardHolderName') }}</label>
2307
+ <input
2308
+ type="text"
2309
+ class="lokotro-input"
2310
+ formControlName="cardHolderName"
2311
+ placeholder="John Doe">
2312
+ @if (cardForm.get('cardHolderName')?.touched && cardForm.get('cardHolderName')?.invalid) {
2313
+ <span class="lokotro-error">
2314
+ {{ localization.translate('required') }}
2315
+ </span>
2316
+ }
2243
2317
  </div>
2244
- </div>
2245
-
2246
- <div class="lokotro-form-actions">
2247
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2248
- {{ localization.translate('cancel') }}
2249
- </button>
2250
- <button type="submit" class="lokotro-btn-primary" [disabled]="cardForm.invalid">
2251
- {{ localization.translate('confirm') }}
2252
- </button>
2253
- </div>
2254
- </form>
2255
-
2318
+ <div class="lokotro-form-row">
2319
+ <div class="lokotro-form-group">
2320
+ <label class="lokotro-label">{{ localization.translate('expiryDate') }}</label>
2321
+ <input
2322
+ type="text"
2323
+ class="lokotro-input"
2324
+ formControlName="expiryDate"
2325
+ placeholder="MM/YY"
2326
+ maxlength="5"
2327
+ (input)="formatExpiryDate($event)">
2328
+ @if (cardForm.get('expiryDate')?.touched && cardForm.get('expiryDate')?.invalid) {
2329
+ <span class="lokotro-error">
2330
+ {{ localization.translate('invalidExpiryDate') }}
2331
+ </span>
2332
+ }
2333
+ </div>
2334
+ <div class="lokotro-form-group">
2335
+ <label class="lokotro-label">{{ localization.translate('cvv') }}</label>
2336
+ <input
2337
+ type="password"
2338
+ class="lokotro-input"
2339
+ formControlName="cvv"
2340
+ placeholder="123"
2341
+ maxlength="4">
2342
+ @if (cardForm.get('cvv')?.touched && cardForm.get('cvv')?.invalid) {
2343
+ <span class="lokotro-error">
2344
+ {{ localization.translate('invalidCvv') }}
2345
+ </span>
2346
+ }
2347
+ </div>
2348
+ </div>
2349
+ <div class="lokotro-form-actions">
2350
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2351
+ {{ localization.translate('cancel') }}
2352
+ </button>
2353
+ <button type="submit" class="lokotro-btn-primary" [disabled]="cardForm.invalid">
2354
+ {{ localization.translate('confirm') }}
2355
+ </button>
2356
+ </div>
2357
+ </form>
2358
+ }
2359
+
2256
2360
  <!-- Flash Form -->
2257
- <form *ngIf="isFlashForm" [formGroup]="flashForm" (ngSubmit)="submitFlashForm()">
2258
- <h3 class="lokotro-form-title">{{ localization.translate('eFlash') }}</h3>
2259
-
2260
- <div class="lokotro-form-group">
2261
- <label class="lokotro-label">Flash Number</label>
2262
- <input
2263
- type="text"
2264
- class="lokotro-input"
2265
- formControlName="flashNumber"
2266
- placeholder="Enter flash number">
2267
- <span class="lokotro-error" *ngIf="flashForm.get('flashNumber')?.touched && flashForm.get('flashNumber')?.invalid">
2268
- {{ localization.translate('required') }}
2269
- </span>
2270
- </div>
2271
-
2272
- <div class="lokotro-form-group">
2273
- <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2274
- <input
2275
- type="password"
2276
- class="lokotro-input"
2277
- formControlName="flashPin"
2278
- placeholder="Enter PIN"
2279
- maxlength="6">
2280
- <span class="lokotro-error" *ngIf="flashForm.get('flashPin')?.touched && flashForm.get('flashPin')?.invalid">
2281
- {{ localization.translate('required') }}
2282
- </span>
2283
- </div>
2284
-
2285
- <div class="lokotro-form-actions">
2286
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2287
- {{ localization.translate('cancel') }}
2288
- </button>
2289
- <button type="submit" class="lokotro-btn-primary" [disabled]="flashForm.invalid">
2290
- {{ localization.translate('confirm') }}
2291
- </button>
2292
- </div>
2293
- </form>
2361
+ @if (isFlashForm) {
2362
+ <form [formGroup]="flashForm" (ngSubmit)="submitFlashForm()">
2363
+ <h3 class="lokotro-form-title">{{ localization.translate('eFlash') }}</h3>
2364
+ <div class="lokotro-form-group">
2365
+ <label class="lokotro-label">Flash Number</label>
2366
+ <input
2367
+ type="text"
2368
+ class="lokotro-input"
2369
+ formControlName="flashNumber"
2370
+ placeholder="Enter flash number">
2371
+ @if (flashForm.get('flashNumber')?.touched && flashForm.get('flashNumber')?.invalid) {
2372
+ <span class="lokotro-error">
2373
+ {{ localization.translate('required') }}
2374
+ </span>
2375
+ }
2376
+ </div>
2377
+ <div class="lokotro-form-group">
2378
+ <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2379
+ <input
2380
+ type="password"
2381
+ class="lokotro-input"
2382
+ formControlName="flashPin"
2383
+ placeholder="Enter PIN"
2384
+ maxlength="6">
2385
+ @if (flashForm.get('flashPin')?.touched && flashForm.get('flashPin')?.invalid) {
2386
+ <span class="lokotro-error">
2387
+ {{ localization.translate('required') }}
2388
+ </span>
2389
+ }
2390
+ </div>
2391
+ <div class="lokotro-form-actions">
2392
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2393
+ {{ localization.translate('cancel') }}
2394
+ </button>
2395
+ <button type="submit" class="lokotro-btn-primary" [disabled]="flashForm.invalid">
2396
+ {{ localization.translate('confirm') }}
2397
+ </button>
2398
+ </div>
2399
+ </form>
2400
+ }
2294
2401
  </div>
2295
- `, isInline: true, styles: [".lokotro-payment-form{width:100%}.lokotro-form-title{font-size:20px;font-weight:600;margin:0 0 24px;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-form-group{margin-bottom:20px}.lokotro-form-row{display:flex;gap:16px}.lokotro-form-row .lokotro-form-group{flex:1}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-input{width:100%;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;box-sizing:border-box}.lokotro-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-error{display:block;margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-user-info-section{margin-bottom:24px}.lokotro-section-subtitle{font-size:16px;font-weight:600;margin:0 0 16px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-divider{border:none;border-top:1px solid var(--lokotro-border, #3A473F);margin:24px 0}.lokotro-form-actions{display:flex;gap:12px;margin-top:32px}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: LokotroMobileMoneyPhoneInputComponent, selector: "lokotro-mobile-money-phone-input", inputs: ["label", "placeholder", "initialCountryCode", "showPrefixHints", "disabled"], outputs: ["countryChanged", "phoneChanged"] }] }); }
2402
+ `, isInline: true, styles: [".lokotro-payment-form{width:100%}.lokotro-form-title{font-size:20px;font-weight:600;margin:0 0 24px;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-form-group{margin-bottom:20px}.lokotro-form-row{display:flex;gap:16px}.lokotro-form-row .lokotro-form-group{flex:1}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-input{width:100%;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;box-sizing:border-box}.lokotro-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-error{display:block;margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-user-info-section{margin-bottom:24px}.lokotro-section-subtitle{font-size:16px;font-weight:600;margin:0 0 16px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-divider{border:none;border-top:1px solid var(--lokotro-border, #3A473F);margin:24px 0}.lokotro-form-actions{display:flex;gap:12px;margin-top:32px}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: LokotroMobileMoneyPhoneInputComponent, selector: "lokotro-mobile-money-phone-input", inputs: ["label", "placeholder", "initialCountryCode", "showPrefixHints", "disabled"], outputs: ["countryChanged", "phoneChanged"] }] }); }
2296
2403
  }
2297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentFormComponent, decorators: [{
2404
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentFormComponent, decorators: [{
2298
2405
  type: Component,
2299
- args: [{ selector: 'lokotro-payment-form', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule, LokotroMobileMoneyPhoneInputComponent], template: `
2406
+ args: [{ selector: 'lokotro-payment-form', standalone: true, imports: [FormsModule, ReactiveFormsModule, LokotroMobileMoneyPhoneInputComponent], template: `
2300
2407
  <div class="lokotro-payment-form">
2301
2408
  <!-- E-Wallet Form -->
2302
- <form *ngIf="isEWalletForm" [formGroup]="ewalletForm" (ngSubmit)="submitEWalletForm()">
2303
- <h3 class="lokotro-form-title">{{ localization.translate('eWallet') }}</h3>
2304
-
2305
- <div class="lokotro-form-group">
2306
- <label class="lokotro-label">{{ localization.translate('walletNumber') }}</label>
2307
- <input
2308
- type="text"
2309
- class="lokotro-input"
2310
- formControlName="walletNumber"
2311
- placeholder="Enter wallet number">
2312
- <span class="lokotro-error" *ngIf="ewalletForm.get('walletNumber')?.touched && ewalletForm.get('walletNumber')?.invalid">
2313
- {{ localization.translate('required') }}
2314
- </span>
2315
- </div>
2316
-
2317
- <div class="lokotro-form-group">
2318
- <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2319
- <input
2320
- type="password"
2321
- class="lokotro-input"
2322
- formControlName="walletPin"
2323
- placeholder="Enter PIN"
2324
- maxlength="6">
2325
- <span class="lokotro-error" *ngIf="ewalletForm.get('walletPin')?.touched && ewalletForm.get('walletPin')?.invalid">
2326
- {{ localization.translate('required') }}
2327
- </span>
2328
- </div>
2329
-
2330
- <div class="lokotro-form-actions">
2331
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2332
- {{ localization.translate('cancel') }}
2333
- </button>
2334
- <button type="submit" class="lokotro-btn-primary" [disabled]="ewalletForm.invalid">
2335
- {{ localization.translate('confirm') }}
2336
- </button>
2337
- </div>
2338
- </form>
2339
-
2340
- <!-- Mobile Money Form -->
2341
- <form *ngIf="isMobileMoneyForm" [formGroup]="mobileMoneyForm" (ngSubmit)="submitMobileMoneyForm()">
2342
- <h3 class="lokotro-form-title">{{ localization.translate('mobileMoneyPayment') }}</h3>
2343
-
2344
- <!-- Personal Information Section - shown when showUserInfoForm is true -->
2345
- <div class="lokotro-user-info-section" *ngIf="showUserInfoForm">
2346
- <h4 class="lokotro-section-subtitle">{{ localization.translate('personalInfo') }}</h4>
2347
-
2348
- <div class="lokotro-form-row">
2349
- <div class="lokotro-form-group">
2350
- <label class="lokotro-label">{{ localization.translate('firstName') }}</label>
2351
- <input
2352
- type="text"
2353
- class="lokotro-input"
2354
- formControlName="firstName"
2355
- placeholder="John">
2356
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('firstName')?.touched && mobileMoneyForm.get('firstName')?.invalid">
2409
+ @if (isEWalletForm) {
2410
+ <form [formGroup]="ewalletForm" (ngSubmit)="submitEWalletForm()">
2411
+ <h3 class="lokotro-form-title">{{ localization.translate('eWallet') }}</h3>
2412
+ <div class="lokotro-form-group">
2413
+ <label class="lokotro-label">{{ localization.translate('walletNumber') }}</label>
2414
+ <input
2415
+ type="text"
2416
+ class="lokotro-input"
2417
+ formControlName="walletNumber"
2418
+ placeholder="Enter wallet number">
2419
+ @if (ewalletForm.get('walletNumber')?.touched && ewalletForm.get('walletNumber')?.invalid) {
2420
+ <span class="lokotro-error">
2357
2421
  {{ localization.translate('required') }}
2358
2422
  </span>
2359
- </div>
2360
-
2361
- <div class="lokotro-form-group">
2362
- <label class="lokotro-label">{{ localization.translate('lastName') }}</label>
2363
- <input
2364
- type="text"
2365
- class="lokotro-input"
2366
- formControlName="lastName"
2367
- placeholder="Doe">
2368
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('lastName')?.touched && mobileMoneyForm.get('lastName')?.invalid">
2423
+ }
2424
+ </div>
2425
+ <div class="lokotro-form-group">
2426
+ <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2427
+ <input
2428
+ type="password"
2429
+ class="lokotro-input"
2430
+ formControlName="walletPin"
2431
+ placeholder="Enter PIN"
2432
+ maxlength="6">
2433
+ @if (ewalletForm.get('walletPin')?.touched && ewalletForm.get('walletPin')?.invalid) {
2434
+ <span class="lokotro-error">
2369
2435
  {{ localization.translate('required') }}
2370
2436
  </span>
2437
+ }
2438
+ </div>
2439
+ <div class="lokotro-form-actions">
2440
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2441
+ {{ localization.translate('cancel') }}
2442
+ </button>
2443
+ <button type="submit" class="lokotro-btn-primary" [disabled]="ewalletForm.invalid">
2444
+ {{ localization.translate('confirm') }}
2445
+ </button>
2446
+ </div>
2447
+ </form>
2448
+ }
2449
+
2450
+ <!-- Mobile Money Form -->
2451
+ @if (isMobileMoneyForm) {
2452
+ <form [formGroup]="mobileMoneyForm" (ngSubmit)="submitMobileMoneyForm()">
2453
+ <h3 class="lokotro-form-title">{{ localization.translate('mobileMoneyPayment') }}</h3>
2454
+ <!-- Personal Information Section - shown when showUserInfoForm is true -->
2455
+ @if (showUserInfoForm) {
2456
+ <div class="lokotro-user-info-section">
2457
+ <h4 class="lokotro-section-subtitle">{{ localization.translate('personalInfo') }}</h4>
2458
+ <div class="lokotro-form-row">
2459
+ <div class="lokotro-form-group">
2460
+ <label class="lokotro-label">{{ localization.translate('firstName') }}</label>
2461
+ <input
2462
+ type="text"
2463
+ class="lokotro-input"
2464
+ formControlName="firstName"
2465
+ placeholder="John">
2466
+ @if (mobileMoneyForm.get('firstName')?.touched && mobileMoneyForm.get('firstName')?.invalid) {
2467
+ <span class="lokotro-error">
2468
+ {{ localization.translate('required') }}
2469
+ </span>
2470
+ }
2471
+ </div>
2472
+ <div class="lokotro-form-group">
2473
+ <label class="lokotro-label">{{ localization.translate('lastName') }}</label>
2474
+ <input
2475
+ type="text"
2476
+ class="lokotro-input"
2477
+ formControlName="lastName"
2478
+ placeholder="Doe">
2479
+ @if (mobileMoneyForm.get('lastName')?.touched && mobileMoneyForm.get('lastName')?.invalid) {
2480
+ <span class="lokotro-error">
2481
+ {{ localization.translate('required') }}
2482
+ </span>
2483
+ }
2484
+ </div>
2485
+ </div>
2486
+ <div class="lokotro-form-group">
2487
+ <label class="lokotro-label">{{ localization.translate('email') }}</label>
2488
+ <input
2489
+ type="email"
2490
+ class="lokotro-input"
2491
+ formControlName="email"
2492
+ placeholder="john.doe@example.com">
2493
+ @if (mobileMoneyForm.get('email')?.touched && mobileMoneyForm.get('email')?.invalid) {
2494
+ <span class="lokotro-error">
2495
+ {{ localization.translate('invalidEmail') }}
2496
+ </span>
2497
+ }
2498
+ </div>
2499
+ <div class="lokotro-form-group">
2500
+ <label class="lokotro-label">{{ localization.translate('phoneNumber') }} ({{ localization.translate('personalPhone') }})</label>
2501
+ <input
2502
+ type="tel"
2503
+ class="lokotro-input"
2504
+ formControlName="personalPhone"
2505
+ placeholder="+243 XXX XXX XXX">
2506
+ @if (mobileMoneyForm.get('personalPhone')?.touched && mobileMoneyForm.get('personalPhone')?.invalid) {
2507
+ <span class="lokotro-error">
2508
+ {{ localization.translate('invalidPhoneNumber') }}
2509
+ </span>
2510
+ }
2511
+ </div>
2512
+ <hr class="lokotro-divider">
2371
2513
  </div>
2514
+ }
2515
+ <!-- Mobile Money Phone Input -->
2516
+ @if (showUserInfoForm) {
2517
+ <h4 class="lokotro-section-subtitle">{{ localization.translate('mobileMoneyDetails') }}</h4>
2518
+ }
2519
+ <lokotro-mobile-money-phone-input
2520
+ [label]="localization.translate('mobileMoneyPhone')"
2521
+ [initialCountryCode]="'243'"
2522
+ [showPrefixHints]="true"
2523
+ (countryChanged)="onCountryChanged($event)"
2524
+ (phoneChanged)="onPhoneChanged($event)"
2525
+ formControlName="phoneNumber">
2526
+ </lokotro-mobile-money-phone-input>
2527
+ @if (mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('invalidPrefix')) {
2528
+ <span class="lokotro-error">
2529
+ {{ localization.translate('invalidPhonePrefix') }}
2530
+ </span>
2531
+ }
2532
+ @if (mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooShort')) {
2533
+ <span class="lokotro-error">
2534
+ {{ localization.translate('phoneTooShort') }}
2535
+ </span>
2536
+ }
2537
+ @if (mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooLong')) {
2538
+ <span class="lokotro-error">
2539
+ {{ localization.translate('phoneTooLong') }}
2540
+ </span>
2541
+ }
2542
+ <div class="lokotro-form-actions">
2543
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2544
+ {{ localization.translate('cancel') }}
2545
+ </button>
2546
+ <button type="submit" class="lokotro-btn-primary" [disabled]="mobileMoneyForm.invalid">
2547
+ {{ localization.translate('confirm') }}
2548
+ </button>
2372
2549
  </div>
2373
-
2550
+ </form>
2551
+ }
2552
+
2553
+ <!-- Card Form -->
2554
+ @if (isCardForm) {
2555
+ <form [formGroup]="cardForm" (ngSubmit)="submitCardForm()">
2556
+ <h3 class="lokotro-form-title">{{ localization.translate('card') }}</h3>
2374
2557
  <div class="lokotro-form-group">
2375
- <label class="lokotro-label">{{ localization.translate('email') }}</label>
2376
- <input
2377
- type="email"
2378
- class="lokotro-input"
2379
- formControlName="email"
2380
- placeholder="john.doe@example.com">
2381
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('email')?.touched && mobileMoneyForm.get('email')?.invalid">
2382
- {{ localization.translate('invalidEmail') }}
2383
- </span>
2558
+ <label class="lokotro-label">{{ localization.translate('cardNumber') }}</label>
2559
+ <input
2560
+ type="text"
2561
+ class="lokotro-input"
2562
+ formControlName="cardNumber"
2563
+ placeholder="1234 5678 9012 3456"
2564
+ maxlength="19"
2565
+ (input)="formatCardNumber($event)">
2566
+ @if (cardForm.get('cardNumber')?.touched && cardForm.get('cardNumber')?.invalid) {
2567
+ <span class="lokotro-error">
2568
+ {{ localization.translate('invalidCardNumber') }}
2569
+ </span>
2570
+ }
2384
2571
  </div>
2385
-
2386
2572
  <div class="lokotro-form-group">
2387
- <label class="lokotro-label">{{ localization.translate('phoneNumber') }} ({{ localization.translate('personalPhone') }})</label>
2388
- <input
2389
- type="tel"
2390
- class="lokotro-input"
2391
- formControlName="personalPhone"
2392
- placeholder="+243 XXX XXX XXX">
2393
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('personalPhone')?.touched && mobileMoneyForm.get('personalPhone')?.invalid">
2394
- {{ localization.translate('invalidPhoneNumber') }}
2395
- </span>
2573
+ <label class="lokotro-label">{{ localization.translate('cardHolderName') }}</label>
2574
+ <input
2575
+ type="text"
2576
+ class="lokotro-input"
2577
+ formControlName="cardHolderName"
2578
+ placeholder="John Doe">
2579
+ @if (cardForm.get('cardHolderName')?.touched && cardForm.get('cardHolderName')?.invalid) {
2580
+ <span class="lokotro-error">
2581
+ {{ localization.translate('required') }}
2582
+ </span>
2583
+ }
2396
2584
  </div>
2397
-
2398
- <hr class="lokotro-divider">
2399
- </div>
2400
-
2401
- <!-- Mobile Money Phone Input -->
2402
- <h4 class="lokotro-section-subtitle" *ngIf="showUserInfoForm">{{ localization.translate('mobileMoneyDetails') }}</h4>
2403
-
2404
- <lokotro-mobile-money-phone-input
2405
- [label]="localization.translate('mobileMoneyPhone')"
2406
- [initialCountryCode]="'243'"
2407
- [showPrefixHints]="true"
2408
- (countryChanged)="onCountryChanged($event)"
2409
- (phoneChanged)="onPhoneChanged($event)"
2410
- formControlName="phoneNumber">
2411
- </lokotro-mobile-money-phone-input>
2412
-
2413
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('invalidPrefix')">
2414
- {{ localization.translate('invalidPhonePrefix') }}
2415
- </span>
2416
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooShort')">
2417
- {{ localization.translate('phoneTooShort') }}
2418
- </span>
2419
- <span class="lokotro-error" *ngIf="mobileMoneyForm.get('phoneNumber')?.touched && mobileMoneyForm.get('phoneNumber')?.hasError('tooLong')">
2420
- {{ localization.translate('phoneTooLong') }}
2421
- </span>
2422
-
2423
- <div class="lokotro-form-actions">
2424
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2425
- {{ localization.translate('cancel') }}
2426
- </button>
2427
- <button type="submit" class="lokotro-btn-primary" [disabled]="mobileMoneyForm.invalid">
2428
- {{ localization.translate('confirm') }}
2429
- </button>
2430
- </div>
2431
- </form>
2432
-
2433
- <!-- Card Form -->
2434
- <form *ngIf="isCardForm" [formGroup]="cardForm" (ngSubmit)="submitCardForm()">
2435
- <h3 class="lokotro-form-title">{{ localization.translate('card') }}</h3>
2436
-
2437
- <div class="lokotro-form-group">
2438
- <label class="lokotro-label">{{ localization.translate('cardNumber') }}</label>
2439
- <input
2440
- type="text"
2441
- class="lokotro-input"
2442
- formControlName="cardNumber"
2443
- placeholder="1234 5678 9012 3456"
2444
- maxlength="19"
2445
- (input)="formatCardNumber($event)">
2446
- <span class="lokotro-error" *ngIf="cardForm.get('cardNumber')?.touched && cardForm.get('cardNumber')?.invalid">
2447
- {{ localization.translate('invalidCardNumber') }}
2448
- </span>
2449
- </div>
2450
-
2451
- <div class="lokotro-form-group">
2452
- <label class="lokotro-label">{{ localization.translate('cardHolderName') }}</label>
2453
- <input
2454
- type="text"
2455
- class="lokotro-input"
2456
- formControlName="cardHolderName"
2457
- placeholder="John Doe">
2458
- <span class="lokotro-error" *ngIf="cardForm.get('cardHolderName')?.touched && cardForm.get('cardHolderName')?.invalid">
2459
- {{ localization.translate('required') }}
2460
- </span>
2461
- </div>
2462
-
2463
- <div class="lokotro-form-row">
2585
+ <div class="lokotro-form-row">
2586
+ <div class="lokotro-form-group">
2587
+ <label class="lokotro-label">{{ localization.translate('expiryDate') }}</label>
2588
+ <input
2589
+ type="text"
2590
+ class="lokotro-input"
2591
+ formControlName="expiryDate"
2592
+ placeholder="MM/YY"
2593
+ maxlength="5"
2594
+ (input)="formatExpiryDate($event)">
2595
+ @if (cardForm.get('expiryDate')?.touched && cardForm.get('expiryDate')?.invalid) {
2596
+ <span class="lokotro-error">
2597
+ {{ localization.translate('invalidExpiryDate') }}
2598
+ </span>
2599
+ }
2600
+ </div>
2601
+ <div class="lokotro-form-group">
2602
+ <label class="lokotro-label">{{ localization.translate('cvv') }}</label>
2603
+ <input
2604
+ type="password"
2605
+ class="lokotro-input"
2606
+ formControlName="cvv"
2607
+ placeholder="123"
2608
+ maxlength="4">
2609
+ @if (cardForm.get('cvv')?.touched && cardForm.get('cvv')?.invalid) {
2610
+ <span class="lokotro-error">
2611
+ {{ localization.translate('invalidCvv') }}
2612
+ </span>
2613
+ }
2614
+ </div>
2615
+ </div>
2616
+ <div class="lokotro-form-actions">
2617
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2618
+ {{ localization.translate('cancel') }}
2619
+ </button>
2620
+ <button type="submit" class="lokotro-btn-primary" [disabled]="cardForm.invalid">
2621
+ {{ localization.translate('confirm') }}
2622
+ </button>
2623
+ </div>
2624
+ </form>
2625
+ }
2626
+
2627
+ <!-- Flash Form -->
2628
+ @if (isFlashForm) {
2629
+ <form [formGroup]="flashForm" (ngSubmit)="submitFlashForm()">
2630
+ <h3 class="lokotro-form-title">{{ localization.translate('eFlash') }}</h3>
2464
2631
  <div class="lokotro-form-group">
2465
- <label class="lokotro-label">{{ localization.translate('expiryDate') }}</label>
2466
- <input
2467
- type="text"
2468
- class="lokotro-input"
2469
- formControlName="expiryDate"
2470
- placeholder="MM/YY"
2471
- maxlength="5"
2472
- (input)="formatExpiryDate($event)">
2473
- <span class="lokotro-error" *ngIf="cardForm.get('expiryDate')?.touched && cardForm.get('expiryDate')?.invalid">
2474
- {{ localization.translate('invalidExpiryDate') }}
2475
- </span>
2632
+ <label class="lokotro-label">Flash Number</label>
2633
+ <input
2634
+ type="text"
2635
+ class="lokotro-input"
2636
+ formControlName="flashNumber"
2637
+ placeholder="Enter flash number">
2638
+ @if (flashForm.get('flashNumber')?.touched && flashForm.get('flashNumber')?.invalid) {
2639
+ <span class="lokotro-error">
2640
+ {{ localization.translate('required') }}
2641
+ </span>
2642
+ }
2476
2643
  </div>
2477
-
2478
2644
  <div class="lokotro-form-group">
2479
- <label class="lokotro-label">{{ localization.translate('cvv') }}</label>
2480
- <input
2481
- type="password"
2482
- class="lokotro-input"
2483
- formControlName="cvv"
2484
- placeholder="123"
2485
- maxlength="4">
2486
- <span class="lokotro-error" *ngIf="cardForm.get('cvv')?.touched && cardForm.get('cvv')?.invalid">
2487
- {{ localization.translate('invalidCvv') }}
2488
- </span>
2645
+ <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2646
+ <input
2647
+ type="password"
2648
+ class="lokotro-input"
2649
+ formControlName="flashPin"
2650
+ placeholder="Enter PIN"
2651
+ maxlength="6">
2652
+ @if (flashForm.get('flashPin')?.touched && flashForm.get('flashPin')?.invalid) {
2653
+ <span class="lokotro-error">
2654
+ {{ localization.translate('required') }}
2655
+ </span>
2656
+ }
2489
2657
  </div>
2490
- </div>
2491
-
2492
- <div class="lokotro-form-actions">
2493
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2494
- {{ localization.translate('cancel') }}
2495
- </button>
2496
- <button type="submit" class="lokotro-btn-primary" [disabled]="cardForm.invalid">
2497
- {{ localization.translate('confirm') }}
2498
- </button>
2499
- </div>
2500
- </form>
2501
-
2502
- <!-- Flash Form -->
2503
- <form *ngIf="isFlashForm" [formGroup]="flashForm" (ngSubmit)="submitFlashForm()">
2504
- <h3 class="lokotro-form-title">{{ localization.translate('eFlash') }}</h3>
2505
-
2506
- <div class="lokotro-form-group">
2507
- <label class="lokotro-label">Flash Number</label>
2508
- <input
2509
- type="text"
2510
- class="lokotro-input"
2511
- formControlName="flashNumber"
2512
- placeholder="Enter flash number">
2513
- <span class="lokotro-error" *ngIf="flashForm.get('flashNumber')?.touched && flashForm.get('flashNumber')?.invalid">
2514
- {{ localization.translate('required') }}
2515
- </span>
2516
- </div>
2517
-
2518
- <div class="lokotro-form-group">
2519
- <label class="lokotro-label">{{ localization.translate('pin') }}</label>
2520
- <input
2521
- type="password"
2522
- class="lokotro-input"
2523
- formControlName="flashPin"
2524
- placeholder="Enter PIN"
2525
- maxlength="6">
2526
- <span class="lokotro-error" *ngIf="flashForm.get('flashPin')?.touched && flashForm.get('flashPin')?.invalid">
2527
- {{ localization.translate('required') }}
2528
- </span>
2529
- </div>
2530
-
2531
- <div class="lokotro-form-actions">
2532
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2533
- {{ localization.translate('cancel') }}
2534
- </button>
2535
- <button type="submit" class="lokotro-btn-primary" [disabled]="flashForm.invalid">
2536
- {{ localization.translate('confirm') }}
2537
- </button>
2538
- </div>
2539
- </form>
2540
- </div>
2541
- `, styles: [".lokotro-payment-form{width:100%}.lokotro-form-title{font-size:20px;font-weight:600;margin:0 0 24px;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-form-group{margin-bottom:20px}.lokotro-form-row{display:flex;gap:16px}.lokotro-form-row .lokotro-form-group{flex:1}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-input{width:100%;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;box-sizing:border-box}.lokotro-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-error{display:block;margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-user-info-section{margin-bottom:24px}.lokotro-section-subtitle{font-size:16px;font-weight:600;margin:0 0 16px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-divider{border:none;border-top:1px solid var(--lokotro-border, #3A473F);margin:24px 0}.lokotro-form-actions{display:flex;gap:12px;margin-top:32px}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }]
2542
- }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: LokotroLocalizationService }], propDecorators: { channel: [{
2543
- type: Input
2544
- }], transactionId: [{
2545
- type: Input
2546
- }], showUserInfoForm: [{
2547
- type: Input
2548
- }], formSubmitted: [{
2549
- type: Output
2550
- }], cancel: [{
2551
- type: Output
2552
- }] } });
2658
+ <div class="lokotro-form-actions">
2659
+ <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
2660
+ {{ localization.translate('cancel') }}
2661
+ </button>
2662
+ <button type="submit" class="lokotro-btn-primary" [disabled]="flashForm.invalid">
2663
+ {{ localization.translate('confirm') }}
2664
+ </button>
2665
+ </div>
2666
+ </form>
2667
+ }
2668
+ </div>
2669
+ `, styles: [".lokotro-payment-form{width:100%}.lokotro-form-title{font-size:20px;font-weight:600;margin:0 0 24px;color:var(--lokotro-text-primary, #F2F0D5);text-align:center}.lokotro-form-group{margin-bottom:20px}.lokotro-form-row{display:flex;gap:16px}.lokotro-form-row .lokotro-form-group{flex:1}.lokotro-label{display:block;margin-bottom:8px;font-size:14px;font-weight:500;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-input{width:100%;padding:14px 16px;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);font-size:16px;transition:border-color .2s;box-sizing:border-box}.lokotro-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-input::placeholder{color:var(--lokotro-text-secondary, #D5D3B8);opacity:.6}.lokotro-error{display:block;margin-top:6px;font-size:12px;color:var(--lokotro-error, #D97652)}.lokotro-user-info-section{margin-bottom:24px}.lokotro-section-subtitle{font-size:16px;font-weight:600;margin:0 0 16px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-divider{border:none;border-top:1px solid var(--lokotro-border, #3A473F);margin:24px 0}.lokotro-form-actions{display:flex;gap:12px;margin-top:32px}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }]
2670
+ }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: LokotroLocalizationService }], propDecorators: { channel: [{
2671
+ type: Input
2672
+ }], transactionId: [{
2673
+ type: Input
2674
+ }], showUserInfoForm: [{
2675
+ type: Input
2676
+ }], formSubmitted: [{
2677
+ type: Output
2678
+ }], cancel: [{
2679
+ type: Output
2680
+ }] } });
2553
2681
 
2554
2682
  /**
2555
2683
  * Lokotro Pay - OTP Verification Component
@@ -2639,8 +2767,8 @@ class LokotroOtpVerificationComponent {
2639
2767
  onCancelClick() {
2640
2768
  this.cancel.emit();
2641
2769
  }
2642
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroOtpVerificationComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2643
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroOtpVerificationComponent, isStandalone: true, selector: "lokotro-otp-verification", inputs: { transactionId: "transactionId", otpDestination: "otpDestination", otpLength: "otpLength" }, outputs: { otpVerified: "otpVerified", resendOtp: "resendOtp", cancel: "cancel" }, ngImport: i0, template: `
2770
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroOtpVerificationComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2771
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroOtpVerificationComponent, isStandalone: true, selector: "lokotro-otp-verification", inputs: { transactionId: "transactionId", otpDestination: "otpDestination", otpLength: "otpLength" }, outputs: { otpVerified: "otpVerified", resendOtp: "resendOtp", cancel: "cancel" }, ngImport: i0, template: `
2644
2772
  <div class="lokotro-otp-verification">
2645
2773
  <div class="lokotro-otp-icon">
2646
2774
  <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
@@ -2648,54 +2776,60 @@ class LokotroOtpVerificationComponent {
2648
2776
  <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
2649
2777
  </svg>
2650
2778
  </div>
2651
-
2779
+
2652
2780
  <h3 class="lokotro-otp-title">{{ localization.translate('enterOtp') }}</h3>
2653
- <p class="lokotro-otp-subtitle" *ngIf="otpDestination">
2654
- {{ localization.translate('otpSentTo') }} {{ otpDestination }}
2655
- </p>
2656
-
2781
+ @if (otpDestination) {
2782
+ <p class="lokotro-otp-subtitle">
2783
+ {{ localization.translate('otpSentTo') }} {{ otpDestination }}
2784
+ </p>
2785
+ }
2786
+
2657
2787
  <div class="lokotro-otp-inputs">
2658
- <input
2659
- *ngFor="let digit of otpDigits; let i = index"
2660
- type="text"
2661
- class="lokotro-otp-input"
2662
- maxlength="1"
2663
- [value]="otpDigits[i]"
2664
- (input)="onOtpInput($event, i)"
2665
- (keydown)="onOtpKeydown($event, i)"
2666
- (paste)="onOtpPaste($event)"
2667
- #otpInput>
2668
- </div>
2669
-
2670
- <div class="lokotro-otp-timer" *ngIf="resendTimer > 0">
2671
- Resend OTP in {{ resendTimer }}s
2788
+ @for (digit of otpDigits; track digit; let i = $index) {
2789
+ <input
2790
+ type="text"
2791
+ class="lokotro-otp-input"
2792
+ maxlength="1"
2793
+ [value]="otpDigits[i]"
2794
+ (input)="onOtpInput($event, i)"
2795
+ (keydown)="onOtpKeydown($event, i)"
2796
+ (paste)="onOtpPaste($event)"
2797
+ #otpInput>
2798
+ }
2672
2799
  </div>
2673
-
2674
- <button
2675
- class="lokotro-resend-btn"
2676
- *ngIf="resendTimer === 0"
2677
- (click)="onResend()">
2678
- {{ localization.translate('resendOtp') }}
2679
- </button>
2680
-
2800
+
2801
+ @if (resendTimer > 0) {
2802
+ <div class="lokotro-otp-timer">
2803
+ Resend OTP in {{ resendTimer }}s
2804
+ </div>
2805
+ }
2806
+
2807
+ @if (resendTimer === 0) {
2808
+ <button
2809
+ class="lokotro-resend-btn"
2810
+ (click)="onResend()">
2811
+ {{ localization.translate('resendOtp') }}
2812
+ </button>
2813
+ }
2814
+
2681
2815
  <div class="lokotro-form-actions">
2682
2816
  <button type="button" class="lokotro-btn-secondary" (click)="onCancelClick()">
2683
2817
  {{ localization.translate('cancel') }}
2684
2818
  </button>
2685
- <button
2686
- type="button"
2687
- class="lokotro-btn-primary"
2819
+ <button
2820
+ type="button"
2821
+ class="lokotro-btn-primary"
2688
2822
  [disabled]="!isOtpComplete"
2689
2823
  (click)="onVerify()">
2690
2824
  {{ localization.translate('verifyOtp') }}
2691
2825
  </button>
2692
2826
  </div>
2693
2827
  </div>
2694
- `, isInline: true, styles: [".lokotro-otp-verification{display:flex;flex-direction:column;align-items:center;text-align:center;padding:24px 0}.lokotro-otp-icon{width:80px;height:80px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-card, #3A4840);border-radius:50%;margin-bottom:24px;color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-title{font-size:24px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-otp-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-otp-inputs{display:flex;gap:12px;margin-bottom:24px}.lokotro-otp-input{width:50px;height:56px;text-align:center;font-size:24px;font-weight:600;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);transition:border-color .2s}.lokotro-otp-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-timer{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin-bottom:24px}.lokotro-resend-btn{background:none;border:none;color:var(--lokotro-accent, #3BFBDA);font-size:14px;font-weight:600;cursor:pointer;margin-bottom:24px;text-decoration:underline}.lokotro-resend-btn:hover{opacity:.8}.lokotro-form-actions{display:flex;gap:12px;width:100%}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }] }); }
2828
+ `, isInline: true, styles: [".lokotro-otp-verification{display:flex;flex-direction:column;align-items:center;text-align:center;padding:24px 0}.lokotro-otp-icon{width:80px;height:80px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-card, #3A4840);border-radius:50%;margin-bottom:24px;color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-title{font-size:24px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-otp-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-otp-inputs{display:flex;gap:12px;margin-bottom:24px}.lokotro-otp-input{width:50px;height:56px;text-align:center;font-size:24px;font-weight:600;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);transition:border-color .2s}.lokotro-otp-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-timer{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin-bottom:24px}.lokotro-resend-btn{background:none;border:none;color:var(--lokotro-accent, #3BFBDA);font-size:14px;font-weight:600;cursor:pointer;margin-bottom:24px;text-decoration:underline}.lokotro-resend-btn:hover{opacity:.8}.lokotro-form-actions{display:flex;gap:12px;width:100%}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }] }); }
2695
2829
  }
2696
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroOtpVerificationComponent, decorators: [{
2830
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroOtpVerificationComponent, decorators: [{
2697
2831
  type: Component,
2698
- args: [{ selector: 'lokotro-otp-verification', standalone: true, imports: [CommonModule, FormsModule], template: `
2832
+ args: [{ selector: 'lokotro-otp-verification', standalone: true, imports: [FormsModule], template: `
2699
2833
  <div class="lokotro-otp-verification">
2700
2834
  <div class="lokotro-otp-icon">
2701
2835
  <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
@@ -2703,50 +2837,56 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2703
2837
  <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
2704
2838
  </svg>
2705
2839
  </div>
2706
-
2840
+
2707
2841
  <h3 class="lokotro-otp-title">{{ localization.translate('enterOtp') }}</h3>
2708
- <p class="lokotro-otp-subtitle" *ngIf="otpDestination">
2709
- {{ localization.translate('otpSentTo') }} {{ otpDestination }}
2710
- </p>
2711
-
2842
+ @if (otpDestination) {
2843
+ <p class="lokotro-otp-subtitle">
2844
+ {{ localization.translate('otpSentTo') }} {{ otpDestination }}
2845
+ </p>
2846
+ }
2847
+
2712
2848
  <div class="lokotro-otp-inputs">
2713
- <input
2714
- *ngFor="let digit of otpDigits; let i = index"
2715
- type="text"
2716
- class="lokotro-otp-input"
2717
- maxlength="1"
2718
- [value]="otpDigits[i]"
2719
- (input)="onOtpInput($event, i)"
2720
- (keydown)="onOtpKeydown($event, i)"
2721
- (paste)="onOtpPaste($event)"
2722
- #otpInput>
2723
- </div>
2724
-
2725
- <div class="lokotro-otp-timer" *ngIf="resendTimer > 0">
2726
- Resend OTP in {{ resendTimer }}s
2849
+ @for (digit of otpDigits; track digit; let i = $index) {
2850
+ <input
2851
+ type="text"
2852
+ class="lokotro-otp-input"
2853
+ maxlength="1"
2854
+ [value]="otpDigits[i]"
2855
+ (input)="onOtpInput($event, i)"
2856
+ (keydown)="onOtpKeydown($event, i)"
2857
+ (paste)="onOtpPaste($event)"
2858
+ #otpInput>
2859
+ }
2727
2860
  </div>
2728
-
2729
- <button
2730
- class="lokotro-resend-btn"
2731
- *ngIf="resendTimer === 0"
2732
- (click)="onResend()">
2733
- {{ localization.translate('resendOtp') }}
2734
- </button>
2735
-
2861
+
2862
+ @if (resendTimer > 0) {
2863
+ <div class="lokotro-otp-timer">
2864
+ Resend OTP in {{ resendTimer }}s
2865
+ </div>
2866
+ }
2867
+
2868
+ @if (resendTimer === 0) {
2869
+ <button
2870
+ class="lokotro-resend-btn"
2871
+ (click)="onResend()">
2872
+ {{ localization.translate('resendOtp') }}
2873
+ </button>
2874
+ }
2875
+
2736
2876
  <div class="lokotro-form-actions">
2737
2877
  <button type="button" class="lokotro-btn-secondary" (click)="onCancelClick()">
2738
2878
  {{ localization.translate('cancel') }}
2739
2879
  </button>
2740
- <button
2741
- type="button"
2742
- class="lokotro-btn-primary"
2880
+ <button
2881
+ type="button"
2882
+ class="lokotro-btn-primary"
2743
2883
  [disabled]="!isOtpComplete"
2744
2884
  (click)="onVerify()">
2745
2885
  {{ localization.translate('verifyOtp') }}
2746
2886
  </button>
2747
2887
  </div>
2748
2888
  </div>
2749
- `, styles: [".lokotro-otp-verification{display:flex;flex-direction:column;align-items:center;text-align:center;padding:24px 0}.lokotro-otp-icon{width:80px;height:80px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-card, #3A4840);border-radius:50%;margin-bottom:24px;color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-title{font-size:24px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-otp-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-otp-inputs{display:flex;gap:12px;margin-bottom:24px}.lokotro-otp-input{width:50px;height:56px;text-align:center;font-size:24px;font-weight:600;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);transition:border-color .2s}.lokotro-otp-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-timer{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin-bottom:24px}.lokotro-resend-btn{background:none;border:none;color:var(--lokotro-accent, #3BFBDA);font-size:14px;font-weight:600;cursor:pointer;margin-bottom:24px;text-decoration:underline}.lokotro-resend-btn:hover{opacity:.8}.lokotro-form-actions{display:flex;gap:12px;width:100%}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }]
2889
+ `, styles: [".lokotro-otp-verification{display:flex;flex-direction:column;align-items:center;text-align:center;padding:24px 0}.lokotro-otp-icon{width:80px;height:80px;display:flex;align-items:center;justify-content:center;background:var(--lokotro-card, #3A4840);border-radius:50%;margin-bottom:24px;color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-title{font-size:24px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-otp-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-otp-inputs{display:flex;gap:12px;margin-bottom:24px}.lokotro-otp-input{width:50px;height:56px;text-align:center;font-size:24px;font-weight:600;background:var(--lokotro-card, #3A4840);border:2px solid var(--lokotro-border, #3A473F);border-radius:12px;color:var(--lokotro-text-primary, #F2F0D5);transition:border-color .2s}.lokotro-otp-input:focus{outline:none;border-color:var(--lokotro-accent, #3BFBDA)}.lokotro-otp-timer{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin-bottom:24px}.lokotro-resend-btn{background:none;border:none;color:var(--lokotro-accent, #3BFBDA);font-size:14px;font-weight:600;cursor:pointer;margin-bottom:24px;text-decoration:underline}.lokotro-resend-btn:hover{opacity:.8}.lokotro-form-actions{display:flex;gap:12px;width:100%}.lokotro-btn-primary,.lokotro-btn-secondary{flex:1;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-btn-primary:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-primary:disabled{opacity:.5;cursor:not-allowed}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }]
2750
2890
  }], ctorParameters: () => [{ type: LokotroLocalizationService }], propDecorators: { transactionId: [{
2751
2891
  type: Input
2752
2892
  }], otpDestination: [{
@@ -2775,70 +2915,78 @@ class LokotroProcessingComponent {
2775
2915
  setTimeout(() => this.currentStep = 3, 6000);
2776
2916
  }
2777
2917
  }
2778
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroProcessingComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2779
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroProcessingComponent, isStandalone: true, selector: "lokotro-processing", inputs: { type: "type", message: "message" }, ngImport: i0, template: `
2918
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroProcessingComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2919
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroProcessingComponent, isStandalone: true, selector: "lokotro-processing", inputs: { type: "type", message: "message" }, ngImport: i0, template: `
2780
2920
  <div class="lokotro-processing">
2781
2921
  <div class="lokotro-processing-spinner">
2782
2922
  <div class="lokotro-spinner-ring"></div>
2783
2923
  <div class="lokotro-spinner-ring"></div>
2784
2924
  <div class="lokotro-spinner-ring"></div>
2785
2925
  </div>
2786
-
2926
+
2787
2927
  <h3 class="lokotro-processing-title">{{ message || localization.translate('processing') }}</h3>
2788
-
2789
- <p class="lokotro-processing-subtitle" *ngIf="type === 'mobileMoney'">
2790
- Please confirm the payment on your mobile device
2791
- </p>
2792
-
2793
- <div class="lokotro-processing-steps" *ngIf="type === 'mobileMoney'">
2794
- <div class="lokotro-step" [class.active]="currentStep >= 1">
2795
- <div class="lokotro-step-icon">📱</div>
2796
- <span>Check your phone</span>
2797
- </div>
2798
- <div class="lokotro-step" [class.active]="currentStep >= 2">
2799
- <div class="lokotro-step-icon">🔐</div>
2800
- <span>Enter your PIN</span>
2801
- </div>
2802
- <div class="lokotro-step" [class.active]="currentStep >= 3">
2803
- <div class="lokotro-step-icon">✅</div>
2804
- <span>Confirm payment</span>
2928
+
2929
+ @if (type === 'mobileMoney') {
2930
+ <p class="lokotro-processing-subtitle">
2931
+ Please confirm the payment on your mobile device
2932
+ </p>
2933
+ }
2934
+
2935
+ @if (type === 'mobileMoney') {
2936
+ <div class="lokotro-processing-steps">
2937
+ <div class="lokotro-step" [class.active]="currentStep >= 1">
2938
+ <div class="lokotro-step-icon">📱</div>
2939
+ <span>Check your phone</span>
2940
+ </div>
2941
+ <div class="lokotro-step" [class.active]="currentStep >= 2">
2942
+ <div class="lokotro-step-icon">🔐</div>
2943
+ <span>Enter your PIN</span>
2944
+ </div>
2945
+ <div class="lokotro-step" [class.active]="currentStep >= 3">
2946
+ <div class="lokotro-step-icon">✅</div>
2947
+ <span>Confirm payment</span>
2948
+ </div>
2805
2949
  </div>
2806
- </div>
2950
+ }
2807
2951
  </div>
2808
- `, isInline: true, styles: [".lokotro-processing{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:48px 24px;min-height:300px}.lokotro-processing-spinner{position:relative;width:80px;height:80px;margin-bottom:32px}.lokotro-spinner-ring{position:absolute;width:100%;height:100%;border:3px solid transparent;border-radius:50%;animation:lokotro-spin 1.5s linear infinite}.lokotro-spinner-ring:nth-child(1){border-top-color:var(--lokotro-accent, #3BFBDA);animation-delay:0s}.lokotro-spinner-ring:nth-child(2){border-right-color:var(--lokotro-primary, #5A5E39);animation-delay:.15s;width:70%;height:70%;top:15%;left:15%}.lokotro-spinner-ring:nth-child(3){border-bottom-color:var(--lokotro-secondary, #6E7346);animation-delay:.3s;width:50%;height:50%;top:25%;left:25%}@keyframes lokotro-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lokotro-processing-title{font-size:20px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-processing-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-processing-steps{display:flex;flex-direction:column;gap:16px;width:100%;max-width:280px}.lokotro-step{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--lokotro-card, #3A4840);border-radius:12px;opacity:.5;transition:opacity .3s}.lokotro-step.active{opacity:1}.lokotro-step-icon{font-size:20px}.lokotro-step span{font-size:14px;color:var(--lokotro-text-primary, #F2F0D5)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
2952
+ `, isInline: true, styles: [".lokotro-processing{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:48px 24px;min-height:300px}.lokotro-processing-spinner{position:relative;width:80px;height:80px;margin-bottom:32px}.lokotro-spinner-ring{position:absolute;width:100%;height:100%;border:3px solid transparent;border-radius:50%;animation:lokotro-spin 1.5s linear infinite}.lokotro-spinner-ring:nth-child(1){border-top-color:var(--lokotro-accent, #3BFBDA);animation-delay:0s}.lokotro-spinner-ring:nth-child(2){border-right-color:var(--lokotro-primary, #5A5E39);animation-delay:.15s;width:70%;height:70%;top:15%;left:15%}.lokotro-spinner-ring:nth-child(3){border-bottom-color:var(--lokotro-secondary, #6E7346);animation-delay:.3s;width:50%;height:50%;top:25%;left:25%}@keyframes lokotro-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lokotro-processing-title{font-size:20px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-processing-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-processing-steps{display:flex;flex-direction:column;gap:16px;width:100%;max-width:280px}.lokotro-step{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--lokotro-card, #3A4840);border-radius:12px;opacity:.5;transition:opacity .3s}.lokotro-step.active{opacity:1}.lokotro-step-icon{font-size:20px}.lokotro-step span{font-size:14px;color:var(--lokotro-text-primary, #F2F0D5)}\n"] }); }
2809
2953
  }
2810
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroProcessingComponent, decorators: [{
2954
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroProcessingComponent, decorators: [{
2811
2955
  type: Component,
2812
- args: [{ selector: 'lokotro-processing', standalone: true, imports: [CommonModule], template: `
2956
+ args: [{ selector: 'lokotro-processing', standalone: true, imports: [], template: `
2813
2957
  <div class="lokotro-processing">
2814
2958
  <div class="lokotro-processing-spinner">
2815
2959
  <div class="lokotro-spinner-ring"></div>
2816
2960
  <div class="lokotro-spinner-ring"></div>
2817
2961
  <div class="lokotro-spinner-ring"></div>
2818
2962
  </div>
2819
-
2963
+
2820
2964
  <h3 class="lokotro-processing-title">{{ message || localization.translate('processing') }}</h3>
2821
-
2822
- <p class="lokotro-processing-subtitle" *ngIf="type === 'mobileMoney'">
2823
- Please confirm the payment on your mobile device
2824
- </p>
2825
-
2826
- <div class="lokotro-processing-steps" *ngIf="type === 'mobileMoney'">
2827
- <div class="lokotro-step" [class.active]="currentStep >= 1">
2828
- <div class="lokotro-step-icon">📱</div>
2829
- <span>Check your phone</span>
2830
- </div>
2831
- <div class="lokotro-step" [class.active]="currentStep >= 2">
2832
- <div class="lokotro-step-icon">🔐</div>
2833
- <span>Enter your PIN</span>
2834
- </div>
2835
- <div class="lokotro-step" [class.active]="currentStep >= 3">
2836
- <div class="lokotro-step-icon">✅</div>
2837
- <span>Confirm payment</span>
2965
+
2966
+ @if (type === 'mobileMoney') {
2967
+ <p class="lokotro-processing-subtitle">
2968
+ Please confirm the payment on your mobile device
2969
+ </p>
2970
+ }
2971
+
2972
+ @if (type === 'mobileMoney') {
2973
+ <div class="lokotro-processing-steps">
2974
+ <div class="lokotro-step" [class.active]="currentStep >= 1">
2975
+ <div class="lokotro-step-icon">📱</div>
2976
+ <span>Check your phone</span>
2977
+ </div>
2978
+ <div class="lokotro-step" [class.active]="currentStep >= 2">
2979
+ <div class="lokotro-step-icon">🔐</div>
2980
+ <span>Enter your PIN</span>
2981
+ </div>
2982
+ <div class="lokotro-step" [class.active]="currentStep >= 3">
2983
+ <div class="lokotro-step-icon">✅</div>
2984
+ <span>Confirm payment</span>
2985
+ </div>
2838
2986
  </div>
2839
- </div>
2987
+ }
2840
2988
  </div>
2841
- `, styles: [".lokotro-processing{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:48px 24px;min-height:300px}.lokotro-processing-spinner{position:relative;width:80px;height:80px;margin-bottom:32px}.lokotro-spinner-ring{position:absolute;width:100%;height:100%;border:3px solid transparent;border-radius:50%;animation:lokotro-spin 1.5s linear infinite}.lokotro-spinner-ring:nth-child(1){border-top-color:var(--lokotro-accent, #3BFBDA);animation-delay:0s}.lokotro-spinner-ring:nth-child(2){border-right-color:var(--lokotro-primary, #5A5E39);animation-delay:.15s;width:70%;height:70%;top:15%;left:15%}.lokotro-spinner-ring:nth-child(3){border-bottom-color:var(--lokotro-secondary, #6E7346);animation-delay:.3s;width:50%;height:50%;top:25%;left:25%}@keyframes lokotro-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lokotro-processing-title{font-size:20px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-processing-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-processing-steps{display:flex;flex-direction:column;gap:16px;width:100%;max-width:280px}.lokotro-step{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--lokotro-card, #3A4840);border-radius:12px;opacity:.5;transition:opacity .3s}.lokotro-step.active{opacity:1}.lokotro-step-icon{font-size:20px}.lokotro-step span{font-size:14px;color:var(--lokotro-text-primary, #F2F0D5)}\n"] }]
2989
+ `, styles: [".lokotro-processing{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:48px 24px;min-height:300px}.lokotro-processing-spinner{position:relative;width:80px;height:80px;margin-bottom:32px}.lokotro-spinner-ring{position:absolute;width:100%;height:100%;border:3px solid transparent;border-radius:50%;animation:lokotro-spin 1.5s linear infinite}.lokotro-spinner-ring:nth-child(1){border-top-color:var(--lokotro-accent, #3BFBDA);animation-delay:0s}.lokotro-spinner-ring:nth-child(2){border-right-color:var(--lokotro-primary, #5A5E39);animation-delay:.15s;width:70%;height:70%;top:15%;left:15%}.lokotro-spinner-ring:nth-child(3){border-bottom-color:var(--lokotro-secondary, #6E7346);animation-delay:.3s;width:50%;height:50%;top:25%;left:25%}@keyframes lokotro-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.lokotro-processing-title{font-size:20px;font-weight:600;margin:0 0 8px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-processing-subtitle{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 32px}.lokotro-processing-steps{display:flex;flex-direction:column;gap:16px;width:100%;max-width:280px}.lokotro-step{display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--lokotro-card, #3A4840);border-radius:12px;opacity:.5;transition:opacity .3s}.lokotro-step.active{opacity:1}.lokotro-step-icon{font-size:20px}.lokotro-step span{font-size:14px;color:var(--lokotro-text-primary, #F2F0D5)}\n"] }]
2842
2990
  }], ctorParameters: () => [{ type: LokotroLocalizationService }], propDecorators: { type: [{
2843
2991
  type: Input
2844
2992
  }], message: [{
@@ -2854,8 +3002,59 @@ class LokotroResultComponent {
2854
3002
  this.localization = localization;
2855
3003
  this.type = 'success';
2856
3004
  this.title = '';
3005
+ /** Auto-redirect duration in seconds. If > 0, will auto-call onAutoRedirect after countdown. */
3006
+ this.autoRedirectSeconds = 0;
2857
3007
  this.primaryAction = new EventEmitter();
2858
3008
  this.secondaryAction = new EventEmitter();
3009
+ /** Emitted when auto-redirect triggers after countdown completes. */
3010
+ this.autoRedirect = new EventEmitter();
3011
+ // Countdown state
3012
+ this.countdownSeconds = 0;
3013
+ this.isCountdownCancelled = false;
3014
+ }
3015
+ ngOnInit() {
3016
+ this.startAutoRedirectCountdown();
3017
+ }
3018
+ ngOnDestroy() {
3019
+ this.clearCountdown();
3020
+ }
3021
+ startAutoRedirectCountdown() {
3022
+ if (this.autoRedirectSeconds <= 0)
3023
+ return;
3024
+ this.countdownSeconds = this.autoRedirectSeconds;
3025
+ this.countdownInterval = setInterval(() => {
3026
+ if (this.isCountdownCancelled) {
3027
+ this.clearCountdown();
3028
+ return;
3029
+ }
3030
+ this.countdownSeconds--;
3031
+ if (this.countdownSeconds <= 0) {
3032
+ this.clearCountdown();
3033
+ this.triggerAutoRedirect();
3034
+ }
3035
+ }, 1000);
3036
+ }
3037
+ cancelCountdown() {
3038
+ if (this.countdownInterval) {
3039
+ this.isCountdownCancelled = true;
3040
+ this.clearCountdown();
3041
+ }
3042
+ }
3043
+ clearCountdown() {
3044
+ if (this.countdownInterval) {
3045
+ clearInterval(this.countdownInterval);
3046
+ this.countdownInterval = undefined;
3047
+ }
3048
+ }
3049
+ triggerAutoRedirect() {
3050
+ if (this.isCountdownCancelled)
3051
+ return;
3052
+ this.autoRedirect.emit();
3053
+ }
3054
+ getCountdownProgress() {
3055
+ if (this.autoRedirectSeconds <= 0)
3056
+ return 0;
3057
+ return (this.countdownSeconds / this.autoRedirectSeconds) * 100;
2859
3058
  }
2860
3059
  formatAmount(amount, currency) {
2861
3060
  const formatter = new Intl.NumberFormat('en-US', {
@@ -2866,143 +3065,237 @@ class LokotroResultComponent {
2866
3065
  return formatter.format(amount);
2867
3066
  }
2868
3067
  onPrimaryClick() {
3068
+ this.cancelCountdown();
2869
3069
  this.primaryAction.emit();
2870
3070
  }
2871
3071
  onSecondaryClick() {
3072
+ this.cancelCountdown();
2872
3073
  this.secondaryAction.emit();
2873
3074
  }
2874
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroResultComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2875
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroResultComponent, isStandalone: true, selector: "lokotro-result", inputs: { type: "type", title: "title", message: "message", amount: "amount", currency: "currency", transactionId: "transactionId", primaryActionLabel: "primaryActionLabel", secondaryActionLabel: "secondaryActionLabel" }, outputs: { primaryAction: "primaryAction", secondaryAction: "secondaryAction" }, ngImport: i0, template: `
3075
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroResultComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
3076
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroResultComponent, isStandalone: true, selector: "lokotro-result", inputs: { type: "type", title: "title", message: "message", amount: "amount", currency: "currency", transactionId: "transactionId", primaryActionLabel: "primaryActionLabel", secondaryActionLabel: "secondaryActionLabel", autoRedirectSeconds: "autoRedirectSeconds" }, outputs: { primaryAction: "primaryAction", secondaryAction: "secondaryAction", autoRedirect: "autoRedirect" }, ngImport: i0, template: `
2876
3077
  <div class="lokotro-result" [class]="'lokotro-result-' + type">
2877
3078
  <!-- Animated Icon -->
2878
3079
  <div class="lokotro-result-icon" [class]="'lokotro-icon-' + type">
2879
3080
  <!-- Success Icon -->
2880
- <svg *ngIf="type === 'success'" class="lokotro-checkmark" viewBox="0 0 52 52">
2881
- <circle class="lokotro-checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
2882
- <path class="lokotro-checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
2883
- </svg>
2884
-
3081
+ @if (type === 'success') {
3082
+ <svg class="lokotro-checkmark" viewBox="0 0 52 52">
3083
+ <circle class="lokotro-checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
3084
+ <path class="lokotro-checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
3085
+ </svg>
3086
+ }
3087
+
2885
3088
  <!-- Error Icon -->
2886
- <svg *ngIf="type === 'error'" class="lokotro-error-icon" viewBox="0 0 52 52">
2887
- <circle class="lokotro-error-circle" cx="26" cy="26" r="25" fill="none"/>
2888
- <path class="lokotro-error-x" fill="none" d="M16 16l20 20M36 16l-20 20"/>
2889
- </svg>
2890
-
3089
+ @if (type === 'error') {
3090
+ <svg class="lokotro-error-icon" viewBox="0 0 52 52">
3091
+ <circle class="lokotro-error-circle" cx="26" cy="26" r="25" fill="none"/>
3092
+ <path class="lokotro-error-x" fill="none" d="M16 16l20 20M36 16l-20 20"/>
3093
+ </svg>
3094
+ }
3095
+
2891
3096
  <!-- Warning Icon -->
2892
- <svg *ngIf="type === 'warning'" class="lokotro-warning-icon" viewBox="0 0 52 52">
2893
- <circle class="lokotro-warning-circle" cx="26" cy="26" r="25" fill="none"/>
2894
- <path class="lokotro-warning-exclaim" fill="none" d="M26 15v15M26 37v1"/>
2895
- </svg>
2896
-
3097
+ @if (type === 'warning') {
3098
+ <svg class="lokotro-warning-icon" viewBox="0 0 52 52">
3099
+ <circle class="lokotro-warning-circle" cx="26" cy="26" r="25" fill="none"/>
3100
+ <path class="lokotro-warning-exclaim" fill="none" d="M26 15v15M26 37v1"/>
3101
+ </svg>
3102
+ }
3103
+
2897
3104
  <!-- Info Icon -->
2898
- <svg *ngIf="type === 'info'" class="lokotro-info-icon" viewBox="0 0 52 52">
2899
- <circle class="lokotro-info-circle" cx="26" cy="26" r="25" fill="none"/>
2900
- <path class="lokotro-info-i" fill="none" d="M26 22v15M26 15v1"/>
2901
- </svg>
3105
+ @if (type === 'info') {
3106
+ <svg class="lokotro-info-icon" viewBox="0 0 52 52">
3107
+ <circle class="lokotro-info-circle" cx="26" cy="26" r="25" fill="none"/>
3108
+ <path class="lokotro-info-i" fill="none" d="M26 22v15M26 15v1"/>
3109
+ </svg>
3110
+ }
2902
3111
  </div>
2903
-
3112
+
2904
3113
  <!-- Title -->
2905
3114
  <h2 class="lokotro-result-title">{{ title }}</h2>
2906
-
3115
+
2907
3116
  <!-- Message -->
2908
- <p class="lokotro-result-message" *ngIf="message">{{ message }}</p>
2909
-
2910
- <!-- Transaction Details -->
2911
- <div class="lokotro-result-details" *ngIf="type === 'success' && (amount || transactionId)">
2912
- <div class="lokotro-detail-row" *ngIf="amount">
2913
- <span class="lokotro-detail-label">{{ localization.translate('amount') }}</span>
2914
- <span class="lokotro-detail-value">{{ formatAmount(amount, currency) }}</span>
3117
+ @if (message) {
3118
+ <p class="lokotro-result-message">{{ message }}</p>
3119
+ }
3120
+
3121
+ <!-- Countdown Indicator -->
3122
+ @if (autoRedirectSeconds > 0 && countdownSeconds > 0 && !isCountdownCancelled) {
3123
+ <div class="lokotro-countdown-indicator"
3124
+ (click)="cancelCountdown()">
3125
+ <div class="lokotro-countdown-progress">
3126
+ <svg viewBox="0 0 36 36" class="lokotro-countdown-circle">
3127
+ <path class="lokotro-countdown-bg"
3128
+ d="M18 2.0845
3129
+ a 15.9155 15.9155 0 0 1 0 31.831
3130
+ a 15.9155 15.9155 0 0 1 0 -31.831"
3131
+ />
3132
+ <path class="lokotro-countdown-fill" [class]="'lokotro-countdown-' + type"
3133
+ [attr.stroke-dasharray]="getCountdownProgress() + ', 100'"
3134
+ d="M18 2.0845
3135
+ a 15.9155 15.9155 0 0 1 0 31.831
3136
+ a 15.9155 15.9155 0 0 1 0 -31.831"
3137
+ />
3138
+ </svg>
3139
+ </div>
3140
+ <span class="lokotro-countdown-text" [class]="'lokotro-text-' + type">
3141
+ {{ localization.translate('redirectingIn', { seconds: countdownSeconds }) }}
3142
+ </span>
3143
+ <svg class="lokotro-countdown-cancel" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3144
+ <path d="M18 6L6 18M6 6l12 12"/>
3145
+ </svg>
2915
3146
  </div>
2916
- <div class="lokotro-detail-row" *ngIf="transactionId">
2917
- <span class="lokotro-detail-label">Transaction ID</span>
2918
- <span class="lokotro-detail-value lokotro-mono">{{ transactionId }}</span>
3147
+ }
3148
+
3149
+ <!-- Transaction Details -->
3150
+ @if (type === 'success' && (amount || transactionId)) {
3151
+ <div class="lokotro-result-details">
3152
+ @if (amount) {
3153
+ <div class="lokotro-detail-row">
3154
+ <span class="lokotro-detail-label">{{ localization.translate('amount') }}</span>
3155
+ <span class="lokotro-detail-value">{{ formatAmount(amount, currency) }}</span>
3156
+ </div>
3157
+ }
3158
+ @if (transactionId) {
3159
+ <div class="lokotro-detail-row">
3160
+ <span class="lokotro-detail-label">Transaction ID</span>
3161
+ <span class="lokotro-detail-value lokotro-mono">{{ transactionId }}</span>
3162
+ </div>
3163
+ }
2919
3164
  </div>
2920
- </div>
2921
-
3165
+ }
3166
+
2922
3167
  <!-- Actions -->
2923
3168
  <div class="lokotro-result-actions">
2924
- <button
2925
- *ngIf="primaryActionLabel"
2926
- class="lokotro-btn-primary"
2927
- (click)="onPrimaryClick()">
2928
- {{ primaryActionLabel }}
2929
- </button>
2930
- <button
2931
- *ngIf="secondaryActionLabel"
2932
- class="lokotro-btn-secondary"
2933
- (click)="onSecondaryClick()">
2934
- {{ secondaryActionLabel }}
2935
- </button>
3169
+ @if (primaryActionLabel) {
3170
+ <button
3171
+ class="lokotro-btn-primary"
3172
+ (click)="onPrimaryClick()">
3173
+ {{ primaryActionLabel }}
3174
+ </button>
3175
+ }
3176
+ @if (secondaryActionLabel) {
3177
+ <button
3178
+ class="lokotro-btn-secondary"
3179
+ (click)="onSecondaryClick()">
3180
+ {{ secondaryActionLabel }}
3181
+ </button>
3182
+ }
2936
3183
  </div>
2937
3184
  </div>
2938
- `, isInline: true, styles: [".lokotro-result{display:flex;flex-direction:column;align-items:center;text-align:center;padding:48px 24px}.lokotro-result-icon{width:80px;height:80px;margin-bottom:24px}.lokotro-checkmark{width:100%;height:100%;stroke-width:3}.lokotro-checkmark-circle{stroke:var(--lokotro-success, #3BFBDA);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-checkmark-check{stroke:var(--lokotro-success, #3BFBDA);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:48;stroke-dashoffset:48;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-error-icon{width:100%;height:100%;stroke-width:3}.lokotro-error-circle{stroke:var(--lokotro-error, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-error-x{stroke:var(--lokotro-error, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:28;stroke-dashoffset:28;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-warning-icon{width:100%;height:100%;stroke-width:3}.lokotro-warning-circle{stroke:var(--lokotro-warning, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-warning-exclaim{stroke:var(--lokotro-warning, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-info-icon{width:100%;height:100%;stroke-width:3}.lokotro-info-circle{stroke:var(--lokotro-info, #3B82F6);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-info-i{stroke:var(--lokotro-info, #3B82F6);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}@keyframes lokotro-stroke{to{stroke-dashoffset:0}}.lokotro-result-title{font-size:24px;font-weight:700;margin:0 0 12px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-result-success .lokotro-result-title{color:var(--lokotro-success, #3BFBDA)}.lokotro-result-error .lokotro-result-title{color:var(--lokotro-error, #D97652)}.lokotro-result-message{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 24px;line-height:1.5}.lokotro-result-details{width:100%;max-width:320px;background:var(--lokotro-card, #3A4840);border-radius:16px;padding:16px;margin-bottom:32px}.lokotro-detail-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0}.lokotro-detail-row:not(:last-child){border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-detail-label{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-detail-value{font-size:14px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-mono{font-family:Courier New,monospace;font-size:12px}.lokotro-result-actions{display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px}.lokotro-btn-primary,.lokotro-btn-secondary{width:100%;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-result-success .lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-success, #3BFBDA),var(--lokotro-success-dark, #27D4B6));color:var(--lokotro-background-dark, #1C2621)}.lokotro-btn-primary:hover{transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
3185
+ `, isInline: true, styles: [".lokotro-result{display:flex;flex-direction:column;align-items:center;text-align:center;padding:48px 24px}.lokotro-result-icon{width:80px;height:80px;margin-bottom:24px}.lokotro-checkmark{width:100%;height:100%;stroke-width:3}.lokotro-checkmark-circle{stroke:var(--lokotro-success, #3BFBDA);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-checkmark-check{stroke:var(--lokotro-success, #3BFBDA);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:48;stroke-dashoffset:48;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-error-icon{width:100%;height:100%;stroke-width:3}.lokotro-error-circle{stroke:var(--lokotro-error, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-error-x{stroke:var(--lokotro-error, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:28;stroke-dashoffset:28;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-warning-icon{width:100%;height:100%;stroke-width:3}.lokotro-warning-circle{stroke:var(--lokotro-warning, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-warning-exclaim{stroke:var(--lokotro-warning, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-info-icon{width:100%;height:100%;stroke-width:3}.lokotro-info-circle{stroke:var(--lokotro-info, #3B82F6);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-info-i{stroke:var(--lokotro-info, #3B82F6);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}@keyframes lokotro-stroke{to{stroke-dashoffset:0}}.lokotro-result-title{font-size:24px;font-weight:700;margin:0 0 12px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-result-success .lokotro-result-title{color:var(--lokotro-success, #3BFBDA)}.lokotro-result-error .lokotro-result-title{color:var(--lokotro-error, #D97652)}.lokotro-result-message{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 24px;line-height:1.5}.lokotro-result-details{width:100%;max-width:320px;background:var(--lokotro-card, #3A4840);border-radius:16px;padding:16px;margin-bottom:32px}.lokotro-detail-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0}.lokotro-detail-row:not(:last-child){border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-detail-label{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-detail-value{font-size:14px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-mono{font-family:Courier New,monospace;font-size:12px}.lokotro-result-actions{display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px}.lokotro-btn-primary,.lokotro-btn-secondary{width:100%;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-result-success .lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-success, #3BFBDA),var(--lokotro-success-dark, #27D4B6));color:var(--lokotro-background-dark, #1C2621)}.lokotro-btn-primary:hover{transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-countdown-indicator{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--lokotro-primary-light, rgba(90, 94, 57, .1));border:1px solid var(--lokotro-primary-border, rgba(90, 94, 57, .3));border-radius:20px;cursor:pointer;margin-bottom:16px;transition:all .2s ease}.lokotro-countdown-indicator:hover{background:var(--lokotro-primary-light, rgba(90, 94, 57, .2))}.lokotro-result-error .lokotro-countdown-indicator{background:var(--lokotro-error-light, rgba(217, 118, 82, .1));border-color:var(--lokotro-error-border, rgba(217, 118, 82, .3))}.lokotro-countdown-progress{width:16px;height:16px}.lokotro-countdown-circle{width:100%;height:100%;transform:rotate(-90deg)}.lokotro-countdown-bg{fill:none;stroke:var(--lokotro-border, #3A473F);stroke-width:3}.lokotro-countdown-fill{fill:none;stroke-width:3;stroke-linecap:round;transition:stroke-dasharray .3s ease}.lokotro-countdown-success{stroke:var(--lokotro-success, #3BFBDA)}.lokotro-countdown-error{stroke:var(--lokotro-error, #D97652)}.lokotro-countdown-warning{stroke:var(--lokotro-warning, #D97652)}.lokotro-countdown-info{stroke:var(--lokotro-info, #3B82F6)}.lokotro-countdown-text{font-size:12px;font-weight:500}.lokotro-text-success{color:var(--lokotro-success, #3BFBDA)}.lokotro-text-error{color:var(--lokotro-error, #D97652)}.lokotro-text-warning{color:var(--lokotro-warning, #D97652)}.lokotro-text-info{color:var(--lokotro-info, #3B82F6)}.lokotro-countdown-cancel{color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }); }
2939
3186
  }
2940
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroResultComponent, decorators: [{
3187
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroResultComponent, decorators: [{
2941
3188
  type: Component,
2942
- args: [{ selector: 'lokotro-result', standalone: true, imports: [CommonModule], template: `
3189
+ args: [{ selector: 'lokotro-result', standalone: true, imports: [], template: `
2943
3190
  <div class="lokotro-result" [class]="'lokotro-result-' + type">
2944
3191
  <!-- Animated Icon -->
2945
3192
  <div class="lokotro-result-icon" [class]="'lokotro-icon-' + type">
2946
3193
  <!-- Success Icon -->
2947
- <svg *ngIf="type === 'success'" class="lokotro-checkmark" viewBox="0 0 52 52">
2948
- <circle class="lokotro-checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
2949
- <path class="lokotro-checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
2950
- </svg>
2951
-
3194
+ @if (type === 'success') {
3195
+ <svg class="lokotro-checkmark" viewBox="0 0 52 52">
3196
+ <circle class="lokotro-checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
3197
+ <path class="lokotro-checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
3198
+ </svg>
3199
+ }
3200
+
2952
3201
  <!-- Error Icon -->
2953
- <svg *ngIf="type === 'error'" class="lokotro-error-icon" viewBox="0 0 52 52">
2954
- <circle class="lokotro-error-circle" cx="26" cy="26" r="25" fill="none"/>
2955
- <path class="lokotro-error-x" fill="none" d="M16 16l20 20M36 16l-20 20"/>
2956
- </svg>
2957
-
3202
+ @if (type === 'error') {
3203
+ <svg class="lokotro-error-icon" viewBox="0 0 52 52">
3204
+ <circle class="lokotro-error-circle" cx="26" cy="26" r="25" fill="none"/>
3205
+ <path class="lokotro-error-x" fill="none" d="M16 16l20 20M36 16l-20 20"/>
3206
+ </svg>
3207
+ }
3208
+
2958
3209
  <!-- Warning Icon -->
2959
- <svg *ngIf="type === 'warning'" class="lokotro-warning-icon" viewBox="0 0 52 52">
2960
- <circle class="lokotro-warning-circle" cx="26" cy="26" r="25" fill="none"/>
2961
- <path class="lokotro-warning-exclaim" fill="none" d="M26 15v15M26 37v1"/>
2962
- </svg>
2963
-
3210
+ @if (type === 'warning') {
3211
+ <svg class="lokotro-warning-icon" viewBox="0 0 52 52">
3212
+ <circle class="lokotro-warning-circle" cx="26" cy="26" r="25" fill="none"/>
3213
+ <path class="lokotro-warning-exclaim" fill="none" d="M26 15v15M26 37v1"/>
3214
+ </svg>
3215
+ }
3216
+
2964
3217
  <!-- Info Icon -->
2965
- <svg *ngIf="type === 'info'" class="lokotro-info-icon" viewBox="0 0 52 52">
2966
- <circle class="lokotro-info-circle" cx="26" cy="26" r="25" fill="none"/>
2967
- <path class="lokotro-info-i" fill="none" d="M26 22v15M26 15v1"/>
2968
- </svg>
3218
+ @if (type === 'info') {
3219
+ <svg class="lokotro-info-icon" viewBox="0 0 52 52">
3220
+ <circle class="lokotro-info-circle" cx="26" cy="26" r="25" fill="none"/>
3221
+ <path class="lokotro-info-i" fill="none" d="M26 22v15M26 15v1"/>
3222
+ </svg>
3223
+ }
2969
3224
  </div>
2970
-
3225
+
2971
3226
  <!-- Title -->
2972
3227
  <h2 class="lokotro-result-title">{{ title }}</h2>
2973
-
3228
+
2974
3229
  <!-- Message -->
2975
- <p class="lokotro-result-message" *ngIf="message">{{ message }}</p>
2976
-
2977
- <!-- Transaction Details -->
2978
- <div class="lokotro-result-details" *ngIf="type === 'success' && (amount || transactionId)">
2979
- <div class="lokotro-detail-row" *ngIf="amount">
2980
- <span class="lokotro-detail-label">{{ localization.translate('amount') }}</span>
2981
- <span class="lokotro-detail-value">{{ formatAmount(amount, currency) }}</span>
3230
+ @if (message) {
3231
+ <p class="lokotro-result-message">{{ message }}</p>
3232
+ }
3233
+
3234
+ <!-- Countdown Indicator -->
3235
+ @if (autoRedirectSeconds > 0 && countdownSeconds > 0 && !isCountdownCancelled) {
3236
+ <div class="lokotro-countdown-indicator"
3237
+ (click)="cancelCountdown()">
3238
+ <div class="lokotro-countdown-progress">
3239
+ <svg viewBox="0 0 36 36" class="lokotro-countdown-circle">
3240
+ <path class="lokotro-countdown-bg"
3241
+ d="M18 2.0845
3242
+ a 15.9155 15.9155 0 0 1 0 31.831
3243
+ a 15.9155 15.9155 0 0 1 0 -31.831"
3244
+ />
3245
+ <path class="lokotro-countdown-fill" [class]="'lokotro-countdown-' + type"
3246
+ [attr.stroke-dasharray]="getCountdownProgress() + ', 100'"
3247
+ d="M18 2.0845
3248
+ a 15.9155 15.9155 0 0 1 0 31.831
3249
+ a 15.9155 15.9155 0 0 1 0 -31.831"
3250
+ />
3251
+ </svg>
3252
+ </div>
3253
+ <span class="lokotro-countdown-text" [class]="'lokotro-text-' + type">
3254
+ {{ localization.translate('redirectingIn', { seconds: countdownSeconds }) }}
3255
+ </span>
3256
+ <svg class="lokotro-countdown-cancel" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3257
+ <path d="M18 6L6 18M6 6l12 12"/>
3258
+ </svg>
2982
3259
  </div>
2983
- <div class="lokotro-detail-row" *ngIf="transactionId">
2984
- <span class="lokotro-detail-label">Transaction ID</span>
2985
- <span class="lokotro-detail-value lokotro-mono">{{ transactionId }}</span>
3260
+ }
3261
+
3262
+ <!-- Transaction Details -->
3263
+ @if (type === 'success' && (amount || transactionId)) {
3264
+ <div class="lokotro-result-details">
3265
+ @if (amount) {
3266
+ <div class="lokotro-detail-row">
3267
+ <span class="lokotro-detail-label">{{ localization.translate('amount') }}</span>
3268
+ <span class="lokotro-detail-value">{{ formatAmount(amount, currency) }}</span>
3269
+ </div>
3270
+ }
3271
+ @if (transactionId) {
3272
+ <div class="lokotro-detail-row">
3273
+ <span class="lokotro-detail-label">Transaction ID</span>
3274
+ <span class="lokotro-detail-value lokotro-mono">{{ transactionId }}</span>
3275
+ </div>
3276
+ }
2986
3277
  </div>
2987
- </div>
2988
-
3278
+ }
3279
+
2989
3280
  <!-- Actions -->
2990
3281
  <div class="lokotro-result-actions">
2991
- <button
2992
- *ngIf="primaryActionLabel"
2993
- class="lokotro-btn-primary"
2994
- (click)="onPrimaryClick()">
2995
- {{ primaryActionLabel }}
2996
- </button>
2997
- <button
2998
- *ngIf="secondaryActionLabel"
2999
- class="lokotro-btn-secondary"
3000
- (click)="onSecondaryClick()">
3001
- {{ secondaryActionLabel }}
3002
- </button>
3282
+ @if (primaryActionLabel) {
3283
+ <button
3284
+ class="lokotro-btn-primary"
3285
+ (click)="onPrimaryClick()">
3286
+ {{ primaryActionLabel }}
3287
+ </button>
3288
+ }
3289
+ @if (secondaryActionLabel) {
3290
+ <button
3291
+ class="lokotro-btn-secondary"
3292
+ (click)="onSecondaryClick()">
3293
+ {{ secondaryActionLabel }}
3294
+ </button>
3295
+ }
3003
3296
  </div>
3004
3297
  </div>
3005
- `, styles: [".lokotro-result{display:flex;flex-direction:column;align-items:center;text-align:center;padding:48px 24px}.lokotro-result-icon{width:80px;height:80px;margin-bottom:24px}.lokotro-checkmark{width:100%;height:100%;stroke-width:3}.lokotro-checkmark-circle{stroke:var(--lokotro-success, #3BFBDA);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-checkmark-check{stroke:var(--lokotro-success, #3BFBDA);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:48;stroke-dashoffset:48;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-error-icon{width:100%;height:100%;stroke-width:3}.lokotro-error-circle{stroke:var(--lokotro-error, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-error-x{stroke:var(--lokotro-error, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:28;stroke-dashoffset:28;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-warning-icon{width:100%;height:100%;stroke-width:3}.lokotro-warning-circle{stroke:var(--lokotro-warning, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-warning-exclaim{stroke:var(--lokotro-warning, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-info-icon{width:100%;height:100%;stroke-width:3}.lokotro-info-circle{stroke:var(--lokotro-info, #3B82F6);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-info-i{stroke:var(--lokotro-info, #3B82F6);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}@keyframes lokotro-stroke{to{stroke-dashoffset:0}}.lokotro-result-title{font-size:24px;font-weight:700;margin:0 0 12px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-result-success .lokotro-result-title{color:var(--lokotro-success, #3BFBDA)}.lokotro-result-error .lokotro-result-title{color:var(--lokotro-error, #D97652)}.lokotro-result-message{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 24px;line-height:1.5}.lokotro-result-details{width:100%;max-width:320px;background:var(--lokotro-card, #3A4840);border-radius:16px;padding:16px;margin-bottom:32px}.lokotro-detail-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0}.lokotro-detail-row:not(:last-child){border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-detail-label{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-detail-value{font-size:14px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-mono{font-family:Courier New,monospace;font-size:12px}.lokotro-result-actions{display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px}.lokotro-btn-primary,.lokotro-btn-secondary{width:100%;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-result-success .lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-success, #3BFBDA),var(--lokotro-success-dark, #27D4B6));color:var(--lokotro-background-dark, #1C2621)}.lokotro-btn-primary:hover{transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }]
3298
+ `, styles: [".lokotro-result{display:flex;flex-direction:column;align-items:center;text-align:center;padding:48px 24px}.lokotro-result-icon{width:80px;height:80px;margin-bottom:24px}.lokotro-checkmark{width:100%;height:100%;stroke-width:3}.lokotro-checkmark-circle{stroke:var(--lokotro-success, #3BFBDA);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-checkmark-check{stroke:var(--lokotro-success, #3BFBDA);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:48;stroke-dashoffset:48;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-error-icon{width:100%;height:100%;stroke-width:3}.lokotro-error-circle{stroke:var(--lokotro-error, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-error-x{stroke:var(--lokotro-error, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:28;stroke-dashoffset:28;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-warning-icon{width:100%;height:100%;stroke-width:3}.lokotro-warning-circle{stroke:var(--lokotro-warning, #D97652);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-warning-exclaim{stroke:var(--lokotro-warning, #D97652);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}.lokotro-info-icon{width:100%;height:100%;stroke-width:3}.lokotro-info-circle{stroke:var(--lokotro-info, #3B82F6);stroke-dasharray:166;stroke-dashoffset:166;animation:lokotro-stroke .6s cubic-bezier(.65,0,.45,1) forwards}.lokotro-info-i{stroke:var(--lokotro-info, #3B82F6);stroke-width:3;stroke-linecap:round;stroke-dasharray:20;stroke-dashoffset:20;animation:lokotro-stroke .3s cubic-bezier(.65,0,.45,1) .6s forwards}@keyframes lokotro-stroke{to{stroke-dashoffset:0}}.lokotro-result-title{font-size:24px;font-weight:700;margin:0 0 12px;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-result-success .lokotro-result-title{color:var(--lokotro-success, #3BFBDA)}.lokotro-result-error .lokotro-result-title{color:var(--lokotro-error, #D97652)}.lokotro-result-message{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0 0 24px;line-height:1.5}.lokotro-result-details{width:100%;max-width:320px;background:var(--lokotro-card, #3A4840);border-radius:16px;padding:16px;margin-bottom:32px}.lokotro-detail-row{display:flex;justify-content:space-between;align-items:center;padding:8px 0}.lokotro-detail-row:not(:last-child){border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-detail-label{font-size:14px;color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-detail-value{font-size:14px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-mono{font-family:Courier New,monospace;font-size:12px}.lokotro-result-actions{display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px}.lokotro-btn-primary,.lokotro-btn-secondary{width:100%;padding:14px 24px;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease}.lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-primary, #5A5E39),var(--lokotro-secondary, #6E7346));color:var(--lokotro-text-primary, #F2F0D5);border:none}.lokotro-result-success .lokotro-btn-primary{background:linear-gradient(135deg,var(--lokotro-success, #3BFBDA),var(--lokotro-success-dark, #27D4B6));color:var(--lokotro-background-dark, #1C2621)}.lokotro-btn-primary:hover{transform:translateY(-2px);box-shadow:0 8px 16px #0003}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #F2F0D5);border:2px solid var(--lokotro-border, #3A473F)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #D5D3B8)}.lokotro-countdown-indicator{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--lokotro-primary-light, rgba(90, 94, 57, .1));border:1px solid var(--lokotro-primary-border, rgba(90, 94, 57, .3));border-radius:20px;cursor:pointer;margin-bottom:16px;transition:all .2s ease}.lokotro-countdown-indicator:hover{background:var(--lokotro-primary-light, rgba(90, 94, 57, .2))}.lokotro-result-error .lokotro-countdown-indicator{background:var(--lokotro-error-light, rgba(217, 118, 82, .1));border-color:var(--lokotro-error-border, rgba(217, 118, 82, .3))}.lokotro-countdown-progress{width:16px;height:16px}.lokotro-countdown-circle{width:100%;height:100%;transform:rotate(-90deg)}.lokotro-countdown-bg{fill:none;stroke:var(--lokotro-border, #3A473F);stroke-width:3}.lokotro-countdown-fill{fill:none;stroke-width:3;stroke-linecap:round;transition:stroke-dasharray .3s ease}.lokotro-countdown-success{stroke:var(--lokotro-success, #3BFBDA)}.lokotro-countdown-error{stroke:var(--lokotro-error, #D97652)}.lokotro-countdown-warning{stroke:var(--lokotro-warning, #D97652)}.lokotro-countdown-info{stroke:var(--lokotro-info, #3B82F6)}.lokotro-countdown-text{font-size:12px;font-weight:500}.lokotro-text-success{color:var(--lokotro-success, #3BFBDA)}.lokotro-text-error{color:var(--lokotro-error, #D97652)}.lokotro-text-warning{color:var(--lokotro-warning, #D97652)}.lokotro-text-info{color:var(--lokotro-info, #3B82F6)}.lokotro-countdown-cancel{color:var(--lokotro-text-secondary, #D5D3B8)}\n"] }]
3006
3299
  }], ctorParameters: () => [{ type: LokotroLocalizationService }], propDecorators: { type: [{
3007
3300
  type: Input
3008
3301
  }], title: [{
@@ -3019,40 +3312,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3019
3312
  type: Input
3020
3313
  }], secondaryActionLabel: [{
3021
3314
  type: Input
3315
+ }], autoRedirectSeconds: [{
3316
+ type: Input
3022
3317
  }], primaryAction: [{
3023
3318
  type: Output
3024
3319
  }], secondaryAction: [{
3025
3320
  type: Output
3321
+ }], autoRedirect: [{
3322
+ type: Output
3026
3323
  }] } });
3027
3324
 
3028
3325
  /**
3029
3326
  * Lokotro Pay - Loading Component
3030
3327
  */
3031
3328
  class LokotroLoadingComponent {
3032
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3033
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroLoadingComponent, isStandalone: true, selector: "lokotro-loading", inputs: { message: "message" }, ngImport: i0, template: `
3329
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3330
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroLoadingComponent, isStandalone: true, selector: "lokotro-loading", inputs: { message: "message" }, ngImport: i0, template: `
3034
3331
  <div class="lokotro-loading">
3035
3332
  <div class="lokotro-loading-spinner">
3036
3333
  <div class="lokotro-pulse-ring"></div>
3037
3334
  <div class="lokotro-pulse-ring"></div>
3038
3335
  <div class="lokotro-pulse-dot"></div>
3039
3336
  </div>
3040
- <p class="lokotro-loading-message" *ngIf="message">{{ message }}</p>
3337
+ @if (message) {
3338
+ <p class="lokotro-loading-message">{{ message }}</p>
3339
+ }
3041
3340
  </div>
3042
- `, isInline: true, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
3341
+ `, isInline: true, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"] }); }
3043
3342
  }
3044
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLoadingComponent, decorators: [{
3343
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLoadingComponent, decorators: [{
3045
3344
  type: Component,
3046
- args: [{ selector: 'lokotro-loading', standalone: true, imports: [CommonModule], template: `
3345
+ args: [{ selector: 'lokotro-loading', standalone: true, imports: [], template: `
3047
3346
  <div class="lokotro-loading">
3048
3347
  <div class="lokotro-loading-spinner">
3049
3348
  <div class="lokotro-pulse-ring"></div>
3050
3349
  <div class="lokotro-pulse-ring"></div>
3051
3350
  <div class="lokotro-pulse-dot"></div>
3052
3351
  </div>
3053
- <p class="lokotro-loading-message" *ngIf="message">{{ message }}</p>
3352
+ @if (message) {
3353
+ <p class="lokotro-loading-message">{{ message }}</p>
3354
+ }
3054
3355
  </div>
3055
- `, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"] }]
3356
+ `, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"] }]
3056
3357
  }], propDecorators: { message: [{
3057
3358
  type: Input
3058
3359
  }] } });
@@ -3069,10 +3370,13 @@ const initialState = {
3069
3370
  currentScreen: LokotroPayScreenNavigation.LoadingScreen
3070
3371
  };
3071
3372
  class LokotroPaymentService {
3373
+ static { this.MOBILE_MONEY_POLL_INTERVAL = 5000; }
3374
+ static { this.MOBILE_MONEY_MAX_ATTEMPTS = 60; }
3072
3375
  constructor(httpClient) {
3073
3376
  this.httpClient = httpClient;
3074
3377
  this.stateSubject = new BehaviorSubject(initialState);
3075
3378
  this.state$ = this.stateSubject.asObservable();
3379
+ this.mobileMoneyPollingAttempts = 0;
3076
3380
  }
3077
3381
  /**
3078
3382
  * Get current state
@@ -3093,6 +3397,8 @@ class LokotroPaymentService {
3093
3397
  * Reset state to initial
3094
3398
  */
3095
3399
  resetState() {
3400
+ this.stopMobileMoneyPolling();
3401
+ this.currentPaymentBody = undefined;
3096
3402
  this.stateSubject.next(initialState);
3097
3403
  }
3098
3404
  /**
@@ -3112,32 +3418,26 @@ class LokotroPaymentService {
3112
3418
  * POST /payments/collect
3113
3419
  */
3114
3420
  createPayment(paymentBody) {
3421
+ this.currentPaymentBody = paymentBody;
3115
3422
  this.updateState({ isLoading: true });
3116
3423
  const requestData = this.convertPaymentBodyToRequest(paymentBody);
3117
- return this.httpClient.post(LokotroPayEnv.endpoints.collect, requestData).pipe(tap(response => {
3118
- if (response.isSuccess && response.data) {
3119
- const paymentInfo = this.parsePaymentInfo(response.data);
3120
- this.updateState({
3121
- isLoading: false,
3122
- transactionId: response.data.transaction_id,
3123
- paymentInfo,
3124
- currentScreen: paymentInfo.showPaymentMethodForm
3125
- ? LokotroPayScreenNavigation.PaymentMethodSelectionScreen
3126
- : LokotroPayScreenNavigation.PaymentFormScreen
3127
- });
3128
- }
3129
- else {
3424
+ console.log('[Lokotro Payment] Creating payment with body:', requestData);
3425
+ return this.httpClient.post(LokotroPayEnv.endpoints.collect, requestData).pipe(switchMap(response => {
3426
+ console.log('[Lokotro Payment] Create payment response:', response);
3427
+ if (!response.isSuccess || !response.data) {
3130
3428
  this.updateState({
3131
3429
  isLoading: false,
3132
3430
  error: {
3133
- message: response.message,
3431
+ message: response.message || 'Failed to create payment',
3134
3432
  title: 'Payment Creation Failed',
3135
3433
  errorCode: response.apiResponseCode,
3136
3434
  timestamp: new Date()
3137
3435
  },
3138
3436
  currentScreen: LokotroPayScreenNavigation.ErrorScreen
3139
3437
  });
3438
+ return of(response);
3140
3439
  }
3440
+ return this.resolveCollectResponse(this.unwrapPayload(response.data), paymentBody).pipe(map(() => response));
3141
3441
  }), catchError(error => {
3142
3442
  this.updateState({
3143
3443
  isLoading: false,
@@ -3156,7 +3456,7 @@ class LokotroPaymentService {
3156
3456
  * GET /payments/transaction/{transaction_id}
3157
3457
  */
3158
3458
  getTransactionDetails(transactionId) {
3159
- return this.httpClient.get(`${LokotroPayEnv.endpoints.transaction}/${transactionId}`).pipe();
3459
+ return this.httpClient.get(`${LokotroPayEnv.endpoints.transaction}/${transactionId}`);
3160
3460
  }
3161
3461
  /**
3162
3462
  * Step 3: Submit payment details
@@ -3170,29 +3470,43 @@ class LokotroPaymentService {
3170
3470
  const requestData = this.convertSubmitRequestToData(request);
3171
3471
  return this.httpClient.post(LokotroPayEnv.endpoints.submit, requestData).pipe(tap(response => {
3172
3472
  if (response.isSuccess && response.data) {
3173
- const submitResponse = this.parseSubmitResponse(response.data);
3473
+ const responseData = this.unwrapPayload(response.data);
3474
+ const submitResponse = this.parseSubmitResponse(responseData);
3174
3475
  if (submitResponse.requiresOtp) {
3175
3476
  this.updateState({
3176
3477
  isLoading: false,
3478
+ transactionId: submitResponse.transactionId || this.resolvePaymentId(request),
3177
3479
  currentScreen: LokotroPayScreenNavigation.EWalletOtpScreen
3178
3480
  });
3481
+ return;
3179
3482
  }
3180
- else if (submitResponse.redirectUrl) {
3181
- // Handle redirect for hosted checkout
3483
+ if (submitResponse.redirectUrl) {
3484
+ this.updateState({
3485
+ isLoading: false,
3486
+ transactionId: submitResponse.transactionId || this.resolvePaymentId(request)
3487
+ });
3182
3488
  window.location.href = submitResponse.redirectUrl;
3489
+ return;
3490
+ }
3491
+ if (submitResponse.status === LokotroPaymentStatus.PendingBankProofUpload) {
3492
+ this.handlePaymentSuccess(responseData, LokotroPaymentStatus.PendingBankProofUpload, 'Upload Proof Required');
3493
+ return;
3183
3494
  }
3184
- else if (LokotroPaymentStatusInfo.isSuccess(submitResponse.status)) {
3185
- this.handlePaymentSuccess(response.data);
3495
+ if (LokotroPaymentStatusInfo.isSuccess(submitResponse.status)) {
3496
+ this.handlePaymentSuccess(responseData);
3497
+ return;
3186
3498
  }
3187
- else if (LokotroPaymentStatusInfo.isPending(submitResponse.status)) {
3499
+ if (LokotroPaymentStatusInfo.isPending(submitResponse.status)) {
3500
+ const txId = submitResponse.transactionId || this.resolvePaymentId(request);
3188
3501
  this.updateState({
3189
3502
  isLoading: false,
3503
+ transactionId: txId,
3190
3504
  currentScreen: LokotroPayScreenNavigation.MobileMoneyProcessingScreen
3191
3505
  });
3506
+ this.startMobileMoneyPolling(txId);
3507
+ return;
3192
3508
  }
3193
- else {
3194
- this.handlePaymentFailure(response.message, response.apiResponseCode);
3195
- }
3509
+ this.handlePaymentFailure(this.getString(responseData['message']) || response.message, response.apiResponseCode);
3196
3510
  }
3197
3511
  else {
3198
3512
  this.handlePaymentFailure(response.message, response.apiResponseCode);
@@ -3209,16 +3523,17 @@ class LokotroPaymentService {
3209
3523
  verifyOtp(request) {
3210
3524
  this.updateState({ isLoading: true });
3211
3525
  return this.httpClient.post(LokotroPayEnv.endpoints.verifyOtp, {
3212
- transaction_id: request.transactionId,
3213
- otp: request.otp
3526
+ payment_id: request.paymentId || request.transactionId || this.currentState.transactionId,
3527
+ otp_code: request.otpCode || request.otp
3214
3528
  }).pipe(tap(response => {
3215
3529
  if (response.isSuccess && response.data) {
3216
- const status = LokotroPaymentStatusInfo.fromString(response.data.status || '');
3530
+ const responseData = this.unwrapPayload(response.data);
3531
+ const status = LokotroPaymentStatusInfo.fromString(this.getString(responseData['status']));
3217
3532
  if (LokotroPaymentStatusInfo.isSuccess(status)) {
3218
- this.handlePaymentSuccess(response.data);
3533
+ this.handlePaymentSuccess(responseData);
3219
3534
  }
3220
3535
  else {
3221
- this.handlePaymentFailure(response.message, response.apiResponseCode);
3536
+ this.handlePaymentFailure(this.getString(responseData['message']) || response.message, response.apiResponseCode);
3222
3537
  }
3223
3538
  }
3224
3539
  else {
@@ -3229,7 +3544,8 @@ class LokotroPaymentService {
3229
3544
  title: 'Verification Failed',
3230
3545
  errorCode: response.apiResponseCode,
3231
3546
  timestamp: new Date()
3232
- }
3547
+ },
3548
+ currentScreen: LokotroPayScreenNavigation.ErrorScreen
3233
3549
  });
3234
3550
  }
3235
3551
  }), catchError(error => {
@@ -3239,7 +3555,8 @@ class LokotroPaymentService {
3239
3555
  message: error.message || 'OTP verification failed',
3240
3556
  title: 'Error',
3241
3557
  timestamp: new Date()
3242
- }
3558
+ },
3559
+ currentScreen: LokotroPayScreenNavigation.ErrorScreen
3243
3560
  });
3244
3561
  return throwError(() => error);
3245
3562
  }));
@@ -3249,7 +3566,9 @@ class LokotroPaymentService {
3249
3566
  * POST /payments/resend-otp
3250
3567
  */
3251
3568
  resendOtp(request) {
3252
- return this.httpClient.post(LokotroPayEnv.endpoints.resendOtp, { transaction_id: request.transactionId });
3569
+ return this.httpClient.post(LokotroPayEnv.endpoints.resendOtp, {
3570
+ payment_id: request.paymentId || request.transactionId || this.currentState.transactionId
3571
+ });
3253
3572
  }
3254
3573
  /**
3255
3574
  * Select payment method
@@ -3266,20 +3585,71 @@ class LokotroPaymentService {
3266
3585
  navigateToScreen(screen) {
3267
3586
  this.updateState({ currentScreen: screen });
3268
3587
  }
3588
+ /**
3589
+ * Start polling for mobile money payment status
3590
+ */
3591
+ startMobileMoneyPolling(transactionId) {
3592
+ this.stopMobileMoneyPolling();
3593
+ this.mobileMoneyPollingAttempts = 0;
3594
+ console.log('[Lokotro Payment] Starting mobile money status polling for:', transactionId);
3595
+ this.mobileMoneyPollingTimer = setInterval(() => {
3596
+ this.mobileMoneyPollingAttempts++;
3597
+ if (this.mobileMoneyPollingAttempts > LokotroPaymentService.MOBILE_MONEY_MAX_ATTEMPTS) {
3598
+ console.warn('[Lokotro Payment] Mobile money polling max attempts reached');
3599
+ this.stopMobileMoneyPolling();
3600
+ this.handlePaymentFailure('Payment took too long. Please try again or contact support.');
3601
+ return;
3602
+ }
3603
+ const endpoint = `${LokotroPayEnv.endpoints.mobileMoneyStatus}/${transactionId}`;
3604
+ this.httpClient.get(endpoint).subscribe({
3605
+ next: (response) => {
3606
+ if (!response.isSuccess || !response.data)
3607
+ return;
3608
+ const data = this.unwrapPayload(response.data);
3609
+ const status = this.getString(data['status']).toLowerCase();
3610
+ console.log(`[Lokotro Payment] Mobile money poll #${this.mobileMoneyPollingAttempts}: status=${status}`);
3611
+ if (status === 'completed' || status === 'success' || status === 'approved') {
3612
+ this.stopMobileMoneyPolling();
3613
+ this.handlePaymentSuccess(data);
3614
+ }
3615
+ else if (status === 'failed' || status === 'error' || status === 'cancelled' || status === 'declined') {
3616
+ this.stopMobileMoneyPolling();
3617
+ this.handlePaymentFailure(this.getString(data['message']) || 'Payment failed or was declined');
3618
+ }
3619
+ },
3620
+ error: (err) => {
3621
+ console.error('[Lokotro Payment] Mobile money polling error:', err);
3622
+ }
3623
+ });
3624
+ }, LokotroPaymentService.MOBILE_MONEY_POLL_INTERVAL);
3625
+ }
3626
+ /**
3627
+ * Stop mobile money status polling
3628
+ */
3629
+ stopMobileMoneyPolling() {
3630
+ if (this.mobileMoneyPollingTimer) {
3631
+ clearInterval(this.mobileMoneyPollingTimer);
3632
+ this.mobileMoneyPollingTimer = undefined;
3633
+ this.mobileMoneyPollingAttempts = 0;
3634
+ console.log('[Lokotro Payment] Mobile money polling stopped');
3635
+ }
3636
+ }
3269
3637
  /**
3270
3638
  * Handle payment success
3271
3639
  */
3272
- handlePaymentSuccess(data) {
3640
+ handlePaymentSuccess(data, paymentStatus = LokotroPaymentStatus.Approved, defaultTitle = 'Success') {
3641
+ this.stopMobileMoneyPolling();
3642
+ const transactionId = this.getString(data['transaction_id']) || this.getString(data['payment_id']);
3273
3643
  const response = {
3274
- message: data['message'] || 'Payment successful',
3275
- title: data['title'] || 'Success',
3276
- customRef: data['custom_ref'] || data['customer_reference'] || '',
3277
- amount: parseFloat(data['amount'] || '0'),
3644
+ message: this.getString(data['message']) || 'Payment successful',
3645
+ title: this.getString(data['title']) || defaultTitle,
3646
+ customRef: this.getString(data['custom_ref']) || this.getString(data['customer_reference']),
3647
+ amount: this.getNumber(data['amount']),
3278
3648
  apiResponseCode: LokotroPayApiResponseCode.LOK000,
3279
- currency: data['currency'] || '',
3280
- paymentStatus: LokotroPaymentStatus.Approved,
3281
- systemRef: data['system_ref'] || data['transaction_id'] || '',
3282
- transactionId: data['transaction_id'],
3649
+ currency: this.getString(data['currency']) || this.getString(data['currency_str']),
3650
+ paymentStatus,
3651
+ systemRef: this.getString(data['system_reference']) || this.getString(data['system_ref']) || transactionId,
3652
+ transactionId,
3283
3653
  timestamp: new Date()
3284
3654
  };
3285
3655
  this.updateState({
@@ -3292,6 +3662,7 @@ class LokotroPaymentService {
3292
3662
  * Handle payment failure
3293
3663
  */
3294
3664
  handlePaymentFailure(message, errorCode) {
3665
+ this.stopMobileMoneyPolling();
3295
3666
  this.updateState({
3296
3667
  isLoading: false,
3297
3668
  error: {
@@ -3303,6 +3674,115 @@ class LokotroPaymentService {
3303
3674
  currentScreen: LokotroPayScreenNavigation.ErrorScreen
3304
3675
  });
3305
3676
  }
3677
+ /**
3678
+ * Resolve the collect response into the same flow the Flutter SDK uses.
3679
+ */
3680
+ resolveCollectResponse(collectData, paymentBody) {
3681
+ const paymentId = this.resolvePaymentId(collectData);
3682
+ const paymentStatus = this.getString(collectData['status']).toLowerCase();
3683
+ const paymentInfo = this.parsePaymentInfo(collectData);
3684
+ const additionalData = this.asRecord(collectData['additional_data']);
3685
+ if (paymentStatus === 'pending_otp' || paymentStatus === 'pending_otp_verification') {
3686
+ this.updateState({
3687
+ isLoading: false,
3688
+ transactionId: paymentId,
3689
+ paymentInfo,
3690
+ currentScreen: LokotroPayScreenNavigation.EWalletOtpScreen
3691
+ });
3692
+ return of(void 0);
3693
+ }
3694
+ if (paymentStatus === 'pending_bank_proof_upload') {
3695
+ this.handlePaymentSuccess(collectData, LokotroPaymentStatus.PendingBankProofUpload, 'Upload Proof Required');
3696
+ return of(void 0);
3697
+ }
3698
+ if (paymentStatus === 'completed' || paymentStatus === 'approved' || paymentStatus === 'success') {
3699
+ this.handlePaymentSuccess(collectData);
3700
+ return of(void 0);
3701
+ }
3702
+ if (paymentStatus === 'session_created') {
3703
+ const redirectUrl = this.getString(collectData['mastercardUrl']) ||
3704
+ this.getString(collectData['redirect_url']) ||
3705
+ this.getString(collectData['payment_url']);
3706
+ if (!redirectUrl) {
3707
+ this.handlePaymentFailure('Hosted session created but no checkout URL was provided');
3708
+ return of(void 0);
3709
+ }
3710
+ this.updateState({
3711
+ isLoading: false,
3712
+ transactionId: paymentId
3713
+ });
3714
+ window.location.href = redirectUrl;
3715
+ return of(void 0);
3716
+ }
3717
+ if (this.getString(additionalData?.['action']) === 'select_payment_method') {
3718
+ this.updateState({
3719
+ isLoading: false,
3720
+ transactionId: paymentId,
3721
+ paymentInfo,
3722
+ currentScreen: LokotroPayScreenNavigation.PaymentMethodSelectionScreen
3723
+ });
3724
+ return of(void 0);
3725
+ }
3726
+ if (paymentStatus === 'failed' || paymentStatus === 'rejected' || paymentStatus === 'declined') {
3727
+ this.handlePaymentFailure(this.getString(collectData['message']) || 'Payment failed');
3728
+ return of(void 0);
3729
+ }
3730
+ if (!paymentId) {
3731
+ this.handlePaymentFailure('Transaction ID not received from payment collection');
3732
+ return of(void 0);
3733
+ }
3734
+ return this.getTransactionDetails(paymentId).pipe(switchMap(response => this.resolveTransactionResponse(response, paymentBody)));
3735
+ }
3736
+ /**
3737
+ * Resolve transaction details into the correct screen or auto-submit flow.
3738
+ */
3739
+ resolveTransactionResponse(response, paymentBody) {
3740
+ if (!response.isSuccess || !response.data) {
3741
+ this.handlePaymentFailure(response.message || 'Failed to load transaction details', response.apiResponseCode);
3742
+ return of(void 0);
3743
+ }
3744
+ const transactionData = this.unwrapPayload(response.data);
3745
+ const paymentId = this.resolvePaymentId(transactionData);
3746
+ if (!paymentId) {
3747
+ this.handlePaymentFailure('Transaction ID not found in transaction details');
3748
+ return of(void 0);
3749
+ }
3750
+ const paymentInfo = this.parseTransactionPaymentInfo(transactionData);
3751
+ const selectedPaymentMethod = this.resolveSelectedPaymentMethod(transactionData, paymentInfo);
3752
+ if (!paymentInfo.showUserInfoForm && !paymentInfo.showPaymentMethodForm) {
3753
+ this.updateState({
3754
+ transactionId: paymentId,
3755
+ paymentInfo,
3756
+ selectedPaymentMethod
3757
+ });
3758
+ if (selectedPaymentMethod?.channel === LokotroPayChannel.Card) {
3759
+ const hostedCheckoutUrl = this.getString(transactionData['mastercardUrl']) ||
3760
+ this.getString(transactionData['redirect_url']);
3761
+ if (hostedCheckoutUrl) {
3762
+ this.updateState({ isLoading: false });
3763
+ window.location.href = hostedCheckoutUrl;
3764
+ return of(void 0);
3765
+ }
3766
+ }
3767
+ const paymentMethodId = selectedPaymentMethod?.id || this.getString(transactionData['payment_method_id']);
3768
+ if (!paymentMethodId) {
3769
+ this.handlePaymentFailure('Payment method not found for auto-processing');
3770
+ return of(void 0);
3771
+ }
3772
+ return this.submitPaymentDetails(this.buildAutoSubmitRequest(paymentId, paymentMethodId, paymentBody)).pipe(map(() => void 0));
3773
+ }
3774
+ const nextScreen = paymentInfo.showPaymentMethodForm
3775
+ ? LokotroPayScreenNavigation.PaymentMethodSelectionScreen
3776
+ : this.getFormScreenForChannel(selectedPaymentMethod?.channel || this.parseChannel(this.getString(transactionData['channel'])));
3777
+ this.updateState({
3778
+ isLoading: false,
3779
+ transactionId: paymentId,
3780
+ paymentInfo,
3781
+ selectedPaymentMethod,
3782
+ currentScreen: nextScreen
3783
+ });
3784
+ return of(void 0);
3785
+ }
3306
3786
  /**
3307
3787
  * Get form screen for payment channel
3308
3788
  */
@@ -3336,14 +3816,12 @@ class LokotroPaymentService {
3336
3816
  fee_covered_by: body.feeCoveredBy || 'buyer',
3337
3817
  delivery_behaviour: body.deliveryBehaviour || 'direct_delivery'
3338
3818
  };
3339
- // Add optional URLs
3340
3819
  if (body.notifyUrl)
3341
3820
  request['notify_url'] = body.notifyUrl;
3342
3821
  if (body.successRedirectUrl)
3343
3822
  request['success_redirect_url'] = body.successRedirectUrl;
3344
3823
  if (body.failRedirectUrl)
3345
3824
  request['fail_redirect_url'] = body.failRedirectUrl;
3346
- // Add user information
3347
3825
  if (body.userInfo === 'full') {
3348
3826
  if (body.firstName)
3349
3827
  request['first_name'] = body.firstName;
@@ -3354,7 +3832,6 @@ class LokotroPaymentService {
3354
3832
  if (body.email)
3355
3833
  request['email'] = body.email;
3356
3834
  }
3357
- // Add payment method specific fields
3358
3835
  if (body.paymentMethodInfo === 'full') {
3359
3836
  if (body.walletNumber)
3360
3837
  request['wallet_number'] = body.walletNumber;
@@ -3375,21 +3852,16 @@ class LokotroPaymentService {
3375
3852
  if (body.cardHolderName)
3376
3853
  request['card_holder_name'] = body.cardHolderName;
3377
3854
  }
3378
- // Add merchant information
3379
3855
  if (body.merchant) {
3380
3856
  request['merchant'] = {
3381
- id: body.merchant.id,
3382
3857
  name: body.merchant.name,
3383
- logo_url: body.merchant.logoUrl,
3384
- website: body.merchant.website,
3385
- description: body.merchant.description
3858
+ url: body.merchant.website,
3859
+ logo: body.merchant.logoUrl
3386
3860
  };
3387
3861
  }
3388
- // Add mastercard payment method
3389
3862
  if (body.mastercardPaymentMethod) {
3390
3863
  request['mastercard_payment_method'] = body.mastercardPaymentMethod;
3391
3864
  }
3392
- // Add metadata
3393
3865
  if (body.metadata) {
3394
3866
  request['metadata'] = body.metadata;
3395
3867
  }
@@ -3400,13 +3872,21 @@ class LokotroPaymentService {
3400
3872
  */
3401
3873
  convertSubmitRequestToData(request) {
3402
3874
  const data = {
3403
- transaction_id: request.transactionId,
3404
- payment_method: request.paymentMethod
3875
+ payment_id: this.resolvePaymentId(request),
3876
+ payment_method_id: request.paymentMethodId || request.paymentMethod || ''
3405
3877
  };
3878
+ if (request.firstName)
3879
+ data['first_name'] = request.firstName;
3880
+ if (request.lastName)
3881
+ data['last_name'] = request.lastName;
3882
+ if (request.email)
3883
+ data['email'] = request.email;
3884
+ if (request.phoneNumber)
3885
+ data['phone_number'] = request.phoneNumber;
3406
3886
  if (request.walletNumber)
3407
- data['wallet_number'] = request.walletNumber;
3887
+ data['ewallet_number'] = request.walletNumber;
3408
3888
  if (request.walletPin)
3409
- data['wallet_pin'] = request.walletPin;
3889
+ data['ewallet_pin'] = request.walletPin;
3410
3890
  if (request.mobileMoneyPhoneNumber)
3411
3891
  data['mobile_money_phone_number'] = request.mobileMoneyPhoneNumber;
3412
3892
  if (request.flashNumber)
@@ -3421,61 +3901,238 @@ class LokotroPaymentService {
3421
3901
  data['card_cvv'] = request.cardCvv;
3422
3902
  if (request.cardHolderName)
3423
3903
  data['card_holder_name'] = request.cardHolderName;
3904
+ if (request.bankTransferAccountId)
3905
+ data['bank_account_id'] = request.bankTransferAccountId;
3906
+ if (request.bankAccountId)
3907
+ data['bank_account_id'] = request.bankAccountId;
3908
+ if (request.bankAccountNumber)
3909
+ data['bank_account_number'] = request.bankAccountNumber;
3910
+ if (request.bankAccountLabel)
3911
+ data['bank_account_label'] = request.bankAccountLabel;
3912
+ if (request.bankId)
3913
+ data['bank_id'] = request.bankId;
3914
+ if (request.bankEntityId)
3915
+ data['bank_entity_id'] = request.bankEntityId;
3916
+ if (request.mastercardPaymentMethod)
3917
+ data['mastercard_payment_method'] = request.mastercardPaymentMethod;
3918
+ if (request.sessionId)
3919
+ data['session_id'] = request.sessionId;
3920
+ if (request.cardData)
3921
+ data['card_data'] = request.cardData;
3922
+ if (typeof request.savePaymentMethod === 'boolean')
3923
+ data['save_payment_method'] = request.savePaymentMethod;
3424
3924
  return data;
3425
3925
  }
3426
3926
  /**
3427
- * Parse payment info from API response
3927
+ * Parse payment info from collect response.
3428
3928
  */
3429
3929
  parsePaymentInfo(data) {
3930
+ const additionalData = this.asRecord(data['additional_data']);
3931
+ const availablePaymentMethods = this.getRecordArray(data['available_payment_methods'] || additionalData?.['payment_methods'])
3932
+ .map(method => this.parsePaymentMethod(method));
3933
+ const action = this.getString(additionalData?.['action']);
3934
+ return {
3935
+ id: this.resolvePaymentId(data),
3936
+ amount: this.getNumber(data['amount']),
3937
+ currency: this.getString(data['currency']) || 'USD',
3938
+ description: this.getString(data['description']),
3939
+ merchantName: this.getString(data['merchant_name']),
3940
+ merchantId: this.getString(data['merchant_id']),
3941
+ availablePaymentMethods,
3942
+ createdAt: new Date(this.getString(data['created_at']) || Date.now()),
3943
+ expiresAt: this.getString(data['expires_at']) ? new Date(this.getString(data['expires_at'])) : undefined,
3944
+ metadata: this.asRecord(data['metadata']),
3945
+ paymentUrl: this.getString(data['payment_url']) || undefined,
3946
+ showUserInfoForm: this.getBoolean(data['show_user_info_form']) || action === 'select_payment_method',
3947
+ showPaymentMethodForm: this.getBoolean(data['show_payment_method_form']) || action === 'select_payment_method'
3948
+ };
3949
+ }
3950
+ /**
3951
+ * Parse payment info from transaction details response.
3952
+ */
3953
+ parseTransactionPaymentInfo(data) {
3954
+ const fillingInfo = this.getString(data['filling_info']) || 'none';
3955
+ const channelInfo = this.getString(data['channel_info']) || 'none';
3430
3956
  return {
3431
- id: data.transaction_id || '',
3432
- amount: parseFloat(data.amount || '0'),
3433
- currency: data.currency || 'USD',
3434
- description: data.description || '',
3435
- merchantName: data.merchant_name || '',
3436
- merchantId: data.merchant_id || '',
3437
- availablePaymentMethods: (data.available_payment_methods || []).map(this.parsePaymentMethod),
3438
- createdAt: new Date(data.created_at || Date.now()),
3439
- expiresAt: data.expires_at ? new Date(data.expires_at) : undefined,
3440
- metadata: data.metadata,
3441
- paymentUrl: data.payment_url,
3442
- showUserInfoForm: data.show_user_info_form || false,
3443
- showPaymentMethodForm: data.show_payment_method_form || false
3957
+ id: this.resolvePaymentId(data),
3958
+ amount: this.getNumber(data['transactional_amount'] ?? data['amount']),
3959
+ currency: this.getString(data['transactional_currency']) || this.getString(data['currency_str']) || 'USD',
3960
+ description: this.getString(data['description']),
3961
+ merchantName: this.getString(data['merchant_name']),
3962
+ merchantId: this.getString(data['merchant_id']),
3963
+ availablePaymentMethods: this.getRecordArray(data['payment_methods']).map(method => this.parsePaymentMethod(method)),
3964
+ createdAt: new Date(this.getString(data['created_at']) || Date.now()),
3965
+ metadata: this.asRecord(data['metadata']),
3966
+ paymentUrl: this.getString(data['payment_url']) || undefined,
3967
+ showUserInfoForm: fillingInfo === 'none',
3968
+ showPaymentMethodForm: channelInfo === 'none',
3969
+ fillingInfo,
3970
+ channelInfo
3444
3971
  };
3445
3972
  }
3446
3973
  /**
3447
- * Parse payment method from API response
3974
+ * Parse payment method from API response.
3448
3975
  */
3449
3976
  parsePaymentMethod(data) {
3977
+ const nestedChannel = this.asRecord(data['channel']);
3978
+ const rawChannel = this.getString(nestedChannel?.['name']) ||
3979
+ this.getString(data['group']) ||
3980
+ this.getString(data['method']) ||
3981
+ this.getString(data['channel']) ||
3982
+ this.getString(data['name']);
3983
+ const name = this.getString(data['name']) || this.getString(data['method']) || rawChannel;
3450
3984
  return {
3451
- id: data['id'] || '',
3452
- name: data['name'] || '',
3453
- displayName: data['display_name'] || '',
3454
- channel: data['channel'] || LokotroPayChannel.None,
3455
- iconUrl: data['icon_url'] || data['icon'] || '',
3456
- isEnabled: data['is_enabled'] ?? true,
3457
- configuration: data['configuration'],
3458
- supportedCurrencies: data['supported_currencies']
3985
+ id: this.getString(data['id']) || this.getString(data['payment_method_id']) || name,
3986
+ name,
3987
+ displayName: this.getString(data['display_name']) || name,
3988
+ channel: this.parseChannel(rawChannel),
3989
+ iconUrl: this.getString(data['icon_url']) || this.getString(data['icon']),
3990
+ isEnabled: this.getBoolean(data['is_enabled'], this.getBoolean(data['available'], true)),
3991
+ configuration: this.asRecord(data['configuration']),
3992
+ supportedCurrencies: Array.isArray(data['supported_currencies'])
3993
+ ? data['supported_currencies']
3994
+ : undefined
3459
3995
  };
3460
3996
  }
3461
3997
  /**
3462
- * Parse submit response from API
3998
+ * Parse submit response from API.
3463
3999
  */
3464
4000
  parseSubmitResponse(data) {
4001
+ const rawStatus = this.getString(data['status']);
3465
4002
  return {
3466
- success: data.success || false,
3467
- message: data.message || '',
3468
- transactionId: data.transaction_id || '',
3469
- status: LokotroPaymentStatusInfo.fromString(data.status || ''),
3470
- requiresOtp: data.requires_otp || false,
3471
- otpDestination: data.otp_destination,
3472
- redirectUrl: data.redirect_url
4003
+ success: this.getBoolean(data['success']),
4004
+ message: this.getString(data['message']),
4005
+ transactionId: this.resolvePaymentId(data),
4006
+ status: LokotroPaymentStatusInfo.fromString(rawStatus),
4007
+ requiresOtp: this.getBoolean(data['requires_otp']) || rawStatus === 'pending_otp' || rawStatus === 'pending_otp_verification',
4008
+ otpDestination: this.getString(data['otp_destination']) || this.getString(data['otp_sent_to']) || undefined,
4009
+ redirectUrl: this.getString(data['redirect_url']) || this.getString(data['mastercardUrl']) || undefined
4010
+ };
4011
+ }
4012
+ /**
4013
+ * Build an auto-submit request using the original payment body.
4014
+ */
4015
+ buildAutoSubmitRequest(paymentId, paymentMethodId, paymentBody) {
4016
+ return {
4017
+ paymentId,
4018
+ paymentMethodId,
4019
+ paymentMethod: paymentBody.paymentMethod,
4020
+ firstName: paymentBody.firstName,
4021
+ lastName: paymentBody.lastName,
4022
+ email: paymentBody.email,
4023
+ phoneNumber: paymentBody.phoneNumber,
4024
+ walletNumber: paymentBody.walletNumber,
4025
+ walletPin: paymentBody.walletPin,
4026
+ mobileMoneyPhoneNumber: paymentBody.mobileMoneyPhoneNumber,
4027
+ flashNumber: paymentBody.flashNumber,
4028
+ flashPin: paymentBody.flashPin,
4029
+ cardNumber: paymentBody.cardNumber,
4030
+ cardExpiryDate: paymentBody.cardExpiryDate,
4031
+ cardCvv: paymentBody.cardCvv,
4032
+ cardHolderName: paymentBody.cardHolderName,
4033
+ mastercardPaymentMethod: paymentBody.mastercardPaymentMethod
3473
4034
  };
3474
4035
  }
3475
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
3476
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
4036
+ /**
4037
+ * Resolve the selected payment method from transaction details.
4038
+ */
4039
+ resolveSelectedPaymentMethod(transactionData, paymentInfo) {
4040
+ const paymentMethodId = this.getString(transactionData['payment_method_id']);
4041
+ const paymentMethodName = this.getString(transactionData['payment_method_str']) || this.getString(transactionData['payment_method']);
4042
+ const normalizedName = this.normalizeMethodKey(paymentMethodName);
4043
+ const exactMatch = paymentInfo.availablePaymentMethods.find(method => method.id === paymentMethodId ||
4044
+ this.normalizeMethodKey(method.name) === normalizedName ||
4045
+ this.normalizeMethodKey(method.displayName) === normalizedName);
4046
+ if (exactMatch) {
4047
+ return exactMatch;
4048
+ }
4049
+ const channel = this.parseChannel(this.getString(transactionData['channel']) || paymentMethodName);
4050
+ if (!paymentMethodId && !paymentMethodName && channel === LokotroPayChannel.None) {
4051
+ return paymentInfo.availablePaymentMethods[0];
4052
+ }
4053
+ return {
4054
+ id: paymentMethodId || paymentMethodName || channel,
4055
+ name: paymentMethodName || paymentMethodId || channel,
4056
+ displayName: paymentMethodName || paymentMethodId || channel,
4057
+ channel,
4058
+ iconUrl: '',
4059
+ isEnabled: true
4060
+ };
4061
+ }
4062
+ /**
4063
+ * Normalize payloads that sometimes return data nested inside data.
4064
+ */
4065
+ unwrapPayload(payload) {
4066
+ const record = this.asRecord(payload);
4067
+ if (!record) {
4068
+ return {};
4069
+ }
4070
+ const nestedData = this.asRecord(record['data']);
4071
+ return nestedData || record;
4072
+ }
4073
+ resolvePaymentId(source) {
4074
+ return this.getString(source['payment_id']) ||
4075
+ this.getString(source['transaction_id']) ||
4076
+ this.getString(source['paymentId']) ||
4077
+ this.getString(source['transactionId']) ||
4078
+ this.currentState.transactionId ||
4079
+ '';
4080
+ }
4081
+ parseChannel(channel) {
4082
+ switch (this.normalizeMethodKey(channel)) {
4083
+ case 'card':
4084
+ return LokotroPayChannel.Card;
4085
+ case 'mobilemoney':
4086
+ return LokotroPayChannel.MobileMoney;
4087
+ case 'wallet':
4088
+ case 'ewallet':
4089
+ case 'lokotrowallet':
4090
+ return LokotroPayChannel.EWallet;
4091
+ case 'flash':
4092
+ case 'eflash':
4093
+ return LokotroPayChannel.EFlash;
4094
+ case 'banktransfer':
4095
+ return LokotroPayChannel.BankTransfer;
4096
+ default:
4097
+ return LokotroPayChannel.None;
4098
+ }
4099
+ }
4100
+ normalizeMethodKey(value) {
4101
+ return value.toLowerCase().replace(/[_\s-]/g, '');
4102
+ }
4103
+ getNumber(value, fallback = 0) {
4104
+ if (typeof value === 'number') {
4105
+ return Number.isFinite(value) ? value : fallback;
4106
+ }
4107
+ if (typeof value === 'string') {
4108
+ const parsedValue = parseFloat(value);
4109
+ return Number.isFinite(parsedValue) ? parsedValue : fallback;
4110
+ }
4111
+ return fallback;
4112
+ }
4113
+ getString(value) {
4114
+ return typeof value === 'string' ? value : '';
4115
+ }
4116
+ getBoolean(value, fallback = false) {
4117
+ return typeof value === 'boolean' ? value : fallback;
4118
+ }
4119
+ asRecord(value) {
4120
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
4121
+ ? value
4122
+ : undefined;
4123
+ }
4124
+ getRecordArray(value) {
4125
+ if (!Array.isArray(value)) {
4126
+ return [];
4127
+ }
4128
+ return value
4129
+ .map(item => this.asRecord(item))
4130
+ .filter((item) => Boolean(item));
4131
+ }
4132
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4133
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
3477
4134
  }
3478
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, decorators: [{
4135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, decorators: [{
3479
4136
  type: Injectable,
3480
4137
  args: [{
3481
4138
  providedIn: 'root'
@@ -3487,6 +4144,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3487
4144
  * Main checkout widget with clean, theme-based design
3488
4145
  */
3489
4146
  class LokotroPayCheckoutComponent {
4147
+ // Retry attempts tracking
4148
+ static { this.MAX_RETRY_ATTEMPTS = 3; }
3490
4149
  constructor(paymentService, localization) {
3491
4150
  this.paymentService = paymentService;
3492
4151
  this.localization = localization;
@@ -3495,6 +4154,11 @@ class LokotroPayCheckoutComponent {
3495
4154
  this.error = new EventEmitter();
3496
4155
  this.closed = new EventEmitter();
3497
4156
  this.isDarkTheme = true;
4157
+ this.retryAttempts = 0;
4158
+ }
4159
+ /** Check if user has retries left */
4160
+ get hasRetriesLeft() {
4161
+ return this.retryAttempts < LokotroPayCheckoutComponent.MAX_RETRY_ATTEMPTS;
3498
4162
  }
3499
4163
  ngOnInit() {
3500
4164
  this.initializeEnvironment();
@@ -3507,11 +4171,18 @@ class LokotroPayCheckoutComponent {
3507
4171
  this.paymentService.resetState();
3508
4172
  }
3509
4173
  initializeEnvironment() {
4174
+ console.log('[Lokotro Checkout] initializeEnvironment - configs:', {
4175
+ token: this.configs.token?.substring(0, 20) + '...',
4176
+ isProduction: this.configs.isProduction,
4177
+ customApiUrl: this.configs.customApiUrl
4178
+ });
3510
4179
  LokotroPayEnv.initialize(this.configs.isProduction ?? false, this.configs.customApiUrl);
4180
+ console.log('[Lokotro Checkout] Calling setAppKey with token');
3511
4181
  this.paymentService.setAppKey(this.configs.token);
3512
4182
  if (this.configs.acceptLanguage) {
3513
4183
  this.paymentService.setAcceptLanguage(this.configs.acceptLanguage);
3514
4184
  }
4185
+ console.log('[Lokotro Checkout] Environment initialized');
3515
4186
  }
3516
4187
  initializeLocalization() {
3517
4188
  if (this.language) {
@@ -3613,6 +4284,8 @@ class LokotroPayCheckoutComponent {
3613
4284
  onFormSubmitted(formData) {
3614
4285
  const submitRequest = {
3615
4286
  transactionId: this.state?.transactionId || '',
4287
+ paymentId: this.state?.transactionId || '',
4288
+ paymentMethodId: this.state?.selectedPaymentMethod?.id || '',
3616
4289
  paymentMethod: this.state?.selectedPaymentMethod?.name || '',
3617
4290
  ...formData
3618
4291
  };
@@ -3626,8 +4299,8 @@ class LokotroPayCheckoutComponent {
3626
4299
  if (!this.state?.transactionId)
3627
4300
  return;
3628
4301
  this.paymentService.verifyOtp({
3629
- transactionId: this.state.transactionId,
3630
- otp
4302
+ paymentId: this.state.transactionId,
4303
+ otpCode: otp
3631
4304
  }).subscribe({
3632
4305
  error: (err) => {
3633
4306
  console.error('[Lokotro Pay] OTP verification error:', err);
@@ -3638,7 +4311,7 @@ class LokotroPayCheckoutComponent {
3638
4311
  if (!this.state?.transactionId)
3639
4312
  return;
3640
4313
  this.paymentService.resendOtp({
3641
- transactionId: this.state.transactionId
4314
+ paymentId: this.state.transactionId
3642
4315
  }).subscribe({
3643
4316
  error: (err) => {
3644
4317
  console.error('[Lokotro Pay] OTP resend error:', err);
@@ -3662,127 +4335,195 @@ class LokotroPayCheckoutComponent {
3662
4335
  this.closed.emit();
3663
4336
  }
3664
4337
  onRetry() {
4338
+ if (!this.hasRetriesLeft) {
4339
+ // No retries left, emit error and close
4340
+ this.onErrorAutoRedirect();
4341
+ return;
4342
+ }
4343
+ this.retryAttempts++;
3665
4344
  this.paymentService.resetState();
3666
4345
  this.initializePayment();
3667
4346
  }
3668
4347
  onDone() {
4348
+ // Emit response callback before closing
4349
+ if (this.state?.response) {
4350
+ this.response.emit({
4351
+ message: this.state.response.message,
4352
+ title: this.state.response.title,
4353
+ customRef: this.state.response.customRef,
4354
+ amount: this.state.response.amount,
4355
+ apiResponseCode: this.state.response.apiResponseCode,
4356
+ currency: this.state.response.currency,
4357
+ paymentStatus: this.state.response.paymentStatus,
4358
+ systemRef: this.state.response.systemRef,
4359
+ transactionId: this.state.response.transactionId,
4360
+ timestamp: this.state.response.timestamp || new Date()
4361
+ });
4362
+ }
3669
4363
  this.closed.emit();
3670
4364
  }
3671
4365
  onContinue() {
3672
4366
  // Continue from warning screen
3673
4367
  this.paymentService.navigateToScreen(LokotroPayScreenNavigation.PaymentMethodSelectionScreen);
3674
4368
  }
3675
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayCheckoutComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
3676
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroPayCheckoutComponent, isStandalone: true, selector: "lokotro-pay-checkout", inputs: { title: "title", titleStyle: "titleStyle", titleBackgroundColor: "titleBackgroundColor", configs: "configs", paymentBody: "paymentBody", enableHapticFeedback: "enableHapticFeedback", backgroundColor: "backgroundColor", padding: "padding", themeConfig: "themeConfig", language: "language" }, outputs: { response: "response", error: "error", closed: "closed" }, ngImport: i0, template: `
4369
+ /** Handle auto-redirect from success screen */
4370
+ onSuccessAutoRedirect() {
4371
+ if (this.state?.response) {
4372
+ this.response.emit({
4373
+ message: this.state.response.message,
4374
+ title: this.state.response.title,
4375
+ customRef: this.state.response.customRef,
4376
+ amount: this.state.response.amount,
4377
+ apiResponseCode: this.state.response.apiResponseCode,
4378
+ currency: this.state.response.currency,
4379
+ paymentStatus: this.state.response.paymentStatus,
4380
+ systemRef: this.state.response.systemRef,
4381
+ transactionId: this.state.response.transactionId,
4382
+ timestamp: this.state.response.timestamp || new Date()
4383
+ });
4384
+ }
4385
+ this.closed.emit();
4386
+ }
4387
+ /** Handle auto-redirect from error screen (after retries exhausted) */
4388
+ onErrorAutoRedirect() {
4389
+ if (this.state?.error) {
4390
+ this.error.emit({
4391
+ message: this.state.error.message,
4392
+ title: this.state.error.title,
4393
+ errorCode: this.state.error.errorCode,
4394
+ timestamp: this.state.error.timestamp
4395
+ });
4396
+ }
4397
+ this.closed.emit();
4398
+ }
4399
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayCheckoutComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
4400
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroPayCheckoutComponent, isStandalone: true, selector: "lokotro-pay-checkout", inputs: { title: "title", titleStyle: "titleStyle", titleBackgroundColor: "titleBackgroundColor", configs: "configs", paymentBody: "paymentBody", enableHapticFeedback: "enableHapticFeedback", backgroundColor: "backgroundColor", padding: "padding", themeConfig: "themeConfig", language: "language" }, outputs: { response: "response", error: "error", closed: "closed" }, ngImport: i0, template: `
3677
4401
  <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle">
3678
4402
  <!-- Header -->
3679
- <div class="lokotro-checkout-header" *ngIf="title" [style.background-color]="titleBackgroundColor">
3680
- <button class="lokotro-back-btn" (click)="onBack()" *ngIf="canGoBack">
3681
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3682
- <path d="M19 12H5M12 19l-7-7 7-7"/>
3683
- </svg>
3684
- </button>
3685
- <h2 class="lokotro-title" [style]="titleStyleString">{{ title }}</h2>
3686
- <button class="lokotro-close-btn" (click)="onClose()">
3687
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3688
- <path d="M18 6L6 18M6 6l12 12"/>
3689
- </svg>
3690
- </button>
3691
- </div>
3692
-
4403
+ @if (title) {
4404
+ <div class="lokotro-checkout-header" [style.background-color]="titleBackgroundColor">
4405
+ @if (canGoBack) {
4406
+ <button class="lokotro-back-btn" (click)="onBack()">
4407
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4408
+ <path d="M19 12H5M12 19l-7-7 7-7"/>
4409
+ </svg>
4410
+ </button>
4411
+ }
4412
+ <h2 class="lokotro-title" [style]="titleStyleString">{{ title }}</h2>
4413
+ <button class="lokotro-close-btn" (click)="onClose()">
4414
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4415
+ <path d="M18 6L6 18M6 6l12 12"/>
4416
+ </svg>
4417
+ </button>
4418
+ </div>
4419
+ }
4420
+
3693
4421
  <!-- Content -->
3694
4422
  <div class="lokotro-checkout-content">
3695
4423
  <!-- Loading Screen -->
3696
- <lokotro-loading
3697
- *ngIf="currentScreen === 'loadingScreen'"
3698
- [message]="localization.translate('loading')">
3699
- </lokotro-loading>
3700
-
4424
+ @if (currentScreen === 'loadingScreen') {
4425
+ <lokotro-loading
4426
+ [message]="localization.translate('loading')">
4427
+ </lokotro-loading>
4428
+ }
4429
+
3701
4430
  <!-- Payment Method Selection Screen -->
3702
- <lokotro-payment-method-selection
3703
- *ngIf="currentScreen === 'paymentMethodSelectionScreen'"
3704
- [paymentMethods]="state?.paymentInfo?.availablePaymentMethods || []"
3705
- [selectedMethod]="state?.selectedPaymentMethod"
3706
- (methodSelected)="onPaymentMethodSelected($event)">
3707
- </lokotro-payment-method-selection>
3708
-
4431
+ @if (currentScreen === 'paymentMethodSelectionScreen') {
4432
+ <lokotro-payment-method-selection
4433
+ [paymentMethods]="state?.paymentInfo?.availablePaymentMethods || []"
4434
+ [selectedMethod]="state?.selectedPaymentMethod"
4435
+ (methodSelected)="onPaymentMethodSelected($event)">
4436
+ </lokotro-payment-method-selection>
4437
+ }
4438
+
3709
4439
  <!-- Payment Form Screens -->
3710
- <lokotro-payment-form
3711
- *ngIf="isPaymentFormScreen"
3712
- [channel]="state?.selectedPaymentMethod?.channel"
3713
- [transactionId]="state?.transactionId"
3714
- (formSubmitted)="onFormSubmitted($event)"
3715
- (cancel)="onCancel()">
3716
- </lokotro-payment-form>
3717
-
4440
+ @if (isPaymentFormScreen) {
4441
+ <lokotro-payment-form
4442
+ [channel]="state?.selectedPaymentMethod?.channel"
4443
+ [transactionId]="state?.transactionId"
4444
+ [showUserInfoForm]="state?.paymentInfo?.showUserInfoForm || false"
4445
+ (formSubmitted)="onFormSubmitted($event)"
4446
+ (cancel)="onCancel()">
4447
+ </lokotro-payment-form>
4448
+ }
4449
+
3718
4450
  <!-- OTP Verification Screen -->
3719
- <lokotro-otp-verification
3720
- *ngIf="currentScreen === 'ewalletOtpScreen'"
3721
- [transactionId]="state?.transactionId"
3722
- (otpVerified)="onOtpVerified($event)"
3723
- (resendOtp)="onResendOtp()"
3724
- (cancel)="onCancel()">
3725
- </lokotro-otp-verification>
3726
-
4451
+ @if (currentScreen === 'ewalletOtpScreen') {
4452
+ <lokotro-otp-verification
4453
+ [transactionId]="state?.transactionId"
4454
+ (otpVerified)="onOtpVerified($event)"
4455
+ (resendOtp)="onResendOtp()"
4456
+ (cancel)="onCancel()">
4457
+ </lokotro-otp-verification>
4458
+ }
4459
+
3727
4460
  <!-- Processing Screens -->
3728
- <lokotro-processing
3729
- *ngIf="isProcessingScreen"
3730
- [type]="currentScreen === 'mobileMoneyProcessingScreen' ? 'mobileMoney' : 'default'"
3731
- [message]="localization.translate('processing')">
3732
- </lokotro-processing>
3733
-
4461
+ @if (isProcessingScreen) {
4462
+ <lokotro-processing
4463
+ [type]="currentScreen === 'mobileMoneyProcessingScreen' ? 'mobileMoney' : 'default'"
4464
+ [message]="localization.translate('processing')">
4465
+ </lokotro-processing>
4466
+ }
4467
+
3734
4468
  <!-- Success Screen -->
3735
- <lokotro-result
3736
- *ngIf="currentScreen === 'successScreen'"
3737
- type="success"
3738
- [title]="state?.response?.title || localization.translate('paymentSuccessful')"
3739
- [message]="state?.response?.message || ''"
3740
- [amount]="state?.response?.amount"
3741
- [currency]="state?.response?.currency"
3742
- [transactionId]="state?.response?.transactionId"
3743
- (primaryAction)="onDone()"
3744
- [primaryActionLabel]="localization.translate('done')">
3745
- </lokotro-result>
3746
-
4469
+ @if (currentScreen === 'successScreen') {
4470
+ <lokotro-result
4471
+ type="success"
4472
+ [title]="state?.response?.title || localization.translate('paymentSuccessful')"
4473
+ [message]="state?.response?.message || ''"
4474
+ [amount]="state?.response?.amount"
4475
+ [currency]="state?.response?.currency"
4476
+ [transactionId]="state?.response?.transactionId"
4477
+ [autoRedirectSeconds]="3"
4478
+ (autoRedirect)="onSuccessAutoRedirect()"
4479
+ (primaryAction)="onDone()"
4480
+ [primaryActionLabel]="localization.translate('done')">
4481
+ </lokotro-result>
4482
+ }
4483
+
3747
4484
  <!-- Error Screen -->
3748
- <lokotro-result
3749
- *ngIf="currentScreen === 'errorScreen'"
3750
- type="error"
3751
- [title]="state?.error?.title || localization.translate('paymentFailed')"
3752
- [message]="state?.error?.message || ''"
3753
- (primaryAction)="onRetry()"
3754
- [primaryActionLabel]="localization.translate('retry')"
3755
- (secondaryAction)="onClose()"
3756
- [secondaryActionLabel]="localization.translate('close')">
3757
- </lokotro-result>
3758
-
4485
+ @if (currentScreen === 'errorScreen') {
4486
+ <lokotro-result
4487
+ type="error"
4488
+ [title]="state?.error?.title || localization.translate('paymentFailed')"
4489
+ [message]="state?.error?.message || ''"
4490
+ [autoRedirectSeconds]="hasRetriesLeft ? 0 : 3"
4491
+ (autoRedirect)="onErrorAutoRedirect()"
4492
+ (primaryAction)="onRetry()"
4493
+ [primaryActionLabel]="hasRetriesLeft ? localization.translate('retry') : undefined"
4494
+ (secondaryAction)="onClose()"
4495
+ [secondaryActionLabel]="localization.translate('close')">
4496
+ </lokotro-result>
4497
+ }
4498
+
3759
4499
  <!-- Warning Screen -->
3760
- <lokotro-result
3761
- *ngIf="currentScreen === 'warningScreen'"
3762
- type="warning"
3763
- [title]="'Warning'"
3764
- [message]="state?.error?.message || ''"
3765
- (primaryAction)="onContinue()"
3766
- [primaryActionLabel]="localization.translate('continue')">
3767
- </lokotro-result>
3768
-
4500
+ @if (currentScreen === 'warningScreen') {
4501
+ <lokotro-result
4502
+ type="warning"
4503
+ [title]="'Warning'"
4504
+ [message]="state?.error?.message || ''"
4505
+ (primaryAction)="onContinue()"
4506
+ [primaryActionLabel]="localization.translate('continue')">
4507
+ </lokotro-result>
4508
+ }
4509
+
3769
4510
  <!-- Info Screen -->
3770
- <lokotro-result
3771
- *ngIf="currentScreen === 'infoScreen'"
3772
- type="info"
3773
- [title]="'Information'"
3774
- [message]="state?.error?.message || ''"
3775
- (primaryAction)="onClose()"
3776
- [primaryActionLabel]="localization.translate('close')">
3777
- </lokotro-result>
4511
+ @if (currentScreen === 'infoScreen') {
4512
+ <lokotro-result
4513
+ type="info"
4514
+ [title]="'Information'"
4515
+ [message]="state?.error?.message || ''"
4516
+ (primaryAction)="onClose()"
4517
+ [primaryActionLabel]="localization.translate('close')">
4518
+ </lokotro-result>
4519
+ }
3778
4520
  </div>
3779
4521
  </div>
3780
- `, isInline: true, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #1C2621);color:var(--lokotro-text-primary, #F2F0D5);font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-checkout-header{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-glass-light, rgba(255, 255, 255, .1))}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #1C2621;--lokotro-surface: #2A3832;--lokotro-text-primary: #F2F0D5;--lokotro-text-secondary: #D5D3B8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LokotroPaymentMethodSelectionComponent, selector: "lokotro-payment-method-selection", inputs: ["paymentMethods", "selectedMethod"], outputs: ["methodSelected"] }, { kind: "component", type: LokotroPaymentFormComponent, selector: "lokotro-payment-form", inputs: ["channel", "transactionId", "showUserInfoForm"], outputs: ["formSubmitted", "cancel"] }, { kind: "component", type: LokotroOtpVerificationComponent, selector: "lokotro-otp-verification", inputs: ["transactionId", "otpDestination", "otpLength"], outputs: ["otpVerified", "resendOtp", "cancel"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel"], outputs: ["primaryAction", "secondaryAction"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }] }); }
4522
+ `, isInline: true, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #1C2621);color:var(--lokotro-text-primary, #F2F0D5);font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-checkout-header{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-glass-light, rgba(255, 255, 255, .1))}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #1C2621;--lokotro-surface: #2A3832;--lokotro-text-primary: #F2F0D5;--lokotro-text-secondary: #D5D3B8}\n"], dependencies: [{ kind: "component", type: LokotroPaymentMethodSelectionComponent, selector: "lokotro-payment-method-selection", inputs: ["paymentMethods", "selectedMethod"], outputs: ["methodSelected"] }, { kind: "component", type: LokotroPaymentFormComponent, selector: "lokotro-payment-form", inputs: ["channel", "transactionId", "showUserInfoForm"], outputs: ["formSubmitted", "cancel"] }, { kind: "component", type: LokotroOtpVerificationComponent, selector: "lokotro-otp-verification", inputs: ["transactionId", "otpDestination", "otpLength"], outputs: ["otpVerified", "resendOtp", "cancel"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel", "autoRedirectSeconds"], outputs: ["primaryAction", "secondaryAction", "autoRedirect"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }] }); }
3781
4523
  }
3782
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
4524
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
3783
4525
  type: Component,
3784
4526
  args: [{ selector: 'lokotro-pay-checkout', standalone: true, imports: [
3785
- CommonModule,
3786
4527
  LokotroPaymentMethodSelectionComponent,
3787
4528
  LokotroPaymentFormComponent,
3788
4529
  LokotroOtpVerificationComponent,
@@ -3792,108 +4533,126 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
3792
4533
  ], template: `
3793
4534
  <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle">
3794
4535
  <!-- Header -->
3795
- <div class="lokotro-checkout-header" *ngIf="title" [style.background-color]="titleBackgroundColor">
3796
- <button class="lokotro-back-btn" (click)="onBack()" *ngIf="canGoBack">
3797
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3798
- <path d="M19 12H5M12 19l-7-7 7-7"/>
3799
- </svg>
3800
- </button>
3801
- <h2 class="lokotro-title" [style]="titleStyleString">{{ title }}</h2>
3802
- <button class="lokotro-close-btn" (click)="onClose()">
3803
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3804
- <path d="M18 6L6 18M6 6l12 12"/>
3805
- </svg>
3806
- </button>
3807
- </div>
3808
-
4536
+ @if (title) {
4537
+ <div class="lokotro-checkout-header" [style.background-color]="titleBackgroundColor">
4538
+ @if (canGoBack) {
4539
+ <button class="lokotro-back-btn" (click)="onBack()">
4540
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4541
+ <path d="M19 12H5M12 19l-7-7 7-7"/>
4542
+ </svg>
4543
+ </button>
4544
+ }
4545
+ <h2 class="lokotro-title" [style]="titleStyleString">{{ title }}</h2>
4546
+ <button class="lokotro-close-btn" (click)="onClose()">
4547
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4548
+ <path d="M18 6L6 18M6 6l12 12"/>
4549
+ </svg>
4550
+ </button>
4551
+ </div>
4552
+ }
4553
+
3809
4554
  <!-- Content -->
3810
4555
  <div class="lokotro-checkout-content">
3811
4556
  <!-- Loading Screen -->
3812
- <lokotro-loading
3813
- *ngIf="currentScreen === 'loadingScreen'"
3814
- [message]="localization.translate('loading')">
3815
- </lokotro-loading>
3816
-
4557
+ @if (currentScreen === 'loadingScreen') {
4558
+ <lokotro-loading
4559
+ [message]="localization.translate('loading')">
4560
+ </lokotro-loading>
4561
+ }
4562
+
3817
4563
  <!-- Payment Method Selection Screen -->
3818
- <lokotro-payment-method-selection
3819
- *ngIf="currentScreen === 'paymentMethodSelectionScreen'"
3820
- [paymentMethods]="state?.paymentInfo?.availablePaymentMethods || []"
3821
- [selectedMethod]="state?.selectedPaymentMethod"
3822
- (methodSelected)="onPaymentMethodSelected($event)">
3823
- </lokotro-payment-method-selection>
3824
-
4564
+ @if (currentScreen === 'paymentMethodSelectionScreen') {
4565
+ <lokotro-payment-method-selection
4566
+ [paymentMethods]="state?.paymentInfo?.availablePaymentMethods || []"
4567
+ [selectedMethod]="state?.selectedPaymentMethod"
4568
+ (methodSelected)="onPaymentMethodSelected($event)">
4569
+ </lokotro-payment-method-selection>
4570
+ }
4571
+
3825
4572
  <!-- Payment Form Screens -->
3826
- <lokotro-payment-form
3827
- *ngIf="isPaymentFormScreen"
3828
- [channel]="state?.selectedPaymentMethod?.channel"
3829
- [transactionId]="state?.transactionId"
3830
- (formSubmitted)="onFormSubmitted($event)"
3831
- (cancel)="onCancel()">
3832
- </lokotro-payment-form>
3833
-
4573
+ @if (isPaymentFormScreen) {
4574
+ <lokotro-payment-form
4575
+ [channel]="state?.selectedPaymentMethod?.channel"
4576
+ [transactionId]="state?.transactionId"
4577
+ [showUserInfoForm]="state?.paymentInfo?.showUserInfoForm || false"
4578
+ (formSubmitted)="onFormSubmitted($event)"
4579
+ (cancel)="onCancel()">
4580
+ </lokotro-payment-form>
4581
+ }
4582
+
3834
4583
  <!-- OTP Verification Screen -->
3835
- <lokotro-otp-verification
3836
- *ngIf="currentScreen === 'ewalletOtpScreen'"
3837
- [transactionId]="state?.transactionId"
3838
- (otpVerified)="onOtpVerified($event)"
3839
- (resendOtp)="onResendOtp()"
3840
- (cancel)="onCancel()">
3841
- </lokotro-otp-verification>
3842
-
4584
+ @if (currentScreen === 'ewalletOtpScreen') {
4585
+ <lokotro-otp-verification
4586
+ [transactionId]="state?.transactionId"
4587
+ (otpVerified)="onOtpVerified($event)"
4588
+ (resendOtp)="onResendOtp()"
4589
+ (cancel)="onCancel()">
4590
+ </lokotro-otp-verification>
4591
+ }
4592
+
3843
4593
  <!-- Processing Screens -->
3844
- <lokotro-processing
3845
- *ngIf="isProcessingScreen"
3846
- [type]="currentScreen === 'mobileMoneyProcessingScreen' ? 'mobileMoney' : 'default'"
3847
- [message]="localization.translate('processing')">
3848
- </lokotro-processing>
3849
-
4594
+ @if (isProcessingScreen) {
4595
+ <lokotro-processing
4596
+ [type]="currentScreen === 'mobileMoneyProcessingScreen' ? 'mobileMoney' : 'default'"
4597
+ [message]="localization.translate('processing')">
4598
+ </lokotro-processing>
4599
+ }
4600
+
3850
4601
  <!-- Success Screen -->
3851
- <lokotro-result
3852
- *ngIf="currentScreen === 'successScreen'"
3853
- type="success"
3854
- [title]="state?.response?.title || localization.translate('paymentSuccessful')"
3855
- [message]="state?.response?.message || ''"
3856
- [amount]="state?.response?.amount"
3857
- [currency]="state?.response?.currency"
3858
- [transactionId]="state?.response?.transactionId"
3859
- (primaryAction)="onDone()"
3860
- [primaryActionLabel]="localization.translate('done')">
3861
- </lokotro-result>
3862
-
4602
+ @if (currentScreen === 'successScreen') {
4603
+ <lokotro-result
4604
+ type="success"
4605
+ [title]="state?.response?.title || localization.translate('paymentSuccessful')"
4606
+ [message]="state?.response?.message || ''"
4607
+ [amount]="state?.response?.amount"
4608
+ [currency]="state?.response?.currency"
4609
+ [transactionId]="state?.response?.transactionId"
4610
+ [autoRedirectSeconds]="3"
4611
+ (autoRedirect)="onSuccessAutoRedirect()"
4612
+ (primaryAction)="onDone()"
4613
+ [primaryActionLabel]="localization.translate('done')">
4614
+ </lokotro-result>
4615
+ }
4616
+
3863
4617
  <!-- Error Screen -->
3864
- <lokotro-result
3865
- *ngIf="currentScreen === 'errorScreen'"
3866
- type="error"
3867
- [title]="state?.error?.title || localization.translate('paymentFailed')"
3868
- [message]="state?.error?.message || ''"
3869
- (primaryAction)="onRetry()"
3870
- [primaryActionLabel]="localization.translate('retry')"
3871
- (secondaryAction)="onClose()"
3872
- [secondaryActionLabel]="localization.translate('close')">
3873
- </lokotro-result>
3874
-
4618
+ @if (currentScreen === 'errorScreen') {
4619
+ <lokotro-result
4620
+ type="error"
4621
+ [title]="state?.error?.title || localization.translate('paymentFailed')"
4622
+ [message]="state?.error?.message || ''"
4623
+ [autoRedirectSeconds]="hasRetriesLeft ? 0 : 3"
4624
+ (autoRedirect)="onErrorAutoRedirect()"
4625
+ (primaryAction)="onRetry()"
4626
+ [primaryActionLabel]="hasRetriesLeft ? localization.translate('retry') : undefined"
4627
+ (secondaryAction)="onClose()"
4628
+ [secondaryActionLabel]="localization.translate('close')">
4629
+ </lokotro-result>
4630
+ }
4631
+
3875
4632
  <!-- Warning Screen -->
3876
- <lokotro-result
3877
- *ngIf="currentScreen === 'warningScreen'"
3878
- type="warning"
3879
- [title]="'Warning'"
3880
- [message]="state?.error?.message || ''"
3881
- (primaryAction)="onContinue()"
3882
- [primaryActionLabel]="localization.translate('continue')">
3883
- </lokotro-result>
3884
-
4633
+ @if (currentScreen === 'warningScreen') {
4634
+ <lokotro-result
4635
+ type="warning"
4636
+ [title]="'Warning'"
4637
+ [message]="state?.error?.message || ''"
4638
+ (primaryAction)="onContinue()"
4639
+ [primaryActionLabel]="localization.translate('continue')">
4640
+ </lokotro-result>
4641
+ }
4642
+
3885
4643
  <!-- Info Screen -->
3886
- <lokotro-result
3887
- *ngIf="currentScreen === 'infoScreen'"
3888
- type="info"
3889
- [title]="'Information'"
3890
- [message]="state?.error?.message || ''"
3891
- (primaryAction)="onClose()"
3892
- [primaryActionLabel]="localization.translate('close')">
3893
- </lokotro-result>
4644
+ @if (currentScreen === 'infoScreen') {
4645
+ <lokotro-result
4646
+ type="info"
4647
+ [title]="'Information'"
4648
+ [message]="state?.error?.message || ''"
4649
+ (primaryAction)="onClose()"
4650
+ [primaryActionLabel]="localization.translate('close')">
4651
+ </lokotro-result>
4652
+ }
3894
4653
  </div>
3895
4654
  </div>
3896
- `, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #1C2621);color:var(--lokotro-text-primary, #F2F0D5);font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-checkout-header{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-glass-light, rgba(255, 255, 255, .1))}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #1C2621;--lokotro-surface: #2A3832;--lokotro-text-primary: #F2F0D5;--lokotro-text-secondary: #D5D3B8}\n"] }]
4655
+ `, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #1C2621);color:var(--lokotro-text-primary, #F2F0D5);font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-checkout-header{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-glass-light, rgba(255, 255, 255, .1))}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #1C2621;--lokotro-surface: #2A3832;--lokotro-text-primary: #F2F0D5;--lokotro-text-secondary: #D5D3B8}\n"] }]
3897
4656
  }], ctorParameters: () => [{ type: LokotroPaymentService }, { type: LokotroLocalizationService }], propDecorators: { title: [{
3898
4657
  type: Input
3899
4658
  }], titleStyle: [{
@@ -3982,10 +4741,8 @@ class LokotroPayModule {
3982
4741
  ],
3983
4742
  };
3984
4743
  }
3985
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3986
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayModule, imports: [CommonModule,
3987
- HttpClientModule,
3988
- ReactiveFormsModule,
4744
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
4745
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
3989
4746
  FormsModule,
3990
4747
  // Standalone components
3991
4748
  LokotroPayCheckoutComponent,
@@ -4001,25 +4758,17 @@ class LokotroPayModule {
4001
4758
  LokotroProcessingComponent,
4002
4759
  LokotroResultComponent,
4003
4760
  LokotroLoadingComponent] }); }
4004
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayModule, imports: [CommonModule,
4005
- HttpClientModule,
4006
- ReactiveFormsModule,
4761
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4007
4762
  FormsModule,
4008
4763
  // Standalone components
4009
4764
  LokotroPayCheckoutComponent,
4010
- LokotroPaymentMethodSelectionComponent,
4011
4765
  LokotroPaymentFormComponent,
4012
- LokotroOtpVerificationComponent,
4013
- LokotroProcessingComponent,
4014
- LokotroResultComponent,
4015
- LokotroLoadingComponent] }); }
4766
+ LokotroOtpVerificationComponent] }); }
4016
4767
  }
4017
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayModule, decorators: [{
4768
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, decorators: [{
4018
4769
  type: NgModule,
4019
4770
  args: [{
4020
4771
  imports: [
4021
- CommonModule,
4022
- HttpClientModule,
4023
4772
  ReactiveFormsModule,
4024
4773
  FormsModule,
4025
4774
  // Standalone components
@@ -4477,210 +5226,242 @@ class LokotroPaymentStatusComponent {
4477
5226
  }
4478
5227
  this.onClose.emit();
4479
5228
  }
4480
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentStatusComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }, { token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Component }); }
4481
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroPaymentStatusComponent, isStandalone: true, selector: "lokotro-payment-status", inputs: { statusConfig: "statusConfig", showHeader: "showHeader", showCloseButton: "showCloseButton" }, outputs: { statusChange: "statusChange", paymentComplete: "paymentComplete", paymentFailed: "paymentFailed", onClose: "onClose", onDoneEvent: "onDoneEvent" }, ngImport: i0, template: `
5229
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentStatusComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }, { token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Component }); }
5230
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: LokotroPaymentStatusComponent, isStandalone: true, selector: "lokotro-payment-status", inputs: { statusConfig: "statusConfig", showHeader: "showHeader", showCloseButton: "showCloseButton" }, outputs: { statusChange: "statusChange", paymentComplete: "paymentComplete", paymentFailed: "paymentFailed", onClose: "onClose", onDoneEvent: "onDoneEvent" }, ngImport: i0, template: `
4482
5231
  <div class="lokotro-payment-status-container">
4483
5232
  <!-- Header -->
4484
- <div class="lokotro-status-header" *ngIf="showHeader">
4485
- <h2 class="lokotro-status-title">{{ localization.translate('paymentStatus') }}</h2>
4486
- <button class="lokotro-close-btn" (click)="onClose.emit()" *ngIf="showCloseButton">
4487
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4488
- <path d="M18 6L6 18M6 6l12 12"/>
4489
- </svg>
4490
- </button>
4491
- </div>
4492
-
5233
+ @if (showHeader) {
5234
+ <div class="lokotro-status-header">
5235
+ <h2 class="lokotro-status-title">{{ localization.translate('paymentStatus') }}</h2>
5236
+ @if (showCloseButton) {
5237
+ <button class="lokotro-close-btn" (click)="onClose.emit()">
5238
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5239
+ <path d="M18 6L6 18M6 6l12 12"/>
5240
+ </svg>
5241
+ </button>
5242
+ }
5243
+ </div>
5244
+ }
5245
+
4493
5246
  <!-- Content -->
4494
5247
  <div class="lokotro-status-content">
4495
5248
  <!-- Loading State -->
4496
- <lokotro-loading
4497
- *ngIf="currentScreen === 'loading'"
4498
- [message]="localization.translate('checkingPaymentStatus')">
4499
- </lokotro-loading>
4500
-
5249
+ @if (currentScreen === 'loading') {
5250
+ <lokotro-loading
5251
+ [message]="localization.translate('checkingPaymentStatus')">
5252
+ </lokotro-loading>
5253
+ }
5254
+
4501
5255
  <!-- Pending State -->
4502
- <div *ngIf="currentScreen === 'pending'" class="status-section pending">
4503
- <div class="status-icon pending-icon">
4504
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4505
- <circle cx="12" cy="12" r="10"/>
4506
- <polyline points="12 6 12 12 16 14"/>
4507
- </svg>
4508
- </div>
4509
- <h3 class="status-title">{{ localization.translate('paymentPending') }}</h3>
4510
- <p class="status-message">{{ localization.translate('paymentPendingMessage') }}</p>
4511
- <div class="payment-info" *ngIf="paymentDetails">
4512
- <div class="info-row">
4513
- <span class="info-label">{{ localization.translate('paymentId') }}:</span>
4514
- <span class="info-value">{{ paymentDetails.paymentId }}</span>
5256
+ @if (currentScreen === 'pending') {
5257
+ <div class="status-section pending">
5258
+ <div class="status-icon pending-icon">
5259
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5260
+ <circle cx="12" cy="12" r="10"/>
5261
+ <polyline points="12 6 12 12 16 14"/>
5262
+ </svg>
4515
5263
  </div>
4516
- <div class="info-row" *ngIf="paymentDetails.amount">
4517
- <span class="info-label">{{ localization.translate('amount') }}:</span>
4518
- <span class="info-value">{{ paymentDetails.amount }} {{ paymentDetails.currency }}</span>
5264
+ <h3 class="status-title">{{ localization.translate('paymentPending') }}</h3>
5265
+ <p class="status-message">{{ localization.translate('paymentPendingMessage') }}</p>
5266
+ @if (paymentDetails) {
5267
+ <div class="payment-info">
5268
+ <div class="info-row">
5269
+ <span class="info-label">{{ localization.translate('paymentId') }}:</span>
5270
+ <span class="info-value">{{ paymentDetails.paymentId }}</span>
5271
+ </div>
5272
+ @if (paymentDetails.amount) {
5273
+ <div class="info-row">
5274
+ <span class="info-label">{{ localization.translate('amount') }}:</span>
5275
+ <span class="info-value">{{ paymentDetails.amount }} {{ paymentDetails.currency }}</span>
5276
+ </div>
5277
+ }
5278
+ </div>
5279
+ }
5280
+ <div class="polling-indicator">
5281
+ <span class="pulse"></span>
5282
+ <span>{{ localization.translate('checkingForUpdates') }}</span>
4519
5283
  </div>
4520
5284
  </div>
4521
- <div class="polling-indicator">
4522
- <span class="pulse"></span>
4523
- <span>{{ localization.translate('checkingForUpdates') }}</span>
4524
- </div>
4525
- </div>
4526
-
5285
+ }
5286
+
4527
5287
  <!-- Processing State -->
4528
- <lokotro-processing
4529
- *ngIf="currentScreen === 'processing'"
4530
- type="default"
4531
- [message]="localization.translate('paymentProcessing')">
4532
- </lokotro-processing>
4533
-
5288
+ @if (currentScreen === 'processing') {
5289
+ <lokotro-processing
5290
+ type="default"
5291
+ [message]="localization.translate('paymentProcessing')">
5292
+ </lokotro-processing>
5293
+ }
5294
+
4534
5295
  <!-- Success State -->
4535
- <lokotro-result
4536
- *ngIf="currentScreen === 'success'"
4537
- type="success"
4538
- [title]="localization.translate('paymentSuccessful')"
4539
- [message]="paymentDetails?.message || localization.translate('paymentSuccessMessage')"
4540
- [amount]="paymentDetails?.amount"
4541
- [currency]="paymentDetails?.currency"
4542
- [transactionId]="paymentDetails?.transactionId"
4543
- (primaryAction)="onDone()"
4544
- [primaryActionLabel]="localization.translate('done')">
4545
- </lokotro-result>
4546
-
5296
+ @if (currentScreen === 'success') {
5297
+ <lokotro-result
5298
+ type="success"
5299
+ [title]="localization.translate('paymentSuccessful')"
5300
+ [message]="paymentDetails?.message || localization.translate('paymentSuccessMessage')"
5301
+ [amount]="paymentDetails?.amount"
5302
+ [currency]="paymentDetails?.currency"
5303
+ [transactionId]="paymentDetails?.transactionId"
5304
+ (primaryAction)="onDone()"
5305
+ [primaryActionLabel]="localization.translate('done')">
5306
+ </lokotro-result>
5307
+ }
5308
+
4547
5309
  <!-- Error State -->
4548
- <lokotro-result
4549
- *ngIf="currentScreen === 'error'"
4550
- type="error"
4551
- [title]="localization.translate('paymentFailed')"
4552
- [message]="errorMessage || localization.translate('paymentFailedMessage')"
4553
- (primaryAction)="retryCheck()"
4554
- [primaryActionLabel]="localization.translate('retry')"
4555
- (secondaryAction)="onClose.emit()"
4556
- [secondaryActionLabel]="localization.translate('close')">
4557
- </lokotro-result>
4558
-
5310
+ @if (currentScreen === 'error') {
5311
+ <lokotro-result
5312
+ type="error"
5313
+ [title]="localization.translate('paymentFailed')"
5314
+ [message]="errorMessage || localization.translate('paymentFailedMessage')"
5315
+ (primaryAction)="retryCheck()"
5316
+ [primaryActionLabel]="localization.translate('retry')"
5317
+ (secondaryAction)="onClose.emit()"
5318
+ [secondaryActionLabel]="localization.translate('close')">
5319
+ </lokotro-result>
5320
+ }
5321
+
4559
5322
  <!-- Cancelled State -->
4560
- <lokotro-result
4561
- *ngIf="currentScreen === 'cancelled'"
4562
- type="error"
4563
- [title]="localization.translate('paymentCancelled')"
4564
- [message]="localization.translate('paymentCancelledMessage')"
4565
- (primaryAction)="onClose.emit()"
4566
- [primaryActionLabel]="localization.translate('close')">
4567
- </lokotro-result>
4568
-
5323
+ @if (currentScreen === 'cancelled') {
5324
+ <lokotro-result
5325
+ type="error"
5326
+ [title]="localization.translate('paymentCancelled')"
5327
+ [message]="localization.translate('paymentCancelledMessage')"
5328
+ (primaryAction)="onClose.emit()"
5329
+ [primaryActionLabel]="localization.translate('close')">
5330
+ </lokotro-result>
5331
+ }
5332
+
4569
5333
  <!-- Expired State -->
4570
- <lokotro-result
4571
- *ngIf="currentScreen === 'expired'"
4572
- type="warning"
4573
- [title]="localization.translate('paymentExpired')"
4574
- [message]="localization.translate('paymentExpiredMessage')"
4575
- (primaryAction)="onClose.emit()"
4576
- [primaryActionLabel]="localization.translate('close')">
4577
- </lokotro-result>
5334
+ @if (currentScreen === 'expired') {
5335
+ <lokotro-result
5336
+ type="warning"
5337
+ [title]="localization.translate('paymentExpired')"
5338
+ [message]="localization.translate('paymentExpiredMessage')"
5339
+ (primaryAction)="onClose.emit()"
5340
+ [primaryActionLabel]="localization.translate('close')">
5341
+ </lokotro-result>
5342
+ }
4578
5343
  </div>
4579
5344
  </div>
4580
- `, isInline: true, styles: [".lokotro-payment-status-container{min-height:300px;display:flex;flex-direction:column;background:var(--lokotro-background, #1C2621);border-radius:16px;border:1px solid var(--lokotro-border, rgba(59, 251, 218, .1));overflow:hidden;font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-status-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, rgba(59, 251, 218, .1))}.lokotro-status-title{font-size:18px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0}.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-close-btn:hover{background:#ffffff1a}.lokotro-status-content{flex:1;padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}.status-section{text-align:center;width:100%;max-width:400px}.status-icon{width:80px;height:80px;margin:0 auto 24px;display:flex;align-items:center;justify-content:center;border-radius:50%}.pending-icon{background:#fbbf241a;color:#fbbf24}.status-title{font-size:22px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0 0 12px}.status-message{font-size:14px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .7));margin:0 0 24px;line-height:1.5}.payment-info{background:#5a5e3933;border-radius:12px;padding:16px;margin-bottom:24px}.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(242,240,213,.1)}.info-row:last-child{border-bottom:none}.info-label{color:var(--lokotro-text-secondary, rgba(242, 240, 213, .6));font-size:14px}.info-value{color:var(--lokotro-text-primary, #F2F0D5);font-size:14px;font-weight:500}.polling-indicator{display:flex;align-items:center;justify-content:center;gap:8px;font-size:12px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .5))}.pulse{width:8px;height:8px;background:var(--lokotro-primary, #3BFBDA);border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.2)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel"], outputs: ["primaryAction", "secondaryAction"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }] }); }
5345
+ `, isInline: true, styles: [".lokotro-payment-status-container{min-height:300px;display:flex;flex-direction:column;background:var(--lokotro-background, #1C2621);border-radius:16px;border:1px solid var(--lokotro-border, rgba(59, 251, 218, .1));overflow:hidden;font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-status-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, rgba(59, 251, 218, .1))}.lokotro-status-title{font-size:18px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0}.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-close-btn:hover{background:#ffffff1a}.lokotro-status-content{flex:1;padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}.status-section{text-align:center;width:100%;max-width:400px}.status-icon{width:80px;height:80px;margin:0 auto 24px;display:flex;align-items:center;justify-content:center;border-radius:50%}.pending-icon{background:#fbbf241a;color:#fbbf24}.status-title{font-size:22px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0 0 12px}.status-message{font-size:14px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .7));margin:0 0 24px;line-height:1.5}.payment-info{background:#5a5e3933;border-radius:12px;padding:16px;margin-bottom:24px}.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(242,240,213,.1)}.info-row:last-child{border-bottom:none}.info-label{color:var(--lokotro-text-secondary, rgba(242, 240, 213, .6));font-size:14px}.info-value{color:var(--lokotro-text-primary, #F2F0D5);font-size:14px;font-weight:500}.polling-indicator{display:flex;align-items:center;justify-content:center;gap:8px;font-size:12px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .5))}.pulse{width:8px;height:8px;background:var(--lokotro-primary, #3BFBDA);border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.2)}}\n"], dependencies: [{ kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel", "autoRedirectSeconds"], outputs: ["primaryAction", "secondaryAction", "autoRedirect"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }] }); }
4581
5346
  }
4582
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
5347
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
4583
5348
  type: Component,
4584
- args: [{ selector: 'lokotro-payment-status', standalone: true, imports: [CommonModule, LokotroLoadingComponent, LokotroResultComponent, LokotroProcessingComponent], template: `
5349
+ args: [{ selector: 'lokotro-payment-status', standalone: true, imports: [LokotroLoadingComponent, LokotroResultComponent, LokotroProcessingComponent], template: `
4585
5350
  <div class="lokotro-payment-status-container">
4586
5351
  <!-- Header -->
4587
- <div class="lokotro-status-header" *ngIf="showHeader">
4588
- <h2 class="lokotro-status-title">{{ localization.translate('paymentStatus') }}</h2>
4589
- <button class="lokotro-close-btn" (click)="onClose.emit()" *ngIf="showCloseButton">
4590
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4591
- <path d="M18 6L6 18M6 6l12 12"/>
4592
- </svg>
4593
- </button>
4594
- </div>
4595
-
5352
+ @if (showHeader) {
5353
+ <div class="lokotro-status-header">
5354
+ <h2 class="lokotro-status-title">{{ localization.translate('paymentStatus') }}</h2>
5355
+ @if (showCloseButton) {
5356
+ <button class="lokotro-close-btn" (click)="onClose.emit()">
5357
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5358
+ <path d="M18 6L6 18M6 6l12 12"/>
5359
+ </svg>
5360
+ </button>
5361
+ }
5362
+ </div>
5363
+ }
5364
+
4596
5365
  <!-- Content -->
4597
5366
  <div class="lokotro-status-content">
4598
5367
  <!-- Loading State -->
4599
- <lokotro-loading
4600
- *ngIf="currentScreen === 'loading'"
4601
- [message]="localization.translate('checkingPaymentStatus')">
4602
- </lokotro-loading>
4603
-
5368
+ @if (currentScreen === 'loading') {
5369
+ <lokotro-loading
5370
+ [message]="localization.translate('checkingPaymentStatus')">
5371
+ </lokotro-loading>
5372
+ }
5373
+
4604
5374
  <!-- Pending State -->
4605
- <div *ngIf="currentScreen === 'pending'" class="status-section pending">
4606
- <div class="status-icon pending-icon">
4607
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
4608
- <circle cx="12" cy="12" r="10"/>
4609
- <polyline points="12 6 12 12 16 14"/>
4610
- </svg>
4611
- </div>
4612
- <h3 class="status-title">{{ localization.translate('paymentPending') }}</h3>
4613
- <p class="status-message">{{ localization.translate('paymentPendingMessage') }}</p>
4614
- <div class="payment-info" *ngIf="paymentDetails">
4615
- <div class="info-row">
4616
- <span class="info-label">{{ localization.translate('paymentId') }}:</span>
4617
- <span class="info-value">{{ paymentDetails.paymentId }}</span>
5375
+ @if (currentScreen === 'pending') {
5376
+ <div class="status-section pending">
5377
+ <div class="status-icon pending-icon">
5378
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
5379
+ <circle cx="12" cy="12" r="10"/>
5380
+ <polyline points="12 6 12 12 16 14"/>
5381
+ </svg>
4618
5382
  </div>
4619
- <div class="info-row" *ngIf="paymentDetails.amount">
4620
- <span class="info-label">{{ localization.translate('amount') }}:</span>
4621
- <span class="info-value">{{ paymentDetails.amount }} {{ paymentDetails.currency }}</span>
5383
+ <h3 class="status-title">{{ localization.translate('paymentPending') }}</h3>
5384
+ <p class="status-message">{{ localization.translate('paymentPendingMessage') }}</p>
5385
+ @if (paymentDetails) {
5386
+ <div class="payment-info">
5387
+ <div class="info-row">
5388
+ <span class="info-label">{{ localization.translate('paymentId') }}:</span>
5389
+ <span class="info-value">{{ paymentDetails.paymentId }}</span>
5390
+ </div>
5391
+ @if (paymentDetails.amount) {
5392
+ <div class="info-row">
5393
+ <span class="info-label">{{ localization.translate('amount') }}:</span>
5394
+ <span class="info-value">{{ paymentDetails.amount }} {{ paymentDetails.currency }}</span>
5395
+ </div>
5396
+ }
5397
+ </div>
5398
+ }
5399
+ <div class="polling-indicator">
5400
+ <span class="pulse"></span>
5401
+ <span>{{ localization.translate('checkingForUpdates') }}</span>
4622
5402
  </div>
4623
5403
  </div>
4624
- <div class="polling-indicator">
4625
- <span class="pulse"></span>
4626
- <span>{{ localization.translate('checkingForUpdates') }}</span>
4627
- </div>
4628
- </div>
4629
-
5404
+ }
5405
+
4630
5406
  <!-- Processing State -->
4631
- <lokotro-processing
4632
- *ngIf="currentScreen === 'processing'"
4633
- type="default"
4634
- [message]="localization.translate('paymentProcessing')">
4635
- </lokotro-processing>
4636
-
5407
+ @if (currentScreen === 'processing') {
5408
+ <lokotro-processing
5409
+ type="default"
5410
+ [message]="localization.translate('paymentProcessing')">
5411
+ </lokotro-processing>
5412
+ }
5413
+
4637
5414
  <!-- Success State -->
4638
- <lokotro-result
4639
- *ngIf="currentScreen === 'success'"
4640
- type="success"
4641
- [title]="localization.translate('paymentSuccessful')"
4642
- [message]="paymentDetails?.message || localization.translate('paymentSuccessMessage')"
4643
- [amount]="paymentDetails?.amount"
4644
- [currency]="paymentDetails?.currency"
4645
- [transactionId]="paymentDetails?.transactionId"
4646
- (primaryAction)="onDone()"
4647
- [primaryActionLabel]="localization.translate('done')">
4648
- </lokotro-result>
4649
-
5415
+ @if (currentScreen === 'success') {
5416
+ <lokotro-result
5417
+ type="success"
5418
+ [title]="localization.translate('paymentSuccessful')"
5419
+ [message]="paymentDetails?.message || localization.translate('paymentSuccessMessage')"
5420
+ [amount]="paymentDetails?.amount"
5421
+ [currency]="paymentDetails?.currency"
5422
+ [transactionId]="paymentDetails?.transactionId"
5423
+ (primaryAction)="onDone()"
5424
+ [primaryActionLabel]="localization.translate('done')">
5425
+ </lokotro-result>
5426
+ }
5427
+
4650
5428
  <!-- Error State -->
4651
- <lokotro-result
4652
- *ngIf="currentScreen === 'error'"
4653
- type="error"
4654
- [title]="localization.translate('paymentFailed')"
4655
- [message]="errorMessage || localization.translate('paymentFailedMessage')"
4656
- (primaryAction)="retryCheck()"
4657
- [primaryActionLabel]="localization.translate('retry')"
4658
- (secondaryAction)="onClose.emit()"
4659
- [secondaryActionLabel]="localization.translate('close')">
4660
- </lokotro-result>
4661
-
5429
+ @if (currentScreen === 'error') {
5430
+ <lokotro-result
5431
+ type="error"
5432
+ [title]="localization.translate('paymentFailed')"
5433
+ [message]="errorMessage || localization.translate('paymentFailedMessage')"
5434
+ (primaryAction)="retryCheck()"
5435
+ [primaryActionLabel]="localization.translate('retry')"
5436
+ (secondaryAction)="onClose.emit()"
5437
+ [secondaryActionLabel]="localization.translate('close')">
5438
+ </lokotro-result>
5439
+ }
5440
+
4662
5441
  <!-- Cancelled State -->
4663
- <lokotro-result
4664
- *ngIf="currentScreen === 'cancelled'"
4665
- type="error"
4666
- [title]="localization.translate('paymentCancelled')"
4667
- [message]="localization.translate('paymentCancelledMessage')"
4668
- (primaryAction)="onClose.emit()"
4669
- [primaryActionLabel]="localization.translate('close')">
4670
- </lokotro-result>
4671
-
5442
+ @if (currentScreen === 'cancelled') {
5443
+ <lokotro-result
5444
+ type="error"
5445
+ [title]="localization.translate('paymentCancelled')"
5446
+ [message]="localization.translate('paymentCancelledMessage')"
5447
+ (primaryAction)="onClose.emit()"
5448
+ [primaryActionLabel]="localization.translate('close')">
5449
+ </lokotro-result>
5450
+ }
5451
+
4672
5452
  <!-- Expired State -->
4673
- <lokotro-result
4674
- *ngIf="currentScreen === 'expired'"
4675
- type="warning"
4676
- [title]="localization.translate('paymentExpired')"
4677
- [message]="localization.translate('paymentExpiredMessage')"
4678
- (primaryAction)="onClose.emit()"
4679
- [primaryActionLabel]="localization.translate('close')">
4680
- </lokotro-result>
5453
+ @if (currentScreen === 'expired') {
5454
+ <lokotro-result
5455
+ type="warning"
5456
+ [title]="localization.translate('paymentExpired')"
5457
+ [message]="localization.translate('paymentExpiredMessage')"
5458
+ (primaryAction)="onClose.emit()"
5459
+ [primaryActionLabel]="localization.translate('close')">
5460
+ </lokotro-result>
5461
+ }
4681
5462
  </div>
4682
5463
  </div>
4683
- `, styles: [".lokotro-payment-status-container{min-height:300px;display:flex;flex-direction:column;background:var(--lokotro-background, #1C2621);border-radius:16px;border:1px solid var(--lokotro-border, rgba(59, 251, 218, .1));overflow:hidden;font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-status-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, rgba(59, 251, 218, .1))}.lokotro-status-title{font-size:18px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0}.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-close-btn:hover{background:#ffffff1a}.lokotro-status-content{flex:1;padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}.status-section{text-align:center;width:100%;max-width:400px}.status-icon{width:80px;height:80px;margin:0 auto 24px;display:flex;align-items:center;justify-content:center;border-radius:50%}.pending-icon{background:#fbbf241a;color:#fbbf24}.status-title{font-size:22px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0 0 12px}.status-message{font-size:14px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .7));margin:0 0 24px;line-height:1.5}.payment-info{background:#5a5e3933;border-radius:12px;padding:16px;margin-bottom:24px}.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(242,240,213,.1)}.info-row:last-child{border-bottom:none}.info-label{color:var(--lokotro-text-secondary, rgba(242, 240, 213, .6));font-size:14px}.info-value{color:var(--lokotro-text-primary, #F2F0D5);font-size:14px;font-weight:500}.polling-indicator{display:flex;align-items:center;justify-content:center;gap:8px;font-size:12px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .5))}.pulse{width:8px;height:8px;background:var(--lokotro-primary, #3BFBDA);border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.2)}}\n"] }]
5464
+ `, styles: [".lokotro-payment-status-container{min-height:300px;display:flex;flex-direction:column;background:var(--lokotro-background, #1C2621);border-radius:16px;border:1px solid var(--lokotro-border, rgba(59, 251, 218, .1));overflow:hidden;font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-status-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, rgba(59, 251, 218, .1))}.lokotro-status-title{font-size:18px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0}.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-close-btn:hover{background:#ffffff1a}.lokotro-status-content{flex:1;padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}.status-section{text-align:center;width:100%;max-width:400px}.status-icon{width:80px;height:80px;margin:0 auto 24px;display:flex;align-items:center;justify-content:center;border-radius:50%}.pending-icon{background:#fbbf241a;color:#fbbf24}.status-title{font-size:22px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0 0 12px}.status-message{font-size:14px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .7));margin:0 0 24px;line-height:1.5}.payment-info{background:#5a5e3933;border-radius:12px;padding:16px;margin-bottom:24px}.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(242,240,213,.1)}.info-row:last-child{border-bottom:none}.info-label{color:var(--lokotro-text-secondary, rgba(242, 240, 213, .6));font-size:14px}.info-value{color:var(--lokotro-text-primary, #F2F0D5);font-size:14px;font-weight:500}.polling-indicator{display:flex;align-items:center;justify-content:center;gap:8px;font-size:12px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .5))}.pulse{width:8px;height:8px;background:var(--lokotro-primary, #3BFBDA);border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.2)}}\n"] }]
4684
5465
  }], ctorParameters: () => [{ type: LokotroPaymentService }, { type: LokotroLocalizationService }, { type: LokotroHttpClientService }], propDecorators: { statusConfig: [{
4685
5466
  type: Input
4686
5467
  }], showHeader: [{