Wise Mirror Configuration
A professional WordPress booking and Stripe payment plugin for The Wise Mirror. Features include dynamic package selection, secure Stripe payments, booking calendar, image uploads, email notifications, booking management, customizable HTML/CSS/JS, and a modular, scalable architecture for future enhancements.
by Waqas Anwar · github.com/waqasdev01/wise-mirror-booking · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/waqasdev01/wise-mirror-booking/archive/refs/heads/main.zipReadme
=== Wise Mirror Configuration === Plugin Name: Wise Mirror Configuration Author: Waqas Anwar Requires at least: WordPress 5.8 Requires PHP: 7.4 Version: 1.0.0
Manages the complete booking and Stripe payment workflow for thewisemirror.com.
== 1. Installation ==
-
In WordPress admin, go to Plugins -> Add New -> Upload Plugin.
-
Choose wise-mirror-booking.zip and click Install Now, then Activate.
-
A new "Wise Mirror" menu item appears in the left admin sidebar.
-
Go to Wise Mirror -> Stripe Settings and enter your Stripe Test keys (Publishable Key + Secret Key). Leave Mode set to "Test Mode" while you're testing.
-
Go to Wise Mirror -> Pricing Settings and confirm/adjust your four packages (Complimentary Discovery Call, Focused Reading, Comprehensive Reading, Deep Dive Reading).
-
Go to Wise Mirror -> Booking Settings and set your working hours, enabled days, and appointment duration.
-
Go to Wise Mirror -> Email Settings and choose WordPress default mail or SMTP (Gmail). If using SMTP, generate a Gmail App Password at https://myaccount.google.com/apppasswords (do NOT use your regular Gmail password).
-
Create (or edit) the booking page in Elementor and drop in a Custom HTML / Shortcode widget containing:
[wise_booking]
Optionally create a separate "Booking Confirmed" page containing:
[wise_booking_status]
Stripe will redirect back to whichever page contains [wise_booking] after payment; that page verifies status inline. If you'd rather send customers to a dedicated confirmation page, that's what [wise_booking_status] is for — drop it on its own page and the plugin's Stripe redirect will carry the booking reference/token automatically once you enable "Success/Failed page selection" in a future update (v1.0.0 confirms on the booking page itself by default).
== 2. Connecting Stripe Webhooks (required) ==
- In your Stripe Dashboard, go to Developers -> Webhooks -> Add endpoint.
- Endpoint URL: copy the URL shown at the top of Wise Mirror -> Stripe Settings (looks like https://yoursite.com/?wmb_stripe_webhook=1).
- Select these events: payment_intent.succeeded, payment_intent.payment_failed, payment_intent.canceled.
- Save, then copy the "Signing secret" Stripe shows you.
- Paste it into Wise Mirror -> Stripe Settings -> Webhook Signing Secret (Test Keys section for a test-mode endpoint, Live Keys section for a live-mode endpoint).
- Repeat steps 1-5 for Live Mode once you're ready to go live.
- The "Webhook Status" panel at the top of the Stripe Settings tab shows Connected / No events received yet for each mode, updated automatically the moment Stripe successfully delivers an event.
== 3. Folder Structure ==
wise-mirror-booking/ |-- wise-mirror-booking.php Main plugin file. Defines constants, | registers the autoloader, and boots | WMB_Loader on the 'plugins_loaded'- | adjacent init sequence. | |-- uninstall.php Runs ONLY when the plugin is deleted | (not on deactivate). Wipes tables and | uploaded images only if the admin | chose "Delete all data" under | Wise Mirror -> Permissions. | |-- includes/ Core logic, shared by admin + public. | |-- class-wmb-activator.php Runs on activation: creates DB | | tables, seeds default packages | | and settings. | |-- class-wmb-deactivator.php Runs on deactivation: clears the | | scheduled daily cron event. | |-- class-wmb-loader.php Boots every subsystem by calling | | each class's register_hooks(). | | This is the file to edit when | | wiring in a brand-new module. | |-- class-wmb-database.php Table schemas (dbDelta) + table | | name helpers. | |-- class-wmb-logger.php Writes/reads the activity log table. | |-- class-wmb-pricing.php Reads/writes the dynamic package | | list (option: wmb_packages). | |-- class-wmb-availability.php Calendar/time-slot generation from | | the admin's working-hours settings. | |-- class-wmb-uploads.php Validates and stores the booking | | photo uploads in a protected | | (non-web-accessible) folder. | |-- class-wmb-booking.php CRUD + status transitions for a | | booking row. | |-- class-wmb-payment.php CRUD + status transitions for a | | payment row. | |-- class-wmb-stripe.php Minimal Stripe REST client: create | | Payment Intent, retrieve Payment | | Intent, verify webhook signatures. | | No Composer/SDK dependency. | |-- class-wmb-webhook.php Listens for Stripe webhook POSTs | | at ?wmb_stripe_webhook=1, verifies | | the signature, and updates booking/ | | payment status + sends emails. | |-- class-wmb-ajax.php All front-end AJAX endpoints: get | | package, get available slots, | | submit booking, check payment | | status (the safeguard that stops | | the success page being reachable | | by URL alone). | |-- class-wmb-email.php Sends every customer/admin email, | | switches wp_mail between default | | and SMTP transport. | |-- class-wmb-export.php Streams Bookings/Payments as CSV. | |-- admin/ Everything shown in wp-admin. | |-- class-wmb-admin.php Registers the "Wise Mirror" menu, | | tab routing, and processes every | | settings form submission. | |-- css/admin.css, js/admin.js Admin styling + the image-preview | | toggle script. | |-- partials/ One file per tab: dashboard.php, | bookings.php, payments.php, | pricing.php, booking-settings.php, | stripe.php, email.php, | custom-code.php, logs.php, | tools.php, permissions.php. | |-- public/ Everything shown to site visitors. | |-- class-wmb-public.php Registers [wise_booking] and | | [wise_booking_status], enqueues | | front-end CSS/JS, prints the | | admin's Custom Code. | |-- partials/booking-form.php The two-column booking form + | | summary card markup. | |-- partials/status-page.php The dedicated confirmation-page | | markup (used with the second | | shortcode). | |-- css/public.css Front-end styling, built from the | | brand color palette. | |-- js/public.js Package selection, calendar/slot | | loading, form submit, Stripe | | Payment Element, and the polling | | logic that verifies payment status | | before ever showing "confirmed". | |-- templates/emails/ One HTML template per notification: | admin-notification.php, | booking-confirmation.php, | payment-successful.php, | payment-failed.php, | payment-cancelled.php. | |-- languages/ Empty; ready for a wise-mirror- | booking.pot translation file.
== 4. How payment verification works (important) ==
The booking form never marks itself "paid" on its own. Two independent checks both have to agree before a customer ever sees a confirmed booking:
-
Stripe Webhook (class-wmb-webhook.php) — the authoritative path. Stripe calls your site directly, server-to-server, the moment a payment intent succeeds/fails/cancels. The plugin verifies the request's cryptographic signature before trusting it at all.
-
Direct verification (class-wmb-ajax.php: check_payment_status) — whenever the booking page or confirmation page loads (including a raw URL visit, refresh, or a booking ID guessed from another confirmation), the plugin re-asks Stripe directly "what is the real status of this Payment Intent right now?" before showing anything. A booking + a matching random access token both have to be present and valid, so a URL can't be replayed for a different booking.
Only when one of these two checks confirms succeeded does the
plugin flip the booking to Paid, send the confirmation emails, and
show the success state.
== 5. Extending the plugin later ==
Future modules — Promo Codes/Discounts, additional payment gateways,
a Customer Dashboard, PDF invoices, SMS notifications, REST API
endpoints — can be added as new classes in includes/ (or a new
includes/modules/ folder) and wired in from class-wmb-loader.php
without modifying existing files. The wmb_loaded action fires once
everything else is registered, specifically so a future module can
hook in cleanly:
add_action( 'wmb_loaded', function () {
// Register a new module here.
} );