@01.software/init 0.9.1 → 0.10.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/dist/ai-docs.d.ts +13 -0
- package/dist/ai-docs.js +1 -1
- package/dist/browser-auth-CJDrpp5T.d.ts +11 -0
- package/dist/{chunk-UA7WNT2F.js → chunk-4LHYICUL.js} +1 -1
- package/dist/chunk-4LHYICUL.js.map +1 -0
- package/dist/{chunk-TBGKXE3Q.js → chunk-NJ4X7VNK.js} +5 -5
- package/dist/chunk-NJ4X7VNK.js.map +1 -0
- package/dist/{chunk-5K2CB2Y5.js → chunk-Q6MSORYN.js} +14 -37
- package/dist/chunk-Q6MSORYN.js.map +1 -0
- package/dist/chunk-STM4DKVZ.js +183 -0
- package/dist/chunk-STM4DKVZ.js.map +1 -0
- package/dist/{chunk-2IGKOSK7.js → chunk-WDWJ73KP.js} +41 -215
- package/dist/chunk-WDWJ73KP.js.map +1 -0
- package/dist/create-app-templates/ecommerce/AGENTS.md +88 -0
- package/dist/create-app-templates/ecommerce/CHANGELOG.md +30 -0
- package/dist/create-app-templates/ecommerce/CLAUDE.md +1 -0
- package/dist/create-app-templates/ecommerce/README.md +139 -0
- package/dist/create-app-templates/ecommerce/app/api/auth/login/route.ts +30 -0
- package/dist/create-app-templates/ecommerce/app/api/auth/logout/route.ts +18 -0
- package/dist/create-app-templates/ecommerce/app/api/auth/register/route.ts +41 -0
- package/dist/create-app-templates/ecommerce/app/api/cart/clear/route.ts +12 -0
- package/dist/create-app-templates/ecommerce/app/api/cart/items/route.ts +45 -0
- package/dist/create-app-templates/ecommerce/app/api/cart/route.ts +14 -0
- package/dist/create-app-templates/ecommerce/app/api/checkout/payment-return/route.ts +86 -0
- package/dist/create-app-templates/ecommerce/app/api/checkout/reconcile/route.ts +50 -0
- package/dist/create-app-templates/ecommerce/app/api/checkout/route.ts +41 -0
- package/dist/create-app-templates/ecommerce/app/cart/page.tsx +10 -0
- package/dist/create-app-templates/ecommerce/app/checkout/page.tsx +10 -0
- package/dist/create-app-templates/ecommerce/app/checkout/success/page.tsx +34 -0
- package/dist/create-app-templates/ecommerce/app/favicon.ico +0 -0
- package/dist/create-app-templates/ecommerce/app/globals.css +67 -0
- package/dist/create-app-templates/ecommerce/app/layout.tsx +23 -0
- package/dist/create-app-templates/ecommerce/app/login/page.tsx +11 -0
- package/dist/create-app-templates/ecommerce/app/page.tsx +5 -0
- package/dist/create-app-templates/ecommerce/app/products/[slug]/page.tsx +46 -0
- package/dist/create-app-templates/ecommerce/app/products/page.tsx +45 -0
- package/dist/create-app-templates/ecommerce/app/register/page.tsx +11 -0
- package/dist/create-app-templates/ecommerce/app/webhook/payment/route.ts +20 -0
- package/dist/create-app-templates/ecommerce/app-config.ts +54 -0
- package/dist/create-app-templates/ecommerce/components/auth/auth-form.tsx +109 -0
- package/dist/create-app-templates/ecommerce/components/cart/cart-content.tsx +119 -0
- package/dist/create-app-templates/ecommerce/components/checkout/checkout-form.tsx +267 -0
- package/dist/create-app-templates/ecommerce/components/checkout/checkout-reconcile.tsx +78 -0
- package/dist/create-app-templates/ecommerce/components/layout/account-nav.tsx +48 -0
- package/dist/create-app-templates/ecommerce/components/layout/account-slot.tsx +12 -0
- package/dist/create-app-templates/ecommerce/components/layout/cart-link.tsx +13 -0
- package/dist/create-app-templates/ecommerce/components/layout/page-shell.tsx +11 -0
- package/dist/create-app-templates/ecommerce/components/layout/site-header.tsx +22 -0
- package/dist/create-app-templates/ecommerce/components/product/add-to-cart.tsx +116 -0
- package/dist/create-app-templates/ecommerce/components/product/product-card.tsx +50 -0
- package/dist/create-app-templates/ecommerce/components/product/product-gallery.tsx +39 -0
- package/dist/create-app-templates/ecommerce/data/mock-catalog.json +173 -0
- package/dist/create-app-templates/ecommerce/eslint.config.mjs +18 -0
- package/dist/create-app-templates/ecommerce/lib/cart/cookie.ts +40 -0
- package/dist/create-app-templates/ecommerce/lib/cart/normalize.ts +32 -0
- package/dist/create-app-templates/ecommerce/lib/cart/parse-cart-request.ts +56 -0
- package/dist/create-app-templates/ecommerce/lib/cart/route-helpers.ts +17 -0
- package/dist/create-app-templates/ecommerce/lib/cart/select-provider.ts +44 -0
- package/dist/create-app-templates/ecommerce/lib/cart/server-cart.ts +96 -0
- package/dist/create-app-templates/ecommerce/lib/cart/sync-on-login.server.ts +34 -0
- package/dist/create-app-templates/ecommerce/lib/cart/use-cart.tsx +151 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/checkout-errors.ts +22 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/checkout-provider.ts +28 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/parse-checkout-payload.ts +76 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/start-checkout.ts +63 -0
- package/dist/create-app-templates/ecommerce/lib/checkout/types.ts +3 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/adapters/mock.ts +336 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software-mappers.ts +312 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software.ts +913 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/product-summary.ts +37 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/provider.server.ts +60 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/provider.ts +96 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/stock.ts +37 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/types.ts +206 -0
- package/dist/create-app-templates/ecommerce/lib/commerce/variant-selection.ts +23 -0
- package/dist/create-app-templates/ecommerce/lib/customer/auth-actions.ts +131 -0
- package/dist/create-app-templates/ecommerce/lib/customer/cart-sync.ts +44 -0
- package/dist/create-app-templates/ecommerce/lib/customer/client.server.ts +109 -0
- package/dist/create-app-templates/ecommerce/lib/customer/current-customer.ts +15 -0
- package/dist/create-app-templates/ecommerce/lib/customer/route-guard.ts +58 -0
- package/dist/create-app-templates/ecommerce/lib/customer/route-helpers.ts +75 -0
- package/dist/create-app-templates/ecommerce/lib/customer/session.ts +108 -0
- package/dist/create-app-templates/ecommerce/lib/format.ts +7 -0
- package/dist/create-app-templates/ecommerce/lib/payment/adapters/mock.ts +84 -0
- package/dist/create-app-templates/ecommerce/lib/payment/adapters/portone.ts +254 -0
- package/dist/create-app-templates/ecommerce/lib/payment/adapters/tosspayments.ts +287 -0
- package/dist/create-app-templates/ecommerce/lib/payment/amount-gate.ts +86 -0
- package/dist/create-app-templates/ecommerce/lib/payment/provider.server.ts +51 -0
- package/dist/create-app-templates/ecommerce/lib/payment/provider.ts +18 -0
- package/dist/create-app-templates/ecommerce/lib/payment/sync-order-payment.ts +96 -0
- package/dist/create-app-templates/ecommerce/lib/payment/types.ts +71 -0
- package/dist/create-app-templates/ecommerce/lib/server-only-guard.ts +20 -0
- package/dist/create-app-templates/ecommerce/next-env.d.ts +6 -0
- package/dist/create-app-templates/ecommerce/next.config.ts +16 -0
- package/dist/create-app-templates/ecommerce/package.json +33 -0
- package/dist/create-app-templates/ecommerce/postcss.config.mjs +7 -0
- package/dist/create-app-templates/ecommerce/tests/customer-auth.test.ts +263 -0
- package/dist/create-app-templates/ecommerce/tests/customer-cart.test.ts +392 -0
- package/dist/create-app-templates/ecommerce/tests/domain.test.ts +1537 -0
- package/dist/create-app-templates/ecommerce/tsconfig.json +35 -0
- package/dist/create-app-templates/registry.json +66 -0
- package/dist/create-app.d.ts +40 -0
- package/dist/create-app.js +652 -0
- package/dist/create-app.js.map +1 -0
- package/dist/detect-Bjxp9wcS.d.ts +13 -0
- package/dist/file-ops.d.ts +21 -0
- package/dist/file-ops.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +40 -0
- package/dist/init.js +5 -4
- package/dist/templates.d.ts +27 -0
- package/dist/templates.js +1 -1
- package/package.json +31 -15
- package/dist/chunk-2IGKOSK7.js.map +0 -1
- package/dist/chunk-5K2CB2Y5.js.map +0 -1
- package/dist/chunk-TBGKXE3Q.js.map +0 -1
- package/dist/chunk-UA7WNT2F.js.map +0 -1
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import { resolveCustomerCartToken } from "../lib/customer/cart-sync.ts";
|
|
5
|
+
import {
|
|
6
|
+
clearSessionAndCartTokens,
|
|
7
|
+
readSessionToken,
|
|
8
|
+
writeSessionToken,
|
|
9
|
+
type SessionCookieStore,
|
|
10
|
+
} from "../lib/customer/session.ts";
|
|
11
|
+
import { resolveCartProvider } from "../lib/cart/select-provider.ts";
|
|
12
|
+
import { resolveCheckoutCommerceProvider } from "../lib/checkout/checkout-provider.ts";
|
|
13
|
+
import { buildSoftwareCommerceProvider } from "../lib/commerce/adapters/software.ts";
|
|
14
|
+
import type { CartProvider, CommerceProvider } from "../lib/commerce/provider.ts";
|
|
15
|
+
import type { ShippingPolicy } from "../lib/commerce/types.ts";
|
|
16
|
+
import { appConfig } from "../app-config.ts";
|
|
17
|
+
|
|
18
|
+
// ---- login cart-sync: merge (union/claim) vs mine -------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A fake customer-JWT commerce client recording how the login cart-sync calls
|
|
22
|
+
* merge/mine. `merge`/`mine` return the resolved customer cart token (the
|
|
23
|
+
* capability handle the storefront persists).
|
|
24
|
+
*/
|
|
25
|
+
function fakeCommerce(opts: {
|
|
26
|
+
mergeToken?: string | null;
|
|
27
|
+
mineToken?: string | null;
|
|
28
|
+
calls: string[];
|
|
29
|
+
}) {
|
|
30
|
+
return {
|
|
31
|
+
cart: {
|
|
32
|
+
async merge(params: { guestCartToken: string }) {
|
|
33
|
+
opts.calls.push(`merge:${params.guestCartToken}`);
|
|
34
|
+
return { cartToken: opts.mergeToken ?? null };
|
|
35
|
+
},
|
|
36
|
+
async mine() {
|
|
37
|
+
opts.calls.push("mine");
|
|
38
|
+
return { cartToken: opts.mineToken ?? null };
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
test("cart-sync: union merge stores the resolved customer cart token", async () => {
|
|
45
|
+
// Overlapping + disjoint guest lines union server-side; merge returns the
|
|
46
|
+
// customer cart's token, which the storefront persists.
|
|
47
|
+
const calls: string[] = [];
|
|
48
|
+
const commerce = fakeCommerce({ mergeToken: "cart_customer", calls });
|
|
49
|
+
const result = await resolveCustomerCartToken(commerce, "cart_guest");
|
|
50
|
+
assert.deepEqual(calls, ["merge:cart_guest"]);
|
|
51
|
+
assert.equal(result.cartToken, "cart_customer");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("cart-sync: claim merge (no existing customer cart) stores the token", async () => {
|
|
55
|
+
// The guest cart is claimed (taken over); merge still returns a token to keep
|
|
56
|
+
// operating the now-customer-bound cart.
|
|
57
|
+
const calls: string[] = [];
|
|
58
|
+
const commerce = fakeCommerce({ mergeToken: "cart_claimed", calls });
|
|
59
|
+
const result = await resolveCustomerCartToken(commerce, "cart_guest");
|
|
60
|
+
assert.deepEqual(calls, ["merge:cart_guest"]);
|
|
61
|
+
assert.equal(result.cartToken, "cart_claimed");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("cart-sync: no guest cart loads the customer's active cart via mine()", async () => {
|
|
65
|
+
// Cross-device: a fresh device has no guest cookie, so mine() resolves the
|
|
66
|
+
// existing customer cart by JWT.
|
|
67
|
+
const calls: string[] = [];
|
|
68
|
+
const commerce = fakeCommerce({ mineToken: "cart_existing", calls });
|
|
69
|
+
const result = await resolveCustomerCartToken(commerce, null);
|
|
70
|
+
assert.deepEqual(calls, ["mine"]);
|
|
71
|
+
assert.equal(result.cartToken, "cart_existing");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("cart-sync: mine() with no active cart resolves null (cookie gets cleared)", async () => {
|
|
75
|
+
const calls: string[] = [];
|
|
76
|
+
const commerce = fakeCommerce({ mineToken: null, calls });
|
|
77
|
+
const result = await resolveCustomerCartToken(commerce, null);
|
|
78
|
+
assert.deepEqual(calls, ["mine"]);
|
|
79
|
+
assert.equal(result.cartToken, null);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ---- provider selection branches on the session token ---------------------
|
|
83
|
+
|
|
84
|
+
const GUEST = { tag: "guest" } as unknown as CartProvider;
|
|
85
|
+
const CUSTOMER = { tag: "customer" } as unknown as CartProvider;
|
|
86
|
+
|
|
87
|
+
test("provider select: a session token routes to the customer cart provider", () => {
|
|
88
|
+
let seenToken: string | null = null;
|
|
89
|
+
const provider = resolveCartProvider("jwt-abc", {
|
|
90
|
+
guest: () => GUEST,
|
|
91
|
+
customer: (token) => {
|
|
92
|
+
seenToken = token;
|
|
93
|
+
return CUSTOMER;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
assert.equal(provider, CUSTOMER);
|
|
97
|
+
assert.equal(seenToken, "jwt-abc");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("provider select: no session token keeps the guest cart provider", () => {
|
|
101
|
+
let customerCalled = false;
|
|
102
|
+
const provider = resolveCartProvider(null, {
|
|
103
|
+
guest: () => GUEST,
|
|
104
|
+
customer: () => {
|
|
105
|
+
customerCalled = true;
|
|
106
|
+
return CUSTOMER;
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
assert.equal(provider, GUEST);
|
|
110
|
+
assert.equal(customerCalled, false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ---- a logged-in user never mints a guest cart (SDK boundary) -------------
|
|
114
|
+
|
|
115
|
+
const POLICY: ShippingPolicy = { currency: "KRW", baseAmount: 0 };
|
|
116
|
+
|
|
117
|
+
/** Fake SDK client recording cart-namespace calls. */
|
|
118
|
+
function fakeSdkClient(calls: string[]) {
|
|
119
|
+
return {
|
|
120
|
+
commerce: {
|
|
121
|
+
cart: {
|
|
122
|
+
async create() {
|
|
123
|
+
calls.push("commerce.cart.create");
|
|
124
|
+
return { cartToken: "cart_customer-bound" };
|
|
125
|
+
},
|
|
126
|
+
async get(token: string) {
|
|
127
|
+
calls.push(`commerce.cart.get:${token}`);
|
|
128
|
+
return { id: "c1", currency: "KRW", items: [] };
|
|
129
|
+
},
|
|
130
|
+
async addItem(params: { cartToken: string }) {
|
|
131
|
+
calls.push(`commerce.cart.addItem:${params.cartToken}`);
|
|
132
|
+
return {};
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
collections: {
|
|
137
|
+
from() {
|
|
138
|
+
return {
|
|
139
|
+
async find() {
|
|
140
|
+
return { docs: [] };
|
|
141
|
+
},
|
|
142
|
+
async update() {
|
|
143
|
+
return {};
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
test("provider builder: createCart() routes through the bound client (no hidden guest fallback)", async () => {
|
|
152
|
+
const calls: string[] = [];
|
|
153
|
+
// The customer cart provider is the shared provider builder bound to the
|
|
154
|
+
// customer-JWT client (see createSoftwareCustomerCartProvider), so create()
|
|
155
|
+
// routes through whatever client the provider was built with — there is no
|
|
156
|
+
// separate guest-cart code path. Bound to the JWT client, the JWT auto-binds
|
|
157
|
+
// `customer`, yielding a customer-bound cart.
|
|
158
|
+
const provider = buildSoftwareCommerceProvider(
|
|
159
|
+
Promise.resolve(fakeSdkClient(calls) as never),
|
|
160
|
+
POLICY,
|
|
161
|
+
);
|
|
162
|
+
const created = await provider.createCart();
|
|
163
|
+
assert.equal(created.cartToken, "cart_customer-bound");
|
|
164
|
+
assert.deepEqual(calls, ["commerce.cart.create"]);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("customer cart provider: addCartItem() operates the cart by token through the client", async () => {
|
|
168
|
+
const calls: string[] = [];
|
|
169
|
+
const provider = buildSoftwareCommerceProvider(
|
|
170
|
+
Promise.resolve(fakeSdkClient(calls) as never),
|
|
171
|
+
POLICY,
|
|
172
|
+
);
|
|
173
|
+
const view = await provider.addCartItem({
|
|
174
|
+
cartToken: "cart_customer-bound",
|
|
175
|
+
item: { productId: "p1", variantId: "v1", quantity: 1 },
|
|
176
|
+
});
|
|
177
|
+
assert.ok(calls.includes("commerce.cart.addItem:cart_customer-bound"));
|
|
178
|
+
assert.ok(calls.includes("commerce.cart.get:cart_customer-bound"));
|
|
179
|
+
assert.equal(view.id, "c1");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ---- checkout provider selection uses the customer session JWT ------------
|
|
183
|
+
|
|
184
|
+
const GUEST_CHECKOUT = { tag: "guest-checkout" } as unknown as CommerceProvider;
|
|
185
|
+
const CUSTOMER_CHECKOUT = {
|
|
186
|
+
tag: "customer-checkout",
|
|
187
|
+
} as unknown as CommerceProvider;
|
|
188
|
+
|
|
189
|
+
test("checkout provider select: a session token routes checkout through the customer provider", () => {
|
|
190
|
+
let seenToken: string | null = null;
|
|
191
|
+
const provider = resolveCheckoutCommerceProvider("jwt-checkout", {
|
|
192
|
+
guest: () => GUEST_CHECKOUT,
|
|
193
|
+
customer: (token) => {
|
|
194
|
+
seenToken = token;
|
|
195
|
+
return CUSTOMER_CHECKOUT;
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
assert.equal(provider, CUSTOMER_CHECKOUT);
|
|
200
|
+
assert.equal(seenToken, "jwt-checkout");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("checkout provider select: no session token preserves the guest checkout provider", () => {
|
|
204
|
+
let customerCalled = false;
|
|
205
|
+
const provider = resolveCheckoutCommerceProvider(null, {
|
|
206
|
+
guest: () => GUEST_CHECKOUT,
|
|
207
|
+
customer: () => {
|
|
208
|
+
customerCalled = true;
|
|
209
|
+
return CUSTOMER_CHECKOUT;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
assert.equal(provider, GUEST_CHECKOUT);
|
|
214
|
+
assert.equal(customerCalled, false);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// ---- checkout crosses the SDK boundary with the bound customer client -----
|
|
218
|
+
|
|
219
|
+
function fakeCheckoutSdkClient(calls: string[]) {
|
|
220
|
+
return {
|
|
221
|
+
commerce: {
|
|
222
|
+
product: {
|
|
223
|
+
async detail() {
|
|
224
|
+
return null;
|
|
225
|
+
},
|
|
226
|
+
async stockCheck() {
|
|
227
|
+
return { allAvailable: true, results: [] };
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
cart: {
|
|
231
|
+
async create() {
|
|
232
|
+
return { cartToken: "cart_customer-bound" };
|
|
233
|
+
},
|
|
234
|
+
async get(cartToken: string) {
|
|
235
|
+
calls.push(`commerce.cart.get:${cartToken}`);
|
|
236
|
+
return {
|
|
237
|
+
id: cartToken === "cart_other_token" ? "cart_other" : "cart_owned",
|
|
238
|
+
currency: "KRW",
|
|
239
|
+
totalAmount: 99000,
|
|
240
|
+
items: [{ id: "item_1", variant: "var_1", quantity: 1 }],
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
async addItem() {
|
|
244
|
+
return {};
|
|
245
|
+
},
|
|
246
|
+
async updateItem() {
|
|
247
|
+
return {};
|
|
248
|
+
},
|
|
249
|
+
async removeItem() {
|
|
250
|
+
return {};
|
|
251
|
+
},
|
|
252
|
+
async clear() {
|
|
253
|
+
return {};
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
orders: {
|
|
257
|
+
async checkout(params: { cartId: string }) {
|
|
258
|
+
calls.push(`commerce.orders.checkout:${params.cartId}`);
|
|
259
|
+
if (params.cartId === "cart_other") {
|
|
260
|
+
throw Object.assign(new Error("Forbidden"), { status: 403 });
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
id: "checkout_1",
|
|
264
|
+
orderNumber: "ORD-1",
|
|
265
|
+
totalAmount: 99000,
|
|
266
|
+
items: [],
|
|
267
|
+
};
|
|
268
|
+
},
|
|
269
|
+
async confirmPaymentReturningOrder() {
|
|
270
|
+
throw new Error("not used");
|
|
271
|
+
},
|
|
272
|
+
async getByPaymentId() {
|
|
273
|
+
throw new Error("not used");
|
|
274
|
+
},
|
|
275
|
+
async updateTransaction() {
|
|
276
|
+
throw new Error("not used");
|
|
277
|
+
},
|
|
278
|
+
async cancelOrder() {
|
|
279
|
+
throw new Error("not used");
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
collections: {
|
|
284
|
+
from(slug: string) {
|
|
285
|
+
return {
|
|
286
|
+
async find() {
|
|
287
|
+
calls.push(`collections.${slug}.find`);
|
|
288
|
+
return { docs: [] };
|
|
289
|
+
},
|
|
290
|
+
async update(id: string) {
|
|
291
|
+
calls.push(`collections.${slug}.update:${id}`);
|
|
292
|
+
return {};
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
test("customer checkout provider: own cart checks out through the bound SDK client", async () => {
|
|
301
|
+
const calls: string[] = [];
|
|
302
|
+
const provider = buildSoftwareCommerceProvider(
|
|
303
|
+
Promise.resolve(fakeCheckoutSdkClient(calls) as never),
|
|
304
|
+
POLICY,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const pending = await provider.checkoutCart({
|
|
308
|
+
cartToken: "cart_owned_token",
|
|
309
|
+
customerSnapshot: {
|
|
310
|
+
name: "Ada",
|
|
311
|
+
email: "ada@example.com",
|
|
312
|
+
phone: "010",
|
|
313
|
+
},
|
|
314
|
+
shippingAddress: {
|
|
315
|
+
recipientName: "Ada",
|
|
316
|
+
phone: "010",
|
|
317
|
+
postalCode: "04524",
|
|
318
|
+
address: "Seoul",
|
|
319
|
+
detailAddress: "101",
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
assert.equal(pending.order.orderNumber, "ORD-1");
|
|
324
|
+
assert.ok(calls.includes("commerce.cart.get:cart_owned_token"));
|
|
325
|
+
assert.ok(calls.includes("collections.carts.update:cart_owned"));
|
|
326
|
+
assert.ok(calls.includes("commerce.orders.checkout:cart_owned"));
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("customer checkout provider: another customer's cart is rejected by the SDK boundary", async () => {
|
|
330
|
+
const calls: string[] = [];
|
|
331
|
+
const provider = buildSoftwareCommerceProvider(
|
|
332
|
+
Promise.resolve(fakeCheckoutSdkClient(calls) as never),
|
|
333
|
+
POLICY,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
await assert.rejects(
|
|
337
|
+
provider.checkoutCart({
|
|
338
|
+
cartToken: "cart_other_token",
|
|
339
|
+
customerSnapshot: {
|
|
340
|
+
name: "Ada",
|
|
341
|
+
email: "ada@example.com",
|
|
342
|
+
phone: "010",
|
|
343
|
+
},
|
|
344
|
+
shippingAddress: {
|
|
345
|
+
recipientName: "Ada",
|
|
346
|
+
phone: "010",
|
|
347
|
+
postalCode: "04524",
|
|
348
|
+
address: "Seoul",
|
|
349
|
+
detailAddress: "101",
|
|
350
|
+
},
|
|
351
|
+
}),
|
|
352
|
+
Object.assign(new Error("Forbidden"), { status: 403 }),
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
assert.ok(calls.includes("commerce.orders.checkout:cart_other"));
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// ---- logout clears both the session JWT and the cart cookie ---------------
|
|
359
|
+
|
|
360
|
+
function fakeCookieStore(): SessionCookieStore & { entries: Map<string, string> } {
|
|
361
|
+
const entries = new Map<string, string>();
|
|
362
|
+
return {
|
|
363
|
+
entries,
|
|
364
|
+
get(name) {
|
|
365
|
+
const value = entries.get(name);
|
|
366
|
+
return value === undefined ? undefined : { value };
|
|
367
|
+
},
|
|
368
|
+
set(name, value) {
|
|
369
|
+
entries.set(name, value);
|
|
370
|
+
},
|
|
371
|
+
delete(name) {
|
|
372
|
+
entries.delete(name);
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
test("logout: clears both the session JWT cookie and the cart cookie atomically", () => {
|
|
378
|
+
const store = fakeCookieStore();
|
|
379
|
+
writeSessionToken(store, "jwt-123");
|
|
380
|
+
store.set(appConfig.cartKey, "cart_customer", {
|
|
381
|
+
httpOnly: true,
|
|
382
|
+
secure: false,
|
|
383
|
+
sameSite: "lax",
|
|
384
|
+
path: "/",
|
|
385
|
+
maxAge: 1,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
clearSessionAndCartTokens(store);
|
|
389
|
+
|
|
390
|
+
assert.equal(readSessionToken(store), null);
|
|
391
|
+
assert.equal(store.entries.get(appConfig.cartKey), undefined);
|
|
392
|
+
});
|