@column-na/card-reveal-react-native

中文文档
0.13.0发布于 2026/10/31SEE LICENSE IN LICENSETypeScriptES Module近 30 天下载 2093

Show a Column card's number, CVV, expiry and cardholder name in your React Native app without the data touching your code.

原始使用文档
文档目录14 节
  1. How it works
  2. Install
  3. Usage
  4. React bindings
  5. Key-bound tokens: prepareReveal()
  6. CardRevealText
  7. Options
  8. ready , unmount , hide and show
  9. Hide, show and copy
  10. Errors
  11. Revealing the PIN
  12. Sandbox and live
  13. Versioning
  14. License

@column-na/card-reveal-react-native

Show a Column card's full number, CVV, expiry and cardholder name in your React Native or Expo app. The card data never touches your code or your servers.

It's pure JavaScript with no native dependencies, so it works in Expo Go, Expo development builds and bare React Native. It requires React 19.1 or later and React Native 0.81 or later.

How it works

  1. The app calls client.prepareReveal(), which makes the reveal's one-time key and gives the app its thumbprint, keyThumbprint. Send it to your server with the card ID.
  2. Your server creates a single-use reveal token, valid for one minute, bound to that key, using your API key:
    curl -X POST 'https://api.column.com/issuing/cards/<card_id>/reveal-tokens' -u :<YOUR API KEY> \
      -H 'Content-Type: application/json' -d '{"reveal_key_thumbprint": "<keyThumbprint>"}'
    # {"id":"crtk_...","card_id":"card_...","expires_at":"...","reveal_key_thumbprint":"..."}
    
    Check that the signed-in user owns the card first. Then send only the token id to the app.
  3. The app renders a CardRevealText for each field and calls prepared.reveal(...). The request goes straight from the device to Column's API, and the card details come back encrypted end to end to the one-time key, which stays in the SDK's memory. The values stay inside the SDK's components: no callback, ref or return value exposes them. The token works only with that key, so a token that leaks is useless to anyone else.

Install

npx expo install @column-na/card-reveal-react-native react-native-get-random-values
# or: npm install @column-na/card-reveal-react-native react-native-get-random-values

Every reveal, live or sandbox, generates a one-time key, which needs a secure random source (crypto.getRandomValues). React Native's engine doesn't provide one, so import the polyfill once, at the top of your app's entry file, before anything else:

import 'react-native-get-random-values';

Without it, reveal() throws invalid_options.

The polyfill is part of the reveal's trust boundary: the one-time key is only as unpredictable as the random bytes it gives the SDK. Use react-native-get-random-values, which installs crypto.getRandomValues backed by the operating system's secure random source, also in Expo apps, and don't replace crypto.getRandomValues with a shim or a test stub in a build that reveals real cards. The SDK checks the source before each key (it refuses all-zero or repeating output, output not written into the array it passed in, and a degenerate key) and fails the reveal with reveal_failed instead of sending a predictable key. The check inspects the very bytes the key is then made from, so a polyfill can't pass it with one set of bytes and seed the key with another, but it can't tell a sound source from one an attacker can reproduce.

Usage

import { useEffect, useMemo, useRef } from 'react';
import { View } from 'react-native';
import {
  CardRevealError,
  CardRevealText,
  createCardRevealClient,
  type CardRevealTextHandle,
} from '@column-na/card-reveal-react-native';

export function CardDetails({
  cardId,
  createRevealToken, // asks your server for a token bound to the key: (keyThumbprint) => Promise<string>
}: {
  cardId: string;
  createRevealToken: (keyThumbprint: string) => Promise<string>;
}) {
  const client = useMemo(() => createCardRevealClient({ environment: 'live' }), []); // or 'sandbox'
  const pan = useRef<CardRevealTextHandle>(null);
  const cvv2 = useRef<CardRevealTextHandle>(null);
  const month = useRef<CardRevealTextHandle>(null);
  const year = useRef<CardRevealTextHandle>(null);

  useEffect(() => {
    let cancelled = false;
    let unmount = () => {};
    (async () => {
      // One prepared key and one token per reveal: both are single use.
      const prepared = await client.prepareReveal();
      const revealToken = await createRevealToken(prepared.keyThumbprint);
      if (cancelled) return;
      const reveal = prepared.reveal({
        cardId,
        revealToken,
        fields: { pan, cvv2, expiration_month: month, expiration_year: year },
        style: { color: '#fff', fontFamily: 'Menlo' },
        fieldStyles: { pan: { fontSize: 20 } },
        format: { pan: 'spaced', expirationYear: 'YY' },
      });
      unmount = reveal.unmount;
      await reveal.ready;
    })().catch((error) => {
      if (CardRevealError.is(error)) console.warn('Card reveal failed:', error.code);
    });
    return () => {
      cancelled = true;
      unmount();
    };
  }, [client, cardId, createRevealToken]);

  return (
    <View>
      <CardRevealText ref={pan} placeholder="•••• •••• •••• ••••" selectable />
      <CardRevealText ref={cvv2} placeholder="•••" />
      <CardRevealText ref={month} placeholder="MM" />
      <CardRevealText ref={year} placeholder="YY" />
    </View>
  );
}

