Skip to main content

cXML Response Generator

The CxmlConnectorResponseGenerator provides three methods: one for the login handshake, one for error responses, and one for the actual cart transfer.


generatePunchoutSetupResponse()

public function generatePunchoutSetupResponse(
string $cxmlString,
string $startUrl,
SalesChannelContext $context
): string

Generates the cXML response to the procurement system's PunchOutSetupRequest. It contains a StartPage URL to which the procurement system redirects the buyer's browser.

Response structure:

<cXML payloadID="..." timestamp="..." xml:lang="en-US">
<Response>
<Status code="200" text="OK"/>
<PunchOutSetupResponse>
<StartPage>
<URL>https://shop.example.com/AgiqonCxmlEntry/auth/...</URL>
</StartPage>
</PunchOutSetupResponse>
</Response>
</cXML>

generateInvalidPunchoutSetupResponse()

public function generateInvalidPunchoutSetupResponse(
SimpleXMLElement $requestXml,
int $statusCode,
string $text,
string $message
): string

Generates an error response (e.g. 400 Bad Request, 401 Unauthorized) when login fails.


generateCxmlPunchOutOrderMessage()

public function generateCxmlPunchOutOrderMessage(
ConnectorSession $session,
CxmlSystemEntity $cxmlSystem,
Cart $cart,
SalesChannelContext $context
): string

The main method for the cart transfer. It generates the full cXML PunchOutOrderMessage body.

Process

  1. Build the header — From and To are swapped per the cXML convention (the original To becomes the response From and vice versa)
  2. Insert BuyerCookie from session additionalFields
  3. PunchOutOrderMessageHeader with operationAllowed, quoteStatus="final", and total price
  4. Optionally: append shipping (when attachShipping is enabled)
  5. Optionally: append tax (when attachTax is enabled)
  6. Iterate line items → find matching resolver for each → recursively render field tree
  7. Fire CxmlResponseGenerationEvent → XML can still be modified
  8. Clean output (remove line breaks and whitespace between tags)

Price configuration

System settingPrice used
isTotalPriceNet() = trueCart::getPrice()::getNetPrice()
isTotalPriceNet() = falseCart::getPrice()::getTotalPrice()

The number of decimal places is controlled by the system's priceDigits setting.

Hierarchical field tree

cXML fields can be nested (parent/child relationship). The generator builds an internal field tree and renders it recursively as XML elements. The order within each level is determined by the fields' position attribute.

XML attributes

Fields can carry an XML attribute in addition to a text value:

  • Standard attribute — set directly on the element
  • Namespace attribute — set with the cXML namespace http://xml.cxml.org/schemas/cXML/1.2.040/cXML.dtd

The attribute value is either resolved from a DataField or configured as a static string.


CxmlResponseGenerationEvent

Just before the XML document is serialised, the generator fires:

EventEvent nameWhat can be changed?
CxmlResponseGenerationEventagiqon_connector.cxml.response.generationThe complete SimpleXMLElement object

Example:

use AgiqonConnector\Connector\System\Cxml\Event\CxmlResponseGenerationEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use SimpleXMLElement;

#[AsEventListener(event: CxmlResponseGenerationEvent::EVENT_NAME)]
final class MyCxmlResponseListener
{
public function __invoke(CxmlResponseGenerationEvent $event): void
{
$xml = $event->getCxml();

// Add custom XML elements or modify existing ones
// $event->setCxml($modifiedXml);
}
}
info

The event receives the fully built SimpleXMLElement. Use setCxml() to replace the entire document with a custom instance.