NapAchte Docs

Technical documentation β€” Backend, Architecture & Advice

1. BACKEND GUIDE

stripeConfig

Purpose: Returns the Stripe publishable key to the frontend so it can initialize Stripe.js securely.
Inputs: None (authenticated user request)
Output: { publishable_key: string }
Edge cases: Should never expose the secret key. Always read from STRIPE_PUBLISHABLE_KEY env var.

stripeCreatePayment

Purpose: Creates a Stripe PaymentIntent + an Order record in escrow status.
Inputs: listing_id, listing_title, listing_photo, seller_email, seller_name, amount_htg, notes
Output: { client_secret, order_id, amount_usd }
Behavior: Converts HTG→USD at 0.0075. Calculates 5% platform fee. Creates Order with status "pending_payment".
Edge cases: Buyer must not be the seller. Amount must be >$0.50 USD (Stripe minimum).

stripeVerifyPayment

Purpose: Confirms a payment succeeded and moves Order to paid_in_escrow.
Inputs: { payment_intent_id, order_id }
Output: { success: true, order_id }
Behavior: Checks PaymentIntent status with Stripe. If succeeded, updates Order. Idempotent.
Edge cases: If already paid_in_escrow, return success without re-processing.

stripeConfirmDelivery

Purpose: Buyer confirms receipt β†’ releases escrow payout to seller.
Inputs: { order_id }
Output: { success: true }
Behavior: Updates delivery_confirmed_by_buyer: true, status: "completed", payout_released: true. Triggers Stripe transfer.
Edge cases: Only the buyer can confirm. Order must be in paid_in_escrow state.

stripeDisputeOrder

Purpose: Buyer opens a dispute on an order.
Inputs: { order_id, reason }
Output: { success: true }
Behavior: Sets order status: "disputed", saves dispute_reason. Notifies admin.
Edge cases: Only works on paid_in_escrow orders. Prevents payout until resolved.

stripeSellerOnboard

Purpose: Creates or retrieves a Stripe Connect account for a seller, returns onboarding URL.
Inputs: None (uses authenticated user email)
Output: { onboarding_url, account_id }
Behavior: Creates SellerAccount record if none exists. Uses Stripe Connect Express.
Edge cases: If already onboarded, return existing account link.

stripeWebhook

Purpose: Handles Stripe webhook events server-side.
Inputs: Raw HTTP body + Stripe-Signature header
Behavior: Validates signature using STRIPE_WEBHOOK_SECRET. Handles payment_intent.succeeded, transfer.created, etc.
Critical: Must use stripe.webhooks.constructEventAsync() (Deno async crypto). Validate via Stripe signature only β€” no user auth.

moncashCreatePayment

Purpose: Initiates a MonCash payment for Haitian mobile money users.
Inputs: listing_id, listing_title, listing_photo, seller_email, amount_htg, notes
Output: { redirect_url, order_id }
Behavior: Creates Order record. Calls MonCash API. Returns redirect URL.
Edge cases: Use MONCASH_MODE to toggle sandbox/production. Handle MonCash token expiration.

moncashReturn

Purpose: MonCash redirects here after payment. Verifies and updates order.
Inputs: Query params from MonCash redirect (transactionId, etc.)
Output: Redirect to /moncash/success?order_id=...
Edge cases: Verify the transaction with MonCash API before marking as paid.

deleteAccount

Purpose: Allows a user to permanently delete their account and all associated data.
Inputs: Authenticated user session
Behavior: Deletes listings, favorites, conversations, messages, orders. Then deletes User record.

2. APP NARRATIVE & FEATURES

What is NapAchte?

"NapAchtel" means "We Buy" in Haitian Creole. It is a Haitian marketplace platform β€” the Craigslist/Facebook Marketplace of Haiti, built specifically for Port-au-Prince and surroundings.

It solves a critical trust problem in informal Haitian commerce: buyers send money and never receive goods, or sellers get scammed. NapAchte introduces escrow-based payments so funds are held securely until the buyer confirms delivery.

The platform also includes a SharkTank-style investment module ("Requin Kapital") where Haitian entrepreneurs can pitch projects and attract local investors.

Feature Breakdown

β€’ Browse & Search β€” Filter by category, neighborhood, price, condition
β€’ Post a Listing β€” Upload photos/video, set price in HTG
β€’ Escrow Checkout β€” 3-step: delivery info β†’ payment method β†’ payment
β€’ Stripe Payments β€” USD card payments via Stripe Connect (5% platform fee)
β€’ MonCash Payments β€” HTG mobile money for Haitian users
β€’ Messaging β€” Real-time buyer-seller chat with file/audio attachments
β€’ Favorites β€” Save listings for later
β€’ Seller Profiles β€” Public profile with ratings, store info, all listings
β€’ Reviews β€” Star ratings + comments on sellers
β€’ Admin Approval β€” Listings go through moderation before going live
β€’ SharkTank β€” Entrepreneurs pitch projects, investors back them
β€’ Seller Onboarding β€” Stripe Connect KYC for sellers to receive payouts
β€’ My Orders β€” Track purchases, confirm delivery, dispute orders
β€’ Multilingual β€” French / Creole / English toggle
β€’ Multi-currency β€” HTG / USD display toggle
β€’ PWA-ready β€” Mobile app feel, safe-area support, Android back button

