Skip to main content

Shopware 6 PunchOut Integration

📋 Voraussetzungen

  • Shopware 6.4.0 oder höher
  • Admin-Zugriff auf Shopware
  • SSL-Zertifikat (HTTPS)
  • PunchFlow Account (Registrierung)

🚀 Schnellstart (3 Minuten)

Kein Plugin nötig!

PunchFlow verbindet sich direkt über die Shopware 6 REST API. Sie müssen kein Plugin installieren - alles läuft über die Standard-API.

Schritt 1: API-Zugang einrichten

  1. Shopware Admin öffnen
  2. Einstellungen → System → Integrationen
  3. "Integration hinzufügen" klicken
  4. Konfiguration:
    Name: PunchFlow Connector
    Zugriffsrechte:
    Produkte (Lesen)
    Kategorien (Lesen)
    Preise (Lesen)
    Lagerbestände (Lesen)
    Bestellungen (Schreiben)
    Kunden (Lesen/Schreiben)
    Sales Channels (Lesen)
  5. Zugangsdaten kopieren (Access ID & Secret)

Schritt 3: 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": "shopware6",
"name": "Shopware Connector",
"api_url": "https://ihr-shop.de/api",
"authentication": {
"auth_type": "api_key",
"api_key": "SWIA...",
"api_secret": "..."
},
"custom_settings": {
"sales_channel_id": "default"
}
}'

⚙️ Erweiterte Konfiguration

API-Konfiguration

PunchFlow nutzt die native Shopware 6 REST API. Keine Plugin-Installation erforderlich!

# PunchFlow-Einstellungen (in app.punchflow.de)
shop_connection:
type: "shopware6"
api_version: "v3"
auth_type: "oauth2"

# PunchOut-Einstellungen
punchout:
session_timeout: 3600 # Sekunden
allowed_protocols:
- cxml
- oci
auto_login: true

# Produkt-Mapping
mapping:
product_number_field: "productNumber" # oder "ean", "manufacturerNumber"
price_calculation: "gross" # oder "net"
stock_management: true

# Kunden-Einstellungen
customers:
auto_create: true
default_group: "B2B-Kunden"
require_approval: false

# Logging
debug:
enabled: false
log_level: "info" # debug, info, warning, error

Sales Channel Konfiguration

Dedizierter Sales Channel für PunchOut:

// API-Request zum Erstellen eines Sales Channels
POST /api/sales-channel
{
"name": "PunchOut B2B",
"typeId": "8a243080f92e4c719546314b577cf82b", // Storefront
"languageId": "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
"currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfba",
"paymentMethodIds": ["purchase_on_account_id"],
"shippingMethodId": "standard_shipping_id",
"countryId": "germany_id",
"navigationCategoryId": "root_category_id",
"customerGroupId": "b2b_customer_group_id",
"domains": [
{
"url": "https://punchout.ihr-shop.de",
"languageId": "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
"currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfba",
"snippetSetId": "765432cd1234567890abcdef12345678"
}
]
}

Benutzer-Authentifizierung

Automatische Anmeldung

// config/packages/punchflow.yaml
punchflow:
auth:
method: "auto" # auto, mapping, manual
user_mapping:
email_field: "extrinsic.UserEmail"
name_field: "extrinsic.UserName"
company_field: "extrinsic.CompanyCode"

Benutzer-Mapping

-- Mapping-Tabelle für bestehende Kunden
CREATE TABLE punchout_user_mapping (
buyer_cookie VARCHAR(255) PRIMARY KEY,
customer_id BINARY(16) NOT NULL,
created_at DATETIME NOT NULL
);

🔄 Workflow-Integration

1. Session-Start

sequenceDiagram
ERP->>PunchFlow: PunchOut Setup Request
PunchFlow->>Shopware: Validate & Create Session
Shopware->>Shopware: Create/Login User
Shopware->>PunchFlow: Session URL
PunchFlow->>ERP: Setup Response with URL
ERP->>Shopware: Redirect User to Shop

2. Warenkorb-Transfer

// Event Subscriber für Cart Transfer
class PunchOutCartTransferSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
CheckoutFinishPageLoadedEvent::class => 'onCheckoutFinish',
];
}

public function onCheckoutFinish(CheckoutFinishPageLoadedEvent $event): void
{
$session = $this->punchoutService->getActiveSession();

if ($session) {
$cart = $event->getPage()->getOrder();
$this->punchoutService->transferCart($cart, $session);
}
}
}

📦 Produkt-Synchronisation

Automatische Sync-Konfiguration

# config/packages/punchflow_sync.yaml
punchflow_sync:
products:
enabled: true
interval: 300 # Sekunden
batch_size: 100
fields:
- productNumber
- name
- description
- price
- stock
- manufacturer
- ean
- categories
custom_fields:
- technical_data
- delivery_time

Manueller Sync

# Alle Produkte synchronisieren
bin/console punchflow:sync:products

# Nur geänderte Produkte
bin/console punchflow:sync:products --changed-only

# Spezifische Kategorie
bin/console punchflow:sync:products --category=hardware

🎨 Frontend-Anpassungen

PunchOut-Modus erkennen

