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

@payconnect.me/kyc-react-native

v0.6.3

Published

PayConnect KYC React Native SDK — embeddable native KYC flow (identity + questionnaire + NFC), driven by the PayConnect Partners API.

Readme

@payconnect.me/kyc-react-native

The PayConnect KYC flow, as a React Native component. You drop <PayConnectKyc /> into your app, give it a session code, and it runs the whole identity check — forms, document photos, selfie, and the NFC chip read on passports and e-IDs.

The PayConnect backend decides what the applicant is asked next. This package draws the screens.

Before you start

| You need | Why | | --- | --- | | React Native 0.76 or newer | Peer dependency. | | React 18 or newer | Peer dependency. | | JDK 17 to build for Android | React Native 0.76 does not work on JDK 24. You get a confusing Gradle error, not a clear one. | | iOS 13.4 or newer | Set by the native pod. | | Android 7.0 or newer (minSdk 24) | Set by the native library. It compiles against SDK 35 and targets Java 17. | | A physical device to test the camera or NFC | Neither works on an emulator or simulator. | | A DOT licence for your app | See The DOT licence. Get this started early — it is issued by email. | | A session code from your own backend | See Get a session code. |

You can build and run the flow on a simulator without a licence by passing mock — see the props table.

Install

npm install @payconnect.me/kyc-react-native \
  react-native-safe-area-context react-native-svg @react-native-community/datetimepicker

All three are required peer dependencies, and all three contain native code — so after installing them you need a native rebuild, and on iOS a pod install first. A JavaScript reload is not enough.

  • react-native-safe-area-context — the SDK renders its own provider, so you do not have to add one.
  • react-native-svg — draws the flags in the nationality picker.
  • @react-native-community/datetimepicker — the calendar behind the date of birth and expiry fields.

Install them in your own app rather than relying on them arriving underneath it. React Native's autolinking only registers native modules that your app declares itself, so a package that reaches your node_modules as somebody else's transitive dependency is bundled but never linked. The JavaScript then loads and the native side is missing, which surfaces at the Verify Info step as TurboModuleRegistry.getEnforcing(...): 'RNCDatePicker' could not be found — followed by a second, more misleading error naming this SDK's DateField.

Two more are optional, and needed only if your applicants reach the Additional Documents step — see File pickers.

That command also pulls in @payconnect.me/kyc-core and @payconnect.me/kyc-contract. You do not install those separately.

Then do the native setup for each platform below. Skipping it means the app will not build.

Android setup

Three things go in your app, not in this package. Gradle resolves a library's native dependencies against your project's settings, so declaring them here would have no effect, and step 3 is a method on your own activity that only you can override.

1. Add the Innovatrics Maven repository

In your project's root android/build.gradle:

allprojects {
    repositories {
        maven { url = uri("https://maven.innovatrics.com/releases") }
    }
}

The DOT camera libraries are not on Maven Central. Without this line the build fails saying it cannot find com.innovatrics.dot:dot-document.

2. Add a packaging exclude

In your app module's android/app/build.gradle, inside the android { } block:

packaging {
    resources {
        excludes += ["META-INF/versions/9/OSGI-INF/MANIFEST.MF"]
    }
}

The NFC library depends on BouncyCastle, and two of its dependencies ship an identical metadata file. Without this the build fails at mergeDebugJavaResource with "3 files found with path 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'".

3. Install KycReactActivityDelegate

Required for the NFC chip read. Skip it and no applicant ever reads a chip. The build still succeeds and nothing warns you at compile time, so this is easy to miss until a device says the chip is unavailable.

In android/app/src/main/java/.../MainActivity.kt:

import com.payconnect.kyc.KycReactActivityDelegate

override fun createReactActivityDelegate(): ReactActivityDelegate =
    KycReactActivityDelegate(this, mainComponentName, fabricEnabled)

That replaces the DefaultReactActivityDelegate the React Native template generates, and changes exactly one thing: new intents reach ComponentActivity as well as React Native.

Why it is needed: DOT's chip reader receives the scanned document through ComponentActivity.addOnNewIntentListener. ReactActivity.onNewIntent calls its delegate first and only falls through to ComponentActivity when the delegate returns false — which the stock delegate never does once a React instance exists. Without this override the tag would reach your activity, React Native would consume the intent, and the reader would wait for a chip it has already been handed: no error, no crash, no timeout.

