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
| Type | Customer | Storefront | Domain | Settles into | Effort |
|---|---|---|---|---|---|
| 1 | Marketplace seller | sellub.com listing | sellub.com/shop/<slug> | Their bank/MoMo | ~10 min self-serve |
| 2 | Marketplace seller w/ own domain | sellub.com listing + their domain | e.g. shop.brand.com | Their bank/MoMo | Type 1 + DNS step |
| 3 | Donation/landing-page integration | Their own static landing (Netlify) calling DPS | theirbrand.com | Their bank/MoMo | Engineering: ~1 day |
| 4 | Full integrator (own product, own backend) | Anything they build | Anything | Their bank/MoMo | Engineering: 1–3 days |
Two universal facts
- 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.
- 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-billingplugin calls Paystack’s/subaccountAPI on the seller’s behalf.
Implication: every client — even a Type-3 “donation page” customer — needs a seller account on
sellub.comto 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)
- Client visits sellub.com/become-seller (or the dashboard’s
/apply). - Fills in the application form: business name, applicant name, email, phone, store type, GPS, ID verification.
- Verifies email.
- Sellub admins review (24–48 h). On approval the seller signs into dashboard.sellub.com.
- 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
/subaccountAPI and stores the returnedACCT_…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:
- Settings → Domain Settings → Add Domain (e.g.
shop.brand.com). - Sellub shows the CNAME target.
- Client adds the CNAME at their DNS provider.
- Click Verify DNS (propagation can take up to 48h).
- Sellub provisions HTTPS automatically. Store now lives at both
shop.brand.comandsellub.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
| Value | Used for |
|---|---|
storeId | Tenant scope (e.g. duabanti) |
appId tag | Top-level grouping (typically same as storeId) |
referencePrefix | ≤ 8 chars (e.g. DNTI) |
| DPS Parse URL + APP_ID + JS_KEY | Shared 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
| Variable | Value (DuabaNti example) |
|---|---|
NEXT_PUBLIC_DPS_PARSE_URL | https://api.platform.duabalabs.com/parse |
NEXT_PUBLIC_DPS_APP_ID | 8786f12b162bea88adbc14dedee7a98a |
NEXT_PUBLIC_DPS_JS_KEY | 3e263c83fcd78d2d36793b30392dcb85 |
NEXT_PUBLIC_DPS_APP_ID_TAG | duabanti (change per client) |
NEXT_PUBLIC_DPS_STORE_ID | duabanti (matches their seller storeId) |
After saving: Deploys → Trigger deploy → Clear cache and deploy site.
3d. Smoke test
- Open the landing page → trigger donate.
- Submit → Paystack hosted page → enter test card.
- After redirect back, verify in the Parse dashboard:
CheckoutandOrderrows with the client’sappId/storeId,status="paid".Paymentrow withstatus="success".
- 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
| Surface | When 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.com | Client backend needs a stateless REST endpoint (Vendure-side tenancy, no Parse SDK) |
ProxyFidelity uses the first.
4b. Tenant identity (pick once, never change)
| Field | Example |
|---|---|
appId | proxyfidelity |
storeId | proxyfidelity |
referencePrefix | PXFI |
flow | direct (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.