@akinon/pz-gpay 1.15.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }