=== Payam Resan - SMS Message Management ===
Contributors: payamresan
Tags: sms, otp, verification, authentication, woocommerce
Requires at least: 6.4
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.7.2
License: GPLv2 or later
WC requires at least: 7.0
WC tested up to: 10.5.2
License URI: https://www.gnu.org/licenses/gpl-2.0.txt

adds OTP login and registration, custom authentication forms, SMS reports, blocking tools, and Payam Resan SMS panel integration to WordPress.

== Description ==

Payam Resan integrates WordPress with the Payam Resan SMS panel so site owners can send SMS messages and manage OTP-based authentication from the WordPress dashboard.

The plugin includes a configurable SMS gateway, OTP login and registration verification, password reset verification, default and popup authentication forms, optional WordPress login replacement, reports, blocking rules, user phone-number fields, test messages, and cleanup tools.

Main features include:

* Payam Resan SMS gateway settings for API key, sender number, active gateway state, and normal or pattern sending mode.
* OTP settings for code length, expiration time, random or fixed codes, maximum attempts, send limits, text templates, pattern keys, pattern parameters, and test recipients.
* Custom login and registration forms with default and popup display modes.
* Optional replacement for the default WordPress `wp-login.php` login screen.
* Password login, OTP login, OTP registration verification, password reset verification, and configurable role exceptions.
* Registration settings for default user role, generated passwords, and terms text or terms page.
* Redirect URL settings after registration, login, password reset, and logout.
* Form style settings for logo, colors, dark or system color mode, and popup button text/icon.
* Blocking system for IP addresses and mobile numbers.
* User phone-number profile field, Users table phone column, and phone-number user search support.
* Send Test tools for normal SMS, pattern SMS, OTP SMS, account status, and gateway connection checks.
* SMS reports and gateway logs stored in custom database tables.
* Manual reset/recreate tools and scheduled cleanup for logs and OTP tokens.
* WooCommerce integration: SMS OTP verification of the billing mobile number at checkout and registration, an OTP login/registration form on the my-account page, and optional two-way sync with the WooCommerce billing phone.
* Internationalization support with Persian translation assets and Jalali date support.

== Installation ==

This plugin is built for a single-site WordPress installation. Multisite networks are not supported; see the FAQ below.

1. Upload the `payam-resan` folder to the `/wp-content/plugins/` directory.
2. Activate the plugin through the Plugins screen in WordPress.
3. Go to Payam Resan > SMS Gateway and enter your Payam Resan API key, sender number, and sending mode.
4. Open Payam Resan > General Settings > OTP Settings and configure code length, expiration, attempts, message text, pattern settings, and test recipient.
5. Open Payam Resan > Register | Login to configure registration role, password behavior, login method, role exceptions, forgot-password behavior, and terms.
6. Open Payam Resan > Redirect Settings to set optional redirect URLs after register, login, password reset, and logout actions.
7. Open Payam Resan > Styles Settings > General Styles to customize form branding, colors, color mode, and popup button display.
8. Open Payam Resan > Short Codes to enable the global shortcode switch and individual form shortcodes.
9. Add a shortcode to a page, or enable the wp-login option in General Settings > Additional Settings to use the custom login template.

== Usage ==

Available admin pages:

* Dashboard - View plugin, gateway, and configuration status.
* SMS Gateway - Configure Payam Resan API credentials, sender number, gateway state, and sending mode.
* General Settings - Manage OTP settings, blocked numbers/IPs, additional settings, and reset/cleanup tools.
* Redirect Settings - Set post-authentication redirect URLs.
* Short Codes - Enable or disable available shortcodes.
* Register | Login - Configure registration, login methods, role exceptions, generated passwords, forgot password, and terms.
* Styles Settings - Customize form branding, color palette, dark/system mode, and popup button display.
* Reports - Review SMS/gateway logs and filter report data.
* Send Test - Send normal and pattern SMS test messages.

Supported message placeholders where available:

