Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
aefcdb5
initial commit for expo plugin
wcastand Oct 3, 2025
59a3ee6
remove unused image assets
wcastand Oct 3, 2025
7987ce7
rebase
wcastand Oct 6, 2025
3f66f5e
added example expo back
wcastand Oct 6, 2025
8b32f09
add libs to make sure it's not commited when testing
wcastand Oct 6, 2025
fc5ff6a
Update node and permissions to allow publishing via OIDC
AndrewGable Oct 31, 2025
cdea6d5
Update package.json version to 0.1.14
os-botify[bot] Nov 3, 2025
44114b0
Update README.md (#55)
zfurtak Jan 13, 2026
1118a95
Update package.json version to 0.1.15
wcastand Jan 13, 2026
538d14e
update pacakge.json
wcastand Jan 22, 2026
e8eebe6
Address PR review feedback
wcastand Jan 22, 2026
815bd6b
trying to fix stuff
Skalakid Jan 26, 2026
1a851ea
Revert package.json changes
Skalakid Jan 26, 2026
5a70a34
Fix expo example app
Skalakid Jan 26, 2026
9079eca
Move example app to apps folder
Skalakid Feb 10, 2026
12757d0
Add common app
Skalakid Feb 10, 2026
7456487
Move expo app to apps folder
Skalakid Feb 10, 2026
5508dab
Rename expo example
Skalakid Feb 11, 2026
ce17a6e
Fix plugin name
Skalakid Feb 11, 2026
67e390e
Clear app.json
Skalakid Feb 11, 2026
04f2d56
Remove hoisting expo
Skalakid Feb 11, 2026
5869131
Fix android
Skalakid Feb 11, 2026
6266a3d
Fix lint
Skalakid Feb 11, 2026
8b7b35e
Update TS check
Skalakid Feb 11, 2026
7f6f19a
Merge branch 'main' into main
Skalakid Feb 11, 2026
ac466d2
Remove unnecessary changes
Skalakid Feb 11, 2026
5396d07
Fix SafeAreaView issues
Skalakid Feb 11, 2026
565b148
Remove unnecessary assets
Skalakid Feb 11, 2026
15a5e05
Update basic example readme
Skalakid Feb 11, 2026
a9c3459
Update expo example readme
Skalakid Feb 11, 2026
6ee5160
Update scripts
Skalakid Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

# Output of the build process & scripts
lib/**/*
plugin/build/**/*
scripts/**/*

babel.config.js
Expand All @@ -14,3 +15,4 @@ react-native.config.js
jest.config.js
webpack.config.js
.eslintrc.js
app.plugin.js
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
},
},
root: true,
ignorePatterns: ['plugins/build/**'],
rules: {
'rulesdir/prefer-underscore-method': 'off',
'rulesdir/prefer-import-module-contents': 'off',
Expand Down Expand Up @@ -66,5 +67,11 @@ module.exports = {
'@typescript-eslint/array-type': ['error', {default: 'array-simple'}],
'@typescript-eslint/consistent-type-definitions': 'off',
'curly': ['error', 'all'],
'@lwc/lwc/no-async-await': 'off',
'no-return-await': 'off',
'no-else-return': 'off',
'import/prefer-default-export': 'off',
'no-restricted-imports': 'off',
'react/destructuring-assignment': 'off',
},
};
4 changes: 2 additions & 2 deletions .github/workflows/typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ jobs:
- name: Typecheck library
run: npm run typecheck -- --project tsconfig.json

- name: Typecheck example app
run: npm run typecheck -- --project example/tsconfig.json
- name: Typecheck common app
run: npm run typecheck -- --project apps/common-app/tsconfig.json
10 changes: 7 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ android.iml

# Cocoapods
#
example/ios/Pods
apps/basic-example/ios/Pods

# Ruby
example/vendor/
apps/basic-example/vendor/

# node.js
#
Expand All @@ -66,6 +66,10 @@ ios/generated
android/generated

# Android libs (Google Tap And Pay SDK)
example/android/libs
apps/basic-example/android/libs

.kotlin/

