=== WALoops OTP Login ===
Contributors: bhagirath25
Tags: whatsapp login, otp login, passwordless login, phone login, whatsapp otp
Requires at least: 5.8
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.6.3
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Add WhatsApp OTP login & registration to WordPress in minutes. 100 free OTPs/month, no credit card required.

== Description ==

**Add WhatsApp OTP login to WordPress in 2 minutes.**

A password-free login and registration option, delivered over WhatsApp — works alongside your existing login form, on any theme, with or without WooCommerce. No password to remember, no password to reset, no password to leak.

**Free forever — 100 OTPs/month**

Get a free API key by creating a WALoops account from the plugin's settings page (no credit card) and start sending real WhatsApp OTPs from WALoops' shared WhatsApp number immediately.

= Everything you need, nothing you don't =

* **Adds, never replaces** — sits alongside your normal login and registration forms as an extra option, never in place of them
* **3 ready-made designs** — Modern, Minimal, and Card form styles, pick one in Settings, no CSS required
* **Works in minutes** — no Meta Developer account needed on the Free or Introduction (shared-number) plans
* **Bring your own number** — Introduction and Starter Ecommerce plans let you send from your own WhatsApp Business number
* **WooCommerce checkout** — offer WhatsApp OTP verification right at checkout on the Starter Ecommerce plan
* **Email code login too** — optionally offer a 6-digit code sent by email alongside (or instead of) WhatsApp; it uses your site's normal email, needs no API key, and a new email address becomes a new account
* **Shortcode-friendly** — place the form anywhere with `[wa_otp_login]`, or drag it on as a native **Elementor** widget or **WPBakery Page Builder** element
* **Spam-proof your other forms** — a "WhatsApp OTP Verify" field/tag for **Contact Form 7**, **WPForms**, **Gravity Forms**, and **Formidable Forms** blocks submission until the visitor's number checks out (experimental for Ninja Forms — test before relying on it)
* **Goes global** — a country dial-code selector on every phone field, pre-filled from the visitor's browser language and always changeable
* Fully translatable (text domain: `waloops-otp-login`)

= Simple, WordPress-friendly pricing =

Start free. Upgrade only when you actually need more volume or your own number.

* **Free — $0/month.** Everything you need to try WhatsApp OTP login on a live site: 100 OTPs/month from our shared number, all 3 form styles, login/register/shortcode placement.
* **Introduction — $5/month.** For sites outgrowing the free quota: 1,000 OTPs/month, use our shared number or connect your own, switch any time.
* **Starter Ecommerce — $9.99/month.** For high-traffic and WooCommerce sites: unlimited OTPs on your own connected WhatsApp Business number, checkout verification, and priority support.

= How it works =

1. Install and activate the plugin.
2. Go to **Settings > WhatsApp OTP Login** and click **Create a free WALoops account** — you'll land straight on your API key. Paste it into the field on this same settings page.
3. Pick where the OTP option should appear (login page, registration page, WooCommerce checkout) — or drop the `[wa_otp_login]` shortcode anywhere.
4. Done. Visitors can now log in or register with just their WhatsApp number.
5. To verify a phone number on your own Contact Form 7, WPForms, Gravity Forms, or Formidable Forms form instead: add a `[wa_otp_verify your-phone]` tag in the CF7 form editor, or drag/add the "WhatsApp OTP Verify" field into the other three builders — no extra setup needed once your API key is saved.
6. Using Elementor or WPBakery Page Builder? Drag the "WhatsApp OTP Login" widget/element onto any page instead of using the shortcode.

== External services ==

This plugin connects to WALoops' WhatsApp OTP service (**https://app.waloops.com/**) to deliver one-time-password messages over WhatsApp. This connection is required for the plugin to work — there's no way to send a WhatsApp message without it.

What is sent, and when:

