✨ app: wallet provisioning#956
Conversation
🦋 Changeset detectedLatest commit: dda713e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds Meawallet wallet provisioning: new SDK dependency and pod patch, Expo config plugins and entitlement, server helper to fetch provisioning credentials, a React hook for eligibility and provisioning flows, UI WalletButtons, and supporting changes (changesets, spellcheck, npm registry). ChangesWallet provisioning flow
Sequence DiagramsequenceDiagram
participant User
participant CardDetails
participant WalletButtons
participant Hook
participant MeaSdk as MeaPushProvisioning
participant Server
participant iOS
participant Android
User->>CardDetails: open card view
CardDetails->>WalletButtons: render(lastFour, displayName)
WalletButtons->>Hook: invoke hook
Hook->>MeaSdk: initialize SDK (cached)
MeaSdk-->>Hook: initialized
alt iOS
Hook->>iOS: check Apple Pay / pass support
iOS-->>Hook: eligible.apple
else Android
Hook->>Android: check Google wallet availability
Android-->>Hook: eligible.google
end
Hook-->>WalletButtons: return eligibility, handlers
User->>WalletButtons: tap "Add to Apple Wallet"
WalletButtons->>Hook: call addToAppleWallet
Hook->>Server: getWalletCredentials()
Server-->>Hook: {cardId, cardSecret}
Hook->>MeaSdk: init OEM tokenization / add pass
Hook->>iOS: present AddPaymentPass
iOS-->>Hook: result
User->>WalletButtons: tap "Add to Google Wallet"
WalletButtons->>Hook: call addToGoogleWallet
Hook->>Server: getWalletCredentials()
Server-->>Hook: {cardId, cardSecret}
Hook->>Android: push card via GooglePay
Android-->>Hook: result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces native wallet provisioning for Apple Pay and Google Pay using the MeaWallet SDK. The implementation includes new UI components, a custom hook for provisioning logic, and Expo config plugins to manage native assets and build-time configurations. Feedback highlights a security risk regarding hardcoded credentials in the .npmrc file and points out the fragility of using regex-based string replacements for modifying build.gradle and Podfile, which could lead to silent failures during the build process.
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 57f13078-e054-43d7-ac63-6fce08240922
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/images/google-wallet-button.svgis excluded by!**/*.svg
📒 Files selected for processing (15)
.changeset/gentle-cases-fold.md.changeset/rich-months-double.md.npmrcapp.config.tsbabel.config.jscommon/eslint/base.mjscspell.jsonpackage.jsonpatches/@meawallet__react-native-mpp.patchsrc/assets/mea_configsrc/components/card/CardDetails.tsxsrc/components/card/WalletButtons.tsxsrc/components/card/WalletButtons.web.tsxsrc/components/card/useWalletProvisioning.tssrc/utils/server.ts
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## offramp #956 +/- ##
===========================================
- Coverage 71.64% 70.71% -0.93%
===========================================
Files 254 254
Lines 10248 10586 +338
Branches 3340 3459 +119
===========================================
+ Hits 7342 7486 +144
- Misses 2627 2821 +194
Partials 279 279
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
42eb9ec to
235b158
Compare
b49e535 to
1cf381c
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0ef42f30-9abb-400b-ba0a-92db897d922d
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/images/google-wallet-button.svgis excluded by!**/*.svg
📒 Files selected for processing (18)
.changeset/chilly-suns-dress.md.changeset/gentle-cases-fold.md.changeset/rich-months-double.md.npmrcapp.config.tscspell.jsonpackage.jsonpatches/@meawallet__react-native-mpp.patchserver/api/card.tsserver/test/api/card.test.tsserver/test/e2e.tsserver/utils/panda.tssrc/assets/mea_configsrc/components/card/CardDetails.tsxsrc/components/card/WalletButtons.tsxsrc/components/card/WalletButtons.web.tsxsrc/components/card/useWalletProvisioning.tssrc/utils/server.ts
27214db to
601a6ee
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 31e2d088-f9ca-4235-b66b-add0dc743ee6
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/images/google-wallet-button.svgis excluded by!**/*.svg
📒 Files selected for processing (13)
.changeset/gentle-cases-fold.md.changeset/rich-months-double.md.npmrcapp.config.tscspell.jsonpackage.jsonpatches/@meawallet__react-native-mpp.patchsrc/assets/mea_configsrc/components/card/CardDetails.tsxsrc/components/card/WalletButtons.tsxsrc/components/card/WalletButtons.web.tsxsrc/components/card/useWalletProvisioning.tssrc/utils/server.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app.config.ts (1)
231-237:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winMove the ndk.debugSymbolLevel setting to buildTypes.release and guard both injections against re-application.
Android's official guidance places
ndk.debugSymbolLevelunderandroid.buildTypes.release.ndk, notdefaultConfig. Setting it indefaultConfigapplies the setting to all build types rather than just release builds. Additionally, both string replacements are non-idempotent—without guards, each prebuild will append anotherndk { ... }block and OkHttp BOM line. The iOS workaround above (line 218) demonstrates the correct pattern: checkif (!contents.includes(...))before modifying and writing the file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e2bf0c92-f419-4cca-a5ec-7e675195e3cf
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/images/google-wallet-button.svgis excluded by!**/*.svg
📒 Files selected for processing (13)
.changeset/gentle-cases-fold.md.changeset/rich-months-double.md.npmrcapp.config.tscspell.jsonpackage.jsonpatches/@meawallet__react-native-mpp.patchsrc/assets/mea_configsrc/components/card/CardDetails.tsxsrc/components/card/WalletButtons.tsxsrc/components/card/WalletButtons.web.tsxsrc/components/card/useWalletProvisioning.tssrc/utils/server.ts
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/utils/server.ts (1)
111-113:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate provisioning fields before returning credentials.
Line 112 only guards that
provisioningexists. Ifidorsecretis missing/invalid, the failure moves downstream into wallet SDK calls.proposed fix
- const card = await parseResponse(response); - if (!card.provisioning) throw new Error("bad card provisioning response"); - return { cardId: card.provisioning.id, cardSecret: card.provisioning.secret }; + const card = await parseResponse(response); + if ( + !card.provisioning || + typeof card.provisioning.id !== "string" || + typeof card.provisioning.secret !== "string" + ) { + throw new Error("bad card provisioning response"); + } + return { cardId: card.provisioning.id, cardSecret: card.provisioning.secret };src/components/card/useWalletProvisioning.ts (1)
42-53:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard against re-entrant provisioning calls.
Line 43 state updates are asynchronous, so rapid taps can execute
provision()more than once and fire duplicate provisioning requests.proposed fix
-import { useState } from "react"; +import { useRef, useState } from "react"; @@ export default function useWalletProvisioning(lastFour: string, displayName: string) { const [provisioning, setProvisioning] = useState(false); + const inFlight = useRef(false); @@ async function provision(addToWallet: (cardData: MppCardDataParameters) => Promise<unknown>) { + if (inFlight.current) return; + inFlight.current = true; setProvisioning(true); try { await initSdk(); const { cardId, cardSecret } = await getWalletCredentials(); await addToWallet(MppCardDataParameters.withCardSecret(cardId, cardSecret)); } catch (error) { reportError(error); } finally { + inFlight.current = false; setProvisioning(false); } }
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4f887e6f-788f-4ed0-9f1a-92905cd9432c
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/images/google-wallet-button.svgis excluded by!**/*.svg
📒 Files selected for processing (12)
.changeset/gentle-cases-fold.md.changeset/rich-months-double.mdapp.config.tscspell.jsonpackage.jsonpatches/@meawallet__react-native-mpp.patchsrc/assets/mea_configsrc/components/card/CardDetails.tsxsrc/components/card/WalletButtons.tsxsrc/components/card/WalletButtons.web.tsxsrc/components/card/useWalletProvisioning.tssrc/utils/server.ts
485c2e5 to
fb55b18
Compare
63ecb97 to
bde9c53
Compare
3c2a5b3 to
3ba3ac4
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3ba3ac4ab1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
255499b to
2d37885
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7cd965a146
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return nextWallet.default.GooglePay.isWalletAvailable() | ||
| .then(async (available) => { | ||
| if (!available) return hiddenWallet; | ||
| const token = await nextWallet.default.GooglePay.checkWalletForCardSuffix(lastFour).catch( |
There was a problem hiding this comment.
Use card-token checks for Google Wallet eligibility
When an Android user already has any Google Wallet token with the same last four digits, this suffix-only check can treat that unrelated token as belonging to the current Exa card; an ACTIVE match makes getGoogleWalletState return "added" and hides the CTA, while a yellow-path match can send tokenize the wrong token. MeaWallet documents checkWalletForCardSuffix as checking only the provided PAN suffix, while checkWalletForCardToken(cardData) checks the corresponding card (https://dev.meawallet.com/api/mpp/react-native/), so use the card-data/token-id path before deciding this card is added or resumable.
Useful? React with 👍 / 👎.
💡 Codex Reviewexa/src/components/send-funds/SendAmount.tsx Line 219 in 0d4b865 When Bridge's quote endpoint succeeds with deposit details but no Lines 5 to 6 in 0d4b865 For pasted or typed whole-number amounts with a single thousands separator, such as exa/src/components/send-funds/Review.tsx Line 169 in 0d4b865 For Bridge offramps this mutation only submits the ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dda713e2e2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return { | ||
| apple: false, | ||
| google, | ||
| googleToken: google === "cta" ? getGoogleVerificationToken(tokens) : null, |
There was a problem hiding this comment.
Do not persist wallet token state
This query returns googleToken under the new ['wallet', ...] cache key, but the global persister in src/utils/queryClient.ts only excludes keys like card, kyc, and lifi, not wallet. As a result, Google Wallet issuer/token ids and stale eligibility state are written to AsyncStorage and restored for up to 30 days; on devices where token state changes or the app account is reused, the UI can briefly call GooglePay.tokenize with a restored stale token before the native recheck completes. Exclude wallet queries from dehydration or keep token ids out of persisted query data.
Useful? React with 👍 / 👎.
Closes #154
Summary by CodeRabbit
New Features
Chores