n1coePayDocs API reference

React Native SDK

@n1co/react-native takes card payments in React Native and Expo apps: card field, security code, 3D Secure and saved cards.

  1. Your server creates the payment intent with your secret key, without page_origins, and gives only its client_secret to the app.
  2. The app shows a CardField and calls confirmPayment({ client_secret, card }).
  3. Your server fulfills the order on the payment_intent.succeeded webhook, never on what the app reports.

The card is never typed into your app. Each field is a WebView showing n1co's page, whose frames send the card straight to n1co. It is JavaScript only: it works in Expo Go.

Install#

Expo

# Expo SDK 57 or 58: Expo Go, development builds, Expo Router
npx expo install react-native-webview expo-crypto react-native-get-random-values
npm install @n1co/react-native

React Native

# React Native 0.86 or later
npm install @n1co/react-native react-native-webview react-native-get-random-values
cd ios && pod install

The first line of your app entry (with Expo Router, app/_layout.tsx) is the crypto.getRandomValues polyfill. Without it, N1coProvider throws crypto_unavailable.

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

Pay with a new card#

app/_layout.tsx
import 'react-native-get-random-values';
import { useRef, useState } from 'react';
import { Button } from 'react-native';
import { CardField, N1coProvider, useN1co, type CardFieldHandle } from '@n1co/react-native';

// The Provider goes once, in the root layout.
export default function RootLayout() {
  return (
    <N1coProvider publishableKey="pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA" host="https://elements.n1co.dev" locale="en">
      <Checkout />
    </N1coProvider>
  );
}

function Checkout() {
  const card = useRef<CardFieldHandle>(null);
  const [complete, setComplete] = useState(false);
  const { confirmPayment } = useN1co();

  async function pay() {
    const client_secret = await createIntentOnYourServer(); // your endpoint
    try {
      const intent = await confirmPayment({ client_secret, card: card.current });
      if (intent.status === 'succeeded') showDone();
      else if (intent.status === 'processing') showPending();
      else showError(intent.last_error?.message); // declined or 3D Secure failed: retry
    } catch (error) {
      showError((error as Error).message); // N1coError: branch on error.code
    }
  }

  return (
    <>
      <CardField ref={card} onChange={(event) => setComplete(event.complete)} />
      <Button title="Pay" disabled={!complete} onPress={pay} />
    </>
  );
}

Without host, the Provider uses the production host, which arrives with the production release. confirmPayment resolves with the intent in a final state, as on the web.

Saved cards#

If your server attached the card with payment_method, confirm with confirmPayment({ client_secret }). With cvc_required: true, ask for the code with a CvcField:

const cvc = useRef<CvcFieldHandle>(null);
<CvcField ref={cvc} options={{ brand: 'visa', last4: '4242' }} />;
await confirmPayment({ client_secret, cvc: cvc.current });

To save a card, use a card session's client_secret:

const { card: saved } = await card.current!.capture({ client_secret });
// saved: { id: 'card_…', brand, last4, exp_month, exp_year }

In update mode, <CardField options={{ update: { brand: 'visa', last4: '4242' } }} /> renews the expiry and code without asking for the number.

3D Secure#

When the bank asks for it, the package shows its page in a modal over your app: a page sheet on iOS, full screen on Android. Android's back button closes it; the attempt fails and can be retried.

Put the Provider in the root layout, not inside a presentation: 'modal' screen.

Common mistakes#

  • Sending page_origins. Omit it on intents and sessions used from an app: the page that pays is n1co's, not yours.
  • Paying twice. See the warning below.
  • Jest. The package ships ES modules: add it to transformIgnorePatterns, for example node_modules/(?!((jest-)?react-native|@react-native|@n1co)/).

The package locks the WebView settings (navigation only to n1co and the bank, no injected scripts, no persistent state) and accepts no WebView props.

The fields, their events and every error code are in the package README.