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.
Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/waqasdev01/wise-mirror-booking/archive/refs/heads/main.zipPlugin 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.
} );