If your app is brownfield and dispatches new intents its own way, call KycReactActivityDelegate.declareNewIntentDelivery() once at startup instead.

The SDK will not let that hang happen. It checks before mounting the reader, and if your activity has not declared that it delivers new intents it refuses to start the scan: it logs why under the tag PayConnectKyc, reports the chip as unavailable, and returns the applicant to the NFC instructions screen, from which Choose alternative method takes them down the two-document path. That is a working flow, and a worse one — every applicant photographs two documents instead of tapping their passport once.

Android needs nothing else, unless your React Native brings a Fresco older than 3.7.0 — see below. The NFC permission is already declared in this package's manifest and is granted at install time — there is no runtime prompt to write.

If your React Native brings a Fresco older than 3.7.0

Check which one it brings:

./gradlew :app:dependencies --configuration releaseRuntimeClasspath | grep "fresco:fresco"

React Native 0.76 brings 3.2.0 and 0.87 brings 3.7.0. If yours is not 3.7.0, set frescoVersion to match, in your project's root android/build.gradle:

ext {
    frescoVersion = "3.2.0"
}

This package decodes the NFC instruction screen's GIF through com.facebook.fresco:animated-gif, and its version has to match the rest of Fresco. Set it too low and libgifimage.so ships 4 KB aligned while its siblings are 16 KB, which Play refuses the whole bundle for. Set it too high and this one edge drags Fresco's entire graph up with it.

iOS setup

1. Add the Innovatrics pod source

At the top of your ios/Podfile:

source 'https://cdn.cocoapods.org/'
source 'https://github.com/innovatrics/innovatrics-podspecs'

Then:

cd ios && pod install

CocoaPods only reads source lines from a Podfile, never from a podspec, so this cannot be done for you. If you add only the Innovatrics line, add the CocoaPods CDN line too — declaring one source turns off the default.

Not a DOT problem — a stock React Native 0.76 app fails the same way. RCT-Folly bundles fmt 11.0.2, whose consteval checks Apple Clang 21 rejects. -DFMT_USE_CONSTEVAL=0 is ignored in that version, so the header has to be patched after every pod install. Add to your post_install block:

fmt_base = File.join(installer.sandbox.root, 'fmt', 'include', 'fmt', 'base.h')
if File.exist?(fmt_base)
  content = File.read(fmt_base)
  unless content.include?('Xcode 26 workaround')
    patched = content.gsub(
      /^(#elif defined\(__cpp_consteval\)\n#  define FMT_USE_CONSTEVAL) 1/,
      "// Xcode 26 workaround: disable consteval\n\\1 0"
    )
    if patched != content
      File.chmod(0o644, fmt_base)
      File.write(fmt_base, patched)
    end
  end
end

Upgrading React Native past 0.76 is the real fix; this unblocks you until then.

2. Turn on NFC (skip if you do not need the chip read)

Three things, all signed into your app. If one is missing the app still builds, and NFC simply reports unsupported at runtime — the same as a phone with no NFC hardware. That makes a missing step hard to spot, so do all three together.

a. In the Apple Developer portal, enable Near Field Communication Tag Reading on your App ID, then regenerate your provisioning profiles.

b. In your App.entitlements:

<key>com.apple.developer.nfc.readersession.formats</key>
<array><string>TAG</string><string>PACE</string></array>

PACE is not optional. Without it, chips that only support PACE fail in a way that looks exactly like a wrong passport number.

c. In your Info.plist:

<key>NFCReaderUsageDescription</key>
<string>Used to read the chip in your passport.</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array><string>A0000002471001</string></array>

A0000002471001 is the identifier of the passport chip application. Without it, iOS will not hand the chip to the reader.

Get a session code

sessionCode is a short-lived credential that your own backend creates. Your API key must never be in your app — a mobile app can be unpacked, and a key inside one cannot be rotated without shipping a new release.

The flow is:

  1. Your app asks your backend for a session.
  2. Your backend calls the PayConnect Partners API with your secret X-API-Key.
  3. Your backend returns only the resulting code to the app.

On your backend

@payconnect.me/kyc-core ships the helper for step 2. It is installed already, as a dependency of this package, but your backend is a separate project — install it there too:

npm install @payconnect.me/kyc-core

Needs Node 20.19 or newer. The package is ESM-only, so import works directly; a CommonJS backend can still require('@payconnect.me/kyc-core') on that version. No bundler needed — this runs under plain node.

// On YOUR backend. Never in the app.
import express from 'express';
import { createKycSession } from '@payconnect.me/kyc-core';

const app = express();
app.use(express.json());

app.post('/kyc-session', async (req, res) => {
  try {
    const code = await createKycSession({
      baseUrl: 'https://partners-api.payconnect.me',
      apiKey: process.env.PARTNERS_API_KEY, // secret — server environment only
      productId: req.body.productId, // the KYC product you are starting
    });
    res.json({ code }); // hand ONLY the code to the app
  } catch (error) {
    res.status(502).json({ error: String(error) });
  }
});

app.listen(3000);

| Option | Type | Required | What it is | | --- | --- | --- | --- | | baseUrl | string | Yes | The PayConnect Partners API: https://partners-api.payconnect.me. | | apiKey | string | Yes | Your partner API key. A secret. | | productId | string | Yes | The UUID of the KYC product to start. PayConnect gives you this. | | fetch | typeof fetch | No | Your own fetch, if you need one. Defaults to the global. |

createKycSession is deliberately not exported from @payconnect.me/kyc-react-native, so it cannot end up in your app bundle by accident.

In your app

Fetch the code from your own endpoint, then pass it to the component:

const response = await fetch('https://your-backend.example.com/kyc-session', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ productId }),
});
const { code } = await response.json();

