Project Awesome project awesome

Stripe terminal

Terminal plugin for in-person payment processing.

Package 30 stars GitHub


Capacitor Stripe Terminal

capacitor-stripe-terminal

Capacitor plugin for Stripe Terminal (unofficial)


Requirements

  • Capacitor: 8.x
  • Stripe Terminal SDK:
    • iOS: 5.3.0 (requires iOS 15.0+)
    • Android: 5.3.0 (requires Android API 23+)

📝 Upgrading from an earlier version? See the Migration Guide for details on breaking changes and upgrade instructions.

Maintainers

Maintainer GitHub Social
Noah Prail nprail @NoahPrail

Installation

Using npm:

npm install capacitor-stripe-terminal

Using yarn:

yarn add capacitor-stripe-terminal

Sync native files:

npx cap sync

Configuration

iOS

Follow all Stripe instructions under "Configure your app".

Android

Add the ACCESS_FINE_LOCATION permission to your app's manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.stripe.example.app">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

On Android, you must also make sure that Location permission has been granted by the user:

if (Capacitor.getPlatform() === 'android') {
  // check if permission is required
  let response = await StripeTerminalPlugin.checkPermissions();

  if (response.location === 'prompt') {
    // if it is required, request it
    response = await StripeTerminalPlugin.requestPermissions();

    if (response.location !== 'granted') {
      // if the request fails, show a message to the user
      throw new Error('Location permission is required.')
    }
  }
}

const terminal = await StripeTerminalPlugin.create({ ... })

If the user does not grant permission, StripeTerminalPlugin will throw an error when you try to initialize it so you will have to handle that.

Hint: If the user denies Location permission the first time you ask for it, Android will not display a prompt to the user on subsequent requests for permission and response will always be denied. You will have to ask the user to go into the app's settings to allow Location permission.

Usage

import {
  StripeTerminalPlugin,
  StripeTerminalError,
  DiscoveryMethod,
} from 'capacitor-stripe-terminal'

// First, initialize the SDK
const terminal = await StripeTerminalPlugin.create({
  fetchConnectionToken: async () => {
    const resp = await fetch('https://your-backend.dev/token', {
      method: 'POST',
    })
    const data = await resp.json()

    return data.secret
  },
  onUnexpectedReaderDisconnect: () => {
    // handle reader disconnect
  },
})

// Start scanning for readers
// To stop scanning, call handle.remove() on the returned handle.
// You must connect to a reader while scanning
const discoverHandle = await terminal.discoverReaders(
  {
    simulated: false,
    discoveryMethod: DiscoveryMethod.BluetoothScan,
  },
  (readers) => {
    if (readers.length) {
      const selectedReader = readers[0]
      const connectionConfig = {
        locationId: '{{LOCATION_ID}}',
      }
      terminal
        .connectBluetoothReader(selectedReader, connectionConfig)
        .then((connectedReader) => {
          // the reader is now connected and usable
        })
    }
  },
  (error) => {
    console.error('discoverReaders error', error)
  },
)

// Once the reader is connected, collect a payment intent!

// listen to user instructions - these should be displayed to the user
const displayHandle = await terminal.didRequestReaderDisplayMessage(
  (displayMessage) => {
    console.log('displayMessage', displayMessage)
  },
)
const inputHandle = await terminal.didRequestReaderInput((inputOptions) => {
  console.log('inputOptions', inputOptions)
})

// retrieve the payment intent
await terminal.retrievePaymentIntent('your client secret created server side')

// collect the payment method
await terminal.collectPaymentMethod()

// and finally, confirm the payment intent
try {
  await terminal.confirmPaymentIntent()
} catch (err) {
  if (err instanceof StripeTerminalError) {
    // structured decline data is available on card declines
    console.error('decline_code', err.decline_code)
  }
  throw err
}

// once you are done, call destroy() to remove all listeners and reset the plugin (e.g. in ngOnDestroy)
await terminal.destroy()

API Reference

See the full API docs here.

Sponsors

Acknowledgements

Back to Capacitor