react-native-amwal-ecr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +20 -0
  3. package/README.md +278 -0
  4. package/RNAmwalEcr.podspec +27 -0
  5. package/android/build.gradle +53 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/java/com/amwalecr/AmwalEcrModule.kt +325 -0
  8. package/android/src/main/java/com/amwalecr/AmwalEcrPackage.kt +31 -0
  9. package/android/src/main/java/com/amwalecr/EcrMapping.kt +135 -0
  10. package/docs/compatibility.md +22 -0
  11. package/ios/AmwalEcr.h +4 -0
  12. package/ios/AmwalEcr.mm +121 -0
  13. package/ios/AmwalEcr.swift +424 -0
  14. package/ios/EcrMapping.swift +142 -0
  15. package/lib/module/EcrOperation.js +18 -0
  16. package/lib/module/EcrOperation.js.map +1 -0
  17. package/lib/module/EcrTerminal.js +211 -0
  18. package/lib/module/EcrTerminal.js.map +1 -0
  19. package/lib/module/NativeAmwalEcr.js +5 -0
  20. package/lib/module/NativeAmwalEcr.js.map +1 -0
  21. package/lib/module/index.js +6 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/module/native.js +16 -0
  24. package/lib/module/native.js.map +1 -0
  25. package/lib/module/native.native.js +4 -0
  26. package/lib/module/native.native.js.map +1 -0
  27. package/lib/module/normalize.js +87 -0
  28. package/lib/module/normalize.js.map +1 -0
  29. package/lib/module/package.json +1 -0
  30. package/lib/module/types.js +2 -0
  31. package/lib/module/types.js.map +1 -0
  32. package/lib/typescript/package.json +1 -0
  33. package/lib/typescript/src/EcrOperation.d.ts +10 -0
  34. package/lib/typescript/src/EcrOperation.d.ts.map +1 -0
  35. package/lib/typescript/src/EcrTerminal.d.ts +34 -0
  36. package/lib/typescript/src/EcrTerminal.d.ts.map +1 -0
  37. package/lib/typescript/src/NativeAmwalEcr.d.ts +14 -0
  38. package/lib/typescript/src/NativeAmwalEcr.d.ts.map +1 -0
  39. package/lib/typescript/src/index.d.ts +5 -0
  40. package/lib/typescript/src/index.d.ts.map +1 -0
  41. package/lib/typescript/src/native.d.ts +3 -0
  42. package/lib/typescript/src/native.d.ts.map +1 -0
  43. package/lib/typescript/src/native.native.d.ts +2 -0
  44. package/lib/typescript/src/native.native.d.ts.map +1 -0
  45. package/lib/typescript/src/normalize.d.ts +5 -0
  46. package/lib/typescript/src/normalize.d.ts.map +1 -0
  47. package/lib/typescript/src/types.d.ts +157 -0
  48. package/lib/typescript/src/types.d.ts.map +1 -0
  49. package/package.json +153 -0
  50. package/src/EcrOperation.ts +25 -0
  51. package/src/EcrTerminal.ts +344 -0
  52. package/src/NativeAmwalEcr.ts +28 -0
  53. package/src/index.tsx +34 -0
  54. package/src/native.native.ts +1 -0
  55. package/src/native.ts +18 -0
  56. package/src/normalize.ts +116 -0
  57. package/src/types.ts +189 -0