* `{domain}` - Current site domain, with any leading `www.` removed.
* `{otp-domain}` - Current site host, verbatim. Required for SMS autofill: the browser only offers the code when the host in the message matches the page exactly.
* `{site-title}` - WordPress site title.
* `{now}` - Current site time.
* `{otp}` - Generated OTP code.
* `{expire}` - OTP expiration time.

== Custom Forms ==

Payam Resan > Custom Forms builds a registration form from the fields you choose and publishes it as a shortcode.

* Pick from the built-in fields (mobile, national code, email, name, password) or define your own, which are stored as user meta.
* Mark each field required or optional, and drag to reorder.
* Choose which field builds the username. A national code, for example, is checked for a valid check digit and for uniqueness before the account is created.
* Show optional fields inline, or collapse them into a panel the user opens.

Custom forms follow the site's active login method, use the global CAPTCHA settings, and are subject to the same rate limits as the built-in forms.

== Shortcodes ==

* `[pr_register_default]` - Displays the default registration form.
* `[pr_login_default]` - Displays the default login form.
* `[pr_register_login_default]` - Displays the default combined registration and login form.
* `[pr_register_popup]` - Displays a registration trigger that opens the popup registration form.
* `[pr_login_popup]` - Displays a login trigger that opens the popup login form.
* `[pr_register_login_popup]` - Displays a combined register/login trigger that opens the popup form.
* `[pr_form id="..."]` - Displays a custom form you built under Payam Resan > Custom Forms.

Shortcodes can be enabled or disabled globally and individually from Payam Resan > Short Codes.

== Frequently Asked Questions ==

= Does this plugin work with any SMS panel? =

The active gateway in this release is Payam Resan. mybe in feature release.

= What sending modes are supported? =

The Payam Resan gateway supports normal message sending and pattern-based sending. Pattern keys and parameters can be managed from the admin settings where available.

= Can I customize the look of the login and registration forms? =

Yes. You can customize the logo, color palette, dark or system color mode, popup/default display, and popup button text or icon from the Styles settings.

= How does OTP verification work? =

A visitor enters a mobile number, receives a code by SMS, and submits the code to complete login, registration, or password-reset verification. OTP length, expiration, attempts, and message text are configurable.

= Can the plugin replace wp-login.php? =

Yes. Enable the wp-login option in Additional Settings. The plugin replaces the default GET login screen with its custom template but skips native WordPress logout, reset-password, registration, check-email, AJAX, REST, and POST flows.

= Does the plugin work with WooCommerce? =

Yes. Enable the integration under Payam Resan > Integrations > WooCommerce, then switch on the capabilities you need: SMS verification of the mobile number at checkout (both the classic [woocommerce_checkout] shortcode and the checkout block are supported, and the order is blocked server-side in either case), SMS verification on the my-account registration form, replacing the my-account login/registration form with the Payam Resan OTP form for logged-out visitors, and two-way sync between the plugin mobile number and the WooCommerce billing phone. A number already registered to another account is rejected before the SMS is sent. When a capability is off, the plugin does not touch that WooCommerce flow. Tested with WooCommerce 10.5.2.

= Can I block users from receiving OTP messages? =

Yes. Go to Payam Resan > General Settings > Blocks Settings to block IP addresses or mobile numbers.

= Does the plugin store SMS logs? =

Yes. SMS gateway attempts and responses are stored in custom database tables and can be reviewed from Payam Resan > Reports.

= How can I clean up old logs or OTP tokens? =

Use Payam Resan > General Settings > Reset Settings. You can manually recreate/reset plugin tables and enable scheduled weekly or monthly cleanup for logs and OTP tokens.

= Does this plugin work with caching plugins? =

Yes, but authentication pages and AJAX-based OTP form interactions should not be cached. Exclude pages that contain Payam Resan shortcodes from page caching if OTP submissions behave unexpectedly.

= What kind of server does this plugin need? =

