<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Emdadul Huq</title>
    <description>The latest articles on DEV Community by Emdadul Huq (@emdadul38).</description>
    <link>https://dev.to/emdadul38</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F154391%2Febbd9315-243b-4ed1-ae83-27e65adea59d.jpeg</url>
      <title>DEV Community: Emdadul Huq</title>
      <link>https://dev.to/emdadul38</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emdadul38"/>
    <language>en</language>
    <item>
      <title>Why Shopify Merchants Need Smarter Discount Protection</title>
      <dc:creator>Emdadul Huq</dc:creator>
      <pubDate>Thu, 03 Sep 2026 17:03:33 +0000</pubDate>
      <link>https://dev.to/emdadul38/why-shopify-merchants-need-smarter-discount-protection-4dl1</link>
      <guid>https://dev.to/emdadul38/why-shopify-merchants-need-smarter-discount-protection-4dl1</guid>
      <description>&lt;p&gt;Discounts are one of the most effective tools Shopify merchants use to increase conversions, reward loyal customers, recover abandoned carts, and run seasonal promotions.&lt;/p&gt;

&lt;p&gt;But there is another side to discounting that receives much less attention:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;discount abuse and revenue leakage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creating a discount is easy. Controlling exactly &lt;strong&gt;who can use it, when they can use it, and under what conditions&lt;/strong&gt; is much harder.&lt;/p&gt;

&lt;p&gt;For stores running high-value promotions, VIP campaigns, influencer coupons, wholesale pricing, or limited-time offers, poorly controlled discounts can quietly reduce margins.&lt;/p&gt;

&lt;p&gt;In this article, I want to explore the problem and how we approached it while building &lt;strong&gt;CrispShift Discount Guard&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Traditional Discount Campaigns
&lt;/h2&gt;

&lt;p&gt;Imagine a Shopify merchant creates the following promotion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50% OFF
Minimum order: $700
Eligible customers: VIP only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The business expectation seems straightforward.&lt;/p&gt;

&lt;p&gt;But several questions immediately appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we verify that the customer is actually a VIP?&lt;/li&gt;
&lt;li&gt;What happens if another discount is already applied?&lt;/li&gt;
&lt;li&gt;Can the coupon be combined with other promotions?&lt;/li&gt;
&lt;li&gt;What happens if the code gets shared publicly?&lt;/li&gt;
&lt;li&gt;Should the discount work outside the intended market?&lt;/li&gt;
&lt;li&gt;How do we know when somebody attempted to use it incorrectly?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without additional protection logic, a discount campaign can become difficult to control.&lt;/p&gt;

&lt;p&gt;A promotional rule should therefore be treated as more than just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;discount = 50%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A more realistic model looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;discount = 50%

AND customer.tag = "vip"

AND order.total &amp;gt;= 700

AND campaign.active = true

AND duplicate_discount = false

AND market_allowed = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns a simple discount into a &lt;strong&gt;policy-controlled discount&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Discount Protection as a Validation Layer
&lt;/h2&gt;

&lt;p&gt;One useful way to think about discount protection is as an additional validation layer around the discount engine.&lt;/p&gt;

&lt;p&gt;Instead of immediately asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What discount should this customer receive?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we first ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this checkout eligible to receive this discount?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Conceptually, the flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checkout
   ↓
Campaign Detection
   ↓
Eligibility Validation
   ↓
Protection Rules
   ↓
Discount Calculation
   ↓
Apply / Reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validation layer can evaluate multiple conditions before allowing the discount.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isEligible&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minimumOrderAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requiredCustomerTag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requiredCustomerTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;campaign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blockDiscountStacking&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasExistingDiscount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual implementation in a production Shopify app will naturally be more complex, but the principle remains the same:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;validation should happen before the promotion is trusted.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Protecting Different Types of Discounts
&lt;/h2&gt;

&lt;p&gt;Discount protection should not be limited to product discounts.&lt;/p&gt;

&lt;p&gt;A merchant may need to protect:&lt;/p&gt;

&lt;h3&gt;
  
  
  Product discounts