* **Creating a free WALoops account**: when you click "Get Free API Key" on the settings page, the email address and WhatsApp number you typed, and your site's URL, are sent to WALoops to create your account and return your API key. Nothing is sent until you click. If you also tick the optional box "Message me on WhatsApp to help me set this up" (unchecked by default), WALoops may send you a WhatsApp message on that number to help with setup; you can reply STOP at any time.
* **When a visitor requests a verification code** (login, registration, checkout, or a Contact Form 7 / WPForms field): their phone number is sent to WALoops so it can send them a WhatsApp message containing the code.
* **When a visitor submits a code**: their phone number and the code they entered are sent to WALoops to be checked.
* **On the settings page**: your account's monthly usage is fetched from WALoops so it can be shown to you. If you choose to connect your own WhatsApp Business number, your Meta access token and phone number ID are also sent to WALoops, to verify and store for your account.
* **Abandoned Cart Reminder** (Starter plan, off by default — only shown to eligible accounts): once turned on, a shopper's phone number, name, and cart contents (items, total, currency, coupon codes, checkout URL) are sent to WALoops after they enter a phone number at checkout and leave without ordering within the configured wait time, so it can send them a WhatsApp reminder. This is decided and timed entirely on your own site (WordPress schedules and cancels the check itself); nothing is sent to WALoops while a shopper is still actively checking out.

* **Email code login** (optional, off by default): the code is generated and checked entirely on your own site. Nothing about it is sent to WALoops.