React bindings

CardRevealProvider and CardRevealSlot do the same in a few lines:

import { CardRevealProvider, CardRevealSlot } from '@column-na/card-reveal-react-native';

<CardRevealProvider
  client={client}
  cardId={cardId}
  mintToken={({ cardId, keyThumbprint }) => myServer.createRevealToken(cardId, keyThumbprint)}
>
  <CardRevealSlot field="pan" placeholder="•••• •••• •••• ••••" selectable />
  <CardRevealSlot field="cvv2" placeholder="•••" />
</CardRevealProvider>;
  • mintToken({ cardId, keyThumbprint }) asks your server for a token bound to the reveal's key and resolves with its id. It's called once per reveal.
  • <CardRevealSlot field="..."> is a CardRevealText for that field and takes its props.
  • <CardRevealProvider> also takes style, fieldStyles, format, timeoutMs and enabled (default true; false holds off, or hides a shown card).
  • Inside it, useCardRevealState() returns { status, error, retry }. status is 'idle', 'preparing', 'minting', 'revealing', 'ready' or 'error'. error is a CardRevealError or whatever mintToken threw. retry() starts over with a new key and token.
  • Changing the client, card, slots, styles, format or timeoutMs reveals again with a new token; unmounting the provider unmounts the reveal and forgets any unused key.
  • useCardReveal({ client, cardId, mintToken, fields }) is the same as a hook, with fields mapping each field to a CardRevealText ref.

Key-bound tokens: prepareReveal()

client.prepareReveal() makes the next reveal's one-time P-256 key pair before the token exists. It resolves with:

  • keyThumbprint: the key's RFC 7638 SHA-256 thumbprint, base64url without padding (43 characters). Pass it to your server, which mints the token with it as reveal_key_thumbprint. It's a hash of a public key, not a secret.
  • reveal(options): reveals with that key, with the same options and result as client.reveal(). It may be called once; a second call throws invalid_options. Invalid options throw before the key is used, so a corrected call can still use it.
  • discard(): forgets the key without revealing, for when the reveal won't happen (the token couldn't be minted, or the screen moved on). Afterwards reveal() throws invalid_options.
const prepared = await client.prepareReveal();
const revealToken = await myServer.createRevealToken(cardId, prepared.keyThumbprint);
const { ready, unmount } = prepared.reveal({ cardId, revealToken, fields: { pan, cvv2 } });

Column answers a bound token only for a reveal that sends that key. Any other key gets a 401 (reveal_failed), and the token isn't used up. The private key stays in the SDK's memory and is forgotten once the reveal's request settles, or when the reveal is torn down. prepareReveal() rejects with invalid_options (no secure random source, or live on react-native-web) or reveal_failed.

Every reveal token is minted with a reveal_key_thumbprint, so every reveal starts with prepareReveal(). client.reveal() takes the same options, but a token minted for another key, or a reveal made without the prepared key, gets a 401.

CardRevealText

PropDescription
placeholderShown before the reveal and after unmount.
styleText style. The reveal's style and fieldStyles apply on top.
selectableWhether the user can select (and copy) the revealed text. Default false.
testIDTest identifier.

Options

OptionDescription
cardIdThe card's ID (card_...).
revealTokenA reveal token ID (crtk_...) from your server. Single use, valid for 1 minute.
fieldsMaps any of pan, cvv2, cardholder_name, expiration_month, expiration_year and pin to a CardRevealText ref. Use a different component for each field; see Revealing the PIN.
style / fieldStylesReact Native text styles for every field, or for one field.
format.pan'spaced' (default: 4111 1111 1111 1111 for 16-digit numbers) or 'plain'.
format.expirationYear'YYYY' (default) or 'YY'.
timeoutMsHow long ready waits before rejecting with timeout. Default 15000, maximum 120000.
hiddentrue reveals the fields masked (••••) until you call show(). Default false.
autoHideMsUnmounts the reveal this many milliseconds after ready resolves (1 to 3600000).
onAutoHideCalled once when autoHideMs unmounts the reveal.

