UNPKG

@moneygraph/sdk

Version:

AI-native SDK for global payouts powered by StratosPay

266 lines (222 loc) 7.55 kB
# MoneyGraph SDK - Base44 Integration Guide Use this guide when building payment applications with Base44 AI app generator. ## Quick Setup ```bash npm install @moneygraph/sdk ``` ```typescript import { MoneyGraph } from '@moneygraph/sdk'; const mg = new MoneyGraph({ apiKey: process.env.MONEYGRAPH_API_KEY // sk_test_* or sk_live_* }); ``` ## Core Concepts ### 1. Mode Detection The SDK automatically detects sandbox vs live mode from your API key: - `sk_test_*` → Sandbox mode (mock data, safe for development) - `sk_live_*` → Live mode (real money!) ```typescript if (mg.isSandbox) { // Use mock personas for instant testing const customer = await mg.onboard.createMockPersona('business_verified'); } ``` ### 2. The Golden Rule: KYC Before Payouts **CRITICAL**: Customers MUST have approved KYC before sending payouts. The SDK enforces this automatically and will throw errors if you try to bypass it. ```typescript // Always check before payout const { allowed, status } = await mg.onboard.canPayout(customerId); if (!allowed) { // Show KYC completion UI } ``` ### 3. Quote & Confirm Pattern FX quotes are valid for **2 minutes**. Always: 1. Get quote → Show to user 2. User confirms → Lock the rate 3. Execute payout ```typescript // Step 1: Get quote const quote = await mg.liquidity.getQuote({ from: 'USD', to: 'NGN', amount: 100 }); // Step 2: Show user the rate // "You send $100, recipient gets ₦${quote.to_amount}" // Step 3: On user confirmation, lock rate await mg.liquidity.confirmQuote(quote.id); // Step 4: Send payout const payout = await mg.payouts.send({ quote_id: quote.id, customer_id: customerId, recipient: { name: 'Jane Doe', bank_code: '058', account_number: '0123456789' } }); ``` ## Building Blocks for Base44 ### Customer Registration Component ```typescript // Create customer on signup async function registerCustomer(formData: { firstName: string; lastName: string; email: string; phone: string; country: string; accountType: 'personal' | 'business'; businessName?: string; }) { if (formData.accountType === 'personal') { return mg.onboard.createCustomer({ account_type: 'personal', first_name: formData.firstName, last_name: formData.lastName, email: formData.email, phone: formData.phone.replace(/\D/g, ''), phone_iso2: formData.country, country: formData.country, }); } else { return mg.onboard.createCustomer({ account_type: 'business', first_name: formData.firstName, last_name: formData.lastName, email: formData.email, phone: formData.phone.replace(/\D/g, ''), phone_iso2: formData.country, country: formData.country, business_details: { business_name: formData.businessName!, }, }); } } ``` ### KYC Flow Component ```typescript async function submitKYC(customerId: string, kycData: { birthday: string; // DD-MM-YYYY format! gender: 'male' | 'female'; idType: 'PASSPORT' | 'DRIVERS' | 'NATIONAL_ID'; idNumber: string; sourceOfFundsId: string; address: { state: string; city: string; street: string; postalCode: string }; }) { // Update customer with KYC info await mg.onboard.updateCustomer(customerId, { birthday: kycData.birthday, gender: kycData.gender, id_type: kycData.idType, id_number: kycData.idNumber, source_of_fund_id: kycData.sourceOfFundsId, state: kycData.address.state, city: kycData.address.city, street: kycData.address.street, postal_code: kycData.address.postalCode, }); // Submit for review await mg.onboard.submitKyc(customerId); return { status: 'pending', message: 'KYC submitted for review' }; } // Get source of funds for dropdown async function getSourceOfFundsOptions() { return mg.reference.getSourceOfFunds(); } ``` ### Send Money Component ```typescript import { MoneyGraphError } from '@moneygraph/sdk'; async function sendMoney(params: { customerId: string; amount: number; fromCurrency: string; toCurrency: string; recipient: { name: string; bankCode: string; accountNumber: string; }; }) { try { // One-liner that handles quote + confirm + send const payout = await mg.sendPayout({ customerId: params.customerId, from: params.fromCurrency as any, to: params.toCurrency as any, amount: params.amount, recipient: { name: params.recipient.name, bank_code: params.recipient.bankCode, account_number: params.recipient.accountNumber, }, }); return { success: true, payoutId: payout.id }; } catch (error) { if (error instanceof MoneyGraphError) { switch (error.code) { case 'KYC_PENDING': return { success: false, error: 'Please complete KYC verification first' }; case 'KYC_REJECTED': return { success: false, error: 'Your KYC was rejected. Please contact support.' }; case 'QUOTE_EXPIRED': return { success: false, error: 'Rate expired. Please try again.' }; case 'INSUFFICIENT_BALANCE': return { success: false, error: 'Insufficient balance' }; default: return { success: false, error: error.message }; } } throw error; } } ``` ### Transaction History Component ```typescript async function getTransactionHistory(customerId: string) { const transactions = await mg.onboard.listTransactions(customerId); return transactions.data.map(tx => ({ id: tx.id, amount: tx.human_readable_amount, type: tx.type, status: tx.status, description: tx.description, date: new Date(tx.created_at).toLocaleDateString(), })); } ``` ## Data Types Quick Reference ### Customer Status ```typescript type KycStatus = 'PENDING' | 'APPROVED' | 'REJECTED'; type AccountType = 'personal' | 'business'; ``` ### Payout Status ```typescript type PayoutStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled'; ``` ### Supported Currencies - **Pay-In**: USD, EUR, NGN, KES, GHS + crypto (USDC, USDT, ETH, BTC) - **Payout**: 107+ countries including NGN, KES, ZAR, GHS, INR, PHP, MXN, BRL, EUR, GBP ## Error Handling Cheat Sheet | Error Code | User Message | Action | |------------|--------------|--------| | `KYC_PENDING` | "Please complete verification" | Show KYC form | | `KYC_REJECTED` | "Verification failed" | Contact support | | `QUOTE_EXPIRED` | "Rate expired, refreshing..." | Auto-retry | | `CUSTOMER_NOT_FOUND` | "Account not found" | Check customer ID | | `VALIDATION_ERROR` | Show field errors | Fix form inputs | | `RATE_LIMITED` | "Too many requests" | Wait and retry | ## Testing in Base44 Use sandbox mode for development: ```typescript // In sandbox, create instant verified customers const testCustomer = await mg.onboard.createMockPersona('business_verified'); // Available personas: // - 'business_verified' - Approved business with directors // - 'individual_verified' - Approved personal account // - 'pending_kyc' - Pending verification // - 'rejected_kyc' - Rejected verification ``` ## Important Notes 1. **Date Format**: Always use DD-MM-YYYY for birthday and registration dates 2. **Phone Numbers**: Remove all non-numeric characters before sending 3. **CORS**: Use a backend proxy (Supabase Edge Function, API route) for production 4. **Webhooks**: Set up webhooks for payout status updates in production ## Need the Backend Proxy? See `recipes/supabase-edge-function.ts` for a ready-to-use Supabase Edge Function that handles CORS and API calls.