@bloonio/lokotro-pay 1.1.2 → 1.2.1

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.
@@ -2,9 +2,9 @@ import * as i0 from '@angular/core';
2
2
  import { Injectable, EventEmitter, Output, Input, Component, forwardRef, InjectionToken, NgModule } from '@angular/core';
3
3
  import * as i1$1 from '@angular/forms';
4
4
  import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators } from '@angular/forms';
5
- import { BehaviorSubject, of, Subject, throwError, interval } from 'rxjs';
6
- import { timeout, map, catchError, tap, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
5
+ import { BehaviorSubject, of, throwError, Subject, interval } from 'rxjs';
7
6
  import { UpperCasePipe } from '@angular/common';
7
+ import { timeout, map, catchError, tap, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
8
8
  import * as i1 from '@angular/common/http';
9
9
  import { HttpHeaders, HttpErrorResponse } from '@angular/common/http';
10
10
 
@@ -17,7 +17,8 @@ import { HttpHeaders, HttpErrorResponse } from '@angular/common/http';
17
17
  */
18
18
  const LOKOTRO_DEV_ENV = {
19
19
  environment: 'development',
20
- apiBaseUrl: 'https://dev.app.api.gtwy.lokotro.com',
20
+ apiBaseUrl: 'http://10.37.31.218:6495',
21
+ // apiBaseUrl: 'https://dev.app.api.gtwy.lokotro.com',
21
22
  paymentApiVersion: 'v1',
22
23
  debugMode: true,
23
24
  logLevel: 'debug',
@@ -948,10 +949,10 @@ class LokotroLocalizationService {
948
949
  this.translationsSubject.next(TRANSLATIONS[language]);
949
950
  }
950
951
  }
951
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLocalizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
952
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLocalizationService, providedIn: 'root' }); }
952
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroLocalizationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
953
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroLocalizationService, providedIn: 'root' }); }
953
954
  }
