@bloonio/lokotro-pay 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);
@@ -3882,9 +3833,6 @@ class LokotroPaymentService {
3882
3833
  if (body.mastercardPaymentMethod) {
3883
3834
  request['mastercard_payment_method'] = body.mastercardPaymentMethod;
3884
3835
  }
3885
- if (body.transactionalCurrency) {
3886
- request['transactional_currency'] = body.transactionalCurrency.toLowerCase();
3887
- }
3888
3836
  if (body.metadata) {
3889
3837
  request['metadata'] = body.metadata;
3890
3838
  }
@@ -4152,36 +4100,10 @@ class LokotroPaymentService {
4152
4100
  .map(item => this.asRecord(item))
4153
4101
  .filter((item) => Boolean(item));
4154
4102
  }
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' }); }
4103
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
4104
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
4183
4105
  }
4184
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentService, decorators: [{
4106
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentService, decorators: [{
4185
4107
  type: Injectable,
4186
4108
  args: [{
4187
4109
  providedIn: 'root'
@@ -4195,39 +4117,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4195
4117
  class LokotroPayCheckoutComponent {
4196
4118
  // Retry attempts tracking
4197
4119
  static { this.MAX_RETRY_ATTEMPTS = 3; }
4198
- constructor(paymentService, localization) {
4120
+ constructor(paymentService, localization, elementRef) {
4199
4121
  this.paymentService = paymentService;
4200
4122
  this.localization = localization;
4123
+ this.elementRef = elementRef;
4201
4124
  this.enableHapticFeedback = true;
4202
4125
  this.response = new EventEmitter();
4203
4126
  this.error = new EventEmitter();
4204
4127
  this.closed = new EventEmitter();
4205
- this.isDarkTheme = true;
4128
+ /** Default to light theme — payment widgets should look like clean white cards. */
4129
+ this.isDarkTheme = false;
4206
4130
  this.retryAttempts = 0;
4207
- this.destroy$ = new Subject();
4208
4131
  }
4209
4132
  /** Check if user has retries left */
4210
4133
  get hasRetriesLeft() {
4211
4134
  return this.retryAttempts < LokotroPayCheckoutComponent.MAX_RETRY_ATTEMPTS;
4212
4135
  }
4213
4136
  ngOnInit() {
4137
+ this.applyThemeConfig();
4214
4138
  this.initializeEnvironment();
4215
4139
  this.initializeLocalization();
4216
4140
  this.subscribeToState();
4217
4141
  this.initializePayment();
4218
4142
  }
4143
+ ngOnChanges(changes) {
4144
+ if (changes['themeConfig']) {
4145
+ this.applyThemeConfig();
4146
+ }
4147
+ }
4219
4148
  ngOnDestroy() {
4220
- this.destroy$.next();
4221
- this.destroy$.complete();
4222
4149
  this.stateSubscription?.unsubscribe();
4223
- this.paymentService.destroy();
4150
+ this.paymentService.resetState();
4151
+ }
4152
+ /**
4153
+ * Apply the merchant-provided theme as CSS custom properties on the
4154
+ * checkout host element. Only sets variables for fields the merchant
4155
+ * provided — undefined fields fall back to the package defaults defined
4156
+ * in lokotro-pay.scss.
4157
+ */
4158
+ applyThemeConfig() {
4159
+ const host = this.elementRef.nativeElement;
4160
+ if (!this.themeConfig)
4161
+ return;
4162
+ const t = this.themeConfig;
4163
+ const tertiary = t.tertiaryColor ?? t.accentColor;
4164
+ const map = [
4165
+ ['--lokotro-primary', t.primaryColor],
4166
+ ['--lokotro-secondary', t.secondaryColor],
4167
+ ['--lokotro-tertiary', tertiary],
4168
+ ['--lokotro-accent', tertiary],
4169
+ ['--lokotro-background', t.backgroundColor],
4170
+ ['--lokotro-surface', t.surfaceColor],
4171
+ ['--lokotro-card', t.surfaceColor],
4172
+ ['--lokotro-text-primary', t.textColor],
4173
+ ['--lokotro-text-secondary', t.secondaryTextColor],
4174
+ ['--lokotro-border', t.borderColor],
4175
+ ['--lokotro-error', t.errorColor],
4176
+ ['--lokotro-success', t.successColor],
4177
+ ['--lokotro-warning', t.warningColor],
4178
+ ['--lokotro-font-family', t.fontFamily],
4179
+ ['--lokotro-radius-md', t.borderRadius]
4180
+ ];
4181
+ for (const [prop, value] of map) {
4182
+ if (value)
4183
+ host.style.setProperty(prop, value);
4184
+ }
4224
4185
  }
4225
4186
  initializeEnvironment() {
4187
+ console.log('[Lokotro Checkout] initializeEnvironment - configs:', {
4188
+ token: this.configs.token?.substring(0, 20) + '...',
4189
+ isProduction: this.configs.isProduction,
4190
+ customApiUrl: this.configs.customApiUrl
4191
+ });
4226
4192
  LokotroPayEnv.initialize(this.configs.isProduction ?? false, this.configs.customApiUrl);
4193
+ console.log('[Lokotro Checkout] Calling setAppKey with token');
4227
4194
  this.paymentService.setAppKey(this.configs.token);
4228
4195
  if (this.configs.acceptLanguage) {
4229
4196
  this.paymentService.setAcceptLanguage(this.configs.acceptLanguage);
4230
4197
  }
4198
+ console.log('[Lokotro Checkout] Environment initialized');
4231
4199
  }
4232
4200
  initializeLocalization() {
4233
4201
  if (this.language) {
@@ -4266,13 +4234,9 @@ class LokotroPayCheckoutComponent {
4266
4234
  });
4267
4235
  }
4268
4236
  initializePayment() {
4269
- this.paymentService.createPayment(this.paymentBody)
4270
- .pipe(takeUntil(this.destroy$))
4271
- .subscribe({
4237
+ this.paymentService.createPayment(this.paymentBody).subscribe({
4272
4238
  error: (err) => {
4273
- if (LokotroPayEnv.debugMode) {
4274
- console.error('[Lokotro Pay] Payment initialization error:', err?.message || err);
4275
- }
4239
+ console.error('[Lokotro Pay] Payment initialization error:', err);
4276
4240
  }
4277
4241
  });
4278
4242
  }
@@ -4338,13 +4302,9 @@ class LokotroPayCheckoutComponent {
4338
4302
  paymentMethod: this.state?.selectedPaymentMethod?.name || '',
4339
4303
  ...formData
4340
4304
  };
4341
- this.paymentService.submitPaymentDetails(submitRequest)
4342
- .pipe(takeUntil(this.destroy$))
4343
- .subscribe({
4305
+ this.paymentService.submitPaymentDetails(submitRequest).subscribe({
4344
4306
  error: (err) => {
4345
- if (LokotroPayEnv.debugMode) {
4346
- console.error('[Lokotro Pay] Form submission error:', err?.message || err);
4347
- }
4307
+ console.error('[Lokotro Pay] Form submission error:', err);
4348
4308
  }
4349
4309
  });
4350
4310
  }
@@ -4354,12 +4314,9 @@ class LokotroPayCheckoutComponent {
4354
4314
  this.paymentService.verifyOtp({
4355
4315
  paymentId: this.state.transactionId,
4356
4316
  otpCode: otp
4357
- }).pipe(takeUntil(this.destroy$))
4358
- .subscribe({
4317
+ }).subscribe({
4359
4318
  error: (err) => {
4360
- if (LokotroPayEnv.debugMode) {
4361
- console.error('[Lokotro Pay] OTP verification error:', err?.message || err);
4362
- }
4319
+ console.error('[Lokotro Pay] OTP verification error:', err);
4363
4320
  }
4364
4321
  });
4365
4322
  }
@@ -4368,12 +4325,9 @@ class LokotroPayCheckoutComponent {
4368
4325
  return;
4369
4326
  this.paymentService.resendOtp({
4370
4327
  paymentId: this.state.transactionId
4371
- }).pipe(takeUntil(this.destroy$))
4372
- .subscribe({
4328
+ }).subscribe({
4373
4329
  error: (err) => {
4374
- if (LokotroPayEnv.debugMode) {
4375
- console.error('[Lokotro Pay] OTP resend error:', err?.message || err);
4376
- }
4330
+ console.error('[Lokotro Pay] OTP resend error:', err);
4377
4331
  }
4378
4332
  });
4379
4333
  }
@@ -4455,9 +4409,10 @@ class LokotroPayCheckoutComponent {
4455
4409
  }
4456
4410
  this.closed.emit();
4457
4411
  }
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">
4412
+ 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 }); }
4413
+ 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: `
4414
+ <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle"
4415
+ role="dialog" aria-modal="true">
4461
4416
  <!-- Header -->
4462
4417
  @if (title) {
4463
4418
  <div class="lokotro-checkout-header" [style.background-color]="titleBackgroundColor">
@@ -4578,9 +4533,9 @@ class LokotroPayCheckoutComponent {
4578
4533
  }
4579
4534
  </div>
4580
4535
  </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"] }] }); }
4536
+ `, 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
4537
  }
4583
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
4538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
4584
4539
  type: Component,
4585
4540
  args: [{ selector: 'lokotro-pay-checkout', standalone: true, imports: [
4586
4541
  LokotroPaymentMethodSelectionComponent,
@@ -4590,7 +4545,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4590
4545
  LokotroResultComponent,
4591
4546
  LokotroLoadingComponent
4592
4547
  ], template: `
4593
- <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle">
4548
+ <div class="lokotro-checkout" [class.lokotro-dark]="isDarkTheme" [style]="containerStyle"
4549
+ role="dialog" aria-modal="true">
4594
4550
  <!-- Header -->
4595
4551
  @if (title) {
4596
4552
  <div class="lokotro-checkout-header" [style.background-color]="titleBackgroundColor">
@@ -4711,8 +4667,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4711
4667
  }
4712
4668
  </div>
4713
4669
  </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: [{
4670
+ `, 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"] }]
4671
+ }], ctorParameters: () => [{ type: LokotroPaymentService }, { type: LokotroLocalizationService }, { type: i0.ElementRef }], propDecorators: { title: [{
4716
4672
  type: Input
4717
4673
  }], titleStyle: [{
4718
4674
  type: Input
@@ -4800,8 +4756,8 @@ class LokotroPayModule {
4800
4756
  ],
4801
4757
  };
4802
4758
  }
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,
4759
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
4760
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4805
4761
  FormsModule,
4806
4762
  // Standalone components
4807
4763
  LokotroPayCheckoutComponent,
@@ -4817,14 +4773,14 @@ class LokotroPayModule {
4817
4773
  LokotroProcessingComponent,
4818
4774
  LokotroResultComponent,
4819
4775
  LokotroLoadingComponent] }); }
4820
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4776
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, imports: [ReactiveFormsModule,
4821
4777
  FormsModule,
4822
4778
  // Standalone components
4823
4779
  LokotroPayCheckoutComponent,
4824
4780
  LokotroPaymentFormComponent,
4825
4781
  LokotroOtpVerificationComponent] }); }
4826
4782
  }
