[{"data":1,"prerenderedAt":2548},["ShallowReactive",2],{"content-translation-map":3,"navigation:en":62,"auto-image:entries:\u002Fen\u002Fblog\u002Fshopware-nexus-or-custom-middleware":145,"blog:en:\u002Fen\u002Fblog\u002Fshopware-nexus-or-custom-middleware":146},{"lex-hesse":4,"mezcrafts":7,"schork":10,"ai-content-creation":13,"ai-integration":16,"devops":19,"ecommerce":22,"edi-integration":25,"erp-integration":28,"individual-development":31,"system-integration":34,"system-modernization-migration":37,"ux-ui-design":40,"agentic-commerce":43,"edi4jtl":45,"own-ai-server-tco":48,"shopware-6-redis-configuration-mistakes":51,"shopware-6-use-cases":54,"shopware-frontends-headless-storefront-nuxt":57,"shopware-nexus-vs-custom-middleware":59},{"en":5,"de":6},"\u002Freferences\u002Flex-hesse","\u002Freferenzen\u002Flex-hesse",{"en":8,"de":9},"\u002Freferences\u002Fmezcrafts","\u002Freferenzen\u002Fmezcrafts",{"en":11,"de":12},"\u002Freferences\u002Fschork","\u002Freferenzen\u002Fschork",{"en":14,"de":15},"\u002Fservices\u002Fai-content-creation","\u002Fleistungen\u002Fki-contentproduktion",{"en":17,"de":18},"\u002Fservices\u002Fai-integration","\u002Fleistungen\u002Fki-integration",{"en":20,"de":21},"\u002Fservices\u002Fdevops","\u002Fleistungen\u002Fdevops",{"en":23,"de":24},"\u002Fservices\u002Fecommerce","\u002Fleistungen\u002Fecommerce-entwicklung",{"en":26,"de":27},"\u002Fservices\u002Fedi-integration","\u002Fleistungen\u002Fedi-integration",{"en":29,"de":30},"\u002Fservices\u002Ferp-integration","\u002Fleistungen\u002Ferp-integration",{"en":32,"de":33},"\u002Fservices\u002Findividual-development","\u002Fleistungen\u002Findividuelle-softwareentwicklung",{"en":35,"de":36},"\u002Fservices\u002Fsystem-integration","\u002Fleistungen\u002Fsystemintegration",{"en":38,"de":39},"\u002Fservices\u002Fsystem-modernization-migration","\u002Fleistungen\u002Fmigration-systemmodernisierung",{"en":41,"de":42},"\u002Fservices\u002Fux-ui-design","\u002Fleistungen\u002Fux-ui-design",{"en":44,"de":44},"\u002Fblog\u002Fagentic-commerce",{"en":46,"de":47},"\u002Fblog\u002Fedi4jtl","\u002Fblog\u002Fedi-integration-jtl",{"en":49,"de":50},"\u002Fblog\u002Fown-ai-server-when-it-pays-off","\u002Fblog\u002Feigener-ki-server-wann-rechnet-er-sich",{"en":52,"de":53},"\u002Fblog\u002Fshopware-6-redis-configuration-mistakes","\u002Fblog\u002Fshopware-6-redis-fehlkonfiguration",{"en":55,"de":56},"\u002Fblog\u002Fshopware-6-use-cases","\u002Fblog\u002Fshopware-6-wann-sinnvoll",{"en":58,"de":58},"\u002Fblog\u002Fshopware-frontends-headless-storefront-nuxt",{"en":60,"de":61},"\u002Fblog\u002Fshopware-nexus-or-custom-middleware","\u002Fblog\u002Fshopware-nexus-oder-eigene-middleware",[63],{"title":64,"path":65,"stem":66,"children":67,"page":89},"En","\u002Fen","en",[68,90],{"title":69,"path":70,"stem":71,"children":72,"page":89},"References","\u002Fen\u002Freferences","en\u002Freferences",[73,79,84],{"title":74,"path":75,"stem":76,"translationKey":77,"icon":78},"Lex & Hesse – B2B Shop for Vehicle Parts with 400,000 Products","\u002Fen\u002Freferences\u002Flex-hesse","en\u002Freferences\u002Flex-hesse","lex-hesse",null,{"title":80,"path":81,"stem":82,"translationKey":83,"icon":78},"MEZ Crafts – B2B Shop for Yarn and Wool with PWA Storefront","\u002Fen\u002Freferences\u002Fmezcrafts","en\u002Freferences\u002Fmezcrafts","mezcrafts",{"title":85,"path":86,"stem":87,"translationKey":88,"icon":78},"Walter Schork – Online Shop for Car Accessories with 200,000 Products","\u002Fen\u002Freferences\u002Fschork","en\u002Freferences\u002Fschork","schork",false,{"title":91,"path":92,"stem":93,"children":94,"page":89},"Services","\u002Fen\u002Fservices","en\u002Fservices",[95,100,105,110,115,120,125,130,135,140],{"title":96,"path":97,"stem":98,"translationKey":99,"icon":99},"AI-Powered Content Production","\u002Fen\u002Fservices\u002Fai-content-creation","en\u002Fservices\u002Fai-content-creation","ai-content-creation",{"title":101,"path":102,"stem":103,"translationKey":104,"icon":104},"AI Integration","\u002Fen\u002Fservices\u002Fai-integration","en\u002Fservices\u002Fai-integration","ai-integration",{"title":106,"path":107,"stem":108,"translationKey":109,"icon":109},"DevOps","\u002Fen\u002Fservices\u002Fdevops","en\u002Fservices\u002Fdevops","devops",{"title":111,"path":112,"stem":113,"translationKey":114,"icon":114},"eCommerce Development","\u002Fen\u002Fservices\u002Fecommerce","en\u002Fservices\u002Fecommerce","ecommerce",{"title":116,"path":117,"stem":118,"translationKey":119,"icon":119},"EDI Integration","\u002Fen\u002Fservices\u002Fedi-integration","en\u002Fservices\u002Fedi-integration","edi-integration",{"title":121,"path":122,"stem":123,"translationKey":124,"icon":124},"ERP Integration","\u002Fen\u002Fservices\u002Ferp-integration","en\u002Fservices\u002Ferp-integration","erp-integration",{"title":126,"path":127,"stem":128,"translationKey":129,"icon":129},"Custom Development","\u002Fen\u002Fservices\u002Findividual-development","en\u002Fservices\u002Findividual-development","individual-development",{"title":131,"path":132,"stem":133,"translationKey":134,"icon":134},"System Integration","\u002Fen\u002Fservices\u002Fsystem-integration","en\u002Fservices\u002Fsystem-integration","system-integration",{"title":136,"path":137,"stem":138,"translationKey":139,"icon":139},"Migration & System Modernization","\u002Fen\u002Fservices\u002Fsystem-modernization-migration","en\u002Fservices\u002Fsystem-modernization-migration","system-modernization-migration",{"title":141,"path":142,"stem":143,"translationKey":144,"icon":144},"UX\u002FUI Design","\u002Fen\u002Fservices\u002Fux-ui-design","en\u002Fservices\u002Fux-ui-design","ux-ui-design",{},{"id":147,"title":148,"body":149,"description":2512,"draft":89,"extension":2513,"meta":2514,"navigation":719,"ogImage":78,"path":2515,"publishAt":2516,"robots":78,"schemaOrg":2517,"seo":2529,"sitemap":2534,"stem":2538,"tags":2539,"translationKey":2546,"__hash__":2547},"blog_en\u002Fen\u002Fblog\u002Fshopware-nexus-or-custom-middleware.md","Shopware Nexus or Custom Middleware? A Decision Guide for ERP Integrations",{"type":150,"value":151,"toc":2493},"minimark",[152,157,166,169,172,177,180,188,256,259,261,265,268,271,274,277,280,286,297,300,303,306,308,312,315,318,321,327,330,340,343,348,354,359,365,368,373,376,378,382,385,404,407,409,413,416,422,428,434,440,446,452,454,458,461,538,541,544,547,549,553,556,559,619,622,625,630,635,638,641,644,647,652,655,658,662,665,668,671,685,691,694,2011,2014,2017,2028,2030,2034,2037,2040,2142,2145,2149,2155,2162,2168,2173,2176,2179,2181,2185,2188,2191,2204,2207,2210,2212,2216,2350,2353,2355,2359,2362,2365,2368,2371,2374,2399,2401,2405,2486,2489],[153,154,156],"h1",{"id":155},"shopware-nexus-or-custom-middleware-a-decision-guide-for-erp-integrations","Shopware Nexus or custom middleware? A decision guide for ERP integrations",[158,159,160,161,165],"p",{},"Since Nexus went into Early Access, the same question comes up in every other project call: do we still need the custom ",[162,163,164],"a",{"href":122},"ERP integration"," at all?",[158,167,168],{},"The answer hangs on two things: how an integration is cut, and who runs it.",[170,171],"hr",{},[173,174,176],"h2",{"id":175},"what-nexus-is","What Nexus is",[158,178,179],{},"Shopware describes Nexus as a native iPaaS built into the platform. In practice it is a cloud service with a visual workflow editor, reachable at nexus.shopware.com and connected to the shop through the Nexus Event-Ingestion Service.",[158,181,182,183,187],{},"A workflow consists of a trigger, a chain of steps, and outbound actions. The documentation lists two trigger types: Shopware events like ",[184,185,186],"code",{},"order.placed"," and scheduled runs in cron style. In between sit conditional branches and data transformations.",[189,190,191,204],"table",{},[192,193,194],"thead",{},[195,196,197,201],"tr",{},[198,199,200],"th",{},"Requirement",[198,202,203],{},"Value",[205,206,207,216,224,232,240,248],"tbody",{},[195,208,209,213],{},[210,211,212],"td",{},"Shopware version",[210,214,215],{},"6.7.1.0 or newer",[195,217,218,221],{},[210,219,220],{},"Account",[210,222,223],{},"active Shopware account with an assigned company",[195,225,226,229],{},[210,227,228],{},"Access",[210,230,231],{},"administrator rights in the shop",[195,233,234,237],{},[210,235,236],{},"Service",[210,238,239],{},"Nexus Event-Ingestion Service under Settings > Shopware Services",[195,241,242,245],{},[210,243,244],{},"Operation",[210,246,247],{},"cloud service at nexus.shopware.com",[195,249,250,253],{},[210,251,252],{},"Status",[210,254,255],{},"Early Access, announced in May 2026",[158,257,258],{},"The documentation lists five action nodes: the Business Central connector, Shopware API calls, Slack notifications, Shopware email, and generic API requests. On top of that there is an output node that writes data to S3-compatible object storage. The product page lists further connectors as under review, among them Pimcore, NetSuite, and Akeneo.",[170,260],{},[173,262,264],{"id":263},"what-is-new-here-and-what-is-not","What is new here, and what is not",[158,266,267],{},"Event-driven workflows with a visual editor and connectors have been around for over a decade. Zapier, Make, Workato, Talend, Apache Camel, Node-RED, n8n. The pattern is established, and Nexus follows it.",[158,269,270],{},"The value of such a tool is not in the editor but in the connectors. A finished connector takes care of authentication for a specific target system, pagination, rate limit behavior, mapping onto its entities, and the question of what an error message from that system actually means. That part eats the days in a custom build, not the transport.",[158,272,273],{},"So the usefulness hangs on a single question: is your target system in the catalog?",[158,275,276],{},"In Early Access, the catalog is still small. Of the documented nodes, exactly one writes to a business system: the Business Central connector. Slack message and Shopware email are notifications, the rest is generic transport: HTTP request, calls against your own Shopware API, storage in S3. Under review are Pimcore, NetSuite, and Akeneo, exactly the candidates that would make the difference.",[158,278,279],{},"For every other system, the generic API request remains. Then you write mapping, authentication, and error handling yourself again, just in someone else's service instead of your own code.",[281,282,283],"blockquote",{},[158,284,285],{},"Whether Nexus saves you work is decided by the connector catalog. If your ERP is not in it, what remains is a workflow editor with an HTTP call.",[158,287,288,289,292,293,296],{},"The event connectivity is also easy to overrate. Nexus delivers Shopware events with no work on your side, but so does a plugin with an ",[184,290,291],{},"EventSubscriber",", and that is a few dozen lines. If the receiver runs outside the shop, the app system with ",[184,294,295],{},"\u003Cwebhooks>"," in the manifest does the job. The native trigger is convenient, but it is not a unique selling point.",[158,298,299],{},"Shopware advertises two numbers: integration costs are supposed to drop by up to 40 percent, and 52 percent of project effort is said to be lost to custom middleware on average. Both numbers carry a footnote mark on the product page, with no derivation behind it. Without a baseline, it stays open what counts as project effort and what the savings were measured against.",[158,301,302],{},"Where the effort sits is not in dispute: between the systems, not in the shop. A matching connector covers the standard part of that. In our experience the bigger part is in the special cases, and those still need a solution of your own.",[158,304,305],{},"One more distinction, because Nexus and the Flow Builder get mixed up regularly. The Flow Builder automates logic inside the shop, such as status changes or sending mail. Nexus connects the shop to systems outside. The two exist side by side.",[170,307],{},[173,309,311],{"id":310},"granularity-decides-the-costs","Granularity decides the costs",[158,313,314],{},"The technical documentation does not name prices, the pricing page does. Shopware lists Nexus there as an add-on: a free introductory phase until September 1, 2026, after that around €10 per 1,000 executions, shown as guide pricing during the beta.",[158,316,317],{},"Billing is per execution, and exactly that changes how you cut an integration.",[158,319,320],{},"As an example, a shop with 5,000 products and 200 orders per day, connected to an ERP.",[158,322,323],{},[324,325,326],"strong",{},"Order flow, event-driven:",[158,328,329],{},"Each order triggers four executions. Order to the ERP, customer sync, status feedback, tracking number back to the shop.",[331,332,337],"pre",{"className":333,"code":335,"language":336},[334],"language-text","200 orders × 4 steps × 30 days = 24,000 executions\u002Fmonth\n24,000 \u002F 1,000 × €10 = €240\u002Fmonth\n","text",[184,338,335],{"__ignoreMap":339},"",[158,341,342],{},"That is unremarkable and cheap for what it delivers.",[158,344,345],{},[324,346,347],{},"Inventory sync, one execution per product:",[331,349,352],{"className":350,"code":351,"language":336},[334],"5,000 products × 24 runs\u002Fday × 30 days = 3,600,000 executions\u002Fmonth\n3,600,000 \u002F 1,000 × €10 = €36,000\u002Fmonth\n",[184,353,351],{"__ignoreMap":339},[158,355,356],{},[324,357,358],{},"The same inventory sync as a batch, one execution per run:",[331,360,363],{"className":361,"code":362,"language":336},[334],"24 runs\u002Fday × 30 days = 720 executions\u002Fmonth\n720 \u002F 1,000 × €10 = €7.20\u002Fmonth\n",[184,364,362],{"__ignoreMap":339},[158,366,367],{},"Same function, same amount of data, but a price difference of factor 5,000. The factor stays the same whatever price per execution you plug in.",[281,369,370],{},[158,371,372],{},"With your own middleware, granularity is a question of runtime and readability. With Nexus, it is the cost question. Trigger per record instead of per run and you pay a thousand times more.",[158,374,375],{},"This is not an argument against Nexus. It is an argument for cutting workflows batch-oriented from the start, wherever real time is not required. Orders should be processed one by one, inventory and prices in bulk.",[170,377],{},[173,379,381],{"id":380},"where-nexus-holds-up","Where Nexus holds up",[158,383,384],{},"In plenty of projects, custom middleware is oversized.",[386,387,388,392,395,398,401],"ul",{},[389,390,391],"li",{},"Standard flows between Shopware and a system that has a connector",[389,393,394],{},"Field mappings without special logic, where an order stays an order",[389,396,397],{},"Manageable volumes where per-execution costs do not matter much",[389,399,400],{},"Notifications to Slack or by mail, where no business logic is needed anyway",[389,402,403],{},"Projects without their own ops team, where every additional component is a risk",[158,405,406],{},"In these cases, custom middleware is a cost driver with no return. If you need a Business Central connection with standard fields, take Nexus.",[170,408],{},[173,410,412],{"id":411},"where-the-limits-are","Where the limits are",[158,414,415],{},"Part of the following comes down to Early Access and should resolve itself over time. For a decision today, the current state is what counts.",[158,417,418,421],{},[324,419,420],{},"At-least-once delivery."," Shopware documents this as a known limitation. An event can arrive more than once. Without idempotency on the receiving side, that can turn into double bookings in the ERP.",[158,423,424,427],{},[324,425,426],{},"No test mode, no undo, limited error details."," This is also stated in the documentation. Whoever changes a workflow changes it in production. As a way out, the docs recommend staging shops, meaning a second environment you maintain yourself. And for debugging there are fewer details available than you are used to from your own middleware.",[158,429,430,433],{},[324,431,432],{},"Error handling with checkpoints."," What happens when the ERP stops responding in the middle of a batch of 3,000 products? Custom middleware has state, resumes from the last confirmed record, and reports the abort to monitoring. The Nexus documentation does not mention such a feature.",[158,435,436,439],{},[324,437,438],{},"Custom data models."," As soon as product data is spread across several entities, configurators are involved, or prices are calculated by customer group, tier, and campaign logic, field mappings are no longer enough.",[158,441,442,445],{},[324,443,444],{},"Systems without a connector."," SAP, Oracle, Salesforce, and practically every ERP in the German Mittelstand run through the generic API request. The effort for mapping and error handling stays with the project. The advantage over any other workflow tool then shrinks to the event trigger.",[158,447,448,451],{},[324,449,450],{},"Operation outside the cloud."," The documentation describes Nexus as a cloud service. A self-hosted variant is not documented. If you need control over the runtime environment for regulatory reasons or because downtime is expensive, you will not find it here.",[170,453],{},[173,455,457],{"id":456},"the-three-year-comparison","The three-year comparison",[158,459,460],{},"The following calculation uses the per-execution price named by Shopware and a middle scenario: a connection to a system with an existing connector, standard fields, 24,000 executions per month. The day rate (€1,000) and the person-days are assumptions from our project work. If you have your own numbers, plug them in, the structure of the calculation stays the same.",[189,462,463,476],{},[192,464,465],{},[195,466,467,470,473],{},[198,468,469],{},"Item",[198,471,472],{},"Nexus",[198,474,475],{},"Custom middleware",[205,477,478,489,500,510,521],{},[195,479,480,483,486],{},[210,481,482],{},"Initial effort",[210,484,485],{},"6 person-days = €6,000",[210,487,488],{},"40 person-days = €40,000",[195,490,491,494,497],{},[210,492,493],{},"Hosting over 3 years",[210,495,496],{},"none",[210,498,499],{},"~€3,600",[195,501,502,505,508],{},[210,503,504],{},"Usage costs over 3 years",[210,506,507],{},"€240\u002Fmonth = €8,640",[210,509,496],{},[195,511,512,515,518],{},[210,513,514],{},"Maintenance over 3 years",[210,516,517],{},"low, connector upkeep is on Shopware",[210,519,520],{},"0.5 person-days\u002Fmonth = €18,000",[195,522,523,528,533],{},[210,524,525],{},[324,526,527],{},"Total",[210,529,530],{},[324,531,532],{},"~€14,600",[210,534,535],{},[324,536,537],{},"~€61,600",[158,539,540],{},"For this scenario, Nexus is about four times cheaper. If you build your own anyway, you should be able to name good reasons.",[158,542,543],{},"At high volumes the calculation flips, because usage costs grow linearly while the costs of your own middleware stay largely fixed. It flips with special logic, because the initial effort on the Nexus side no longer sits at 6 person-days. And it flips for systems without a connector, because both sides then build the same mapping, except one of them also bills per execution.",[158,545,546],{},"One item is missing from the table because it is hard to put a number on.",[170,548],{},[173,550,552],{"id":551},"lock-in-on-critical-processes","Lock-in on critical processes",[158,554,555],{},"The calculation above rates effort, but not how hard the decision is to walk back later. For a Slack notification that plays no role, for the order flow it does.",[158,557,558],{},"The dependency has four levels:",[189,560,561,574],{},[192,562,563],{},[195,564,565,568,571],{},[198,566,567],{},"Level",[198,569,570],{},"Dependency",[198,572,573],{},"What it means",[205,575,576,587,597,608],{},[195,577,578,581,584],{},[210,579,580],{},"Logic",[210,582,583],{},"Workflows live as configuration in the Shopware cloud, not in your repository",[210,585,586],{},"No diff, no code review, no rollback through your normal deployment process",[195,588,589,591,594],{},[210,590,244],{},[210,592,593],{},"Cloud only, no failover in your own hands",[210,595,596],{},"When something breaks, the fix is on the vendor",[195,598,599,602,605],{},[210,600,601],{},"Price",[210,603,604],{},"Usage-based and tied to the Shopware account",[210,606,607],{},"A price change is hard to dodge on short notice, because a migration takes time",[195,609,610,613,616],{},[210,611,612],{},"Platform",[210,614,615],{},"The integration logic belongs to the shop system",[210,617,618],{},"A platform switch then affects not just the shop but the entire integration",[158,620,621],{},"The last row weighs heaviest in practice.",[158,623,624],{},"Custom middleware is exactly the part that makes replatforming affordable. It knows the ERP, the mappings, and the special cases from years in production. When the shop system changes, you swap the shop adapter and keep the rest.",[281,626,627],{},[158,628,629],{},"Map the ERP integration entirely in Nexus and a shop switch becomes an integration project on top. In practice, exactly this calculation often decides whether a replatforming happens at all.",[631,632,634],"h3",{"id":633},"what-being-tied-to-the-account-means-in-practice","What being tied to the account means in practice",[158,636,637],{},"Usage-based prices are plannable as long as the model stays stable. An episode from last year shows what being tied to a Shopware account means in practice.",[158,639,640],{},"As of March 24, 2025, Shopware introduced a Fair Usage Policy. Since then, the Community Edition is free to use without restriction up to €1 million GMV per year. Above that, you move to a paid plan. All Community Edition users are required to report their GMV, regardless of size, with the first deadline on June 30, 2025.",[158,642,643],{},"What a move means is on the pricing page: Rise starts at €600 per month plus VAT, Evolve at €2,400, Beyond on request.",[158,645,646],{},"For planning, the interesting part is less the threshold than where the policy takes hold. The Community Edition code is still MIT-licensed, the policy did not change that. It governs access to the Shopware Account and Store. Anyone above the threshold who does not switch risks a blocked account and store, including cancellation of rented extensions. The shop itself keeps running: according to Shopware, the Community Edition can still be used without account and store.",[281,648,649],{},[158,650,651],{},"Nexus requires an active Shopware account and hangs on exactly that level. Unlike the shop, there is no MIT-licensed part here that keeps running without the service.",[158,653,654],{},"This is not an accusation. Every vendor is allowed to evolve its model, and there were understandable reasons for this step. For your own planning it just means: the more business process hangs on a service, the less room you have the next time the vendor adjusts.",[158,656,657],{},"The same goes for product decisions. OpenAI withdrew Instant Checkout after around five months, and no vendor is immune to such course corrections. For a Slack notification that is survivable, for an order flow less so.",[631,659,661],{"id":660},"reducing-lock-in-without-giving-up-nexus","Reducing lock-in without giving up Nexus",[158,663,664],{},"The way out is splitting the integration: Nexus takes the trigger and transport, the business logic stays in your own code. The workflow no longer models conditions, mappings, or error handling. It calls your own endpoint and hands over the raw data. Everything from that point on lives in the repository, versioned, testable, and independent of Nexus.",[158,666,667],{},"This split only pays off for critical flows. A Slack message for every order above €5,000 belongs entirely in the canvas, meaning the visual workflow editor of Nexus. A dedicated endpoint for that would be overhead.",[158,669,670],{},"For the critical flows, a fair question remains: if the business logic lives in your own code anyway, why send the event through Nexus at all? There are two reasons, and at least one should apply:",[386,672,673,679],{},[389,674,675,678],{},[324,676,677],{},"The target side has a finished connector."," Your endpoint prepares the data and hands it back, the connector does the writing into the target system. That saves you its authentication and error handling while the business logic stays in the repository.",[389,680,681,684],{},[324,682,683],{},"The same event goes to several receivers, and that list changes."," An additional receiver is a configuration change in the workflow, a release in your own code.",[158,686,687,688,690],{},"If neither applies, Nexus has no job left in that flow. The event then runs through a paid service only to land at an endpoint that does everything itself. A plugin with an ",[184,689,291],{}," takes it there directly.",[158,692,693],{},"The following endpoint shows the receiving side. The idempotency in it is mandatory, not a precaution: Shopware documents at-least-once delivery, so the same event can arrive more than once.",[331,695,699],{"className":696,"code":697,"language":698,"meta":339,"style":339},"language-php shiki shiki-themes vitesse-light vitesse-dark","\u003C?php\n\ndeclare(strict_types=1);\n\nnamespace App\\Controller;\n\nuse App\\Message\\ProcessOrderMessage;\nuse Psr\\Log\\LoggerInterface;\nuse Symfony\\Component\\HttpFoundation\\JsonResponse;\nuse Symfony\\Component\\HttpFoundation\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\nuse Symfony\\Component\\Routing\\Attribute\\Route;\nuse Symfony\\Component\\Messenger\\MessageBusInterface;\nuse Symfony\\Contracts\\Cache\\CacheInterface;\nuse Symfony\\Contracts\\Cache\\ItemInterface;\n\nfinal class OrderWebhookController\n{\n    public function __construct(\n        private readonly MessageBusInterface $bus,\n        private readonly CacheInterface $idempotencyStore,\n        private readonly LoggerInterface $logger,\n        private readonly string $webhookSecret,\n    ) {\n    }\n\n    #[Route('\u002Fhooks\u002Forder-placed', methods: ['POST'])]\n    public function __invoke(Request $request): Response\n    {\n        $payload = $request->getContent();\n\n        \u002F\u002F 1. Verify the signature. The endpoint is publicly reachable,\n        \u002F\u002F    authenticity rests entirely on the HMAC.\n        if (!$this->isSignatureValid($payload, (string) $request->headers->get('X-Signature'))) {\n            return new JsonResponse(['error' => 'invalid signature'], Response::HTTP_UNAUTHORIZED);\n        }\n\n        $data = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);\n        $orderNumber = (string) ($data['orderNumber'] ?? '');\n\n        if ($orderNumber === '') {\n            return new JsonResponse(['error' => 'orderNumber missing'], Response::HTTP_BAD_REQUEST);\n        }\n\n        \u002F\u002F 2. Idempotency. With at-least-once delivery the same event\n        \u002F\u002F    arrives more than once. The protection belongs on your side.\n        $isDuplicate = true;\n        $this->idempotencyStore->get(\n            'order_hook_' . $orderNumber,\n            function (ItemInterface $item) use (&$isDuplicate): bool {\n                $item->expiresAfter(86400);\n                $isDuplicate = false;\n\n                return true;\n            }\n        );\n\n        if ($isDuplicate) {\n            $this->logger->info('Duplicate call discarded', ['orderNumber' => $orderNumber]);\n\n            return new JsonResponse(['status' => 'duplicate'], Response::HTTP_OK);\n        }\n\n        \u002F\u002F 3. Accept and process asynchronously. The caller gets an immediate\n        \u002F\u002F    response, the actual work runs in your own worker.\n        $this->bus->dispatch(new ProcessOrderMessage($orderNumber, $data));\n\n        return new JsonResponse(['status' => 'accepted'], Response::HTTP_ACCEPTED);\n    }\n\n    private function isSignatureValid(string $payload, string $received): bool\n    {\n        $expected = hash_hmac('sha256', $payload, $this->webhookSecret);\n\n        return $received !== '' && hash_equals($expected, $received);\n    }\n}\n","php",[184,700,701,714,721,745,750,769,774,795,815,840,862,884,912,935,959,981,986,998,1004,1019,1041,1058,1075,1092,1101,1107,1112,1159,1186,1192,1217,1222,1229,1235,1300,1347,1353,1358,1392,1433,1438,1456,1493,1498,1503,1509,1515,1529,1546,1566,1604,1624,1638,1643,1653,1659,1665,1670,1683,1728,1733,1771,1776,1781,1787,1793,1830,1835,1873,1878,1883,1918,1923,1963,1968,2000,2005],{"__ignoreMap":339},[702,703,706,710],"span",{"class":704,"line":705},"line",1,[702,707,709],{"class":708},"s5TCs","\u003C?",[702,711,713],{"class":712},"sfsYZ","php\n",[702,715,717],{"class":704,"line":716},2,[702,718,720],{"emptyLinePlaceholder":719},true,"\n",[702,722,724,728,732,735,738,742],{"class":704,"line":723},3,[702,725,727],{"class":726},"sTPum","declare",[702,729,731],{"class":730},"si6no","(",[702,733,734],{"class":712},"strict_types",[702,736,737],{"class":730},"=",[702,739,741],{"class":740},"sqbOQ","1",[702,743,744],{"class":730},");\n",[702,746,748],{"class":704,"line":747},4,[702,749,720],{"emptyLinePlaceholder":719},[702,751,753,756,760,763,766],{"class":704,"line":752},5,[702,754,755],{"class":726},"namespace",[702,757,759],{"class":758},"s_NWU"," App",[702,761,762],{"class":730},"\\",[702,764,765],{"class":758},"Controller",[702,767,768],{"class":730},";\n",[702,770,772],{"class":704,"line":771},6,[702,773,720],{"emptyLinePlaceholder":719},[702,775,777,780,783,785,788,790,793],{"class":704,"line":776},7,[702,778,779],{"class":726},"use",[702,781,759],{"class":782},"sHLBJ",[702,784,762],{"class":730},[702,786,787],{"class":782},"Message",[702,789,762],{"class":730},[702,791,792],{"class":782},"ProcessOrderMessage",[702,794,768],{"class":730},[702,796,798,800,803,805,808,810,813],{"class":704,"line":797},8,[702,799,779],{"class":726},[702,801,802],{"class":782}," Psr",[702,804,762],{"class":730},[702,806,807],{"class":782},"Log",[702,809,762],{"class":730},[702,811,812],{"class":782},"LoggerInterface",[702,814,768],{"class":730},[702,816,818,820,823,825,828,830,833,835,838],{"class":704,"line":817},9,[702,819,779],{"class":726},[702,821,822],{"class":782}," Symfony",[702,824,762],{"class":730},[702,826,827],{"class":782},"Component",[702,829,762],{"class":730},[702,831,832],{"class":782},"HttpFoundation",[702,834,762],{"class":730},[702,836,837],{"class":782},"JsonResponse",[702,839,768],{"class":730},[702,841,843,845,847,849,851,853,855,857,860],{"class":704,"line":842},10,[702,844,779],{"class":726},[702,846,822],{"class":782},[702,848,762],{"class":730},[702,850,827],{"class":782},[702,852,762],{"class":730},[702,854,832],{"class":782},[702,856,762],{"class":730},[702,858,859],{"class":782},"Request",[702,861,768],{"class":730},[702,863,865,867,869,871,873,875,877,879,882],{"class":704,"line":864},11,[702,866,779],{"class":726},[702,868,822],{"class":782},[702,870,762],{"class":730},[702,872,827],{"class":782},[702,874,762],{"class":730},[702,876,832],{"class":782},[702,878,762],{"class":730},[702,880,881],{"class":782},"Response",[702,883,768],{"class":730},[702,885,887,889,891,893,895,897,900,902,905,907,910],{"class":704,"line":886},12,[702,888,779],{"class":726},[702,890,822],{"class":782},[702,892,762],{"class":730},[702,894,827],{"class":782},[702,896,762],{"class":730},[702,898,899],{"class":782},"Routing",[702,901,762],{"class":730},[702,903,904],{"class":782},"Attribute",[702,906,762],{"class":730},[702,908,909],{"class":782},"Route",[702,911,768],{"class":730},[702,913,915,917,919,921,923,925,928,930,933],{"class":704,"line":914},13,[702,916,779],{"class":726},[702,918,822],{"class":782},[702,920,762],{"class":730},[702,922,827],{"class":782},[702,924,762],{"class":730},[702,926,927],{"class":782},"Messenger",[702,929,762],{"class":730},[702,931,932],{"class":782},"MessageBusInterface",[702,934,768],{"class":730},[702,936,938,940,942,944,947,949,952,954,957],{"class":704,"line":937},14,[702,939,779],{"class":726},[702,941,822],{"class":782},[702,943,762],{"class":730},[702,945,946],{"class":782},"Contracts",[702,948,762],{"class":730},[702,950,951],{"class":782},"Cache",[702,953,762],{"class":730},[702,955,956],{"class":782},"CacheInterface",[702,958,768],{"class":730},[702,960,962,964,966,968,970,972,974,976,979],{"class":704,"line":961},15,[702,963,779],{"class":726},[702,965,822],{"class":782},[702,967,762],{"class":730},[702,969,946],{"class":782},[702,971,762],{"class":730},[702,973,951],{"class":782},[702,975,762],{"class":730},[702,977,978],{"class":782},"ItemInterface",[702,980,768],{"class":730},[702,982,984],{"class":704,"line":983},16,[702,985,720],{"emptyLinePlaceholder":719},[702,987,989,992,995],{"class":704,"line":988},17,[702,990,991],{"class":708},"final",[702,993,994],{"class":708}," class",[702,996,997],{"class":758}," OrderWebhookController\n",[702,999,1001],{"class":704,"line":1000},18,[702,1002,1003],{"class":730},"{\n",[702,1005,1007,1010,1013,1016],{"class":704,"line":1006},19,[702,1008,1009],{"class":708},"    public",[702,1011,1012],{"class":708}," function",[702,1014,1015],{"class":782}," __construct",[702,1017,1018],{"class":730},"(\n",[702,1020,1022,1025,1028,1031,1034,1038],{"class":704,"line":1021},20,[702,1023,1024],{"class":708},"        private",[702,1026,1027],{"class":708}," readonly",[702,1029,1030],{"class":782}," MessageBusInterface",[702,1032,1033],{"class":730}," $",[702,1035,1037],{"class":1036},"s9nN2","bus",[702,1039,1040],{"class":730},",\n",[702,1042,1044,1046,1048,1051,1053,1056],{"class":704,"line":1043},21,[702,1045,1024],{"class":708},[702,1047,1027],{"class":708},[702,1049,1050],{"class":782}," CacheInterface",[702,1052,1033],{"class":730},[702,1054,1055],{"class":1036},"idempotencyStore",[702,1057,1040],{"class":730},[702,1059,1061,1063,1065,1068,1070,1073],{"class":704,"line":1060},22,[702,1062,1024],{"class":708},[702,1064,1027],{"class":708},[702,1066,1067],{"class":782}," LoggerInterface",[702,1069,1033],{"class":730},[702,1071,1072],{"class":1036},"logger",[702,1074,1040],{"class":730},[702,1076,1078,1080,1082,1085,1087,1090],{"class":704,"line":1077},23,[702,1079,1024],{"class":708},[702,1081,1027],{"class":708},[702,1083,1084],{"class":726}," string",[702,1086,1033],{"class":730},[702,1088,1089],{"class":1036},"webhookSecret",[702,1091,1040],{"class":730},[702,1093,1095,1098],{"class":704,"line":1094},24,[702,1096,1097],{"class":730},"    )",[702,1099,1100],{"class":730}," {\n",[702,1102,1104],{"class":704,"line":1103},25,[702,1105,1106],{"class":730},"    }\n",[702,1108,1110],{"class":704,"line":1109},26,[702,1111,720],{"emptyLinePlaceholder":719},[702,1113,1115,1119,1121,1123,1127,1131,1133,1136,1140,1143,1146,1148,1151,1153,1156],{"class":704,"line":1114},27,[702,1116,1118],{"class":1117},"s8w-G","    #[",[702,1120,909],{"class":782},[702,1122,731],{"class":730},[702,1124,1126],{"class":1125},"scnC2","'",[702,1128,1130],{"class":1129},"spP0B","\u002Fhooks\u002Forder-placed",[702,1132,1126],{"class":1125},[702,1134,1135],{"class":730},",",[702,1137,1139],{"class":1138},"s_xSY"," methods",[702,1141,1142],{"class":730},":",[702,1144,1145],{"class":730}," [",[702,1147,1126],{"class":1125},[702,1149,1150],{"class":1129},"POST",[702,1152,1126],{"class":1125},[702,1154,1155],{"class":730},"])",[702,1157,1158],{"class":1117},"]\n",[702,1160,1162,1164,1166,1169,1171,1173,1175,1178,1181,1183],{"class":704,"line":1161},28,[702,1163,1009],{"class":708},[702,1165,1012],{"class":708},[702,1167,1168],{"class":782}," __invoke",[702,1170,731],{"class":730},[702,1172,859],{"class":782},[702,1174,1033],{"class":730},[702,1176,1177],{"class":1036},"request",[702,1179,1180],{"class":730},")",[702,1182,1142],{"class":708},[702,1184,1185],{"class":782}," Response\n",[702,1187,1189],{"class":704,"line":1188},29,[702,1190,1191],{"class":730},"    {\n",[702,1193,1195,1198,1201,1204,1206,1208,1211,1214],{"class":704,"line":1194},30,[702,1196,1197],{"class":730},"        $",[702,1199,1200],{"class":1036},"payload",[702,1202,1203],{"class":730}," =",[702,1205,1033],{"class":730},[702,1207,1177],{"class":1036},[702,1209,1210],{"class":708},"->",[702,1212,1213],{"class":1138},"getContent",[702,1215,1216],{"class":730},"();\n",[702,1218,1220],{"class":704,"line":1219},31,[702,1221,720],{"emptyLinePlaceholder":719},[702,1223,1225],{"class":704,"line":1224},32,[702,1226,1228],{"class":1227},"snYqZ","        \u002F\u002F 1. Verify the signature. The endpoint is publicly reachable,\n",[702,1230,1232],{"class":704,"line":1231},33,[702,1233,1234],{"class":1227},"        \u002F\u002F    authenticity rests entirely on the HMAC.\n",[702,1236,1238,1241,1244,1247,1250,1253,1255,1258,1261,1263,1265,1267,1270,1272,1274,1276,1278,1281,1283,1286,1288,1290,1293,1295,1298],{"class":704,"line":1237},34,[702,1239,1240],{"class":726},"        if",[702,1242,1243],{"class":730}," (",[702,1245,1246],{"class":708},"!",[702,1248,1249],{"class":730},"$",[702,1251,1252],{"class":712},"this",[702,1254,1210],{"class":708},[702,1256,1257],{"class":1138},"isSignatureValid",[702,1259,1260],{"class":730},"($",[702,1262,1200],{"class":1036},[702,1264,1135],{"class":730},[702,1266,1243],{"class":730},[702,1268,1269],{"class":708},"string",[702,1271,1180],{"class":730},[702,1273,1033],{"class":730},[702,1275,1177],{"class":1036},[702,1277,1210],{"class":708},[702,1279,1280],{"class":1036},"headers",[702,1282,1210],{"class":708},[702,1284,1285],{"class":1138},"get",[702,1287,731],{"class":730},[702,1289,1126],{"class":1125},[702,1291,1292],{"class":1129},"X-Signature",[702,1294,1126],{"class":1125},[702,1296,1297],{"class":730},")))",[702,1299,1100],{"class":730},[702,1301,1303,1306,1309,1312,1315,1317,1320,1322,1325,1328,1331,1333,1336,1339,1342,1345],{"class":704,"line":1302},35,[702,1304,1305],{"class":726},"            return",[702,1307,1308],{"class":726}," new",[702,1310,1311],{"class":782}," JsonResponse",[702,1313,1314],{"class":730},"([",[702,1316,1126],{"class":1125},[702,1318,1319],{"class":1129},"error",[702,1321,1126],{"class":1125},[702,1323,1324],{"class":708}," =>",[702,1326,1327],{"class":1125}," '",[702,1329,1330],{"class":1129},"invalid signature",[702,1332,1126],{"class":1125},[702,1334,1335],{"class":730},"],",[702,1337,1338],{"class":782}," Response",[702,1340,1341],{"class":708},"::",[702,1343,1344],{"class":712},"HTTP_UNAUTHORIZED",[702,1346,744],{"class":730},[702,1348,1350],{"class":704,"line":1349},36,[702,1351,1352],{"class":730},"        }\n",[702,1354,1356],{"class":704,"line":1355},37,[702,1357,720],{"emptyLinePlaceholder":719},[702,1359,1361,1363,1366,1368,1371,1373,1375,1377,1380,1382,1385,1387,1390],{"class":704,"line":1360},38,[702,1362,1197],{"class":730},[702,1364,1365],{"class":1036},"data",[702,1367,1203],{"class":730},[702,1369,1370],{"class":782}," json_decode",[702,1372,1260],{"class":730},[702,1374,1200],{"class":1036},[702,1376,1135],{"class":730},[702,1378,1379],{"class":726}," true",[702,1381,1135],{"class":730},[702,1383,1384],{"class":740}," 512",[702,1386,1135],{"class":730},[702,1388,1389],{"class":712}," JSON_THROW_ON_ERROR",[702,1391,744],{"class":730},[702,1393,1395,1397,1400,1402,1404,1406,1408,1411,1413,1416,1418,1420,1422,1425,1428,1431],{"class":704,"line":1394},39,[702,1396,1197],{"class":730},[702,1398,1399],{"class":1036},"orderNumber",[702,1401,1203],{"class":730},[702,1403,1243],{"class":730},[702,1405,1269],{"class":708},[702,1407,1180],{"class":730},[702,1409,1410],{"class":730}," ($",[702,1412,1365],{"class":1036},[702,1414,1415],{"class":730},"[",[702,1417,1126],{"class":1125},[702,1419,1399],{"class":1129},[702,1421,1126],{"class":1125},[702,1423,1424],{"class":730},"]",[702,1426,1427],{"class":708}," ??",[702,1429,1430],{"class":1125}," ''",[702,1432,744],{"class":730},[702,1434,1436],{"class":704,"line":1435},40,[702,1437,720],{"emptyLinePlaceholder":719},[702,1439,1441,1443,1445,1447,1450,1452,1454],{"class":704,"line":1440},41,[702,1442,1240],{"class":726},[702,1444,1410],{"class":730},[702,1446,1399],{"class":1036},[702,1448,1449],{"class":708}," ===",[702,1451,1430],{"class":1125},[702,1453,1180],{"class":730},[702,1455,1100],{"class":730},[702,1457,1459,1461,1463,1465,1467,1469,1471,1473,1475,1477,1480,1482,1484,1486,1488,1491],{"class":704,"line":1458},42,[702,1460,1305],{"class":726},[702,1462,1308],{"class":726},[702,1464,1311],{"class":782},[702,1466,1314],{"class":730},[702,1468,1126],{"class":1125},[702,1470,1319],{"class":1129},[702,1472,1126],{"class":1125},[702,1474,1324],{"class":708},[702,1476,1327],{"class":1125},[702,1478,1479],{"class":1129},"orderNumber missing",[702,1481,1126],{"class":1125},[702,1483,1335],{"class":730},[702,1485,1338],{"class":782},[702,1487,1341],{"class":708},[702,1489,1490],{"class":712},"HTTP_BAD_REQUEST",[702,1492,744],{"class":730},[702,1494,1496],{"class":704,"line":1495},43,[702,1497,1352],{"class":730},[702,1499,1501],{"class":704,"line":1500},44,[702,1502,720],{"emptyLinePlaceholder":719},[702,1504,1506],{"class":704,"line":1505},45,[702,1507,1508],{"class":1227},"        \u002F\u002F 2. Idempotency. With at-least-once delivery the same event\n",[702,1510,1512],{"class":704,"line":1511},46,[702,1513,1514],{"class":1227},"        \u002F\u002F    arrives more than once. The protection belongs on your side.\n",[702,1516,1518,1520,1523,1525,1527],{"class":704,"line":1517},47,[702,1519,1197],{"class":730},[702,1521,1522],{"class":1036},"isDuplicate",[702,1524,1203],{"class":730},[702,1526,1379],{"class":726},[702,1528,768],{"class":730},[702,1530,1532,1534,1536,1538,1540,1542,1544],{"class":704,"line":1531},48,[702,1533,1197],{"class":730},[702,1535,1252],{"class":712},[702,1537,1210],{"class":708},[702,1539,1055],{"class":1036},[702,1541,1210],{"class":708},[702,1543,1285],{"class":1138},[702,1545,1018],{"class":730},[702,1547,1549,1552,1555,1557,1560,1562,1564],{"class":704,"line":1548},49,[702,1550,1551],{"class":1125},"            '",[702,1553,1554],{"class":1129},"order_hook_",[702,1556,1126],{"class":1125},[702,1558,1559],{"class":708}," .",[702,1561,1033],{"class":730},[702,1563,1399],{"class":1036},[702,1565,1040],{"class":730},[702,1567,1569,1572,1574,1576,1578,1581,1583,1586,1588,1591,1593,1595,1597,1599,1602],{"class":704,"line":1568},50,[702,1570,1571],{"class":708},"            function",[702,1573,1243],{"class":730},[702,1575,978],{"class":782},[702,1577,1033],{"class":730},[702,1579,1580],{"class":1036},"item",[702,1582,1180],{"class":730},[702,1584,1585],{"class":726}," use",[702,1587,1243],{"class":730},[702,1589,1590],{"class":708},"&",[702,1592,1249],{"class":730},[702,1594,1522],{"class":1036},[702,1596,1180],{"class":730},[702,1598,1142],{"class":708},[702,1600,1601],{"class":726}," bool",[702,1603,1100],{"class":730},[702,1605,1607,1610,1612,1614,1617,1619,1622],{"class":704,"line":1606},51,[702,1608,1609],{"class":730},"                $",[702,1611,1580],{"class":1036},[702,1613,1210],{"class":708},[702,1615,1616],{"class":1138},"expiresAfter",[702,1618,731],{"class":730},[702,1620,1621],{"class":740},"86400",[702,1623,744],{"class":730},[702,1625,1627,1629,1631,1633,1636],{"class":704,"line":1626},52,[702,1628,1609],{"class":730},[702,1630,1522],{"class":1036},[702,1632,1203],{"class":730},[702,1634,1635],{"class":726}," false",[702,1637,768],{"class":730},[702,1639,1641],{"class":704,"line":1640},53,[702,1642,720],{"emptyLinePlaceholder":719},[702,1644,1646,1649,1651],{"class":704,"line":1645},54,[702,1647,1648],{"class":726},"                return",[702,1650,1379],{"class":726},[702,1652,768],{"class":730},[702,1654,1656],{"class":704,"line":1655},55,[702,1657,1658],{"class":730},"            }\n",[702,1660,1662],{"class":704,"line":1661},56,[702,1663,1664],{"class":730},"        );\n",[702,1666,1668],{"class":704,"line":1667},57,[702,1669,720],{"emptyLinePlaceholder":719},[702,1671,1673,1675,1677,1679,1681],{"class":704,"line":1672},58,[702,1674,1240],{"class":726},[702,1676,1410],{"class":730},[702,1678,1522],{"class":1036},[702,1680,1180],{"class":730},[702,1682,1100],{"class":730},[702,1684,1686,1689,1691,1693,1695,1697,1700,1702,1704,1707,1709,1711,1713,1715,1717,1719,1721,1723,1725],{"class":704,"line":1685},59,[702,1687,1688],{"class":730},"            $",[702,1690,1252],{"class":712},[702,1692,1210],{"class":708},[702,1694,1072],{"class":1036},[702,1696,1210],{"class":708},[702,1698,1699],{"class":1138},"info",[702,1701,731],{"class":730},[702,1703,1126],{"class":1125},[702,1705,1706],{"class":1129},"Duplicate call discarded",[702,1708,1126],{"class":1125},[702,1710,1135],{"class":730},[702,1712,1145],{"class":730},[702,1714,1126],{"class":1125},[702,1716,1399],{"class":1129},[702,1718,1126],{"class":1125},[702,1720,1324],{"class":708},[702,1722,1033],{"class":730},[702,1724,1399],{"class":1036},[702,1726,1727],{"class":730},"]);\n",[702,1729,1731],{"class":704,"line":1730},60,[702,1732,720],{"emptyLinePlaceholder":719},[702,1734,1736,1738,1740,1742,1744,1746,1749,1751,1753,1755,1758,1760,1762,1764,1766,1769],{"class":704,"line":1735},61,[702,1737,1305],{"class":726},[702,1739,1308],{"class":726},[702,1741,1311],{"class":782},[702,1743,1314],{"class":730},[702,1745,1126],{"class":1125},[702,1747,1748],{"class":1129},"status",[702,1750,1126],{"class":1125},[702,1752,1324],{"class":708},[702,1754,1327],{"class":1125},[702,1756,1757],{"class":1129},"duplicate",[702,1759,1126],{"class":1125},[702,1761,1335],{"class":730},[702,1763,1338],{"class":782},[702,1765,1341],{"class":708},[702,1767,1768],{"class":712},"HTTP_OK",[702,1770,744],{"class":730},[702,1772,1774],{"class":704,"line":1773},62,[702,1775,1352],{"class":730},[702,1777,1779],{"class":704,"line":1778},63,[702,1780,720],{"emptyLinePlaceholder":719},[702,1782,1784],{"class":704,"line":1783},64,[702,1785,1786],{"class":1227},"        \u002F\u002F 3. Accept and process asynchronously. The caller gets an immediate\n",[702,1788,1790],{"class":704,"line":1789},65,[702,1791,1792],{"class":1227},"        \u002F\u002F    response, the actual work runs in your own worker.\n",[702,1794,1796,1798,1800,1802,1804,1806,1809,1811,1814,1817,1819,1821,1823,1825,1827],{"class":704,"line":1795},66,[702,1797,1197],{"class":730},[702,1799,1252],{"class":712},[702,1801,1210],{"class":708},[702,1803,1037],{"class":1036},[702,1805,1210],{"class":708},[702,1807,1808],{"class":1138},"dispatch",[702,1810,731],{"class":730},[702,1812,1813],{"class":726},"new",[702,1815,1816],{"class":782}," ProcessOrderMessage",[702,1818,1260],{"class":730},[702,1820,1399],{"class":1036},[702,1822,1135],{"class":730},[702,1824,1033],{"class":730},[702,1826,1365],{"class":1036},[702,1828,1829],{"class":730},"));\n",[702,1831,1833],{"class":704,"line":1832},67,[702,1834,720],{"emptyLinePlaceholder":719},[702,1836,1838,1841,1843,1845,1847,1849,1851,1853,1855,1857,1860,1862,1864,1866,1868,1871],{"class":704,"line":1837},68,[702,1839,1840],{"class":726},"        return",[702,1842,1308],{"class":726},[702,1844,1311],{"class":782},[702,1846,1314],{"class":730},[702,1848,1126],{"class":1125},[702,1850,1748],{"class":1129},[702,1852,1126],{"class":1125},[702,1854,1324],{"class":708},[702,1856,1327],{"class":1125},[702,1858,1859],{"class":1129},"accepted",[702,1861,1126],{"class":1125},[702,1863,1335],{"class":730},[702,1865,1338],{"class":782},[702,1867,1341],{"class":708},[702,1869,1870],{"class":712},"HTTP_ACCEPTED",[702,1872,744],{"class":730},[702,1874,1876],{"class":704,"line":1875},69,[702,1877,1106],{"class":730},[702,1879,1881],{"class":704,"line":1880},70,[702,1882,720],{"emptyLinePlaceholder":719},[702,1884,1886,1889,1891,1894,1896,1898,1900,1902,1904,1906,1908,1911,1913,1915],{"class":704,"line":1885},71,[702,1887,1888],{"class":708},"    private",[702,1890,1012],{"class":708},[702,1892,1893],{"class":1138}," isSignatureValid",[702,1895,731],{"class":730},[702,1897,1269],{"class":726},[702,1899,1033],{"class":730},[702,1901,1200],{"class":1036},[702,1903,1135],{"class":730},[702,1905,1084],{"class":726},[702,1907,1033],{"class":730},[702,1909,1910],{"class":1036},"received",[702,1912,1180],{"class":730},[702,1914,1142],{"class":708},[702,1916,1917],{"class":726}," bool\n",[702,1919,1921],{"class":704,"line":1920},72,[702,1922,1191],{"class":730},[702,1924,1926,1928,1931,1933,1936,1938,1940,1943,1945,1947,1949,1951,1953,1955,1957,1959,1961],{"class":704,"line":1925},73,[702,1927,1197],{"class":730},[702,1929,1930],{"class":1036},"expected",[702,1932,1203],{"class":730},[702,1934,1935],{"class":782}," hash_hmac",[702,1937,731],{"class":730},[702,1939,1126],{"class":1125},[702,1941,1942],{"class":1129},"sha256",[702,1944,1126],{"class":1125},[702,1946,1135],{"class":730},[702,1948,1033],{"class":730},[702,1950,1200],{"class":1036},[702,1952,1135],{"class":730},[702,1954,1033],{"class":730},[702,1956,1252],{"class":712},[702,1958,1210],{"class":708},[702,1960,1089],{"class":1036},[702,1962,744],{"class":730},[702,1964,1966],{"class":704,"line":1965},74,[702,1967,720],{"emptyLinePlaceholder":719},[702,1969,1971,1973,1975,1977,1980,1982,1985,1988,1990,1992,1994,1996,1998],{"class":704,"line":1970},75,[702,1972,1840],{"class":726},[702,1974,1033],{"class":730},[702,1976,1910],{"class":1036},[702,1978,1979],{"class":708}," !==",[702,1981,1430],{"class":1125},[702,1983,1984],{"class":708}," &&",[702,1986,1987],{"class":782}," hash_equals",[702,1989,1260],{"class":730},[702,1991,1930],{"class":1036},[702,1993,1135],{"class":730},[702,1995,1033],{"class":730},[702,1997,1910],{"class":1036},[702,1999,744],{"class":730},[702,2001,2003],{"class":704,"line":2002},76,[702,2004,1106],{"class":730},[702,2006,2008],{"class":704,"line":2007},77,[702,2009,2010],{"class":730},"}\n",[158,2012,2013],{},"If Nexus goes away, you replace the trigger with an EventSubscriber in your own plugin, a cron job, or a queue consumer. The endpoint, the processing, the error handling, and the mappings stay untouched. The switch costs days, not months.",[158,2015,2016],{},"Three rules cover the day-to-day:",[386,2018,2019,2022,2025],{},[389,2020,2021],{},"Every flow gets an answer up front to the question of what happens if it does not run for two days. If the outage is survivable, everything may go into the canvas.",[389,2023,2024],{},"If the outage gets expensive, the business rules belong in code, not in the canvas.",[389,2026,2027],{},"If that leaves the workflow with nothing but a single call, the flow does not belong in Nexus but on your own EventSubscriber.",[170,2029],{},[173,2031,2033],{"id":2032},"the-third-option-a-self-hosted-workflow-engine","The third option: a self-hosted workflow engine",[158,2035,2036],{},"Workflow engines of this pattern also exist for self-hosting. With n8n, the same kind of workflow runs on your own infrastructure, without per-execution billing.",[158,2038,2039],{},"For Shopware there are community nodes, for example from solution25 and codeblick. Where no node fits, the HTTP node remains. For calls against the Shopware Admin API that is no practical drawback.",[189,2041,2042,2054],{},[192,2043,2044],{},[195,2045,2046,2049,2051],{},[198,2047,2048],{},"Criterion",[198,2050,472],{},[198,2052,2053],{},"n8n, self-hosted",[205,2055,2056,2066,2077,2088,2099,2110,2121,2132],{},[195,2057,2058,2060,2063],{},[210,2059,244],{},[210,2061,2062],{},"cloud at Shopware only",[210,2064,2065],{},"your own server or Kubernetes",[195,2067,2068,2071,2074],{},[210,2069,2070],{},"Running costs",[210,2072,2073],{},"per execution",[210,2075,2076],{},"infrastructure, independent of volume",[195,2078,2079,2082,2085],{},[210,2080,2081],{},"Workflow definition",[210,2083,2084],{},"configuration in the Shopware cloud",[210,2086,2087],{},"JSON, exportable and versionable in Git",[195,2089,2090,2093,2096],{},[210,2091,2092],{},"Shopware events",[210,2094,2095],{},"configured in the service",[210,2097,2098],{},"plugin with EventSubscriber or app webhook, built yourself",[195,2100,2101,2104,2107],{},[210,2102,2103],{},"Scaling",[210,2105,2106],{},"by the vendor",[210,2108,2109],{},"queue mode with Redis and multiple workers",[195,2111,2112,2115,2118],{},[210,2113,2114],{},"Test mode and undo",[210,2116,2117],{},"not available in Early Access",[210,2119,2120],{},"available",[195,2122,2123,2126,2129],{},[210,2124,2125],{},"Error handling",[210,2127,2128],{},"limited error details",[210,2130,2131],{},"your own error workflows, retries, your own monitoring",[195,2133,2134,2137,2139],{},[210,2135,2136],{},"Operational effort",[210,2138,496],{},[210,2140,2141],{},"entirely on you",[158,2143,2144],{},"The core of the comparison sits in two rows: Nexus delivers the events out of the box and takes over operations, n8n delivers control and traceability. The operational effort is the price: n8n in production means a main instance, workers, Redis, and Postgres, plus backups, updates, and monitoring.",[631,2146,2148],{"id":2147},"two-things-to-know-about-n8n","Two things to know about n8n",[158,2150,2151,2154],{},[324,2152,2153],{},"The license is not OSI-approved."," n8n has been under the Sustainable Use License since March 17, 2022, before that Apache 2.0 with Commons Clause. For your own shop this is uncritical: use for internal business purposes is explicitly allowed, and that is exactly what connecting your own systems is. What is off limits is reselling n8n itself as a service.",[158,2156,2157,2158,2161],{},"A second point matters more in practice: files with ",[184,2159,2160],{},".ee."," in their path are not under the Sustainable Use License but require an enterprise license. This covers features n8n lists as enterprise features, SSO among them. If you need those, you pay with n8n too.",[158,2163,2164,2167],{},[324,2165,2166],{},"The canvas problem stays."," With n8n, too, the logic lives in a graphical editor. The difference is that workflows can be exported as JSON and put into the repository. Properly reviewing those JSON diffs is still barely possible.",[281,2169,2170],{},[158,2171,2172],{},"n8n takes vendor lock-in and per-execution billing out of the equation. It does not change the underlying problem: business logic in a canvas is harder to check than code.",[158,2174,2175],{},"The recommendation from the previous section stands. Nexus or n8n: trigger and transport belong in the engine, the business logic in your own endpoint.",[158,2177,2178],{},"If you do not need a canvas in the first place, Temporal or a plain combination of Symfony Messenger and cron is often leaner. Kestra, Windmill, and Node-RED go in the same direction as n8n and are worth a look when a specific requirement fits better there.",[170,2180],{},[173,2182,2184],{"id":2183},"the-hybrid-approach","The hybrid approach",[158,2186,2187],{},"In most mid-sized projects, the decision is not an either-or.",[158,2189,2190],{},"A practical cut:",[386,2192,2193,2199],{},[389,2194,2195,2198],{},[324,2196,2197],{},"A workflow engine"," takes the standard flows, notifications, and everything event-driven and low-volume. Whether Nexus or n8n depends on whether an ops team exists",[389,2200,2201,2203],{},[324,2202,475],{}," takes the critical flows with state, checkpoints, and special logic, typically product data, prices, and order processing",[158,2205,2206],{},"That shrinks the custom build considerably without the parts that cost money in a failure hanging on a third-party service. The middleware also survives a platform switch.",[158,2208,2209],{},"The price of this path is running and documenting two systems. It pays off from the point where at least one flow hits the limits of Nexus.",[170,2211],{},[173,2213,2215],{"id":2214},"decision-matrix","Decision matrix",[189,2217,2218,2231],{},[192,2219,2220],{},[195,2221,2222,2224,2226,2229],{},[198,2223,2048],{},[198,2225,472],{},[198,2227,2228],{},"Hybrid",[198,2230,475],{},[205,2232,2233,2247,2261,2274,2287,2298,2309,2323,2337],{},[195,2234,2235,2238,2241,2244],{},[210,2236,2237],{},"Connector available",[210,2239,2240],{},"yes",[210,2242,2243],{},"partly",[210,2245,2246],{},"irrelevant",[195,2248,2249,2252,2255,2258],{},[210,2250,2251],{},"Executions per month (rough guide)",[210,2253,2254],{},"below ~50,000",[210,2256,2257],{},"mixed",[210,2259,2260],{},"above ~200,000",[195,2262,2263,2266,2268,2271],{},[210,2264,2265],{},"Special logic in the mapping",[210,2267,496],{},[210,2269,2270],{},"in places",[210,2272,2273],{},"extensive",[195,2275,2276,2279,2282,2285],{},[210,2277,2278],{},"Error handling with checkpoints needed",[210,2280,2281],{},"no",[210,2283,2284],{},"for some flows",[210,2286,2240],{},[195,2288,2289,2292,2294,2296],{},[210,2290,2291],{},"Traceability required when things fail",[210,2293,2281],{},[210,2295,2284],{},[210,2297,2240],{},[195,2299,2300,2303,2305,2307],{},[210,2301,2302],{},"Control over the runtime environment needed",[210,2304,2281],{},[210,2306,2284],{},[210,2308,2240],{},[195,2310,2311,2314,2317,2320],{},[210,2312,2313],{},"Own ops team",[210,2315,2316],{},"not needed",[210,2318,2319],{},"helpful",[210,2321,2322],{},"required",[195,2324,2325,2328,2331,2334],{},[210,2326,2327],{},"Platform switch conceivable in the next few years",[210,2329,2330],{},"uncritical",[210,2332,2333],{},"core flows stay portable",[210,2335,2336],{},"fully portable",[195,2338,2339,2342,2344,2347],{},[210,2340,2341],{},"Two days of downtime survivable",[210,2343,2240],{},[210,2345,2346],{},"for the Nexus flows",[210,2348,2349],{},"does not have to be",[158,2351,2352],{},"If you agree with the whole Nexus column but have an ops team and want to avoid per-execution billing, you end up with a self-hosted n8n. The technical assessment stays the same, only who runs it changes.",[170,2354],{},[173,2356,2358],{"id":2357},"conclusion","Conclusion",[158,2360,2361],{},"Technically, Nexus is a cloud iPaaS in an established product category. Whether it holds up is decided by the connector catalog: if your target system is in it, it takes real work off your plate. For those cases, custom middleware is off the table. Saying that openly is hard for an agency that lives off exactly these projects. It does not change the fact that it is true.",[158,2363,2364],{},"What remains are the flows with state, special logic, or volume. There, Nexus in its current state is not a replacement but a complement for the simple flows next to them. The reasons come from the documentation itself: no test mode, limited error details, at-least-once delivery. Part of that should resolve itself with upcoming releases.",[158,2366,2367],{},"The cost calculation alone does not carry the decision. For the flows that carry the business, the dependency weighs more than the effort. If you put the ERP integration entirely into a platform that belongs to the shop system, a platform switch means replacing both at once.",[158,2369,2370],{},"Whatever you choose, the same rule applies: business logic in your own repository, trigger and transport in the engine. That keeps the benefit and makes the switch a matter of days.",[158,2372,2373],{},"👉 If you want to know which of your integrations belong in Nexus and which do not:",[2375,2376,2396],"nuxt-link",{"className":2377,"to":2395},[2378,2379,189,2380,2381,2382,2383,2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394],"not-prose","mx-auto","text-white","px-4","py-1.5","text-base","font-semibold","leading-7","shadow-sm","ring-1","duration-300","ease","rounded-lg","bg-primary","ring-primary","hover:bg-secondary","hover:ring-secondary","\u002Fen\u002Fcontact",[158,2397,2398],{},"👉 get in touch",[170,2400],{},[173,2402,2404],{"id":2403},"sources","Sources",[386,2406,2407,2416,2424,2434,2442,2450,2458,2466,2479],{},[389,2408,2409,2415],{},[162,2410,2414],{"href":2411,"rel":2412},"https:\u002F\u002Fdocs.shopware.com\u002Fen\u002Fshopware-6-en\u002Fshopware-services\u002Fshopware-nexus",[2413],"nofollow","Shopware Docs: Shopware Nexus"," for requirements, activation, triggers, and actions",[389,2417,2418,2423],{},[162,2419,2422],{"href":2420,"rel":2421},"https:\u002F\u002Fdeveloper.shopware.com\u002Fdocs\u002Fproducts\u002Fnexus\u002Fgetting-started.html",[2413],"Shopware Developer Docs: Nexus, Getting Started"," for the known limitations in Early Access",[389,2425,2426,2431,2432],{},[162,2427,2430],{"href":2428,"rel":2429},"https:\u002F\u002Fdeveloper.shopware.com\u002Fdocs\u002Fguides\u002Fplugins\u002Fplugins\u002Fplugin-fundamentals\u002Flistening-to-events.html",[2413],"Shopware Developer Docs: Listening to events"," for the plugin route via ",[184,2433,291],{},[389,2435,2436,2441],{},[162,2437,2440],{"href":2438,"rel":2439},"https:\u002F\u002Fdeveloper.shopware.com\u002Fdocs\u002Fguides\u002Fplugins\u002Fapps\u002Fwebhook.html",[2413],"Shopware Developer Docs: App system, webhooks"," for manifest, registration, and signature verification",[389,2443,2444,2449],{},[162,2445,2448],{"href":2446,"rel":2447},"https:\u002F\u002Fwww.shopware.com\u002Fen\u002Fproducts\u002Fshopware-nexus\u002F",[2413],"Shopware: Nexus product page"," for the claims on integration costs and project effort",[389,2451,2452,2457],{},[162,2453,2456],{"href":2454,"rel":2455},"https:\u002F\u002Fwww.shopware.com\u002Fen\u002Fpricing\u002F",[2413],"Shopware: Pricing"," for the edition prices and Nexus billing",[389,2459,2460,2465],{},[162,2461,2464],{"href":2462,"rel":2463},"https:\u002F\u002Fwww.shopware.com\u002Fen\u002Fnews\u002Ffair-usage-policy-community-edition\u002F",[2413],"Shopware: Fair Usage Policy for the Community Edition"," for threshold, reporting duty, and deadlines",[389,2467,2468,2473,2474],{},[162,2469,2472],{"href":2470,"rel":2471},"https:\u002F\u002Fdocs.n8n.io\u002Fsustainable-use-license\u002F",[2413],"n8n: Sustainable Use License"," and ",[162,2475,2478],{"href":2476,"rel":2477},"https:\u002F\u002Fgithub.com\u002Fn8n-io\u002Fn8n\u002Fblob\u002Fmaster\u002FLICENSE.md",[2413],"LICENSE.md",[389,2480,2481],{},[162,2482,2485],{"href":2483,"rel":2484},"https:\u002F\u002Fdocs.n8n.io\u002Fhosting\u002Fscaling\u002Fqueue-mode\u002F",[2413],"n8n Docs: Queue Mode",[158,2487,2488],{},"Information current as of July 2026. Nexus is in Early Access, scope and terms can change.",[2490,2491,2492],"style",{},"html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .sHLBJ, html code.shiki .sHLBJ{--shiki-default:#998418;--shiki-dark:#B8A965}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .scnC2, html code.shiki .scnC2{--shiki-default:#B5695977;--shiki-dark:#C98A7D77}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":339,"searchDepth":716,"depth":716,"links":2494},[2495,2496,2497,2498,2499,2500,2501,2505,2508,2509,2510,2511],{"id":175,"depth":716,"text":176},{"id":263,"depth":716,"text":264},{"id":310,"depth":716,"text":311},{"id":380,"depth":716,"text":381},{"id":411,"depth":716,"text":412},{"id":456,"depth":716,"text":457},{"id":551,"depth":716,"text":552,"children":2502},[2503,2504],{"id":633,"depth":723,"text":634},{"id":660,"depth":723,"text":661},{"id":2032,"depth":716,"text":2033,"children":2506},[2507],{"id":2147,"depth":723,"text":2148},{"id":2183,"depth":716,"text":2184},{"id":2214,"depth":716,"text":2215},{"id":2357,"depth":716,"text":2358},{"id":2403,"depth":716,"text":2404},"Nexus is the cloud iPaaS built into Shopware. What it takes off your plate, where the limits are, what per-execution billing means for your architecture, and which flows are better kept in your own hands.","md",{},"\u002Fen\u002Fblog\u002Fshopware-nexus-or-custom-middleware","2026-07-31 08:00:00",[2518],{"headline":148,"mainEntityOfPage":2519,"author":2521,"publisher":2526,"datePublished":2527,"@type":2528},{"@id":2520},"#webpage",{"@id":2522,"name":2523,"url":2524,"@type":2525},"#identity","Kickbyte GmbH","https:\u002F\u002Fkickbyte.de","Organization",{"@id":2522,"name":2523,"url":2524,"@type":2525},"2026-07-31T08:00:00Z","BlogPosting",{"title":148,"description":2530,"ogTitle":2531,"ogDescription":2532,"robots":2533},"Nexus in practice: what the native iPaaS is, available connectors, per-execution costs, a three-year comparison, and n8n as a self-hosted alternative.","Shopware Nexus or Custom Middleware? A Decision Guide - Kickbyte","What Shopware Nexus takes off your plate, where the limits are, and which ERP flows are better kept in your own hands.","index,follow",{"loc":2515,"lastmod":2535,"changefreq":2536,"priority":2537},"2026-07-31","monthly",0.8,"en\u002Fblog\u002Fshopware-nexus-or-custom-middleware",[2540,472,2541,2542,2543,2544,2545],"Shopware","n8n","ERP-Integration","Systemintegration","Middleware","E-Commerce","shopware-nexus-vs-custom-middleware","PjQdvpcItsIQ-XvfUnLfWlVn7AkkXcIRr1f4OSqfIqk",1787334845170]