# Salesforce Commerce Cloud (SFCC)

Cette intégration explique comment ajouter un **bouton unique « Ajouter à Wallet »** à une boutique 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 cadeau). Les pages de click & collect peuvent également convenir lorsqu’une carte de retrait est utilisée comme jeton de scan en magasin.

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

<details>

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

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

</details>

{% hint style="info" %}
Pour l’utilisation générique du SDK cinto (mode d’ID de carte, détection de plateforme, personnalisation du QR code sur desktop), voir [Sur votre site web](https://github.com/TheWalletCrew/docs/blob/main/enroll/on-your-website.md).
{% endhint %}

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

Le code de la boutique Salesforce Commerce Cloud est empaqueté et déployé sous forme de **cartridges**. Une cartridge peut ajouter des contrôleurs, des scripts, des templates et des métadonnées de configuration, puis être branchée à un site en l’ajoutant au **chemin de cartridge**.

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

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

### Cartridge

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

### SFRA vs SiteGenesis

Les deux architectures peuvent prendre en charge l’intégration :

* **SFRA**: contrôleurs et templates ISML. Le bouton est généralement ajouté dans un template de compte ou un remote include.
* **SiteGenesis**: pipelines (hérités) 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 du navigateur.

## Fonctionnement

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

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

Dans cette configuration SFCC, la carte est récupérée à l’aide de :

* un **type de carte** (exemple : `utilisateur`)
* un identifiant SFCC stable envoyé comme **identifiant externe** (exemple : `sfcc.customerNo`)
* une **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 carte et une émission de carte déjà configurés dans The Wallet Crew.
* L’ID de tenant The Wallet Crew disponible (utilisé par l’URL du script du SDK).
* Un identifiant stable disponible sur les pages authentifiées, par exemple :
  * SFCC `CustomerNo` (typique pour les cartes de fidélité)
  * référence de retrait (typique pour les cartes de retrait click & collect)
  * ID de carte cadeau (lorsque les cartes cadeau sont stockées dans SFCC ou dans un système connecté)
* Le nom de clé de l’identifiant externe convenu lors de l’onboarding (exemple : `sfcc.customerNo`).

## Implémenter l’intégration sous forme de cartridge

Le schéma typique est :

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

{% stepper %}
{% step %}

#### Ajouter la cartridge au chemin des cartridges

Après avoir déployé la cartridge d’intégration sur l’instance, ajoutez-la au chemin des cartridges du site dans Business Manager. La cartridge doit être placée **avant** la cartridge storefront de base afin que les surcharges soient résolues correctement.

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

{% step %}

#### Stocker la configuration du tenant comme des 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 **des préférences de site personnalisées**.

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 généralement créées comme **des préférences personnalisées au niveau du site** (et non au niveau de l’organisation).

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

{% hint style="info" %}
Conserver le secret comme une préférence de **Mot de passe** aide à éviter toute divulgation accidentelle dans les exports UI et les captures d’écran.
{% endhint %}

</details>

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

{% step %}

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

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

Choix d’implémentation courants :

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

Le cache doit être géré avec prudence. Les pages spécifiques à un client ne doivent pas partager du HTML mis en cache entre clients lorsque l’identifiant est intégré au balisage.
{% endstep %}

{% step %}

#### Rendre le bouton « Ajouter à Wallet » dans ISML

Le SDK cinto repose sur :

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

La cartridge d’intégration fournit généralement un partial ISML qui peut être inclus là où c’est nécessaire (tableau de bord du compte, liste de cartes cadeau, page click & collect).
{% endstep %}
{% endstepper %}

## Conventions d’identifiant externe (casse + échappement)

Le SDK cinto peut récupérer une carte à 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 de nommage recommandée pour les clés

L’option la plus stable consiste à éviter les majuscules/minuscules mixtes dans les clés d’identifiant.

Les clés sont généralement :

* préfixées par un namespace 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 modifier peut avoir un impact sur les installations existantes. Un changement de nom de clé doit être considéré comme un sujet de migration.
{% endhint %}

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

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

* Préfixez un caractère majuscule avec `_` 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`

Cela n’affecte que le **nom de l’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 :

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

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

### Structure des fichiers de la 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 brute de l’identifiant (UTF-8), encodé en hexadécimal.
 * 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 rend un widget sûr à inclure sous forme de remote include.

{% 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ésactive le cache pour éviter de servir l’identifiant d’un client à un autre.
    // La stratégie de cache-control diffère selon les bases SFRA, donc gardez-la 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');

    // Exemple d’identifiant : numéro de 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 : utilisez 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 chargeur de script doit être inclus **une 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 gardez 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 remote includes sont couramment utilisés pour éviter de polluer le contrôleur principal et pour garder le cache explicite.

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

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

{% endcode %}

## Content Security Policy (CSP)

Certaines boutiques SFCC appliquent une Content Security Policy qui bloque les scripts tiers par défaut. Si le script du SDK est bloqué, il faut autoriser `https://sdk.neostore.cloud` pour `script-src` est nécessaire.

Dans les projets SFRA, la CSP est souvent gérée via des middlewares et des en-têtes de réponse. Le changement doit suivre la stratégie CSP existante du projet (mode rapport uniquement vs mode imposé).

## Valider le résultat

La validation couvre généralement le rendu de la plateforme et l’installation de la carte.

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

## Dépannage

### Le bouton ne s’affiche pas

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

### Cliquer sur le bouton provoque une erreur

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

* le nom de la clé d’identifiant externe correspond à celui configuré pour SFCC (exemple : `sfcc.customerNo`)
* la valeur de l’identifiant correspond exactement à ce qui est signé côté serveur (aucune coupure, mise en forme ou modification de type)
* le HMAC a été calculé avec le bon secret du tenant

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

Cela indique généralement un cache HTML partagé entre les sessions. Assurez-vous que :

* les pages spécifiques à un client ne sont pas mises en cache au niveau de la page
* les remote includes utilisés pour les widgets de compte ne sont pas mis 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’une cartridge dans SFCC ?</strong></summary>

Une cartridge est l’unité déployable qui contient le code storefront et les métadonnées. Le chemin de cartridges du site définit quelles cartridges sont actives et dans quel ordre elles sont résolues.

</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 également être utilisé s’il est conservé 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 de site personnalisées 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, les ressources de contenu ou le JavaScript du navigateur.

</details>

<details>

<summary><strong>La même approche peut-elle être utilisée pour les cartes cadeau et le click &#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 prendre en charge le même schéma HMAC côté serveur + rendu de template. Seuls les points d’entrée de l’implémentation diffèrent (contrôleurs vs pipelines).

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thewalletcrew.io/connectors/fr/e-commerce/salesforce-commerce-cloud.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
