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/migrationPayment 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.
