conneqt/module-punchout

Punchout System

Maintainers

Package info

git.dev.epartment.nl/conneqt/m2/module-punchout

Type:magento2-module

pkg:composer/conneqt/module-punchout

Statistics

Installs: 1 174

Dependents: 0

Suggesters: 0

0.3.6 2026-02-26 10:59 UTC

README

conneqt/module-punchout

What this module does

This module adds PunchOut support to Magento 2 so a customer can start in a procurement system, punch into Magento, add products to a cart, and send the cart back to the procurement or ERP system.

The module currently supports:

  • OCI punchout
  • OCI AFAS variant
  • cXML PunchOutSetupRequest / PunchOutOrderMessage flow

In short, the module does four things:

  1. Accepts a punchout login request from an external system.
  2. Maps that request to a Magento customer using a PunchoutSystem and PunchoutLink.
  3. Lets the customer shop inside Magento in a normal storefront session.
  4. Returns the selected cart back to the external system as OCI form fields or a cXML PunchOutOrderMessage.

It also stores inbound and outbound traffic in a Request Log, which is useful when testing or troubleshooting integrations.

Contents

Installation

In production, use the --keep-generated flag where appropriate.

Install from a zip file

  1. Unzip the module to app/code/Conneqt/Punchout
  2. Enable the module:
php bin/magento module:enable Conneqt_Punchout
  1. Apply database and data patches:
php bin/magento setup:upgrade
  1. Flush cache:
php bin/magento cache:flush

Install with Composer

composer require conneqt/module-punchout
php bin/magento module:enable Conneqt_Punchout
php bin/magento setup:upgrade
php bin/magento cache:flush

How it works

OCI flow

  1. The procurement system sends an HTTP request to:

    {base-url}/conneqt-punchout/index/login
    
  2. Controller/Index/Login.php validates the required OCI parameters:

    • USERNAME
    • PASSWORD
    • HOOK_URL
  3. Helper/Login.php finds a matching PunchoutLink and verifies that:

    • the link contains identifier rows for USERNAME and PASSWORD
    • those values match the customer attributes punchout_username and punchout_password
  4. If the request is valid, the customer is logged into Magento and the punchout request data is stored in the session.

  5. While the customer shops, the observers:

    • Observer/AddToCart.php
    • Observer/RemoveFromCart.php

    keep the punchout item table in sync with the quote.

  6. When the customer opens checkout, Observer/PunchoutAction.php redirects the normal checkout flow to:

    /conneqt-punchout/index/customcheckout
    
  7. Block/Response/Data.php and Helper/Data.php convert the quote into OCI response fields such as NEW_ITEM-... entries.

  8. view/frontend/templates/postData.phtml auto-posts those fields back to the external HOOK_URL.

cXML flow

  1. The procurement system sends a cXML PunchOutSetupRequest to:

    {base-url}/conneqt-punchout/index/auth
    
  2. Controller/Index/Auth.php parses the cXML request and extracts these values:

    • From/Credential/@domain
    • Sender/Credential/Identity
    • Sender/Credential/SharedSecret
    • BuyerCookie
    • BrowserFormPost/URL
  3. Helper/CxmlLogin.php validates the request against Magento configuration.

  4. If authentication succeeds, Magento creates a RequestLog entry with the intermediate cXML auth status and returns a cXML PunchOutSetupResponse with a Magento start page URL.

  5. That start page points to Controller/Index/CreateSession.php, which re-opens the stored request, logs the customer into Magento, and writes the final punchout session data into Magento's session.

  6. The customer shops in Magento.

  7. At checkout return time, Helper/Data.php::createCxmlData() builds a cXML PunchOutOrderMessage.

  8. view/frontend/templates/postData.phtml posts it back to the BrowserFormPost/URL as the cXML-urlencoded form field.

Magento admin configuration

Global configuration

Path:

Stores > Configuration > Conneqt Punchout > General

Defined in:

  • etc/adminhtml/system.xml

Important settings:

  • Enable: must be set to Yes
  • Max Description length: used when generating outbound item descriptions
  • Magento Attribute Mapping: available for additional mapping use cases

Punchout System

Path:

Conneqt > PunchoutSystem

Defined in:

  • view/adminhtml/ui_component/conneqt_punchout_punchoutsystem_form.xml

Fields:

  • System Name
  • Type
    • oci
    • oci_afas
    • cxml
  • Shared Secret
    • shown for cxml
    • used by Helper/CxmlLogin.php

Punchout Link

Path:

Conneqt > PunchoutLink

Defined in:

  • view/adminhtml/ui_component/conneqt_punchout_punchoutlink_form.xml

Fields:

  • Website ID (store_id in the form)
  • customer_id
  • punchoutsystem_id
  • Identifier Mapping dynamic rows
    • Identifier
    • Value

