Files
vmall/apps/mall/mock/api.ts
T
james 2125ae437f feat(mall): live flash-sale page and activity price snapshots
The flash-sale page loads active sessions and their items through the shared
client with live sale price, remaining activity stock, sell-through, and a
countdown, and adds the SKU to the existing cart so checkout resolves the
authoritative price. Payment and order detail tag and render the snapshotted
activity unit price.

The flash-sale fixtures leave the page; the fixed-data adapter still serves the
domain as the rollback path.
2026-09-18 12:53:26 +00:00

781 lines
26 KiB
TypeScript

// Mock API adapter: implements the full @vmall/shared ApiClient surface with
// fixed data + in-memory cart/order state. Pages call $api exactly as they
// would against the live backend, so switching back is a config flip.
import { ApiError } from "@vmall/shared";
import type {
AccountSummary,
Address,
AddressBookEntry,
AddressInput,
ApiClient,
AuthTokens,
Cart,
CartItem,
Coupon,
CouponTemplate,
CouponTemplateInput,
FlashSaleItem,
FlashSaleItemInput,
FlashSaleSession,
FlashSaleSessionInput,
HomeContent,
IntegralOrder,
IntegralProduct,
IntegralProductInput,
Invoice,
InvoiceKind,
Order,
Product,
PublicFlashSaleSession,
RedeemPointsBody,
Shipment,
ShopProfile,
User,
} from "@vmall/shared";
import {
BASE_CURRENCY,
MOCK_BANNERS,
MOCK_BRANDS,
MOCK_CATEGORIES,
MOCK_COUPONS,
MOCK_CURRENCIES,
INTEGRAL_PRODUCTS,
MOCK_PROMOS,
MOCK_QUICK_LINKS,
MOCK_STORES,
MOCK_USER,
USER_STATS,
MOCK_ADDRESSES,
defaultAddress,
lowestSku,
mockConvertMinor,
productById,
searchMockProducts,
seckillProducts,
seedOrders,
storeById,
} from "./data";
interface MockState {
token: string | null;
cart: CartItem[];
orders: Order[];
shipments: Shipment[];
invoices: Invoice[];
addresses: AddressBookEntry[];
/** In-memory only: claims made during this browser session. */
coupons: Coupon[];
/** In-memory points catalog and redemptions for the fixed-data path. */
pointsProducts: IntegralProduct[];
redemptions: IntegralOrder[];
addressSeq: number;
orderSeq: number;
invoiceSeq: number;
redemptionSeq: number;
}
// v3: address book joined the persisted state.
const STORAGE_KEY = "vmall.mock.state.v3";
type PersistedState = Pick<MockState, "cart" | "orders" | "shipments" | "invoices" | "addresses" | "orderSeq" | "invoiceSeq" | "addressSeq">;
// Load cart/order session state persisted by a previous page load (client only).
function loadPersisted(): PersistedState | null {
if (!import.meta.client) return null;
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const parsed: unknown = JSON.parse(raw);
if (typeof parsed !== "object" || parsed === null) return null;
const p = parsed as Partial<PersistedState>;
if (!Array.isArray(p.cart) || !Array.isArray(p.orders)) return null;
if (!Array.isArray(p.shipments) || !Array.isArray(p.invoices)) return null;
if (typeof p.orderSeq !== "number" || typeof p.invoiceSeq !== "number") return null;
if (!Array.isArray(p.addresses) || typeof p.addressSeq !== "number") return null;
return p as PersistedState;
} catch {
return null;
}
}
/** Mock coupons are global fixtures, so any shop id renders them as its own. */
function mockTemplate(t: (typeof MOCK_COUPONS)[number], shopId: string): CouponTemplate {
return {
id: t.id,
shop_id: shopId,
title: t.title,
amount_minor: t.amountMinor,
threshold_minor: t.thresholdMinor,
currency: t.currency,
stock: 100,
enabled: true,
starts_at: "2026-01-01T00:00:00.000Z",
ends_at: `${t.expiresAt}T23:59:59.000Z`,
created_at: "2026-01-01T00:00:00.000Z",
};
}
function mockOwnedCoupon(t: (typeof MOCK_COUPONS)[number]): Coupon {
const template = mockTemplate(t, MOCK_STORES[0].id);
return {
id: `uc-${t.id}`,
user_id: MOCK_USER.id,
template_id: template.id,
shop_id: template.shop_id,
title: template.title,
amount_minor: template.amount_minor,
threshold_minor: template.threshold_minor,
currency: template.currency,
starts_at: template.starts_at,
ends_at: template.ends_at,
status: "claimed",
order_id: null,
claimed_at: "2026-01-01T00:00:00.000Z",
redeemed_at: null,
};
}
function seedCoupons(): Coupon[] {
return MOCK_COUPONS.map(mockOwnedCoupon);
}
function seedPointsProducts(): IntegralProduct[] {
return INTEGRAL_PRODUCTS.map((p) => ({
id: p.id,
name: p.name,
subtitle: null,
content: null,
image: p.image,
points_price: p.points,
stock: p.stock,
published: true,
recommend: false,
position: 0,
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-01T00:00:00.000Z",
}));
}
/** One synthesized active session covering the fixed seckill fixtures. */
function mockFlashSales(): PublicFlashSaleSession[] {
const now = Date.now();
const items = seckillProducts().map((entry, index) => {
const sku = lowestSku(entry.product);
const stock = sku?.stock ?? 100;
const sold = Math.round((stock * entry.soldPct) / 100);
return {
id: `fsi${index + 1}`,
session_id: "fs1",
sku_id: sku?.id ?? "",
product_id: entry.product.id,
product_slug: entry.product.slug,
product_name: entry.product.name,
image: entry.product.images[0] ?? null,
sku_code: sku?.sku_code ?? "",
sale_price_minor: entry.seckillPriceMinor,
currency: sku?.currency ?? BASE_CURRENCY,
original_price_minor: sku?.price_minor ?? entry.seckillPriceMinor,
original_currency: sku?.currency ?? BASE_CURRENCY,
reserved_stock: Math.max(stock - sold, 0),
sold_count: sold,
per_customer_limit: 2,
};
});
return [
{
id: "fs1",
shop_id: MOCK_STORES[0].id,
label: { en: "Flash sale", zh: "限时秒杀" },
starts_at: new Date(now - 3_600_000).toISOString(),
ends_at: new Date(now + 6 * 3_600_000).toISOString(),
enabled: true,
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-01T00:00:00.000Z",
items,
},
];
}
function initialState(): MockState {
const persisted = loadPersisted();
// Coupons and points are session-only, so a restored snapshot re-seeds them.
if (persisted) {
return {
token: null,
...persisted,
coupons: seedCoupons(),
pointsProducts: seedPointsProducts(),
redemptions: [],
redemptionSeq: 0,
};
}
const seed = seedOrders(MOCK_USER.id);
const seededAddresses: AddressBookEntry[] = MOCK_ADDRESSES.map((a, i) => ({
id: a.id,
user_id: MOCK_USER.id,
recipient: a.recipient,
phone: a.phone,
country: "US",
region: a.region,
city: a.city,
line1: a.line1,
postal_code: a.postalCode,
is_default: a.isDefault,
created_at: `2026-08-0${i + 1}T09:00:00.000Z`,
}));
return {
token: null,
cart: [],
orders: seed.orders,
shipments: seed.shipments,
invoices: seed.invoices,
addresses: seededAddresses,
coupons: seedCoupons(),
pointsProducts: seedPointsProducts(),
redemptions: [],
addressSeq: 100,
orderSeq: 100,
invoiceSeq: 100,
redemptionSeq: 0,
};
}
function unsupported(): never {
throw new ApiError(501, "MOCK_UNSUPPORTED", "This admin/shop endpoint is not part of the mall mock.");
}
type MockStoreRecord = NonNullable<ReturnType<typeof storeById>>;
/** Mock stores carry a nested `rate`; the API exposes flat score fields. */
function toShopProfile(store: MockStoreRecord): ShopProfile {
return {
id: store.id,
slug: store.slug,
name: store.name,
company: store.company,
region: store.region,
address: store.address,
logo: store.logo,
banner: store.banner,
notice: store.notice,
after_sale: store.afterSale,
score_rating: store.rate.score,
score_agreement: store.rate.agree,
score_service: store.rate.service,
score_speed: store.rate.speed,
};
}
function skuIndex(): Record<string, { product: Product; skuId: string }> {
const index: Record<string, { product: Product; skuId: string }> = {};
for (const p of searchMockProducts({ perPage: 1000 }).items) {
for (const s of p.skus ?? []) index[s.id] = { product: p, skuId: s.id };
}
return index;
}
export function createMockApi(): ApiClient {
const state = initialState();
function persist(): void {
if (!import.meta.client) return;
try {
const snapshot: PersistedState = {
cart: state.cart,
orders: state.orders,
shipments: state.shipments,
invoices: state.invoices,
orderSeq: state.orderSeq,
invoiceSeq: state.invoiceSeq,
addresses: state.addresses,
addressSeq: state.addressSeq,
};
localStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot));
} catch {
/* storage unavailable: keep in-memory only */
}
}
function tokens(): AuthTokens {
state.token = `mock-token-${Date.now()}`;
return { token: state.token, user: MOCK_USER };
}
function cartSnapshot(): Cart {
return { items: state.cart.map((i) => ({ ...i })) };
}
function nextOrderNo(): string {
state.orderSeq += 1;
return `VM20260917${String(state.orderSeq).padStart(3, "0")}`;
}
return {
register: () => Promise.resolve(tokens()),
login: () => Promise.resolve(tokens()),
me: (): Promise<User> => Promise.resolve(MOCK_USER),
getAccountSummary: (): Promise<AccountSummary> =>
Promise.resolve({
balance_minor: USER_STATS.balanceMinor,
frozen_minor: USER_STATS.frozenMinor,
currency: BASE_CURRENCY,
points: USER_STATS.points,
}),
listProducts: (q = {}) =>
Promise.resolve(
searchMockProducts({
q: q.q,
categoryId: q.category_id,
brandId: q.brand_id,
shopId: q.shop_id,
sort: q.sort,
order: q.order,
page: q.page,
perPage: q.per_page,
}),
),
getProduct: (idOrSlug) => {
const product = productById(idOrSlug);
if (!product) return Promise.reject(new ApiError(404, "NOT_FOUND", "Product not found"));
return Promise.resolve(product);
},
listCategories: () => Promise.resolve(MOCK_CATEGORIES),
listBrands: () => Promise.resolve(MOCK_BRANDS),
listCurrencies: () => Promise.resolve(MOCK_CURRENCIES),
convert: (amountMinor, from, to) =>
Promise.resolve({ amount_minor: mockConvertMinor(amountMinor, from, to), currency: to }),
getCart: () => Promise.resolve(cartSnapshot()),
addCartItem: (skuId, qty) => {
const entry = skuIndex()[skuId];
if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "SKU not found"));
const sku = entry.product.skus?.find((s) => s.id === skuId);
if (!sku || !sku.active) return Promise.reject(new ApiError(400, "SKU_INACTIVE", "SKU is not available"));
const existing = state.cart.find((i) => i.sku_id === skuId);
const nextQty = (existing?.qty ?? 0) + qty;
if (nextQty > sku.stock) return Promise.reject(new ApiError(400, "OUT_OF_STOCK", "Not enough stock"));
if (existing) existing.qty = nextQty;
else {
state.cart.push({
sku_id: skuId,
product_id: entry.product.id,
product_name: entry.product.name,
sku_code: sku.sku_code,
image: entry.product.images[0] ?? null,
unit_price_minor: sku.price_minor,
currency: sku.currency,
qty,
// The live cart view carries these too; see replace-mock-api-wave-3.
shop_id: entry.product.shop_id,
shop_name: storeById(entry.product.shop_id)?.name ?? MOCK_STORES[0].name,
stock: sku.stock,
});
}
persist();
return Promise.resolve(cartSnapshot());
},
updateCartItem: (skuId, qty) => {
const item = state.cart.find((i) => i.sku_id === skuId);
if (!item) return Promise.reject(new ApiError(404, "NOT_FOUND", "Cart item not found"));
if (qty <= 0) state.cart = state.cart.filter((i) => i.sku_id !== skuId);
else item.qty = qty;
persist();
return Promise.resolve(cartSnapshot());
},
removeCartItem: (skuId) => {
state.cart = state.cart.filter((i) => i.sku_id !== skuId);
persist();
return Promise.resolve(cartSnapshot());
},
checkout: (
shippingAddress: Address,
currency: string,
couponByShop: Record<string, string> = {},
) => {
if (state.cart.length === 0) {
return Promise.reject(new ApiError(400, "EMPTY_CART", "Cart is empty"));
}
const byShop: Record<string, CartItem[]> = {};
for (const item of state.cart) {
const product = productById(item.product_id);
const shopId = product?.shop_id ?? "unknown";
(byShop[shopId] ??= []).push(item);
}
const created: Order[] = [];
for (const [shopId, items] of Object.entries(byShop)) {
const subtotal = items.reduce((sum, i) => sum + i.unit_price_minor * i.qty, 0);
const couponId = couponByShop[shopId];
let discount = 0;
if (couponId) {
const coupon = state.coupons.find((c) => c.id === couponId);
if (!coupon || coupon.status !== "claimed" || coupon.shop_id !== shopId) {
return Promise.reject(new ApiError(409, "CONFLICT", "Coupon is not redeemable"));
}
if (subtotal < coupon.threshold_minor) {
return Promise.reject(
new ApiError(409, "CONFLICT", "Order does not reach the coupon threshold"),
);
}
discount = Math.min(coupon.amount_minor, subtotal);
}
const order: Order = {
id: `o-${Date.now()}-${shopId}`,
order_no: nextOrderNo(),
shop_id: shopId,
user_id: MOCK_USER.id,
status: "pending_payment",
currency,
total_minor: subtotal - discount,
discount_minor: discount,
coupon_id: couponId ?? null,
items: items.map((i, k) => ({
id: `oi-${Date.now()}-${k}`,
sku_id: i.sku_id,
product_name: i.product_name,
sku_code: i.sku_code,
unit_price_minor: i.unit_price_minor,
qty: i.qty,
image: i.image,
flash_sale_item_id: null,
})),
shipping_address: shippingAddress,
created_at: new Date().toISOString(),
};
if (couponId) {
const coupon = state.coupons.find((c) => c.id === couponId);
if (coupon) {
coupon.status = "redeemed";
coupon.order_id = order.id;
coupon.redeemed_at = order.created_at;
}
}
created.push(order);
}
state.orders = [...created, ...state.orders];
state.cart = [];
persist();
return Promise.resolve(created);
},
listMyOrders: (page = 1) => {
const perPage = 10;
const start = (page - 1) * perPage;
return Promise.resolve({
items: state.orders.slice(start, start + perPage),
total: state.orders.length,
page,
per_page: perPage,
});
},
getOrder: (id) => {
const order = state.orders.find((o) => o.id === id || o.order_no === id);
if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found"));
return Promise.resolve(order);
},
cancelOrder: (id) => {
const order = state.orders.find((o) => o.id === id);
if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found"));
if (order.status !== "pending_payment") {
return Promise.reject(new ApiError(409, "INVALID_STATE", "Only unpaid orders can be cancelled"));
}
order.status = "cancelled";
const coupon = state.coupons.find((c) => c.order_id === order.id);
if (coupon) {
coupon.status = "claimed";
coupon.order_id = null;
coupon.redeemed_at = null;
}
persist();
return Promise.resolve(order);
},
payOrder: (id) => {
const order = state.orders.find((o) => o.id === id);
if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found"));
if (order.status !== "pending_payment") {
return Promise.reject(new ApiError(409, "INVALID_STATE", "Only unpaid orders can be paid"));
}
order.status = "paid";
persist();
return Promise.resolve(order);
},
confirmDelivered: (shipmentId) => {
const shipment = state.shipments.find((s) => s.id === shipmentId);
if (!shipment) return Promise.reject(new ApiError(404, "NOT_FOUND", "Shipment not found"));
shipment.status = "delivered";
shipment.delivered_at = new Date().toISOString();
const order = state.orders.find((o) => o.id === shipment.order_id);
if (order) order.status = "completed";
persist();
return Promise.resolve(shipment);
},
listMyShipments: () => Promise.resolve(state.shipments.map((s) => ({ ...s }))),
requestInvoice: (orderId: string, title: string, taxNo: string | null, kind: InvoiceKind) => {
const order = state.orders.find((o) => o.id === orderId);
if (!order) return Promise.reject(new ApiError(404, "NOT_FOUND", "Order not found"));
state.invoiceSeq += 1;
const invoice: Invoice = {
id: `inv-${Date.now()}`,
invoice_no: `INV20260917${String(state.invoiceSeq).padStart(3, "0")}`,
order_id: order.id,
order_no: order.order_no,
title,
tax_no: taxNo,
kind,
amount_minor: order.total_minor,
currency: order.currency,
status: "requested",
issued_at: null,
created_at: new Date().toISOString(),
};
state.invoices = [invoice, ...state.invoices];
persist();
return Promise.resolve(invoice);
},
listMyInvoices: () => Promise.resolve(state.invoices.map((i) => ({ ...i }))),
// Mirror of the seeded storefront-content rows, so the home page renders
// identically when every domain is configured to fixed data.
getHomeContent: (): Promise<HomeContent> =>
Promise.resolve({
banners: MOCK_BANNERS.map((b, i) => ({
id: `bn${i + 1}`,
image: b.image,
url: b.url,
position: i,
active: true,
})),
promos: MOCK_PROMOS.map((p, i) => ({
id: `pr${i + 1}`,
image: p.image,
url: p.url,
position: i,
active: true,
})),
quick_links: MOCK_QUICK_LINKS.map((q, i) => ({
id: `ql${i + 1}`,
label: q.label,
url: q.url,
glyph: q.glyph,
position: i,
active: true,
})),
floor_adverts: Array.from({ length: 6 }, (_, i) => ({
id: `fa${i + 1}`,
image: `/mock/floor-adv-${i + 1}.svg`,
position: i,
active: true,
})),
}),
listShops: (): Promise<ShopProfile[]> => Promise.resolve(MOCK_STORES.map(toShopProfile)), getShop: (slug) => {
const store = storeById(slug);
if (!store) return Promise.reject(new ApiError(404, "NOT_FOUND", "Shop not found"));
return Promise.resolve(toShopProfile(store));
},
listMyAddresses: () =>
Promise.resolve(
[...state.addresses].sort(
(a, b) => Number(b.is_default) - Number(a.is_default) || b.created_at.localeCompare(a.created_at),
),
),
createAddress: (input: AddressInput) => {
const makeDefault = input.is_default === true || state.addresses.length === 0;
if (makeDefault) for (const a of state.addresses) a.is_default = false;
state.addressSeq += 1;
const entry: AddressBookEntry = {
id: `a-${state.addressSeq}`,
user_id: MOCK_USER.id,
recipient: input.recipient,
phone: input.phone,
country: input.country,
region: input.region,
city: input.city,
line1: input.line1,
postal_code: input.postal_code,
is_default: makeDefault,
created_at: new Date().toISOString(),
};
state.addresses.push(entry);
persist();
return Promise.resolve(entry);
},
updateAddress: (id: string, input: AddressInput) => {
const entry = state.addresses.find((a) => a.id === id);
if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "Address not found"));
if (input.is_default === true && !entry.is_default) {
for (const a of state.addresses) a.is_default = false;
}
entry.recipient = input.recipient;
entry.phone = input.phone;
entry.country = input.country;
entry.region = input.region;
entry.city = input.city;
entry.line1 = input.line1;
entry.postal_code = input.postal_code;
entry.is_default = input.is_default === true || entry.is_default;
persist();
return Promise.resolve({ ...entry });
},
deleteAddress: (id: string) => {
const entry = state.addresses.find((a) => a.id === id);
if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "Address not found"));
const wasDefault = entry.is_default;
state.addresses = state.addresses.filter((a) => a.id !== id);
if (wasDefault && state.addresses.length > 0) {
const latest = state.addresses.reduce((a, b) => (a.created_at > b.created_at ? a : b));
latest.is_default = true;
}
persist();
return Promise.resolve([...state.addresses]);
},
setDefaultAddress: (id: string) => {
const entry = state.addresses.find((a) => a.id === id);
if (!entry) return Promise.reject(new ApiError(404, "NOT_FOUND", "Address not found"));
for (const a of state.addresses) a.is_default = a.id === id;
persist();
return Promise.resolve({ ...entry });
},
listShopCouponTemplates: (shopId: string) =>
Promise.resolve(MOCK_COUPONS.map((t) => mockTemplate(t, shopId))),
listMyCoupons: () => Promise.resolve(state.coupons.map((c) => ({ ...c }))),
claimCoupon: (templateId: string) => {
const template = MOCK_COUPONS.find((t) => t.id === templateId);
if (!template) return Promise.reject(new ApiError(404, "NOT_FOUND", "Coupon not found"));
if (state.coupons.some((c) => c.template_id === templateId)) {
return Promise.reject(new ApiError(409, "CONFLICT", "Coupon already claimed"));
}
const coupon = mockOwnedCoupon(template);
state.coupons.push(coupon);
persist();
return Promise.resolve({ ...coupon });
},
listPointsProducts: () =>
Promise.resolve(
state.pointsProducts.filter((p) => p.published).map((p) => ({ ...p })),
),
listFlashSales: () => Promise.resolve(mockFlashSales()),
listMyRedemptions: () => Promise.resolve(state.redemptions.map((o) => ({ ...o }))),
redeemPoints: (body: RedeemPointsBody) => {
const product = state.pointsProducts.find(
(p) => p.id === body.product_id && p.published,
);
if (!product) {
return Promise.reject(new ApiError(404, "NOT_FOUND", "Points product not found"));
}
if (body.qty <= 0 || body.qty > product.stock) {
return Promise.reject(new ApiError(409, "CONFLICT", "Insufficient points stock"));
}
const total = product.points_price * body.qty;
if (total > USER_STATS.points) {
return Promise.reject(new ApiError(409, "CONFLICT", "Insufficient points"));
}
state.redemptionSeq += 1;
const order: IntegralOrder = {
id: `po-${state.redemptionSeq}`,
order_no: `PM${String(state.redemptionSeq).padStart(8, "0")}`,
user_id: MOCK_USER.id,
status: "pending_fulfillment",
total_points: total,
shipping_address: body.shipping_address,
items: [
{
id: `poi-${state.redemptionSeq}`,
order_id: `po-${state.redemptionSeq}`,
product_id: product.id,
name: product.name,
image: product.image,
points_price: product.points_price,
qty: body.qty,
},
],
created_at: new Date().toISOString(),
};
product.stock -= body.qty;
state.redemptions = [order, ...state.redemptions];
persist();
return Promise.resolve({ ...order });
},
shop: {
getMyShop: () => unsupported(),
listMyProducts: () => unsupported(),
getProduct: () => unsupported(),
createProduct: () => unsupported(),
updateProduct: () => unsupported(),
publish: () => unsupported(),
unpublish: () => unsupported(),
upsertSku: () => unsupported(),
listOrders: () => unsupported(),
getOrder: () => unsupported(),
createShipment: () => unsupported(),
listShipments: () => unsupported(),
markShipped: () => unsupported(),
listInvoices: () => unsupported(),
issueInvoice: () => unsupported(),
listCouponTemplates: () => unsupported(),
createCouponTemplate: (_body: CouponTemplateInput) => unsupported(),
updateCouponTemplate: (_id: string, _body: CouponTemplateInput) => unsupported(),
deleteCouponTemplate: (_id: string) => unsupported(),
listFlashSales: () => unsupported(),
createFlashSale: (_body: FlashSaleSessionInput) => unsupported(),
updateFlashSale: (_id: string, _body: FlashSaleSessionInput) => unsupported(),
deleteFlashSale: (_id: string) => unsupported(),
addFlashSaleItem: (_sessionId: string, _body: FlashSaleItemInput) => unsupported(),
updateFlashSaleItem: (_id: string, _body: FlashSaleItemInput) => unsupported(),
deleteFlashSaleItem: (_id: string) => unsupported(),
},
admin: {
listUsers: () => unsupported(),
setUserRole: () => unsupported(),
listShops: () => unsupported(),
createShop: () => unsupported(),
setShopStatus: () => unsupported(),
listOrders: () => unsupported(),
listCurrencies: () => unsupported(),
upsertCurrency: () => unsupported(),
setRate: () => unsupported(),
getContent: () => unsupported(),
replaceContent: () => unsupported(),
setShopProfile: () => unsupported(),
getBrands: () => unsupported(),
replaceBrands: () => unsupported(),
listPointsProducts: () => unsupported(),
createPointsProduct: (_body: IntegralProductInput) => unsupported(),
updatePointsProduct: (_id: string, _body: IntegralProductInput) => unsupported(),
setPointsProductPublished: (_id: string, _published: boolean) => unsupported(),
listPointsRedemptions: (_page?: number) => unsupported(),
fulfillRedemption: (_id: string) => unsupported(),
cancelRedemption: (_id: string) => unsupported(),
},
};
}
export { defaultAddress };