**Email delivery** (Settings > Email delivery): by default the code email goes out through WordPress's own `wp_mail()` (and any SMTP plugin you use), or PHP `mail()` if you pick it. If you instead pick an email provider (Gmail, Outlook / Microsoft 365, Zoho, Yahoo, SendGrid, Brevo, Mailgun, Amazon SES, Postmark) or your own SMTP server, the plugin logs in to that provider's SMTP server with the credentials you enter and hands it the visitor's email address and the message, only when a code is sent (or when you click Send test). Nothing is sent to a provider you haven't chosen. Their terms apply: [Google](https://policies.google.com/terms), [Microsoft](https://www.microsoft.com/servicesagreement), [Zoho](https://www.zoho.com/terms.html), [Yahoo](https://legal.yahoo.com/us/en/yahoo/terms/otos/index.html), [SendGrid](https://www.twilio.com/legal/tos), [Brevo](https://www.brevo.com/legal/termsofuse/), [Mailgun](https://www.mailgun.com/legal/terms/), [Amazon SES](https://aws.amazon.com/service-terms/), [Postmark](https://postmarkapp.com/terms-of-service).

No data is sent to any other third party. See WALoops' [Terms of Service](https://waloops.com/terms) and [Privacy Policy](https://waloops.com/privacy-policy).

== Frequently Asked Questions ==

= Do I need a Meta/Facebook Developer account? =

No — not on the Free or Introduction plans, where you can send from WALoops' shared WhatsApp number. Connecting your own number (optional on Introduction, required on Starter Ecommerce) does require your own WhatsApp Business Cloud API app.

= What happens when I run out of free OTPs for the month? =

Sends are blocked with a clear "monthly limit reached" message until the next calendar month starts, or until you upgrade.

= Does this replace my normal login form? =

No. It's added as an extra option alongside your existing username/password login and registration forms.

= Can I customize the WhatsApp message text? =

WhatsApp only allows pre-approved wording for one-time-passcode (Authentication-category) messages — you can't write fully custom copy. Right now the message is "{code} is your verification code. For your security, do not share this code." More variants (with an expiry line, code-only) will appear as an option once they're approved with Meta.

= Is my visitors' data safe? =

Phone numbers and OTP codes are transmitted over HTTPS and are used solely to deliver and verify the one-time password. See the "External services" section above for exactly what's sent and when.

= Why did an abandoned-cart reminder go out later than the wait time I set? =

The reminder timer is scheduled on WordPress's own cron, which only actually runs when someone visits your site — on a low-traffic store that can mean a delay past the configured wait time. For reliable, on-time delivery, ask your host to add a real server cron job that calls your site's `wp-cron.php` every minute or two (most hosting control panels have a "Cron Jobs" section for this) — the exact URL to use is shown on the Settings page once Abandoned Cart Reminder is available on your account.

= Does this work with international phone numbers? =

Yes. Every phone field has a country dial-code selector (pre-filled from the visitor's browser language, always changeable) — there's no restriction to a specific country, as long as WhatsApp is reachable at that number.

= How does the form-builder verification work? =

Add the `[wa_otp_verify your-phone]` tag (CF7) or the "WhatsApp OTP Verify" field (WPForms, Gravity Forms, Formidable Forms) to your form. A visitor enters their WhatsApp number, receives a code, and enters it — the form can't be submitted until that check passes. No phone number is stored by this plugin for this feature; verification is a signed, short-lived token checked again on submission, the same way a nonce works.

= Which form builders are supported? =

Contact Form 7, WPForms, Gravity Forms, and Formidable Forms are fully supported. Ninja Forms support is experimental — please test it on your own site before relying on it. Elementor Pro Forms, Fluent Forms, and SureForms aren't supported yet.

== Screenshots ==

1. Settings page — usage, API key, message style, and where the OTP form shows.
2. The "Modern" front-end OTP form on a login page.
3. The "Card" front-end OTP form style.
4. Usage dashboard showing your monthly OTP quota.

== Changelog ==

= 1.6.3 =
* New: sites on the Free plan see a card on the settings page explaining the Introduction plan ($5/month), which sends login codes from your own WhatsApp Business number (your name and logo) instead of the shared number. Shown only on this plugin's settings page; paid plans never see it.

= 1.6.2 =
* New: bundled translations for Spanish (Spain, Mexico, Colombia, Argentina, Chile, Peru), Portuguese (Brazil), French, German, Italian, Arabic, Hindi, Indonesian and Turkish. The login form, messages and settings screens follow the site language (Settings > General > Site Language). Community translations from translate.wordpress.org take priority once they exist.
* Fix: the Ninja Forms field and a few settings-screen messages were always shown in English.

= 1.6.1 =
* New: once 20 visitors have signed in with a WhatsApp code, administrators see a one-time notice asking for a review on WordPress.org. "Maybe later" hides it for 30 days; "Leave a review" or "I already did" hides it for good.
* New: `wa_otp_login_verified` action, fired after a visitor signs in with a WhatsApp code.

= 1.6.0 =
* New: optional **Email code (OTP)** login and registration, next to WhatsApp OTP. A visitor enters their email, gets a 6-digit code (valid 10 minutes, single use, 5 wrong tries before it's burned, rate-limited per email and per IP) and is logged in — a first-time email becomes a real account (a WooCommerce customer on WooCommerce sites), an existing one is logged in. Sent with `wp_mail()`, so it needs no API key and works even if you only want email. Off by default; turn it on under **Login methods** in Settings. When both methods are on, the form gets WhatsApp | Email code tabs and you choose which opens first. New filter `wa_otp_login_email_login_allowed` lets you keep email-code login away from sensitive accounts (e.g. administrators).

= 1.5.0 =
* New: Abandoned Cart Reminder (Starter plan, off by default) — sends a WhatsApp reminder when a WooCommerce shopper enters a phone number at checkout and leaves without ordering. WordPress itself decides when a cart counts as abandoned (a delayed check, rescheduled on every checkout change and cancelled once the order completes) and reports it once to your WALoops account, which fires it as a "Checkout Abandoned" Connection under Automation — build the actual reminder message there. Works independently of whether the WhatsApp OTP login widget itself is enabled. Also now tracks email-only carts (no phone entered) with a channel flag for a future email reminder — not yet sent.

= 1.4.0 =
* New: "Form Builder" admin page — the Login form stays fixed to just a Mobile Number (that's all OTP login needs), but the Register form now always collects a Full Name alongside the Mobile Number, plus a tick-box list of optional fields (Email, Company Name, Address, City, Date of Birth) you can turn on. Collected values are saved to the new WordPress account: Full Name sets the account's name/display name, Email replaces the generated placeholder address, everything else is stored as user meta.

= 1.3.1 =
* Fix: the new "WordPress Login Page" setting (see 1.3.0) was checked via `class_exists( 'WooCommerce' )` at a point in the load order where it was always false — WordPress loads plugin files alphabetically by folder name, and "waloops-otp-login" sorts before "woocommerce", so the check ran before WooCommerce's own file had loaded. This silently fell back to the non-WooCommerce behavior, meaning wp-login.php kept showing WhatsApp OTP as primary even with the new setting off. The check now runs on `plugins_loaded` instead, after every plugin has loaded.

= 1.3.0 =
* New: "Get Free API Key" is now a real quick-start — enter your email (pre-filled from your WordPress admin email) and WhatsApp number and get a working API key immediately, without leaving wp-admin or visiting app.waloops.com. This also creates your WALoops account behind the scenes (with a generated password emailed to you) so you can log in later to upgrade your plan — you're never asked to type a password during setup.
* Fix: on WordPress 5.7+ (which wraps the password field's label separately from its input+show/hide button), the 1.2.0 "OTP is primary" redesign only hid the input, leaving the "Password" label behind on its own — looked like a disabled/broken field. The whole password field now hides together.
* Change: on WooCommerce sites, wp-login.php (Login/Registration) is now a separate "Show on" setting from WooCommerce's own My Account login/register, and defaults off — wp-login.php is WordPress's backend login, typically used by site staff/admins, not customers, so it no longer gets the "OTP is primary" treatment unless explicitly turned on.

= 1.2.0 =
* Change: WhatsApp OTP is now shown as the *primary* login/register option wherever it's enabled, with the site's own username/password fields tucked behind a small "Log in with username & password instead" link (togglable in Settings, or turn it off entirely to make WhatsApp OTP the only way in).
* Fix: the country dial-code default was guessed from the browser's UI language (`navigator.language`), which is often wrong (e.g. an English-Canada browser locale showing +1 Canada regardless of where the visitor actually is). Now resolved server-side from a CDN geo header when present, falling back to the WooCommerce store's base country or the site's own locale, with a browser-timezone-based refinement on the front end — the same method used by WALoops Quick Checkout.
* Fix: a visitor who is already logged in now sees a "You are already logged in as X. Log out?" notice in place of the form (same idiom wp-login.php itself uses), instead of the widget silently rendering nothing.
* Fix: login and register are now the same flow — a WhatsApp number that verifies successfully but has no linked account gets one created automatically, regardless of which form (login or register) the visitor used. Previously the plain login form dead-ended first-time numbers with "No account is linked to this WhatsApp number yet. Please register first.", which made no sense once OTP verification is itself the proof of identity.
* New: real design pass on the "Modern"/"Minimal"/"Card" styles — a proper title and description, and buttons that use WordPress's own `button`/`button-primary` classes so they pick up the active theme's (or wp-admin login screen's) real button color instead of a hardcoded WhatsApp-green.
* New: `wa_otp_login_show_logged_in_notice`, `wa_otp_login_form_title`, `wa_otp_login_form_description` filters and `wa_otp_login_before_render`/`wa_otp_login_after_render` actions — see HOOKS.md.

= 1.1.1 =
* Fix: "Show on Login page" / "Show on Registration page" now also appear on WooCommerce's own My Account login/register tabs (`woocommerce_login_form` / `woocommerce_register_form`), not just wp-login.php. Previously, on WooCommerce sites (which almost always use the My Account page instead of wp-login.php), the widget silently never appeared even with the settings enabled.

= 1.1.0 =
* New: phone verification for Contact Form 7 (`[wa_otp_verify your-phone]` form-tag), WPForms, Gravity Forms, and Formidable Forms ("WhatsApp OTP Verify" field) — blocks form submission until the visitor's WhatsApp number is OTP-verified.
* New: experimental Ninja Forms support for the same feature.
* New: native Elementor widget and WPBakery Page Builder element for the login/register form.
* New: country dial-code selector on every phone field (login, registration, checkout, and the new form fields), defaulting from the visitor's browser language.

= 1.0.0 =
* Initial release: Free/Introduction/Starter plans, login/register/WooCommerce-checkout integration, three form styles, own-number connection.

== Upgrade Notice ==

= 1.6.0 =
Adds an optional email-code login method (off by default — enable it under Login methods in Settings).

= 1.2.0 =
WhatsApp OTP is now the primary login/register option (password fields move behind a small toggle, configurable in Settings); also fixes wrong country-code defaults and the form showing to already-logged-in visitors.

= 1.1.1 =
Fixes the widget not appearing on WooCommerce's My Account login/register page.

= 1.1.0 =
Adds Contact Form 7 and WPForms phone-verification support.

= 1.0.0 =
Initial release.
