@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 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
@@ -0,0 +1,7 @@
1
+ # @akinon/pz-hepsipay
2
+
3
+ ## 1.91.0-rc.4
4
+
5
+ ### Minor Changes
6
+
7
+ - 7eb51ca: ZERO-3424 :Update package versions
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
+ };