For a small site with occasional logins: PHP 7.4, WordPress 6.4, 128 MB memory limit, 1 core and 1 GB of RAM. For up to 10,000 users and up to 50 people logging in at the same time: PHP 8.1 or newer, 256 MB memory limit, 2 cores and 4 GB, and 10 to 20 PHP workers. Above 50,000 users or 100 simultaneous logins: 4 cores, 8 GB, 30 to 50 workers, and ideally a separate PHP pool for admin-ajax.php so a rush of logins cannot slow the rest of the site.

PHP opcache should be enabled at every size. Without it, each request spends time re-reading plugin files before doing any work.

= How many people can log in at the same time? =

That depends far more on your server and your SMS panel than on the plugin. Sending a code is a call to your SMS panel that happens while the visitor waits, so each send occupies one PHP worker until the panel answers.

With a healthy panel that answers in under a second, 10 workers comfortably handle around 50 people logging in at once, and the last person in a burst of 100 waits about five seconds. If your panel is slow, that wait grows in direct proportion. The number to plan against is simple: everyone in the queue must receive their code while it is still valid, so compare the wait against your OTP expiry time.

= Do I need Redis or another object cache? =

Not for a small site, but it is the single most useful addition for a busy one. Without a persistent object cache, WordPress stores short-lived data in the options table, and this plugin keeps the security check, the send limits and the panel health state there. Measured, that is 2 row writes and 3 queries for every security check issued - around 200 writes when 100 people log in at once. With Redis or Memcached installed, that cost disappears.

= Does this plugin work on WordPress multisite? =

No. The plugin is built for a single site. It keeps its tables and settings per site, and it resolves users through WordPress's network-wide user tables, which a network installation is not set up for. Activating it across a network leaves subsites without their database tables until an administrator opens their dashboard, and phone numbers are treated as unique across the whole network rather than per site. A warning is shown in the dashboard when a network is detected.

== Screenshots ==

1. Dashboard status overview.
2. SMS Gateway settings page.
3. OTP configuration panel.
4. Login and registration settings.
5. Form styling and popup button settings.
6. Blocked mobile numbers and IP addresses.
7. Reports and SMS log filters.
8. Send Test page for normal and pattern SMS.
9. Reset and scheduled cleanup settings.

== Changelog ==

