@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.
- package/fesm2022/bloonio-lokotro-pay.mjs +519 -1193
- package/fesm2022/bloonio-lokotro-pay.mjs.map +1 -1
- package/index.d.ts +6 -269
- package/package.json +1 -1
|
@@ -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 -
|
|
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
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
this.
|
|
2580
|
-
this.
|
|
2581
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
this.stateSubject.next(initialState);
|
|
2571
|
+
ngOnDestroy() {
|
|
2572
|
+
if (this.timerInterval) {
|
|
2573
|
+
clearInterval(this.timerInterval);
|
|
2574
|
+
}
|
|
2603
2575
|
}
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
*/
|
|
2607
|
-
setAppKey(appKey) {
|
|
2608
|
-
this.httpClient.setAppKey(appKey);
|
|
2576
|
+
get isOtpComplete() {
|
|
2577
|
+
return this.otpDigits.every(digit => digit !== '');
|
|
2609
2578
|
}
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
*/
|
|
2613
|
-
setAcceptLanguage(language) {
|
|
2614
|
-
this.httpClient.setAcceptLanguage(language);
|
|
2579
|
+
get otpValue() {
|
|
2580
|
+
return this.otpDigits.join('');
|
|
2615
2581
|
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
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
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
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
|
-
|
|
2728
|
-
|
|
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
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
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
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
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
|
-
|
|
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:
|
|
3924
|
-
type:
|
|
3925
|
-
args: [{
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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"] }
|
|
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
|
|
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,
|
|
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
|