@akinon/pz-gpay 1.15.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.
- package/.gitattributes +15 -0
- package/.prettierrc +13 -0
- package/CHANGELOG.md +7 -0
- package/package.json +20 -0
- package/readme.md +38 -0
- package/src/endpoints.ts +45 -0
- package/src/index.tsx +1 -0
- package/src/routes/garanti-pay-redirect.tsx +29 -0
- package/src/views/gpay-option.tsx +111 -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,20 @@
|
|
1
|
+
{
|
2
|
+
"name": "@akinon/pz-gpay",
|
3
|
+
"version": "1.15.0",
|
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
|
+
"react": "^18.2.0",
|
15
|
+
"react-dom": "^18.2.0",
|
16
|
+
"typescript": "^4.7.4",
|
17
|
+
"react-hook-form": "7.31.3",
|
18
|
+
"@hookform/resolvers": "2.9.0"
|
19
|
+
}
|
20
|
+
}
|
package/readme.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# pz-gpay
|
2
|
+
|
3
|
+
### Example Usage
|
4
|
+
##### File Path: src/views/checkout/steps/payment/options/gpay.tsx
|
5
|
+
|
6
|
+
```javascript
|
7
|
+
import { GPayOption } from '@akinon/pz-gpay';
|
8
|
+
|
9
|
+
export default function GPay() {
|
10
|
+
return (
|
11
|
+
<GPayOption
|
12
|
+
translations={{
|
13
|
+
title: 'Pay with GarantiPay',
|
14
|
+
description:
|
15
|
+
'Click the button below to pay for your order using GarantiPay.',
|
16
|
+
button: 'Pay Now'
|
17
|
+
}}
|
18
|
+
titleClassName="text-4xl"
|
19
|
+
/>
|
20
|
+
);
|
21
|
+
}
|
22
|
+
```
|
23
|
+
|
24
|
+
##### File Path: src/app/[commerce]/[locale]/[currency]/orders/garanti-pay-redirect/page.tsx
|
25
|
+
|
26
|
+
```javascript
|
27
|
+
import { GarantiPayRedirect } from '@akinon/pz-gpay/src/routes/garanti-pay-redirect';
|
28
|
+
|
29
|
+
export default GarantiPayRedirect;
|
30
|
+
```
|
31
|
+
|
32
|
+
### Props
|
33
|
+
| Properties | Type | Description |
|
34
|
+
|----------------------|--------|--------------------------------------------|
|
35
|
+
| translations | object | The translations of the component. |
|
36
|
+
| titleClassName | string | The CSS class to apply to the title. |
|
37
|
+
| descriptionClassName | string | The CSS class to apply to the description. |
|
38
|
+
| buttonClassName | string | The CSS class to apply to the button. |
|
package/src/endpoints.ts
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
import { CheckoutContext, PreOrder } from '@akinon/next/types';
|
2
|
+
import { api } from '@akinon/next/data/client/api';
|
3
|
+
import { buildClientRequestUrl } from '@akinon/next/utils';
|
4
|
+
import { setPaymentStepBusy } from '@akinon/next/redux/reducers/checkout';
|
5
|
+
|
6
|
+
interface CheckoutResponse {
|
7
|
+
pre_order?: PreOrder;
|
8
|
+
errors: {
|
9
|
+
non_field_errors: string;
|
10
|
+
};
|
11
|
+
context_list?: CheckoutContext[];
|
12
|
+
template_name?: string;
|
13
|
+
redirect_url?: string;
|
14
|
+
}
|
15
|
+
|
16
|
+
export const gpayApi = api.injectEndpoints({
|
17
|
+
endpoints: (build) => ({
|
18
|
+
setGPayMethod: build.mutation<CheckoutResponse, void>({
|
19
|
+
query: () => ({
|
20
|
+
url: buildClientRequestUrl(`/orders/checkout?page=GPaySelectionPage`),
|
21
|
+
method: 'POST',
|
22
|
+
body: {}
|
23
|
+
}),
|
24
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
25
|
+
dispatch(setPaymentStepBusy(true));
|
26
|
+
await queryFulfilled;
|
27
|
+
dispatch(setPaymentStepBusy(false));
|
28
|
+
}
|
29
|
+
}),
|
30
|
+
setCompleteGPay: build.mutation<CheckoutResponse, void>({
|
31
|
+
query: () => ({
|
32
|
+
url: buildClientRequestUrl(`/orders/checkout?page=GPayRedirectPage`),
|
33
|
+
method: 'POST',
|
34
|
+
body: {}
|
35
|
+
}),
|
36
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
37
|
+
dispatch(setPaymentStepBusy(true));
|
38
|
+
await queryFulfilled;
|
39
|
+
dispatch(setPaymentStepBusy(false));
|
40
|
+
}
|
41
|
+
})
|
42
|
+
})
|
43
|
+
});
|
44
|
+
|
45
|
+
export const { useSetGPayMethodMutation, useSetCompleteGPayMutation } = gpayApi;
|
package/src/index.tsx
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export * from './views/gpay-option';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { cookies } from 'next/headers';
|
2
|
+
import settings from 'settings';
|
3
|
+
import { redirect } from 'next/navigation';
|
4
|
+
import { ROUTES } from 'routes';
|
5
|
+
|
6
|
+
export const GarantiPayRedirect = async () => {
|
7
|
+
const nextCookies = cookies();
|
8
|
+
const sessionId = nextCookies.get('osessionid')?.value;
|
9
|
+
|
10
|
+
if (!sessionId) {
|
11
|
+
return redirect(ROUTES.CHECKOUT);
|
12
|
+
}
|
13
|
+
|
14
|
+
const commerceUrl = settings.commerceUrl;
|
15
|
+
|
16
|
+
const response = await fetch(`${commerceUrl}/orders/garanti-pay-redirect`, {
|
17
|
+
headers: {
|
18
|
+
Cookie: nextCookies.toString()
|
19
|
+
}
|
20
|
+
});
|
21
|
+
|
22
|
+
const data = await response.text();
|
23
|
+
|
24
|
+
if (!data.includes('form3D')) {
|
25
|
+
return redirect(ROUTES.CHECKOUT);
|
26
|
+
}
|
27
|
+
|
28
|
+
return <div dangerouslySetInnerHTML={{ __html: data }}></div>;
|
29
|
+
};
|
@@ -0,0 +1,111 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import * as yup from 'yup';
|
4
|
+
import { ReactNode, useEffect, useState } from 'react';
|
5
|
+
import { RootState } from 'redux/store';
|
6
|
+
import { SubmitHandler, useForm } from 'react-hook-form';
|
7
|
+
import { useAppSelector } from '@akinon/next/redux/hooks';
|
8
|
+
import { useSetPaymentOptionMutation } from '@akinon/next/data/client/checkout';
|
9
|
+
import {
|
10
|
+
useSetCompleteGPayMutation,
|
11
|
+
useSetGPayMethodMutation
|
12
|
+
} from '../endpoints';
|
13
|
+
import { twMerge } from 'tailwind-merge';
|
14
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
15
|
+
import { Button } from '@akinon/next/components/button';
|
16
|
+
|
17
|
+
const gpayFormSchema = yup.object();
|
18
|
+
|
19
|
+
enum Elements {
|
20
|
+
title,
|
21
|
+
description,
|
22
|
+
button
|
23
|
+
}
|
24
|
+
|
25
|
+
type ElementClassKey = `${keyof typeof Elements}ClassName`;
|
26
|
+
|
27
|
+
type GpayOptionProps = {
|
28
|
+
[key in ElementClassKey]?: string;
|
29
|
+
} & {
|
30
|
+
translations?: {
|
31
|
+
[element in keyof typeof Elements]?: string | ReactNode;
|
32
|
+
};
|
33
|
+
};
|
34
|
+
|
35
|
+
export function GPayOption(props: GpayOptionProps) {
|
36
|
+
const [formError, setFormError] = useState(null);
|
37
|
+
|
38
|
+
const { preOrder } = useAppSelector((state: RootState) => state.checkout);
|
39
|
+
const isPaymentStepBusy = useAppSelector(
|
40
|
+
(state: RootState) => state.checkout.steps.payment.busy
|
41
|
+
);
|
42
|
+
|
43
|
+
const { handleSubmit } = useForm({
|
44
|
+
resolver: yupResolver(gpayFormSchema)
|
45
|
+
});
|
46
|
+
|
47
|
+
const [setPaymentOption] = useSetPaymentOptionMutation();
|
48
|
+
const [setGPayMethod] = useSetGPayMethodMutation();
|
49
|
+
const [setCompleteGPay] = useSetCompleteGPayMutation();
|
50
|
+
|
51
|
+
const onSubmit: SubmitHandler<null> = async () => {
|
52
|
+
if (isPaymentStepBusy) return;
|
53
|
+
|
54
|
+
setFormError([]);
|
55
|
+
|
56
|
+
await setGPayMethod();
|
57
|
+
|
58
|
+
const response = await setCompleteGPay().unwrap();
|
59
|
+
|
60
|
+
if (response?.errors?.non_field_errors) {
|
61
|
+
setFormError(response?.errors?.non_field_errors);
|
62
|
+
}
|
63
|
+
};
|
64
|
+
|
65
|
+
// this is necessary because we need to set the payment_option after the redirect
|
66
|
+
useEffect(() => {
|
67
|
+
setPaymentOption(preOrder.payment_option.pk);
|
68
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
69
|
+
}, []);
|
70
|
+
|
71
|
+
return (
|
72
|
+
<form
|
73
|
+
id="gpay_payment"
|
74
|
+
className="p-5 space-y-5 lg:p-10"
|
75
|
+
onSubmit={handleSubmit(onSubmit)}
|
76
|
+
>
|
77
|
+
<h1 className={twMerge('text-2xl font-bold', props.titleClassName)}>
|
78
|
+
{props.translations?.title ?? 'Pay with GarantiPay'}
|
79
|
+
</h1>
|
80
|
+
<div className={twMerge('text-sm', props.descriptionClassName)}>
|
81
|
+
{props.translations?.description ? (
|
82
|
+
props.translations.description
|
83
|
+
) : (
|
84
|
+
<>
|
85
|
+
<li>
|
86
|
+
Click on the “Pay with GarantiPay” button. Guarantee for payment
|
87
|
+
you will be redirected to the payment page.
|
88
|
+
</li>
|
89
|
+
<li>
|
90
|
+
Log in to your account with your username and password on the
|
91
|
+
Garanti Pay page. If you do not have a Garanti Pay membership,
|
92
|
+
create a new membership you can create.
|
93
|
+
</li>
|
94
|
+
<li>
|
95
|
+
Complete the payment process by selecting your card and payment
|
96
|
+
type.
|
97
|
+
</li>
|
98
|
+
</>
|
99
|
+
)}
|
100
|
+
</div>
|
101
|
+
<Button className={twMerge('w-full mt-3', props.buttonClassName)}>
|
102
|
+
{props.translations?.button ?? 'Pay with GarantiPay'}
|
103
|
+
</Button>
|
104
|
+
{formError && (
|
105
|
+
<div className="w-full text-xs text-start px-1 mt-3 text-error">
|
106
|
+
{formError}
|
107
|
+
</div>
|
108
|
+
)}
|
109
|
+
</form>
|
110
|
+
);
|
111
|
+
}
|