4827
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPayModule, decorators: [{
4783
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPayModule, decorators: [{
4828
4784
  type: NgModule,
4829
4785
  args: [{
4830
4786
  imports: [
@@ -4853,110 +4809,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
4853
4809
 
4854
4810
  /**
4855
4811
  * 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
4812
+ *
4813
+ * Neutral white-background palette intended for payment surfaces.
4814
+ * Brand colors (primary/secondary/tertiary) default to slate so the
4815
+ * widget renders correctly with no themeConfig provided. Merchants
4816
+ * override at runtime via `<lokotro-pay-checkout [themeConfig]>`.
4860
4817
  */
4861
4818
  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
4819
+ // Brand - neutral defaults (override via themeConfig)
4820
+ primary: '#0F172A',
4821
+ primaryDark: '#020617',
4822
+ primaryLight: '#1E293B',
4823
+ primaryVariant: '#0F172A',
4824
+ secondary: '#475569',
4825
+ secondaryDark: '#334155',
4826
+ secondaryLight: '#64748B',
4827
+ secondaryVariant: '#1E293B',
4828
+ // Tertiary (highlight / accent stop)
4829
+ tertiary: '#2563EB',
4830
+ tertiaryDark: '#1D4ED8',
4831
+ tertiaryLight: '#3B82F6',
4832
+ // Backwards-compat aliases
4833
+ accent: '#2563EB',
4834
+ accentDark: '#1D4ED8',
4835
+ accentLight: '#3B82F6',
4836
+ // Semantic - status colors (do not override)
4837
+ success: '#16A34A',
4838
+ successDark: '#15803D',
4839
+ successLight: '#22C55E',
4840
+ error: '#DC2626',
4841
+ errorDark: '#B91C1C',
4842
+ errorLight: '#EF4444',
4843
+ warning: '#D97706',
4844
+ warningDark: '#B45309',
4845
+ warningLight: '#F59E0B',
4846
+ info: '#2563EB',
4847
+ infoDark: '#1D4ED8',
4848
+ infoLight: '#3B82F6',
4849
+ // Neutral
4893
4850
  white: '#FFFFFF',
4894
4851
  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'
4852
+ grey: '#94A3B8',
4853
+ greyLight: '#E2E8F0',
4854
+ greyDark: '#475569',
4855
+ // Light theme (default)
4856
+ textPrimaryLight: '#0F172A',
4857
+ textSecondaryLight: '#475569',
4858
+ textTertiaryLight: '#64748B',
4859
+ textDisabledLight: '#94A3B8',
4860
+ // Dark theme (opt-in)
4861
+ textPrimaryDark: '#F8FAFC',
4862
+ textSecondaryDark: '#CBD5E1',
4863
+ textTertiaryDark: '#94A3B8',
4864
+ textDisabledDark: '#64748B',
4865
+ backgroundLight: '#FFFFFF',
4866
+ surfaceLight: '#F7F8FA',
4867
+ cardLight: '#FFFFFF',
4868
+ backgroundDark: '#0F172A',
4869
+ surfaceDark: '#1E293B',
4870
+ cardDark: '#1E293B',
4871
+ // Glass overlays
4872
+ glassLight: 'rgba(255, 255, 255, 0.7)',
4873
+ glassDark: 'rgba(15, 23, 42, 0.04)',
4874
+ glassBlur: 'rgba(15, 23, 42, 0.5)',
4875
+ borderLight: '#E2E8F0',
4876
+ borderDark: '#334155',
4877
+ dividerLight: '#F1F5F9',
4878
+ dividerDark: '#1E293B',
4879
+ shadowLight: 'rgba(15, 23, 42, 0.04)',
4880
+ shadowMedium: 'rgba(15, 23, 42, 0.08)',
4881
+ shadowDark: 'rgba(15, 23, 42, 0.12)',
4882
+ overlayLight: 'rgba(255, 255, 255, 0.7)',
4883
+ overlayDark: 'rgba(15, 23, 42, 0.5)',
4884
+ // Transaction (semantic)
4885
+ transactionSent: '#DC2626',
4886
+ transactionReceived: '#16A34A',
4887
+ transactionPending: '#D97706',
4888
+ transactionFailed: '#475569'
4938
4889
  };
4939
4890
  /**
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
4891
+ * Default theme — clean white payment surface.
4892
+ *
4893
+ * The checkout component sets these as CSS custom properties on its
4894
+ * host element, so any merchant override scopes only to the widget.
4954
4895
  */
4955
4896
  const LokotroPayDefaultTheme = {
4956
4897
  light: {
4957
4898
  '--lokotro-primary': LokotroPayColors.primary,
4958
4899
  '--lokotro-secondary': LokotroPayColors.secondary,
4959
- '--lokotro-accent': LokotroPayColors.accent,
4900
+ '--lokotro-tertiary': LokotroPayColors.tertiary,
4901
+ '--lokotro-accent': LokotroPayColors.tertiary,
4960
4902
  '--lokotro-background': LokotroPayColors.backgroundLight,
4961
4903
  '--lokotro-surface': LokotroPayColors.surfaceLight,
4962
4904
  '--lokotro-card': LokotroPayColors.cardLight,
@@ -4972,7 +4914,8 @@ const LokotroPayDefaultTheme = {
4972
4914
  dark: {
4973
4915
  '--lokotro-primary': LokotroPayColors.primary,
4974
4916
  '--lokotro-secondary': LokotroPayColors.secondary,
4975
- '--lokotro-accent': LokotroPayColors.accent,
4917
+ '--lokotro-tertiary': LokotroPayColors.tertiary,
4918
+ '--lokotro-accent': LokotroPayColors.tertiary,
4976
4919
  '--lokotro-background': LokotroPayColors.backgroundDark,
4977
4920
  '--lokotro-surface': LokotroPayColors.surfaceDark,
4978
4921
  '--lokotro-card': LokotroPayColors.cardDark,
@@ -5285,8 +5228,8 @@ class LokotroPaymentStatusComponent {
5285
5228
  }
5286
5229
  this.onClose.emit();
5287
5230
  }
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: `
5231
+ 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 }); }
5232
+ 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
5233
  <div class="lokotro-payment-status-container">
5291
5234
  <!-- Header -->
5292
5235
  @if (showHeader) {
@@ -5403,7 +5346,7 @@ class LokotroPaymentStatusComponent {
5403
5346
  </div>
5404
5347
  `, 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
5348
  }
5406
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
5349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
5407
5350
  type: Component,
5408
5351
  args: [{ selector: 'lokotro-payment-status', standalone: true, imports: [LokotroLoadingComponent, LokotroResultComponent, LokotroProcessingComponent], template: `
5409
5352
  <div class="lokotro-payment-status-container">