Documentation

ParaBuyer System Documentation

Derived from implemented codebase — 8/28/2026

PART 1 — High-Level Architecture

System Architecture Diagram

graph TB
    subgraph "Frontend (React + Vite)"
        UI[UI Layer - Pages & Components]
        SDK[Base44 SDK Client]
        STRIPE_JS[Stripe.js / PaymentElement]
    end

    subgraph "Backend Functions (Deno Deploy)"
        CDT[createDraftTransaction]
        UTS[updateTransactionShipping]
        CPI[createPaymentIntent]
        AP[approvePurchase]
        RP[releasePayment]
        REF[refundPayment]
        PREF[partialRefund]
        SDE[sendDisputeEmail]
        EKE[emitKlaviyoEvent]
        CSC[calculateShippingCost]
        CT[calculateTax]
        GSL[generateShippingLabel]
        CAC[checkAbandonedCheckouts]
        CEA[checkExpiredApprovalWindows]
        CUO[checkUnshippedOrders]
        CED[checkExpiredDeliveries]
        SWH[stripeWebhook]
        EPW[easypostWebhook]
        SON[sendOfferNotification]
        SD[saveDraft]
    end

    subgraph "Database (Base44 Entities)"
        EQ[Equipment]
        TX[Transaction]
        MSG[Message]
        SP[SellerProfile]
        USR[User]
        WL[Watchlist]
        REV[Review]
        SS[SavedSearch]
        DR[DisputeReconciliation]
        STK[SellerStrike]
    end

    subgraph "External Services"
        STRIPE[Stripe Payments + Connect + Tax]
        EASYPOST[EasyPost Shipping]
        KLAVIYO[Klaviyo Email/Events]
    end

    UI --> SDK
    UI --> STRIPE_JS
    SDK --> CDT & UTS & CPI & AP & RP & REF
    STRIPE_JS --> STRIPE

    UTS --> CSC --> EASYPOST
    UTS --> CT --> STRIPE
    CPI --> STRIPE
    SWH --> STRIPE
    GSL --> EASYPOST
    EPW --> EASYPOST

    AP --> EKE --> KLAVIYO
    SDE --> EKE
    REF --> EKE
    SWH --> EKE
    SON --> KLAVIYO

    CDT --> TX & EQ
    UTS --> TX & EQ
    SWH --> TX & EQ & MSG
    AP --> TX & EQ & SP
    RP --> TX & EQ
    REF --> TX & EQ & DR & STK
    CAC --> TX & EQ

    CEA -->|Scheduled: 1hr| TX
    CUO -->|Scheduled: Daily| TX
    CAC -->|Scheduled: 5min| TX & EQ

Computation Locations

CalculationWhereDetails
Platform Fee (5%)updateTransactionShipping5% of base price. Shipping only. Pickup = $0.
Shipping CostcalculateShippingCost → EasyPostReal carrier rates + markup. Called from updateTransactionShipping.
Sales TaxcalculateTax → Stripe Tax APIJurisdiction-based. Called from updateTransactionShipping.
Seller PayoutapprovePurchase / releasePaymentapprovePurchase: total - platform_fee. releasePayment: base_price - 5% seller fee.

PART 2 — Transaction State Machine (Source of Truth)

Canonical Transaction Lifecycle