= 1.7.2 =
* **The Reports page opens quickly even with a very large log.** It used to count every row in the log on each load, which on a busy site was almost all of the time the page took. That total is now remembered for a few minutes at a time, so opening the page and moving through the results is immediate.
* **Moving to the next or previous page of reports is now instant at any depth.** Previously the further into the log you went, the slower each page became. Jumping straight to a very distant page number is still slower, but stepping through pages no longer is.
* Fixed: the line under the report table counted one page more than there were whenever the records divided evenly - 40 records shown 20 at a time reported "3 pages".
* **Fixed: the report date fields no longer fill themselves in with today's date.** Because they sit in the same form as the level and per-page controls, simply changing how many rows to show was enough to limit the report to a single day without you choosing a date. The fields now start empty and stay empty until you pick a date, and they show a calendar icon and an "All dates" hint so it is clear what leaving them blank means.
* **Logging in with a mobile number is now fast no matter how many users you have.** Looking the number up was the first thing every login did, and it got slower as your site grew - about a third of a second on a site with a hundred thousand users, paid twice for each login. It is now under a millisecond, and it stays there as you grow.
* This works because the plugin keeps its own small lookup list of numbers. It is filled in the background after you update, and kept up to date automatically whenever a number changes - including numbers saved by WooCommerce, Digits or the user profile screen.
* **Nothing is at risk while that list is being built.** Until it is ready, and if scheduled tasks are not running on your site, logins fall back to the old method. The worst case is that logins stay as fast as they are today, never that someone cannot sign in.
* If you ever need to rebuild the list, use Regenerate Tables on the Reset tab. It is safe to do on a live site.
* **A failing SMS panel no longer holds visitors for twenty seconds.** When the panel stops answering, sending a login code now gives up after 10 seconds instead of 20. A working panel replies in well under a second, so this costs you no successful messages - it only shortens the wait when something is already wrong, and lets a busy site keep serving visitors while the panel is down.
* Developers can adjust the limit with the `payam_resan_gateway_send_timeout` filter if their panel is genuinely slow to answer.
* **A panel outage no longer takes the whole site down with it.** Previously, every login attempt during an outage tied up one of the server's limited request slots until it gave up, so a long outage could make the entire site stop responding - pages that have nothing to do with SMS included. Now, after several failures in a row, the plugin stops calling the panel for a minute and reports the failure immediately. It tries again on its own once the minute is up, and resumes normally the moment the panel answers. Nothing to switch on and nothing to reset by hand.
* This only reacts to a panel that cannot be reached. A panel that answers - even to say the API key is wrong or the credit has run out - is treated as working, so a settings mistake never stops the plugin from trying.
* You can still send test messages from the admin screens while this is active, which is how you check whether your panel is back.
* **Login, registration and form requests no longer load the admin area.** Every one of those requests used to parse the plugin's entire settings layer and run a database schema check that the response could not use. Removing that work cuts roughly 113 KB of parsing and 2 MB of memory from each request.
* The periodic update check also came off that path. It used to run on visitor requests and could make a network call while someone waited for their code. Update checks now happen on admin pages and scheduled tasks, as they should; installing updates is unchanged.
* **The Persian calendar library is only loaded when a date is actually shown.** It used to be read on every page of your site, including pages that show no dates at all. Reports, exports and the expiry time in your SMS text are unchanged.
* **The login and registration form builder is only loaded on pages that show a form.** Previously every page of your site loaded it, whether or not it had a form. Pages without a form are now noticeably lighter, and the forms themselves are unchanged - including the WooCommerce account page.
* **The security-check (CAPTCHA) code is only loaded for the provider you actually use.** If CAPTCHA is off, none of it loads at all; if it is on, only the one you selected does. Your forms, the image challenge and its refresh button all behave exactly as before.
* **Your form styling settings are no longer read on every page.** They were the largest thing the plugin loaded on every single request of your site - more than half of it - even though only a page showing a login or registration form needs them. They are now read when a form is actually displayed. Your saved styling is untouched, and the change is applied automatically when you update.

= 1.7.1 =
* Updates are now verified before they are installed. A file that cannot be proven to come from Payam Resan is refused, and nothing is installed.
* A release older than the one you are running is refused as well.
* The update service moved to its own address.

= 1.7.0 =
* **Deleting the plugin now cleans up after itself.** Removing Payam Resan from the Plugins screen always clears its scheduled tasks, temporary caches, and expired password-reset tokens - including an update-check task that used to be left running twice a day for a plugin that was no longer installed.
* **Your data is kept unless you say otherwise.** Settings, the log and OTP token tables, the mobile numbers stored on user profiles, and the SMS log files are removed only when you tick **Delete all Payam Resan data when the plugin is deleted** on the Reset tab. It is off by default, so deleting and reinstalling picks up exactly where you left off.
* The consent box has to be ticked again every time the Reset tab is saved, so permission granted once cannot be spent later by accident.
* Numbers that belong to other plugins - the WooCommerce billing phone and the Digits fields - are never touched, whichever option you choose.
* Regenerated the translation template and updated the Persian translation.

= 1.6.2 =
* **Dashboard overview widget.** Added a native WordPress Dashboard widget that shows how many SMS were sent today, how many custom forms are active, and whether the SMS gateway is configured. An on-demand "Test connection" button checks the Payam Resan panel and shows the remaining credit, and the widget links straight to Settings and Send Test. Show or hide it from the Dashboard's own Screen Options like any core widget. It loads nothing on any other admin page or on the front end, and does no work while hidden.
* **WordPress.org Plugin Check compliance.** Resolved the Plugin Check errors and warnings in the plugin's own code: escaped all output, corrected internationalization (no low-level translation calls), prefixed global variables and hook names, and used prepared statements with identifier placeholders for custom-table queries. The bundled update-checker library is exempted from these checks; update functionality is unchanged.
* **Security hardening.** Additional hardening of the password-reset, SMS transport, registration, and export paths.
* **Cleanup tools polish.** Refined the manual reset/recreate and scheduled-cleanup UI on the Reset tab: responsive two-column layout, a calendar icon and translatable placeholders on the date fields, and fixes to the OTP cleanup date filter and duplicate notices.
* Regenerated the translation template and updated the Persian translation.

