DPSSellub on DPS

Sellub on DPS

This is the explicit story of how Sellub sits on top of the DPS layer. Sellub is not a peer of DPS — it is one of DPS’s consumer apps, the same way Duabanti or ProxyFidelity is. Three Sellub artifacts are provisioned by DPS, and Sellub’s checkout pipeline is fully outsourced to DPS.

Diagram

                            ┌──────────────────────────┐
                            │     dps-server (Parse)    │
                            │                          │
                            │  • App registry          │
                            │  • Templates             │
                            │  • Checkout pipeline     │
                            │  • Paystack splits       │
                            │  • VendureChannel map    │
                            └──────────────────────────┘
                                       ▲       ▲
                          provisions   │       │  checkout
                                       │       │  ( vendure flow,
                                       │       │    channelToken )
                                       │       │
              ┌────────────────────────┼───────┼────────────────────────┐
              │                        │       │                        │
              ▼                        ▼       ▼                        ▼
    ┌──────────────────┐   ┌──────────────────┐   ┌────────────────────────┐
    │  sellub-server   │   │ sellub-dashboard │   │   sellub-storefront    │
    │  (Vendure +      │   │ (operator + ops) │   │   (Next.js, public)    │
    │   GraphQL +      │   │                  │   │                        │
    │   Postgres +     │   │  api.sellub.com  │   │   sellub.com           │
    │   Redis)         │   │  + dashboard     │   │   shop.brand.com       │
    │                  │   │                  │   │                        │
    │  api.sellub.com  │   │  dashboard.      │   │   /api/dps/checkout    │
    │                  │   │  sellub.com      │   │   → @duabalabs/        │
    │                  │   │                  │   │     dps-client (server)│
    └──────────────────┘   └──────────────────┘   └────────────────────────┘

How DPS provisions Sellub

Three Sellub app types are registered in the DPS template registry:

Template IDDocker imageCloud functionStatus
sellub-serverghcr.io/duabalabs/sellub-serverdps_deploySellubServer + dps_startSellubServer / dps_stopSellubServer / dps_restartSellubServer / dps_getSellubServerStatus
sellub-dashboard❌ (TBD)dps_deploySellubDashboard⚠️
sellub-storefront❌ (TBD)dps_deploySellubStorefront⚠️

When an operator provisions a Sellub stack:

  1. The dashboard’s Sellub wizard (src/components/sellub/...) collects org, project, store name, channel info.
  2. dps_deploySellubServer deploys a sellub-server container via docker-orchestrator.ts. Compose template includes vendure-server, vendure-worker, postgres, redis, with env_file for secrets.
  3. The Vendure server boots and a default channel is created.
  4. seed-tenant-channels.ts (see DPS-INTEGRATION-TRACKER.md) registers the new Vendure channel with DPS by upserting a VendureChannel Parse row mapping channelToken → {appId, storeId, adminApiUrl, adminToken, label}.
  5. The dashboard tracks the running container with the same parse-server lifecycle code (consolidated via createServerActions() and the isServerApp flag — see Supported App Types).

The shared lifecycle

Sellub and parse-server share ~80% of their dashboard surface. Both are Docker-deployed server apps with identical lifecycle patterns. The consolidated paths:

PatternMechanismFile
Instance control (Start/Stop/Restart)createServerActions() maps appType → cloud-function namesOverviewSection.tsx
Instance data fetchisServerApp flag → dps_getInstanceDetails (generic)OverviewSection.tsx
Header status chipsUnified parseInstanceData || sellubInstanceDatapage.tsx
Header toolbar controlsUnified iData patternpage.tsx
Docker logsDOCKER_LOG_TYPES = ['parse-server', 'sellub', 'n8n']LogsSection.tsx
Docker metricsdps_getAppMetrics resolves container by appIddocker statsmodular-app-functions.ts
Tab navigationsellubSectionMap + parseServerSectionMap (both in handleNavigateToSection)page.tsx
Section routingModularAppManager with SECTION_OVERRIDES per appTypeModularAppManager.tsx
Quick actions renderisServerApp flag → shared Start/Stop/Restart/Refresh UIOverviewSection.tsx
Stats grid + Quick linksisServerApp flag → shared tier/requests/storage/uptime cardsOverviewSection.tsx

Type-specific differences:

  • Cloud-function names: dps_startParseInstance vs dps_startSellubServer (mapped via serverFunctionMap).
  • Sellub has no Cloud Functions tab (it’s a Vendure/GraphQL server, not Parse).
  • Parse Server has a Credentials tab; Sellub uses SellubCredentialsSection override.
  • Parse Dashboard link label: “Open Parse Dashboard” vs “Open Admin Dashboard”.

