npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@portalduct/react-sdk-factorcloud

v1.3.4

Published

React SDK for integrating with PortalDuct and FactorCloud APIs

Readme

@portalduct/react-sdk-factorcloud

React SDK for connecting FactorCloud debtors to carrier portals and syncing invoice data through an embedded modal workflow.

Auto-managed credentials

The SDK bootstraps its runtime configuration from the check-customer-status webhook. Supply your FactorCloud factor ID and the PortalDuct webhook bearer token and the SDK will fetch the latest FactorCloud and PortalDuct API tokens, API base URLs, and PortalDuct customer UUID for you.

Installation

npm install @portalduct/react-sdk-factorcloud

Required peer dependencies (install if you don't have them):

npm install react react-dom @tanstack/react-query react-hook-form @hookform/resolvers lucide-react

Quick Start

1. Drop in the prebuilt Sync banner (no manual CSS import needed)

The bundle ships with its own stylesheet, so you can simply import the banner component and start collecting connections. Only two required inputs are needed up front: the PortalDuct webhook bearer token (to call the Supabase webhooks) and your FactorCloud factor ID. Everything else is populated automatically after the first customer status check.

import { SyncBanner, type SDKConfig } from '@portalduct/react-sdk-factorcloud';

const baseConfig = {
  portalduct: {
    webhook_bearer_token: 'fc_webhook_token',
    // Optional overrides if you are not using the default Supabase functions
    sync_webhook_url: 'https://example.supabase.co/functions/v1/webhook-sync-connection',
    verify_webhook_url: 'https://example.supabase.co/functions/v1/webhook-verify-connection',
    environment: 'sandbox', // Optional: force the environment sent to Supabase
  },
  factorcloud: {
    factorId: 'your_factor_id',
    client_id: 'optional_client_id', // Optional: pre-select a client
  },
} satisfies Partial<SDKConfig>;

function MyApp() {
  return (
    <SyncBanner
      config={baseConfig as SDKConfig} // Remaining fields are hydrated by the SDK
      onSyncComplete={() => console.log('Sync complete')}
      onSyncError={(error) => console.error('Sync failed', error)}
    />
  );
}

TypeScript tip: When you only provide the minimal fields above, cast the object to SDKConfig at the callsite (as shown). The SDK fills in the missing properties after the status check resolves.

2. Prefer manual control? Wrap your app with QueryClientProvider

If you need custom layout control, render SyncWorkflow directly. The banner example above auto-creates a React Query client, but for full control create your own instance and wrap your app:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { SyncWorkflow, type SDKConfig } from '@portalduct/react-sdk-factorcloud';

const baseConfig = {
  portalduct: { webhook_bearer_token: 'fc_webhook_token' },
  factorcloud: { factorId: 'your_factor_id' },
} satisfies Partial<SDKConfig>;

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* Your app content */}
      <SyncWorkflow
        config={baseConfig as SDKConfig}
        trigger={<button>Sync Invoices</button>}
      />
    </QueryClientProvider>
  );
}

That's it! Clicking the "Sync Invoices" button opens a modal workflow that handles:

  • Client selection (if no client_id provided)
  • Debtor selection
  • Portal connection setup
  • Credential management (tokens are refreshed automatically)
  • Invoice synchronization

Configuration

Minimal configuration inputs

Only two fields are required at initialization time:

| Field | Required? | Description | |-------|-----------|-------------| | portalduct.webhook_bearer_token | ✅ | Authenticates requests to the Supabase webhooks (check-customer-status, webhook-sync-connection, webhook-verify-connection). | | factorcloud.factorId | ✅ | Identifies which FactorCloud account should be synced. |

Optional overrides you can provide:

| Field | When to set it | |-------|----------------| | portalduct.sync_webhook_url / portalduct.verify_webhook_url | Override the default Supabase function URLs. | | portalduct.environment | Force a specific environment string to be sent to the webhook payload. The SDK auto-detects sandbox vs production, so most integrations can omit this. | | factorcloud.client_id | Skip the client-selection step during syncing. |

Everything else—PortalDuct API bearer token, FactorCloud API bearer token, API base URLs, and the PortalDuct customer UUID—is hydrated automatically from the customer status response.

Runtime configuration shape

Internally the SDK works with a fully populated configuration object:

import type { SDKConfig } from '@portalduct/react-sdk-factorcloud';

