Chapter 7: Multiple Coupons with Active / Inactive State
Until now, your plugin supported one coupon configuration. In this chapter we’ll refactor it so that:
- You can define multiple coupons in admin
- Each coupon has:
- Code
- Discount type (flat/percentage)
- Discount value
- Min / max order value
- Customer type (any/new/existing)
- Status: active / inactive
- At checkout, when a customer enters a code, the plugin:
- Looks up a matching active coupon
- Applies its specific rules
We’ll store all coupons in a single option:
sdr_coupons
as an array of coupon rule arrays.
From this chapter onward, we no longer use the old single-coupon options like sdr_discount_type, sdr_flat_discount_amount, etc., for logic. They can remain in DB but are ignored.
1. Data Structure for Coupons
Each coupon in sdr_coupons will look like:
array(
‘code’ => ‘WELCOME10’,
‘discount_type’ => ‘percentage’, // or ‘flat’
‘discount_value’=> 10, // 10% or 10 currency units
‘min_order’ => 1000,
‘max_order’ => 0, // 0 = no limit
‘customer_type’ => ‘new’, // any|new|existing
‘status’ => ‘active’, // active|inactive
);
2. Update the Admin Save Handler (Global + Add Coupon)
We’ll keep:
- Global options:
- sdr_checkout_message
- sdr_enable_discount (master switch)
- New behavior:
- A Global Settings form to update message + master enable
- A New Coupon form to add coupons into sdr_coupons
Replace your existing sdr_handle_settings_form_submit() with this:
/**
* Handle settings form submission (global settings + add coupon).
*/
function sdr_handle_settings_form_submit() {
// GLOBAL SETTINGS SAVE.
if ( isset( $_POST['sdr_global_settings_submit'] ) ) {
// Check nonce.
if ( ! isset( $_POST['sdr_global_settings_nonce'] ) || ! wp_verify_nonce( $_POST['sdr_global_settings_nonce'], 'sdr_save_global_settings' ) ) {
return;
}
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$message = isset( $_POST['sdr_checkout_message'] )
? wp_kses_post( wp_unslash( $_POST['sdr_checkout_message'] ) )
: '';
$enable_discount = isset( $_POST['sdr_enable_discount'] ) && 'yes' === $_POST['sdr_enable_discount']
? 'yes'
: 'no';
update_option( 'sdr_checkout_message', $message );
update_option( 'sdr_enable_discount', $enable_discount );
add_settings_error(
'sdr_messages',
'sdr_message',
__( 'Global settings saved.', 'simple-discount-rules' ),
'updated'
);
}
// ADD NEW COUPON.
if ( isset( $_POST['sdr_add_coupon_submit'] ) ) {
// Check nonce.
if ( ! isset( $_POST['sdr_add_coupon_nonce'] ) || ! wp_verify_nonce( $_POST['sdr_add_coupon_nonce'], 'sdr_add_coupon' ) ) {
return;
}
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$code = isset( $_POST['sdr_coupon_code'] )
? strtoupper( sanitize_text_field( wp_unslash( $_POST['sdr_coupon_code'] ) ) )
: '';
$discount_type = isset( $_POST['sdr_discount_type'] )
? sanitize_text_field( wp_unslash( $_POST['sdr_discount_type'] ) )
: 'flat';
if ( ! in_array( $discount_type, array( 'flat', 'percentage' ), true ) ) {
$discount_type = 'flat';
}
$discount_value = isset( $_POST['sdr_discount_value'] )
? floatval( wp_unslash( $_POST['sdr_discount_value'] ) )
: 0;
if ( $discount_value < 0 ) {
$discount_value = 0;
}
$min_order = isset( $_POST['sdr_min_order_amount'] )
? floatval( wp_unslash( $_POST['sdr_min_order_amount'] ) )
: 0;
if ( $min_order < 0 ) {
$min_order = 0;
}
$max_order = isset( $_POST['sdr_max_order_amount'] )
? floatval( wp_unslash( $_POST['sdr_max_order_amount'] ) )
: 0;
if ( $max_order < 0 ) {
$max_order = 0;
}
$customer_type = isset( $_POST['sdr_customer_type'] )
? sanitize_text_field( wp_unslash( $_POST['sdr_customer_type'] ) )
: 'any';
if ( ! in_array( $customer_type, array( 'any', 'new', 'existing' ), true ) ) {
$customer_type = 'any';
}
$status = isset( $_POST['sdr_status'] )
? sanitize_text_field( wp_unslash( $_POST['sdr_status'] ) )
: 'active';
if ( ! in_array( $status, array( 'active', 'inactive' ), true ) ) {
$status = 'inactive';
}
// Basic validation.
if ( empty( $code ) || $discount_value <= 0 ) { add_settings_error( 'sdr_messages', 'sdr_coupon_error', __( 'Coupon code and discount value are required.', 'simple-discount-rules' ), 'error' ); return; } $coupons = get_option( 'sdr_coupons', array() ); if ( ! is_array( $coupons ) ) { $coupons = array(); } $new_coupon = array( 'code' => $code,
'discount_type' => $discount_type,
'discount_value'=> $discount_value,
'min_order' => $min_order,
'max_order' => $max_order,
'customer_type' => $customer_type,
'status' => $status,
);
$coupons[] = $new_coupon;
update_option( 'sdr_coupons', $coupons );
add_settings_error(
'sdr_messages',
'sdr_coupon_added',
__( 'New coupon added.', 'simple-discount-rules' ),
'updated'
);
}
}
add_action( 'admin_init', 'sdr_handle_settings_form_submit' );
3. Handle Toggle / Delete Actions for Coupons
We’ll allow toggling active/inactive and deleting coupons via GET links from the list table.
Add this function:
/**
* Handle coupon row actions: toggle status / delete.
*/
function sdr_handle_coupon_row_actions() {
if ( ! isset( $_GET['sdr_coupon_action'], $_GET['sdr_coupon_index'] ) ) {
return;
}
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
if ( ! isset( $_GET['sdr_coupon_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['sdr_coupon_nonce'] ) ), 'sdr_coupon_action' ) ) {
return;
}
$action = sanitize_text_field( wp_unslash( $_GET['sdr_coupon_action'] ) );
$index = intval( $_GET['sdr_coupon_index'] );
$coupons = get_option( 'sdr_coupons', array() );
if ( ! is_array( $coupons ) || ! isset( $coupons[ $index ] ) ) {
return;
}
if ( 'toggle' === $action ) {
$current_status = isset( $coupons[ $index ]['status'] ) ? $coupons[ $index ]['status'] : 'inactive';
$coupons[ $index ]['status'] = ( 'active' === $current_status ) ? 'inactive' : 'active';
update_option( 'sdr_coupons', $coupons );
add_settings_error(
'sdr_messages',
'sdr_coupon_toggled',
__( 'Coupon status updated.', 'simple-discount-rules' ),
'updated'
);
} elseif ( 'delete' === $action ) {
unset( $coupons[ $index ] );
$coupons = array_values( $coupons ); // re-index
update_option( 'sdr_coupons', $coupons );
add_settings_error(
'sdr_messages',
'sdr_coupon_deleted',
__( 'Coupon deleted.', 'simple-discount-rules' ),
'updated'
);
}
}
add_action( 'admin_init', 'sdr_handle_coupon_row_actions' );
4. Update the Settings Page to Show “Global Settings + New Coupon + Coupon List”
Now we replace sdr_render_settings_page() so it:
- Shows global helper message + master enable (Form 1)
- Shows “Add New Coupon” form (Form 2)
- Lists all coupons with toggle/delete actions
Replace your existing sdr_render_settings_page() with this version:
<?php
/**
* Render the plugin settings page.
*/
function sdr_render_settings_page() {
$current_message = get_option(
'sdr_checkout_message',
__( 'Enter your special discount code at checkout to claim the offer.', 'simple-discount-rules' )
);
$enable_discount = get_option( 'sdr_enable_discount', 'yes' );
$coupons = get_option( 'sdr_coupons', array() );
if ( ! is_array( $coupons ) ) {
$coupons = array();
}
// Show any messages.
settings_errors( 'sdr_messages' );
?>
<div class="wrap">
<h1><?php esc_html_e( 'Simple Discount Rules – Settings', 'simple-discount-rules' ); ?></h1>
<h2><?php esc_html_e( 'Global Settings', 'simple-discount-rules' ); ?></h2>
<form method="post" action="">
<?php wp_nonce_field( 'sdr_save_global_settings', 'sdr_global_settings_nonce' ); ?>
<table class="form-table" role="presentation">
<tbody>
<tr>
<th scope="row">
<?php esc_html_e( 'Enable All Discounts', 'simple-discount-rules' ); ?>
</th>
<td>
<label>
<input type="checkbox" name="sdr_enable_discount" value="yes" <?php checked( $enable_discount, 'yes' ); ?> />
<?php esc_html_e( 'Enable all coupon-based rules', 'simple-discount-rules' ); ?>
</label>
<p class="description"><?php esc_html_e( 'Uncheck to disable all discounts.', 'simple-discount-rules' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="sdr_checkout_message"><?php esc_html_e( 'Checkout Helper Message', 'simple-discount-rules' ); ?></label>
</th>
<td>
<textarea name="sdr_checkout_message" id="sdr_checkout_message" rows="4" cols="50" class="large-text"><?php echo esc_textarea( $current_message ); ?></textarea>
<p class="description"><?php esc_html_e( 'Shown near the coupon box.', 'simple-discount-rules' ); ?></p>
</td>
</tr>
</tbody>
</table>
<?php submit_button( __( 'Save Global Settings', 'simple-discount-rules' ), 'primary', 'sdr_global_settings_submit' ); ?>
</form>
<hr />
<h2><?php esc_html_e( 'Add New Coupon', 'simple-discount-rules' ); ?></h2>
<form method="post" action="">
<?php wp_nonce_field( 'sdr_add_coupon', 'sdr_add_coupon_nonce' ); ?>
<table class="form-table" role="presentation">
<tbody>
<tr>
<th scope="row"><label for="sdr_coupon_code"><?php esc_html_e( 'Coupon Code', 'simple-discount-rules' ); ?></label></th>
<td>
<input type="text" id="sdr_coupon_code" name="sdr_coupon_code" class="regular-text" />
<p class="description"><?php esc_html_e( 'Example: WELCOME10', 'simple-discount-rules' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="sdr_discount_type"><?php esc_html_e( 'Discount Type', 'simple-discount-rules' ); ?></label></th>
<td>
<select name="sdr_discount_type" id="sdr_discount_type">
<option value="flat"><?php esc_html_e( 'Flat', 'simple-discount-rules' ); ?></option>
<option value="percentage"><?php esc_html_e( 'Percentage', 'simple-discount-rules' ); ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="sdr_discount_value"><?php esc_html_e( 'Discount Value', 'simple-discount-rules' ); ?></label></th>
<td>
<input type="number" step="0.01" min="0" id="sdr_discount_value" name="sdr_discount_value" class="small-text" />
<p class="description"><?php echo wp_kses_post( __( 'Flat = currency value, Percentage = percent value.', 'simple-discount-rules' ) ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><label for="sdr_min_order_amount"><?php esc_html_e( 'Minimum Order Value', 'simple-discount-rules' ); ?></label></th>
<td>
<input type="number" step="0.01" min="0" id="sdr_min_order_amount" name="sdr_min_order_amount" class="small-text" />
</td>
</tr>
<tr>
<th scope="row"><label for="sdr_max_order_amount"><?php esc_html_e( 'Maximum Order Value', 'simple-discount-rules' ); ?></label></th>
<td>
<input type="number" step="0.01" min="0" id="sdr_max_order_amount" name="sdr_max_order_amount" class="small-text" />
</td>
</tr>
<tr>
<th scope="row"><label for="sdr_customer_type"><?php esc_html_e( 'Eligible Customers', 'simple-discount-rules' ); ?></label></th>
<td>
<select name="sdr_customer_type" id="sdr_customer_type">
<option value="any"><?php esc_html_e( 'All customers', 'simple-discount-rules' ); ?></option>
<option value="new"><?php esc_html_e( 'New customers', 'simple-discount-rules' ); ?></option>
<option value="existing"><?php esc_html_e( 'Existing customers', 'simple-discount-rules' ); ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label for="sdr_status"><?php esc_html_e( 'Status', 'simple-discount-rules' ); ?></label></th>
<td>
<select name="sdr_status" id="sdr_status">
<option value="active"><?php esc_html_e( 'Active', 'simple-discount-rules' ); ?></option>
<option value="inactive"><?php esc_html_e( 'Inactive', 'simple-discount-rules' ); ?></option>
</select>
</td>
</tr>
</tbody>
</table>
<?php submit_button( __( 'Add Coupon', 'simple-discount-rules' ), 'secondary', 'sdr_add_coupon_submit' ); ?>
</form>
<hr />
<h2><?php esc_html_e( 'Existing Coupons', 'simple-discount-rules' ); ?></h2>
<?php if ( empty( $coupons ) ) : ?>
<p><?php esc_html_e( 'No coupons found.', 'simple-discount-rules' ); ?></p>
<?php else : ?>
<table class="widefat fixed striped">
<thead>
<tr>
<th>Code</th>
<th>Type</th>
<th>Value</th>
<th>Min</th>
<th>Max</th>
<th>Customer</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ( $coupons as $index => $coupon ) : ?>
<?php
$code = $coupon['code'];
$type = $coupon['discount_type'];
$value = $coupon['discount_value'];
$min = $coupon['min_order'];
$max = $coupon['max_order'];
$cust_type = $coupon['customer_type'];
$status = $coupon['status'];
?>
<tr>
<td><code><?php echo esc_html( $code ); ?></code></td>
<td><?php echo ( 'percentage' === $type ) ? 'Percentage' : 'Flat'; ?></td>
<td><?php echo esc_html( $value ); ?></td>
<td><?php echo $min ? wc_price( $min ) : '—'; ?></td>
<td><?php echo $max ? wc_price( $max ) : '—'; ?></td>
<td><?php echo esc_html( ucfirst( $cust_type ) ); ?></td>
<td><?php echo ( 'active' === $status ) ? 'Active' : 'Inactive'; ?></td>
<td>---</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
}
?>
5. Update the Discount Logic to Use the Coupon List
We already have sdr_is_customer_eligible() from Chapter 6. Now we change sdr_apply_flat_discount() so it:
- Checks global enable
- Reads the entered code from session
- Looks up the first active coupon whose code matches (case-insensitive)
- Uses that coupon’s settings instead of global options
Replace your existing sdr_apply_flat_discount() with:
/**
* Apply a discount when the correct custom coupon code is entered.
* Works with multiple coupons defined in sdr_coupons option.
*
* @param WC_Cart $cart Cart object.
*/
function sdr_apply_flat_discount( $cart ) {
if ( ! sdr_is_woocommerce_active() ) {
return;
}
// Don't run in admin except during AJAX.
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
// Check global enable switch.
$enabled = get_option( 'sdr_enable_discount', 'yes' );
if ( 'yes' !== $enabled ) {
return;
}
// Fetch the entered code from the session.
$entered_code = '';
if ( class_exists( 'WC' ) && WC()->session ) {
$entered_code = WC()->session->get( 'sdr_coupon_code', '' );
}
if ( empty( $entered_code ) ) {
return;
}
// Load all stored coupons.
$coupons = get_option( 'sdr_coupons', array() );
if ( ! is_array( $coupons ) || empty( $coupons ) ) {
// User tried to apply a code but no coupons exist.
if ( isset( $_POST['sdr_coupon_code'] ) ) {
wc_add_notice(
__( 'The discount code you entered is not valid.', 'simple-discount-rules' ),
'error'
);
}
return;
}
// Find a matching active coupon.
$matched_coupon = null;
foreach ( $coupons as $coupon ) {
$code = isset( $coupon['code'] ) ? $coupon['code'] : '';
$status = isset( $coupon['status'] ) ? $coupon['status'] : 'inactive';
if ( 'active' !== $status || empty( $code ) ) {
continue;
}
// Case-insensitive compare.
if ( 0 === strcasecmp( $entered_code, $code ) ) {
$matched_coupon = $coupon;
break;
}
}
if ( ! $matched_coupon ) {
if ( isset( $_POST['sdr_coupon_code'] ) ) {
wc_add_notice(
__( 'The discount code you entered is not valid.', 'simple-discount-rules' ),
'error'
);
}
return;
}
// Extract coupon fields safely.
$coupon_code = isset( $matched_coupon['code'] ) ? $matched_coupon['code'] : '';
$discount_type = isset( $matched_coupon['discount_type'] ) ? $matched_coupon['discount_type'] : 'flat';
$discount_value = isset( $matched_coupon['discount_value'] ) ? floatval( $matched_coupon['discount_value'] ) : 0;
$min_order = isset( $matched_coupon['min_order'] ) ? floatval( $matched_coupon['min_order'] ) : 0;
$max_order = isset( $matched_coupon['max_order'] ) ? floatval( $matched_coupon['max_order'] ) : 0;
$customer_type = isset( $matched_coupon['customer_type'] ) ? $matched_coupon['customer_type'] : 'any';
if ( $discount_value <= 0 ) { return; } $cart_subtotal = floatval( $cart->get_subtotal() );
// Minimum order restriction.
if ( $min_order > 0 && $cart_subtotal < $min_order ) { if ( isset( $_POST['sdr_coupon_code'] ) ) { $msg = sprintf( __( 'This discount is only valid for orders of at least %s.', 'simple-discount-rules' ), wc_price( $min_order ) ); wc_add_notice( $msg, 'error' ); } return; } // Maximum order restriction. if ( $max_order > 0 && $cart_subtotal > $max_order ) {
if ( isset( $_POST['sdr_coupon_code'] ) ) {
$msg = sprintf(
__( 'This discount is only valid for orders up to %s.', 'simple-discount-rules' ),
wc_price( $max_order )
);
wc_add_notice( $msg, 'error' );
}
return;
}
// Validate customer type (new/existing).
if ( function_exists( 'sdr_is_customer_eligible' ) ) {
list( $eligible, $error_message ) = sdr_is_customer_eligible( $customer_type );
if ( ! $eligible ) {
if ( isset( $_POST['sdr_coupon_code'] ) && ! empty( $error_message ) ) {
wc_add_notice( $error_message, 'error' );
}
return;
}
}
// Begin discount calculation.
$discount = 0.0;
if ( 'percentage' === $discount_type ) {
$discount = ( $cart_subtotal * $discount_value ) / 100;
if ( $discount > $cart_subtotal ) {
$discount = $cart_subtotal;
}
} else {
// Flat discount.
$discount = min( $discount_value, $cart_subtotal );
}
if ( $discount <= 0 ) { return; } // Show success message only when user applies code manually. if ( isset( $_POST['sdr_coupon_code'] ) ) { if ( 'percentage' === $discount_type ) { $notice = sprintf( __( 'Your %s%% discount has been applied!', 'simple-discount-rules' ), esc_html( $discount_value ) ); } else { $notice = __( 'Your special discount has been applied!', 'simple-discount-rules' ); } wc_add_notice( $notice, 'success' ); } // Build discount label. if ( 'percentage' === $discount_type ) { $label = sprintf( __( 'Special Discount (%1$s – %2$s%%)', 'simple-discount-rules' ), esc_html( $coupon_code ), esc_html( $discount_value ) ); } else { $label = sprintf( __( 'Special Discount (%s)', 'simple-discount-rules' ), esc_html( $coupon_code ) ); } // Apply the discount as a negative fee. $cart->add_fee( $label, - $discount );
}
add_action( 'woocommerce_cart_calculate_fees', 'sdr_apply_flat_discount', 20, 1 );
6. Test Your Multi-Coupon Setup
- In WooCommerce → Simple Discount Rules:
- Turn on “Enable All Discounts”
- Add several coupons:
- WELCOME10: 10% off, min ₹1000, customer type “new”, status active
- FLAT200: flat 200, min ₹500, status active
- VIP50: 50% off, customer type “existing”, status inactive
- In checkout:
- Enter WELCOME10 as a new user → discount applies, respects min value
- Enter FLAT200 → correct fixed amount applies
- Enter VIP50 → no discount (inactive), error “code not valid”
- Toggle VIP50 to Active in admin → try again → now works for existing customers
You now have a tiny discount rules engine that supports multiple conditions and multiple coupons.



















0 Comments