@mohasinac/appkit 3.2.5 → 3.2.7
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/_internal/client/features/layout/SidebarCollapseToggle.js +1 -1
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +3 -3
- package/dist/_internal/server/features/products/data.d.ts +2 -1
- package/dist/_internal/server/jobs/core/countersReconcile.js +2 -1
- package/dist/_internal/shared/actions/action-registry.js +62 -0
- package/dist/_internal/shared/features/products/types.d.ts +3 -2
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +12 -0
- package/dist/constants/api-endpoints.js +4 -0
- package/dist/constants/field-names.d.ts +2 -0
- package/dist/constants/field-names.js +2 -0
- package/dist/features/about/components/PublicProfileView.js +8 -5
- package/dist/features/account/components/UserOffersPanel.js +0 -1
- package/dist/features/addresses/repository/addresses.repository.d.ts +14 -1
- package/dist/features/addresses/repository/addresses.repository.js +122 -1
- package/dist/features/addresses/schemas/firestore.d.ts +22 -3
- package/dist/features/addresses/schemas/firestore.js +25 -0
- package/dist/features/addresses/schemas/index.d.ts +2 -2
- package/dist/features/admin/components/AdminAddressClustersView.d.ts +5 -0
- package/dist/features/admin/components/AdminAddressClustersView.js +38 -0
- package/dist/features/admin/components/AdminAddressesView.d.ts +5 -0
- package/dist/features/admin/components/AdminAddressesView.js +137 -0
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -4
- package/dist/features/admin/components/AdminFulfillmentView.js +2 -3
- package/dist/features/admin/components/AdminOrderEditorView.js +1 -3
- package/dist/features/admin/components/AdminPaymentClustersView.d.ts +5 -0
- package/dist/features/admin/components/AdminPaymentClustersView.js +37 -0
- package/dist/features/admin/components/AdminPaymentMethodsView.d.ts +5 -0
- package/dist/features/admin/components/AdminPaymentMethodsView.js +130 -0
- package/dist/features/admin/components/AdminSiteSettingsView.js +2 -2
- package/dist/features/admin/components/index.d.ts +8 -0
- package/dist/features/admin/components/index.js +4 -0
- package/dist/features/admin/schemas/firestore.d.ts +3 -5
- package/dist/features/admin/types/product.types.d.ts +2 -1
- package/dist/features/auth/actions/profile-actions.d.ts +3 -1
- package/dist/features/auth/actions/profile-actions.js +29 -2
- package/dist/features/auth/schemas/firestore.d.ts +8 -10
- package/dist/features/cart/schemas/index.d.ts +6 -6
- package/dist/features/categories/components/CategoryBundlesListing.js +1 -3
- package/dist/features/checkout/schemas/firestore.d.ts +4 -4
- package/dist/features/copilot/components/AdminCopilotView.js +4 -4
- package/dist/features/events/schemas/firestore.d.ts +2 -2
- package/dist/features/grouped/schemas/firestore.d.ts +2 -4
- package/dist/features/history/hooks/useHistory.js +3 -2
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +6 -6
- package/dist/features/homepage/actions/homepage-section-actions.js +2 -2
- package/dist/features/homepage/components/PromoGrid.js +1 -1
- package/dist/features/homepage/schemas/firestore.d.ts +15 -14
- package/dist/features/layout/AppLayoutShell.js +3 -18
- package/dist/features/layout/background-seed-defaults.d.ts +19 -0
- package/dist/features/layout/background-seed-defaults.js +14 -0
- package/dist/features/lottery/schemas/firestore.d.ts +2 -2
- package/dist/features/messages/hooks/useConversation.js +5 -13
- package/dist/features/messages/hooks/useConversations.js +0 -1
- package/dist/features/messages/schemas/firestore.d.ts +2 -2
- package/dist/features/orders/components/RefundHistoryTable.js +1 -3
- package/dist/features/orders/schemas/firestore.d.ts +2 -1
- package/dist/features/orders/schemas/index.d.ts +11 -7
- package/dist/features/orders/schemas/index.js +1 -1
- package/dist/features/payments/repository/saved-payment-methods.repository.d.ts +48 -0
- package/dist/features/payments/repository/saved-payment-methods.repository.js +236 -0
- package/dist/features/payments/schemas/index.d.ts +1 -0
- package/dist/features/payments/schemas/index.js +1 -0
- package/dist/features/payments/schemas/saved-methods-firestore.d.ts +63 -0
- package/dist/features/payments/schemas/saved-methods-firestore.js +60 -0
- package/dist/features/payments/server.d.ts +2 -0
- package/dist/features/payments/server.js +2 -0
- package/dist/features/products/actions/product-actions.d.ts +2 -1
- package/dist/features/products/api/[id]/route.js +1 -1
- package/dist/features/products/api/route.js +1 -1
- package/dist/features/products/components/CompareOverlay.d.ts +18 -1
- package/dist/features/products/components/CompareOverlay.js +0 -16
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ShowGroupSection.js +1 -3
- package/dist/features/products/repository/product-features.repository.js +1 -1
- package/dist/features/products/schemas/firestore.d.ts +3 -6
- package/dist/features/promotions/actions/coupon-actions.d.ts +2 -1
- package/dist/features/promotions/hooks/useCouponValidate.d.ts +2 -1
- package/dist/features/promotions/repository/coupons.repository.d.ts +2 -1
- package/dist/features/promotions/schemas/firestore.d.ts +4 -4
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/reviews/repository/reviews.repository.d.ts +12 -0
- package/dist/features/reviews/repository/reviews.repository.js +24 -0
- package/dist/features/reviews/schemas/firestore.d.ts +5 -1
- package/dist/features/reviews/schemas/firestore.js +2 -0
- package/dist/features/reviews/schemas/index.d.ts +6 -6
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/schemas/firestore.d.ts +6 -6
- package/dist/features/search/types/index.d.ts +3 -2
- package/dist/features/search/types/index.js +0 -1
- package/dist/features/seller/components/FulfillmentView.js +2 -3
- package/dist/features/seller/components/PrintCenterView.js +1 -3
- package/dist/features/seller/components/SellerAddressesView.js +7 -4
- package/dist/features/seller/components/SellerOffersPanel.js +0 -1
- package/dist/features/seller/components/SellerReviewsView.js +0 -1
- package/dist/features/seller/schemas/firestore.d.ts +2 -4
- package/dist/features/server-errors/schemas/firestore.d.ts +2 -2
- package/dist/features/store-extensions/schemas/firestore.d.ts +2 -2
- package/dist/features/stores/components/StoreScopedSearch.js +2 -4
- package/dist/features/stores/repository/store.repository.js +3 -1
- package/dist/features/stores/types/index.d.ts +2 -1
- package/dist/features/stores/types/index.js +0 -1
- package/dist/features/support/schemas/firestore.d.ts +2 -2
- package/dist/features/support/schemas/index.d.ts +8 -8
- package/dist/features/wishlist/schemas/index.d.ts +2 -2
- package/dist/features/wishlist/types/index.d.ts +2 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/next/routing/route-map.d.ts +8 -0
- package/dist/next/routing/route-map.js +4 -0
- package/dist/providers/auth-firebase/provider.js +3 -1
- package/dist/providers/auth-firebase/session.js +3 -1
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +66 -66
- package/dist/schemas/webhooks/razorpay.d.ts +50 -50
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-schemas.d.ts +2 -0
- package/dist/security/pii-schemas.js +2 -0
- package/dist/seo/json-ld.d.ts +2 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Form.d.ts +3 -1
- package/dist/ui/components/Form.js +2 -1
- package/dist/ui/components/Radio.d.ts +7 -0
- package/dist/ui/components/Radio.js +7 -0
- package/dist/ui/components/surface-tokens.d.ts +4 -0
- package/dist/ui/components/surface-tokens.js +24 -0
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +1 -1
- package/dist/ui/rich-text/RichText.js +0 -1
- package/package.json +5 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Seed defaults for siteSettings.theme.background — Firestore data values, not CSS styling.
|
|
2
|
+
// These are the first-boot theme configuration and are exempt from the hex-tokens audit.
|
|
3
|
+
// Override via the admin theme editor (Site Settings → Themes).
|
|
4
|
+
export const SEED_LIGHT_BG = {
|
|
5
|
+
type: "color",
|
|
6
|
+
value: "#f9fafb",
|
|
7
|
+
overlay: { enabled: false, color: "#000000", opacity: 0 },
|
|
8
|
+
};
|
|
9
|
+
export const SEED_DARK_BG = {
|
|
10
|
+
type: "color",
|
|
11
|
+
value: "#030712",
|
|
12
|
+
overlay: { enabled: false, color: "#000000", opacity: 0 },
|
|
13
|
+
};
|
|
14
|
+
export const OVERLAY_FALLBACK_COLOR = "#000000";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { LotteryEntryStatus } from "../types";
|
|
5
5
|
export declare const LOTTERY_ENTRIES_COLLECTION: "lotteryEntries";
|
|
6
|
-
export
|
|
6
|
+
export type LotteryEntryDocument = {
|
|
7
7
|
id: string;
|
|
8
8
|
sourceType: "event" | "product";
|
|
9
9
|
/** Set when sourceType === "event" */
|
|
@@ -33,7 +33,7 @@ export interface LotteryEntryDocument {
|
|
|
33
33
|
flaggedAt?: Date;
|
|
34
34
|
submittedAt: Date;
|
|
35
35
|
drawnAt?: Date;
|
|
36
|
-
}
|
|
36
|
+
};
|
|
37
37
|
export declare const LOTTERY_ENTRY_INDEXED_FIELDS: readonly ["sourceType", "eventId", "productId", "userId", "status", "submittedAt", "userLotteryNumber"];
|
|
38
38
|
/** Canonical field name constants for lotteryEntries collection — use instead of hardcoded strings. */
|
|
39
39
|
export declare const LOTTERY_ENTRY_FIELDS: {
|
|
@@ -51,7 +51,6 @@ export function useConversation(conversationId) {
|
|
|
51
51
|
setConversation(await fetchDetail(conversationId));
|
|
52
52
|
}
|
|
53
53
|
catch (e) {
|
|
54
|
-
// toast-intentionally-silent: background refetch, error surfaced via error state
|
|
55
54
|
void normalizeError(e);
|
|
56
55
|
setError(e instanceof Error ? e : new Error(String(e)));
|
|
57
56
|
}
|
|
@@ -102,19 +101,12 @@ export function useConversation(conversationId) {
|
|
|
102
101
|
// the ping path isn't subscribed (e.g. provider not registered).
|
|
103
102
|
await refetch();
|
|
104
103
|
}, [conversationId, refetch]);
|
|
105
|
-
const markRead = useCallback(
|
|
104
|
+
const markRead = useCallback(() => {
|
|
106
105
|
if (!conversationId)
|
|
107
|
-
return;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
credentials: "include",
|
|
112
|
-
});
|
|
113
|
-
await refetch();
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
// toast-intentionally-silent: read-receipt is best-effort, non-critical
|
|
117
|
-
}
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
return fetch(READ_ENDPOINT(conversationId), { method: "POST", credentials: "include" })
|
|
108
|
+
.then(() => void refetch())
|
|
109
|
+
.catch((err) => void normalizeError(err));
|
|
118
110
|
}, [conversationId, refetch]);
|
|
119
111
|
return {
|
|
120
112
|
conversation,
|
|
@@ -31,7 +31,6 @@ export function useConversations(userId) {
|
|
|
31
31
|
setConversations(await fetchList());
|
|
32
32
|
}
|
|
33
33
|
catch (e) {
|
|
34
|
-
// toast-intentionally-silent: background refetch, error surfaced via error state
|
|
35
34
|
void normalizeError(e);
|
|
36
35
|
setError(e instanceof Error ? e : new Error(String(e)));
|
|
37
36
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* a subcollection when the thread exceeds ~200 messages.
|
|
7
7
|
*/
|
|
8
8
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
9
|
-
export
|
|
9
|
+
export type ConversationMessage = {
|
|
10
10
|
id: string;
|
|
11
11
|
senderId: string;
|
|
12
12
|
senderRole: "buyer" | "seller";
|
|
@@ -14,7 +14,7 @@ export interface ConversationMessage {
|
|
|
14
14
|
isRead: boolean;
|
|
15
15
|
sentAt: Date;
|
|
16
16
|
attachments?: string[];
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
export interface ConversationDocument extends BaseDocument {
|
|
19
19
|
buyerId: string;
|
|
20
20
|
buyerDisplayName: string;
|
|
@@ -21,7 +21,5 @@ export function RefundHistoryTable({ order, className = "" }) {
|
|
|
21
21
|
const currency = order.currency ?? "INR";
|
|
22
22
|
if (events.length === 0 && order.contestable !== false)
|
|
23
23
|
return null;
|
|
24
|
-
return (_jsxs(Div, { className: `${className}`, rounded: "xl", surface: "default", border: "default", children: [order.contestable === false && (
|
|
25
|
-
// audit-inline-style-ok: asymmetric border-radius (top corners only); no appkit rounded= variant covers partial corners
|
|
26
|
-
_jsx(Div, { surface: "warning-surface", padding: "inline", style: { borderTopLeftRadius: "0.75rem", borderTopRightRadius: "0.75rem" }, children: _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(WarningIcon, {}), _jsx(Text, { size: "sm", weight: "semibold", className: "text-warning", children: REFUND_COPY.history.nonContestableBanner })] }) })), events.length > 0 && (_jsxs(Div, { className: `${__P.p4}`, children: [_jsx(Heading, { level: 4, className: "mb-3", color: "muted", size: "sm", weight: "semibold", children: REFUND_COPY.history.heading }), _jsx(Stack, { gap: "none", children: events.map((e) => (_jsx(RefundRow, { event: e, currency: currency }, e.refundId))) })] }))] }));
|
|
24
|
+
return (_jsxs(Div, { className: `${className}`, rounded: "xl", surface: "default", border: "default", children: [order.contestable === false && (_jsx(Div, { surface: "warning-surface", padding: "inline", roundedTop: "lg", children: _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(WarningIcon, {}), _jsx(Text, { size: "sm", weight: "semibold", className: "text-warning", children: REFUND_COPY.history.nonContestableBanner })] }) })), events.length > 0 && (_jsxs(Div, { className: `${__P.p4}`, children: [_jsx(Heading, { level: 4, className: "mb-3", color: "muted", size: "sm", weight: "semibold", children: REFUND_COPY.history.heading }), _jsx(Stack, { gap: "none", children: events.map((e) => (_jsx(RefundRow, { event: e, currency: currency }, e.refundId))) })] }))] }));
|
|
27
25
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { OrderStatus, PaymentStatus } from "../types";
|
|
5
5
|
import type { OrderType } from "../utils/order-splitter";
|
|
6
6
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
7
|
+
import type { ListingType } from "../../products/types";
|
|
7
8
|
export type ShippingMethod = "custom" | "shiprocket";
|
|
8
9
|
export type RefundType = "full" | "partial";
|
|
9
10
|
export type OrderPayoutStatus = "eligible" | "requested" | "paid";
|
|
@@ -58,7 +59,7 @@ export interface OrderDocumentItem {
|
|
|
58
59
|
unitPrice: number;
|
|
59
60
|
totalPrice: number;
|
|
60
61
|
/** SB8-F â€" set when the item is a prize-draw entry; drives the reveals badge. */
|
|
61
|
-
listingType?:
|
|
62
|
+
listingType?: ListingType;
|
|
62
63
|
/** SB8-F â€" per-item reveal status; flips through pending â†' open â†' revealed/closed. */
|
|
63
64
|
prizeRevealStatus?: "pending" | "open" | "closed" | "revealed";
|
|
64
65
|
/** SB8-F â€" ISO timestamp; deadline by which the buyer must claim the prize. */
|
|
@@ -417,10 +417,10 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
417
417
|
updatedAt: string | Date | z.objectOutputType<{
|
|
418
418
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
419
419
|
}, z.ZodTypeAny, "passthrough">;
|
|
420
|
+
userId: string;
|
|
420
421
|
productId: string;
|
|
421
422
|
quantity: number;
|
|
422
423
|
productTitle: string;
|
|
423
|
-
userId: string;
|
|
424
424
|
userEmail: string;
|
|
425
425
|
paymentStatus: "pending" | "paid" | "failed" | "processing" | "refunded" | "partial_refund";
|
|
426
426
|
orderDate: string | Date | z.objectOutputType<{
|
|
@@ -546,10 +546,10 @@ export declare const orderFirestoreSchema: z.ZodObject<{
|
|
|
546
546
|
updatedAt: string | Date | z.objectInputType<{
|
|
547
547
|
toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
|
|
548
548
|
}, z.ZodTypeAny, "passthrough">;
|
|
549
|
+
userId: string;
|
|
549
550
|
productId: string;
|
|
550
551
|
quantity: number;
|
|
551
552
|
productTitle: string;
|
|
552
|
-
userId: string;
|
|
553
553
|
userEmail: string;
|
|
554
554
|
paymentStatus: "pending" | "paid" | "failed" | "processing" | "refunded" | "partial_refund";
|
|
555
555
|
orderDate: string | Date | z.objectInputType<{
|
|
@@ -756,7 +756,7 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
756
756
|
storeId?: string | undefined;
|
|
757
757
|
attributes?: Record<string, string> | undefined;
|
|
758
758
|
}>, "many">;
|
|
759
|
-
address: z.
|
|
759
|
+
address: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>;
|
|
760
760
|
orderStatus: z.ZodEnum<["pending", "confirmed", "processing", "shipped", "delivered", "cancelled", "refunded", "return_requested", "returned"]>;
|
|
761
761
|
paymentStatus: z.ZodString;
|
|
762
762
|
paymentGateway: z.ZodOptional<z.ZodString>;
|
|
@@ -791,7 +791,10 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
791
791
|
}, "strip", z.ZodTypeAny, {
|
|
792
792
|
currency: string;
|
|
793
793
|
id: string;
|
|
794
|
-
address:
|
|
794
|
+
address: {} & {
|
|
795
|
+
[k: string]: unknown;
|
|
796
|
+
};
|
|
797
|
+
userId: string;
|
|
795
798
|
items: {
|
|
796
799
|
title: string;
|
|
797
800
|
price: number;
|
|
@@ -803,7 +806,6 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
803
806
|
attributes?: Record<string, string> | undefined;
|
|
804
807
|
}[];
|
|
805
808
|
total: number;
|
|
806
|
-
userId: string;
|
|
807
809
|
paymentStatus: string;
|
|
808
810
|
subtotal: number;
|
|
809
811
|
orderStatus: "cancelled" | "pending" | "processing" | "refunded" | "confirmed" | "shipped" | "delivered" | "return_requested" | "returned";
|
|
@@ -826,7 +828,10 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
826
828
|
}, {
|
|
827
829
|
currency: string;
|
|
828
830
|
id: string;
|
|
829
|
-
address:
|
|
831
|
+
address: {} & {
|
|
832
|
+
[k: string]: unknown;
|
|
833
|
+
};
|
|
834
|
+
userId: string;
|
|
830
835
|
items: {
|
|
831
836
|
title: string;
|
|
832
837
|
price: number;
|
|
@@ -838,7 +843,6 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
838
843
|
attributes?: Record<string, string> | undefined;
|
|
839
844
|
}[];
|
|
840
845
|
total: number;
|
|
841
|
-
userId: string;
|
|
842
846
|
paymentStatus: string;
|
|
843
847
|
subtotal: number;
|
|
844
848
|
orderStatus: "cancelled" | "pending" | "processing" | "refunded" | "confirmed" | "shipped" | "delivered" | "return_requested" | "returned";
|
|
@@ -194,7 +194,7 @@ export const orderSchema = z.object({
|
|
|
194
194
|
userId: z.string(),
|
|
195
195
|
items: z.array(orderItemSchema),
|
|
196
196
|
// address shape is intentionally open to allow extending apps
|
|
197
|
-
address: z.
|
|
197
|
+
address: z.object({}).passthrough(),
|
|
198
198
|
orderStatus: orderStatusSchema,
|
|
199
199
|
paymentStatus: z.string(),
|
|
200
200
|
paymentGateway: z.string().optional(),
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SavedPaymentMethodsRepository
|
|
3
|
+
*
|
|
4
|
+
* Persists user payment identifiers (UPI VPAs, card tokens, etc.) for
|
|
5
|
+
* checkout pre-fill and cross-account fraud detection.
|
|
6
|
+
*
|
|
7
|
+
* PII: `identifier` is encrypted at rest (AES-256-GCM) — never returned
|
|
8
|
+
* to clients. `identifierHash` is unencrypted SHA-256 for cross-account
|
|
9
|
+
* dedup queries. `displayLabel` is pre-masked and safe to display.
|
|
10
|
+
*/
|
|
11
|
+
import { BaseRepository, type DocumentSnapshot } from "../../../providers/db-firebase";
|
|
12
|
+
import { type SavedPaymentMethodBanStatus, type SavedPaymentMethodCreateInput, type SavedPaymentMethodDocument } from "../schemas/saved-methods-firestore";
|
|
13
|
+
export declare class SavedPaymentMethodsRepository extends BaseRepository<SavedPaymentMethodDocument> {
|
|
14
|
+
constructor();
|
|
15
|
+
private decrypt;
|
|
16
|
+
private encrypt;
|
|
17
|
+
protected mapDoc<D = SavedPaymentMethodDocument>(snap: DocumentSnapshot): D;
|
|
18
|
+
createWithId(id: string, data: Partial<SavedPaymentMethodDocument>): Promise<SavedPaymentMethodDocument>;
|
|
19
|
+
update(id: string, data: Partial<SavedPaymentMethodDocument>): Promise<SavedPaymentMethodDocument>;
|
|
20
|
+
/** List all saved methods for a user. Never returns raw `identifier` — only `displayLabel`. */
|
|
21
|
+
listByUser(userId: string): Promise<SavedPaymentMethodDocument[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Idempotent upsert by (userId + identifierHash).
|
|
24
|
+
* Computes hash + encrypts identifier before write.
|
|
25
|
+
* Updates lastUsedAt on re-add.
|
|
26
|
+
*/
|
|
27
|
+
upsertForUser(userId: string, input: SavedPaymentMethodCreateInput): Promise<SavedPaymentMethodDocument>;
|
|
28
|
+
/** Cross-account lookup by identifierHash. Returns display-safe docs (identifier stripped). */
|
|
29
|
+
listByIdentifierHash(hash: string): Promise<SavedPaymentMethodDocument[]>;
|
|
30
|
+
/** Batch-ban all methods for a user. Used by hard-ban cascade. */
|
|
31
|
+
banAllForUser(userId: string, banData: {
|
|
32
|
+
banReason: string;
|
|
33
|
+
bannedBy: string;
|
|
34
|
+
}): Promise<number>;
|
|
35
|
+
/** Reverse auto-ban cascade on user unban. Leaves manually-banned methods untouched. */
|
|
36
|
+
unbanAutoForUser(userId: string): Promise<number>;
|
|
37
|
+
/** List by banStatus for admin view. Returns display-safe docs. */
|
|
38
|
+
listByBanStatus(banStatus: SavedPaymentMethodBanStatus, limit?: number, offset?: number): Promise<SavedPaymentMethodDocument[]>;
|
|
39
|
+
banById(id: string, banData: {
|
|
40
|
+
banReason: string;
|
|
41
|
+
bannedBy: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
clearBanById(id: string): Promise<void>;
|
|
44
|
+
deleteForUser(userId: string, id: string): Promise<void>;
|
|
45
|
+
/** Public wrapper — compute identifier hash from outside the class (e.g. in API routes). */
|
|
46
|
+
computeIdentifierHash(type: string, identifier: string): string;
|
|
47
|
+
}
|
|
48
|
+
export declare const savedPaymentMethodsRepository: SavedPaymentMethodsRepository;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SavedPaymentMethodsRepository
|
|
3
|
+
*
|
|
4
|
+
* Persists user payment identifiers (UPI VPAs, card tokens, etc.) for
|
|
5
|
+
* checkout pre-fill and cross-account fraud detection.
|
|
6
|
+
*
|
|
7
|
+
* PII: `identifier` is encrypted at rest (AES-256-GCM) — never returned
|
|
8
|
+
* to clients. `identifierHash` is unencrypted SHA-256 for cross-account
|
|
9
|
+
* dedup queries. `displayLabel` is pre-masked and safe to display.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
13
|
+
import { DatabaseError } from "../../../errors";
|
|
14
|
+
import { serverLogger } from "../../../monitoring";
|
|
15
|
+
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
16
|
+
import { PAYMENT_METHOD_PII_FIELDS, decryptPiiFields, encryptPiiFields, } from "../../../security";
|
|
17
|
+
import { SAVED_PAYMENT_METHOD_FIELDS, SAVED_PAYMENT_METHODS_COLLECTION, } from "../schemas/saved-methods-firestore";
|
|
18
|
+
function normaliseIdentifier(s) {
|
|
19
|
+
return s.toLowerCase().replace(/\s+/g, "").trim();
|
|
20
|
+
}
|
|
21
|
+
function computeIdentifierHash(type, identifier) {
|
|
22
|
+
return createHash("sha256")
|
|
23
|
+
.update(`${type}|${normaliseIdentifier(identifier)}`)
|
|
24
|
+
.digest("hex");
|
|
25
|
+
}
|
|
26
|
+
export class SavedPaymentMethodsRepository extends BaseRepository {
|
|
27
|
+
constructor() {
|
|
28
|
+
super(SAVED_PAYMENT_METHODS_COLLECTION);
|
|
29
|
+
}
|
|
30
|
+
decrypt(doc) {
|
|
31
|
+
return decryptPiiFields(doc, [...PAYMENT_METHOD_PII_FIELDS]);
|
|
32
|
+
}
|
|
33
|
+
encrypt(data) {
|
|
34
|
+
return encryptPiiFields(data, [...PAYMENT_METHOD_PII_FIELDS]);
|
|
35
|
+
}
|
|
36
|
+
mapDoc(snap) {
|
|
37
|
+
const raw = super.mapDoc(snap);
|
|
38
|
+
return this.decrypt(raw);
|
|
39
|
+
}
|
|
40
|
+
async createWithId(id, data) {
|
|
41
|
+
return super.createWithId(id, this.encrypt(data));
|
|
42
|
+
}
|
|
43
|
+
async update(id, data) {
|
|
44
|
+
return super.update(id, this.encrypt(data));
|
|
45
|
+
}
|
|
46
|
+
/** List all saved methods for a user. Never returns raw `identifier` — only `displayLabel`. */
|
|
47
|
+
async listByUser(userId) {
|
|
48
|
+
try {
|
|
49
|
+
const snap = await this.getCollection()
|
|
50
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.USER_ID, "==", userId)
|
|
51
|
+
.orderBy(SAVED_PAYMENT_METHOD_FIELDS.LAST_USED_AT, "desc")
|
|
52
|
+
.get();
|
|
53
|
+
return snap.docs.map((d) => {
|
|
54
|
+
const doc = this.mapDoc(d);
|
|
55
|
+
doc.identifier = ""; // never expose decrypted PII to caller
|
|
56
|
+
return doc;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
void normalizeError(error);
|
|
61
|
+
throw new DatabaseError(`Failed to list payment methods for user:${userId}`, error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Idempotent upsert by (userId + identifierHash).
|
|
66
|
+
* Computes hash + encrypts identifier before write.
|
|
67
|
+
* Updates lastUsedAt on re-add.
|
|
68
|
+
*/
|
|
69
|
+
async upsertForUser(userId, input) {
|
|
70
|
+
try {
|
|
71
|
+
const hash = computeIdentifierHash(input.type, input.identifier);
|
|
72
|
+
// Check for existing
|
|
73
|
+
const existing = await this.getCollection()
|
|
74
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.USER_ID, "==", userId)
|
|
75
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.IDENTIFIER_HASH, "==", hash)
|
|
76
|
+
.limit(1)
|
|
77
|
+
.get();
|
|
78
|
+
const now = new Date();
|
|
79
|
+
if (!existing.empty) {
|
|
80
|
+
const docId = existing.docs[0].id;
|
|
81
|
+
return this.update(docId, { lastUsedAt: now, displayLabel: input.displayLabel });
|
|
82
|
+
}
|
|
83
|
+
const docRef = this.getCollection().doc();
|
|
84
|
+
const data = {
|
|
85
|
+
userId,
|
|
86
|
+
type: input.type,
|
|
87
|
+
identifier: input.identifier,
|
|
88
|
+
displayLabel: input.displayLabel,
|
|
89
|
+
identifierHash: hash,
|
|
90
|
+
isDefault: input.isDefault ?? false,
|
|
91
|
+
lastUsedAt: now,
|
|
92
|
+
createdAt: now,
|
|
93
|
+
updatedAt: now,
|
|
94
|
+
};
|
|
95
|
+
await docRef.set(prepareForFirestore(this.encrypt(data)));
|
|
96
|
+
serverLogger.info("Saved payment method created", { userId, type: input.type, docId: docRef.id });
|
|
97
|
+
const refetched = await this.findById(docRef.id);
|
|
98
|
+
if (!refetched)
|
|
99
|
+
throw new DatabaseError("Payment method not readable after create");
|
|
100
|
+
refetched.identifier = "";
|
|
101
|
+
return refetched;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
void normalizeError(error);
|
|
105
|
+
throw new DatabaseError(`Failed to upsert payment method for user:${userId}`, error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Cross-account lookup by identifierHash. Returns display-safe docs (identifier stripped). */
|
|
109
|
+
async listByIdentifierHash(hash) {
|
|
110
|
+
try {
|
|
111
|
+
const snap = await this.getCollection()
|
|
112
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.IDENTIFIER_HASH, "==", hash)
|
|
113
|
+
.get();
|
|
114
|
+
return snap.docs.map((d) => {
|
|
115
|
+
const doc = this.mapDoc(d);
|
|
116
|
+
doc.identifier = "";
|
|
117
|
+
return doc;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
void normalizeError(error);
|
|
122
|
+
throw new DatabaseError(`Failed to lookup payment methods by hash`, error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Batch-ban all methods for a user. Used by hard-ban cascade. */
|
|
126
|
+
async banAllForUser(userId, banData) {
|
|
127
|
+
try {
|
|
128
|
+
const snap = await this.getCollection()
|
|
129
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.USER_ID, "==", userId)
|
|
130
|
+
.get();
|
|
131
|
+
if (snap.empty)
|
|
132
|
+
return 0;
|
|
133
|
+
const now = new Date();
|
|
134
|
+
const batch = this.db.batch();
|
|
135
|
+
snap.docs.forEach((doc) => {
|
|
136
|
+
batch.update(doc.ref, {
|
|
137
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BAN_STATUS]: "banned",
|
|
138
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BAN_REASON]: banData.banReason,
|
|
139
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BANNED_BY]: banData.bannedBy,
|
|
140
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BANNED_AT]: now,
|
|
141
|
+
[SAVED_PAYMENT_METHOD_FIELDS.AUTO_BANNED]: true,
|
|
142
|
+
[SAVED_PAYMENT_METHOD_FIELDS.UPDATED_AT]: now,
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
await batch.commit();
|
|
146
|
+
return snap.size;
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
void normalizeError(error);
|
|
150
|
+
throw new DatabaseError(`Failed to ban payment methods for user:${userId}`, error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Reverse auto-ban cascade on user unban. Leaves manually-banned methods untouched. */
|
|
154
|
+
async unbanAutoForUser(userId) {
|
|
155
|
+
try {
|
|
156
|
+
const snap = await this.getCollection()
|
|
157
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.USER_ID, "==", userId)
|
|
158
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.AUTO_BANNED, "==", true)
|
|
159
|
+
.get();
|
|
160
|
+
if (snap.empty)
|
|
161
|
+
return 0;
|
|
162
|
+
const now = new Date();
|
|
163
|
+
const batch = this.db.batch();
|
|
164
|
+
snap.docs.forEach((doc) => {
|
|
165
|
+
batch.update(doc.ref, {
|
|
166
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BAN_STATUS]: null,
|
|
167
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BAN_REASON]: null,
|
|
168
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BANNED_BY]: null,
|
|
169
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BANNED_AT]: null,
|
|
170
|
+
[SAVED_PAYMENT_METHOD_FIELDS.AUTO_BANNED]: null,
|
|
171
|
+
[SAVED_PAYMENT_METHOD_FIELDS.UPDATED_AT]: now,
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
await batch.commit();
|
|
175
|
+
return snap.size;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
void normalizeError(error);
|
|
179
|
+
throw new DatabaseError(`Failed to unban payment methods for user:${userId}`, error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** List by banStatus for admin view. Returns display-safe docs. */
|
|
183
|
+
async listByBanStatus(banStatus, limit = 50, offset = 0) {
|
|
184
|
+
try {
|
|
185
|
+
const snap = await this.getCollection()
|
|
186
|
+
.where(SAVED_PAYMENT_METHOD_FIELDS.BAN_STATUS, "==", banStatus)
|
|
187
|
+
.orderBy(SAVED_PAYMENT_METHOD_FIELDS.BANNED_AT, "desc")
|
|
188
|
+
.limit(limit)
|
|
189
|
+
.offset(offset)
|
|
190
|
+
.get();
|
|
191
|
+
return snap.docs.map((d) => {
|
|
192
|
+
const doc = this.mapDoc(d);
|
|
193
|
+
doc.identifier = "";
|
|
194
|
+
return doc;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
void normalizeError(error);
|
|
199
|
+
throw new DatabaseError(`Failed to list payment methods by banStatus:${banStatus}`, error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async banById(id, banData) {
|
|
203
|
+
await this.update(id, {
|
|
204
|
+
banStatus: "banned",
|
|
205
|
+
banReason: banData.banReason,
|
|
206
|
+
bannedBy: banData.bannedBy,
|
|
207
|
+
bannedAt: new Date(),
|
|
208
|
+
autoBanned: false,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async clearBanById(id) {
|
|
212
|
+
const now = new Date();
|
|
213
|
+
await this.db.collection(SAVED_PAYMENT_METHODS_COLLECTION).doc(id).update({
|
|
214
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BAN_STATUS]: null,
|
|
215
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BAN_REASON]: null,
|
|
216
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BANNED_BY]: null,
|
|
217
|
+
[SAVED_PAYMENT_METHOD_FIELDS.BANNED_AT]: null,
|
|
218
|
+
[SAVED_PAYMENT_METHOD_FIELDS.AUTO_BANNED]: null,
|
|
219
|
+
[SAVED_PAYMENT_METHOD_FIELDS.UNBAN_REQUEST_NOTE]: null,
|
|
220
|
+
[SAVED_PAYMENT_METHOD_FIELDS.UNBAN_REQUESTED_AT]: null,
|
|
221
|
+
[SAVED_PAYMENT_METHOD_FIELDS.UPDATED_AT]: now,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
async deleteForUser(userId, id) {
|
|
225
|
+
const existing = await this.findById(id);
|
|
226
|
+
if (!existing || existing.userId !== userId) {
|
|
227
|
+
throw new DatabaseError(`Payment method ${id} not found for user:${userId}`);
|
|
228
|
+
}
|
|
229
|
+
await this.delete(id);
|
|
230
|
+
}
|
|
231
|
+
/** Public wrapper — compute identifier hash from outside the class (e.g. in API routes). */
|
|
232
|
+
computeIdentifierHash(type, identifier) {
|
|
233
|
+
return computeIdentifierHash(type, identifier);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export const savedPaymentMethodsRepository = new SavedPaymentMethodsRepository();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
|
+
export * from "./saved-methods-firestore";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
export declare const payoutStatusEnumSchema: z.ZodEnum<["pending", "processing", "completed", "failed"]>;
|
|
4
5
|
export declare const payoutPaymentMethodSchema: z.ZodEnum<["bank_transfer", "upi"]>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "./firestore";
|
|
2
|
+
export * from "./saved-methods-firestore";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
4
5
|
import { auditTimestampsShape, firestoreDateSchema, paiseSchema } from "../../../schemas/firestore-helpers";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SavedPaymentMethod Firestore Document Types & Constants
|
|
3
|
+
*
|
|
4
|
+
* Stores user-saved payment identifiers (UPI VPAs, card tokens, bank accounts)
|
|
5
|
+
* for checkout pre-fill and cross-account fraud detection.
|
|
6
|
+
*
|
|
7
|
+
* PII: `identifier` (full UPI VPA / card PAN) is encrypted at rest via
|
|
8
|
+
* encryptPiiFields. `identifierHash` is a deterministic SHA-256 of the
|
|
9
|
+
* normalised identifier and stored unencrypted for cross-account queries.
|
|
10
|
+
* `displayLabel` is a pre-masked string and never contains full PII.
|
|
11
|
+
*/
|
|
12
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
13
|
+
export type SavedPaymentMethodType = "upi" | "card" | "bank_account" | "wallet";
|
|
14
|
+
export type SavedPaymentMethodBanStatus = "banned" | "suspicious" | "unban_requested";
|
|
15
|
+
export interface SavedPaymentMethodDocument extends BaseDocument {
|
|
16
|
+
userId: string;
|
|
17
|
+
type: SavedPaymentMethodType;
|
|
18
|
+
/** Full UPI VPA / card PAN / account number — ENCRYPTED at rest. */
|
|
19
|
+
identifier: string;
|
|
20
|
+
/** Pre-masked display string, never contains full PII. e.g. "9876...@paytm", "XXXX 4321 (HDFC)" */
|
|
21
|
+
displayLabel: string;
|
|
22
|
+
/** SHA-256(type|normalised(identifier)) — unencrypted; enables cross-account dedup without decrypting. */
|
|
23
|
+
identifierHash: string;
|
|
24
|
+
isDefault?: boolean;
|
|
25
|
+
lastUsedAt?: Date;
|
|
26
|
+
banStatus?: SavedPaymentMethodBanStatus;
|
|
27
|
+
banReason?: string;
|
|
28
|
+
bannedBy?: string;
|
|
29
|
+
bannedAt?: Date;
|
|
30
|
+
autoBanned?: boolean;
|
|
31
|
+
unbanRequestNote?: string;
|
|
32
|
+
unbanRequestedAt?: Date;
|
|
33
|
+
}
|
|
34
|
+
export declare const SAVED_PAYMENT_METHODS_COLLECTION: "savedPaymentMethods";
|
|
35
|
+
export declare const SAVED_PAYMENT_METHOD_INDEXED_FIELDS: readonly ["userId", "identifierHash", "banStatus", "type", "createdAt"];
|
|
36
|
+
export declare const SAVED_PAYMENT_METHOD_PUBLIC_FIELDS: readonly ["id", "userId", "type", "displayLabel", "identifierHash", "isDefault", "lastUsedAt", "banStatus", "banReason", "bannedAt", "unbanRequestNote", "unbanRequestedAt", "createdAt", "updatedAt"];
|
|
37
|
+
export declare const SAVED_PAYMENT_METHOD_UPDATABLE_FIELDS: readonly ["displayLabel", "isDefault", "lastUsedAt", "unbanRequestNote", "unbanRequestedAt"];
|
|
38
|
+
export declare const SAVED_PAYMENT_METHOD_FIELDS: {
|
|
39
|
+
readonly USER_ID: "userId";
|
|
40
|
+
readonly TYPE: "type";
|
|
41
|
+
readonly IDENTIFIER: "identifier";
|
|
42
|
+
readonly DISPLAY_LABEL: "displayLabel";
|
|
43
|
+
readonly IDENTIFIER_HASH: "identifierHash";
|
|
44
|
+
readonly IS_DEFAULT: "isDefault";
|
|
45
|
+
readonly LAST_USED_AT: "lastUsedAt";
|
|
46
|
+
readonly BAN_STATUS: "banStatus";
|
|
47
|
+
readonly BAN_REASON: "banReason";
|
|
48
|
+
readonly BANNED_BY: "bannedBy";
|
|
49
|
+
readonly BANNED_AT: "bannedAt";
|
|
50
|
+
readonly AUTO_BANNED: "autoBanned";
|
|
51
|
+
readonly UNBAN_REQUEST_NOTE: "unbanRequestNote";
|
|
52
|
+
readonly UNBAN_REQUESTED_AT: "unbanRequestedAt";
|
|
53
|
+
readonly CREATED_AT: "createdAt";
|
|
54
|
+
readonly UPDATED_AT: "updatedAt";
|
|
55
|
+
};
|
|
56
|
+
export type SavedPaymentMethodCreateInput = {
|
|
57
|
+
type: SavedPaymentMethodType;
|
|
58
|
+
/** Raw identifier — will be encrypted + hashed by repository. */
|
|
59
|
+
identifier: string;
|
|
60
|
+
displayLabel: string;
|
|
61
|
+
isDefault?: boolean;
|
|
62
|
+
};
|
|
63
|
+
export type SavedPaymentMethodUpdateInput = Partial<Pick<SavedPaymentMethodDocument, (typeof SAVED_PAYMENT_METHOD_UPDATABLE_FIELDS)[number]>>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SavedPaymentMethod Firestore Document Types & Constants
|
|
3
|
+
*
|
|
4
|
+
* Stores user-saved payment identifiers (UPI VPAs, card tokens, bank accounts)
|
|
5
|
+
* for checkout pre-fill and cross-account fraud detection.
|
|
6
|
+
*
|
|
7
|
+
* PII: `identifier` (full UPI VPA / card PAN) is encrypted at rest via
|
|
8
|
+
* encryptPiiFields. `identifierHash` is a deterministic SHA-256 of the
|
|
9
|
+
* normalised identifier and stored unencrypted for cross-account queries.
|
|
10
|
+
* `displayLabel` is a pre-masked string and never contains full PII.
|
|
11
|
+
*/
|
|
12
|
+
export const SAVED_PAYMENT_METHODS_COLLECTION = "savedPaymentMethods";
|
|
13
|
+
export const SAVED_PAYMENT_METHOD_INDEXED_FIELDS = [
|
|
14
|
+
"userId",
|
|
15
|
+
"identifierHash",
|
|
16
|
+
"banStatus",
|
|
17
|
+
"type",
|
|
18
|
+
"createdAt",
|
|
19
|
+
];
|
|
20
|
+
export const SAVED_PAYMENT_METHOD_PUBLIC_FIELDS = [
|
|
21
|
+
"id",
|
|
22
|
+
"userId",
|
|
23
|
+
"type",
|
|
24
|
+
"displayLabel",
|
|
25
|
+
"identifierHash",
|
|
26
|
+
"isDefault",
|
|
27
|
+
"lastUsedAt",
|
|
28
|
+
"banStatus",
|
|
29
|
+
"banReason",
|
|
30
|
+
"bannedAt",
|
|
31
|
+
"unbanRequestNote",
|
|
32
|
+
"unbanRequestedAt",
|
|
33
|
+
"createdAt",
|
|
34
|
+
"updatedAt",
|
|
35
|
+
];
|
|
36
|
+
export const SAVED_PAYMENT_METHOD_UPDATABLE_FIELDS = [
|
|
37
|
+
"displayLabel",
|
|
38
|
+
"isDefault",
|
|
39
|
+
"lastUsedAt",
|
|
40
|
+
"unbanRequestNote",
|
|
41
|
+
"unbanRequestedAt",
|
|
42
|
+
];
|
|
43
|
+
export const SAVED_PAYMENT_METHOD_FIELDS = {
|
|
44
|
+
USER_ID: "userId",
|
|
45
|
+
TYPE: "type",
|
|
46
|
+
IDENTIFIER: "identifier",
|
|
47
|
+
DISPLAY_LABEL: "displayLabel",
|
|
48
|
+
IDENTIFIER_HASH: "identifierHash",
|
|
49
|
+
IS_DEFAULT: "isDefault",
|
|
50
|
+
LAST_USED_AT: "lastUsedAt",
|
|
51
|
+
BAN_STATUS: "banStatus",
|
|
52
|
+
BAN_REASON: "banReason",
|
|
53
|
+
BANNED_BY: "bannedBy",
|
|
54
|
+
BANNED_AT: "bannedAt",
|
|
55
|
+
AUTO_BANNED: "autoBanned",
|
|
56
|
+
UNBAN_REQUEST_NOTE: "unbanRequestNote",
|
|
57
|
+
UNBAN_REQUESTED_AT: "unbanRequestedAt",
|
|
58
|
+
CREATED_AT: "createdAt",
|
|
59
|
+
UPDATED_AT: "updatedAt",
|
|
60
|
+
};
|
|
@@ -5,3 +5,5 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { PaymentsRepository } from "./repository/payments.repository";
|
|
7
7
|
export { payoutRepository } from "./repository/payout.repository";
|
|
8
|
+
export { savedPaymentMethodsRepository, SavedPaymentMethodsRepository, } from "./repository/saved-payment-methods.repository";
|
|
9
|
+
export * from "./schemas/saved-methods-firestore";
|
|
@@ -5,3 +5,5 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { PaymentsRepository } from "./repository/payments.repository";
|
|
7
7
|
export { payoutRepository } from "./repository/payout.repository";
|
|
8
|
+
export { savedPaymentMethodsRepository, SavedPaymentMethodsRepository, } from "./repository/saved-payment-methods.repository";
|
|
9
|
+
export * from "./schemas/saved-methods-firestore";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ListingType } from "../types";
|
|
1
2
|
import type { ProductDocument } from "../schemas";
|
|
2
3
|
import type { FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
3
4
|
export interface ProductListActionParams {
|
|
@@ -6,7 +7,7 @@ export interface ProductListActionParams {
|
|
|
6
7
|
page?: number;
|
|
7
8
|
pageSize?: number;
|
|
8
9
|
/** Canonical listing-kind discriminator (SB1-G Phase 4). */
|
|
9
|
-
listingType?:
|
|
10
|
+
listingType?: ListingType;
|
|
10
11
|
featured?: boolean;
|
|
11
12
|
storeId?: string;
|
|
12
13
|
categoriesIn?: string[];
|