= 1.6.0 =
* **Added Digits plugin compatibility.** Users who registered with the Digits plugin can now log in and receive OTP through Payam Resan's forms and shortcodes, and are found in phone-number searches - turned on with a single switch under Integrations &rarr; Digits.
* **Added a full migration path off Digits.** Preview first, then migrate every Digits number into Payam Resan's own field, with a report of how many were migrated, skipped for a conflict, or errored. A user who already has a Payam Resan number keeps it - a differing Digits number is reported, never written over it.
* **Added optional two-way number sync**: while both plugins are active, a new Digits registration or number change is copied into Payam Resan, and a Payam Resan change is copied back to Digits.
* **Added export and import.** Back up the Digits numbers to a CSV, and import a CSV - including one from another site - matching users by email, username, or user ID. Import works even without the Digits plugin installed, so you can restore onto a fresh site.
* Everything Digits-related stays completely off and loads nothing on the site unless the Digits plugin is active and the integration is enabled.

= 1.5.0 =
* **Fixed scheduled cleanup, which had never deleted a single record.** The cron ran outside the dashboard, where one of the functions it called was not loaded, so it failed on every run on every site. The log table simply grew forever.
* **Cleanup now finishes the job.** It used to delete at most 1,000 records per run with no repeat, while every SMS writes two - so on a busy site it could never catch up. It now works through the backlog in batches until the table is clean, within a safe time limit per run.
* **Added a retention setting**: keep logs for 1, 7, 30, 90, 180 or 365 days, or forever. This is separate from the cleanup interval - the interval is how often the cleanup runs, retention is how much history it keeps. Set to **forever** by default, so upgrading never deletes anything you still have.
* Added the missing **Daily** cleanup interval. It was selectable internally but had no effect.
* Fixed the cleanup deleting by the wrong time window on sites whose timezone is not UTC.
* **Added log export** on the Reports page, in Excel, CSV, JSON or PHP array. Exports exactly what your current filters show - level, date range - and optionally only the most recent N records.
* **Added user phone-number export**, under General Settings &rarr; Export. Pick the fields you need alongside the number, optionally limit to certain roles, and download as Excel, CSV, JSON or PHP array. Only users who actually have a number are included, and numbers are normalized to a single 09xxxxxxxxx format whichever field they came from.
* **Fixed the recipient missing from every non-pattern SMS log.** The number reached the SMS panel correctly but was never recorded, so Reports showed a dash where the recipient should be. OTP logins, registrations and Send Test were all affected; pattern messages were not.
* **Fixed the plugin's diagnostic log file keeping only its most recent line**, because each write replaced the file instead of adding to it.
* **The diagnostic log directory is no longer readable from the web.** It sits under uploads with a predictable name and contains phone numbers and message text.
* Fixed the Reports date filter putting a Gregorian date back into the Jalali date box, so filtering a second time produced nonsense.
* Fixed a PHP warning on the Reports page when a date was entered in an unexpected format.
* Fixed the scheduled cleanup rebuilding the whole log table on every dashboard page load, which could time out on a large table.