stateDiagram-v2
    [*] --> draft : createDraftTransaction (buyer initiates)

    draft --> pending_payment : updateTransactionShipping\n(address + tax + shipping calculated)
    draft --> cancelled : checkAbandonedCheckouts (30min timeout)

    pending_payment --> paid : stripeWebhook payment_intent.succeeded
    pending_payment --> payment_failed : stripeWebhook payment_intent.payment_failed
    pending_payment --> cancelled : stripeWebhook payment_intent.canceled\nOR checkAbandonedCheckouts (30min)

    paid --> shipped : Seller adds tracking (shipping orders)
    paid --> delivered : Seller confirms delivery (pickup: via approval)
    paid --> completed : Buyer approves (pickup: approvePurchase)
    paid --> disputed : Buyer rejects (shipping: BuyerApprovalActions)
    paid --> cancelled : checkUnshippedOrders (7-day timeout)

    shipped --> delivered : easypostWebhook (tracking delivered)

    delivered --> completed : Buyer approves (approvePurchase)
    delivered --> disputed : Buyer rejects (shipping: BuyerApprovalActions)
    delivered --> completed : checkExpiredDeliveries (48hr auto-approve)

    completed --> transferred : approvePurchase (Stripe Transfer created)

    disputed --> refunded : Admin full refund (refundPayment)
    disputed --> refunded : Admin partial refund (partialRefund)
    disputed --> transferred : Admin releases to seller

    payment_failed --> [*]
    cancelled --> [*]
    refunded --> [*]
    transferred --> [*]

State Transition Table (Verified from Code)

StateEntry TriggerAllowed ActionsValid TransitionsEquipment Status
draftcreateDraftTransactionUpdate shipping/addresspending_payment, cancelledactive (unchanged)
pending_paymentupdateTransactionShippingSubmit payment via Stripepaid, payment_failed, cancelledreserved_checkout
paidstripeWebhook (payment_intent.succeeded)Add tracking, approve, rejectshipped, delivered, completed, disputed, cancelledpending_buyer_approval
shippedSeller adds tracking / generateShippingLabelWait for deliverydeliveredpending_buyer_approval
deliveredeasypostWebhook / seller confirmsApprove, rejectcompleted, disputedpending_buyer_approval
completedapprovePurchase / auto-approveTransfer to sellertransferredsold
transferredapprovePurchase (Stripe Transfer)Terminalsold
disputedBuyer rejects (shipping only)Admin review/refund/releaserefunded, transferredpending_buyer_approval (unchanged)
refundedrefundPayment / partialRefundTerminalactive (relisted)
cancelledAbandoned checkout / timeoutTerminalactive (relisted)
payment_failedstripeWebhookTerminalactive (relisted)

Critical Rules (Enforced in Code)

  • Reject (shipping) → ALWAYS sets status to disputed. Calls sendDisputeEmail. NEVER auto-refunds. (BuyerApprovalActions.jsx:118-143)
  • Reject (pickup) → Calls refundPayment directly. Sets status to refunded. Equipment → active. (BuyerApprovalActions.jsx:92-116)
  • Equipment reservation → Only happens at pending_payment (updateTransactionShipping). Draft does NOT reserve equipment.
  • Seller payout calculation differs: approvePurchase deducts platform_fee from total. releasePayment deducts 5% seller fee from base price.

PART 3 — Core User Flows

Flow 1: Shipping Order Lifecycle

sequenceDiagram
    participant B as Buyer
    participant FE as Frontend
    participant CDT as createDraftTransaction
    participant UTS as updateTransactionShipping
    participant CSC as calculateShippingCost
    participant CT as calculateTax
    participant CPI as createPaymentIntent
    participant S as Stripe
    participant SWH as stripeWebhook
    participant GSL as generateShippingLabel
    participant EP as EasyPost
    participant EPW as easypostWebhook
    participant AP as approvePurchase
    participant K as Klaviyo
    participant DB as Database

    B->>FE: Click "Buy Now" or Accept Offer
    FE->>CDT: {equipmentId, buyerEmail, agreedPrice}
    CDT->>DB: Create Transaction (status: draft)
    CDT-->>FE: transactionId

    B->>FE: Enter shipping address
    FE->>UTS: {transactionId, delivery_method: shipping, shippingAddress}
    UTS->>CSC: Calculate shipping cost (EasyPost)
    CSC->>EP: Get rates
    EP-->>CSC: carrier + cost
    UTS->>CT: Calculate tax (Stripe Tax)
    CT->>S: Tax calculation
    S-->>CT: tax amount + jurisdiction
    UTS->>DB: Update Transaction (status: pending_payment)
    UTS->>DB: Update Equipment (status: reserved_checkout)
    UTS-->>FE: {success, transaction}

    B->>FE: Enter payment details
    FE->>CPI: {transactionId}
    CPI->>S: Create PaymentIntent
    S-->>CPI: clientSecret
    CPI-->>FE: clientSecret
    FE->>S: Confirm payment (Stripe.js)
    S-->>SWH: payment_intent.succeeded
    SWH->>DB: Transaction → paid, Equipment → pending_buyer_approval
    SWH->>GSL: Auto-generate shipping label
    GSL->>EP: Create shipment + label
    SWH->>K: transaction_paid + Payment Confirmed events
    SWH->>DB: Create system messages (buyer + seller)

    Note over EP,EPW: Carrier ships package...
    EP->>EPW: tracking.delivered webhook
    EPW->>DB: Transaction → delivered, set delivery_confirmed_date

    B->>FE: Click "Approve Item"
    FE->>AP: {transactionId}
    AP->>S: Create Transfer (seller payout)
    AP->>DB: Transaction → transferred, Equipment → sold
    AP->>K: buyer_accepted event