Connection Map

USER (Browser / Mobile WebView)
  └─► FRONTEND (React + Vite)
        β”œβ”€β–Ί Entities (DB): Listing, Order, Favorite, Conversation, Message, Review, Project, SellerAccount, PayoutRequest, User
        └─► Backend Functions (Deno Deploy)
              β”œβ”€β–Ί STRIPE API (PaymentIntents, Connect Accounts, Transfers, Webhooks)
              └─► MONCASH API (Create payment, Verify transaction, Sandbox/Prod)

Authentication Flow:
  AuthProvider β†’ isAuthenticated?
    No  β†’ Public pages (Home, Browse, ListingDetail)
    Yes β†’ Check user_type β†’ No user_type β†’ /onboarding
                          β†’ Has user_type β†’ Full app access

Protected routes: PostListing, Messages, MyFavorites, MyListings, MyOrders, Profile
Admin-only routes: /admin/approval, /admin/migration

Payment Data Flow

Stripe path:
  stripeConfig() β†’ get publishable key
  stripeCreatePayment() β†’ create PaymentIntent + Order (pending_payment)
  Stripe.js renders card form β†’ stripe.confirmPayment()
  stripeVerifyPayment() β†’ Order updated to paid_in_escrow
  [Buyer confirms delivery] β†’ stripeConfirmDelivery() β†’ payout to seller

MonCash path:
  moncashCreatePayment() β†’ create Order + MonCash token
  Redirect to MonCash portal
  MonCash redirects to moncashReturn()
  Order updated β†’ /moncash/success

3. LAST ADVICE FOR THE BACKEND

Architectural Considerations

β€’ Escrow is the core trust layer β€” never release funds without explicit buyer confirmation. The Order state machine (pending_payment β†’ paid_in_escrow β†’ completed/disputed/refunded) must have server-side state transition guards.
β€’ Stripe webhooks are your source of truth β€” don't rely only on frontend stripeVerifyPayment calls. The webhook handler should also handle payment_intent.succeeded.
β€’ MonCash is async β€” always verify transactions server-side on the return handler, never trust query params alone.
β€’ HTG/USD conversion is hardcoded at 0.0075 β€” consider making this a configurable admin setting if the gourde rate changes.

Pitfalls to Avoid

β€’ Stripe sync crypto in Deno β†’ always use constructEventAsync()
β€’ Buyer = Seller checkout β†’ check server-side too, not just frontend
β€’ Releasing payout without delivery confirm β†’ state machine guard in stripeConfirmDelivery
β€’ MonCash sandbox in production β†’ validate MONCASH_MODE env var on deploy
β€’ Large file uploads in entity fields β†’ always use UploadFile integration, store only URL
β€’ Missing seller_email on old listings β†’ migration function exists, run once on deploy
β€’ Admin routes without role check β†’ every admin function checks user.role === "admin"

Monitoring & Observability

β€’ Log all payment events with order_id, amount_usd, user_email, and status at every state transition
β€’ Log Stripe webhook events with event type and processing result
β€’ Alert on disputed orders β€” these need manual admin intervention
β€’ Track MonCash failures separately β€” the API is less reliable than Stripe
β€’ Monitor listing approval queue β€” listings stuck in "pending" cause seller frustration

Security Best Practices

β€’ Never expose STRIPE_SECRET_KEY to the frontend β€” backend functions only
β€’ Validate webhook signatures on every Stripe/MonCash callback
β€’ Use service role sparingly β€” only after verifying authenticated user OR webhook signature
β€’ RLS on sensitive entities β€” Orders, Messages, Favorites scoped to created_by
β€’ Rate limit payment functions β€” prevent >10 PaymentIntents per user per hour
β€’ Sanitize delivery notes β€” stored in Order notes and sent to sellers

Scaling Considerations

β€’ Listing enrichment is N+1 β€” each ListingCard fetches seller data separately. Cache seller profiles in the listing record itself as the catalog grows.
β€’ Messages use real-time subscriptions β€” archive old conversations to keep the active dataset small.
β€’ Image uploads go through Base44 CDN β€” consider compressing images client-side before upload.
β€’ SharkTank projects can be paginated using the same usePaginatedListings pattern as the browse page.
NapAchte