@akinon/next 1.14.1 → 1.16.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/CHANGELOG.md +15 -0
- package/assets/styles/index.scss +28 -28
- package/bin/pz-install-plugins.js +5 -2
- package/components/image.tsx +8 -0
- package/components/index.ts +1 -0
- package/components/modal.tsx +66 -0
- package/components/plugin-module.tsx +10 -8
- package/components/selected-payment-option-view.tsx +0 -4
- package/data/client/address.ts +107 -107
- package/data/client/b2b.ts +106 -106
- package/data/client/checkout.ts +516 -516
- package/data/server/form.ts +4 -4
- package/hooks/use-payment-options.ts +1 -1
- package/instrumentation/index.ts +5 -0
- package/instrumentation/node.ts +20 -0
- package/middlewares/complete-gpay.ts +159 -0
- package/middlewares/default.ts +102 -99
- package/middlewares/index.ts +3 -1
- package/package.json +7 -2
- package/plugins.js +1 -1
- package/redux/middlewares/checkout.ts +265 -265
- package/redux/reducers/index.ts +14 -14
- package/types/commerce/b2b.ts +117 -117
- package/utils/generate-commerce-search-params.ts +22 -22
- package/utils/get-currency.ts +29 -29
- package/with-pz-config.js +2 -2
- package/.editorconfig +0 -7
package/data/server/form.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Cache, CacheKey } from
|
|
2
|
-
import { FormType } from
|
|
1
|
+
import { Cache, CacheKey } from "../../lib/cache";
|
|
2
|
+
import { FormType } from "../../types/commerce/form";
|
|
3
3
|
|
|
4
|
-
import appFetch from
|
|
5
|
-
import { form } from
|
|
4
|
+
import appFetch from "../../utils/app-fetch";
|
|
5
|
+
import { form } from "../urls";
|
|
6
6
|
|
|
7
7
|
const getFormDataHandler = (pk: number) => {
|
|
8
8
|
return async function () {
|
|
@@ -17,7 +17,7 @@ export const usePaymentOptions = () => {
|
|
|
17
17
|
const paymentTypeToPluginMap = {
|
|
18
18
|
pay_on_delivery: 'pz-pay-on-delivery',
|
|
19
19
|
bkm_express: 'pz-bkm',
|
|
20
|
-
|
|
20
|
+
credit_payment: 'pz-credit-payment',
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const isInitialTypeIncluded = (type: string) => initialTypes.has(type);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
2
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
3
|
+
import { Resource } from '@opentelemetry/resources';
|
|
4
|
+
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
|
5
|
+
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
|
|
6
|
+
|
|
7
|
+
const sdk = new NodeSDK({
|
|
8
|
+
resource: new Resource({
|
|
9
|
+
[SemanticResourceAttributes.SERVICE_NAME]: 'pz-next-app'
|
|
10
|
+
}),
|
|
11
|
+
spanProcessor: new SimpleSpanProcessor(
|
|
12
|
+
new OTLPTraceExporter({
|
|
13
|
+
url: `${
|
|
14
|
+
process.env.PZ_DASHBOARD_URL ?? 'http://localhost:3005'
|
|
15
|
+
}/api/traces`
|
|
16
|
+
})
|
|
17
|
+
)
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
sdk.start();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
|
|
2
|
+
import Settings from 'settings';
|
|
3
|
+
import { Buffer } from 'buffer';
|
|
4
|
+
import logger from '../utils/log';
|
|
5
|
+
import { getUrlPathWithLocale } from '../utils/localization';
|
|
6
|
+
import { PzNextRequest } from '.';
|
|
7
|
+
|
|
8
|
+
const streamToString = async (stream: ReadableStream<Uint8Array> | null) => {
|
|
9
|
+
if (stream) {
|
|
10
|
+
const chunks = [];
|
|
11
|
+
let result = '';
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
for await (const chunk of stream as any) {
|
|
15
|
+
chunks.push(Buffer.from(chunk));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
result = Buffer.concat(chunks).toString('utf-8');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error('Error while reading body stream', {
|
|
21
|
+
middleware: 'complete-gpay',
|
|
22
|
+
error
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const withCompleteGpay =
|
|
32
|
+
(middleware: NextMiddleware) =>
|
|
33
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
34
|
+
const url = req.nextUrl.clone();
|
|
35
|
+
const ip = req.headers.get('x-forwarded-for') ?? '';
|
|
36
|
+
|
|
37
|
+
if (url.search.indexOf('GPayCompletePage') === -1) {
|
|
38
|
+
return middleware(req, event);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
|
|
42
|
+
const requestHeaders = {
|
|
43
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
44
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
45
|
+
Cookie: `osessionid=${req.cookies.get('osessionid')?.value ?? ''}`,
|
|
46
|
+
'x-currency': req.cookies.get('pz-currency')?.value ?? ''
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const body = await streamToString(req.body);
|
|
51
|
+
|
|
52
|
+
const request = await fetch(requestUrl, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: requestHeaders,
|
|
55
|
+
body
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
logger.info('Complete GPay payment request', {
|
|
59
|
+
requestUrl,
|
|
60
|
+
status: request.status,
|
|
61
|
+
requestHeaders,
|
|
62
|
+
ip
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const response = await request.json();
|
|
66
|
+
|
|
67
|
+
const { context_list: contextList, errors } = response;
|
|
68
|
+
const redirectionContext = contextList?.find(
|
|
69
|
+
(context) => context.page_context?.redirect_url
|
|
70
|
+
);
|
|
71
|
+
const redirectUrl = redirectionContext?.page_context?.redirect_url;
|
|
72
|
+
|
|
73
|
+
if (errors && Object.keys(errors).length) {
|
|
74
|
+
logger.error('Error while completing GPay payment', {
|
|
75
|
+
middleware: 'complete-gpay',
|
|
76
|
+
errors,
|
|
77
|
+
requestHeaders,
|
|
78
|
+
ip
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return NextResponse.redirect(
|
|
82
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
83
|
+
'/orders/checkout/',
|
|
84
|
+
req.cookies.get('pz-locale')?.value
|
|
85
|
+
)}`,
|
|
86
|
+
{
|
|
87
|
+
status: 303,
|
|
88
|
+
headers: {
|
|
89
|
+
'Set-Cookie': `pz-pos-error=${JSON.stringify(errors)}; path=/;`
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
logger.info('Order success page context list', {
|
|
96
|
+
middleware: 'complete-gpay',
|
|
97
|
+
contextList,
|
|
98
|
+
ip
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!redirectUrl) {
|
|
102
|
+
logger.warn(
|
|
103
|
+
'No redirection url for order success page found in page_context. Redirecting to checkout page.',
|
|
104
|
+
{
|
|
105
|
+
middleware: 'complete-gpay',
|
|
106
|
+
requestHeaders,
|
|
107
|
+
response: JSON.stringify(response),
|
|
108
|
+
ip
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
113
|
+
'/orders/checkout/',
|
|
114
|
+
req.cookies.get('pz-locale')?.value
|
|
115
|
+
)}`;
|
|
116
|
+
|
|
117
|
+
return NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
|
|
121
|
+
redirectUrl,
|
|
122
|
+
req.cookies.get('pz-locale')?.value
|
|
123
|
+
)}`;
|
|
124
|
+
|
|
125
|
+
logger.info('Redirecting to order success page', {
|
|
126
|
+
middleware: 'complete-gpay',
|
|
127
|
+
redirectUrlWithLocale,
|
|
128
|
+
ip
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Using POST method while redirecting causes an error,
|
|
132
|
+
// So we use 303 status code to change the method to GET
|
|
133
|
+
const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
|
|
134
|
+
|
|
135
|
+
nextResponse.headers.set(
|
|
136
|
+
'Set-Cookie',
|
|
137
|
+
request.headers.get('set-cookie') ?? ''
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return nextResponse;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.error('Error while completing GPay payment', {
|
|
143
|
+
middleware: 'complete-gpay',
|
|
144
|
+
error,
|
|
145
|
+
requestHeaders,
|
|
146
|
+
ip
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return NextResponse.redirect(
|
|
150
|
+
`${url.origin}${getUrlPathWithLocale(
|
|
151
|
+
'/orders/checkout/',
|
|
152
|
+
req.cookies.get('pz-locale')?.value
|
|
153
|
+
)}`,
|
|
154
|
+
303
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export default withCompleteGpay;
|
package/middlewares/default.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
|
|
|
2
2
|
import Settings from 'settings';
|
|
3
3
|
import {
|
|
4
4
|
PzNextRequest,
|
|
5
|
+
withCompleteGpay,
|
|
5
6
|
withOauthLogin,
|
|
6
7
|
withPrettyUrl,
|
|
7
8
|
withRedirectionPayment,
|
|
@@ -122,128 +123,130 @@ const withPzDefault =
|
|
|
122
123
|
withRedirectionPayment(
|
|
123
124
|
withThreeDRedirection(
|
|
124
125
|
withUrlRedirection(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
NextResponse
|
|
126
|
+
withCompleteGpay(
|
|
127
|
+
async (req: PzNextRequest, event: NextFetchEvent) => {
|
|
128
|
+
let middlewareResult: NextResponse | void =
|
|
129
|
+
NextResponse.next();
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
url.basePath = `/${commerceUrl}`;
|
|
140
|
-
url.pathname = `/${
|
|
141
|
-
locale.length ? `${locale}/` : ''
|
|
142
|
-
}${currency}${prettyUrl ?? pathnameWithoutLocale}`;
|
|
143
|
-
|
|
144
|
-
Settings.rewrites.forEach((rewrite) => {
|
|
145
|
-
url.pathname = url.pathname.replace(
|
|
146
|
-
rewrite.source,
|
|
147
|
-
rewrite.destination
|
|
131
|
+
try {
|
|
132
|
+
const { locale, prettyUrl, currency } =
|
|
133
|
+
req.middlewareParams.rewrites;
|
|
134
|
+
const { defaultLocaleValue } = Settings.localization;
|
|
135
|
+
const url = req.nextUrl.clone();
|
|
136
|
+
const pathnameWithoutLocale = url.pathname.replace(
|
|
137
|
+
urlLocaleMatcherRegex,
|
|
138
|
+
''
|
|
148
139
|
);
|
|
149
|
-
});
|
|
150
140
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
141
|
+
url.basePath = `/${commerceUrl}`;
|
|
142
|
+
url.pathname = `/${
|
|
143
|
+
locale.length ? `${locale}/` : ''
|
|
144
|
+
}${currency}${prettyUrl ?? pathnameWithoutLocale}`;
|
|
155
145
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
middlewareResult.headers.get(
|
|
161
|
-
'pz-override-response'
|
|
162
|
-
) !== 'true'
|
|
163
|
-
) {
|
|
164
|
-
middlewareResult.headers.set(
|
|
165
|
-
'x-middleware-rewrite',
|
|
166
|
-
url.href
|
|
146
|
+
Settings.rewrites.forEach((rewrite) => {
|
|
147
|
+
url.pathname = url.pathname.replace(
|
|
148
|
+
rewrite.source,
|
|
149
|
+
rewrite.destination
|
|
167
150
|
);
|
|
168
|
-
}
|
|
169
|
-
} else {
|
|
170
|
-
// if middleware.ts doesn't have a return value.
|
|
171
|
-
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
151
|
+
});
|
|
172
152
|
|
|
173
|
-
middlewareResult =
|
|
174
|
-
|
|
153
|
+
middlewareResult = (await middleware(
|
|
154
|
+
req,
|
|
155
|
+
event
|
|
156
|
+
)) as NextResponse | void;
|
|
175
157
|
|
|
176
|
-
|
|
177
|
-
middlewareResult
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
158
|
+
// if middleware.ts has a return value for current url
|
|
159
|
+
if (middlewareResult instanceof NextResponse) {
|
|
160
|
+
// pz-override-response header is used to prevent 404 page for custom responses.
|
|
161
|
+
if (
|
|
162
|
+
middlewareResult.headers.get(
|
|
163
|
+
'pz-override-response'
|
|
164
|
+
) !== 'true'
|
|
165
|
+
) {
|
|
166
|
+
middlewareResult.headers.set(
|
|
167
|
+
'x-middleware-rewrite',
|
|
168
|
+
url.href
|
|
169
|
+
);
|
|
186
170
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
sameSite: 'none',
|
|
191
|
-
secure: true,
|
|
192
|
-
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7) // 7 days
|
|
193
|
-
});
|
|
171
|
+
} else {
|
|
172
|
+
// if middleware.ts doesn't have a return value.
|
|
173
|
+
// e.g. NextResponse.next() doesn't exist in middleware.ts
|
|
194
174
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
req.cookies.get('pz-locale').value !== locale
|
|
198
|
-
) {
|
|
199
|
-
logger.debug('Locale changed', {
|
|
200
|
-
locale,
|
|
201
|
-
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
202
|
-
ip
|
|
203
|
-
});
|
|
204
|
-
}
|
|
175
|
+
middlewareResult = NextResponse.rewrite(url);
|
|
176
|
+
}
|
|
205
177
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
178
|
+
if (!url.pathname.startsWith(`/${currency}/orders`)) {
|
|
179
|
+
middlewareResult.cookies.set(
|
|
180
|
+
'pz-locale',
|
|
181
|
+
locale?.length > 0 ? locale : defaultLocaleValue,
|
|
182
|
+
{
|
|
183
|
+
sameSite: 'none',
|
|
184
|
+
secure: true,
|
|
185
|
+
expires: new Date(
|
|
186
|
+
Date.now() + 1000 * 60 * 60 * 24 * 7
|
|
187
|
+
) // 7 days
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
middlewareResult.cookies.set('pz-currency', currency, {
|
|
192
|
+
sameSite: 'none',
|
|
193
|
+
secure: true,
|
|
194
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7) // 7 days
|
|
195
|
+
});
|
|
210
196
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
197
|
+
if (
|
|
198
|
+
req.cookies.get('pz-locale') &&
|
|
199
|
+
req.cookies.get('pz-locale').value !== locale
|
|
200
|
+
) {
|
|
201
|
+
logger.debug('Locale changed', {
|
|
202
|
+
locale,
|
|
203
|
+
oldLocale: req.cookies.get('pz-locale')?.value,
|
|
204
|
+
ip
|
|
205
|
+
});
|
|
206
|
+
}
|
|
214
207
|
|
|
215
|
-
if (process.env.ACC_APP_VERSION) {
|
|
216
208
|
middlewareResult.headers.set(
|
|
217
|
-
'
|
|
218
|
-
|
|
209
|
+
'pz-url',
|
|
210
|
+
req.nextUrl.toString()
|
|
219
211
|
);
|
|
220
|
-
}
|
|
221
212
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
213
|
+
if (req.cookies.get('pz-set-currency')) {
|
|
214
|
+
middlewareResult.cookies.delete('pz-set-currency');
|
|
215
|
+
}
|
|
225
216
|
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
217
|
+
if (process.env.ACC_APP_VERSION) {
|
|
218
|
+
middlewareResult.headers.set(
|
|
219
|
+
'acc-app-version',
|
|
220
|
+
process.env.ACC_APP_VERSION
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Set CSRF token if not set
|
|
225
|
+
try {
|
|
226
|
+
const url = `${Settings.commerceUrl}${user.csrfToken}`;
|
|
227
|
+
|
|
228
|
+
if (!req.cookies.get('csrftoken')) {
|
|
229
|
+
const { csrf_token } = await (
|
|
230
|
+
await fetch(url)
|
|
231
|
+
).json();
|
|
232
|
+
middlewareResult.cookies.set('csrftoken', csrf_token);
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
logger.error('CSRF Error', {
|
|
236
|
+
error,
|
|
237
|
+
ip
|
|
238
|
+
});
|
|
231
239
|
}
|
|
232
240
|
} catch (error) {
|
|
233
|
-
logger.error('
|
|
241
|
+
logger.error('withPzDefault Error', {
|
|
234
242
|
error,
|
|
235
243
|
ip
|
|
236
244
|
});
|
|
237
245
|
}
|
|
238
|
-
} catch (error) {
|
|
239
|
-
logger.error('withPzDefault Error', {
|
|
240
|
-
error,
|
|
241
|
-
ip
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
246
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
+
return middlewareResult;
|
|
248
|
+
}
|
|
249
|
+
)
|
|
247
250
|
)
|
|
248
251
|
)
|
|
249
252
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import withRedirectionPayment from './redirection-payment';
|
|
|
5
5
|
import withLocale from './locale';
|
|
6
6
|
import withOauthLogin from './oauth-login';
|
|
7
7
|
import withUrlRedirection from './url-redirection';
|
|
8
|
+
import withCompleteGpay from './complete-gpay';
|
|
8
9
|
import { NextRequest } from 'next/server';
|
|
9
10
|
|
|
10
11
|
export {
|
|
@@ -14,7 +15,8 @@ export {
|
|
|
14
15
|
withRedirectionPayment,
|
|
15
16
|
withLocale,
|
|
16
17
|
withOauthLogin,
|
|
17
|
-
withUrlRedirection
|
|
18
|
+
withUrlRedirection,
|
|
19
|
+
withCompleteGpay
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export interface PzNextRequest extends NextRequest {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.16.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"pz-postdev": "bin/pz-postdev.js"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@opentelemetry/sdk-node": "0.46.0",
|
|
18
|
+
"@opentelemetry/exporter-trace-otlp-http": "0.46.0",
|
|
19
|
+
"@opentelemetry/resources": "1.19.0",
|
|
20
|
+
"@opentelemetry/semantic-conventions": "1.19.0",
|
|
21
|
+
"@opentelemetry/sdk-trace-node": "1.19.0",
|
|
17
22
|
"@reduxjs/toolkit": "1.9.7",
|
|
18
23
|
"cross-spawn": "7.0.3",
|
|
19
24
|
"react-redux": "8.1.3",
|
|
@@ -26,7 +31,7 @@
|
|
|
26
31
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
27
32
|
"@typescript-eslint/parser": "6.7.4",
|
|
28
33
|
"eslint": "^8.14.0",
|
|
29
|
-
"@akinon/eslint-plugin-projectzero": "1.
|
|
34
|
+
"@akinon/eslint-plugin-projectzero": "1.16.0",
|
|
30
35
|
"eslint-config-prettier": "8.5.0"
|
|
31
36
|
}
|
|
32
37
|
}
|
package/plugins.js
CHANGED