Skip to content

ArlanBiati/tap-to-pay-react-native-demo

Repository files navigation

Tap To Pay Mock with Stripe


home home

Description

An application created using React Native and Expo with the aim of creating a sample app with Tap to Pay functionality using stripe-terminal-react-native.


Necessary Configurations

  • Expo SDK 49+
  • React Native 0.72+

Prerequisites

  • Stripe Account (Development) to use API tokens
  • Expo Account to generate the build via EAS

Getting Started

  • To use the mock, you need to download this repository and build it via EAS using the command:

    ❯ eas build --profile development

After that, EAS will take care of creating the build and installing it on your emulator (Android/iOS).

  • With the app installed on the simulator, you need to start the project with the command:

    ❯ npm run start

    and also the local server created with json-server

    ❯ npm run server

With these two commands running, our project is up and ready for testing.


Variable Configuration

Stripe uses public keys to make the front-back connection, and it is necessary to change two of them for things to work:

  • Public key to connect to the readers: /src/db/db.json
  • LocationId to identify the physical company: /src/stripe-page.tsx

Both keys can be created or updated using the Stripe CLI. Below is the code used:

Example commands:

  • To create the location:

    Screenshot 2024-05-14 at 19 44 05
  • To create the public token:

    Screenshot 2024-05-14 at 19 44 30

Understanding the Code

Patterns

  • As a standard for the mock here, I decided to wrap all requests inside trycatch and also add alerts for successful cases. For error cases, I will only return a console.log.

1. DiscoverReaders

useEffect(() => {
  discoverReaders({
    discoveryMethod: 'localMobile',
    simulated: true,
  })
}, [discoverReaders])

This useEffect has a function from stripe-terminal itself that is responsible for locating available readers. After locating them, we move on to the second step.


2. ConnectReader

async function connectReader(selectedReader: any) {
  setLoadingConnectingReader(true)
  try {
    const { reader, error } = await connectLocalMobileReader({
      reader: selectedReader,
      locationId: locationIdStripeMock,
    })

    if (error) {
      console.log('connectLocalMobileReader error:', error)
      return
    }

    Alert.alert('Reader connected successfully')

    console.log('Reader connected successfully', reader)
  } catch (error) {
    console.log(error)
  } finally {
    setLoadingConnectingReader(false)
  }
}

Function used to connect to the reader found earlier. In it, we can destructure the reader and the error, which can be handled if it occurs. It is also at this step that we use LocationId to identify which establishment that reader belongs to.


3. PaymentIntent

async function paymentIntent() {
  setLoadingCreatePayment(true)
  try {
    const { error, paymentIntent } = await createPaymentIntent({
      amount: 100,
      currency: 'usd',
      paymentMethodTypes: ['card_present'],
      offlineBehavior: 'prefer_online',
    })

    if (error) {
      console.log('Error creating payment intent', error)
      return
    }

    setPayment(paymentIntent)

    Alert.alert('Payment intent created successfully')
  } catch (error) {
    console.log(error)
  } finally {
    setLoadingCreatePayment(false)
  }
}

In this function, we create the payment intent responsible for capturing the amount, currency type, payment methods (card, virtual card, etc.), and so on. Just like in the previous function, we can destructure the request and obtain the payment intent data or the error, if it occurs.

*** The amount always equals its full value, without dots. Example: U$ 1.00 = 100; U$ 10.00 = 1000;


4. CollectPayment

async function collectPayment() {
  setLoadingCollectPayment(true)
  try {
    const { error, paymentIntent } = await collectPaymentMethod({
      paymentIntent: payment,
    })

    if (error) {
      console.log('Error collecting payment', error)
      return
    }

    Alert.alert('Payment successfully collected', '', [
      {
        text: 'Ok',
        onPress: async () => {
          await confirmPayment(paymentIntent)
        },
      },
    ])
  } catch (error) {
    console.log(error)
  } finally {
    setLoadingCollectPayment(false)
  }
}

In this function, we collect the payment intent created earlier. It is here that we open the Tap to Pay functionality for the user to touch the payment method and make the payment.


5. ConfirmPayment

async function confirmPayment(payment: any) {
  setLoadingConfirmPayment(true)
  try {
    const { error, paymentIntent } = await confirmPaymentIntent(payment)

    if (error) {
      console.log('Error confirm payment', error)
      return
    }

    Alert.alert('Payment successfully confirmed!', 'Congratulations')
    console.log('Payment confirmed', paymentIntent)
  } catch (error) {
    console.log(error)
  } finally {
    setLoadingConfirmPayment(false)
  }
}

Here, we confirm all previous steps and finalize the sales process.


See the app in action!

Now you can observe how the app behaves in real time!


home


Documentations

Here are the documentations that helped me create the mock and my production app:


License

This project is licensed. Please see the license file for more details.


Development by

Arlan Gustavo Biati

@ArlanBiati

[email protected]

ArlanBiati

About

A demo app of Tap to Pay with Stripe on React Native/Expo

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published