Flow 2: Pickup Order Lifecycle

sequenceDiagram
    participant B as Buyer
    participant FE as Frontend
    participant CDT as createDraftTransaction
    participant UTS as updateTransactionShipping
    participant CT as calculateTax
    participant CPI as createPaymentIntent
    participant S as Stripe
    participant SWH as stripeWebhook
    participant AP as approvePurchase
    participant K as Klaviyo
    participant DB as Database

    B->>FE: Click "Buy Now" (pickup_only item)
    FE->>CDT: {equipmentId, buyerEmail}
    CDT->>DB: Create Transaction (status: draft, platform_fee: 0)

    B->>FE: Enter address (for tax only)
    FE->>UTS: {transactionId, delivery_method: pickup, buyerTaxAddress}
    UTS->>CT: Calculate tax on base price
    UTS->>DB: Transaction → pending_payment (no shipping fee, no platform fee)
    UTS->>DB: Equipment → reserved_checkout

    B->>FE: Pay
    FE->>CPI: {transactionId}
    CPI->>S: Create PaymentIntent
    S-->>SWH: payment_intent.succeeded
    SWH->>DB: Transaction → paid, Equipment → pending_buyer_approval
    SWH->>K: transaction_paid events
    SWH->>DB: Pickup coordination message created

    Note over B,DB: Buyer and seller coordinate pickup via messages

    B->>FE: Click "Approve Item"
    FE->>AP: {transactionId}
    AP->>S: Create Transfer
    AP->>DB: Transaction → transferred, Equipment → sold
    AP->>K: buyer_accepted event

Flow 3: Dispute Flow (Shipping Reject)

sequenceDiagram
    participant B as Buyer
    participant FE as Frontend (BuyerApprovalActions)
    participant SDE as sendDisputeEmail
    participant EKE as emitKlaviyoEvent
    participant K as Klaviyo
    participant A as Admin (AdminDisputes)
    participant REF as refundPayment
    participant PREF as partialRefund
    participant S as Stripe
    participant DB as Database

    B->>FE: Click "Reject Shipment"
    B->>FE: Provide reason + optional photos
    FE->>DB: Transaction.update({status: disputed, rejection_reason, rejection_date})
    FE->>SDE: {transactionId, reason}
    SDE->>DB: Fetch transaction data
    SDE-->>A: Email to support@parabuyer.com
    SDE->>EKE: {eventName: dispute_opened}
    EKE->>K: Dispute Opened (buyer + seller)
    FE->>DB: Message to seller (dispute opened)

    Note over A: Admin reviews in AdminDisputes panel

    alt Full Refund
        A->>REF: {transactionId, reason, sellerAtFault, adminNotes}
        REF->>S: stripe.refunds.create (full amount)
        REF->>DB: Transaction → refunded, Equipment → active
        REF->>DB: Create DisputeReconciliation record
        REF->>DB: Create SellerStrike (if sellerAtFault)
        REF->>EKE: refund_processed event
    else Partial Refund
        A->>PREF: {transactionId, refundAmount, adminNotes, sellerAtFault}
        PREF->>S: stripe.refunds.create (partial)
        PREF->>DB: Transaction → refunded, Equipment → active
        PREF->>DB: Create DisputeReconciliation record
    else Release to Seller
        A->>AP: Release funds → Stripe Transfer
    end