This entity links:

  • a Magento website
  • a Magento customer
  • a punchout system definition
  • the values used to authenticate the punchout request

OCI identifier mapping

For OCI, create at least these two rows:

IdentifierValue
USERNAMEthe punchout username
PASSWORDthe punchout password

cXML identifier mapping

For cXML, the current implementation expects the mapping to work like this:

  • Identifier = the value from From/Credential/@domain
  • Value = the value from Sender/Credential/Identity

Example:

IdentifierValue
NetworkIdbuyer@example

The cXML shared secret is not stored in the link. It is read from the selected PunchoutSystem record.

Customer attributes

Defined by:

  • Setup/Patch/Data/CustomerAttribute.php

Added customer attributes:

  • punchout_username
  • punchout_password

These are used directly by the OCI login flow in Helper/Login.php.

For OCI, make sure the customer values match the values used in the PunchoutLink identifier mapping.

Note: there is also Setup/Patch/Data/CustomerSecretKeyAttribute.php, but the current cXML login flow does not use that customer attribute for authentication. cXML authentication uses PunchoutSystem.shared_secret instead.

Request Log

Path:

Conneqt > RequestLog

Stored in:

  • conneqt_punchout_requestlog
  • defined in etc/db_schema.xml

Useful statuses from Api/Data/RequestLogInterface.php:

StatusMeaning
0Invalid Request
1Session Active
2Closed
3cXML auth complete
4cXML session active

Use the request log to confirm:

  • whether Magento accepted the inbound punchout request
  • whether cXML authentication completed
  • what request data was received
  • what response data Magento sent back

Important files

The files below are a good starting point when you want to understand or debug the module.

FileWhat it does
Controller/Index/Login.phpEntry point for OCI punchout login requests
Helper/Login.phpMatches OCI requests to PunchoutLink + customer credentials and writes request logs
Controller/Index/Auth.phpEntry point for cXML PunchOutSetupRequest
Controller/Index/CreateSession.phpCompletes the cXML login by converting the temporary auth token into a Magento session
Helper/CxmlLogin.phpcXML-specific authentication and setup response generation
Helper/Data.phpConverts the Magento quote into OCI or cXML response payloads
Block/Response/Data.phpMakes the punchout response data available to the frontend template
view/frontend/templates/postData.phtmlAuto-posts the generated payload back to the procurement system
Observer/PunchoutAction.phpRedirects checkout to the punchout return page
Observer/AddToCart.phpStores punchout item snapshots when products are added
Observer/RemoveFromCart.phpRemoves punchout item snapshots when products are removed
view/adminhtml/ui_component/conneqt_punchout_punchoutsystem_form.xmlAdmin form definition for punchout system records
view/adminhtml/ui_component/conneqt_punchout_punchoutlink_form.xmlAdmin form definition for link records
etc/db_schema.xmlDefines the punchoutsystem, punchoutlink, requestlog, and punchoutitems tables

Testing with PunchOutCommerce tools

These instructions are based on the current implementation in this module.

OCI RoundTrip Tester

Tool:

https://punchoutcommerce.com/tools/oci-roundtrip-tester

Magento setup before testing

  1. Enable the module in:

    Stores > Configuration > Conneqt Punchout > General > Enable = Yes
    
  2. Create or choose a customer.

  3. Fill the customer's custom attributes:

    • punchout_username
    • punchout_password
  4. Create a PunchoutSystem with:

    • Type = OCI or OCI AFAS
  5. Create a PunchoutLink with:

    • Website ID = website for the customer/storefront
    • customer_id = the customer that should be logged in
    • punchoutsystem_id = the OCI system you created
    • Identifier Mapping rows:

      IdentifierValue
      USERNAMEsame as customer punchout_username
      PASSWORDsame as customer punchout_password

punchout-link.png

Which tester fields map to Magento?

On the OCI tester page, use these values:

OCI tester fieldFill in
URL{base-url}/conneqt-punchout/index/login
USERNAMEthe same value as PunchoutLink identifier USERNAME and customer punchout_username
PASSWORDthe same value as PunchoutLink identifier PASSWORD and customer punchout_password
HOOK_URLleave the tester default or use your own receiver URL
~TARGETusually leave default
~OkCodeusually leave default
~CALLERusually leave default
extra name/value pairsoptional; this module ignores most extra OCI fields unless you custom-handle them

Test steps

  1. Open the OCI tester page.
  2. Fill in the fields from the table above.
  3. Submit the tester form.
  4. Magento should log in the configured customer and open the storefront.
  5. Add one or more products to the cart.
  6. Open checkout or the cart flow that triggers checkout redirection.
  7. Magento should redirect to the punchout response page and auto-post the cart back to the tester HOOK_URL.
  8. The tester should show the returned OCI form fields.

If OCI login fails

