DPSClient Onboarding

Client Onboarding

How to onboard a new client onto the Duaba ecosystem. There are four onboarding types, each suited to a different kind of customer.

Type matrix

TypeCustomerStorefrontDomainSettles intoEffort
1Marketplace sellersellub.com listingsellub.com/shop/<slug>Their bank/MoMo~10 min self-serve
2Marketplace seller w/ own domainsellub.com listing + their domaine.g. shop.brand.comTheir bank/MoMoType 1 + DNS step
3Donation/landing-page integrationTheir own static landing (Netlify) calling DPStheirbrand.comTheir bank/MoMoEngineering: ~1 day
4Full integrator (own product, own backend)Anything they buildAnythingTheir bank/MoMoEngineering: 1–3 days

Two universal facts

  1. Money flow is identical — every checkout passes through DPS → Paystack with a split into the client’s Paystack subaccount. The platform keeps a configurable percentage; the client keeps the rest.
  2. The Paystack subaccount is created automatically by the Sellub dashboard the first time the client connects their bank or mobile-money account. There is no manual step in the Paystack dashboard for the client — Sellub’s dps-e-billing plugin calls Paystack’s /subaccount API on the seller’s behalf.

Implication: every client — even a Type-3 “donation page” customer — needs a seller account on sellub.com to obtain their subaccount. The seller dashboard is the single onboarding surface; how the client uses the resulting subaccount differs per type.

Step 0 — Create the seller account (every type)

  1. Client visits sellub.com/become-seller (or the dashboard’s /apply).
  2. Fills in the application form: business name, applicant name, email, phone, store type, GPS, ID verification.
  3. Verifies email.
  4. Sellub admins review (24–48 h). On approval the seller signs into dashboard.sellub.com.
  5. First sign-in setup wizard:
    • Store profile (name, description, logo, banner).
    • Operating hours, delivery zones, pickup location (Type 1/2 only).
    • Payment Account — connect bank or mobile money. On submit, Sellub calls Paystack’s /subaccount API and stores the returned ACCT_… code on the seller’s Vendure channel (customFieldsPaystacksubaccountcode). This is the moment the Paystack subaccount comes into existence.
    • First product (Type 1/2 only) or skip (Type 3/4).

Once Payment Account is verified by Paystack, the seller can receive payouts.

Type 1 — Marketplace seller

After Step 0, everything is done. Products appear at sellub.com/shop/<slug>, checkout runs through Sellub’s storefront, splits flow automatically.

Next steps: add products, configure delivery zones, set hours, smoke-test with the Paystack TEST card (4084 0840 8408 4081, CVV 408, exp 12/34, OTP 123456).

No extra DPS configuration required. Sellub’s external-payments plugin on api.sellub.com already routes everything correctly.

Type 2 — Marketplace seller with own domain

After Step 0:

  1. Settings → Domain Settings → Add Domain (e.g. shop.brand.com).
  2. Sellub shows the CNAME target.
  3. Client adds the CNAME at their DNS provider.
  4. Click Verify DNS (propagation can take up to 48h).
  5. Sellub provisions HTTPS automatically. Store now lives at both shop.brand.com and sellub.com/shop/<slug>.

Branding overrides on the custom domain: logo + banner, brand colour (tints buttons/accents), social links (footer of custom-domain storefront only).

Same money flow as Type 1. The custom domain only changes presentation.

Type 3 — Donation / landing-page integration

Live worked example: duabanti.com.

Use when: the client runs their own static landing site (Next.js on Netlify, plain HTML, etc.) and wants to take payments without ever showing Sellub branding.

3a. Capture integration credentials

ValueUsed for
storeIdTenant scope (e.g. duabanti)
appId tagTop-level grouping (typically same as storeId)
referencePrefix≤ 8 chars (e.g. DNTI)
DPS Parse URL + APP_ID + JS_KEYShared across all clients — see below

The Paystack subaccount code is already attached server-side — the frontend doesn’t need it. DPS resolves it by storeId.

3b. Wire the landing site

Copy the pattern from apps/duabanti-landing/src/lib/dps/client.ts — see client SDK for the canonical form.

Trigger a donation:

const session = await getDps().createDonation({
  amount: 50,
  currency: 'GHS',
  customer: { email, firstName, lastName },
  campaign: 'general',
});
window.location.href = session.checkoutUrl;

Anonymous Parse session required. Before the first checkout from a public page:

if (!Parse.User.current()) {
  // @ts-ignore
  await Parse.AnonymousUtils.logIn();
}

3c. Netlify environment variables

VariableValue (DuabaNti example)
NEXT_PUBLIC_DPS_PARSE_URLhttps://api.platform.duabalabs.com/parse
NEXT_PUBLIC_DPS_APP_ID8786f12b162bea88adbc14dedee7a98a
NEXT_PUBLIC_DPS_JS_KEY3e263c83fcd78d2d36793b30392dcb85
NEXT_PUBLIC_DPS_APP_ID_TAGduabanti (change per client)
NEXT_PUBLIC_DPS_STORE_IDduabanti (matches their seller storeId)

After saving: Deploys → Trigger deploy → Clear cache and deploy site.

3d. Smoke test

  1. Open the landing page → trigger donate.
  2. Submit → Paystack hosted page → enter test card.
  3. After redirect back, verify in the Parse dashboard:
    • Checkout and Order rows with the client’s appId/storeId, status="paid".
    • Payment row with status="success".
  4. Verify in Paystack TEST → Transactions: split rows for platform fee + client subaccount.

Any failure: ssh dps@20.90.115.254 "sudo docker logs dps-server-app --tail 200".

Type 4 — Full integrator (ProxyFidelity pattern)

Live worked example: ProxyFidelity (proxyfidelity.com + dedicated Parse server + iOS/Android apps).

Use when: the client has their own full product — backend, mobile/web app, branded admin — and only wants DPS for payment infrastructure (and optionally orders/fulfillment). They terminate webhooks themselves, build their own admin UI, and may run their own Parse server alongside.

4a. Choose the integration surface

SurfaceWhen to use
@duabalabs/dps-client (Parse JS SDK + dps_* cloud functions)Client app/site can talk to Parse directly — same path as Type 3
Sellub external-payments HTTP API on api.sellub.comClient backend needs a stateless REST endpoint (Vendure-side tenancy, no Parse SDK)

ProxyFidelity uses the first.

4b. Tenant identity (pick once, never change)

FieldExample
appIdproxyfidelity
storeIdproxyfidelity
referencePrefixPXFI
flowdirect (no Vendure catalog)

4c. Server-to-server calls (master key)

import Parse from 'parse/node';
Parse.initialize(DPS_APP_ID, DPS_JS_KEY, DPS_MASTER_KEY);
Parse.serverURL = 'https://api.platform.duabalabs.com/parse';
 
const session = await Parse.Cloud.run('dps_checkout_create', {
  appId: 'proxyfidelity',
  storeId: 'proxyfidelity',
  flow: 'direct',
  currency: 'GHS',
  amount: 1000,
  customer: { email, firstName, lastName, vendureCustomerId },
});

The integrator’s backend then redirects the user to session.checkoutUrl, and verifies via dps_checkout_verify on return.

Source-of-truth doc

Full original walkthrough: apps/dps/CLIENT-ONBOARDING.md.