[Home Page] Add a Time Sensitive case for failed billing for existing customers#84289
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
|
@ZhenjaHorbach Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
@mountiny 🦜 🙏 |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c87f9474a
ℹ️ 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".
|
@ZhenjaHorbach could you please review? |
Reviewer Checklist
Screenshots/VideosAndroid: HybridApp2026-03-06.16.10.31.movAndroid: mWeb Chrome2026-03-06.16.12.42.moviOS: HybridApp2026-03-06.16.10.31.moviOS: mWeb Safari2026-03-06.16.12.42.movMacOS: Chrome / Safari2026-03-06.16.06.23.mov |
|
We don't show the failed billing widget after the login 2026-03-06.16.13.40.mov |
|
But overall changes look good! |
joekaufmanexpensify
left a comment
There was a problem hiding this comment.
Good for product
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index c5e5ecba..24e2e3cb 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -1000,6 +1000,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Verbindung der persönlichen Karte ${cardName} reparieren` : 'Verbindung der persönlichen Karte reparieren'),
subtitle: 'Wallet',
},
+ fixFailedBilling: {title: 'Wir konnten Ihre hinterlegte Karte nicht belasten', subtitle: 'Abonnement'},
},
assignedCards: 'Ihre Expensify Karten',
assignedCardsRemaining: ({amount}: {amount: string}) => `${amount} verbleibend`,
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 07da5190..f6454eb8 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -1003,6 +1003,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Réparer la connexion de la carte personnelle ${cardName}` : 'Corriger la connexion de la carte personnelle'),
subtitle: 'Portefeuille',
},
+ fixFailedBilling: {title: 'Nous n’avons pas pu débiter votre carte enregistrée', subtitle: 'Abonnement'},
},
assignedCards: 'Vos cartes Expensify',
assignedCardsRemaining: ({amount}: {amount: string}) => `${amount} restant`,
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 0ac44f8c..3133aa94 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -999,6 +999,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Correggi la connessione della carta personale ${cardName}` : 'Correggi connessione carta personale'),
subtitle: 'Portafoglio',
},
+ fixFailedBilling: {title: 'Non abbiamo potuto addebitare la carta salvata nel profilo', subtitle: 'Abbonamento'},
},
assignedCards: 'Le tue Carte Expensify',
assignedCardsRemaining: ({amount}: {amount: string}) => `${amount} rimanenti`,
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 1e6be866..4b8f051e 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -991,6 +991,7 @@ const translations: TranslationDeepObject<typeof en> = {
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会計`,
},
fixPersonalCardConnection: {title: ({cardName}: {cardName?: string}) => (cardName ? `${cardName}個人カードの接続を修正` : '個人カードの連携を修正'), subtitle: 'ウォレット'},
+ fixFailedBilling: {title: '登録されているカードから請求できませんでした', subtitle: 'サブスクリプション'},
},
assignedCards: 'お客様の Expensify カード',
assignedCardsRemaining: ({amount}: {amount: string}) => `残額:${amount}`,
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index ae031990..2624492e 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -998,6 +998,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Verbinding van persoonlijke kaart ${cardName} herstellen` : 'Verbinding persoonlijke kaart herstellen'),
subtitle: 'Portemonnee',
},
+ fixFailedBilling: {title: 'We konden je kaart in ons bestand niet belasten', subtitle: 'Abonnement'},
},
assignedCards: 'Je Expensify Kaarten',
assignedCardsRemaining: ({amount}: {amount: string}) => `${amount} resterend`,
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index a3f8e742..62bb6ea3 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -999,6 +999,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Napraw połączenie z prywatną kartą ${cardName}` : 'Napraw połączenie karty prywatnej'),
subtitle: 'Portfel',
},
+ fixFailedBilling: {title: 'Nie mogliśmy obciążyć zapisanej karty', subtitle: 'Subskrypcja'},
},
assignedCards: 'Twoje Karty Expensify',
assignedCardsRemaining: ({amount}: {amount: string}) => `Pozostało ${amount}`,
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 50d8b652..051adca2 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -997,6 +997,7 @@ const translations: TranslationDeepObject<typeof en> = {
title: ({cardName}: {cardName?: string}) => (cardName ? `Corrigir conexão do cartão pessoal ${cardName}` : 'Corrigir conexão do cartão pessoal'),
subtitle: 'Carteira',
},
+ fixFailedBilling: {title: 'Não foi possível cobrar o cartão cadastrado', subtitle: 'Assinatura'},
},
assignedCards: 'Seus Cartões Expensify',
assignedCardsRemaining: ({amount}: {amount: string}) => `${amount} restante`,
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index d8a54863..265add6c 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -977,6 +977,7 @@ const translations: TranslationDeepObject<typeof en> = {
defaultSubtitle: '工作区',
subtitle: ({policyName}: {policyName: string}) => `${policyName} > 会计`,
},
+ fixFailedBilling: {title: '我们无法向您档案中的银行卡收费', subtitle: '订阅'},
},
assignedCards: '你的 Expensify 卡',
assignedCardsRemaining: ({amount}: {amount: string}) => `剩余 ${amount}`,
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
|
@adamgrzybowski TS check is failing |
|
@grgia fixed |
@grgia |
|
Yeah, @ZhenjaHorbach, good you reminded that The widget needs I am not sure if calling |
|
@grgia, what should we do next with this PR? |
|
Yeah we should provide that data from BE in OpenApp I assume, we are now a bit busy with some higher priority tasks |
|
@mountiny I can pair with Adam and make necessary BE changes |
|
Actually I've checked it and |
|
@ZhenjaHorbach can you check your |
Here is my response |
|
Actually looks like in my case this issue is related to After login I don't have CC:@jnowakow |
|
Thanks, @ZhenjaHorbach! I'll check how it looks for |
|
I've opened PR with fix for this issue |
|
@jnowakow do you know what is the status of your PR? |
|
It's merged and deployed 🎉 So you should be able to rerun tests on this PR |
Thanks! |
|
But it works! 2026-03-20.11.51.21.mov |
|
Conflicts |
…sure re-renders on balance changes
46ae15c to
5a5fb57
Compare
|
🚧 @grgia has triggered a test Expensify/App build. You can view the workflow run here. |
This comment has been minimized.
This comment has been minimized.
|
🚧 @grgia has triggered a test Expensify/App build. You can view the workflow run here. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
@adamgrzybowski @ZhenjaHorbach @grgia Can this one please be tested internally, blocked because of this |
|
🚀 Deployed to production by https://github.com/Julesssss in version: 9.3.43-3 🚀
|
I hope it works as expected! 2026-03-20.11.51.21.mov |
Explanation of Change
This PR adds a new "Fix Failed Billing" widget to the Time Sensitive Section on the home page. When a user has an existing subscription (
hasPurchases === true) and their billing card has been declined (due to either an expired card or insufficient funds), a high-priority widget is shown prompting them to update their payment card.Changes:
useTimeSensitiveBillinghook that checksNVP_PRIVATE_BILLING_STATUSfor card-expired or insufficient-funds errors andACCOUNT.hasPurchasesto determine visibility.FixFailedBillingwidget component with a danger-styled CTA button that navigates to the payment card update flow.hasCardExpiredErrorandhasInsufficientFundsErrorfromSubscriptionUtils.useTimeSensitiveBillinghook covering positive/negative cases and Onyx reactivity.Fixed Issues
$ #81726
PROPOSAL:
Tests
hasPurchases: true)NVP_PRIVATE_BILLING_STATUS)hasPurchasesdo NOT see the widget even if billing errors existhasPurchasesbut no billing errors do NOT see the widgetOffline tests
QA Steps
hasPurchases: trueand a declined billing card (expired or insufficient funds)PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-03-05.at.15.52.47.mov