// code is what you pass as sessionCode — see below.

Full details, including everything else @payconnect.me/kyc-core exports, are in its README.

Use it

import { PayConnectKyc } from '@payconnect.me/kyc-react-native';

export function KycScreen({ sessionCode }: { sessionCode: string }) {
  return (
    <PayConnectKyc
      sessionCode={sessionCode}
      baseUrl="https://partners-api.payconnect.me"
      returnTo="https://your-app.example.com/kyc/callback"
      onEvent={(event) => {
        if (event.type === 'completed') {
          closeModal();
        }
      }}
    />
  );
}

Render it full-screen, or in a modal. It manages its own navigation, so you do not add screens or routes of your own.

Props

Only sessionCode is required.

| Prop | Type | Default | What it does | | --- | --- | --- | --- | | sessionCode | string | — | Required. The session code your backend created. | | baseUrl | string | https://partners-api.payconnect.me | The PayConnect Partners API. The default is already correct, so you can leave this out. | | language | 'en' \| 'ru' \| 'th' | 'en' | Starting language. The applicant can change it from the header. | | onEvent | (event) => void | — | Called when the flow reaches an outcome. See Events. | | returnTo | string | — | Where the "Return to service" button sends the applicant. See Sending the applicant back to you. | | mock | boolean | false | Replaces the camera and chip read with fake ones so you can run the flow on a simulator. Everything else still hits the real baseUrl. | | dotLicenseBase64 | string | — | Your DOT licence as base64, if you serve it rather than bundling a file. See The DOT licence. | | theme | Partial<KycTheme> | PayConnect theme | Narrow colour and spacing overrides. Merged over the defaults. | | token | string | — | Optional bearer token. Most integrations do not need one — the session code is the credential. | | surfaceId | string | — | Only for hand-off between devices. Pass surfaceIdFor(sessionCode). See Taking over a session. | | nationality | string | — | An ISO-3 nationality the applicant has already given, so the flow does not ask again. See Resuming a session that already knows things. | | skipWelcome | boolean | false | Skip the welcome screen, for an applicant who has already seen it. Same section as above. | | onScreenshotDetected | (sessionId) => void | — | iOS only. The applicant screenshotted a protected screen. See Screen capture protection. |

Every prop in one example

Only sessionCode is required. This shows every one together, with the values you would actually pass.

import {
  PayConnectKyc,
  defaultTheme,
  surfaceIdFor,
  type KycEvent,
} from '@payconnect.me/kyc-react-native';

