Merchant Onboarding
Create a MerchantOrg and Merchant to start managing your business. This requires an authenticated session from Initialize Client.
Quick Links
- API Reference - Merchants API documentation
- Initialize Client - Get bearer token first
- Service Manager - Manage categories after onboarding
Interactive Demo
- Preview
- View Source
Authentication Required
You need to initialize the client first to get a bearer token before onboarding a merchant.
Go to Initialize ClientMerchantOnboardingDemo.jsx
import React, { useState, useEffect } from 'react';
import { OpenAPI, MerchantOrgService, MerchantService } from '@scp/sdk';
// Session storage for cookbook demos
const Session = {
save(data) {
const existing = this.load();
localStorage.setItem('scp_session', JSON.stringify({ ...existing, ...data }));
},
load() {
try {
return JSON.parse(localStorage.getItem('scp_session') || '{}');
} catch { return {}; }
},
get tenantId() { return this.load().tenant_id; },
get merchantOrgId() { return this.load().merchant_org_id; },
get merchantId() { return this.load().merchant_id; },
get merchantName() { return this.load().merchant_name; },
get isAuthenticated() {
return !!(this.load().tenant_id && OpenAPI.TOKEN);
},
get isMerchantSetup() {
const s = this.load();
return !!(s.tenant_id && s.merchant_org_id && s.merchant_id && OpenAPI.TOKEN);
},
};
export default function MerchantOnboardingDemo() {
const [merchantName, setMerchantName] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [session, setSession] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isMerchantSetup, setIsMerchantSetup] = useState(false);
const generateId = () => Math.random().toString(36).substring(2, 10);
useEffect(() => {
// Initialize OpenAPI from localStorage
const savedUrl = localStorage.getItem('scp_api_url');
const savedToken = localStorage.getItem('scp_token');
if (savedUrl) OpenAPI.BASE = savedUrl;
if (savedToken) OpenAPI.TOKEN = savedToken;
setIsAuthenticated(Session.isAuthenticated);
setIsMerchantSetup(Session.isMerchantSetup);
if (Session.isMerchantSetup) {
setSession(Session.load());
}
}, []);
const setupMerchant = async () => {
if (!merchantName.trim()) {
setError('Enter a merchant name');
return;
}
setLoading(true);
setError(null);
try {
const uniqueId = generateId();
const tenantId = Session.tenantId;
// Create MerchantOrg using SDK
const orgResponse = await MerchantOrgService.merchantOrgCreate({
requestBody: {
name: merchantName + ' Org ' + uniqueId,
tenant_id: tenantId
}
});
const org = orgResponse.data || orgResponse;
// Create Merchant using SDK
const merchantResponse = await MerchantService.merchantCreate({
requestBody: {
name: merchantName,
public_name: merchantName,
tenant_id: tenantId,
merchant_org_id: org.id
}
});
const merch = merchantResponse.data || merchantResponse;
// Save to session
Session.save({
merchant_org_id: org.id,
merchant_id: merch.id,
merchant_name: merchantName
});
setSession(Session.load());
setIsMerchantSetup(true);
} catch (err) {
setError('Setup failed: ' + (err.body?.error || err.message));
} finally {
setLoading(false);
}
};
const styles = {
container: {
padding: 24,
fontFamily: 'system-ui, -apple-system, sans-serif',
maxWidth: 520,
border: '1px solid #e5e7eb',
borderRadius: 12,
background: '#fff',
},
warningBox: {
background: '#fef3c7',
border: '1px solid #fbbf24',
borderRadius: 10,
padding: 20,
},
warningTitle: {
fontWeight: 600,
color: '#92400e',
margin: '0 0 8px 0',
fontSize: 15,
},
warningText: {
fontSize: 13,
color: '#78350f',
margin: '0 0 16px 0',
lineHeight: 1.5,
},
sessionBar: {
background: '#f0fdf4',
border: '1px solid #86efac',
borderRadius: 8,
padding: 12,
marginBottom: 20,
},
sessionText: {
fontSize: 12,
color: '#166534',
margin: 0,
},
input: {
width: '100%',
padding: 14,
borderRadius: 8,
border: '1px solid #d1d5db',
marginBottom: 12,
fontSize: 14,
boxSizing: 'border-box',
},
label: {
fontSize: 13,
fontWeight: 500,
color: '#374151',
marginBottom: 6,
display: 'block',
},
button: (bg) => ({
display: 'inline-block',
padding: '14px 24px',
background: bg,
color: 'white',
border: 'none',
borderRadius: 8,
cursor: 'pointer',
fontSize: 14,
fontWeight: 600,
textDecoration: 'none',
}),
fullButton: (bg) => ({
width: '100%',
padding: 14,
background: bg,
color: 'white',
border: 'none',
borderRadius: 8,
cursor: 'pointer',
fontSize: 14,
fontWeight: 600,
}),
successBox: {
background: '#f0fdf4',
border: '1px solid #86efac',
borderRadius: 10,
padding: 20,
},
errorBox: {
marginTop: 12,
padding: 14,
background: '#fee2e2',
borderRadius: 8,
color: '#dc2626',
fontSize: 13,
},
infoGrid: {
display: 'grid',
gap: 10,
fontSize: 13,
color: '#15803d',
},
link: {
display: 'inline-block',
marginTop: 16,
padding: '12px 20px',
background: '#059669',
color: 'white',
textDecoration: 'none',
borderRadius: 8,
fontSize: 14,
fontWeight: 500,
},
};
// Not authenticated - redirect to Initialize Client
if (!isAuthenticated) {
return (
<div style={styles.container}>
<div style={styles.warningBox}>
<p style={styles.warningTitle}>Authentication Required</p>
<p style={styles.warningText}>
You need to initialize the client first to get a bearer token before onboarding a merchant.
</p>
<a href="./initialize-client" style={styles.button('#059669')}>
Go to Initialize Client
</a>
</div>
</div>
);
}
// Merchant already setup - show success
if (isMerchantSetup && session) {
return (
<div style={styles.container}>
<div style={styles.successBox}>
<p style={{ fontWeight: 600, color: '#166534', margin: '0 0 16px 0', fontSize: 16 }}>
Merchant Onboarding Complete
</p>
<div style={styles.infoGrid}>
<div><strong>Merchant:</strong> {session.merchant_name}</div>
<div><strong>tenant_id:</strong> <code>{session.tenant_id?.substring(0, 12)}...</code></div>
<div><strong>merchant_org_id:</strong> <code>{session.merchant_org_id?.substring(0, 12)}...</code></div>
<div><strong>merchant_id:</strong> <code>{session.merchant_id?.substring(0, 12)}...</code></div>
</div>
<p style={{ fontSize: 12, color: '#15803d', marginTop: 16, marginBottom: 0 }}>
Your merchant is set up. Continue to Service Manager.
</p>
<a href="./service-manager" style={styles.link}>
Continue to Service Manager →
</a>
</div>
</div>
);
}
// Show merchant setup form
return (
<div style={styles.container}>
<div style={styles.sessionBar}>
<p style={styles.sessionText}>
<strong>Authenticated</strong> | tenant_id: <code>{Session.tenantId?.substring(0, 8)}...</code>
</p>
</div>
<label style={styles.label}>Merchant Name</label>
<input
value={merchantName}
onChange={e => setMerchantName(e.target.value)}
placeholder="e.g., Bella's Salon, Downtown Spa"
style={styles.input}
/>
<p style={{ fontSize: 12, color: '#6b7280', marginBottom: 16 }}>
This will create a MerchantOrg and Merchant under your tenant.
</p>
<button
onClick={setupMerchant}
disabled={loading}
style={styles.fullButton('#059669')}
>
{loading ? 'Creating Merchant...' : 'Create Merchant'}
</button>
{error && <div style={styles.errorBox}>{error}</div>}
</div>
);
}
Business Hierarchy
Tenant (Platform)
└── MerchantOrg (Business Organization)
└── Merchant (Business Location)
└── Services, Categories, Staff, etc.
- MerchantOrg - Represents a business organization (e.g., "Bella's Beauty Group")
- Merchant - Represents a physical location or storefront (e.g., "Bella's Salon - Downtown")
API Reference
Create Merchant Organization
import { MerchantOrgService } from '@scp/sdk';
const org = await MerchantOrgService.merchantOrgCreate({
requestBody: {
name: "Bella's Beauty Group",
tenant_id: tenantId
}
});
console.log(org.id); // merchant_org_id
Create Merchant
import { MerchantService } from '@scp/sdk';
const merchant = await MerchantService.merchantCreate({
requestBody: {
name: "Bella's Salon",
public_name: "Bella's Salon - Downtown",
tenant_id: tenantId,
merchant_org_id: org.id
}
});
console.log(merchant.id); // merchant_id
Session Data Stored
After onboarding, the session includes:
| Key | Description |
|---|---|
scp_session.merchant_org_id | MerchantOrg UUID |
scp_session.merchant_id | Merchant UUID |
scp_session.merchant_name | Merchant display name |
Next Steps
Continue to Service Manager to create service categories.