954
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLocalizationService, decorators: [{
955
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroLocalizationService, decorators: [{
955
956
  type: Injectable,
956
957
  args: [{
957
958
  providedIn: 'root'
@@ -1013,8 +1014,8 @@ class LokotroPaymentMethodSelectionComponent {
1013
1014
  parent.appendChild(placeholder);
1014
1015
  }
1015
1016
  }
1016
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1017
- 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: `
1017
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1018
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LokotroPaymentMethodSelectionComponent, isStandalone: true, selector: "lokotro-payment-method-selection", inputs: { paymentMethods: "paymentMethods", selectedMethod: "selectedMethod" }, outputs: { methodSelected: "methodSelected" }, ngImport: i0, template: `
1018
1019
  <div class="lokotro-method-selection">
1019
1020
  <h3 class="lokotro-section-title">{{ localization.translate('selectPaymentMethod') }}</h3>
1020
1021
 
@@ -1067,7 +1068,7 @@ class LokotroPaymentMethodSelectionComponent {
1067
1068
  </div>
1068
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"] }); }
1069
1070
  }
1070
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, decorators: [{
1071
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentMethodSelectionComponent, decorators: [{
1071
1072
  type: Component,
1072
1073
  args: [{ selector: 'lokotro-payment-method-selection', standalone: true, imports: [], template: `
1073
1074
  <div class="lokotro-method-selection">
@@ -1252,6 +1253,7 @@ class LokotroHttpClientService {
1252
1253
  this.acceptLanguage = 'fr';
1253
1254
  this.customHeaders = {};
1254
1255
  this.instanceId = ++LokotroHttpClientService.instanceCounter;
1256
+ console.log(`[Lokotro HTTP] Instance #${this.instanceId} created`);
1255
1257
  }
1256
1258
  /**
1257
1259
  * Configure the HTTP client
@@ -1271,7 +1273,9 @@ class LokotroHttpClientService {
1271
1273
  * Set app-key for Lokotro Gateway authentication
1272
1274
  */
1273
1275
  setAppKey(appKey) {
1276
+ console.log(`[Lokotro HTTP #${this.instanceId}] setAppKey called with:`, appKey?.substring(0, 20) + '...');
1274
1277
  this.appKey = appKey;
1278
+ console.log(`[Lokotro HTTP #${this.instanceId}] appKey now set to:`, this.appKey?.substring(0, 20) + '...');
1275
1279
  }
1276
1280
  /**
1277
1281
  * Remove app-key
@@ -1295,6 +1299,7 @@ class LokotroHttpClientService {
1295
1299
  * Build request headers
1296
1300
  */
1297
1301
  buildHeaders() {
1302
+ console.log(`[Lokotro HTTP #${this.instanceId}] buildHeaders - appKey value:`, this.appKey?.substring(0, 20) + '...');
1298
1303
  let headers = new HttpHeaders({
1299
1304
  'Content-Type': 'application/json',
1300
1305
  'Accept': 'application/json',
@@ -1304,9 +1309,10 @@ class LokotroHttpClientService {
1304
1309
  });
1305
1310
  if (this.appKey) {
1306
1311
  headers = headers.set('app-key', this.appKey);
1312
+ console.log(`[Lokotro HTTP #${this.instanceId}] app-key header added`);
1307
1313
  }
1308
- else if (LokotroPayEnv.debugMode) {
1309
- console.warn('[Lokotro HTTP] appKey is not set');
1314
+ else {
1315
+ console.warn(`[Lokotro HTTP #${this.instanceId}] WARNING: appKey is not set, app-key header NOT added`);
1310
1316
  }
1311
1317
  // Add custom headers
1312
1318
  Object.entries(this.customHeaders).forEach(([key, value]) => {
@@ -1468,10 +1474,10 @@ class LokotroHttpClientService {
1468
1474
  return LokotroPayApiResponseCode.LOK001; // General error
1469
1475
  }
1470
1476
  }
1471
- 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 }); }
1472
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroHttpClientService, providedIn: 'root' }); }
1477
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroHttpClientService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
1478
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroHttpClientService, providedIn: 'root' }); }
1473
1479
  }
1474
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroHttpClientService, decorators: [{
1480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroHttpClientService, decorators: [{
1475
1481
  type: Injectable,
1476
1482
  args: [{
1477
1483
  providedIn: 'root'
@@ -1580,10 +1586,10 @@ class LokotroCountryService {
1580
1586
  this.countriesCache = undefined;
1581
1587
  this.countriesSubject.next([]);
1582
1588
  }
1583
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroCountryService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1584
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroCountryService, providedIn: 'root' }); }
1589
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroCountryService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1590
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroCountryService, providedIn: 'root' }); }
1585
1591
  }
1586
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroCountryService, decorators: [{
1592
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroCountryService, decorators: [{
1587
1593
  type: Injectable,
1588
1594
  args: [{
1589
1595
  providedIn: 'root'
@@ -1745,8 +1751,8 @@ class LokotroMobileMoneyPhoneInputComponent {
1745
1751
  }
1746
1752
  return null;
1747
1753
  }
1748
- 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 }); }
1749
- 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: [
1754
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, deps: [{ token: LokotroCountryService }, { token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
1755
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", 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: [
1750
1756
  {
1751
1757
  provide: NG_VALUE_ACCESSOR,
1752
1758
  useExisting: forwardRef(() => LokotroMobileMoneyPhoneInputComponent),
@@ -1865,7 +1871,7 @@ class LokotroMobileMoneyPhoneInputComponent {
1865
1871
  </div>
1866
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" }] }); }
1867
1873
  }
1868
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, decorators: [{
1874
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroMobileMoneyPhoneInputComponent, decorators: [{
1869
1875
  type: Component,
1870
1876
  args: [{ selector: 'lokotro-mobile-money-phone-input', standalone: true, imports: [FormsModule, ReactiveFormsModule, UpperCasePipe], providers: [
1871
1877
  {
@@ -2129,8 +2135,8 @@ class LokotroPaymentFormComponent {
2129
2135
  onCancel() {
2130
2136
  this.cancel.emit();
2131
2137
  }
2132
- 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 }); }
2133
- 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: `
2138
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", 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: "21.2.9", type: LokotroPaymentFormComponent, isStandalone: true, selector: "lokotro-payment-form", inputs: { channel: "channel", transactionId: "transactionId", showUserInfoForm: "showUserInfoForm" }, outputs: { formSubmitted: "formSubmitted", cancel: "cancel" }, ngImport: i0, template: `
2134
2140
  <div class="lokotro-payment-form">
2135
2141
  <!-- E-Wallet Form -->
2136
2142
  @if (isEWalletForm) {
@@ -2393,9 +2399,9 @@ class LokotroPaymentFormComponent {
2393
2399
  </form>
2394
2400
  }
2395
2401
  </div>
2396
- `, 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"] }] }); }
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],[formArray],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"] }] }); }
2397
2403
  }
2398
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentFormComponent, decorators: [{
2404
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentFormComponent, decorators: [{
2399
2405
  type: Component,
2400
2406
  args: [{ selector: 'lokotro-payment-form', standalone: true, imports: [FormsModule, ReactiveFormsModule, LokotroMobileMoneyPhoneInputComponent], template: `
2401
2407
  <div class="lokotro-payment-form">
@@ -2761,8 +2767,8 @@ class LokotroOtpVerificationComponent {
2761
2767
  onCancelClick() {
2762
2768
  this.cancel.emit();
2763
2769
  }
2764
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroOtpVerificationComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2765
- 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: `
2770
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroOtpVerificationComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2771
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", 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: `
2766
2772
  <div class="lokotro-otp-verification">
2767
2773
  <div class="lokotro-otp-icon">
2768
2774
  <svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
@@ -2821,7 +2827,7 @@ class LokotroOtpVerificationComponent {
2821
2827
  </div>
2822
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 }] }); }
2823
2829
  }
2824
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroOtpVerificationComponent, decorators: [{
2830
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroOtpVerificationComponent, decorators: [{
2825
2831
  type: Component,
2826
2832
  args: [{ selector: 'lokotro-otp-verification', standalone: true, imports: [FormsModule], template: `
2827
2833
  <div class="lokotro-otp-verification">
@@ -2909,8 +2915,8 @@ class LokotroProcessingComponent {
2909
2915
  setTimeout(() => this.currentStep = 3, 6000);
2910
2916
  }
2911
2917
  }
2912
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroProcessingComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2913
- 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: `
2918
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroProcessingComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
2919
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LokotroProcessingComponent, isStandalone: true, selector: "lokotro-processing", inputs: { type: "type", message: "message" }, ngImport: i0, template: `
2914
2920
  <div class="lokotro-processing">
2915
2921
  <div class="lokotro-processing-spinner">
2916
2922
  <div class="lokotro-spinner-ring"></div>
@@ -2945,7 +2951,7 @@ class LokotroProcessingComponent {
2945
2951
  </div>
2946
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"] }); }
2947
2953
  }
2948
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroProcessingComponent, decorators: [{
2954
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroProcessingComponent, decorators: [{
2949
2955
  type: Component,
2950
2956
  args: [{ selector: 'lokotro-processing', standalone: true, imports: [], template: `
2951
2957
  <div class="lokotro-processing">
@@ -2990,6 +2996,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
2990
2996
  /**
2991
2997
  * Lokotro Pay - Result Component
2992
2998
  * Success, Error, Warning, Info screens
2999
+ *
3000
+ * Designed for financial UX: clean white surface, prominent amount focal
3001
+ * on success, filled status badge, restrained transaction-detail panel.
2993
3002
  */
2994
3003
  class LokotroResultComponent {
2995
3004
  constructor(localization) {
@@ -3050,13 +3059,15 @@ class LokotroResultComponent {
3050
3059
  return 0;
3051
3060
  return (this.countdownSeconds / this.autoRedirectSeconds) * 100;
3052
3061
  }
3062
+ formatNumber(amount) {
3063
+ return new Intl.NumberFormat('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(amount);
3064
+ }
3053
3065
  formatAmount(amount, currency) {
3054
- const formatter = new Intl.NumberFormat('en-US', {
3066
+ return new Intl.NumberFormat('en-US', {
3055
3067
  style: 'currency',
3056
3068
  currency: currency || 'USD',
3057
3069
  minimumFractionDigits: 2
3058
- });
3059
- return formatter.format(amount);
3070
+ }).format(amount);
3060
3071
  }
3061
3072
  onPrimaryClick() {
3062
3073
  this.cancelCountdown();
@@ -3066,230 +3077,196 @@ class LokotroResultComponent {
3066
3077
  this.cancelCountdown();
3067
3078
  this.secondaryAction.emit();
3068
3079
  }
3069
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroResultComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
3070
- 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: `
3080
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroResultComponent, deps: [{ token: LokotroLocalizationService }], target: i0.ɵɵFactoryTarget.Component }); }
3081
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", 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: `
3071
3082
  <div class="lokotro-result" [class]="'lokotro-result-' + type">
3072
- <!-- Animated Icon -->
3073
- <div class="lokotro-result-icon" [class]="'lokotro-icon-' + type">
3074
- <!-- Success Icon -->
3075
- @if (type === 'success') {
3076
- <svg class="lokotro-checkmark" viewBox="0 0 52 52">
3077
- <circle class="lokotro-checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
3078
- <path class="lokotro-checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
3079
- </svg>
3080
- }
3081
-
3082
- <!-- Error Icon -->
3083
- @if (type === 'error') {
3084
- <svg class="lokotro-error-icon" viewBox="0 0 52 52">
3085
- <circle class="lokotro-error-circle" cx="26" cy="26" r="25" fill="none"/>
3086
- <path class="lokotro-error-x" fill="none" d="M16 16l20 20M36 16l-20 20"/>
3087
- </svg>
3088
- }
3089
-
3090
- <!-- Warning Icon -->
3091
- @if (type === 'warning') {
3092
- <svg class="lokotro-warning-icon" viewBox="0 0 52 52">
3093
- <circle class="lokotro-warning-circle" cx="26" cy="26" r="25" fill="none"/>
3094
- <path class="lokotro-warning-exclaim" fill="none" d="M26 15v15M26 37v1"/>
3095
- </svg>
3083
+ <div class="lokotro-result-card">
3084
+ <!-- Status Badge (filled circle with icon) -->
3085
+ <div class="lokotro-status-badge" [class]="'lokotro-status-' + type">
3086
+ @if (type === 'success') {
3087
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3088
+ <polyline points="5 12 10 17 19 8"/>
3089
+ </svg>
3090
+ }
3091
+ @if (type === 'error') {
3092
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3093
+ <line x1="18" y1="6" x2="6" y2="18"/>
3094
+ <line x1="6" y1="6" x2="18" y2="18"/>
3095
+ </svg>
3096
+ }
3097
+ @if (type === 'warning') {
3098
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3099
+ <line x1="12" y1="8" x2="12" y2="13"/>
3100
+ <line x1="12" y1="17" x2="12.01" y2="17"/>
3101
+ </svg>
3102
+ }
3103
+ @if (type === 'info') {
3104
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3105
+ <line x1="12" y1="11" x2="12" y2="16"/>
3106
+ <line x1="12" y1="7" x2="12.01" y2="7"/>
3107
+ </svg>
3108
+ }
3109
+ </div>
3110
+
3111
+ <!-- Amount focal (success only, when amount provided) -->
3112
+ @if (type === 'success' && amount !== undefined && amount !== null) {
3113
+ <div class="lokotro-amount-focal">
3114
+ <span class="lokotro-amount-currency">{{ (currency || '').toUpperCase() }}</span>
3115
+ <span class="lokotro-amount-value">{{ formatNumber(amount) }}</span>
3116
+ </div>
3096
3117
  }
3097
-
3098
- <!-- Info Icon -->
3099
- @if (type === 'info') {
3100
- <svg class="lokotro-info-icon" viewBox="0 0 52 52">
3101
- <circle class="lokotro-info-circle" cx="26" cy="26" r="25" fill="none"/>
3102
- <path class="lokotro-info-i" fill="none" d="M26 22v15M26 15v1"/>
3103
- </svg>
3118
+
3119
+ <!-- Title -->
3120
+ <h2 class="lokotro-result-title">{{ title }}</h2>
3121
+
3122
+ <!-- Message -->
3123
+ @if (message) {
3124
+ <p class="lokotro-result-message">{{ message }}</p>
3104
3125
  }
3105
- </div>
3106
-
3107
- <!-- Title -->
3108
- <h2 class="lokotro-result-title">{{ title }}</h2>
3109
-
3110
- <!-- Message -->
3111
- @if (message) {
3112
- <p class="lokotro-result-message">{{ message }}</p>
3113
- }
3114
-
3115
- <!-- Countdown Indicator -->
3116
- @if (autoRedirectSeconds > 0 && countdownSeconds > 0 && !isCountdownCancelled) {
3117
- <div class="lokotro-countdown-indicator"
3118
- (click)="cancelCountdown()">
3119
- <div class="lokotro-countdown-progress">
3126
+
3127
+ <!-- Countdown Indicator -->
3128
+ @if (autoRedirectSeconds > 0 && countdownSeconds > 0 && !isCountdownCancelled) {
3129
+ <button type="button" class="lokotro-countdown-indicator" (click)="cancelCountdown()">
3120
3130
  <svg viewBox="0 0 36 36" class="lokotro-countdown-circle">
3121
3131
  <path class="lokotro-countdown-bg"
3122
- d="M18 2.0845
3123
- a 15.9155 15.9155 0 0 1 0 31.831
3124
- a 15.9155 15.9155 0 0 1 0 -31.831"
3125
- />
3132
+ d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
3126
3133
  <path class="lokotro-countdown-fill" [class]="'lokotro-countdown-' + type"
3127
3134
  [attr.stroke-dasharray]="getCountdownProgress() + ', 100'"
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
- />
3135
+ d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
3132
3136
  </svg>
3133
- </div>
3134
- <span class="lokotro-countdown-text" [class]="'lokotro-text-' + type">
3135
- {{ localization.translate('redirectingIn', { seconds: countdownSeconds }) }}
3136
- </span>
3137
- <svg class="lokotro-countdown-cancel" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3138
- <path d="M18 6L6 18M6 6l12 12"/>
3139
- </svg>
3140
- </div>
3141
- }
3142
-
3143
- <!-- Transaction Details -->
3144
- @if (type === 'success' && (amount || transactionId)) {
3145
- <div class="lokotro-result-details">
3146
- @if (amount) {
3147
- <div class="lokotro-detail-row">
3148
- <span class="lokotro-detail-label">{{ localization.translate('amount') }}</span>
3149
- <span class="lokotro-detail-value">{{ formatAmount(amount, currency) }}</span>
3150
- </div>
3151
- }
3152
- @if (transactionId) {
3137
+ <span class="lokotro-countdown-text">
3138
+ {{ localization.translate('redirectingIn', { seconds: countdownSeconds }) }}
3139
+ </span>
3140
+ <svg class="lokotro-countdown-cancel" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3141
+ <path d="M18 6L6 18M6 6l12 12"/>
3142
+ </svg>
3143
+ </button>
3144
+ }
3145
+
3146
+ <!-- Transaction Details -->
3147
+ @if (type === 'success' && transactionId) {
3148
+ <div class="lokotro-result-details">
3153
3149
  <div class="lokotro-detail-row">
3154
3150
  <span class="lokotro-detail-label">Transaction ID</span>
3155
3151
  <span class="lokotro-detail-value lokotro-mono">{{ transactionId }}</span>
3156
3152
  </div>
3153
+ </div>
3154
+ }
3155
+ </div>
3156
+
3157
+ <!-- Actions -->
3158
+ @if (primaryActionLabel || secondaryActionLabel) {
3159
+ <div class="lokotro-result-actions">
3160
+ @if (primaryActionLabel) {
3161
+ <button type="button" class="lokotro-btn-primary" (click)="onPrimaryClick()">
3162
+ {{ primaryActionLabel }}
3163
+ </button>
3164
+ }
3165
+ @if (secondaryActionLabel) {
3166
+ <button type="button" class="lokotro-btn-secondary" (click)="onSecondaryClick()">
3167
+ {{ secondaryActionLabel }}
3168
+ </button>
3157
3169
  }
3158
3170
  </div>
3159
3171
  }
3160
-
3161
- <!-- Actions -->
3162
- <div class="lokotro-result-actions">
3163
- @if (primaryActionLabel) {
3164
- <button
3165
- class="lokotro-btn-primary"
3166
- (click)="onPrimaryClick()">
3167
- {{ primaryActionLabel }}
3168
- </button>
3169
- }
3170
- @if (secondaryActionLabel) {
3171
- <button
3172
- class="lokotro-btn-secondary"
3173
- (click)="onSecondaryClick()">
3174
- {{ secondaryActionLabel }}
3175
- </button>
3176
- }
3177
- </div>
3178
3172
  </div>
3179
- `, 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"] }); }
3173
+ `, isInline: true, styles: [".lokotro-result{display:flex;flex-direction:column;align-items:stretch;max-width:420px;margin:0 auto;padding:24px 16px;gap:20px}.lokotro-result-card{display:flex;flex-direction:column;align-items:center;text-align:center;padding:32px 24px;background:var(--lokotro-card, #FFFFFF);border:1px solid var(--lokotro-border, #E2E8F0);border-radius:20px;box-shadow:0 1px 3px var(--lokotro-shadow-light, rgba(15, 23, 42, .04)),0 8px 24px var(--lokotro-shadow-light, rgba(15, 23, 42, .04))}.lokotro-status-badge{width:72px;height:72px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:24px;animation:lokotro-pop .5s cubic-bezier(.34,1.56,.64,1)}.lokotro-status-badge svg{width:36px;height:36px;color:#fff}.lokotro-status-success{background:var(--lokotro-success, #16A34A);box-shadow:0 0 0 8px #16a34a1f,0 8px 20px #16a34a40}.lokotro-status-error{background:var(--lokotro-error, #DC2626);box-shadow:0 0 0 8px #dc26261f,0 8px 20px #dc262640}.lokotro-status-warning{background:var(--lokotro-warning, #D97706);box-shadow:0 0 0 8px #d977061f,0 8px 20px #d9770640}.lokotro-status-info{background:var(--lokotro-info, #2563EB);box-shadow:0 0 0 8px #2563eb1f,0 8px 20px #2563eb40}@keyframes lokotro-pop{0%{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}.lokotro-amount-focal{display:flex;flex-direction:column;align-items:center;gap:4px;margin-bottom:16px}.lokotro-amount-currency{font-size:12px;font-weight:600;letter-spacing:.08em;color:var(--lokotro-text-secondary, #64748B)}.lokotro-amount-value{font-size:40px;font-weight:700;line-height:1;color:var(--lokotro-text-primary, #0F172A);font-variant-numeric:tabular-nums}.lokotro-result-title{font-size:20px;font-weight:600;margin:0 0 6px;color:var(--lokotro-text-primary, #0F172A)}.lokotro-result-message{font-size:14px;color:var(--lokotro-text-secondary, #64748B);margin:0;line-height:1.5;max-width:320px}.lokotro-result-details{width:100%;background:var(--lokotro-surface, #F7F8FA);border:1px solid var(--lokotro-border, #E2E8F0);border-radius:12px;padding:12px 16px;margin-top:24px}.lokotro-detail-row{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:6px 0}.lokotro-detail-row:not(:last-child){border-bottom:1px solid var(--lokotro-divider, #F1F5F9)}.lokotro-detail-label{font-size:13px;color:var(--lokotro-text-secondary, #64748B)}.lokotro-detail-value{font-size:13px;font-weight:600;color:var(--lokotro-text-primary, #0F172A);text-align:right;overflow:hidden;text-overflow:ellipsis;max-width:200px;white-space:nowrap}.lokotro-mono{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,monospace;font-size:12px}.lokotro-result-actions{display:flex;flex-direction:column;gap:10px;width:100%}.lokotro-btn-primary,.lokotro-btn-secondary{width:100%;padding:14px 24px;border-radius:12px;font-size:15px;font-weight:600;cursor:pointer;transition:all .18s ease;font-family:inherit}.lokotro-btn-primary{background:var(--lokotro-primary, #0F172A);color:#fff;border:none}.lokotro-btn-primary:hover{transform:translateY(-1px);box-shadow:0 6px 16px var(--lokotro-shadow-medium, rgba(15, 23, 42, .12))}.lokotro-btn-primary:active{transform:translateY(0)}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #0F172A);border:1.5px solid var(--lokotro-border, #E2E8F0)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #64748B);background:var(--lokotro-surface, #F7F8FA)}.lokotro-countdown-indicator{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;background:var(--lokotro-surface, #F7F8FA);border:1px solid var(--lokotro-border, #E2E8F0);border-radius:999px;cursor:pointer;margin-top:20px;transition:all .18s ease;color:var(--lokotro-text-secondary, #64748B);font-family:inherit}.lokotro-countdown-indicator:hover{background:var(--lokotro-card, #FFFFFF);border-color:var(--lokotro-text-tertiary, #64748B)}.lokotro-countdown-circle{width:14px;height:14px;transform:rotate(-90deg)}.lokotro-countdown-bg{fill:none;stroke:var(--lokotro-border, #E2E8F0);stroke-width:4}.lokotro-countdown-fill{fill:none;stroke-width:4;stroke-linecap:round;transition:stroke-dasharray .3s ease}.lokotro-countdown-success{stroke:var(--lokotro-success, #16A34A)}.lokotro-countdown-error{stroke:var(--lokotro-error, #DC2626)}.lokotro-countdown-warning{stroke:var(--lokotro-warning, #D97706)}.lokotro-countdown-info{stroke:var(--lokotro-info, #2563EB)}.lokotro-countdown-text{font-size:12px;font-weight:500}.lokotro-countdown-cancel{color:inherit;opacity:.7}\n"] }); }
3180
3174
  }
3181
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroResultComponent, decorators: [{
3175
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroResultComponent, decorators: [{
3182
3176
  type: Component,
3183
3177
  args: [{ selector: 'lokotro-result', standalone: true, imports: [], template: `
3184
3178
  <div class="lokotro-result" [class]="'lokotro-result-' + type">
3185
- <!-- Animated Icon -->
3186
- <div class="lokotro-result-icon" [class]="'lokotro-icon-' + type">
3187
- <!-- Success Icon -->
3188
- @if (type === 'success') {
3189
- <svg class="lokotro-checkmark" viewBox="0 0 52 52">
3190
- <circle class="lokotro-checkmark-circle" cx="26" cy="26" r="25" fill="none"/>
3191
- <path class="lokotro-checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
3192
- </svg>
3193
- }
3194
-
3195
- <!-- Error Icon -->
3196
- @if (type === 'error') {
3197
- <svg class="lokotro-error-icon" viewBox="0 0 52 52">
3198
- <circle class="lokotro-error-circle" cx="26" cy="26" r="25" fill="none"/>
3199
- <path class="lokotro-error-x" fill="none" d="M16 16l20 20M36 16l-20 20"/>
3200
- </svg>
3201
- }
3202
-
3203
- <!-- Warning Icon -->
3204
- @if (type === 'warning') {
3205
- <svg class="lokotro-warning-icon" viewBox="0 0 52 52">
3206
- <circle class="lokotro-warning-circle" cx="26" cy="26" r="25" fill="none"/>
3207
- <path class="lokotro-warning-exclaim" fill="none" d="M26 15v15M26 37v1"/>
3208
- </svg>
3179
+ <div class="lokotro-result-card">
3180
+ <!-- Status Badge (filled circle with icon) -->
3181
+ <div class="lokotro-status-badge" [class]="'lokotro-status-' + type">
3182
+ @if (type === 'success') {
3183
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3184
+ <polyline points="5 12 10 17 19 8"/>
3185
+ </svg>
3186
+ }
3187
+ @if (type === 'error') {
3188
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3189
+ <line x1="18" y1="6" x2="6" y2="18"/>
3190
+ <line x1="6" y1="6" x2="18" y2="18"/>
3191
+ </svg>
3192
+ }
3193
+ @if (type === 'warning') {
3194
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3195
+ <line x1="12" y1="8" x2="12" y2="13"/>
3196
+ <line x1="12" y1="17" x2="12.01" y2="17"/>
3197
+ </svg>
3198
+ }
3199
+ @if (type === 'info') {
3200
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
3201
+ <line x1="12" y1="11" x2="12" y2="16"/>
3202
+ <line x1="12" y1="7" x2="12.01" y2="7"/>
3203
+ </svg>
3204
+ }
3205
+ </div>
3206
+
3207
+ <!-- Amount focal (success only, when amount provided) -->
3208
+ @if (type === 'success' && amount !== undefined && amount !== null) {
3209
+ <div class="lokotro-amount-focal">
3210
+ <span class="lokotro-amount-currency">{{ (currency || '').toUpperCase() }}</span>
3211
+ <span class="lokotro-amount-value">{{ formatNumber(amount) }}</span>
3212
+ </div>
3209
3213
  }
3210
-
3211
- <!-- Info Icon -->
3212
- @if (type === 'info') {
3213
- <svg class="lokotro-info-icon" viewBox="0 0 52 52">
3214
- <circle class="lokotro-info-circle" cx="26" cy="26" r="25" fill="none"/>
3215
- <path class="lokotro-info-i" fill="none" d="M26 22v15M26 15v1"/>
3216
- </svg>
3214
+
3215
+ <!-- Title -->
3216
+ <h2 class="lokotro-result-title">{{ title }}</h2>
3217
+
3218
+ <!-- Message -->
3219
+ @if (message) {
3220
+ <p class="lokotro-result-message">{{ message }}</p>
3217
3221
  }
3218
- </div>
3219
-
3220
- <!-- Title -->
3221
- <h2 class="lokotro-result-title">{{ title }}</h2>
3222
-
3223
- <!-- Message -->
3224
- @if (message) {
3225
- <p class="lokotro-result-message">{{ message }}</p>
3226
- }
3227
-
3228
- <!-- Countdown Indicator -->
3229
- @if (autoRedirectSeconds > 0 && countdownSeconds > 0 && !isCountdownCancelled) {
3230
- <div class="lokotro-countdown-indicator"
3231
- (click)="cancelCountdown()">
3232
- <div class="lokotro-countdown-progress">
3222
+
3223
+ <!-- Countdown Indicator -->
3224
+ @if (autoRedirectSeconds > 0 && countdownSeconds > 0 && !isCountdownCancelled) {
3225
+ <button type="button" class="lokotro-countdown-indicator" (click)="cancelCountdown()">
3233
3226
  <svg viewBox="0 0 36 36" class="lokotro-countdown-circle">
3234
3227
  <path class="lokotro-countdown-bg"
3235
- d="M18 2.0845
3236
- a 15.9155 15.9155 0 0 1 0 31.831
3237
- a 15.9155 15.9155 0 0 1 0 -31.831"
3238
- />
3228
+ d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
3239
3229
  <path class="lokotro-countdown-fill" [class]="'lokotro-countdown-' + type"
3240
3230
  [attr.stroke-dasharray]="getCountdownProgress() + ', 100'"
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
- />
3231
+ d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
3245
3232
  </svg>
3246
- </div>
3247
- <span class="lokotro-countdown-text" [class]="'lokotro-text-' + type">
3248
- {{ localization.translate('redirectingIn', { seconds: countdownSeconds }) }}
3249
- </span>
3250
- <svg class="lokotro-countdown-cancel" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3251
- <path d="M18 6L6 18M6 6l12 12"/>
3252
- </svg>
3253
- </div>
3254
- }
3255
-
3256
- <!-- Transaction Details -->
3257
- @if (type === 'success' && (amount || transactionId)) {
3258
- <div class="lokotro-result-details">
3259
- @if (amount) {
3260
- <div class="lokotro-detail-row">
3261
- <span class="lokotro-detail-label">{{ localization.translate('amount') }}</span>
3262
- <span class="lokotro-detail-value">{{ formatAmount(amount, currency) }}</span>
3263
- </div>
3264
- }
3265
- @if (transactionId) {
3233
+ <span class="lokotro-countdown-text">
3234
+ {{ localization.translate('redirectingIn', { seconds: countdownSeconds }) }}
3235
+ </span>
3236
+ <svg class="lokotro-countdown-cancel" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
3237
+ <path d="M18 6L6 18M6 6l12 12"/>
3238
+ </svg>
3239
+ </button>
3240
+ }
3241
+
3242
+ <!-- Transaction Details -->
3243
+ @if (type === 'success' && transactionId) {
3244
+ <div class="lokotro-result-details">
3266
3245
  <div class="lokotro-detail-row">
3267
3246
  <span class="lokotro-detail-label">Transaction ID</span>
3268
3247
  <span class="lokotro-detail-value lokotro-mono">{{ transactionId }}</span>
3269
3248
  </div>
3249
+ </div>
3250
+ }
3251
+ </div>
3252
+
3253
+ <!-- Actions -->
3254
+ @if (primaryActionLabel || secondaryActionLabel) {
3255
+ <div class="lokotro-result-actions">
3256
+ @if (primaryActionLabel) {
3257
+ <button type="button" class="lokotro-btn-primary" (click)="onPrimaryClick()">
3258
+ {{ primaryActionLabel }}
3259
+ </button>
3260
+ }
3261
+ @if (secondaryActionLabel) {
3262
+ <button type="button" class="lokotro-btn-secondary" (click)="onSecondaryClick()">
3263
+ {{ secondaryActionLabel }}
3264
+ </button>
3270
3265
  }
3271
3266
  </div>
3272
3267
  }
3273
-
3274
- <!-- Actions -->
3275
- <div class="lokotro-result-actions">
3276
- @if (primaryActionLabel) {
3277
- <button
3278
- class="lokotro-btn-primary"
3279
- (click)="onPrimaryClick()">
3280
- {{ primaryActionLabel }}
3281
- </button>
3282
- }
3283
- @if (secondaryActionLabel) {
3284
- <button
3285
- class="lokotro-btn-secondary"
3286
- (click)="onSecondaryClick()">
3287
- {{ secondaryActionLabel }}
3288
- </button>
3289
- }
3290
- </div>
3291
3268
  </div>
3292
- `, 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"] }]
3269
+ `, styles: [".lokotro-result{display:flex;flex-direction:column;align-items:stretch;max-width:420px;margin:0 auto;padding:24px 16px;gap:20px}.lokotro-result-card{display:flex;flex-direction:column;align-items:center;text-align:center;padding:32px 24px;background:var(--lokotro-card, #FFFFFF);border:1px solid var(--lokotro-border, #E2E8F0);border-radius:20px;box-shadow:0 1px 3px var(--lokotro-shadow-light, rgba(15, 23, 42, .04)),0 8px 24px var(--lokotro-shadow-light, rgba(15, 23, 42, .04))}.lokotro-status-badge{width:72px;height:72px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:24px;animation:lokotro-pop .5s cubic-bezier(.34,1.56,.64,1)}.lokotro-status-badge svg{width:36px;height:36px;color:#fff}.lokotro-status-success{background:var(--lokotro-success, #16A34A);box-shadow:0 0 0 8px #16a34a1f,0 8px 20px #16a34a40}.lokotro-status-error{background:var(--lokotro-error, #DC2626);box-shadow:0 0 0 8px #dc26261f,0 8px 20px #dc262640}.lokotro-status-warning{background:var(--lokotro-warning, #D97706);box-shadow:0 0 0 8px #d977061f,0 8px 20px #d9770640}.lokotro-status-info{background:var(--lokotro-info, #2563EB);box-shadow:0 0 0 8px #2563eb1f,0 8px 20px #2563eb40}@keyframes lokotro-pop{0%{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}.lokotro-amount-focal{display:flex;flex-direction:column;align-items:center;gap:4px;margin-bottom:16px}.lokotro-amount-currency{font-size:12px;font-weight:600;letter-spacing:.08em;color:var(--lokotro-text-secondary, #64748B)}.lokotro-amount-value{font-size:40px;font-weight:700;line-height:1;color:var(--lokotro-text-primary, #0F172A);font-variant-numeric:tabular-nums}.lokotro-result-title{font-size:20px;font-weight:600;margin:0 0 6px;color:var(--lokotro-text-primary, #0F172A)}.lokotro-result-message{font-size:14px;color:var(--lokotro-text-secondary, #64748B);margin:0;line-height:1.5;max-width:320px}.lokotro-result-details{width:100%;background:var(--lokotro-surface, #F7F8FA);border:1px solid var(--lokotro-border, #E2E8F0);border-radius:12px;padding:12px 16px;margin-top:24px}.lokotro-detail-row{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:6px 0}.lokotro-detail-row:not(:last-child){border-bottom:1px solid var(--lokotro-divider, #F1F5F9)}.lokotro-detail-label{font-size:13px;color:var(--lokotro-text-secondary, #64748B)}.lokotro-detail-value{font-size:13px;font-weight:600;color:var(--lokotro-text-primary, #0F172A);text-align:right;overflow:hidden;text-overflow:ellipsis;max-width:200px;white-space:nowrap}.lokotro-mono{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,monospace;font-size:12px}.lokotro-result-actions{display:flex;flex-direction:column;gap:10px;width:100%}.lokotro-btn-primary,.lokotro-btn-secondary{width:100%;padding:14px 24px;border-radius:12px;font-size:15px;font-weight:600;cursor:pointer;transition:all .18s ease;font-family:inherit}.lokotro-btn-primary{background:var(--lokotro-primary, #0F172A);color:#fff;border:none}.lokotro-btn-primary:hover{transform:translateY(-1px);box-shadow:0 6px 16px var(--lokotro-shadow-medium, rgba(15, 23, 42, .12))}.lokotro-btn-primary:active{transform:translateY(0)}.lokotro-btn-secondary{background:transparent;color:var(--lokotro-text-primary, #0F172A);border:1.5px solid var(--lokotro-border, #E2E8F0)}.lokotro-btn-secondary:hover{border-color:var(--lokotro-text-secondary, #64748B);background:var(--lokotro-surface, #F7F8FA)}.lokotro-countdown-indicator{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;background:var(--lokotro-surface, #F7F8FA);border:1px solid var(--lokotro-border, #E2E8F0);border-radius:999px;cursor:pointer;margin-top:20px;transition:all .18s ease;color:var(--lokotro-text-secondary, #64748B);font-family:inherit}.lokotro-countdown-indicator:hover{background:var(--lokotro-card, #FFFFFF);border-color:var(--lokotro-text-tertiary, #64748B)}.lokotro-countdown-circle{width:14px;height:14px;transform:rotate(-90deg)}.lokotro-countdown-bg{fill:none;stroke:var(--lokotro-border, #E2E8F0);stroke-width:4}.lokotro-countdown-fill{fill:none;stroke-width:4;stroke-linecap:round;transition:stroke-dasharray .3s ease}.lokotro-countdown-success{stroke:var(--lokotro-success, #16A34A)}.lokotro-countdown-error{stroke:var(--lokotro-error, #DC2626)}.lokotro-countdown-warning{stroke:var(--lokotro-warning, #D97706)}.lokotro-countdown-info{stroke:var(--lokotro-info, #2563EB)}.lokotro-countdown-text{font-size:12px;font-weight:500}.lokotro-countdown-cancel{color:inherit;opacity:.7}\n"] }]
3293
3270
  }], ctorParameters: () => [{ type: LokotroLocalizationService }], propDecorators: { type: [{
3294
3271
  type: Input
3295
3272
  }], title: [{
@@ -3320,8 +3297,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
3320
3297
  * Lokotro Pay - Loading Component
3321
3298
  */
3322
3299
  class LokotroLoadingComponent {
3323
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3324
- 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: `
3300
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroLoadingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3301
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LokotroLoadingComponent, isStandalone: true, selector: "lokotro-loading", inputs: { message: "message" }, ngImport: i0, template: `
3325
3302
  <div class="lokotro-loading">
3326
3303
  <div class="lokotro-loading-spinner">
3327
3304
  <div class="lokotro-pulse-ring"></div>
@@ -3334,7 +3311,7 @@ class LokotroLoadingComponent {
3334
3311
  </div>
3335
3312
  `, 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"] }); }
3336
3313
  }
3337
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroLoadingComponent, decorators: [{
3314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroLoadingComponent, decorators: [{
3338
3315
  type: Component,
3339
3316
  args: [{ selector: 'lokotro-loading', standalone: true, imports: [], template: `
3340
3317
  <div class="lokotro-loading">
@@ -3364,16 +3341,6 @@ const initialState = {
3364
3341
  currentScreen: LokotroPayScreenNavigation.LoadingScreen
3365
3342
  };
3366
3343
  class LokotroPaymentService {
3367
- /** Allowed domains for redirect URLs to prevent open redirect attacks */
3368
- static { this.ALLOWED_REDIRECT_DOMAINS = [
3369
- 'lokotro.com',
3370
- 'app.lokotro.com',
3371
- 'api.gtwy.lokotro.com',
3372
- 'dev.app.api.gtwy.lokotro.com',
3373
- 'app.api.gtwy.lokotro.com',
3374
- 'mastercard.com',
3375
- 'ap-gateway.mastercard.com',
3376
- ]; }
3377
3344
  static { this.MOBILE_MONEY_POLL_INTERVAL = 5000; }
3378
3345
  static { this.MOBILE_MONEY_MAX_ATTEMPTS = 60; }
3379
3346
  constructor(httpClient) {
@@ -3381,7 +3348,6 @@ class LokotroPaymentService {
3381
3348
  this.stateSubject = new BehaviorSubject(initialState);
3382
3349
  this.state$ = this.stateSubject.asObservable();
3383
3350
  this.mobileMoneyPollingAttempts = 0;
3384
- this.pollingStop$ = new Subject();
3385
3351
  }
3386
3352
  /**
3387
3353
  * Get current state
@@ -3426,7 +3392,9 @@ class LokotroPaymentService {
3426
3392
  this.currentPaymentBody = paymentBody;
3427
3393
  this.updateState({ isLoading: true });
3428
3394
  const requestData = this.convertPaymentBodyToRequest(paymentBody);
3395
+ console.log('[Lokotro Payment] Creating payment with body:', requestData);
3429
3396
  return this.httpClient.post(LokotroPayEnv.endpoints.collect, requestData).pipe(switchMap(response => {
3397
+ console.log('[Lokotro Payment] Create payment response:', response);
3430
3398
  if (!response.isSuccess || !response.data) {
3431
3399
  this.updateState({
3432
3400
  isLoading: false,
@@ -3484,10 +3452,6 @@ class LokotroPaymentService {
3484
3452
  return;
3485
3453
  }
3486
3454
  if (submitResponse.redirectUrl) {
3487
- if (!this.isValidRedirectUrl(submitResponse.redirectUrl)) {
3488
- this.handlePaymentFailure('Invalid redirect URL received from server');
3489
- return;
3490
- }
3491
3455
  this.updateState({
3492
3456
  isLoading: false,
3493
3457
  transactionId: submitResponse.transactionId || this.resolvePaymentId(request)
@@ -3598,37 +3562,34 @@ class LokotroPaymentService {
3598
3562
  startMobileMoneyPolling(transactionId) {
3599
3563
  this.stopMobileMoneyPolling();
3600
3564
  this.mobileMoneyPollingAttempts = 0;
3601
- this.pollingStop$ = new Subject();
3565
+ console.log('[Lokotro Payment] Starting mobile money status polling for:', transactionId);
3602
3566
  this.mobileMoneyPollingTimer = setInterval(() => {
3603
3567
  this.mobileMoneyPollingAttempts++;
3604
3568
  if (this.mobileMoneyPollingAttempts > LokotroPaymentService.MOBILE_MONEY_MAX_ATTEMPTS) {
3569
+ console.warn('[Lokotro Payment] Mobile money polling max attempts reached');
3605
3570
  this.stopMobileMoneyPolling();
3606
3571
  this.handlePaymentFailure('Payment took too long. Please try again or contact support.');
3607
3572
  return;
3608
3573
  }
3609
3574
  const endpoint = `${LokotroPayEnv.endpoints.mobileMoneyStatus}/${transactionId}`;
3610
- this.pollingSubscription = this.httpClient.get(endpoint)
3611
- .pipe(takeUntil(this.pollingStop$))
3612
- .subscribe({
3575
+ this.httpClient.get(endpoint).subscribe({
3613
3576
  next: (response) => {
3614
3577
  if (!response.isSuccess || !response.data)
3615
3578
  return;
3616
3579
  const data = this.unwrapPayload(response.data);
3617
3580
  const status = this.getString(data['status']).toLowerCase();
3618
- const parsedStatus = LokotroPaymentStatusInfo.fromString(status);
3619
- if (LokotroPaymentStatusInfo.isSuccess(parsedStatus)) {
3581
+ console.log(`[Lokotro Payment] Mobile money poll #${this.mobileMoneyPollingAttempts}: status=${status}`);
3582
+ if (status === 'completed' || status === 'success' || status === 'approved') {
3620
3583
  this.stopMobileMoneyPolling();
3621
3584
  this.handlePaymentSuccess(data);
3622
3585
  }
3623
- else if (LokotroPaymentStatusInfo.isFailure(parsedStatus)) {
3586
+ else if (status === 'failed' || status === 'error' || status === 'cancelled' || status === 'declined') {
3624
3587
  this.stopMobileMoneyPolling();
3625
3588
  this.handlePaymentFailure(this.getString(data['message']) || 'Payment failed or was declined');
3626
3589
  }
3627
3590
  },
3628
3591
  error: (err) => {
3629
- if (LokotroPayEnv.debugMode) {
3630
- console.error('[Lokotro Payment] Mobile money polling error:', err?.message || err);
3631
- }
3592
+ console.error('[Lokotro Payment] Mobile money polling error:', err);
3632
3593
  }
3633
3594
  });
3634
3595
  }, LokotroPaymentService.MOBILE_MONEY_POLL_INTERVAL);
@@ -3637,13 +3598,11 @@ class LokotroPaymentService {
3637
3598
  * Stop mobile money status polling
3638
3599
  */
3639
3600
  stopMobileMoneyPolling() {
3640
- this.pollingStop$.next();
3641
- this.pollingSubscription?.unsubscribe();
3642
- this.pollingSubscription = undefined;
3643
3601
  if (this.mobileMoneyPollingTimer) {
3644
3602
  clearInterval(this.mobileMoneyPollingTimer);
3645
3603
  this.mobileMoneyPollingTimer = undefined;
3646
3604
  this.mobileMoneyPollingAttempts = 0;
3605
+ console.log('[Lokotro Payment] Mobile money polling stopped');
3647
3606
  }
3648
3607
  }
3649
3608
  /**
@@ -3719,10 +3678,6 @@ class LokotroPaymentService {
3719
3678
  this.handlePaymentFailure('Hosted session created but no checkout URL was provided');
3720
3679
  return of(void 0);
3721
3680
  }
3722
- if (!this.isValidRedirectUrl(redirectUrl)) {
3723
- this.handlePaymentFailure('Invalid checkout redirect URL received from server');
3724
- return of(void 0);
3725
- }
3726
3681
  this.updateState({
3727
3682
  isLoading: false,
3728
3683
  transactionId: paymentId
@@ -3775,10 +3730,6 @@ class LokotroPaymentService {
3775
3730
  const hostedCheckoutUrl = this.getString(transactionData['mastercardUrl']) ||
3776
3731
  this.getString(transactionData['redirect_url']);
3777
3732
  if (hostedCheckoutUrl) {
3778
- if (!this.isValidRedirectUrl(hostedCheckoutUrl)) {
3779
- this.handlePaymentFailure('Invalid hosted checkout URL received from server');
3780
- return of(void 0);
3781
- }
3782
3733
  this.updateState({ isLoading: false });
3783
3734
  window.location.href = hostedCheckoutUrl;
3784
3735
  return of(void 0);
@@ -3885,6 +3836,12 @@ class LokotroPaymentService {
3885
3836
  if (body.transactionalCurrency) {
3886
3837
  request['transactional_currency'] = body.transactionalCurrency.toLowerCase();
3887
3838
  }
3839
+ if (body.nativePayToken) {
3840
+ request['native_pay_token'] = body.nativePayToken;
3841
+ }
3842
+ if (body.nativePayTokenType) {
3843
+ request['native_pay_token_type'] = body.nativePayTokenType;
3844
+ }
3888
3845
  if (body.metadata) {
3889
3846
  request['metadata'] = body.metadata;
3890
3847
  }
@@ -4152,36 +4109,10 @@ class LokotroPaymentService {
4152
4109
  .map(item => this.asRecord(item))
4153
4110
  .filter((item) => Boolean(item));
4154
4111
  }
4155
- /**
4156
- * Validate that a redirect URL points to an allowed domain.
4157
- * Prevents open redirect attacks if the API response is compromised.
4158
- */
4159
- isValidRedirectUrl(url) {
4160
- try {
4161
- const parsed = new URL(url);
4162
- if (parsed.protocol !== 'https:') {
4163
- return false;
4164
- }
4165
- return LokotroPaymentService.ALLOWED_REDIRECT_DOMAINS.some(domain => parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`));
4166
- }
4167
- catch {
4168
- return false;
4169
- }
4170
- }
4171
- /**
4172
- * Full cleanup - call when the host component is destroyed.
4173
- * Addresses singleton state collision by ensuring polling and state are reset.
4174
- */
4175
- destroy() {
4176
- this.stopMobileMoneyPolling();
4177
- this.pollingStop$.complete();
4178
- this.currentPaymentBody = undefined;
4179
- this.stateSubject.next(initialState);
4180
- }
4181
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4182
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
4112
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4113
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
4183
4114
  }
4184
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, decorators: [{
4115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentService, decorators: [{
4185
4116
  type: Injectable,
4186
4117
  args: [{
4187
4118
  providedIn: 'root'
@@ -4195,39 +4126,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4195
4126
  class LokotroPayCheckoutComponent {
4196
4127
  // Retry attempts tracking
4197
4128
  static { this.MAX_RETRY_ATTEMPTS = 3; }
4198
- constructor(paymentService, localization) {
4129
+ constructor(paymentService, localization, elementRef) {
4199
4130
  this.paymentService = paymentService;
4200
4131
  this.localization = localization;
4132
+ this.elementRef = elementRef;
4201
4133
  this.enableHapticFeedback = true;
4202
4134
  this.response = new EventEmitter();
4203
4135
  this.error = new EventEmitter();
4204
4136
  this.closed = new EventEmitter();
4205
- this.isDarkTheme = true;
4137
+ /** Default to light theme — payment widgets should look like clean white cards. */
4138
+ this.isDarkTheme = false;
4206
4139
  this.retryAttempts = 0;
4207
- this.destroy$ = new Subject();
4208
4140
  }
4209
4141
  /** Check if user has retries left */
4210
4142
  get hasRetriesLeft() {
4211
4143
  return this.retryAttempts < LokotroPayCheckoutComponent.MAX_RETRY_ATTEMPTS;
4212
4144
  }
4213
4145
  ngOnInit() {
4146
+ this.applyThemeConfig();
4214
4147
  this.initializeEnvironment();
4215
4148
  this.initializeLocalization();
4216
4149
  this.subscribeToState();
4217
4150
  this.initializePayment();
4218
4151
  }
4152
+ ngOnChanges(changes) {
4153
+ if (changes['themeConfig']) {
4154
+ this.applyThemeConfig();
4155
+ }
4156
+ }
4219
4157
  ngOnDestroy() {
4220
- this.destroy$.next();
4221
- this.destroy$.complete();
4222
4158
  this.stateSubscription?.unsubscribe();
4223
- this.paymentService.destroy();
4159
+ this.paymentService.resetState();
4160
+ }
4161
+ /**
4162
+ * Apply the merchant-provided theme as CSS custom properties on the
4163
+ * checkout host element. Only sets variables for fields the merchant
4164
+ * provided — undefined fields fall back to the package defaults defined
4165
+ * in lokotro-pay.scss.
4166
+ */
4167
+ applyThemeConfig() {
4168
+ const host = this.elementRef.nativeElement;
4169
+ if (!this.themeConfig)
4170
+ return;
4171
+ const t = this.themeConfig;
4172
+ const tertiary = t.tertiaryColor ?? t.accentColor;
4173
+ const map = [
4174
+ ['--lokotro-primary', t.primaryColor],
4175
+ ['--lokotro-secondary', t.secondaryColor],
4176
+ ['--lokotro-tertiary', tertiary],
4177
+ ['--lokotro-accent', tertiary],
4178
+ ['--lokotro-background', t.backgroundColor],
4179
+ ['--lokotro-surface', t.surfaceColor],
4180
+ ['--lokotro-card', t.surfaceColor],
4181
+ ['--lokotro-text-primary', t.textColor],
4182
+ ['--lokotro-text-secondary', t.secondaryTextColor],
4183
+ ['--lokotro-border', t.borderColor],
4184
+ ['--lokotro-error', t.errorColor],
4185
+ ['--lokotro-success', t.successColor],
4186
+ ['--lokotro-warning', t.warningColor],
4187
+ ['--lokotro-font-family', t.fontFamily],
4188
+ ['--lokotro-radius-md', t.borderRadius]
4189
+ ];
4190
+ for (const [prop, value] of map) {
4191
+ if (value)
4192
+ host.style.setProperty(prop, value);
4193
+ }
4224
4194
  }
4225
4195
  initializeEnvironment() {
4196
+ console.log('[Lokotro Checkout] initializeEnvironment - configs:', {
4197
+ token: this.configs.token?.substring(0, 20) + '...',
4198
+ isProduction: this.configs.isProduction,
4199
+ customApiUrl: this.configs.customApiUrl
4200
+ });
4226
4201
  LokotroPayEnv.initialize(this.configs.isProduction ?? false, this.configs.customApiUrl);
4202
+ console.log('[Lokotro Checkout] Calling setAppKey with token');
4227
4203
  this.paymentService.setAppKey(this.configs.token);
4228
4204
  if (this.configs.acceptLanguage) {
4229
4205
  this.paymentService.setAcceptLanguage(this.configs.acceptLanguage);
4230
4206
  }
4207
+ console.log('[Lokotro Checkout] Environment initialized');
4231
4208
  }
4232
4209
  initializeLocalization() {
4233
4210
  if (this.language) {
@@ -4266,13 +4243,9 @@ class LokotroPayCheckoutComponent {
4266
4243
  });
4267
4244
  }
4268
4245
  initializePayment() {
4269
- this.paymentService.createPayment(this.paymentBody)
4270
- .pipe(takeUntil(this.destroy$))
4271
- .subscribe({
4246
+ this.paymentService.createPayment(this.paymentBody).subscribe({
4272
4247
  error: (err) => {
4273
- if (LokotroPayEnv.debugMode) {
4274
- console.error('[Lokotro Pay] Payment initialization error:', err?.message || err);
4275
- }
4248
+ console.error('[Lokotro Pay] Payment initialization error:', err);
4276
4249
  }
4277
4250
  });
4278
4251
  }
@@ -4338,13 +4311,9 @@ class LokotroPayCheckoutComponent {
4338
4311
  paymentMethod: this.state?.selectedPaymentMethod?.name || '',
4339
4312
  ...formData
4340
4313
  };
4341
- this.paymentService.submitPaymentDetails(submitRequest)
4342
- .pipe(takeUntil(this.destroy$))
4343
- .subscribe({
4314
+ this.paymentService.submitPaymentDetails(submitRequest).subscribe({
4344
4315
  error: (err) => {
4345
- if (LokotroPayEnv.debugMode) {
4346
- console.error('[Lokotro Pay] Form submission error:', err?.message || err);
4347
- }
4316
+ console.error('[Lokotro Pay] Form submission error:', err);
4348
4317
  }
4349
4318
  });
4350
4319
  }
@@ -4354,12 +4323,9 @@ class LokotroPayCheckoutComponent {
4354
4323
  this.paymentService.verifyOtp({
4355
4324
  paymentId: this.state.transactionId,
4356
4325
  otpCode: otp
4357
- }).pipe(takeUntil(this.destroy$))
4358
- .subscribe({
4326
+ }).subscribe({
4359
4327
  error: (err) => {
4360
- if (LokotroPayEnv.debugMode) {
4361
- console.error('[Lokotro Pay] OTP verification error:', err?.message || err);
4362
- }
4328
+ console.error('[Lokotro Pay] OTP verification error:', err);
4363
4329
  }
4364
4330
  });
4365
4331
  }
@@ -4368,12 +4334,9 @@ class LokotroPayCheckoutComponent {
4368
4334
  return;
4369
4335
  this.paymentService.resendOtp({
4370
4336
  paymentId: this.state.transactionId
4371
- }).pipe(takeUntil(this.destroy$))
4372
- .subscribe({
4337
+ }).subscribe({
4373
4338
  error: (err) => {
4374
- if (LokotroPayEnv.debugMode) {
4375
- console.error('[Lokotro Pay] OTP resend error:', err?.message || err);
4376
- }
4339
+ console.error('[Lokotro Pay] OTP resend error:', err);
4377
4340
  }
4378
4341
  });
4379
4342
  }
@@ -4455,9 +4418,10 @@ class LokotroPayCheckoutComponent {
4455
4418
  }
4456
4419
  this.closed.emit();
4457
4420
  }
4458
- 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 }); }
4459
- 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: `
4460
- <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle">
4421
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayCheckoutComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
4422
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", 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" }, usesOnChanges: true, ngImport: i0, template: `
4423
+ <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle"
4424
+ role="dialog" aria-modal="true">
4461
4425
  <!-- Header -->
4462
4426
  @if (title) {
4463
4427
  <div class="lokotro-checkout-header" [style.background-color]="titleBackgroundColor">
@@ -4578,9 +4542,9 @@ class LokotroPayCheckoutComponent {
4578
4542
  }
4579
4543
  </div>
4580
4544
  </div>
4581
- `, 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"] }] }); }
4545
+ `, isInline: true, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #FFFFFF);color:var(--lokotro-text-primary, #0F172A);font-family:var(--lokotro-font-family, -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-background, #FFFFFF);border-bottom:1px solid var(--lokotro-border, #E2E8F0)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #0F172A)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-secondary, #475569);border-radius:8px;transition:background-color .2s,color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-surface, #F7F8FA);color:var(--lokotro-text-primary, #0F172A)}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #0F172A;--lokotro-surface: #1E293B;--lokotro-card: #1E293B;--lokotro-text-primary: #F8FAFC;--lokotro-text-secondary: #CBD5E1;--lokotro-border: #334155}\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"] }] }); }
4582
4546
  }
4583
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
4547
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
4584
4548
  type: Component,
4585
4549
  args: [{ selector: 'lokotro-pay-checkout', standalone: true, imports: [
4586
4550
  LokotroPaymentMethodSelectionComponent,
@@ -4590,7 +4554,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4590
4554
  LokotroResultComponent,
4591
4555
  LokotroLoadingComponent
4592
4556
  ], template: `
4593
- <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle">
4557
+ <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle"
4558
+ role="dialog" aria-modal="true">
4594
4559
  <!-- Header -->
4595
4560
  @if (title) {
4596
4561
  <div class="lokotro-checkout-header" [style.background-color]="titleBackgroundColor">
@@ -4711,8 +4676,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4711
4676
  }
4712
4677
  </div>
4713
4678
  </div>
4714
- `, 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"] }]
4715
- }], ctorParameters: () => [{ type: LokotroPaymentService }, { type: LokotroLocalizationService }], propDecorators: { title: [{
4679
+ `, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #FFFFFF);color:var(--lokotro-text-primary, #0F172A);font-family:var(--lokotro-font-family, -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-background, #FFFFFF);border-bottom:1px solid var(--lokotro-border, #E2E8F0)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #0F172A)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-secondary, #475569);border-radius:8px;transition:background-color .2s,color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-surface, #F7F8FA);color:var(--lokotro-text-primary, #0F172A)}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #0F172A;--lokotro-surface: #1E293B;--lokotro-card: #1E293B;--lokotro-text-primary: #F8FAFC;--lokotro-text-secondary: #CBD5E1;--lokotro-border: #334155}\n"] }]
4680
+ }], ctorParameters: () => [{ type: LokotroPaymentService }, { type: LokotroLocalizationService }, { type: i0.ElementRef }], propDecorators: { title: [{
4716
4681
  type: Input
4717
4682
  }], titleStyle: [{
4718
4683
  type: Input
@@ -4800,8 +4765,8 @@ class LokotroPayModule {
4800
4765
  ],
4801
4766
  };
4802
4767
  }
4803
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
4804
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4768
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
4769
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4805
4770
  FormsModule,
4806
4771
  // Standalone components
4807
4772
  LokotroPayCheckoutComponent,
@@ -4817,14 +4782,14 @@ class LokotroPayModule {
4817
4782
  LokotroProcessingComponent,
4818
4783
  LokotroResultComponent,
4819
4784
  LokotroLoadingComponent] }); }
4820
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4785
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4821
4786
  FormsModule,
4822
4787
  // Standalone components
4823
4788
  LokotroPayCheckoutComponent,
4824
4789
  LokotroPaymentFormComponent,
4825
4790
  LokotroOtpVerificationComponent] }); }
4826
4791
  }
4827
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, decorators: [{
4792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, decorators: [{
4828
4793
  type: NgModule,
4829
4794
  args: [{
4830
4795
  imports: [
@@ -4853,110 +4818,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4853
4818
 
4854
4819
  /**
4855
4820
  * Lokotro Pay - Color Constants
4856
- * Angular version of the Flutter Lokotro Pay plugin colors
4857
- */
4858
- /**
4859
- * Color scheme for Lokotro Pay - matching Lokotroo App design
4821
+ *
4822
+ * Neutral white-background palette intended for payment surfaces.
4823
+ * Brand colors (primary/secondary/tertiary) default to slate so the
4824
+ * widget renders correctly with no themeConfig provided. Merchants
4825
+ * override at runtime via `<lokotro-pay-checkout [themeConfig]>`.
4860
4826
  */
4861
4827
  const LokotroPayColors = {
4862
- // Primary Brand Colors - Dark Green Theme
4863
- primary: '#5A5E39',
4864
- primaryDark: '#151E1A',
4865
- primaryLight: '#2A3832',
4866
- primaryVariant: '#121917',
4867
- // Secondary Colors - Olive Green
4868
- secondary: '#6E7346',
4869
- secondaryDark: '#5A5E39',
4870
- secondaryLight: '#868B57',
4871
- secondaryVariant: '#4D5131',
4872
- // Accent Colors - Turquoise for contrast
4873
- accent: '#3BFBDA',
4874
- accentDark: '#27D4B6',
4875
- accentLight: '#65FCE3',
4876
- // Success Colors - Enhanced Turquoise
4877
- success: '#3BFBDA',
4878
- successDark: '#27D4B6',
4879
- successLight: '#65FCE3',
4880
- // Error Colors - Terracotta
4881
- error: '#D97652',
4882
- errorDark: '#B15F41',
4883
- errorLight: '#E48F6F',
4884
- // Warning Colors - Terracotta
4885
- warning: '#D97652',
4886
- warningDark: '#B15F41',
4887
- warningLight: '#E48F6F',
4888
- // Info Colors
4889
- info: '#3B82F6',
4890
- infoDark: '#2563EB',
4891
- infoLight: '#60A5FA',
4892
- // Neutral Colors
4828
+ // Brand - neutral defaults (override via themeConfig)
4829
+ primary: '#0F172A',
4830
+ primaryDark: '#020617',
4831
+ primaryLight: '#1E293B',
4832
+ primaryVariant: '#0F172A',
4833
+ secondary: '#475569',
4834
+ secondaryDark: '#334155',
4835
+ secondaryLight: '#64748B',
4836
+ secondaryVariant: '#1E293B',
4837
+ // Tertiary (highlight / accent stop)
4838
+ tertiary: '#2563EB',
4839
+ tertiaryDark: '#1D4ED8',
4840
+ tertiaryLight: '#3B82F6',
4841
+ // Backwards-compat aliases
4842
+ accent: '#2563EB',
4843
+ accentDark: '#1D4ED8',
4844
+ accentLight: '#3B82F6',
4845
+ // Semantic - status colors (do not override)
4846
+ success: '#16A34A',
4847
+ successDark: '#15803D',
4848
+ successLight: '#22C55E',
4849
+ error: '#DC2626',
4850
+ errorDark: '#B91C1C',
4851
+ errorLight: '#EF4444',
4852
+ warning: '#D97706',
4853
+ warningDark: '#B45309',
4854
+ warningLight: '#F59E0B',
4855
+ info: '#2563EB',
4856
+ infoDark: '#1D4ED8',
4857
+ infoLight: '#3B82F6',
4858
+ // Neutral
4893
4859
  white: '#FFFFFF',
4894
4860
  black: '#000000',
4895
- grey: '#9E9E9E',
4896
- greyLight: '#E0E0E0',
4897
- greyDark: '#424242',
4898
- // Text Colors - Light Theme (Cream tints)
4899
- textPrimaryLight: '#1C2621',
4900
- textSecondaryLight: '#6E7346',
4901
- textTertiaryLight: '#8F9367',
4902
- textDisabledLight: '#BDBDBD',
4903
- // Text Colors - Dark Theme (Green tints)
4904
- textPrimaryDark: '#F2F0D5',
4905
- textSecondaryDark: '#D5D3B8',
4906
- textTertiaryDark: '#B1AF97',
4907
- textDisabledDark: '#666666',
4908
- // Background Colors - Light Theme (Cream)
4909
- backgroundLight: '#F2F0D5',
4910
- surfaceLight: '#F8F7E8',
4911
- cardLight: '#FFFFFA',
4912
- // Background Colors - Dark Theme (Green tints)
4913
- backgroundDark: '#1C2621',
4914
- surfaceDark: '#2A3832',
4915
- cardDark: '#3A4840',
4916
- // Glassmorphism Colors
4917
- glassLight: 'rgba(255, 255, 255, 0.1)',
4918
- glassDark: 'rgba(0, 0, 0, 0.1)',
4919
- glassBlur: 'rgba(0, 0, 0, 0.5)',
4920
- // Border Colors
4921
- borderLight: '#E5E2C0',
4922
- borderDark: '#3A473F',
4923
- // Divider Colors
4924
- dividerLight: '#ECEAD7',
4925
- dividerDark: '#2E3933',
4926
- // Shadow Colors
4927
- shadowLight: 'rgba(0, 0, 0, 0.1)',
4928
- shadowMedium: 'rgba(0, 0, 0, 0.2)',
4929
- shadowDark: 'rgba(0, 0, 0, 0.3)',
4930
- // Overlay Colors
4931
- overlayLight: 'rgba(255, 255, 255, 0.5)',
4932
- overlayDark: 'rgba(0, 0, 0, 0.5)',
4933
- // Transaction Type Colors
4934
- transactionSent: '#D97652',
4935
- transactionReceived: '#3BFBDA',
4936
- transactionPending: '#6E7346',
4937
- transactionFailed: '#3A4840'
4861
+ grey: '#94A3B8',
4862
+ greyLight: '#E2E8F0',
4863
+ greyDark: '#475569',
4864
+ // Light theme (default)
4865
+ textPrimaryLight: '#0F172A',
4866
+ textSecondaryLight: '#475569',
4867
+ textTertiaryLight: '#64748B',
4868
+ textDisabledLight: '#94A3B8',
4869
+ // Dark theme (opt-in)
4870
+ textPrimaryDark: '#F8FAFC',
4871
+ textSecondaryDark: '#CBD5E1',
4872
+ textTertiaryDark: '#94A3B8',
4873
+ textDisabledDark: '#64748B',
4874
+ backgroundLight: '#FFFFFF',
4875
+ surfaceLight: '#F7F8FA',
4876
+ cardLight: '#FFFFFF',
4877
+ backgroundDark: '#0F172A',
4878
+ surfaceDark: '#1E293B',
4879
+ cardDark: '#1E293B',
4880
+ // Glass overlays
4881
+ glassLight: 'rgba(255, 255, 255, 0.7)',
4882
+ glassDark: 'rgba(15, 23, 42, 0.04)',
4883
+ glassBlur: 'rgba(15, 23, 42, 0.5)',
4884
+ borderLight: '#E2E8F0',
4885
+ borderDark: '#334155',
4886
+ dividerLight: '#F1F5F9',
4887
+ dividerDark: '#1E293B',
4888
+ shadowLight: 'rgba(15, 23, 42, 0.04)',
4889
+ shadowMedium: 'rgba(15, 23, 42, 0.08)',
4890
+ shadowDark: 'rgba(15, 23, 42, 0.12)',
4891
+ overlayLight: 'rgba(255, 255, 255, 0.7)',
4892
+ overlayDark: 'rgba(15, 23, 42, 0.5)',
4893
+ // Transaction (semantic)
4894
+ transactionSent: '#DC2626',
4895
+ transactionReceived: '#16A34A',
4896
+ transactionPending: '#D97706',
4897
+ transactionFailed: '#475569'
4938
4898
  };
4939
4899
  /**
4940
- * CSS Gradient definitions
4941
- */
4942
- const LokotroPayGradients = {
4943
- primary: `linear-gradient(135deg, ${LokotroPayColors.primary}, ${LokotroPayColors.secondary})`,
4944
- secondary: `linear-gradient(135deg, ${LokotroPayColors.secondary}, ${LokotroPayColors.secondaryLight})`,
4945
- accent: `linear-gradient(135deg, ${LokotroPayColors.accent}, ${LokotroPayColors.accentLight})`,
4946
- success: `linear-gradient(135deg, ${LokotroPayColors.success}, ${LokotroPayColors.successDark})`,
4947
- error: `linear-gradient(135deg, ${LokotroPayColors.error}, ${LokotroPayColors.errorDark})`,
4948
- warning: `linear-gradient(135deg, ${LokotroPayColors.warning}, ${LokotroPayColors.warningDark})`,
4949
- info: `linear-gradient(135deg, ${LokotroPayColors.info}, ${LokotroPayColors.infoLight})`,
4950
- card: `linear-gradient(135deg, ${LokotroPayColors.backgroundDark}, ${LokotroPayColors.secondary})`
4951
- };
4952
- /**
4953
- * Default theme configuration
4900
+ * Default theme — clean white payment surface.
4901
+ *
4902
+ * The checkout component sets these as CSS custom properties on its
4903
+ * host element, so any merchant override scopes only to the widget.
4954
4904
  */
4955
4905
  const LokotroPayDefaultTheme = {
4956
4906
  light: {
4957
4907
  '--lokotro-primary': LokotroPayColors.primary,
4958
4908
  '--lokotro-secondary': LokotroPayColors.secondary,
4959
- '--lokotro-accent': LokotroPayColors.accent,
4909
+ '--lokotro-tertiary': LokotroPayColors.tertiary,
4910
+ '--lokotro-accent': LokotroPayColors.tertiary,
4960
4911
  '--lokotro-background': LokotroPayColors.backgroundLight,
4961
4912
  '--lokotro-surface': LokotroPayColors.surfaceLight,
4962
4913
  '--lokotro-card': LokotroPayColors.cardLight,
@@ -4972,7 +4923,8 @@ const LokotroPayDefaultTheme = {
4972
4923
  dark: {
4973
4924
  '--lokotro-primary': LokotroPayColors.primary,
4974
4925
  '--lokotro-secondary': LokotroPayColors.secondary,
4975
- '--lokotro-accent': LokotroPayColors.accent,
4926
+ '--lokotro-tertiary': LokotroPayColors.tertiary,
4927
+ '--lokotro-accent': LokotroPayColors.tertiary,
4976
4928
  '--lokotro-background': LokotroPayColors.backgroundDark,
4977
4929
  '--lokotro-surface': LokotroPayColors.surfaceDark,
4978
4930
  '--lokotro-card': LokotroPayColors.cardDark,
@@ -5285,8 +5237,8 @@ class LokotroPaymentStatusComponent {
5285
5237
  }
5286
5238
  this.onClose.emit();
5287
5239
  }
5288
- 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 }); }
5289
- 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: `
5240
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentStatusComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }, { token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Component }); }
5241
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", 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: `
5290
5242
  <div class="lokotro-payment-status-container">
5291
5243
  <!-- Header -->
5292
5244
  @if (showHeader) {
@@ -5403,7 +5355,7 @@ class LokotroPaymentStatusComponent {
5403
5355
  </div>
5404
5356
  `, 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"] }] }); }
5405
5357
  }
5406
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
5358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
5407
5359
  type: Component,
5408
5360
  args: [{ selector: 'lokotro-payment-status', standalone: true, imports: [LokotroLoadingComponent, LokotroResultComponent, LokotroProcessingComponent], template: `
5409
5361
  <div class="lokotro-payment-status-container">