{# Resources/views/storefront/page/checkout/confirm/index.html.twig #}
{% sw_extends '@Storefront/storefront/page/checkout/confirm/index.html.twig' %}

{% block page_checkout_confirm_submit %}
{% if punchout_session is defined %}
<button class="btn btn-primary" type="submit">
Zum Einkaufssystem zurück
</button>
{% else %}
{{ parent() }}
{% endif %}
{% endblock %}

Checkout-Anpassungen

// Resources/app/storefront/src/punchout-checkout.js
export default class PunchOutCheckout {
constructor() {
this.sessionId = this.getSessionId();
if (this.sessionId) {
this.customizeCheckout();
}
}

customizeCheckout() {
// Zahlungsarten ausblenden
document.querySelector('.checkout-payment').style.display = 'none';

// Versandarten vereinfachen
this.simplifyShipping();

// Transfer-Button hinzufügen
this.addTransferButton();
}

addTransferButton() {
const btn = document.createElement('button');
btn.textContent = 'Warenkorb übertragen';
btn.className = 'btn btn-primary btn-block';
btn.onclick = () => this.transferCart();

document.querySelector('.checkout-aside-action')
.appendChild(btn);
}

async transferCart() {
const response = await fetch('/punchout/transfer', {
method: 'POST',
headers: {
'X-PunchOut-Session': this.sessionId
}
});

if (response.ok) {
window.location.href = await response.text();
}
}
}

🧪 Testing

Test-Umgebung einrichten

# Docker-basierte Testumgebung
docker-compose -f docker-compose.test.yml up -d

# Test-Daten importieren
bin/console punchflow:fixtures:load

# Test-Session erstellen
bin/console punchflow:test:session \
--protocol=cxml \
--buyer=test@example.com

Automatische Tests

// Tests/Integration/PunchOutTest.php
class PunchOutTest extends TestCase
{
public function testCxmlSetupRequest(): void
{
$client = $this->createClient();

$cxml = file_get_contents(__DIR__ . '/fixtures/setup-request.xml');

$client->request('POST', '/punchout/setup', [], [], [
'CONTENT_TYPE' => 'application/xml',
], $cxml);

$this->assertResponseIsSuccessful();
$this->assertStringContainsString('<Status code="200"',
$client->getResponse()->getContent());
}

public function testOciTransfer(): void
{
// Test OCI transfer
}
}

Test-Szenarien

# Vollständiger Durchlauf
bin/console punchflow:test:full \
--steps=setup,browse,add-to-cart,transfer \
--validate=true

# Nur Verbindung testen
bin/console punchflow:test:connection

📊 Monitoring & Analytics

Dashboard-Widgets

// Admin Dashboard Widget
Shopware.Component.register('punchflow-dashboard', {
template: `
<sw-card title="PunchOut Analytics">
<sw-chart-card
:chart-data="sessionsData"
title="Sessions (7 Tage)">
</sw-chart-card>

<sw-data-grid
:dataSource="recentSessions"
:columns="columns">
</sw-data-grid>
</sw-card>
`,

data() {
return {
sessionsData: [],
recentSessions: []
};
},

created() {
this.loadAnalytics();
}
});

Logging & Debugging

// src/Service/PunchOutLogger.php
class PunchOutLogger
{
public function logSession(array $data): void
{
$this->logger->info('PunchOut Session', [
'session_id' => $data['session_id'],
'protocol' => $data['protocol'],
'buyer' => $data['buyer_email'],
'timestamp' => time(),
'context' => [
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT']
]
]);
}
}

🔧 Troubleshooting

Häufige Probleme

Problem: Session wird nicht erstellt

# Debug-Modus aktivieren
bin/console config:set punchflow.debug.enabled true

# Logs prüfen
tail -f var/log/punchout_*.log

# Verbindung testen
bin/console punchflow:test:connection --verbose

Problem: Produkte werden nicht übertragen

// Prüfung im Code
if ($this->punchoutService->hasActiveSession()) {
dump($cart->getLineItems());
dump($this->mapper->mapCartItems($cart));
}

Problem: Authentifizierung schlägt fehl

# Credentials überprüfen
bin/console punchflow:validate:credentials

# Token neu generieren
bin/console punchflow:auth:refresh

Debug-Tools

# PunchOut Session simulieren
bin/console punchflow:debug:session \
--dump-request \
--dump-response \
--stop-on-error

# XML validieren
bin/console punchflow:validate:xml /path/to/request.xml

🚀 Performance-Optimierung

Caching-Strategie

# config/packages/cache.yaml
framework:
cache:
pools:
punchflow.cache:
adapter: cache.adapter.redis
default_lifetime: 3600
tags: true

Asynchrone Verarbeitung

// src/MessageHandler/PunchOutTransferHandler.php
class PunchOutTransferHandler implements MessageHandlerInterface
{
public function __invoke(PunchOutTransferMessage $message)
{
// Asynchroner Transfer
$this->punchoutService->transferAsync(
$message->getSessionId(),
$message->getCartData()
);
}
}

📚 Weitere Ressourcen

💡 Best Practices

  1. Separater Sales Channel für PunchOut-Traffic
  2. Regelmäßige Synchronisation der Produktdaten
  3. Monitoring aller PunchOut-Sessions
  4. Caching für bessere Performance
  5. Fehler-Handling mit automatischen Retries
  6. Logging aller Transaktionen

🆘 Support

Bei Fragen oder Problemen: