@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 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-gpay
2
+
3
+ ## 1.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 96dd05f: ZERO-2385: Add gitattributes file
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. |
@@ -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
+ }