| Server IP : 52.25.153.185 / Your IP : 216.73.216.194 Web Server : Apache System : Linux ip-172-26-6-158 5.10.0-45-cloud-amd64 #1 SMP Debian 5.10.259-1 (2026-07-02) x86_64 User : daemon ( 1) PHP Version : 8.1.10 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /bitnami/wordpress/wp-content/plugins/fluentformpro/src/Payments/Classes/ |
Upload File : |
<?php
namespace FluentFormPro\Payments\Classes;
use FluentForm\Framework\Helpers\ArrayHelper;
use FluentFormPro\Payments\PaymentHelper;
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly.
}
class CouponModel
{
private $table = 'fluentform_coupons';
public function getCoupons($paginate = false)
{
$query = wpFluent()->table($this->table);
if ($paginate) {
if (!$perPage = intval($_REQUEST['per_page'])) {
$perPage = 10;
}
$coupons = $query->paginate($perPage)->toArray();
$coupons['data'] = $this->processGetCoupons($coupons['data']);
return $coupons;
}
$coupons = $query->get();
return $this->processGetCoupons($coupons);
}
public function getCouponByCode($code)
{
$coupon = wpFluent()->table($this->table)
->where('code', $code)
->first();
if (!$coupon) {
return $coupon;
}
$coupon->settings = $this->processSettings($coupon->settings);
if ($coupon->start_date == '0000-00-00') {
$coupon->start_date = '';
}
if ($coupon->expire_date == '0000-00-00') {
$coupon->expire_date = '';
}
return $coupon;
}
public function getCouponsByCodes($codes)
{
$coupons = wpFluent()->table($this->table)
->whereIn('code', $codes)
->get();
return $this->processGetCoupons($coupons);
}
public function insert($data)
{
$data['created_at'] = current_time('mysql');
$data['updated_at'] = current_time('mysql');
$data['created_by'] = get_current_user_id();
if (isset($data['settings'])) {
$data['settings'] = maybe_serialize($data['settings']);
}
return wpFluent()->table($this->table)
->insertGetId($data);
}
public function update($id, $data)
{
$data['updated_at'] = current_time('mysql');
if (isset($data['settings'])) {
$data['settings'] = maybe_serialize($data['settings']);
}
return wpFluent()->table($this->table)
->where('id', $id)
->update($data);
}
public function delete($id)
{
return wpFluent()->table($this->table)
->where('id', $id)
->delete();
}
public function getValidCoupons($coupons, $formId, $amountTotal)
{
$validCoupons = [];
$userId = get_current_user_id();
// Batch-fetch per-user usage counts for all limited coupons in one query
$userCouponCounts = [];
if ($userId) {
$limitedCodes = [];
foreach ($coupons as $coupon) {
$couponLimit = ArrayHelper::get($coupon->settings, 'coupon_limit', 0);
if ($couponLimit) {
$limitedCodes[] = $coupon->code;
}
}
if ($limitedCodes) {
$userCouponCounts = $this->batchCouponAppliedCounts($limitedCodes, $userId);
}
}
// One grouped query for every max_use coupon instead of a COUNT per coupon.
$globalCouponCounts = [];
$maxUseCodes = [];
foreach ($coupons as $coupon) {
if ($coupon->max_use) {
$maxUseCodes[] = $coupon->code;
}
}
if ($maxUseCodes) {
$globalCouponCounts = $this->batchCouponGlobalUsedCounts($maxUseCodes);
}
$otherCouponCodes = [];
foreach ($coupons as $coupon) {
if ($coupon->status != 'active') {
continue;
}
if ($this->isDateExpire($coupon)) {
continue;
}
if ($formIds = ArrayHelper::get($coupon->settings, 'allowed_form_ids')) {
if (!in_array($formId, $formIds)) {
continue;
}
}
if ($coupon->min_amount && $coupon->min_amount > $amountTotal) {
continue;
}
$couponLimit = ArrayHelper::get($coupon->settings, 'coupon_limit', 0);
if ($couponLimit) {
if (!$userId) {
continue;
}
$used = isset($userCouponCounts[$coupon->code]) ? $userCouponCounts[$coupon->code] : 0;
if ($used >= (int) $couponLimit) {
continue;
}
}
if ($coupon->max_use) {
// Not atomic with submission persistence: concurrent redemptions can both pass.
$globalUsed = isset($globalCouponCounts[$coupon->code]) ? $globalCouponCounts[$coupon->code] : 0;
if ($globalUsed >= (int) $coupon->max_use) {
continue;
}
}
if ($otherCouponCodes && $coupon->stackable != 'yes') {
continue;
}
$discountAmount = $coupon->amount;
if ($coupon->coupon_type == 'percent') {
$discountAmount = ($coupon->amount / 100) * $amountTotal;
}
$amountTotal = $amountTotal - $discountAmount;
$otherCouponCodes[] = $coupon->code;
$validCoupons[] = $coupon;
}
return $validCoupons;
}
public function migrate()
{
global $wpdb;
$charsetCollate = $wpdb->get_charset_collate();
$table = $wpdb->prefix . $this->table;
if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) {
$sql = "CREATE TABLE $table (
id int(11) NOT NULL AUTO_INCREMENT,
title varchar(192),
code varchar(192),
coupon_type varchar(255) DEFAULT 'percent',
amount decimal(10,2) NULL,
status varchar(192) DEFAULT 'active',
stackable varchar(192) DEFAULT 'no',
settings longtext,
created_by INT(11) NULL,
min_amount INT(11) NULL,
max_use INT(11) NULL,
start_date date NULL,
expire_date date NULL,
created_at timestamp NULL,
updated_at timestamp NULL,
PRIMARY KEY (id)
) $charsetCollate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
}
public function isCouponCodeAvailable($code, $exceptId = false)
{
$query = wpFluent()->table($this->table)
->where('code', $code);
if ($exceptId) {
$query = $query->where('id', '!=', $exceptId);
}
return $query->first();
}
protected function processGetCoupons($coupons)
{
foreach ($coupons as $coupon) {
if (!empty($coupon->settings)) {
$coupon->settings = $this->processSettings($coupon->settings);
} else {
$coupon->settings = [
'allowed_form_ids' => [],
'coupon_limit' => 0,
];
}
if ($coupon->start_date == '0000-00-00') {
$coupon->start_date = '';
}
if ($coupon->expire_date == '0000-00-00') {
$coupon->expire_date = '';
}
}
return $coupons;
}
protected function processSettings($settings)
{
$settings = PaymentHelper::safeUnserialize($settings);
$settings['coupon_limit'] = ArrayHelper::get($settings, 'coupon_limit', 0);
return $settings;
}
public function hasLimit($couponCode, $couponLimit, $userId)
{
$couponApplied = $this->couponAppliedCount($couponCode, $userId);
return (int) $couponLimit - $couponApplied > 0;
}
public function isDateExpire($coupon)
{
$start_date = '';
$expire_date = '';
if ($coupon->start_date && ("0000-00-00" != $coupon->start_date)) {
$start_date = strtotime($coupon->start_date);
}
if ($coupon->expire_date && ("0000-00-00" != $coupon->expire_date)) {
$expire_date = strtotime($coupon->expire_date);
}
$today = strtotime('today midnight');
if ($start_date && $expire_date) {
return !($start_date <= $today && $today <= $expire_date); // start-date<=today<=expire-date
}
if ($start_date) {
return !($start_date <= $today);
}
if ($expire_date) {
return !($today <= $expire_date);
}
return false;
}
protected function couponAppliedCount($couponCode, $userId)
{
$counts = $this->batchCouponAppliedCounts([$couponCode], $userId);
return isset($counts[$couponCode]) ? $counts[$couponCode] : 0;
}
public function couponGlobalUsedCount($couponCode)
{
$counts = $this->batchCouponGlobalUsedCounts([$couponCode]);
return isset($counts[$couponCode]) ? $counts[$couponCode] : 0;
}
protected function batchCouponGlobalUsedCounts(array $couponCodes)
{
$counts = array_fill_keys($couponCodes, 0);
// Fast path: a non-stacked submission stores a single code, counted grouped.
$exact = wpFluent()
->table('fluentform_entry_details')
->select(['field_value', wpFluent()->raw('COUNT(*) as usage_count')])
->where('field_name', 'payment-coupon')
->whereIn('field_value', $couponCodes)
->groupBy('field_value')
->get();
foreach ($exact as $row) {
$counts[$row->field_value] += (int) $row->usage_count;
}
$this->addStackedCouponCounts($counts, null);
return $counts;
}
protected function batchCouponAppliedCounts(array $couponCodes, $userId)
{
$counts = array_fill_keys($couponCodes, 0);
$exact = wpFluent()
->table('fluentform_entry_details')
->select(['fluentform_entry_details.field_value', wpFluent()->raw('COUNT(*) as usage_count')])
->where('fluentform_entry_details.field_name', 'payment-coupon')
->whereIn('fluentform_entry_details.field_value', $couponCodes)
->join('fluentform_submissions', function ($table) use ($userId) {
$table->on('fluentform_submissions.id', '=', 'fluentform_entry_details.submission_id');
$table->on('fluentform_submissions.user_id', '=', wpFluent()->raw(intval($userId)));
})
->groupBy('fluentform_entry_details.field_value')
->get();
foreach ($exact as $row) {
$counts[$row->field_value] += (int) $row->usage_count;
}
$this->addStackedCouponCounts($counts, $userId);
return $counts;
}
// Stacked coupons are persisted as one joined value ("A, B" via
// Components/Coupon.php::addCouponsToSubmission, which implodes with ", ").
// Split on that exact delimiter so a code containing a comma (e.g. "SAVE,20")
// survives as one token and still counts toward max_use and per-user
// coupon_limit instead of being shattered into unmatched fragments.
// $userId === null counts global usage; an integer scopes to that user.
protected function addStackedCouponCounts(array &$counts, $userId)
{
$codes = array_keys($counts);
if (!$codes) {
return;
}
$query = wpFluent()
->table('fluentform_entry_details')
->select(['fluentform_entry_details.field_value'])
->where('fluentform_entry_details.field_name', 'payment-coupon')
->where('fluentform_entry_details.field_value', 'LIKE', '%, %')
->where(function ($q) use ($codes) {
// Prefilter: only joined rows that mention a requested code. The
// authoritative match is the exact token comparison below.
foreach ($codes as $code) {
$q->orWhere(
'fluentform_entry_details.field_value',
'LIKE',
'%' . addcslashes($code, '%_\\') . '%'
);
}
});
if (null !== $userId) {
$query->join('fluentform_submissions', function ($table) use ($userId) {
$table->on('fluentform_submissions.id', '=', 'fluentform_entry_details.submission_id');
$table->on('fluentform_submissions.user_id', '=', wpFluent()->raw(intval($userId)));
});
}
foreach ($query->get() as $row) {
foreach (array_map('trim', explode(', ', $row->field_value)) as $code) {
if (isset($counts[$code])) {
$counts[$code]++;
}
}
}
}
}