@moneygraph/sdk
Version:
AI-native SDK for global payouts powered by StratosPay
537 lines (536 loc) • 19.4 kB
JavaScript
;
/**
* MoneyGraph SDK - Onboard Module
*
* Complete customer onboarding including:
* - Customer CRUD operations
* - Director management (for business accounts)
* - KYC submission and document upload
* - Wallet and transaction access
* - Mock personas for sandbox testing
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OnboardModule = void 0;
const client_1 = require("../api/client");
// Mock data for sandbox personas
const MOCK_PERSONAS = {
business_verified: {
mode: 'test',
first_name: 'Jane',
last_name: 'Enterprise',
email: 'jane@enterprise.test',
phone: '+12025550100',
account_type: 'business',
birthday: '15-04-1982',
gender: 'female',
id_type: 'DRIVERS',
id_number: 'DL123456789',
source_of_funds: 'Ordinary Business Activities',
kyc_status: 'APPROVED',
document: {
document: 'https://example.com/mock-document.png',
selfie: 'https://example.com/mock-selfie.png',
},
address: {
country: 'US',
state: 'NY',
city: 'New York',
street: '456 Broadway',
postal_code: '10013',
},
business_details: {
business_name: 'Enterprise Solutions LLC',
website: 'https://enterprise.test',
staff_size: '5-50',
registration_number: 'LLC12345678',
tax_number: '87-1234567',
registration_date: '10-03-2010',
registration_location: 'New York',
registration_type: 'Private Company',
mcc: 'General Services',
complete_ownership: false,
address: {
country: 'US',
state: 'NY',
city: 'New York',
street: '456 Broadway, Suite 500',
postal_code: '10013',
},
},
},
individual_verified: {
mode: 'test',
first_name: 'John',
last_name: 'Smith',
email: 'john.smith@test.com',
phone: '+12025550198',
account_type: 'personal',
birthday: '20-06-1985',
gender: 'male',
id_type: 'PASSPORT',
id_number: 'P123456789',
source_of_funds: 'Personal Savings',
kyc_status: 'APPROVED',
document: {
document: 'https://example.com/mock-passport.png',
selfie: 'https://example.com/mock-selfie.png',
},
address: {
country: 'US',
state: 'CA',
city: 'San Francisco',
street: '123 Main Street',
postal_code: '94105',
},
},
pending_kyc: {
mode: 'test',
first_name: 'Pending',
last_name: 'User',
email: 'pending@test.com',
phone: '+12025550199',
account_type: 'personal',
birthday: null,
gender: null,
id_type: null,
id_number: null,
source_of_funds: null,
kyc_status: 'PENDING',
document: { document: null, selfie: null },
address: {
country: 'US',
state: null,
city: null,
street: null,
postal_code: null,
},
},
rejected_kyc: {
mode: 'test',
first_name: 'Rejected',
last_name: 'User',
email: 'rejected@test.com',
phone: '+12025550200',
account_type: 'personal',
birthday: '01-01-1990',
gender: 'male',
id_type: 'PASSPORT',
id_number: 'INVALID123',
source_of_funds: 'Personal Savings',
kyc_status: 'REJECTED',
document: {
document: 'https://example.com/blurry-doc.png',
selfie: 'https://example.com/bad-selfie.png',
},
address: {
country: 'US',
state: 'TX',
city: 'Austin',
street: '789 Oak Ave',
postal_code: '78701',
},
},
};
// Sandbox storage for mock data
const sandboxCustomers = new Map();
const sandboxDirectors = new Map();
let mockIdCounter = 1000000;
function generateMockId() {
return String(mockIdCounter++).padStart(7, '0');
}
class OnboardModule {
constructor(client) {
this.client = client;
}
// ===========================================================================
// CUSTOMER OPERATIONS
// ===========================================================================
/**
* Create a new customer (personal or business)
*/
async createCustomer(params) {
if (this.client.isSandbox) {
const id = generateMockId();
const customer = {
id,
mode: 'test',
first_name: params.first_name,
last_name: params.last_name,
email: params.email,
phone: `+${params.phone}`,
account_type: params.account_type,
birthday: null,
gender: null,
id_type: null,
id_number: null,
source_of_funds: null,
kyc_status: 'PENDING',
document: { document: null, selfie: null },
address: {
country: params.country,
state: null,
city: null,
street: null,
postal_code: null,
},
...(params.account_type === 'business' && {
business_details: {
business_name: params.business_details.business_name,
website: null,
staff_size: null,
registration_number: null,
tax_number: null,
registration_date: null,
registration_location: null,
registration_type: null,
mcc: null,
complete_ownership: false,
address: params.business_details.address || {
country: null,
state: null,
city: null,
street: null,
postal_code: null,
},
},
}),
};
sandboxCustomers.set(id, customer);
sandboxDirectors.set(id, []);
return customer;
}
return this.client.post('/customer/create', params);
}
/**
* Get all customers (paginated)
*/
async listCustomers(page = 1) {
if (this.client.isSandbox) {
const customers = Array.from(sandboxCustomers.values());
return {
data: customers.slice((page - 1) * 20, page * 20),
links: {
first: '?page=1',
last: `?page=${Math.ceil(customers.length / 20)}`,
prev: page > 1 ? `?page=${page - 1}` : null,
next: page * 20 < customers.length ? `?page=${page + 1}` : null,
},
meta: {
current_page: page,
from: (page - 1) * 20 + 1,
last_page: Math.ceil(customers.length / 20),
path: '/customer/users',
per_page: 20,
to: Math.min(page * 20, customers.length),
total: customers.length,
links: [],
},
};
}
return this.client.get('/customer/users', { page });
}
/**
* Get a single customer by ID
*/
async getCustomer(customerId) {
if (this.client.isSandbox) {
const customer = sandboxCustomers.get(customerId);
if (!customer) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
return customer;
}
return this.client.get(`/customer/users/${customerId}`);
}
/**
* Update a customer
*/
async updateCustomer(customerId, params) {
if (this.client.isSandbox) {
const customer = sandboxCustomers.get(customerId);
if (!customer) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
if (customer.kyc_status === 'APPROVED') {
throw new client_1.MoneyGraphError('kyc already approved', 'KYC_ALREADY_APPROVED', 403);
}
const updated = {
...customer,
...params,
address: { ...customer.address, ...params },
business_details: customer.business_details
? { ...customer.business_details, ...params.business_details }
: undefined,
};
sandboxCustomers.set(customerId, updated);
return updated;
}
return this.client.post(`/customer/update/${customerId}`, params);
}
/**
* Create a mock persona for testing (sandbox only)
* Instantly creates a customer with pre-filled data
*/
async createMockPersona(type) {
if (!this.client.isSandbox) {
throw new client_1.MoneyGraphError('Mock personas are only available in sandbox mode', 'SANDBOX_ONLY');
}
const id = generateMockId();
const persona = MOCK_PERSONAS[type];
const customer = {
id,
...persona,
};
sandboxCustomers.set(id, customer);
sandboxDirectors.set(id, []);
// Add mock directors for business accounts
if (type === 'business_verified') {
sandboxDirectors.set(id, [
{
id: crypto.randomUUID(),
first_name: 'Adam',
last_name: 'Director',
email: 'adam@enterprise.test',
phone: '+12025551234',
country: 'US',
state: 'NY',
city: 'New York',
street: '456 Broadway',
postal_code: '10013',
birthday: '15-08-1978',
ownership: 30,
position: 'Chief Financial Officer',
mode: 'test',
},
]);
}
return customer;
}
// ===========================================================================
// KYC OPERATIONS
// ===========================================================================
/**
* Submit KYC for review
*/
async submitKyc(customerId) {
if (this.client.isSandbox) {
const customer = sandboxCustomers.get(customerId);
if (!customer) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
// In sandbox, auto-approve after 2 seconds
setTimeout(() => {
const c = sandboxCustomers.get(customerId);
if (c && c.kyc_status === 'PENDING') {
sandboxCustomers.set(customerId, { ...c, kyc_status: 'APPROVED' });
}
}, 2000);
return;
}
await this.client.post(`/customer/submit_kyc/${customerId}`);
}
/**
* Upload KYC document
* @param documentType - 'document' or 'selfie'
*/
async uploadDocument(customerId, documentType, file, filename) {
if (this.client.isSandbox) {
const customer = sandboxCustomers.get(customerId);
if (!customer) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
// Mock the document upload
const updated = {
...customer,
document: {
...customer.document,
[documentType]: `https://mock.storage/${customerId}/${filename}`,
},
};
sandboxCustomers.set(customerId, updated);
return;
}
await this.client.uploadFile(`/customer/upload_document/${customerId}/${documentType}`, file, filename);
}
/**
* Check if customer can make payouts (KYC approved)
*/
async canPayout(customerId) {
const customer = await this.getCustomer(customerId);
if (customer.kyc_status === 'APPROVED') {
return { allowed: true, status: 'APPROVED' };
}
return {
allowed: false,
status: customer.kyc_status,
reason: customer.kyc_status === 'PENDING'
? 'KYC verification is still pending'
: 'KYC verification was rejected',
};
}
// ===========================================================================
// DIRECTOR OPERATIONS (for business customers)
// ===========================================================================
/**
* List all directors for a business customer
*/
async listDirectors(customerId) {
if (this.client.isSandbox) {
const directors = sandboxDirectors.get(customerId) || [];
return {
data: directors,
links: { first: '?page=1', last: '?page=1', prev: null, next: null },
meta: {
current_page: 1,
from: 1,
last_page: 1,
path: `/customer/director/all/${customerId}`,
per_page: 20,
to: directors.length,
total: directors.length,
links: [],
},
};
}
return this.client.get(`/customer/director/all/${customerId}`);
}
/**
* Get a single director
*/
async getDirector(customerId, directorId) {
if (this.client.isSandbox) {
const directors = sandboxDirectors.get(customerId) || [];
const director = directors.find(d => d.id === directorId);
if (!director) {
throw new client_1.MoneyGraphError('Director not found', 'DIRECTOR_NOT_FOUND', 404);
}
return director;
}
return this.client.get(`/customer/director/all/${customerId}/${directorId}`);
}
/**
* Create a new director
*/
async createDirector(customerId, params) {
if (this.client.isSandbox) {
if (!sandboxCustomers.has(customerId)) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
const director = {
id: crypto.randomUUID(),
first_name: params.first_name,
last_name: params.last_name,
email: params.email,
phone: `+${params.phone}`,
country: params.country,
state: params.state,
city: params.city,
street: params.street,
postal_code: params.postal_code,
birthday: params.birthday,
ownership: params.ownership,
position: params.position,
mode: 'test',
};
const directors = sandboxDirectors.get(customerId) || [];
directors.push(director);
sandboxDirectors.set(customerId, directors);
return director;
}
return this.client.post(`/customer/director/create/${customerId}`, params);
}
/**
* Update a director
*/
async updateDirector(customerId, directorId, params) {
if (this.client.isSandbox) {
const directors = sandboxDirectors.get(customerId) || [];
const index = directors.findIndex(d => d.id === directorId);
if (index === -1) {
throw new client_1.MoneyGraphError('Director not found', 'DIRECTOR_NOT_FOUND', 404);
}
const updated = { ...directors[index], ...params };
directors[index] = updated;
sandboxDirectors.set(customerId, directors);
return updated;
}
return this.client.post(`/customer/director/update/${customerId}/${directorId}`, params);
}
/**
* Delete a director
*/
async deleteDirector(customerId, directorId) {
if (this.client.isSandbox) {
const directors = sandboxDirectors.get(customerId) || [];
const index = directors.findIndex(d => d.id === directorId);
if (index === -1) {
throw new client_1.MoneyGraphError('Director not found', 'DIRECTOR_NOT_FOUND', 404);
}
directors.splice(index, 1);
sandboxDirectors.set(customerId, directors);
return;
}
await this.client.delete(`/customer/director/delete/${customerId}/${directorId}`);
}
// ===========================================================================
// WALLET & TRANSACTION OPERATIONS
// ===========================================================================
/**
* Get all wallets for a customer
*/
async getWallets(customerId) {
if (this.client.isSandbox) {
if (!sandboxCustomers.has(customerId)) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
// Return mock USD wallet
return [
{
id: crypto.randomUUID(),
currency_id: crypto.randomUUID(),
amount: 0,
human_readable_amount: 0,
currency: 'USD',
mode: 'test',
},
];
}
const response = await this.client.get(`/customer/wallets/${customerId}`);
return response.data;
}
/**
* Get all transactions for a customer (paginated)
*/
async listTransactions(customerId, page = 1) {
if (this.client.isSandbox) {
if (!sandboxCustomers.has(customerId)) {
throw new client_1.MoneyGraphError('User not found', 'CUSTOMER_NOT_FOUND', 404);
}
return {
data: [],
links: { first: '?page=1', last: '?page=1', prev: null, next: null },
meta: {
current_page: 1,
from: 0,
last_page: 1,
path: `/customer/transactions/${customerId}`,
per_page: 20,
to: 0,
total: 0,
links: [],
},
};
}
return this.client.get(`/customer/transactions/${customerId}`, { page });
}
/**
* Get a single transaction
*/
async getTransaction(customerId, transactionId) {
if (this.client.isSandbox) {
throw new client_1.MoneyGraphError('Transaction not found', 'NOT_FOUND', 404);
}
return this.client.get(`/customer/transactions/${customerId}/${transactionId}`);
}
}
exports.OnboardModule = OnboardModule;