@akinon/pz-hepsipay 1.91.0-rc.4
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/.gitattributes +15 -0
- package/.prettierrc +13 -0
- package/CHANGELOG.md +7 -0
- package/package.json +19 -0
- package/readme.md +153 -0
- package/src/index.tsx +185 -0
package/.gitattributes
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
*.js text eol=lf
|
|
2
|
+
*.jsx text eol=lf
|
|
3
|
+
*.ts text eol=lf
|
|
4
|
+
*.tsx text eol=lf
|
|
5
|
+
*.json text eol=lf
|
|
6
|
+
*.md text eol=lf
|
|
7
|
+
|
|
8
|
+
.eslintignore text eol=lf
|
|
9
|
+
.eslintrc text eol=lf
|
|
10
|
+
.gitignore text eol=lf
|
|
11
|
+
.prettierrc text eol=lf
|
|
12
|
+
.yarnrc text eol=lf
|
|
13
|
+
|
|
14
|
+
* text=auto
|
|
15
|
+
|
package/.prettierrc
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bracketSameLine": false,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"singleQuote": true,
|
|
5
|
+
"jsxSingleQuote": false,
|
|
6
|
+
"bracketSpacing": true,
|
|
7
|
+
"semi": true,
|
|
8
|
+
"useTabs": false,
|
|
9
|
+
"arrowParens": "always",
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"proseWrap": "never",
|
|
12
|
+
"trailingComma": "none"
|
|
13
|
+
}
|
package/CHANGELOG.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akinon/pz-hepsipay",
|
|
3
|
+
"version": "1.91.0-rc.4",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "src/index.tsx",
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"react": "^18.0.0",
|
|
8
|
+
"react-dom": "^18.0.0"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/node": "^18.7.8",
|
|
12
|
+
"@types/react": "^18.0.17",
|
|
13
|
+
"@types/react-dom": "^18.0.6",
|
|
14
|
+
"prettier": "^3.0.3",
|
|
15
|
+
"react": "^18.2.0",
|
|
16
|
+
"react-dom": "^18.2.0",
|
|
17
|
+
"typescript": "^5.2.2"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Project Zero - Hepsipay Plugin
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
You can use the following command to install the extension with the latest plugins:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
|
|
9
|
+
npx @akinon/projectzero@latest --plugins
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
# Adding HepsiPay Payment Method to Checkout
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 1. Create Hepsipay File
|
|
18
|
+
|
|
19
|
+
**views/checkout/steps/payment/options/hepsipay.tsx**
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
23
|
+
import CheckoutAgreements from '../agreements';
|
|
24
|
+
|
|
25
|
+
export default function Hepsipay() {
|
|
26
|
+
return (
|
|
27
|
+
<PluginModule
|
|
28
|
+
props={{
|
|
29
|
+
agreementCheckbox: (
|
|
30
|
+
<CheckoutAgreements control={null} error={null} fieldId="agreement" />
|
|
31
|
+
)
|
|
32
|
+
}}
|
|
33
|
+
component={Component.Hepsipay}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 2. Update Payment Step
|
|
40
|
+
|
|
41
|
+
**views/checkout/steps/payment/index.tsx**
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import HepsiPay from './options/hepsipay';
|
|
45
|
+
|
|
46
|
+
export const PaymentOptionViews: Array<CheckoutPaymentOption> = [
|
|
47
|
+
{
|
|
48
|
+
slug: 'hepsipay',
|
|
49
|
+
view: HepsiPay
|
|
50
|
+
}
|
|
51
|
+
// Other payment methods can be added here
|
|
52
|
+
];
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Props
|
|
56
|
+
|
|
57
|
+
### agreementCheckbox
|
|
58
|
+
|
|
59
|
+
| Property | Type | Description |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `agreementCheckbox` | `React.ReactElement` | A controlled checkbox React element (e.g., terms agreement checkbox |
|
|
62
|
+
| |
|
|
63
|
+
|
|
64
|
+
### customUIRender
|
|
65
|
+
|
|
66
|
+
A function to fully customize the Hepsipay component's appearance. If provided, the default UI will not be shown; instead, the JSX returned by this function will be rendered.
|
|
67
|
+
|
|
68
|
+
| Property | Type | Description |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `customUIRender` | `React.ReactNode` | Optional function to fully customize the Hepsipay component. Receives frameUrl, errors, control, and onSubmit props. |
|
|
71
|
+
| |
|
|
72
|
+
|
|
73
|
+
#### Example
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { Button } from '@/components/ui/button';
|
|
77
|
+
import { Icon } from '@/components/ui/icon';
|
|
78
|
+
import Script from 'next/script';
|
|
79
|
+
|
|
80
|
+
<PluginModule
|
|
81
|
+
props={{
|
|
82
|
+
customUIRender: ({ onSubmit, errors, control }) => (
|
|
83
|
+
<>
|
|
84
|
+
<div id="hepsipay-frame" data-testid="hepsipay-frame"></div>
|
|
85
|
+
<form onSubmit={onSubmit}>
|
|
86
|
+
<div className="flex items-start flex-col border-t border-solid border-gray-400 py-4 space-y-4">
|
|
87
|
+
<CheckoutAgreements
|
|
88
|
+
control={control}
|
|
89
|
+
error={errors.agreement}
|
|
90
|
+
fieldId="agreement"
|
|
91
|
+
/>
|
|
92
|
+
<Button
|
|
93
|
+
className="group uppercase mt-4 inline-flex items-center justify-center w-full"
|
|
94
|
+
type="submit"
|
|
95
|
+
data-testid="checkout-bank-account-place-order"
|
|
96
|
+
>
|
|
97
|
+
<span>Place Order</span>
|
|
98
|
+
<Icon
|
|
99
|
+
name="chevron-end"
|
|
100
|
+
size={12}
|
|
101
|
+
className="fill-primary-foreground ml-2 h-3 group-hover:fill-primary"
|
|
102
|
+
/>
|
|
103
|
+
</Button>
|
|
104
|
+
</div>
|
|
105
|
+
</form>
|
|
106
|
+
|
|
107
|
+
<Script
|
|
108
|
+
src="https://images.hepsiburada.net/hepsipay/packages/pf/hepsipay-latest.min.js"
|
|
109
|
+
defer
|
|
110
|
+
/>
|
|
111
|
+
</>
|
|
112
|
+
)
|
|
113
|
+
}}
|
|
114
|
+
component={Component.Hepsipay}
|
|
115
|
+
/>;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### initOptionsOverrides
|
|
119
|
+
|
|
120
|
+
`initOptionsOverrides` is an object used to partially override the default settings passed to the Hepsipay SDK's `init` function. This allows customization of many options such as iframe behavior and appearance, as well as callback functions.
|
|
121
|
+
|
|
122
|
+
| Property | Type | Description |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `frameUrl` | `string` | The URL of the payment iframe (required). |
|
|
125
|
+
| `frameDivId` | `string` | The ID of the HTML div where the iframe will be embedded. |
|
|
126
|
+
| `paymentStatusCallback` | `(isPaymentAllowed: boolean) => void` | Called when the payment status changes. |
|
|
127
|
+
| `frameFailureCallback` | `() => void` | Called if the iframe fails to load or an error occurs. |
|
|
128
|
+
| `onPaymentSuccessCallback` | `(payload: any) => void` | Callback function called when the payment is successfully completed. |
|
|
129
|
+
| `maxHeight` | `string` | Maximum height for the iframe (e.g., `'800'`). |
|
|
130
|
+
| `disableDynamicHeight` | `boolean` | Enables or disables dynamic height adjustment for the iframe. |
|
|
131
|
+
| `showFramePaymentButton` | `boolean` | Shows or hides the payment button inside the iframe. |
|
|
132
|
+
| `useApiCallOnSuccessCallback` | `boolean` | Determines whether to perform an API call on payment success. |
|
|
133
|
+
| `selectedPaymentInfoCallback` | `(payload: any) => void` | Called when the selected payment information changes. |
|
|
134
|
+
| `overrideFontFamily` | `string` | Custom font family to be used inside the iframe. |
|
|
135
|
+
| `hideHeader` | `boolean` | Shows or hides the header section of the iframe. |
|
|
136
|
+
|
|
137
|
+
#### Example
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
<PluginModule
|
|
141
|
+
props={{
|
|
142
|
+
initOptionsOverrides: {
|
|
143
|
+
hideHeader: false,
|
|
144
|
+
maxHeight: '100',
|
|
145
|
+
overrideFontFamily: 'Roboto',
|
|
146
|
+
onPaymentSuccessCallback: (payload) => {
|
|
147
|
+
console.log('Success!', payload);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}}
|
|
151
|
+
component={Component.Hepsipay}
|
|
152
|
+
/>
|
|
153
|
+
```
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Button, Icon } from '@theme/components';
|
|
2
|
+
import Script from 'next/script';
|
|
3
|
+
import { useEffect, useCallback, useState } from 'react';
|
|
4
|
+
import { RootState } from '@theme/redux/store';
|
|
5
|
+
import { useAppSelector } from '@akinon/next/redux/hooks';
|
|
6
|
+
import { useSetWalletCompletePageMutation } from '@akinon/next/data/client/checkout';
|
|
7
|
+
import * as yup from 'yup';
|
|
8
|
+
import { SubmitHandler, useForm } from 'react-hook-form';
|
|
9
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
10
|
+
import React from 'react';
|
|
11
|
+
|
|
12
|
+
interface HepsipayTranslationsProps {
|
|
13
|
+
placeholderInput: string;
|
|
14
|
+
availableWarning: string;
|
|
15
|
+
agreementError: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface CustomUIRenderProps {
|
|
19
|
+
onSubmit: () => void;
|
|
20
|
+
control: any;
|
|
21
|
+
errors: any;
|
|
22
|
+
frameUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface InitOptions {
|
|
26
|
+
frameUrl: string;
|
|
27
|
+
frameDivId: string;
|
|
28
|
+
paymentStatusCallback: (isPaymentAllowed: boolean) => void;
|
|
29
|
+
frameFailureCallback: () => void;
|
|
30
|
+
onPaymentSuccessCallback: (payload: any) => void;
|
|
31
|
+
maxHeight: string;
|
|
32
|
+
disableDynamicHeight: boolean;
|
|
33
|
+
showFramePaymentButton: boolean;
|
|
34
|
+
useApiCallOnSuccessCallback: boolean;
|
|
35
|
+
selectedPaymentInfoCallback: (payload: any) => void;
|
|
36
|
+
overrideFontFamily: string;
|
|
37
|
+
hideHeader: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface HepsipayProps {
|
|
41
|
+
translations: Record<string, HepsipayTranslationsProps>;
|
|
42
|
+
agreementCheckbox?: React.ReactElement;
|
|
43
|
+
customUIRender?: (props: CustomUIRenderProps) => React.ReactNode;
|
|
44
|
+
initOptionsOverrides?: Partial<InitOptions>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Hepsipay = ({
|
|
48
|
+
translations,
|
|
49
|
+
agreementCheckbox,
|
|
50
|
+
customUIRender,
|
|
51
|
+
initOptionsOverrides
|
|
52
|
+
}: HepsipayProps) => {
|
|
53
|
+
const defaultTranslations = {
|
|
54
|
+
placeholderInput: 'PLACE ORDER',
|
|
55
|
+
availableWarning:
|
|
56
|
+
'Hepsipay is not available for this user. Please choose another payment method.',
|
|
57
|
+
agreementError: 'This Field is Required'
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const _translations = {
|
|
61
|
+
...defaultTranslations,
|
|
62
|
+
...translations
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const hepsiPayFormSchema = () =>
|
|
66
|
+
yup.object().shape({
|
|
67
|
+
agreement: yup.boolean().oneOf([true], 'This Field is Required')
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
handleSubmit,
|
|
72
|
+
control,
|
|
73
|
+
formState: { errors }
|
|
74
|
+
} = useForm({
|
|
75
|
+
resolver: yupResolver(hepsiPayFormSchema())
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const [isPaymentSuccessful, setIsPaymentSuccessful] = useState(false);
|
|
79
|
+
|
|
80
|
+
const context_extras = useAppSelector(
|
|
81
|
+
(state: RootState) => state.checkout.preOrder?.context_extras
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const hepsipayAvailability = useAppSelector(
|
|
85
|
+
(state: RootState) => state.checkout?.hepsipayAvailability
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const frameUrl = context_extras?.FrameUrl;
|
|
89
|
+
|
|
90
|
+
const [setWalletCompletePage] = useSetWalletCompletePageMutation();
|
|
91
|
+
|
|
92
|
+
const initHepsipay = useCallback(() => {
|
|
93
|
+
if (!frameUrl || !window.HepsipaySdk) return;
|
|
94
|
+
|
|
95
|
+
const defaultInitOptions: InitOptions = {
|
|
96
|
+
frameUrl,
|
|
97
|
+
frameDivId: 'hepsipay-frame',
|
|
98
|
+
paymentStatusCallback: () => {},
|
|
99
|
+
frameFailureCallback: () => {},
|
|
100
|
+
onPaymentSuccessCallback: () => {
|
|
101
|
+
setIsPaymentSuccessful(true);
|
|
102
|
+
setWalletCompletePage(true);
|
|
103
|
+
},
|
|
104
|
+
maxHeight: '800',
|
|
105
|
+
disableDynamicHeight: false,
|
|
106
|
+
showFramePaymentButton: false,
|
|
107
|
+
useApiCallOnSuccessCallback: false,
|
|
108
|
+
selectedPaymentInfoCallback: () => {},
|
|
109
|
+
overrideFontFamily: 'Inter',
|
|
110
|
+
hideHeader: false
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const mergedInitOptions: InitOptions = {
|
|
114
|
+
...defaultInitOptions,
|
|
115
|
+
...initOptionsOverrides,
|
|
116
|
+
frameUrl
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
window.HepsipaySdk.init(mergedInitOptions);
|
|
120
|
+
}, [frameUrl, setWalletCompletePage]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (window.HepsipaySdk) {
|
|
124
|
+
initHepsipay();
|
|
125
|
+
}
|
|
126
|
+
}, [initHepsipay]);
|
|
127
|
+
|
|
128
|
+
const onSubmit: SubmitHandler<null> = async () => {
|
|
129
|
+
if (window.HepsipaySdk) {
|
|
130
|
+
window.HepsipaySdk.initPayment();
|
|
131
|
+
} else {
|
|
132
|
+
console.error('Hepsipay SDK is not available');
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (isPaymentSuccessful) return null;
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className="p-4 sm:px-6">
|
|
140
|
+
{hepsipayAvailability ? (
|
|
141
|
+
customUIRender ? (
|
|
142
|
+
customUIRender({
|
|
143
|
+
onSubmit: handleSubmit(onSubmit),
|
|
144
|
+
control,
|
|
145
|
+
errors,
|
|
146
|
+
frameUrl
|
|
147
|
+
})
|
|
148
|
+
) : (
|
|
149
|
+
<>
|
|
150
|
+
<div id="hepsipay-frame" data-testid="hepsipay-frame"></div>
|
|
151
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
152
|
+
<div className="flex items-start flex-col border-t border-solid border-gray-400 py-4 space-y-4">
|
|
153
|
+
{agreementCheckbox &&
|
|
154
|
+
React.cloneElement(agreementCheckbox, {
|
|
155
|
+
control,
|
|
156
|
+
error: errors.agreement,
|
|
157
|
+
fieldId: 'agreement'
|
|
158
|
+
})}
|
|
159
|
+
<Button
|
|
160
|
+
className="group uppercase mt-4 inline-flex items-center justify-center w-full"
|
|
161
|
+
type="submit"
|
|
162
|
+
data-testid="checkout-bank-account-place-order"
|
|
163
|
+
>
|
|
164
|
+
<span>{_translations.placeholderInput}</span>
|
|
165
|
+
<Icon
|
|
166
|
+
name="chevron-end"
|
|
167
|
+
size={12}
|
|
168
|
+
className="fill-primary-foreground ml-2 h-3 group-hover:fill-primary"
|
|
169
|
+
/>
|
|
170
|
+
</Button>
|
|
171
|
+
</div>
|
|
172
|
+
</form>
|
|
173
|
+
|
|
174
|
+
<Script
|
|
175
|
+
src="https://images.hepsiburada.net/hepsipay/packages/pf/hepsipay-latest.min.js"
|
|
176
|
+
defer
|
|
177
|
+
/>
|
|
178
|
+
</>
|
|
179
|
+
)
|
|
180
|
+
) : (
|
|
181
|
+
<div className="py-12 px-4">{_translations.availableWarning}</div>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
};
|