Aller au contenu

Story NOTIF-006: Consent + cross-border disclosure copy contract (NOTIF-owned, consumed by CP)

Module: notifications Slice: v0.0.1 NOTIF — consent copy block + cross-border disclosure asset (PRD V0 Envelope v0.0.1; design §2.1 + §1.4; NOTIF-US-08) Side: [BACKEND] (content/contract supply — the rendering UI is CP-owned) Version target: V0 Sub-version: v0.0.1 Priority: 10 (v0.0.1; after NOTIF-005) Depends on: NOTIF-005 (opt-out link the copy references), NOTIF-001a (the customer_consented flag already carried at dispatch) Can develop concurrently with: NOTIF-005 Merge order: After NOTIF-005 Estimated complexity: S PRD User Stories: NOTIF-US-08 (AC-08.1, AC-08.2, AC-08.3); PRD V0 Envelope lock #2 + #3 Wireframe: N/A — copy contract only; the consent UI wireframe lives in the CP design (design §2.1)


⚠️ [ARCH-PENDING] — statut PRÉLIMINAIRE

Architecture is v0.0.0-only; v0.0.1 consent UI is a forward-compat checkpoint, owned on the rendering side by customer-portal (CP). This NOTIF story delivers only the canonical FR copy block + the privacy/disclosure content asset that CP consumes. Reconcile the asset-delivery mechanism (shared content module vs. served endpoint) with the ARCHITECT v0.0.1 pass before dev-start.


Objective