= 1.4.0 =
* Added a **user profile**. Once enabled, a logged-in visitor sees a small tabbed profile inside the login form instead of just a logout link: General (first and last name) and Security (password, email, mobile). Everything the plugin recorded is shown; you choose which values can be edited.
* The profile is off by default, and while it is off none of its files, endpoints or assets are loaded - it costs the site nothing.
* Changing the password asks for the current password first, and you stay logged in afterwards.
* A new mobile number is only saved after the code sent to it is confirmed, because the mobile is what OTP login uses to identify the account.
* The profile follows your Styles settings like every other form.
* Added a **Profile button** in Styles settings, shown instead of the register/login button once the visitor is logged in. Its text accepts `{display_name}`, `{user_login}`, `{first_name}` and `{last_name}`.
* Merged **Short Codes** into the Forms menu as a tab, so the plugin menu is shorter. The old Short Codes URL redirects to it.
* The shortcodes list now also shows the forms you built, marked as added by you, with a copy button and a link to the form.
* Fixed the Short Codes page loading its data under the wrong key, which silently returned an error.

= 1.3.0 =
* Added a Custom Forms admin page. Build a registration form from the fields you choose and publish it as `[pr_form id="your-form"]`. The six built-in shortcodes are untouched.
* Fields come from a catalogue: the built-in ones plus any you define yourself, which are stored as user meta.
* Added a built-in national code field with real check-digit validation.
* The username can be built from a field you nominate - a national code, for example - and is checked for validity and uniqueness before the account is created.
* Optional fields can be shown inline or collapsed into a panel the user opens.
* The OTP code field is now focused as soon as the code is sent, and can be filled from the SMS. Both are switchable under General Settings > OTP.
* Added the `{otp-domain}` placeholder. SMS autofill only works when the message's last line is `@host #code` with the host matching the page exactly, and `{domain}` strips a leading `www.`. Existing messages are unchanged; the OTP settings page validates your template and offers a one-click fix.
* Fixed IP rate limiting, which never applied: the address was bound to a numeric placeholder against a text column, so the count matched the wrong rows or none at all.
* Fixed dbDelta never applying a schema change to any table, because `CREATE TABLE IF NOT EXISTS` made it read the table name as "IF".
* Added database schema versioning, so a schema change reaches an install that is already active, and indexed the OTP token table for the rate-limit queries.
* Fixed user lookup for accounts whose username is all digits, which were previously unreachable.
* Fixed the OTP field length being read from an attribute the renderer never emitted.

= 1.2.0 =
* Added a CAPTCHA settings section under General Settings, with a per-form switch for the login, registration and password-reset flows.
* Added a built-in numeric CAPTCHA that works with no external service: a small math question drawn as an image, with an automatic plain-text fallback when the PHP GD extension is unavailable.
* Added support for Google reCAPTCHA v2 (checkbox), Google reCAPTCHA v3 (score, with a configurable threshold), hCaptcha and Cloudflare Turnstile.
* The CAPTCHA is verified server-side on every request that can send an SMS or authenticate a visitor, so it cannot be bypassed from the browser.
* Solving the CAPTCHA once covers the rest of that authentication flow, so resending an OTP code does not ask for a second challenge.
* A built-in challenge is single-use: a wrong answer consumes it and the form loads a fresh question automatically.
* Persian and Arabic digits are accepted as the built-in CAPTCHA answer.
* Fixed the site key and secret key placeholders in the CAPTCHA settings, which described each other.

= 1.1.0 =
* Added an Integrations settings section, structured so further plugin integrations can be added later.
* Added WooCommerce compatibility, with each capability behind its own toggle.
* Added SMS OTP verification of the mobile number at checkout, supporting both the classic [woocommerce_checkout] shortcode and the WooCommerce checkout block (Store API). The order is blocked server-side in both cases.
* Added SMS OTP verification on the WooCommerce my-account registration form.
* Added replacement of the WooCommerce my-account login/registration form with the Payam Resan OTP form for logged-out visitors.
* Added optional two-way sync between the plugin mobile number and the WooCommerce billing phone, including billing phone in user lookups.
* Added a duplicate-number guard: a mobile already registered to another account is rejected before the SMS is sent, so one number cannot create multiple accounts.
* The checkout phone field is forced to be required while checkout verification is enabled.
* When a capability is disabled, the plugin no longer attaches any hook to that WooCommerce flow.
* The OTP widget now reuses WooCommerce's own markup (classic classes, and wc-block-components in the checkout block) so it matches the active theme.
* Added the payam_resan_phone_meta_keys filter and the payam_resan_after_update_user_mobile action.
* Declared WooCommerce HPOS (custom order tables) and cart/checkout blocks compatibility.
* Tested with WooCommerce 10.5.2.
* Note: translation template (payam-resan.pot) should be regenerated for the new strings before release.