&lt;/h3&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;20% off selected products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules could include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Selected products only
Minimum quantity
Specific customer segment
Campaign schedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Order discounts
&lt;/h3&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$100 off orders above $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible protections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Minimum subtotal
Customer eligibility
Country restrictions
No conflicting promotions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shipping discounts
&lt;/h3&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Free shipping for VIP customers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer tag = VIP
Market = US
Order total &amp;gt;= $150
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having the same protection model across product, order, and shipping discounts makes the campaign system easier to manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Preventing Duplicate Discount Stacking
&lt;/h2&gt;

&lt;p&gt;One of the most important problems is unintended discount stacking.&lt;/p&gt;

&lt;p&gt;Consider this scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIP50 → 50% OFF
WELCOME20 → 20% OFF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If both discounts are allowed simultaneously, the final discount may become significantly larger than the merchant intended.&lt;/p&gt;

&lt;p&gt;The business expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50% maximum discount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but the checkout behavior might effectively produce something far more aggressive.&lt;/p&gt;

&lt;p&gt;For high-margin businesses this may be acceptable.&lt;/p&gt;

&lt;p&gt;For low-margin businesses, it can be extremely expensive.&lt;/p&gt;

&lt;p&gt;A protection layer therefore needs to detect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;existing discount
+
incoming protected discount
+
combination policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;before determining whether the campaign should continue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Customer Eligibility Should Be Explicit
&lt;/h2&gt;