# expo plugin build
plugin/build/
*.tsbuildinfo
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node-linker=hoisted
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CODE_OF_CONDUCT.md
.well-known
desktop/dist/**/*.js
dist/**/*.js
plugin/build/**/*.js
assets/animations
android
ios
Expand Down
92 changes: 78 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
- ♻️ Universal solution (Apple Wallet and Google Wallet)
- 💳 Easy and secure addition of payment cards from your app directly
- 💰 Supports Visa, MasterCard, Amex and Discover payment cards
- 🎉 Expo support (expo-plugin)

## Getting started

Using the `react-native-wallet` Push Provisioning features requires proper configuration and access from both Google and Apple.
Using the `react-native-wallet` Push Provisioning features requires proper configuration and access from both Google and Apple.
Please follow the instructions below before installing the library:

### Android
Expand All @@ -20,10 +21,10 @@ To be able to interact with the Google Wallet on the Android please make sure to
1. Visit [the Google Pay Android Push Provisioning API documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/) and request access to it.

2. Once getting an approval from the Google team
1. Download the [TapAndPay SDK](https://developers.google.com/pay/issuers/apis/push-provisioning/android/releases).
2. Unzip it and extract the SDK into the `/android/libs` folder in your React Native project (if there is no `libs` folder, create one).
1. Download the [TapAndPay SDK](https://developers.google.com/pay/issuers/apis/push-provisioning/android/releases).
2. Unzip it and extract the SDK into the `/android/libs` folder in your React Native project (if there is no `libs` folder, create one).
3. Add `/android/libs` to `.gitignore`.

3. Then connect the SDK to your project in `build.gradle`, for example like in [example/android/build.gradle](https://github.com/Expensify/react-native-wallet/blob/main/example/android/build.gradle):

```groovy
Expand All @@ -37,7 +38,7 @@ allprojects {

#### Step 2: Whitelist your app for SDK use.

To use the Google SDK in your app, you will need to whitelist your app details. Without it, calling some functions will result in a `Not verified` error.
To use the Google SDK in your app, you will need to whitelist your app details. Without it, calling some functions will result in a `Not verified` error.
To resolve it, please follow the instructions from [the official Google documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/allowlist).

For your builds, you will need to prepare your app's `package name` and `fingerprint` following [these steps](https://developers.google.com/pay/issuers/apis/push-provisioning/android/allowlist#how_to_get_your_apps_fingerprint). The values should, for example, look like this:
Expand Down Expand Up @@ -72,13 +73,13 @@ Make sure to familiarize yourself with that document before deploying your app.

### Step 2: Activate the entitlement

After getting a positive response from Apple, open the developer portal panel and search `Certificates, Identifiers & Profiles` -> `Profiles` -> `Our distribution profile` -> `Edit` and add the `ApplePay In-App Provisioning Distribution` entitlement.
After getting a positive response from Apple, open the developer portal panel and search `Certificates, Identifiers & Profiles` -> `Profiles` -> `Our distribution profile` -> `Edit` and add the `ApplePay In-App Provisioning Distribution` entitlement.
It’s available only for the production environment so your QA must work with physical devices and cards.


### Step 3: Add the entitlement to your project

Add `com.apple.developer.payment-pass-provisioning` entitlement to your project. Find or create `.entitlements` file in your project and add the entitlement like below (similarly to [WalletExample.entitlements](https://github.com/Expensify/react-native-wallet/blob/main/example/ios/WalletExample/WalletExample.entitlements)):
Add `com.apple.developer.payment-pass-provisioning` entitlement to your project. Find or create `.entitlements` file in your project and add the entitlement like below (similarly to [WalletExample.entitlements](https://github.com/Expensify/react-native-wallet/blob/main/example/ios/WalletExample/WalletExample.entitlements)):

```xml
<?xml version="1.0" encoding="UTF-8"?>
Expand All @@ -105,14 +106,44 @@ or
yarn add @expensify/react-native-wallet
```

### Expo plugin

To use `@expensify/react-native-wallet` with expo, you will need to use the expo-plugin.

In your `app.json` file, add the following configuration:

```json
{
"expo": {
"plugins": [
[
"@expensify/react-native-wallet",
{
enableApplePayProvisioning: true,
googleTapAndPaySdkPath: "./libs/tapandpay-v18.7.0.zip", // path to the Google Tap & Pay SDK zip file
},
],
]
}
}
```

You will need to rebuild your app after that since the lib has native files.

```bash
npx expo prebuild --clean
npx expo run:ios
npx expo run:android
```

## Required data


## Required data
Here you can find data elements used in the library, essential to work with Google Wallet and Apple Wallet APIs.

### Android
- **Opaque Payment Card** (OPC) - a binary blob of information Google Pay receives from the issuer app that could be presented to TSP to receive a token.
- **Token Service Provider** (TSP) - a service that enhances payment security by replacing a credit card number during transactions with a unique digital identifier - token. The TSP specifies the tokenization service used to create a given token e.g. Visa, MasterCard, American Express.
- **Token Service Provider** (TSP) - a service that enhances payment security by replacing a credit card number during transactions with a unique digital identifier - token. The TSP specifies the tokenization service used to create a given token e.g. Visa, MasterCard, American Express.
- **Token Reference ID** - a unique identifying number to refer to a DPAN (Dynamic Personal Account Number). Token Providers will assign each DPAN an issuer token ID at the time of tokenization.

### iOS
Expand Down Expand Up @@ -170,10 +201,10 @@ The library offers seven functions for seamless integration and use of the Apple
### `AddToWalletButton`

A ready-to-use component that simplifies the addition of payment cards to Google Wallet and Apple Wallet. The button automatically adapts its appearance according to the platform and language specified.
It uses official assets provided by [Google](https://developers.google.com/wallet/generic/resources/brand-guidelines) and [Apple](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/) in their Wallet-related branding guidelines.
It uses official assets provided by [Google](https://developers.google.com/wallet/generic/resources/brand-guidelines) and [Apple](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/) in their Wallet-related branding guidelines.

> [!IMPORTANT]
> Please bear in mind the brand rules provided by [Google](https://developers.google.com/wallet/generic/resources/brand-guidelines) and [Apple](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/) when adding this component to your application.
> Please bear in mind the brand rules provided by [Google](https://developers.google.com/wallet/generic/resources/brand-guidelines) and [Apple](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/) when adding this component to your application.

Adhering to these guidelines is crucial not only to comply with legal requirements but also to reassure users of the authenticity and security of your application.

Expand Down Expand Up @@ -204,6 +235,40 @@ Adhering to these guidelines is crucial not only to comply with legal requiremen
|:----:|:---------------:|
| <img src="./assets/buttons/android_button.svg" height="50" /> | <img src="./assets/buttons/ios_button.svg" height="50" /> |

# Hex Encoding

This library use base64 encoding to encode the data. Yours might use hex encoding.

If that is the case, you will need to convert the data in the proper format.

This is an example of how to convert data to hex format:

```ts
import { Buffer } from "buffer"
const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").toString("hex")

[...]

const issuerEncryptPayloadCallback = async (
nonce: string,
nonceSignature: string,
certificate: string[],
): Promise<IOSEncryptPayload> => {
try {
const payload = {
encryptionDetails: {
nonce: base64ToHex(nonce),
nonceSignature: base64ToHex(nonceSignature),
wspCertificates: certificate,
},
serialNumber,
wspId: "APPLE" as const,
}
[...]
```

This will depend on the implementation of your backend/issuer so make sure to check with them otherwise IOS might fail to provision the card.

# Publishing your app

To successfully publish your app, you will need to navigate through a series of mandatory test cases on both platforms.
Expand All @@ -214,14 +279,14 @@ Before deploying your app to the Google Play Store, make sure you have taken car

The latest information about deploying apps with Google TapAndPay SDK can be found in the [pre-launch process](https://developers.google.com/pay/issuers/apis/push-provisioning/android/launch-process#step_3_issuer_app_product_review) and [beta tests](https://developers.google.com/pay/issuers/apis/push-provisioning/android/beta-testing) sections in Google documentation. Make sure to complete all of the steps specified by Google connected to the __Google's branding__, __API safety__, and __app stability__.

The app will need to be reviewed by Google. During this process, it will need to pass 4 mandatory test cases that are specified [here](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases). They verify how your app handles card state tracking in different scenarios.
The app will need to be reviewed by Google. During this process, it will need to pass 4 mandatory test cases that are specified [here](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases). They verify how your app handles card state tracking in different scenarios.

> [!NOTE]
>Please make sure to hide the `Add to Google Wallet` buttons when cards are already added to the wallet.

### iOS

When implementing the In-App Push Provisioning feature in your App make sure that your app follows Apple's [branding guidelines connected to Apple Wallet](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/). Remember that you must not create your own buttons or your app could be rejected at revision. You can use the[ AddWalletButton component](#components) instead!
When implementing the In-App Push Provisioning feature in your App make sure that your app follows Apple's [branding guidelines connected to Apple Wallet](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/). Remember that you must not create your own buttons or your app could be rejected at revision. You can use the[ AddWalletButton component](#components) instead!
When the pass is already provisioned, make sure to hide this button and replace it with text like `Added to Apple Wallet`. The card is fully provisioned once it added to your main device (user's iPhone) and all linked devices (for example Apple Watch).

Next to branding guidelines, please follow the instructions and best practices from [the In-App provisioning documentation]((#ios)) provided by Apple.
Expand All @@ -246,7 +311,6 @@ Additionally, when submitting your app to the App Store, you must include:

`@expensify/react-native-wallet` is compatible with the five latest minor releases of React Native (≥0.76.0) and works exclusively with the new architecture.


# Contributing

Contributions to this library are done under [https://github.com/Expensify/App](https://github.com/Expensify/App). Please refer to that repo and all its guidelines for contributing.
Expand Down
2 changes: 2 additions & 0 deletions app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// This file configures the entry file for your plugin.
module.exports = require('./plugins/build');
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
136 changes: 136 additions & 0 deletions apps/basic-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Basic Example App

Example React Native application demonstrating the `@expensify/react-native-wallet` library with Apple Pay and Google Pay integration.

## Prerequisites

- Node.js 18+
- React Native development environment ([Setup Guide](https://reactnative.dev/docs/environment-setup))
- For Android: Google TapAndPay SDK (see setup below)
- For iOS: Xcode and CocoaPods

## Setup

### 1. Install dependencies

From the repository root:

```bash
npm install
```

### 2. Configure Google TapAndPay SDK (Android only)

The Google TapAndPay SDK is required for Android builds. Extract and place the SDK files in the Android libs directory:

```bash
# Create the libs directory if it doesn't exist
mkdir -p android/libs
```

Extract the SDK ZIP file contents into `android/libs/`
The extracted files should be directly in` android/libs/`, not in a subdirectory
Example structure:

```
android/libs/com/google/android/gms/play-services-tapandpay/[version]/...
```

**Important**: Place the **extracted contents** of the ZIP file into `android/libs/`, not the ZIP file itself. The Gradle build expects a Maven repository structure.

### 3. Install iOS dependencies

```bash
cd ios
pod install
cd ..
```

## Running the App

### Start Metro Bundler

```bash
npm start
```

### iOS

```bash
npm run ios
```

Or open in Xcode:
```bash
open ios/WalletExample.xcworkspace
```

### Android

```bash
npm run android
```

## Development

### Reload the app

- **iOS**: Press <kbd>Cmd ⌘</kbd> + <kbd>R</kbd> in the simulator
- **Android**: Press <kbd>R</kbd> twice or <kbd>Ctrl/Cmd</kbd> + <kbd>M</kbd> to open Developer Menu

### Clean build

```bash
# iOS
npm run clean-ios
npm run pods

# Android
npm run clean-android
```

## Project Structure

- `index.ts` - Entry point
- `app.json` - React Native configuration
- `android/libs/` - Google TapAndPay SDK location
- `../common-app/` - Shared app code used by both example apps

## Troubleshooting

### SDK not found (Android)

If you get errors about missing TapAndPay SDK:
1. Verify the SDK files are extracted into `android/libs/` directory
2. The structure should be: `android/libs/com/google/android/gms/play-services-tapandpay/...`
3. Do NOT place the .zip file directly - extract it first
4. Clean and rebuild: `cd android && ./gradlew clean && cd ..`

### Metro bundler issues

Clear cache and restart:
```bash
npm start -- --reset-cache
```

### iOS build issues

```bash
cd ios
pod deintegrate
pod install
cd ..
```

### Android build issues

```bash
cd android
./gradlew clean
cd ..
```

## Learn More

- [@expensify/react-native-wallet](../../README.md) - Main library documentation

Loading
Loading