Check:

  • module enabled flag
  • PunchoutLink belongs to the correct website and customer
  • link has USERNAME and PASSWORD identifier rows
  • customer punchout_username and punchout_password match exactly
  • request log entry in Conneqt > RequestLog

cXML PunchOut Tester

Tool:

https://punchoutcommerce.com/tools/cxml-punchout-tester

Magento setup before testing

  1. Enable the module:

    Stores > Configuration > Conneqt Punchout > General > Enable = Yes
    
  2. Create or choose a customer.

  3. Create a PunchoutSystem with:

    • Type = CXML
    • Shared Secret = the value you will also enter into the tester as SharedSecret
  4. Create a PunchoutLink with:

    • Website ID = storefront website to use
    • customer_id = customer to log in
    • punchoutsystem_id = the CXML punchout system
    • Identifier Mapping row where:

      IdentifierValue
      same as FromDomainsame as SenderIdentity

    Example:

    IdentifierValue
    NetworkIdbuyer@example

Important: in the current implementation, cXML authentication is matched using From/Credential/@domain, Sender/Credential/Identity, and Sender/Credential/SharedSecret.

Which tester fields map to Magento?

The cXML tester has a top URL field and a variable editor / XML template.

Use the values below:

cXML tester field / variableFill in
URL{base-url}/conneqt-punchout/index/auth
SupplierSetupURL{base-url}/conneqt-punchout/index/auth
FromDomainsame as the Identifier value in Magento, for example NetworkId
SenderIdentitysame as the Value in the Magento identifier row
SharedSecretsame as PunchoutSystem > Shared Secret
PunchoutOrderReceiverusually leave the tester default https://punchoutcommerce.com/tools/cxml-punchout-return
BuyerCookieleave generated/default value or enter your own test value
FromIdentitynot validated by the current module; can be left as your buyer identifier
ToDomain / ToIdentitynot validated by the current module; can be left at tester defaults or supplier identifiers

Where to fill the values in the cXML tester page

The tester page usually shows:

  • a top URL field
  • a variable list with names like FromDomain, SenderIdentity, SharedSecret, BuyerCookie, PunchoutOrderReceiver, and SupplierSetupURL
  • the full XML textarea containing placeholders such as @FromDomain@ and @SharedSecret@

Recommended approach:

  1. Set the top URL field to:

    {base-url}/conneqt-punchout/index/auth
    
  2. In the variable list, set:

    • FromDomain
    • SenderIdentity
    • SharedSecret
    • SupplierSetupURL
  3. Leave PunchoutOrderReceiver pointing to the tester return URL unless you want to receive the cXML elsewhere.

  4. Make sure the XML template contains:

    • <BrowserFormPost><URL>@PunchoutOrderReceiver@</URL></BrowserFormPost>
    • <SupplierSetup><URL>@SupplierSetupURL@</URL></SupplierSetup>

Test steps

  1. Open the cXML tester page.
  2. Fill in the fields and variables from the mapping above.
  3. Submit the tester.
  4. Magento should authenticate the cXML request and return a PunchOutSetupResponse.
  5. Follow the returned start page into Magento.
  6. Add products to the cart.
  7. Open checkout so the punchout response page is triggered.
  8. Magento should post a cXML-urlencoded field back to the tester's PunchoutOrderReceiver URL.
  9. The tester should display the returned PunchOutOrderMessage.

If cXML login fails

Check:

  • module enabled flag
  • PunchoutSystem type is cxml
  • PunchoutSystem.shared_secret matches the tester SharedSecret
  • PunchoutLink points to the correct customer and system
  • identifier mapping matches FromDomain -> SenderIdentity
  • Conneqt > RequestLog status changes to CXML Auth complete and then CXML Session Active

Troubleshooting notes

  • The frontend route for this module is defined in etc/frontend/routes.xml as:

    conneqt-punchout
    
  • Normal public entry URLs are:

    /conneqt-punchout/index/login
    /conneqt-punchout/index/auth
    /conneqt-punchout/index/customcheckout
    
  • Observer/PunchoutAction.php only redirects checkout when punchout session data contains a HOOK_URL.

  • The cXML storefront return flow uses Helper/Data.php::createCxmlData(). The separate method Helper/CxmlLogin.php::generatePunchoutOrderMessage() exists, but it is not the main storefront return path used by the postData.phtml template.

  • Controller/Index/CustomCheckoutCart.php currently looks like development/debug code and is not part of the main documented flow.

  • If the cart returns empty data, first verify that products were added after the punchout session was created and that conneqt_punchout_punchoutitems is being populated.

Database entities created by this module

Defined in etc/db_schema.xml:

  • conneqt_punchout_punchoutsystem
  • conneqt_punchout_punchoutlink
  • conneqt_punchout_requestlog
  • conneqt_punchout_punchoutitems

These tables store:

  • punchout system definitions
  • customer-to-system authentication mappings
  • inbound/outbound request logs
  • staged item data used when building outbound punchout responses