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 & EQComputation Locations
| Calculation | Where | Details |
|---|---|---|
| Platform Fee (5%) | updateTransactionShipping | 5% of base price. Shipping only. Pickup = $0. |
| Shipping Cost | calculateShippingCost → EasyPost | Real carrier rates + markup. Called from updateTransactionShipping. |
| Sales Tax | calculateTax → Stripe Tax API | Jurisdiction-based. Called from updateTransactionShipping. |
| Seller Payout | approvePurchase / releasePayment | approvePurchase: 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)
| State | Entry Trigger | Allowed Actions | Valid Transitions | Equipment Status |
|---|---|---|---|---|
| draft | createDraftTransaction | Update shipping/address | pending_payment, cancelled | active (unchanged) |
| pending_payment | updateTransactionShipping | Submit payment via Stripe | paid, payment_failed, cancelled | reserved_checkout |
| paid | stripeWebhook (payment_intent.succeeded) | Add tracking, approve, reject | shipped, delivered, completed, disputed, cancelled | pending_buyer_approval |
| shipped | Seller adds tracking / generateShippingLabel | Wait for delivery | delivered | pending_buyer_approval |
| delivered | easypostWebhook / seller confirms | Approve, reject | completed, disputed | pending_buyer_approval |
| completed | approvePurchase / auto-approve | Transfer to seller | transferred | sold |
| transferred | approvePurchase (Stripe Transfer) | Terminal | — | sold |
| disputed | Buyer rejects (shipping only) | Admin review/refund/release | refunded, transferred | pending_buyer_approval (unchanged) |
| refunded | refundPayment / partialRefund | Terminal | — | active (relisted) |
| cancelled | Abandoned checkout / timeout | Terminal | — | active (relisted) |
| payment_failed | stripeWebhook | Terminal | — | active (relisted) |
Critical Rules (Enforced in Code)
- Reject (shipping) → ALWAYS sets status to
disputed. CallssendDisputeEmail. NEVER auto-refunds. (BuyerApprovalActions.jsx:118-143) - Reject (pickup) → Calls
refundPaymentdirectly. Sets status torefunded. Equipment → active. (BuyerApprovalActions.jsx:92-116) - Equipment reservation → Only happens at
pending_payment(updateTransactionShipping). Draft does NOT reserve equipment. - Seller payout calculation differs:
approvePurchasededucts platform_fee from total.releasePaymentdeducts 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 eventFlow 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 eventFlow 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
endFlow 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)
endFlow 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 Name | Klaviyo Metric | Triggered From | Recipients | Method | Status |
|---|---|---|---|---|---|
| offer_made | Offer Made | sendOfferNotification | Buyer + Seller | Direct (Client API) | ✅ Working |
| transaction_paid | Payment Confirmed | stripeWebhook | Buyer + Seller | Centralized + Legacy | ✅ Working (dual) |
| — | Payment Received | stripeWebhook | Seller | Legacy (Client API) | ✅ Working |
| item_shipped | Item Shipped | — | — | — | ❌ NOT IMPLEMENTED |
| item_delivered | Item Delivered | — | — | — | ❌ NOT IMPLEMENTED |
| buyer_accepted | Buyer Accepts Funds Released | approvePurchase | Buyer + Seller | Centralized + Legacy | ✅ Working (dual) |
| dispute_opened | Dispute Opened | sendDisputeEmail | Buyer + Seller | Centralized | ✅ Working |
| refund_processed | Refund Processed | refundPayment | Buyer + Seller | Centralized | ✅ Working |
| — | Buyer Rejected Purchase | refundPayment | Buyer + Seller | Legacy (Server API) | ✅ Working |
| — | Partial Refund Issued | partialRefund | Buyer + Seller | Direct (Server API) | ✅ Working |
| — | Checkout Abandoned | checkAbandonedCheckouts | Buyer | Direct (Client API) | ✅ Working |
| — | Listing Incomplete | checkIncompleteDraftListings | Seller | Direct | ✅ Working |
| — | Watchlist Item Sold | releasePayment | Watchers | Direct (Client API) | ✅ Working |
| — | Price Dropped | notifyPriceDropped (entity automation) | Watchers | Direct | ✅ 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
| Step | Action | Expected UI | Expected State | Expected Event |
|---|---|---|---|---|
| 1 | Buyer clicks 'Buy Now' on shipping-enabled item | Redirected to TransactionShipping page | TX: draft, EQ: active | — |
| 2 | Buyer selects 'Shipping', enters valid US address | Shipping cost calculated, tax displayed, total shown | TX: pending_payment, EQ: reserved_checkout | — |
| 3 | Buyer clicks 'Proceed to Checkout' | TransactionCheckout page loads, Stripe PaymentElement shown | TX: pending_payment | — |
| 4 | Buyer submits valid card payment | PaymentSuccess page shown with order details | TX: paid, EQ: pending_buyer_approval | transaction_paid, Payment Confirmed (buyer), Payment Received (seller) |
| 5 | Verify shipping label auto-generated | Transaction gets tracking_status: label_generated | TX: paid (tracking_provided_date set) | — |
| 6 | Buyer clicks 'Approve Item' | Success message, transaction complete | TX: transferred, EQ: sold | buyer_accepted, Buyer Accepts Funds Released |
Flow B: Pickup Order — Happy Path
| Step | Action | Expected UI | Expected State | Expected Event |
|---|---|---|---|---|
| 1 | Buyer clicks 'Buy Now' on pickup-only item | TransactionShipping with pickup selected | TX: draft, EQ: active | — |
| 2 | Buyer enters address (for tax), clicks proceed | Tax calculated (no shipping fee, no platform fee) | TX: pending_payment, EQ: reserved_checkout | — |
| 3 | Buyer pays | PaymentSuccess shown | TX: paid, EQ: pending_buyer_approval | transaction_paid |
| 4 | Buyer clicks 'Approve Item' after pickup | Transaction complete | TX: transferred, EQ: sold | buyer_accepted |
Flow C: Shipping Reject → Dispute
| Step | Action | Expected UI | Expected State | Expected Event |
|---|---|---|---|---|
| 1 | Buyer clicks 'Reject Shipment' | Dialog: reason textarea + photo upload + warning about dispute | (no change yet) | — |
| 2 | Buyer enters reason, clicks 'Yes, Reject Shipment' | Success, dispute opened | TX: disputed | dispute_opened |
| 3 | Verify: NO automatic refund created | Stripe has NO refund for this PI | TX: disputed (NOT refunded) | Email to support@parabuyer.com |
| 4 | Admin opens AdminDisputes, full refunds | Refund processed | TX: refunded, EQ: active | refund_processed, Buyer Rejected Purchase |
Flow D: Pickup Reject → Direct Refund
| Step | Action | Expected UI | Expected State | Expected Event |
|---|---|---|---|---|
| 1 | Buyer clicks 'Reject Pickup' | Dialog: reason + info about direct refund | (no change yet) | — |
| 2 | Buyer submits reason | Refund processed immediately | TX: refunded, EQ: active | refund_processed, Buyer Rejected Purchase |
| 3 | Verify: NO dispute created | TX.status is refunded, NOT disputed | TX: refunded | — |
Flow E: Abandoned Checkout
| Step | Action | Expected UI | Expected State | Expected Event |
|---|---|---|---|---|
| 1 | Buyer starts checkout, enters address, gets to payment | — | TX: pending_payment, EQ: reserved_checkout | — |
| 2 | Buyer closes browser (no payment) | — | TX: pending_payment, EQ: reserved_checkout | — |
| 3 | Wait 30+ minutes | checkAbandonedCheckouts runs | TX: cancelled, EQ: active | Checkout Abandoned (Klaviyo to buyer) |
| 4 | Verify item shows in marketplace | Item visible in marketplace search | EQ: active | Message to seller (relisted) |
Flow F: Auto-Approve (7-day/48hr Expiry)
| Step | Action | Expected UI | Expected State | Expected Event |
|---|---|---|---|---|
| 1 | Shipping item delivered, buyer does nothing for 48hr | checkExpiredDeliveries auto-captures payment | TX: completed, EQ: (sold via capturePayment) | Email to buyer + seller |
| 2 | Pickup paid, buyer does nothing for 7 days | checkExpiredApprovalWindows auto-approves | TX: completed | Email 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.
Become part of the ParaBuyer community
Keep up to date with exciting tips, useful how-tos and news!