# Adobe Commerce (Magento)

Cette intégration explique comment ajouter un **bouton unique « Ajouter à Wallet »** à la **Mon compte** zone dans **Adobe Commerce (Magento 2)**. Cette intégration Magento Apple Wallet / Google Wallet aide les clients à enregistrer une **carte de fidélité ou d’adhésion** (et éventuellement une **carte cadeau Wallet**) directement dans leur Wallet mobile, en utilisant un identifiant disponible dans la session client d’Adobe Commerce.

<figure><img src="https://3097111101-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWLc8AHXW4tdrAXUBfrYF%2Fuploads%2FCY6Kgh7mmUXLfmUr2Twl%2Fimage.png?alt=media&#x26;token=ecec6631-c67d-40f6-9a62-a84b2f4a9496" alt="Adobe Commerce My Account page showing an “Add to Wallet” button."><figcaption></figcaption></figure>

<details>

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

* Un programme de fidélité affiche « Ajouter à Wallet » à côté du numéro d’adhésion dans **Mon compte**.
* Un programme de carte cadeau affiche un bouton « Ajouter à Wallet » uniquement pour les **actives** cartes cadeaux.
* Un flux de retrait (pick & collect) affiche un CTA de pass de collecte sur une page dédiée au retrait, où une référence de collecte est disponible côté serveur.

</details>

Adobe Commerce et Magento Open Source partagent la même architecture de vitrine Magento 2. Les modèles d’implémentation de cette page s’appliquent aux deux, avec de légères différences dans le déploiement et la configuration de sécurité.

{% 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 %}

### 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 Adobe Commerce, le pass est récupéré en utilisant :

* A **type de carte** (exemple : `utilisateur`)
* Un identifiant client Magento envoyé en tant que **identifiant externe** (exemple : `magento.customer_Id`)
* 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 client stable disponible dans la session sur la page de compte. Cet identifiant est utilisé par The Wallet Crew pour récupérer ou créer le pass. Le nom de clé de l’identifiant (exemple : `magento.customer_Id`) est défini lors de l’intégration du projet.

### Ajouter le bouton à la page de compte client Adobe Commerce

Les pages de la vitrine Adobe Commerce sont construites à partir de **XML de layout** et **templates PHTML**. Rendre le bouton depuis un block Magento conserve les secrets côté serveur et permet un échappement sûr pour les attributs HTML.

L’implémentation de référence ci‑dessous ajoute le bouton à la page de tableau de bord client par défaut (`customer/account`).

#### Stocker le secret utilisé pour calculer le HMAC

Le secret HMAC doit rester côté serveur. Deux options courantes sont utilisées dans les projets Adobe Commerce.

**Option A (recommandée) : stocker les valeurs dans `app/etc/env.php`**

Stocker les valeurs dans la configuration de déploiement pour qu’elles ne soient pas modifiables depuis l’admin Adobe Commerce.

{% code title="app/etc/env.php (exemple)" %}

```php
<?php
return [
    // ...
    'walletcrew' => [
        'tenant_id' => '<<tenantId>>',
        'hmac_secret' => '<<hmacSecret>>',
    ],
];
```

{% endcode %}

**Option B : stocker le secret comme variable d’environnement**

Ceci maintient les secrets en dehors du code et de la base de données. Cela fonctionne aussi bien avec des déploiements en conteneur.

{% code title="Variables d’environnement (exemple)" %}

```
THEWALLETCREW_TENANT_ID=<<tenantId>>
THEWALLETCREW_HMAC_SECRET=<<hmacSecret>>
```

{% endcode %}

{% hint style="info" %}
Dans **Adobe Commerce Cloud**, les variables d’environnement sont souvent utilisées comme source de vérité pour les secrets. Le `app/etc/env.php` fichier est couramment généré pendant le déploiement.
{% endhint %}

{% hint style="warning" %}
Stocker le secret dans des fichiers de thème ou le rendre dans le HTML doit être évité. Seul le HMAC calculé doit atteindre la vitrine.
{% endhint %}

#### Implémentation de référence (layout XML + block + PHTML)

Cette implémentation crée :

* un **Block** classe qui lit l’id client connecté et calcule le HMAC
* un **template PHTML** qui rend le chargeur du SDK + `<div data-neostore-addToWalletButton ...>`
* un **XML de layout** entrée qui insère le block dans le tableau de bord du compte client

Elle nécessite également un scaffold minimal de module afin qu’Adobe Commerce puisse charger les fichiers.

{% code title="app/code/Vendor/WalletCrewCinto/registration.php" %}

```php
<?php
use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Vendor_WalletCrewCinto',
    __DIR__
);
```

{% endcode %}

