# Magento 2

Install and configure the punchout.dev module PunchoutDev_Bridge for Magento 2.4, with the cart marker, auto-login and the Content Security Policy.

URL: https://punchout.dev/docs/plugins/magento/

The punchout.dev module `PunchoutDev_Bridge` for Magento 2.4 prints the script tag, marks the cart page for punchout.dev and signs punchout buyers in to their customer account.

## Requirements

| Requirement | Value |
| --- | --- |
| Magento | 2.4, Open Source or Commerce |
| PHP | 8.1 to 8.4, with `sodium` and `json` |
| Module | `PunchoutDev_Bridge` (composer name `punchout-dev/module-bridge`), version 2.0.0, no Composer dependencies |
| Download | `PunchoutDev-Bridge-<version>.zip` from the shop's **Embed** tab in the portal (Shop platform: Magento) |

## Install

The zip already contains the folders `PunchoutDev/Bridge/`. Unpack it into `app/code`, so the module lands in `app/code/PunchoutDev/Bridge`. Then run:

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

In production mode, also run:

```bash
bin/magento setup:di:compile
bin/magento setup:static-content:deploy
```

`setup:upgrade` creates one table for single-use login tokens. Nothing else in the database is touched.

## Configure

Open **Stores → Configuration → punchout.dev → General**. You can set each value at default, website or store-view scope. The configuration paths start with `punchout_dev/general/`.

| Setting | Path | Value |
| --- | --- | --- |
| Embed the snippet | `snippet_enabled` | Yes (default). No prints no script tag. |
| Snippet URL | `snippet_url` | `https://js.punchout.dev/pob.js`, from the portal's Embed tab. Empty prints nothing. |
| API base URL (optional) | `api_base` | Leave empty. |
| Enable auto-login | `enabled` | Yes (default). |
| Shop ID | `tenant_id` | The **Shop ID** from the portal's Embed tab. Also printed as `data-shop-id` on the script tag. |
| Public key lines | `public_keys` | Every line from the portal's Auto-login tab, section **Public keys**, one per line. Lines starting with `#` and blank lines are ignored. |

The same values from the command line:

```bash
bin/magento config:set punchout_dev/general/tenant_id pob_live_…
bin/magento config:set punchout_dev/general/public_keys "k1=…"
bin/magento config:set punchout_dev/general/snippet_url https://js.punchout.dev/pob.js
bin/magento cache:flush
```

The module prints the script tag into `before.body.end` on every storefront page.

## What the module marks

The module puts an `afterToHtml` interceptor on `Magento\Checkout\Block\Cart\Grid` and wraps its output in `<div data-punchout-cart>`. It acts only on the block named `checkout.cart.form`, the cart form on the cart page, so a mini cart or another listing built from the same class is never marked. The marker follows the block class, not the template, and works in Luma and Hyvä alike. An empty cart is left unmarked.

## Auto-login

The module's login endpoint is `GET /punchout-dev/login?token=…`. It is a frontend route and never resolves in the admin area. Set up the portal side on the shop's **Auto-login** tab:

| Portal field | Value |
| --- | --- |
| Adapter | Magento |
| Login path | `/punchout-dev/login`, prefixed with the store's base path when the shop does not sit at the domain root, for example `/shop/pub/punchout-dev/login` |
| Customer reference | Email address (recommended) or Customer ID |

| Customer reference | Matched against |
| --- | --- |
| Email address | A customer of the current website with that address |
| Customer ID | The Magento customer entity ID, on the current website |
| Customer number | Not supported |

:::caution
Magento has no customer number. With **Customer number** selected, the module refuses every token with reason `unsupported_ref_kind` and the buyer lands as a guest. The portal's Auto-login tab says so when the adapter is Magento. Use Email address or Customer ID.
:::

Then give each buyer credential its **Shop customer reference** on the **Buyers** tab. A customer still waiting for their email confirmation is not signed in. Admin users cannot be signed in this way at all: they live in a separate table that the customer login never reads.

After the login, the module redirects to the landing path as it is, without adding the store's base path. Set the **Landing path** on the General tab to the full cart path, for example `/checkout/cart/`, or `/shop/pub/checkout/cart/` for a store under a sub-path.

The full flow is on the [Auto-login](https://punchout.dev/docs/plugins/auto-login/) page.

## Content Security Policy

Magento 2.4 ships the `Magento_Csp` module. In report-only mode, the script tag loads without changes. If the storefront enforces its policy, the policy must allow the script tag's sources. The punchout.dev module ships no `csp_whitelist.xml`, because the snippet URL is a setting. The portal's **Embed** tab shows the four lines for your shop:

```txt
script-src https://js.punchout.dev
connect-src https://api.punchout.dev
style-src 'unsafe-inline'
form-action https://<host of each buyer system>:*
```

`style-src 'unsafe-inline'` is needed because the panel's styles are inline. `form-action` is needed because the cart is handed to the buyer system from the shop page. The Embed tab builds that line from **Allowed return hosts** on the General tab, one `https://<host>:*` per host, and shows the placeholder `https://buyer-system.example` while the list is empty. For the [test punchout](https://punchout.dev/docs/getting-started/test-punchout/), `form-action` must also allow `https://api.punchout.dev`. The full explanation is on [Install the tag](https://punchout.dev/docs/getting-started/install-the-tag/).

## Setup self-test

1. On the shop's **Setup** tab in the portal, create a setup link (valid 60 minutes by default).
2. Put 2 or 3 different products in the cart.
3. Open the cart page with the setup link and click **Detect cart**.

With the module in place, the result names the source **Shop plugin**. Confirm with **Yes, that's it**. That completes the **Cart detected** step in the portal.

## Update

Download the new zip from the Embed tab, unpack it over `app/code/PunchoutDev/Bridge`, then run `bin/magento setup:upgrade` and `bin/magento cache:flush` (plus the two production-mode commands). Magento can keep serving the previous template after files are replaced in place. Clear its preprocessed copy as well:

```bash
rm -rf var/view_preprocessed/pub/static/app/code/PunchoutDev
```

### Coming from PunchoutNinja_Bridge 1.x

To Magento, 2.0.0 is a new module: run `bin/magento module:disable PunchoutNinja_Bridge`, delete `app/code/PunchoutNinja` and never run both. Enter the values again under **Stores → Configuration → punchout.dev** and change the portal's **Login path** to `/punchout-dev/login`.

## Troubleshooting

The module writes one info line per login to `var/log/system.log`, with a reason word when it refuses the token. It never logs the token or the customer. Magento-specific reasons:

| Reason | Meaning |
| --- | --- |
| `unsupported_ref_kind` | The portal's Customer reference is Customer number |
| `customer_unresolved` | No customer matches on this website |
| `customer_wrong_website` | The customer ID belongs to another website |
| `customer_unconfirmed` | The customer has not confirmed their email address |
| `store_unresolved` | The current store could not be determined |

The common reason words are on the [Auto-login](https://punchout.dev/docs/plugins/auto-login/) page. For more, see [Troubleshooting](https://punchout.dev/docs/troubleshooting/).

## Full README

The module's README covers the token checks and the development tests: [Magento module README](https://punchout.dev/docs/plugins/magento/readme/).
