@akinon/next 2.0.0-beta.16 → 2.0.0-beta.17
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/CHANGELOG.md +12 -0
- package/api/barcode-search.ts +59 -0
- package/bin/pz-generate-routes.js +11 -1
- package/components/plugin-module.tsx +2 -1
- package/data/client/account.ts +5 -1
- package/data/client/checkout.ts +23 -10
- package/middlewares/bfcache-headers.ts +18 -0
- package/middlewares/default.ts +8 -6
- package/middlewares/index.ts +3 -1
- package/middlewares/oauth-login.ts +200 -57
- package/package.json +2 -2
- package/redux/middlewares/checkout.ts +9 -0
- package/redux/reducers/checkout.ts +6 -0
- package/types/commerce/checkout.ts +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 2.0.0-beta.17
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8218dafa: ZERO-4160: Refactor oauth-login middleware to use fetch for login and callback responses
|
|
8
|
+
- 2a8ddcf6: ZERO-4111: Add total balance field to store credit localization and update related components
|
|
9
|
+
- 8a7fd0f4: ZERO-4065: Add '[segment]' to skipSegments in route generation
|
|
10
|
+
- 36143125: ZERO-3987: Add barcode scanner functionality with modal and button
|
|
11
|
+
- f7e0f646: ZERO-4032: Add bfcache-headers middleware, integrate it into the default chain, and introduce a new environment variable for control.
|
|
12
|
+
- 94a86fcc: ZERO-4065: Expand skipSegments array to include additional segments for route generation
|
|
13
|
+
- bcaad120: ZERO-4158: Add logging for OAuth login and callback redirects
|
|
14
|
+
|
|
3
15
|
## 2.0.0-beta.16
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import Settings from 'settings';
|
|
3
|
+
|
|
4
|
+
export async function GET(request: NextRequest) {
|
|
5
|
+
try {
|
|
6
|
+
const { searchParams } = new URL(request.url);
|
|
7
|
+
const barcode = searchParams.get('search_text');
|
|
8
|
+
|
|
9
|
+
if (!barcode) {
|
|
10
|
+
return NextResponse.json(
|
|
11
|
+
{ error: 'Missing search_text parameter (barcode)' },
|
|
12
|
+
{ status: 400 }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (Settings.commerceUrl === 'default') {
|
|
17
|
+
return NextResponse.json(
|
|
18
|
+
{ error: 'Commerce URL is not configured' },
|
|
19
|
+
{ status: 500 }
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const queryParams = new URLSearchParams();
|
|
24
|
+
queryParams.append('search_text', barcode);
|
|
25
|
+
|
|
26
|
+
searchParams.forEach((value, key) => {
|
|
27
|
+
if (key !== 'search_text') {
|
|
28
|
+
queryParams.append(key, value);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const apiUrl = `${Settings.commerceUrl}/list/?${queryParams.toString()}`;
|
|
33
|
+
|
|
34
|
+
const headers: Record<string, string> = {
|
|
35
|
+
Accept: 'application/json',
|
|
36
|
+
'Content-Type': 'application/json'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const response = await fetch(apiUrl, {
|
|
40
|
+
method: 'GET',
|
|
41
|
+
headers
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
return NextResponse.json(
|
|
46
|
+
{ error: `API request failed with status: ${response.status}` },
|
|
47
|
+
{ status: response.status }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
return NextResponse.json(data);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return NextResponse.json(
|
|
55
|
+
{ error: (error as Error).message },
|
|
56
|
+
{ status: 500 }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -25,7 +25,17 @@ const generateRoutes = () => {
|
|
|
25
25
|
const routes = [];
|
|
26
26
|
const excludedDirs = ['api', 'pz-not-found'];
|
|
27
27
|
|
|
28
|
-
const skipSegments = [
|
|
28
|
+
const skipSegments = [
|
|
29
|
+
'[pz]',
|
|
30
|
+
'[commerce]',
|
|
31
|
+
'[locale]',
|
|
32
|
+
'[currency]',
|
|
33
|
+
'[session]',
|
|
34
|
+
'[segment]',
|
|
35
|
+
'[url]',
|
|
36
|
+
'[theme]',
|
|
37
|
+
'[member_type]'
|
|
38
|
+
];
|
|
29
39
|
const skipCatchAllRoutes = ['[...prettyurl]', '[...not_found]'];
|
|
30
40
|
|
|
31
41
|
const walkDirectory = (dir, basePath = '') => {
|
|
@@ -55,6 +55,7 @@ export enum Component {
|
|
|
55
55
|
SavedCard = 'SavedCardOption',
|
|
56
56
|
VirtualTryOnPlugin = 'VirtualTryOnPlugin',
|
|
57
57
|
BasketVirtualTryOn = 'BasketVirtualTryOn',
|
|
58
|
+
BarcodeScannerPlugin = 'BarcodeScannerPlugin',
|
|
58
59
|
IyzicoSavedCard = 'IyzicoSavedCardOption',
|
|
59
60
|
Hepsipay = 'Hepsipay',
|
|
60
61
|
FlowPayment = 'FlowPayment',
|
|
@@ -117,7 +118,7 @@ const PluginComponents = new Map([
|
|
|
117
118
|
[Plugin.FlowPayment, [Component.FlowPayment]],
|
|
118
119
|
[
|
|
119
120
|
Plugin.VirtualTryOn,
|
|
120
|
-
[Component.VirtualTryOnPlugin, Component.BasketVirtualTryOn]
|
|
121
|
+
[Component.VirtualTryOnPlugin, Component.BasketVirtualTryOn, Component.BarcodeScannerPlugin]
|
|
121
122
|
],
|
|
122
123
|
[Plugin.Hepsipay, [Component.Hepsipay]],
|
|
123
124
|
[Plugin.MasterpassRest, [Component.MasterpassRest]],
|
package/data/client/account.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
AccountOrderCancellation,
|
|
8
8
|
AccountOrderCancellationReason,
|
|
9
9
|
ContactFormType,
|
|
10
|
+
LoyaltyBalanceItem,
|
|
10
11
|
Order,
|
|
11
12
|
Quotations
|
|
12
13
|
} from '../../types';
|
|
@@ -220,7 +221,10 @@ const accountApi = api.injectEndpoints({
|
|
|
220
221
|
method: 'PATCH'
|
|
221
222
|
})
|
|
222
223
|
}),
|
|
223
|
-
getLoyaltyBalance: builder.query<
|
|
224
|
+
getLoyaltyBalance: builder.query<
|
|
225
|
+
{ balance: number; balances?: LoyaltyBalanceItem[] },
|
|
226
|
+
void
|
|
227
|
+
>({
|
|
224
228
|
query: () => buildClientRequestUrl(account.loyaltyBalance)
|
|
225
229
|
}),
|
|
226
230
|
getLoyaltyTransactions: builder.query<LoyaltyTransactions, void>({
|
package/data/client/checkout.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../../redux/reducers/checkout';
|
|
11
11
|
import {
|
|
12
12
|
CheckoutContext,
|
|
13
|
+
AccountUsage,
|
|
13
14
|
ExtraField,
|
|
14
15
|
GuestLoginFormParams,
|
|
15
16
|
Order,
|
|
@@ -776,16 +777,28 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
776
777
|
url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage)
|
|
777
778
|
})
|
|
778
779
|
}),
|
|
779
|
-
payWithLoyaltyBalance: build.mutation<
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
780
|
+
payWithLoyaltyBalance: build.mutation<
|
|
781
|
+
any,
|
|
782
|
+
string | { account_usages: AccountUsage[] }
|
|
783
|
+
>({
|
|
784
|
+
query: (params) => {
|
|
785
|
+
const isAccountUsages =
|
|
786
|
+
typeof params === 'object' && 'account_usages' in params;
|
|
787
|
+
|
|
788
|
+
return {
|
|
789
|
+
url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage, {
|
|
790
|
+
useFormData: true
|
|
791
|
+
}),
|
|
792
|
+
method: 'POST',
|
|
793
|
+
body: isAccountUsages
|
|
794
|
+
? {
|
|
795
|
+
account_usages: JSON.stringify(params.account_usages)
|
|
796
|
+
}
|
|
797
|
+
: {
|
|
798
|
+
loyalty_amount_to_use: params
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
},
|
|
789
802
|
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
790
803
|
dispatch(setPaymentStepBusy(true));
|
|
791
804
|
dispatch(setPaymentOptions([]));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NextMiddleware } from 'next/server';
|
|
2
|
+
|
|
3
|
+
const withBfcacheHeaders = (middleware: NextMiddleware): NextMiddleware => {
|
|
4
|
+
return async (req, event) => {
|
|
5
|
+
const response = await middleware(req, event);
|
|
6
|
+
|
|
7
|
+
if (process.env.BF_CACHE === 'true' && response) {
|
|
8
|
+
response.headers.set(
|
|
9
|
+
'Cache-Control',
|
|
10
|
+
'private, no-cache, max-age=0, must-revalidate'
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return response;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default withBfcacheHeaders;
|
package/middlewares/default.ts
CHANGED
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
withUrlRedirection,
|
|
15
15
|
withCompleteWallet,
|
|
16
16
|
withWalletCompleteRedirection,
|
|
17
|
-
withMasterpassRestCallback
|
|
17
|
+
withMasterpassRestCallback,
|
|
18
|
+
withBfcacheHeaders
|
|
18
19
|
} from '.';
|
|
19
20
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
20
21
|
import { getPzSegmentsConfig, encodePzValue, isLegacyMode } from '../utils/pz-segments';
|
|
@@ -256,10 +257,11 @@ const withPzDefault =
|
|
|
256
257
|
withCompleteWallet(
|
|
257
258
|
withWalletCompleteRedirection(
|
|
258
259
|
withMasterpassRestCallback(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
withBfcacheHeaders(
|
|
261
|
+
async (
|
|
262
|
+
req: PzNextRequest,
|
|
263
|
+
event: NextFetchEvent
|
|
264
|
+
) => {
|
|
263
265
|
let middlewareResult: NextResponse | void =
|
|
264
266
|
NextResponse.next();
|
|
265
267
|
|
|
@@ -563,7 +565,7 @@ const withPzDefault =
|
|
|
563
565
|
}
|
|
564
566
|
|
|
565
567
|
return middlewareResult;
|
|
566
|
-
}
|
|
568
|
+
})
|
|
567
569
|
)
|
|
568
570
|
)
|
|
569
571
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import withSavedCardRedirection from './saved-card-redirection';
|
|
|
12
12
|
import withCompleteWallet from './complete-wallet';
|
|
13
13
|
import withWalletCompleteRedirection from './wallet-complete-redirection';
|
|
14
14
|
import withMasterpassRestCallback from './masterpass-rest-callback';
|
|
15
|
+
import withBfcacheHeaders from './bfcache-headers';
|
|
15
16
|
import { NextRequest } from 'next/server';
|
|
16
17
|
|
|
17
18
|
export {
|
|
@@ -28,7 +29,8 @@ export {
|
|
|
28
29
|
withSavedCardRedirection,
|
|
29
30
|
withCompleteWallet,
|
|
30
31
|
withWalletCompleteRedirection,
|
|
31
|
-
withMasterpassRestCallback
|
|
32
|
+
withMasterpassRestCallback,
|
|
33
|
+
withBfcacheHeaders
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
export interface PzNextRequest extends NextRequest {
|
|
@@ -6,78 +6,221 @@ import {
|
|
|
6
6
|
} from 'next/server';
|
|
7
7
|
import Settings from 'settings';
|
|
8
8
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
9
|
+
import logger from '../utils/log';
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
const LOGIN_URL_REGEX = /^\/(\w+)\/login\/?$/;
|
|
12
|
+
const CALLBACK_URL_REGEX = /^\/(\w+)\/login\/callback\/?$/;
|
|
13
|
+
|
|
14
|
+
function buildCommerceHeaders(req: NextRequest): Record<string, string> {
|
|
15
|
+
return {
|
|
16
|
+
'x-forwarded-host':
|
|
17
|
+
req.headers.get('x-forwarded-host') || req.headers.get('host') || '',
|
|
18
|
+
'x-forwarded-for': req.headers.get('x-forwarded-for') ?? '',
|
|
19
|
+
'x-forwarded-proto': req.headers.get('x-forwarded-proto') || 'https',
|
|
20
|
+
'x-currency': req.cookies.get('pz-currency')?.value ?? '',
|
|
21
|
+
cookie: req.headers.get('cookie') ?? ''
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function getRequestBody(
|
|
26
|
+
req: NextRequest
|
|
27
|
+
): Promise<{ content: string; contentType: string } | undefined> {
|
|
28
|
+
if (req.method !== 'POST') return undefined;
|
|
29
|
+
|
|
30
|
+
const content = await req.text();
|
|
31
|
+
if (!content.length) return undefined;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
content,
|
|
35
|
+
contentType:
|
|
36
|
+
req.headers.get('content-type') ?? 'application/x-www-form-urlencoded'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function fetchCommerce(
|
|
41
|
+
url: string,
|
|
42
|
+
req: NextRequest,
|
|
43
|
+
body?: { content: string; contentType: string }
|
|
44
|
+
): Promise<Response> {
|
|
45
|
+
const headers = buildCommerceHeaders(req);
|
|
46
|
+
|
|
47
|
+
if (body) {
|
|
48
|
+
headers['content-type'] = body.contentType;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return fetch(url, {
|
|
52
|
+
method: body ? 'POST' : 'GET',
|
|
53
|
+
headers,
|
|
54
|
+
body: body?.content,
|
|
55
|
+
redirect: 'manual'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function forwardCookies(from: Response, to: NextResponse): void {
|
|
60
|
+
from.headers.getSetCookie().forEach((cookie) => {
|
|
61
|
+
to.headers.append('set-cookie', cookie);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function buildRedirectResponse(
|
|
66
|
+
commerceResponse: Response,
|
|
67
|
+
location: string,
|
|
68
|
+
origin: string
|
|
69
|
+
): NextResponse {
|
|
70
|
+
const response = NextResponse.redirect(new URL(location, origin));
|
|
71
|
+
forwardCookies(commerceResponse, response);
|
|
72
|
+
return response;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function commercePassthrough(commerceResponse: Response): NextResponse {
|
|
76
|
+
return new NextResponse(commerceResponse.body, {
|
|
77
|
+
status: commerceResponse.status,
|
|
78
|
+
headers: commerceResponse.headers
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildOAuthCallbackCookie(referer: string): string {
|
|
83
|
+
return `pz-oauth-callback-url=${encodeURIComponent(referer)}; Path=/`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function handleLogin(
|
|
87
|
+
req: NextRequest,
|
|
88
|
+
provider: string
|
|
89
|
+
): Promise<{ response: NextResponse; redirected: boolean }> {
|
|
90
|
+
const commerceResponse = await fetchCommerce(
|
|
91
|
+
`${Settings.commerceUrl}/${provider}/login/`,
|
|
92
|
+
req
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const location = commerceResponse.headers.get('location');
|
|
96
|
+
if (!location) {
|
|
97
|
+
return {
|
|
98
|
+
response: commercePassthrough(commerceResponse),
|
|
99
|
+
redirected: false
|
|
25
100
|
};
|
|
101
|
+
}
|
|
26
102
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
headers
|
|
33
|
-
}
|
|
34
|
-
);
|
|
103
|
+
const response = buildRedirectResponse(
|
|
104
|
+
commerceResponse,
|
|
105
|
+
location,
|
|
106
|
+
req.nextUrl.origin
|
|
107
|
+
);
|
|
35
108
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
}
|
|
109
|
+
response.headers.append(
|
|
110
|
+
'set-cookie',
|
|
111
|
+
buildOAuthCallbackCookie(req.headers.get('referer') || '')
|
|
112
|
+
);
|
|
42
113
|
|
|
43
|
-
|
|
44
|
-
|
|
114
|
+
return { response, redirected: true };
|
|
115
|
+
}
|
|
45
116
|
|
|
46
|
-
|
|
47
|
-
|
|
117
|
+
async function handleCallback(
|
|
118
|
+
req: NextRequest,
|
|
119
|
+
provider: string,
|
|
120
|
+
search: string
|
|
121
|
+
): Promise<{ response: NextResponse; redirected: boolean }> {
|
|
122
|
+
const body = await getRequestBody(req);
|
|
123
|
+
const commerceResponse = await fetchCommerce(
|
|
124
|
+
`${Settings.commerceUrl}/${provider}/login/callback/${search}`,
|
|
125
|
+
req,
|
|
126
|
+
body
|
|
127
|
+
);
|
|
48
128
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
129
|
+
const location = commerceResponse.headers.get('location');
|
|
130
|
+
if (!location) {
|
|
131
|
+
return {
|
|
132
|
+
response: commercePassthrough(commerceResponse),
|
|
133
|
+
redirected: false
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
response: buildRedirectResponse(
|
|
139
|
+
commerceResponse,
|
|
140
|
+
location,
|
|
141
|
+
req.nextUrl.origin
|
|
142
|
+
),
|
|
143
|
+
redirected: true
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function handleBasketRedirect(req: NextRequest): NextResponse | null {
|
|
148
|
+
const hasSession = req.cookies.get('osessionid');
|
|
149
|
+
const messages = req.cookies.get('messages')?.value;
|
|
150
|
+
|
|
151
|
+
if (!messages) return null;
|
|
152
|
+
if (!messages.includes('Successfully signed in') && !hasSession) return null;
|
|
153
|
+
|
|
154
|
+
let redirectUrl = `${req.nextUrl.origin}${getUrlPathWithLocale(
|
|
155
|
+
'/auth/oauth-login',
|
|
156
|
+
req.cookies.get('pz-locale')?.value
|
|
157
|
+
)}`;
|
|
158
|
+
|
|
159
|
+
const callbackUrl = req.cookies.get('pz-oauth-callback-url')?.value ?? '';
|
|
160
|
+
if (callbackUrl.length) {
|
|
161
|
+
redirectUrl += `?next=${encodeURIComponent(callbackUrl)}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const response = NextResponse.redirect(redirectUrl);
|
|
165
|
+
response.cookies.delete('messages');
|
|
166
|
+
response.cookies.delete('pz-oauth-callback-url');
|
|
167
|
+
return response;
|
|
168
|
+
}
|
|
56
169
|
|
|
57
|
-
|
|
170
|
+
const withOauthLogin =
|
|
171
|
+
(middleware: NextMiddleware) =>
|
|
172
|
+
async (req: NextRequest, event: NextFetchEvent) => {
|
|
173
|
+
const { pathname, search } = req.nextUrl;
|
|
174
|
+
|
|
175
|
+
if (!pathname.includes('/login') && !pathname.startsWith('/baskets/basket')) {
|
|
58
176
|
return middleware(req, event);
|
|
59
177
|
}
|
|
60
178
|
|
|
61
|
-
|
|
179
|
+
logger.info('OAuth login redirect', {
|
|
180
|
+
host: req.headers.get('host'),
|
|
181
|
+
'x-forwarded-host': req.headers.get('x-forwarded-host'),
|
|
182
|
+
'x-forwarded-for': req.headers.get('x-forwarded-for')
|
|
183
|
+
});
|
|
62
184
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
185
|
+
const loginMatch = LOGIN_URL_REGEX.exec(pathname);
|
|
186
|
+
if (loginMatch) {
|
|
187
|
+
try {
|
|
188
|
+
const { response, redirected } = await handleLogin(req, loginMatch[1]);
|
|
189
|
+
if (!redirected) {
|
|
190
|
+
logger.warn('OAuth login: no redirect from commerce', {
|
|
191
|
+
provider: loginMatch[1]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return response;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
logger.error('OAuth login fetch failed', { error });
|
|
197
|
+
return middleware(req, event);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
72
200
|
|
|
73
|
-
|
|
74
|
-
|
|
201
|
+
const callbackMatch = CALLBACK_URL_REGEX.exec(pathname);
|
|
202
|
+
if (callbackMatch) {
|
|
203
|
+
try {
|
|
204
|
+
const { response, redirected } = await handleCallback(
|
|
205
|
+
req,
|
|
206
|
+
callbackMatch[1],
|
|
207
|
+
search
|
|
208
|
+
);
|
|
209
|
+
if (!redirected) {
|
|
210
|
+
logger.warn('OAuth callback: no redirect from commerce', {
|
|
211
|
+
provider: callbackMatch[1]
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return response;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.error('OAuth callback fetch failed', { error });
|
|
217
|
+
return middleware(req, event);
|
|
75
218
|
}
|
|
219
|
+
}
|
|
76
220
|
|
|
77
|
-
|
|
78
|
-
response
|
|
79
|
-
response
|
|
80
|
-
return response;
|
|
221
|
+
if (pathname.startsWith('/baskets/basket')) {
|
|
222
|
+
const response = handleBasketRedirect(req);
|
|
223
|
+
if (response) return response;
|
|
81
224
|
}
|
|
82
225
|
|
|
83
226
|
return middleware(req, event);
|
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": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.17",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"set-cookie-parser": "2.6.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.
|
|
38
|
+
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.17",
|
|
39
39
|
"@babel/core": "7.26.10",
|
|
40
40
|
"@babel/preset-env": "7.26.9",
|
|
41
41
|
"@babel/preset-typescript": "7.27.0",
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
setHasGiftBox,
|
|
15
15
|
setInstallmentOptions,
|
|
16
16
|
setLoyaltyBalance,
|
|
17
|
+
setLoyaltyBalances,
|
|
17
18
|
setPaymentChoices,
|
|
18
19
|
setPaymentOptions,
|
|
19
20
|
setRetailStores,
|
|
@@ -224,6 +225,14 @@ export const contextListMiddleware: Middleware = ({
|
|
|
224
225
|
dispatch(setLoyaltyBalance(context.page_context.balance));
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
if (context.page_context.balances) {
|
|
229
|
+
dispatch(setLoyaltyBalances(context.page_context.balances));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (context.page_context.accounts) {
|
|
233
|
+
dispatch(setLoyaltyBalances(context.page_context.accounts));
|
|
234
|
+
}
|
|
235
|
+
|
|
227
236
|
if (context.page_context.retail_stores) {
|
|
228
237
|
dispatch(setRetailStores(context.page_context.retail_stores));
|
|
229
238
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
CreditCardType,
|
|
10
10
|
DeliveryOption,
|
|
11
11
|
InstallmentOption,
|
|
12
|
+
LoyaltyBalanceItem,
|
|
12
13
|
PaymentChoice,
|
|
13
14
|
PaymentOption,
|
|
14
15
|
CheckoutCreditPaymentOption,
|
|
@@ -49,6 +50,7 @@ export interface CheckoutState {
|
|
|
49
50
|
bankAccounts: BankAccount[];
|
|
50
51
|
selectedBankAccountPk: number;
|
|
51
52
|
loyaltyBalance?: string;
|
|
53
|
+
loyaltyBalances?: LoyaltyBalanceItem[];
|
|
52
54
|
retailStores: RetailStore[];
|
|
53
55
|
attributeBasedShippingOptions: AttributeBasedShippingOption[];
|
|
54
56
|
selectedShippingOptions: Record<string, number>;
|
|
@@ -188,6 +190,9 @@ const checkoutSlice = createSlice({
|
|
|
188
190
|
setLoyaltyBalance(state, { payload }) {
|
|
189
191
|
state.loyaltyBalance = payload;
|
|
190
192
|
},
|
|
193
|
+
setLoyaltyBalances(state, { payload }) {
|
|
194
|
+
state.loyaltyBalances = payload;
|
|
195
|
+
},
|
|
191
196
|
setRetailStores(state, { payload }) {
|
|
192
197
|
state.retailStores = payload;
|
|
193
198
|
},
|
|
@@ -234,6 +239,7 @@ export const {
|
|
|
234
239
|
setBankAccounts,
|
|
235
240
|
setSelectedBankAccountPk,
|
|
236
241
|
setLoyaltyBalance,
|
|
242
|
+
setLoyaltyBalances,
|
|
237
243
|
setRetailStores,
|
|
238
244
|
setAttributeBasedShippingOptions,
|
|
239
245
|
setSelectedShippingOptions,
|
|
@@ -68,6 +68,18 @@ export interface CheckoutPaymentOption {
|
|
|
68
68
|
viewProps?: any;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
export interface LoyaltyBalanceItem {
|
|
72
|
+
label_id: number | null;
|
|
73
|
+
label: string | null;
|
|
74
|
+
balance: string;
|
|
75
|
+
currency?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AccountUsage {
|
|
79
|
+
label_id: number | null;
|
|
80
|
+
amount: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
71
83
|
export interface GiftBox {
|
|
72
84
|
note: string;
|
|
73
85
|
gift_video: boolean;
|
|
@@ -91,6 +103,7 @@ export interface PreOrder {
|
|
|
91
103
|
notes?: string;
|
|
92
104
|
user_phone_number?: string;
|
|
93
105
|
loyalty_money?: string;
|
|
106
|
+
loyalty_account_usages?: AccountUsage[];
|
|
94
107
|
currency_type_label?: string;
|
|
95
108
|
is_guest?: boolean;
|
|
96
109
|
is_post_order?: boolean;
|
|
@@ -151,6 +164,8 @@ export interface CheckoutContext {
|
|
|
151
164
|
redirect_url?: string;
|
|
152
165
|
context_data?: any;
|
|
153
166
|
balance?: string;
|
|
167
|
+
balances?: LoyaltyBalanceItem[];
|
|
168
|
+
accounts?: LoyaltyBalanceItem[];
|
|
154
169
|
attribute_based_shipping_options?: AttributeBasedShippingOption[];
|
|
155
170
|
paymentData?: any;
|
|
156
171
|
paymentMethod?: string;
|