{% code title="app/code/Vendor/WalletCrewCinto/etc/module.xml" %}

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_WalletCrewCinto" setup_version="1.0.0"/>
</config>
```

{% endcode %}

Après le déploiement des fichiers, activez le module et actualisez les fichiers générés et les caches.

{% code title="Activer le module (exemple)" %}

```bash
bin/magento module:enable Vendor_WalletCrewCinto
bin/magento setup:upgrade
bin/magento cache:flush
```

{% endcode %}

{% stepper %}
{% step %}

#### Ajouter le layout XML sur le tableau de bord client

Ajoutez une mise à jour de layout pour injecter un block dans le conteneur de contenu du tableau de bord.

{% code title="app/code/Vendor/WalletCrewCinto/view/frontend/layout/customer\_account\_index.xml" %}

```xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <block class="Vendor\WalletCrewCinto\Block\AddToWallet"
                   name="walletcrew.add_to_wallet"
                   template="Vendor_WalletCrewCinto::add-to-wallet.phtml"
                   cacheable="false"/>
        </referenceContainer>
    </body>
</page>
```

{% endcode %}

`cacheable="false"` assure que les identifiants spécifiques au client ne sont pas mis en cache entre les sessions.
{% endstep %}

{% step %}

#### Implémenter le Block (calculer le HMAC côté serveur)

Le block lit l’id du client connecté et calcule `hash_hmac('sha256', $value, $secret)`.

{% code title="app/code/Vendor/WalletCrewCinto/Block/AddToWallet.php" %}

```php
<?php
declare(strict_types=1);

namespace Vendor\WalletCrewCinto\Block;

use Magento\Customer\Model\Session as CustomerSession;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Escaper;
use Magento\Framework\View\Element\Template;

final class AddToWallet extends Template
{
    public function __construct(
        Template\Context $context,
        private readonly CustomerSession $customerSession,
        private readonly DeploymentConfig $deploymentConfig,
        private readonly Escaper $escaper,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }

    public function getTenantId(): string
    {
        $fromEnvPhp = (string)($this->deploymentConfig->get('walletcrew/tenant_id') ?? '');
        if ($fromEnvPhp !== '') {
            return $fromEnvPhp;
        }

        return (string)(getenv('THEWALLETCREW_TENANT_ID') ?: getenv('WALLETCREW_TENANT_ID'));
    }

    public function getCustomerId(): ?string
    {
        $id = $this->customerSession->getCustomerId();
        return $id ? (string)$id : null;
    }

    public function getCustomerIdHmac(): ?string
    {
        $customerId = $this->getCustomerId();
        if ($customerId === null) {
            return null;
        }

        $secret = (string)($this->deploymentConfig->get('walletcrew/hmac_secret') ?? '');
        if ($secret === '') {
            $secret = (string)(getenv('THEWALLETCREW_HMAC_SECRET') ?: getenv('WALLETCREW_HMAC_SECRET'));
        }
        if ($secret === '') {
            return null;
        }

        return hash_hmac('sha256', $customerId, $secret);
    }

    public function escAttr(?string $value): string
    {
        return $this->escaper->escapeHtmlAttr((string)$value);
    }
}
```

{% endcode %}

{% hint style="info" %}
Cet exemple utilise l’id client Magento comme `magento.customer_Id` . Si un id CRM est stocké sur l’entité client, il peut être utilisé à la place.
{% endhint %}
{% endstep %}

{% step %}

#### Rendre le bouton cinto dans un template PHTML

Le template injecte le tenant id, l’id client et le HMAC depuis le block.

{% code title="app/code/Vendor/WalletCrewCinto/view/frontend/templates/add-to-wallet.phtml" %}

```php
<?php
/** @var \Vendor\WalletCrewCinto\Block\AddToWallet $block */
$tenantId = $block->getTenantId();
$customerId = $block->getCustomerId();
$hmac = $block->getCustomerIdHmac();

if (!$tenantId || !$customerId || !$hmac) {
    return;
}
?>

<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, "<?= $block->escAttr($tenantId) ?>", { language: "fr" });
</script>

<div data-neostore-addToWalletButton
     data-neostore-passType="user"
     data-neostore-externalIdentifiers-magento.customer_Id-value="<?= $block->escAttr($customerId) ?>"
     data-neostore-externalIdentifiers-magento.customer_Id-hmac="<?= $block->escAttr($hmac) ?>"
