<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/ecommerce/</id>
  <link href="https://www.endpointdev.com/blog/tags/ecommerce/"/>
  <link href="https://www.endpointdev.com/blog/tags/ecommerce/" rel="self"/>
  <updated>2025-10-29T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Adventures in Vibe-Coding with Replit</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/10/adventures-in-vibe-coding-with-replit/"/>
      <id>https://www.endpointdev.com/blog/2025/10/adventures-in-vibe-coding-with-replit/</id>
      <published>2025-10-29T00:00:00+00:00</published>
      <author>
        <name>Seth Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/10/adventures-in-vibe-coding-with-replit/nyc-streets-from-above.webp&#34; alt=&#34;A New York City intersection viewed from above, at a 40 degree angle&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Seth Jensen, 2025, shot on Kodak 200 with a Nikon FE --&gt;
&lt;p&gt;A few weeks back, I tried out Replit Agent to see how viable it is as a development tool. I built two apps from scratch, which took 10–15 hours total, with a decent amount of human input along the way to clarify the agent&amp;rsquo;s questions.&lt;/p&gt;
&lt;h3 id=&#34;project-1-multi-llm-code-review-api&#34;&gt;Project 1: Multi-LLM code review API&lt;/h3&gt;
&lt;p&gt;The idea for the first project was to have a multi-LLM code review app, where you would pass a repository on GitHub or locally to an API, and it would run several full-repo code reviews, ranking and deduplicating them before returning a response. This setup should allow me to have a web frontend or CLI without much added complexity.&lt;/p&gt;
&lt;h4 id=&#34;the-prompt&#34;&gt;The prompt&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please build an app based on this app specification: app_spec.yaml (460 lines)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;the-results&#34;&gt;The results&lt;/h4&gt;
&lt;p&gt;After much back and forth (and about 40 agent-generated Git commits), I got this message:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;🎉 SUCCESS! The Multi-LLM Code Review Assistant is now fully working!&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well, I actually got this message several times after sending just about any command to the agent. It&amp;rsquo;s very excited about its own work, even when it doesn&amp;rsquo;t work! In this case, the API connections were working, and testing with curl gave output, but I couldn&amp;rsquo;t get it to give more than one code review tip for a large codebase — not a very effective code-review app.&lt;/p&gt;
&lt;p&gt;I tried asking the agent to diagnose and fix this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;So, this seems to not be returning very many code review suggestions. If I go
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;look at the repos I&amp;#39;m putting in, there are plenty of code issues that could be
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;returned, especially with documentation. How can we make this system find more
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;issues?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In response, Replit&amp;rsquo;s agent created a full React frontend for the API! Neat, but far from what I asked — the app still doesn&amp;rsquo;t return substantial code reviews.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/10/adventures-in-vibe-coding-with-replit/code-review-unwanted-frontend.webp&#34; alt=&#34;Code Review AI Multi-LLM Assistant interface showing dark-themed navigation sidebar with options for Submit Code, Dashboard, Analytics, and Settings. Main content area displays &amp;lsquo;Multi-LLM Code Review Assistant&amp;rsquo; title with description and two buttons: GitHub Repository and Upload ZIP File.&#34;&gt;&lt;/p&gt;
&lt;p&gt;So after ~$100 of agent credits, I found that Replit was not the tool for this app — I&amp;rsquo;m now trying to build a &lt;a href=&#34;/blog/2025/10/creating-agentic-ai-apps/&#34;&gt;CrewAI&lt;/a&gt; app to accomplish the same thing (stay tuned for a post about that).&lt;/p&gt;
&lt;h3 id=&#34;project-2-end-point-ecommerce--hugo-site&#34;&gt;Project 2: End Point Ecommerce + Hugo site&lt;/h3&gt;
&lt;p&gt;A common theme in Replit apps we&amp;rsquo;ve experimented with is that it prefers to make everything a web app with a React frontend. I was curious how it&amp;rsquo;d do with a different web stack: The &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt; static site generator for the frontend and our recently launched &lt;a href=&#34;/expertise/end-point-ecommerce/&#34;&gt;End Point Ecommerce&lt;/a&gt; for the backend.&lt;/p&gt;
&lt;p&gt;This is an interesting problem for an LLM, since Hugo is widely used (including on &lt;a href=&#34;/blog/2021/08/converting-to-hugo/&#34;&gt;this site&lt;/a&gt;), but still small compared to React, Vue, and similar frameworks, and since End Point Ecommerce is well-documented but too new for any online discussion. However, it&amp;rsquo;s a modern .NET ecommerce framework and the code is readable and straightforward.&lt;/p&gt;
&lt;h4 id=&#34;the-prompt-1&#34;&gt;The prompt&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Build me a hugo website frontend to interact with end point ecommerce:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://ecommerce.endpointdev.com/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;For the UI, make a halloween-themed storefront with orange text, black and grey
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;backgrounds, and halloween-themed test products. Use the Ballast font for
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;headers, and charter for body text for now.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After providing this initial prompt, I followed up with these:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Here is the github link for end point ecommerce, which this site will connect to:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://github.com/EndPointCorp/end-point-ecommerce
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please make the app as simple as possible, while still working
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Oh, and please use this demo API to connect to for now, so you don&amp;#39;t have to
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;build any backend at all, just interface with this:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://demo.ecommerce.endpointdev.com/swagger/index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;the-results-version-1&#34;&gt;The results (version 1)&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/10/adventures-in-vibe-coding-with-replit/site-v1.png&#34; alt=&#34;Spooky Store Halloween ecommerce website showing three product cards featuring jack-o&amp;rsquo;-lantern pumpkin carving designs. Products listed are: Pumpkin Spice Treats with pumpkin emoji ($3.99), Spider Web Candy with spider emoji ($2.49), and Ghost Marshmallows with skull emoji ($4.99). Dark background with orange Halloween-themed branding.&#34;&gt;&lt;/p&gt;
&lt;p&gt;It generated a nice, simple ecommerce storefront, using Hugo and End Point Ecommerce. Impressive! However, there&amp;rsquo;s one red flag: if you look at the products &lt;a href=&#34;https://demo.ecommerce.endpointdev.com/&#34;&gt;in the demo API&lt;/a&gt; we&amp;rsquo;re using, they are simple groceries — Apple, Banana, etc. So where did &amp;ldquo;Pumpkin spice treats,&amp;rdquo; &amp;ldquo;Spider Web Candy,&amp;rdquo; and &amp;ldquo;Ghost Marshmallows&amp;rdquo; come from?&lt;/p&gt;
&lt;p&gt;It turns out, these are &amp;ldquo;fallback products&amp;rdquo; Replit had made when it failed to connect to the End Point Ecommerce demo API. Not great for an ecommerce site! Your users would see bogus but real-looking products when they&amp;rsquo;re unable to see or buy real products (I didn&amp;rsquo;t test this far, but the site may have even accepted payment info for fake products).&lt;/p&gt;
&lt;p&gt;So I followed up:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please remove the fallback products&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;the-results-version-2&#34;&gt;The results (version 2)&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/10/adventures-in-vibe-coding-with-replit/site-v2.png&#34; alt=&#34;Spooky Store Halloween ecommerce website with jack-o&amp;rsquo;-lantern decorations. Features welcome banner with spider icons and orange border. Error message displayed stating &amp;lsquo;Something spooky happened! Could not load products&amp;rsquo; with skull icons, indicating products failed to load in the Haunting Collection section.&#34;&gt;&lt;/p&gt;
&lt;p&gt;This looks more promising! We get an error message written by the frontend. In production we might want to display more specific messages based on the response, but for this demo, I&amp;rsquo;m happy with this way of displaying errors.&lt;/p&gt;
&lt;p&gt;In true &amp;ldquo;vibe-coding&amp;rdquo; fashion, I asked the Replit agent to diagnose the network error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;There is a network error while loading products, what is the issue?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It responded with a couple options, the main one being assume it&amp;rsquo;s a CORS issue.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Possible Solutions
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Option 1: Server-Side Proxy (Recommended)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- Create a simple server endpoint that fetches the products
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- Your frontend calls your server, your server calls the external API
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- No CORS issues since server-to-server requests aren&amp;#39;t restricted&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I was suspicious of it being a CORS issue since everything was hosted in the Replit container, so after poking around myself, I wrote this prompt:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please use the web api to sign up for an account by POSTing to the /api/User
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;end point and getting an API key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, after reviewing the project for this post, I found that I misunderstood the issue in this prompt — no authentication is needed to GET &lt;code&gt;/api/products&lt;/code&gt; on the demo API of End Point Ecommerce, the site was just disallowing this cross-origin request. Replit&amp;rsquo;s agent went ahead and implemented the CORS change anyway, though it didn&amp;rsquo;t correct my prompt.&lt;/p&gt;
&lt;p&gt;After a few more followup prompts&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please use the &amp;#34;basePrice&amp;#34;, &amp;#34;discountAmount&amp;#34;, and &amp;#34;discountedPrice&amp;#34; keys that
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;are returned from the API to show the real prices of the items
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Now, please fetch the images from the API as well&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&amp;hellip;we have a decent working Hugo site connected with the End Point Ecommerce demo API!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/10/adventures-in-vibe-coding-with-replit/site-v3.png&#34; alt=&#34;Spooky Store Halloween ecommerce website displaying three product cards in a row: red apple for $1.99, banana bunch on yellow background for $0.25, and raw beef for $7.99. Shopping cart button visible in top right corner. Dark background with orange Halloween-themed header.&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;feature-addition-cart&#34;&gt;Feature addition: cart&lt;/h4&gt;
&lt;p&gt;I wrote this prompt next, without having done my research on the backend (which made for an interesting unintentional experiment):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please add a lightweight cart management system which uses localStorage, with
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;options to add to, remove from, or update quantity of items in the cart. Add a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;simple checkout form with contact information, shipping address, and billing
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;address. For now, instead of payment information, just have a button which adds
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a commonly used dummy credit card number.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There&amp;rsquo;s an issue here: I specified using &lt;code&gt;localStorage&lt;/code&gt;, which is a fine way to set up a frontend cart system, but it ignores the existing system End Point Ecommerce already supplies. I hadn&amp;rsquo;t checked the documentation before making this prompt, and neither did Replit; it happily obliged and created a cart management system on the frontend. It&amp;rsquo;s nice that the agent did what I asked, but if I were trying to work with the agent, without cross-referencing documentation myself, I would be disappointed.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the interesting part about using a non-deterministic system like AI agents: responding to one prompt, it might ignore my specific (and misguided) requests, but in another it might blindly execute the tasks it&amp;rsquo;s given. By the nature of LLMs, you can&amp;rsquo;t predict what you&amp;rsquo;ll get.&lt;/p&gt;
&lt;h4 id=&#34;bonus-a-few-extra-notes&#34;&gt;Bonus: a few extra notes&lt;/h4&gt;
&lt;p&gt;A couple final notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No .gitignore was generated, so the Hugo-generated &lt;code&gt;public/&lt;/code&gt; folder is tracked in Git&lt;/li&gt;
&lt;li&gt;The agent used plain CSS, without SCSS or a framework — I&amp;rsquo;d recommend specifying the stack further in the prompt if you want an app that&amp;rsquo;ll be nice to maintain. You don&amp;rsquo;t want to assume the agent will make smart coding decisions on its own.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;when-id-use-replit-and-similar-do-it-all-ai-agents&#34;&gt;When I&amp;rsquo;d use Replit (and similar do-it-all AI agents)&lt;/h4&gt;
&lt;p&gt;I would use Replit for very quick-and-dirty web development, when sites which shouldn&amp;rsquo;t last a long time — although I&amp;rsquo;ve seen too many projects originally intended to be short-lived become important for many years&amp;hellip;&lt;/p&gt;
&lt;p&gt;Replit handles its containerized deployments well; if I needed to share a working demo site within a few hours, Replit would be a good pick. Especially if you like React frontends, even when you might not have asked for one! But if I was dealing with customer data, orders, or my own data I cared about, I would want to be heavily involved throughout the development process.&lt;/p&gt;
&lt;p&gt;For projects where performance and accuracy matter (read: most projects), I will be taking a more directed approach. I&amp;rsquo;ve had some good results using Continue or GitHub CoPilot as a coding assistant for small tasks in existing repos, rather than letting AI taking the wheel completely.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Announcing End Point Ecommerce, Our New Open Source Project</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/08/announcing-end-point-ecommerce-our-new-open-source-project/"/>
      <id>https://www.endpointdev.com/blog/2025/08/announcing-end-point-ecommerce-our-new-open-source-project/</id>
      <published>2025-08-21T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/08/announcing-end-point-ecommerce-our-new-open-source-project/railing-blue-sky.webp&#34; alt=&#34;A white plastered railing from a low angle, behind which is a pure blue sky. The railing comes down and to the right from the top left corner of the image, then has a right angle in the center of the image, coming back up and to the right for another quarter of the image before stopping.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Seth Jensen, 2025. --&gt;
&lt;p&gt;Today we&amp;rsquo;re pleased to announce a new open source project: End Point Ecommerce.&lt;/p&gt;
&lt;p&gt;End Point Ecommerce is a minimalist ecommerce system that&amp;rsquo;s quick to set up and easy to understand. It is meant for developers to own, adapt, customize, and extend.&lt;/p&gt;
&lt;p&gt;You can learn more about it on &lt;a href=&#34;https://ecommerce.endpointdev.com/&#34;&gt;our landing page&lt;/a&gt;. You can also fork it today on &lt;a href=&#34;https://github.com/EndPointCorp/end-point-ecommerce&#34;&gt;GitHub&lt;/a&gt;. There are running demos here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://demo.ecommerce.endpointdev.com/swagger/index.html&#34;&gt;REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://admin.demo.ecommerce.endpointdev.com&#34;&gt;Admin Portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://demo.ecommerce.endpointdev.com&#34;&gt;Web Store&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Are you interested in using End Point Ecommerce for your ecommerce site? Feel free to &lt;a href=&#34;/contact/&#34;&gt;contact us&lt;/a&gt; and we can work together to help you deploy, customize, and maintain it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/08/announcing-end-point-ecommerce-our-new-open-source-project/epec_swagger.webp&#34; alt=&#34;REST API&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/08/announcing-end-point-ecommerce-our-new-open-source-project/epec_admin_portal.webp&#34; alt=&#34;Admin Portal&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/08/announcing-end-point-ecommerce-our-new-open-source-project/epec_web_store.webp&#34; alt=&#34;Web Store&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;key-features&#34;&gt;Key features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Built with &lt;a href=&#34;https://dotnet.microsoft.com/en-us/apps/aspnet&#34;&gt;ASP.NET&lt;/a&gt;, &lt;a href=&#34;https://www.postgresql.org/&#34;&gt;PostgreSQL&lt;/a&gt;, and &lt;a href=&#34;https://www.authorize.net/&#34;&gt;Authorize.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Multiple deployment strategies, including &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;Docker Compose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Code base designed to follow &lt;a href=&#34;https://learn.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures#clean-architecture&#34;&gt;Clean Architecture&lt;/a&gt; and &lt;a href=&#34;https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/february/best-practice-an-introduction-to-domain-driven-design&#34;&gt;Domain Driven Design&lt;/a&gt; concepts, without being dogmatic about it — simplicity trumps all&lt;/li&gt;
&lt;li&gt;Good test coverage with &lt;a href=&#34;https://xunit.net/&#34;&gt;xUnit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Includes a backend admin portal for managing the store, a REST API for building user-facing store frontends, and a simple web frontend&lt;/li&gt;
&lt;li&gt;Limited functionality — implements the bare minimum of ecommerce use cases&lt;/li&gt;
&lt;li&gt;Product catalog, shopping cart, order submission, email delivery&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;how-we-got-here&#34;&gt;How we got here&lt;/h3&gt;
&lt;p&gt;We recently had the opportunity to help one of our clients migrate their ecommerce site. They were using an established ecommerce engine and were having various problems with it. Their requirements were simple, and they didn&amp;rsquo;t need all the bells and whistles of big ecommerce solutions. They didn&amp;rsquo;t want to deal with the overhead, bloat, and difficulty of finding maintainers.&lt;/p&gt;
&lt;p&gt;During initial investigation and planning, we didn&amp;rsquo;t find any options that were small and easy to set up, understand, and customize, so we decided to build them a new site from scratch.&lt;/p&gt;
&lt;p&gt;We realize that there may be others out there in the same boat that we were. You want to set up a custom ecommerce site but you don&amp;rsquo;t want to deal with the complexity of bigger off-the-shelf solutions. You want something which you can thoroughly understand quickly, and modify to your heart&amp;rsquo;s content, as if it were your own code base that you developed from scratch.&lt;/p&gt;
&lt;h3 id=&#34;who-is-this-for&#34;&gt;Who is this for?&lt;/h3&gt;
&lt;p&gt;This project is meant for .NET developers who want a solid and simple foundation to build and customize ecommerce sites. To that end, the feature set is very basic, the code base is very straightforward, and the documentation is very developer-oriented. The idea being that developers can quickly get up to speed with how everything works, develop ownership over the code base, and implement their bespoke use cases.&lt;/p&gt;
&lt;p&gt;This project is not meant for non-technical folks. There is no installation wizard, no templating engine, no plugin framework, no no-code customization capabilities.&lt;/p&gt;
&lt;p&gt;If you want to set up a simple online store, or develop a highly customized one from scratch, think of this as a way of skipping the first few steps of building the foundational architecture and basic functionality.&lt;/p&gt;
&lt;p&gt;Please check it out! And do whatever you want with it!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>When to DIY vs Hire for Ecommerce Development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/07/diy-or-call-a-pro/"/>
      <id>https://www.endpointdev.com/blog/2025/07/diy-or-call-a-pro/</id>
      <published>2025-07-01T00:00:00+00:00</published>
      <author>
        <name>Greg Hanson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/07/diy-or-call-a-pro/two-buildings.webp&#34; alt=&#34;On the left, a 70s-era yellow brick building with a satellite dish on top is seen from below, rising above the tops of two green trees. On the right, split by a thin black line, are three houses with distinct bay window facade toppers on their second stories.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Seth Jensen, 2025. --&gt;
&lt;h3 id=&#34;a-practical-guide-for-business-owners&#34;&gt;A Practical Guide for Business Owners&lt;/h3&gt;
&lt;p&gt;Many ecommerce businesses start with a scrappy DIY mindset. And that works — until it doesn’t. Knowing when to build it yourself and when to bring in a professional can mean the difference between momentum and mess.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve worked with business owners who built their first site in a weekend — and others who sunk $50,000 into a platform they barely used. Here’s how to figure out which path fits your goals, skills, and budget.&lt;/p&gt;
&lt;h3 id=&#34;when-to-diy-with-confidence&#34;&gt;When to DIY (With Confidence)&lt;/h3&gt;
&lt;p&gt;There are plenty of situations where handling development yourself makes good business sense:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You&amp;rsquo;re on a tight budget&lt;/strong&gt; and need proof-of-concept&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You&amp;rsquo;re using simple, user-friendly tools&lt;/strong&gt; like Shopify, Wix, or Squarespace&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You just need a basic store or landing page&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You’re comfortable with tech&lt;/strong&gt; and enjoy learning new tools&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;helpful-diy-tools&#34;&gt;Helpful DIY Tools&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Shopify&lt;/li&gt;
&lt;li&gt;Wordpress / WooCommerce (basic setup)&lt;/li&gt;
&lt;li&gt;Webflow&lt;/li&gt;
&lt;li&gt;Canva (for design)&lt;/li&gt;
&lt;li&gt;Zapier (for automation)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;watch-out-for&#34;&gt;Watch Out For&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Mobile layout issues that won’t go away&lt;/li&gt;
&lt;li&gt;Slow site speeds or broken checkout processes&lt;/li&gt;
&lt;li&gt;Inventory or shipping errors you can’t fix&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the fixes start taking more time than they’re worth — or cost you sales — it’s time to reconsider.&lt;/p&gt;
&lt;h3 id=&#34;when-to-hire-a-developer-or-consultancy&#34;&gt;When to Hire a Developer or Consultancy&lt;/h3&gt;
&lt;p&gt;DIY stops making sense when your business starts scaling or when needs get technical. Here are common triggers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You need custom integrations&lt;/strong&gt; — like with an inventory system (e.g., Art of Books), ERP, or POS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Site speed or UX are hurting conversions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You’ve outgrown platform support&lt;/strong&gt; and need human help, fast&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your time is more valuable elsewhere&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;project-types-worth-hiring-for&#34;&gt;Project Types Worth Hiring For&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Speed optimization&lt;/li&gt;
&lt;li&gt;Custom checkout flows&lt;/li&gt;
&lt;li&gt;APconnections (shipping, inventory, accounting)&lt;/li&gt;
&lt;li&gt;Multi-language or multi-country setups&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;keep-in-mind-there-are-additional-facets-to-diy&#34;&gt;Keep in mind there are additional facets to DIY&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Hosting — AWS, Azure, Linode&lt;/li&gt;
&lt;li&gt;DNS — Cloudflare&lt;/li&gt;
&lt;li&gt;Domains and SSL certificates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If these items are not known to you, consider talking to a consultant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; You don’t need to hire full-time. Start with project-based freelancers or small consultancies.
Good consultancies will bill you on an hourly &amp;ldquo;as needed&amp;rdquo; basis. They should also provide estimates for projects that may exceed 5–10 hours.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;the-hybrid-approach-best-of-both-worlds&#34;&gt;The Hybrid Approach: Best of Both Worlds&lt;/h3&gt;
&lt;p&gt;For many business owners, a blended model is ideal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Have a pro build the foundation&lt;/strong&gt; — you manage content, products, and marketing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use no-code platforms&lt;/strong&gt; for updates, and freelancers for complex changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set up clear roles&lt;/strong&gt; — who maintains what, and when to escalate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-it-all-costs&#34;&gt;What It All Costs&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Type&lt;/th&gt;
          &lt;th&gt;Typical Cost&lt;/th&gt;
          &lt;th&gt;Best For&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;DIY&lt;/td&gt;
          &lt;td&gt;$30–$300/month&lt;/td&gt;
          &lt;td&gt;Simple stores, startups, testing ideas&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Freelancer&lt;/td&gt;
          &lt;td&gt;$500–$5,000/project&lt;/td&gt;
          &lt;td&gt;Specific features or upgrades&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Consultancy&lt;/td&gt;
          &lt;td&gt;$3,000–$50,000+&lt;/td&gt;
          &lt;td&gt;Full builds, scaling projects, business plan integration, ongoing support&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Always make sure you have &lt;em&gt;admin access&lt;/em&gt;, version control (e.g., GitHub), ownership of code, and documentation if you hire.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;conclusion-know-your-roi&#34;&gt;Conclusion: Know Your ROI&lt;/h3&gt;
&lt;p&gt;The right path depends on what gives you &lt;em&gt;momentum&lt;/em&gt; without draining your &lt;em&gt;time&lt;/em&gt; or &lt;em&gt;wallet&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t hire someone just because you’re tired.&lt;/li&gt;
&lt;li&gt;Don’t DIY just because it’s cheap.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The smartest businesses find a rhythm — build what they can, hire when it matters, and stay focused on growth.&lt;/p&gt;
&lt;h3 id=&#34;bonus-quick-self-assessment&#34;&gt;Bonus: Quick Self-Assessment&lt;/h3&gt;
&lt;p&gt;Should you DIY or Hire?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you have more time than budget right now?&lt;/li&gt;
&lt;li&gt;Are your tech skills good enough for the tools you’re using?&lt;/li&gt;
&lt;li&gt;Are bugs and delays costing you money or customers?&lt;/li&gt;
&lt;li&gt;Are you trying to integrate systems beyond your experience?&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;If you answered &lt;em&gt;yes&lt;/em&gt; to the first two → DIY may work.
If you said &lt;em&gt;yes&lt;/em&gt; to the last two → it’s time to bring in help.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;need-help-evaluating-your-ecommerce-development-path&#34;&gt;Need help evaluating your ecommerce development path?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;/&#34;&gt;End Point&lt;/a&gt; helps business owners like you balance time, cost, and complexity in ecommerce builds.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/contact/&#34;&gt;Contact us for a quick consultation or send your current project for review.&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interchange Compression for SessionDB</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/04/interchange-compress-sessiondb/"/>
      <id>https://www.endpointdev.com/blog/2025/04/interchange-compress-sessiondb/</id>
      <published>2025-04-30T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/04/interchange-compress-sessiondb/misty-lake.webp&#34; alt=&#34;A misty lake with mountains hidden by fog and clouds is viewed through a rectangular train window, straight on.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Seth Jensen, 2025. --&gt;
&lt;p&gt;New support for compression of sessions and more lists stored in a RDBMS has been added to core Interchange.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;,&lt;/sup&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;A new module, &lt;code&gt;Vend::Util::Compress&lt;/code&gt;, operates as a general interface for compressing and uncompressing scalar data in Interchange. The module currently offers hooks for the following compression algorithms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zstd (preferred)&lt;/li&gt;
&lt;li&gt;Gzip&lt;/li&gt;
&lt;li&gt;Brotli&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional algorithms can be easily added as needed.&lt;/p&gt;
&lt;p&gt;Each algorithm depends on the developer having installed the necessary CPAN modules for the given compression type:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IO::Compress::Zstd&lt;/code&gt;/&lt;code&gt;IO::Uncompress::UnZstd&lt;/code&gt;&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IO::Compress::Gzip&lt;/code&gt;/&lt;code&gt;IO::Uncompress::Gunzip&lt;/code&gt;&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IO::Compress::Brotli&lt;/code&gt;/&lt;code&gt;IO::Uncompress::Brotli&lt;/code&gt;&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Vend::Util::Compress&lt;/code&gt; exports &lt;code&gt;compress()&lt;/code&gt; and &lt;code&gt;uncompress()&lt;/code&gt; on demand. In scalar context, they return the reference to the scalar holding the transformed data. In list context, they return additional measurements from the process.&lt;/p&gt;
&lt;p&gt;List &lt;code&gt;compress()&lt;/code&gt; returns an array of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$ref&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$before_size&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$after_size&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$elapsed_time&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$alert&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;List &lt;code&gt;uncompress()&lt;/code&gt; returns an array of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$ref&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$elapsed_time&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$alert&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any errors encountered when called in scalar context are written to the catalog error log. Errors encountered when called in list context are returned in &lt;code&gt;$alert&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;compression-performance-comparison&#34;&gt;Compression Performance Comparison&lt;/h3&gt;
&lt;p&gt;The following chart summarizes the results of running the same Interchange instance through several steps of session growth to measure performance and impact of the three supported compression algorithms. Tests ranged from a minimum session size of 68 kilobytes up to a maximum of 110 megabytes. Fields marked in green indicate the best performance of the particular parameter, across all algorithms, for each given session size. Any fields marked in yellow indicate performance measurements reaching a level of concern. Any fields marked in red indicate measurements reaching a level of unacceptable performance in any typical situation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/04/interchange-compress-sessiondb/compression_reduction_chart.png&#34; alt=&#34;Results chart for tests of compression algorithms. For each algorithm, there are 4 rows: &amp;ldquo;after&amp;rdquo;, &amp;ldquo;reduction&amp;rdquo;, &amp;ldquo;cmp time&amp;rdquo;, and &amp;ldquo;unc time&amp;rdquo;. There are 9 columns, 6.8k to 110M. Zstd shows white results for everything except compression time, where every data point is green. Its compressed size at 110M is 7.7M. Gzip shows white results for everything except compression time at 110M, where it is yellow (1.15s), with an after-compression size of 3.9M. Brotli shows all green, with a 110M after-compression size of 2.3M, but the compression time is yellow at 1.7M before, and red for all larger sizes, maxing out at 160s for 110M.&#34;&gt;&lt;/p&gt;
&lt;p&gt;For size reduction, all 3 algorithms produced a substantial decrease across the board, generally exceeding 90% and increasing the reduction factor as the session grew. All 3 algorithms also uncompressed the session on retrieval at comparable rates, well within tolerance. Where we see significant separation is in the time to compress. Zstd is the fastest across the board, but really stands out when working with larger sessions, when the impact of compression is most needed for reducing transfer latency between the Interchange and database servers.&lt;/p&gt;
&lt;h3 id=&#34;configuring-your-catalog-to-use-vendutilcompress&#34;&gt;Configuring Your Catalog to Use Vend::Util::Compress&lt;/h3&gt;
&lt;p&gt;To begin using session compression, add the new catalog configuration parameter, &lt;code&gt;SessionDBCompression&lt;/code&gt;, set to one of the aforementioned compression algorithms. Note the values for specifying compression type to use are case-sensitive. For example, to enable Zstd:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SessionDBCompression Zstd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;An empty value for the parameter bypasses compression. An invalid value logs an error and passes through the data unmodified. If the catalog is configured for file-based sessions, the setting has no impact.&lt;/p&gt;
&lt;p&gt;The same compression applies to both sessions and more lists when MoreDB is also set.&lt;/p&gt;
&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://www.interchangecommerce.org/&#34;&gt;https://www.interchangecommerce.org/&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/interchange/interchange/commit/35c3452bdfa1a192238196c98d12519cf153af1b&#34;&gt;https://github.com/interchange/interchange/commit/35c3452bdfa1a192238196c98d12519cf153af1b&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://metacpan.org/pod/IO::Compress::Zstd&#34;&gt;https://metacpan.org/pod/IO::Compress::Zstd&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/pod/IO::Uncompress::UnZstd&#34;&gt;https://metacpan.org/pod/IO::Uncompress::UnZstd&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://metacpan.org/pod/IO::Compress::Gzip&#34;&gt;https://metacpan.org/pod/IO::Compress::Gzip&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/pod/IO::Uncompress::Gunzip&#34;&gt;https://metacpan.org/pod/IO::Uncompress::Gunzip&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://metacpan.org/pod/IO::Compress::Brotli&#34;&gt;https://metacpan.org/pod/IO::Compress::Brotli&lt;/a&gt;, &lt;a href=&#34;https://metacpan.org/pod/IO::Uncompress::Brotli&#34;&gt;https://metacpan.org/pod/IO::Uncompress::Brotli&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

      </content>
    </entry>
  
    <entry>
      <title>Interchange 3rd Party Tax Support</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2023/03/interchange-vend-tax/"/>
      <id>https://www.endpointdev.com/blog/2023/03/interchange-vend-tax/</id>
      <published>2023-03-21T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2023/03/interchange-vend-tax/cloudy-lake.webp&#34; alt=&#34;Looking across a lake, a mountain rises to the left, splitting the dark storm clouds from their reflection with a long, thin triangle of the mountain and its reflection. On the lower side of the hill is a small town, with its light green fields standing out against the otherwise unbroken dark green pine trees. The lake&amp;rsquo;s wind-blown ripply texture is broken up on the far right by a rock and grass outcropping, along with a railing on the right side.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Juan Pablo Ventoso, 2022 --&gt;
&lt;p&gt;New 3rd-party tax API support has been added to core Interchange&lt;sup&gt;1&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In the wake of the Wayfair court decision&lt;sup&gt;2&lt;/sup&gt;, many businesses running Interchange catalogs lack the necessary tools for full compliance. A new translation layer has been created in &lt;code&gt;Vend::Tax&lt;/code&gt; to connect the standard sales tax structures and routines that operate within Interchange, and the development of vendor-specific 3rd-party tax providers. The goal of the &lt;code&gt;Vend::Tax&lt;/code&gt; framework is to create a space to allow for development of any number of vendor-specific tax services to support tax calculation in Interchange.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Vend::Tax&lt;/code&gt; defines 3 new tags to support its function:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[tax-lookup]&lt;/code&gt;: Returns calculated tax amount determined by specific 3rd-party provider. Tax may be estimated or live lookup, depending on settings. Data required to calculate tax will be provider dependent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[load-tax-averages]&lt;/code&gt;: Requests and stores tax averages for running in estimate mode, for providers that support it. Stores estimates by default in the &lt;code&gt;tax_averages&lt;/code&gt; table. Further, allows for local tracking of jurisdictions with nexus, which can be used by live lookups to determine if a particular lookup can be skipped entirely. See &lt;code&gt;load_tax_averages&lt;/code&gt; Job and &lt;code&gt;tax_averages&lt;/code&gt; table definition in the &lt;a href=&#34;https://github.com/interchange/interchange/tree/master/dist/strap&#34;&gt;Strap demo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[send-tax-transaction]&lt;/code&gt;: Reports to provider the resulting tax transaction for a given order, for providers that support it. By default, operates based on &lt;code&gt;transactions.tax_sent&lt;/code&gt; field. &lt;code&gt;0&lt;/code&gt; or empty indicates transaction not reported. &lt;code&gt;1&lt;/code&gt; indicates transaction successfully reported. &lt;code&gt;-1&lt;/code&gt; indicates an error attempting to report transaction, requiring manual intervention. See &lt;code&gt;send_tax_transactions&lt;/code&gt; Job in Strap demo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conceptually, &lt;code&gt;Vend::Tax&lt;/code&gt; is patterned off of &lt;code&gt;Vend::Payment&lt;/code&gt; for payment transactions. It provides the defined interface that Interchange will use, via the 3 tags shown above, but must delegate to vendor-specific implementations through tax gateway modules.&lt;/p&gt;
&lt;p&gt;Additionally, connectors have already been constructed for TaxJar&lt;sup&gt;3&lt;/sup&gt; and Avalara&lt;sup&gt;4&lt;/sup&gt; in core Interchange, so that catalogs can convert to use either of these popular services for calculating their sales tax with an upgrade and a few adjustments to their catalog configuration. Both TaxJar and Avalara provide sales tax calculation, product categorization, and reporting tools that allow merchants to comply with Wayfair and keep their focus on their business.&lt;/p&gt;
&lt;p&gt;While currently only TaxJar and Avalara are supported — see &lt;code&gt;Vend::Tax::TaxJar&lt;/code&gt; and &lt;code&gt;Vend::Tax::Avalara&lt;/code&gt; — &lt;code&gt;Vend::Tax&lt;/code&gt; was designed to facilitate and encourage the development of any 3rd-party providers through the creation of new &lt;code&gt;Vend::Tax::&amp;lt;ServiceName&amp;gt;&lt;/code&gt; modules. A review of either of the TaxJar or Avalara tax gateway modules should be instructive for the expected interface with the 3 usertags.&lt;/p&gt;
&lt;p&gt;For merchants who have, or wish to establish, relationships with other tax providers, they are free to build, or contract with consultants to build, their own provider&amp;rsquo;s vendor-specific module and use that to back the work performed within &lt;code&gt;Vend::Tax&lt;/code&gt;. Any such development projects are encouraged to be submitted for inclusion in Interchange core, giving the merchant and developer the benefit of community improvements and core inclusion on future upgrades.&lt;/p&gt;
&lt;h3 id=&#34;configuring-your-catalog-to-use-vendtax&#34;&gt;Configuring Your Catalog to Use Vend::Tax&lt;/h3&gt;
&lt;p&gt;In order to leverage &lt;code&gt;[tax-lookup]&lt;/code&gt; to back the &lt;code&gt;[salestax]&lt;/code&gt; tag, use it with the &lt;code&gt;SalesTax&lt;/code&gt; directive. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SalesTax  [tax-lookup service=TaxJar]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;service&lt;/code&gt; parameter should correspond with the segment of the namespace of the tax gateway module after &lt;code&gt;Vend::Tax&lt;/code&gt;. Here, the example enables TaxJar in module &lt;code&gt;Vend::Tax::TaxJar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If your business requires sufficient customization, the existing tax gateway modules can be subclassed, keeping all your customizations separate from core code and safe from collisions during upgrades. An example of such an expansion to Avalara would be to create module &lt;code&gt;Vend::Tax::Avalara::MyBiz&lt;/code&gt; to subclass &lt;code&gt;Vend::Tax::Avalara&lt;/code&gt; and change the config setup to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SalesTax  [tax-lookup service=Avalara::MyBiz]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additionally, using the Strap setup, you can easily enable TaxJar specifically by setting the following variables in variable.txt:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TAXSERVICE TaxJar
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TAXTOKEN [your TaxJar token]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_ADDRESS [business address]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_CITY [business city]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_STATE [business state postal code]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_ZIP [business zip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_COUNTRY [business ISO two-letter country code]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For Avalara in Strap, you also would set the &lt;code&gt;TAXSERVICE&lt;/code&gt; and &lt;code&gt;NEXUS_&lt;/code&gt; variables, but then set your account API user and password:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TAXSERVICE Avalara
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AVALARA_USER [your API user]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AVALARA_PASSWORD [your API password]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_ADDRESS [business address]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_CITY [business city]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_STATE [business state postal code]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_ZIP [business zip]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NEXUS_COUNTRY [business iso two-letter country code]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, like the payment modules, in order to make a particular tax service available, it must be required from &lt;code&gt;interchange.cfg&lt;/code&gt;. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Require module Vend::Tax::TaxJar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;references&#34;&gt;References&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://www.interchangecommerce.org/&#34;&gt;https://www.interchangecommerce.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.lbmc.com/blog/wayfair-case-sales-tax/&#34;&gt;https://www.lbmc.com/blog/wayfair-case-sales-tax/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.taxjar.com/&#34;&gt;https://www.taxjar.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.avalara.com/&#34;&gt;https://www.avalara.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce customer names with interesting Unicode characters</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/12/ecommerce-customer-names-interesting-unicode/"/>
      <id>https://www.endpointdev.com/blog/2021/12/ecommerce-customer-names-interesting-unicode/</id>
      <published>2021-12-29T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/12/ecommerce-customer-names-interesting-unicode/20210710_085128-sm.jpg&#34; alt=&#34;Photo of a small garden in front of a house basement window with two cats looking out&#34;&gt;&lt;/p&gt;
&lt;!-- photo by Jon Jensen --&gt;
&lt;p&gt;One of our clients with a busy ecommerce site sees a lot of orders, and among those, sometimes there are unusual customer name and address submissions.&lt;/p&gt;
&lt;p&gt;We first noticed in 2015 that they had a customer order come in with an emoji in the name field of the order. The emoji was 😏 and we half-jokingly wondered if that was a new sign of fraud.&lt;/p&gt;
&lt;p&gt;Over the following 3 years only a few more orders came in with various emoji in customer&amp;rsquo;s names, but in mid-2018 emoji started to appear increasingly frequently until now one now appears on average every day or two.&lt;/p&gt;
&lt;p&gt;Why the sudden appearance of emoji in 2015? It correlates with the rapid shift to browsing the web and shopping on mobile devices. Mobile visits now represent more than half of this client&amp;rsquo;s ecommerce traffic.&lt;/p&gt;
&lt;p&gt;Most people in 2015 didn&amp;rsquo;t even know how to type emoji on a desktop or laptop computer, but mobile touchscreen keyboards began showing emoji choices around that time, so the mobile explanation makes sense. And mobile keyboard autocorrect also sometimes offers emoji in addition to words, making them even more common in the past few years.&lt;/p&gt;
&lt;p&gt;Just for fun I wanted to automatically find all such &amp;ldquo;interesting&amp;rdquo; names, so I wrote a simple report that uses SQL to query their PostgreSQL ecommerce database.&lt;/p&gt;
&lt;p&gt;To preserve customer privacy, names shown here have been changed and limited to a few names that are common in the United States.&lt;/p&gt;
&lt;h3 id=&#34;real-names-with-bonuses&#34;&gt;Real names with bonuses&lt;/h3&gt;
&lt;p&gt;First let&amp;rsquo;s look at the apparently real names with emoji and other non-alphabetical Unicode characters mixed in:&lt;/p&gt;
&lt;p&gt;Amy 🩺&lt;br&gt;
Amy💕&lt;br&gt;
Amy👑&lt;br&gt;
👸Amy&lt;br&gt;
Anna 💫&lt;br&gt;
Anna 😎&lt;br&gt;
Anna ❤️&lt;br&gt;
Anna💊💉&lt;br&gt;
Bob☯️&lt;br&gt;
Bob😍😘😜👑💞&lt;br&gt;
Bob🐞&lt;br&gt;
Brenda 🌙&lt;br&gt;
Brenda 🥳🤪&lt;br&gt;
Brenda☠️&lt;br&gt;
B R E N D A 💕💅🏾💃🏾👜&lt;br&gt;
Cameron 🌻&lt;br&gt;
Cameron 😎&lt;br&gt;
Cameron 💁🏻‍♀️&lt;br&gt;
Doug 💕&lt;br&gt;
Doug 🅱️&lt;br&gt;
Doug 🌸🤩&lt;br&gt;
Doug 👦🏻❤️&lt;br&gt;
Emily 🌷&lt;br&gt;
Emily 😊🇨🇺&lt;br&gt;
Emily😚&lt;br&gt;
Emily 🎰👑❤️&lt;br&gt;
Frank 🥰&lt;br&gt;
Frank ⚗️&lt;br&gt;
Frank🔆&lt;br&gt;
Frank💞💰&lt;br&gt;
Jane 💕&lt;br&gt;
Jane⁸&lt;br&gt;
Jane ❤️❤️&lt;br&gt;
Jane🔆&lt;br&gt;
Jill 👑🎀&lt;br&gt;
Jill 👼🏽✨🌫&lt;br&gt;
Jill🍓🍒🍒&lt;br&gt;
Jill👸🏽💖&lt;br&gt;
Jim 🐰&lt;br&gt;
Jim, 💕&lt;br&gt;
Jim’s iPhone✨&lt;br&gt;
Joe 🐯&lt;br&gt;
Joe 🇦🇺&lt;br&gt;
Joe😏&lt;br&gt;
Joe🏀🎸‼️&lt;br&gt;
John 👪&lt;br&gt;
John⁵&lt;br&gt;
John🎭&lt;br&gt;
Karen 🌻🌹&lt;br&gt;
Karen 👑✨&lt;br&gt;
Karen 🔒❤️&lt;br&gt;
Karen🎀👑&lt;br&gt;
Kate💙💚&lt;br&gt;
Kate🤪🤞🏽💙&lt;br&gt;
Kate.💘&lt;br&gt;
K$ate💉&lt;br&gt;
Liz🌺&lt;br&gt;
Liz❣️&lt;br&gt;
Liz❤️🙃&lt;br&gt;
Liz Mama💙💙&lt;br&gt;
Mary 🎀&lt;br&gt;
Mary💘&lt;br&gt;
Mary👼🏼💓&lt;br&gt;
Mary⁹&lt;br&gt;
Mike 👑&lt;br&gt;
Mike 👑🌸&lt;br&gt;
Mike 👩🏻‍🌾&lt;br&gt;
Mike⁶&lt;br&gt;
Sarah 👑&lt;br&gt;
Sarah👑A.&lt;br&gt;
✨Sarah✨&lt;br&gt;
💛🌻 Sarah&lt;br&gt;
Steve🐑&lt;br&gt;
Steve 🦁&lt;br&gt;
Steve♓️💓&lt;br&gt;
Victoria 🥴&lt;br&gt;
Victoria🌻&lt;br&gt;
V𝚒𝚌𝚝𝚘𝚛𝚒𝚊&lt;br&gt;
V I C T O R I A 🤍&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Would you have expected all that in ecommerce orders? I didn&amp;rsquo;t!&lt;/p&gt;
&lt;h3 id=&#34;fake-names&#34;&gt;Fake names&lt;/h3&gt;
&lt;p&gt;Next let&amp;rsquo;s look at placeholder names with people&amp;rsquo;s role or self-description or similar:&lt;/p&gt;
&lt;p&gt;Amor ⚽️&lt;br&gt;
Babe ❤️&lt;br&gt;
C𝚒𝚝𝚒𝚣𝚎𝚗&lt;br&gt;
Daddy🥴😏&lt;br&gt;
Daddy😘👴👨🙏👨👩👧🙇🏾&lt;br&gt;
Fly High 🕊&lt;br&gt;
Forever 💍💜&lt;br&gt;
Granny 👵🏽&lt;br&gt;
HOME ❤️&lt;br&gt;
Home🏠&lt;br&gt;
Home🏠💜&lt;br&gt;
Hubby🥰&lt;br&gt;
💦Juicy🍑&lt;br&gt;
me!! 💛&lt;br&gt;
mi amor ❤️&lt;br&gt;
Mom💗&lt;br&gt;
Mom♥️&lt;br&gt;
Mom 🐥💛&lt;br&gt;
MOMMY 💗&lt;br&gt;
Myself 😘&lt;br&gt;
Princess👑&lt;br&gt;
princess❤️&lt;br&gt;
Queen 😍💖🔓&lt;br&gt;
Queen💘&lt;br&gt;
The Husband💍❤️&lt;br&gt;
Wifey 😈✌🏽👅&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Perhaps the occurrence of &amp;ldquo;home&amp;rdquo; several times reflects a mobile address book auto-fill function for billing or shipping address fields?&lt;/p&gt;
&lt;h3 id=&#34;not-names-at-all&#34;&gt;Not names at all!&lt;/h3&gt;
&lt;p&gt;Then there are those customers who didn&amp;rsquo;t provide any kind of name at all, just emoji and other special characters:&lt;/p&gt;
&lt;p&gt;🦋&lt;br&gt;
💙&lt;br&gt;
🤡🎪&lt;br&gt;
💗☁️&lt;br&gt;
🍌&lt;br&gt;
♥️&lt;br&gt;
🌙&lt;br&gt;
⁵&lt;br&gt;
∅&lt;br&gt;&lt;/p&gt;
&lt;p&gt;I guess only one or two of those per year doesn&amp;rsquo;t amount to much, but they&amp;rsquo;re interesting to see.&lt;/p&gt;
&lt;h3 id=&#34;strange-addresses&#34;&gt;Strange addresses&lt;/h3&gt;
&lt;p&gt;In addition to the name fields we also checked the address fields for unusual characters and found (again, details changed to preserve privacy):&lt;/p&gt;
&lt;p&gt;125 E 27😎&lt;br&gt;
227 W 24 Circle ⭕️&lt;br&gt;
3 Blvd. George Washington™&lt;br&gt;&lt;/p&gt;
&lt;h3 id=&#34;simply-odd&#34;&gt;Simply odd&lt;/h3&gt;
&lt;p&gt;The prizewinner for oddity, which seems like some kind of copy-and-paste mistake, is this in the city field of an address:&lt;/p&gt;
&lt;p&gt;Indian® Roadmaster™ Classic&lt;/p&gt;
&lt;p&gt;Maybe at least one motorcycle has achieved sentience and needed to do some online shopping!&lt;/p&gt;
&lt;h3 id=&#34;interesting-unicode-ranges&#34;&gt;&amp;ldquo;Interesting&amp;rdquo; Unicode ranges&lt;/h3&gt;
&lt;p&gt;When searching for interesting Unicode ranges, we could just look for characters in the &lt;a href=&#34;https://unicode.org/emoji/charts/full-emoji-list.html&#34;&gt;Unicode emoji ranges&lt;/a&gt;. That would be fairly straightforward since there are just a few ranges to match.&lt;/p&gt;
&lt;p&gt;But we were curious what other unusual characters were getting used aside from emoji, so we wanted to include other classes of characters. So perhaps we should include everything to start and then exclude the entire class of Unicode &amp;ldquo;word characters&amp;rdquo;? That covers the world&amp;rsquo;s standard characters used for names and addresses, including not just Roman/Latin with optional diacritics, but also other character sets such as Cyrillic, Arabic, Hebrew, Korean, Chinese, Japanese, Devanagari, Thai, and many others.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.postgresql.org/docs/current/functions-matching.html#POSIX-CLASS-SHORTHAND-ESCAPES-TABLE&#34;&gt;PostgreSQL POSIX regular expressions&lt;/a&gt; include the class &amp;ldquo;word character&amp;rdquo; represented by either &lt;code&gt;[[:word:]]&lt;/code&gt; or the Perl shorthand &lt;code&gt;\w&lt;/code&gt;. I started with that, but found it covered too many things I did want to see, such as the visually double-width Latin characters that are part of the Chinese word character range, and some special numbers.&lt;/p&gt;
&lt;p&gt;So I switched back to matching what I want, rather than excluding what I don&amp;rsquo;t want, and I manually went through the &lt;a href=&#34;https://www.unicode.org/charts/&#34;&gt;Unicode code charts&lt;/a&gt; and noted the ranges to include.&lt;/p&gt;
&lt;p&gt;The list of Unicode code ranges I came up with, in hexadecimal, is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;250-2ba
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2bc-2c5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2cc-2dc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2de-2ff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;58d-58e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fd5-fd8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1d00-1dbf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2070-2079
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;207b-209f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;20d0-2104
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2106-2115
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2117-215f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2163-218b
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2190-2211
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2213-266e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2670-2bff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2e00-2e7f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2ff0-2fff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3004
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3012-3013
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3020
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3200-33ff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4dc0-4dff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;a000-abf9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fff0-fffc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fffe-1d35f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1d360-1d37f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1d400-1d7ff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1ec70-1ecbf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1ed00-1ed4f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1ee00-1eeff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1f000-10ffff&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Those ranges exclude several fairly common characters that people (or their software&amp;rsquo;s autocorrect) used in their address fields, which we wanted to ignore, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Music sharp sign: ♯ (instead of # before a number)&lt;/li&gt;
&lt;li&gt;numero: №&lt;/li&gt;
&lt;li&gt;care of: ℅&lt;/li&gt;
&lt;li&gt;replacement character: � (though this could be interesting if it reveals unknown encoding errors)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;the-sql-query&#34;&gt;The SQL query&lt;/h3&gt;
&lt;p&gt;PostgreSQL allows us to represent Unicode characters in hexadecimal numbers as either &lt;code&gt;\u&lt;/code&gt; plus 4 digits or &lt;code&gt;\U&lt;/code&gt; plus 8 digits. So the character 2ba is written in a Postgres string as &lt;code&gt;\u02ba&lt;/code&gt; and the range fffe-1d35f is written in a Postgres regex range as &lt;code&gt;[\ufffe-\U0001d35f]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With a little scripting to put it all together, I came up with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;order_number,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;order_timestamp::&lt;span style=&#34;color:#038&#34;&gt;date&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;order_date,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;fname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;lname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;company,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;address1,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;address2,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;city,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;state&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_fname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_lname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_company,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_address1,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_address2,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_city,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_state,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;phone&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;FROM&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;orders&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;WHERE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;concat(fname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;lname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;company,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;address1,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;address2,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;city,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;state&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_fname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_lname,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_company,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_address1,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_address2,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_city,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;b_state,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;phone)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;~&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;[\u0250-\u02ba\u02bc-\u02c5\u02cc-\u02dc\u02de-\u02ff\u058d-\u058e\u0fd5-\u0fd8\u1d00-\u1dbf\u2070-\u2079\u207b-\u209f\u20d0-\u2104\u2106-\u2115\u2117-\u215f\u2163-\u218b\u2190-\u2211\u2213-\u266e\u2670-\u2bff\u2e00-\u2e7f\u2ff0-\u2fff\u3004\u3012-\u3013\u3020\u3200-\u33ff\u4dc0-\u4dff\ua000-\uabf9\ufff0-\ufffc\ufffe-\U0001d35f\U0001d360-\U0001d37f\U0001d400-\U0001d7ff\U0001ec70-\U0001ecbf\U0001ed00-\U0001ed4f\U0001ee00-\U0001eeff\U0001f000-\U0010ffff]&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;-- limit how many years back to go
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AND&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;order_timestamp&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;SELECT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;-&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#038&#34;&gt;interval&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;3 years&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;-- exclude any order that had PII expunged for GDPR
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;AND&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;expunged_at&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;IS&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;NULL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ORDER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;order_timestamp&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DESC&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Try a similar query on databases you have access to, and see what interesting user submissions you discover. I always find surprises and in addition to being fun, sometimes we find things that help us improve our input validation and user guidance so that more mistakes are caught when they&amp;rsquo;re easy for the customer to correct.&lt;/p&gt;
&lt;p&gt;Happy holidays!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Our Vue Storefront “Proof of Concept” Experience</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/08/vue-storefront-magento-integration/"/>
      <id>https://www.endpointdev.com/blog/2020/08/vue-storefront-magento-integration/</id>
      <published>2020-08-10T00:00:00+00:00</published>
      <author>
        <name>Kürşat Kutlu Aydemir</name>
      </author>
      <content type="html">
        &lt;p&gt;Recently we experimented internally with integrating &lt;a href=&#34;https://www.vuestorefront.io/&#34;&gt;Vue Storefront&lt;/a&gt; and &lt;a href=&#34;https://business.adobe.com/products/magento/open-source.html&#34;&gt;Magento&lt;/a&gt; 2.3. Vue Storefront is an open source Progressive Web App (PWA) that aims to work with many ecommerce platforms.&lt;/p&gt;
&lt;p&gt;What initially piqued our interest was the possibility of integrating Vue Storefront with the venerable ecommerce back-end platform &lt;a href=&#34;https://www.interchangecommerce.org/i/dev&#34;&gt;Interchange&lt;/a&gt;, which many of our clients use. Vue Storefront’s promise of ease of integration with any ecommerce backend made us curious to see whether it would make a good modern front-end for Interchange.&lt;/p&gt;
&lt;p&gt;Since Vue Storefront seems to be most commonly used with Magento, we decided to start our experiment with a standard Vue Storefront/​Magento 2.3 proof-of-concept integration.&lt;/p&gt;
&lt;h3 id=&#34;poc-of-vue-storefrontmagento-23&#34;&gt;PoC of Vue Storefront/​Magento 2.3&lt;/h3&gt;
&lt;p&gt;OK, to be honest, at the beginning we blindly expected that Vue Storefront would be a copy/​paste front-end template solution that would fairly easily be made to work with its standard integration to a Magento backend. Sadly, this was not the case for us.&lt;/p&gt;
&lt;p&gt;Before beginning our journey here, to summarize the Vue Storefront integration with Magento let’s have a look at this diagram to see what components are included:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/GitHub-Architecture-VS.png&#34; alt=&#34;VS Architecture&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 1&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At first, we wanted to see how all these components can be installed and run on a single server with modest resources.&lt;/p&gt;
&lt;p&gt;I walked through the &lt;a href=&#34;https://docs.vuestorefront.io/guide/&#34;&gt;Vue Storefront documentation&lt;/a&gt; and a few &lt;a href=&#34;https://medium.com/the-vue-storefront-journal/proof-of-concept-how-to-run-pwa-for-magento-in-a-week-c0fa04fadd3d&#34;&gt;blog posts&lt;/a&gt; to figure out Vue Storefront and Magento integration.&lt;/p&gt;
&lt;h3 id=&#34;preparing-the-environment&#34;&gt;Preparing the Environment&lt;/h3&gt;
&lt;p&gt;I downloaded and installed the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OS: CentOS Linux 7 (64-bit)&lt;/li&gt;
&lt;li&gt;PHP 7.2.26&lt;/li&gt;
&lt;li&gt;Magento 2.3 with sample data&lt;/li&gt;
&lt;li&gt;Elasticsearch 5.6&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Vue Storefront API&lt;/li&gt;
&lt;li&gt;Vue Storefront&lt;/li&gt;
&lt;li&gt;mage2vuestorefront bridge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation of those components is fairly easy. We started our virtual server with 4 GB of memory, which we thought should be plenty for a toy setup.&lt;/p&gt;
&lt;h3 id=&#34;indexing-elasticsearch-with-mage2vuestorefront&#34;&gt;Indexing Elasticsearch with mage2vuestorefront&lt;/h3&gt;
&lt;p&gt;PHP and Magento 2.3 had a series of memory usage issues. While running the mage2vuestorefront indexer Magento used most of the memory and caused Elasticsearch to go down each time I tried to index Elasticsearch. Then I configured PHP and Apache httpd server to use a very modest amount memory and made Magento 2.3 work on this server without making it crash due to unavailable memory but the performance became a nightmare on the PHP &amp;amp; Magento 2.3 side. mage2vuestorefront ran without issue, but very slowly.&lt;/p&gt;
&lt;p&gt;I am not very familiar with the Magento 2 API, but while indexing Elasticsearch with mage2vuestorefront it makes several individual API calls. Especially if you have several products in Magento 2, mage2vuestorefront calls the Magento API for almost all products. Due to PHP and Magento’s high memory usage available memory might become a problem during indexing.&lt;/p&gt;
&lt;p&gt;The Vue Storefront developers &lt;a href=&#34;https://docs.vuestorefront.io/guide/cookbook/elastic.html#_0-introduction&#34;&gt;explain&lt;/a&gt; a big reason why they chose Elasticsearch as one of the essential components of this integration:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Vue Storefront&lt;/em&gt; defines itself backend-agnostic PWA e-commerce solution where &lt;em&gt;Vue Storefront&lt;/em&gt; is a storefront as the name dictates, and &lt;em&gt;Elasticsearch&lt;/em&gt; works as a datastore for &lt;em&gt;catalog&lt;/em&gt; and its sibling data such as &lt;em&gt;taxrule&lt;/em&gt;, &lt;em&gt;products&lt;/em&gt; and so on.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I am unsure if the backend-agnostic approach even really applies to the suggested implementation of Vue Storefront with Magento 2, or if does apply with significant headaches. I’ll detail my concerns about this later.&lt;/p&gt;
&lt;h3 id=&#34;viewing-the-vue-storefront-application&#34;&gt;Viewing the Vue Storefront Application&lt;/h3&gt;
&lt;p&gt;The responsive and offline supportive design of Vue Storefront is elegant. It uses Elasticsearch via vue-storefront-api as its indexing search engine to provide a seamless user experience. Although the overall integration and the produced data structure looks complex there are only a few catalog data stores that need to be indexed on Elasticsearch. Also, since this is an ecommerce integration, you can expect that only products and product-related data are going to be available in such an integration.&lt;/p&gt;
&lt;p&gt;In the Vue Storefront and Vue Storefront API app home directories (&lt;code&gt;vue-storefront/&lt;/code&gt; and &lt;code&gt;vue-storefront-api/&lt;/code&gt;) I ran &lt;code&gt;npm start&lt;/code&gt; to start the Node.js apps, then navigated to the Vue Storefront homepage to view its default template:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/storefront_temphost_net.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 2&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There is a hamburger menu in the top-left which opens the categories menu of Vue Storefront:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/storefronttemphostnet_cats_menu.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 3&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As I mentioned, the data being viewed is relatively simple according to the count of the catalog types (essentially Products, Categories, Attributes, and Tax Rules) which are already categorized and these are a bunch of specific sets of data.&lt;/p&gt;
&lt;p&gt;By searching through the categories Women &amp;gt; Tops &amp;gt; Jackets, the search output list is shown by Vue Storefront:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/storefronttemphostnet_cats_search.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 4&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&#34;designing-a-new-approach-for-interchange&#34;&gt;Designing a New Approach for Interchange&lt;/h3&gt;
&lt;p&gt;When exploring the features and components of Vue Storefront we discussed how we can adapt Interchange to use Vue Storefront. We ended up with two main options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Keep the same approach of Vue Storefront’s current integration with Magento and other ecommerce backends using Elasticsearch and vue-storefront-api as the backend gateways&lt;/li&gt;
&lt;li&gt;Remove the Elasticsearch and vue-storefront-api dependencies and create a new API in Interchange producing similar API outputs to the ones vue-storefront-api &amp;amp; Elasticsearch are creating. I also found a similar approach where they integrated SAP Hybris with Vue Storefront. I thought that this way would fit our intention.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;inspired-by-a-similar-approach-of-vue-storefront--sap-commerce&#34;&gt;Inspired By A Similar Approach of Vue Storefront + SAP Commerce&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://hybrismart.com/2019/02/13/vue-storefront-sap-commerce-open-source-pwa-storefront-integration-demo/&#34;&gt;Vue Storefront + SAP Commerce: Open-source PWA Storefront Integration (+DEMO)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This article discusses several approaches, including &lt;a href=&#34;https://github.com/DivanteLtd/storefront-integration-sdk&#34;&gt;Vue Storefront developers’ boilerplate integration&lt;/a&gt; and their custom solutions. In the following table they show different options and comments on their resolutions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/t1-1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 5&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you can see in Figure 5, they decided to go with the 4th option as their most suitable and least problematic solution. Here is their statement of why:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;After some analysis, we have come to believe that using custom middleware only for a bunch of simple requests is an additional burden and thick layer of complexity. We decided to get rid of it as well.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So, option #4 turned out to be the best choice. For the option #4, we need to parse Magento and Elasticsearch APIs and generate the compatible responses.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is how their solution looks according to option #4:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/p2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 6&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Using this approach, they decided to remove the Elasticsearch and Vue Storefront API components from the Vue Storefront boilerplate integration and developed their SAP Commerce custom Vue Storefront API to produce responses similar to the responses taken from Elasticsearch through Vue Storefront API to pretend that Vue Storefront is taking its responses from Elasticsearch API + Magento API.&lt;/p&gt;
&lt;p&gt;We decided to discard Elasticsearch when integrating Interchange with Vue Storefront for simplicity, and to cut down on the cost such additional search solutions create.&lt;/p&gt;
&lt;p&gt;After our discussions, I was inspired by this SAP Commerce + Vue Storefront integration demo. We finally decided to move forward with creating an Interchange API to produce the similar responses to those from the Elasticsearch &amp;amp; Magento APIs, so we could make the Interchange API pretend to be the Elasticsearch API + Magento API.&lt;/p&gt;
&lt;p&gt;We need to mention the Magento 2 API, or at least the Magento 2 data structure, because the Vue Storefront API and Vue Storefront boilerplate code is heavily designed to be taking ecommerce data (Products, Categories, Product Attributes, Tax Rules etc.) almost identical to the Magento API’s provided data.&lt;/p&gt;
&lt;p&gt;So our Interchange + Vue Storefront integration looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/08/vue-storefront-magento-integration/Vsinterchange_arch.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Figure 7&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Compare Figure 1 with Figure 7 to see the difference between this approach and Vue Storefront’s suggested solution.&lt;/p&gt;
&lt;h4 id=&#34;interchange-api-prototype-design&#34;&gt;Interchange API Prototype Design&lt;/h4&gt;
&lt;p&gt;In order to achieve the API prototyping for our Interchange/​Vue Storefront integration, I cloned vue-storefront and vue-storefront-api, which I had previously integrated with Magento 2.3, into a new user, keeping all settings the same. This cloned Vue Storefront application started initially with the same configuration as vue-storefront-api and Elasticsearch were not excluded yet. So I decided to replace the vue-storefront-api endpoints that vue-storefront is calling with endpoints from the new Interchange API.&lt;/p&gt;
&lt;p&gt;As the default SAP Commerce/​Vue Storefront integration suggests, I walked through the outputs of search results in “product”, “category”, and “attributes”. Product data JSON output has some universal attributes like name or title, description, or price, but it also has some custom product attributes adapted from the Magento 2 API (specifically, the data structure of Magento is applied).&lt;/p&gt;
&lt;p&gt;At this point I started questioning if Vue Storefront is not really platform agnostic, or if it is, but with some headaches. The next section will detail these headaches but to summarize, adapting the data structure of new ecommerce backends to Vue Storefront is a real pain. It forces you to use its data structure since that’s a part of core development, and they rely mainly on Elasticsearch, and I guess because of that the developers of Vue Storefront believe that Elasticsearch is an essential part of this “agnostic” approach. Refer to &lt;a href=&#34;https://docs.vuestorefront.io/guide/data/elastic-queries.html#getting-data-from-elasticsearch&#34;&gt;Getting data from Elasticsearch&lt;/a&gt; for custom implementations using the &lt;a href=&#34;https://github.com/vuestorefront/vue-storefront/tree/master/core/lib&#34;&gt;core Elasticsearch lib&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, I moved on and produced product and product categories outputs that Vue Storefront expects as the search results at the homepage and the search box output results.&lt;/p&gt;
&lt;p&gt;Documentation is another pain. This is discussed further in the next section, but briefly, it’s hard to say that you could clearly pick the data structure that all kind of output results from the documentation. You need to dig into the TypeScript code, and the exact search output result is what is actually being expected.&lt;/p&gt;
&lt;p&gt;One conflict was that the identifiers are required to be numeric, not strings. If your ecommerce data structure doesn’t provide its identifiers as numbers you’ll need to alter and add a new numeric unique identifier along with the existing one, which is what I did.&lt;/p&gt;
&lt;p&gt;Product attributes also need to be adapted to Vue Storefront (really to Magento’s data structure). The attribute names, attribute lists, attributes’ assigned values, and categories attached to the products and configurable child products all come along with the product list on a search output. If your ecommerce data is not structured similarly to Magento’s data structure, you need to dig and restructure even for minimal changes like product attributes and configurable child products. For example, if your data structure doesn’t allow configurable child products then you can probably only provide each option (such as product color or size options) as a new product, which will be a real pain.&lt;/p&gt;
&lt;p&gt;Most of Vue Storefront’s app endpoints are defined in &lt;code&gt;config/default.json&lt;/code&gt; which is the default config on a new install. On the other hand, the ecommerce catalog data (which is indexed in Elasticsearch) is available through the Elasticsearch implementation which is by default defined in &lt;code&gt;lib/search/adapter/api/searchAdapter.ts&lt;/code&gt;. In my case I needed to alter this implementation, which is generating the Elasticsearch (actually the Vue Storefront API) endpoints dynamically by looking up the related catalog index (product, category, attributes, taxes, etc.), and change the Elasticsearch definition in the Vue Storefront config file to the new Interchange API, adapted to emulate the Vue Storefront API and Elasticsearch:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;elasticsearch&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;httpAuth&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/api/catalog&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;vue_storefront_catalog&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I needed to set the Interchange API URL to the host property and not necessarily set an API-defining URL sub-path name to the index property above. This way our API would pretend to be the Elasticsearch endpoint as long as it can generate the JSON output that Vue Storefront expects.&lt;/p&gt;
&lt;p&gt;These configuration changes generally satisfied Vue Storefront’s expectations.&lt;/p&gt;
&lt;h3 id=&#34;common-and-exceptional-issues&#34;&gt;Common and Exceptional Issues&lt;/h3&gt;
&lt;p&gt;Many concerns and challenges are discussed in &lt;a href=&#34;https://hybrismart.com/2019/02/13/vue-storefront-sap-commerce-open-source-pwa-storefront-integration-demo/#h.4jhhd7ba1u4i&#34;&gt;this article on Vue Storefront + SAP Commerce integration&lt;/a&gt; which I mentioned earlier.&lt;/p&gt;
&lt;p&gt;A review of some issues I detailed earlier:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Required numeric unique IDs for all catalogs (products, categories, etc.)&lt;/li&gt;
&lt;li&gt;Poor documentation&lt;/li&gt;
&lt;li&gt;Catalog data structure is managed on the Vue Storefront side, heavily relying on its own data types and models. Your backend may need to be adapted to meet Vue Storefront’s expectations, even if you are using Elasticsearch.&lt;/li&gt;
&lt;li&gt;A structure which is constantly being reorganized, creating a big maintenance concern.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Despite my concerns, I thought we could still implement our Interchange API with a front-end framework, but I hadn’t asked the question: Is it reusable enough to mantain? Each of our clients would have different UI and UX expectations. One UI template model could never be enough, which would mean continuous front-end template design and enough maintenance to match the operational cost of existing Interchange front-end solutions. Adapting existing data to Vue Storefront’s new data model would bring more costs along with it.&lt;/p&gt;
&lt;p&gt;While Vue Storefront’s offline working model is well designed, for the purpose of integrating with a non-Magento backend, this is overshadowed by its many issues. You would be better off looking elsewhere for a more modular, truly platform-agnostic, easier-maintained, component- and data model-independent front-end.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Magento 2: Creating a custom theme</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/06/magento-2-creating-a-custom-theme/"/>
      <id>https://www.endpointdev.com/blog/2020/06/magento-2-creating-a-custom-theme/</id>
      <published>2020-06-24T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/06/magento-2-creating-a-custom-theme/paint-orange-blue.jpg&#34; alt=&#34;blue and yellow paint from a tube on a canvas&#34;&gt;
&lt;a href=&#34;https://flic.kr/p/Se3vkA&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/mariaeklind/&#34;&gt;Maria Eklind&lt;/a&gt;, &lt;a href=&#34;https://creativecommons.org/licenses/by-sa/2.0/&#34;&gt;CC BY-SA 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my previous post, we went through the steps needed to &lt;a href=&#34;/blog/2020/04/magento-2-creating-a-custom-module/&#34;&gt;create a custom module in Magento 2&lt;/a&gt;. While modules consist of a set of classes to add new features to Magento, a theme controls how these features, and the entire website in general, will be displayed to the user. As stated in the &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-overview.html&#34;&gt;Magento guide&lt;/a&gt;, a theme uses a combination of custom templates, layouts, styles, and images to provide a consistent look and feel across a Magento store.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-new-magento-2-theme&#34;&gt;Creating a new Magento 2 theme&lt;/h3&gt;
&lt;p&gt;We can create a theme based on a default “parent” theme or create a standalone theme from scratch. In most cases, I would recommend the first option. For this example, we will use &lt;a href=&#34;https://magento2-demo.magebit.com/&#34;&gt;Luma&lt;/a&gt; as our parent theme. The other option would be inheriting from the default “blank” theme.&lt;/p&gt;
&lt;p&gt;Here’s an initial task list to get our new theme ready:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the theme&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;registration.php&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;theme.xml&lt;/code&gt; information file&lt;/li&gt;
&lt;li&gt;Activate the new theme&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-theme&#34;&gt;Creating a new directory for the theme&lt;/h4&gt;
&lt;p&gt;While all our backend code should go in &lt;code&gt;app/code&lt;/code&gt;, the frontend content is expected to go in &lt;code&gt;app/design&lt;/code&gt;. And as our theme will only apply design changes to the frontend content, we should create the new directory for it under the path &lt;code&gt;app/design/frontend&lt;/code&gt;. If we want to create a theme for the admin area instead, we need to create the directory inside &lt;code&gt;app/design/adminhtml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s create a directory named &lt;code&gt;EndPoint&lt;/code&gt; (our vendor name, continuing with the example from our previous article) and a subdirectory inside it, &lt;code&gt;MyTheme&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; {website_root}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/design/frontend/EndPoint/MyTheme&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-registrationphp&#34;&gt;Creating registration.php&lt;/h4&gt;
&lt;p&gt;Similar to the file we created for our module, &lt;code&gt;registration.php&lt;/code&gt; tells Magento to register the new theme with the name and location we specify. Our file will be located at &lt;code&gt;app/design/frontend/EndPoint/MyTheme/registration.php&lt;/code&gt; and should have the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;\Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;register&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;THEME&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;frontend/EndPoint/MyTheme&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, Magento will know what path our theme will have.&lt;/p&gt;
&lt;h4 id=&#34;creating-themexml&#34;&gt;Creating theme.xml&lt;/h4&gt;
&lt;p&gt;The next step is to create our theme information file, where we will specify the theme name and parent theme. So our &lt;code&gt;app/design/frontend/EndPoint/MyTheme/theme.xml&lt;/code&gt; file should have the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;theme&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:Config/etc/theme.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;MyTheme&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;Magento/luma&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/theme&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Optional&lt;/em&gt;: If we want our theme to be easily distributed as a package, we can also create a &lt;code&gt;composer.json&lt;/code&gt; file in the theme’s root directory. The content for the file should be as follows, specifying the theme’s description, dependencies, version, and license types:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;endpoint/mytheme&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;My Theme by End Point&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;require&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;magento/theme-frontend-luma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;magento/framework&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;magento2-theme&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;license&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;OSL-3.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AFL-3.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;autoload&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;files&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;registration.php&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;activating-our-new-theme&#34;&gt;Activating our new theme&lt;/h4&gt;
&lt;p&gt;That was easy! We have everything we need to activate our new theme. To do that we log in to our admin area and enable our theme. Once in the dashboard, we need to go to Content &amp;gt; Design &amp;gt; Configuration, edit our store view, and select our new theme from the dropdown list:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;magento-2-creating-a-custom-theme/magento-admin-select-theme.jpg&#34; alt=&#34;Selecting our theme&#34;&gt;&lt;/p&gt;
&lt;p&gt;Magento will search for new themes every time we log in to the admin area, so our new theme will appear on the list automatically.&lt;/p&gt;
&lt;h3 id=&#34;adding-custom-content&#34;&gt;Adding custom content&lt;/h3&gt;
&lt;p&gt;We have the basic structure for our theme, but when enabled, it will look the same as its parent theme (Luma, in this case), since we didn’t add any design rules or static files yet. Let’s do some more things with our theme to change how it’s displayed in the frontend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a custom &lt;code&gt;etc/view.xml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Add a custom logo&lt;/li&gt;
&lt;li&gt;Add static files (JavaScript, CSS, images, fonts)&lt;/li&gt;
&lt;li&gt;Add a custom layout&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-custom-view-file&#34;&gt;Creating a custom view file&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;etc/view.xml&lt;/code&gt; controls many frontend configurations like the product thumbnail width, how the product image gallery is displayed, and the image magnifier tool, among other things. To add our custom view file to our theme, we need to copy the existing file from our parent theme. For Luma, it will be located at &lt;code&gt;vendor/magento/theme-frontend-luma/etc/view.xml&lt;/code&gt;. To copy the file, we need to run the following in our website’s root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/design/frontend/EndPoint/MyTheme/etc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp vendor/magento/theme-frontend-blank/etc/view.xml app/design/frontend/EndPoint/MyTheme/etc/view.xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we can use our preferred text editor to change the values we want, like setting a custom size for the images in the category page grid:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;category_page_grid&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;small_image&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt;&amp;gt;300&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;height&lt;/span&gt;&amp;gt;300&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;height&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;adding-a-custom-logo&#34;&gt;Adding a custom logo&lt;/h4&gt;
&lt;p&gt;Adding a logo to our theme is really simple. We just need to save our picture in SVG format as &lt;code&gt;web/images/logo.svg&lt;/code&gt;. If we want to use a different filename or format for our logo, we will have to create a default layout file for our theme in the path &lt;code&gt;/Magento_Theme/layout/default.xml&lt;/code&gt; inside our theme root with content similar to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;page&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:View/Layout/etc/page_configuration.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;referenceBlock&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;arguments&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_file&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;images/custom_logo.png&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_width&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;300&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_height&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;200&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_alt&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;Custom logo name&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/arguments&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/referenceBlock&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/page&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use different image formats such as SVG, PNG, or JPG. We can also use a custom width and height for the logo, and set a custom alternate text.&lt;/p&gt;
&lt;h4 id=&#34;adding-static-files-javascriptcssimagesfonts&#34;&gt;Adding static files (JavaScript/​CSS/​images/​fonts)&lt;/h4&gt;
&lt;p&gt;All the static files should be located inside the &lt;code&gt;web&lt;/code&gt; directory. Common static files include JavaScript files, stylesheets, images, and fonts. The JavaScript files should be located at &lt;code&gt;web/js&lt;/code&gt;, stylesheets at &lt;code&gt;web/css&lt;/code&gt;, images at &lt;code&gt;web/images&lt;/code&gt;, and our custom fonts should be located at &lt;code&gt;web/fonts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All the static files will be published as direct links, without any processing from Magento, at the &lt;code&gt;pub/static/frontend/EndPoint/MyTheme/en_US&lt;/code&gt; path. The default locale/​language is en_US; we can change it for our theme if needed.&lt;/p&gt;
&lt;h4 id=&#34;adding-a-custom-layout&#34;&gt;Adding a custom layout&lt;/h4&gt;
&lt;p&gt;Finally, if we want to use the new assets we added and have custom content on different sections of our website, we need to extend or override the existing layout files from our parent theme.&lt;/p&gt;
&lt;p&gt;For example, if we want to add a reference to new stylesheet and JavaScript files we added, we need to extend the existing header layout from our parent theme. To do this, we will create a new layout file located at &lt;code&gt;Magento_Theme/layout/default_head_blocks.xml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;page&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:View/Layout/etc/page_configuration.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Custom stylesheet --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;css&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;css/mytheme.css&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Custom JavaScript --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;js/mytheme.js&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/page&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way we will be adding a reference to a new stylesheet named &lt;code&gt;mytheme.css&lt;/code&gt; that we have inside the &lt;code&gt;web/css&lt;/code&gt; directory of our theme, and a new script named &lt;code&gt;mytheme.js&lt;/code&gt; inside the &lt;code&gt;web/js&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;After we make all the desired changes to our theme, we will need to tell Magento to update the frontend. We need to deploy the new changes and clear the cache. We can achieve that by running this from our website root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:static-content:deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento cache:clean&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This process can take some minutes to complete. After it’s done, we can go to our website to see if the frontend changes are applied:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;magento-2-creating-a-custom-theme/magento-frontend.jpg&#34; alt=&#34;Website homepage&#34;&gt;&lt;/p&gt;
&lt;p&gt;Looks awesome! Of course, there’s a lot more we can do from there, from extending or overriding layouts from modules (Magento or third-party) to bundling scripts or using Less files for our custom styles. But, that is material for later posts, so that’s all for now! Please add any questions you might have below.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Shopify Admin API: Importing Products in Bulk</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/05/shopify-product-creation/"/>
      <id>https://www.endpointdev.com/blog/2020/05/shopify-product-creation/</id>
      <published>2020-05-04T00:00:00+00:00</published>
      <author>
        <name>Patrick Lewis</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/05/shopify-product-creation/banner.jpg&#34; alt=&#34;Cash Register&#34;&gt;
&lt;a href=&#34;https://www.flickr.com/photos/mrvjtod/279845948/&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/mrvjtod/&#34;&gt;Chris Young&lt;/a&gt;, used under &lt;a href=&#34;https://creativecommons.org/licenses/by-sa/2.0/&#34;&gt;CC BY-SA 2.0&lt;/a&gt;, cropped from original.&lt;/p&gt;
&lt;p&gt;I recently worked on an interesting project for a store owner who was facing a daunting task: he had an inventory of hundreds of thousands of &lt;a href=&#34;https://en.wikipedia.org/wiki/Magic%3A_The_Gathering&#34;&gt;Magic: The Gathering&lt;/a&gt; (MTG) cards that he wanted to sell online through his Shopify store. The logistics of tracking down artwork and current market pricing for each card made it impossible to do manually.&lt;/p&gt;
&lt;p&gt;My solution was to create a custom Rails application that retrieves inventory data from a combination of APIs and then automatically creates products for each card in Shopify. The resulting project turned what would have been a months- or years-long task into a bulk upload that only took a few hours to complete and allowed the store owner to immediately start selling his inventory online. The online store launch turned out to be even more important than initially expected due to current closures of physical stores.&lt;/p&gt;
&lt;h3 id=&#34;application-requirements&#34;&gt;Application Requirements&lt;/h3&gt;
&lt;p&gt;The main requirements for the Rails application were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Retrieving product data for MTG cards by merging results from a combination of sources/APIs&lt;/li&gt;
&lt;li&gt;Mapping card attributes and metadata into the format expected by the Shopify Admin API for creating Product records&lt;/li&gt;
&lt;li&gt;Performing a bulk push of products to Shopify&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There were some additional considerations like staying within rate limits for both the inventory data and Shopify APIs, but I will address those further in a follow-up post.&lt;/p&gt;
&lt;h3 id=&#34;retrieving-card-artwork-and-pricing&#34;&gt;Retrieving Card Artwork and Pricing&lt;/h3&gt;
&lt;p&gt;I ended up using a combination of two APIs to retrieve MTG card data: &lt;a href=&#34;https://mtgjson.com/&#34;&gt;MTGJSON&lt;/a&gt; for card details like the name of the card and the set it belonged to, and &lt;a href=&#34;https://scryfall.com/&#34;&gt;Scryfall&lt;/a&gt; for retrieving card images and current market pricing. It was relatively easy to combine the two because MTGJSON provided Scryfall IDs for all of its records, allowing me to merge results from the two APIs together.&lt;/p&gt;
&lt;h3 id=&#34;working-with-the-shopify-admin-api-in-ruby&#34;&gt;Working With the Shopify Admin API in Ruby&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&#34;https://shopify.dev/docs/admin-api&#34;&gt;Shopify Admin API&lt;/a&gt; deals in terms of generic &lt;a href=&#34;https://shopify.dev/docs/admin-api/rest/reference/products/product&#34;&gt;Product&lt;/a&gt; records with predefined attributes like &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;product_type&lt;/code&gt;. The official &lt;a href=&#34;https://github.com/Shopify/shopify_api&#34;&gt;shopify_api&lt;/a&gt; Ruby gem made it very easy to connect to my client’s Shopify store and create new products by creating &lt;code&gt;Shopify::Product&lt;/code&gt; objects with a hash of attributes like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  attrs = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;images&lt;/span&gt;: [{ &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;src&lt;/span&gt;: scryfall_card.image_uris.large }],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;options&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Card Type&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#038&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Condition&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;product_type&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MTG Singles&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;tags&lt;/span&gt;: card.setCode,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;title&lt;/span&gt;: card.name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;variants&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;inventory_management&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;shopify&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;inventory_quantity&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;option1&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Foil&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;option2&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Like New&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a60;background-color:#fff0f0&#34;&gt;price&lt;/span&gt;: scryfall_card.prices.usd_foil
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Shopify&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Product&lt;/span&gt;.new(attrs).save&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The actual production code is a bit more complicated to account for outliers like cards with multiple “faces” and cards that come in both regular and foil variants, but the example above shows the basic shape of the attributes expected by the Shopify API.&lt;/p&gt;
&lt;h3 id=&#34;pushing-50000-products-to-shopify&#34;&gt;Pushing 50,000 Products to Shopify&lt;/h3&gt;
&lt;p&gt;After I completed testing with individual products and confirmed the ability to take a specific card and turn it into a Shopify product with artwork and pricing pre-populated it was time to perform the full upload of all 50,000+ cards in the MTGJSON database. I decided to use &lt;a href=&#34;https://sidekiq.org/&#34;&gt;Sidekiq&lt;/a&gt; and create jobs for each card upload so that I could rate limit the workers to stay within rate limits for both the Scryfall and Shopify APIs, and also have persistence that would allow me to pause/resume the queue or retry individual failed jobs.&lt;/p&gt;
&lt;p&gt;The Sidekiq approach to queueing up all of the card uploads worked great; I was able to use the Sidekiq dashboard to monitor the queue of 50,000 jobs as it worked its way through each card, and was able to see the Shopify products being created on the store in real time. Once the inventory was in place in Shopify the store owner was able to start updating his inventory levels and make cards available for sale via the Shopify Admin.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;A custom Ruby application using the Shopify API is a powerful solution for online storefronts that need to retrieve a large number of inventory data from external sources. I was pleased with how this project turned out; it was rewarding to create a custom application that leveraged several APIs and automated a task that would have been extremely repetitive, and probably impossibly time-consuming, to do manually. It was encouraging to do my first upload of a card and see it show up on the Shopify store with artwork, pricing, and card details pre-populated.&lt;/p&gt;
&lt;p&gt;The development model used for this project could be applied to stores in a wide variety of markets. This project used external APIs to retrieve product information but that data source could easily be replaced with a spreadsheet, CSV file, or some other export file containing bulk information on products to be sold.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Magento 2: Creating a custom module</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/04/magento-2-creating-a-custom-module/"/>
      <id>https://www.endpointdev.com/blog/2020/04/magento-2-creating-a-custom-module/</id>
      <published>2020-04-01T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/04/magento-2-creating-a-custom-module/bridge-wires.jpg&#34; alt=&#34;Bridge with wires&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/q4ZBGVzJskE&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@olajidetunde&#34;&gt;Babatunde Olajide&lt;/a&gt;, cropped from original&lt;/p&gt;
&lt;p&gt;A Magento module is a set of classes and routines that will depend on and interact with other Magento classes in order to add a specific feature to a Magento application. While a &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-overview.html&#34;&gt;theme&lt;/a&gt; is orientated towards the front-end and user experience, a &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/architecture/archi_perspectives/components/modules/mod_intro.html&#34;&gt;module&lt;/a&gt; is orientated towards backend logic and application flow.&lt;/p&gt;
&lt;p&gt;We will need to create a custom module if we want to add or change the existing logic at a level where Magento doesn’t provide a setting or option for it. For example, if our business has a specific feature or set of features or requirements that are not common to the market, a module can fill that gap for us.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-basic-magento-2-module&#34;&gt;Creating a basic Magento 2 module&lt;/h3&gt;
&lt;p&gt;Creating a simple module in Magento 2 is not that hard. We will need to accomplish the following tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the module&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;registration.php&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;etc/module.xml&lt;/code&gt; information file&lt;/li&gt;
&lt;li&gt;Install the new module&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-module&#34;&gt;Creating a new directory for the module&lt;/h4&gt;
&lt;p&gt;Where should the new directory for our module be placed? We have two options to choose from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app/code/{vendor}/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vendor/{vendor}/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your module is intended for a specific website you’re working on, you can use the first option. If you’re creating a module with the intention of it being used on several websites, it’s best to choose the second option. We’ll use the first for this example.&lt;/p&gt;
&lt;p&gt;Let’s create a directory named &lt;code&gt;EndPoint&lt;/code&gt; (our vendor name) with a subdirectory inside it, &lt;code&gt;MyModule&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; {website_root}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/EndPoint/MyModule&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-the-registrationphp-script&#34;&gt;Creating the registration.php script&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;registration.php&lt;/code&gt; file tells Magento to register the new module under a specific name and location. Let’s create a file named &lt;code&gt;app/code/EndPoint/MyModule/registration.php&lt;/code&gt; with the folllowing content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;\Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;register&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;MODULE&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;EndPoint_MyModule&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’re telling Magento that our module will be named EndPoint_MyModule.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-etcmodulexml-information-file&#34;&gt;Creating the etc/module.xml information file&lt;/h4&gt;
&lt;p&gt;Now, let’s create our module information file, where we’ll specify the module version number. First, we need to create the &lt;code&gt;etc&lt;/code&gt; directory inside &lt;code&gt;app/code/EndPoint/MyModule&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir app/code/EndPoint/MyModule/etc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;then create &lt;code&gt;module.xml&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;config&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:Module/etc/module.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;EndPoint_MyModule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;setup_version=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;installing-the-new-module&#34;&gt;Installing the new module&lt;/h4&gt;
&lt;p&gt;That’s it! We have everything we need to install our new module. Now we need to tell Magento we want to install and enable our new module. So from our website root we need to run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:upgrade&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Magento will output a list of module names and configuration updates, and our new module &lt;code&gt;EndPoint_MyModule&lt;/code&gt; should be listed in that output.&lt;/p&gt;
&lt;h3 id=&#34;adding-a-custom-route-to-our-module&#34;&gt;Adding a custom route to our module&lt;/h3&gt;
&lt;p&gt;Now we have a working, enabled module, but it’s not doing anything yet! What’s a simple way to check that our module is enabled? Let’s set up a custom route, so if we hit a URL like &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt; we can return a custom response from a controller.&lt;/p&gt;
&lt;p&gt;Creating a custom route will need some steps on its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the controller&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;etc/routes.xml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Create the controller&lt;/li&gt;
&lt;li&gt;Upgrade the new module&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-controller&#34;&gt;Creating a new directory for the controller&lt;/h4&gt;
&lt;p&gt;First we need to create a new directory where the new PHP controller for our custom route will live. The new directory path should be:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app/code/EndPoint/MyModule/Controller&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We can create as many directory levels we want, depending on our desired path. For example, if we create a class named &lt;code&gt;Index&lt;/code&gt; in &lt;code&gt;app/code/EndPoint/MyModule/Controller&lt;/code&gt;, the URL that will be routed to this controller will be &lt;code&gt;https://{our_website}/mymodule/index&lt;/code&gt; (the “Controller” directory is ignored). If we create a class named &lt;code&gt;HelloWorld&lt;/code&gt; in &lt;code&gt;app/code/EndPoint/MyModule/Controller/Test&lt;/code&gt;, the resulting URL will be &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-etcroutesxml-file&#34;&gt;Creating the etc/routes.xml file&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;routes.xml&lt;/code&gt; will tell Magento what base URL will be used for our module. First, we need to create the “frontend” directory where the routes.xml file needs to be placed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir app/code/EndPoint/MyModule/etc/frontend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, we want the base URL to be &lt;code&gt;MyModule&lt;/code&gt;, so we need to create an XML file inside the new directory that will route all requests made to the given URL to our module controllers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; ?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;config&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:App/etc/routes.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;router&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;route&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;frontName=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mymodule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mymodule&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;EndPoint_MyModule&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/route&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/router&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-the-controller&#34;&gt;Creating the controller&lt;/h4&gt;
&lt;p&gt;If we want to respond to requests for &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt; we first need to create the base &lt;code&gt;Controller&lt;/code&gt; directory and the &lt;code&gt;Test&lt;/code&gt; subdirectory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/EndPoint/MyModule/Controller/Test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Under this directory, we’ll create our custom Magento controller. All route controllers should extend &lt;code&gt;\Magento\Framework\App\Action\Action&lt;/code&gt;. We also need to have a public &lt;code&gt;construct()&lt;/code&gt; method to pass the context to our ancestor and an &lt;code&gt;execute()&lt;/code&gt; function that will be called when the URL is hit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; EndPoint\MyModule\Controller\Test;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;HelloWorld&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; \Magento\Framework\App\Action\Action
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        \Magento\Framework\App\Action\Context &lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;execute&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello world!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;upgrading-the-new-module&#34;&gt;Upgrading the new module&lt;/h4&gt;
&lt;p&gt;We have everything in place to tell Magento we have new changes to be deployed. How we do that? First, we need to upgrade our Magento setup. But since we added a new controller that gets parameters from the dependency injector in the construct, we also need to compile the dependency injection engine (including factories, proxies, and interceptors). And finally, we need to clear the cache so new content will be served from our custom URL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:di:compile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento cache:flush&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This process can take a few minutes to complete, but after it’s done we can try to reach our new custom URL. If we get a response like the one below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/04/magento-2-creating-a-custom-module/magento-hello-world-response.jpg&#34; alt=&#34;Hello world!&#34;&gt;&lt;/p&gt;
&lt;p&gt;That means our module is working!&lt;/p&gt;
&lt;p&gt;That’s all for now. In upcoming posts, we’ll start complicating things a bit by overriding Magento classes with our custom ones and creating custom controllers that will return information from the Magento core classes. We will also explore how to customize the front-end by creating a theme. Don’t forget to add any questions, suggestions, or issues in the comments below!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>E-commerce Client Project Management</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/03/from-dev-to-pm/"/>
      <id>https://www.endpointdev.com/blog/2020/03/from-dev-to-pm/</id>
      <published>2020-03-12T00:00:00+00:00</published>
      <author>
        <name>Greg Hanson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/03/from-dev-to-pm/banner.jpg&#34; alt=&#34;&#34;&gt;
Photo by &lt;a href=&#34;https://unsplash.com/@youxventures&#34;&gt;You X Ventures&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/photos/Oalh2MojUuk&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;moving-from-writing-code-to-managing-the-show&#34;&gt;Moving from writing code to managing the show&lt;/h3&gt;
&lt;p&gt;Many times engineers/​developers make the move from development to project management.
It’s a natural move, we want the folks who know the nuts and bolts of e-commerce projects to
eventually manage them.&lt;/p&gt;
&lt;p&gt;So that’s all fine and dandy, but what if you haven’t been a “manager” before?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do you manage an e-commerce client?&lt;/li&gt;
&lt;li&gt;How do you manage an e-commerce project?&lt;/li&gt;
&lt;li&gt;How do you manage engineers/​developers for an e-commerce project?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An answer for each of the above is always: “It depends.” Or maybe more familiarly for Perl developers: &lt;a href=&#34;https://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it&#34;&gt;TIMTOWTDI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The reason for that of course is that all of the above questions have variables that will
change for every situation.&lt;/p&gt;
&lt;p&gt;As a developer, you understand the large number of outcomes that can be introduced into an application by using a single variable. You also understand that the number of outcomes increases proportionally with the number of variables.&lt;/p&gt;
&lt;p&gt;The same holds true for management. When you are faced with managing a project, your “variables” now move from placeholders in your code, to placeholders in your project. Where you may have assigned a variable for a “string”, “integer”, or “boolean”, you now may have a “client”, “project”, or “team of developers”.&lt;/p&gt;
&lt;p&gt;The point here is that while variables will change from project to project, the “structure” of how you run that project can still remain consistent. Much like designing code to return consistent results while using a wide range of variables.&lt;/p&gt;
&lt;p&gt;In order to achieve this type of consistency, the core operations that run within the project, need to be reliable. Over time you will come to develop processes in your projects that will be reliable, and that you will return to time after time, project after project. To get you started, here are a few that should be at the core any project management:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Know your client.&lt;/li&gt;
&lt;li&gt;Learn the project.&lt;/li&gt;
&lt;li&gt;Know your developers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are 3 basic rules that should guide any project, and we will explore each in more detail below.&lt;/p&gt;
&lt;p&gt;Now as you can see, each situation will vary widely. A good project manager will understand
that any project is multifaceted, and consists largely of human resources.&lt;/p&gt;
&lt;p&gt;Let me also note that while this topic is titled “E-commerce Client Project Management”, it could
just as easily be titled “Client Project Management”. The principles of project management apply
to many different environments.&lt;/p&gt;
&lt;p&gt;So if you are new to project management, the first piece of advice I will give is to:&lt;/p&gt;
&lt;h3 id=&#34;1-know-your-client&#34;&gt;1. Know your client&lt;/h3&gt;
&lt;p&gt;Everything flows from the client. Always remember, the client is paying you to
represent their best interest. This can sometimes mean that you will need to tell the client
that you are not the best choice for a particular project!&lt;/p&gt;
&lt;p&gt;“Wait&amp;hellip;” you will ask, “why would I tell a client to use someone other than me to manage their
project? And how would that benefit me? I am in business to manage projects after all!”&lt;/p&gt;
&lt;p&gt;I would answer that question with another: How long to you want to be in business as a project
manager? If the answer is anything other than “a long time”, you can stop reading now.&lt;/p&gt;
&lt;p&gt;Let me give you a quick analogy. Let’s say you went to a car “lot A”, and told the salesman that
you needed a very specific type of vehicle because you deliver a highly perishable product.
Let’s also assume that this salesperson did not have that specific vehicle on his lot, but that
he also knew of another “lot B” where the exact vehicle was sitting. What would you want that
salesperson to do? Would you want him to just tell you that they could customize a vehicle on their
lot to do most of what you need? Would you want him to refer you to the other dealership?&lt;/p&gt;
&lt;p&gt;Or, would you like him to explain all options that he knew of that might apply to your situation?
If you chose this last option, score extra points for your potential future as a project manager.
To further reinforce this, remember, &lt;strong&gt;very few clients only need one project managed&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ok, so we have established that you need to know your client. That sounds easy, or does it?
How do you “know your client”? Let’s try to distill
it down a bit. There are many ways of “knowing” someone. We can know their personality, their
family, their business, their religion, politics… ok, enough already. But believe it or not,
all of the aforementioned points will come into play with a long-term client. But we want to talk
about the basics here. We will assume we have a first-time client, with a first-time (to us)
project.&lt;/p&gt;
&lt;p&gt;Find out about the clients’ business. How do they make money with this business? While you are
talking to the client about this, ask questions that get the client to explain and promote
their business to you. For most, this will be easy&amp;hellip; sometimes to the point of having to learn
how to politely stop them! But you will learn far more about their business, and their personal
approach to that business by &lt;em&gt;listening&lt;/em&gt; than you will by talking.&lt;/p&gt;
&lt;p&gt;Just get them started, and let
them roll. In the early meetings, ask questions about why and how the client feels their
product/​service is superior to others.
Find out who in the company was responsible for various aspects of the product/​service. Learn who
the stakeholders are. Try not to ask too many technical questions until the project outline has
been defined, and you personally have a good feel for what it is comprised of.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Clients are not free&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Clients are a long-term (and ongoing) proposition. Take care of them and they will return
again and again. Ignore them and they will respond in kind, and usually very quickly. The
time you spend building client relationships is normally non-billable. So keep that in mind the
next time a client is hard to deal with and you think “We don’t need clients like this, let’s
just find a new one”. You will invest a lot of time and energy with a client.
Try not to waste it.&lt;/p&gt;
&lt;h3 id=&#34;2-learn-your-project&#34;&gt;2. Learn your project&lt;/h3&gt;
&lt;p&gt;If you have a good understanding of the client and their business, you next want them to explain
to you what they are attempting to accomplish with this project. Let me clarify here: You do &lt;em&gt;not&lt;/em&gt;
want them to explain how they want to do a project. You want to first learn &lt;strong&gt;what the client
wants to accomplish with the project&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is important, and should be &lt;em&gt;understood completely&lt;/em&gt; because if you blow this phase
you can easily miss one of the major points of e-commerce project management. I have found that
about 7 of 10 clients have major misconceptions about how things actually work in our business.
They usually have a very good idea of &lt;em&gt;what&lt;/em&gt; they want to have happen in the end, but not so
much &lt;em&gt;how&lt;/em&gt; to make that happen.&lt;/p&gt;
&lt;p&gt;When I say &lt;em&gt;understand completely&lt;/em&gt;, I am talking about the 3 of 10 that &lt;em&gt;do&lt;/em&gt; know what they are
talking about and &lt;em&gt;need&lt;/em&gt; to be taken literally.&lt;/p&gt;
&lt;p&gt;It will be up to you to determine what type of client you are dealing with. Use your past
experiences with people to figure this out. It can be hard. You have to make sure you don’t insult
someone’s intelligence, and at the same time you need to know on what level you should attempt to
communicate with them. I have found it’s
best to let the client talk, try to ask “steering” questions that can unearth what level of
experience you are dealing with. These conversations will set the stage for the rest of the
project. You will determine how much you will need to supply in the way of structure and advice
for the project, as well as give you a clue as to what developers and resources you will use
for the project.&lt;/p&gt;
&lt;p&gt;Because this post is geared towards developers becoming project managers, I would tell you that this is a
place to lean on your background. You should be able to listen to the client, ask the right
technical questions, to get a good feel for how you will manage the project.&lt;/p&gt;
&lt;p&gt;Once you have figured out how much of what type of role you will have to play in the project,
you will need to either define the proposed scope of the project, or examine scope definition
given to you by the client. There are many threads that can stem from this, but are not germane
in this post, and we can move on to resourcing or how to manage your developers.&lt;/p&gt;
&lt;p&gt;Depending on the project, you will most likely need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Small project: Just do it once client approves.&lt;/li&gt;
&lt;li&gt;Small to medium-small project: Give a ballpark estimate of costs, usually un-billed.&lt;/li&gt;
&lt;li&gt;Medium project: Give a more detailed estimate, perhaps billing for estimating time.&lt;/li&gt;
&lt;li&gt;Medium to large project: Give a detailed estimate, billing for estimating time.&lt;/li&gt;
&lt;li&gt;Large to very large project: Give estimate of time for a formal quote. A quote is in essence a
small project on its own.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Again, estimating a project is an art in itself, and is not part of this post.&lt;/p&gt;
&lt;p&gt;Normally, in all of the above other than #1, you will need to confer with developers on your team
to supply the client with an accurate estimate of costs. Of course, in order to be an efficient
PM, you first need to:&lt;/p&gt;
&lt;h3 id=&#34;3-know-your-developers&#34;&gt;3. Know your developers&lt;/h3&gt;
&lt;p&gt;This should be pretty self-explanatory, but obviously you are not going to put a front-end
developer on a code-intensive back-end project.&lt;/p&gt;
&lt;p&gt;Another obvious point is that we are talking about a situation in which you have the luxury of
multiple developers to choose from. If that is not the case, then you will be faced with another
task, that of serving as a “translator” between the client and the developer.&lt;/p&gt;
&lt;p&gt;Given that statement, there are nuances that need to be considered when matching developers with
clients, or how to control communications.&lt;/p&gt;
&lt;p&gt;If the client is a larger company, try to get them to assign a champion for the project, someone
who is preferably somewhat technical, has authority to make some level of changes and decisions,
and who is appointed by a high-ranking person in the company. This person should be the contact
point, representing the client for all daily project decisions and questions. In larger projects,
this person may oversee a team from the client, that will interface with your team of developers.
Without this person, you should warn the client that undue and excessive delays due to
communications &lt;em&gt;will&lt;/em&gt; occur.&lt;/p&gt;
&lt;p&gt;If the client is a smaller company, try to get an owner or other heavily committed person to be
your point of contact who will automatically escalate your questions so that you do not have
to wait around for answers. You won’t want to wait for answers, and the owner will not want to
pay for your time or lost calendar days to wait for answers either!&lt;/p&gt;
&lt;p&gt;All this so far is great, if you are the point of communication for your company. In many cases it
will prove to be best for this to happen. You can monitor all conversations, communications, and
prevent misunderstandings and hopefully anticipate problems before they occur.&lt;/p&gt;
&lt;p&gt;But if you need to have your developers talk directly to the client’s rep, you have some thinking
to do. Certain traits do not mix well:&lt;/p&gt;
&lt;p&gt;If your client is very opinionated about their project, try to work out directions and methods
&lt;em&gt;before&lt;/em&gt; allowing communication between developers and client. Use Trello, Slack, etc to assign specific tasks for
specific people. Try to track progress of predefined tasks, and limit creation of undiscussed features.
Stick to the program; this is not the place for “developer creativity”. This is where you really get tested,
you have to know your developers so that you do not create resentment through restraint, but at the same
time picky clients will not accept drawing outside the lines.&lt;/p&gt;
&lt;p&gt;If your client is tech-savvy, easy to get along with, and you can convince your developers of that,
direct interaction can be extremely productive. But don’t just assume this will work. Be on the
the first phone calls or video conferences, be a fly on the wall, observe the dynamics.
Don’t run the meeting; step in only if necessary.&lt;/p&gt;
&lt;p&gt;Use your resources. If you have two front-end people, and you have friction between the client and
one of them, introduce the other for a separate task. Observe, did it work out? Do I need to
phase out one in favor of another?&lt;/p&gt;
&lt;p&gt;If possible never “pull” a developer completely off a project, unless of course it’s requested/​required. (Do the words “burning your bridges” sound familiar?)&lt;/p&gt;
&lt;p&gt;Always talk to the developer in private about whatever the issue is, and always point out to
developers that relationships do not depend on talent alone. What works for one client will
not work for the next.&lt;/p&gt;
&lt;p&gt;Make sure you know your developers. You need to stand up for them when talking to clients. If
you are not comfortable with a developer, you need to either discuss it with a superior, or if
you are the superior you need to let them go. You cannot lie to a client about your faith in
a developer. Clients don’t want to hear you bad-mouth a developer in your own company — this
simply makes you and the developer look bad. The client expects you to have faith in your developer,
and to stand up for them.&lt;/p&gt;
&lt;p&gt;With that said, back to rule #1, know your client. You have to know what that client will
tolerate or expect. You may have to be firm with some clients, so that you can give them what
is the best solution for their project.&lt;/p&gt;
&lt;p&gt;Other clients may require a soft approach and may completely understand “how this stuff works”.
When you get one of those, you will understand very quickly, and be very thankful.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Managing projects is a challenging proposition. But if you put things in perspective,
you will find that it can be simplified and rewarding. Try to keep the big picture in
mind as you proceed through the project. It’s easy to get drawn into the details of a
project, and sometimes that is what is required. However, if you have good resources
available to you, it is up to you to put the areas of responsibility into the proper hands.
Coming from development, this will be a real temptation. However, you have a new title now,
so put it to work!&lt;/p&gt;
&lt;p&gt;Remember, you are not working on this project to create something for you, or
your developers. You are creating something for your client. The project’s goals
are largely dependent on your clients needs.
I am not saying that you cannot create something that you or your developers like,
quite the contrary. I am suggesting that by focusing on the needs of the client, and really
listening to them, then sharing the expertise of you and your team &lt;em&gt;with&lt;/em&gt; the client, you can
arrive at something that will not only work extremely well for your client,
but also provide you and your team with a great sense of accomplishment.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce sales tax primer</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/01/ecommerce-sales-tax-primer/"/>
      <id>https://www.endpointdev.com/blog/2020/01/ecommerce-sales-tax-primer/</id>
      <published>2020-01-13T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;p&gt;Co-authored by &lt;a href=&#34;/team/greg-hanson/&#34;&gt;Greg Hanson&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/01/ecommerce-sales-tax-primer/image-0.jpg&#34; alt=&#34;Roman tax burning&#34;&gt;
&lt;a href=&#34;https://www.flickr.com/photos/internetarchivebookimages/14591980537&#34;&gt;Source image&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tax collection is one of the topics &lt;em&gt;du jour&lt;/em&gt; for those of us in the ecommerce industry. Since state and local authorities are now able to levy taxes on ecommerce goods, taxation for online stores has become quite complicated. The purpose of this post is to give you some next steps and ideas on implementation if you’re new to the topic and need to know how to get started on tax collection for your ecommerce business.&lt;/p&gt;
&lt;p&gt;Current ecommerce sales tax policy stems from the 2018 U.S. Supreme Court decision South Dakota v. Wayfair, Inc. Since that decision, favoring South Dakota, 30 states have enacted legislation to require ecommerce stores to pay sales tax if they fit the definition of having an ‘economic nexus’, that is, they do enough business in the state to be worth taxing.&lt;/p&gt;
&lt;h3 id=&#34;talk-to-your-tax-accountant&#34;&gt;Talk to your Tax Accountant&lt;/h3&gt;
&lt;p&gt;So the first and most important note is to get your own legal counsel in regards to your taxes. There are many rules and things are changing every month with local and state authorities, so you’ll need reliable counsel on the topic.&lt;/p&gt;
&lt;p&gt;If you’re looking for someone to help, make sure this person has:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Knowledge about product variants. For example, clothing may not be taxed in some areas.&lt;/li&gt;
&lt;li&gt;Familiarity with tax policy in the entire country, and not just one local retail area.&lt;/li&gt;
&lt;li&gt;The ability to help you determine in which states you have a tax ‘nexus’ and need to pay sales tax. For many small businesses, you might only do enough business to pay sales tax in your home state and a few large ones like California or New York.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;research-software&#34;&gt;Research Software&lt;/h3&gt;
&lt;p&gt;Luckily for anyone starting to collect sales tax, there are some very good SaaS (software as a service) systems out there to make tax collection, reporting, and compliance easy. These software systems typically integrate with your ecommerce store by providing the store with the correct tax amount for the sale and collecting information for you on your reports and filing information to get the taxes paid to the correct authorities. Some systems might even file things for you.&lt;/p&gt;
&lt;p&gt;After you’ve worked with your tax accountant on what you need, start looking at the available software. The two main companies End Point has worked with are &lt;a href=&#34;https://www.taxjar.com/&#34;&gt;TaxJar&lt;/a&gt; and &lt;a href=&#34;https://www.avalara.com/us/en/index.html&#34;&gt;Avalara&lt;/a&gt;, though there are a number of other vendors in the market.&lt;/p&gt;
&lt;img src=&#34;/blog/2020/01/ecommerce-sales-tax-primer/image-1.svg&#34; alt=&#34;TaxJar&#34; width=&#34;48%&#34; style=&#34;padding-right: 2%&#34; /&gt;
&lt;img src=&#34;/blog/2020/01/ecommerce-sales-tax-primer/image-2.svg&#34; alt=&#34;Avalara&#34; width=&#34;48%&#34; style=&#34;padding-left: 2%&#34; /&gt;
&lt;h4 id=&#34;why-use-an-automated-tax-solution&#34;&gt;Why use an automated tax solution?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;They automatically update tax rates as they change with local and state regulation.&lt;/li&gt;
&lt;li&gt;They can integrate into your checkout processes via API or plugin to automatically calculate the correct tax for the right location and product.&lt;/li&gt;
&lt;li&gt;They can have product-specific taxes, for things that might not be taxed like food and clothing in certain states.&lt;/li&gt;
&lt;li&gt;They give you end-of-year reports and help with your payments.&lt;/li&gt;
&lt;li&gt;You can customize settings, in case there are some states where you have a ‘nexus’ (are subject to sales tax) and other places where sales tax payments are not required.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;what-to-look-for-in-automated-tax-software&#34;&gt;What to look for in automated tax software:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Choose a reputable company with large brand presence. You don’t want to do anything experimental and unreliable here.&lt;/li&gt;
&lt;li&gt;Pick something with documentation you can understand. You don’t want working with your tax software to be a pain, and you might need to refer to their documentation when configuring it or changing things in the future. Make sure that company speaks your language and you can easily get answers to your questions.&lt;/li&gt;
&lt;li&gt;This company should be able to guide you through all aspects of tax collections, filing and payment. You will legally be liable for taxes on all sales in states where you have nexus, &lt;em&gt;whether you collect the taxes or not&lt;/em&gt;! So make sure the company you select can work with you not only to set up the system, but on an ongoing basis providing support in filing reports and making payments.&lt;/li&gt;
&lt;li&gt;Integration with your platform is a key component of what you choose. Both Avalara and TaxJar have existing plugins for sites running on WordPress, Shopify, BigCommerce, Magento, and others. Keep in mind though that the integration might be different depending on your platform. If you’re on a custom platform, talk to your development team about integrations; they can read the docs and give you an estimate and recommendation for ease of implementation (that’s where we come in for many clients). For Interchange stores, we have integration code for both TaxJar and Avilara to leverage.&lt;/li&gt;
&lt;li&gt;Consider how your inventory or ERP system might be affected. Many of our ecommerce clients sell in-house or over the phone. Consider how your other systems might need to tie into this new tax system.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;implementation&#34;&gt;Implementation&lt;/h3&gt;
&lt;p&gt;So you’ve done the hard part, right? You sorted out what states you need to be compliant with and picked a software solution. Now all you need to do is get it working. This is really where your software consultant, such as us at End Point, would come in and get you to the finish line. The steps to implementation are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up your account and pay for your tax solution software.&lt;/li&gt;
&lt;li&gt;Work with the tax solution provider to set up any required bank accounts or payment channels.&lt;/li&gt;
&lt;li&gt;Configure your settings.&lt;/li&gt;
&lt;li&gt;Share the API key and information with your developer.&lt;/li&gt;
&lt;li&gt;Test the implementation. I recommend you do this with several orders in different scenarios: products that do and don’t have sales tax, locations that do and don’t have tax, locations that tax shipping, etc. Test all the variants you know about.&lt;/li&gt;
&lt;li&gt;Go live with your tax solution on your site.&lt;/li&gt;
&lt;li&gt;Make sure to check back for your reports and filings for later in the year.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Need help picking a system or looking at implementation? &lt;a href=&#34;/contact/&#34;&gt;Call us today&lt;/a&gt; and we can help.&lt;/p&gt;
&lt;h3 id=&#34;other-resources&#34;&gt;Other resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.salestaxinstitute.com/sales_tax_faqs/wayfair-economic-nexus&#34;&gt;https://www.salestaxinstitute.com/sales_tax_faqs/wayfair-economic-nexus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.thebalancesmb.com/how-to-collect-report-and-pay-state-sales-taxes-399043&#34;&gt;https://www.thebalancesmb.com/how-to-collect-report-and-pay-state-sales-taxes-399043&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.bigcommerce.com/blog/ecommerce-sales-tax/&#34;&gt;https://www.bigcommerce.com/blog/ecommerce-sales-tax/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.taxjar.com/guides/intro-to-sales-tax/&#34;&gt;https://www.taxjar.com/guides/intro-to-sales-tax/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Running Magento 2 in Windows with XAMPP</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/"/>
      <id>https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/</id>
      <published>2019-03-22T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/e-commerce-safe.jpg&#34; alt=&#34;Ecommerce&#34; /&gt;&lt;br&gt;&lt;a href=&#34;https://burst.shopify.com/photos/computer-security-lock-and-payment?q=e-commerce&#34;&gt;Photo by Nicole De Khors&lt;/a&gt; · &lt;a href=&#34;https://burst.shopify.com/licenses/shopify-some-rights-reserved&#34;&gt;Burst, Some Rights Reserved&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Magento is an open source ecommerce platform, written in PHP and relying on MySQL/​MariaDB for persistence. According to &lt;a href=&#34;https://trends.builtwith.com/shop&#34;&gt;BuiltWith&lt;/a&gt;, Magento is the third most used platform in ecommerce websites. It began its life in 2008 with its first general release, and a major update (Magento 2) was released in 2015.&lt;/p&gt;
&lt;p&gt;And now, more than three years after, Magento 1 is slowly dying: There won’t be any more quality fixes or security updates from June 2020, and there won’t be extended support for fixes or new payment methods. So the obvious choice will be Magento 2 from now on.&lt;/p&gt;
&lt;p&gt;But is it fully tested yet? Is it stable enough? If we already have a website running with Magento 1, what should we do? Migrating to Magento 2 is not just hitting an “Update” button: Themes are incompatible, most extensions won’t work, and of course, there’s a big set of changes to get familiar with.&lt;/p&gt;
&lt;p&gt;So a good approach might be to get a clean Magento 2 version deployed locally, to look what we need to do to get our website updated and running, test the backend, find where the configuration sections are located, and so on. And many business users, and even some developers like myself, have Microsoft Windows installed on our computers.&lt;/p&gt;
&lt;h3 id=&#34;environment-setup&#34;&gt;Environment setup&lt;/h3&gt;
&lt;p&gt;The environment I used for this testing installation was Windows 10 Professional. As a first step, we’ll need to make sure that &lt;code&gt;localhost&lt;/code&gt; is published in our local hosts file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to the folder &lt;code&gt;%SystemRoot%\system32\drivers\etc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Backup the existing hosts file&lt;/li&gt;
&lt;li&gt;Open a text editor with administrator rights&lt;/li&gt;
&lt;li&gt;Open the hosts file&lt;/li&gt;
&lt;li&gt;Make sure the first line after the commented (#) lines is &lt;code&gt;127.0.0.1 localhost&lt;/code&gt; and the second is &lt;code&gt;::1 localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open a cmd window with administrator rights and run the command &lt;code&gt;ipconfig /flushdns&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we’re ready to install the environment needed to run Magento. I recommend using &lt;a href=&#34;https://www.apachefriends.org/&#34;&gt;XAMPP&lt;/a&gt;, a free Apache distribution for Windows that includes MariaDB, PHP, and Perl in a single package. Magento 2 currently runs with PHP 7.2 and it will not work with newer versions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install XAMPP 7.2 with default settings: &lt;a href=&#34;https://www.apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&#34;&gt;apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Download Magento 2. You will need to register on the website first: &lt;a href=&#34;https://magento.com/tech-resources/download&#34;&gt;magento.com/tech-resources/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create a new “magento” subfolder (or whatever name you prefer) inside the htdocs folder in the XAMPP installation (usually &lt;code&gt;C:\xampp&lt;/code&gt;) and uncompress the Magento 2 archive there.&lt;/li&gt;
&lt;li&gt;Start the XAMPP Control Panel from the Windows start menu. In the “Apache” section, click the “Config” button and, on the menu that appears, select “PHP (php.ini)”. Remove the semicolon before the &lt;code&gt;extension=intl&lt;/code&gt;, &lt;code&gt;extension=soap&lt;/code&gt;, and &lt;code&gt;extension=xsl&lt;/code&gt; texts to enable the intl, soap and xsl extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/xampp-control-panel.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Screenshot of the XAMPP Control Panel with the PHP config menu displayed.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;mysql-and-magento-setup&#34;&gt;MySQL and Magento setup&lt;/h3&gt;
&lt;p&gt;We have all the files in place and the environment ready to start configuring the database and install Magento 2.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Start the Apache and MySQL services from the XAMPP Control Panel. Wait for the green status texts to appear.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a “magento” (or whatever name you prefer) database in MySQL from phpMyAdmin, installed already on XAMPP: &lt;a href=&#34;http://localhost/phpmyadmin&#34;&gt;localhost/phpmyadmin&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the Magento 2 setup from &lt;a href=&#34;http://localhost/magento&#34;&gt;localhost/magento&lt;/a&gt; (replace the “magento” part of the URL with whatever name you have chosen to host Magento). If the setup program requires to do any additional configuration change, do it as instructed. Do a screenshot or save the final page contents for later use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-installation-success.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Example of the page that will be displayed when the Magento 2 installation finishes.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;fixing-known-issues&#34;&gt;Fixing known issues&lt;/h3&gt;
&lt;p&gt;When we finish the installation process, we will have a Magento 2 instance running on our host. But we’re not ready yet! There are a couple known bugs with Magento 2 and XAMPP at the moment I’m writing this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Magento admin page may not work (it shows a blank page): Fix this issue by updating the “isPathInDirectories” function inside the &lt;code&gt;Validator.php&lt;/code&gt; file as instructed in &lt;a href=&#34;https://magento.stackexchange.com/questions/252188/magento-2-2-7-admin-panel-blank-page&#34;&gt;this article.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We might not be able to upload images (like a custom logo or product pictures): Fix this issue by updating the &lt;code&gt;design_config_form.xml&lt;/code&gt; file as shown &lt;a href=&#34;https://community.magento.com/t5/Magento-2-x-Technical-Issues/A-technical-problem-with-the-server-created-an-error-Try-again/m-p/115085#M7549&#34;&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we finish making these changes, we will need to &lt;b&gt;restart the Apache service&lt;/b&gt; from the XAMPP control panel. And that’s it! We should be ready to open the Magento front page, and login into the backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-front-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;An empty home page with the default theme enabled in Magento 2.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-back-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;This is how the Magento 2 back-end dashboard looks like once we login.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;setting-up-the-store-a-roadmap&#34;&gt;Setting up the store: A roadmap&lt;/h3&gt;
&lt;p&gt;The purpose of this article is to get Magento 2 up and running on Windows computer, and that’s what I hope we’ve achieved so far. But just to point in a direction on what to do next, below is my usual roadmap to set up the store, add a few products and publish the catalog on the home page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the default store name and logo from the section Content &amp;gt; Design &amp;gt; Configuration and choose “Edit” in the “Default store view”.&lt;/li&gt;
&lt;li&gt;Set up the default store configuration from the section Stores &amp;gt; Configuration.&lt;/li&gt;
&lt;li&gt;Create the product categories from the section Catalog &amp;gt; Categories. Set up a category name and its dependency from another category, or from the default master category.&lt;/li&gt;
&lt;li&gt;Create/edit the product attributes from the section Stores &amp;gt; Attributes (check the option “visible in storefront” to show the products in the product page). For example, custom attributes for book products would be Author, Publisher&amp;hellip;&lt;/li&gt;
&lt;li&gt;Add the attributes to the default attribute set from the section Stores &amp;gt; Attribute. This is required for the attributes to show up when adding a new product by default.&lt;/li&gt;
&lt;li&gt;Create a set of products from the section Catalog &amp;gt; Products. Set the custom variations for each product from the “Customizable options” section. Set up a short and long description, a custom SKU for each customizable option, and a tax mode.&lt;/li&gt;
&lt;li&gt;Add a widget to the homepage to show the product catalog from the section Content &amp;gt; Homepage &amp;gt; Edit &amp;gt; Show/Hide Editor &amp;gt; Add widget &amp;gt; Products list. Here we can also add any content we like, including HTML tags, images, and third-party content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It’s possible—​and pretty straightforward—​to run a Magento 2 instance in Windows with XAMPP. But this is just a kick-off, a simple overview: there’s a lot more to do if we want to get a full ecommerce website that is ready to scale up and support enterprise-​level traffic, like caching optimization, load balancing, and advanced server-​side monitoring.&lt;/p&gt;
&lt;p&gt;At End Point, we have professionals with a strong background in deploying powerful, reliable, fully responsive, fast, and SEO-​optimized ecommerce websites. We have experience in migrating Magento to newer versions, and even creating full Magento 2 websites from scratch. &lt;a href=&#34;/contact/&#34;&gt;Drop us a line if you want to hear more.&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Currency exchange rates with exchangeratesapi.io</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/07/currency-rates-exchangeratesapi-io/"/>
      <id>https://www.endpointdev.com/blog/2018/07/currency-rates-exchangeratesapi-io/</id>
      <published>2018-07-14T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2018/07/currency-rates-exchangeratesapi-io/38037392525_3501fee918_o-sm.jpg&#34; alt=&#34;city street with currency exchange signs&#34; /&gt;
&lt;p&gt;Several of our clients run ecommerce sites, built on custom software, that allow customers to pay in their choice of a few different currencies.&lt;/p&gt;
&lt;p&gt;Some have set up separate merchant bank accounts for each currency and use separate payment gateway accounts to accept payments natively in each currency. But more commonly they use a payment gateway that allows them to accept payment in several currencies, but receive funds converted into their specified native currency in their merchant bank account.&lt;/p&gt;
&lt;p&gt;In either case, it is common to store prices for products, shipping, etc. in one base currency (say, USD for companies based in the U.S.) and dynamically convert prices for customers. Non-native prices may need to be marked up to cover the cost of conversion into the native currency, depending on the terms of the agreement with the payment gateway or bank.&lt;/p&gt;
&lt;p&gt;Because currency exchange rates change often, and because payment gateways generally do not offer a way to retrieve the exchange rates in advance, we need our own source for ongoing retrieval and storage of our exchange rates (also known as forex rates).&lt;/p&gt;
&lt;p&gt;For a while we were very pleased with &lt;a href=&#34;https://fixer.io/&#34;&gt;Fixer.io&lt;/a&gt;, which was a free service that collected exchange rates from the European Central Bank (ECB) and provided current or historical rates via a simple JSON API. We were sad to find that in March 2018 they deprecated that API and in June 2018 they discontinued it entirely, as &lt;a href=&#34;https://github.com/fixerAPI/fixer#readme&#34;&gt;described to their users&lt;/a&gt;. Fixer.io has transitioned to a paid service and they appear to have improved their operation to retrieve exchange rate data from at least a dozen more sources than the ECB, and to store far more frequent rate updates. Those are nice features, but not something our clients need.&lt;/p&gt;
&lt;p&gt;Fixer.io still offers a free plan that allows 1000 API calls per month but in that plan the exchange rates are all based on the Euro. You need to sign up for at least the $10/month plan to retrieve exchange rates from the other currencies as base. As our customers needed exchange rates based on USD and CAD, the free plan wouldn’t work for us. They offer a limited-time Legacy Plan which would work, but that would just be “kicking the can down the road” until that plan too is discontinued. Their lowest paid plan is inexpensive, but with one of our clients retrieving exchange rates just once per week for only 4 currencies, it felt like overkill to track another paid service and API key, deal with renewals and updated credit card payment details, etc.&lt;/p&gt;
&lt;p&gt;We quickly found another service called &lt;a href=&#34;https://exchangeratesapi.io/&#34;&gt;Exchange Rates API&lt;/a&gt;, run by &lt;a href=&#34;https://github.com/madisvain&#34;&gt;Madis Väin&lt;/a&gt;, which is free to use without any account setup or API key needed. It is also based on the published ECB exchange rates, and uses the same API that Fixer.io did. The &lt;a href=&#34;https://github.com/madisvain/exchangeratesapi&#34;&gt;server side is open source&lt;/a&gt; written in Python using the &lt;a href=&#34;https://github.com/channelcat/sanic&#34;&gt;Sanic&lt;/a&gt; web server library (similar to Flask, but focused on speed and async request handlers).&lt;/p&gt;
&lt;p&gt;The ExchangeRatesAPI.io service runs on &lt;a href=&#34;https://www.heroku.com/&#34;&gt;Heroku&lt;/a&gt; and the open source code is easy to deploy to your own Heroku instance if you want to run your own private service.&lt;/p&gt;
&lt;p&gt;Thanks to Madis Väin for providing it, and to Fixer.io for the good service in the past!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Ecommerce Shakeups: Magento Acquisition and Etsy Rate Increases</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/06/ecommerce-shakeups/"/>
      <id>https://www.endpointdev.com/blog/2018/06/ecommerce-shakeups/</id>
      <published>2018-06-19T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2018/06/ecommerce-shakeups/ecommerce-shakeups.jpg&#34; alt=&#34;Magento, Etsy&#34; /&gt;
&lt;p&gt;If you’ve been paying any attention to much in the ecommerce world, there have been a couple shakeups and transitions that could affect how you look at your ecommerce options these days.&lt;/p&gt;
&lt;h3 id=&#34;adobe-to-acquire-magento&#34;&gt;Adobe to Acquire Magento&lt;/h3&gt;
&lt;p&gt;A few weeks ago, &lt;a href=&#34;http://news.adobe.com/press-release/corporate/adobe-acquire-magento-commerce&#34;&gt;it was announced&lt;/a&gt; Adobe would acquire Magento in a large acquisition. We’ve seen Magento clients come and go. It used to be the case that the Magento Community version was free and open source, but lacking in features, and the Magento Enterprise version was not free and came with many more features but was closed source.&lt;/p&gt;
&lt;p&gt;But, times change, and admittedly I haven’t looked into the current Magento offerings until writing this post. The two current options for Magento are Magento Commerce Starter and Magento Commerce Pro, more details &lt;a href=&#34;https://web.archive.org/web/20180610090249/https://magento.com/trial/plans-comparison&#34;&gt;here&lt;/a&gt;. These plans are not for small potatoes, starting at $2k/mo. I can see how the cost of this is worth it in lieu of paying a full time developer, but this is not a good fit for small businesses just getting started.&lt;/p&gt;
&lt;p&gt;There at not many public details on the acquisition, other than bringing Magento to Adobe’s range of “physical and digital goods across a range of industries, including consumer packaged goods, retail, wholesale, manufacturing, and the public sector”. Only time will tell.&lt;/p&gt;
&lt;h3 id=&#34;etsy-hikes-rates&#34;&gt;Etsy Hikes Rates&lt;/h3&gt;
&lt;p&gt;I am personally connected to the craft industry by way of my own hobby, so I’ve heard rumblings of the changes going on within Etsy with a new CEO throughout the last year. They will be &lt;a href=&#34;https://craftindustryalliance.org/etsy-shuts-down-etsy-wholesale/&#34;&gt;shutting down Etsy wholesale&lt;/a&gt; as of July 31st, 2018, &lt;a href=&#34;https://craftindustryalliance.org/etsy-close-etsy-studio-etsy-manufacturing-focus-sales-growth-continues/&#34;&gt;closing Etsy Studio &amp;amp; Manufacturing&lt;/a&gt; later this year, and last week, they announced increasing transaction fees from 3.5% to 5% which will now also apply to shipping charges. With that money, they will be offering improved tools and marketing efforts. You can read the official announcement &lt;a href=&#34;https://www.etsy.com/seller-news/2018-june-update&#34;&gt;here&lt;/a&gt; and more Q&amp;amp;A from Etsy &lt;a href=&#34;https://www.etsy.com/teams/7722/discussions/discuss/18737155/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are so many overwhelming options when it comes to determining what the best ecommerce solution is for any size of ecommerce business, and whether or not now is the right time to move away from Etsy. There is no one size fits all approach, and the best solution is very dependent on the company size and sales, in-house personnel and their skillset, structural organization, and so much more.&lt;/p&gt;
&lt;p&gt;There are many competing SaaS options like &lt;a href=&#34;https://www.shopify.com/&#34;&gt;Shopify&lt;/a&gt;, &lt;a href=&#34;https://www.bigcommerce.com/&#34;&gt;Big Commerce&lt;/a&gt;, &lt;a href=&#34;https://www.bigcartel.com/&#34;&gt;Big Cartel&lt;/a&gt;, &lt;a href=&#34;https://www.squarespace.com/&#34;&gt;Squarespace&lt;/a&gt;. We’ve helped set up these software as a solution environments for many clients and they can be a great fit for business owners interested in offloading support and maintenance. In addition to SaaS options, &lt;a href=&#34;https://woocommerce.com/&#34;&gt;WooCommerce&lt;/a&gt; in WordPress is the most popular open-source [free] solution, although many plugins and themes for it are not free. There are plenty of other open source ecommerce options out there, running on a web framework/​language (Ruby on Rails, Django, MVC in PHP, etc.) of your choosing.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Like I mentioned above, finding the right solution is very circumstantial. I tend to suggest starting with a low-investment starting point for ecommerce, in whatever shape or form that takes for you, and then incrementally moving up if that’s what you need.&lt;/p&gt;
&lt;p&gt;While Etsy might offer a nice low-investment starting point, more exposure and marketing tools, you ultimately aren’t building your own brand on your own domain. I have seen a handful of people in the craft industry keep their Etsy shop open for a specific audience but grow things on their own domain running on another SaaS platform.&lt;/p&gt;
&lt;p&gt;On the flip side, many of End Point clients have businesses that are further along and the more enterprisey versions or custom software development solutions make great sense for them, with or without in-house development staff.&lt;/p&gt;
&lt;p&gt;If you’re interested in finding out more about setting up a SaaS shop, moving away from Etsy, or understanding your options with Magento, &lt;a href=&#34;/contact/&#34;&gt;contact us&lt;/a&gt; to find out how we can support your business goals.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Instant TLS Upgrades Through Proxy Magic!</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/06/tls-proxy-magic/"/>
      <id>https://www.endpointdev.com/blog/2018/06/tls-proxy-magic/</id>
      <published>2018-06-14T00:00:00+00:00</published>
      <author>
        <name>David Christensen</name>
      </author>
      <content type="html">
        &lt;img alt=&#34;cards&#34; src=&#34;/blog/2018/06/tls-proxy-magic/cards.jpg&#34; /&gt;
&lt;h3 id=&#34;tls-shutdowns-are-real&#34;&gt;TLS shutdowns are real&lt;/h3&gt;
&lt;p&gt;The payment gateways have been warning for years about the impending and required TLS
updates. Authorize.net and PayPal—to name a few—have stopped accepting transaction requests from
servers using TLS 1.0. Despite the many warnings about this (and many delays in the final
enforcement date), some projects are affected by this and payments are coming to a stop, customers
cannot checkout, and e-commerce is at a standstill.&lt;/p&gt;
&lt;p&gt;Ideally, getting to security compliance would include a larger migration to update your underlying
operating system and your application. But a migration and software update can be an expensive
project and in some cases, the business can’t wait weeks while this is done.&lt;/p&gt;
&lt;p&gt;End Point has worked with several clients recently to try to remedy the situation by using a reverse
proxy to fix this and we’ve had good success on getting payments flowing again.&lt;/p&gt;
&lt;h3 id=&#34;what-is-a-proxy&#34;&gt;What is a proxy?&lt;/h3&gt;
&lt;p&gt;A proxy is a mid-point, essentially a digital middleman, moving your data from one place to another.
In two recent client instances, we ended up using nginx (the stack’s webserver) as
the reverse proxy, basically running a separate server for just shuttling requests to/​from the
payment gateway. Since we want to be able to run the gateway in both live and test modes, we use
two separate server definitions in our nginx include, one for each.&lt;/p&gt;
&lt;p&gt;Since the proxy is talking to the gateway in TLS 1.2 the payment gateway is happy. Since the
application can talk http to the proxy running on the same machine, your application is happy.
Since payments are now flowing, the business is happy.&lt;/p&gt;
&lt;h3 id=&#34;why-use-a-proxy&#34;&gt;Why use a proxy?&lt;/h3&gt;
&lt;p&gt;While we always impress on clients the importance of staying up-to-date with their entire stack (operating system,
language, application frameworks), this is not always practical for some sites, whether for cost
reasons or some technical limitations which keep them on a specific library or framework version.
In our case, these clients had been migrated to CentOS 7 already (which supports TLS 1.2), but the
versions of Ruby and Ruby on Rails they were on were too old to be able to use the TLS 1.2 libraries
built-in.&lt;/p&gt;
&lt;p&gt;This can then be a fast way to get payments flowing again, ideally the first step on updating the
site to work with a modern stack. This can also minimize development costs compared to doing a full
migration or complete stack upgrade.&lt;/p&gt;
&lt;h3 id=&#34;steps&#34;&gt;Steps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Run on an operating system version that supports TLS 1.2 natively. In practice for us, this is CentOS 7, but modern Debian, Ubuntu, and other Linux distributions would work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Locate the existing payment gateway URLs for live and test modes. For Authorize.net, this differs depending on if you are connecting to their legacy systems or their modern systems.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up and configure the proxy. We used nginx for this; a later section covers the sample configs with explanations. We created a config file that we can just drop in an &lt;code&gt;/etc/nginx/sites/&lt;/code&gt; directory to be able to quickly set up and deploy the reverse proxies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adjust the configuration of the software to point to alternate payment gateway URLs. If you are using Rails’ ActiveMerchant, this can be accomplished via an application-level configuration override. We provide an example of the overriding of the live/test mode URLs for a Rails application later.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;nginx-sample-config&#34;&gt;nginx sample config&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# apitest.authorize.net
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    listen 127.0.0.1:1337 default_server;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resolver 8.8.8.8;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    access_log /var/log/nginx/sites/authnet-proxies/access_log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error_log /var/log/nginx/sites/authnet-proxies/error_log warn;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    location / {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_pass_header Authorization;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_pass https://apitest.authorize.net$request_uri;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_set_header Host apitest.authorize.net;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_redirect off;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# api.authorize.net
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    listen 127.0.0.1:1338 default_server;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resolver 8.8.8.8;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    access_log /var/log/nginx/sites/authnet-proxies/access_log;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error_log /var/log/nginx/sites/authnet-proxies/error_log warn;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    location / {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_pass_header Authorization;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_pass https://api.authorize.net$request_uri;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_set_header Host api.authorize.net;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        proxy_redirect off;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this file, we are defining a separate server block for each service (test mode and production),
choosing an arbitrarily defined TCP port. Because nginx is speaking TLS 1.2 to the upstream gateway
and we are speaking plain HTTP only to &lt;code&gt;localhost&lt;/code&gt; and not storing any information regarding the request, we
are able to fulfill the PCI DSS requirements.&lt;/p&gt;
&lt;p&gt;A few notes about this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We needed the &lt;code&gt;resolver&lt;/code&gt; line for this to work given our configuration/​setup because we are
referring to the upstream servers by domain name instead of IP addresses; we just used the
easily-memorable Google public DNS resolver servers for this purpose.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need the &lt;code&gt;Authorization&lt;/code&gt; header passed through the proxy, as this is what contains the site
credentials when making a request from ActiveMerchant.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need the &lt;code&gt;Host&lt;/code&gt; header defined explicitly in the server block, as we want to override the
&lt;code&gt;localhost&lt;/code&gt; value that will be sent by the application due to application configuration.
Authorize.net was (understandably) very picky about this being provided and correct.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The port numbers here are arbitrary and just need to match what we configure the application to
connect to; one for the test server and one for production.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This setup will work for multiple development environments running on the same server as long as
they are configured to point to the correct test/​live server. There is nothing inherently
insecure about doing this, as no authorization is shared, it just bounces the connection.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;rails-sample-application-config&#34;&gt;Rails sample application config&lt;/h3&gt;
&lt;p&gt;A configuration file for the Authorize.net CIM gateway, for clients using ActiveMerchant as the base for payment gateway integration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;File&lt;/span&gt;.expand_path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;../boot&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#038&#34;&gt;__FILE__&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;rails/all&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Application&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveMerchant&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Billing&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AuthorizeNetCimGateway&lt;/span&gt;.test_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:1337/xml/v1/request.api&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveMerchant&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Billing&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AuthorizeNetCimGateway&lt;/span&gt;.live_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:1338/xml/v1/request.api&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And another one for the legacy Authorize.net gateway:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;File&lt;/span&gt;.expand_path(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;../boot&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#038&#34;&gt;__FILE__&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;rails/all&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;module&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyApp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Application&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Rails&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveMerchant&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Billing&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AuthorizeNetGateway&lt;/span&gt;.test_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:1337/gateway/transaction.dll&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;ActiveMerchant&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;Billing&lt;/span&gt;::&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;AuthorizeNetGateway&lt;/span&gt;.live_url = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://localhost:1338/gateway/transaction.dll&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ensure that you override the URLs depending on the gateway setup that your merchant account is set up for. Also make sure that your &lt;code&gt;localhost&lt;/code&gt; URLs are using the ports corresponding to the &lt;code&gt;server&lt;/code&gt; blocks set up in nginx to make sure these are proxying to the correct server.&lt;/p&gt;
&lt;p&gt;While this example is using Authorize.net, you could use a similar setup for any payment gateway which allows you to set the URLs.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Hopefully this gives you some ideas about how you can use reverse proxies to upgrade the outgoing
connection strength. If you need assistance getting something like this set up, feel free to &lt;a href=&#34;/contact/&#34;&gt;contact us&lt;/a&gt; for help.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;(Co-authored by &lt;a href=&#34;/blog/authors/elizabeth-garrett-christensen/&#34;&gt;Elizabeth Garrett Christensen&lt;/a&gt;.)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Expert Help with Your SaaS System</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/04/expert-help-with-saas/"/>
      <id>https://www.endpointdev.com/blog/2018/04/expert-help-with-saas/</id>
      <published>2018-04-23T00:00:00+00:00</published>
      <author>
        <name>Elizabeth Garrett Christensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2018/04/expert-help-with-saas/123go.jpg&#34; width=&#34;770&#34; height=&#34;485&#34; alt=&#34;1, 2, 3 … Let’s Go!&#34; /&gt;
&lt;p&gt;Are you thinking about starting a new website and using BigCommerce, Shopify, or WooCommerce? The software-as-a-service offerings on the market today can help you get a great-looking, low-cost ecommerce website up and running quickly, but you may need more help than you first expect.&lt;/p&gt;
&lt;p&gt;In order to get the most out of your site, ecommerce consultants like us at End Point can guide you through setup, customize your site to fit your brand, manage all of your technical requirements, and help drive traffic to your site. This post is an overview of services we offer to clients looking for a SaaS solution.&lt;/p&gt;
&lt;h3 id=&#34;select-the-right-vendor&#34;&gt;Select the Right Vendor&lt;/h3&gt;
&lt;p&gt;Working with an expert consultant can help you make the right choices from the start. Sometimes knowing the level of customization that your project requires can be a real challenge. End Point has experience with everything from out-of-the-box SaaS platforms to large-scale, custom software developments—​we take time to understand your requirements and guide you to the perfect solution for your business.&lt;/p&gt;
&lt;h3 id=&#34;custom-design&#34;&gt;Custom Design&lt;/h3&gt;
&lt;p&gt;Most SaaS offerings include ready-to-use, beautiful design templates, but you’ll often find that they require some tweaking. We can customize your design so that it fits perfectly with the look and feel of your brand, without the time and expense of going through the design process from scratch.&lt;/p&gt;
&lt;h3 id=&#34;user-experience-and-navigation&#34;&gt;User Experience and Navigation&lt;/h3&gt;
&lt;p&gt;Working with an ecommerce consultant that has experience with user behavior, site navigation, and business workflows can help you develop the best way to organize your pages and products. With improved navigation, we can ensure that your users are finding what they’re looking for and getting where they’re going quickly and seamlessly.&lt;/p&gt;
&lt;h3 id=&#34;customizing-your-store&#34;&gt;Customizing Your Store&lt;/h3&gt;
&lt;p&gt;Customizing your store to work with your specific needs, like shipping integrations, 3rd party apps, and APIs can be a challenge. End Point can help you research the solutions already in place, or build custom solutions for your site.&lt;/p&gt;
&lt;h3 id=&#34;email-setup&#34;&gt;Email Setup&lt;/h3&gt;
&lt;p&gt;End Point can help you set up your email on your desktop or in the cloud, and can help you configure and maintain all of your in-house email needs.&lt;/p&gt;
&lt;h3 id=&#34;dns-and-domain-names&#34;&gt;DNS and Domain Names&lt;/h3&gt;
&lt;p&gt;End Point has DevOps experts who can make sure everything with your domain name and DNS changes is handled quickly and correctly during the setup or migration of your site from one provider to another.&lt;/p&gt;
&lt;h3 id=&#34;search-engine-optimization-seo&#34;&gt;Search Engine Optimization (SEO)&lt;/h3&gt;
&lt;p&gt;A solid search engine optimization strategy is critical to the success of your website, and therefore critical to the success of your business. A high Google ranking means more page views—​and more conversions. At End Point, we design and build with SEO in mind from the start to help you get the most value from your online presence.&lt;/p&gt;
&lt;h3 id=&#34;payments-and-merchants&#34;&gt;Payments and Merchants&lt;/h3&gt;
&lt;p&gt;End Point has experience with many major merchant vendors. We can help you find the right merchant, be it PayPal, Stripe, Authorize.net, Braintree, etc. We can make sure that payments are set up, tested, and integrated into your business cash flow. We also have experience with PCI compliance and can help you step through an evaluation or mitigation process and get your system in tip-top shape.&lt;/p&gt;
&lt;h3 id=&#34;project-management&#34;&gt;Project Management&lt;/h3&gt;
&lt;p&gt;Are you trying to wrangle a team of people? Your old vendor and your new one? Do you need help in managing the project? Our project managers are here to help you meet your business goals and get to the finish line on time and within budget.&lt;/p&gt;
&lt;h3 id=&#34;system-migrations&#34;&gt;System Migrations&lt;/h3&gt;
&lt;p&gt;Working with an expert consultant can really help when you are moving from one system to another. We have customizable data export and import scripts and many automated tools to move your content to your new platform and can save you hours of moving products and pages by hand.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Working with a consultant agency can really give your SaaS project the professional edge you’ll need to compete in today’s ecommerce market. We offer consulting packages to fit your budget and the depth of services that you need. Talk to us today about how we could help get your project off the ground.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;(&lt;a href=&#34;/blog/authors/jon-allen/&#34;&gt;Jon Allen&lt;/a&gt; co-authored this article.)&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Vue in Ecommerce: Routing and Persistence</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/03/vue-router-in-ecommerce/"/>
      <id>https://www.endpointdev.com/blog/2018/03/vue-router-in-ecommerce/</id>
      <published>2018-03-01T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/03/vue-router-in-ecommerce/vue-shop.png&#34; alt=&#34;Vue Shop created by Matheus Azzi&#34; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I recently wrote about &lt;a href=&#34;/blog/2018/02/vue-in-ecommerce/&#34;&gt;Vue in Ecommerce&lt;/a&gt; and pointed to a handful of references to get started. Today, I’ll talk about using &lt;a href=&#34;https://router.vuejs.org/en/&#34;&gt;vue-router&lt;/a&gt; in a small ecommerce application, combined with &lt;a href=&#34;https://www.npmjs.com/package/vuex-persist&#34;&gt;vuex-persist&lt;/a&gt; for state storage.&lt;/p&gt;
&lt;p&gt;I forked this &lt;a href=&#34;https://github.com/matheusazzi/shop-vue&#34;&gt;Vue Shop on GitHub&lt;/a&gt; from Matheus Azzi. It was a great starting point for to see how basic component organization and state management might look in a Vue ecommerce application, but it is a single page ecommerce app with no separate page for a product detail, checkout, or static pages, so here I go into some details on routing and persistence in a Vue ecommerce application.&lt;/p&gt;
&lt;h3 id=&#34;vue-router&#34;&gt;Vue Router&lt;/h3&gt;
&lt;p&gt;In looking through the documentation, I don’t see a great elevator pitch on what it is that Vue Router does. If you are new to routing, it’s a tool to map the URL request to the Vue component. Since I’m coming from the Rails perspective, I’m quite familiar with the Ruby on Rails routing from URL pattern matching, constraints, resources to Ruby on Rails controllers and actions. Vue routing via vue-router has some similar elements.&lt;/p&gt;
&lt;p&gt;When you create a basic Vue application via vue-cli, you are given the option to include vue-router:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vue init webpack myapp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;? Project name myapp
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;? Project description A Vue.js project
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;? Author Steph &amp;lt;steph@endpoint.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;? Vue build standalone
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;? Install vue-router? (Y/n) &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you select Yes here, the main differences you’ll see are that an application with vue-router installed will call &lt;code&gt;&amp;lt;router-view/&amp;gt;&lt;/code&gt; to render the view for the current router, instead of a &lt;code&gt;&amp;lt;HelloWorld/&amp;gt;&lt;/code&gt; component, and that a vue-router app will include src/router/index.js with basic routing configuration to your &lt;code&gt;&amp;lt;HelloWorld/&amp;gt;&lt;/code&gt; component.&lt;/p&gt;
&lt;h4 id=&#34;without-routing&#34;&gt;Without Routing&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// App.vue without vue-router
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;template&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;div id=&amp;#34;app&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;img src=&amp;#34;./assets/logo.png&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;HelloWorld/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/template&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import HelloWorld from &amp;#39;./components/HelloWorld&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export default {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name: &amp;#39;App&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  components: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    HelloWorld
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;with-routing&#34;&gt;With Routing&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// App.vue with vue-router
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;template&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;div id=&amp;#34;app&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;img src=&amp;#34;./assets/logo.png&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;router-view/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/template&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export default {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  name: &amp;#39;App&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// src/router/index.js
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Vue from &amp;#39;vue&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Router from &amp;#39;vue-router&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import HelloWorld from &amp;#39;@/components/HelloWorld&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Vue.use(Router)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export default new Router({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  routes: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      path: &amp;#39;/&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      name: &amp;#39;HelloWorld&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      component: HelloWorld
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Moving on, building out an ecommerce app with some basic page set up, not including user accounts, might look like the setup below, which includes basic components and routes for the Home, Cart, Checkout, Receipt, and About page. This simplified set-up doesn’t even include a product detail page, but one could accomplish that with a bit of URL matching via parameters.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// src/router/index.js
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Vue from &amp;#39;vue&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Router from &amp;#39;vue-router&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Home from &amp;#39;@/components/pages/Home&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Receipt from &amp;#39;@/components/pages/Receipt&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import About from &amp;#39;@/components/pages/About&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Cart from &amp;#39;@/components/pages/Cart&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Checkout from &amp;#39;@/components/pages/Checkout&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Vue.use(Router)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export default new Router({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  mode: &amp;#39;history&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  routes: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &amp;#39;/&amp;#39;, component: Home },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &amp;#39;/receipt&amp;#39;, component: Receipt },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &amp;#39;/about&amp;#39;, component: About },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &amp;#39;/cart&amp;#39;, component: Cart },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    { path: &amp;#39;/checkout&amp;#39;, component: Checkout }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h5 id=&#34;history-mode&#34;&gt;History Mode&lt;/h5&gt;
&lt;p&gt;One thing you also might notice is the ‘history’ mode setting above, overriding the default hash mode. You can read about that &lt;a href=&#34;https://router.vuejs.org/en/essentials/history-mode.html&#34;&gt;here&lt;/a&gt;, but it leverages &lt;code&gt;history.pushState&lt;/code&gt; to URL navigation. The history mode override must be combined with server configuration to ensure that non-static asset URL requests (i.e. all of our routes) hit the Vue index.html app in our Vue application, and then renders the component associated with the requested route. I’m using Apache for my Vue application, so I followed the instructions on that documentation for vue-router history mode configuration.&lt;/p&gt;
&lt;h5 id=&#34;using-router-link&#34;&gt;Using &amp;lt;router-link/&amp;gt;&lt;/h5&gt;
&lt;p&gt;One thing you also will want to use throughout your templates is &lt;code&gt;&amp;lt;router-link/&amp;gt;&lt;/code&gt;, instead of hard-coding rigid URL paths. For example, instead of linking to the cart via &lt;code&gt;&amp;lt;a href=&amp;quot;/cart&amp;quot;&amp;gt;Cart&amp;lt;/a&amp;gt;&lt;/code&gt;, you’ll want to link via &lt;code&gt;&amp;lt;router-link to=&amp;quot;cart&amp;quot;&amp;gt;Cart&amp;lt;/router-link&amp;gt;&lt;/code&gt;. This will be resolve to the proper link upon rendering depending on your routing configuration.&lt;/p&gt;
&lt;h3 id=&#34;vuex-persistedstate&#34;&gt;Vuex PersistedState&lt;/h3&gt;
&lt;p&gt;If you are still reading, you’ve now seen the basic configuration for vue-router in an ecommerce application running on Vue. The ecommerce application I forked uses &lt;a href=&#34;https://vuex.vuejs.org/en/intro.html&#34;&gt;vuex&lt;/a&gt;, a state management pattern and library to store the state of our application. What you’ll notice with a shopping cart application, though, is that you need to persist the state of your cart for sessions. Without further setup, every page refresh on your application will result in losing the state of the cart.&lt;/p&gt;
&lt;p&gt;To address this behavior, there are a few options to persist your state in Vue. I chose &lt;a href=&#34;https://www.npmjs.com/package/vuex-persist&#34;&gt;vuex-persist&lt;/a&gt;, which stores the state (for all modules or specific modules) in localStorage. After installing vuex-persist, I modified the following to include the shoppingCart module in my stored state:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// src/store/index.js
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Vue from &amp;#39;vue&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import Vuex from &amp;#39;vuex&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;import VuexPersistence from &amp;#39;vuex-persist&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;// import other stuff
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Vue.use(Vuex)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;const vuexLocal = new VuexPersistence({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  storage: window.localStorage,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  reducer: state =&amp;gt; ({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shoppingCart: state.shoppingCart
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export default new Vuex.Store({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  // ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  plugins: [vuexLocal.plugin]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It’s as simple as that to persist the state of your shopping cart using all the vuex-persist defaults, and there are some alternative Promise-based stores supported if you are interested.&lt;/p&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What’s Next?&lt;/h3&gt;
&lt;p&gt;What’s next in building out your ecommerce application? From here, one might consider adding user authentication &amp;amp; authorization support, as well as support for retrieving and rendering products in the product list view. I hope to address those in upcoming blog posts in this series on Vue in ecommerce.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Vue and Ecommerce: An Introduction</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/02/vue-in-ecommerce/"/>
      <id>https://www.endpointdev.com/blog/2018/02/vue-in-ecommerce/</id>
      <published>2018-02-19T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/02/vue-in-ecommerce/vue-shop.png&#34; alt=&#34;Vue Shop created by Matheus Azzi&#34; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;I speak the domain specific language of ecommerce, Ruby on Rails, JavaScript, and jQuery. Lately, I’ve been getting up to speed on &lt;a href=&#34;https://vuejs.org/&#34;&gt;Vue.js&lt;/a&gt;. I’ve been working on writing a small ecommerce site using Vue.js, because for me, creating an application addressing familiar paradigm in a new technology is a great way to learn.&lt;/p&gt;
&lt;p&gt;Vue.js, initially released about 3 years ago, is a lightweight JS framework that can be adopted incrementally. In the case of my small example site, Vue.js serves the frontend shop content and connects to a decoupled backend that can be run on any platform of choice. On my path to get up to speed on Vue, I found great resources that I wanted to write about before I get into the details of my ecommerce app. Here they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, I started with the &lt;a href=&#34;https://vuejs.org/v2/guide/&#34;&gt;Vue.js documentation&lt;/a&gt;. The documentation is great as a starting point to understand some of the terminology. I often don’t love the documentation that comes with technologies, but I found the &lt;b&gt;Essentials&lt;/b&gt; section in the Vue documentation to be a great launching point.&lt;/li&gt;
&lt;li&gt;After I worked through some of the Vue.js documentation, I did a simple search for “&lt;a href=&#34;https://www.google.com/search?q=vue+jsfiddle&#34;&gt;vue jsfiddle&lt;/a&gt;” and experimented with a few of those fiddles. Having come from a jQuery background (with &lt;a href=&#34;https://mustache.github.io/&#34;&gt;mustache&lt;/a&gt;), I was familiar with how templating and logic inside a template might work, but less familiar with &lt;a href=&#34;https://vuejs.org/v2/guide/instance.html&#34;&gt;the vue instance&lt;/a&gt; and &lt;a href=&#34;https://vuejs.org/v2/guide/class-and-style.html&#34;&gt;binding&lt;/a&gt;, so I targeted jsfiddles with this in mind.&lt;/li&gt;
&lt;li&gt;Next, I looked for resources with Vue + ecommerce specifically. Here’s where I found some good stuff:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://scotch.io/courses/build-an-online-shop-with-vue/introduction&#34;&gt;Scotch.io Course on Vue + Ecommerce&lt;/a&gt;: This online course has a great amount of overlap with the main documentation of Vue, but some of the examples are ecommerce specific, which serves as a good introduction.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://medium.com/@connorleech/standing-on-the-shoulders-of-giants-node-js-vue-2-stripe-heroku-and-amazon-s3-c6fe03ee1118&#34;&gt;Vue + Third Party Integration for Ecommerce&lt;/a&gt;: This Medium article talks about integrating third party tools used with Vue (Stripe, AWS S3, Heroku) to build an ecommerce site. While this post doesn’t go into the nitty gritty details of a Vue application, it does provide many code examples for checkout and payment integration, although noting that &lt;a href=&#34;https://github.com/fromAtoB/vue-stripe-elements&#34;&gt;vue-stripe-elements&lt;/a&gt; is now the module of choice for Stripe integration in Vue.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://vuejsfeed.com/blog/vue-js-storefront-pwa-for-ecommerce&#34;&gt;Vue API App + Magento Backend&lt;/a&gt;: This is a front-end open source Vue application designed to pair with a Magento backend, capitalizing on the popularity of Magento.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://moltin.com/&#34;&gt;Moltin&lt;/a&gt;: This company is providing backend microservices for ecommerce, with Vue.js support (and other technologies) to build out your store.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://travishorn.com/vue-online-store-with-shopping-cart-c072433f8d9e&#34;&gt;Vue + Ecommerce with Vuex&lt;/a&gt;: Here’s another Medium article which features &lt;a href=&#34;https://vuex.vuejs.org/en/&#34;&gt;vuex&lt;/a&gt;, the state management library in Vue, essentially going through examples of state management to manage a shopping cart.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://codepen.io/mjweaver01/pen/yerzox&#34;&gt;CodePen Vue + Ecommerce Example&lt;/a&gt;: Here’s a standalone CodePen example of a Vue shopping cart. While I find this a little hard to parse because there’s a significant amount of code in the example, you can download and experiment with it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/sdras/sample-vue-shop&#34;&gt;Stripe Integration Example&lt;/a&gt;: Here’s a GitHub repo that demonstrates Stripe integration in serverless Vue shop.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/matheusazzi/shop-vue&#34;&gt;Basic Vue Shop on GitHub&lt;/a&gt;: Why reinvent the wheel when someone has already built out product listing and cart functionality? After reviewing all the examples above, this was my choice for a starting point in building out a Vue shop, but it is a basic starter point, without the use of &lt;a href=&#34;https://router.vuejs.org/en/&#34;&gt;vue-router&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;vue-libraries--dependencies&#34;&gt;Vue Libraries / Dependencies&lt;/h3&gt;
&lt;p&gt;You might have noticed I mentioned a few Vue libraries and tools. Here are additional resources for getting up to speed on those dependencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installing Node.js and npm, if you aren’t already familiar with it. &lt;a href=&#34;https://www.quora.com/How-does-npm-compare-to-other-packaging-systems-like-Ruby-gems-and-Pythons-pip&#34;&gt;Here&lt;/a&gt; is a great comparison if you are coming from the Rails space.&lt;/li&gt;
&lt;li&gt;Already mentioned, &lt;a href=&#34;https://vuex.vuejs.org/en/intro.html&#34;&gt;vuex&lt;/a&gt; state management in Vue.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://router.vuejs.org/en/&#34;&gt;vue-router&lt;/a&gt;. In an ecommerce site, you would possibly have routing for the index, product detail, cart, checkout, user account, order detail pages.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/vuejs/vue-cli&#34;&gt;vue-cli&lt;/a&gt;: simple command line interface for scaffolding Vue projects. Think “rails generate &amp;hellip;” for Vue.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/pagekit/vue-resource&#34;&gt;vue-resource&lt;/a&gt;, or a comparable module (&lt;a href=&#34;https://alligator.io/vuejs/rest-api-axios/&#34;&gt;vue-axios&lt;/a&gt;) for making API and AJAX requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;incremental-adoption&#34;&gt;Incremental Adoption&lt;/h3&gt;
&lt;p&gt;While I am writing a Vue ecommerce shop based on the shop-vue GitHub repo mentioned above, in the ecommerce space, we commonly take on incremental ecommerce updates rather than “The Big Rewrite”, as noted in &lt;a href=&#34;/blog/2017/12/enhancing-your-sites-with-vue/&#34;&gt;Greg’s article&lt;/a&gt;. Components that could be introduced incrementally into existing ecommerce applications might include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dynamic marketing elements on homepage or product listing page, e.g. promo tools, “Customers Also Looked at”, “Products Recently Viewed”&lt;/li&gt;
&lt;li&gt;Cart + Checkout process (standalone component)&lt;/li&gt;
&lt;li&gt;Admin elements for data management and processing&lt;/li&gt;
&lt;li&gt;User account section&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Sunsetting Piggybak, A Ruby on Rails Ecommerce Gem</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/02/sunsetting-piggybak/"/>
      <id>https://www.endpointdev.com/blog/2018/02/sunsetting-piggybak/</id>
      <published>2018-02-14T00:00:00+00:00</published>
      <author>
        <name>Steph Skardal</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/02/sunsetting-piggybak/piggybak.jpg&#34; alt=&#34;Screenshot of website for Piggybak: Open Source Ruby on Rails Ecommerce&#34; /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Hi there! I’m Steph, the original creator and not-very-good maintainer
of &lt;a href=&#34;https://github.com/piggybak/piggybak&#34;&gt;Piggybak&lt;/a&gt;, a modular ecommerce
platform written in Ruby on Rails. With the help of End Point, I created
Piggybak after building a custom Ruby on Rails ecommerce application
for one of our clients here at End Point.&lt;/p&gt;
&lt;p&gt;My goal with Piggybak was to create a lightweight, modular ecommerce
platform in the form of a Ruby on Rails gem that could be combined with
other Ruby on Rails gems for quick setup. Over the years, End Point has
had much success working in &lt;a href=&#34;http://www.icdevgroup.org/&#34;&gt;Interchange&lt;/a&gt;, an
ecommerce framework written in Perl.  The web stack has evolved greatly
over the years, as has the capacity for modularity and the ability to
decouple front-end and back-end architecture.&lt;/p&gt;
&lt;p&gt;Fast forward about 4 years after Piggybak was released, and we’ve
decided to retire it. Not only did I leave the maintenance up to End Point
after I left to work as an in-house software engineer for the last couple
of years, but I was also in a position to evaluate using Piggybak as
the base for a custom solution.&lt;/p&gt;
&lt;p&gt;While I think there are some great Ruby on Rails gems to help support
your ecommerce application (see below), one of the main things I realized
was that the modularity in Piggybak often doesn’t suit the needs for the
target audience of custom Ruby on Rails ecommerce platforms.&lt;/p&gt;
&lt;p&gt;These days, here’s my oversimplified categorization of those looking
for ecommerce solutions, divided into two audiences:&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Audience #1: Boilerplate Saas with Theme Customization&lt;/b&gt;&lt;br /&gt;
Those sellers where boilerplate ecommerce solutions work, with simple
product and variant modeling. Shopify, Magento, BigCommerce, WooCommerce
can be decent popular options for that audience, but there are so many
other options out there.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Audience #2: Custom Ecommerce in Ruby on Rails&lt;/b&gt;&lt;br /&gt;
Companies going this route have custom enough needs where a small
team can develop and maintain a custom solution, using Ruby on Rails
efficiently that don’t require them to depend on a pre-existing data
model or business rule.&lt;/p&gt;
&lt;p&gt;Not only did Piggybak suffer from a lack of community involvement, but it
also didn’t suit the needs of the two audiences listed above. Because
it was complex enough written in the form of a Rails gem (engine),
it required a developer with some knowledge to install and customize
further. And because it defined assumptions around the product-variant
data model and business rules for inventory and payment management,
it wasn’t necessarily a good fit for custom ecommerce needs.&lt;/p&gt;
&lt;h3 id=&#34;other-ruby-on-rails-gems&#34;&gt;Other Ruby on Rails Gems&lt;/h3&gt;
&lt;p&gt;While I’m sad to sunset Piggybak, I still believe Rails offers great
options for ecommerce needs, including these popular Ruby on Rails gems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/sferik/rails_admin&#34;&gt;Rails_admin&lt;/a&gt;, active_admin (no longer maintained), &lt;a href=&#34;https://github.com/thoughtbot/administrate&#34;&gt;administrate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/carrierwaveuploader/carrierwave&#34;&gt;Carrierwave&lt;/a&gt;: File attachment management. Has decent third party support.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/plataformatec/devise&#34;&gt;Devise&lt;/a&gt;. User authentication marches on.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/CanCanCommunity/cancancan&#34;&gt;Cancancan&lt;/a&gt;. User authorization.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kaminari/kaminari&#34;&gt;Kaminari&lt;/a&gt;. Pagination.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://rspec.info/&#34;&gt;RSpec&lt;/a&gt;, &lt;a href=&#34;https://github.com/thoughtbot/factory_bot&#34;&gt;FactoryBot&lt;/a&gt;, &lt;a href=&#34;https://github.com/teamcapybara/capybara&#34;&gt;Capybara&lt;/a&gt;, the testing stack I am most familiar with.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Shopify/active_shipping&#34;&gt;ActiveShipping&lt;/a&gt;. Shipping for ecommerce.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/activemerchant/active_merchant&#34;&gt;ActiveMerchant&lt;/a&gt;. Payment gateways for ecommerce.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are wondering if there are any Ruby on Rails ecommerce gems still
out there, you can look at &lt;a href=&#34;https://solidus.io/&#34;&gt;Solidus&lt;/a&gt;,
&lt;a href=&#34;https://spreecommerce.org/&#34;&gt;Spree&lt;/a&gt;, and &lt;a href=&#34;http://www.ror-e.com/&#34;&gt;RoR-E&lt;/a&gt;.
End Point has a long history with Spree, and experience with the other
two platforms, but again, the audience of those businesses choosing
a custom path may not want to be tied to the data models and business
rules adopted by these existing platforms.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>End Point Featured as Global Leader in Ruby on Rails &amp; Ecommerce Development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/12/end-point-recognized-clutch/"/>
      <id>https://www.endpointdev.com/blog/2017/12/end-point-recognized-clutch/</id>
      <published>2017-12-21T00:00:00+00:00</published>
      <author>
        <name>Ben Witten</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2017/12/end-point-recognized-clutch/clutch.png&#34; width=&#34;185&#34; height=&#34;200&#34; style=&#34;float: right; margin: 1em&#34; /&gt;
&lt;p&gt;As the year comes to a close and we reflect on the goals we’ve accomplished and the work that made it all possible, we are proud to announce our inclusion on the Clutch Global Leaders List, encapsulating the 475+ most highly reviewed companies from around the world. We placed as a leader on both Top Ruby on Rails Developers and Top Ecommerce Developers, being the best New York-based firm on the former and in the top three from New York on the latter.&lt;/p&gt;
&lt;p&gt;Achieving the status of a Clutch Global Leader in not just one, but two categories speaks volumes on the effort we’ve put forth in 2017. We couldn’t have hit this milestone without skilled work from End Point staff and the great relationships we’ve nurtured with clients. Looking back on these memorable projects, here are some of the things are clients wrote on our Clutch profile:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I’ve worked with a lot of different service providers. Results can vary across the board. End Point is very transparent about what they’re working on in terms of providing complete details. We don’t have any surprises at the end of the month as a result. They’re very dependable, delivering what they promise every time,” explained the Director of Ecommerce for a tourism marketing company.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;They went on to describe our flexibility and close partnership:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Many of their team members are instrumental in helping us keep doing everything that we need. Our business evolves and changes. They help us make sure that what we’re building is up to snuff, and scales well with our company. We’ve grown considerably since we’ve hired them.”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Another client, the owner of a B2B platform, commented on our project management expertise and what it was like to work with our team:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I am very satisfied with End Point. They are a fantastic company to work with. Their engineers are outstanding, their communication skills are excellent, and the delivery speed is great. I have nothing but good things to say about their company.”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;To learn more about our clients’ experiences and how we’ve achieved the status of a Clutch Global Leader, stay up-to-date with &lt;a href=&#34;https://clutch.co/profile/end-point&#34;&gt;our Clutch profile&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Client Case Study: Vervante—​Publishing, Production and Fulfillment Services</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/08/client-case-study-vervante-publishing/"/>
      <id>https://www.endpointdev.com/blog/2017/08/client-case-study-vervante-publishing/</id>
      <published>2017-08-28T00:00:00+00:00</published>
      <author>
        <name>Greg Hanson</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;a-real-life-scenario&#34;&gt;A real-life scenario&lt;/h3&gt;
&lt;p&gt;The following is a real-life example of services we have provided for one of our clients.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://store.vervante.com/c/affil/index.html&#34;&gt;Vervante Corporation&lt;/a&gt; provides a print on demand and order fulfillment service for thousands of customers, in their case, “Authors”. Vervante needed a way for these authors to keep track of their products. Essentially they needed an &lt;a href=&#34;https://en.wikipedia.org/wiki/Inventory_management_software&#34;&gt;Inventory management system&lt;/a&gt;. So we designed a complete system from the ground up that allows Vervante’s authors many custom functions that simply are not offered in a pre-built package anywhere.&lt;/p&gt;
&lt;p&gt;This is also a good time to mention that &lt;strong&gt;you should always view your web presence, in fact your business itself, as a process&lt;/strong&gt;, not a one time “setup”. Your products will change, your customers will change, the web will change, &lt;strong&gt;everything&lt;/strong&gt; will change. &lt;strong&gt;If you want your business to be successful, you will change.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;some-specifics&#34;&gt;Some Specifics&lt;/h3&gt;
&lt;p&gt;While it is beyond the scope of this case study to describe all of the programs that were developed for Vervante, it will be valuable for the reader to sample just a few of the areas to understand how diverse a single business can be. Here are a few of the functions we have built from scratch, over several years to continue to provide Vervante, their authors, and even their vendors with efficient processes to achieve their daily business needs.&lt;/p&gt;
&lt;h3 id=&#34;requirements&#34;&gt;Requirements&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Author Requirement—​First, in some cases, the best approach to a problem is to use someone else’s solution! Vervante’s authors have large data files that are converted to a product, and then shipped on demand as the orders come in. So we initially provided a custom file transfer process so that customers could directly upload their files to a server we set up for Vervante. Soon Vervante’s rapid growth outpaced the efficacy of this system, so we investigated and determined the most efficient and cost-effective approach was to incorporate a 3rd party service. So we recommended a well known file transfer service and wrote a program to communicate with the file transfer service API. Now a client can easily describe and upload large files to Vervante.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;object class=&#34;BLOG_video_class&#34; classid=&#34;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&#34; codebase=&#34;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0&#34; height=&#34;266&#34; id=&#34;BLOG_video-f735687e5d91b6e5&#34; width=&#34;320&#34;&gt;&lt;param name=&#34;movie&#34; value=&#34;https://www.youtube.com/get_player&#34;/&gt;
&lt;param name=&#34;bgcolor&#34; value=&#34;#FFFFFF&#34;/&gt;
&lt;param name=&#34;allowfullscreen&#34; value=&#34;true&#34;/&gt;
&lt;param name=&#34;flashvars&#34; value=&#34;flvurl=https://redirector.googlevideo.com/videoplayback?requiressl%3Dyes%26id%3Df735687e5d91b6e5%26itag%3D5%26source%3Dblogger%26app%3Dblogger%26cmo%3Dsecure_transport%253Dyes%26cmo%3Dsensitive_content%253Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1508990116%26sparams%3Drequiressl,id,itag,source,ip,ipbits,expire%26signature%3D99FD6C851283A8BC063EB63455AA30622B1A8437.3D4F2FF3F744A46AD71889580CB2708476EA61F4%26key%3Dck2&amp;iurl=https://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Df735687e5d91b6e5%26offsetms%3D5000%26itag%3Dw160%26sigh%3DIi_vgaZOeZV9xdyNjeqlp9MvQPM&amp;autoplay=0&amp;ps=blogger&#34;/&gt;
&lt;embed allowfullscreen=&#34;true&#34; bgcolor=&#34;#FFFFFF&#34; flashvars=&#34;flvurl=https://redirector.googlevideo.com/videoplayback?requiressl%3Dyes%26id%3Df735687e5d91b6e5%26itag%3D5%26source%3Dblogger%26app%3Dblogger%26cmo%3Dsecure_transport%253Dyes%26cmo%3Dsensitive_content%253Dyes%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1508990116%26sparams%3Drequiressl,id,itag,source,ip,ipbits,expire%26signature%3D99FD6C851283A8BC063EB63455AA30622B1A8437.3D4F2FF3F744A46AD71889580CB2708476EA61F4%26key%3Dck2&amp;iurl=https://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Df735687e5d91b6e5%26offsetms%3D5000%26itag%3Dw160%26sigh%3DIi_vgaZOeZV9xdyNjeqlp9MvQPM&amp;autoplay=0&amp;ps=blogger&#34; height=&#34;266&#34; src=&#34;https://www.youtube.com/get_player&#34; type=&#34;application/x-shockwave-flash&#34; width=&#34;320&#34;/&gt;&lt;/object&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&#34;https://drive.google.com/file/d/0B_fTO4RaXomtd3REUWR3U1hERmM/view&#34;&gt;View File Save Process&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Storage Requirement—​The remote storage of these large files caused Vervante a dramatic inefficiency as relates to access times, as they worked daily on these files to format, organize, and create product masters. So we needed to provide Vervante with a local server to act as a file server that was on their local network (LAN), where it could be rapidly accessed and manipulated. This was a challenge, as Vervante did not have IT personnel on site. So we purchased an appropriate server, set up everything in our offices, and shipped the complete server to them! They plugged the server into their local network, and with a long phone call, we had the server up and running and remotely managed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Author Requirement—​On the website, the authors first wanted to see what they had in inventory. Some customers provided Vervante with some product components that needed to be included with a complete product, while others relied on Vervante to build all components of their products. They also requested a way to set minimum inventory stock requirements.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So we built an interface that would allow authors to:&lt;/p&gt;
&lt;p&gt;(a) See their current stock levels for all products,&lt;/p&gt;
&lt;p&gt;(b) View outstanding orders for these items,&lt;/p&gt;
&lt;p&gt;(c) Set minimum inventory levels that they would like to have maintained at the fulfillment warehouse.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;margin-top:20px;&#34;&gt;&lt;a href=&#34;/blog/2017/08/client-case-study-vervante-publishing/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;441&#34; data-original-width=&#34;1211&#34; height=&#34;233&#34; src=&#34;/blog/2017/08/client-case-study-vervante-publishing/image-0.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For example a finished product may consist of a book, a CD, and a DVD. A customer may supply the CD and require Vervante to produce the book and the DVD “on demand” for the product. We created a system that tracked all items at a “base” item level, and then allowed Vervante to “build” products with as many of these “base” items as necessary, to create the final product. The base items could be combined to create an item, and two or more items could be combined to produce yet another item. It is a recursive item inventory system, built from scratch specifically for Vervante.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2017/08/client-case-study-vervante-publishing/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;437&#34; data-original-width=&#34;1227&#34; src=&#34;/blog/2017/08/client-case-study-vervante-publishing/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Vervante Vendor (fulfillment warehouse) Requirement—​Additionally, the &lt;em&gt;fulfillment warehouse&lt;/em&gt; that receives, stores, builds and ships end user products, needed access to this system. They had several needs including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Retrieving pending orders for shipment&lt;/li&gt;
&lt;li&gt;Creating packing / pick slips for the orders&lt;/li&gt;
&lt;li&gt;Create shipping labels for orders&lt;/li&gt;
&lt;li&gt;Manage returns&lt;/li&gt;
&lt;li&gt;Input customer supplied inventory&lt;/li&gt;
&lt;li&gt;Input fulfillment created inventory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In our administrative user interface for the fulfillment house, we developed a series of customer specific processes to address the above needs. Here is a high level example of how a few of the items on the list above are achieved:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- The fulfillment house logs into the user admin first thing in the morning, and prints the outstanding orders.
- The “orders” are formatted similar to a packing slip, and each slip has all line items of the order, and a bar code imprinted on the slip.
- This document is used as a “pick” slip, and is placed in a “pick” basket. The user then goes through the warehouse, gathers the appropriate items, and when complete the order is placed on a feed belt to the shipper location.
- When the basket lands in front of the shipper, that person checks the contents of the basket against the slip, and then uses a bar code scanner to scan the order. That scan triggers a query into our system that returns all applicable shipping data into an Endicia or UPS system. 
- A shipping label is created, and the shipping cost and tracking information is returned to the our system.
- Additionally the inventory is decremented accordingly when the order receives a shipping label and tracking number.
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Requirements: administrative / accounting—​Vervante also needed an administrative / accounting arm, designed to control all of the accounting functions such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recording customers’ fulfillment charges&lt;/li&gt;
&lt;li&gt;Recording customers’ sales (Vervante sells product for the customers as well as fulfilling outside orders)&lt;/li&gt;
&lt;li&gt;Determining fulfillment vendor fees and payments&lt;/li&gt;
&lt;li&gt;Tracking shipping costs&lt;/li&gt;
&lt;li&gt;Monthly billing of all customers&lt;/li&gt;
&lt;li&gt;Monthly payments for all customers.&lt;/li&gt;
&lt;li&gt;Interface with in-house accounting systems and keeping systems in sync&lt;/li&gt;
&lt;li&gt;Tracking and posting outside order transactions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above described processes are just a few of the processes that we developed from scratch, and matched to Vervante’s needs. It is also a tiny portion of their system.&lt;/p&gt;
&lt;h3 id=&#34;last-but-not-least&#34;&gt;Last, but not least&lt;/h3&gt;
&lt;p&gt;Oh, and one other interesting fact: When Vervante first came to us several years ago, they had fewer than 20 customers. Today, they provide order fulfillment and print on demand services for nearly 4000 customers. So when we say to plan ahead for growth, we have experience in that area.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Custom eCommerce Development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/08/custom-ecommerce-development/"/>
      <id>https://www.endpointdev.com/blog/2017/08/custom-ecommerce-development/</id>
      <published>2017-08-28T00:00:00+00:00</published>
      <author>
        <name>Greg Hanson</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;strong&gt;“Custom eCommerce” means different things to different people and organizations.&lt;/strong&gt;
For some eCommerce shopping cart sites that pump out literally hundreds of websites a year, it may mean you get to choose from a dizzying array of “templates” that can set your website apart from others.&lt;/p&gt;
&lt;p&gt;For others, it may be a slightly more involved arrangement where you can “create categories” to group display of your products on your website &amp;hellip; after you have entered your products into a prearranged database schema.&lt;/p&gt;
&lt;p&gt;There are many levels of “custom” out there. Generally speaking, the closer you get to true “custom”, the more accurate the term “development” becomes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It is very important for your business that you decide what fits your needs, and that you match your needs to a platform or company that can provide appropriate services.&lt;/strong&gt; As you can imagine, this will depend entirely on your business.&lt;/p&gt;
&lt;h3 id=&#34;example-scenarios&#34;&gt;Example scenarios&lt;/h3&gt;
&lt;p&gt;For example, a &lt;strong&gt;small one- or two-person business&lt;/strong&gt; that does fulfillment of online orders may be well suited for a pre-built approach, where you pay a monthly fee to simply log into an admin, add your products, and some content, and the company does the rest. It handles all of the “details.”&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;slightly larger company&lt;/strong&gt; that has maybe 5–10 employees, and possibly a staff member with some understanding of websites, may choose to purchase a package that requires more customization and company related input, and perhaps even design or choice of templates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;From this level up, decisions become far more important and complex.&lt;/strong&gt; For example, even though the previously described company &lt;em&gt;may&lt;/em&gt; be perfectly suited with the choice described, if sales are expected to increase dramatically in the near future, or if the company is in a niche market where custom accounting or regulations require specific handling of records, a &lt;em&gt;more advanced approach may be warranted&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;what-we-do&#34;&gt;What we do&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;The purpose of this post is not to give you guidelines as to what sort of website service you should buy, or consultancy you should hire for your company.&lt;/strong&gt; Rather it is to point out some of the types of things that we at End Point do for companies that need a higher level of custom eCommerce development. In fact, the development we do is not limited to eCommerce.&lt;/p&gt;
&lt;p&gt;We offer a &lt;strong&gt;full range of business consultancy and IT development&lt;/strong&gt; services. We can guide you through many areas of your business development. True, we primarily provide services to companies that sell things on the web. But we also provide support for inventory management systems in your warehouses, accounts receivable / payable integration with your websites, management of your POS (point of sale) machines, strategic pricing for seasonal products with expiry dates, and the list goes on.&lt;/p&gt;
&lt;h3 id=&#34;real-life-scenarios&#34;&gt;Real-life scenarios&lt;/h3&gt;
&lt;p&gt;The following is a real-life example of services we have provided for one client.&lt;/p&gt;
&lt;h4 id=&#34;case-study-vervante&#34;&gt;&lt;a href=&#34;/blog/2017/08/client-case-study-vervante-publishing/&#34;&gt;Case Study: Vervante&lt;/a&gt;&lt;/h4&gt;
&lt;h3 id=&#34;consultant-vs-service&#34;&gt;Consultant vs Service&lt;/h3&gt;
&lt;p&gt;Hopefully, the real life scenarios will help serve as an example as to how complex business needs can be, and how using an out of the box “eCommerce” website, will not work in every circumstance. When you find a good business consultant, you will know it. &lt;strong&gt;A consultant will not try to make your business fit into their template, they will listen to you and then assemble and tailor products to fit your business.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Regardless of the state of maturity of your business, very seldom will a single “system” or “website” cover all of your business needs. It will more likely be a collection of systems. Which systems and how they work together is likely what will determine success or failure. The more mature your business, the broader the scope of systems required to support the growing requirements of your business.&lt;/p&gt;
&lt;p&gt;So whether you are a sole proprietor getting started with your business, or you are a CTO tasked with organizing and optimizing the many systems in your organization, understanding what type of service or partner you need, is the first step.
In the future I will spotlight a few other examples of how we have assisted businesses in growing and improving how they do business.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Zero Pricing in Interchange using CommonAdjust</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/06/zero-pricing-in-interchange-using/"/>
      <id>https://www.endpointdev.com/blog/2017/06/zero-pricing-in-interchange-using/</id>
      <published>2017-06-26T00:00:00+00:00</published>
      <author>
        <name>Mark Johnson</name>
      </author>
      <content type="html">
        &lt;p&gt;Product pricing can be quite complex. A typical Interchange catalog will have at least one table in the ProductFiles directive (often products plus either options or variants) and those tables will often have one or more pricing fields (usually price and sales_price). But usually a single, static price isn’t sufficient for more complex needs, such as accessory adjustments, quantity pricing, product grouping&amp;ndash;not to mention promotions, sales, or other conditional features that may change a product’s price for a given situation, dependent on the user’s account or session.&lt;/p&gt;
&lt;p&gt;Typically to handle these variety of pricing possibilities, a catalog developer will implement a CommonAdjust algorithm. CommonAdjust can accommodate all the above pricing adjustments and more, and is a powerful tool (yet can become quite arcane when reaching deeper complexity).  CommonAdjust is enabled by setting the PriceField directive to a non-existent field value in the tables specified in ProductFiles.&lt;/p&gt;
&lt;p&gt;To give an adequate introduction and treatise on CommonAdjust would be at a minimum its own post, and likely a series. There are many elements that make up a CommonAdjust string, and subtle operator nuances that instruct it to operate in differing patterns. It is even possible for elements themselves to return new CommonAdjust atoms (a feature we will be leveraging in this discussion). So I will assume for this writing that the reader is familiar generally with CommonAdjust and we will implement a very simple example to demonstrate henceforth.&lt;/p&gt;
&lt;p&gt;To start, let’s create a CommonAdjust string that simply replaces the typical PriceField setting, and we’ll allow it to accommodate a static sales price:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ProductFiles products
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PriceField 0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CommonAdjust :sale_price ;:price&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above, in words, indicates that our products live in the products table, and we want CommonAdjust to handle our pricing by setting PriceField to a non-existent field (0 is a safe bet not to be a valid field in the products table). Our CommonAdjust string is comprised of two atoms, both of which are settors of type database lookup. In the products table, we have 2 fields: sale_price and price. If sale_price is “set” (meaning a non-zero numeric value or another CommonAdjust atom) it will be used as it comes first in the list. The semicolon indicates to Interchange “if a previous atom set a price by this point, we’re done with this iteration” and, thus, the price field will be ignored.  Otherwise, the next atom is checked (the price field), and as long as the price field is set, it will be used instead.&lt;/p&gt;
&lt;p&gt;A few comments here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The bare colon indicates that the field is not restricted to a particular table. Typically, to specify the field, you would have a value like “products:price” or “variants:price”. But part of the power of ProductFiles holding products in different tables is you can pick up a sku from any of them. And at that point, you don’t know whether you’re looking at a sku from products, variants, or as many additional tables as you’d like to grab products from. But if all of them have a price and sales_price field, then you can pick up the pricing from any of them by leaving the table off. You can think of “:price” as “*:price” where asterisk is “table this sku came from”.&lt;/li&gt;
&lt;li&gt;The only indicator that CommonAdjust recognizes as a terminal value is a non-zero numeric value. The proposed price is coerced to numeric, added on to the accumulated price effects of the rest of the CommonAdjust string (if applicable), and the final value is tested for truth. If it is false (empty, undef, or 0) then the process repeats.&lt;/li&gt;
&lt;li&gt;What happens if &lt;em&gt;none&lt;/em&gt; of the atoms produce a non-zero numeric value? If Interchange reaches the end of the original CommonAdjust string without hitting a set atom, it will relent and return a zero cost.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, we finally introduce our situation, and one that is not at all uncommon. What if I &lt;em&gt;want&lt;/em&gt; a zero price? Let’s say I have a promotion for buy one product, get this other product for free. Typically, a developer would be able to expect to override the prices from the database optionally by leveraging the “mv_price” parameter in the cart. So, let’s adjust our CommonAdjust to accommodate that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CommonAdjust $ ;:sale_price ;:price&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The $ settor in the first atom means “look in the line-item hash for the mv_price parameter and use that, if it’s set”. But as we’ve discussed above, we “set” an atom by making it a non-zero numeric value or another CommonAdjust atom. So if we set mv_price to 0, we’ve gained nothing. CommonAdjust will move on to the next atom (sale_price database settor) and pick up that product’s pricing from the database.  And even if we set that product’s sale_price and price fields to 0, it means &lt;em&gt;everyone&lt;/em&gt; purchasing that item would get it for free (not just our promotion that allows the item to be free with the specific purchase of another item).&lt;/p&gt;
&lt;p&gt;In the specific case of using the $ settor in CommonAdjust, we &lt;em&gt;can&lt;/em&gt; set mv_price to the keyword “free”, and that will allow us to price the item for 0. But this restricts us to &lt;em&gt;only&lt;/em&gt; be able to use $ and mv_price to have a free item. What if the price comes from a complex calculation, out of a usertag settor? Or out of a calc block settor? The special “free” keyword doesn’t work there.&lt;/p&gt;
&lt;p&gt;Fortunately, there is a rarely used CommonAdjust settor that will allow for a 0 price item in a general solution. As I mentioned above, CommonAdjust calculations can themselves return other CommonAdjust atoms, which will then be operated on in a subsequent iteration. This frees us from just the special handling that works on $ and mv_price as such an atom can be returned from &lt;em&gt;any&lt;/em&gt; of the CommonAdjust atoms and work.&lt;/p&gt;
&lt;p&gt;The settor of interest is &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, and according to what documentation there is on it, it was never even intended to be used as a pricing settor! Rather, it was to be a way of redirecting to additional modes for shipping or tax calculations, which can also leverage CommonAdjust for their particular purposes. However, the key to its usefulness here is thus: it does not perform any test on the value tied to it. It is set, untested, into the final result of this call to the chain_cost() routine and returned. And with no test, the fact that it’s Perly false as numeric 0 is irrelevant.&lt;/p&gt;
&lt;p&gt;So building on our current CommonAdjust, let’s leverage &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; to allow our companion product to have a zero cost (assuming it is the 2nd line item in the cart):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[calcn]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $Items-&amp;gt;[1]{mv_price} = &amp;#39;&amp;gt;&amp;gt;0&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[/calcn]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now what happens is, $ in the first atom picks up the value out of mv_price and, because it’s a CommonAdjust atom, is processed in a second iteration. But this CommonAdjust atom is very simple: take the value tied to &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; and return it, untested.&lt;/p&gt;
&lt;p&gt;Perhaps our pricing is more complex than we can (or would like to) support with using $. So we want to write a usertag, where we have the full power of global Perl at our disposal, but we still have circumstances where that usertag may need to return zero-cost items. Using the built-in “free” solution, we’re stuck, short of setting mv_price in the item hash within the usertag, which we may not want to do for a variety of reasons. But using &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, we have no such restriction. So let’s change CommonAdjust:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CommonAdjust $ ;[my-special-pricing] ;:sale_price ;:price&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now instead of setting mv_price in the item, let’s construct [my-special-pricing] to do some heavy lifting:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UserTag my-special-pricing Routine &amp;lt;&amp;lt;EOR
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sub {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # A bunch of conditional, complicated code, but then ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    elsif (buy_one_get_one_test($item)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        # This is where we know this normally priced item is supposed to be
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        # free because of our promotion. Excellent!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        return &amp;#39;&amp;gt;&amp;gt;0&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    # remaining code we don&amp;#39;t care about for this discussion
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EOR&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we haven’t slapped a zero cost onto the line item in a sticky fashion, like we do by setting mv_price. So presumably, above, if the user gets sneaky and removes the “buy one” sku identified by our promotion, our equally clever buy_one_get_one_test() sniffs it out, and the 0 price is no longer in effect.&lt;/p&gt;
&lt;p&gt;For more information on CommonAdjust, see the &lt;a href=&#34;http://www.icdevgroup.org/docs/glossary/price.html&#34;&gt;Custom Pricing section of ‘price’ glossary entry&lt;/a&gt;. And for more examples of leveraging CommonAdjust for quantity and attribute pricing adjustments, see the &lt;a href=&#34;http://www.icdevgroup.org/docs/confs/CommonAdjust.html#CommonAdjust_examples&#34;&gt;Examples section of the CommonAdjust document entry&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Postal code pain and fun</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/05/postal-code-pain-and-fun/"/>
      <id>https://www.endpointdev.com/blog/2017/05/postal-code-pain-and-fun/</id>
      <published>2017-05-10T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img align=&#34;right&#34; src=&#34;/blog/2017/05/postal-code-pain-and-fun/image-0.jpeg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;We do a lot of ecommerce development at End Point. You know the usual flow as a customer: Select products, add to the shopping cart, then check out. Checkout asks questions about the buyer, payment, and delivery, at least. Some online sales are for “soft goods”, downloadable items that don’t require a delivery address. Much of online sales are still for physical goods to be delivered to an address. For that, a postal code or zip code is usually required.&lt;/p&gt;
&lt;h3 id=&#34;no-postal-code&#34;&gt;No postal code?&lt;/h3&gt;
&lt;p&gt;I say &lt;em&gt;usually&lt;/em&gt; because there are some countries that do not use postal codes at all. An ecommerce site that expects to ship products to buyers in one of those countries needs to allow for an empty postal code at checkout time. Otherwise, customers may leave thinking they aren’t welcome there. The more creative among them will make up something to put in there, such as “00000” or “99999” or “NONE”.&lt;/p&gt;
&lt;p&gt;Someone has helpfully assembled and maintains a machine-readable (in Ruby, easily convertible to JSON or other formats) &lt;a href=&#34;https://web.archive.org/web/20201128190754/https://gist.github.com/kennwilson/3902548&#34;&gt;list of the countries that don’t require a postal code&lt;/a&gt;. You may be surprised to see on the list such countries as Hong Kong, Ireland, Panama, Saudi Arabia, and South Africa. Some countries on the list actually do have postal codes but do not require them or commonly use them.&lt;/p&gt;
&lt;h3 id=&#34;do-you-really-need-the-customers-address&#34;&gt;Do you really need the customer’s address?&lt;/h3&gt;
&lt;p&gt;&lt;img align=&#34;right&#34; src=&#34;/blog/2017/05/postal-code-pain-and-fun/image-1.jpeg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;When selling both downloadable and shipped products, it would be nice to not bother asking the customer for an address at all. Unfortunately even when there is no shipping address because there’s nothing to ship, the billing address is still needed if payment is made by credit card through a normal credit card payment gateway—​as opposed to PayPal, Amazon Pay, Venmo, Bitcoin, or other alternative payment methods.&lt;/p&gt;
&lt;p&gt;The credit card &lt;a href=&#34;https://en.wikipedia.org/wiki/Address_Verification_System&#34;&gt;Address Verification System (AVS)&lt;/a&gt; allows merchants to ask a credit card issuing bank whether the mailing address provided matches the address on file for that credit card. Normally only two parts are checked: (1) the street address numeric part, for example, “123” if “123 Main St.” was provided; (2) the zip or postal code, normally only the first 5 digits for US zip codes, and often non-US postal code AVS doesn’t work at all with non-US banks.&lt;/p&gt;
&lt;p&gt;Before sending the address to AVS, validating the &lt;em&gt;format&lt;/em&gt; of postal codes is simple for many countries: 5 digits in the US (allowing an optional &lt;em&gt;-nnnn&lt;/em&gt; for ZIP+4), and 4 or 5 digits in most others countries—​see the Wikipedia &lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_postal_codes&#34;&gt;List of postal codes&lt;/a&gt; in various countries for a high-level view. Canada is slightly more complicated: 6 characters total, alternating a letter followed by a number, formally with a space in the middle, like K1A 0B1 as explained in Wikipedia’s &lt;a href=&#34;https://en.wikipedia.org/wiki/Postal_codes_in_Canada#Components_of_a_postal_code&#34;&gt;components of a Canadian postal code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So most countries’ postal codes can be validated in software with simple regular expressions, to catch typos such as transpositions and missing or extra characters.&lt;/p&gt;
&lt;h3 id=&#34;uk-postcodes&#34;&gt;UK postcodes&lt;/h3&gt;
&lt;p&gt;&lt;img align=&#34;right&#34; src=&#34;/blog/2017/05/postal-code-pain-and-fun/image-2.jpeg&#34; style=&#34;margin: 1em&#34; width=&#34;200&#34;/&gt;The most complicated postal codes I have worked with is the United Kingdom’s, because they can be from 5 to 7 characters, with an unpredictable mix of letters and numbers, normally formatted with a space in the middle. The benefit they bring is that they encode a lot of detail about the address, and it’s possible to catch transposed character errors that would be missed in a purely numeric postal code. The Wikipedia article &lt;a href=&#34;https://en.wikipedia.org/wiki/Postcodes_in_the_United_Kingdom&#34;&gt;Postcodes in the United Kingdom&lt;/a&gt; has the gory details.&lt;/p&gt;
&lt;p&gt;It is common to use a regular expression to validate UK postcodes in software, and many of these regexes are to some degree wrong. Most let through many invalid postcodes, and some disallow valid codes.&lt;/p&gt;
&lt;p&gt;We recently had a client get a customer report of a valid UK postcode being rejected during checkout on their ecommerce site. The validation code was using a regex that is widely copied in software in the wild:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]?[0-9][ABD-HJLN-UW-Z]{2}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(This example removes support for the odd exception GIR 0AA for simplicity’s sake.)&lt;/p&gt;
&lt;p&gt;The customer’s valid postcode that doesn’t pass that test was W1F 0DP, in London, which the Royal Mail website confirms is valid. The problem is that the regex above doesn’t allow for F in the third position, as that was not valid at the time the regex was written.&lt;/p&gt;
&lt;p&gt;This is one problem with being too strict in validations of this sort: The rules change over time, usually to allow things that once were not allowed. Reusable, maintained software libraries that specialize in UK postal codes can keep up, but there is always lag time between when updates are released and when they’re incorporated into production software. And copied or customized regexes will likely stay the way they are until someone runs into a problem.&lt;/p&gt;
&lt;p&gt;The ecommerce site in question is running on the &lt;a href=&#34;http://www.icdevgroup.org/&#34;&gt;Interchange&lt;/a&gt; ecommerce platform, which is based on Perl, so the most natural place to look for an updated validation routine is on CPAN, the Perl network of open source library code. There we find the nice module &lt;a href=&#34;https://metacpan.org/pod/Geo::UK::Postcode&#34;&gt;Geo::UK::Postcode&lt;/a&gt; which has a more current validation routine and a nice interface. It also has a function to format a UK postcode in the canonical way, capitalized (easy) and with the space in the correct place (less easy).&lt;/p&gt;
&lt;p&gt;It also presents us with a new decision: Should we use the basic “valid” test, or the “strict” one? This is where it gets a little trickier. The “valid” check uses a regex validation approach will still let through some invalid postcodes, because it doesn’t know what all the current valid delivery destinations are. This module has a “strict” check that uses a &lt;a href=&#34;https://github.com/mjemmeson/Geo-UK-Postcode-Regex/blob/master/lib/Geo/UK/Postcode/Regex.pm#L664-L3652&#34;&gt;comprehensive list of all the “outcode” data&lt;/a&gt;—​which as you can see if you look at that source code, is extensive.&lt;/p&gt;
&lt;p&gt;The bulkiness of that list, and its short shelf life—​the likelihood that it will become outdated and reject a future valid postcode—​makes strict validation checks like this of questionable value for basic ecommerce needs. Often it is better to let a few invalid postcodes through now so that future valid ones will also be allowed.&lt;/p&gt;
&lt;p&gt;The ecommerce site I mentioned also does in-browser validation via JavaScript before ever submitting the order to the server. Loading a huge list of valid outcodes would waste a lot of bandwidth and slow down checkout loading, especially on mobile devices. So a more lax regex check there is a good choice.&lt;/p&gt;
&lt;h3 id=&#34;when-christmas-comes&#34;&gt;When Christmas comes&lt;/h3&gt;
&lt;p&gt;There’s no Christmas gift of a single UK postal code validation solution for all needs, but there are some fun trivia notes in the Wikipedia page covering &lt;a href=&#34;https://en.wikipedia.org/wiki/Postal_code#Non-geographic_codes&#34;&gt;Non-geographic postal codes&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A fictional address is used by UK Royal Mail for letters to Santa Claus:&lt;/p&gt;
&lt;p&gt;Santa’s Grotto&lt;br&gt;
Reindeerland XM4 5HQ&lt;/p&gt;
&lt;p&gt;Previously, the postcode SAN TA1 was used.&lt;/p&gt;
&lt;p&gt;In Finland the special postal code 99999 is for Korvatunturi, the place where Santa Claus (Joulupukki in Finnish) is said to live, although mail is delivered to the Santa Claus Village in Rovaniemi.&lt;/p&gt;
&lt;p&gt;In Canada the amount of mail sent to Santa Claus increased every Christmas, up to the point that Canada Post decided to start an official Santa Claus letter-response program in 1983. Approximately one million letters come in to Santa Claus each Christmas, including from outside of Canada, and they are answered in the same languages in which they are written. Canada Post introduced a special address for mail to Santa Claus, complete with its own postal code:&lt;/p&gt;
&lt;p&gt;SANTA CLAUS&lt;br&gt;
NORTH POLE  H0H 0H0&lt;/p&gt;
&lt;p&gt;In Belgium bpost sends a small present to children who have written a letter to Sinterklaas. They can use the non-geographic postal code 0612, which refers to the date Sinterklaas is celebrated (6 December), although a fictional town, street and house number are also used. In Dutch, the address is:&lt;/p&gt;
&lt;p&gt;Sinterklaas&lt;br&gt;
Spanjestraat 1&lt;br&gt;
0612 Hemel&lt;/p&gt;
&lt;p&gt;This translates as “1 Spain Street, 0612 Heaven”. In French, the street is called “Paradise Street”:&lt;/p&gt;
&lt;p&gt;Saint-Nicolas&lt;br&gt;
Rue du Paradis 1&lt;br&gt;
0612 Ciel&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;That UK postcode for Santa doesn’t validate in some of the regexes, but the simpler Finnish, Canadian, and Belgian ones do, so if you want to order something online for Santa, you may want to choose one of those countries for delivery. :)&lt;/p&gt;

      </content>
    </entry>
  
</feed>