ready, unmount, hide and show

  • ready resolves once every field shows its value.
  • If any field fails, or the timeout passes, ready rejects and every field returns to its placeholder.
  • unmount() does the same. If ready is still pending, it rejects with unmounted.
  • hide() and show() mask and unmask every field; see Hide, show and copy.
  • Invalid options throw invalid_options synchronously.

Hide, show and copy

  • reveal.hide() masks every field in place (each character but spaces shows as •) and reveal.show() shows it again, without a new token or request. A masked CardRevealText isn't selectable. Both do nothing after unmount() or a failure. hidden: true starts masked.
  • autoHideMs removes the values that long after ready, as unmount() does (the placeholders come back), and calls onAutoHide once. Showing them again needs a new token.
  • Copy: React Native has no clipboard API without a native module (@react-native-clipboard/clipboard or expo-clipboard), and this SDK has no native code, so it doesn't offer a copy button. copyable and onCopy are web only and throw invalid_options here. Set selectable on CardRevealText so the cardholder can copy with the system text selection.

Errors

Every error is a CardRevealError with a stable code: invalid_options, container_not_found (a ref isn't attached to a mounted CardRevealText), reveal_failed, timeout and unmounted. field and the HTTP status are set when known, and apiCode when Column's API returned an error code. A 401 means the token is missing, invalid, already used, expired, for a different card or bound to a different reveal key. Messages never contain the token or card data, and the SDK never logs either.

Revealing the PIN

Add pin to fields to show a debit card's PIN. The SDK asks Column only for the fields you list, so the PIN is returned only when you include pin, and a PIN-only reveal doesn't return the card number or CVV. Your server creates reveal tokens the same way either way. The card's program must have PIN reveal enabled. Verify the cardholder before you reveal their PIN.

Reveal pin on its own rather than alongside the card number. If the PIN can't be revealed, the whole reveal fails, so asking for it with other fields would hide those too. When it fails, reveal_failed carries Column's error code in apiCode:

apiCodeMeaning
pin_not_revealableThe PIN was set before PIN reveal was available. Set the PIN again to make it revealable.
feature_not_enabledPIN reveal isn't enabled for the card's program.
invalid_field_valueThe card has no PIN, or isn't a debit card.

These failures happen before the reveal token is used, so the same token still works for a reveal without pin.

Sandbox and live

LiveSandbox
DataReal cardsSimulated cards
RequestColumn directly: GET https://api.column.com/issuing/cards/{id}/reveal with Live-Mode: Yes, encrypted end to endThe same request with Live-Mode: No, also encrypted

Both render into the same CardRevealText components. In sandbox, sandboxBaseUrl can point at a local mock. Use http://127.0.0.1:<port> on the iOS simulator and http://10.0.2.2:<port> on the Android emulator.

Live reveal isn't available on React Native Web, where it throws invalid_options. On the web, use @column-na/card-reveal, which keeps card data in isolated iframes.

Versioning

This package follows semantic versioning and releases at the same version as the web SDK. Every request sends X-Column-SDK-Version: card-reveal-react-native/<version>.

The SDK sends no analytics.

License

Proprietary. Use is subject to the Column Card Reveal SDK License in LICENSE and requires an agreement with Column.

版本与发布记录

@column-na/card-reveal-react-native 共发布 14 个版本,首次发布于 2026/10/15;过去 12 个月发布了 14 个版本。

版本发布日期距今
0.13.0latest2026/10/3110 天前
0.12.02026/10/3011 天前
0.11.02026/10/3011 天前
0.10.02026/10/3011 天前
0.9.02026/10/2912 天前
0.8.02026/10/2912 天前

关于 @column-na/card-reveal-react-native 的常见问题

@column-na/card-reveal-react-native 是什么?

Show a Column card's number, CVV, expiry and cardholder name in your React Native app without the data touching your code.

如何安装 @column-na/card-reveal-react-native?

在项目目录运行 npm install @column-na/card-reveal-react-native,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=19.1.0)、react-native(>=0.81.4)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@column-na/card-reveal-react-native 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@column-na/card-reveal-react-native 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@column-na/card-reveal-react-native 可以商用吗?

@column-na/card-reveal-react-native 使用 SEE LICENSE IN LICENSE 许可。许可条款写在包内的独立文件中,请阅读该文件确认可否商用。以上为概括,不构成法律意见,请以许可原文为准。

@column-na/card-reveal-react-native 的下载量有多少?

根据 npm 官方下载统计,@column-na/card-reveal-react-native 在 2026/10/11 – 2026/11/09 这 30 天内被下载 2,093 次,日均约 70 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@column-na/card-reveal-react-native 还在维护吗?

最新版本 0.13.0 发布于 2026/10/31(10 天前);至今共发布 14 个版本,其中过去 12 个月发布了 14 个;首次发布于 2026/10/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。