Magento 2 PunchOut Integration
Voraussetzungenâ
- Magento 2.4.0 oder höher
- Admin-Zugriff auf Magento
- SSL-Zertifikat (HTTPS)
- PunchFlow Account (Registrierung)
Schnellstart (3 Minuten)â
Kein Modul nötig!
PunchFlow verbindet sich direkt ĂŒber die Magento 2 REST/GraphQL API. Sie mĂŒssen kein Modul installieren - alles lĂ€uft ĂŒber die Standard-API.
Schritt 1: Integration Token erstellenâ
- Magento Admin öffnen
- System â Integrationen
- "Neue Integration hinzufĂŒgen" klicken
- Konfiguration:
Name: PunchFlow Connector
API Ressourcen-Zugriff:
â Catalog (Lesen)
â Sales (Lesen/Schreiben)
â Customers (Lesen/Schreiben)
â Carts (Lesen/Schreiben)
â Store (Lesen) - Integration aktivieren und Access Token kopieren
Schritt 2: PunchFlow konfigurierenâ
# Via API
curl -X POST "https://api.punchflow.de/api/v1/connectors?merchant_id=<YOUR_MERCHANT_ID>" \
-H "Authorization: Bearer YOUR_PUNCHFLOW_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "magento2",
"name": "Magento 2 Connector",
"api_url": "https://ihr-shop.de/rest/V1",
"authentication": {
"auth_type": "bearer",
"access_token": "your-integration-token"
},
"custom_settings": {
"store_code": "default",
"use_graphql": true
}
}'
Erweiterte Konfigurationâ
API-Konfigurationâ
PunchFlow unterstĂŒtzt sowohl REST als auch GraphQL:
# PunchFlow-Einstellungen (in app.punchflow.de)
shop_connection:
type: "magento2"
api_version: "V1"
use_graphql: true # Aktiviert GraphQL fĂŒr schnellere Produktabfragen
# PunchOut-Einstellungen
punchout:
session_timeout: 3600 # Sekunden
allowed_protocols:
- cxml
- oci
auto_login: true
# Produkt-Mapping
mapping:
product_id_field: "sku" # oder "entity_id"
price_calculation: "final_price" # oder "price", "special_price"
stock_management: true
# Kunden-Einstellungen
customers:
auto_create: true
default_group_id: 2 # B2B-Kundengruppe
require_approval: false
# Logging
debug:
enabled: false
log_level: "info"
Store View Konfigurationâ
Dedizierter Store View fĂŒr PunchOut:
# Store View erstellen via CLI
bin/magento store:create \
--name="PunchOut B2B" \
--code="punchout_b2b" \
--website_id=1 \
--group_id=1
# Konfiguration setzen
bin/magento config:set --scope=stores --scope-code=punchout_b2b \
web/unsecure/base_url "https://punchout.ihr-shop.de/"
Benutzer-Authentifizierungâ
Automatische Anmeldungâ
// app/code/PunchFlow/PunchOut/Plugin/CustomerSessionPlugin.php
class CustomerSessionPlugin
{
public function aroundAuthenticate(
\Magento\Customer\Model\Session $subject,
callable $proceed,
$customerId
) {
// PunchOut Session Check
if ($this->punchoutSession->isActive()) {
$customer = $this->getOrCreateCustomer(
$this->punchoutSession->getBuyerEmail()
);
return $subject->loginById($customer->getId());
}
return $proceed($customerId);
}
}
Kundengruppen-Mappingâ
// di.xml Konfiguration
<type name="PunchFlow\PunchOut\Model\CustomerMapper">
<arguments>
<argument name="groupMapping" xsi:type="array">
<item name="ariba" xsi:type="number">3</item>
<item name="coupa" xsi:type="number">4</item>
<item name="oracle" xsi:type="number">5</item>
</argument>
</arguments>
</type>
Workflow-Integrationâ
1. Session-Startâ
sequenceDiagram
ERP->>PunchFlow: PunchOut Setup Request
PunchFlow->>Magento: REST/GraphQL Session erstellen
Magento->>Magento: Kunde anlegen/anmelden
Magento->>PunchFlow: Session URL
PunchFlow->>ERP: Setup Response mit URL
ERP->>Magento: User Redirect zum Shop
2. Warenkorb-Transferâ
// Observer fĂŒr Cart Transfer
namespace PunchFlow\PunchOut\Observer;
class CheckoutSubmitObserver implements ObserverInterface
{
public function execute(Observer $observer)
{
$quote = $observer->getEvent()->getQuote();
if ($this->punchoutSession->isActive()) {
// Warenkorb an PunchFlow senden
$this->punchoutService->transferCart($quote);
// Redirect zur ERP
throw new LocalizedException(
__('Redirecting to procurement system...')
);
}
}
}
GraphQL Integrationâ
Produkt-Abfragenâ
PunchFlow nutzt GraphQL fĂŒr effiziente Produktabfragen:
query GetProducts($search: String, $pageSize: Int, $currentPage: Int) {
products(
search: $search
pageSize: $pageSize
currentPage: $currentPage
) {
items {
sku
name
description {
html
}
price_range {
minimum_price {
final_price {
value
currency
}
}
}
stock_status
image {
url
}
... on ConfigurableProduct {
variants {
product {
sku
name
}
attributes {
code
value_index
}
}
}
}
total_count
page_info {
current_page
page_size
total_pages
}
}
}
Cart-Operationenâ
mutation CreateCart {
createEmptyCart
}
mutation AddToCart($cartId: String!, $sku: String!, $quantity: Float!) {
addSimpleProductsToCart(
input: {
cart_id: $cartId
cart_items: [
{
data: {
sku: $sku
quantity: $quantity
}
}
]
}
) {
cart {
items {
id
product {
sku
name
}
quantity
prices {
row_total {
value
}
}
}
prices {
grand_total {
value
currency
}
}
}
}
}
Produkt-Synchronisationâ
Automatische Sync-Konfigurationâ
<!-- etc/crontab.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<group id="punchflow">
<job name="punchflow_sync_products"
instance="PunchFlow\PunchOut\Cron\SyncProducts"
method="execute">
<schedule>*/5 * * * *</schedule>
</job>
</group>
</config>
Manueller Syncâ
# Alle Produkte synchronisieren
bin/magento punchflow:sync:products
# Nur geÀnderte Produkte (seit letztem Sync)
bin/magento punchflow:sync:products --changed-only
# Spezifische Kategorie
bin/magento punchflow:sync:products --category-id=42
Frontend-Anpassungenâ
PunchOut-Modus erkennenâ
<?php // view/frontend/templates/checkout/button.phtml ?>
<?php if ($block->isPunchoutSession()): ?>
<button type="button"
class="action primary checkout"
id="punchout-transfer-button"
data-mage-init='{"punchoutTransfer": {}}'>
<span><?= __('Zum Einkaufssystem zurĂŒck') ?></span>
</button>
<?php else: ?>
<?= $block->getChildHtml('default.checkout.button') ?>
<?php endif; ?>
JavaScript Integrationâ
// view/frontend/web/js/punchout-transfer.js
define([
'jquery',
'mage/url',
'mage/storage'
], function ($, urlBuilder, storage) {
'use strict';
return function (config, element) {
$(element).on('click', function () {
var serviceUrl = urlBuilder.build('punchout/cart/transfer');
$('body').trigger('processStart');
storage.post(serviceUrl, JSON.stringify({}))
.done(function (response) {
if (response.redirect_url) {
window.location.href = response.redirect_url;
}
})
.fail(function () {
alert($.mage.__('Transfer failed. Please try again.'));
})
.always(function () {
$('body').trigger('processStop');
});
});
};
});
B2B-Funktionen (Magento Commerce)â
Shared Catalog Integrationâ
// Preisberechnung fĂŒr B2B-Kunden
class B2BPriceResolver
{
public function getPrice(
ProductInterface $product,
CustomerInterface $customer
): float {
// Shared Catalog Preis abrufen
$sharedCatalogId = $this->getSharedCatalogId($customer);
if ($sharedCatalogId) {
return $this->sharedCatalogPrice->getPrice(
$product->getSku(),
$sharedCatalogId
);
}
return $product->getFinalPrice();
}
}
Company Account Mappingâ
// Buyer zu Company zuordnen
class CompanyMapper
{
public function mapBuyerToCompany(
string $buyerId,
string $companyCode
): void {
$company = $this->companyRepository->get($companyCode);
// Kunde der Company zuordnen
$this->customerCompanyRelation->assign(
$buyerId,
$company->getId()
);
}
}
Testingâ
Test-Umgebung einrichtenâ
# Test-Datenbank erstellen
bin/magento setup:db-schema:split-quote --host="localhost" --dbname="magento_quote"
# Test-Fixtures laden
bin/magento punchflow:fixtures:load --env=testing
# Test-Session erstellen
bin/magento punchflow:test:session \
--protocol=cxml \
--buyer-email=test@example.com
Automatische Testsâ
// Test/Integration/PunchOutFlowTest.php
class PunchOutFlowTest extends \PHPUnit\Framework\TestCase
{
public function testCxmlSetupRequest(): void
{
$cxml = file_get_contents(__DIR__ . '/_files/setup-request.xml');
$request = $this->createRequest('POST', '/punchout/setup')
->withHeader('Content-Type', 'application/xml')
->withBody($cxml);
$response = $this->dispatch($request);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString(
'<Status code="200"',
$response->getBody()->getContents()
);
}
public function testCartTransfer(): void
{
// Session erstellen
$session = $this->createPunchoutSession();
// Produkt zum Warenkorb hinzufĂŒgen
$this->addProductToCart('TEST-SKU', 2);
// Transfer ausfĂŒhren
$response = $this->post('/punchout/transfer', [
'session_token' => $session->getToken()
]);
$this->assertStringContainsString(
'<PunchOutOrderMessage>',
$response->getContent()
);
}
}
Monitoring & Analyticsâ
Admin Dashboard Widgetâ
// Block/Adminhtml/Dashboard/PunchOut.php
class PunchOut extends \Magento\Backend\Block\Template
{
public function getSessionStats(): array
{
return [
'today' => $this->sessionRepository->countToday(),
'week' => $this->sessionRepository->countThisWeek(),
'month' => $this->sessionRepository->countThisMonth(),
'success_rate' => $this->calculateSuccessRate(),
];
}
public function getRecentSessions(): Collection
{
return $this->sessionRepository
->getList($this->getDefaultCriteria())
->setPageSize(10);
}
}
Loggingâ
<!-- etc/logging.xml -->
<config>
<group name="punchflow">
<handler name="punchflow_file" type="file">
<param name="path">var/log/punchflow.log</param>
<param name="level">INFO</param>
</handler>
<logger name="punchflow">
<handler>punchflow_file</handler>
</logger>
</group>
</config>
Troubleshootingâ
HĂ€ufige Problemeâ
Problem: Session wird nicht erstelltâ
# Debug-Modus aktivieren
bin/magento config:set punchflow/debug/enabled 1
# Logs prĂŒfen
tail -f var/log/punchflow.log
# Verbindung testen
bin/magento punchflow:test:connection --verbose
Problem: GraphQL Fehlerâ
# GraphQL Schema neu generieren
bin/magento setup:upgrade
bin/magento cache:flush
# GraphQL Query testen
curl -X POST https://ihr-shop.de/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ products(search: \"test\") { items { sku name } } }"}'
Problem: Customer Token ungĂŒltigâ
# Token-Lebensdauer prĂŒfen
bin/magento config:show oauth/access_token_lifetime/customer
# Token neu generieren
bin/magento punchflow:auth:refresh --customer-id=123
Debug-Toolsâ
# PunchOut Session simulieren
bin/magento punchflow:debug:session \
--dump-request \
--dump-response
# cXML validieren
bin/magento punchflow:validate:xml var/import/request.xml
Performance-Optimierungâ
Caching-Strategieâ
<!-- etc/cache.xml -->
<config>
<type name="punchflow_products" translate="label">
<label>PunchFlow Product Cache</label>
<description>Cached product data for PunchOut</description>
<lifetime>3600</lifetime>
</type>
</config>
Indexer fĂŒr Produktdatenâ
// Indexer/ProductData.php
class ProductData implements IndexerActionInterface
{
public function executeFull(): void
{
// Alle Produkte fĂŒr PunchOut indizieren
$this->indexer->reindexAll();
}
public function executeRow($id): void
{
// Einzelnes Produkt neu indizieren
$this->indexer->reindexRow($id);
}
}
Weitere Ressourcenâ
Best Practicesâ
- Separater Store View fĂŒr PunchOut-Traffic
- GraphQL nutzen fĂŒr schnellere Produktabfragen
- B2B Kundengruppen fĂŒr Preisdifferenzierung
- Caching aktivieren fĂŒr bessere Performance
- RegelmĂ€Ăige Synchronisation der Produktdaten
- Logging aktivieren fĂŒr Troubleshooting
Supportâ
Bei Fragen oder Problemen:
- Email: magento@punchflow.de
- Telefon: +49 30 123 456 78
- Chat: app.punchflow.de/chat