&lt;p&gt;Not every discount belongs to every customer.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIP50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;might only be intended for customers containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer.tags = ["vip"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The campaign can define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"percentage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eligibility"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"requiredCustomerTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minimumOrderAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validation becomes deterministic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Has VIP tag?
      ↓
     Yes
      ↓
Order &amp;gt;= $700?
      ↓
     Yes
      ↓
Apply Discount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reject Discount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much safer than allowing a campaign to depend only on possession of a coupon code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coupon Codes Are Not Secrets
&lt;/h2&gt;

&lt;p&gt;This is an important principle.&lt;/p&gt;

&lt;p&gt;A merchant may create a coupon like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIP50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and distribute it privately.&lt;/p&gt;

&lt;p&gt;But once a code is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sent through email,&lt;/li&gt;
&lt;li&gt;shared with influencers,&lt;/li&gt;
&lt;li&gt;posted in communities,&lt;/li&gt;
&lt;li&gt;copied from checkout,&lt;/li&gt;
&lt;li&gt;indexed by coupon websites,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;it should no longer be considered secret.&lt;/p&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;knowing the coupon code should not automatically mean being eligible for the promotion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Eligibility rules become the actual security boundary.&lt;/p&gt;

&lt;p&gt;The coupon code is simply an identifier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Detecting Suspicious Checkout Behavior
&lt;/h2&gt;

&lt;p&gt;Another useful layer is checkout behavior monitoring.&lt;/p&gt;

&lt;p&gt;Suppose a protected coupon is intended for a controlled acquisition campaign.&lt;/p&gt;

&lt;p&gt;A system may want to detect signals such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Protected coupon applied
Customer does not satisfy campaign rules
Coupon entered manually
Multiple conflicting discounts detected
Unexpected checkout context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important point is not necessarily to block every unusual event.&lt;/p&gt;

&lt;p&gt;Instead, merchants should be able to define what constitutes unacceptable behavior for a particular campaign.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;protectionRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;blockDuplicateStacking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;requireCustomerTag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;requiredTag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minimumOrderAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;blockInvalidCheckout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different merchants can then apply different levels of protection.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visibility Matters as Much as Blocking
&lt;/h2&gt;

&lt;p&gt;Blocking invalid discounts solves only half the problem.&lt;/p&gt;

&lt;p&gt;The merchant also needs to understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What was blocked?
Why was it blocked?
Which campaign was involved?
How often is it happening?
How much discount value may have been prevented?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For that reason, protection systems should maintain analytics around blocked attempts.&lt;/p&gt;

&lt;p&gt;A simplified event might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"campaign"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VIP50"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing_customer_tag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cartTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discountValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aggregating these events can help merchants identify patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Estimating Protected Revenue
&lt;/h2&gt;

&lt;p&gt;Suppose someone attempts to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50% OFF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;on a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700 cart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the attempt is rejected because the customer is not eligible.&lt;/p&gt;

&lt;p&gt;The prevented discount value would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$700 × 50% = $350
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be represented as &lt;strong&gt;estimated protected revenue&lt;/strong&gt; or &lt;strong&gt;estimated protected discount value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There is an important distinction here.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;not necessarily confirmed revenue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The customer may abandon the checkout after the discount is rejected.&lt;/p&gt;

&lt;p&gt;Therefore analytics should clearly distinguish between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Blocked discount value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Confirmed paid-order revenue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good analytics should never blur these two metrics.&lt;/p&gt;




&lt;h2&gt;
  
  
  Campaign Health Is Another Important Problem
&lt;/h2&gt;

&lt;p&gt;Discount protection is not only about malicious or unauthorized usage.&lt;/p&gt;

&lt;p&gt;Operational failures can also create problems.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Campaign active in database
        ↓
Discount engine failed to synchronize
        ↓
Merchant assumes campaign is working
        ↓
Checkout behaves differently
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A discount management system should therefore provide visibility into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Campaign Status
Engine Status
Synchronization Status
Protection Activity
Blocked Attempts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of simply showing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a more useful operational state might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Campaign: Active
Engine: Synced
Protection: Enabled
Last Sync: Successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes troubleshooting significantly easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example: Protected VIP Campaign
&lt;/h2&gt;

&lt;p&gt;Let's combine everything.&lt;/p&gt;

&lt;p&gt;A merchant wants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50% OFF for VIP customers
Minimum order: $700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Protection configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"campaign"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VIP50"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"percentage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minimumOrderAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"requiredCustomerTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blockDuplicateStacking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blockInvalidCheckout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now consider three customers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer A
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIP tag: Yes
Order: $900
Existing discount: No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ Discount allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customer B
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIP tag: No
Order: $900
Existing discount: No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Discount blocked
Reason: Customer eligibility
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customer C
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VIP tag: Yes
Order: $900
Existing discount: Yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Discount blocked
Reason: Duplicate discount stacking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coupon is identical in all three checkouts.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;context&lt;/strong&gt; determines whether it should be accepted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building CrispShift Discount Guard
&lt;/h2&gt;

&lt;p&gt;These problems motivated us to build &lt;strong&gt;CrispShift Discount Guard&lt;/strong&gt; for Shopify.&lt;/p&gt;

&lt;p&gt;The goal is not simply to create another discount generator.&lt;/p&gt;

&lt;p&gt;The goal is to give merchants an additional control layer around their promotions.&lt;/p&gt;

&lt;p&gt;The system focuses on areas such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Protected discount campaigns
Customer eligibility
Minimum order validation
Product eligibility
Country and market rules
Campaign scheduling
Duplicate discount protection
Checkout validation
Blocked-attempt analytics
Estimated protected revenue
Campaign and engine health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The broader idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A promotion should behave like a business rule, not just a coupon code.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A Better Mental Model for Discounts
&lt;/h2&gt;

&lt;p&gt;Traditionally, we might represent a promotion as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Coupon → Discount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more complex Shopify stores, I think a better model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Coupon
   ↓
Campaign
   ↓
Eligibility
   ↓
Protection
   ↓
Validation
   ↓
Discount
   ↓
Analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives merchants much greater control over promotional behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Discount systems become more complicated as a Shopify store grows.&lt;/p&gt;

&lt;p&gt;A small store may be perfectly comfortable with simple coupon codes.&lt;/p&gt;

&lt;p&gt;But stores running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;influencer campaigns,&lt;/li&gt;
&lt;li&gt;affiliate promotions,&lt;/li&gt;
&lt;li&gt;VIP programs,&lt;/li&gt;
&lt;li&gt;wholesale pricing,&lt;/li&gt;
&lt;li&gt;high-value discounts,&lt;/li&gt;
&lt;li&gt;seasonal campaigns,&lt;/li&gt;
&lt;li&gt;customer-segment promotions,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;need stronger controls.&lt;/p&gt;

&lt;p&gt;The most important principle is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possessing a discount code should not automatically establish eligibility for the discount.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Treat discounts as policies.&lt;/p&gt;

&lt;p&gt;Validate the checkout context.&lt;/p&gt;

&lt;p&gt;Track rejected attempts.&lt;/p&gt;

&lt;p&gt;Monitor campaign health.&lt;/p&gt;

&lt;p&gt;And give merchants enough visibility to understand exactly how their promotions are being used.&lt;/p&gt;

&lt;p&gt;That is the direction we're taking with &lt;strong&gt;CrispShift Discount Guard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you're building Shopify apps or working with complex discount strategies, I'd be interested to hear how you're approaching discount abuse, eligibility validation, and coupon leakage.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;CrispShift Discount Guard:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://apps.shopify.com/discount-guard-pro" rel="noopener noreferrer"&gt;https://apps.shopify.com/discount-guard-pro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Original article:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://medium.com/@emdadul225/why-shopify-merchants-need-smarter-discount-protection-472de74165f9" rel="noopener noreferrer"&gt;https://medium.com/@emdadul225/why-shopify-merchants-need-smarter-discount-protection-472de74165f9&lt;/a&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>couponabuse</category>
      <category>couponmisuse</category>
      <category>discountguard</category>
    </item>
    <item>
      <title>How to Install Pyenv on Ubuntu 24.04</title>
      <dc:creator>Emdadul Huq</dc:creator>
      <pubDate>Thu, 25 Jul 2024 06:07:36 +0000</pubDate>
      <link>https://dev.to/emdadul38/how-to-install-pyenv-on-ubuntu-2404-5807</link>
      <guid>https://dev.to/emdadul38/how-to-install-pyenv-on-ubuntu-2404-5807</guid>
      <description>&lt;p&gt;Due to the slowness of repositories or even lack thereof being updated with specific versions of Python, I’ve decided to move some of my environments over to Pyenv to allow me to dynamically install and configure Python specifically for my environment. As it turns out this will also allow VS Code to allow me to choose the version of Python that I’d like to use when testing. So, here’s a quick guide to installing Pyenv on Ubuntu 24.04&lt;/p&gt;

&lt;h3&gt;
  
  
  Update and install dependencies
&lt;/h3&gt;

&lt;p&gt;we need to ensure our package cache is updated, and then install the dependencies to download, and build python from pyenv.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install make build-essential 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Install Pyenv using pyenv-installer
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

curl https://pyenv.run | bash


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Configure user profile to use pyenv
&lt;/h3&gt;

&lt;p&gt;Ensure the following is in your ~/.bash_profile (if exists), ~/.profile (for login shells), ~/.bashrc (for interactive shells), or ~/.zshrc&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Load pyenv automatically by appending
# the following to 
# ~/.bash_profile if it exists, otherwise ~/.profile (for login shells)
# and ~/.bashrc (for interactive shells) :

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] &amp;amp;&amp;amp; export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Optionally enable pyenv-virtualenv&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

eval "$(pyenv virtualenv-init -)"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Reload your profile
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

source ~/.profile


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Install python using pyenv
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

pyenv install &amp;lt;version&amp;gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Set your python version
&lt;/h3&gt;

&lt;p&gt;pyenv shell   — select just for current shell session&lt;br&gt;
pyenv local   — automatically select whenever you are in the current directory (or its subdirectories)&lt;br&gt;
pyenv global   — select globally for your user account&lt;/p&gt;
&lt;h3&gt;
  
  
  Validate your installation of python
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

python3 --version


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Reference &lt;a href="https://github.com/pyenv/pyenv?tab=readme-ov-file#simple-python-version-management-pyenv" rel="noopener noreferrer"&gt;Pyenv&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwulh2cnwhhyxo6236dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwulh2cnwhhyxo6236dz.png" alt="Sample"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pyenv</category>
      <category>pythonversionmanagement</category>
      <category>versioning</category>
      <category>multipleversionsofpython</category>
    </item>
    <item>
      <title>How to configure an Nginx server on Ubuntu 22.04 with Django and uWSGI</title>
      <dc:creator>Emdadul Huq</dc:creator>
      <pubDate>Sat, 09 Sep 2023 09:43:41 +0000</pubDate>
      <link>https://dev.to/emdadul38/how-to-configure-an-nginx-server-on-ubuntu-2204-with-django-and-uwsgi-1med</link>
      <guid>https://dev.to/emdadul38/how-to-configure-an-nginx-server-on-ubuntu-2204-with-django-and-uwsgi-1med</guid>
      <description>&lt;h2&gt;
  
  
  1. Update Your Server
&lt;/h2&gt;

&lt;p&gt;It’s always a good idea to start by updating your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the latest and greatest packages installed on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use a Virtual Environment for Python
&lt;/h2&gt;

&lt;p&gt;The venv module allows you to create virtual Python environments. This allows you to isolate installed packages from the system.&lt;/p&gt;

