@fundly.ai/payment-sdk
v1.5.3
Published
Fundly Payment SDK for seamless integration with Fundly Pay systems.
Maintainers
Readme
Fundly Payment SDK
A comprehensive React-based payment SDK for integrating Fundly's payment capabilities into your applications. Supports multiple payment modes including UPI, QR codes, cash, cheque, and Fundly Pay.
📦 Installation
npm install @fundly.ai/payment-sdk🚀 Quick Start
Basic Usage (React)
import { PaymentOptionPage, sdkConfig } from '@fundly.ai/payment-sdk';
import '@fundly.ai/payment-sdk/css';
function App() {
// Configure SDK callbacks (optional but recommended)
React.useEffect(() => {
sdkConfig.configure({
onNavigateHome: () => {
console.log('User wants to go back');
// Handle navigation in your app
},
onShareText: (text) => {
console.log('Share text:', text);
// Handle text sharing (e.g., payment link)
},
onShareImage: (base64Data, fileName, fileType) => {
console.log('Share image:', fileName);
// Handle image sharing (e.g., receipt)
},
onOpenCamera: async () => {
// Return captured image as base64
return { name: 'photo.jpg', type: 'image/jpeg', data: 'base64...' };
},
onOpenGallery: async () => {
// Return selected image as base64
return { name: 'image.jpg', type: 'image/jpeg', data: 'base64...' };
}
});
}, []);
return (
<PaymentOptionPage
config={{
environment: "production", // "production", "preprod", or "uat" (default: "production")
partnerCredentials: {
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD"
},
distributorIdnum: "YOUR_DISTRIBUTOR_ID",
chemistId: "YOUR_CHEMIST_ID", // Either chemistId or leadId is required
// leadId: "YOUR_LEAD_ID", // Alternative to chemistId for lead-based payments
typeOfApp: "FUNDLY_CUSTOMER_APP", // or "FUNDLY_COLLECT_APP"
paymentInvoice: [
{ invoiceId: "INV001", paidAmount: 1000 }
],
payableValue: "1000",
paymentType: "INVOICE_COLLECT", // or "DIRECT_COLLECT"
}}
onPaymentCollected={(response) => {
console.log('Payment successful!', response);
// Handle successful payment
}}
onPaymentFailed={(response) => {
console.log('Payment failed', response);
// Handle failed payment
}}
/>
);
}🌐 Embed Script (No React / No npm)
Use the embed script to integrate Fundly Pay into Angular, Vue, plain HTML, Flutter WebView, or Android WebView — no React dependency or build system required.
Load from CDN
<!-- CSS (required) -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@fundly.ai/[email protected]/dist/embed.css" />
<!-- JS (self-contained — React bundled in) -->
<script
src="https://cdn.jsdelivr.net/npm/@fundly.ai/[email protected]/dist/embed.js"
integrity="SHA384_HASH_FROM_RELEASE_NOTES"
crossorigin="anonymous"
></script>Always use the versioned URL in production. The
latestalias (@latest) auto-updates and may introduce breaking changes.
Quick Start (Plain HTML)
<div id="fundly-root"></div>
<script>
FundlySDK.mount('fundly-root', {
flow: 'collection', // 'collection' | 'repayment' | 'transactions'
config: {
environment: 'production',
partnerCredentials: { username: 'YOUR_USER', password: 'YOUR_PASS' },
distributorIdnum: 'DIST_001',
chemistId: 'CHEM_001',
payableValue: '5000',
paymentType: 'INVOICE_COLLECT',
typeOfApp: 'WEB',
paymentInvoice: [{ invoiceId: 'INV-001', paidAmount: 5000 }],
},
onPaymentCollected: function(response) {
console.log('Payment success', response);
FundlySDK.unmount('fundly-root');
},
onNavigateHome: function() {
window.location.href = '/dashboard';
},
});
</script>Embed API
| Method | Description |
|---|---|
| FundlySDK.mount(containerId, options) | Render SDK into a DOM element. If already mounted, replaces cleanly. |
| FundlySDK.unmount(containerId) | Unmount React, free all event listeners. Call before navigating away. |
| FundlySDK.version | Semver string of the loaded embed script. |
Platform Guides
See EMBED_INTEGRATION.md for full integration examples including:
- Angular (script tag +
ngOnInit/ngOnDestroy) - Vue 3 (onMounted/onBeforeUnmount)
- Flutter InAppWebView (Dart bridge handlers)
- Android WebView (Kotlin
@JavascriptInterface)
🔧 Configuration
Payment Config
interface PartnerCredentials {
username: string; // Partner username
password: string; // Partner password
}
interface PaymentConfig {
environment?: 'production' | 'preprod' | 'uat'; // Environment to use (default: "production")
partnerCredentials: PartnerCredentials; // Required: Partner authentication credentials
distributorIdnum: string; // Required: Distributor business ID
chemistId?: string; // Optional: Chemist/Retailer business ID (either chemistId or leadId required)
leadId?: string; // Optional: Lead ID for lead-based payments (either chemistId or leadId required)
typeOfApp: 'FUNDLY_CUSTOMER_APP' | 'FUNDLY_COLLECT_APP';
paymentInvoice: Array<{
invoiceId: string;
paidAmount: number;
}>;
payableValue: string; // Total amount as string
paymentType: 'INVOICE_COLLECT' | 'DIRECT_COLLECT';
salesmanId?: number; // Optional: Salesman ID
payerMobileNumber?: string; // Optional: Payer mobile number
redirectionUrl?: string; // Optional: URL to redirect after UPI payment
transactionId?: string; // Optional: Transaction ID to display result
showResultOnly?: boolean; // Optional: Skip payment options, show result only
successButtonText?: string; // Optional: Button text on success drawer (default: "Home")
failureButtonText?: string; // Optional: Button text on failure drawer (default: "Go Back")
}Payment Callbacks
The PaymentOptionPage component accepts optional callbacks to handle payment completion:
<PaymentOptionPage
config={paymentConfig}
onPaymentCollected={(response) => {
// Called when payment is successful
// Navigate to success page, show receipt, etc.
}}
onPaymentFailed={(response) => {
// Called when payment fails
// Transaction status: FAILED, CANCELLED, or DECLINED
// Show error message, retry option, etc.
}}
/>Note: For DYNAMIC_QR, PAYMENT_LINK, and UPI payment modes, the SDK automatically polls the transaction status until the payment is completed or fails.
SDK Callbacks
Configure platform-specific behaviors using sdkConfig.configure():
import { sdkConfig } from '@fundly.ai/payment-sdk';
sdkConfig.configure({
// Called when user wants to exit the SDK
onNavigateHome: () => void;
// Called when user wants to share text (payment link)
onShareText: (text: string) => void;
// Called when user wants to share image (receipt)
onShareImage: (base64Data: string, fileName: string, fileType: string) => void;
// Called when user wants to share image with byte data (Android WebView)
onShareByteImage: (byteUrl: string, receiptText: string) => void;
// Called when user wants to capture photo (cheque/cash proof)
onOpenCamera: () => Promise<ImageFile | null>;
// Called when user wants to select image from gallery
onOpenGallery: () => Promise<ImageFile | null>;
});ImageFile Interface:
interface ImageFile {
name: string; // File name
type: string; // MIME type (e.g., 'image/jpeg')
data: string; // Base64 encoded image data
}🌍 Environment Configuration
The SDK supports three environments: production, preprod (pre-production), and uat (User Acceptance Testing).
Environment Options
Set the environment using the environment parameter in PaymentConfig:
| Environment | Description | Use Case |
|------------|-------------|----------|
| production | Production environment (default) | Live payments, production deployments |
| preprod | Pre-production/staging | Testing before production release |
| uat | User Acceptance Testing | QA and UAT testing |
Production Environment (Default)
By default, or when environment: "production", the SDK uses production URLs:
<PaymentOptionPage
config={{
environment: "production", // or omit this parameter for production
partnerCredentials: {
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD"
},
distributorIdnum: "YOUR_DISTRIBUTOR_ID",
chemistId: "YOUR_CHEMIST_ID",
// ... other config
}}
/>Pre-Production Environment
For staging and pre-production testing:
<PaymentOptionPage
config={{
environment: "preprod", // Pre-production environment
partnerCredentials: {
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD"
},
distributorIdnum: "YOUR_DISTRIBUTOR_ID",
chemistId: "YOUR_CHEMIST_ID",
// ... other config
}}
/>UAT Environment
For User Acceptance Testing:
<PaymentOptionPage
config={{
environment: "uat", // UAT environment
partnerCredentials: {
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD"
},
distributorIdnum: "YOUR_DISTRIBUTOR_ID",
chemistId: "YOUR_CHEMIST_ID",
// ... other config
}}
/>Example: Environment-based Configuration
// Automatically switch based on your app's environment
const getEnvironment = () => {
switch (process.env.NODE_ENV) {
case 'production':
return 'production';
case 'staging':
return 'preprod';
case 'test':
return 'uat';
default:
return 'preprod';
}
};
<PaymentOptionPage
config={{
environment: getEnvironment(),
partnerCredentials: {
username: process.env.PARTNER_USERNAME,
password: process.env.PARTNER_PASSWORD
},
// ... other config
}}
/>Environment URLs
Each environment uses different API endpoints configured internally. The SDK automatically routes requests to the correct endpoints based on the environment parameter you provide.
Important Notes:
- Always use uat or preprod environments during development and testing
- Switch to production environment (default) for production deployments
- The environment affects all API calls throughout the SDK
- Use appropriate credentials for each environment
- Contact Fundly support for environment-specific credentials and configurations
💳 Supported Payment Modes
The SDK automatically displays available payment options based on your configuration:
- Fundly Pay - One-click payment with credit line
- Generate QR - Dynamic UPI QR code generation for instant payments
- Payment Link - Generate and share payment links
- UPI - Direct UPI payment via payment gateway (supports all UPI apps)
- Scan QR - Scan distributor's static QR code
- Cash - Cash payment with OTP verification
- Cheque - Cheque payment with image upload and verification
UPI Payment Flow
The UPI payment mode provides a seamless payment experience:
- User selects UPI option - SDK displays UPI payment selection
- Redirect to payment gateway - User is redirected to a secure payment page
- Complete payment - User completes payment using any UPI app (Google Pay, PhonePe, Paytm, etc.)
- Automatic return - After payment, user is automatically redirected back to your app
- Status polling - SDK automatically polls for payment status and shows result
Key Features:
- Automatic transaction status tracking
- Supports all major UPI apps
- Zero additional charges for UPI payments
- Secure payment gateway integration
Display Payment Result Only
Sometimes you may want to show only the payment result (success/failure) without displaying payment options. This is useful when:
- User returns from a payment gateway redirect
- You want to display a transaction's current status
- You're handling payment initiation separately
Use Case Example: User initiates UPI payment on /scan-qr page, gets redirected to payment gateway, completes payment, and returns to /scan-qr?orderId=PTXN123. Your app detects the orderId and opens the SDK to show only the result.
// Detect payment return in your app
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const orderIdFromUrl = urlParams.get("orderId") || urlParams.get("transactionId");
if (orderIdFromUrl) {
// User returned from payment gateway - show SDK with result only
setShowPaymentSDK(true);
setTransactionId(orderIdFromUrl);
}
}, []);
// Render SDK in result-only mode
{showPaymentSDK && (
<PaymentOptionPage
config={{
// ... your config
transactionId: transactionId, // Transaction ID to fetch
showResultOnly: true, // Skip payment options UI
redirectionUrl: window.location.href // For consistency
}}
onPaymentCollected={(response) => {
console.log('Payment successful!', response);
// Handle success, navigate, etc.
}}
onPaymentFailed={(response) => {
console.log('Payment failed', response);
// Handle failure
}}
/>
)}How it works:
- SDK fetches transaction status using the provided
transactionId - If status is
SUCCESS/COMPLETED→ Shows success drawer and callsonPaymentCollected - If status is
FAILED/CANCELLED/DECLINED→ Shows failure drawer and callsonPaymentFailed - If status is still
PENDING/PROCESSING→ Shows UPI drawer with polling until completion
👤 Customer Types: Chemist vs Lead
The SDK supports two types of customers for payment collection:
Chemist-based Payments (Existing Customers)
For registered chemists/retailers with established accounts:
<PaymentOptionPage
config={{
partnerCredentials: { username: "...", password: "..." },
distributorIdnum: "DIST123",
chemistId: "CHEM456", // Use chemistId for registered customers
typeOfApp: "FUNDLY_CUSTOMER_APP",
// ... other config
}}
/>Lead-based Payments (New/Prospective Customers)
For leads or new customers who don't have a chemist account yet:
<PaymentOptionPage
config={{
partnerCredentials: { username: "...", password: "..." },
distributorIdnum: "DIST123",
leadId: "LEAD789", // Use leadId for leads/new customers
typeOfApp: "FUNDLY_CUSTOMER_APP",
// ... other config
}}
/>Important Notes:
- You must provide either
chemistIdorleadId(not both) - If both are provided,
chemistIdtakes precedence - Lead-based payments support the same payment modes as chemist-based payments
- Analytics events are tracked with the appropriate customer identifier
🎨 UI Customization
Custom Button Text
You can customize the button text shown on success and failure drawers to better match your app's navigation behavior:
<PaymentOptionPage
config={{
// ... your config
successButtonText: "Close", // Custom text for success drawer button
failureButtonText: "Close", // Custom text for failure drawer button
}}
/>Common Use Cases:
1. Web Applications with Tab Closing
When your app opens the SDK in a new tab that should be closed after payment:
config={{
successButtonText: "Close",
failureButtonText: "Close",
}}
sdkConfig.configure({
onNavigateHome: () => {
window.close(); // Close the tab/window
}
});2. Single Page Applications (SPA)
When navigating back to a specific page in your app:
config={{
successButtonText: "Back to Dashboard",
failureButtonText: "Retry Payment",
}}
sdkConfig.configure({
onNavigateHome: () => {
navigate('/dashboard'); // Use your router
}
});3. Default Behavior
If you don't specify custom text, the defaults are used:
- Success drawer: "Home"
- Failure drawer: "Go Back"
🎨 Styling
Import the CSS file in your application:
import '@fundly.ai/payment-sdk/css';The SDK uses Tailwind CSS internally and provides a clean, mobile-first UI.
🔐 Authentication & Security
Partner Credentials
The SDK requires partner credentials for authentication. Important security considerations:
import { PaymentOptionPage } from '@fundly.ai/payment-sdk';
<PaymentOptionPage
config={{
partnerCredentials: {
username: process.env.PARTNER_USERNAME, // Use environment variables
password: process.env.PARTNER_PASSWORD // NEVER hardcode credentials
},
// ... other config
}}
/>Security Best Practices:
- Never commit credentials to version control
- Store credentials in environment variables or secure vaults
- Use different credentials for sandbox and production environments
- Rotate credentials periodically
- Implement proper access controls in your application
Access Token (Optional)
You can also set a JWT token for additional authentication:
import { setAccessToken } from '@fundly.ai/payment-sdk';
// Set token before rendering PaymentOptionPage
setAccessToken('your-jwt-token');📊 Analytics & Tracking
The SDK automatically tracks payment events using CleverTap analytics. No manual configuration needed - analytics is initialized automatically when you render the PaymentOptionPage component.
Automatic Analytics Initialization
When the SDK mounts, it automatically initializes analytics with the appropriate environment settings based on the environment parameter in your PaymentConfig.
Tracked Events (v2)
The SDK tracks exactly two events in the payment lifecycle:
1. Payment_SDK_Launched
Fires when the payment options screen fully loads.
Key fields included:
invoice_count,payment_amount,payment_type,partial_paymentsupplier_id,supplier_name,user_type,user_uuidsession_id,platform,sdk_version,sdk_entry_pointis_payment_enabled,status- All 9
payment_option_*availability booleans scan_channel,scan_origin,salesman_name- Carry-forward fields from upstream:
view_id,scan_id,bill_id,bill_type,bill_status,bill_total_amount,outstanding_amount,notification_id,notification_channel(null for non-QR/non-notification flows)
2. Payment_Initiated
Fires when the user selects a payment method and proceeds.
Key fields included:
fundly_transaction_id,invoice_count,partial_paymentpayment_amount,payment_type,payment_method,payment_modepayment_channel,payment_gateway,otp_required,retry_countsupplier_id,supplier_name,user_type,user_uuidsession_id,platform,mobile_changed- FUNDLY_PAY loan fields where applicable
Note:
Payment_Transactionis not fired by the SDK. It is owned by the backend.
Analytics Failures
All analytics calls are wrapped in try/catch. A tracking failure will never block or interrupt the payment flow.
📚 API Reference
Components
PaymentOptionPage
Main payment UI component with all payment options.
Props:
config: PaymentConfig- Payment configuration object
Utilities
sdkConfig.configure(callbacks: SDKCallbacks)
Configure platform-specific callbacks for SDK behavior.
setAccessToken(token: string)
Set authentication token for API calls.
getAccessToken(): string | null
Get current authentication token.
🛠️ Development
Building the SDK
npm run build # Build React npm package + embed script
npm run build:embed # Build embed script only (faster)Testing
npm run test:embed # Run Playwright integration tests for embed.jsRequires
npm run buildfirst to producedist/embed.js.
SRI Hash (for release notes)
npm run sri # Compute SHA-384 hash + print CDN URLsLocal Testing
# Link locally
npm link
# In your test project
npm link @fundly.ai/payment-sdkUnlink
npm unlink @fundly.ai/payment-sdk📝 TypeScript Support
The SDK is written in TypeScript and provides full type definitions. Import types:
import type {
PaymentConfig,
PartnerCredentials,
PaymentInvoice,
SDKCallbacks,
ImageFile,
Environment
} from '@fundly.ai/payment-sdk';🐛 Troubleshooting
Payment options not showing
- Verify authentication token is set
- Check that
typeOfAppmatches your use case - Ensure
paymentInvoicearray is properly formatted
Camera/Gallery not working
- Implement
onOpenCameraandonOpenGallerycallbacks - Return image data in the correct format (base64 string)
SDK not closing
- Implement
onNavigateHomecallback - Handle navigation in your application's routing system
📄 License
MIT License © Fundly Developers
🤝 Support
For issues and questions:
- Email: [email protected]