= 1.0.5 =
* Added plugin update system for auto-update
* update translation

= 1.0.4 =
* Remove the display of notices for other plugins on the plugin pages and disrupt the page layout.
* Added SendBulk(post) method to payam-resan gateway.
* Change the normal send method from Send to SendBulk


= 1.0.3 =
* Added some helper function to handle pattern key and param in admin area.
* Fix plugin-check warnings and errors.
* Standardization according to PHPCS and WordPress standard coding rules.
* Patch for overlap after user register with plugin and change personal information from woocommerce edit accounts page.

= 1.0.0 =
* Initial beta release prepared for the current Payam Resan codebase.
* Added Payam Resan SMS gateway configuration with account status, connection testing, normal sending, and pattern sending support.
* Added OTP login, registration verification, password reset verification, configurable OTP messages, pattern parameters, attempts, expiration, limits, and test sending.
* Added default and popup shortcodes for login, registration, and combined register/login forms.
* Added optional custom wp-login.php template for the default login screen.
* Added registration, login method, role exception, generated password, terms, and redirect settings.
* Added form style settings for logo, colors, dark/system mode, and popup button presentation.
* Added user phone-number profile fields, Users table phone column, and phone-number user lookup/search support.
* Added block lists for mobile numbers and IP addresses.
* Added SMS reports, logging tables, dashboard status views, send-test tools, reset tools, and scheduled cleanup for logs and OTP tokens.
* Added Persian translation assets, Jalali date support, and message placeholders for site/domain/time/OTP values.

= 0.3.0 =
* Activated custom WordPress login template support.
* Improved handling and testing for server IP access restrictions.

= 0.2.0 =
* Implemented popup shortcodes and tested plugin functionality.
* Added data helpers for plugin settings and repeatable content.
* Cleaned up and fixed shortcode registration and callback functions.

= 0.1.0 =
* Initial release with basic SMS sending through the Payam Resan panel.
* Added shortcode enable/disable options.

== Upgrade Notice ==

= 1.7.2 =
Logging in with a mobile number is now fast on sites of any size, and a failing SMS panel no longer holds visitors for twenty seconds. No settings change.

= 1.7.1 =
Updates are now verified before installation. Nothing changes in how you update.

= 1.0.0 =
Initial beta release with Payam Resan SMS gateway settings, OTP authentication, custom forms, wp-login replacement, reports, blocking, tests, and cleanup tools.

== License ==

This plugin is licensed under GPLv2 or later. See https://www.gnu.org/licenses/gpl-2.0.txt.

You are free to use the plugin on any site, modify the source code, and share or distribute copies.

You must retain the copyright notice and attribute the original author, Payam Resan.

== Bundled Libraries ==


== Credits ==

* Developed by Payam Resan.
* Vazir font by Rastikerdar, licensed under the SIL Open Font License (OFL).
* Jalali date support is provided by the bundled JDF library.
* Plugin Update Checker — https://github.com/YahnisElsts/plugin-update-checker
    * Copyright (c) 2023 Jānis Elsts
    * Licensed under the MIT License. The full MIT license text is included in the plugin README.

== Support ==

For documentation, issues, or feature requests, use the following links:

* Website: https://payam-resan.com/
* Plugin URI: https://payam-resan.com/lab/payam-resan/
* Contact: https://payam-resan.com/contact/
* GitHub: https://github.com/payam-resan
* Support email: plugin@payam-resan.com
* Phone: 021-91012767, 021-74912