Flow 4: Abandoned Checkout Cleanup (Scheduled every 5 min)

sequenceDiagram
    participant CRON as Scheduler (5 min)
    participant CAC as checkAbandonedCheckouts
    participant S as Stripe
    participant K as Klaviyo
    participant DB as Database

    CRON->>CAC: Trigger

    CAC->>DB: Filter transactions (status: draft OR pending_payment)
    loop Each transaction older than 30 minutes
        CAC->>DB: Re-fetch transaction (guard stale data)
        CAC->>S: Check PaymentIntent status (guard race condition)
        alt Payment succeeded on Stripe
            CAC-->>CAC: SKIP (payment completed)
        else Still pending/draft
            CAC->>K: Checkout Abandoned event (if not already sent)
            CAC->>DB: Transaction → cancelled
            CAC->>DB: Equipment → active (if was reserved_checkout)
            CAC->>DB: Message to seller (item relisted)
        end
    end

    CAC->>DB: Filter equipment (status: reserved_checkout)
    loop Each orphaned equipment (no active transaction)
        CAC->>DB: Equipment → active
        CAC->>DB: Message to seller (orphan relisted)
    end

Flow 5: Listing Lifecycle

stateDiagram-v2
    [*] --> draft_unclaimed : Seller starts form\n(auto-created on SellEquipmentForm)

    draft_unclaimed --> active : Seller submits listing\n(SingleItemForm.handleSubmit)
    draft_unclaimed --> draft_unclaimed : saveDraft\n(explicit save or auto-save on unmount)
    draft_unclaimed --> [*] : checkIncompleteDraftListings\n(hard-delete after 7 days)

    active --> reserved_checkout : updateTransactionShipping\n(buyer enters address)
    active --> unlisted : Seller unlists (manual)
    active --> part_of_kit : Added to full_kit

    reserved_checkout --> active : checkAbandonedCheckouts\n(30min timeout)
    reserved_checkout --> pending_buyer_approval : stripeWebhook\n(payment succeeded)

    pending_buyer_approval --> sold : approvePurchase / releasePayment\n(funds transferred)
    pending_buyer_approval --> active : refundPayment\n(dispute resolved → refund)

    unlisted --> active : Seller relists
    sold --> [*]

PART 4 — Event System (Klaviyo Integration)

Events are delivered to Klaviyo via two methods: (1) Centralized via emitKlaviyoEvent backend function using Server API (Klaviyo-API-Key header), and (2) Legacy/Direct via individual backend functions using Client API (token param) or Server API.

Event NameKlaviyo MetricTriggered FromRecipientsMethodStatus
offer_madeOffer MadesendOfferNotificationBuyer + SellerDirect (Client API)✅ Working
transaction_paidPayment ConfirmedstripeWebhookBuyer + SellerCentralized + Legacy✅ Working (dual)
Payment ReceivedstripeWebhookSellerLegacy (Client API)✅ Working
item_shippedItem Shipped❌ NOT IMPLEMENTED
item_deliveredItem Delivered❌ NOT IMPLEMENTED
buyer_acceptedBuyer Accepts Funds ReleasedapprovePurchaseBuyer + SellerCentralized + Legacy✅ Working (dual)
dispute_openedDispute OpenedsendDisputeEmailBuyer + SellerCentralized✅ Working
refund_processedRefund ProcessedrefundPaymentBuyer + SellerCentralized✅ Working
Buyer Rejected PurchaserefundPaymentBuyer + SellerLegacy (Server API)✅ Working
Partial Refund IssuedpartialRefundBuyer + SellerDirect (Server API)✅ Working
Checkout AbandonedcheckAbandonedCheckoutsBuyerDirect (Client API)✅ Working
Listing IncompletecheckIncompleteDraftListingsSellerDirect✅ Working
Watchlist Item SoldreleasePaymentWatchersDirect (Client API)✅ Working
Price DroppednotifyPriceDropped (entity automation)WatchersDirect✅ Working

