Skip to content

Latest commit

 

History

History
163 lines (135 loc) · 9.44 KB

FIRESTORE.md

File metadata and controls

163 lines (135 loc) · 9.44 KB

Firestore data model

ShoeSafari stores the public product catalog and private commerce details in Firestore. Collection and field names below reflect the current client code. Firestore document IDs are generated by Firestore for new products and orders; the order's orderId is a separate human-readable identifier.

ShoeSafariProducts/{productDocumentId}

The storefront reads every document in ShoeSafariProducts. The admin product forms create and update these fields:

Field Firestore type Required for current UI Meaning
name string Yes Product display name.
price number Yes Product price in USD, in major units (for example, 79.99). It is a catalog value, not a trusted checkout total.
img string Yes Firebase Storage download URL used for the product image.
available boolean, optional No Whether customers can buy the product. Only false means unavailable; legacy records without this field remain available. New products default to true.

The product document ID is used as the catalog product ID and in the checkout quote. Do not put customer, payment, or order data in product documents. The quote endpoint reads catalog prices server-side and calculates the payable total; browser-submitted prices and totals are untrusted.

ShoeSafariOrders/{orderDocumentId}

lib/orderStore.js creates an order after a successful Stellar payment. The fields below describe new records; Firestore serverTimestamp() values resolve to Firestore timestamps after the write completes.

Field Firestore type Required on new records Meaning
orderId string Yes Human-readable order identifier, also represented by the on-chain order hash.
paymentMethod string Yes Currently "stellar".
status string Yes Commerce-side mirror of the observed on-chain status (Paid at creation, later Shipped or Refunded). See status authority below.
userId string or null Yes Firebase Auth UID for a signed-in buyer; null for guest checkout. It is the ownership key for customer order queries.
amountUsd number Yes USD-denominated checkout amount for display.
amountRaw string Yes Integer token amount in the token's smallest units. Stored as a decimal string to preserve integer precision.
transactionHash string Yes Stellar transaction hash for the payment.
ledger number or null Yes Ledger from the payment receipt when available.
buyerWallet string or null Yes Stellar address reported by the payment receipt when available.
tokenContract string or null Yes Token contract address from the receipt when available.
orderIdHash string or null Yes Order identifier hash from the receipt when available.
customer map Yes Delivery/contact details described below.
items array of maps Yes Purchased line items; see current and legacy shapes below.
createdAt timestamp Yes Server-generated creation time.
updatedAt timestamp Yes Server-generated creation/update time. Updated when the admin view mirrors a status change.
completedAt timestamp Added on completion Server timestamp written when status becomes Shipped or Refunded; starts the personal-data retention period. Legacy records use updatedAt as the completion time when available.
personalDataAnonymizedAt timestamp Optional Server timestamp marking removal of direct customer identifiers from this Firestore copy.

The customer map currently contains firstName, lastName, email, and address, each a string. These values are copied from checkout form state; the application currently writes an empty string if a value is absent. Treat them as private personal information. Collect and retain only fields needed to fulfill the order, limit access to the merchant and the owning customer, and never add card number, expiry, CVV, OTP, or other payment credentials to this document. The card inputs in the checkout UI are not part of the Stellar order record. Retain direct customer fields and the Firebase userId for one year (365 days) after completion (Shipped or Refunded), then use the admin orders page to anonymize them. A verified customer deletion request may be honored earlier. The action clears the customer map, userId, and copied buyerWallet; it records personalDataAnonymizedAt and leaves order IDs, transaction hashes, amounts, status, and line items intact for accounting. The order page continues to show the buyer address from the public chain index; this action does not erase or change that public chain data.

New checkout quote line items use this shape:

Field Firestore type Meaning
productId string Product document ID.
name string Product name captured at checkout.
quantity number Purchased quantity.
unitPriceUsd number Server-confirmed unit price in USD.
totalUsd number Server-confirmed line total in USD.

If quote line items are unavailable, the checkout currently falls back to the browser's saved cart items. Older records may therefore contain id, name, price, and img instead of the quote fields. Consumers should tolerate missing item fields and both shapes; do not assume cart values in a legacy record were server-verified.

Status authority and ownership

The Soroban escrow contract and its indexed events are authoritative for payment and escrow state. The Firestore status is denormalized commerce metadata for customer/admin display and may be absent, stale, or out of sync; it is not proof that funds settled. Dispatch and refund actions must be authorized and verified on-chain. Firestore status writes only mirror a successful on-chain action.

Customer order reads must be restricted to documents whose userId equals the authenticated Firebase UID. Guest records (userId: null) do not have an authenticated customer owner and must not be exposed through a broad public query. Merchant/admin access should be granted through server-managed authorization, not only the client-side email allowlist. Product catalog reads may be public; product writes and order reads/updates must be protected by Firestore Security Rules or a trusted server.

Existing documents and migration guidance

Readers must tolerate old documents with missing optional metadata, absent timestamps, legacy cart item fields, and missing status; show an unavailable value or derive display state from on-chain events rather than failing the whole order view. Do not infer ownership when userId is missing. Before changing a required field, collection name, or item shape, deploy readers that accept both old and new forms, then backfill records with an idempotent, reviewed migration. Keep original document IDs and transaction hashes stable; never rewrite chain-derived facts based only on Firestore values. No migration is required for this documentation update.

Trusted Stellar checkout quotes

The checkout quote API reads product prices through Firebase Admin on the server. Configure FIREBASE_SERVICE_ACCOUNT_JSON with a server-only service account and CHECKOUT_QUOTE_SECRET with a private random secret of at least 32 characters. Never prefix these variables with NEXT_PUBLIC_ or expose them to browser code. The API accepts product IDs and quantities, ignores prices and totals from the browser, and returns a signed quote that expires after 10 minutes. The wallet flow verifies the signature and expiry immediately before building the transaction. Buyers see the server-confirmed total and quote expiry before signing.

Security rules and administrator claims

The checked-in firestore.rules file is the authorization boundary. Product documents are publicly readable and only writable by accounts whose Firebase ID token contains the boolean custom claim admin: true. Customers can read their own orders; only admins can read all orders or update/delete orders. Guest order creation is limited to documents with a null userId. All other paths are denied by default. The browser AdminGuard only controls navigation and display; Firestore evaluates the signed token claim for every operation.

Grant or revoke the claim from a trusted machine with the Firebase Admin SDK and credentials permitted to update users' claims. Create a service account with the minimum required Firebase Authentication permissions, set GOOGLE_APPLICATION_CREDENTIALS to its private file path, and run:

FIREBASE_PROJECT_ID=your-project node scripts/set-admin-claim.mjs grant FIREBASE_UID
FIREBASE_PROJECT_ID=your-project node scripts/set-admin-claim.mjs revoke FIREBASE_UID

The script preserves other custom claims. Never put the service-account file in the repository or a browser bundle. After changing a claim, have the user sign in again or refresh their ID token. Removing the claim denies subsequent Firestore requests once Firebase issues the refreshed token.

Run the Firestore rules tests locally with Java installed. This is the same command used by the Firestore rules CI workflow:

npm run test:rules:firestore

The emulator uses the demo-shoesafari demo project ID and never contacts a production Firebase project or requires production credentials. Run both Firestore and Storage rules suites with npm run test:rules. Deploy the production rules separately with:

firebase deploy --only firestore:rules --config firebase.firestore.json --project your-project-id