Shopware 6 Gateway Plugin (Model B)
Überblick
Das PunchFlow Gateway Plugin für Shopware 6 ermöglicht den Gateway/Proxy-Modus (Model B) für PunchOut-Integration. Im Gegensatz zum API-basierten Ansatz (Model A) läuft hier der gesamte PunchOut-Flow direkt im Shop.
Model A vs Model B
- Model A (Hosted Catalog): Produkte werden zu PunchFlow synchronisiert, Einkauf auf PunchFlow
- Model B (Gateway/Proxy): Benutzer kaufen direkt im Shop, Plugin handhabt Session und cXML
Voraussetzungen
- Shopware 6.5.0 oder höher (6.7+ empfohlen)
- PHP 8.1 oder höher
- Composer
- PunchFlow Account mit Gateway-Lizenz
Installation
Via Composer (empfohlen)
# Im Shopware-Root-Verzeichnis
composer require punchflow/shopware6-punchout
# Plugin aktivieren
bin/console plugin:refresh
bin/console plugin:install --activate PunchFlowPunchOut
# Cache leeren
bin/console cache:clear
Manuelle Installation
# Plugin herunterladen
cd custom/plugins
git clone https://github.com/punchflow/shopware6-punchout.git PunchFlowPunchOut
# Composer Autoload aktualisieren
cd ../..
composer dump-autoload
# Plugin installieren
bin/console plugin:refresh
bin/console plugin:install --activate PunchFlowPunchOut
bin/console cache:clear
Konfiguration
Admin-Oberfläche
- Administration → Einstellungen → Plugins → PunchFlow PunchOut
- Konfigurieren Sie:
| Einstellung | Beschreibung | Standard |
|---|---|---|
| API Secret | Shared Secret für HMAC-Validierung | Erforderlich |
| Session Timeout | Sitzungsdauer in Minuten | 60 |
| Debug Mode | Erweiterte Protokollierung | Aus |
| Auto-Login | Automatische Kundenerstellung | Ein |
| Product ID Field | Feld für SupplierPartID | productNumber |
Umgebungsvariablen
# .env.local
PUNCHFLOW_API_SECRET=your-secure-secret-key
PUNCHFLOW_DEBUG=false
PUNCHFLOW_SESSION_TIMEOUT=3600
config/packages/punchflow.yaml
punchflow_punchout:
api_secret: '%env(PUNCHFLOW_API_SECRET)%'
session_timeout: '%env(int:PUNCHFLOW_SESSION_TIMEOUT)%'
debug: '%env(bool:PUNCHFLOW_DEBUG)%'
# Produkt-Mapping
mapping:
supplier_part_id: 'productNumber' # oder 'ean', 'manufacturerNumber'
unspsc_attribute: 'customFields.punchflow_unspsc'
manufacturer_part_id: 'manufacturerNumber'
# Kunden-Konfiguration
customer:
auto_create: true
default_group: 'B2B'
default_payment_method: 'invoice'
default_salutation: 'mr'
# cXML-Konfiguration
cxml:
version: '1.2.050'
language: 'de-DE'
currency_from_context: true
Architektur
Plugin-Struktur
PunchFlowPunchOut/
├── src/
│ ├── Controller/
│ │ └── PunchOutController.php # Haupt-Endpoints
│ ├── Entity/
│ │ ├── PunchOutSession/ # Session-Entity
│ │ └── PunchOutOrderMessage/ # cXML-Nachrichten
│ ├── Service/
│ │ ├── SessionService.php # Session-Management
│ │ ├── TokenValidationService.php # HMAC-Validierung
│ │ ├── CxmlOrderMessageBuilder.php # cXML-Generierung
│ │ ├── ProductMappingService.php # Produkt-Mapping
│ │ ├── CookieService.php # Cookie-Handling
│ │ └── PunchOutLogger.php # Logging
│ ├── Subscriber/
│ │ └── CheckoutSubscriber.php # Checkout-Events
│ ├── Migration/
│ │ └── Migration1701234567CreatePunchOutTables.php
│ └── Resources/
│ ├── config/
│ │ ├── services.xml
│ │ └── routes.xml
│ └── app/
│ └── administration/ # Admin UI
├── tests/
│ ├── Unit/
│ └── Integration/
└── composer.json
Datenbank-Schema
-- punchflow_session
CREATE TABLE punchflow_session (
id BINARY(16) NOT NULL,
session_token VARCHAR(255) NOT NULL,
buyer_cookie VARCHAR(255) NOT NULL,
buyer_id VARCHAR(255),
buyer_name VARCHAR(255),
network_id VARCHAR(100),
return_url LONGTEXT NOT NULL,
operation VARCHAR(50),
cart_token VARCHAR(255),
customer_id BINARY(16),
status VARCHAR(50) NOT NULL,
ip_address VARCHAR(45),
user_agent VARCHAR(500),
punchflow_session_id VARCHAR(255),
buyer_metadata JSON,
cart_snapshot JSON,
incoming_cxml LONGTEXT,
outgoing_cxml LONGTEXT,
created_at DATETIME(3) NOT NULL,
expires_at DATETIME(3) NOT NULL,
completed_at DATETIME(3),
updated_at DATETIME(3),
PRIMARY KEY (id),
INDEX idx_session_token (session_token),
INDEX idx_buyer_cookie (buyer_cookie),
INDEX idx_status (status)
);
-- punchflow_order_message
CREATE TABLE punchflow_order_message (
id BINARY(16) NOT NULL,
session_id BINARY(16) NOT NULL,
cxml_content LONGTEXT NOT NULL,
payload_id VARCHAR(255) NOT NULL,
direction VARCHAR(20),
message_type VARCHAR(50),
sent_at DATETIME(3),
response_code INT,
error_message VARCHAR(1000),
cart_data JSON,
item_count INT,
total_amount VARCHAR(50),
currency VARCHAR(3),
created_at DATETIME(3) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (session_id) REFERENCES punchflow_session(id) ON DELETE CASCADE
);
API-Endpunkte
Session starten
POST /punchout/start
Content-Type: application/json
X-PunchFlow-Signature: hmac-sha256-signature
X-PunchFlow-Timestamp: 1701234567
X-PunchFlow-Nonce: unique-request-id
{
"buyer_cookie": "abc123",
"return_url": "https://erp.company.com/punchout/return",
"buyer_id": "DUNS:123456789",
"buyer_name": "Acme Corp",
"operation": "create",
"extrinsics": {
"UserEmail": "buyer@company.com",
"UserName": "Max Mustermann"
}
}
Response:
{
"success": true,
"session_id": "abc123def456",
"redirect_url": "https://shop.de/punchout/session/abc123def456",
"expires_at": "2024-12-01T12:00:00Z"
}
Warenkorb übertragen
POST /punchout/transfer
Cookie: punchout_session=abc123def456
# Response: Auto-Submit HTML Form mit cXML
Session-Status abfragen
GET /punchout/session/{sessionId}
X-PunchFlow-Signature: hmac-sha256-signature
# Response
{
"session_id": "abc123def456",
"status": "active",
"cart_items": 3,
"cart_total": "299.99",
"currency": "EUR",
"expires_at": "2024-12-01T12:00:00Z"
}
Sicherheit
HMAC-Validierung
Alle API-Requests werden mit HMAC-SHA256 signiert:
// Signature berechnen
$payload = $timestamp . $nonce . $requestBody;
$signature = hash_hmac('sha256', $payload, $apiSecret);
// Header setzen
X-PunchFlow-Signature: sha256=$signature
X-PunchFlow-Timestamp: $timestamp
X-PunchFlow-Nonce: $nonce
Nonce-Tracking
// Nonces werden in PSR-6 Cache gespeichert
// Verhindert Replay-Attacks
$cacheKey = "punchout_nonce_{$nonce}";
if ($this->cache->hasItem($cacheKey)) {
throw new NonceReusedException();
}
$this->cache->save(
$this->cache->getItem($cacheKey)->set(true)->expiresAfter(3600)
);
Session-Cookies
// Sichere Cookie-Konfiguration
$cookie = Cookie::create('punchout_session')
->withValue($sessionToken)
->withExpires($expiresAt)
->withSecure(true)
->withHttpOnly(true)
->withSameSite(Cookie::SAMESITE_STRICT);
cXML PunchOutOrderMessage
Generierte Struktur
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.050/cXML.dtd">
<cXML version="1.2.050" payloadID="20241201120000.abc123@punchflow.de"
timestamp="2024-12-01T12:00:00+01:00" xml:lang="de-DE">
<Header>
<From>
<Credential domain="DUNS">
<Identity>PunchFlowShop</Identity>
</Credential>
</From>
<To>
<Credential domain="NetworkID">
<Identity>AN123456789</Identity>
</Credential>
</To>
<Sender>
<Credential domain="PunchFlow">
<Identity>PunchFlow</Identity>
<SharedSecret>HIDDEN</SharedSecret>
</Credential>
<UserAgent>PunchFlow Shopware 6 Plugin</UserAgent>
</Sender>
</Header>
<Message>
<PunchOutOrderMessage>
<BuyerCookie>abc123</BuyerCookie>
<PunchOutOrderMessageHeader operationAllowed="edit">
<Total>
<Money currency="EUR">299.99</Money>
</Total>
</PunchOutOrderMessageHeader>
<ItemIn quantity="2">
<ItemID>
<SupplierPartID>SW-PROD-001</SupplierPartID>
</ItemID>
<ItemDetail>
<UnitPrice>
<Money currency="EUR">99.99</Money>
</UnitPrice>
<Description xml:lang="de-DE">Produktname</Description>
<UnitOfMeasure>EA</UnitOfMeasure>
<Classification domain="UNSPSC">43211500</Classification>
<ManufacturerPartID>MPN-123</ManufacturerPartID>
<ManufacturerName>Hersteller GmbH</ManufacturerName>
</ItemDetail>
</ItemIn>
</PunchOutOrderMessage>
</Message>
</cXML>
Admin-Oberfläche
Session-Übersicht
Das Plugin fügt einen neuen Menüpunkt "PunchOut Sessions" hinzu:
- Aktive Sessions anzeigen
- Session-Details mit cXML-Vorschau
- Session manuell beenden
- Statistiken und Charts
Konfiguration im Admin
// Administration → Einstellungen → Plugins → PunchFlow PunchOut
// Konfigurationsfelder:
- API Secret (verschlüsselt)
- Session Timeout
- Debug Mode Toggle
- Auto-Login aktivieren
- Standard-Kundengruppe
- Produkt-ID Feld auswählen
Testing
Unit Tests ausführen
# Im Plugin-Verzeichnis
./vendor/bin/phpunit --configuration phpunit.xml.dist
# Mit Coverage
./vendor/bin/phpunit --coverage-html coverage/
Test-Session erstellen
# Via CLI
bin/console punchflow:session:create \
--buyer-cookie="test123" \
--return-url="https://test.erp.com/return" \
--buyer-email="test@example.com"
# Via API (mit gültiger Signatur)
curl -X POST "https://shop.de/punchout/start" \
-H "Content-Type: application/json" \
-H "X-PunchFlow-Signature: sha256=..." \
-H "X-PunchFlow-Timestamp: $(date +%s)" \
-H "X-PunchFlow-Nonce: $(uuidgen)" \
-d '{"buyer_cookie": "test123", "return_url": "https://test.erp.com/return"}'
Troubleshooting
Logs prüfen
# Plugin-spezifische Logs
tail -f var/log/punchflow_punchout.log
# Shopware Logs
tail -f var/log/prod.log | grep -i punchout
Debug-Modus aktivieren
# config/packages/punchflow.yaml
punchflow_punchout:
debug: true
Häufige Probleme
"Invalid Signature" Fehler
# Secret prüfen
bin/console debug:config punchflow_punchout api_secret
# Timestamp-Drift prüfen (max 5 Minuten)
date +%s # Lokale Zeit
Session wird nicht gefunden
# Session in Datenbank prüfen
bin/console dbal:run-sql "SELECT * FROM punchflow_session WHERE session_token = 'xxx'"
# Cookie prüfen (Browser DevTools)
cXML-Validierungsfehler
# cXML gegen DTD validieren
xmllint --dtdvalid http://xml.cxml.org/schemas/cXML/1.2.050/cXML.dtd output.xml
Performance
Caching-Empfehlungen
# config/packages/cache.yaml
framework:
cache:
pools:
punchflow.session_cache:
adapter: cache.adapter.redis
default_lifetime: 3600
Session-Cleanup
# Abgelaufene Sessions bereinigen (Cronjob)
bin/console punchflow:session:cleanup --older-than="7 days"
# Oder via Scheduled Task
# Das Plugin registriert automatisch einen Task
Updates
Plugin aktualisieren
composer update punchflow/shopware6-punchout
bin/console plugin:update PunchFlowPunchOut
bin/console cache:clear
Migrationen ausführen
bin/console database:migrate --all PunchFlowPunchOut
Support
Bei Fragen oder Problemen:
- Email: shopware@punchflow.de
- Telefon: +49 30 123 456 78
- GitHub Issues: github.com/punchflow/shopware6-punchout/issues
- Chat: app.punchflow.de/chat