@akinon/pz-tamara-extension 2.0.0-beta.9 → 2.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/CHANGELOG.md +123 -6
- package/jest.config.js +7 -0
- package/package.json +12 -5
- package/readme.md +88 -0
- package/src/__tests__/tamara.test.ts +72 -0
- package/src/components/FormComponent.tsx +31 -14
- package/src/index.tsx +5 -0
- package/src/pages/TamaraPaymentGateway.tsx +36 -19
- package/src/types.ts +35 -0
- package/src/utils/index.ts +40 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,137 @@
|
|
|
1
1
|
# @akinon/pz-tamara-extension
|
|
2
2
|
|
|
3
|
-
## 2.0.
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
## 2.0.0
|
|
6
|
+
|
|
7
|
+
## 2.0.0-beta.27
|
|
8
|
+
|
|
9
|
+
## 2.0.0-beta.26
|
|
10
|
+
|
|
11
|
+
## 2.0.0-beta.25
|
|
12
|
+
|
|
13
|
+
## 2.0.0-beta.24
|
|
14
|
+
|
|
15
|
+
## 2.0.0-beta.23
|
|
16
|
+
|
|
17
|
+
## 2.0.0-beta.22
|
|
18
|
+
|
|
19
|
+
## 2.0.0-beta.21
|
|
20
|
+
|
|
21
|
+
## 2.0.0-beta.20
|
|
22
|
+
|
|
23
|
+
## 1.126.0
|
|
24
|
+
|
|
25
|
+
## 1.125.2
|
|
26
|
+
|
|
27
|
+
## 1.125.1
|
|
28
|
+
|
|
29
|
+
## 1.125.0
|
|
30
|
+
|
|
31
|
+
## 1.124.0
|
|
32
|
+
|
|
33
|
+
## 1.123.0
|
|
34
|
+
|
|
35
|
+
## 1.122.0
|
|
36
|
+
|
|
37
|
+
## 1.121.0
|
|
38
|
+
|
|
39
|
+
## 1.120.0
|
|
40
|
+
|
|
41
|
+
## 1.119.0
|
|
42
|
+
|
|
43
|
+
## 1.118.0
|
|
44
|
+
|
|
45
|
+
## 1.117.0
|
|
46
|
+
|
|
47
|
+
## 1.116.0
|
|
48
|
+
|
|
49
|
+
## 1.115.0
|
|
50
|
+
|
|
51
|
+
## 1.114.0
|
|
52
|
+
|
|
53
|
+
## 1.113.0
|
|
54
|
+
|
|
55
|
+
## 1.112.0
|
|
56
|
+
|
|
57
|
+
## 1.111.0
|
|
58
|
+
|
|
59
|
+
## 1.110.0
|
|
60
|
+
|
|
61
|
+
## 1.109.0
|
|
62
|
+
|
|
63
|
+
## 1.108.0
|
|
64
|
+
|
|
65
|
+
## 1.107.0
|
|
66
|
+
|
|
67
|
+
## 1.106.0
|
|
68
|
+
|
|
69
|
+
### Minor Changes
|
|
70
|
+
|
|
71
|
+
- 155cd23: ZERO-3377: Add customization options for Tamara payment gateway component
|
|
72
|
+
|
|
73
|
+
## 1.105.0
|
|
74
|
+
|
|
75
|
+
## 1.104.0
|
|
76
|
+
|
|
77
|
+
## 1.103.0
|
|
78
|
+
|
|
79
|
+
## 1.102.0
|
|
80
|
+
|
|
81
|
+
## 1.101.0
|
|
82
|
+
|
|
83
|
+
## 1.100.0
|
|
84
|
+
|
|
85
|
+
## 1.99.0
|
|
86
|
+
|
|
87
|
+
### Minor Changes
|
|
88
|
+
|
|
89
|
+
- d58538b: ZERO-3638: Enhance RC pipeline: add fetch, merge, and pre-release setup with conditional commit
|
|
90
|
+
|
|
91
|
+
## 1.98.0
|
|
92
|
+
|
|
93
|
+
## 1.97.0
|
|
94
|
+
|
|
95
|
+
## 1.96.0
|
|
96
|
+
|
|
97
|
+
## 1.95.0
|
|
98
|
+
|
|
99
|
+
## 1.94.0
|
|
100
|
+
|
|
101
|
+
## 1.93.0
|
|
102
|
+
|
|
103
|
+
## 1.92.0
|
|
104
|
+
|
|
105
|
+
## 1.91.0
|
|
4
106
|
|
|
5
107
|
### Minor Changes
|
|
6
108
|
|
|
7
|
-
-
|
|
109
|
+
- 1e53e17: ZERO-3302: decimal precision in formatDecimal function
|
|
110
|
+
- 07248e0: ZERO-3302: fix decimal formatting for Tamara payment gateway
|
|
8
111
|
|
|
9
|
-
##
|
|
112
|
+
## 1.90.0
|
|
10
113
|
|
|
11
|
-
##
|
|
114
|
+
## 1.89.0
|
|
12
115
|
|
|
13
|
-
|
|
116
|
+
### Minor Changes
|
|
117
|
+
|
|
118
|
+
- 1ba5af2: ZERO-3354: Add data-testids for tamara package
|
|
119
|
+
|
|
120
|
+
## 1.88.0
|
|
121
|
+
|
|
122
|
+
## 1.87.0
|
|
123
|
+
|
|
124
|
+
## 1.86.0
|
|
125
|
+
|
|
126
|
+
## 1.85.0
|
|
127
|
+
|
|
128
|
+
## 1.84.0
|
|
14
129
|
|
|
15
130
|
### Minor Changes
|
|
16
131
|
|
|
17
|
-
-
|
|
132
|
+
- 624a4eb: ZERO-3276: Update installation instructions across multiple README files to standardize format and improve clarity
|
|
133
|
+
|
|
134
|
+
## 1.83.0
|
|
18
135
|
|
|
19
136
|
## 1.82.0
|
|
20
137
|
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/pz-tamara-extension",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index.tsx",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test:watch": "jest --watch"
|
|
9
|
+
},
|
|
6
10
|
"peerDependencies": {
|
|
7
|
-
"react": "^18.0.0",
|
|
8
|
-
"react-dom": "^18.0.0"
|
|
11
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
12
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
9
13
|
},
|
|
10
14
|
"devDependencies": {
|
|
15
|
+
"@types/jest": "^29.5.14",
|
|
11
16
|
"@types/node": "^18.7.8",
|
|
12
17
|
"@types/react": "^18.0.17",
|
|
13
18
|
"@types/react-dom": "^18.0.6",
|
|
19
|
+
"jest": "^29.7.0",
|
|
14
20
|
"prettier": "^3.0.3",
|
|
15
|
-
"react": "
|
|
16
|
-
"react-dom": "
|
|
21
|
+
"react": "19.2.5",
|
|
22
|
+
"react-dom": "19.2.5",
|
|
23
|
+
"ts-jest": "^29.3.1",
|
|
17
24
|
"typescript": "^5.2.2"
|
|
18
25
|
}
|
|
19
26
|
}
|
package/readme.md
CHANGED
|
@@ -40,6 +40,94 @@ const TamaraGateway = async ({
|
|
|
40
40
|
export default TamaraGateway;
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## Customizing the Tamara Component
|
|
44
|
+
|
|
45
|
+
You can customize the appearance of the Tamara payment gateway using the `renderer` prop. This allows you to provide custom rendering functions for different parts of the component.
|
|
46
|
+
|
|
47
|
+
### Custom Form Component
|
|
48
|
+
|
|
49
|
+
```jsx
|
|
50
|
+
import { TamaraPaymentGateway } from '@akinon/pz-tamara-extension';
|
|
51
|
+
|
|
52
|
+
const TamaraGateway = async ({
|
|
53
|
+
searchParams: { sessionId },
|
|
54
|
+
params: { currency, locale }
|
|
55
|
+
}: {
|
|
56
|
+
searchParams: Record<string, string>;
|
|
57
|
+
params: { currency: string; locale: string };
|
|
58
|
+
}) => {
|
|
59
|
+
return (
|
|
60
|
+
<TamaraPaymentGateway
|
|
61
|
+
sessionId={sessionId}
|
|
62
|
+
currency={currency}
|
|
63
|
+
locale={locale}
|
|
64
|
+
extensionUrl={process.env.TAMARA_EXTENSION_URL}
|
|
65
|
+
hashKey={process.env.TAMARA_HASH_KEY}
|
|
66
|
+
renderer={{
|
|
67
|
+
formComponent: {
|
|
68
|
+
renderForm: ({ extensionUrl, sessionId, context, csrfToken, autoSubmit }) => (
|
|
69
|
+
<div className="custom-tamara-form-wrapper">
|
|
70
|
+
<h3 className="text-lg font-semibold mb-4">Tamara Payment</h3>
|
|
71
|
+
<p className="mb-4">You are beign redirected to Tamara payment page...</p>
|
|
72
|
+
<form
|
|
73
|
+
action={`${extensionUrl}/form-page/?sessionId=${sessionId}`}
|
|
74
|
+
method="post"
|
|
75
|
+
encType="multipart/form-data"
|
|
76
|
+
id="tamara-custom-form"
|
|
77
|
+
className="hidden"
|
|
78
|
+
>
|
|
79
|
+
<input type="hidden" name="csrf_token" value={csrfToken} />
|
|
80
|
+
<input type="hidden" name="data" value={JSON.stringify(context)} />
|
|
81
|
+
{autoSubmit && (
|
|
82
|
+
<script
|
|
83
|
+
dangerouslySetInnerHTML={{
|
|
84
|
+
__html: "document.getElementById('tamara-custom-form').submit()"
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
</form>
|
|
89
|
+
<div className="loader w-12 h-12 border-4 border-t-4 border-gray-200 border-t-blue-500 rounded-full animate-spin"></div>
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
},
|
|
93
|
+
paymentGateway: {
|
|
94
|
+
renderContainer: ({ children }) => (
|
|
95
|
+
<div className="p-8 max-w-lg mx-auto bg-white rounded-lg shadow-md">
|
|
96
|
+
{children}
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export default TamaraGateway;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Custom Renderer API
|
|
109
|
+
|
|
110
|
+
The renderer prop accepts an object with the following structure:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
interface TamaraRendererProps {
|
|
114
|
+
formComponent?: {
|
|
115
|
+
renderForm?: (props: {
|
|
116
|
+
extensionUrl: string;
|
|
117
|
+
sessionId: string;
|
|
118
|
+
context: any;
|
|
119
|
+
csrfToken: string;
|
|
120
|
+
autoSubmit: boolean;
|
|
121
|
+
}) => React.ReactNode;
|
|
122
|
+
renderLoading?: () => React.ReactNode;
|
|
123
|
+
renderError?: (error: string) => React.ReactNode;
|
|
124
|
+
};
|
|
125
|
+
paymentGateway?: {
|
|
126
|
+
renderContainer?: (props: { children: React.ReactNode }) => React.ReactNode;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
43
131
|
## API Routes
|
|
44
132
|
|
|
45
133
|
### Check Availability API
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { formatDecimal, getQuantizeFormat } from '../utils';
|
|
2
|
+
|
|
3
|
+
describe('Utility Functions', () => {
|
|
4
|
+
describe('formatDecimal', () => {
|
|
5
|
+
it('should format decimal numbers correctly', () => {
|
|
6
|
+
expect(formatDecimal(10)).toBe('10');
|
|
7
|
+
expect(formatDecimal(10.5)).toBe('10.5');
|
|
8
|
+
expect(formatDecimal(10.55)).toBe('10.55');
|
|
9
|
+
expect(formatDecimal(10.555)).toBe('10.555');
|
|
10
|
+
|
|
11
|
+
expect(formatDecimal(0)).toBe('0');
|
|
12
|
+
expect(formatDecimal(0.0)).toBe('0');
|
|
13
|
+
|
|
14
|
+
expect(formatDecimal(-10.5)).toBe('-10.5');
|
|
15
|
+
|
|
16
|
+
expect(formatDecimal(0.001)).toBe('0.001');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should use provided quantizeFormat when specified', () => {
|
|
20
|
+
expect(formatDecimal(10.557, 0.01)).toBe('10.55');
|
|
21
|
+
expect(formatDecimal(10.557, 0.1)).toBe('10.5');
|
|
22
|
+
expect(formatDecimal(10.557, 1)).toBe('10');
|
|
23
|
+
|
|
24
|
+
expect(formatDecimal(10.557, 0)).toBe('10.557');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('getQuantizeFormat', () => {
|
|
29
|
+
it('should return correct quantize format for different price strings', () => {
|
|
30
|
+
expect(getQuantizeFormat('10')).toBe(0);
|
|
31
|
+
expect(getQuantizeFormat('10.5')).toBe(0.1);
|
|
32
|
+
expect(getQuantizeFormat('10.55')).toBe(0.01);
|
|
33
|
+
expect(getQuantizeFormat('10.555')).toBe(0.001);
|
|
34
|
+
|
|
35
|
+
expect(getQuantizeFormat('0')).toBe(0);
|
|
36
|
+
expect(getQuantizeFormat('0.0')).toBe(0.1);
|
|
37
|
+
|
|
38
|
+
expect(getQuantizeFormat('-10.5')).toBe(0.1);
|
|
39
|
+
|
|
40
|
+
expect(getQuantizeFormat('0.001')).toBe(0.001);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('Decimal Formatting Integration', () => {
|
|
45
|
+
it('should correctly round basket item amounts', () => {
|
|
46
|
+
const priceAsString = '100.55';
|
|
47
|
+
const quantizeFormat = getQuantizeFormat(priceAsString);
|
|
48
|
+
|
|
49
|
+
const amount = 100.557;
|
|
50
|
+
const roundedAmount =
|
|
51
|
+
Math.floor(amount / quantizeFormat) * quantizeFormat;
|
|
52
|
+
|
|
53
|
+
expect(roundedAmount).toBe(100.55);
|
|
54
|
+
expect(formatDecimal(amount, quantizeFormat)).toBe('100.55');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should correctly calculate and round tax amounts', () => {
|
|
58
|
+
const priceAsString = '100.55';
|
|
59
|
+
const quantizeFormat = getQuantizeFormat(priceAsString);
|
|
60
|
+
|
|
61
|
+
const amount = 100.55;
|
|
62
|
+
const taxRate = 18.0 / 100;
|
|
63
|
+
const taxAmount = amount * taxRate;
|
|
64
|
+
|
|
65
|
+
const roundedTaxAmount =
|
|
66
|
+
Math.floor(taxAmount / quantizeFormat) * quantizeFormat;
|
|
67
|
+
|
|
68
|
+
expect(roundedTaxAmount).toBe(18.09);
|
|
69
|
+
expect(formatDecimal(taxAmount, quantizeFormat)).toBe('18.09');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { cookies } from 'next/headers';
|
|
3
|
-
|
|
4
|
-
type FormComponentProps = {
|
|
5
|
-
extensionUrl: string;
|
|
6
|
-
sessionId: string;
|
|
7
|
-
context: any;
|
|
8
|
-
};
|
|
3
|
+
import { FormComponentProps } from '../types';
|
|
9
4
|
|
|
10
5
|
const FormComponent = async ({
|
|
11
6
|
extensionUrl,
|
|
12
7
|
sessionId,
|
|
13
|
-
context
|
|
8
|
+
context,
|
|
9
|
+
renderer,
|
|
10
|
+
autoSubmit = true
|
|
14
11
|
}: FormComponentProps) => {
|
|
15
12
|
const nextCookies = await cookies();
|
|
16
13
|
|
|
@@ -20,9 +17,15 @@ const FormComponent = async ({
|
|
|
20
17
|
|
|
21
18
|
const csrfToken = nextCookies.get('csrftoken')?.value ?? '';
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
const DefaultForm = ({
|
|
21
|
+
extensionUrl,
|
|
22
|
+
sessionId,
|
|
23
|
+
context,
|
|
24
|
+
csrfToken,
|
|
25
|
+
autoSubmit
|
|
26
|
+
}) => (
|
|
24
27
|
<form
|
|
25
|
-
action={`${
|
|
28
|
+
action={`${extensionUrl}/form-page/?sessionId=${sessionId}`}
|
|
26
29
|
method="post"
|
|
27
30
|
encType="multipart/form-data"
|
|
28
31
|
id="tamara-extension-form"
|
|
@@ -30,13 +33,27 @@ const FormComponent = async ({
|
|
|
30
33
|
<input type="hidden" name="csrf_token" value={csrfToken} />
|
|
31
34
|
<input type="hidden" name="data" value={JSON.stringify(context)} />
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
{autoSubmit && (
|
|
37
|
+
<script
|
|
38
|
+
dangerouslySetInnerHTML={{
|
|
39
|
+
__html: "document.getElementById('tamara-extension-form').submit()"
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
38
43
|
</form>
|
|
39
44
|
);
|
|
45
|
+
|
|
46
|
+
const RenderForm = renderer?.renderForm || DefaultForm;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<RenderForm
|
|
50
|
+
extensionUrl={extensionUrlWithoutSlash}
|
|
51
|
+
sessionId={sessionId}
|
|
52
|
+
context={context}
|
|
53
|
+
csrfToken={csrfToken}
|
|
54
|
+
autoSubmit={autoSubmit}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
40
57
|
};
|
|
41
58
|
|
|
42
59
|
export default FormComponent;
|
package/src/index.tsx
CHANGED
|
@@ -6,23 +6,18 @@ import {
|
|
|
6
6
|
fetchData,
|
|
7
7
|
generateHash,
|
|
8
8
|
getRandomString,
|
|
9
|
-
formatDecimal
|
|
9
|
+
formatDecimal,
|
|
10
|
+
getQuantizeFormat
|
|
10
11
|
} from '../utils';
|
|
11
|
-
|
|
12
|
-
type TamaraPaymentGatewayProps = {
|
|
13
|
-
sessionId: string;
|
|
14
|
-
currency: string;
|
|
15
|
-
locale: string;
|
|
16
|
-
extensionUrl: string;
|
|
17
|
-
hashKey: string;
|
|
18
|
-
};
|
|
12
|
+
import { TamaraPaymentGatewayProps } from '../types';
|
|
19
13
|
|
|
20
14
|
export const TamaraPaymentGateway = async ({
|
|
21
15
|
sessionId,
|
|
22
16
|
currency,
|
|
23
17
|
locale,
|
|
24
18
|
extensionUrl,
|
|
25
|
-
hashKey
|
|
19
|
+
hashKey,
|
|
20
|
+
renderer
|
|
26
21
|
}: TamaraPaymentGatewayProps) => {
|
|
27
22
|
if (!sessionId || !currency || !locale) {
|
|
28
23
|
return <></>;
|
|
@@ -67,6 +62,10 @@ export const TamaraPaymentGateway = async ({
|
|
|
67
62
|
0
|
|
68
63
|
);
|
|
69
64
|
|
|
65
|
+
const quantizeFormat = getQuantizeFormat(
|
|
66
|
+
preOrder.pre_order.unpaid_amount.toString()
|
|
67
|
+
);
|
|
68
|
+
|
|
70
69
|
let cumulativeAmount = 0;
|
|
71
70
|
let totalTaxAmount = 0;
|
|
72
71
|
const basketItems = basketItemSet.map((item: any, index: number) => {
|
|
@@ -74,6 +73,10 @@ export const TamaraPaymentGateway = async ({
|
|
|
74
73
|
const weight = basketItemAmount / totalProductAmount;
|
|
75
74
|
let amount = remainingAmount * weight + basketItemAmount;
|
|
76
75
|
|
|
76
|
+
if (quantizeFormat !== 0) {
|
|
77
|
+
amount = Math.floor(amount / quantizeFormat) * quantizeFormat;
|
|
78
|
+
}
|
|
79
|
+
|
|
77
80
|
cumulativeAmount += amount;
|
|
78
81
|
|
|
79
82
|
if (index === basketItemSet.length - 1) {
|
|
@@ -82,7 +85,14 @@ export const TamaraPaymentGateway = async ({
|
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
const taxRate = Number(item.tax_rate || 0) / 100;
|
|
85
|
-
|
|
88
|
+
const itemTaxAmount = amount * taxRate;
|
|
89
|
+
|
|
90
|
+
if (quantizeFormat !== 0) {
|
|
91
|
+
totalTaxAmount +=
|
|
92
|
+
Math.floor(itemTaxAmount / quantizeFormat) * quantizeFormat;
|
|
93
|
+
} else {
|
|
94
|
+
totalTaxAmount += itemTaxAmount;
|
|
95
|
+
}
|
|
86
96
|
|
|
87
97
|
return {
|
|
88
98
|
name: item.product?.name?.substring(0, 255) || 'none',
|
|
@@ -91,7 +101,7 @@ export const TamaraPaymentGateway = async ({
|
|
|
91
101
|
sku: item.product?.sku,
|
|
92
102
|
quantity: item.quantity,
|
|
93
103
|
total_amount: {
|
|
94
|
-
amount: formatDecimal(amount)
|
|
104
|
+
amount: formatDecimal(amount, quantizeFormat)
|
|
95
105
|
}
|
|
96
106
|
};
|
|
97
107
|
});
|
|
@@ -100,10 +110,10 @@ export const TamaraPaymentGateway = async ({
|
|
|
100
110
|
hash,
|
|
101
111
|
salt,
|
|
102
112
|
tax_amount: {
|
|
103
|
-
amount: formatDecimal(totalTaxAmount)
|
|
113
|
+
amount: formatDecimal(totalTaxAmount, quantizeFormat)
|
|
104
114
|
},
|
|
105
115
|
shipping_amount: {
|
|
106
|
-
amount: formatDecimal(shippingAmount)
|
|
116
|
+
amount: formatDecimal(shippingAmount, quantizeFormat)
|
|
107
117
|
},
|
|
108
118
|
order_items: basketItems,
|
|
109
119
|
billing_address: {
|
|
@@ -126,11 +136,18 @@ export const TamaraPaymentGateway = async ({
|
|
|
126
136
|
}
|
|
127
137
|
};
|
|
128
138
|
|
|
139
|
+
const DefaultContainer = ({ children }) => <>{children}</>;
|
|
140
|
+
const RenderContainer =
|
|
141
|
+
renderer?.paymentGateway?.renderContainer || DefaultContainer;
|
|
142
|
+
|
|
129
143
|
return (
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
<RenderContainer>
|
|
145
|
+
<FormComponent
|
|
146
|
+
extensionUrl={extensionUrl}
|
|
147
|
+
sessionId={sessionId}
|
|
148
|
+
context={context}
|
|
149
|
+
renderer={renderer?.formComponent}
|
|
150
|
+
/>
|
|
151
|
+
</RenderContainer>
|
|
135
152
|
);
|
|
136
153
|
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface TamaraRendererProps {
|
|
4
|
+
formComponent?: {
|
|
5
|
+
renderForm?: (props: {
|
|
6
|
+
extensionUrl: string;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
context: any;
|
|
9
|
+
csrfToken: string;
|
|
10
|
+
autoSubmit: boolean;
|
|
11
|
+
}) => React.ReactNode;
|
|
12
|
+
renderLoading?: () => React.ReactNode;
|
|
13
|
+
renderError?: (error: string) => React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
paymentGateway?: {
|
|
16
|
+
renderContainer?: (props: { children: React.ReactNode }) => React.ReactNode;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FormComponentProps {
|
|
21
|
+
extensionUrl: string;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
context: any;
|
|
24
|
+
renderer?: TamaraRendererProps['formComponent'];
|
|
25
|
+
autoSubmit?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TamaraPaymentGatewayProps {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
currency: string;
|
|
31
|
+
locale: string;
|
|
32
|
+
extensionUrl: string;
|
|
33
|
+
hashKey: string;
|
|
34
|
+
renderer?: TamaraRendererProps;
|
|
35
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
2
|
|
|
3
3
|
export const fetchData = async (url: string, requestHeaders: HeadersInit) => {
|
|
4
4
|
const response = await fetch(url, { headers: requestHeaders });
|
|
@@ -18,11 +18,45 @@ export const generateHash = (...values: string[]): string => {
|
|
|
18
18
|
return crypto.createHash('sha512').update(hashStr, 'utf8').digest('hex');
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export const formatDecimal = (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
export const formatDecimal = (
|
|
22
|
+
value: number,
|
|
23
|
+
quantizeFormat?: number
|
|
24
|
+
): string => {
|
|
25
|
+
const valueStr = value.toString();
|
|
26
|
+
|
|
27
|
+
// If quantizeFormat is provided, use it to determine decimal places
|
|
28
|
+
if (quantizeFormat !== undefined && quantizeFormat !== 0) {
|
|
29
|
+
// Calculate decimal places from quantizeFormat
|
|
30
|
+
const decimalPlaces = Math.abs(Math.log10(quantizeFormat));
|
|
31
|
+
const roundedValue = Math.floor(value / quantizeFormat) * quantizeFormat;
|
|
32
|
+
return roundedValue.toFixed(decimalPlaces);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Otherwise, determine decimal places from the value
|
|
36
|
+
let decimalPlaces = 0;
|
|
37
|
+
if (valueStr.includes('.')) {
|
|
38
|
+
decimalPlaces = valueStr.split('.')[1].length;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (decimalPlaces === 0) {
|
|
42
|
+
return valueStr;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const defaultQuantizeFormat = Math.pow(10, -decimalPlaces);
|
|
46
|
+
const roundedValue =
|
|
47
|
+
Math.floor(value / defaultQuantizeFormat) * defaultQuantizeFormat;
|
|
48
|
+
|
|
49
|
+
return roundedValue.toFixed(decimalPlaces);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const getQuantizeFormat = (priceAsString: string): number => {
|
|
53
|
+
const decimalPlaces = priceAsString.includes('.')
|
|
54
|
+
? priceAsString.split('.')[1].length
|
|
25
55
|
: 0;
|
|
26
56
|
|
|
27
|
-
|
|
57
|
+
if (decimalPlaces === 0) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return Math.pow(10, -decimalPlaces);
|
|
28
62
|
};
|