Missing Events (Registered in emitKlaviyoEvent but never called)

🔴 CRITICAL: item_shipped event never emitted

emitKlaviyoEvent has a mapping for 'item_shipped' → 'Item Shipped', but no backend function ever calls emitKlaviyoEvent({eventName: 'item_shipped'}). Shipping label generation and tracking addition do NOT trigger this event. Buyers and sellers receive no Klaviyo email when item ships.

🔴 CRITICAL: item_delivered event never emitted

emitKlaviyoEvent has a mapping for 'item_delivered' → 'Item Delivered', but no backend function ever calls it. The easypostWebhook updates the transaction status but does NOT emit a Klaviyo event. Buyers receive no email when their package is delivered.

PART 5 — Data Lifecycle

A. Listing Data Flow

graph LR
    A[User opens SellEquipmentForm] --> B[Equipment.create status:draft_unclaimed]
    B --> C{User fills form}
    C -->|Save Draft| D[saveDraft backend → update Equipment]
    C -->|Auto-save on unmount| D
    C -->|Submit| E[Equipment.create status:active]
    E --> F[Delete draft record]
    B -->|7 days unused| G[checkIncompleteDraftListings → hard-delete]
    
    E --> H{Marketplace visible}
    H -->|Buyer starts checkout| I[updateTransactionShipping → reserved_checkout]
    I -->|Payment succeeds| J[stripeWebhook → pending_buyer_approval]
    I -->|Abandoned 30min| K[checkAbandonedCheckouts → active]
    J -->|Approved/Transferred| L[sold]
    J -->|Refunded| M[active - relisted]

B. Transaction Data Flow

graph TD
    A[createDraftTransaction] -->|Creates| B[Transaction: draft]
    B -->|updateTransactionShipping| C[Transaction: pending_payment]
    C -->|Links| D[PaymentIntent on Stripe]
    D -->|stripeWebhook| E[Transaction: paid]
    E -->|Seller ships| F[Transaction: shipped]
    F -->|EasyPost webhook| G[Transaction: delivered]
    G -->|Buyer approves| H[Transaction: transferred]
    
    E -->|Buyer rejects shipping| I[Transaction: disputed]
    I -->|Admin refund| J[Transaction: refunded]
    I -->|Admin partial refund| J
    I -->|Admin releases| H

    E -->|Buyer rejects pickup| J
    
    C -->|Abandoned| K[Transaction: cancelled]
    D -->|Payment fails| L[Transaction: payment_failed]

C. Payment Data Flow

graph TD
    A[createPaymentIntent] -->|Creates| B[Stripe PaymentIntent]
    B -->|Buyer confirms via Stripe.js| C[payment_intent.succeeded]
    C -->|Webhook updates| D[Transaction.paid + payment_confirmed_date]
    
    D -->|Buyer approves| E{approvePurchase}
    E -->|Capture if requires_capture| F[Captured]
    E -->|Create Transfer| G[Funds → Seller Connected Account]
    G --> H[Transaction.transferred + transfer_id]
    
    D -->|Auto-approve expired| I{checkExpiredApprovalWindows}
    I -->|capturePayment| F
    
    D -->|7-day timeout no ship| J{checkUnshippedOrders}
    J -->|Cancel uncaptured PI| K[Payment cancelled]
    J -->|Refund succeeded PI| L[Stripe Refund]

    D -->|Dispute → Admin refund| M{refundPayment}
    M -->|stripe.refunds.create| N[Full Refund + DisputeReconciliation]

    D -->|Dispute → Admin partial| O{partialRefund}
    O -->|stripe.refunds.create partial| P[Partial Refund + DisputeReconciliation]