export function KycScreen({
  sessionCode,
  licenceBase64,
}: {
  sessionCode: string;
  licenceBase64?: string;
}) {
  return (
    <PayConnectKyc
      /* Required. The code your backend minted with createKycSession. */
      sessionCode={sessionCode}

      /* The Partners API. This is the default, so you can leave it out. */
      baseUrl="https://partners-api.payconnect.me"

      /* Optional bearer token. Most integrations omit this entirely — the
         session code is the credential. */
      token={undefined}

      /* Only for handing a session between devices. Always use surfaceIdFor,
         never a random id, or a reopened app looks like a new device. */
      surfaceId={surfaceIdFor(sessionCode)}

      /* Starting language: 'en' | 'ru' | 'th'. The applicant can change it
         from the header at any point. */
      language="en"

      /* Where the "Return to service" button sends the applicant. Must be
         https. Omit it and the button is not shown. */
      returnTo="https://your-app.example.com/kyc/callback"

      /* An ISO-3 nationality the applicant already gave — from the link that
         opened your app. Ignored if it names no country we know, in which case
         the flow simply asks. Omit it and the flow asks. */
      nationality="THA"

      /* They have already seen the welcome screen, so do not show it again. */
      skipWelcome

      /* Outcome callback. See the Events table below. */
      onEvent={(event: KycEvent) => {
        switch (event.type) {
          case 'ready':
            hideSpinner();
            break;
          case 'completed':
            // The applicant finished. NOT the same as approved — wait for
            // your webhook before granting anything.
            closeModal();
            break;
          case 'failed':
            showError(event.reason);
            break;
          case 'closed':
            closeModal();
            break;
        }
      }}

      /* Narrow overrides. Spread defaultTheme.colors — the colours object
         is all-or-nothing to TypeScript, even though only the keys you
         change take effect. */
      theme={{
        colors: { ...defaultTheme.colors, cta: '#22C55E', ctaPressed: '#16A34A' },
        radius: 8,
        cardRadius: 28,
      }}

      /* Your DOT licence as base64, if you serve it rather than bundling a
         .lic file. Takes priority over the bundled file. */
      dotLicenseBase64={licenceBase64}

      /* true replaces the camera and chip read with fakes so the flow runs on
         a simulator. Every API call still goes to baseUrl. */
      mock={false}

      /* iOS only. The flow blocks capture by itself; this reports the one
         thing iOS will not let anyone prevent. */
      onScreenshotDetected={(sessionId) => track('kyc_screenshot', { sessionId })}
    />
  );
}

Passing undefined for an optional prop is the same as leaving it out, so you can drop any line above that you do not need.

Events

onEvent receives one of four objects. Every one carries sessionId.

| event.type | Meaning | What to do | | --- | --- | --- | | ready | The flow has loaded and is showing its first screen. | Hide your own loading spinner. | | completed | The applicant finished every step. | Close the flow. This does not mean approved. | | failed | The flow could not continue. Read event.reason. | Show your own error, offer a retry. | | closed | The applicant backed out. | Close the flow. |

Read this twice: completed means the applicant reached the end, not that they passed. The real verdict arrives at your backend as a webhook. Never unlock an account, release funds, or mark a customer verified based on completed.

Sending the applicant back to you

returnTo is optional. It is the URL behind the Return to service button on the success screen. PayConnect does not store a return address, so if you leave it out the applicant sees "you may now close this window" and has no way back to you.

When the button is tapped, two values are added to your URL:

https://your-app.example.com/kyc/callback?session=<sessionCode>&status=completed

Anything already in your query string is kept. It must be https — anything else is dropped and the button is not shown.

Treat status as a hint for what to show next, never as proof of the outcome. The webhook is the proof.

Taking over a session from another device

Skip this unless you support handing a session between devices — for example, a customer starting on desktop and finishing on their phone via a QR code.

Pass surfaceId={surfaceIdFor(sessionCode)} and the device that handed the session over can stop waiting straight away, instead of hanging until the applicant finishes a whole step. The id is minted once and held for the life of the process — the same rule the PayConnect web flow uses, so the app, a desktop browser and a mobile browser can all recognise one another. An app that is killed and reopened on the same session is a new surface, and the applicant taps once to continue, exactly as a browser whose tab was closed would.

import { PayConnectKyc, surfaceIdFor } from '@payconnect.me/kyc-react-native';

<PayConnectKyc sessionCode={code} surfaceId={surfaceIdFor(code)} />;

Resuming a session that already knows things

Skip this unless applicants reach your app from a link or a QR code.

When somebody starts KYC in a browser and moves to your app, they have already met the welcome screen and they may have already chosen their nationality. The PayConnect web flow carries both facts in its link, as started=1 and n=<ISO-3>. Pass them on and the applicant is not asked twice:

