A React Native library for integrating with Apple Wallet (iOS) and Google Wallet (Android), providing a unified API for adding passes to mobile wallets.
PKAddPassButton on iOS, the official Google Wallet button on Android) with localized branding.AddPassCompleted to learn the outcome of an add-pass flow.addPasses presents multiple .pkpass files in a single Apple Wallet sheet. Google Wallet accepts a single combined JWT per call; see Platform Differences.INVALID_PASS, ERR_WALLET_NOT_AVAILABLE, ERR_WALLET_UNKNOWN, etc.) shared across platforms; see Error Codes. User cancellation is reported by resolving addPass/addPasses with false, not by rejecting.npm install @azizuysal/wallet-kit
# or
yarn add @azizuysal/wallet-kit
@azizuysal/wallet-kit@^2 supports the last six React Native minors. Each cell in the matrix is exercised by the CI build matrix (iOS + Android, new arch by default, old-arch opt-out covered on 0.80/0.81).
| React Native | iOS floor | Android minSdk / compileSdk / targetSdk |
Kotlin | Arch |
|---|---|---|---|---|
| 0.85.x | 15.1 | 24 / 36 / 36 | 2.1.20 | New (required) |
| 0.84.x | 15.1 | 24 / 36 / 36 | 2.1.20 | New (required) |
| 0.83.x | 15.1 | 24 / 36 / 36 | 2.1.20 | New (required) |
| 0.82.x | 15.1 | 24 / 36 / 36 | 2.1.20 | New (required) |
| 0.81.x | 15.1 | 24 / 36 / 36 | 2.1.20 | New default, old arch opt-out supported |
| 0.80.x | 15.1 | 24 / 35 / 35 | 2.1.20 | New default, old arch opt-out supported |
React Native versions below 0.80 are not supported in 2.x. If you are on an older RN, pin to @azizuysal/wallet-kit@^1, which was the stabilization line focused on correctness fixes without the support-window tightening.
pod install in the ios directoryminSdkVersion is at least 24. @azizuysal/wallet-kit@^2 pins minSdk=24 to match the React Native 0.80+ floor.AndroidManifest.xml:
import WalletKit, {
WalletButton,
WalletButtonStyle,
createWalletEventEmitter,
type WalletError,
} from '@azizuysal/wallet-kit';
// Check if device can add passes
const canAddPasses = await WalletKit.canAddPasses();
if (canAddPasses) {
console.log('Device supports adding passes to wallet');
}
// Add a single pass
try {
// For iOS: pass base64-encoded .pkpass file
// For Android: pass JWT token string
const success = await WalletKit.addPass(passData);
if (success) {
console.log('Pass was added to the wallet');
} else {
console.log('User cancelled or the pass was already in the wallet');
}
} catch (error) {
const walletError = error as WalletError;
if (walletError.code === 'INVALID_PASS') {
console.error('Pass data was rejected:', walletError.message);
} else {
console.error('Failed to present wallet sheet:', walletError);
}
}
// Add multiple passes (iOS accepts any count; Android requires a single combined JWT)
try {
const success = await WalletKit.addPasses([pass1, pass2, pass3]);
if (success) {
console.log('All passes were added to the wallet');
}
} catch (error) {
const walletError = error as WalletError;
if (walletError.code === 'ERR_WALLET_MULTIPLE_NOT_SUPPORTED') {
// Android: combine your passes into a single JWT server-side and call addPass instead.
} else {
console.error('Failed to add passes:', walletError);
}
}
import { WalletButton, WalletButtonStyle } from '@azizuysal/wallet-kit';
function MyComponent() {
const handleAddPass = async () => {
try {
await WalletKit.addPass(passData);
} catch (error) {
console.error(error);
}
};
return (
<WalletButton
style={{ width: 200, height: 48 }}
addPassButtonStyle={WalletButtonStyle.primary}
onPress={handleAddPass}
/>
);
}
import { createWalletEventEmitter } from '@azizuysal/wallet-kit';
const eventEmitter = createWalletEventEmitter();
const subscription = eventEmitter.addListener(
'AddPassCompleted',
(success: boolean) => {
console.log('Pass added successfully:', success);
}
);
// Don't forget to remove the listener when done
subscription.remove();
For complete API documentation, see the API Reference.
All methods reject with an Error whose code property is one of:
INVALID_PASS — Pass data is missing, empty, or not in a recognized wallet pass format (neither a base64-encoded .pkpass nor a JWT). This can be raised either by the JS layer (before the native call) or by the native layer.UNSUPPORTED_VERSION — The pass version is not supported (iOS only).ERR_WALLET_NOT_AVAILABLE — The wallet app is not available on the device.ERR_WALLET_ACTIVITY_NULL — Android only: no activity was attached when the call was made.ERR_WALLET_MULTIPLE_NOT_SUPPORTED — Android only: addPasses was called with more than one entry. The Google Wallet API only accepts a single JWT per call; combine multiple passes into one JWT on your server.ERR_WALLET_IN_PROGRESS — Android only: another add-pass call is already in flight. Wait for it to resolve or reject before issuing another.ERR_WALLET_UNKNOWN — An unexpected error occurred.User cancellation is not reported as a Promise rejection on either platform. The addPass / addPasses promise resolves when the wallet sheet is presented; the final outcome (added vs. cancelled) is delivered via the AddPassCompleted event. See Listening to Events for the correct pattern.
iOS requires base64-encoded .pkpass files:
const passData = await RNFS.readFile('path/to/pass.pkpass', 'base64');
Android requires JWT tokens:
const passData = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...';
The native buttons follow platform-specific design guidelines:
The 2.0 release narrows the supported React Native range and changes the shape of the add-pass API. The list below covers every breaking change and the mechanical fix for each.
addPass / addPasses return Promise<boolean>1.x resolved with void as soon as the wallet sheet was presented, and delivered the outcome on a separate AddPassCompleted event. 2.x resolves the promise with the outcome directly: true if every pass was newly added, false on cancel or if a pass was already in the library.
// 1.x
await WalletKit.addPass(passData);
emitter.addListener('AddPassCompleted', (success) => {
/* handle outcome */
});
// 2.x
const success = await WalletKit.addPass(passData);
The AddPassCompleted event still fires as a secondary notification channel for multi-listener scenarios; you do not have to migrate listeners. The Promise return value is the primary API in 2.x.
ERR_WALLET_CANCELLED was removedNeither platform ever rejected with this code in 1.x; it was a type-system phantom. 2.x removes it. If you had a catch branch keyed on ERR_WALLET_CANCELLED, delete it — cancellation now resolves the promise with false.
// 1.x — dead branch, never triggered
try {
await WalletKit.addPass(passData);
} catch (error) {
if (error.code === 'ERR_WALLET_CANCELLED') {
/* never reached */
}
}
// 2.x
const success = await WalletKit.addPass(passData);
if (!success) {
/* cancelled or already-present */
}
ERR_WALLET_IN_PROGRESS is now cross-platformIn 1.x this code was Android-only. 2.x also emits it on iOS when a concurrent addPass/addPasses is issued while a previous call is still awaiting the delegate callback. If your iOS code previously relied on fire-and-forget concurrent calls, serialize them instead.
peerDependencies changed from "*" to ">=0.80.0" for react-native and ">=19.1.0" for react. If your app is on React Native below 0.80, stay on @azizuysal/wallet-kit@^1.
minSdkVersion must be at least 24 in your app (android/build.gradle). This was already required transitively by RN 0.76+; 2.x makes it explicit.
wallet-kit.podspec declares :ios => "15.1". Your app's iOS deployment target must be 15.1 or higher. This matches the React Native 0.80–0.85 floor.
The library is now a TurboModule (JS-to-native) and a Fabric component (WalletButton). On RN 0.82+ (New Arch only) it runs natively. On RN 0.80/0.81 with old-arch opted in, the bridge code paths are retained behind compile-time flags and continue to work. No consumer action required.
"The package doesn't seem to be linked"
cd ios && pod install"Can't add passes"
"Google Wallet is not available"
"Activity is null"
The example app includes sample .pkpass files in example/ios/Samples/ directory. These are automatically loaded when running the iOS example app.
To test the Android implementation, you will need to generate a signed JWT. For detailed instructions on how to generate a test JWT, please see the JWT Generation Guide.
Check the example directory for a complete working example with both iOS and Android implementations.
cd example
yarn install
cd ios && pod install && cd ..
yarn ios # or yarn android
Found a security vulnerability? Please refer to our security policy for reporting procedures.
See the contributing guide to learn how to contribute to the repository and the development workflow.
This package uses automated releases via GitHub Actions. See RELEASING.md for details on the release process.
MIT