@bloonio/lokotro-pay 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1310,9 +1310,6 @@ class LokotroHttpClientService {
1310
1310
  get(path, queryParams) {
1311
1311
  const url = this.buildUrl(path);
1312
1312
  const headers = this.buildHeaders();
1313
- if (LokotroPayEnv.debugMode) {
1314
- console.log('[Lokotro Pay HTTP] GET', url, queryParams);
1315
- }
1316
1313
  return this.http.get(url, {
1317
1314
  headers,
1318
1315
  params: queryParams
@@ -1324,9 +1321,6 @@ class LokotroHttpClientService {
1324
1321
  post(path, data, queryParams) {
1325
1322
  const url = this.buildUrl(path);
1326
1323
  const headers = this.buildHeaders();
1327
- if (LokotroPayEnv.debugMode) {
1328
- console.log('[Lokotro Pay HTTP] POST', url, data);
1329
- }
1330
1324
  return this.http.post(url, data, {
1331
1325
  headers,
1332
1326
  params: queryParams
@@ -1338,9 +1332,6 @@ class LokotroHttpClientService {
1338
1332
  put(path, data, queryParams) {
1339
1333
  const url = this.buildUrl(path);
1340
1334
  const headers = this.buildHeaders();
1341
- if (LokotroPayEnv.debugMode) {
1342
- console.log('[Lokotro Pay HTTP] PUT', url, data);
1343
- }
1344
1335
  return this.http.put(url, data, {
1345
1336
  headers,
1346
1337
  params: queryParams
@@ -1352,9 +1343,6 @@ class LokotroHttpClientService {
1352
1343
  delete(path, queryParams) {
1353
1344
  const url = this.buildUrl(path);
1354
1345
  const headers = this.buildHeaders();
1355
- if (LokotroPayEnv.debugMode) {
1356
- console.log('[Lokotro Pay HTTP] DELETE', url);
1357
- }
1358
1346
  return this.http.delete(url, {
1359
1347
  headers,
1360
1348
  params: queryParams
@@ -2564,945 +2552,78 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2564
2552
  }] } });
2565
2553
 
2566
2554
  /**
2567
- * Lokotro Pay - Payment Service
2568
- * Main payment service that follows the Lokotro Gateway API flow
2569
- */
2570
- /**
2571
- * Initial payment state
2555
+ * Lokotro Pay - OTP Verification Component
2572
2556
  */
2573
- const initialState = {
2574
- isLoading: false,
2575
- currentScreen: LokotroPayScreenNavigation.LoadingScreen
2576
- };
2577
- class LokotroPaymentService {
2578
- constructor(httpClient) {
2579
- this.httpClient = httpClient;
2580
- this.stateSubject = new BehaviorSubject(initialState);
2581
- this.state$ = this.stateSubject.asObservable();
2582
- }
2583
- /**
2584
- * Get current state
2585
- */
2586
- get currentState() {
2587
- return this.stateSubject.value;
2557
+ class LokotroOtpVerificationComponent {
2558
+ constructor(localization) {
2559
+ this.localization = localization;
2560
+ this.otpLength = 6;
2561
+ this.otpVerified = new EventEmitter();
2562
+ this.resendOtp = new EventEmitter();
2563
+ this.cancel = new EventEmitter();
2564
+ this.otpDigits = [];
2565
+ this.resendTimer = 60;
2588
2566
  }
2589
- /**
2590
- * Update state
2591
- */
2592
- updateState(partialState) {
2593
- this.stateSubject.next({
2594
- ...this.currentState,
2595
- ...partialState
2596
- });
2567
+ ngOnInit() {
2568
+ this.otpDigits = new Array(this.otpLength).fill('');
2569
+ this.startResendTimer();
2597
2570
  }
2598
- /**
2599
- * Reset state to initial
2600
- */
2601
- resetState() {
2602
- this.stateSubject.next(initialState);
2571
+ ngOnDestroy() {
2572
+ if (this.timerInterval) {
2573
+ clearInterval(this.timerInterval);
2574
+ }
2603
2575
  }
2604
- /**
2605
- * Set app-key for authentication
2606
- */
2607
- setAppKey(appKey) {
2608
- this.httpClient.setAppKey(appKey);
2576
+ get isOtpComplete() {
2577
+ return this.otpDigits.every(digit => digit !== '');
2609
2578
  }
2610
- /**
2611
- * Set accept language
2612
- */
2613
- setAcceptLanguage(language) {
2614
- this.httpClient.setAcceptLanguage(language);
2579
+ get otpValue() {
2580
+ return this.otpDigits.join('');
2615
2581
  }
2616
- /**
2617
- * Step 1: Create payment transaction
2618
- * POST /payments/collect
2619
- */
2620
- createPayment(paymentBody) {
2621
- this.updateState({ isLoading: true });
2622
- const requestData = this.convertPaymentBodyToRequest(paymentBody);
2623
- if (LokotroPayEnv.debugMode) {
2624
- console.log('[Lokotro Payment] Creating payment:', requestData);
2625
- }
2626
- return this.httpClient.post(LokotroPayEnv.endpoints.collect, requestData).pipe(tap(response => {
2627
- if (response.isSuccess && response.data) {
2628
- const paymentInfo = this.parsePaymentInfo(response.data);
2629
- this.updateState({
2630
- isLoading: false,
2631
- transactionId: response.data.transaction_id,
2632
- paymentInfo,
2633
- currentScreen: paymentInfo.showPaymentMethodForm
2634
- ? LokotroPayScreenNavigation.PaymentMethodSelectionScreen
2635
- : LokotroPayScreenNavigation.PaymentFormScreen
2636
- });
2637
- }
2638
- else {
2639
- this.updateState({
2640
- isLoading: false,
2641
- error: {
2642
- message: response.message,
2643
- title: 'Payment Creation Failed',
2644
- errorCode: response.apiResponseCode,
2645
- timestamp: new Date()
2646
- },
2647
- currentScreen: LokotroPayScreenNavigation.ErrorScreen
2648
- });
2582
+ startResendTimer() {
2583
+ this.resendTimer = 60;
2584
+ this.timerInterval = setInterval(() => {
2585
+ this.resendTimer--;
2586
+ if (this.resendTimer <= 0) {
2587
+ clearInterval(this.timerInterval);
2649
2588
  }
2650
- }), catchError(error => {
2651
- this.updateState({
2652
- isLoading: false,
2653
- error: {
2654
- message: error.message || 'Failed to create payment',
2655
- title: 'Error',
2656
- timestamp: new Date()
2657
- },
2658
- currentScreen: LokotroPayScreenNavigation.ErrorScreen
2659
- });
2660
- return throwError(() => error);
2661
- }));
2589
+ }, 1000);
2662
2590
  }
2663
- /**
2664
- * Step 2: Get transaction details
2665
- * GET /payments/transaction/{transaction_id}
2666
- */
2667
- getTransactionDetails(transactionId) {
2668
- if (LokotroPayEnv.debugMode) {
2669
- console.log('[Lokotro Payment] Getting transaction details:', transactionId);
2670
- }
2671
- return this.httpClient.get(`${LokotroPayEnv.endpoints.transaction}/${transactionId}`).pipe(tap(response => {
2672
- if (response.isSuccess && response.data) {
2673
- if (LokotroPayEnv.debugMode) {
2674
- console.log('[Lokotro Payment] Transaction details:', response.data);
2675
- }
2591
+ onOtpInput(event, index) {
2592
+ const input = event.target;
2593
+ const value = input.value.replace(/\D/g, '');
2594
+ if (value) {
2595
+ this.otpDigits[index] = value[0];
2596
+ input.value = value[0];
2597
+ // Focus next input
2598
+ if (index < this.otpLength - 1) {
2599
+ const inputs = document.querySelectorAll('.lokotro-otp-input');
2600
+ inputs[index + 1]?.focus();
2676
2601
  }
2677
- }));
2678
- }
2679
- /**
2680
- * Step 3: Submit payment details
2681
- * POST /payments/submit
2682
- */
2683
- submitPaymentDetails(request) {
2684
- this.updateState({
2685
- isLoading: true,
2686
- currentScreen: LokotroPayScreenNavigation.ProcessingScreen
2687
- });
2688
- const requestData = this.convertSubmitRequestToData(request);
2689
- if (LokotroPayEnv.debugMode) {
2690
- console.log('[Lokotro Payment] Submitting payment details:', requestData);
2691
2602
  }
2692
- return this.httpClient.post(LokotroPayEnv.endpoints.submit, requestData).pipe(tap(response => {
2693
- if (response.isSuccess && response.data) {
2694
- const submitResponse = this.parseSubmitResponse(response.data);
2695
- if (submitResponse.requiresOtp) {
2696
- this.updateState({
2697
- isLoading: false,
2698
- currentScreen: LokotroPayScreenNavigation.EWalletOtpScreen
2699
- });
2700
- }
2701
- else if (submitResponse.redirectUrl) {
2702
- // Handle redirect for hosted checkout
2703
- window.location.href = submitResponse.redirectUrl;
2704
- }
2705
- else if (LokotroPaymentStatusInfo.isSuccess(submitResponse.status)) {
2706
- this.handlePaymentSuccess(response.data);
2707
- }
2708
- else if (LokotroPaymentStatusInfo.isPending(submitResponse.status)) {
2709
- this.updateState({
2710
- isLoading: false,
2711
- currentScreen: LokotroPayScreenNavigation.MobileMoneyProcessingScreen
2712
- });
2713
- }
2714
- else {
2715
- this.handlePaymentFailure(response.message, response.apiResponseCode);
2716
- }
2717
- }
2718
- else {
2719
- this.handlePaymentFailure(response.message, response.apiResponseCode);
2720
- }
2721
- }), catchError(error => {
2722
- this.handlePaymentFailure(error.message);
2723
- return throwError(() => error);
2724
- }));
2603
+ else {
2604
+ this.otpDigits[index] = '';
2605
+ }
2725
2606
  }
2726
- /**
2727
- * Step 4: Verify OTP (for e-wallet and flash payments)
2728
- * POST /payments/verify-otp
2729
- */
2730
- verifyOtp(request) {
2731
- this.updateState({ isLoading: true });
2732
- if (LokotroPayEnv.debugMode) {
2733
- console.log('[Lokotro Payment] Verifying OTP:', request.transactionId);
2607
+ onOtpKeydown(event, index) {
2608
+ if (event.key === 'Backspace' && !this.otpDigits[index] && index > 0) {
2609
+ const inputs = document.querySelectorAll('.lokotro-otp-input');
2610
+ inputs[index - 1]?.focus();
2734
2611
  }
2735
- return this.httpClient.post(LokotroPayEnv.endpoints.verifyOtp, {
2736
- transaction_id: request.transactionId,
2737
- otp: request.otp
2738
- }).pipe(tap(response => {
2739
- if (response.isSuccess && response.data) {
2740
- const status = LokotroPaymentStatusInfo.fromString(response.data.status || '');
2741
- if (LokotroPaymentStatusInfo.isSuccess(status)) {
2742
- this.handlePaymentSuccess(response.data);
2743
- }
2744
- else {
2745
- this.handlePaymentFailure(response.message, response.apiResponseCode);
2746
- }
2747
- }
2748
- else {
2749
- this.updateState({
2750
- isLoading: false,
2751
- error: {
2752
- message: response.message || 'OTP verification failed',
2753
- title: 'Verification Failed',
2754
- errorCode: response.apiResponseCode,
2755
- timestamp: new Date()
2756
- }
2757
- });
2612
+ }
2613
+ onOtpPaste(event) {
2614
+ event.preventDefault();
2615
+ const pastedData = event.clipboardData?.getData('text').replace(/\D/g, '');
2616
+ if (pastedData) {
2617
+ for (let i = 0; i < Math.min(pastedData.length, this.otpLength); i++) {
2618
+ this.otpDigits[i] = pastedData[i];
2758
2619
  }
2759
- }), catchError(error => {
2760
- this.updateState({
2761
- isLoading: false,
2762
- error: {
2763
- message: error.message || 'OTP verification failed',
2764
- title: 'Error',
2765
- timestamp: new Date()
2766
- }
2620
+ const inputs = document.querySelectorAll('.lokotro-otp-input');
2621
+ inputs.forEach((input, i) => {
2622
+ input.value = this.otpDigits[i] || '';
2767
2623
  });
2768
- return throwError(() => error);
2769
- }));
2770
- }
2771
- /**
2772
- * Step 5: Resend OTP (if needed)
2773
- * POST /payments/resend-otp
2774
- */
2775
- resendOtp(request) {
2776
- if (LokotroPayEnv.debugMode) {
2777
- console.log('[Lokotro Payment] Resending OTP:', request.transactionId);
2778
- }
2779
- return this.httpClient.post(LokotroPayEnv.endpoints.resendOtp, { transaction_id: request.transactionId });
2780
- }
2781
- /**
2782
- * Fetch available banks configuration
2783
- * GET /payments/get-config-bank
2784
- */
2785
- fetchAvailableBanks() {
2786
- if (LokotroPayEnv.debugMode) {
2787
- console.log('[Lokotro Payment] Fetching available banks');
2788
- }
2789
- return this.httpClient.get('/payments/get-config-bank').pipe(map(response => {
2790
- const banksData = response.data?.['data'] || [];
2791
- const banks = banksData.map(b => this.parseBank(b));
2792
- return {
2793
- ...response,
2794
- data: banks
2795
- };
2796
- }), tap(response => {
2797
- if (LokotroPayEnv.debugMode) {
2798
- console.log('[Lokotro Payment] Banks fetched:', response.data?.length);
2799
- }
2800
- }));
2801
- }
2802
- /**
2803
- * Select payment method
2804
- */
2805
- selectPaymentMethod(method) {
2806
- this.updateState({
2807
- selectedPaymentMethod: method,
2808
- currentScreen: this.getFormScreenForChannel(method.channel)
2809
- });
2810
- }
2811
- /**
2812
- * Navigate to screen
2813
- */
2814
- navigateToScreen(screen) {
2815
- this.updateState({ currentScreen: screen });
2816
- }
2817
- /**
2818
- * Handle payment success
2819
- */
2820
- handlePaymentSuccess(data) {
2821
- const response = {
2822
- message: data['message'] || 'Payment successful',
2823
- title: data['title'] || 'Success',
2824
- customRef: data['custom_ref'] || data['customer_reference'] || '',
2825
- amount: parseFloat(data['amount'] || '0'),
2826
- apiResponseCode: LokotroPayApiResponseCode.LOK000,
2827
- currency: data['currency'] || '',
2828
- paymentStatus: LokotroPaymentStatus.Approved,
2829
- systemRef: data['system_ref'] || data['transaction_id'] || '',
2830
- transactionId: data['transaction_id'],
2831
- timestamp: new Date()
2832
- };
2833
- this.updateState({
2834
- isLoading: false,
2835
- response,
2836
- currentScreen: LokotroPayScreenNavigation.SuccessScreen
2837
- });
2838
- }
2839
- /**
2840
- * Handle payment failure
2841
- */
2842
- handlePaymentFailure(message, errorCode) {
2843
- this.updateState({
2844
- isLoading: false,
2845
- error: {
2846
- message: message || 'Payment failed',
2847
- title: 'Payment Failed',
2848
- errorCode,
2849
- timestamp: new Date()
2850
- },
2851
- currentScreen: LokotroPayScreenNavigation.ErrorScreen
2852
- });
2853
- }
2854
- /**
2855
- * Get form screen for payment channel
2856
- */
2857
- getFormScreenForChannel(channel) {
2858
- switch (channel) {
2859
- case LokotroPayChannel.EWallet:
2860
- case LokotroPayChannel.LokotroWallet:
2861
- return LokotroPayScreenNavigation.EWalletFormScreen;
2862
- case LokotroPayChannel.MobileMoney:
2863
- return LokotroPayScreenNavigation.MobileMoneyFormScreen;
2864
- case LokotroPayChannel.Card:
2865
- case LokotroPayChannel.VirtualCard:
2866
- return LokotroPayScreenNavigation.CardFormScreen;
2867
- case LokotroPayChannel.EFlash:
2868
- return LokotroPayScreenNavigation.FlashFormScreen;
2869
- case LokotroPayChannel.BankTransfer:
2870
- return LokotroPayScreenNavigation.BankTransferFormScreen;
2871
- default:
2872
- return LokotroPayScreenNavigation.PaymentFormScreen;
2873
- }
2874
- }
2875
- /**
2876
- * Convert payment body to API request format
2877
- */
2878
- convertPaymentBodyToRequest(body) {
2879
- const request = {
2880
- customer_reference: body.customerReference,
2881
- amount: body.amount,
2882
- currency: body.currency.toLowerCase(),
2883
- payment_method: body.paymentMethod || 'wallet',
2884
- user_info: body.userInfo || 'full',
2885
- payment_method_info: body.paymentMethodInfo || 'full',
2886
- fee_covered_by: body.feeCoveredBy || 'buyer',
2887
- delivery_behaviour: body.deliveryBehaviour || 'direct_delivery'
2888
- };
2889
- // Add optional URLs
2890
- if (body.notifyUrl)
2891
- request['notify_url'] = body.notifyUrl;
2892
- if (body.successRedirectUrl)
2893
- request['success_redirect_url'] = body.successRedirectUrl;
2894
- if (body.failRedirectUrl)
2895
- request['fail_redirect_url'] = body.failRedirectUrl;
2896
- // Add user information
2897
- if (body.userInfo === 'full') {
2898
- if (body.firstName)
2899
- request['first_name'] = body.firstName;
2900
- if (body.lastName)
2901
- request['last_name'] = body.lastName;
2902
- if (body.phoneNumber)
2903
- request['phone_number'] = body.phoneNumber;
2904
- if (body.email)
2905
- request['email'] = body.email;
2906
- }
2907
- // Add payment method specific fields
2908
- if (body.paymentMethodInfo === 'full') {
2909
- if (body.walletNumber)
2910
- request['wallet_number'] = body.walletNumber;
2911
- if (body.walletPin)
2912
- request['wallet_pin'] = body.walletPin;
2913
- if (body.mobileMoneyPhoneNumber)
2914
- request['mobile_money_phone_number'] = body.mobileMoneyPhoneNumber;
2915
- if (body.flashNumber)
2916
- request['flash_number'] = body.flashNumber;
2917
- if (body.flashPin)
2918
- request['flash_pin'] = body.flashPin;
2919
- if (body.cardNumber)
2920
- request['card_number'] = body.cardNumber;
2921
- if (body.cardExpiryDate)
2922
- request['card_expiry_date'] = body.cardExpiryDate;
2923
- if (body.cardCvv)
2924
- request['card_cvv'] = body.cardCvv;
2925
- if (body.cardHolderName)
2926
- request['card_holder_name'] = body.cardHolderName;
2927
- }
2928
- // Add merchant information
2929
- if (body.merchant) {
2930
- request['merchant'] = {
2931
- id: body.merchant.id,
2932
- name: body.merchant.name,
2933
- logo_url: body.merchant.logoUrl,
2934
- website: body.merchant.website,
2935
- description: body.merchant.description
2936
- };
2937
- }
2938
- // Add mastercard payment method
2939
- if (body.mastercardPaymentMethod) {
2940
- request['mastercard_payment_method'] = body.mastercardPaymentMethod;
2941
- }
2942
- // Add metadata
2943
- if (body.metadata) {
2944
- request['metadata'] = body.metadata;
2945
- }
2946
- return request;
2947
- }
2948
- /**
2949
- * Convert submit request to API format
2950
- */
2951
- convertSubmitRequestToData(request) {
2952
- const data = {
2953
- transaction_id: request.transactionId,
2954
- payment_method: request.paymentMethod
2955
- };
2956
- if (request.walletNumber)
2957
- data['wallet_number'] = request.walletNumber;
2958
- if (request.walletPin)
2959
- data['wallet_pin'] = request.walletPin;
2960
- if (request.mobileMoneyPhoneNumber)
2961
- data['mobile_money_phone_number'] = request.mobileMoneyPhoneNumber;
2962
- if (request.flashNumber)
2963
- data['flash_number'] = request.flashNumber;
2964
- if (request.flashPin)
2965
- data['flash_pin'] = request.flashPin;
2966
- if (request.cardNumber)
2967
- data['card_number'] = request.cardNumber;
2968
- if (request.cardExpiryDate)
2969
- data['card_expiry_date'] = request.cardExpiryDate;
2970
- if (request.cardCvv)
2971
- data['card_cvv'] = request.cardCvv;
2972
- if (request.cardHolderName)
2973
- data['card_holder_name'] = request.cardHolderName;
2974
- if (request.bankTransferAccountId)
2975
- data['bank_transfer_account_id'] = request.bankTransferAccountId;
2976
- return data;
2977
- }
2978
- /**
2979
- * Parse payment info from API response
2980
- */
2981
- parsePaymentInfo(data) {
2982
- return {
2983
- id: data.transaction_id || '',
2984
- amount: parseFloat(data.amount || '0'),
2985
- currency: data.currency || 'USD',
2986
- description: data.description || '',
2987
- merchantName: data.merchant_name || '',
2988
- merchantId: data.merchant_id || '',
2989
- availablePaymentMethods: (data.available_payment_methods || []).map(this.parsePaymentMethod),
2990
- createdAt: new Date(data.created_at || Date.now()),
2991
- expiresAt: data.expires_at ? new Date(data.expires_at) : undefined,
2992
- metadata: data.metadata,
2993
- paymentUrl: data.payment_url,
2994
- showUserInfoForm: data.show_user_info_form || false,
2995
- showPaymentMethodForm: data.show_payment_method_form || false,
2996
- fillingInfo: data.filling_info
2997
- };
2998
- }
2999
- /**
3000
- * Parse payment method from API response
3001
- */
3002
- parsePaymentMethod(data) {
3003
- return {
3004
- id: data['id'] || '',
3005
- name: data['name'] || '',
3006
- displayName: data['display_name'] || '',
3007
- channel: data['channel'] || LokotroPayChannel.None,
3008
- iconUrl: data['icon_url'] || data['icon'] || '',
3009
- isEnabled: data['is_enabled'] ?? true,
3010
- configuration: data['configuration'],
3011
- supportedCurrencies: data['supported_currencies']
3012
- };
3013
- }
3014
- /**
3015
- * Parse submit response from API
3016
- */
3017
- parseSubmitResponse(data) {
3018
- return {
3019
- success: data.success || false,
3020
- message: data.message || '',
3021
- transactionId: data.transaction_id || '',
3022
- status: LokotroPaymentStatusInfo.fromString(data.status || ''),
3023
- requiresOtp: data.requires_otp || false,
3024
- otpDestination: data.otp_destination,
3025
- redirectUrl: data.redirect_url
3026
- };
3027
- }
3028
- /**
3029
- * Parse bank from API response
3030
- */
3031
- parseBank(data) {
3032
- return {
3033
- id: data['id'] || '',
3034
- name: data['name'] || '',
3035
- abreviation: data['abreviation'] || '',
3036
- bankLogoUrl: data['bank_logo_url'] || '',
3037
- hasRibNomenclatureConstraint: data['has_rib_nomenclature_constraint'] || false,
3038
- ribAccountNumberFormatStr: data['rib_account_number_format_str'] || '',
3039
- bankAccounts: (data['bank_accounts'] || []).map(a => this.parseBankAccount(a)),
3040
- entityAvailables: (data['entity_availables'] || []).map(e => this.parseBankEntity(e))
3041
- };
3042
- }
3043
- /**
3044
- * Parse bank account
3045
- */
3046
- parseBankAccount(data) {
3047
- return {
3048
- id: data['id'] || '',
3049
- identifier: data['identifier'] || '',
3050
- accountNumber: data['account_number'] || '',
3051
- accountLabel: data['account_label'] || '',
3052
- // createdAt: (data['created_at'] as string), // Removed undefined field
3053
- refBank: data['ref_bank'] ? this.parseBank(data['ref_bank']) : undefined,
3054
- refCurrency: data['ref_currency'] ? this.parseBankCurrency(data['ref_currency']) : undefined
3055
- };
3056
- }
3057
- /**
3058
- * Parse bank entity
3059
- */
3060
- parseBankEntity(data) {
3061
- return {
3062
- id: data['id'] || '',
3063
- name: data['name'] || '',
3064
- countryFlag: data['country_flag']
3065
- };
3066
- }
3067
- /**
3068
- * Parse bank currency
3069
- */
3070
- parseBankCurrency(data) {
3071
- return {
3072
- id: data['id'] || '',
3073
- name: data['name'] || '',
3074
- code: data['code'] || '',
3075
- symbol: data['symbol']
3076
- };
3077
- }
3078
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
3079
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
3080
- }
3081
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, decorators: [{
3082
- type: Injectable,
3083
- args: [{
3084
- providedIn: 'root'
3085
- }]
3086
- }], ctorParameters: () => [{ type: LokotroHttpClientService }] });
3087
-
3088
- /**
3089
- * Lokotro Pay - Bank Transfer Form Component
3090
- */
3091
- class LokotroBankTransferFormComponent {
3092
- constructor(fb, localization, paymentService) {
3093
- this.fb = fb;
3094
- this.localization = localization;
3095
- this.paymentService = paymentService;
3096
- this.showUserInfoForm = false;
3097
- this.formSubmitted = new EventEmitter();
3098
- this.cancel = new EventEmitter();
3099
- this.isLoading = true;
3100
- this.allBanks = [];
3101
- this.cities = [];
3102
- this.filteredBanks = [];
3103
- this.accounts = [];
3104
- this.selectedCity = null;
3105
- this.selectedBank = null;
3106
- this.selectedAccount = null;
3107
- }
3108
- ngOnInit() {
3109
- this.initForm();
3110
- this.fetchBanks();
3111
- }
3112
- initForm() {
3113
- const config = {
3114
- city: ['', Validators.required],
3115
- bank: [{ value: '', disabled: true }, Validators.required],
3116
- account: [{ value: '', disabled: true }, Validators.required]
3117
- };
3118
- if (this.showUserInfoForm) {
3119
- config['firstName'] = ['', Validators.required];
3120
- config['lastName'] = ['', Validators.required];
3121
- config['email'] = ['', [Validators.required, Validators.email]];
3122
- config['personalPhone'] = ['', Validators.required];
3123
- }
3124
- this.bankTransferForm = this.fb.group(config);
3125
- }
3126
- fetchBanks() {
3127
- this.isLoading = true;
3128
- this.paymentService.fetchAvailableBanks().subscribe({
3129
- next: (response) => {
3130
- if (response.isSuccess && response.data) {
3131
- this.allBanks = response.data;
3132
- this.extractCities();
3133
- }
3134
- this.isLoading = false;
3135
- },
3136
- error: (err) => {
3137
- console.error('Failed to fetch banks', err);
3138
- this.isLoading = false;
3139
- }
3140
- });
3141
- }
3142
- extractCities() {
3143
- const cityMap = new Map();
3144
- this.allBanks.forEach(bank => {
3145
- bank.entityAvailables.forEach(city => {
3146
- if (!cityMap.has(city.id)) {
3147
- cityMap.set(city.id, city);
3148
- }
3149
- });
3150
- });
3151
- this.cities = Array.from(cityMap.values());
3152
- }
3153
- onCityChange() {
3154
- const cityId = this.bankTransferForm.get('city')?.value;
3155
- const bankControl = this.bankTransferForm.get('bank');
3156
- const accountControl = this.bankTransferForm.get('account');
3157
- bankControl?.reset('');
3158
- bankControl?.disable();
3159
- accountControl?.reset('');
3160
- accountControl?.disable();
3161
- this.filteredBanks = [];
3162
- this.accounts = [];
3163
- this.selectedAccount = null;
3164
- if (cityId) {
3165
- this.selectedCity = this.cities.find(c => c.id === cityId) || null;
3166
- this.filteredBanks = this.allBanks.filter(bank => bank.entityAvailables.some(e => e.id === cityId));
3167
- bankControl?.enable();
3168
- }
3169
- }
3170
- onBankChange() {
3171
- const bankId = this.bankTransferForm.get('bank')?.value;
3172
- const accountControl = this.bankTransferForm.get('account');
3173
- accountControl?.reset('');
3174
- accountControl?.disable();
3175
- this.accounts = [];
3176
- this.selectedAccount = null;
3177
- if (bankId) {
3178
- this.selectedBank = this.filteredBanks.find(b => b.id === bankId) || null;
3179
- if (this.selectedBank) {
3180
- this.accounts = this.selectedBank.bankAccounts;
3181
- accountControl?.enable();
3182
- }
3183
- }
3184
- }
3185
- onAccountChange() {
3186
- const accountId = this.bankTransferForm.get('account')?.value;
3187
- if (accountId) {
3188
- this.selectedAccount = this.accounts.find(a => a.id === accountId) || null;
3189
- }
3190
- else {
3191
- this.selectedAccount = null;
3192
- }
3193
- }
3194
- onSubmit() {
3195
- if (this.bankTransferForm.valid && this.selectedAccount) {
3196
- const submitData = {
3197
- bankTransferAccountId: this.selectedAccount.id
3198
- };
3199
- if (this.showUserInfoForm) {
3200
- submitData['firstName'] = this.bankTransferForm.value.firstName;
3201
- submitData['lastName'] = this.bankTransferForm.value.lastName;
3202
- submitData['email'] = this.bankTransferForm.value.email;
3203
- submitData['phoneNumber'] = this.bankTransferForm.value.personalPhone;
3204
- }
3205
- this.formSubmitted.emit(submitData);
3206
- }
3207
- }
3208
- onCancel() {
3209
- this.cancel.emit();
3210
- }
3211
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroBankTransferFormComponent, deps: [{ token: i1$1.FormBuilder }, { token: LokotroLocalizationService }, { token: LokotroPaymentService }], target: i0.ɵɵFactoryTarget.Component }); }
3212
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroBankTransferFormComponent, isStandalone: true, selector: "lokotro-bank-transfer-form", inputs: { showUserInfoForm: "showUserInfoForm" }, outputs: { formSubmitted: "formSubmitted", cancel: "cancel" }, ngImport: i0, template: `
3213
- <div class="lokotro-bank-transfer-form">
3214
- <form [formGroup]="bankTransferForm" (ngSubmit)="onSubmit()">
3215
- <h3 class="lokotro-form-title">{{ localization.translate('bankTransfer') || 'Bank Transfer' }}</h3>
3216
-
3217
- <!-- Personal Information Section -->
3218
- <div class="lokotro-user-info-section" *ngIf="showUserInfoForm">
3219
- <h4 class="lokotro-section-subtitle">{{ localization.translate('personalInfo') }}</h4>
3220
-
3221
- <div class="lokotro-form-row">
3222
- <div class="lokotro-form-group">
3223
- <label class="lokotro-label">{{ localization.translate('firstName') }}</label>
3224
- <input type="text" class="lokotro-input" formControlName="firstName" placeholder="John">
3225
- <span class="lokotro-error" *ngIf="bankTransferForm.get('firstName')?.touched && bankTransferForm.get('firstName')?.invalid">
3226
- {{ localization.translate('required') }}
3227
- </span>
3228
- </div>
3229
-
3230
- <div class="lokotro-form-group">
3231
- <label class="lokotro-label">{{ localization.translate('lastName') }}</label>
3232
- <input type="text" class="lokotro-input" formControlName="lastName" placeholder="Doe">
3233
- <span class="lokotro-error" *ngIf="bankTransferForm.get('lastName')?.touched && bankTransferForm.get('lastName')?.invalid">
3234
- {{ localization.translate('required') }}
3235
- </span>
3236
- </div>
3237
- </div>
3238
-
3239
- <div class="lokotro-form-group">
3240
- <label class="lokotro-label">{{ localization.translate('email') }}</label>
3241
- <input type="email" class="lokotro-input" formControlName="email" placeholder="john.doe@example.com">
3242
- <span class="lokotro-error" *ngIf="bankTransferForm.get('email')?.touched && bankTransferForm.get('email')?.invalid">
3243
- {{ localization.translate('invalidEmail') }}
3244
- </span>
3245
- </div>
3246
-
3247
- <div class="lokotro-form-group">
3248
- <label class="lokotro-label">{{ localization.translate('phoneNumber') }}</label>
3249
- <input type="tel" class="lokotro-input" formControlName="personalPhone" placeholder="+243 XXX XXX XXX">
3250
- <span class="lokotro-error" *ngIf="bankTransferForm.get('personalPhone')?.touched && bankTransferForm.get('personalPhone')?.invalid">
3251
- {{ localization.translate('invalidPhoneNumber') }}
3252
- </span>
3253
- </div>
3254
-
3255
- <hr class="lokotro-divider">
3256
- </div>
3257
-
3258
- <!-- Bank Selection Section -->
3259
- <h4 class="lokotro-section-subtitle" *ngIf="showUserInfoForm">{{ localization.translate('bankDetails') || 'Bank Details' }}</h4>
3260
-
3261
- <!-- Loading State -->
3262
- <div *ngIf="isLoading" class="lokotro-loading-text">
3263
- {{ localization.translate('loading') || 'Loading...' }}
3264
- </div>
3265
-
3266
- <div *ngIf="!isLoading">
3267
- <!-- City Selection -->
3268
- <div class="lokotro-form-group">
3269
- <label class="lokotro-label">{{ localization.translate('selectCity') || 'Select City' }}</label>
3270
- <select class="lokotro-input" formControlName="city" (change)="onCityChange()">
3271
- <option value="">{{ localization.translate('selectCity') || 'Select City' }}</option>
3272
- <option *ngFor="let city of cities" [value]="city.id">{{ city.name }}</option>
3273
- </select>
3274
- </div>
3275
-
3276
- <!-- Bank Selection -->
3277
- <div class="lokotro-form-group">
3278
- <label class="lokotro-label">{{ localization.translate('selectBank') || 'Select Bank' }}</label>
3279
- <select class="lokotro-input" formControlName="bank" (change)="onBankChange()">
3280
- <option value="">{{ localization.translate('selectBank') || 'Select Bank' }}</option>
3281
- <option *ngFor="let bank of filteredBanks" [value]="bank.id">{{ bank.name }}</option>
3282
- </select>
3283
- </div>
3284
-
3285
- <!-- Account Selection -->
3286
- <div class="lokotro-form-group">
3287
- <label class="lokotro-label">{{ localization.translate('selectBankAccount') || 'Select Account' }}</label>
3288
- <select class="lokotro-input" formControlName="account" (change)="onAccountChange()">
3289
- <option value="">{{ localization.translate('selectBankAccount') || 'Select Account' }}</option>
3290
- <option *ngFor="let account of accounts" [value]="account.id">{{ account.accountLabel }} ({{ account.accountNumber }})</option>
3291
- </select>
3292
- </div>
3293
- </div>
3294
-
3295
- <!-- Selected Account Summary -->
3296
- <div class="lokotro-account-summary" *ngIf="selectedAccount">
3297
- <div class="lokotro-info-box">
3298
- <p><strong>{{ localization.translate('bankAccountSummary') || 'Account Number' }}:</strong> {{ selectedAccount.accountNumber }}</p>
3299
- <p><strong>{{ localization.translate('accountLabel') || 'Label' }}:</strong> {{ selectedAccount.accountLabel }}</p>
3300
- <p class="lokotro-info-text">
3301
- {{ localization.translate('bankTransferProofInstructions') || 'A payment link will be sent to your email to upload the proof of transfer.' }}
3302
- </p>
3303
- </div>
3304
- </div>
3305
-
3306
- <div class="lokotro-form-actions">
3307
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
3308
- {{ localization.translate('cancel') }}
3309
- </button>
3310
- <button type="submit" class="lokotro-btn-primary" [disabled]="bankTransferForm.invalid || isLoading">
3311
- {{ localization.translate('confirmBankTransfer') || 'Confirm Transfer' }}
3312
- </button>
3313
- </div>
3314
- </form>
3315
- </div>
3316
- `, isInline: true, styles: [".lokotro-bank-transfer-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-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)}.lokotro-loading-text{text-align:center;color:var(--lokotro-text-secondary, #D5D3B8);padding:20px}.lokotro-account-summary{margin-top:20px;padding:16px;background:#3a484080;border-radius:12px;border:1px solid var(--lokotro-border, #3A473F)}.lokotro-info-box p{margin:8px 0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-info-text{font-style:italic;color:var(--lokotro-text-secondary, #D5D3B8);font-size:14px;margin-top:12px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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: "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"] }] }); }
3317
- }
3318
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroBankTransferFormComponent, decorators: [{
3319
- type: Component,
3320
- args: [{ selector: 'lokotro-bank-transfer-form', standalone: true, imports: [CommonModule, FormsModule, ReactiveFormsModule], template: `
3321
- <div class="lokotro-bank-transfer-form">
3322
- <form [formGroup]="bankTransferForm" (ngSubmit)="onSubmit()">
3323
- <h3 class="lokotro-form-title">{{ localization.translate('bankTransfer') || 'Bank Transfer' }}</h3>
3324
-
3325
- <!-- Personal Information Section -->
3326
- <div class="lokotro-user-info-section" *ngIf="showUserInfoForm">
3327
- <h4 class="lokotro-section-subtitle">{{ localization.translate('personalInfo') }}</h4>
3328
-
3329
- <div class="lokotro-form-row">
3330
- <div class="lokotro-form-group">
3331
- <label class="lokotro-label">{{ localization.translate('firstName') }}</label>
3332
- <input type="text" class="lokotro-input" formControlName="firstName" placeholder="John">
3333
- <span class="lokotro-error" *ngIf="bankTransferForm.get('firstName')?.touched && bankTransferForm.get('firstName')?.invalid">
3334
- {{ localization.translate('required') }}
3335
- </span>
3336
- </div>
3337
-
3338
- <div class="lokotro-form-group">
3339
- <label class="lokotro-label">{{ localization.translate('lastName') }}</label>
3340
- <input type="text" class="lokotro-input" formControlName="lastName" placeholder="Doe">
3341
- <span class="lokotro-error" *ngIf="bankTransferForm.get('lastName')?.touched && bankTransferForm.get('lastName')?.invalid">
3342
- {{ localization.translate('required') }}
3343
- </span>
3344
- </div>
3345
- </div>
3346
-
3347
- <div class="lokotro-form-group">
3348
- <label class="lokotro-label">{{ localization.translate('email') }}</label>
3349
- <input type="email" class="lokotro-input" formControlName="email" placeholder="john.doe@example.com">
3350
- <span class="lokotro-error" *ngIf="bankTransferForm.get('email')?.touched && bankTransferForm.get('email')?.invalid">
3351
- {{ localization.translate('invalidEmail') }}
3352
- </span>
3353
- </div>
3354
-
3355
- <div class="lokotro-form-group">
3356
- <label class="lokotro-label">{{ localization.translate('phoneNumber') }}</label>
3357
- <input type="tel" class="lokotro-input" formControlName="personalPhone" placeholder="+243 XXX XXX XXX">
3358
- <span class="lokotro-error" *ngIf="bankTransferForm.get('personalPhone')?.touched && bankTransferForm.get('personalPhone')?.invalid">
3359
- {{ localization.translate('invalidPhoneNumber') }}
3360
- </span>
3361
- </div>
3362
-
3363
- <hr class="lokotro-divider">
3364
- </div>
3365
-
3366
- <!-- Bank Selection Section -->
3367
- <h4 class="lokotro-section-subtitle" *ngIf="showUserInfoForm">{{ localization.translate('bankDetails') || 'Bank Details' }}</h4>
3368
-
3369
- <!-- Loading State -->
3370
- <div *ngIf="isLoading" class="lokotro-loading-text">
3371
- {{ localization.translate('loading') || 'Loading...' }}
3372
- </div>
3373
-
3374
- <div *ngIf="!isLoading">
3375
- <!-- City Selection -->
3376
- <div class="lokotro-form-group">
3377
- <label class="lokotro-label">{{ localization.translate('selectCity') || 'Select City' }}</label>
3378
- <select class="lokotro-input" formControlName="city" (change)="onCityChange()">
3379
- <option value="">{{ localization.translate('selectCity') || 'Select City' }}</option>
3380
- <option *ngFor="let city of cities" [value]="city.id">{{ city.name }}</option>
3381
- </select>
3382
- </div>
3383
-
3384
- <!-- Bank Selection -->
3385
- <div class="lokotro-form-group">
3386
- <label class="lokotro-label">{{ localization.translate('selectBank') || 'Select Bank' }}</label>
3387
- <select class="lokotro-input" formControlName="bank" (change)="onBankChange()">
3388
- <option value="">{{ localization.translate('selectBank') || 'Select Bank' }}</option>
3389
- <option *ngFor="let bank of filteredBanks" [value]="bank.id">{{ bank.name }}</option>
3390
- </select>
3391
- </div>
3392
-
3393
- <!-- Account Selection -->
3394
- <div class="lokotro-form-group">
3395
- <label class="lokotro-label">{{ localization.translate('selectBankAccount') || 'Select Account' }}</label>
3396
- <select class="lokotro-input" formControlName="account" (change)="onAccountChange()">
3397
- <option value="">{{ localization.translate('selectBankAccount') || 'Select Account' }}</option>
3398
- <option *ngFor="let account of accounts" [value]="account.id">{{ account.accountLabel }} ({{ account.accountNumber }})</option>
3399
- </select>
3400
- </div>
3401
- </div>
3402
-
3403
- <!-- Selected Account Summary -->
3404
- <div class="lokotro-account-summary" *ngIf="selectedAccount">
3405
- <div class="lokotro-info-box">
3406
- <p><strong>{{ localization.translate('bankAccountSummary') || 'Account Number' }}:</strong> {{ selectedAccount.accountNumber }}</p>
3407
- <p><strong>{{ localization.translate('accountLabel') || 'Label' }}:</strong> {{ selectedAccount.accountLabel }}</p>
3408
- <p class="lokotro-info-text">
3409
- {{ localization.translate('bankTransferProofInstructions') || 'A payment link will be sent to your email to upload the proof of transfer.' }}
3410
- </p>
3411
- </div>
3412
- </div>
3413
-
3414
- <div class="lokotro-form-actions">
3415
- <button type="button" class="lokotro-btn-secondary" (click)="onCancel()">
3416
- {{ localization.translate('cancel') }}
3417
- </button>
3418
- <button type="submit" class="lokotro-btn-primary" [disabled]="bankTransferForm.invalid || isLoading">
3419
- {{ localization.translate('confirmBankTransfer') || 'Confirm Transfer' }}
3420
- </button>
3421
- </div>
3422
- </form>
3423
- </div>
3424
- `, styles: [".lokotro-bank-transfer-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-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)}.lokotro-loading-text{text-align:center;color:var(--lokotro-text-secondary, #D5D3B8);padding:20px}.lokotro-account-summary{margin-top:20px;padding:16px;background:#3a484080;border-radius:12px;border:1px solid var(--lokotro-border, #3A473F)}.lokotro-info-box p{margin:8px 0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-info-text{font-style:italic;color:var(--lokotro-text-secondary, #D5D3B8);font-size:14px;margin-top:12px!important}\n"] }]
3425
- }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: LokotroLocalizationService }, { type: LokotroPaymentService }], propDecorators: { showUserInfoForm: [{
3426
- type: Input
3427
- }], formSubmitted: [{
3428
- type: Output
3429
- }], cancel: [{
3430
- type: Output
3431
- }] } });
3432
-
3433
- /**
3434
- * Lokotro Pay - OTP Verification Component
3435
- */
3436
- class LokotroOtpVerificationComponent {
3437
- constructor(localization) {
3438
- this.localization = localization;
3439
- this.otpLength = 6;
3440
- this.otpVerified = new EventEmitter();
3441
- this.resendOtp = new EventEmitter();
3442
- this.cancel = new EventEmitter();
3443
- this.otpDigits = [];
3444
- this.resendTimer = 60;
3445
- }
3446
- ngOnInit() {
3447
- this.otpDigits = new Array(this.otpLength).fill('');
3448
- this.startResendTimer();
3449
- }
3450
- ngOnDestroy() {
3451
- if (this.timerInterval) {
3452
- clearInterval(this.timerInterval);
3453
- }
3454
- }
3455
- get isOtpComplete() {
3456
- return this.otpDigits.every(digit => digit !== '');
3457
- }
3458
- get otpValue() {
3459
- return this.otpDigits.join('');
3460
- }
3461
- startResendTimer() {
3462
- this.resendTimer = 60;
3463
- this.timerInterval = setInterval(() => {
3464
- this.resendTimer--;
3465
- if (this.resendTimer <= 0) {
3466
- clearInterval(this.timerInterval);
3467
- }
3468
- }, 1000);
3469
- }
3470
- onOtpInput(event, index) {
3471
- const input = event.target;
3472
- const value = input.value.replace(/\D/g, '');
3473
- if (value) {
3474
- this.otpDigits[index] = value[0];
3475
- input.value = value[0];
3476
- // Focus next input
3477
- if (index < this.otpLength - 1) {
3478
- const inputs = document.querySelectorAll('.lokotro-otp-input');
3479
- inputs[index + 1]?.focus();
3480
- }
3481
- }
3482
- else {
3483
- this.otpDigits[index] = '';
3484
- }
3485
- }
3486
- onOtpKeydown(event, index) {
3487
- if (event.key === 'Backspace' && !this.otpDigits[index] && index > 0) {
3488
- const inputs = document.querySelectorAll('.lokotro-otp-input');
3489
- inputs[index - 1]?.focus();
3490
- }
3491
- }
3492
- onOtpPaste(event) {
3493
- event.preventDefault();
3494
- const pastedData = event.clipboardData?.getData('text').replace(/\D/g, '');
3495
- if (pastedData) {
3496
- for (let i = 0; i < Math.min(pastedData.length, this.otpLength); i++) {
3497
- this.otpDigits[i] = pastedData[i];
3498
- }
3499
- const inputs = document.querySelectorAll('.lokotro-otp-input');
3500
- inputs.forEach((input, i) => {
3501
- input.value = this.otpDigits[i] || '';
3502
- });
3503
- // Focus last filled input or first empty
3504
- const focusIndex = Math.min(pastedData.length, this.otpLength - 1);
3505
- inputs[focusIndex]?.focus();
2624
+ // Focus last filled input or first empty
2625
+ const focusIndex = Math.min(pastedData.length, this.otpLength - 1);
2626
+ inputs[focusIndex]?.focus();
3506
2627
  }
3507
2628
  }
3508
2629
  onVerify() {
@@ -3918,23 +3039,448 @@ class LokotroLoadingComponent {
3918
3039
  </div>
3919
3040
  <p class="lokotro-loading-message" *ngIf="message">{{ message }}</p>
3920
3041
  </div>
3921
- `, isInline: true, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
3042
+ `, isInline: true, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
3043
+ }
3044
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLoadingComponent, decorators: [{
3045
+ type: Component,
3046
+ args: [{ selector: 'lokotro-loading', standalone: true, imports: [CommonModule], template: `
3047
+ <div class="lokotro-loading">
3048
+ <div class="lokotro-loading-spinner">
3049
+ <div class="lokotro-pulse-ring"></div>
3050
+ <div class="lokotro-pulse-ring"></div>
3051
+ <div class="lokotro-pulse-dot"></div>
3052
+ </div>
3053
+ <p class="lokotro-loading-message" *ngIf="message">{{ message }}</p>
3054
+ </div>
3055
+ `, styles: [".lokotro-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;padding:24px}.lokotro-loading-spinner{position:relative;width:60px;height:60px;margin-bottom:24px}.lokotro-pulse-ring{position:absolute;width:100%;height:100%;border:3px solid var(--lokotro-accent, #3BFBDA);border-radius:50%;opacity:0;animation:lokotro-pulse 2s cubic-bezier(.215,.61,.355,1) infinite}.lokotro-pulse-ring:nth-child(2){animation-delay:.5s}.lokotro-pulse-dot{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:16px;height:16px;background:var(--lokotro-accent, #3BFBDA);border-radius:50%;animation:lokotro-dot-pulse 2s ease-in-out infinite}@keyframes lokotro-pulse{0%{transform:scale(.5);opacity:.8}to{transform:scale(1.5);opacity:0}}@keyframes lokotro-dot-pulse{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.2)}}.lokotro-loading-message{font-size:16px;color:var(--lokotro-text-secondary, #D5D3B8);margin:0}\n"] }]
3056
+ }], propDecorators: { message: [{
3057
+ type: Input
3058
+ }] } });
3059
+
3060
+ /**
3061
+ * Lokotro Pay - Payment Service
3062
+ * Main payment service that follows the Lokotro Gateway API flow
3063
+ */
3064
+ /**
3065
+ * Initial payment state
3066
+ */
3067
+ const initialState = {
3068
+ isLoading: false,
3069
+ currentScreen: LokotroPayScreenNavigation.LoadingScreen
3070
+ };
3071
+ class LokotroPaymentService {
3072
+ constructor(httpClient) {
3073
+ this.httpClient = httpClient;
3074
+ this.stateSubject = new BehaviorSubject(initialState);
3075
+ this.state$ = this.stateSubject.asObservable();
3076
+ }
3077
+ /**
3078
+ * Get current state
3079
+ */
3080
+ get currentState() {
3081
+ return this.stateSubject.value;
3082
+ }
3083
+ /**
3084
+ * Update state
3085
+ */
3086
+ updateState(partialState) {
3087
+ this.stateSubject.next({
3088
+ ...this.currentState,
3089
+ ...partialState
3090
+ });
3091
+ }
3092
+ /**
3093
+ * Reset state to initial
3094
+ */
3095
+ resetState() {
3096
+ this.stateSubject.next(initialState);
3097
+ }
3098
+ /**
3099
+ * Set app-key for authentication
3100
+ */
3101
+ setAppKey(appKey) {
3102
+ this.httpClient.setAppKey(appKey);
3103
+ }
3104
+ /**
3105
+ * Set accept language
3106
+ */
3107
+ setAcceptLanguage(language) {
3108
+ this.httpClient.setAcceptLanguage(language);
3109
+ }
3110
+ /**
3111
+ * Step 1: Create payment transaction
3112
+ * POST /payments/collect
3113
+ */
3114
+ createPayment(paymentBody) {
3115
+ this.updateState({ isLoading: true });
3116
+ const requestData = this.convertPaymentBodyToRequest(paymentBody);
3117
+ return this.httpClient.post(LokotroPayEnv.endpoints.collect, requestData).pipe(tap(response => {
3118
+ if (response.isSuccess && response.data) {
3119
+ const paymentInfo = this.parsePaymentInfo(response.data);
3120
+ this.updateState({
3121
+ isLoading: false,
3122
+ transactionId: response.data.transaction_id,
3123
+ paymentInfo,
3124
+ currentScreen: paymentInfo.showPaymentMethodForm
3125
+ ? LokotroPayScreenNavigation.PaymentMethodSelectionScreen
3126
+ : LokotroPayScreenNavigation.PaymentFormScreen
3127
+ });
3128
+ }
3129
+ else {
3130
+ this.updateState({
3131
+ isLoading: false,
3132
+ error: {
3133
+ message: response.message,
3134
+ title: 'Payment Creation Failed',
3135
+ errorCode: response.apiResponseCode,
3136
+ timestamp: new Date()
3137
+ },
3138
+ currentScreen: LokotroPayScreenNavigation.ErrorScreen
3139
+ });
3140
+ }
3141
+ }), catchError(error => {
3142
+ this.updateState({
3143
+ isLoading: false,
3144
+ error: {
3145
+ message: error.message || 'Failed to create payment',
3146
+ title: 'Error',
3147
+ timestamp: new Date()
3148
+ },
3149
+ currentScreen: LokotroPayScreenNavigation.ErrorScreen
3150
+ });
3151
+ return throwError(() => error);
3152
+ }));
3153
+ }
3154
+ /**
3155
+ * Step 2: Get transaction details
3156
+ * GET /payments/transaction/{transaction_id}
3157
+ */
3158
+ getTransactionDetails(transactionId) {
3159
+ return this.httpClient.get(`${LokotroPayEnv.endpoints.transaction}/${transactionId}`).pipe();
3160
+ }
3161
+ /**
3162
+ * Step 3: Submit payment details
3163
+ * POST /payments/submit
3164
+ */
3165
+ submitPaymentDetails(request) {
3166
+ this.updateState({
3167
+ isLoading: true,
3168
+ currentScreen: LokotroPayScreenNavigation.ProcessingScreen
3169
+ });
3170
+ const requestData = this.convertSubmitRequestToData(request);
3171
+ return this.httpClient.post(LokotroPayEnv.endpoints.submit, requestData).pipe(tap(response => {
3172
+ if (response.isSuccess && response.data) {
3173
+ const submitResponse = this.parseSubmitResponse(response.data);
3174
+ if (submitResponse.requiresOtp) {
3175
+ this.updateState({
3176
+ isLoading: false,
3177
+ currentScreen: LokotroPayScreenNavigation.EWalletOtpScreen
3178
+ });
3179
+ }
3180
+ else if (submitResponse.redirectUrl) {
3181
+ // Handle redirect for hosted checkout
3182
+ window.location.href = submitResponse.redirectUrl;
3183
+ }
3184
+ else if (LokotroPaymentStatusInfo.isSuccess(submitResponse.status)) {
3185
+ this.handlePaymentSuccess(response.data);
3186
+ }
3187
+ else if (LokotroPaymentStatusInfo.isPending(submitResponse.status)) {
3188
+ this.updateState({
3189
+ isLoading: false,
3190
+ currentScreen: LokotroPayScreenNavigation.MobileMoneyProcessingScreen
3191
+ });
3192
+ }
3193
+ else {
3194
+ this.handlePaymentFailure(response.message, response.apiResponseCode);
3195
+ }
3196
+ }
3197
+ else {
3198
+ this.handlePaymentFailure(response.message, response.apiResponseCode);
3199
+ }
3200
+ }), catchError(error => {
3201
+ this.handlePaymentFailure(error.message);
3202
+ return throwError(() => error);
3203
+ }));
3204
+ }
3205
+ /**
3206
+ * Step 4: Verify OTP (for e-wallet and flash payments)
3207
+ * POST /payments/verify-otp
3208
+ */
3209
+ verifyOtp(request) {
3210
+ this.updateState({ isLoading: true });
3211
+ return this.httpClient.post(LokotroPayEnv.endpoints.verifyOtp, {
3212
+ transaction_id: request.transactionId,
3213
+ otp: request.otp
3214
+ }).pipe(tap(response => {
3215
+ if (response.isSuccess && response.data) {
3216
+ const status = LokotroPaymentStatusInfo.fromString(response.data.status || '');
3217
+ if (LokotroPaymentStatusInfo.isSuccess(status)) {
3218
+ this.handlePaymentSuccess(response.data);
3219
+ }
3220
+ else {
3221
+ this.handlePaymentFailure(response.message, response.apiResponseCode);
3222
+ }
3223
+ }
3224
+ else {
3225
+ this.updateState({
3226
+ isLoading: false,
3227
+ error: {
3228
+ message: response.message || 'OTP verification failed',
3229
+ title: 'Verification Failed',
3230
+ errorCode: response.apiResponseCode,
3231
+ timestamp: new Date()
3232
+ }
3233
+ });
3234
+ }
3235
+ }), catchError(error => {
3236
+ this.updateState({
3237
+ isLoading: false,
3238
+ error: {
3239
+ message: error.message || 'OTP verification failed',
3240
+ title: 'Error',
3241
+ timestamp: new Date()
3242
+ }
3243
+ });
3244
+ return throwError(() => error);
3245
+ }));
3246
+ }
3247
+ /**
3248
+ * Step 5: Resend OTP (if needed)
3249
+ * POST /payments/resend-otp
3250
+ */
3251
+ resendOtp(request) {
3252
+ return this.httpClient.post(LokotroPayEnv.endpoints.resendOtp, { transaction_id: request.transactionId });
3253
+ }
3254
+ /**
3255
+ * Select payment method
3256
+ */
3257
+ selectPaymentMethod(method) {
3258
+ this.updateState({
3259
+ selectedPaymentMethod: method,
3260
+ currentScreen: this.getFormScreenForChannel(method.channel)
3261
+ });
3262
+ }
3263
+ /**
3264
+ * Navigate to screen
3265
+ */
3266
+ navigateToScreen(screen) {
3267
+ this.updateState({ currentScreen: screen });
3268
+ }
3269
+ /**
3270
+ * Handle payment success
3271
+ */
3272
+ handlePaymentSuccess(data) {
3273
+ const response = {
3274
+ message: data['message'] || 'Payment successful',
3275
+ title: data['title'] || 'Success',
3276
+ customRef: data['custom_ref'] || data['customer_reference'] || '',
3277
+ amount: parseFloat(data['amount'] || '0'),
3278
+ apiResponseCode: LokotroPayApiResponseCode.LOK000,
3279
+ currency: data['currency'] || '',
3280
+ paymentStatus: LokotroPaymentStatus.Approved,
3281
+ systemRef: data['system_ref'] || data['transaction_id'] || '',
3282
+ transactionId: data['transaction_id'],
3283
+ timestamp: new Date()
3284
+ };
3285
+ this.updateState({
3286
+ isLoading: false,
3287
+ response,
3288
+ currentScreen: LokotroPayScreenNavigation.SuccessScreen
3289
+ });
3290
+ }
3291
+ /**
3292
+ * Handle payment failure
3293
+ */
3294
+ handlePaymentFailure(message, errorCode) {
3295
+ this.updateState({
3296
+ isLoading: false,
3297
+ error: {
3298
+ message: message || 'Payment failed',
3299
+ title: 'Payment Failed',
3300
+ errorCode,
3301
+ timestamp: new Date()
3302
+ },
3303
+ currentScreen: LokotroPayScreenNavigation.ErrorScreen
3304
+ });
3305
+ }
3306
+ /**
3307
+ * Get form screen for payment channel
3308
+ */
3309
+ getFormScreenForChannel(channel) {
3310
+ switch (channel) {
3311
+ case LokotroPayChannel.EWallet:
3312
+ case LokotroPayChannel.LokotroWallet:
3313
+ return LokotroPayScreenNavigation.EWalletFormScreen;
3314
+ case LokotroPayChannel.MobileMoney:
3315
+ return LokotroPayScreenNavigation.MobileMoneyFormScreen;
3316
+ case LokotroPayChannel.Card:
3317
+ case LokotroPayChannel.VirtualCard:
3318
+ return LokotroPayScreenNavigation.CardFormScreen;
3319
+ case LokotroPayChannel.EFlash:
3320
+ return LokotroPayScreenNavigation.FlashFormScreen;
3321
+ default:
3322
+ return LokotroPayScreenNavigation.PaymentFormScreen;
3323
+ }
3324
+ }
3325
+ /**
3326
+ * Convert payment body to API request format
3327
+ */
3328
+ convertPaymentBodyToRequest(body) {
3329
+ const request = {
3330
+ customer_reference: body.customerReference,
3331
+ amount: body.amount,
3332
+ currency: body.currency.toLowerCase(),
3333
+ payment_method: body.paymentMethod || 'wallet',
3334
+ user_info: body.userInfo || 'full',
3335
+ payment_method_info: body.paymentMethodInfo || 'full',
3336
+ fee_covered_by: body.feeCoveredBy || 'buyer',
3337
+ delivery_behaviour: body.deliveryBehaviour || 'direct_delivery'
3338
+ };
3339
+ // Add optional URLs
3340
+ if (body.notifyUrl)
3341
+ request['notify_url'] = body.notifyUrl;
3342
+ if (body.successRedirectUrl)
3343
+ request['success_redirect_url'] = body.successRedirectUrl;
3344
+ if (body.failRedirectUrl)
3345
+ request['fail_redirect_url'] = body.failRedirectUrl;
3346
+ // Add user information
3347
+ if (body.userInfo === 'full') {
3348
+ if (body.firstName)
3349
+ request['first_name'] = body.firstName;
3350
+ if (body.lastName)
3351
+ request['last_name'] = body.lastName;
3352
+ if (body.phoneNumber)
3353
+ request['phone_number'] = body.phoneNumber;
3354
+ if (body.email)
3355
+ request['email'] = body.email;
3356
+ }
3357
+ // Add payment method specific fields
3358
+ if (body.paymentMethodInfo === 'full') {
3359
+ if (body.walletNumber)
3360
+ request['wallet_number'] = body.walletNumber;
3361
+ if (body.walletPin)
3362
+ request['wallet_pin'] = body.walletPin;
3363
+ if (body.mobileMoneyPhoneNumber)
3364
+ request['mobile_money_phone_number'] = body.mobileMoneyPhoneNumber;
3365
+ if (body.flashNumber)
3366
+ request['flash_number'] = body.flashNumber;
3367
+ if (body.flashPin)
3368
+ request['flash_pin'] = body.flashPin;
3369
+ if (body.cardNumber)
3370
+ request['card_number'] = body.cardNumber;
3371
+ if (body.cardExpiryDate)
3372
+ request['card_expiry_date'] = body.cardExpiryDate;
3373
+ if (body.cardCvv)
3374
+ request['card_cvv'] = body.cardCvv;
3375
+ if (body.cardHolderName)
3376
+ request['card_holder_name'] = body.cardHolderName;
3377
+ }
3378
+ // Add merchant information
3379
+ if (body.merchant) {
3380
+ request['merchant'] = {
3381
+ id: body.merchant.id,
3382
+ name: body.merchant.name,
3383
+ logo_url: body.merchant.logoUrl,
3384
+ website: body.merchant.website,
3385
+ description: body.merchant.description
3386
+ };
3387
+ }
3388
+ // Add mastercard payment method
3389
+ if (body.mastercardPaymentMethod) {
3390
+ request['mastercard_payment_method'] = body.mastercardPaymentMethod;
3391
+ }
3392
+ // Add metadata
3393
+ if (body.metadata) {
3394
+ request['metadata'] = body.metadata;
3395
+ }
3396
+ return request;
3397
+ }
3398
+ /**
3399
+ * Convert submit request to API format
3400
+ */
3401
+ convertSubmitRequestToData(request) {
3402
+ const data = {
3403
+ transaction_id: request.transactionId,
3404
+ payment_method: request.paymentMethod
3405
+ };
3406
+ if (request.walletNumber)
3407
+ data['wallet_number'] = request.walletNumber;
3408
+ if (request.walletPin)
3409
+ data['wallet_pin'] = request.walletPin;
3410
+ if (request.mobileMoneyPhoneNumber)
3411
+ data['mobile_money_phone_number'] = request.mobileMoneyPhoneNumber;
3412
+ if (request.flashNumber)
3413
+ data['flash_number'] = request.flashNumber;
3414
+ if (request.flashPin)
3415
+ data['flash_pin'] = request.flashPin;
3416
+ if (request.cardNumber)
3417
+ data['card_number'] = request.cardNumber;
3418
+ if (request.cardExpiryDate)
3419
+ data['card_expiry_date'] = request.cardExpiryDate;
3420
+ if (request.cardCvv)
3421
+ data['card_cvv'] = request.cardCvv;
3422
+ if (request.cardHolderName)
3423
+ data['card_holder_name'] = request.cardHolderName;
3424
+ return data;
3425
+ }
3426
+ /**
3427
+ * Parse payment info from API response
3428
+ */
3429
+ parsePaymentInfo(data) {
3430
+ return {
3431
+ id: data.transaction_id || '',
3432
+ amount: parseFloat(data.amount || '0'),
3433
+ currency: data.currency || 'USD',
3434
+ description: data.description || '',
3435
+ merchantName: data.merchant_name || '',
3436
+ merchantId: data.merchant_id || '',
3437
+ availablePaymentMethods: (data.available_payment_methods || []).map(this.parsePaymentMethod),
3438
+ createdAt: new Date(data.created_at || Date.now()),
3439
+ expiresAt: data.expires_at ? new Date(data.expires_at) : undefined,
3440
+ metadata: data.metadata,
3441
+ paymentUrl: data.payment_url,
3442
+ showUserInfoForm: data.show_user_info_form || false,
3443
+ showPaymentMethodForm: data.show_payment_method_form || false
3444
+ };
3445
+ }
3446
+ /**
3447
+ * Parse payment method from API response
3448
+ */
3449
+ parsePaymentMethod(data) {
3450
+ return {
3451
+ id: data['id'] || '',
3452
+ name: data['name'] || '',
3453
+ displayName: data['display_name'] || '',
3454
+ channel: data['channel'] || LokotroPayChannel.None,
3455
+ iconUrl: data['icon_url'] || data['icon'] || '',
3456
+ isEnabled: data['is_enabled'] ?? true,
3457
+ configuration: data['configuration'],
3458
+ supportedCurrencies: data['supported_currencies']
3459
+ };
3460
+ }
3461
+ /**
3462
+ * Parse submit response from API
3463
+ */
3464
+ parseSubmitResponse(data) {
3465
+ return {
3466
+ success: data.success || false,
3467
+ message: data.message || '',
3468
+ transactionId: data.transaction_id || '',
3469
+ status: LokotroPaymentStatusInfo.fromString(data.status || ''),
3470
+ requiresOtp: data.requires_otp || false,
3471
+ otpDestination: data.otp_destination,
3472
+ redirectUrl: data.redirect_url
3473
+ };
3474
+ }
3475
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, deps: [{ token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
3476
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, providedIn: 'root' }); }
3922
3477
  }
3923
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroLoadingComponent, decorators: [{
3924
- type: Component,
3925
- args: [{ selector: 'lokotro-loading', standalone: true, imports: [CommonModule], template: `
3926
- <div class="lokotro-loading">
3927
- <div class="lokotro-loading-spinner">
3928
- <div class="lokotro-pulse-ring"></div>
3929
- <div class="lokotro-pulse-ring"></div>
3930
- <div class="lokotro-pulse-dot"></div>
3931
- </div>
3932
- <p class="lokotro-loading-message" *ngIf="message">{{ message }}</p>
3933
- </div>
3934
- `, 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"] }]
3935
- }], propDecorators: { message: [{
3936
- type: Input
3937
- }] } });
3478
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentService, decorators: [{
3479
+ type: Injectable,
3480
+ args: [{
3481
+ providedIn: 'root'
3482
+ }]
3483
+ }], ctorParameters: () => [{ type: LokotroHttpClientService }] });
3938
3484
 
3939
3485
  /**
3940
3486
  * Lokotro Pay - Checkout Component
@@ -4102,7 +3648,7 @@ class LokotroPayCheckoutComponent {
4102
3648
  onBack() {
4103
3649
  const currentScreen = this.currentScreen;
4104
3650
  // Navigate back based on current screen
4105
- if (this.isPaymentFormScreen || currentScreen === LokotroPayScreenNavigation.BankTransferFormScreen) {
3651
+ if (this.isPaymentFormScreen) {
4106
3652
  this.paymentService.navigateToScreen(LokotroPayScreenNavigation.PaymentMethodSelectionScreen);
4107
3653
  }
4108
3654
  else if (currentScreen === LokotroPayScreenNavigation.EWalletOtpScreen) {
@@ -4165,19 +3711,10 @@ class LokotroPayCheckoutComponent {
4165
3711
  *ngIf="isPaymentFormScreen"
4166
3712
  [channel]="state?.selectedPaymentMethod?.channel"
4167
3713
  [transactionId]="state?.transactionId"
4168
- [showUserInfoForm]="state?.paymentInfo?.showUserInfoForm || false"
4169
3714
  (formSubmitted)="onFormSubmitted($event)"
4170
3715
  (cancel)="onCancel()">
4171
3716
  </lokotro-payment-form>
4172
3717
 
4173
- <!-- Bank Transfer Form Screen -->
4174
- <lokotro-bank-transfer-form
4175
- *ngIf="currentScreen === 'bankTransferFormScreen'"
4176
- [showUserInfoForm]="state?.paymentInfo?.showUserInfoForm || false"
4177
- (formSubmitted)="onFormSubmitted($event)"
4178
- (cancel)="onCancel()">
4179
- </lokotro-bank-transfer-form>
4180
-
4181
3718
  <!-- OTP Verification Screen -->
4182
3719
  <lokotro-otp-verification
4183
3720
  *ngIf="currentScreen === 'ewalletOtpScreen'"
@@ -4240,7 +3777,7 @@ class LokotroPayCheckoutComponent {
4240
3777
  </lokotro-result>
4241
3778
  </div>
4242
3779
  </div>
4243
- `, isInline: true, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #1C2621);color:var(--lokotro-text-primary, #F2F0D5);font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-checkout-header{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-glass-light, rgba(255, 255, 255, .1))}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #1C2621;--lokotro-surface: #2A3832;--lokotro-text-primary: #F2F0D5;--lokotro-text-secondary: #D5D3B8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LokotroPaymentMethodSelectionComponent, selector: "lokotro-payment-method-selection", inputs: ["paymentMethods", "selectedMethod"], outputs: ["methodSelected"] }, { kind: "component", type: LokotroPaymentFormComponent, selector: "lokotro-payment-form", inputs: ["channel", "transactionId", "showUserInfoForm"], outputs: ["formSubmitted", "cancel"] }, { kind: "component", type: LokotroBankTransferFormComponent, selector: "lokotro-bank-transfer-form", inputs: ["showUserInfoForm"], outputs: ["formSubmitted", "cancel"] }, { kind: "component", type: LokotroOtpVerificationComponent, selector: "lokotro-otp-verification", inputs: ["transactionId", "otpDestination", "otpLength"], outputs: ["otpVerified", "resendOtp", "cancel"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel"], outputs: ["primaryAction", "secondaryAction"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }] }); }
3780
+ `, isInline: true, styles: [".lokotro-checkout{display:flex;flex-direction:column;min-height:100%;background:var(--lokotro-background, #1C2621);color:var(--lokotro-text-primary, #F2F0D5);font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-checkout-header{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, #3A473F)}.lokotro-title{flex:1;text-align:center;font-size:18px;font-weight:600;margin:0;color:var(--lokotro-text-primary, #F2F0D5)}.lokotro-back-btn,.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-back-btn:hover,.lokotro-close-btn:hover{background:var(--lokotro-glass-light, rgba(255, 255, 255, .1))}.lokotro-checkout-content{flex:1;padding:16px;overflow-y:auto}.lokotro-dark{--lokotro-background: #1C2621;--lokotro-surface: #2A3832;--lokotro-text-primary: #F2F0D5;--lokotro-text-secondary: #D5D3B8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LokotroPaymentMethodSelectionComponent, selector: "lokotro-payment-method-selection", inputs: ["paymentMethods", "selectedMethod"], outputs: ["methodSelected"] }, { kind: "component", type: LokotroPaymentFormComponent, selector: "lokotro-payment-form", inputs: ["channel", "transactionId", "showUserInfoForm"], outputs: ["formSubmitted", "cancel"] }, { kind: "component", type: LokotroOtpVerificationComponent, selector: "lokotro-otp-verification", inputs: ["transactionId", "otpDestination", "otpLength"], outputs: ["otpVerified", "resendOtp", "cancel"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel"], outputs: ["primaryAction", "secondaryAction"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }] }); }
4244
3781
  }
4245
3782
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayCheckoutComponent, decorators: [{
4246
3783
  type: Component,
@@ -4248,7 +3785,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4248
3785
  CommonModule,
4249
3786
  LokotroPaymentMethodSelectionComponent,
4250
3787
  LokotroPaymentFormComponent,
4251
- LokotroBankTransferFormComponent,
4252
3788
  LokotroOtpVerificationComponent,
4253
3789
  LokotroProcessingComponent,
4254
3790
  LokotroResultComponent,
@@ -4291,19 +3827,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4291
3827
  *ngIf="isPaymentFormScreen"
4292
3828
  [channel]="state?.selectedPaymentMethod?.channel"
4293
3829
  [transactionId]="state?.transactionId"
4294
- [showUserInfoForm]="state?.paymentInfo?.showUserInfoForm || false"
4295
3830
  (formSubmitted)="onFormSubmitted($event)"
4296
3831
  (cancel)="onCancel()">
4297
3832
  </lokotro-payment-form>
4298
3833
 
4299
- <!-- Bank Transfer Form Screen -->
4300
- <lokotro-bank-transfer-form
4301
- *ngIf="currentScreen === 'bankTransferFormScreen'"
4302
- [showUserInfoForm]="state?.paymentInfo?.showUserInfoForm || false"
4303
- (formSubmitted)="onFormSubmitted($event)"
4304
- (cancel)="onCancel()">
4305
- </lokotro-bank-transfer-form>
4306
-
4307
3834
  <!-- OTP Verification Screen -->
4308
3835
  <lokotro-otp-verification
4309
3836
  *ngIf="currentScreen === 'ewalletOtpScreen'"
@@ -4444,7 +3971,6 @@ class LokotroPayModule {
4444
3971
  LokotroHttpClientService,
4445
3972
  LokotroPaymentService,
4446
3973
  LokotroLocalizationService,
4447
- LokotroCountryService,
4448
3974
  {
4449
3975
  provide: LOKOTRO_ENV_CONFIG,
4450
3976
  useValue: config?.env ?? null,
@@ -4468,17 +3994,13 @@ class LokotroPayModule {
4468
3994
  LokotroOtpVerificationComponent,
4469
3995
  LokotroProcessingComponent,
4470
3996
  LokotroResultComponent,
4471
- LokotroLoadingComponent,
4472
- LokotroMobileMoneyPhoneInputComponent,
4473
- LokotroBankTransferFormComponent], exports: [LokotroPayCheckoutComponent,
3997
+ LokotroLoadingComponent], exports: [LokotroPayCheckoutComponent,
4474
3998
  LokotroPaymentMethodSelectionComponent,
4475
3999
  LokotroPaymentFormComponent,
4476
4000
  LokotroOtpVerificationComponent,
4477
4001
  LokotroProcessingComponent,
4478
4002
  LokotroResultComponent,
4479
- LokotroLoadingComponent,
4480
- LokotroMobileMoneyPhoneInputComponent,
4481
- LokotroBankTransferFormComponent] }); }
4003
+ LokotroLoadingComponent] }); }
4482
4004
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayModule, imports: [CommonModule,
4483
4005
  HttpClientModule,
4484
4006
  ReactiveFormsModule,
@@ -4490,9 +4012,7 @@ class LokotroPayModule {
4490
4012
  LokotroOtpVerificationComponent,
4491
4013
  LokotroProcessingComponent,
4492
4014
  LokotroResultComponent,
4493
- LokotroLoadingComponent,
4494
- LokotroMobileMoneyPhoneInputComponent,
4495
- LokotroBankTransferFormComponent] }); }
4015
+ LokotroLoadingComponent] }); }
4496
4016
  }
4497
4017
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPayModule, decorators: [{
4498
4018
  type: NgModule,
@@ -4510,8 +4030,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4510
4030
  LokotroProcessingComponent,
4511
4031
  LokotroResultComponent,
4512
4032
  LokotroLoadingComponent,
4513
- LokotroMobileMoneyPhoneInputComponent,
4514
- LokotroBankTransferFormComponent,
4515
4033
  ],
4516
4034
  exports: [
4517
4035
  LokotroPayCheckoutComponent,
@@ -4521,18 +4039,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4521
4039
  LokotroProcessingComponent,
4522
4040
  LokotroResultComponent,
4523
4041
  LokotroLoadingComponent,
4524
- LokotroMobileMoneyPhoneInputComponent,
4525
- LokotroBankTransferFormComponent,
4526
4042
  ],
4527
4043
  }]
4528
4044
  }] });
4529
4045
 
4530
- /**
4531
- * Lokotro Pay - Core Models and Interfaces
4532
- * Angular version of the Flutter Lokotro Pay plugin
4533
- */
4534
- // Re-export country models
4535
-
4536
4046
  /**
4537
4047
  * Lokotro Pay - Color Constants
4538
4048
  * Angular version of the Flutter Lokotro Pay plugin colors
@@ -4710,10 +4220,6 @@ class LokotroPaymentStatusComponent {
4710
4220
  this.currentScreen = 'loading';
4711
4221
  this.paymentDetails = null;
4712
4222
  this.errorMessage = '';
4713
- /** OTP verification state */
4714
- this.otpDestination = '';
4715
- this.rawStatus = '';
4716
- this.isVerifyingOtp = false;
4717
4223
  this.destroy$ = new Subject();
4718
4224
  this.pollingAttempts = 0;
4719
4225
  }
@@ -4790,26 +4296,8 @@ class LokotroPaymentStatusComponent {
4790
4296
  throw error;
4791
4297
  }))
4792
4298
  .subscribe(response => {
4793
- // DEBUG: Always log for troubleshooting
4794
- console.log('[DEBUG] ====== RAW HTTP RESPONSE ======');
4795
- console.log('[DEBUG] response:', response);
4796
- console.log('[DEBUG] response.data:', response.data);
4797
- console.log('[DEBUG] response.data type:', typeof response.data);
4798
- console.log('[DEBUG] response.data keys:', response.data ? Object.keys(response.data) : 'null');
4799
4299
  if (response.isSuccess && response.data) {
4800
- // Unwrap the nested API response structure
4801
- // API returns: { status_code, message, data: { ...actual transaction details } }
4802
- const apiResponse = response.data;
4803
- console.log('[DEBUG] apiResponse:', apiResponse);
4804
- console.log('[DEBUG] apiResponse.data:', apiResponse.data);
4805
- console.log('[DEBUG] apiResponse.status_code:', apiResponse.status_code);
4806
- const transactionData = apiResponse.data || response.data;
4807
- console.log('[DEBUG] ====== TRANSACTION DATA ======');
4808
- console.log('[DEBUG] transactionData:', transactionData);
4809
- console.log('[DEBUG] transactionData.status:', transactionData.status);
4810
- console.log('[DEBUG] transactionData.amount:', transactionData.amount);
4811
- console.log('[DEBUG] transactionData.transactional_amount:', transactionData.transactional_amount);
4812
- this.handleStatusResponse(transactionData);
4300
+ this.handleStatusResponse(response.data);
4813
4301
  }
4814
4302
  else {
4815
4303
  // Sanitize error message from response
@@ -4906,66 +4394,22 @@ class LokotroPaymentStatusComponent {
4906
4394
  handleStatusResponse(data) {
4907
4395
  if (!data)
4908
4396
  return;
4909
- if (LokotroPayEnv.debugMode) {
4910
- console.log('[Lokotro Payment Status] Raw response data:', data);
4911
- }
4912
- // Store raw status for OTP detection
4913
- const statusStr = data.status || '';
4914
- this.rawStatus = statusStr.toLowerCase();
4915
- // Map status string to enum
4916
- const status = LokotroPaymentStatusInfo.fromString(statusStr);
4917
- // Resolve amount (try multiple fields)
4918
- let amount;
4919
- if (data.amount !== undefined && data.amount !== null) {
4920
- amount = typeof data.amount === 'string' ? parseFloat(data.amount) : data.amount;
4921
- }
4922
- else if (data.transactional_amount !== undefined && data.transactional_amount !== null) {
4923
- amount = typeof data.transactional_amount === 'string' ? parseFloat(data.transactional_amount) : data.transactional_amount;
4924
- }
4925
- // Resolve currency
4926
- const currency = data.currency_str || data.transactional_currency || data.currency;
4927
- // Resolve payment ID
4928
- const paymentId = data.identifier || data.payment_id || data.transaction_id || this.statusConfig.paymentId;
4929
- // Get OTP destination if available
4930
- if (data.otp_sent_to) {
4931
- this.otpDestination = data.otp_sent_to;
4932
- }
4933
- else if (data.customer_email) {
4934
- this.otpDestination = data.customer_email;
4935
- }
4936
- console.log('[DEBUG] Mapping response. Redirects:', {
4937
- rawSuccess: data.success_redirect_url,
4938
- rawFail: data.fail_redirect_url,
4939
- rawNotify: data.notify_url
4940
- });
4941
4397
  const statusResponse = {
4942
- code: 0,
4398
+ code: data.code || 0,
4943
4399
  message: data.message || '',
4944
- paymentId: paymentId,
4945
- transactionId: data.identifier || data.transaction_id,
4946
- status: status,
4947
- amount: amount,
4948
- currency: currency,
4400
+ paymentId: data.transaction_id || this.statusConfig.paymentId,
4401
+ transactionId: data.transaction_id,
4402
+ status: LokotroPaymentStatusInfo.fromString(data.status || ''),
4403
+ amount: data.amount ? parseFloat(data.amount) : undefined,
4404
+ currency: data.currency,
4949
4405
  merchantReference: data.merchant_reference,
4950
4406
  customerReference: data.customer_reference,
4951
4407
  completedAt: data.completed_at,
4952
4408
  createdAt: data.created_at,
4953
- metadata: data.metadata,
4954
- // Redirect URLs
4955
- successRedirectUrl: data.success_redirect_url,
4956
- failRedirectUrl: data.fail_redirect_url,
4957
- notifyUrl: data.notify_url
4409
+ metadata: data.metadata
4958
4410
  };
4959
- console.log('[DEBUG] Mapped statusResponse:', statusResponse);
4960
4411
  this.paymentDetails = statusResponse;
4961
4412
  this.statusChange.emit(statusResponse);
4962
- // Check for OTP verification status FIRST (before generic pending handling)
4963
- if (this.rawStatus === 'pending_otp' || this.rawStatus === 'pending_otp_verification') {
4964
- console.log('[Lokotro Payment Status] OTP verification required, showing OTP screen');
4965
- this.currentScreen = 'otp';
4966
- this.stopPolling(); // Stop polling while user enters OTP
4967
- return;
4968
- }
4969
4413
  switch (statusResponse.status) {
4970
4414
  case LokotroPaymentStatus.Pending:
4971
4415
  this.currentScreen = 'pending';
@@ -4973,29 +4417,25 @@ class LokotroPaymentStatusComponent {
4973
4417
  case LokotroPaymentStatus.Processing:
4974
4418
  this.currentScreen = 'processing';
4975
4419
  break;
4976
- case LokotroPaymentStatus.PendingBankProofUpload:
4977
- // Bank transfer pending proof upload - show pending screen
4978
- this.currentScreen = 'pending';
4979
- break;
4980
4420
  case LokotroPaymentStatus.Approved:
4981
- // Emit event and let parent handle screen transition
4982
- // Keep showing processing until parent navigates
4983
- this.currentScreen = 'processing';
4421
+ this.currentScreen = 'success';
4984
4422
  this.paymentComplete.emit(statusResponse);
4985
4423
  this.stopPolling();
4986
4424
  break;
4987
4425
  case LokotroPaymentStatus.Failed:
4988
4426
  case LokotroPaymentStatus.Declined:
4989
- // Emit event and let parent handle screen transition
4427
+ this.currentScreen = 'error';
4990
4428
  this.errorMessage = statusResponse.message;
4991
4429
  this.paymentFailed.emit(statusResponse);
4992
4430
  this.stopPolling();
4993
4431
  break;
4994
4432
  case LokotroPaymentStatus.Cancelled:
4433
+ this.currentScreen = 'cancelled';
4995
4434
  this.paymentFailed.emit(statusResponse);
4996
4435
  this.stopPolling();
4997
4436
  break;
4998
4437
  case LokotroPaymentStatus.Expired:
4438
+ this.currentScreen = 'expired';
4999
4439
  this.paymentFailed.emit(statusResponse);
5000
4440
  this.stopPolling();
5001
4441
  break;
@@ -5037,98 +4477,6 @@ class LokotroPaymentStatusComponent {
5037
4477
  }
5038
4478
  this.onClose.emit();
5039
4479
  }
5040
- /**
5041
- * Handle OTP verification
5042
- */
5043
- onOtpVerified(otp) {
5044
- console.log('[Lokotro Payment Status] Verifying OTP:', otp);
5045
- this.isVerifyingOtp = true;
5046
- this.currentScreen = 'processing';
5047
- const verifyPayload = {
5048
- payment_id: this.statusConfig.paymentId,
5049
- otp_code: otp
5050
- };
5051
- this.httpClient.post(LokotroPayEnv.endpoints.verifyOtp, verifyPayload).subscribe({
5052
- next: (response) => {
5053
- console.log('[Lokotro Payment Status] OTP verify response:', response);
5054
- this.isVerifyingOtp = false;
5055
- if (response.isSuccess && response.data) {
5056
- const apiResponse = response.data;
5057
- const verifyData = apiResponse.data || response.data;
5058
- const verifyStatus = (verifyData.status || '').toLowerCase();
5059
- if (verifyStatus === 'completed' || verifyStatus === 'approved' || verifyStatus === 'success') {
5060
- // Payment successful - emit event and let parent handle screen transition
5061
- // Keep showing processing screen until parent navigates away
5062
- if (this.paymentDetails) {
5063
- this.paymentDetails.status = LokotroPaymentStatus.Approved;
5064
- this.paymentComplete.emit(this.paymentDetails);
5065
- }
5066
- // Don't set currentScreen = 'success' - let parent handle
5067
- }
5068
- else if (verifyStatus === 'failed' || verifyStatus === 'declined') {
5069
- // OTP verification failed - emit event and let parent handle
5070
- this.errorMessage = verifyData.message || this.localization.translate('otpVerificationFailed');
5071
- if (this.paymentDetails) {
5072
- this.paymentDetails.status = LokotroPaymentStatus.Failed;
5073
- this.paymentFailed.emit(this.paymentDetails);
5074
- }
5075
- // Don't set currentScreen = 'error' - let parent handle
5076
- }
5077
- else {
5078
- // Still processing, restart polling
5079
- this.startStatusCheck();
5080
- }
5081
- }
5082
- else {
5083
- this.errorMessage = this.getSanitizedErrorMessage(response);
5084
- if (this.paymentDetails) {
5085
- this.paymentDetails.status = LokotroPaymentStatus.Failed;
5086
- this.paymentFailed.emit(this.paymentDetails);
5087
- }
5088
- }
5089
- },
5090
- error: (error) => {
5091
- console.error('[Lokotro Payment Status] OTP verify error:', error);
5092
- this.isVerifyingOtp = false;
5093
- this.errorMessage = this.getSanitizedErrorMessage(error);
5094
- if (this.paymentDetails) {
5095
- this.paymentDetails.status = LokotroPaymentStatus.Failed;
5096
- this.paymentFailed.emit(this.paymentDetails);
5097
- }
5098
- }
5099
- });
5100
- }
5101
- /**
5102
- * Handle OTP resend
5103
- */
5104
- onResendOtp() {
5105
- console.log('[Lokotro Payment Status] Resending OTP');
5106
- const resendPayload = {
5107
- payment_id: this.statusConfig.paymentId
5108
- };
5109
- this.httpClient.post(LokotroPayEnv.endpoints.resendOtp, resendPayload).subscribe({
5110
- next: (response) => {
5111
- console.log('[Lokotro Payment Status] OTP resend response:', response);
5112
- if (response.isSuccess && response.data) {
5113
- const apiResponse = response.data;
5114
- const resendData = apiResponse.data || response.data;
5115
- if (resendData.otp_sent_to) {
5116
- this.otpDestination = resendData.otp_sent_to;
5117
- }
5118
- }
5119
- },
5120
- error: (error) => {
5121
- console.error('[Lokotro Payment Status] OTP resend error:', error);
5122
- }
5123
- });
5124
- }
5125
- /**
5126
- * Handle OTP cancellation
5127
- */
5128
- onOtpCancel() {
5129
- console.log('[Lokotro Payment Status] OTP cancelled');
5130
- this.onClose.emit();
5131
- }
5132
4480
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentStatusComponent, deps: [{ token: LokotroPaymentService }, { token: LokotroLocalizationService }, { token: LokotroHttpClientService }], target: i0.ɵɵFactoryTarget.Component }); }
5133
4481
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: LokotroPaymentStatusComponent, isStandalone: true, selector: "lokotro-payment-status", inputs: { statusConfig: "statusConfig", showHeader: "showHeader", showCloseButton: "showCloseButton" }, outputs: { statusChange: "statusChange", paymentComplete: "paymentComplete", paymentFailed: "paymentFailed", onClose: "onClose", onDoneEvent: "onDoneEvent" }, ngImport: i0, template: `
5134
4482
  <div class="lokotro-payment-status-container">
@@ -5183,17 +4531,6 @@ class LokotroPaymentStatusComponent {
5183
4531
  [message]="localization.translate('paymentProcessing')">
5184
4532
  </lokotro-processing>
5185
4533
 
5186
- <!-- OTP Verification State -->
5187
- <lokotro-otp-verification
5188
- *ngIf="currentScreen === 'otp'"
5189
- [transactionId]="statusConfig?.paymentId"
5190
- [otpDestination]="otpDestination"
5191
- [otpLength]="6"
5192
- (otpVerified)="onOtpVerified($event)"
5193
- (resendOtp)="onResendOtp()"
5194
- (cancel)="onOtpCancel()">
5195
- </lokotro-otp-verification>
5196
-
5197
4534
  <!-- Success State -->
5198
4535
  <lokotro-result
5199
4536
  *ngIf="currentScreen === 'success'"
@@ -5240,11 +4577,11 @@ class LokotroPaymentStatusComponent {
5240
4577
  </lokotro-result>
5241
4578
  </div>
5242
4579
  </div>
5243
- `, isInline: true, styles: [".lokotro-payment-status-container{min-height:300px;display:flex;flex-direction:column;background:var(--lokotro-background, #1C2621);border-radius:16px;border:1px solid var(--lokotro-border, rgba(59, 251, 218, .1));overflow:hidden;font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-status-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, rgba(59, 251, 218, .1))}.lokotro-status-title{font-size:18px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0}.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-close-btn:hover{background:#ffffff1a}.lokotro-status-content{flex:1;padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}.status-section{text-align:center;width:100%;max-width:400px}.status-icon{width:80px;height:80px;margin:0 auto 24px;display:flex;align-items:center;justify-content:center;border-radius:50%}.pending-icon{background:#fbbf241a;color:#fbbf24}.status-title{font-size:22px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0 0 12px}.status-message{font-size:14px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .7));margin:0 0 24px;line-height:1.5}.payment-info{background:#5a5e3933;border-radius:12px;padding:16px;margin-bottom:24px}.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(242,240,213,.1)}.info-row:last-child{border-bottom:none}.info-label{color:var(--lokotro-text-secondary, rgba(242, 240, 213, .6));font-size:14px}.info-value{color:var(--lokotro-text-primary, #F2F0D5);font-size:14px;font-weight:500}.polling-indicator{display:flex;align-items:center;justify-content:center;gap:8px;font-size:12px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .5))}.pulse{width:8px;height:8px;background:var(--lokotro-primary, #3BFBDA);border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.2)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel"], outputs: ["primaryAction", "secondaryAction"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }, { kind: "component", type: LokotroOtpVerificationComponent, selector: "lokotro-otp-verification", inputs: ["transactionId", "otpDestination", "otpLength"], outputs: ["otpVerified", "resendOtp", "cancel"] }] }); }
4580
+ `, isInline: true, styles: [".lokotro-payment-status-container{min-height:300px;display:flex;flex-direction:column;background:var(--lokotro-background, #1C2621);border-radius:16px;border:1px solid var(--lokotro-border, rgba(59, 251, 218, .1));overflow:hidden;font-family:var(--lokotro-font-family, \"Comfortaa\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.lokotro-status-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;background:var(--lokotro-surface, #2A3832);border-bottom:1px solid var(--lokotro-border, rgba(59, 251, 218, .1))}.lokotro-status-title{font-size:18px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0}.lokotro-close-btn{background:none;border:none;padding:8px;cursor:pointer;color:var(--lokotro-text-primary, #F2F0D5);border-radius:8px;transition:background-color .2s}.lokotro-close-btn:hover{background:#ffffff1a}.lokotro-status-content{flex:1;padding:24px;display:flex;flex-direction:column;align-items:center;justify-content:center}.status-section{text-align:center;width:100%;max-width:400px}.status-icon{width:80px;height:80px;margin:0 auto 24px;display:flex;align-items:center;justify-content:center;border-radius:50%}.pending-icon{background:#fbbf241a;color:#fbbf24}.status-title{font-size:22px;font-weight:600;color:var(--lokotro-text-primary, #F2F0D5);margin:0 0 12px}.status-message{font-size:14px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .7));margin:0 0 24px;line-height:1.5}.payment-info{background:#5a5e3933;border-radius:12px;padding:16px;margin-bottom:24px}.info-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(242,240,213,.1)}.info-row:last-child{border-bottom:none}.info-label{color:var(--lokotro-text-secondary, rgba(242, 240, 213, .6));font-size:14px}.info-value{color:var(--lokotro-text-primary, #F2F0D5);font-size:14px;font-weight:500}.polling-indicator{display:flex;align-items:center;justify-content:center;gap:8px;font-size:12px;color:var(--lokotro-text-secondary, rgba(242, 240, 213, .5))}.pulse{width:8px;height:8px;background:var(--lokotro-primary, #3BFBDA);border-radius:50%;animation:pulse 1.5s ease-in-out infinite}@keyframes pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(1.2)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: LokotroLoadingComponent, selector: "lokotro-loading", inputs: ["message"] }, { kind: "component", type: LokotroResultComponent, selector: "lokotro-result", inputs: ["type", "title", "message", "amount", "currency", "transactionId", "primaryActionLabel", "secondaryActionLabel"], outputs: ["primaryAction", "secondaryAction"] }, { kind: "component", type: LokotroProcessingComponent, selector: "lokotro-processing", inputs: ["type", "message"] }] }); }
5244
4581
  }
5245
4582
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: LokotroPaymentStatusComponent, decorators: [{
5246
4583
  type: Component,
5247
- args: [{ selector: 'lokotro-payment-status', standalone: true, imports: [CommonModule, LokotroLoadingComponent, LokotroResultComponent, LokotroProcessingComponent, LokotroOtpVerificationComponent], template: `
4584
+ args: [{ selector: 'lokotro-payment-status', standalone: true, imports: [CommonModule, LokotroLoadingComponent, LokotroResultComponent, LokotroProcessingComponent], template: `
5248
4585
  <div class="lokotro-payment-status-container">
5249
4586
  <!-- Header -->
5250
4587
  <div class="lokotro-status-header" *ngIf="showHeader">
@@ -5297,17 +4634,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
5297
4634
  [message]="localization.translate('paymentProcessing')">
5298
4635
  </lokotro-processing>
5299
4636
 
5300
- <!-- OTP Verification State -->
5301
- <lokotro-otp-verification
5302
- *ngIf="currentScreen === 'otp'"
5303
- [transactionId]="statusConfig?.paymentId"
5304
- [otpDestination]="otpDestination"
5305
- [otpLength]="6"
5306
- (otpVerified)="onOtpVerified($event)"
5307
- (resendOtp)="onResendOtp()"
5308
- (cancel)="onOtpCancel()">
5309
- </lokotro-otp-verification>
5310
-
5311
4637
  <!-- Success State -->
5312
4638
  <lokotro-result
5313
4639
  *ngIf="currentScreen === 'success'"
@@ -5383,5 +4709,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
5383
4709
  * Generated bundle index. Do not edit.
5384
4710
  */
5385
4711
 
5386
- export { LOKOTRO_ENV_CONFIG, LOKOTRO_PAY_CONFIG, LokotroBankTransferFormComponent, LokotroCountryService, LokotroCountryUtils, LokotroHttpClientService, LokotroLoadingComponent, LokotroLocalizationService, LokotroMobileMoneyPhoneInputComponent, LokotroOtpVerificationComponent, LokotroPayApiResponseCode, LokotroPayApiResponseCodeInfo, LokotroPayChannel, LokotroPayChannelInfo, LokotroPayCheckoutComponent, LokotroPayColors, LokotroPayEnv, LokotroPayFillingInfo, LokotroPayLanguage, LokotroPayLanguageInfo, LokotroPayModule, LokotroPayResultScreen, LokotroPayScreenNavigation, LokotroPayScreenNavigationInfo, LokotroPaymentFormComponent, LokotroPaymentMethodSelectionComponent, LokotroPaymentService, LokotroPaymentStatus, LokotroPaymentStatusComponent, LokotroProcessingComponent, LokotroResultComponent };
4712
+ export { LOKOTRO_ENV_CONFIG, LOKOTRO_PAY_CONFIG, LokotroHttpClientService, LokotroLoadingComponent, LokotroLocalizationService, LokotroOtpVerificationComponent, LokotroPayApiResponseCode, LokotroPayApiResponseCodeInfo, LokotroPayChannel, LokotroPayChannelInfo, LokotroPayCheckoutComponent, LokotroPayColors, LokotroPayEnv, LokotroPayFillingInfo, LokotroPayLanguage, LokotroPayLanguageInfo, LokotroPayModule, LokotroPayResultScreen, LokotroPayScreenNavigation, LokotroPayScreenNavigationInfo, LokotroPaymentFormComponent, LokotroPaymentMethodSelectionComponent, LokotroPaymentService, LokotroPaymentStatus, LokotroPaymentStatusComponent, LokotroProcessingComponent, LokotroResultComponent };
5387
4713
  //# sourceMappingURL=bloonio-lokotro-pay.mjs.map