import { PayConnectKyc } from '@payconnect.me/kyc-react-native';

// However you already parse the link that opened your app. React Native's built-in `URL` does not
// implement `searchParams`, so use your own parser or `react-native-url-polyfill` — do not assume it.
const { c, n, started } = parseIncomingLink(url);

<PayConnectKyc sessionCode={c} nationality={n} skipWelcome={started === '1'} />;

nationality is validated, not trusted. A code naming no country this SDK knows is ignored and the flow asks the question as usual, because that value decides which document the applicant is asked to photograph. You do not have to pre-check it, but you can — normalizeNationality is exported, returns the canonical upper-case ISO-3 or null, and is exactly what the SDK runs:

import { normalizeNationality } from '@payconnect.me/kyc-react-native';

normalizeNationality('tha'); // 'THA'
normalizeNationality('NOTACODE'); // null

When a nationality is supplied the flow also stops asking the backend which modules the session contains — there is nothing left for the answer to change — and the identity progress bar is scaled to the three steps that remain rather than showing a quarter already finished.

Screen capture protection

The SDK blocks screen capture by itself while the flow is showing a document, a selfie or an answer. There is nothing to install and nothing to wire up — no Info.plist key, no entitlement, no manifest line, no MainActivity change.

| Platform | What the applicant's device does | | --- | --- | | Android | Refuses screenshots, blanks recordings and casts, and shows an empty recents thumbnail | | iOS | Saves stills and recordings black, covers the content in the app switcher and while a recording or mirror runs, and reports the screenshot to you |

Protection releases on the three terminal screens — success, already-complete and error. None of them shows a document or a selfie, and the error screen prints a support code your applicant is meant to photograph and send to you. It is also off before the flow has mounted anything, which is what stops a reloaded bundle leaving the app permanently secure.

iOS cannot refuse a screenshot. No app can — the platform offers no such API. What it can do is blank what the capture records, which is what the SDK does: the applicant's own display stays live and the saved image comes out black. onScreenshotDetected tells you it happened.

<PayConnectKyc
  sessionCode={code}
  onScreenshotDetected={(sessionId) => track('kyc_screenshot', { sessionId })}
/>

Android does not call it, and that is not a gap: the capture never happens, so there is nothing to report.

Two limits worth knowing:

  • The Android date picker is not covered. @react-native-community/datetimepicker opens a dialog in a window of its own, which does not inherit the flag. It shows a date grid and nothing else.
  • On Android the flag belongs to your activity, not to the SDK. If your app sets FLAG_SECURE for its own reasons, the SDK clears it when the flow reaches a terminal screen. Set it again after completed or closed if you need it to outlive the flow.

The DOT licence

Camera capture and chip reading use the Innovatrics DOT SDK, which needs a licence. The licence is tied to your app's identifierapplicationId on Android, bundle ID on iOS — not to this SDK. PayConnect's own licence will not work inside your app.

How to get one

  1. Settle your applicationId and bundle ID first. Changing either later means a new licence request.
  2. Email [email protected] with those identifiers, and PayConnect will arrange the licence.
  3. Ask for NFC to be included if you want the chip read. It is a separate library on the licence, and it is easy to be issued one without it.

Where to put it

Either bundle the file:

  • Android: android/app/src/main/res/raw/dot_license.lic — that folder must contain only .lic files.
  • iOS: add dot_license.lic to your app target's Copy Bundle Resources.

Or pass the bytes at runtime, which takes priority over the bundled file:

<PayConnectKyc sessionCode={code} dotLicenseBase64={licenceFromYourServer} />

Never commit a .lic file to source control.

If your licence does not cover NFC

Nothing breaks. The SDK detects it, reports NFC as unsupported, and asks the applicant for two supporting documents instead. That is a fully supported, compliant path — not a degraded one. Thai national ID applicants skip this module entirely.

File pickers

The Additional Documents step needs a file picker, and React Native has none built in.

npm install @react-native-documents/picker react-native-image-picker

| Package | Needed for | Note | | --- | --- | --- | | @react-native-documents/picker | Choosing an image or PDF from Files/Photos. Required to upload at all. | Any version from 10 upwards. On React Native 0.76–0.78, ask for @^10.1.7 explicitly — picker 11 and above require React Native >=0.79, and npm reports that as ERESOLVE rather than picking 10 for you. On 0.79 and newer the unpinned command above is right. | | react-native-image-picker | Photographing a document there and then. | ^8.2.1 is what PayConnect tests against, and the peer range asks for >=8. |

