react-amwal-pay 0.1.16 → 0.1.18

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.
@@ -5,17 +5,21 @@ import {
5
5
  type AmwalPayConfig,
6
6
  } from './index';
7
7
  import NetworkClient from './network/NetworkClient';
8
+ import Logger from './utils/Logger';
8
9
  import { type EventSubscription } from 'react-native';
9
10
 
10
11
  class AmwalPaySDK {
11
12
  private static instance: AmwalPaySDK;
13
+ private logger: Logger;
12
14
 
13
15
  private onResponseSubscription: EventSubscription | null = null;
14
16
 
15
17
  private onCustomerIdSubscription: EventSubscription | null = null;
16
18
 
17
19
  private constructor() {
18
- // Initialize the event emitter
20
+ // Initialize the logger
21
+ this.logger = Logger.getInstance();
22
+ this.logger.info('AmwalPaySDK', 'SDK instance created');
19
23
  }
20
24
 
21
25
  static getInstance(): AmwalPaySDK {
@@ -25,6 +29,32 @@ class AmwalPaySDK {
25
29
  return AmwalPaySDK.instance;
26
30
  }
27
31
 
32
+ /**
33
+ * Enable or disable debug logging
34
+ */
35
+ setDebugEnabled(enabled: boolean): void {
36
+ this.logger.setDebugEnabled(enabled);
37
+ this.logger.info(
38
+ 'AmwalPaySDK',
39
+ `Debug logging ${enabled ? 'enabled' : 'disabled'}`
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Get SDK logs for debugging purposes
45
+ */
46
+ getLogs(): string {
47
+ return this.logger.exportLogs();
48
+ }
49
+
50
+ /**
51
+ * Clear all SDK logs
52
+ */
53
+ clearLogs(): void {
54
+ this.logger.clearLogs();
55
+ this.logger.info('AmwalPaySDK', 'Logs cleared');
56
+ }
57
+
28
58
  /**
29
59
  * Initiates the payment process by first fetching a session token and then starting the payment flow
30
60
  * @param config The payment configuration
@@ -33,6 +63,12 @@ class AmwalPaySDK {
33
63
  config: Omit<AmwalPayConfig, 'sessionToken'>
34
64
  ): Promise<void> {
35
65
  try {
66
+ this.logger.info('AmwalPaySDK', 'Starting payment process', {
67
+ merchantId: config.merchantId,
68
+ customerId: config.customerId,
69
+ environment: config.environment,
70
+ });
71
+
36
72
  // Set up event listeners before starting the payment process
37
73
  this.setupEventListeners(config);
38
74
 
@@ -40,10 +76,11 @@ class AmwalPaySDK {
40
76
  const networkClient = NetworkClient.getInstance();
41
77
 
42
78
  // Fetch session token
43
- console.log(
44
- 'Fetching session token for environment:',
45
- config.environment
46
- );
79
+ this.logger.debug('AmwalPaySDK', 'Fetching session token', {
80
+ environment: config.environment,
81
+ merchantId: config.merchantId,
82
+ });
83
+
47
84
  const sessionToken = await networkClient.fetchSessionToken(
48
85
  config.environment,
49
86
  config.merchantId,
@@ -51,13 +88,13 @@ class AmwalPaySDK {
51
88
  config.secureHash
52
89
  );
53
90
 
54
- console.log('Session token result:', sessionToken ? 'Success' : 'Failed');
55
-
56
91
  if (!sessionToken) {
57
- // If session token is null, the error has already been shown by NetworkClient
92
+ this.logger.error('AmwalPaySDK', 'Failed to fetch session token');
58
93
  return;
59
94
  }
60
95
 
96
+ this.logger.info('AmwalPaySDK', 'Session token fetched successfully');
97
+
61
98
  // Create complete config with session token
62
99
  const completeConfig: AmwalPayConfig = {
63
100
  ...config,
@@ -65,17 +102,21 @@ class AmwalPaySDK {
65
102
  };
66
103
 
67
104
  // Initiate the payment process
68
- console.log(
69
- 'Initiating native payment with config:',
70
- JSON.stringify(completeConfig)
105
+ this.logger.debug(
106
+ 'AmwalPaySDK',
107
+ 'Initiating native payment',
108
+ completeConfig
71
109
  );
72
110
  initiate(completeConfig);
111
+
112
+ this.logger.info('AmwalPaySDK', 'Payment process initiated successfully');
73
113
  } catch (error) {
74
- console.error('Error starting payment:', error);
114
+ this.logger.error('AmwalPaySDK', 'Error starting payment', error);
75
115
  }
76
116
  }
77
117
 
78
118
  dispose(): void {
119
+ this.logger.info('AmwalPaySDK', 'Disposing SDK instance');
79
120
  // Remove all event listeners
80
121
  this.removeEventListeners();
81
122
  }
@@ -90,43 +131,46 @@ class AmwalPaySDK {
90
131
  // Remove any existing listeners
91
132
  this.removeEventListeners();
92
133
 
93
- console.log('🟢 Setting up event listeners...');
94
- console.log(
95
- '🟢 onResponse callback exists?',
96
- typeof config.onResponse === 'function'
97
- );
98
- console.log(
99
- '🟢 onCustomerId callback exists?',
100
- typeof config.onCustomerId === 'function'
101
- );
134
+ this.logger.debug('AmwalPaySDK', 'Setting up event listeners', {
135
+ hasOnResponse: typeof config.onResponse === 'function',
136
+ hasOnCustomerId: typeof config.onCustomerId === 'function',
137
+ });
102
138
 
103
139
  this.onResponseSubscription = onResponse((response) => {
104
- console.log('🟢 SDK onResponse listener triggered with:', response);
105
- console.log('Received AmwalPayResponse:', response);
140
+ this.logger.info('AmwalPaySDK', 'Received payment response', response);
106
141
  if (config.onResponse) {
107
142
  config.onResponse(response);
108
143
  } else {
109
- console.error('❌ config.onResponse is not a function!');
144
+ this.logger.error(
145
+ 'AmwalPaySDK',
146
+ 'onResponse callback is not a function'
147
+ );
110
148
  }
111
149
  });
112
150
 
113
151
  this.onCustomerIdSubscription = onCustomerId((customerId) => {
114
- console.log('🟢 SDK onCustomerId listener triggered with:', customerId);
115
- console.log('Received customerId:', customerId);
152
+ this.logger.info('AmwalPaySDK', 'Received customer ID', { customerId });
116
153
  if (config.onCustomerId) {
117
154
  config.onCustomerId(customerId);
118
155
  } else {
119
- console.error('❌ config.onCustomerId is not a function!');
156
+ this.logger.error(
157
+ 'AmwalPaySDK',
158
+ 'onCustomerId callback is not a function'
159
+ );
120
160
  }
121
161
  });
122
162
 
123
- console.log('🟢 Event listeners set up complete');
163
+ this.logger.debug('AmwalPaySDK', 'Event listeners setup completed');
124
164
  }
125
165
 
126
166
  /**
127
167
  * Removes all event listeners
128
168
  */
129
169
  private removeEventListeners(): void {
170
+ if (this.onResponseSubscription || this.onCustomerIdSubscription) {
171
+ this.logger.debug('AmwalPaySDK', 'Removing event listeners');
172
+ }
173
+
130
174
  this.onResponseSubscription?.remove();
131
175
  this.onCustomerIdSubscription?.remove();
132
176
  this.onResponseSubscription = null;
package/src/index.tsx CHANGED
@@ -8,6 +8,7 @@ import ReactAmwalPay, {
8
8
  } from './NativeReactAmwalPay';
9
9
  import AmwalPaySDK from './AmwalPaySDK';
10
10
  import { UuidUtil } from './utils/UuidUtil';
11
+ import Logger, { LogLevel, type LogEntry } from './utils/Logger';
11
12
  import { NativeEventEmitter, type EventSubscription } from 'react-native';
12
13
 
13
14
  // Create an event emitter for the native module
@@ -63,4 +64,7 @@ export {
63
64
  type AmwalPayConfig,
64
65
  AmwalPaySDK,
65
66
  UuidUtil,
67
+ Logger,
68
+ LogLevel,
69
+ type LogEntry,
66
70
  };
@@ -1,11 +1,15 @@
1
1
  import { Alert } from 'react-native';
2
2
  import { Environment } from '../NativeReactAmwalPay';
3
3
  import SecureHashUtil from '../utils/SecureHashUtil';
4
+ import Logger from '../utils/Logger';
4
5
 
5
6
  class NetworkClient {
6
7
  private static instance: NetworkClient;
8
+ private logger: Logger;
7
9
 
8
- private constructor() {}
10
+ private constructor() {
11
+ this.logger = Logger.getInstance();
12
+ }
9
13
 
10
14
  static getInstance(): NetworkClient {
11
15
  if (!NetworkClient.instance) {
@@ -15,16 +19,19 @@ class NetworkClient {
15
19
  }
16
20
 
17
21
  private getWebhookUrl(env: Environment): string {
18
- switch (env) {
19
- case Environment.SIT:
20
- return 'https://test.amwalpg.com:24443/';
21
- case Environment.UAT:
22
- return 'https://test.amwalpg.com:14443/';
23
- case Environment.PROD:
24
- return 'https://webhook.amwalpg.com/';
25
- default:
26
- return 'https://test.amwalpg.com:24443/';
27
- }
22
+ const urls = {
23
+ [Environment.SIT]: 'https://test.amwalpg.com:24443/',
24
+ [Environment.UAT]: 'https://test.amwalpg.com:14443/',
25
+ [Environment.PROD]: 'https://webhook.amwalpg.com/',
26
+ };
27
+
28
+ const url = urls[env] || urls[Environment.SIT];
29
+ this.logger.debug(
30
+ 'NetworkClient',
31
+ `Using webhook URL for ${Environment[env]}`,
32
+ { url }
33
+ );
34
+ return url;
28
35
  }
29
36
 
30
37
  async fetchSessionToken(
@@ -34,6 +41,12 @@ class NetworkClient {
34
41
  secureHashValue: string
35
42
  ): Promise<string | null> {
36
43
  try {
44
+ this.logger.info('NetworkClient', 'Fetching session token', {
45
+ environment: Environment[env],
46
+ merchantId,
47
+ hasCustomerId: !!customerId,
48
+ });
49
+
37
50
  const webhookUrl = this.getWebhookUrl(env);
38
51
 
39
52
  const dataMap = {
@@ -46,6 +59,11 @@ class NetworkClient {
46
59
  dataMap
47
60
  );
48
61
 
62
+ this.logger.debug('NetworkClient', 'Making API request', {
63
+ url: `${webhookUrl}Membership/GetSDKSessionToken`,
64
+ method: 'POST',
65
+ });
66
+
49
67
  const response = await fetch(
50
68
  `${webhookUrl}Membership/GetSDKSessionToken`,
51
69
  {
@@ -65,21 +83,35 @@ class NetworkClient {
65
83
 
66
84
  const responseData = await response.json();
67
85
 
86
+ this.logger.debug('NetworkClient', 'API response received', {
87
+ status: response.status,
88
+ ok: response.ok,
89
+ success: responseData.success,
90
+ });
91
+
68
92
  if (response.ok && responseData.success) {
93
+ this.logger.info('NetworkClient', 'Session token fetched successfully');
69
94
  return responseData.data.sessionToken;
70
95
  } else {
71
96
  const errorMessage =
72
97
  responseData.errorList?.join(',') || 'Unknown error';
98
+ this.logger.error('NetworkClient', 'API request failed', {
99
+ status: response.status,
100
+ errorMessage,
101
+ responseData,
102
+ });
73
103
  this.showErrorDialog(errorMessage);
74
104
  return null;
75
105
  }
76
106
  } catch (error) {
107
+ this.logger.error('NetworkClient', 'Network request failed', error);
77
108
  this.showErrorDialog('Something Went Wrong');
78
109
  return null;
79
110
  }
80
111
  }
81
112
 
82
113
  private showErrorDialog(message: string): void {
114
+ this.logger.warn('NetworkClient', 'Showing error dialog', { message });
83
115
  Alert.alert('Error', message, [{ text: 'OK' }]);
84
116
  }
85
117
  }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Logger utility for AmwalPay SDK
3
+ * Provides structured logging with different levels and optional debug mode
4
+ */
5
+
6
+ export enum LogLevel {
7
+ DEBUG = 0,
8
+ INFO = 1,
9
+ WARN = 2,
10
+ ERROR = 3,
11
+ }
12
+
13
+ export interface LogEntry {
14
+ timestamp: string;
15
+ level: LogLevel;
16
+ tag: string;
17
+ message: string;
18
+ data?: any;
19
+ }
20
+
21
+ class Logger {
22
+ private static instance: Logger;
23
+ private isDebugEnabled: boolean = __DEV__;
24
+ private logLevel: LogLevel = LogLevel.INFO;
25
+ private logs: LogEntry[] = [];
26
+ private maxLogEntries: number = 1000;
27
+
28
+ private constructor() {}
29
+
30
+ static getInstance(): Logger {
31
+ if (!Logger.instance) {
32
+ Logger.instance = new Logger();
33
+ }
34
+ return Logger.instance;
35
+ }
36
+
37
+ /**
38
+ * Enable or disable debug logging
39
+ */
40
+ setDebugEnabled(enabled: boolean): void {
41
+ this.isDebugEnabled = enabled;
42
+ }
43
+
44
+ /**
45
+ * Set minimum log level
46
+ */
47
+ setLogLevel(level: LogLevel): void {
48
+ this.logLevel = level;
49
+ }
50
+
51
+ /**
52
+ * Log debug message
53
+ */
54
+ debug(tag: string, message: string, data?: any): void {
55
+ this.log(LogLevel.DEBUG, tag, message, data);
56
+ }
57
+
58
+ /**
59
+ * Log info message
60
+ */
61
+ info(tag: string, message: string, data?: any): void {
62
+ this.log(LogLevel.INFO, tag, message, data);
63
+ }
64
+
65
+ /**
66
+ * Log warning message
67
+ */
68
+ warn(tag: string, message: string, data?: any): void {
69
+ this.log(LogLevel.WARN, tag, message, data);
70
+ }
71
+
72
+ /**
73
+ * Log error message
74
+ */
75
+ error(tag: string, message: string, data?: any): void {
76
+ this.log(LogLevel.ERROR, tag, message, data);
77
+ }
78
+
79
+ /**
80
+ * Internal logging method
81
+ */
82
+ private log(level: LogLevel, tag: string, message: string, data?: any): void {
83
+ if (level < this.logLevel) {
84
+ return;
85
+ }
86
+
87
+ const timestamp = new Date().toISOString();
88
+ const logEntry: LogEntry = {
89
+ timestamp,
90
+ level,
91
+ tag,
92
+ message,
93
+ data,
94
+ };
95
+
96
+ // Store log entry
97
+ this.logs.push(logEntry);
98
+
99
+ // Maintain max log entries
100
+ if (this.logs.length > this.maxLogEntries) {
101
+ this.logs.shift();
102
+ }
103
+
104
+ // Console output
105
+ if (this.isDebugEnabled || level >= LogLevel.WARN) {
106
+ const levelStr = LogLevel[level];
107
+ const prefix = `[AmwalPay][${levelStr}][${tag}]`;
108
+
109
+ switch (level) {
110
+ case LogLevel.DEBUG:
111
+ console.debug(prefix, message, data || '');
112
+ break;
113
+ case LogLevel.INFO:
114
+ console.info(prefix, message, data || '');
115
+ break;
116
+ case LogLevel.WARN:
117
+ console.warn(prefix, message, data || '');
118
+ break;
119
+ case LogLevel.ERROR:
120
+ console.error(prefix, message, data || '');
121
+ break;
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Get all stored logs
128
+ */
129
+ getLogs(): LogEntry[] {
130
+ return [...this.logs];
131
+ }
132
+
133
+ /**
134
+ * Get logs filtered by level
135
+ */
136
+ getLogsByLevel(level: LogLevel): LogEntry[] {
137
+ return this.logs.filter((log) => log.level === level);
138
+ }
139
+
140
+ /**
141
+ * Get logs filtered by tag
142
+ */
143
+ getLogsByTag(tag: string): LogEntry[] {
144
+ return this.logs.filter((log) => log.tag === tag);
145
+ }
146
+
147
+ /**
148
+ * Clear all stored logs
149
+ */
150
+ clearLogs(): void {
151
+ this.logs = [];
152
+ }
153
+
154
+ /**
155
+ * Export logs as JSON string
156
+ */
157
+ exportLogs(): string {
158
+ return JSON.stringify(this.logs, null, 2);
159
+ }
160
+ }
161
+
162
+ export default Logger;