></div>
```

{% endcode %}

Si une autre langue est requise, mettez à jour `{ language: "fr" }`. Si omis, le SDK utilise la langue du navigateur.

{% hint style="info" %}
La documentation complète du SDK cinto (options, repli bureau, et détection de plateforme) est disponible dans [Sur votre site web](https://app.gitbook.com/s/WLc8AHXW4tdrAXUBfrYF/enroll/on-your-website).
{% endhint %}
{% endstep %}
{% endstepper %}

#### Remarques sur le nom de l’identifiant externe

Les navigateurs mettent les attributs HTML en minuscules. Le SDK cinto inclut une règle d’échappement pour préserver les caractères majuscules en les préfixant par `_`.

C’est pourquoi `magento.customer_Id` est écrit avec un underscore avant `I`. Sans l’underscore, `customer_Id` serait interprété comme `customerid`.

#### Optionnel : liste d’autorisation CSP pour Adobe Commerce

Certaines configurations Adobe Commerce appliquent une Content Security Policy qui peut bloquer par défaut les scripts tiers. Si le script du SDK est bloqué, autorisez `https://sdk.neostore.cloud`.

{% hint style="info" %}
La configuration CSP de Magento diffère selon les versions et les configurations. Si CSP est activé, il faut l’aligner avec la stratégie CSP existante du projet.
{% endhint %}

Si le projet utilise le mécanisme de liste blanche CSP de Magento, autoriser l’hôte du SDK pour `script-src` est généralement suffisant.

{% code title="app/code/Vendor/WalletCrewCinto/etc/csp\_whitelist.xml (exemple)" %}

```xml
<?xml version="1.0"?>
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
    <policies>
        <policy id="script-src">
            <values>
                <value id="walletcrew-sdk" type="host">https://sdk.neostore.cloud</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>
```

{% endcode %}

{% hint style="warning" %}
Les projets Adobe Commerce centralisent souvent la gestion CSP (report-only vs enforced). L’emplacement et le processus de la liste d’autorisation doivent correspondre aux conventions du projet.
{% endhint %}

### 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 courantes sont un script SDK bloqué (CSP) ou un balisage manquant. Le HTML final doit contenir le chargeur du SDK cinto et le `<div data-neostore-addToWalletButton ...>` élément.

**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 :

* Le nom de clé de l’identifiant externe correspond à celui configuré pour Magento (exemple : `magento.customer_Id`).
* Le HMAC a été calculé avec le secret tenant correct et la valeur exacte de l’identifiant.

**La mauvaise langue s’affiche**

Définissez `{ language: "fr" }` (ou un autre code de langue ISO 639) dans le `initialize()` options.

### FAQ

<details>

<summary><strong>Cela nécessite-t‑il un module personnalisé ?</strong></summary>

Pas nécessairement. Une surcharge de thème peut suffire lorsque les valeurs côté serveur pour l’identifiant client et le HMAC peuvent être rendues. Un module personnalisé est généralement préféré car il maintient le calcul du HMAC hors des templates et centralise le chargement des secrets (configuration de déploiement ou variables d’environnement).

</details>

<details>

<summary><strong>Quel identifiant client doit être utilisé ?</strong></summary>

L’id interne client d’Adobe Commerce est couramment utilisé car il est stable et toujours disponible sur la page de compte. Un projet peut aussi utiliser un id CRM s’il est stocké sur l’entité client. L’identifiant doit rester stable dans le temps.

</details>

<details>

<summary><strong>D’où vient le secret HMAC ?</strong></summary>

Le HMAC est calculé avec un secret de tenant provenant de The Wallet Crew. Cela rend l’échange d’identifiants à l’épreuve des falsifications et empêche l’énumération. Le secret est généralement stocké dans la configuration de déploiement d’Adobe Commerce (`app/etc/env.php`) ou comme variable d’environnement et jamais exposé à la vitrine.

</details>

<details>

<summary><strong>La même approche peut‑elle être utilisée pour les cartes cadeaux ?</strong></summary>

Oui. Le même modèle de carte cadeau Wallet Magento fonctionne lorsque la carte cadeau est émise comme type de pass dans The Wallet Crew et qu’un identifiant de carte cadeau stable existe dans Adobe Commerce (ou dans un système connecté). La zone Mon compte peut afficher un bouton pour chaque carte cadeau en changeant `data-neostore-passType` et la clé/valeur de l’identifiant externe par l’identifiant de la carte cadeau, puis en signant cet identifiant avec HMAC côté serveur.

</details>

<details>

<summary><strong>Comment gérer la rotation des secrets ?</strong></summary>

Un plan de rotation utilise typiquement une courte fenêtre de chevauchement. Pendant cette fenêtre, le backend peut accepter à la fois l’ancien et le nouveau secret pour la validation HMAC. Après la fenêtre, seul le nouveau secret reste actif.

</details>

<details>

<summary><strong>Le bouton peut‑il être affiché ailleurs que sur la page de compte ?</strong></summary>

Oui. Le même SDK peut être utilisé sur n’importe quelle page authentifiée où un identifiant stable est disponible côté serveur. Des exemples courants incluent une page de liste de cartes cadeaux, un tableau de bord de fidélité, ou une page pick & collect lorsqu’un pass de retrait est utilisé comme jeton de scan en magasin.

</details>