v0.0.1 introduces an explicit consent checkbox on the Customer Portal landing page and a cross-border data-flow disclosure (ARTCI mitigations #2 and #3; loi n°2013-450 transparency duty, NOTIF-US-08). The rendering is CP-owned; NOTIF owns the canonical French copy so the wording does not drift between modules. This story delivers that copy as a versioned, single-source content asset (consent label, "learn more" link, privacy-notice page body, cross-border sub-processor disclosure) and the contract by which CP retrieves it.


Backend Scope

Entities

None. This story supplies content, not data rows. The customer_consented boolean is already persisted at dispatch (NOTIF-001a, v0.0.0).

Migrations

None.

Service Layer

  • Ship the canonical FR copy as a versioned content asset (proposed: a notif-content resource bundle keyed consent.*, privacy.*, disclosure.*, with a content_version string e.g. papillon_privacy_fr_v1). CP imports the bundle (or fetches it from a NOTIF-served read-only endpoint — decide in ARCHITECT pass).
  • The privacy-notice page body (design §1.4) and the cross-border disclosure naming Meta Platforms Inc. + BSP + SMS aggregator (AC-08.2) are part of the same versioned bundle so they cannot drift from the SMS/WA body's {privacy_url}.

API Endpoints

Method Path Request Response Auth
GET /api/notifications/content/privacy (proposed — or static bundle import) ?lang=fr versioned FR content block None (public content)

Validation Rules

  • One canonical content_version; the SMS body {privacy_url} and the CP "En savoir plus" link MUST resolve to the same content (single source of truth — design §2.1 last line).
  • Disclosure text MUST name all three sub-processors (Meta, BSP, SMS aggregator), the legal basis (legitimate interest of the broker), and the right of objection (STOP / opt-out link).

Multi-Tenant Considerations

  • v0.0.1 ships a platform-default FR page only (no tenant branding). {tenant_name} is the only tenant-specific interpolation in the consent label. Tenant-override of the privacy page is V1 (PRD V0 §6.2 #3). No per-tenant content table in v0.0.1.

Audit & Logging

  • No new audit event owned here. The customer_consented flag captured by CP is carried into NOTIF dispatch (NOTIF-001a) and already audited via the send events.

Frontend Scope

The consent checkbox + landing-page rendering is CP-owned (design §2.1). NOTIF supplies the strings below verbatim; CP renders them. Listed here so the copy contract is testable.

French micro-copy catalog (verbatim — locked, single source)

Key FR copy
consent.checkbox.label J'ai lu et j'accepte que {tenant_name} et Papillon traitent mes données pour ce renouvellement.
consent.checkbox.learn_more En savoir plus
consent.checkbox.helper_off Cochez la case ci-dessus pour continuer.
consent.error.save_failed Nous n'avons pas pu enregistrer votre choix. Réessayez.
consent.error.retry_button Réessayer
consent.offline.banner Hors ligne — votre choix sera sauvegardé dès la reconnexion.
consent.collapsed.footer Vie privée · vos choix
privacy.page.link_label Confidentialité et vos données

Privacy-notice page body

Use the locked FR content of design §1.4 verbatim (sections: "Pourquoi vous recevez ce message", "Qui voit vos données", "Vos droits", "Durée de conservation", "Plus d'informations"). It names the broker (responsable), Papillon (sous-traitant technique), the SMS operator, and Meta Platforms / WhatsApp Cloud API (transfert hors UE / hors Côte d'Ivoire).

State Behavior French Copy
Loading Skeleton card, CTA disabled (≤ 600 ms)
Empty (initial) Unticked checkbox, CTA disabled "Cochez la case ci-dessus pour continuer."
Error Save failed → warning banner, CTA not blocked, lazy retry "Nous n'avons pas pu enregistrer votre choix. Réessayez." / "Réessayer"
Offline / low network Store consent in sessionStorage, subtle caption, CTA enabled "Hors ligne — votre choix sera sauvegardé dès la reconnexion."
Success Checkbox ticked, CTA enabled, no banner

Responsive Behavior

  • Mobile-first (CUSTOMER), 360 / 768 / 1024 px; checkbox touch target ≥ 48 px (per design §2.1).

Connectivity Behavior (CUSTOMER stories only)

  • The disclosure asset MUST NOT inflate the CP initial payload above the < 200 KB budget (AC-08.3) — server-side rendered or lazy-loaded. The privacy page itself targets < 50 KB (design §1.4).

Acceptance Criteria

AC-006.1: Canonical consent + disclosure copy is single-source
Given the SMS/WA body privacy_url and the CP "En savoir plus" link
When either is opened
Then both resolve to the same versioned FR content (content_version), with no drift
And the consent label reads exactly "J'ai lu et j'accepte que {tenant_name} et Papillon traitent mes données pour ce renouvellement."
AC-006.2: Disclosure names all sub-processors and legal basis
Given the cross-border disclosure content asset
When it is read
Then it explicitly names Meta Platforms Inc. (WhatsApp Cloud API, transfert hors UE), the BSP, and the SMS aggregator as sub-processors
And it states the legal basis (intérêt légitime du courtier) and the right of objection (STOP / lien d'opt-out)
AC-006.3: Disclosure asset respects the CP payload budget
Given the CP landing page < 200 KB initial-payload budget
When the disclosure asset is delivered
Then it is server-side rendered or lazy-loaded and does not push the initial payload above the budget

Compliance Rules

  • ARTCI mitigation #2 (explicit consent capture) and #3 (privacy-notice link in every send) — PRD V0 Envelope "Verrouillé".
  • NOTIF-US-08 / loi n°2013-450 transparency duty: cross-border disclosure. The legal-audit disposition for cross-border WhatsApp (gap #6) is RE-RESEARCH NEEDED → statut PRÉLIMINAIRE / INCERTAIN on the cross-border transfer itself. Emit [LEGAL-REVIEW]. The disclosure copy may be drafted, but the underlying WA cross-border transfer remains under the ARTCI B5 + B10 production BLOQUANT and must be validated by counsel before public production.

Standards & Conventions

  • docs/standards/compliance-discipline.md (PRÉLIMINAIRE / INCERTAIN handling — this story carries both flags).
  • docs/standards/connectivity-low-bandwidth.md (payload budgets).
  • docs/standards/design-system.md (Checkbox/Card components CP reuses).
  • docs/standards/api-guidelines.md (if served via endpoint, header-based versioning).

Testing Requirements

Unit Tests

  • Content bundle exposes every key in the micro-copy catalog with exact FR strings.
  • content_version is stable and equals the value embedded in the SMS {privacy_url} resolution.

Integration Tests

  • CP consumes the bundle and renders the consent label with {tenant_name} interpolated.
  • Privacy URL from an SMS body and the CP "En savoir plus" link reach identical content.

What QA Will Validate

  • The disclosure page names Meta + BSP + SMS aggregator and the legal basis; the consent string matches byte-for-byte; CP initial payload stays under 200 KB.

Out of Scope

  • The functional opt-out link endpoint (NOTIF-005).
  • The consent checkbox component and its save API (CP-owned).
  • Per-tenant privacy-page branding/override (V1).
  • English version of the disclosure (V1 / marketing site).

Definition of Done

  • [ ] Canonical FR content bundle shipped with all catalog keys + privacy page body + cross-border disclosure
  • [ ] content_version single-sourced with the SMS/WA {privacy_url}
  • [ ] All acceptance criteria pass manually
  • [ ] Unit + integration tests written and passing
  • [ ] [CUSTOMER] disclosure delivery keeps CP initial payload < 200 KB
  • [ ] French micro-copy matches the catalog exactly (byte-for-byte)
  • [ ] [LEGAL-REVIEW] acknowledged for the cross-border disclosure (gap #6 RE-RESEARCH; ARTCI B5+B10 BLOQUANT noted)
  • [ ] [ARCH-PENDING] asset-delivery mechanism reconciled with the ARCHITECT v0.0.1 pass before merge to a release branch