The From: address of an email can be forged by anyone (spoofing) — an allowlist alone is not sufficient protection for something that can email your whole membership. The required subject keyword acts as a shared secret: only people who know it can trigger a send, even if they forge an authorized From: address. The keyword is stripped from the subject before delivery, so members never see it. Audience & attachments Embed a code filter in the subject to target membership levels: Meeting notes [codes: GOLD,SILVER]. The tag is removed before delivery. Without a tag, the membership code filter saved on the Group Email page is applied; if that filter is blank, all members receive the message. Works on both main and chapter sites. Chapter sites always send to the local chapter roster only — the code filter narrows within that roster. Attachments are uploaded to the media library. Files up to 5 MB are attached to the outgoing email; larger files (and any additional attachments beyond the first) become download links in the message body. Processed messages are marked read in the mailbox and their Message-IDs are recorded, so nothing is ever sent twice. Messages from unauthorized senders are marked read, logged (when WP_DEBUG is on) and never sent. Sales — sell memberships The Sales half of GSheet Membership puts a configurable buy-membership picker on any page and, on a successful payment, writes (or updates) the buyer's row in your private Google Sheet so they can immediately log in on your site. Three-dimensional picker — buyers choose Delivery (Electronic / Print / Both) × Duration (1-Year and Lifetime included by default; the Pro add-on adds custom levels and multi-year durations) × Postage (Regular, First Class, Canada/Mexico, Other International), and the price updates live as they change selections. Discrete pricing matrix — you set the exact price for every saleable combination in the admin. Leave any cell blank to mark that combination as not for sale: it is hidden / disabled in the picker and rejected server-side. Stripe Checkout — redirect-style checkout (no card form on your site, lower PCI burden). Test mode and Live mode are stored side-by-side and toggled with one switch. PayPal Standard Checkout (requires the Pro add-on) — buyers can optionally pay via PayPal instead of (or in addition to) Stripe. Both gateways share the same sheet-upsert pipeline, purchase log, and retry UI. Sheet upsert — on a verified webhook the plugin first tries to update the row that matches the buyer's email; if there is no row, it appends one. Renewals extend an existing future expiration date instead of overwriting it. Reliable webhook pipeline — per-event and per-email atomic locks prevent duplicate writes from concurrent webhook redeliveries; a sheet-write failure returns HTTP 500 so Stripe retries, and the admin sees a notice + a per-row "Retry" button on the Recent Purchases page. Buyer confirmation email — configurable subject and body with placeholders (first name, selection, amount, code, expiration, login URL, etc.). Admin direct-apply — a separate [gsms_admin_picker] shortcode lets a site administrator apply a membership directly to the sheet without taking a payment, perfect for comp memberships, cheque / cash-in-hand orders, or back-filling an old purchase. Requirements PHP 7.4+ with the standard openssl, hash, and mbstring extensions (already enabled on every major WordPress host). WordPress 5.9+. The access features (above) configured with a working Service Account — the sales pipeline reuses the same sheet-writer. A Stripe account for live or test payments (a PayPal developer account too, if you use the Pro add-on's PayPal checkout). Selling setup Configure the Access settings first (see Installation above) and confirm the Save & Test Connection button succeeds. Go to Membership Sales in the WordPress admin sidebar. Every sub-page shows a "Setup" notice at the top reminding you which shortcode to put on which page. Fill in the Pricing Matrix, Code Matrix, and Payments sub-pages (see Configuration below). Create a page containing only the shortcode [gsma_membership_purchase] — that is the buy page. Create (or pick) a thank-you page and put [gsma_user_info] on it (that shortcode shows the buyer's status with an inline login form when they're not yet signed in). Select that page on the Settings sub-page under Thank-you page. Shortcodes [gsma_membership_purchase] — renders the full picker (Delivery × Duration × Postage) with live total and the Buy membership button. Place this on the page where you want to take payments. [gsms_admin_picker] — admin-only variant of the picker that applies a membership directly to the sheet without any payment. Only site administrators (manage_options) see it; everyone else sees a polite notice. [gsma_user_info] — place it on the thank-you page so buyers see their membership status (and an inline login form when they aren't logged in). Admin sub-pages Pricing Matrix — one row per Delivery, one column per Postage, repeated for each Duration. Type a price into a cell to make it saleable; leave it blank to make that combination unavailable. Code Matrix — the membership code written into the buyer's sheet row for each combination. Every membership level gets its own row of codes. Levels beyond the built-in 1-Year and Lifetime, including multi-year durations, can be added with the Pro add-on; each new level gets its own Code Matrix row automatically. A warning is shown if any saleable cell is missing a code. Payments — enable/disable Stripe (and PayPal, with the Pro add-on); mode switch (Test / Live for Stripe, Sandbox / Live for PayPal); keys and secrets for each mode; webhook signing secret. The webhook URLs are shown here for paste-into-Stripe (and paste-into-PayPal with Pro). Settings — currency, thank-you page picker, optional override of the access settings' expiration column, buyer confirmation email templates, and option labels. Recent Purchases — the last 200 webhook deliveries with their outcome (gateway status + sheet status). Failed sheet writes show an inline Retry form; completed Stripe and PayPal rows show a Refund control. Configuration: webhooks Stripe: Developers → Webhooks → Add endpoint. Paste the Stripe Webhook URL shown on the plugin's Payments page (ends in /wp-json/gsma-sales/v1/stripe-webhook). Subscribe to a single event: checkout.session.completed. Copy the resulting Signing secret (whsec_...) and paste it into Webhook signing secret on the plugin's Payments page. PayPal (requires the Pro add-on): In the PayPal developer dashboard → My Apps & Credentials → your app → Add webhook. Paste the PayPal Webhook URL shown on the plugin's Payments page (ends in /wp-json/gsma-sales/v1/paypal-webhook). Subscribe to CHECKOUT.ORDER.APPROVED and PAYMENT.CAPTURE.COMPLETED. Copy the resulting Webhook ID into the matching field on the plugin's Payments page.