# Salesforce Commerce Cloud (SFCC)

Cette intégration explique comment ajouter un **bouton unique « Ajouter à Wallet »** à une vitrine Salesforce Commerce Cloud. Les emplacements courants sont la **Mon compte** zone (carte de fidélité ou d’adhésion) et les pages authentifiées où un identifiant stable est disponible (par exemple une liste de cartes-cadeaux). Les pages « Pick & collect » peuvent également convenir lorsqu’un pass de retrait est utilisé comme jeton de scan en magasin.

<figure><img src="https://3097111101-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWLc8AHXW4tdrAXUBfrYF%2Fuploads%2FMAXVV8T3j5sxIpwRScLd%2Fimage.png?alt=media&#x26;token=5e5cbb0d-f7c9-4bdf-8aa1-29dd5e6fe809" alt="Salesforce Commerce Cloud and Apple/Google Wallet integration with add to wallet button"><figcaption></figcaption></figure>

Un identifiant stable disponible dans la session client est signé côté serveur, puis transmis au SDK cinto de The Wallet Crew en tant qu’identifiant externe. Cela rend l’échange d’identifiants inviolable et évite d’exposer des secrets dans le code de la vitrine.

<details>

<summary><strong>Exemples concrets</strong></summary>

* Un programme de fidélité affiche « Ajouter au Wallet » dans **Mon compte** pour les clients connectés.
* Un programme de cartes-cadeaux affiche un bouton par **actives** carte-cadeau dans le portefeuille du compte.
* Un flux pick & collect affiche un CTA pour le pass de retrait sur une page dédiée au retrait, en utilisant une référence de retrait comme identifiant.

</details>