&lt;p&gt;After installing venv, make a directory to house your virtual environments, and then create a virtual environment named env using venv. You can call your virtual environment whatever you prefer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install python3-venv
mkdir Workstations &amp;amp;&amp;amp; cd Workstations
python3 -m venv env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now activate your virtual environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the name of your virtual environment in the parenthesis as a prefix to your env&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q3id7qbni7m0b20ew3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q3id7qbni7m0b20ew3w.png" alt="Virtual environment configuration" width="790" height="148"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Create a Django Project
&lt;/h2&gt;

&lt;p&gt;Now we install the Django web framework using the pip package installer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next let’s create a Django project with django-admin which should be on your path by default. Feel free to choose a project name that suites you. For example we will use the name of our project called mysite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django-admin startproject mysite
cd mysite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now test our project using following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py runserver 0.0.0.0:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a browser, you should be able to visit mysite:8000 and see the default Django landing page, if not, you might you be see a error message like this:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1raq5ul9t6lj0o7zl4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1raq5ul9t6lj0o7zl4r.png" alt="Error message on show without add the domain " width="800" height="605"&gt;&lt;/a&gt;&lt;br&gt;
if you see this error message, add your domain name to the &lt;code&gt;mysite/settings.py&lt;/code&gt; file.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquamcn6dd28x7ziyvh6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fquamcn6dd28x7ziyvh6b.png" alt="Added domain name on Allowed hosts section" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try to visit &lt;code&gt;mysite.com:8000&lt;/code&gt; again and this time you will see the Django landing page.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Get Started with uWSGI
&lt;/h2&gt;

&lt;p&gt;First we need to install the web server gateway interface (WSGI). In this case, we will be using &lt;code&gt;uWSGI&lt;/code&gt;. You will also need the development packages for your version of Python to be installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install python3.8-dev
sudo apt-get install gcc
pip install uwsgi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will see to send requests from the client to Nginx which will pass them to a socket that will hand them off to uWSGI before finally being given to Django.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcqnvgehqghwzm0gwtvgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcqnvgehqghwzm0gwtvgl.png" alt="working flow from client to django using uWSGI" width="800" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;server-test.py&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World!"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test our uWSGI directly talking to python with the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uwsgi --http :8000 --wsgi-file server-test.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a browser, you should be able to visit mysite.com:8000 and see the test &lt;strong&gt;Hello World!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If this works for you, we have demonstrated that uWSGI is able to pass requests to Python.&lt;/p&gt;

&lt;p&gt;Now we can similarly serve the Django Project with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uwsgi --http :8000 --module mysite.wsgi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this works because the path mysite/wsgi.py exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Configure the Nginx Web Server
&lt;/h2&gt;