PART 6 — QA Flow Documentation

Flow A: Shipping Order — Happy Path

StepActionExpected UIExpected StateExpected Event
1Buyer clicks 'Buy Now' on shipping-enabled itemRedirected to TransactionShipping pageTX: draft, EQ: active
2Buyer selects 'Shipping', enters valid US addressShipping cost calculated, tax displayed, total shownTX: pending_payment, EQ: reserved_checkout
3Buyer clicks 'Proceed to Checkout'TransactionCheckout page loads, Stripe PaymentElement shownTX: pending_payment
4Buyer submits valid card paymentPaymentSuccess page shown with order detailsTX: paid, EQ: pending_buyer_approvaltransaction_paid, Payment Confirmed (buyer), Payment Received (seller)
5Verify shipping label auto-generatedTransaction gets tracking_status: label_generatedTX: paid (tracking_provided_date set)
6Buyer clicks 'Approve Item'Success message, transaction completeTX: transferred, EQ: soldbuyer_accepted, Buyer Accepts Funds Released

Flow B: Pickup Order — Happy Path

StepActionExpected UIExpected StateExpected Event
1Buyer clicks 'Buy Now' on pickup-only itemTransactionShipping with pickup selectedTX: draft, EQ: active
2Buyer enters address (for tax), clicks proceedTax calculated (no shipping fee, no platform fee)TX: pending_payment, EQ: reserved_checkout
3Buyer paysPaymentSuccess shownTX: paid, EQ: pending_buyer_approvaltransaction_paid
4Buyer clicks 'Approve Item' after pickupTransaction completeTX: transferred, EQ: soldbuyer_accepted

Flow C: Shipping Reject → Dispute

StepActionExpected UIExpected StateExpected Event
1Buyer clicks 'Reject Shipment'Dialog: reason textarea + photo upload + warning about dispute(no change yet)
2Buyer enters reason, clicks 'Yes, Reject Shipment'Success, dispute openedTX: disputeddispute_opened
3Verify: NO automatic refund createdStripe has NO refund for this PITX: disputed (NOT refunded)Email to support@parabuyer.com
4Admin opens AdminDisputes, full refundsRefund processedTX: refunded, EQ: activerefund_processed, Buyer Rejected Purchase

Flow D: Pickup Reject → Direct Refund

StepActionExpected UIExpected StateExpected Event
1Buyer clicks 'Reject Pickup'Dialog: reason + info about direct refund(no change yet)
2Buyer submits reasonRefund processed immediatelyTX: refunded, EQ: activerefund_processed, Buyer Rejected Purchase
3Verify: NO dispute createdTX.status is refunded, NOT disputedTX: refunded

Flow E: Abandoned Checkout

StepActionExpected UIExpected StateExpected Event
1Buyer starts checkout, enters address, gets to paymentTX: pending_payment, EQ: reserved_checkout
2Buyer closes browser (no payment)TX: pending_payment, EQ: reserved_checkout
3Wait 30+ minutescheckAbandonedCheckouts runsTX: cancelled, EQ: activeCheckout Abandoned (Klaviyo to buyer)
4Verify item shows in marketplaceItem visible in marketplace searchEQ: activeMessage to seller (relisted)

Flow F: Auto-Approve (7-day/48hr Expiry)

StepActionExpected UIExpected StateExpected Event
1Shipping item delivered, buyer does nothing for 48hrcheckExpiredDeliveries auto-captures paymentTX: completed, EQ: (sold via capturePayment)Email to buyer + seller
2Pickup paid, buyer does nothing for 7 dayscheckExpiredApprovalWindows auto-approvesTX: completedEmail to buyer + seller

PART 7 — Validation & Gap Analysis

Critical Issues

🔴 CRITICAL: item_shipped Klaviyo event never emitted

