Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ The library offers functions for seamless integration and use of the Apple Walle
| **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | ✅ |
| **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | ✅ |
| **listAppleWalletPasses** | Lists Apple Wallet Secure Element passes available to the app, including activation state and whether the pass is remote (for example on Apple Watch). Filter the result for `activationState === 'requireActivation'` or `activationState === 'pending'` to build a pending-cards UI. | None | `IOSWalletPass[]` | ✅ | ❌ |
| **presentAppleWalletPass** | Opens the installed iPhone pass for these last digits, preferring one that still needs verification. Returns `false` when `lastDigits` is empty, when no pass matches, when more than one card ends in those digits, and on Android. | `lastDigits: string` | `boolean` | ✅ | ✅ |
| **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. When `IOSCardData.primaryAccountIdentifier` is set, the device picker only lists devices that do not already have the card. If PassKit cannot present the flow because the card is already on every device, the call fails with a clear error. If the issuer callback throws, the Add Payment Pass sheet is closed and the promise rejects with that error. | `data`: `IOSCardData`,<br>`issuerEncrypt-`<br>`PayloadCallback: IOSIssuerCallback` | `void` | ✅ | ❌ |
| **canAddCardToAppleWallet** | Checks whether the card can still be added to the iPhone or a paired Apple Watch. Pass `lastDigits` to use the same id lookup as `addCardToAppleWallet`. Returns `false` on Android. | `primaryAccountIdentifier: string`,<br>`lastDigits?: string` | `boolean` | ✅ | ✅ |
| **activateAppleWalletPass** | Activates an existing Secure Element pass in Apple Wallet using activation data from your issuer backend. Resolves `true` when the pass is already active. For `requireActivation`, returns Apple's result, which can be `false`. Rejects for any other state (`pending`, `suspended`), a missing pass, unavailable activation, or invalid or non-base64 activation data. | `data`: `IOSPassActivationData` | `boolean` | ✅ | ❌ |
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/java/com/expensify/wallet/WalletModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,10 @@ class WalletModule internal constructor(context: ReactApplicationContext) :
.emit(eventName, params)
}

override fun presentAppleWalletPass(lastDigits: String, promise: Promise?) {
promise?.resolve(false)
}

override fun IOSPresentAddPaymentPassView(cardData: ReadableMap?, promise: Promise?) {
// no-op
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public NativeWalletSpec(ReactApplicationContext reactContext) {
@DoNotStrip
public abstract void listAppleWalletPasses(Promise promise);

@ReactMethod
@DoNotStrip
public abstract void presentAppleWalletPass(String lastDigits, Promise promise);

@ReactMethod
@DoNotStrip
public abstract void IOSPresentAddPaymentPassView(ReadableMap cardData, Promise promise);
Expand Down
10 changes: 10 additions & 0 deletions ios/RNWallet.mm
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ - (instancetype)init {
resolve([walletManager getCardStatusBySuffixWithLast4Digits:last4Digits]);
}

RCT_REMAP_METHOD(presentAppleWalletPass,
presentAppleWalletPass:(NSString *)lastDigits
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
dispatch_async(dispatch_get_main_queue(), ^{
resolve(@([self->walletManager presentAppleWalletPassWithLastDigits:lastDigits]));
});
}

RCT_REMAP_METHOD(getCardStatusByIdentifier,
getCardStatusByIdentifier:(NSString *)identifier
tsp:(NSString *)tsp
Expand Down
21 changes: 21 additions & 0 deletions ios/WalletManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,27 @@ open class WalletManager: UIViewController {
}
}

@objc public func presentAppleWalletPass(lastDigits: NSString) -> Bool {
let suffix = lastDigits as String
guard !suffix.isEmpty else {
return false
}

let candidates = passLibrary.passes(of: .secureElement)
.filter { !$0.isRemotePass }
.compactMap(\.secureElementPass)
.filter {
$0.primaryAccountNumberSuffix.hasSuffix(suffix) &&
$0.passActivationState != .deactivated
}
guard Set(candidates.map(\.primaryAccountIdentifier)).count == 1,
let pass = candidates.first(where: { $0.passActivationState == .requiresActivation }) ?? candidates.first else {
return false
}
passLibrary.present(pass)
return true
}

@objc public func canAddCardToAppleWallet(primaryAccountIdentifier: NSString, lastDigits: NSString) -> Bool {
let suffix = lastDigits as String
let resolved = suffix.isEmpty
Expand Down
1 change: 1 addition & 0 deletions src/NativeWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export interface Spec extends TurboModule {
resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise<number>;
listTokens(): Promise<TokenInfo[]>;
listAppleWalletPasses(): Promise<IOSWalletPassNative[]>;
presentAppleWalletPass(lastDigits: string): Promise<boolean>;
IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise<IOSAddPaymentPassData>;
IOSHandleAddPaymentPassResponse(payload: IOSEncryptPayload): Promise<IOSAddPaymentPassData | null>;
IOSFailAddPaymentPass(): Promise<void>;
Expand Down
13 changes: 13 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,18 @@ async function activateAppleWalletPass(passData: IOSPassActivationData): Promise
return Wallet.activateAppleWalletPass(passData);
}

async function presentAppleWalletPass(lastDigits: string): Promise<boolean> {
if (Platform.OS === 'android') {
return false;
}

if (!Wallet) {
return getModuleLinkingRejection();
}

return Wallet.presentAppleWalletPass(lastDigits);
}

export type {
AndroidCardData,
AndroidWalletData,
Expand Down Expand Up @@ -306,6 +318,7 @@ export {
resumeAddCardToGoogleWallet,
listTokens,
listAppleWalletPasses,
presentAppleWalletPass,
canAddCardToAppleWallet,
addCardToAppleWallet,
activateAppleWalletPass,
Expand Down
Loading