@@ -0,0 +1,28 @@
1
+ import {
2
+ TurboModuleRegistry,
3
+ type CodegenTypes,
4
+ type TurboModule,
5
+ } from 'react-native';
6
+
7
+ export interface Spec extends TurboModule {
8
+ isReachable(request: CodegenTypes.UnsafeObject): Promise<boolean>;
9
+ sale(request: CodegenTypes.UnsafeObject): Promise<CodegenTypes.UnsafeObject>;
10
+ voidTransaction(
11
+ request: CodegenTypes.UnsafeObject
12
+ ): Promise<CodegenTypes.UnsafeObject>;
13
+ refund(
14
+ request: CodegenTypes.UnsafeObject
15
+ ): Promise<CodegenTypes.UnsafeObject>;
16
+ inquire(
17
+ request: CodegenTypes.UnsafeObject
18
+ ): Promise<CodegenTypes.UnsafeObject>;
19
+ inquireByReference(
20
+ request: CodegenTypes.UnsafeObject
21
+ ): Promise<CodegenTypes.UnsafeObject>;
22
+ receipt(
23
+ request: CodegenTypes.UnsafeObject
24
+ ): Promise<CodegenTypes.UnsafeObject>;
25
+ cancel(operationId: string): Promise<boolean>;
26
+ }
27
+
28
+ export default TurboModuleRegistry.getEnforcing<Spec>('AmwalEcr');
package/src/index.tsx ADDED
@@ -0,0 +1,34 @@
1
+ export { EcrOperation } from './EcrOperation';
2
+ export { EcrTerminal } from './EcrTerminal';
3
+ export {
4
+ normalizeInquiry,
5
+ normalizeReceipt,
6
+ normalizeResult,
7
+ } from './normalize';
8
+ export type {
9
+ EcrApproved,
10
+ EcrConfig,
11
+ EcrDatedTransactionOptions,
12
+ EcrDeclined,
13
+ EcrEnvironment,
14
+ EcrFailed,
15
+ EcrFailure,
16
+ EcrFailureKind,
17
+ EcrInquiry,
18
+ EcrInquiryByReferenceOptions,
19
+ EcrInquiryFailed,
20
+ EcrInquiryFound,
21
+ EcrInquiryNotFound,
22
+ EcrNextStep,
23
+ EcrOriginalTransactionOptions,
24
+ EcrReceipt,
25
+ EcrReceiptFailed,
26
+ EcrReceiptReady,
27
+ EcrReceiptUnavailable,
28
+ EcrReferenceOptions,
29
+ EcrRefundOptions,
30
+ EcrResult,
31
+ EcrTerminalOptions,
32
+ EcrTransaction,
33
+ EcrTransport,
34
+ } from './types';
@@ -0,0 +1 @@
1
+ export { default as nativeEcr } from './NativeAmwalEcr';
package/src/native.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type NativeAmwalEcr from './NativeAmwalEcr';
2
+
3
+ const unsupported = (): never => {
4
+ throw new Error(
5
+ "'react-native-amwal-ecr' is only supported on Android and iOS."
6
+ );
7
+ };
8
+
9
+ export const nativeEcr: typeof NativeAmwalEcr = {
10
+ isReachable: async () => unsupported(),
11
+ sale: async () => unsupported(),
12
+ voidTransaction: async () => unsupported(),
13
+ refund: async () => unsupported(),
14
+ inquire: async () => unsupported(),
15
+ inquireByReference: async () => unsupported(),
16
+ receipt: async () => unsupported(),
17
+ cancel: async () => unsupported(),
18
+ };
@@ -0,0 +1,116 @@
1
+ import type {
2
+ EcrFailure,
3
+ EcrFailureKind,
4
+ EcrInquiry,
5
+ EcrReceipt,
6
+ EcrResult,
7
+ } from './types';
8
+
9
+ type NativeRecord = Record<string, unknown>;
10
+
11
+ const knownFailures = new Set<EcrFailureKind>([
12
+ 'unreachable',
13
+ 'timeout',
14
+ 'malformed',
15
+ 'connectionLost',
16
+ 'unauthenticated',
17
+ 'cancelled',
18
+ 'unsupported',
19
+ ]);
20
+
21
+ function record(value: unknown, label: string): NativeRecord {
22
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
23
+ throw new Error(`The native ECR host returned an invalid ${label}.`);
24
+ }
25
+ return value as NativeRecord;
26
+ }
27
+
28
+ function failure(value: unknown): EcrFailure {
29
+ const raw = record(value, 'failure');
30
+ const rawKind = String(raw.kind ?? 'malformed');
31
+ const kind = knownFailures.has(rawKind as EcrFailureKind)
32
+ ? (rawKind as EcrFailureKind)
33
+ : 'malformed';
34
+ return {
35
+ kind,
36
+ message: String(
37
+ raw.message ?? 'The native ECR host did not explain the failure.'
38
+ ),
39
+ outcomeIsUnknown: kind !== 'unreachable' && kind !== 'unsupported',
40
+ };
41
+ }
42
+
43
+ export function normalizeInquiry(value: unknown): EcrInquiry {
44
+ const raw = record(value, 'inquiry');
45
+ switch (raw.outcome) {
46
+ case 'found':
47
+ case 'notFound':
48
+ return raw as unknown as EcrInquiry;
49
+ case 'failed':
50
+ return { ...raw, failure: failure(raw.failure) } as EcrInquiry;
51
+ default:
52
+ throw new Error(
53
+ 'The native ECR host returned an unknown inquiry outcome.'
54
+ );
55
+ }
56
+ }
57
+
58
+ export function normalizeReceipt(value: unknown): EcrReceipt {
59
+ const raw = record(value, 'receipt');
60
+ switch (raw.outcome) {
61
+ case 'ready':
62
+ case 'unavailable':
63
+ return raw as unknown as EcrReceipt;
64
+ case 'failed':
65
+ return { ...raw, failure: failure(raw.failure) } as EcrReceipt;
66
+ default:
67
+ throw new Error(
68
+ 'The native ECR host returned an unknown receipt outcome.'
69
+ );
70
+ }
71
+ }
72
+
73
+ export function normalizeResult(value: unknown): EcrResult {
74
+ const raw = record(value, 'transaction result');
75
+ switch (raw.outcome) {
76
+ case 'approved':
77
+ return {
78
+ ...raw,
79
+ outcomeIsUnknown: false,
80
+ nextStep: 'NONE',
81
+ } as unknown as EcrResult;
82
+ case 'declined': {
83
+ const nextStep =
84
+ raw.nextStep === 'INQUIRE_BY_MERCHANT_REFERENCE'
85
+ ? 'INQUIRE_BY_MERCHANT_REFERENCE'
86
+ : 'NONE';
87
+ return {
88
+ ...raw,
89
+ nextStep,
90
+ outcomeIsUnknown:
91
+ raw.responseCode === '91' ||
92
+ nextStep === 'INQUIRE_BY_MERCHANT_REFERENCE',
93
+ } as unknown as EcrResult;
94
+ }
95
+ case 'failed': {
96
+ const recovered = raw.recovered
97
+ ? normalizeInquiry(raw.recovered)
98
+ : undefined;
99
+ const mappedFailure = failure(raw.failure);
100
+ const settled = recovered?.outcome === 'found';
101
+ const outcomeIsUnknown = mappedFailure.outcomeIsUnknown && !settled;
102
+ return {
103
+ ...raw,
104
+ failure: mappedFailure,
105
+ recovered,
106
+ settled,
107
+ outcomeIsUnknown,
108
+ nextStep: outcomeIsUnknown ? 'INQUIRE_BY_MERCHANT_REFERENCE' : 'NONE',
109
+ } as unknown as EcrResult;
110
+ }
111
+ default:
112
+ throw new Error(
113
+ 'The native ECR host returned an unknown transaction outcome.'
114
+ );
115
+ }
116
+ }
package/src/types.ts ADDED
@@ -0,0 +1,189 @@
1
+ /**
2
+ * How the till reaches the terminal.
3
+ *
4
+ * `wifi` and `ethernet` open a TCP socket to the terminal's local IP.
5
+ * `webService` drives the terminal through the Amwal payment host over REST —
6
+ * no local network involved; it needs `merchantId`, `terminalId` and
7
+ * `environment` in the config instead of a host. `usbCable` and `bluetooth`
8
+ * are not handled by this library and answer with a typed `unsupported`
9
+ * failure without sending anything.
10
+ */
11
+ export type EcrTransport =
12
+ 'ethernet' | 'wifi' | 'bluetooth' | 'usbCable' | 'webService';
13
+
14
+ /**
15
+ * Hub deployment for Web Service ECR. URLs are resolved inside the native
16
+ * SDKs; integrators pass only this name.
17
+ */
18
+ export type EcrEnvironment = 'SIT' | 'UAT' | 'PROD';
19
+
20
+ export interface EcrConfig {
21
+ ecrId?: string;
22
+ currencyCode?: string;
23
+ minorUnitDigits?: number;
24
+ port?: number;
25
+ connectTimeoutMs?: number;
26
+ responseTimeoutMs?: number;
27
+ probeTimeoutMs?: number;
28
+ secureHashKey?: string;
29
+ autoInquireOnFailure?: boolean;
30
+ /** Backend merchant ID — required for the `webService` transport. */
31
+ merchantId?: string;
32
+ /** Backend terminal ID — required for the `webService` transport. */
33
+ terminalId?: string;
34
+ /** Hub deployment for the `webService` transport. Defaults to `'SIT'`. */
35
+ environment?: EcrEnvironment;
36
+ }
37
+
38
+ export interface EcrTerminalOptions {
39
+ /**
40
+ * The terminal's IP address. Required for `wifi` and `ethernet`; ignored by
41
+ * `webService`, which addresses the terminal through the payment host.
42
+ */
43
+ host?: string;
44
+ serialNumber?: string;
45
+ transport?: EcrTransport;
46
+ config?: EcrConfig;
47
+ }
48
+
49
+ export type EcrNextStep = 'NONE' | 'INQUIRE_BY_MERCHANT_REFERENCE';
50
+
51
+ export type EcrFailureKind =
52
+ | 'unreachable'
53
+ | 'timeout'
54
+ | 'malformed'
55
+ | 'connectionLost'
56
+ | 'unauthenticated'
57
+ | 'cancelled'
58
+ | 'unsupported';
59
+
60
+ export interface EcrFailure {
61
+ kind: EcrFailureKind;
62
+ message: string;
63
+ outcomeIsUnknown: boolean;
64
+ }
65
+
66
+ export interface EcrApproved {
67
+ outcome: 'approved';
68
+ merchantReferenceId: string;
69
+ amount: string;
70
+ responseCode: string;
71
+ rrn: string;
72
+ authCode: string;
73
+ maskedPan: string;
74
+ partialApproval: boolean;
75
+ requestedAmount: string;
76
+ raw: string;
77
+ outcomeIsUnknown: false;
78
+ nextStep: 'NONE';
79
+ }
80
+
81
+ export interface EcrDeclined {
82
+ outcome: 'declined';
83
+ merchantReferenceId: string;
84
+ responseCode: string;
85
+ reason: string;
86
+ nextStep: EcrNextStep;
87
+ raw: string;
88
+ outcomeIsUnknown: boolean;
89
+ }
90
+
91
+ export interface EcrFailed {
92
+ outcome: 'failed';
93
+ merchantReferenceId: string;
94
+ failure: EcrFailure;
95
+ recovered?: EcrInquiry;
96
+ settled: boolean;
97
+ outcomeIsUnknown: boolean;
98
+ nextStep: EcrNextStep;
99
+ }
100
+
101
+ export type EcrResult = EcrApproved | EcrDeclined | EcrFailed;
102
+
103
+ export interface EcrTransaction {
104
+ transactionId: string;
105
+ stan: string;
106
+ type: string;
107
+ status: string;
108
+ amount: string;
109
+ totalAmount: string;
110
+ currency: string;
111
+ transactionTime: string;
112
+ maskedPan: string;
113
+ cardHolderName: string;
114
+ rrn: string;
115
+ authCode: string;
116
+ batchId: string;
117
+ terminalId: string;
118
+ isRefunded: boolean;
119
+ canVoid: boolean;
120
+ canRefund: boolean;
121
+ partialApproval: boolean;
122
+ authorizedAmount: string;
123
+ }
124
+
125
+ export interface EcrInquiryFound {
126
+ outcome: 'found';
127
+ merchantReferenceId: string;
128
+ transaction: EcrTransaction;
129
+ raw: string;
130
+ }
131
+
132
+ export interface EcrInquiryNotFound {
133
+ outcome: 'notFound';
134
+ merchantReferenceId: string;
135
+ reason: string;
136
+ raw: string;
137
+ }
138
+
139
+ export interface EcrInquiryFailed {
140
+ outcome: 'failed';
141
+ merchantReferenceId: string;
142
+ failure: EcrFailure;
143
+ }
144
+
145
+ export type EcrInquiry =
146
+ EcrInquiryFound | EcrInquiryNotFound | EcrInquiryFailed;
147
+
148
+ export interface EcrReceiptReady {
149
+ outcome: 'ready';
150
+ merchantReferenceId: string;
151
+ url: string;
152
+ raw: string;
153
+ }
154
+
155
+ export interface EcrReceiptUnavailable {
156
+ outcome: 'unavailable';
157
+ merchantReferenceId: string;
158
+ reason: string;
159
+ raw: string;
160
+ }
161
+
162
+ export interface EcrReceiptFailed {
163
+ outcome: 'failed';
164
+ merchantReferenceId: string;
165
+ failure: EcrFailure;
166
+ }
167
+
168
+ export type EcrReceipt =
169
+ EcrReceiptReady | EcrReceiptUnavailable | EcrReceiptFailed;
170
+
171
+ export interface EcrReferenceOptions {
172
+ merchantReferenceId?: string;
173
+ operationId?: string;
174
+ }
175
+
176
+ export interface EcrOriginalTransactionOptions extends EcrReferenceOptions {
177
+ originalTerminalId?: string;
178
+ }
179
+
180
+ export interface EcrDatedTransactionOptions extends EcrOriginalTransactionOptions {
181
+ receiptNumber: string;
182
+ transactionDate: string;
183
+ }
184
+
185
+ export interface EcrRefundOptions extends EcrDatedTransactionOptions {}
186
+
187
+ export interface EcrInquiryByReferenceOptions extends EcrOriginalTransactionOptions {
188
+ transactionDate?: string;
189
+ }