# Your own integration

How developers of shops on other platforms mark the cart with data-punchout-cart and build a login endpoint for auto-login with the Custom adapter.

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

This page is for developers who connect a shop on a platform without a punchout.dev plugin. You mark the cart in the template and, if you want auto-login, build the login endpoint.

Both steps are optional. The script tag alone works on any shop, see [Install the tag](https://punchout.dev/docs/getting-started/install-the-tag/) and [Cart detection](https://punchout.dev/docs/getting-started/cart-detection/).

## Before you start

1. On the shop's **General** tab in the portal, set **Shop platform** to **Other**.
2. Put the script tag from the **Embed** tab on every page of the shop, ideally in the layout template:

```html
<script async src="https://js.punchout.dev/pob.js" data-shop-id="pob_live_…"></script>
```

The tag must run at least on the page a punchout lands on (the **Landing path**) and on the cart page. Keep the landing URL's query string intact, through redirects too, because the punchout depends on it.

## 1. Mark the cart container

Wrap the line items of the cart page in one element with the attribute `data-punchout-cart`. The attribute needs no value:

```html
<div data-punchout-cart>
  <table class="cart-items">
    <!-- one row per line item: name, article number, quantity, unit price, line total -->
  </table>
</div>
```

How punchout.dev treats it:

| Rule | Detail |
| --- | --- |
| First match wins | punchout.dev reads the first element with the attribute on the page. Render it once. |
| It takes precedence | A marker takes precedence over a cart container stored on the **Cart** tab. With a marker, no cart detection is needed. |
| It means "this page has a cart" | When the marker is on a page during a punchout, punchout.dev reads the cart there right away. |
| Quantity fields count | A quantity in an input field, such as `<input value="2">`, is read like visible text. The contents of password, email, phone and card fields are never read. |

What to put inside, and what not:

- **Only on the real cart page.** Never mark a mini cart, a cart drawer, an off-canvas panel or a popup. Such an element exists on many pages and only while it is open. A marker on it makes the tag read the drawer on every page.
- **The line items, not the whole page.** Keep the order summary, coupon forms, shipping estimates and the checkout button outside. The punchout.dev plugins wrap the line-item table wherever the platform's template allows it.
- **Leave it out for an empty cart.** An empty marker still counts as a cart, and the panel ends with "Analysis failed.".
- **Client-rendered carts are fine.** If your cart's line items are rendered by JavaScript after load, put the marker on a container that is already in the server HTML. The tag waits until the cart has finished loading before it reads it.

Check the marker with the setup self-test: create a setup link on the shop's **Setup** tab, open it on the cart page with 2 or 3 products in the cart and click **Detect cart**. Before the click the panel says "Shop plugin detected — the cart is already marked.", and the result names the source **Shop plugin**. Confirm with **Yes, that's it** to complete the **Cart detected** step.

## 2. Build the login endpoint (optional)

Auto-login needs an endpoint on your shop that accepts a signed login token, signs the right customer in and redirects. See [Auto-login](https://punchout.dev/docs/plugins/auto-login/) for what it does for the buyer.

The [login token specification](https://punchout.dev/docs/plugins/custom/login-token/) is the contract. Your endpoint must:

1. Accept `GET <login path>?token=<token>`.
2. Verify the token's signature with the public key line from the portal. The specification describes the token format.
3. Run the checks in the order the specification gives: it comes from punchout.dev and is a login token, it was issued for your Shop ID, it is within its validity window of 120 seconds, it has not been used before (record each use atomically, for example under a unique database key), and its `next` is a relative path.
4. Find exactly one customer for `customer.kind` and `customer.value` and sign them in.
5. Redirect to `next` on your own shop, unchanged. Answer with `Cache-Control: no-store` and `Referrer-Policy: no-referrer`.
6. On any failure, redirect to `next` without signing anyone in. The punchout must continue as a guest.

Never log the token or `customer.value`.

### Test vector

The [login token specification](https://punchout.dev/docs/plugins/custom/login-token/) describes a test vector for checking your endpoint:

| Part | Content |
| --- | --- |
| Public key | A test key's public key, the value your endpoint is configured with. A test key only, never a real one. |
| Token | One token signed with the matching test key |
| Claims | The decoded payload of that token |

The test token verifies for the Shop ID `pob_live_test0000000000000000` within its validity window. That window lies in the past, so pass a fixed "now" inside it when you test against it.

The punchout.dev plugins implement the same checks in PHP. Their source ships in the plugin zips, each with a `bin/selftest.php` that can verify a token.

## 3. Configure the portal for your endpoint

The shop's **Auto-login** tab has an adapter for this case.

| Field | Value |
| --- | --- |
| Enable auto-login | On |
| Adapter | **Custom adapter** |
| Login path | The path of your endpoint, starting with `/` (not `//` or `/\`), for example `/punchout/login` |
| Customer reference | **Customer ID**, **Customer number** or **Email address**. It arrives as `customer.kind` (`id`, `number` or `email`); your endpoint decides what it matches. |

Then:

- Paste every line from the section **Public keys** into your endpoint's configuration and accept tokens signed with any of them. The specification describes the line format.
- Give each buyer credential a **Shop customer reference** on the **Buyers** tab.
- Set the **Landing path** on the General tab to the full path of your cart page. It becomes the token's `next`.

The login path is appended to the shop's first entry under **Allowed origins** on the General tab. The adapter only records which endpoint the shop runs and changes nothing in the token.

## Related

- [Shop plugins](https://punchout.dev/docs/plugins/) for Shopware, WooCommerce and Magento
- [Auto-login](https://punchout.dev/docs/plugins/auto-login/), including the reason words the plugins log
- [Troubleshooting](https://punchout.dev/docs/troubleshooting/)
