feat(banking): mode "Bientôt disponible" pendant la fenêtre KYC Powens
Ajoute un état intermédiaire entre "feature désactivée silencieusement"
et "feature pleinement active" : un teaser visible dans /parametres
pour les Pro/Business qui annonce que la connexion bancaire arrive,
avec une note rassurante sur la lecture seule. Permet d'annoncer la
feature aux users payants pendant le délai d'agrément AISP / KYC
Powens, sans risque de cliquer dans le vide.
- Nouveau flag d'env `BANKING_TEASER_ENABLED` (boolean, default false)
- `GET /banking/status` renvoie désormais `{ enabled, comingSoon }`
où comingSoon = !enabled && BANKING_TEASER_ENABLED
- `BankingSection` : nouveau composant `ComingSoonCard` (halo glow,
copy explicite sur l'agrément AISP en cours, rassurance lecture
seule) affiché quand comingSoon=true et l'org est Pro/Business
- `parametres.tsx` : la section "Banque" apparaît si enabled OU
comingSoon (au lieu de uniquement enabled)
- ConfigMap K3s : `BANKING_TEASER_ENABLED='true'` en prod pour
annoncer la feature aux clients payants pendant le KYC
Trois états possibles désormais :
enabled=true → feature active (post-KYC)
enabled=false + comingSoon=true → teaser "Bientôt disponible"
enabled=false + comingSoon=false → section invisible (kill switch dur)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
51217175ad
commit
3207f873e9
@ -66,17 +66,20 @@ export default class BankingController {
|
|||||||
/**
|
/**
|
||||||
* GET /banking/status — public.
|
* GET /banking/status — public.
|
||||||
*
|
*
|
||||||
* Kill switch lu côté SPA pour décider d'afficher la section banque.
|
* Trois états :
|
||||||
* On considère banking activé si :
|
* - { enabled: true } → feature pleinement active
|
||||||
* - `BANKING_ENABLED` == 'true' (ou non défini = true par défaut en dev)
|
* - { enabled: false, comingSoon: true } → KYC Powens en cours,
|
||||||
* - ET les credentials Powens sont configurés
|
* teaser "Bientôt disponible" affiché aux Pro/Business
|
||||||
*
|
* - { enabled: false, comingSoon: false } → section complètement
|
||||||
* En prod on déploie avec `BANKING_ENABLED=false` tant que le KYC
|
* cachée (kill switch dur)
|
||||||
* Powens prod n'est pas finalisé — la feature est dormante, aucun
|
|
||||||
* bouton n'apparaît, aucun call init n'est tenté.
|
|
||||||
*/
|
*/
|
||||||
async status({ response }: HttpContext) {
|
async status({ response }: HttpContext) {
|
||||||
return response.json({ data: { enabled: isBankingEnabled() } })
|
return response.json({
|
||||||
|
data: {
|
||||||
|
enabled: isBankingEnabled(),
|
||||||
|
comingSoon: !isBankingEnabled() && env.get('BANKING_TEASER_ENABLED') === true,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -127,6 +127,13 @@ export default await Env.create(new URL('../', import.meta.url), {
|
|||||||
| (sinon calculée : https://<slug>.biapi.pro/2.0/).
|
| (sinon calculée : https://<slug>.biapi.pro/2.0/).
|
||||||
*/
|
*/
|
||||||
BANKING_ENABLED: Env.schema.boolean.optional(),
|
BANKING_ENABLED: Env.schema.boolean.optional(),
|
||||||
|
/**
|
||||||
|
* Teaser "Bientôt disponible" affiché dans /parametres pour les
|
||||||
|
* Pro/Business quand BANKING_ENABLED=false. Permet d'annoncer la
|
||||||
|
* feature aux users payants pendant la fenêtre KYC Powens. Si false,
|
||||||
|
* la section est complètement cachée.
|
||||||
|
*/
|
||||||
|
BANKING_TEASER_ENABLED: Env.schema.boolean.optional(),
|
||||||
BANKING_PROVIDER: Env.schema.enum.optional(['powens'] as const),
|
BANKING_PROVIDER: Env.schema.enum.optional(['powens'] as const),
|
||||||
POWENS_DOMAIN: Env.schema.string.optional(),
|
POWENS_DOMAIN: Env.schema.string.optional(),
|
||||||
POWENS_API_BASE_URL: Env.schema.string.optional({ format: 'url', tld: false }),
|
POWENS_API_BASE_URL: Env.schema.string.optional({ format: 'url', tld: false }),
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { toast } from "sonner";
|
|||||||
import {
|
import {
|
||||||
Banknote,
|
Banknote,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
|
Clock,
|
||||||
Lock,
|
Lock,
|
||||||
Loader2,
|
Loader2,
|
||||||
Trash2,
|
Trash2,
|
||||||
@ -84,6 +85,12 @@ export function BankingSection({
|
|||||||
return <UpsellCard />;
|
return <UpsellCard />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Banking pas encore activé mais teaser ON → afficher "Bientôt disponible"
|
||||||
|
// pour annoncer la feature aux Pro/Business pendant la fenêtre KYC Powens.
|
||||||
|
if (status?.comingSoon) {
|
||||||
|
return <ComingSoonCard />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BankingPaidView
|
<BankingPaidView
|
||||||
isLoading={connectionsQuery.isLoading}
|
isLoading={connectionsQuery.isLoading}
|
||||||
@ -96,6 +103,42 @@ export function BankingSection({
|
|||||||
// Upsell (Free) — visuel cohérent avec la section Marque (Business)
|
// Upsell (Free) — visuel cohérent avec la section Marque (Business)
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// Coming Soon (Pro/Business, KYC Powens en cours)
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
function ComingSoonCard() {
|
||||||
|
return (
|
||||||
|
<Card padding="md" className="flex items-start gap-4 flex-wrap">
|
||||||
|
<div className="flex items-start gap-3 flex-1 min-w-[280px]">
|
||||||
|
<div className="relative shrink-0">
|
||||||
|
<div className="absolute inset-0 rounded-full bg-rubis-glow blur-xl opacity-60" />
|
||||||
|
<div className="relative rounded-full bg-rubis-glow p-2.5 text-rubis-deep">
|
||||||
|
<Clock size={18} aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-[10.5px] uppercase tracking-[0.12em] text-rubis font-semibold">
|
||||||
|
Bientôt disponible
|
||||||
|
</p>
|
||||||
|
<p className="mt-1.5 font-display text-[18px] font-bold text-ink leading-tight">
|
||||||
|
Connexion bancaire en finalisation
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 text-[13px] text-ink-2 leading-relaxed max-w-[480px]">
|
||||||
|
Rubis termine son agrément AISP (lecture seule, lecture des
|
||||||
|
virements entrants uniquement) pour détecter automatiquement
|
||||||
|
les factures payées. Vous serez notifié dès l'ouverture.
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 text-[12px] text-ink-3 leading-relaxed">
|
||||||
|
🔒 Lecture seule · Aucun déplacement de fonds · Compatible
|
||||||
|
toutes banques françaises
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function UpsellCard() {
|
function UpsellCard() {
|
||||||
return (
|
return (
|
||||||
<Card padding="md" className="flex items-center justify-between gap-4 flex-wrap">
|
<Card padding="md" className="flex items-center justify-between gap-4 flex-wrap">
|
||||||
|
|||||||
@ -47,7 +47,10 @@ export type BankingSettings = {
|
|||||||
export function useBankingStatus() {
|
export function useBankingStatus() {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["banking", "status"] as const,
|
queryKey: ["banking", "status"] as const,
|
||||||
queryFn: () => api.get<{ enabled: boolean }>("/api/v1/banking/status"),
|
queryFn: () =>
|
||||||
|
api.get<{ enabled: boolean; comingSoon: boolean }>(
|
||||||
|
"/api/v1/banking/status",
|
||||||
|
),
|
||||||
// Le flag bouge rarement — long cache OK.
|
// Le flag bouge rarement — long cache OK.
|
||||||
staleTime: 5 * 60_000,
|
staleTime: 5 * 60_000,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -46,10 +46,12 @@ function ParametresPage() {
|
|||||||
const { data: sub } = useSubscription();
|
const { data: sub } = useSubscription();
|
||||||
const planLabel = sub?.plan === "pro" ? "Pro" : sub?.plan === "business" ? "Business" : "Free";
|
const planLabel = sub?.plan === "pro" ? "Pro" : sub?.plan === "business" ? "Business" : "Free";
|
||||||
const search = Route.useSearch();
|
const search = Route.useSearch();
|
||||||
// Kill switch banking : la section ENTIÈRE disparaît si BANKING_ENABLED=false
|
// Kill switch banking : la section apparaît si la feature est ON, OU si
|
||||||
// côté API (typiquement en prod tant que le KYC Powens n'est pas validé).
|
// la prod est en mode "teaser" (KYC Powens en cours, mais on annonce
|
||||||
|
// qu'elle arrive). Sinon la section reste invisible.
|
||||||
const { data: bankingStatus } = useBankingStatus();
|
const { data: bankingStatus } = useBankingStatus();
|
||||||
const showBanking = bankingStatus?.enabled === true;
|
const showBanking =
|
||||||
|
bankingStatus?.enabled === true || bankingStatus?.comingSoon === true;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|||||||
@ -156,6 +156,10 @@ data:
|
|||||||
# - POWENS_REDIRECT_URI doit matcher EXACTEMENT ce qui est whitelisté
|
# - POWENS_REDIRECT_URI doit matcher EXACTEMENT ce qui est whitelisté
|
||||||
# côté console Powens prod.
|
# côté console Powens prod.
|
||||||
BANKING_ENABLED: 'false'
|
BANKING_ENABLED: 'false'
|
||||||
|
# Teaser "Bientôt disponible" affiché aux Pro/Business pendant la
|
||||||
|
# fenêtre KYC Powens. Flip à 'false' pour cacher complètement la
|
||||||
|
# section (cas où on veut remettre la feature sous le tapis).
|
||||||
|
BANKING_TEASER_ENABLED: 'true'
|
||||||
BANKING_PROVIDER: 'powens'
|
BANKING_PROVIDER: 'powens'
|
||||||
POWENS_DOMAIN: 'rubis'
|
POWENS_DOMAIN: 'rubis'
|
||||||
POWENS_API_BASE_URL: 'https://rubis.biapi.pro/2.0/'
|
POWENS_API_BASE_URL: 'https://rubis.biapi.pro/2.0/'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user