&lt;p&gt;Install Nginx with apt-get as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now with Nginx installed, you will be able to visit the default &lt;code&gt;Welcome to nginx!&lt;/code&gt; page in your browser at &lt;a href="http://mysite.com" rel="noopener noreferrer"&gt;http://mysite.com&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Let’s tell Nginx about our Django project by creating a configuration file at &lt;code&gt;/etc/nginx/sites-available/mysite.conf&lt;/code&gt;. Change the highlighted lines to suite your needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# the upstream component nginx needs to connect to
upstream django {
    server unix:///home/emdad/mysite/mysite.sock;
}
# configuration of the server
server {
    listen      80;
    server_name mysite.com www.mysite.com;
    charset     utf-8;
    # max upload size
    client_max_body_size 75M;
    # Django media and static files
    location /media  {
        alias /home/emdad/mysite/media;
    }
    location /static {
        alias /home/emdad/mysite/static;
    }
    # Send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /home/emdad/mysite/uwsgi_params;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to create the &lt;code&gt;/home/emdad/mysite/uwsgi_params&lt;/code&gt; file highlighted above on line 26 as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;
uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;
uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we can publish our changes by creating a symbolic link from &lt;code&gt;sites-available&lt;/code&gt; to &lt;code&gt;sites-enabled&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ln -s /etc/nginx/sites-available/mysite.conf /etc/nginx/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We must edit the &lt;code&gt;mysite/settings.py&lt;/code&gt; file to explicitly tell Nginx where our static files reside.&lt;/p&gt;

&lt;p&gt;First add import os at the very beginning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"""
Django settings for mysite project.
Generated by 'django-admin startproject' using Django 3.1.2.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and STATIC_ROOT = os.path.join(BASE_DIR, "static/") at the very end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these changes in place, we can now tell Django to put all static files in the static folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py collectstatic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the Nginx server to apply changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo /etc/init.d/nginx restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point if you visit &lt;a href="http://mysite.com" rel="noopener noreferrer"&gt;http://mysite.com&lt;/a&gt; a browser, you will probably see an Nginx 502 Bad Gateway error,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir media
wget https://www.google.com/logos/doodles/2020/thank-yo…c-transportation-workers-6753651837108759-2xa.gif -O media/media.gif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally visit &lt;a href="http://mysite.com/media/media.gif" rel="noopener noreferrer"&gt;http://mysite.com/media/media.gif&lt;/a&gt; in a browser and you should see the image&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Get Nginx, uWSGI, and Django to Work Together
&lt;/h2&gt;

&lt;p&gt;Let’s take this one step further and have Nginx, uWSGI, and Django work together with the help of the UNIX socket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uwsgi --socket mysite.sock --module mysite.wsgi --chmod-socket=666
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can actually try the above command without the &lt;code&gt;--chmod-socket=666&lt;/code&gt; argument and/or with a &lt;code&gt;--chmod-socket=664&lt;/code&gt; argument instead. If either of those work for you, just keep that in mind going forward.&lt;/p&gt;

&lt;p&gt;Once again, visit &lt;a href="http://mysite.com" rel="noopener noreferrer"&gt;http://mysite.com&lt;/a&gt; in a browser, and this time you should see the default Django landing page!&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Configure uWSGI for Production
&lt;/h2&gt;

&lt;p&gt;we configure a file at the root of your Django project called mysite_uwsgi.ini .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[uwsgi]
# full path to Django project's root directory
chdir            = /home/emdad/mysite/
# Django's wsgi file
module           = mysite.wsgi
# full path to python virtual env
home             = /home/emdad/env
# enable uwsgi master process
master          = true
# maximum number of worker processes
processes       = 10
# the socket (use the full path to be safe
socket          = /home/emdad/mysite/mysite.sock
# socket permissions
chmod-socket    = 666
# clear environment on exit
vacuum          = true
# daemonize uwsgi and write messages into given log
daemonize       = /home/emdad/uwsgi-emperor.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then proceed to start up uwsgi and specify the ini file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uwsgi --ini mysite_uwsgi.ini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;visit &lt;a href="http://mysite.com" rel="noopener noreferrer"&gt;http://mysite.com&lt;/a&gt; in a browser and see the default Django landing page if everything works correctly.&lt;/p&gt;

&lt;p&gt;As one last configuration option for uWSGI, let’s run uWSGI in emperor mode. This will monitor the uWSGI config file directory for changes and will spawn vassals (i.e. instances) for each one it finds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /home/emdad/mysite/env/
mkdir vassals
sudo ln -s /home/emdad/mysite/mysite_uwsgi.ini /home/emdad/env/vassals/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run uWSGI in emperor mode as a test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uwsgi --emperor /home/emdad/env/vassals --uid www-data --gid www-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://mysite.com" rel="noopener noreferrer"&gt;http://mysite.com&lt;/a&gt; a browser and you will see the default Django landing page if everything works correctly.&lt;/p&gt;

&lt;p&gt;Finally, we want to start up uWSGI when the system boots. Create a systemd service file at /etc/systemd/system/emperor.uwsgi.service with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=uwsgi emperor for mysite domains website
After=network.target
[Service]
User=emdad
Restart=always
ExecStart=/home/emdad/mysite/env/bin/uwsgi --emperor /home/emdad/mysite/env/vassals --uid www-data --gid www-data
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the service to allow it to execute on system boot and start it so you can test it without a reboot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl enable emperor.uwsgi.service
systemctl start emperor.uwsgi.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://mysite.com" rel="noopener noreferrer"&gt;http://mysite.com&lt;/a&gt; a browser and you will see the default Django landing page if everything works correctly.&lt;/p&gt;

&lt;p&gt;Check the status of the service and stop it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl status emperor.uwsgi.service
systemctl stop emperor.uwsgi.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now reboot your system to make sure that your website is accessible at startup.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