emitKlaviyoEvent supports 'item_shipped' but nothing calls it. When a seller adds tracking or a label is generated, no Klaviyo event fires. Buyer gets no email about shipment. Fix: Call emitKlaviyoEvent from generateShippingLabel success or AddTrackingForm submit.

🔴 CRITICAL: item_delivered Klaviyo event never emitted

emitKlaviyoEvent supports 'item_delivered' but the easypostWebhook does NOT call it when tracking status changes to 'delivered'. Buyer receives no email about delivery. Fix: Call emitKlaviyoEvent from easypostWebhook when status is 'delivered'.

🔴 CRITICAL: Seller payout calculation INCONSISTENCY between approvePurchase and releasePayment

approvePurchase calculates: transferAmount = total_amount_cents - platform_fee_cents. releasePayment calculates: transferAmount = base_price_cents - 5%_seller_fee_cents (excludes shipping, tax). These yield DIFFERENT payout amounts for the same transaction. Only one function should be the canonical payout path.

🔴 CRITICAL: capturePayment function has confusing dual purpose

capturePayment both captures the Stripe payment AND sets the transaction to 'shipped' (if label generated). It also sets status to 'completed' for pickup. It's called by checkExpiredApprovalWindows as the auto-approve mechanism. This creates a risk: calling capturePayment on an already-succeeded PaymentIntent could skip the capture but still change transaction status to 'shipped' instead of 'completed'.

🔴 CRITICAL: checkExpiredDeliveries uses deprecated stripe_connect_id field

checkExpiredDeliveries (line 51) checks sellerProfile.stripe_connect_id. The rest of the system uses stripe_account_id. This means auto-approve for delivered items may fail to transfer funds if the seller only has stripe_account_id set.

Warnings

🟡 WARNING: Dual event system (Centralized + Legacy)

stripeWebhook and approvePurchase both emit events via emitKlaviyoEvent AND via legacy direct Klaviyo calls. This means buyers/sellers may receive duplicate emails for the same event if Klaviyo flows are configured on both metric names (e.g., 'Payment Confirmed' from both centralized and legacy).

🟡 WARNING: checkExpiredApprovalWindows delegates to capturePayment then overwrites status

checkExpiredApprovalWindows calls capturePayment (which may set status to 'shipped'), then immediately overwrites to 'completed'. The intermediate state change is unnecessary and could cause race conditions if the automation runs concurrently.

🟡 WARNING: No Klaviyo event for unshipped order cancellation

checkUnshippedOrders sends emails via Base44 SendEmail but does NOT emit a Klaviyo event. This means the cancellation is not tracked in Klaviyo for analytics or flow purposes.

🟡 WARNING: releasePayment does not use emitKlaviyoEvent

releasePayment sends emails directly via Base44 and Klaviyo Client API for watchlist notifications, but does not call emitKlaviyoEvent for the 'funds_released' event. There is no centralized 'funds_released' metric in the system.

🟡 WARNING: Transaction.status 'completed' vs 'transferred' ambiguity

approvePurchase sets status to 'transferred' after creating a Stripe Transfer. capturePayment and checkExpiredApprovalWindows set status to 'completed'. Both represent 'deal done'. The final state for buyer-approved transactions depends on which code path ran. This makes querying for 'completed sales' unreliable unless both statuses are checked.

Informational

🔵 INFO: Abandoned checkout cleanup runs every 5 minutes

Recently changed from 30-minute interval. Equipment reserved via reserved_checkout is released after 30 minutes of inactivity. Double-guard checks Stripe PaymentIntent status before cancelling.

🔵 INFO: Draft auto-save on unmount added to SingleItemForm

When a seller navigates away from the sell form, formData is automatically saved to the draft via saveDraft. This is fire-and-forget (no error handling on unmount).

🔵 INFO: DisputeReconciliation entity tracks full audit trail

Every refund (full or partial) creates a DisputeReconciliation record preserving original transaction economics. Duplicate refunds are prevented by checking existing reconciliation records.