<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Kelvin Zhang on Medium]]></title>
        <description><![CDATA[Stories by Kelvin Zhang on Medium]]></description>
        <link>https://medium.com/@0kzh?source=rss-c1f32e521eec------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*_qXfBJPSMoACT2TZD4GjSw.jpeg</url>
            <title>Stories by Kelvin Zhang on Medium</title>
            <link>https://medium.com/@0kzh?source=rss-c1f32e521eec------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 23 Jun 2026 07:55:44 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@0kzh/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How to Build a Raspberry Pi Arcade Machine]]></title>
            <link>https://medium.com/swlh/how-to-build-a-raspberry-pi-arcade-machine-3de4df2894c6?source=rss-c1f32e521eec------2</link>
            <guid isPermaLink="false">https://medium.com/p/3de4df2894c6</guid>
            <category><![CDATA[diy]]></category>
            <category><![CDATA[hardware]]></category>
            <category><![CDATA[gaming]]></category>
            <category><![CDATA[raspberry-pi]]></category>
            <category><![CDATA[arcade]]></category>
            <dc:creator><![CDATA[Kelvin Zhang]]></dc:creator>
            <pubDate>Thu, 28 May 2020 01:52:19 GMT</pubDate>
            <atom:updated>2021-10-18T06:22:03.107Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4sqr-zG_slnRcp60S2O-KA.jpeg" /><figcaption>One of the designs we considered for our arcade’s banner</figcaption></figure><p>Some of my fondest childhood memories were the sleepovers with friends where we would play retro games on an old CRT TV until the sun peeked over the horizon. Video games have been a foundational part of my growing up, and with the rapid development of the gaming industry in the 2000s, I had nearly forgotten about the vintage games that were sitting in my attic collecting dust. It wasn’t until my first internship, where the company I worked for had their own DIY arcade machine, that I was hit by a wave of nostalgia from my childhood.</p><p>Ever since then, it’s been a dream of mine to build my own arcade, and in my first term of university, that’s what a couple of friends and I ended up doing.</p><p>Here’s how we did it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6clTWIUbxQbw6vYD2gg_PA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/596/0*IbKlLVzoi_ABujc8.jpg" /></figure><p>For our design, we decided to go with a full-size arcade machine with two sets of controls for fighting games such as Street Fighter, Super Smash Brothers, and Tekken.</p><p>For our plans, we went with a pre-made plan from TheGeekPub to save time.</p><p>You can find these plans <a href="https://www.thegeekpub.com/product/arcade-cabinet-plans/">here.</a></p><p>On the left is a quick mockup of what we wanted our arcade to look like.</p><p>The design we ended up going with was 27” wide, 66” tall, and 24” deep from front-to-back.</p><p>We wanted to have a keyboard tray in the front for easy configuration, as well as a swinging door where we could store extra materials and tools.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*75V1UR53-jQ04QkEg29aAA.jpeg" /></figure><p>The total cost of the machine was $650 CAD, with the parts costing the most being the monitor, paint, joysticks, the Raspberry Pi, and the wood.</p><p>We thankfully didn’t have to buy any tools since we spent most of our time in our university’s machine shop. Technically, everything here can be done with nothing more than a jigsaw and a hand-drill, but having other tools will significantly expedite the process.</p><p>Here’s a list of all the hardware we used to build the arcade, along with the tools used. For a detailed cost breakdown, with links to buy the parts, check out <a href="https://docs.google.com/spreadsheets/d/1U9Ngz0q1U5A4WASkf_xxyVhe-MG3i99Qqqv3H59QDeM/edit?usp=sharing">this spreadsheet.</a></p><h4>Electronics</h4><ul><li>20 Hikig LED Push Buttons &amp; 2 Joysticks</li><li>2x Easyget Zero-Delay USB Encoder</li><li>27” Acer Monitor</li><li>Raspberry Pi 3</li><li>USB Speaker</li><li>Keyboard</li><li>Mouse</li></ul><h4>Hardware</h4><ul><li>2x MDF wood 3/4” x 49 x 97”</li><li>2x Inset overlay clip hinges</li><li>8 x 5/8-inch Particle board screws</li><li>L-brackets for support</li><li>3” Caster wheels w/ brakes</li><li>Mailbox lock for the cabinet</li><li>1 1/2” Nails</li><li>Wood glue</li><li>10” x 18” Sheet of Plexiglas</li><li>5 cans of flat black primer</li><li>5 cans of black gloss paint</li><li>3 cans of pink spray paint</li><li>1 can of fluorescent yellow spray paint</li></ul><h4>Tools Used</h4><ul><li>Circular saw</li><li>Jigsaw</li><li>Drill press with varying Forstner bits</li><li>Router</li><li>Orbital sander</li><li>Milling machine</li><li>Cordless drill</li><li>Wood clamps</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UqMozG5hQMl24kK0MaSXdw.jpeg" /></figure><figure><img alt="We started by taking the two sheets of MDF and traced out the dimensions for each component. We then cut the sheets using a c" src="https://cdn-images-1.medium.com/max/1024/0*YNIFHTWrzDTclBte.jpg" /></figure><h3>‎Initial Planning</h3><p>We started by taking the two sheets of MDF and traced out the dimensions for each component. We then cut the sheets using a circular saw.</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SG-YgULNbm2Vu0ND.jpg" /></figure><p>‎</p><p>‎</p><h3>Cutting the Sides</h3><p>We traced out an outline for one side of the arcade onto a sheet of MDF, using a compass for rounded edges. After cutting that piece using a jigsaw, we laid it on top of the other and transferred the lines over. After cutting that piece down, we sanded both sides to match.</p><p>‎</p><p>‎</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*L__zjNa2WhXjvs4D.jpg" /></figure><p>‎</p><p>‎</p><h3>Backer Blocks</h3><p>In preparation for assembly, we used scrap wood to cut 1x1&quot; backer blocks for the other pieces to rest on. Although the size/length of the blocks isn’t that important, take great care in making sure they line up on both sides. Ours were a bit off and they caused a lot of issues.</p><p>‎</p><p>‎</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bAeG-_5EntF1Ihu2.jpg" /></figure><p>‎</p><h3>Painting (attempt 1)</h3><p>We started by sanding down our pieces with an orbital sander and increasingly-finer grains of sandpaper. We then applied a matte primer followed by a gloss black finish. The gloss layer turned out to be a mess: it was very difficult to get an even layer with spray cans and even the smallest particle of dust would cause an effect known as orange peeling. In retrospect, we probably should’ve gone with a bucket of paint and a paintbrush.</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_1ovNj-460q5mASlVt5iJQ.jpeg" /></figure><p>‎</p><h3>… attempt 3</h3><p>It took us multiple painstaking attempts of sanding away the previous paint, re-priming, and re-painting before we finally managed to nail a clear finish.</p><p>Using the spray can at an angle helped a lot.</p><p>By the end of the process, we had spent nearly $100 on paint. But hey, at least it looks nice? ¯\_(ツ)_/¯</p><p>‎</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2TQfSoIxmiH7Cby0vLSXKA.jpeg" /></figure><p>‎</p><p>‎</p><h3>Vaporwave Styling</h3><p>To complete the retro look, we created a design consisting of a sun with horizon lines. This pattern was transferred to the side panels with strips of painter’s tape cut with an X-Acto knife.</p><p><em>a e s t h e t i c~</em>‎</p><p>‎</p><p>‎</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Mg3yCto5O964Yrz0SnBZjA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*G-Yh0q2zsQRFkzIA.jpg" /></figure><h3>Wheels</h3><p>To make our arcade easily transportable between locations, we installed a few casters on the bottom panel.</p><p><strong>TIP:</strong> If you complete this step early, you can use it to transport other parts.</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V5pdLXH4CZKKewjK-mjigA.jpeg" /></figure><h3>Speaker</h3><p>To make our speaker, we used a drilling template and went at it using a standard hand drill. We then fastened $5 speakers from the dollar store using a strip of rubber and nails.</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/538/1*SPebs4SsFR3wOuBg_Rl1kQ.jpeg" /></figure><p>‎</p><h3>Cabinet Lock</h3><p>As the front door for our arcade is intended to be used as a cabinet, we milled down a section of the wood in order to fit a standard mailbox lock.</p><p>The locking mechanism works by fastening itself to an L-bracket mounted on the right side of the cabinet.</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_xzJKWXiPW0t7qplMRMXBw.jpeg" /></figure><p>‎</p><p>‎</p><h3>Keyboard Tray</h3><p>We installed a keyboard tray by attaching a pair of rollers to a piece of wood. As we’re new to hardware, we didn’t know the rails had a clever design where you can adjust the height/inset before fastening the screws.</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gsso07c8YMxNFLJPZ5wL6Q.jpeg" /></figure><p>‎</p><h3>Marquee</h3><p>We designed our marquee in Photoshop and got it printed for around $10. We then cut down a piece of Plexiglas and mounted it on the backer blocks.</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*so6V2c0FHaGZn0SN.jpg" /></figure><p>‎</p><h3>Control Panel</h3><p>To make the control panel, we stuck a drilling template to a piece of MDF wood using tape and then used a drill press with a 1 1/8&quot; Forster bit to drill holes for the buttons.</p><p>There are different button layouts available, so choose wisely! We made the mistake of choosing one where the buttons didn’t line up with the arc of our fingers, leading to a clumsier experience.</p><p>‎</p><p>‎</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3yCpQD2ESho1MXhycMEkJA.jpeg" /></figure><h3>Wiring it Up</h3><p>This step is pretty simple: screw the buttons into the control panel, connect each button to a USB encoder, and plug the USB output into the Raspberry Pi.</p><p>Once your controls are ready to go, simply plug in a monitor, keyboard, and mouse into your Raspberry Pi and follow these steps on <a href="https://retropie.org.uk/docs/First-Installation/">Installing RetroPie</a>.</p><p>This should be everything you need to get a game up and running!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aSkLBv-N0psCpDQqTeDXdg.jpeg" /></figure><h3>Putting Everything Together</h3><p>The last thing we need to do is assemble everything. We already have all the components so this shouldn’t take too long.</p><p>Start by laying one side panel flat on its surface, and use nails to fasten the top, middle, bottom, and back panels onto the backer blocks. L-brackets and wood glue can be used for additional support.</p><p>Then, place the other side panel on top, and once again use nails to fix everything in place.</p><p>The front door can be mounted using inset clip hinges.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HrwyymnHme7G42sbUUaCPg.jpeg" /></figure><h3>Installing the Monitor</h3><p>The final step is installing the monitor. The best practice is to buy the monitor first, and to plan the width of your arcade accordingly, leaving around an inch of wiggle room for a 3/4&quot; backer block on each side.</p><p>Alternatively, if the shape of your monitor permits, you can opt to install a panel across the entire width for your monitor to rest on.</p><p>No matter which approach you take, it’s best to have one person align/trace the backer blocks while the other holds the monitor in-place (while being careful not to drop it!)</p><h3>Wrapping It Up</h3><p>That’s it! Here’s what the arcade looks like, assembled and everything!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1001/1*YRNTDwlRVv4Fjv6pUzlDCg.jpeg" /></figure><p>This project wouldn’t have been possible without the help of the folks at Waterloo Engineering’s Machine Student Shop. Thank you for helping us with any questions we had, and teaching us how to use the different tools.</p><h4>Connect with us!</h4><p><a href="https://www.linkedin.com/in/kelvin-zhang/">Kelvin Zhang</a><br><a href="https://www.linkedin.com/in/williamtran10/">William Tran</a><br><a href="https://www.linkedin.com/in/kevinzhang99/">Kevin Zhang</a><br><a href="https://www.linkedin.com/in/frankchen70/">Frank Chen</a></p><p>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3de4df2894c6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/how-to-build-a-raspberry-pi-arcade-machine-3de4df2894c6">How to Build a Raspberry Pi Arcade Machine</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Comprehensive Guide to Validating and Formatting Credit Cards]]></title>
            <link>https://medium.com/hootsuite-engineering/a-comprehensive-guide-to-validating-and-formatting-credit-cards-b9fa63ec7863?source=rss-c1f32e521eec------2</link>
            <guid isPermaLink="false">https://medium.com/p/b9fa63ec7863</guid>
            <category><![CDATA[payments]]></category>
            <category><![CDATA[credit-cards]]></category>
            <category><![CDATA[payment-processing]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[user-experience]]></category>
            <dc:creator><![CDATA[Kelvin Zhang]]></dc:creator>
            <pubDate>Fri, 31 Aug 2018 21:42:46 GMT</pubDate>
            <atom:updated>2018-08-31T21:42:46.543Z</atom:updated>
            <content:encoded><![CDATA[<p>Credit card forms are one of those elements that every online business will have to implement at one point or another, and they can often be a pain point for both developers and users. That being said, it’s crucial that these forms are designed to be user-friendly and intuitive as they are the entry point for paying users: it would be a shame if a company lost a potential customer to a badly-designed payment form, even if they did everything else right.</p><p>The goal of this post is to explain how to implement your own super-awesome credit card form, complete with bug-free validation and formatting. If you’re only here for the code, feel free to scroll to the very bottom to view the final implementation.</p><h3>Table of Contents</h3><ul><li><a href="https://medium.com/p/b9fa63ec7863#7ffa">Types of Credit Cards</a></li><li><a href="https://medium.com/p/b9fa63ec7863#c33a">Validating Cards</a></li><li><a href="https://medium.com/p/b9fa63ec7863#e3b8">Validating the CVV</a></li><li><a href="https://medium.com/p/b9fa63ec7863#e84c">Formatting Cards</a></li><li><a href="https://medium.com/p/b9fa63ec7863#282c">Wrapping It Up</a></li></ul><h3>Types of Credit Cards</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2F331543c19f&amp;dntp=1&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2F331543c19f&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="438" frameborder="0" scrolling="no"><a href="https://medium.com/media/a58c578ba88fb4f8b955bb0857dc5697/href">https://medium.com/media/a58c578ba88fb4f8b955bb0857dc5697/href</a></iframe><p>A couple of things to note here:</p><ul><li>As part of Mastercard’s <a href="https://www.mastercard.us/en-us/issuers/get-support/2-series-bin-expansion.html">2-series expansion</a>, their cards can now begin with 2</li><li>American Express has an unusual CVV that is 4 digits long</li><li><strong>The cards that you support should depend on what your selected payment gateway supports. It’s bad practice to allow unsupported cards to pass through your client-side validation.</strong></li><li>Maestro is a complete pain to deal with and is unsupported by most payment gateways. As such, Maestro will not be covered in the remainder of this post.</li></ul><p>For a list of common payment gateways and the cards they support, see <a href="https://developer.ariasystems.net/UserDocumentation/07Payments_and_Credits/Payment_Gateways/06_Supported_Payment_Gateways_and_Payment_Methods">this list by Aria Systems</a>. For a list of test cards, see <a href="https://github.com/drmonkeyninja/test-payment-cards">this Github repo</a>. For more cards and their RegEx patterns, view <a href="https://gist.github.com/michaelkeevildown/9096cd3aac9029c4e6e05588448a8841">this list on Github.</a></p><h3>Validating Cards</h3><p><strong>Why?</strong></p><ul><li>The user gets immediate feedback on an input error; if an invalid card is entered, they don’t need to click on the submit button, wait for the server to return an error, and then fill out the form again.</li><li>It lessens your server load and prevents invalid requests from counting towards your API rate limit.</li></ul><p><strong>The Luhn Algorithm</strong></p><p>Credit card numbers may look random, but there is actually a hidden meaning behind each group of numbers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/460/0*2sYEHfIj5UHkF3CI" /></figure><p>In the above diagram:</p><ol><li><strong>Major Industry Identifier (MII)</strong> — identifies the industry of the card. See <a href="https://en.wikipedia.org/wiki/ISO/IEC_7812#Major_industry_identifier">here</a> for a list of industries and their corresponding digits.</li><li><strong>Issuer Identification Number (IIN)</strong> — identifies the issuer of the card. American Express starts with 34 or 37, Mastercard starts with 2221–2720 or 51–55, Visa starts with 4. See here for a <a href="https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN)">list</a> of all IIN ranges. This is especially useful for future updates if card issuers ever decide to expand their IIN ranges.</li><li><strong>Account Number</strong> — identifies the customer’s account</li><li><strong>Checksum — makes</strong> sure that the account number is valid</li></ol><p>The Luhn Algorithm determines the validity of a card using the account number and checksum (labels 3 and 4). It works almost like magic:</p><ol><li>From the rightmost digit of your card number, double every other digit.</li><li>If the doubled digit is larger than 9 (ex. 8 * 2 = 16), subtract 9 from the product (16–9 = 7).</li><li>Sum the digits.</li><li>If there is no remainder after dividing by 10 (sum % 10 == 0), the card is valid.</li></ol><p>Using the card from above, here is the Luhn Algorithm in action:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/627/1*g3-dgbQsemeq1FqtOu4SMw.png" /></figure><p>Summing up the last row gives us a value of 90, which is a multiple of 10. This card is valid!</p><p>Here’s a Javascript implementation of the Luhn Algorithm:</p><pre><strong>function</strong> <strong>checkLuhn</strong>(value) {<br>  // remove all non digit characters<br>  <strong>var</strong> value = value.replace(/\D/g, &#39;&#39;);<br>  <strong>var</strong> sum = 0;<br>  <strong>var</strong> shouldDouble = false;<br>  // loop through values starting at the rightmost side<br>  <strong>for</strong> (<strong>var</strong> i = value.length - 1; i &gt;= 0; i--) {<br>    <strong>var</strong> digit = parseInt(value.charAt(i));<br>    <br>    <strong>if</strong> (shouldDouble) {<br>      <strong>if</strong> ((digit *= 2) &gt; 9) digit -= 9;<br>    }<br><br>    sum += digit;<br>    shouldDouble = !shouldDouble;<br>  }<br>  <strong>return</strong> (sum % 10) == 0;<br>}</pre><p><a href="https://codepen.io/kelvinzhang/pen/rrgaxK?editors=1010">Demo</a></p><p>You can view implementations of the Luhn Algorithm in other languages such as Java, Swift, PHP, and Python <a href="https://en.wikipedia.org/wiki/Luhn_algorithm#Implementation_examples">here</a>.</p><p><strong>Checking for Supported Cards</strong></p><p>With reference to the list of cards above and their specifications, we can create a validator based on the RegEx for each specific card.</p><p>The best way to keep track of different cards and their patterns is to store them in an object literal:</p><pre><strong>var</strong> acceptedCreditCards = {<br>  visa: /^4[0-9]{12}(?:[0-9]{3})?$/,<br>  mastercard: /^5[1-5][0-9]{14}$|^2(?:2(?:2[1-9]|[3-9][0-9])|[3-6][0-9][0-9]|7(?:[01][0-9]|20))[0-9]{12}$/,<br>  amex: /^3[47][0-9]{13}$/,<br>  discover: /^65[4-9][0-9]{13}|64[4-9][0-9]{13}|6011[0-9]{12}|(622(?:12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|9[01][0-9]|92[0-5])[0-9]{10})$/,<br>  diners_club: /^3(?:0[0-5]|[68][0-9])[0-9]{11}$/,<br>  jcb: /^(?:2131|1800|35[0-9]{3})[0-9]{11}$/<br>};</pre><p>We can then create a function that tests the inputted value against all RegEx patterns to determine the card’s validity:</p><pre><strong>function</strong> <strong>checkSupported</strong>(value) {<br>  // remove all non digit characters<br>  <strong>var</strong> value = value.replace(/\D/g, &#39;&#39;);<br>  <strong>var</strong> accepted = false;<br>  <br>  // loop through the keys (visa, mastercard, amex, etc.)<br>  Object.keys(acceptedCreditCards).forEach(<strong>function</strong>(key) {<br>    <strong>var</strong> regex = acceptedCreditCards[key];<br>    <strong>if</strong> (regex.test(value)) {<br>      accepted = true;<br>    }<br>  });<br>  <br>  <strong>return</strong> accepted;<br>}</pre><p><a href="https://codepen.io/kelvinzhang/pen/NBmPjX?editors=1010">Demo</a></p><p><strong>Putting it Together</strong></p><p>Finally, we can combine the Luhn algorithm with our supported credit cards checker to complete our magical validation formula.</p><pre><strong>function</strong> <strong>validateCard</strong>(value) {<br>  // remove all non digit characters<br>  <strong>var</strong> value = value.replace(/\D/g, &#39;&#39;);<br>  <strong>var</strong> sum = 0;<br>  <strong>var</strong> shouldDouble = false;<br>  // loop through values starting at the rightmost side<br>  <strong>for</strong> (<strong>var</strong> i = value.length - 1; i &gt;= 0; i--) {<br>    <strong>var</strong> digit = parseInt(value.charAt(i));<br><br>    <strong>if</strong> (shouldDouble) {<br>      <strong>if</strong> ((digit *= 2) &gt; 9) digit -= 9;<br>    }<br><br>    sum += digit;<br>    shouldDouble = !shouldDouble;<br>  }<br>  <br>  <strong>var</strong> valid = (sum % 10) == 0;<br>  <strong>var</strong> accepted = false;<br>  <br>  // loop through the keys (visa, mastercard, amex, etc.)<br>  Object.keys(acceptedCreditCards).forEach(<strong>function</strong>(key) {<br>    <strong>var</strong> regex = acceptedCreditCards[key];<br>    <strong>if</strong> (regex.test(value)) {<br>      accepted = true;<br>    }<br>  });<br>  <br>  <strong>return</strong> valid &amp;&amp; accepted;<br>}</pre><p><a href="https://codepen.io/kelvinzhang/pen/LBoEWK?editors=1010">Demo</a></p><h3>Validating the CVV</h3><p><strong>Why?</strong></p><ul><li>For the exact same reasons as validating credit card numbers: to reduce the number of invalid requests being made to the server.</li></ul><p>The card verification value (CVV) is a set of 3–4 digit numbers on the back of your card and is used for security reasons. Most CVVs are 3 digits, with the exception of Maestro, <a href="http://www.maestrocard.com/gateway/where/where_faqs.html">which may not even require a CVV</a>, and American Express, which has a CVV of 4 digits. Since we’re not supporting Maestro, American Express is the only exception we’ll have to make.</p><p>A CVV doesn’t have anything like a Luhn algorithm to check its validity, so all we have to do is check its length:</p><pre><strong>function</strong> <strong>validateCVV</strong>(creditCard, cvv) {<br>  // remove all non digit characters<br>  <strong>var</strong> creditCard = creditCard.replace(/\D/g, &#39;&#39;);<br>  <strong>var</strong> cvv = cvv.replace(/\D/g, &#39;&#39;);<br>  // american express and cvv is 4 digits<br>  <strong>if</strong> ((acceptedCreditCards.amex).test(creditCard)) {<br>    <strong>if</strong>((/^\d{4}$/).test(cvv))<br>      <strong>return</strong> true;<br>  } <strong>else</strong> <strong>if</strong> ((/^\d{3}$/).test(cvv)) { // other card &amp; cvv is 3 digits<br>    <strong>return</strong> true;<br>  }<br>  <strong>return</strong> false;<br>}</pre><p>Let’s also set a maxlength for it:</p><pre>$(&#39;#cvv&#39;).attr(&#39;maxlength&#39;, 4);</pre><p>We can then integrate this with our credit card validation.</p><p><a href="https://codepen.io/kelvinzhang/pen/zLQMmP?editors=1010">Demo</a></p><p><strong>Toggling the Submit Button</strong></p><p>When the credit card or CVV is invalid, we should disable the submit button because we don’t want invalid form data to be sent to the server. This is as easy as changing the #status element to a submit button and then toggling the disabled prop.</p><p><a href="https://codepen.io/kelvinzhang/pen/bjPOXW?editors=1010">Demo</a></p><h3>Formatting Cards</h3><p><strong>Why?</strong></p><ul><li>The user can see at a glance whether they missed or added an extra character</li><li>It’s easier for the user to go back and change a digit in the case of typos</li></ul><p>There are also a couple of UX goals we want to accomplish when adding auto-formatting:</p><ol><li>We don’t want to disallow the user from typing spaces as they enter their card number</li><li>The user should be able to insert and remove digits before and after a formatted space</li><li>The cursor position should be retained when inserting and removing digits</li><li>When formatting is changed (ex. American Express → Visa), digits should be re-formatted to match the new layout</li></ol><p>With those goals in mind, here are a couple of approaches to formatting cards:</p><p><strong>Input Masking Libraries</strong></p><p>Advantages:</p><ul><li>many libraries to choose from</li><li>easy to implement</li><li>wide variety of pre-built masks</li></ul><p>Disadvantages:</p><ul><li>some are bulky and slow to load</li><li>many have bugs that are hard to fix without modifying the source</li><li>all give you less control over what’s happening</li></ul><p>Here are some of the input masking libraries I’ve tested:</p><p>Plain Javascript:<a href="https://github.com/RobinHerbots/Inputmask"> https://github.com/RobinHerbots/Inputmask</a> (180KB)<br>React:<a href="https://github.com/estelle/input-masking"> https://github.com/estelle/input-masking</a> (5KB)<br>Angular, Ember, Vue:<a href="https://github.com/text-mask/text-mask"> https://github.com/text-mask/text-mask</a> (4KB)</p><p>Regardless of the library used, the logic behind each implementation should be similar:</p><pre>$(&quot;#cc&quot;).on(&quot;input propertychange paste&quot;, <strong>function</strong>() {<br>  <strong>var</strong> value = $(&quot;#cc&quot;).val().replace(/\D/g, &#39;&#39;);<br>  <strong>var</strong> mask;<br>  <strong>if</strong> ((/^3[47]\d{0,13}$/).test(value)) { // American Express<br>    // set mask to 4-6-5<br>  } <strong>else</strong> <strong>if</strong> ((/^3(?:0[0-5]|[68]\d)\d{0,11}$/).test(value)) { // Diner&#39;s Club<br>    // set mask to 4-6-4<br>  } <strong>else</strong> <strong>if</strong> ((/^\d{0,16}$/).test(value)) { // Other Credit Cards<br>    // set mask to 4-4-4-4<br>  }<br>  <br>  // apply your input mask to #cc<br>});</pre><p><a href="https://codepen.io/kelvinzhang/pen/XBQOxK?editors=1010">Here’s an implementation</a> of RobinHerbots’s Inputmask, which I believe to be the best library from the list above. Although it is significantly larger in size and comes with an array of unnecessary features, it allows user input of spaces, inserting/removing digits after spaces, and re-formatting of cards.</p><p>However, the cursor position isn’t retained if the card is re-formatted. If you start out by entering an American Express number (ex. 3782 822463 10005), and then delete the 3 in the beginning, the card is re-formatted correctly but the cursor skips to the end.</p><p>Although this isn’t that big of an issue, I wasn’t happy with it. It seemed that whatever library I used would miss out on at least one of the 4 goals. In the end, I got fed up and decided to implement my own input mask.</p><p><strong>Custom Input Masking</strong></p><p>I wanted my custom input mask to achieve all of the 4 goals while also retaining some quality of life features of input masking libraries, such as limiting the length. At its core, an input mask updates the current input value with the correctly formatted value.</p><p>To accomplish this, I created a function that takes in a card number and outputs the correctly formatted number. In this function, I also limit the length of the input depending on the card type:</p><pre><strong>function</strong> <strong>formatCardNumber</strong>(value) {<br>  // remove all non digit characters<br>  <strong>var</strong> value = value.replace(/\D/g, &#39;&#39;);<br>  <strong>var</strong> formattedValue;<br>  <strong>var</strong> maxLength;<br>  // american express, 15 digits<br>  <strong>if</strong> ((/^3[47]\d{0,13}$/).test(value)) {<br>    formattedValue = value.replace(/(\d{4})/, &#39;$1 &#39;).replace(/(\d{4}) (\d{6})/, &#39;$1 $2 &#39;);<br>    maxLength = 17;<br>  } <strong>else</strong> <strong>if</strong>((/^3(?:0[0-5]|[68]\d)\d{0,11}$/).test(value)) { // diner&#39;s club, 14 digits<br>    formattedValue = value.replace(/(\d{4})/, &#39;$1 &#39;).replace(/(\d{4}) (\d{6})/, &#39;$1 $2 &#39;);<br>    maxLength = 16;<br>  } <strong>else</strong> <strong>if</strong> ((/^\d{0,16}$/).test(value)) { // regular cc number, 16 digits<br>    formattedValue = value.replace(/(\d{4})/, &#39;$1 &#39;).replace(/(\d{4}) (\d{4})/, &#39;$1 $2 &#39;).replace(/(\d{4}) (\d{4}) (\d{4})/, &#39;$1 $2 $3 &#39;);<br>    maxLength = 19;<br>  }<br>  <br>  $(&#39;#cc&#39;).attr(&#39;maxlength&#39;, maxLength);<br>  <strong>return</strong> formattedValue;<br>}</pre><p>The core functionality is achieved by a chain of .replace methods. This allows for the card to be formatted as it is being typed. As such, we’re also not making use of the acceptedCreditCards object that we defined earlier on. The RegEx is modified to match the IIN ranges of each issuer. For example, we can change the mask to 4–6–5 as soon as 34 or 37 is entered (American Express). Furthermore, for the cards that we support, only American Express (15 digits) and Diner’s Club (14 digits) require special formatting.</p><p>We can then update our input to reflect the formatted value:</p><pre>$(&#39;#cc&#39;).on(&#39;input&#39;, <strong>function</strong>() {<br>  <strong>var</strong> value = $(&#39;#cc&#39;).val();<br>  <strong>var</strong> formattedValue = formatCardNumber(value);<br>  $(&#39;#cc&#39;).val(formattedValue);<br>});</pre><p><a href="https://codepen.io/kelvinzhang/pen/ZjdVQy?editors=1010">Demo</a></p><p>For 25 lines of code, this isn’t too bad. It allows user-input of spaces and re-formats credit card numbers. However, deleting any digit or inserting a digit before a space will move the cursor to the end. You also can’t delete spaces.</p><p>All of these bugs happen because updating the value of an input will move the cursor to the end. We can fix this by storing the cursor position and then updating it. There are also two blocks here that adjust the cursor position to allow for the removal of spaces and the insertion of digits before a space.</p><pre>$(&#39;#cc&#39;).on(&#39;input&#39;, <strong>function</strong>() {<br>  <strong>var</strong> node = $(&#39;#cc&#39;)[0]; // vanilla javascript element<br>  <strong>var</strong> cursor = node.selectionStart; // store cursor position<br>  <strong>var</strong> lastValue = $(&#39;#cc&#39;).val(); // get value before formatting<br>  <br>  <strong>var</strong> formattedValue = formatCardNumber(lastValue);<br>  $(&#39;#cc&#39;).val(formattedValue); // set value to formatted<br>  <br>  // keep the cursor at the end on addition of spaces<br>  <strong>if</strong>(cursor === lastValue.length) {<br>    cursor = formattedValue.length;<br>    // decrement cursor when backspacing</pre><pre>// i.e. &quot;4444 |&quot; =&gt; backspace =&gt; &quot;4444|&quot;<br>    <strong>if</strong>($(&#39;#cc&#39;).attr(&#39;data-lastvalue&#39;) &amp;&amp; $(&#39;#cc&#39;).attr(&#39;data-lastvalue&#39;).charAt(cursor - 1) == &quot; &quot;) {<br>      cursor--;<br>    }<br>  }<br><br>  <strong>if</strong> (lastValue !== formattedValue) {<br>    // increment cursor when inserting character before a space</pre><pre>// i.e. &quot;1234| 6&quot; =&gt; &quot;5&quot; typed =&gt; &quot;1234 5|6&quot;<br>    <strong>if</strong>(lastValue.charAt(cursor) == &quot; &quot; &amp;&amp; formattedValue.charAt(cursor - 1) == &quot; &quot;) {<br>      cursor++;<br>    }<br>  }<br>  <br>  // set cursor position<br>  node.selectionStart = cursor;<br>  node.selectionEnd = cursor;<br>  // store last value<br>  $(&#39;#cc&#39;).attr(&#39;data-lastvalue&#39;, formattedValue);<br>});</pre><p><a href="https://codepen.io/kelvinzhang/pen/jpoRMm?editors=1010">Demo</a></p><p>Perfect! Now users can type spaces as they’re entering their card number, insert/remove digits before and after a formatted space, retain the cursor position when a digit is inserted or removed, and, when formatting is changed, re-format the card while preserving the cursor position.</p><h3>Wrapping It Up</h3><p>Now that we have the validation and formatting of credit cards complete, let’s combine them into the ultimate credit card form.</p><p><a href="https://codepen.io/kelvinzhang/pen/GBawbV?editors=1010">Final result</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b9fa63ec7863" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hootsuite-engineering/a-comprehensive-guide-to-validating-and-formatting-credit-cards-b9fa63ec7863">A Comprehensive Guide to Validating and Formatting Credit Cards</a> was originally published in <a href="https://medium.com/hootsuite-engineering">Hootsuite Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>