{% hint style="info" %}
Pour l’utilisation générique du SDK cinto (mode id de pass, détection de plateforme, personnalisation du QR sur bureau), voir [Sur votre site web](https://app.gitbook.com/s/WLc8AHXW4tdrAXUBfrYF/enroll/on-your-website).
{% endhint %}

## Concepts SFCC utilisés dans cette intégration

Le code de la vitrine Salesforce Commerce Cloud est empaqueté et déployé en tant que **cartridges**. Un cartridge peut ajouter des contrôleurs, scripts, templates et métadonnées de configuration, puis être branché sur un site en l’ajoutant au **chemin de cartridge**.

Ce guide suppose qu’un cartridge dédié est créé pour l’intégration The Wallet Crew, généralement nommé comme `int_TheWalletCrew`. Le cartridge est ensuite référencé par :

* l’application vitrine (SFRA ou SiteGenesis)
* la configuration Business Manager (préférences du site et, optionnellement, objets personnalisés)

### Cartridge

A **cartridge** est l’unité de déploiement dans SFCC. L’ordre des cartridges importe car SFCC résout les templates et scripts en scannant le chemin de cartridges de gauche à droite.

### SFRA vs SiteGenesis

Les deux architectures peuvent supporter l’intégration :

* **SFRA**: contrôleurs et templates ISML. Le bouton est généralement ajouté dans un template de compte ou une inclusion distante.
* **SiteGenesis**: pipelines (héritage) et templates ISML. Le bouton est généralement ajouté dans une page de compte rendue par pipeline.

Le modèle de sécurité reste identique. Le HMAC est calculé côté serveur et jamais dans le JavaScript navigateur.

## Comment cela fonctionne

Le SDK cinto de The Wallet Crew affiche le CTA approprié selon l’appareil.

Sur iOS, le bouton télécharge un pass Apple Wallet. Sur Android, il ouvre Google Wallet. Sur bureau, le SDK redirige vers une page de pass hébergée qui peut être scannée ou ouverte sur mobile.

Dans cette configuration SFCC, le pass est récupéré en utilisant :

* un **type de carte** (exemple : `utilisateur`)
* un identifiant SFCC stable envoyé en tant que **identifiant externe** (exemple : `sfcc.customerNo`)
* un **HMAC** signature qui prouve que l’identifiant a été émis par le backend de la Marque

{% hint style="warning" %}
Le HMAC doit être calculé côté serveur. Il ne doit pas être généré dans le navigateur.
{% endhint %}

## Prérequis

* Un modèle de pass et une émission de pass déjà configurés dans The Wallet Crew.
* L’identifiant de locataire (tenant id) The Wallet Crew disponible (utilisé par l’URL du script du SDK).
* Un identifiant stable disponible sur les pages authentifiées, tel que :
  * SFCC `CustomerNo` (typique pour les cartes de fidélité)
  * référence de retrait (typique pour les pass de retrait pick & collect)
  * id de carte-cadeau (lorsque les cartes-cadeaux sont stockées dans SFCC ou un système connecté)
* Le nom de clé d’identifiant externe convenu lors de l’onboarding (exemple : `sfcc.customerNo`).

## Implémenter l’intégration en tant que cartridge

Le schéma typique est :

1. Lire l’identifiant dans le code SFCC côté serveur.
2. Calculer un HMAC avec le secret du tenant.
3. Rendre un conteneur cinto dans le template ISML avec l’identifiant + HMAC.
4. Charger le SDK cinto depuis `sdk.neostore.cloud`.

{% stepper %}
{% step %}

#### Ajouter le cartridge au chemin de cartridges

Après avoir déployé le cartridge d’intégration sur l’instance, ajoutez-le au chemin de cartridges du site dans Business Manager. Le cartridge doit être placé **avant** avant le cartridge de vitrine de base afin que les overrides se résolvent correctement.

{% hint style="info" %}
Les libellés exacts du menu Business Manager varient selon la version SFCC. L’essentiel est le **chemin de cartridges au niveau du site** (pas celui global).
{% endhint %}
{% endstep %}

{% step %}

#### Stocker la configuration du tenant en tant que préférences du site

Les valeurs et secrets du tenant doivent rester côté serveur. Dans SFCC, cela se fait généralement avec **préférences personnalisées du site**.

Au minimum, ces valeurs sont généralement requises.

<details>

<summary><strong>Préférences recommandées (IDs, types, exemples)</strong></summary>

Ces valeurs sont typiquement créées en tant que **préférences personnalisées au niveau du site** (pas au niveau de l’organisation).

* `theWalletCrewTenantId` (String)\
  Exemple : `molia`
* `theWalletCrewHmacSecret` (Password)\
  Exemple : `I1M8emrrJSns4Hnuibbm45eWfLQMosPGKSp1JzKsCrXeWmhjE8lZhxC2tfSRX5IJ`
* `theWalletCrewDefaultPassType` (String)\
  Exemple : `utilisateur`
* `theWalletCrewExternalIdentifierKey` (String)\
  Exemple : `sfcc.customerNo`
* `theWalletCrewLanguage` (String, optionnel)\
  Exemple : `fr`\
  Lorsqu’elle est omise, cinto utilise la détection de la langue du navigateur.

{% hint style="info" %}
Conserver le secret en tant que **Password** préférence aide à prévenir les divulgations accidentelles dans les exports d’UI et les captures d’écran.
{% endhint %}

</details>

{% hint style="warning" %}
Le secret HMAC ne doit pas être stocké dans les templates, assets de contenu ou toute configuration côté client.
{% endhint %}
{% endstep %}

{% step %}

#### Calculer le HMAC côté serveur

Les scripts côté serveur SFCC peuvent calculer un HMAC SHA-256 en utilisant le secret du tenant et la valeur exacte de l’identifiant.

Choix d’implémentation courants :

* un script helper exposé par le cartridge, utilisé par les contrôleurs de compte
* un pattern décorateur qui enrichit le modèle de vue avec `identifier` et `identifierHmac`

La mise en cache doit être gérée avec précaution. Les pages spécifiques au client ne doivent pas partager du HTML mis en cache entre clients lorsque l’identifiant est intégré dans le balisage.
{% endstep %}

{% step %}

#### Rendre le bouton « Ajouter au Wallet » en ISML

Le SDK cinto repose sur :

* un script chargeur du SDK référant l’id du tenant
* un élément conteneur marqué comme bouton « Ajouter au Wallet »
* un `passType`
* une paire clé/valeur d’identifiant externe plus le HMAC côté serveur

Le cartridge d’intégration fournit typiquement un partial ISML qui peut être inclus là où nécessaire (tableau de bord du compte, liste de cartes-cadeaux, page pick & collect).
{% endstep %}
{% endstepper %}

## Conventions d’identifiants externes (casse + échappement)

Le SDK cinto peut récupérer un pass à partir d’un identifiant externe :

* `passType` (exemple : `utilisateur`)
* `externalIdentifier.key` (exemple : `sfcc.customerNo`)
* `externalIdentifier.value` (exemple : `00012345`)
* `externalIdentifier.hmac` (HMAC-SHA256 de la valeur)

### Convention recommandée pour les noms de clé

L’option la plus stable est d’éviter les majuscules mélangées dans les clés d’identifiant.

Les clés sont généralement :

* nominées avec un namespace préfixe comme `sfcc.`
* en minuscules
* en utilisant `_` comme séparateur si nécessaire

Exemples :

* `sfcc.customer_no`
* `sfcc.pickup_ref`
* `sfcc.gift_card_id`

{% hint style="info" %}
Si le tenant The Wallet Crew est déjà configuré avec une clé en casse mixte, la changer peut impacter des installations existantes. Un changement de nommage de clé doit être traité comme un sujet de migration.
{% endhint %}

### Si une clé en casse mixte doit être utilisée dans des attributs HTML

Les navigateurs traitent les noms d’attributs HTML comme minuscules. cinto prend en charge une règle d’échappement pour les caractères majuscules dans `data-neostore-externalIdentifiers-...` attributs :

* Préfixer un caractère majuscule par `_` dans le nom de l’attribut.
* `y2.customer_Id` est interprété comme `y2.customerId`.

Appliqué aux exemples SFCC :

* Clé d’identifiant dans The Wallet Crew : `sfcc.customerNo`
* Clé d’identifiant dans les attributs HTML : `sfcc.customer_No`

Ceci n’affecte que le **nom d’attribut**, pas la valeur signée.

## Implémentation de référence SFRA à copier/coller (contrôleur + partial ISML)

Cette implémentation de référence utilise :

* **préférences du site** pour stocker la configuration du tenant
* un **helper HMAC côté serveur**
* un **contrôleur d’inclusion distante** qui rend un partial ISML

Il est conçu pour être collé dans un cartridge dédié (exemple : `int_TheWalletCrew`) et appelé depuis une page de compte.

### Disposition des fichiers du cartridge

* `cartridge/scripts/TheWalletCrew/hmac.js`
* `cartridge/controllers/TheWalletCrew.js`
* `cartridge/templates/default/TheWalletCrew/addToWalletButton.isml`

### 1) Helper HMAC (`cartridge/scripts/TheWalletCrew/hmac.js`)

{% code title="cartridge/scripts/TheWalletCrew/hmac.js" %}

```javascript
'use strict';

var Mac = require('dw/crypto/Mac');
var Encoding = require('dw/crypto/Encoding');

/**
 * HMAC-SHA256 sur la chaîne d’identifiant brute (UTF-8), encodé en hex.
 * La sortie est mise en minuscules pour correspondre aux exemples cinto.
 */
function hmacSha256Hex(secret, identifierValue) {
    var mac = new Mac(Mac.HMAC_SHA_256);
    var bytes = mac.digest(identifierValue, secret);
    return Encoding.toHex(bytes).toLowerCase();
}

module.exports = {
    hmacSha256Hex: hmacSha256Hex
};
```

{% endcode %}

### 2) Contrôleur (`cartridge/controllers/TheWalletCrew.js`)

Ce contrôleur affiche un widget sûr à inclure en tant qu’inclusion distante.

{% code title="cartridge/controllers/TheWalletCrew\.js" %}

```javascript
'use strict';

var server = require('server');
var Site = require('dw/system/Site');

var theWalletCrewHmac = require('*/cartridge/scripts/TheWalletCrew/hmac');

server.get('Button', function (req, res, next) {
    // Désactiver la mise en cache pour éviter de servir l’identifiant d’un client à un autre.
    // La stratégie de Cache-control diffère selon les baselines SFRA, donc restez explicite.
    res.setHttpHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
    res.setHttpHeader('Pragma', 'no-cache');

    var site = Site.getCurrent();
    var tenantId = site.getCustomPreferenceValue('theWalletCrewTenantId');
    var secret = site.getCustomPreferenceValue('theWalletCrewHmacSecret');
    var passType = site.getCustomPreferenceValue('theWalletCrewDefaultPassType') || 'user';
    var externalKey = site.getCustomPreferenceValue('theWalletCrewExternalIdentifierKey') || 'sfcc.customerNo';
    var language = site.getCustomPreferenceValue('theWalletCrewLanguage');

    // Identifiant d’exemple : numéro du client connecté.
    var identifierValue = (customer && customer.profile) ? customer.profile.customerNo : null;

    if (!tenantId || !secret || !identifierValue) {
        res.render('TheWalletCrew/addToWalletButton', { enabled: false });
        return next();
    }

    var identifierHmac = theWalletCrewHmac.hmacSha256Hex(secret, identifierValue);

    // Recommandé dans les templates côté serveur : utiliser la forme d’attribut JSON.
    // Cela évite les pièges de casse des attributs HTML.
    var externalIdentifiers = {};
    externalIdentifiers[externalKey] = { value: identifierValue, hmac: identifierHmac };

    res.render('TheWalletCrew/addToWalletButton', {
        enabled: true,
        tenantId: tenantId,
        passType: passType,
        language: language,
        externalIdentifiersJson: JSON.stringify(externalIdentifiers)
    });

    return next();
});

module.exports = server.exports();
```

{% endcode %}

### 3) Partial ISML (`cartridge/templates/default/TheWalletCrew/addToWalletButton.isml`)

{% code title="cartridge/templates/default/TheWalletCrew/addToWalletButton.isml" %}

```html
<isif condition="${pdict.enabled}">
    <script type="text/javascript">
    (function (n, e, o) {
        var s=n.createElement("script");
        s.src="https://sdk.neostore.cloud/scripts/"+e+"/cinto@1";
        s.async=1;
        s.onload=function(){neostore.cinto.initialize(e,o);};
        n.body.appendChild(s);
    })(document, "${pdict.tenantId}", <isprint value="${pdict.language ? ('{ \"language\": \"' + pdict.language + '\" }') : '{ }'}" encoding="off" />);
    </script>

    <div
        data-neostore-addToWalletButton
        data-neostore-passType="${pdict.passType}"
        data-neostore-externalIdentifiers="<isprint value="${pdict.externalIdentifiersJson}" encoding="off" />">
    </div>
</isif>
```

{% endcode %}

{% hint style="warning" %}
Le script chargeur doit être inclus **une seule fois par page**. Lorsque plusieurs boutons sont rendus sur la même page, déplacez le chargeur dans un partial de pied de page partagé et ne conservez que le `<div data-neostore-addToWalletButton ...>` balisage dans le template du widget.
{% endhint %}

### 4) Inclure le widget dans un template de compte

Dans SFRA, les inclusions distantes sont couramment utilisées pour éviter d’encombrer le contrôleur principal et pour garder la mise en cache explicite.

{% code title="Exemple d’inclusion (ISML)" %}

```html
<isinclude url="${URLUtils.url('TheWalletCrew-Button')}" />
```

{% endcode %}

## Content Security Policy (CSP)

Certaines vitrines SFCC appliquent une Content Security Policy qui bloque par défaut les scripts tiers. Si le script SDK est bloqué, autorisez dans la liste blanche `https://sdk.neostore.cloud` pour `script-src` est requis.

Dans les projets SFRA, la CSP est souvent gérée via un middleware et des en-têtes de réponse. Le changement doit suivre la stratégie CSP existante du projet (report-only vs enforced).

## Valider le résultat

La validation couvre typiquement le rendu selon la plateforme et l’installation du pass.

1. Sur iOS Safari, le CTA doit afficher **Ajouter à Apple Wallet**.
2. Sur Android Chrome, le CTA doit afficher **Ajouter à Google Wallet**.
3. Sur bureau, le CTA doit rediriger vers une page de pass hébergée.
4. Après l’installation, le pass doit être visible dans Apple Wallet ou Google Wallet.

## Dépannage

### Le bouton ne s’affiche pas

Les causes fréquentes sont un script SDK bloqué (CSP) ou un balisage manquant. La page rendue doit contenir le chargeur SDK et l’élément conteneur cinto.

### Cliquer sur le bouton mène à une erreur

Cela signifie généralement que l’identifiant externe ou le HMAC ne correspond pas à ce que The Wallet Crew attend. Confirmez :

* la clé d’identifiant externe correspond à celle configurée pour SFCC (exemple : `sfcc.customerNo`)
* la valeur de l’identifiant correspond exactement à ce qui est signé côté serveur (sans trimming, formatage ou changement de type)
* le HMAC a été calculé avec le secret du tenant correct

### Le bouton s’affiche pour le mauvais client

Ceci indique généralement une mise en cache HTML entre sessions. Assurez-vous que :

* les pages spécifiques au client ne sont pas mises en cache au niveau de la page
* les inclusions distantes utilisées pour les widgets de compte ne sont pas mises en cache entre clients
* toute couche CDN respecte les cookies de session pour les pages authentifiées

## FAQ

<details>

<summary><strong>Qu’est-ce qu’un cartridge dans SFCC ?</strong></summary>

Un cartridge est l’unité déployable qui contient le code de la vitrine et les métadonnées. Le chemin de cartridges du site définit quels cartridges sont actifs et dans quel ordre ils sont résolus.

</details>

<details>

<summary><strong>Quel identifiant fonctionne le mieux pour les cartes de fidélité ?</strong></summary>

Le numéro client SFCC (`CustomerNo`) est couramment utilisé car il est stable et disponible sur les pages authentifiées. Un id CRM peut aussi être utilisé s’il est persistant sur le profil et reste stable dans le temps.

</details>

<details>

<summary><strong>Où le secret du tenant doit-il être stocké dans SFCC ?</strong></summary>

Les préférences personnalisées du site sont généralement utilisées car elles restent côté serveur et peuvent être gérées par site. Le secret ne doit jamais être exposé dans les templates, assets de contenu ou le JavaScript du navigateur.

</details>

<details>

<summary><strong>La même approche peut-elle être utilisée pour les cartes-cadeaux et le pick &#x26; collect ?</strong></summary>

Oui. Le même schéma fonctionne tant qu’un identifiant stable existe pour l’objet et que le bouton peut être rendu sur une page qui a accès à cet identifiant côté serveur.

</details>

<details>

<summary><strong>L’intégration nécessite-t-elle SFRA ?</strong></summary>

Non. SFRA et SiteGenesis peuvent tous deux supporter le même schéma HMAC côté serveur + rendu de template. Seuls les points d’entrée d’implémentation diffèrent (contrôleurs vs pipelines).

</details>