// After the first status check completes the SDK produces an object like this:
const resolvedConfig: SDKConfig = {
  portalduct: {
    bearer_token: 'pd_token',
    env_url: 'https://sandbox.portalduct.com',
    customer_id: '896ec608-373f-4fde-82e5-00b1f427dba5',
    webhook_bearer_token: 'fc_webhook_token',
    environment: 'sandbox',
    sync_webhook_url: 'https://example.supabase.co/functions/v1/webhook-sync-connection',
    verify_webhook_url: 'https://example.supabase.co/functions/v1/webhook-verify-connection',
  },
  factorcloud: {
    bearer_token: 'fc_token',
    factorId: 'your_factor_id',
    env_url: 'https://api.int.factorcloud.com',
    client_id: 'optional_client_id',
  },
};

Use the useCustomerStatus hook to observe and reuse the merged configuration:

import { useCustomerStatus, type SDKConfig } from '@portalduct/react-sdk-factorcloud';

const baseConfig = {
  portalduct: { webhook_bearer_token: 'fc_webhook_token' },
  factorcloud: { factorId: 'your_factor_id' },
} satisfies Partial<SDKConfig>;

function DebugConfig() {
  const { resolvedConfig, loading } = useCustomerStatus(baseConfig as SDKConfig);

  if (loading || !resolvedConfig) return <p>Loading…</p>;
  return <pre>{JSON.stringify(resolvedConfig, null, 2)}</pre>;
}

Standalone Modal Components

Import individual modals for custom workflows:

import { useState } from 'react';
import {
  ConfigModal,
  ConnectDebtorModal,
  RequestPortalModal,
  UpdateCredentialsModal,
  SyncProgressModal,
  ClientSelectionModal,
  type SDKConfig,
} from '@portalduct/react-sdk-factorcloud';

function CustomWorkflow() {
  const [open, setOpen] = useState(false);
  const [config, setConfig] = useState<Partial<SDKConfig>>({
    portalduct: { webhook_bearer_token: 'fc_webhook_token' },
    factorcloud: { factorId: 'your_factor_id' },
  });

  return (
    <ConfigModal
      open={open}
      onOpenChange={setOpen}
      onSave={(next) => setConfig(next)}
      currentConfig={config as SDKConfig}
    />
  );
}

Direct API Access

Need to orchestrate data yourself? Pull the resolved config from useCustomerStatus first, then hand it to the lower-level API clients:

import { useCustomerStatus, type SDKConfig } from '@portalduct/react-sdk-factorcloud';
import {
  PortalDuctAPI,
  FactorCloudAPI,
  ConnectionSyncAPI,
} from '@portalduct/react-sdk-factorcloud';

const baseConfig = {
  portalduct: { webhook_bearer_token: 'fc_webhook_token' },
  factorcloud: { factorId: 'your_factor_id' },
} satisfies Partial<SDKConfig>;

function CustomDataFlow() {
  const { resolvedConfig } = useCustomerStatus(baseConfig as SDKConfig);

  if (!resolvedConfig) {
    return <p>Loading credentials…</p>;
  }

  const portalductApi = new PortalDuctAPI(
    resolvedConfig.portalduct.env_url,
    resolvedConfig.portalduct.bearer_token,
  );

  const factorcloudApi = new FactorCloudAPI(
    resolvedConfig.factorcloud.env_url,
    resolvedConfig.factorcloud.factorId,
    resolvedConfig.factorcloud.bearer_token,
  );

  const connectionSync = new ConnectionSyncAPI({
    webhookToken: resolvedConfig.portalduct.webhook_bearer_token,
    customerId: resolvedConfig.portalduct.customer_id,
    environment: resolvedConfig.portalduct.environment,
    webhookUrl: resolvedConfig.portalduct.sync_webhook_url,
    verifyWebhookUrl: resolvedConfig.portalduct.verify_webhook_url,
    portalductBaseUrl: resolvedConfig.portalduct.env_url,
  });

  // ...custom logic using the clients above
}

Data Orchestration Service

For complex data synchronization workflows you can instantiate the orchestration service once the config has been resolved:

import { useEffect } from 'react';
import { useCustomerStatus, DataOrchestrationService, type SDKConfig } from '@portalduct/react-sdk-factorcloud';

const baseConfig = {
  portalduct: { webhook_bearer_token: 'fc_webhook_token' },
  factorcloud: { factorId: 'your_factor_id' },
} satisfies Partial<SDKConfig>;

function SyncAllDebtors() {
  const { resolvedConfig } = useCustomerStatus(baseConfig as SDKConfig);

  useEffect(() => {
    if (!resolvedConfig) return;

    const orchestration = new DataOrchestrationService(resolvedConfig);
    orchestration.getFactorCloudClients().then(console.log);
  }, [resolvedConfig]);

  return null;
}

Security

⚠️ Never expose API tokens in client-side code in production!

Even though the SDK auto-fetches credentials, the webhook you call still needs to run on a trusted backend (the provided Supabase functions or your own infrastructure). Store the webhook bearer token securely (environment variables, secret manager, etc.) and never hard-code it in publicly distributed builds.