PayConnect verifies the Additional Documents flow against picker 10.1.7 and 12.0.2. pick()'s options, its response fields and its cancel behaviour are identical across the two — the major bumps carry the picker's own React Native floor, not an API change. This SDK's peer range therefore does not cap the picker: the constraint belongs to the picker package, which declares it, and duplicating it here only locked out hosts on current React Native.

Both are genuinely optional, so a flow that never reaches Additional Documents — every applicant using a Thai national ID skips the module entirely — does not have to install either. Your app bundles and runs without them.

If one is missing when an applicant does reach that step, the SDK throws an error naming the exact package, rather than failing as undefined is not a function. The step cannot upload without @react-native-documents/picker, so install both unless you are certain nobody will get there.

Testing your app

This package ships TypeScript source rather than compiled JavaScript, and @payconnect.me/kyc-core and @payconnect.me/kyc-contract are ESM-only. Metro handles both without any configuration.

Jest does not. Add this to your jest.config.js or your test suite dies with Unexpected token 'export' before a single test runs:

module.exports = {
  preset: 'react-native',
  transformIgnorePatterns: ['node_modules/(?!(@react-native|react-native|@payconnect\\.me)/)'],
};

That is the React Native preset's own default with @payconnect.me added. Do not widen it to all of node_modules — that transpiles every dependency on every run.

If something goes wrong

| What you see | Cause | Fix | | --- | --- | --- | | Build fails: "3 files found with path META-INF/versions/9/OSGI-INF/MANIFEST.MF" | Missing packaging exclude. | Android setup, step 2. | | Build fails: cannot find com.innovatrics.dot:dot-document | Missing Maven repository. | Android setup, step 1. | | pod install cannot find dot-document | Missing Podfile source line. | iOS setup, step 1. | | A vague Gradle error that names nothing useful | Wrong JDK. | Use JDK 17: export JAVA_HOME="$(/usr/libexec/java_home -v 17)". | | iOS build fails inside fmt/format-inl.h | Xcode 26+ against the fmt bundled with React Native 0.76. | iOS setup, step 1 — the collapsed post_install patch. | | NFC says unsupported on a phone that definitely has NFC | Missing iOS entitlement or capability, or a licence issued without NFC. | iOS setup, step 2, then check your licence. | | Android: every applicant is sent to the two-document path, and adb logcat -s PayConnectKyc says the chip read was not started | MainActivity still uses the template's delegate, so the SDK refuses to start the scan rather than let it hang. | Android setup, step 3. | | Camera screen never opens | Running on an emulator or simulator. | Use a physical device, or pass mock to skip capture. | | The flow stops at the selfie comparison step | You are using mock against a real backend. Mock capture uploads nothing, so there is nothing to compare. | Expected. Use real capture on a device to go further. | | Unexpected token 'export' in your tests | Jest is not transforming this package. | Testing your app. |

Also exported

Most integrations only need PayConnectKyc. These are available if you need them:

| Export | Use | | --- | --- | | defaultTheme, KycTheme, KycColors | Building a theme override. | | safeReturnTo, appendKycStatus | Checking a returnTo URL yourself before passing it in, if it came from somewhere untrusted such as a scanned code. | | surfaceIdFor | Device hand-off, above. | | normalizeNationality | Checking a nationality code yourself before passing it in, for the same reason. Returns the upper-case ISO-3, or null if the flow would ignore it. | | createTranslate, pickLanguage, LANGUAGE_LABELS | Reading the SDK's own copy, for example to label your own language switcher. | | initializeDotSdk, getLivenessMode, getNfcReader, STUB_NFC_READER | Direct access to the native seams. Rarely needed. | | getScreenCaptureGuard, STUB_SCREEN_CAPTURE_GUARD | Arming the capture block around your own screens. The flow already does its own. | | DEFAULT_PARTNERS_API_BASE_URL | The production URL, as a constant. |

Types: PayConnectKycProps, KycLanguage, KycEvent, KycEventType, KycEventHandler, KycReturnStatus, NfcAvailability, NfcReader, MrzKey, ScreenCaptureGuard, Translate, KycMessageKey.

Support