How Sellub-storefront uses DPS for checkout

Sellub-storefront is a Next.js app with API routes — it uses the server-side pattern with the DPS master key. Not the browser-direct pattern.

Files:

Required env vars on the storefront:

VariablePurpose
DPS_PARSE_URLhttps://api.platform.duabalabs.com/parse
DPS_APP_IDDPS Parse app id
DPS_MASTER_KEYDPS Parse master key (server-side only)
DPS_APP_ID_TAGTenant tag (e.g. sellub)
DPS_STORE_IDTenant store id
DPS_VENDURE_CHANNEL_TOKENSellub channel token (vendure flow)

The vendure flow

Sellub-storefront uses the vendure flow (not direct). What that means at the DPS layer:

  1. Storefront’s /api/dps/checkout calls dps_checkout_create with flow: 'vendure' and the channelToken.
  2. dps-server looks up the VendureChannel row → resolves {appId, storeId, adminApiUrl, adminToken}.
  3. dps-server calls Sellub’s Vendure Admin API to create a draft order on the tenant’s channel.
  4. dps-server initialises Paystack against the draft order’s total, with a split into the seller’s Paystack subaccount (resolved by storeId).
  5. Storefront receives {authorization_url, reference} and redirects the customer.
  6. After payment, Paystack webhook hits dps-server, which marks the Order paid and notifies Vendure.

Compare to the direct flow (Duabanti, Duabaconnect, Duabatrade): DPS skips Vendure entirely and inits Paystack directly against the supplied amount (or summed lineItems). Donations and subscriptions don’t need a catalog.

VendureChannel mapping schema

Reference table (lives in dps-server’s master MongoDB):

FieldTypeNotes
channelTokenstringunique, indexed; matches Vendure channel token
appIdstringtenant slug — e.g. sellub, duabatrade
storeIdstringSellub storeId for this channel
adminApiUrlstringVendure Admin API URL used by the dps adapter
adminTokenstringsession / API token for Admin API calls
labelstringhuman-readable tenant label

Seed script

apps/dps/dps-server/seed-tenant-channels.ts provisions Vendure channels in sellub-server and registers them with DPS in one shot:

cd apps/dps/dps-server
SELLUB_ADMIN_API_URL=https://api.sellub.com/admin-api \
SELLUB_ADMIN_USER=superadmin \
SELLUB_ADMIN_PASSWORD=••• \
PARSE_SERVER_URL=https://dps.duabalabs.com/parse \
PARSE_APP_ID=••• \
PARSE_MASTER_KEY=••• \
npx ts-node seed-tenant-channels.ts             # seed every tenant in TENANTS
npx ts-node seed-tenant-channels.ts duabatrade  # seed a single tenant

The script:

  1. Logs into the Vendure Admin API (captures vendure-auth-token).
  2. Creates a channel with code = appId, generates a channelToken (or uses ${TENANT}_CHANNEL_TOKEN env if provided), prints the token.
  3. Calls dps_admin_registerVendureChannel (master key) to upsert the VendureChannel Parse row.

Where money flows

Same pipeline for every Sellub seller — including custom-domain Type-2 sellers:

  Customer
     │  pays via Paystack hosted page

  Paystack
     │  splits payment
     ├──► Platform fee (Duabalabs main account)
     └──► Seller subaccount (ACCT_… created by Sellub on first
          bank/MoMo connect — see Client Onboarding step 0)

Sellub’s dps-e-billing plugin owns the subaccount-creation call to Paystack. DPS owns the checkout init + split routing.

Cross-cutting concerns this owns

  • Auth handoff — Sellub admins authenticate against dashboard.sellub.com (Sellub-server NextAuth), not against DPS. DPS manages a separate operator login at platform.duabalabs.com. The two never share sessions; DPS holds the master key the storefront’s API route uses to call DPS server-to-server.
  • Data isolation — Each Sellub channel is one tenant in DPS’s VendureChannel table. Each Sellub store’s catalog data lives only in the Sellub Vendure database. DPS only stores the checkout/order/payment trail tagged with appId + storeId.
  • Observability — Sellub-server’s Docker container logs are surfaced in the DPS dashboard via dps_getInstanceLogs (shared with parse-server). Metrics via dps_getAppMetrics. Database backups via dps_createPostgresBackup.

Source-of-truth docs