Dette er en demoshop til testformål. Ordrer vil ikke blive gennemført. Luk

Videre til indhold
  • Forside
  • Løsninger
    • Hjemmesider
    • Nyhedsbreve
    • Webshop
    • WordPress
  • Webalive plugin
  • Kundeportal
  • kontakt
  • Forside
  • Løsninger
    • Hjemmesider
    • Nyhedsbreve
    • Webshop
    • WordPress
  • Webalive plugin
  • Kundeportal
  • kontakt

© 2019

Webalive plugin

← Tilbage til alle plugins

WebAlive Members Premium

WebAlive Members Premium

Af WebAlive

Faktureringsfrekvens
DKK 127,00 per måned EUR 17,00
DKK 1.219,20 pr. år EUR 163,20
  • Sikker betaling med Stripe
  • Priserne inkluderer moms
  • Annuller når som helst i din kundeportal
Komplet medlemsstyring til foreninger. Håndtér medlemmer, begivenheder, betalinger, import/eksport og automatisk synkronisering med WordPress-brugere.
  • Automatiske opdateringer af dine licenserede domæner
  • Licens til op til 3 domæner
  • Administrer dine domæner og fakturaer i kundeportalen
Version 1.11.21 WP 5.0+ Testet 7.1.3 PHP 8.0+ Opdateret 2026-10-30

Beskrivelse

WebAlive Members Premium er et omfattende plugin designet til at hjælpe foreninger med at administrere deres medlemmer, begivenheder, betalinger og integration med WordPress-brugere. Nøglefunktioner:
  • Administrér medlemmer med detaljerede profiler
  • Synkronisér wa_members-tabellen med WordPress-brugere
  • Opret, opdatér eller fjern brugere automatisk baseret på medlemshandlinger
  • Importér medlemmer fra CSV eller ekstern MSSQL-database
  • Moderniseret import-workflow med AJAX-feedback
  • Importoversigt med statistik (tilføjede/overspringede/opdaterede/fejl)
  • Valgfri Fortryd Import & Logfremviser for sporbarhed
  • Håndtér begivenheder, aktiviteter og betalingsdata
  • AJAX-drevet adminpanel med Bootstrap UI
  • Responsive modaler til redigér/fjern/genaktivér medlem
  • Valgfri Mailchimp-integration til nyhedsbreve og automation
WebAlive Members Premium Køb nu
Ændringslog

1.11.21 (2026-10-23)

  • Fix: Re-release of the 1.11.20 changes, since the 1.11.20 build was installed without them. The checkout login inline script no longer contains & characters.

1.11.20 (2026-10-23)

  • Fix: The checkout login inline script no longer contains & characters, so it cannot break when the output is HTML-encoded (SyntaxError at &&).

1.11.19 (2026-10-20)

  • New: Members now store address coordinates (WGS84) in new GeoLat / GeoLon columns of the members table, added automatically on update.
  • Change: Coordinates are saved when an address is picked in the address selector: admin add/edit member, new-member sign-up forms (QuickPay, WooCommerce, Vipps) and the member portal profile. Typing an address manually clears the stored coordinates until a suggestion is picked.

1.11.18 (2026-10-19)

  • Fix: Address autocomplete (admin member forms, sign-up forms and member portal) works again. DAWA closed on October 1, 2026; the plugin now bundles a small selector that uses KDS Adressevaelger, the official successor. It does not require WooCommerce.
  • External service: while typing a Danish address, the search text and the visitor's IP address are sent from the browser to https://adressevaelger.dk, using KDS's public token. Data terms: https://dataforsyningen.dk/terms.

1.11.17 (2026-10-18)

  • Feature: Protect the member portal password sign-in, one-time code sign-in, password reset request and the new member sign-up forms with Webalive CAPTCHA (wa-captcha). Enable each form under CAPTCHA → Custom Forms. When a form is enabled there it replaces the built-in reCAPTCHA v3 check for that form; forms that are not enabled keep their existing behaviour.

1.11.16 (2026-10-17)

  • Fix: Mailchimp address sync (including Mailchimp Audit "Add") no longer skips ADDRESS when the WAMP member has no Country; it uses the Default country setting, or DK when that is blank.

1.11.15 (2026-10-17)

  • Fix: Mobile member cards no longer truncate values (e.g. email) to the per-column max width; long values wrap.
  • UI: Every card field, including address, zip, city, mobile and status, now shows its label in an aligned two-column layout.

1.11.14 (2026-10-17)

  • Fix: Member cards on mobile now always show Show / Edit / Remove (Unremove) buttons, even when the Name column is not enabled in the list columns.
  • Fix: Card labels no longer overlap their values on mobile.

1.11.13 (2026-10-17)

  • UI: Make the member list, member cards, and show/edit/remove dialogs usable on mobile screens.
  • Fix: Remove duplicate member detail and edit modal containers so actions target a single dialog.

1.11.12 (2026-10-14)

  • Fix: Corrected character encoding in the plugin readme.

1.11.11

  • Tested with WordPress 7.1.3 and WooCommerce 11.1.2.

1.11.10 (2026-09-16)

  • Fix: Clicking a pagination link on page=wam-premium dropped the current sort back to the default. wamPremium.list.update() only merged the caller's data payload, so pagination clicks sent paged alone and the PHP handler fell back to wam_premium_list_default_sort_column + wam_premium_list_default_sort_order. update() now merges wamPremium.list._state.orderby and _state.order (if set) into every AJAX call, matching what display() already did, so the sort survives page navigation, edits, removes, unremoves and status/del filter changes.

1.11.9 (2026-09-15)

  • New: The admin member edit modal now supports editing the second household member for categories flagged wam_cat_duplicate = 1, in parity with the front-end sign-up form and portal. When the modal opens, WA_Members_Premium_Members_Ajax::fetch_member_edit_form_callback() looks up the sibling wam_members row (same MemberNo, different id, DelReason = 0) and pre-fills the previously-empty Fname2 / Lname2 / Email2 fields with the current member 2 values, plus a hidden member2_id. The change handler on #memberstatus is triggered right after the form is injected so the fields show automatically when the current category is a duplicate category.
  • Change: update_member_detail_callback() now UPDATES the existing member 2 row (name + email) and mirrors the primary member's shared fields (Address, Zip, City, Mobile, Phone, Area, MemberStatus, NextPayment) to it, instead of only INSERTing a new sibling on first save. If no sibling exists yet and all three member 2 fields are provided, the row is inserted as before. If member 2 fields are cleared, the sibling row is left untouched — deletion of a household member still requires the explicit "Remove" action.

1.11.8 (2026-09-15)

  • Fix: The sort direction on column headers on page=wam-premium never toggled between asc and desc. 1.11.7 tried to fix a previous double-flip by passing the href's order through unchanged, but the AJAX-rendered href always contains order=asc (WordPress builds it from $_GET, which is empty in the AJAX POST context), so every click resolved to asc. The click handler now derives the new direction from wamPremium.list._state: same column as before flips asc↔desc, different column starts at asc. Also updates the sorted asc/sorted desc/sortable classes on the clicked TH immediately so the arrow reflects the state before the AJAX round-trip returns.
  • Fix: The current sort and page were lost every time the list was AJAX-refreshed (after editing a member, removing, unremoving, adding, etc.). wamPremium.list.display() only sent s, status and del, so the PHP handler fell back to the default sort. A shared wamPremium.list._state (orderby, order, paged) is now updated on every sort click and pagination click, and display() merges it into the AJAX payload so the list snaps back to the same column, direction and page after any refresh.
  • Fix: The bulk actions "Export Arrears PDF" / "Export Arrears Excel" threw ReferenceError: exportRestanceMembers is not defined. The dispatcher was added in 1.11.7 but the helper function itself was not — both bulk options now succeed.
  • Change: PDF / Excel exports (members, addresses, arrears) now honour the sort currently shown in the list. wamPremium.list._state.orderby and _state.order are sent along with the export request, and the three export callbacks (export_members_callback, export_member_addresses_callback, export_restance_callback) apply the same ORDER BY clause to the underlying SELECT. Column key is whitelisted against the actual wam_members columns; unknown or empty values fall back to MemberNo ASC so the previous default is preserved.

1.11.7 (2026-09-15)

  • Fix: Column sorting on the member admin list (page=wam-premium) never sorted by anything except MemberNo. Members_List_Table::prepare_items() sorted the raw DB rows before transforming them into display rows, so Name, Email, Street, Zip, City, MemberStatus, NextPayment and the other visible keys did not exist on the row array being sorted — every non-MemberNo sort silently compared empty strings. prepare_items() now builds the display rows first, then sorts the display rows, then paginates. usort_reorder() prefers a ${column}Sort value when present so NextPayment sorts by the raw ISO date instead of the localised label, and case is normalised for text columns.
  • New: Two bulk actions on the same list: "Export Arrears PDF" and "Export Arrears Excel". These reuse the existing member export column configuration (wam_premium_list_columns) but always include every active member (DelReason = 0, selection is ignored on purpose) and replace the visible NextPayment column with a per-row status cell: red Arrears when NextPayment is empty or < CURDATE(), otherwise green Paid. Handled by the new WA_Members_Premium_Members_Ajax::export_restance_callback() (registered as wp_ajax_export_restance).

1.11.6 (2026-09-14)

  • Fix: The MemberNo reservation itself now refuses to mint a new number when the submitted email already exists in wam_members. WAM_Premium_New_Member_Ajax::get_and_reserve_next_memberno() accepts an optional $email argument: if any row (any DelReason) already carries that email (case- and whitespace-insensitive), the row's existing MemberNo is returned and the auto-increment option is left untouched. This means every sign-up flow (Vipps, WooCommerce, standard AJAX) automatically reuses the original member's number — no matter which gateway or callback is involved. Callers WAM_Premium_New_Member_Ajax::ajax_prepare(), WAM_Premium_Woo_New_Member_Ajax::ajax_prepare() and WAM_Premium_Vipps_New_Member_Ajax::ajax_prepare() now parse the submitted email once at the top of the request and pass it to the reservation helper.

1.11.5 (2026-09-14)

  • Fix: WAM_Premium_Vipps_New_Member_Ajax::ajax_prepare() now looks up existing wam_members rows by email BEFORE reserving a new MemberNo. If any row already carries the submitted email (case- and whitespace-insensitive, any DelReason), that row's MemberNo is reused for the Vipps agreement. Consequence: the MobilePay app shows the existing MemberNo in the payment description instead of a freshly-minted one, and no MemberNo is wasted from the reservation sequence. handle_return() still runs its own email guard as belt-and-suspenders. Logs vipps_prepare_email_reused / vipps_prepare_email_new in wam_payment_log so the pre-agreement branch is visible.

1.11.4 (2026-09-14)

  • Fix: The Vipps direct email-reuse guard now hard-fails with a wp_die() when the submitted email is empty after sanitisation, instead of silently falling through and inserting a fresh member row with the reserved MemberNo. This closes the last window where the flow could still create a duplicate for the same person.
  • Diagnostic: Every Vipps direct sign-up now writes three explicit log rows to wam_payment_log — vipps_email_guard_start (records plugin version, sanitised email, table name and reserved memberno as seen by the running code), vipps_email_guard_lookup (records the raw SELECT result plus $wpdb->last_error) and either vipps_member_reused or vipps_member_created. This makes it impossible for a duplicate to be created without leaving a trail that shows exactly which branch ran and what the SELECT returned.

1.11.3 (2026-09-14)

  • Fix: The Vipps MobilePay direct flow email-reuse lookup added in 1.11.2 sorted by id ASC when several rows shared the same email. Per policy the LOWEST MemberNo must always be the surviving record. The ORDER BY is changed to MemberNo ASC so duplicates that happened to have a lower row id but a higher MemberNo can never override the original member.

1.11.2 (2026-09-14)

  • Fix: The Vipps MobilePay Recurring direct flow (WAM_Premium_Vipps_Redirect::handle_return()) now performs the email-existence check inline before touching create_members_from_fields(). If any row in wam_members already carries the submitted email (regardless of DelReason, regardless of duplicates) the flow reuses the lowest-id row: its MemberNo becomes the payment's memberno, its id becomes member_id, and only non-empty submitted fields overwrite the existing columns. The helper is only invoked when zero rows match. This guarantees that the direct flow can never insert a new wam_members row for an email that is already present, independent of helper-side logic. Logs vipps_member_reused with the resolved id / MemberNo so the reuse is visible in wam_payment_log.

1.11.1 (2026-09-14)

  • Change: The email-match upsert in WAM_Premium_Helper::create_members_from_fields() now only writes fields that carry a non-empty value on top of the existing member row. Mobile, Phone, Address (etc.) are still updated when the current sign-up includes them, but no longer blanked out when Vipps Login / QuickPay direct returns a partial payload (for example when the userinfo scope excludes phone_number). Non-empty values overwrite as before; empty values leave the existing column untouched.

1.11.0 (2026-09-14)

  • Fix: The Vipps MobilePay Recurring direct flow (WAM_Premium_Vipps_Redirect::handle_new_member_callback()) bypassed WAM_Premium_Helper::create_members_from_fields() and inserted directly into wam_members with $wpdb->insert(). Even when the submitted email already existed in the table, a fresh duplicate row was created with a new MemberNo. The direct INSERT for the primary member is replaced by a call to the helper, which now hits the upsert path and reuses the existing row (updates name / address / phone / category / next payment) instead of creating a new member.
  • Fix: The upsert lookup in create_members_from_fields() restricted matches to DelReason = 0. When the same email had a previously removed row (or several rows across removed / active states), a fresh row was still inserted and the site ended up with parallel MemberNo entries for the same person. The lookup now matches on email alone, orders by id ASC and reuses the first row it finds regardless of DelReason. The chosen row's MemberNo and UniqID are preserved and its address / name / phone / category / next payment are updated. This guarantees at most one member record per email; the new payment is simply logged as an additional kontingent payment on the existing member.
  • New: Admin JS now ships with WampModal.alert() and WampModal.confirm() (in the new admin/js/wam-premium-modal.js), returning Promises with focus-safe OK / Cancel buttons and Escape handling. Every previous window.alert() and window.confirm() in the admin bundle (Vipps refund, POA sends and deletes, category deletes, bulk removes, kontingent charges, import prompts, MobilePay log clear) now go through the modal. WampModal.confirm(message, { title, okText, cancelText }) resolves true on OK / Enter and false on Cancel / Escape / overlay click; WampModal.alert(message, { title, okText }) resolves when dismissed.
  • Change: The MobilePay log page (?wamp_log_cleared=1 return) now uses WampModal.alert() for the confirmation prompt so the entire admin surface uses the same modal shell.

1.10.8 (2026-09-13)

  • Fix: The mobile auto-redirect IIFE ran before overlayI18n was initialised. Inside the IIFE, buildOverlay() dereferenced overlayI18n.title which was still undefined (only the var declaration is hoisted, not its assignment). The resulting TypeError aborted the IIFE mid-flight, so the follow-up setTimeout that hands off to Vipps was never scheduled and the form stayed visible on the page. The IIFE now runs after overlayI18n is defined, so buildOverlay() gets the copy it needs and the redirect fires.

1.10.7 (2026-09-13)

  • Fix: The mobile auto-redirect introduced in 1.10.5 could stall because a stale sessionStorage['wam_vipps_auto_started_no_hint'] flag from a previous test blocked the redirect on every subsequent visit. The flag is removed. callbackReturn (URL carries wam_vipps_prefill or wam_vipps_prefill_error) still guards against loops: Vipps returns ?wam_vipps_prefill_error=cancelled when the visitor cancels in-app, which suppresses the auto-redirect in that browser tab. A voluntary page reload always starts a fresh Vipps flow, which is the expected behaviour.

1.10.6 (2026-09-13)

  • Change: On mobile, [wam_new_member_form] renders no form at all — it shows a clean welcome overlay with the site's custom_logo (falling back to the site title), a heading and the sentence "You are being sent to MobilePay...". No buttons, no phone input. The browser is automatically redirected to Vipps immediately (all previous loop guards remain: callbackReturn, overlayDismissed, and the sessionStorage single-shot flag).
  • Change: The desktop inline block, category summaries and the entire form are hidden on mobile so the visitor sees only the welcome overlay.

1.10.5 (2026-09-13)

  • Change: On mobile, [wam_new_member_form] now redirects to Vipps automatically as soon as the shortcode script executes — the visitor never sees the overlay button. Guarded by callbackReturn (URL already carries a prefill token or error), overlayDismissed (visitor chose to fill the form manually earlier), and a sessionStorage flag wam_vipps_auto_started_no_hint so the browser cannot loop back to Vipps after cancelling or after the Vipps error redirect. The manual overlay button is kept as a fallback for cases where the auto redirect is blocked.

1.10.4 (2026-09-13)

  • Change: On mobile devices the MobilePay prefill overlay no longer asks the visitor for a mobile number. Vipps Login supports launching MobilePay directly when the auth URL is opened without login_hint, so the app itself signs the user in against the number active in the MobilePay app. The overlay now only shows the "Become a member" heading, the subtitle and one large "Continue to MobilePay" button that sends the browser straight to wam_vipps_login=start without any msisdn parameter. The country select and phone input are moved back into the inline block on close, and the auto-start / autofill polling paths are skipped on mobile.

1.10.3 (2026-09-13)

  • Change: Moved the "or type your data below" divider inside the MobilePay prefill box on [wam_new_member_form], so it visually belongs to the prefill panel and directly separates the MobilePay button from the manual form below. Show/hide logic is unchanged — the divider is bundled with the box and shares the same visibility as the prefill panel.

1.10.2 (2026-09-13)

  • Fix: When wam_premium_new_member_gateway = 'vipps' (Vipps MobilePay Recurring direct, no WooCommerce) [wam_new_member_form] created the member row but never sent any email. Neither the new member nor the site admin were notified. The QuickPay direct flow already delivered a welcome mail via the WAET template but likewise never fired an admin notification, and the wam_premium_orders_notify_email setting was only used as an unused resolver.
  • New: Helper WAM_Premium_Helper::send_new_member_notifications() sends a plain wp_mail() welcome mail to the new member (name, member number, category, amount, currency, portal URL) and a separate admin notification to every address listed in wam_premium_orders_notify_email (comma / semicolon / newline separated), falling back to get_option( 'admin_email' ).
  • Change: The Vipps direct redirect (WAM_Premium_Vipps_Redirect::handle_new_member_callback()) now calls the helper right after member creation, so both mails are dispatched before the browser is redirected to the continue / thank-you page. QuickPay direct callback and the WooCommerce order callback also call the admin-notification part so the site admin is notified regardless of the chosen gateway. Welcome mail is skipped in those two flows because the existing WAET / QuickPay path already sends the customer receipt.

1.10.1 (2026-09-13)

  • Fix: The Vipps prefill block on [wam_new_member_form] is now rendered style="display:none" in the initial HTML, so it is invisible from first paint. JS reveals it (style.display = '') only when the primary form fields are actually empty and no callback token is present in the URL. On Vipps round-trips the box is never shown at all — there is no window where it flashes for a couple of seconds before being hidden again.

1.10.0 (2026-09-13)

  • Change: When the page loads with ?wam_vipps_prefill=TOKEN (i.e. we came back from the MobilePay round-trip and data is already on its way), the inline prefill block and the mobile overlay are hidden immediately as the shortcode script starts, before the AJAX prefill fetch. Previously they flashed for 1-2 seconds until the fetch resolved. The initial updatePrefillVisibility() calls are also skipped on callback return so nothing rebuilds the overlay while data is loading.

1.9.60 (2026-09-13)

  • Fix: The membership rules modal could be overlaid by the active theme's footer when the visitor scrolled the page in the background, making the close button unreachable. The modal is now moved to document.body when opened (so it escapes any parent stacking context created by the theme container / shortcode wrapper), its overlay z-index is raised to 2147483647, and page scrolling is locked while the modal is open. Closing restores scrolling and puts the modal back where it was rendered.

1.9.59 (2026-09-13)

  • Change: Rules acceptance label on [wam_new_member_form] no longer renders "membership rules" as an underlined link. Only the 👁 icon is clickable and opens the rules modal, so the label reads as plain text with the icon at the end.

1.9.58 (2026-09-13)

  • Change: Moved the "Membership Rules Page" setting to the top of Payment options -> New Member Payment Gateway section. It was previously buried under QuickPay-specific settings, even though the modal is used by every sign-up gateway (QuickPay, Vipps, WooCommerce). Option key wam_premium_payment_quickpay_membership_rules is unchanged; existing selections are preserved.
  • Change: The rules acceptance label on [wam_new_member_form] now shows a small 👁 icon next to the "membership rules" link so visitors can immediately see it is clickable and opens the rules modal. Only rendered when a rules page is configured.

1.9.57 (2026-09-13)

  • Change: On a successful Vipps callback fetch, the mobile overlay and the inline prefill block are now hidden immediately (before the setVal loop starts firing the individual input / change events on #email, #fname, etc.). The previous flow waited for the form-wide input listener to observe every populated field and could leave the overlay lingering when Vipps did not return one of the seven watched fields, such as phone, on this sale unit.

1.9.56 (2026-09-13)

  • Fix: The 1.9.55 mobile overlay built a fresh <input> and <select> in JS and appended them after page load. Safari iOS often refuses to offer contact / phone autofill on inputs that were not present at initial DOM parse, so 1.9.55 lowered the autofill chance instead of maximising it.
  • Change: The overlay now MOVES the existing #wam-vipps-msisdn and #wam-vipps-cc from the inline block into the overlay when it opens, and moves them back on close. Same DOM identity, same event listeners, same server-rendered attributes — the elements were present at initial parse, which is the state Safari and Chrome autofill require.
  • Change: The phone <input> is now rendered with autocomplete="tel-national" (the local subscriber number, without country code) instead of the generic tel token. This matches how the country prefix is handled separately and improves autofill matching on Chrome / Safari saved contact data.

1.9.55 (2026-09-13)

  • Fix: Removed the failing mobile-overlay CSS introduced across 1.9.52, 1.9.53 and 1.9.54. The #mobile-number-box inline display:none, the is-mobile-overlay class flip driven by !important rules, the scoped wam-vipps-phone-row grid, the desktop/mobile copy split and the autofill polling loop are all gone. Those layers fought the active theme's Bootstrap grid and left the phone input rendered off-screen.
  • Change: The desktop inline prefill block is restored to the simple 1.9.51 layout — same .form-group.row + col-sm-* markup that worked. The mobile overlay is now built lazily by JS and appended to document.body, so it lives outside the theme container and cannot inherit theme gutters or column widths. All overlay styles are inline (fixed backdrop, viewport-sized flex column, centered heading "Become a member", subtitle, flag select, phone input, MobilePay button, close/manual-fill actions).
  • Kept: country prefix parsing for +45, 0047, +46 and +358; automatic hide of the prefill block once the primary form fields are all filled; guarded automatic Vipps redirect on mobile; sessionStorage guard against redirect loops; localStorage phone + country persistence across the Vipps round-trip.

1.9.54 (2026-09-13)

  • Fix: The mobile MobilePay overlay inherited the active theme's Bootstrap .row / .col-sm-* gutters and widths, pushing the country select and phone input off the right edge. Its internal controls now use plugin-owned .wam-vipps-phone-row, .wam-vipps-phone-control and .wam-vipps-phone-action classes with a scoped responsive CSS grid, explicit width/min-width resets and !important overrides. The overlay itself also resets theme typography, box sizing and text alignment so its heading, input and button stay centered and fully visible regardless of theme CSS.

1.9.53 (2026-09-13)

  • Fix: The #mobile-number-box introduced in 1.9.52 could remain hidden because it was rendered with inline display:none and the visibility code tried to reveal it by assigning an empty display value. It now uses an explicit display:block whenever the primary form is not complete (desktop and detected mobile), while complete forms still set display:none. This makes the phone input deterministic even when the active theme has competing display rules.

1.9.52 (2026-09-13)

  • Change: The MobilePay phone prompt now follows a browser-friendly mobile autofill pattern: the container is rendered as #mobile-number-box with display:none, then shown explicitly when /Android|iPhone|iPad|iPod|Mobile/i matches the user agent (plus modern iPadOS touch detection). The input uses type="tel", inputmode="tel", autocomplete="tel", autocapitalize="off", and spellcheck="false" with a spaced phone placeholder.
  • Change: Added a short mobile autofill poll (up to five seconds) because Safari/Chrome password/contact autofill may update an input without dispatching input or change. As soon as a newly autofilled valid number appears, country-prefix parsing and the guarded automatic Vipps redirect run. Numbers restored only from WAMP localStorage remain excluded from auto-start.

1.9.51 (2026-09-13)

  • Change: The Vipps prefill block on [wam_new_member_form] now hides automatically once all primary member fields are populated. On mobile devices it opens as a full-screen overlay with the dedicated heading "Become a member" and instruction "Enter your mobile number and continue to MobilePay"; visitors can close the overlay to complete the form manually.
  • Change: Mobile browser autofill and pasted international numbers are normalized automatically. Prefixes +45/0045, +47/0047, +46/0046 and +358/00358 select DK/NO/SE/FI respectively and leave only the local number visible. On mobile, a valid completed number starts the Vipps Login flow automatically after a short input pause. A session guard prevents repeat redirects for the same number and callback/cancel returns never auto-start another flow.

1.9.50 (2026-09-13)

  • Fix: The 1.9.49 silent-upsert only touched the WAMP_MEMBER row. The reserved MemberNo from ajax_prepare still ended up on _wam_memberno order post-meta and on the payment record, so downstream lookups (portal, payment list, receipt) kept resolving to the reserved number that had no matching row and the site displayed the sign-up as a brand new member. WAM_Premium_Helper::create_members_from_fields() now returns upsert_hit (bool) and the resolved MemberNo of the existing row, and create_members_from_order() overwrites _wam_memberno on the order with that existing MemberNo on an upsert hit — so callback, payment sync and portal all point at the returning member.
  • Note: The reserved MemberNo from ajax_prepare is intentionally not re-cycled; the sequence simply skips one number on an upsert hit.

1.9.49 (2026-09-12)

  • Change: [wam_new_member_form] no longer rejects sign-ups whose email is already registered as an active member. Two coordinated changes: (1) ajax_check_member_email always returns success so the visitor is never blocked mid-form with "You are already a member.", and (2) WAM_Premium_Helper::create_members_from_fields() now performs a silent upsert — if an active row with the submitted email exists (Email = %s AND DelReason = 0), the existing row is UPDATED with the newly submitted Fname, Lname, Address, Zip, City, Mobile, Phone, MemberStatus (category) and NextPayment, and its id is returned as primary_id. MemberNo, UniqID, Status and DelReason on the existing row are preserved. When no match exists the INSERT path runs unchanged. Result: a returning member can re-sign up and pay through the normal flow without producing a duplicate row, and their profile is refreshed with whatever they typed.
  • Note: The reserved MemberNo from ajax_prepare is not rolled back on an upsert match — the counter simply skips one number for that sign-up. Kept intentionally simple; no side effect other than a tiny gap in the memberno sequence.

1.9.48 (2026-09-12)

  • Fix: Turning off wam_premium_signup_dual_required still triggered "E-mail for medlem 2 er påkrævet" (and equivalents for fname2 / lname2) on [wam_new_member_form]. wp_localize_script stringifies PHP false to an empty string when it ships to the browser, so the JS gate wam_premium_front.dual_required !== false was "" !== false — always true — and the required-check ran even when the toggle was off. Switched to a plain truthy check so false / "" / 0 / undefined all correctly skip the block; true / "yes" / 1 still enforce it.

1.9.47 (2026-09-12)

  • Fix: The red * markers on the 2nd member fields (E-mail (2), First Name (2), Last Name (2)) on [wam_new_member_form] were hard-coded and stayed visible even after the site admin turned off wam_premium_signup_dual_required on Settings -> Categories. The asterisks are now rendered only when the toggle is enabled, matching the JS validator behaviour introduced in 1.9.44.

1.9.46 (2026-09-12)

  • Change: The "Require 2nd member details on sign-up" toggle introduced in 1.9.44 has been moved from Settings -> Members options to Settings -> Categories, where it logically belongs (it controls sign-up behaviour for duplicate-enabled categories). The underlying option key wam_premium_signup_dual_required and default value (yes) are unchanged; sites that already saved a value keep it.

1.9.45 (2026-09-12)

  • Change: Added a small "— or type your data below —" divider on [wam_new_member_form] between the MobilePay prefill block and the manual input fields, so it is visually clear that the visitor can either use MobilePay or fill the form manually. Divider only renders when the Vipps prefill block is enabled and shown. i18n-wrapped with the wa-members-premium text domain.

1.9.44 (2026-09-12)

  • New: Central site-wide toggle "Require 2nd member details on sign-up" under Settings -> Categories (option wam_premium_signup_dual_required, default yes). When set to no, the 2nd person's first name, last name and email fields on [wam_new_member_form] are shown for duplicate-enabled categories but no longer required by the front-end validator, so a visitor can sign up alone even when the chosen category permits two members. Backend already accepts an empty 2nd member and creates only a single-person row when the fields are blank — no change needed there.

1.9.43 (2026-09-12)

  • New: Diagnostic log of all Vipps MobilePay Login (OIDC) prefill calls. New DB table wam_vipps_login_log (created via dbDelta in the activator + self-healing ensure_table() on demand so existing installs don't need reactivation) captures the round-trip for every prefill attempt: authorize_start (with the MSISDN hint and state), token_exchange_error, userinfo_ok (with sub and which fields were prefilled), callback_error (state expired / user cancelled / no code), prefill_expired, and prefill_fetched. Rows include timestamp, event, MSISDN, sub/state, error code + message, list of mapped field keys, IP and truncated User-Agent. Auto-trims to the newest 1000 rows on each insert.
  • New: Log admin view rendered as a "MobilePay log" tab on Payments (page=wam-premium-payments&tab=mobilepay_log). Tab is only visible when wam_premium_vipps_enabled = 'yes'. Shows the newest 100 entries in a widefat table with a red / green row background per outcome, and a nonced "Clear log" button (admin_post_wam_vipps_login_log_clear action, manage_options capability check).
  • Change: WAM_Premium_Vipps_Login::build_authorize_url() and the callback + AJAX prefill fetch in WAM_Premium_Vipps_Login_Redirect now emit log rows at every branch so the admin can see exactly where a prefill flow stalled without touching wp-config.php debug logs.

1.9.42 (2026-09-12)

  • Change: The Bootstrap 5 .form-select dropdown arrow on the Vipps country-code select is shrunk (background-size 10px x 8px on desktop, 12px x 10px on tablet/mobile) and its reserved right-side padding tightened (1.5rem → 1.75rem responsive) so the arrow sits closer to the +45 text and the whole control fits its compact 85px width.

1.9.41 (2026-09-12)

  • Change: Country-code select on the Vipps prefill block resized to a compact 85px / 10px (desktop) so it no longer dominates the row next to the mobile input. Sizing moved from an inline attribute to a scoped <style> block on #wam-vipps-cc with responsive breakpoints — tablets (≤991.98px) get 90px / 11px, phones (≤575.98px) get 96px / 12px so the touch target and flag stay legible on small screens.

1.9.40 (2026-09-12)

  • Fix: Country-code select on the Vipps prefill block rendered as an empty rectangle because it used the Bootstrap .form-control class. form-control applies appearance: none to strip native form chrome and is intended for <input> / <textarea> only — for <select> Bootstrap 5 requires .form-select, which paints its own SVG dropdown arrow and reserves horizontal room for both the arrow and the selected text. With .form-control the select had no arrow and its content was clipped, so neither the DK flag nor +45 was visible. Class is now .form-select, with an explicit flex: 0 0 140px and the emoji font-family fallback still in place, so 🇩🇰 +45 renders correctly in both closed and open states.

1.9.39 (2026-09-12)

  • Fix: Country-code select on the Vipps prefill block still rendered no flag in the closed state because the inline font-family used the CSS keyword inherit inside a font-list (font-family: inherit, 'Segoe UI Emoji', ...). inherit is a keyword, not a font name, and browsers reject the whole declaration when it appears mid-list — so the select fell back to the theme's .form-control stack ("Segoe UI", sans-serif) with no emoji fallback. The style now lists real font names ("Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif, "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", "Twemoji Mozilla") so the flag glyph is picked up from the emoji fonts on both Windows and macOS.

1.9.38 (2026-09-12)

  • Fix: Country-code select on the Vipps prefill block rendered empty in the closed state because the theme's .form-control font stack had no emoji fallback — the open dropdown looked correct because it uses the OS font, but the button itself couldn't paint the regional-indicator glyphs. The flag now lives back in the <option> text (🇩🇰 +45 etc., no extra span), and the <select> carries an inline font-family with "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji" appended so the flag renders both when selected and when the dropdown is open.

1.9.37 (2026-09-12)

  • Fix: The DK/NO/SE/FI country-code select on the Vipps prefill block rendered as an empty control on Windows/Chrome because the browser used the theme's .form-control font (no emoji glyphs) for the closed state, while the open dropdown used the OS font (with flags). The flag now lives in a dedicated <span id="wam-vipps-cc-flag"> prepended to the input group, and the <select> options contain plain text only (+45, +47, +46, +358). JS updates the span whenever the selection changes, so the correct flag renders in both closed and open states across all browsers regardless of font stack.

1.9.36 (2026-09-12)

  • Change: The Vipps MobilePay prefill block on [wam_new_member_form] now shows a country-code select (DK 🇩🇰 +45, NO 🇳🇴 +47, SE 🇸🇪 +46, FI 🇫🇮 +358) in front of the mobile input. Default is DK. The selected country is persisted alongside the number in localStorage (wam_vipps_msisdn_cc / wam_vipps_msisdn), and only the local subscriber digits are ever shown in the input. On click the concatenated <cc><digits> is sent as the OIDC login_hint=MSISDN:... parameter, so Vipps receives the correctly formatted number for any of the four Nordic markets.

1.9.35 (2026-09-12)

  • Change: The mobile-hint input #wam-vipps-msisdn on the [wam_new_member_form] Vipps prefill block now remembers the previously typed number across the Vipps redirect. The value is written to localStorage (key wam_vipps_msisdn) both when the visitor types and when they click "Fetch info from MobilePay", and restored on page load if the input is empty. The stored/displayed value is stripped of the DK country-code prefix (45) when the total is 10 digits, so only the local 8-digit number ever appears in the field.

1.9.34 (2026-09-12)

  • Fix: Vipps MobilePay Login prefill re-prompted for the phone number even when the visitor had typed it on the [wam_new_member_form] shortcode. The OIDC login_hint parameter was sent as MSISDN:21411266 (local 8-digit DK format), but Vipps requires the country code prefix without +, e.g. MSISDN:4521411266. normalize_msisdn() now prefixes the default country code (45 for Denmark) when the input is 8 local digits, leaves numbers that already start with + or are 10+ digits untouched, and exposes an apply_filters( 'wam_premium_vipps_login_default_country_code', '45' ) hook for sites that need a different default (e.g. 47 for Norway).

1.9.33 (2026-09-12)

  • Change: Vipps MobilePay Userinfo prefill on [wam_new_member_form] now only requests the four fields Vipps returns for this integration — full name, phone number, email and address. Requested scopes trimmed from openid name email address phoneNumber birthDate to openid name email address phoneNumber, the birthdate key removed from the mapped payload, and the Settings help text + docblocks updated to match. Nothing else in the flow changes: the same OIDC start/callback endpoints, same JS prefill of #fname / #lname / #email / #mobile / #address / #zip / #city on the sign-up form.

1.9.32 (2026-09-12)

  • Fix: Clicking "Hent info fra MobilePay" on [wam_new_member_form] failed with WordPress' "Det link, du fulgte, er udløbet." nonce screen. $vipps_start_url was built with wp_nonce_url() which HTML-escapes ampersands to &amp; for safe display in attributes. That is correct for the anchor href (the browser decodes entities in attribute values), but the same string was passed to JS via wp_json_encode() and assigned to window.location.href when the admin typed a mobile hint — the browser then treated &amp; as literal characters in the URL, so the nonce parameter arrived as amp;_wpnonce and check_admin_referer( 'wam_vipps_login_start' ) rejected the request as expired. The URL is now built directly with add_query_arg() including _wpnonce => wp_create_nonce(...) so the raw URL contains literal &. The anchor still uses esc_url() (correct HTML-entity handling in attributes), and the JS receives the same raw URL via wp_json_encode() with plain &, so both flows navigate to a valid ?wam_vipps_login=start&_wpnonce=...&return=... request.

1.9.31 (2026-09-12)

  • Fix: Category admin duplicated events and inserted duplicate rows. wamPremium.category.fetchCategories() re-registered every modal / form event handler on document via $(document).on(...) on every call — and fetchCategories() runs on init, search keyup, status change, and after every add / edit / delete. After the first add, the add-form submit handler fired twice; after the second, three times; so deleting a category and creating a new one appeared to "suddenly show 2 rows" because two insert AJAX calls fired for a single click. The .wam-select-image handler was duplicated the same way, forcing the admin to click the image button twice and opening multiple wp.media frames on top of each other.
  • Change: All the delegated handlers registered inside fetchCategories() are now namespaced .wamcat and preceded by a single $(document).off('.wamcat') guard, so each list refresh cleanly removes any previously-bound WAMP category handlers before re-attaching a fresh set. No behaviour change apart from single-firing events — submits fire once, image picker opens once, and the row count matches what the admin actually did.

1.9.30 (2026-09-12)

  • Fix: add_category() INSERT did not include wam_cat_selectable or wam_cat_image_id in the data array — both were read from $_POST and then silently dropped. The row landed with wam_cat_selectable = 0 (schema default), so newly created categories never appeared on the [wam_new_member_form] / [quickpay_new_member_form] shortcodes and the checkbox appeared unchecked on next view even though the admin had ticked it during creation. Both columns are now written on create with matching %d format specifiers.
  • Change: On the Add Category form the "Selectable" checkbox is now pre-checked by default so newly created categories are immediately usable on the sign-up forms unless the admin deliberately unchecks it.
  • Change: On the Add Category form the wam_cat_id dropdown now preselects the lowest unused ID in the 1..12 range, so the admin no longer has to open the dropdown and pick an id manually. Existing behaviour (skipping already-used ids) is unchanged.

1.9.29

  • Marked as tested with WordPress 7.1 and WooCommerce 11.1.0.

1.9.28 (2026-09-12)

  • New: [wam_new_member_form] shortcode can now prefill Name / Email / Address / Zip / City / Mobile / Birthdate from Vipps MobilePay Userinfo. Opt-in via new setting under Payment options -> Vipps MobilePay: "Enable Userinfo prefill on new member form". Uses the existing Vipps Client ID / Client Secret / Subscription Key / MSN (Vipps Login product must be enabled on the sale unit in the Vipps MobilePay portal; same credentials, separate activation). Scopes requested: openid name email address phoneNumber birthDate.
  • New: Class WAM_Premium_Vipps_Login (OIDC helper: authorize URL, code -> token -> userinfo, address / phone / name mapping to form fields) and front-end redirect handler WAM_Premium_Vipps_Login_Redirect exposing two virtual endpoints via query var wam_vipps_login=start|callback, plus an AJAX endpoint wam_vipps_prefill_fetch that returns the mapped userinfo payload to the shortcode JS.
  • Change: The [wam_new_member_form] form now renders a "Hent info fra MobilePay" button + optional mobile-hint input at the top of the form when the new prefill setting is enabled. Clicking the button starts the Vipps Login OIDC flow (redirects to Vipps for user consent); the callback stores the returned userinfo in a short-lived transient keyed by a one-time token and redirects back to the shortcode page with ?wam_vipps_prefill=TOKEN. Inline JS fetches the payload via AJAX and populates the existing form fields (#fname, #lname, #email, #mobile, #address, #zip, #city), then strips the query parameter so a page refresh doesn't re-fetch. Users can still edit any field before submitting.
  • Security: Vipps Login state parameter tied to the return URL is stored in a 10-minute transient and verified in the callback; prefill token is a 32-char random string, transient TTL 10 minutes, one-shot delete on fetch; capability-free (public flow) but nonce-checked on the AJAX fetch to avoid drive-by URL replay.

1.9.27 (2026-09-02)

  • Fix: Optional-fields get_config() never fell back to the defaults' enabled value — it always resolved to 'no' whenever the stored option row for a field was missing an enabled key. That meant Area (and Mobile / Phone), all declared as 'enabled' => 'yes' in defaults, actually reported as disabled, so render_fields() and collect_from_post() skipped them: the Area select never rendered on the admin Add / Edit forms, and edits to Area (when the field somehow was posted) were silently dropped. Fallback now uses $default['enabled'].
  • Fix: The Area select is now force-enabled in get_config() regardless of the stored value. Area is a core member field with its own auto-resolve process (wamp_resolve_area from postnr + street via WAMP_MEMBER_AREA) and must always be shown on the admin Add / Edit forms; the previous 1.9.25 fix only re-linked the JS #area id and did not address the field being skipped entirely.
  • Change: Settings → Members options → Optional fields now renders the Area row's enabled checkbox as checked and disabled with an "always enabled" note, so the state can't be accidentally toggled off (the label + order columns are still editable).

1.9.26 (2026-09-02)

  • New: Settings -> Members options tab now offers an "Export CSV" button next to the existing "Import CSV" button for the Area data table. Downloads the current WAMP_MEMBER_AREA contents as a CSV with the same 5 columns the importer expects (Area, Street, StartNo, EndNo, EvenOdd) plus a header row, so admins can pull the current rules out, edit them, and re-import via the existing "Replace all" import flow. Uses admin-post.php?action=wamp_area_csv_export with a dedicated nonce and manage_options capability check; filename wam-members-area-YYYY-MM-DD.csv, semicolon-delimited so Danish Excel opens it directly.

1.9.25 (2026-09-02)

  • Fix: The Area (område) dropdown on the admin Add member and Edit member forms is now correctly wired to the automatic street+house-number lookup again. The optional-fields renderer previously emitted id="wam-opt-Area" while the admin JS (auto-resolve on address/zip change, prefill in the single→double flow, enable/disable toggles) still targets #area, so the Area value was never populated automatically and the field appeared empty in Edit and Add new. The area_select variant now emits id="area" (only Area is special-cased; other optional fields keep the wam-opt-<Key> id scheme). Field name="Area" and the underlying WAMP_MEMBER_AREA lookup process (wamp_resolve_area from postnr + vej) are unchanged.

1.9.24 (2026-09-02)

  • Add: Members whose email is permanently deleted in Mailchimp can now open the configured hosted signup form directly from the member portal and subscribe themselves again.

1.9.23 (2026-09-02)

  • Fix: Mailchimp audit Add and Batch Add now convert mapped WAMP date values from ISO YYYY-MM-DD to Mailchimp's required MM/DD/YYYY format, preventing HTTP 400 responses when creating members with date-type merge fields.

1.9.22 (2026-07-26)

  • New: Betalingsoversigt member-year-list export (XLSX + PDF) now includes an Email column (member Email from wam_members, blank when missing). Column order: Member No., Name, Address, Email, Amount, Payment method, Date.
  • Fix: PDF header text ("Printed — members", title "Kontingent ") rendered in English on Danish sites because the source strings used positional %1$s / %2$d placeholders that did not match the existing PO msgids (which use plain %s / %d). Source strings now use plain %s / %d so Poedit's "Update from source" merges them with the existing "Trykt %s — %d medlemmer" / "Kontingent %s" translations.
  • New: PDF stats line "Paid: %d — Not paid: %d" registered as a new translatable string with the wa-members-premium text domain (was already using __() but the string itself is new, so the Danish translation lands via the normal Poedit workflow).

1.9.21 (2026-07-26)

  • Change: Betalingsoversigt member-year-list export now delivers XLSX or PDF instead of CSV, matching the plugin's other member exports. Two buttons replace the single "Export member list (CSV)" button: "Export XLSX %d" (via OpenSpout, streamed with WriterFactory::createFromFile + openToBrowser) and "Export PDF %d" (via Dompdf, A4 landscape, styled table with header + row per member + totals footer). Data is unchanged — six columns per member (Member No., Name, Address, Amount, Payment method, Date), blank payment fields when the member has no completed kontingent payment in the selected year. Handler route now takes wam_fmt=xlsx|pdf alongside wam_export=member_year_list; default falls back to XLSX.

1.9.20 (2026-07-26)

  • Change: Payments Dashboard (page=wam-premium-payments&tab=dashboard) now scopes its top summary cards, channel breakdown (pie), "Paying members excl. BS" count and monthly bar chart to a selectable year. A year dropdown at the top of the tab lists every distinct YEAR(p.created_at) present in wam_payment plus "All years"; default is the current calendar year, persisted in the URL as wam_year. Previously the top card mixed rows across every year, so a fresh look at the dashboard could show e.g. 102 completed payments where many were from 2024 — the year filter now makes the current year the default view. The monthly chart renders the 12 months of the selected year (Jan–Dec) instead of the rolling last-12-months window when a specific year is selected.
  • Note: the "Payments by year" panel added in 1.9.18 intentionally stays all-time (it exists precisely to compare years side by side) and is unaffected by the year filter. "Members with subscription" also stays as a current-state count — it reflects live rows in wam_subscriptions, not a per-year history.

1.9.19 (2026-07-26)

  • New: "Export member list (CSV)" button on the Betalingsoversigt tab (page=wam-premium-payments&tab=paid_members). Streams a per-member roster for the selected year with six columns: Member No., Name, Address (address + ZIP + city on one line), Amount, Payment method, Date. Every active member (wam_members with DelReason = 0) is included exactly once; members without a completed kontingent payment for the year keep the last three columns blank so the sheet can be used as a paid/not-paid overview. The membership category filter (WAMP_PAY_CATEGORY_NEW / _RENEW) and the completed-status matcher (Payments_List_Table::completed_status_where_sql) match what the on-screen dashboard uses. When the year filter is set to "All years" the export defaults to the current calendar year (a specific year is required to answer "has this member paid for the year?"). Route: wam_export=member_year_list with a wam_paid_export nonce; capability follows the existing management cap.

1.9.18 (2026-07-26)

  • New: Payments Dashboard (page=wam-premium-payments&tab=dashboard) now includes a "Payments by year" panel below the monthly chart. It groups all completed payments by YEAR(p.created_at) — descending, one row per year — with total count, total amount and a per-channel breakdown (Betalingsservice / MobilePay / Payment) using the same color codes as the pie chart. This exposes that the top summary card (e.g. "102 completed payments") mixes multiple years, and shows how many payments belong to each year. Rows without a valid created_at (NULL / '0000-00-00 00:00:00') are grouped under "Unknown year" so all-time totals still reconcile with the summary card.

1.9.17 (2026-07-06)

  • Change: The portal login ("E-mail og adgangskode" tab) now accepts a WordPress username in the email field in addition to an email address. UI text is unchanged — label is still "E-mail" — but the browser no longer refuses non-email input, and the server tries the value as-is against wp_signon (which already resolves both user_email and user_login). Empty input still fails with the existing "Missing email or password" message. The "Engangsloginkode" and "Nulstil adgangskode" tabs still require a real email address (they must send a mail).

1.9.16 (2026-07-05)

  • New: page=wam-premium-orders now surfaces membership payments (kontingent + new-member) that are NOT backed by a WooCommerce shop_order — the flows that were decoupled from Woo. A second section "Medlemsbetalinger uden WooCommerce-ordre" is rendered below the existing WC orders list; it pulls from wam_payment where category IN (WAMP_PAY_CATEGORY_NEW, WAMP_PAY_CATEGORY_RENEW) AND (orderno is empty OR starts with MB / BS), i.e. MobilePay renewals, Betalingsservice imports, and portal-kontingent rows that never generated a WC post. Same year filter (from/to) and search term (s) as the WC table.
  • New: summary dashboard at the top of the Orders page — total number and amount for WC orders vs non-Woo membership payments in the current date range, plus a combined total. Follows the same layout as the Betalingsoversigt dashboard added in 1.9.15.

1.9.15 (2026-07-04)

  • Fix: Betalingsoversigt (tab=paid_members) classified MobilePay / Vipps / QuickPay membership payments as "Andre betalinger" instead of "Medlemsbetalinger". Root cause: the split matched p.type = 'kontingent', but real membership payments carry p.type = 'MobilePay' / 'Betalingsservice' / 'Payment' and identify as membership through p.category IN (WAMP_PAY_CATEGORY_NEW, WAMP_PAY_CATEGORY_RENEW) — the same categorization the dashboard already uses. Classification is now category-based; legacy rows without a category still fall to "other" as before.
  • New: small summary dashboard at the top of the Betalingsoversigt tab. Shows total payment count + amount for the selected year, split by category (Medlemsbetalinger vs Andre betalinger) and by type (MobilePay, Betalingsservice, Payment). Updates automatically when the year filter changes.

1.9.14 (2026-07-04)

  • Fix: bulk send from the kontingent page (kontingent_send_request) failed with "SMTP Error: Could not connect to SMTP host." from the second or third recipient onwards when the site uses WP Mail SMTP with a remote SMTP host. Root cause: the WAET queue processor iterated the batch and called wp_mail() back-to-back with no gap, so every mail opened a fresh SMTP connection and the SMTP server rate-limited connections-per-minute.
  • New: WAM_Premium_Waet_Processor inserts a configurable pause between sends inside a batch — default 500 ms, controlled by option wam_premium_waet_send_delay_ms (0 to disable). Both queue-processing loops (WAET-rendered process_entry and pre-rendered send_waet_entry) honor it. Single-message sends are unaffected because the delay only runs from the second iteration onward.
  • New: on wp_mail failure the processor now inspects the last PHPMailer error; if it contains "Could not connect to SMTP host" the send is retried once after a 5-second backoff before being marked as error. Retries + backoff durations are logged so the WAET admin log clearly shows the recovery attempt.

1.9.13 (2026-07-04)

  • Fix: i18n gap for the Member 2 add card in the member portal profile pane. The three JS strings introduced in 1.8.96 (add, add_member2_intro, name_required) were called via t('key', 'fallback') but the corresponding entries were never added to the wam_premium_front.i18n array wired up in class-wam-premium-front.php, so the JS always fell back to English regardless of the site's locale. Keys are now registered on the PHP side + translated in wa-members-premium-da_DK.po so the card renders in Danish ("Tilføj", "Tilføj det andet husstandsmedlem.", "Fornavn og efternavn er påkrævet.").

1.9.12 (2026-07-04)

  • Fix: full audit + alignment of the merge-tag payload across the three kontingent send flows so TEST and LIVE cannot diverge any more. Added the following to both LIVE send_request (bulk) and queue_payment_request_email (per-row) which were previously only present in the admin test send:
    • order_id (constant '0' for kontingent request emails; overridden in payment flows that already carry an order id)
    • card_brand, card_no, card_issued_to (empty strings — no card at request time)
    • action_url, action_link (aliased to the wam-go link; bulk previously only exposed active_url)
    • currency (DKK, or WooCommerce currency when available)
    • site_name, site_url, portal_url
    • FIK-streng on LIVE per-row (bulk already had it)
  • Result: any template using {{order_id}}, {{action_url}}, {{currency}}, {{site_name}}, {{portal_url}}, {{FIK-streng}} etc. now renders identically in test and live emails, no matter which of the three send paths queued the message.

1.9.11 (2026-07-04)

  • Fix: {{member_year}} merge tag was only populated in the admin test-send, not in the two LIVE sends (send_request and queue_payment_request_email). Now added to both LIVE vars arrays as date_i18n( 'Y' ) so {{member_year}} renders the current year identically in TEST and LIVE. Templates that used {{member_year}} in subject or body previously produced an empty year on live sends.

1.9.10 (2026-07-04)

  • New: page=wam-premium-payments&tab=paid_members (Betalingsoversigt) is now filtered by payment year. A year dropdown next to the search field lists every distinct YEAR(p.created_at) present in the wam_payment table plus an "All years" option. Default is the current calendar year, so freshly opened tab shows only this year's payments — not all 500 latest rows across all history. Selected year persists as wam_year in the URL query and is preserved through search / reset. p.created_at is the actual payment date (set by BS import from the CSV Betaltdato field, and by real-time inserts from the moment the payment completed), so imports of historical years land in the correct year bucket regardless of when they were imported.

1.9.9 (2026-07-04)

  • Fix: 1.9.8 still rendered {{name}} / {{fname}} as "Test User" in the admin test email even though the vars payload had been switched to WAM_Premium_Helper::get_member_template_context(). Root cause: two hardcoded 'Test User' strings elsewhere in send_test_email_ajax_callback — (1) the enqueue-time $receiver = [ 'name' => 'Test User' ] argument, which WAET's queue worker maps back onto {{name}} as a fallback when it re-hydrates template variables from the receiver record; (2) an unused $template_variables local array that carried 'name' => 'Test User' and was easy to accidentally start passing back into the payload. Both replaced with the resolved member name (or an empty string when the test member cannot be looked up).

1.9.8 (2026-07-04)

  • Fix: admin test-send (Settings -> Email options -> "Send test") now populates every member-scoped merge tag from the selected test_memberno's actual row in wam_members, instead of hardcoded "Test", "User" and "Test User". {{fname}} / {{lname}} / {{name}} / {{address}} / {{zip}} / {{city}} / {{mobile}} / {{phone}} / {{area}} / {{uniqid}} / {{next_payment}} / {{title}} / {{year}} / {{meta1..4}} / {{note1}} / {{note2}} now render with the same values a real recipient would see. Only the recipient envelope address stays as the admin-entered test_email so the test mail lands in the tester's inbox rather than the member's.
  • Fix: the LIVE bulk send (send_request in class-wam-premium-kontingent-ajax.php) and per-row live send (queue_payment_request_email in class-wam-premium-payments-ajax.php) also now merge the same full member context into their vars payloads, so a live-sent email exposes the exact same merge tags as the test-sent email. Previously LIVE only exposed a small subset (fname, lname, name, email, member_no, member_type, amount, payment_link, active_url, org info, FIK-streng) and was missing address, zip, city, mobile, phone, area, uniqid, next_payment, title, year, meta1..4, note1..2 — templates that referenced those merge tags rendered empty in production.
  • Implementation: shared helper WAM_Premium_Helper::get_member_template_context( int $memberno ) returns a normalized string map of scalar member fields keyed by common merge-tag names. Used by send_test_email_ajax_callback, send_request and queue_payment_request_email — a single source of truth so TEST and LIVE cannot diverge. Empty string is returned for columns that are null / missing on the row.

1.9.7 (2026-07-04)

  • Fix: {{subject}} merge tag was never populated inside the email body — the subject template was only passed as the top-level subject payload key that WAET uses for the mail envelope, not as a template variable. All three kontingent send paths now also add the resolved subject to vars['subject'] so {{subject}} in the HTML body renders correctly. Applies to bulk send_request (Kontingent tab), per-row queue_payment_request_email (Payments-Kontingent + Members list) and admin test-send (Settings -> Email options -> Send test).

1.9.6 (2026-07-04)

  • Fix: {{amount}} and {{member_type}} merge tags were empty in both the admin test-send (payment_request) and the per-row queue_payment_request_email live send. Only the bulk send_request had them populated. Both flows now resolve the values through a shared helper so all three sends produce identical {{amount}} / {{member_type}} output.
  • New: helper WAM_Premium_Helper::get_member_kontingent_context( int $memberno ) returns [ 'amount' => "NNN,NN kr", 'member_type' => "<category name>" ] for a given member number by joining wam_members.MemberStatus -> wam_categories.wam_cat_amount / wam_cat_name. Empty amount when no category or no amount is set. Used by send_test_email_ajax_callback (payment_request test), queue_payment_request_email, and the bulk send_request (kontingent-ajax) for a single source of truth.

1.9.5 (2026-07-04)

  • Change: queue_payment_request_email (the per-row "Send request" action on the Payments -> Kontingent page and the Members admin list) now also builds {{payment_link}} via WAM_Premium_Helper::build_kontingent_wam_go_url() — same wam-go auto-login URL to ?tab=kontingent as the bulk send_request and the admin test send. Previously this single-member live send was still emitting the plain /<slug>?id=N URL, so members clicking the button on the payments-kontingent page received a different URL shape than members reached via the bulk send. Raw slug link kept as fallback when the helper cannot resolve UniqID or target page.

1.9.4 (2026-07-04)

  • Change: {{payment_link}} merge tag now resolves to a wam-go auto-login URL that lands the recipient directly on the Kontingent tab of the configured payment landing page, instead of the previous plain /<slug>?id=N URL that required a manual login. Both the real kontingent request send (send_request in class-wam-premium-kontingent-ajax.php) and the admin test send (send_test_email_ajax_callback for payment_request in wam-premium-helpers.php) use the same resolution so the test email now matches production behavior.
  • New: helper WAM_Premium_Helper::build_kontingent_wam_go_url( int $memberno ) looks up the member's UniqID in wam_members (DelReason = 0, latest row), resolves the target page id from wam_premium_waet_payment_request_payment_link (falling back to wam_premium_portal_page_id), and builds the wam-go URL with ?tab=kontingent as the target. Returns empty string when any prerequisite is missing so callers can fall back to the raw slug link.
  • Fix: admin test send for the test member no longer produced <host>/medlemsportal?id=N; it now produces <host>/wam-go/<memberno>/<uniqid>/<post_id>/?target=<url-encoded permalink>?tab=kontingent, matching what a real member receives.

1.9.3 (2026-07-03)

  • New: admin Edit member modal now includes a Next payment row so the year can be adjusted directly from the member card. Shows the current NextPayment value read-only and a Year dropdown ranging from the earliest year present in wam_members.NextPayment (or current year - 2) to current year + 10. Saving replaces only the year portion of the existing NextPayment, keeping month + day intact; if the member had no prior NextPayment value the new date defaults to YYYY-01-01. Add-member form is unchanged (NextPayment is still derived from the selected category there).

1.9.2 (2026-07-03)

  • Fix: HTTP 500 on admin-ajax.php when the admin Members page bootstraps or Add-member is opened. fetch_add_member_form called WAM_Premium_Optional_Fields::render_fields() without a leading backslash inside the namespaced file admin/classes/class-wam-premium-members-ajax.php (namespace WAM_Premium\Admin;), so PHP resolved the class as WAM_Premium\Admin\WAM_Premium_Optional_Fields and fataled with "Class not found". Same namespace-resolution defect pattern fixed in 1.8.62 / 1.8.65 / 1.8.66. Now uses the fully qualified \WAM_Premium_Optional_Fields::render_fields().

1.9.1 (2026-07-03)

  • New: Note1 and Note2 added to the Optional fields list so they can be toggled/labelled/reordered like the other configurable fields. Both default to disabled. When enabled they appear on the Add member and Edit member forms and (with a value) on the member detail card. The existing "Note" textarea in the removed-member reason section is untouched; if Note2 is also enabled as an optional field on a removed-member view the two inputs coexist — admin can disable the optional-field copy to avoid duplication.

1.9.0 (2026-07-03)

  • Milestone version bump closing the 1.8.x line. No functional changes beyond the notes below (which were staged as 1.8.104 before the version bump).
  • New: admin member detail card (fetch_member_detail_callback, opened when clicking a member row in the Members list) now also respects the Settings -> Optional fields configuration. Mobile / Phone / Area rows are hidden when the corresponding optional field is disabled, and their labels come from the config so they stay in sync with the Add/Edit forms. Title, YEAR, meta1, meta2, meta3 and meta4 are appended as extra rows when enabled AND the member row has a non-empty value — in the same order as configured on the Optional fields tab.

1.8.103 (2026-07-03)

  • New: dedicated Optional fields tab under Settings (Settings -> Optional fields). Per-site configuration of nine optional member-record fields — Mobile, Phone, Area, Title, YEAR, meta1, meta2, meta3, meta4 — with three controls each: enabled toggle, custom label, and display order. Configuration is stored in a single option wam_premium_optional_fields and applied to both the admin Add member form and the admin Edit member modal. Disabled fields are omitted from the form entirely; enabled fields render in the configured order with the configured label. All nine fields are now also persisted through both the add-member and edit-member AJAX handlers so meta1-4 / Title / YEAR values entered on the forms are written to the corresponding wa_members columns.
  • Implementation: new helper class WAM_Premium_Optional_Fields (lib/classes/class-wam-premium-optional-fields.php) exposes get_config(), get_defaults(), render_fields( $values ) (returns the HTML block used by both forms) and collect_from_post( $post ) (returns the sanitized column=>value pairs for the enabled fields only). Both AJAX form generators now call WAM_Premium_Optional_Fields::render_fields() instead of the previous hardcoded Mobile/Phone/Area blocks.

1.8.102 (2026-06-28)

  • Change: new-member form email check (wam_check_member_email) now only blocks when a member with the entered email exists AND DelReason = 0 (active). Members with DelReason != 0 (previously cancelled/excluded/etc.) no longer trigger the "You are already a member." error — the front-end form allows the email through so the submit flow can resume/reactivate their existing membership instead of treating them as a brand-new member. SQL changed from SELECT COUNT(*) FROM {member_table} WHERE email = %s to ... WHERE email = %s AND DelReason = 0.
  • Change: new-member form email validation in wam-premium-front.js now fires live while the user types (debounced 400ms) in addition to the existing blur trigger, and uses delegated $(document).on() handlers on #email / #email2 so the check still binds correctly when the form is rendered after DOM ready (e.g. when the shortcode is inside a tab / lazy-loaded container). A lightweight client-side format pre-check avoids hammering the server before the address looks like a plausible email.

1.8.101 (2026-06-21)

  • New: dedicated Roles tab under Settings (Settings -> Roles). Lists every WordPress role currently registered on the site (via wp_roles()->role_names) with two checkbox columns: "Member management" (Members / Categories / Subscriptions / Payments / POA / Orders) and "System settings" (Settings / Mailchimp Audit / WAM-Go Audit / WAM ID Audit). Admin ticks the boxes for whichever roles should have access; on save the corresponding capability is added or removed on each role via add_cap / remove_cap. The Administrator row is read-only with both boxes ticked (admin always has both).
  • New: dedicated capability wamp_manage_settings for the system-settings page group. Replaces the previous hardcoded manage_options check on Settings, Mailchimp Audit, WAM-Go Audit and WAM ID Audit (both menu registration and inline defense-in-depth current_user_can() calls). Administrator is granted the cap automatically via install().
  • Change: WAM_Premium_Roles::install() no longer hardcodes any role slug or display name beyond administrator and wamp_member_admin. The previous one-time grants to content_editor (1.8.99) and "Content Editor" by display name (1.8.100) are removed — those grants are now under the admin's explicit control via the new Roles tab. INSTALL_VERSION bumped from 3 to 4 so existing sites pick up the new admin cap.

1.8.100 (2026-06-21)

  • Fix: in 1.8.99 the install routine looked up the staff role by the hard-coded slug content_editor, which is not the actual slug used on every site. The lookup now iterates wp_roles()->role_names and matches any role whose display name equals "Content Editor" (case-insensitive, with or without space/dash) so the correct role is found regardless of the underlying slug. INSTALL_VERSION bumped from 2 to 3 so existing sites re-run the install routine once on next page load.

1.8.99 (2026-06-21)

  • Fix: WAM_Premium_Roles::install() now also grants wamp_manage_members to the content_editor role (if it exists), in addition to administrator and wamp_member_admin. INSTALL_VERSION bumped from 1 to 2 so existing sites re-run the install routine once on next page load and pick up the new grant. After this update Content Editor staff users can again see Members / Categories / Subscriptions / Payments / POA / Orders. Settings / Mailchimp Audit / WAM-Go Audit / WAM ID Audit remain administrator-only (manage_options).

1.8.98 (2026-06-21)

  • Revert: removed the WAM_Premium_Roles::enforce_caps() self-healing mechanism and its init priority-99 hook that was added in 1.8.97. No additional roles are touched by the plugin; the cap whitelist is back to what it has been since 1.8.52: wamp_manage_members is only granted to administrator and wamp_member_admin via install(), and any other role that needs it (e.g. a custom content_editor staff role) is managed entirely outside the plugin via a role-editor plugin or WP-CLI — the plugin does not add to, remove from, or otherwise modify those roles.

1.8.97 (2026-06-21)

  • Fix (defensive): wamp_manage_members capability is now self-healed on every request via WAM_Premium_Roles::enforce_caps() (hooked on init, priority 99). The method walks a whitelist of roles and re-adds the capability if it is missing. Previously the cap was only granted on plugin activation (via register_activation_hook → install()), so if a third-party role plugin or DB restore stripped the cap from a custom role later, the role lost access to the member-management pages (Members, Categories, Subscriptions, Payments, POA, Orders) until the cap was re-added by hand. Default whitelist: administrator, wamp_member_admin, content_editor. Extend via filter: add_filter( 'wamp_manage_members_roles', fn($roles) => array_merge($roles, ['my_custom_role']) );. Cheap: in-memory has_cap check first, write only when actually missing, guarded by a per-request static flag so it runs at most once per page load.

1.8.96 (2026-06-19)

  • New: member portal #pane-profile now shows an "Add" card for Member 2 when the household membership (MemberStatus = 2 / dual category) only has the primary wam_members record created. Previously the right-hand Member 2 card was hidden entirely until an admin or a checkout flow inserted the secondary row, so members had no self-service way to fill in their partner's name, email and mobile. The Add card collects First name, Last name, Email and Mobile and creates the secondary wam_members row (same MemberNo, inherits Address/Zip/City/Area/MemberStatus/NextPayment from the primary, generates UniqID, fires wam_member_created so the WP user + Mailchimp sync handlers run as for any new member). New endpoints: AJAX action wam_profile_add_secondary and REST route POST wam/v1/profile-add-secondary. The boot payload (wam_portal_boot / wam/v1/portal-boot) now also returns a can_add_secondary boolean so the JS knows whether to render the Add card. Once a secondary exists, the existing Edit card (email + mobile) takes over as before.

1.8.95 (2026-06-19)

  • Fix: clicking "Save" (Gem) in the member edit modal no longer fails silently. The submit handler now shows an error notice when the AJAX request itself fails (network/PHP fatal/nonce expired) or when the server returns success=false (e.g. update rejected, member not found). Previously a non-success response just left the modal open with no indication of what went wrong.

1.8.94 (2026-06-19)

  • Fix: Mailchimp ADDRESS is no longer composed when the WAMP Address (street) column is empty. Previously, members with only Zip (or Zip+City) but no street were sent with addr1="" plus zip/country, which Mailchimp rejected as "Please enter a complete address" (HTTP 400) — that failure aborted the whole upsert for the member. Now the sync skips ADDRESS for those members and successfully syncs the remaining merge fields (FNAME, LNAME, MEMBERNO, ZIP, etc.).
  • Improved: audit "Add" diagnostics and mc_upsert_request skip log now say address requires a non-empty street (WAMP Address column is empty) when that is why ADDRESS was not composed.

1.8.93 (2026-06-19)

  • Improved: address-skip diagnostics (both mc_upsert_request log and audit "Add" response) now quote the actual stored value of the Default country setting (e.g. default country setting = '' vs default country setting = 'DK') instead of just saying "empty". Makes it immediately obvious whether the option was actually saved in the database vs typed but not submitted.

1.8.92 (2026-06-19)

  • New: audit "Add" response now also lists every Mailchimp tag that was NOT sent and the exact reason (e.g. "ADDRESS: no Country in WAMP row and no Default country in Settings -> Mailchimp", "NEXTPAY: paired to NextPayment but the WAMP value is empty", "MSTATUS: paired to MemberStatus but tag does not exist in Mailchimp audience"). Makes the audit page self-diagnosing for every member.

1.8.91 (2026-06-19)

  • Fix: the "Create missing merge tags in Mailchimp" button (Settings -> Mailchimp) again bootstraps the curated WAMP-relevant tag set in the audience (FNAME, LNAME, PHONE, MOBILE, ADDRESS, MEMBERNO, NEXTPAY, MSTATUS, BIRTHDAY, AGE, GENDER, STATUS, EXTID, NOTE1, NOTE2, TITLE, AREA, YEAR) plus any already-paired tags. This is an explicit admin bootstrap action only; runtime sync still ONLY processes tags the admin has paired on the settings page.
  • New: audit "Add" action now returns the exact merge fields actually sent (count + tag list) so admins can see immediately whether real data reached Mailchimp. Message format: "Added to Mailchimp (N merge fields sent: FNAME, LNAME, ...)".

1.8.90 (2026-06-19)

  • Change: address-type Mailchimp merge tags (e.g. ADDRESS) are no longer offered as a WAMP pairing in Settings -> Mailchimp. Mailchimp's address is a composite object (street + city + zip + country) that does not map 1:1 to WAMP's Address column (which is street only).
  • Change: address-type tags are now composed automatically by the sync from WAMP Address (street) + City + Zip + Country (or the Default country admin setting). All other tags still require explicit pairing on the settings page.
  • UI: the WAMP-column dropdown for address-type rows is replaced with an info note explaining the auto-composition.

1.8.89 (2026-06-19)

  • Change: Mailchimp sync now ONLY processes merge tags that the admin has explicitly paired on Settings -> Mailchimp (Merge Field Mapping). All built-in default mappings and same-name fallbacks have been removed. Unpaired tags are never synced.
  • Removed: get_default_merge_mapping() (no longer used; the active mapping is the only source of truth).
  • Removed: "Create missing merge tags" no longer creates tags from a hard-coded fallback list — it only creates the tags currently paired in settings.

1.8.88 (2026-06-19)

  • Fix: ExtID column is now exposed in the Mailchimp Merge Field Mapping UI dropdown and accepted by the save handler. Default mapping references it (EXTID -> ExtID); previously it would have been silently filtered out by the allowed-columns whitelist when admin tried to map it.

1.8.87 (2026-06-19)

  • Fix: default mapping merge tag names now respect Mailchimp's 10-character limit. Previously NEXTPAYMENT (11) and MEMBERSTATUS (12) were either rejected at create or silently truncated, breaking sync. Now using NEXTPAY and MSTATUS.
  • Improved: mc_upsert_request log entries now state the EXACT reason a mapped WAMP column was not sent (e.g. "NEXTPAY: not created in Mailchimp audience" or "ADDRESS: address requires country (WAMP has no Country and default country setting is empty)").

1.8.86 (2026-06-19)

  • New: "Default country (address sync)" setting under Mailchimp options. Holds an ISO-2 country code (e.g. DK) that is used as the country in Mailchimp address payloads when the WAMP Country column is empty (or the column does not exist on this install). This is an explicit admin configuration, not a silent fallback.
  • Result: members with Address/City/Zip but no Country (e.g. Sara: "Øresunds alle 25", 2791 Dragør) now sync to Mailchimp's ADDRESS field when the default country is set.

1.8.85 (2026-06-19)

  • Improved: Mailchimp default merge mapping now also covers BIRTHDAY, AGE, GENDER/MALEFEMALE, NEXTPAYMENT, MEMBERSTATUS, STATUS, EXTID, NOTE1, NOTE2. These are pushed by audit Add / Batch Add / Auto-sync when the corresponding tag exists in the Mailchimp audience.
  • New: WAMP_PAYLOG now records the exact upsert payload sent to Mailchimp from the audit page, including (a) non-empty WAMP columns for the member, (b) the merge_fields actually sent, (c) WAMP columns that have a mapping but did NOT get included (and which MC tag was expected). Use this to diagnose any missing data.

1.8.84 (2026-06-19)

  • Improved: replaced the comma-separated "Delete merge tags" input with a per-row trash icon in the Merge Field Mapping table. Click the icon, confirm, and the merge tag is removed from Mailchimp via AJAX. Reserved tags (EMAIL, UNIQID, GUID) show a lock icon instead.

1.8.83 (2026-06-18)

  • New: "Delete merge tags in Mailchimp" input + button under Settings -> Mailchimp -> Merge Field Mapping. Admin enters comma-separated tag names and confirms; the merge fields are removed from the Mailchimp audience via DELETE /lists/{id}/merge-fields/{merge_id} (irreversible). Requires manage_options + nonce.

1.8.82 (2026-06-18)

  • Fix: skip Mailchimp-reserved merge tag names (UNIQID, EMAIL, GUID) when creating tags via the settings button (Mailchimp returns HTTP 400 "reserved and can't be used"). CS_UNIQID is still used as the WAMP UniqID merge field.
  • Hardened: "Create missing merge tags" no longer aborts the whole batch on a single reserved-name response.

1.8.81 (2026-06-18)

  • Fix: ZIP merge tag is now explicitly created as text in Mailchimp (postnummer / Danish ZIP, preserves leading zeros).
  • New: "Fix merge tag types" button under Settings -> Mailchimp -> Merge Field Mapping. PATCHes existing merge fields in Mailchimp so their type matches WAMP expectations (e.g. ZIP -> text).

1.8.80 (2026-06-18)

  • New: explicit "Create missing merge tags in Mailchimp" button under Settings -> Mailchimp -> Merge Field Mapping; calls Mailchimp API directly to create configured/default tags (FNAME, LNAME, PHONE, MOBILE, ADDRESS, CITY, ZIP, COUNTRY, UNIQID, CS_UNIQID, MEMBERNO, ID, etc.) and ADDRESS as native address type.
  • Removed: implicit on-the-fly auto-creation of merge tags during sync (was confusing and ran without admin consent).
  • Removed: country fallback to DK in address sync. Address payload is now built strictly from WAMP Country value (skipped when missing/invalid).

1.8.79 (2026-06-18)

  • Fix: ADDRESS merge tag is now created via Mailchimp API as address type (not plain text), so structured address sync works correctly.
  • Fix: address sync now applies country fallback (DK) when country is empty, preventing address payload from being skipped.

1.8.78 (2026-06-18)

  • Improved: Mailchimp audit/add/sync now ensures configured/default merge tags exist in the audience before building merge payloads.
  • Fix: preserved default merge-tag mappings even when custom mapping is saved, so tags like MOBILE, UNIQID and CS_UNIQID continue to sync as expected.

1.8.77 (2026-06-18)

  • Change: removed Mailchimp URL setting from WAMP Mailchimp options.
  • Change: Mailchimp API base URL is now derived automatically from API key datacenter suffix (for example -us22 -> https://us22.api.mailchimp.com/3.0).
  • Fix: prevents merge-tag fetch HTTP 404 caused by entering admin UI host URLs.

1.8.75 (2026-06-16)

  • New: when an existing member's email is changed (from admin Members module or the front-end portal), the Mailchimp sync handler now automatically reconciles the Mailchimp audience without admin having to run the Mailchimp Audit page. Flow in WAM_Premium_Mailchimp_Sync::sync_member_to_mailchimp() after the wam_member_updated action fires: (1) GET Mailchimp by the new email's subscriber hash — if a contact exists there, the row is upserted normally (PUT with merge fields, same as before). (2) If no contact exists under the new email, the handler queries Mailchimp's /search-members endpoint with the WAMP UniqID and filters the result to a contact whose CS_UNIQID merge field matches exactly; this finds the SAME person under a different (old) email. (3) Dual-membership guard: before touching the old email's Mailchimp contact, the handler checks wam_members for any OTHER active row (DelReason=0, different id) that still owns the old email — if yes (e.g. household with 1 MemberNo and 2 names where the second person just got their own email added), the old contact is left alone and only the new email is upserted on the renamed person. (4) When the old email is exclusively this person's, the handler PATCHes /lists/{list_id}/members/{md5(old_email)} with the new email + full merge fields if the old contact's status is subscribed/unsubscribed/pending; for cleaned/archived/transactional (Mailchimp forbids PATCHing those — typos on cleaned addresses being the user's documented case) the old contact is permanently deleted via /actions/delete-permanent and a fresh contact is created under the new email via PUT. (5) If no UniqID match is found at all, the upsert simply creates a new contact under the new email as before. New helper search_mailchimp_member_by_uniqid() is also reusable from other flows. No call sites need to change — the existing wam_member_updated action payload ([ 'id' => N ]) is enough; the rename detection is fully server-side via the Mailchimp API.

1.8.74 (2026-06-16)

  • New: new online member signups via the front-end / QuickPay flow are now automatically pushed to Mailchimp at the moment the wam_members rows are INSERTed. Previously this was only handled by the Mailchimp Audit page after the fact (admin had to run a manual batch). Implementation: WAM_Premium_Helper::create_members_from_order() now fires the existing wam_member_created action for the freshly-inserted primary row (and the duplicate/co-applicant row when present), passing [ 'id' => N ] to match the payload shape already used by the admin members-AJAX code paths. The existing WAM_Premium_Mailchimp_Sync::sync_member_to_mailchimp() handler picks up the action when Mailchimp sync is enabled (option mailchimp_sync = 1); it reads the row from wam_members, builds merge fields from the configured mapping, and calls upsert_member_row_to_mailchimp() (PUT to /lists/{list_id}/members/{md5(email)}, status = subscribed). The action runs only AFTER successful insert, AFTER the idempotency guard short-circuits, so calling create_members_from_order() multiple times for the same order does not produce repeat Mailchimp upserts. Safe by design: when Mailchimp settings are missing, the option is disabled, the member has no email, or Mailchimp returns an error, the helper does NOT fail the order — the sync result is logged to WAMP_PAYLOG (wam_member_mc_sync_triggered) and the existing Mailchimp Audit page remains as the recovery tool for after-the-fact reconciliation.

1.8.73 (2026-06-16)

  • Fix: WAM ID Audit "Soft-deleted rows pending hard delete" table was confusing because it showed the MemberStatus column (category id, values 1-5) but no explicit DelReason column, so it looked like rows with DelReason != 99 were being offered for hard-delete. The SQL filter is correct (WHERE DelReason = 99), but the UI didn't display it. Now the table includes an explicit DelReason column that should always read 99 for every row in this section, and a runtime guard skips any row whose DelReason somehow isn't 99 (belt-and-braces: never offer hard-delete on a non-soft-deleted row).

1.8.72 (2026-06-16)

  • Improved: WAM ID Audit cleanup is now a two-step flow. Step 1 (duplicate groups, top of the page) keeps the Soft-delete button on unreferenced rows — it sets DelReason = 99 (reversible, row stays in the database). Step 2 is a new section below labeled "Soft-deleted rows pending hard delete" that lists every wam_members row with DelReason = 99 and is NOT referenced by any shop_order. Each such row gets a Hard-delete button which performs DELETE FROM wam_members WHERE id = X after a JS confirm and a safety re-check on the server (refuses if any order has appeared that references the row). This separates the destructive step from the discovery step: admin first soft-deletes a row, verifies nothing else breaks (welcome emails, portal logins, payment receipts), and only then hard-deletes from the second section. Both buttons go through POST with nonce + manage_options check; hard-delete additionally re-verifies the row's DelReason is still 99 before issuing the DELETE.

1.8.71 (2026-06-16)

  • Improved: WAM ID Audit page redesigned to focus on actual duplicate cleanup. Instead of listing every order with its sibling rows, the page now queries wam_members directly and shows ONLY groups where 2 or more rows share the same MemberNo AND the same normalized name (Fname + Lname, case/whitespace insensitive) — i.e. the actual triple-INSERT bug duplicates. Each group lists its rows in id order with: row id, name, email, MemberStatus, DelReason, and a "Referenced by" column showing every shop_order that points to this row via _wam_member_primary_id or _wam_member_second_id. Rows that are NOT referenced by any order (the bug-leftover duplicates) get a Soft-delete button that sets DelReason = 99 (POST + nonce + manage_options capability check), removing the row from active lookups without losing data. Order-based filters (filter by order id, only-issues checkbox) are removed since the page is now member-centric; the MemberNo filter is kept for spot-checks. Page remains read-only by default — only the explicit per-row Soft-delete button writes (one row at a time).

1.8.70 (2026-06-16)

  • Improved: WAM ID Audit page now also surfaces sibling member rows for each order's MemberNo. For every order the page already showed _wam_member_primary_id and _wam_member_second_id; it now adds a "Sibling rows for MemberNo X" sub-row that lists every wam_members row sharing the same MemberNo and tags each one as PRIMARY (referenced as _wam_member_primary_id on this order), SECOND (referenced as _wam_member_second_id on this order), OTHER ORDER #N (referenced by a different shop_order via either primary or second meta), or UNREFERENCED (no shop_order references it). UNREFERENCED rows that match the primary row's name+email are the prime suspects for triple-INSERT bug duplicates and are highlighted in red. The issue-flag now also triggers when sibling rows look like bug duplicates (extra UNREFERENCED rows with identical name+email as the primary), making the "Only show issues" filter directly useful as a cleanup work list. Read-only — still no writes.

1.8.69 (2026-06-16)

  • New: admin-only audit tool under WebaLive Members Premium → "WAM ID Audit" (page=wam-premium-wam-id-audit). Read-only diagnostic view that lists every WooCommerce shop_order which carries _wam_member_primary_id post meta together with the related _wam_member_second_id, _wam_memberid, _wam_memberno, _wam_is_new_member, _wam_category_id and _wam_order_sku values. The page resolves each id back to its wam_members row (Fname/Lname/Email/MemberStatus/DelReason) so an administrator can quickly confirm which row is the canonical "valid" member for an order. Used as the inventory step for cleaning up triple-INSERT bug duplicates: it makes the order ↔ member id linkage visible without touching any data. Page requires manage_options and is hidden from non-admins. Filters: by MemberNo, by order id, and a checkbox to only show orders where the resolved primary or secondary id is missing/soft-deleted (delreason != 0 or row gone). No data is written; no AJAX actions; nothing scheduled.

1.8.68 (2026-06-16)

  • Fix: the membership-payments table inside the member detail modal (admin Members page) was empty for newly signed-up members — their first kontingent/subscription payment did not appear even though the row existed in wam_payment with the correct memberno. Root cause: the SQL filter in fetch_member_detail_callback() only matched rows where reference LIKE 'Kontingent %' OR orderno LIKE 'BS%', which fits portal-renewal orders (whose _wam_order_title is set to "Kontingent ÅÅÅÅ") and BS-import rows, but NOT brand-new signups. New-member rows are written by WAM_Premium_Helper::upsert_payment_from_quickpay(), which derives reference from the order's _wam_order_title (typically the product/category name like "Nyt medlemskab") and uses the WC order id as orderno (no BS prefix), so they never matched the filter. Fix: extend the filter so the modal also picks up rows where category is WAMP_PAY_CATEGORY_NEW (1) or WAMP_PAY_CATEGORY_RENEW (2), keeping the existing legacy reference/orderno matches as a fallback for older data and BS imports. The category column is set consistently by get_order_category_info() and is the same field that already drives the renewal-receipt email gate, so this aligns the UI with the rest of the membership-payment logic.

1.8.67 (2026-06-11)

  • Fix: new online member signups via WooCommerce + QuickPay created the member 3 times in wa_members instead of once. Root cause: WAM_Premium_Helper::create_members_from_order() is invoked from three independent code paths for the same WooCommerce order — WAM_Premium_Gateway::process_payment() (gateway.php:136, runs at checkout submit), the Woo payments finalizer (woo-payments.php:232, runs in the WC order completion chain), and the QuickPay server callback receiver (wam-premium-callback.php:641, runs when QP POSTs the capture callback). Each path was protected by the existing _wam_is_new_member='1' gate (added earlier to stop portal-kontingent orders from inserting), but there was no idempotency check on the function itself, so all three calls executed $wpdb->insert(WAMP_MEMBER, ...) — and if a co-applicant (_wam_duplicate_member) was present, the result was 6 rows. Fix is a single idempotency guard placed at the top of create_members_from_order(), right after the new-member gate: if _wam_member_primary_id is already set on the order (>0), the function logs member_create_skipped_duplicate and returns the existing primary/duplicate IDs without inserting. This makes the function safe to call any number of times for the same order from any hook, without changing the public contract (callers still get a success/primary_id/duplicate_id result). No call sites were removed — keeping all three paths means a member is still created reliably even if one hook fails to fire (e.g. QuickPay callback delayed, or order completion hook suppressed by another plugin).

1.8.66 (2026-06-11)

  • Fix: latent namespace-resolution fatals in three namespaced AJAX classes (class-wam-premium-kontingent-ajax.php, class-wam-premium-payments-ajax.php, class-wam-premium-members-ajax.php). Same root cause as 1.8.62/1.8.65: each file begins with namespace WAM_Premium\Admin;, but the capability check used WAM_Premium_Roles::can_manage() and one helper call used WAM_Premium_Helper::waet_enqueue_by_uuid() without a leading backslash. PHP resolves unqualified class names into the current namespace (no fallback to global for classes), so these calls would have fataled the moment any of these AJAX endpoints were hit (bulk_charge, send_request, delete_member, pdf_nocontact, search_member, create_payment, move_payment_member, mark_paid, queue_payment_request_email). Fixed 10 references in total (6 in kontingent-ajax, 3 in payments-ajax, 1 in members-ajax) by switching to the fully qualified \WAM_Premium_Roles::can_manage() / \WAM_Premium_Helper::... form and using class_exists('\\WAM_Premium_Roles') so the existence check also targets the global namespace. The class-wam-premium-poa.php file uses the same pattern but is in the global namespace and was unaffected; left untouched.
  • Audit: scanned all 17 namespaced WAMP admin classes plus the front include files for the same defect. No other instances found. WC function calls (wc_get_order, get_woocommerce_currency_symbol, etc.) in the rest of the plugin are either already guarded with function_exists() or live inside code paths that only run when WooCommerce is active (gateway, finalizer, autocharge, renewals, callback, woo-payments, quickpay-metabox).

1.8.65 (2026-06-11)

  • Fix: fatal "Call to undefined function get_woocommerce_currency_symbol()" when opening the Payments / Kontingent page on sites where WooCommerce is not active. The call in Kontingent_Page::render() (class-wam-premium-kontingent-page.php:151) was unguarded; all other WC calls in the codebase already use function_exists(). Now uses the same guard with a DKK fallback. (Error was reported as WAM_Premium\Admin\get_woocommerce_currency_symbol because the file is namespaced and PHP shows the FQ name in not-found errors.)

1.8.64 (2026-06-10)

  • New: "Side panel flyout breakpoint" setting under Settings → General (members_options tab). Lets the site admin choose at which viewport width (px) the right info panel collapses into the flyout introduced in 1.8.63. Default 1280, allowed 600–2400, 0 = always show as flyout, -1 = never use flyout (panel stays inline at all viewports). Stored in option wam_premium_side_flyout_breakpoint.
  • Refactor: the flyout @media block was moved out of the static wam-premium.css and is now emitted dynamically from wam-premium-right-column.php using the saved breakpoint. The CSS file only keeps the base hidden state for the toggle/close/backdrop elements.

1.8.63 (2026-06-10)

  • Improved: on narrow viewports (≤ 1280px) the right-hand WAMP info panel (#wam-premium-admin-side) is no longer just hidden — it now slides in from the right edge as a flyout. A small toggle button is fixed to the right margin (chevron icon) so the user can pull it in on demand. Close via the X inside the panel, the backdrop, or the Esc key. Wide screens are unchanged. Implementation is plain CSS + a few lines of vanilla JS, no jQuery dependency.

1.8.62 (2026-06-08)

  • Fix: fatal "Class WAM_Premium\Admin\WA_Members_Admin_Helper not found" thrown from the members-list AJAX handler (get_columns() in class-wam-premium-members-list.php), which made the list render empty rows on every site. The helper class lives in the GLOBAL namespace, but the list-table file is namespaced as WAM_Premium\Admin, so the unprefixed reference resolved into the wrong namespace at call time. The string-based class_exists('WA_Members_Admin_Helper') check did NOT catch it because that form already looks in the global namespace. Fixed by using the fully qualified \WA_Members_Admin_Helper reference (plus matching class_exists('\\WA_Members_Admin_Helper')).

1.8.61 (2026-06-08)

  • Fix: Settings → Table options now correctly saves per-column Label and Max width values. Previously the Removed Member List table rendered the same name="wam_premium_list_column_labels[KEY]" and name="wam_premium_list_column_widths[KEY]" inputs as the Active table; PHP keeps the LAST value for duplicate keys in $_POST, so the empty input from the Removed table silently overwrote whatever the user typed in the Active table, and the save handler then skipped the empty value entirely. The Removed list now shows Label and Max width as read-only and points the user to edit them in the Active list above. Both lists keep their own independent Show-checkboxes and column order.

1.8.60 (2026-06-08)

  • Improved: members-list column picker (Settings → Table options) now introspects the actual wa_members table via SHOW COLUMNS and only offers UI keys that are backed by an existing DB column. Keys mapping to virtual/composite columns (Name requires Fname+Lname, Street requires Address) are handled in the mapping. Result is cached per-request.
  • Hardened: the WP_List_Table now also filters the rendered column headers against the existing DB columns, so old saved selections for fields that have since been removed from the schema silently drop out instead of producing empty columns.

1.8.59 (2026-06-08)

  • Fix: Members list (page=wam-premium) showed an empty table on sites whose wa_members table does not contain every optional column added in 1.8.57 (Title, MaleFemale, Country, ExtID). On those sites the explicit SELECT failed (Unknown column) and the whole result set came back empty. The query now selects * so missing optional columns no longer abort the query; the row mapping already uses ?? '' fallbacks for the optional fields, so cells stay empty when the underlying column is absent.

1.8.58 (2026-06-07)

  • New: per-column "Max width (chars)" setting under Settings → Table options for both Active and Removed member lists. When set (> 0) the cell is clipped with an ellipsis at that character width, so wide tables fit in narrow viewports without breaking text into multiple lines. Empty value = no limit (default WP behavior).
  • Improved: responsive layout for the members list (page=wam-premium). On viewports below 1280px the right sidebar ("other plugins" / promo) is hidden and the table wrapper allows horizontal scroll instead of squeezing text. Below 960px the list-table font size is reduced (13px) and below 782px further reduced (12px) with tighter cell padding, so content stays readable on laptops and tablets without browser zoom.

1.8.57 (2026-06-07)

  • Fix: Members list (page=wam-premium) now actually shows data for the optional columns Title, Country, Gender (MaleFemale) and ExtID when they are enabled under Settings → Table options. The underlying query and row mapping were missing these database fields, so the column header appeared but the cells were always empty even though phpMyAdmin showed valid data.

1.8.56 (2026-06-01)

  • Fix: Admin-bar "Members Premium" entry is now visible to the Member Administrator role (not only administrators). Registration was moved out of the admin-only class so the entry appears in all contexts (front-end and back-end) for any user with the WAMP management capability.
  • New: Plugin icon shown next to the "Members Premium" label in the admin bar.

1.8.55 (2026-06-01)

  • New: WAMP entry added to the WordPress admin bar (top black bar). Visible to administrators and to the Member Administrator role. Provides quick links to Members, Categories, Payments, Subscriptions, POA (when enabled) and Orders (when WooCommerce is active). Settings link is shown to administrators only.

1.8.54 (2026-06-01)

  • Change: POA feature now defaults to disabled (wam_premium_poa_active = no) on sites that have never set the option. Existing sites where the option has been explicitly saved (yes or no) are unaffected. To enable POA, tick the checkbox under Settings → POA options.

1.8.53 (2026-06-01)

  • UI: "POA Entries" submenu is now only registered when POA is enabled in Settings (wam_premium_poa_active = yes). When POA is disabled, the menu item is hidden.
  • UI: "Orders" submenu under Members Premium is now only registered when WooCommerce is active. On sites without Woo, the item is hidden.

1.8.52 (2026-06-01)

  • New: dedicated "Member Administrator" role + custom capability wamp_manage_members so non-admin staff can manage the member registry without being granted full WordPress administrator rights. Administrators automatically receive the capability (auto-installed on plugin update via WAM_Premium_Roles::install(), idempotent). The new role grants only basic read + wamp_manage_members — nothing else.
  • Access: the following pages and AJAX actions are now gated by the new capability (Administrator + Member Administrator can use them): Members, Categories, Subscriptions, Payments, Import Payments, Import Members, POA Entries, Orders, plus the kontingent AJAX (bulk_charge, send_request, delete_member, pdf_nocontact, search_member, create_payment), refresh_member_next_payment, move_payment_member, queue_payment_request_email, and POA add/delete AJAX endpoints.
  • Access: the following remain administrator-only (manage_options): Settings, Mailchimp Audit, WAM-Go Audit, and the WAM-Go column in the members list. These are system-configuration / debug tools.
  • Frontend: the existing member-only-page bypass (wam_premium_mep_member_only_bypass) now also bypasses for users with the new capability, so Member Administrators can view member-only content while supporting users.
  • Helper: WAM_Premium_Roles::can_manage() is the single source of truth for runtime checks; admins always pass via fallback to manage_options so the gates remain robust even mid-upgrade.

1.8.51 (2026-05-30)

  • Fix: Mage event orders that pay through the WAM gateway and create a subscription in the same flow as the first charge (typical case: member who has previously paid kontingent via bank and now buys an event ticket) sometimes ended up with WWEF attendee posts but no Mage attendee posts (mep_events_attendees), leaving the ticket PDF without QR code or ticket number. Order #17449 is a representative example. The QuickPay callback now calls WAM_Premium_Payment_Finalizer::ensure_event_data($order_id) immediately after the existing finalize sequence; this re-fires woocommerce_order_status_{current} and _changed so Mage's and WWEF's own attendee-creation hooks get a second chance. Idempotent (gated by _wamp_event_data_bound order-meta), no-op for non-event orders, no-op when Mage attendees already exist. Member-create, kontingent emails, and all other WAMP business logic are untouched.

1.8.50 (2026-05-30)

  • Internal (additive only, no behavior change): introduce skeleton classes WAM_Premium_Payment_Finalizer and WAM_Premium_Post_Payment in lib/classes/. These define the contract for a future unified payment finalizer and the named action hooks for post-payment side-effects (member created/renewed, email dispatched, promotion applied, finalized). No existing payment path calls into them yet — they are loaded but inert. This is the structural foundation for migrating the 6 divergent payment paths (callback subscription, callback direct, autocharge, vipps, vipps renewal, admin/portal/bridge) onto a single code path in subsequent releases.

1.8.49 (2026-05-22)

  • Compatibility: Tested with WordPress 7.0 and WooCommerce 10.7.

1.8.48

  • Fix: Edit and Delete buttons in member details card modal now work correctly — they close the details modal and open the appropriate edit/delete modal with proper AJAX loading.
  • Fix: Modal transitions properly chain via fadeOut callback instead of triggering unhandled events.

1.8.47

  • Admin POA: "Send POA Reminders Now" now reports honestly. Previously the UI always said "sent" even when the reminder option was off, no verify page was configured, no candidate POAs existed, members had no email on file, or wp_mail() returned false / fired wp_mail_failed. The AJAX response now includes a stats object (reminder_enabled, verify_page, candidates, attempted, sent, skipped_no_email, mail_errors) and a human-readable reason when nothing is sent.

1.8.46

  • Admin POA PDF: Added section name (Confirmed in green / Unconfirmed in orange) at the bottom-center of every page so pages from the two sections can no longer be mixed up. Implemented via a pre-render of the Confirmed section to count its pages, then DomPDF's page_script() callback stamps the correct label on each page of the final PDF.

1.8.45

  • Admin POA PDF: Fixed unwanted page break right after the section subheader and content overflowing past the page bottom. Removed the outer table-wrapper that was used to repeat the colored banner — DomPDF could not break cleanly inside that giant cell. The Confirmed (green) / Unconfirmed (orange) banner is now a simple block at the top of each section.
  • Admin POA PDF: Footer now shows just Page X (the / Y total page count was never resolved by DomPDF inside a fixed-positioned footer and rendered as / 0). Also widened the bottom page margin so the footer no longer collides with the last data row.

1.8.44

  • Admin POA PDF: Reverted to a reliable fixed <footer> (site name, generation date, Page X / Y). The previous @page named-page margin boxes were unreliable in DomPDF and made the footer disappear entirely. The Confirmed (green) / Unconfirmed (orange) section indicator is now rendered as a repeating colored band at the top of every page within each section (via repeating table head), which is far more robust.

1.8.43

  • Admin POA PDF: Footer now shows the current section name (Confirmed in green, Unconfirmed in orange) on the right, instead of total page count. Implemented via DomPDF named pages and @page margin boxes.

1.8.42

  • Admin POA: All POA-related UI labels in the admin dashboard, the POA list page and the POA PDF export now use English source strings wrapped in __() / esc_html__() so they are properly i18n-ready (Danish translations belong in .po/.mo).
  • Admin POA: JS dashboard pill labels (Board, Members, Yes, No, Unlimited) are now passed in via wam_premium_poa.i18n instead of being hardcoded.

1.8.41

  • Admin POA: Reworked the bulk-action Export PDF from page=wam-premium-poa (Apply on the bulk dropdown). The PDF now uses the event/begivenhed as title and a footer with site name, generation date/time and page number.
  • Admin POA: PDF is split into two sections with a page break — first Bekræftede, then Ubekræftede. Each section shows a subheader with the total count for the section.
  • Admin POA: Rows are sorted by Fuldmagt til and then by first/last name. When Fuldmagt til changes, a small group block is rendered with a per-recipient mini dashboard (Antal, Ja, Nej, Ubegrænset).
  • Admin POA: List columns are now Medlem (navn + medlemsnummer i parentes), Fuldmagt til, Instruktion and Oprettet (dato).

1.8.40

  • Admin POA dashboard: Consolidated Bekræftet (confirmed count) into the Antal card, now showing format "Total (Confirmed)" for compact display.
  • Admin POA dashboard: Removed the separate Bekræftet card; one less visual element to reduce clutter.
  • Admin POA dashboard: Added distinct background colors to each dashboard card for better visual hierarchy and semantic clarity.

1.8.39

  • Admin POA dashboard: Refined visual styling for better readability with clearer, card-level breakdown layout for Fuldmagt til and Instruction.
  • Admin POA dashboard: Breakdown metrics are now shown as compact styled items (still including confirmed counts in parentheses).

1.8.38

  • Admin POA dashboard: Improved summary details with clearer breakdowns:
  • Fuldmagt til now shows board vs member counts and confirmed counts in parentheses.
  • Instruction now shows Ja/Nej/Ubegrænset counts with confirmed counts in parentheses.

1.8.37

  • Admin POA: Added a dashboard at the top of the page=wam-premium-poa screen in the same card style as page=wam-premium, showing totals for Antal, Fuldmagt til, Instruction, and Bekræftet.
  • Admin POA: Dashboard counters now update live when using search/event filters and after deleting entries.

1.8.36

  • Portal Profile: Member 2 (partner) card now has its own independent Edit / Save button, so the primary member card and the partner card can be edited and saved separately without affecting each other. Saving Member 2 only writes the partner's email/mobile and never touches the primary member row. New AJAX/REST endpoint wam_profile_save_secondary (wam/v1/profile-save-secondary).

1.8.35

  • Fix: Fatal Cannot declare class ComposerAutoloaderInit… when generating POA PDF (and other dompdf paths). The plugin shipped two identical dompdf installations (lib/dompdf/ and dompdf/dompdf/); both autoloaders could be required in the same request, so the same composer init class was declared twice. All dompdf require sites now go through lib/dompdf/autoload.inc.php and are guarded by class_exists('\Dompdf\Dompdf') to defend against other plugins that ship dompdf as well.
  • WAM-Go: Added a third, ultra-permissive rewrite (/wam-go/.+?/<post_id>/) and a template_redirect 404 safety net that catches any /wam-go/…/<numeric post_id>/ URL the rewrite rules missed (e.g. literal << Test MemberNo >> segments containing characters the web server normalizes/strips). The visitor is redirected to the trailing post permalink without any login attempt.
  • Portal POA tab: Added an intro panel at the top of the tab with a Create power of attorney button that links to the configured POA shortcode page (Settings → POA page). Shown both when the member has no documents yet and above the existing list.
  • Portal UI: Reduced tab padding and font size and forced nowrap so the now seven possible tabs (Profile, Cards, Subscription, Payments, Orders, POA, Settings) try to stay on a single line on desktop. On narrow screens they wrap normally with even tighter spacing.
  • i18n: New keys poa_create_intro, poa_create_btn.

1.8.34

  • WAM-Go: More aggressive detection of unresolved newsletter merge tags / preview placeholders. Links containing <<…>>, *|…|*, or text like Test MemberNo/Test UniqueId (sent by Mailchimp test campaigns) now skip the login attempt and redirect straight to the target post (e.g. /wam-go/<< Test MemberNo >>/<< Test UniqueId >>/9115/ lands on post 9115 without login).
  • WAM-Go: When an actual login attempt fails (invalid token, unknown member), the visitor is now redirected to the requested post permalink instead of the site home page, so the intended content is still shown.
  • POA shortcode: When the visitor is already logged in (e.g. arrived via /wam-go/), the [wam_poa] shortcode now auto-detects their member number from their account and goes straight to the candidate view, hiding the "Enter Your Member ID" search field.

1.8.33

  • i18n: Added missing PHP-localized strings payment ("Payment") and phone ("Phone") used as labels in the portal MobilePay subscription card. Previously fell back to the lowercase i18n key (so the labels rendered as untranslated "PAYMENT"/"PHONE" via CSS uppercase, with no way to translate them).
  • UX: Portal MobilePay subscription card — payer phone is now displayed formatted (+45 21 41 12 66 instead of 4521411266). Falls back to the raw value if the format cannot be detected.

1.8.32

  • Fix: WAMP WAET bootstrap now ensures WAET template/variable tables exist before creating templates, and only treats a template as created if it can be read back afterwards. This fixes cases where no WAMP WAET templates appeared even after deleting them and reloading.

1.8.31

  • Improve: Added a standard organization footer to WAMP WAET startup templates: © {{org_name}} • {{org_addr}} • {{org_zip}} {{org_city}} • {{org_email}}.

1.8.30

  • Fix: WAMP WAET bootstrap now retries automatically when the expected WAMP-owned templates are still missing, instead of treating the version flag alone as success. This prevents a "no templates created" state from getting stuck.

1.8.29

  • Fix: WAMP WAET startup templates now ship their header title contrast through template CSS (WAET-native) instead of relying on inline heading color, and existing WAMP-owned WAET templates get that CSS rule added on bootstrap.

1.8.28

  • Fix: WAET startup template headers now force white heading text in the header block so {{subject}} always keeps contrast against the header background across themes.

1.8.27

  • Improve: Updated WAET startup table markup so table cells wrap content in <div> inside <td> for better WAET editor compatibility when editing templates later.

1.8.26

  • Improve: WAMP WAET startup templates are now theme-aware and built with WAET theme classes (container, header, content, footer, button/btn) so they respect selected WAET theme settings (light/blue/red).

1.8.25

  • Improve: Added a dedicated WAMP startup-template folder for WAET (templates/waet-startup/) with maintainable template files and manifest.
  • Improve: WAET bootstrap now loads startup templates from files, making it easier to refine and ship polished default templates.

1.8.24

  • New: WAMP now auto-creates ready-to-use WAET templates for all Email settings cards (standard member message, payment request, membership receipt, new member receipt, one-time code, subscription receipt) and auto-assigns them in WAMP settings when missing.
  • New: WAMP now auto-registers required WAET merge tags used by these templates, so templates remain editable and fully functional after customization.

1.8.23

  • Fix: Portal MobilePay subscribe — payment row reference is now always Kontingent YY (e.g. Kontingent 26) instead of {Cat_name} YY. This makes the row appear in the Kontingent overview list (which filters on reference LIKE 'Kontingent %' OR orderno LIKE 'BS%') — previously the latest MobilePay payment was missing from the list.
  • i18n: Replaced Danish source strings with English (Danish moves to .po/.mo translations as it should). All visible English fallback strings in the front portal now use proper English: "Skift betalingsmetode" → "Change payment method", "Start fast betaling med MobilePay" → "Start MobilePay subscription", "Current kontingent" → "Current membership fee", "Kontingent payment completed." → "Membership fee payment completed.", "No kontingent payments found." → "No membership fee payments found." etc. Added missing PHP-localized strings for previously JS-only fallbacks (okMobilePay, okExplainedMp, retry_card, actions, active, total, view, no_orders, signing_in, enter_code, member_number, link_account, email_diff, no_access, yes_verify, no_member_found, kontingent_paid_*).
  • Note: i18n keys themselves still use the kontingent_* / noKontingent identifiers (they are not user-visible). The DB field WAMP_PAYMENT.reference = 'Kontingent YY' is also unchanged because it is referenced by the SQL filter reference LIKE 'Kontingent %' used to build the membership fee overview — changing the reference would orphan all existing payment rows.

1.8.22

  • Fix: Portal "Fast betaling" tab — Tilføj betalingskort (QuickPay card setup) is now hidden on sites where Vipps/MobilePay is the configured payment method. Previously the QuickPay button could appear when no MobilePay agreement existed yet, even on Vipps-only sites.
  • Fix: After a successful MobilePay portal subscription the user landed on a blank ?tab=payment page (the legacy payments tab does not exist in the new layout). The Vipps return URL now sends the member to ?tab=kontingent so the Kontingent tab is focused.
  • Fix: Portal MobilePay subscribe (existing member) now mirrors the new-member flow end-to-end: an initial_charge_amount equal to the kontingent is sent to Vipps, and on return a WAMP_PAYMENT row (status=processed, type=MobilePay, acquirer=vipps, RENEW category, orderno=MB{yy}{memberno}, reference={cat_name} {yy}, transid=chargeId) is inserted plus WAMP_MEMBER.NextPayment is updated. The Kontingent tab now correctly shows "Kontingent betalt" instead of "Ikke betalt" after subscribing.

1.8.21

  • Change: Vipps productName now contains only the category/type name (e.g. Husstandsmedlem) — the site-name prefix (Nordstrandforeningen - ) has been dropped. Affects both the new-member signup flow and the portal Start fast betaling/Skift betalingsmetode flow.

1.8.20

  • (placeholder)

1.8.19

  • Fix: Portal "Kontingent" tab — the Start fast betaling med MobilePay button is now only shown when the member actually needs to pay (canPay or isOverdue). Members whose kontingent is already paid ("Kontingent betalt") no longer see the button.
  • Fix: "Sæt din adgangskode" (welcome-flow direct password set) returned Ugyldig handling. The REST endpoint wam/v1/password-change only handled send/verify steps; the set_direct step now works there too (parity with the AJAX handler).
  • Fix: Portal "Kontingent" tab — when Vipps/MobilePay is enabled, the Betal nu (QuickPay one-shot card) button is hidden so members only see the MobilePay flow. When Vipps is disabled, only the Betal nu card flow is shown.

1.8.18

  • Fix: payer_phone on MobilePay subscription rows is now reliably populated. Vipps Recurring v3 GET /agreements typically does not echo back phoneNumber (it sits behind userinfoUrl which requires extra OAuth scope), so we now (1) persist the row immediately on agreement creation with status='pending' and the phone we just sent to Vipps — capturing the number "while we still have it", before the customer ever changes MobilePay number; (2) on return, fall back through: agreement response → transient → WAMP_MEMBER.Mobile (E.164-normalized); and (3) self-heal existing MobilePay rows with empty payer_phone on the next portal load via a one-shot UPDATE from WAMP_MEMBER.Mobile.

1.8.17

  • New: Portal "Fixed Payment" tab — for active MobilePay agreements a Skift betalingsmetode button now lets the member stop the current Vipps agreement and start a new one (e.g. when their MobilePay number changes). Implemented via new AJAX action wam_vipps_portal_subscribe.
  • New: Portal "Kontingent" tab — members without an active recurring agreement now see a Start fast betaling med MobilePay button that initiates a new Vipps agreement (no initial charge; first real charge happens at next renewal). Reuses the same AJAX action.
  • New: WAM_Premium_Vipps_Redirect handles a mode='portal_subscribe' transient — on ACTIVE status the subscription row is upserted (without creating a new member / WP user) and the user is sent back to the portal payment tab.
  • Fix: A logout_redirect filter (priority 99) now honors ?redirect_to= from the logout URL when the host matches home_url(), overriding third-party plugins/themes that force a home redirect on logout.
  • New: i18n strings change_payment_method, confirm_change_payment_method, start_mobilepay_subscription, payment_setup_redirect_mp.

1.8.16

  • Fix: A wam_subscriptions row is now ensured for every order that carries a _wam_qp_subscription_id when WAM_Premium_Helper::maybe_mark_order_completed_after_capture() runs. Closes the gap where a QuickPay callback that fires late, in non-success state, or not at all (admin completion / async finalization) left no Fixed Payment row even though the order was marked paid.
  • Fix: Portal "Fixed Payment" tab now self-heals: when no active wam_subscriptions row exists for the member, the lookup falls back to wam_payment for a captured row carrying a subscription_id and rebuilds the subscription row. For Vipps/MobilePay agreements the live agreement is fetched (get_agreement) so status, payer phone, amount, currency and interval reflect the real state.
  • Schema: wam_payment.transid widened from INT to VARCHAR(64) via dbDelta on activator run. Vipps chargeId (e.g. chr_xxxxxxxx) was being silently cast to 0 on insert, so legacy rows show no transid; new inserts will now persist the real id. (Existing zero-valued rows are not backfilled.)

1.8.15

  • New: After a brand-new member is created from a frontend payment (_wam_is_new_member order), the QuickPay continue page now redirects to /wam-go/<memberno>/<uniqid>/<portal_page_id>/?wam-welcome=<memberno>&tab=settings. The member lands already-logged-in on the configured portal page (wam_premium_portal_page_id) with the Settings tab open. Idempotent via _wam_welcome_redirected order meta.
  • New: Portal Settings tab now shows a direct “Set your password” form (no email verification code) for users who have never set a password — e.g. just-created members arriving via the welcome wam-go redirect. Eligibility is server-gated by user_meta _wam_password_set. After the first set, subsequent password changes use the existing OTP flow.
  • New: i18n strings set_password, set_password_help.

1.8.14

  • Fix: WAM_Premium_User_Sync now hydrates the member payload from the database when only an id is supplied to the wam_member_created action. Previously the handler returned early because the action payload contained no email, leaving any pre-existing WP user with a stale _wam_memberid after a member row was hard-deleted and re-created with a new auto-increment id. With hydration the existing WP user's _wam_memberid (and _wam_memberno) is now updated to the new row id at create time — preventing the same root cause that 1.8.13 only patched at portal-boot time.

1.8.13

  • Fix: current_memberid() now also re-links _wam_memberid when the stored meta value points to a non-existent or deleted member row (stale meta). Previously only missing meta triggered the auto-link. Fixes “No profile data” for users whose WAMP row was recreated/renumbered after the initial link.

1.8.12

  • Fix: current_memberid() auto-links _wam_memberid for logged-in users with missing meta, using the existing link_memberid_by_email_fname() helper. This makes the portal-boot path consistent with the OTP-verify, MEP-event-redirect, and member-link form paths that already auto-link. Fixes portal “ukendt medlem” after successful OTP login when meta was not set during verify.

1.8.11

  • Fix: portal Cards pane no longer crashes with ReferenceError: wam_premium_front is not defined — window. prefix added on wam_premium_front.portal.payment_tab lookup, matching the safe pattern used elsewhere in the script. This restores portal rendering for logged-in members on sites where the global was not yet bound at render time (regression vs. 1.8.7).
  • Fix: removed defensive SHOW COLUMNS … LIKE 'payer_phone' fallback in get_subscription_state_by_memberid(). The column is created/upgraded by dbDelta() via the activator on plugin version bump, so the runtime check is unnecessary noise.

1.8.10

  • Fix: version bump for migration retest; schema update is triggered on plugin version change.
  • Fix: subscription table create SQL keeps dbDelta-safe CREATE TABLE " . $table . " ( style.

1.8.9

  • Fix: table migration flow is now kept strictly version-driven; WAM_Premium_Activator::create_all_tables() runs on plugin version bump.
  • Fix: migration bootstrap now calls init_tables() in instance context.
  • Fix: settings warning removed by defining Free From Month saved value before first use.

1.8.8

  • Fix: portal subscription lookup now tolerates missing payer_phone column on older sites and returns null instead of breaking REST JSON.
  • Fix: schema upgrade version check aligned with current plugin version so create_all_tables() runs on version bump.
  • Fix: subscription table SQL declaration corrected for upgrade runs.

1.8.7

  • Fix: next_payment date calculation — free_from_month no longer double-skips year.
  • Fix: Free From Month setting now has "Not used" option (value 0) to disable the feature.
  • New: Payer phone number (E.164) stored from Vipps agreement in subscription record.
  • New: Portal Cards pane shows MobilePay icon, payer phone, and hides expiry/change-card button for MobilePay subscriptions.
  • New: Portal subscription data returns actual provider from DB instead of hardcoded 'quickpay'.
  • New: Member Portal settings tab in admin — toggle individual portal tabs active/inactive.
  • Fix: Member edit form double-member toggle — #memberstatus now has data-duplicate attribute and duplicate-fields HTML.
  • Fix: Admin JS change handler listens on both #CategorySelect and #memberstatus for duplicate toggle.
  • Fix: update_member_detail_callback now creates secondary member when switching to a duplicate category.

1.8.6

  • Fix: next_payment date calculation — free_from_month no longer double-skips year.
  • Fix: Free From Month setting now has "Not used" option (value 0) to disable the feature.
  • New: Payer phone number (E.164) stored from Vipps agreement in subscription record.
  • New: Portal Cards pane shows MobilePay icon, payer phone, and hides expiry/change-card button for MobilePay subscriptions.
  • New: Portal subscription data returns actual provider from DB instead of hardcoded 'quickpay'.
  • New: Member Portal settings tab in admin — toggle individual portal tabs active/inactive.
  • Fix: Member edit form double-member toggle — #memberstatus now has data-duplicate attribute and duplicate-fields HTML.
  • Fix: Admin JS change handler listens on both #CategorySelect and #memberstatus for duplicate toggle.
  • Fix: update_member_detail_callback now creates secondary member when switching to a duplicate category.

1.8.5

  • Fix: Membership rules modal rewritten with custom CSS/JS — no Bootstrap dependency.
  • Fix: Modal opens via jQuery click handler; closes on X, button, overlay click, or Escape key.
  • Enhancement: MobilePay payments now use order format MBYYNNNN (year + member number).
  • Enhancement: Payment status set to 'processed' instead of 'captured' for MobilePay.
  • Enhancement: Payment type set to 'MobilePay', reference to 'Kontingent YY'.
  • Enhancement: subscription_id and transid stored in payment records for Vipps tracking.
  • Enhancement: Renewal payments now include member name and email.
  • New: Refund button for MobilePay payments in admin Betalingsoversigt (calls Vipps Recurring API).
  • Enhancement: Subscriber overview shows Provider column and 'MobilePay' instead of card details.
  • Enhancement: Payment table shows Type and Reference columns.

1.8.4

  • Fix: Membership rules modal moved back inside form; uses native Bootstrap data-bs-toggle with href anchor.
  • Fix: HTML attributes kept out of translated strings to prevent translation cache conflicts.

1.8.3

  • Fix: Membership rules modal now opens via JS click handler instead of inline attributes, avoiding translation cache conflicts.

1.8.2

  • Fix: Membership rules link now opens in a Bootstrap modal instead of a new tab.
  • Fix: Modal placed outside form element to prevent form submission conflicts.

1.8.1

  • Feature: Vipps Order Prefix setting to prevent order ID collisions across sites sharing the same MSN.
  • Admin: New Member Payment Gateway selector (QuickPay / Vipps MobilePay / WooCommerce) controls form payment flow.

1.8.0

  • Feature: Vipps MobilePay Recurring integration for new member sign-up (Faste betalinger).
  • New API client class for Vipps Recurring API v3 (agreements + charges).
  • New AJAX handler for [wam_new_member_form] → Vipps MobilePay flow.
  • New redirect handler that polls agreement status and creates member on approval.
  • New cron handler for automatic renewals via Vipps charge API.
  • WAMP_SUBSCRIPTION schema: added provider, amount, interval_count, interval_unit, cat_id columns.
  • Updated upsert_subscription() to support new columns.
  • Admin settings: Vipps MobilePay configuration section (MSN, client_id, client_secret, subscription key, test mode, continue page).
  • Admin settings: New Member Payment Gateway selector (QuickPay / Vipps MobilePay / WooCommerce) controls which flow the form uses.

1.7.90

  • Feature: [wam_new_member_form] now uses standard WooCommerce checkout instead of order-pay page.
  • Auto-creates a virtual WC product per WAM category (matched by SKU, syncs name/price/image on change).
  • Adds product to cart and redirects to /kasse with full billing fields, gateway selection, and no "guest" message.
  • WAM meta is stamped on the order via woocommerce_checkout_order_created hook.
  • Billing fields are pre-filled from the membership form data.
  • handle_payment_complete still triggers member creation via map_order_lines (product has _wam_cat_id meta).

1.7.89

  • Feature: reCAPTCHA v3 spam protection on new member forms (both QuickPay and WooCommerce flows). Uses existing site/secret keys from plugin settings. Skips gracefully when keys are not configured.

1.7.88

  • Fix: New member orders now set billing_country to DK — missing country caused WooCommerce payment gateways to be unavailable on the order-pay page.

1.7.88

  • Fix: Edit and Delete buttons in member details card now work correctly — they close the details modal and open the appropriate edit/delete modal.
  • Fix: Modal transitions properly chain via fadeOut callback instead of triggering unhandled events.

1.7.87

  • Fix: WooCommerce new member form ([wam_new_member_form]) now has a working JS submit handler — previously only the QuickPay form was bound.
  • Fix: JS form handler reads AJAX action from data-wam-action attribute, supporting both QP and WC flows.
  • Fix: Handles both redirect and redirect_url response keys for WC checkout redirect.

1.7.86

  • Fix: New member form — added missing i18n keys for validation messages (emailRequired, firstNameRequired, etc.), which caused silent JS failure on submit.
  • Fix: New member form — accept_rules checkbox validation now correctly stops form submission with early return.

1.7.85

  • Fix: New member form — secondary name/email fields now visible on page load when a duplicate member type is pre-selected.

1.7.84

  • Improve: FIK import — per-row checkbox to include/exclude individual payments before import, with check-all toggle.
  • Improve: FIK import — validates FIK strings against configured prefix, skipping foreign FIK (e.g. BS indbetalingskort).
  • Fix: FIK import — date parsing now handles dd.mm.yyyy format (bank CSV standard).

1.7.83

  • New: FIK import tab on Payments Import page — parses FIK +71 payment strings from CSV (Dato, FIK-streng, Beløb), extracts member number, and imports payments with status "Betalt via FIK" / type "FIK-betaling".
  • Dedup: FIK import checks transid (digit string) + date + type to prevent duplicate imports.

1.7.82

  • Fix: Portal cards tab — QP payment link opens in popup window instead of full redirect, keeping portal page alive so MitID app-switch can return to the QP window.

1.7.81

  • Fix: Portal cards tab — QuickPay subscription now created via REST API (POST /subscriptions + PUT /link) instead of Payment Window form POST, solving mobile MitID redirect failures where subscriptions were never created.

1.7.80

  • New: FIK +71 payment string support — settings for prefix and account under Payments, new {{FIK-streng}} template variable for payment request, receipt, and test emails.
  • New: Payment date field and "Advance NextPayment" checkbox in Create Payment modal.

1.7.79

  • New: "Create payment" button on Kontingent tab — manual payment registration via modal with member search.

1.7.78

  • Fix: Removed non-existent sku and product_id columns from WAMP_PAYMENT inserts — resolves "Unknown column" DB errors.
  • Fix: Payment inserts now write category and reference (e.g. "Kontingent 2026") via get_order_category_info().
  • Fix: WC email suppression filters corrected to accept 3 parameters (matches WooCommerce hook signature).
  • Fix: Kontingent callback email dedup now checks _wam_kontingent_receipt_sent_at guard.

1.7.77

  • i18n: All PHP and JS source strings converted to English — Danish translations via .po/.mo files.
  • New: Member export (PDF/Excel) now uses the columns configured in Settings for the current view (active/removed).
  • New: PDF export auto-switches to landscape orientation when more than 7 columns are active.
  • New: Custom column labels from Settings are used as export headers.

1.7.76

  • Fix: Ad-hoc intro tekst bruger nu inline i stedet for

    — undgår ugyldig nesting i WAET skabelonens eksisterende

    -wrapper.

1.7.75

  • New: Dobbeltmedlemmer med forskellig email vises nu som separate valgbare rækker på kontingent email-siden.
  • Fix: Dedup i send_request ændret fra MemberNo til id, så begge emails modtager betalingsanmodningen.

1.7.74

  • Fix: Bootstrap/Popper JS no longer loaded globally on all admin pages — fixes WooCommerce order save redirect.
  • New: Ad-hoc intro text field on kontingent email group page — bold text prepended to payment request emails.

1.7.73

  • New: "Ingen email" checkbox on member edit form — clears email address for members without active email.
  • Fix: Member search now finds multi-word queries like "flemming aunel" by matching each word against full name.
  • Fix: Search input is trimmed so leading/trailing spaces no longer cause "Ingen emner fundet".

1.7.72

  • Fix: Removed 60-minute dedup guard on payment request emails — emails now send every time.
  • New: {{amount}} placeholder — formatted kontingent amount from category (e.g. 200,00 kr).

1.7.71

  • New: {{member_type}} placeholder in payment request emails — resolves to wam_cat_name via MemberStatus.
  • New: {{active_url}} placeholder — wam-go auto-login link to member portal kontingent tab.
  • New: {{amount}} placeholder — formatted kontingent amount from category (e.g. 200,00 kr).
  • Fix: Order-pay retry on failed/pending orders now works. Stale QuickPay meta cleared before retrying.
  • Fix: Bridge maybe_done() now checks order.is_paid() before skipping charge.
  • Fix: Duplicate WAMP_PAYMENT rows no longer created on order-pay retries.

1.7.69

  • Fix: Kontingent amounts now count double-member categories (dobbelt medlemmer) as 1 restance per MemberNo instead of 2. Summary cards, member list, bulk charge, and send-request all deduplicate by MemberNo. Double-member names shown joined with "&" in table.

1.7.68

  • Feature: Subject field added to all 6 WAET email template cards in settings (Standard member message, Payment request, and One-time code were missing it).
  • Feature: Test emails and production sends now use the configured subject with {{placeholder}} resolution.
  • Fix: Intro text newlines now render as line breaks in emails — WAET Renderer applies nl2br for _text variables.

1.7.67

  • Feature: Recover Payment button in QuickPay metabox when no transaction is linked to an order. Searches QuickPay by the stored recurring order_id, syncs all payment data (card, status, transaction id) back to WAMP_PAYMENT and order meta, and completes the order if already captured.

1.7.66

  • Fix: Fatal error on WooCommerce thank-you page — woocommerce_valid_order_statuses_for_order_again filter closure now accepts optional $order parameter since WC calls it with only 1 argument from woocommerce_order_again_button().

1.7.65

  • Fix: Active and removed member lists now use separate column settings. Active list uses wam_premium_list_columns, removed list uses wam_premium_list_notactive_columns with own sortable configuration in Settings.

1.7.64

  • Improvement: Subscription delete button now has two-step flow — active subscriptions get cancelled in QuickPay first, already cancelled subscriptions get permanently deleted from the database. Different icons and confirm messages for each action.

1.7.63

  • Fix: Refresh NextPayment prompt was skipped when member had non-kontingent payments — now checks only kontingent payments (reference LIKE 'Kontingent %' OR orderno LIKE 'BS%') matching the member overlay display.

1.7.62

  • Fix: Refresh NextPayment now also prompts for year when member has zero payments in WAMP_PAYMENT, even if WC order meta exists.

1.7.61

  • Fix: Fatal error in payment edit form — number_format_i18n() now casts amount to float before formatting.
  • New: Reference field added to payment edit form — admin can edit payment reference, with change logging.
  • Improvement: Refresh NextPayment now prompts admin for year when no orders found, instead of silent error. Uses renewal month from settings to set forfaldsdato.

1.7.60

  • New: Date (created_at) field added to payment edit form — admin can now correct the payment date.
  • New: Refresh NextPayment button now sets forfaldsdato when member has no payments — uses renewal month from settings to calculate overdue date.

1.7.59

  • New: "Betalingsoversigt" tab replaces old "Members Payment Status" — shows two separate tables for kontingent payments and other payments, with search across memberno/name/address.
  • New: Edit, delete, and move payments between members via modal dialogs. Move feature lets admin reassign a payment from one member to another.
  • New: All payment edits, deletes, and moves are logged to WAMP_PAYLOG with admin username and change details.
  • Improvement: Kontingent tab "Medlemsnr." column width set to 100px for compact layout.
  • Improvement: Kontingent tab card column now shows card brand icons (SVG) matching the subscriptions page style.

1.7.58

  • Fix: Undefined variable $subject_label in payment/subscription acknowledge emails — replaced with $vars['subject'] which holds the resolved subject line.
  • Fix: dompdf fatal error "Cannot declare class ComposerAutoloaderInit..." when another plugin already loaded dompdf — added class_exists guard before requiring autoloader.

1.7.57

  • Improvement: Kontingent "Har email" group now shows separate rows for dual members (dobbeltmedlemmer) — each member with their own email gets their own row, increasing contact reach. Summary counts also reflect individual members.
  • New: PDF download button on "Ingen kontakt" tab — exports member list with medlemsnr, navn, adresse, postnr, by, telefon, mobil, kategori and forfaldsdato for follow-up.

1.7.56

  • CRITICAL Fix: Progress bar notice still appeared on page load because WordPress core common.js relocates ALL .notice elements out of their container to the top of the page, bypassing display:none on the parent. Replaced .notice with custom .wam-progress-notice class that WP does not touch.

1.7.55

  • CRITICAL Fix: Kontingent progress bar ("Behandler...") was visible on page load with active spinner. Root cause: WordPress core common.js auto-initializes .notice.is-dismissible elements and overrides display:none. Removed is-dismissible class; dismiss button kept with manual handler. Spinner no longer starts with is-active in HTML — only activated by JS when bulk action runs.

1.7.54

  • Fix: Event orders now use autocapture=true in QuickPay recurring charge — prevents capture failures caused by timing issues when the manual capture call runs before QP has fully authorized. Membership orders keep existing autocapture=false + manual capture flow.
  • Fix: Event orders now correctly set payment category to WAMP_PAY_CATEGORY_EVENT (4) instead of WAMP_PAY_CATEGORY_NEW (0).

1.7.53

  • Fix: Progress bar spinner now stops reliably — moved cleanup from complete to success/error callbacks, spinner hidden via CSS visibility, finishProgress() function prevents any code-path from leaving it active.
  • Fix: NextPayment now uses the pre-calculated _wam_next_payment date from order meta instead of re-computing +1 year from DB (which caused double-advance to 2028 instead of 2027).
  • Fix: Amount in WAET payment acknowledge email now formatted with 2 decimals (e.g. "200,00" instead of "200").
  • Fix: WooCommerce customer_completed_order email now fully suppressed for kontingent orders — added to the kontingent suppression list alongside the other WC email types.

1.7.52

  • Fix: Kontingent orders now use the payment acknowledge email template instead of the subscription acknowledge template — kontingent charges go through QuickPay subscription but should use the payment template for correct email content.
  • Fix: Kontingent progress bar ("Behandler...") is now dismissible (X button) and auto-fades after bulk action completes. Notice style changes to success on completion.

1.7.51

  • CRITICAL Fix: Kontingent bulk charge always reported failure ("0 OK, 1 fejl") because charge_subscription() checked for a non-existent 'success' key in the QuickPay response array — now correctly checks HTTP status from raw.status.
  • CRITICAL Fix: WooCommerce "failed order" email (customer_failed_order, failed_order) was NOT suppressed for kontingent orders, causing customers to receive "payment failed" messages even though the charge was captured successfully. Both email types are now suppressed.

1.7.50

  • Fix: Kontingent progress bar ("Behandler...") now auto-hides after 6 seconds when bulk action completes.

1.7.49

  • Fix: Member detail modal could not be closed (X / click outside) on Kontingent tab — close handler was inside paymentList.init() which is skipped when payment table is absent.
  • Fix: Clicking outside modal content (backdrop) now closes the modal.
  • Add: "Luk" (Close) button added to member detail card alongside Edit/Delete.
  • Fix: Undefined variables ($brand, $bin, $last4, $exp_month, $exp_year) in quickpay_payment_by_subscription — card metadata now initialised from QuickPay response with fallback to WAMP_SUBSCRIPTION.

1.7.48

  • Fix: Payment acknowledgment email now fills all template placeholders — card_brand, card_no, card_issued_to fall back to WAMP_SUBSCRIPTION data when QuickPay metadata is empty.
  • Fix: card_issued_to defaults to member name when not provided by QuickPay.
  • Fix: Kontingent orders now set _wam_order_title and _wam_next_payment so member_year is correctly resolved in email templates.
  • Fix: All WooCommerce transactional emails (new order, processing, on-hold, invoice, cancelled) are suppressed for kontingent orders — receipts are handled exclusively by WAET.

1.7.47

  • Fix: Kontingent bulk action spinner now stops after completion.
  • Fix: paymentList AJAX no longer fires on kontingent tab (avoids unnecessary request).
  • Fix: NextPayment refresh removes member row from kontingent table when updated (member is no longer overdue).

1.7.46

  • Fix: NextPayment refresh button now works on Kontingent tab (handler was only bound on Members page).
  • Fix: Removed MemberID/memberid column guessing — all member lookups now use the correct id column (fetch_member_row_by_id, member_row_identity_where, maybe_mark_order_completed_after_capture).
  • Fix: Payments list number_format error on PHP 8.x when amount is a string.

1.7.45

  • Fix: Safety-net hook (priority 99) on order completion ensures NextPayment is synced even when the primary handler misses it.
  • Add: Manual NextPayment refresh button (dashicons-update) in member detail modal — fetches latest _wam_next_payment from completed orders.

1.7.44

  • Fix: Kontingent view-member modal now reuses the full member detail card from the members page (fetch_member_detail) instead of a custom simplified view.

1.7.43

  • Add: New Kontingent tab replaces Payments List — dashboard summary cards + 3-group member table (has card / has email / no contact).
  • Add: Bulk charge for members with active subscription card — creates WC order + QuickPay recurring charge + advances NextPayment.
  • Add: Bulk send payment request email for members without card but with email (via WAET, with 60-min dedup).
  • Add: Member info modal + soft-delete option for members with no card and no email.

1.7.42

  • Fix: Payments dashboard now groups by the type column directly (Betalingsservice, MobilePay, Payment) instead of deriving channel from multiple columns.
  • Fix: Dashboard summary totals and pie chart are no longer limited by a date cutoff — all completed payments are counted.
  • Add: Per-channel summary cards (BS, MobilePay, Payment) with payment count and member count in the dashboard header.
  • Fix: Double members (sharing MemberNo) now count as two persons in all dashboard member counts.
  • Fix: Order payment processing — all code paths now route through the central maybe_mark_order_completed_after_capture() function.

1.7.41

  • Add: MC audit — "Forgotten" (GDPR permanently deleted) emails are now cached server-side and filtered out of the "Missing in Mailchimp" ADD list into a separate read-only section.
  • Add: MC audit — "Batch Add All" button tries to subscribe all missing members at once; forgotten emails are automatically detected and cached for future audit runs.
  • Add: Single ADD errors for forgotten emails are now also cached, so they disappear from the ADD list on the next audit run.

1.7.40

  • Fix: MC audit email-change detection now matches by UniqID (or ID+name fallback) instead of MemberNo — prevents false "Update email" for household members (husband/wife sharing MemberNo but with different UniqID).

1.7.39

  • Fix: MC merge fields — universal rule: empty WAMP values are now always omitted from the API payload (prevents Invalid Resource errors for all field types).
  • Fix: MC merge field code de-duplicated — explicit mapping and same-name fallback merged into a single clean code path.
  • Add: MC audit now detects email changes — members whose email was changed in WAMP are shown in a separate "Email changed" section instead of "Missing in MC".
  • Add: MC audit email-change action respects MC contact status: subscribed/unsubscribed/pending → PATCH email; cleaned/archived → permanent delete + re-create.

1.7.38

  • Add: QuickPay Branding ID setting — optional branding_id is now sent with all Payment Window requests when configured.

1.7.37

  • Fix: Event order reorder bypass — added woocommerce_valid_order_statuses_for_order_again filter so WC never processes event order-again requests.
  • Fix: Moved URL blocker from template_redirect (too late) to wp_loaded priority 10 (before WC_Form_Handler at priority 20).
  • Fix: Expanded event order detection to also check event_id, _wwef_attendees, _wwef_ticket_label, _wwep_event_id, and mep_events post type.

1.7.36

  • Fix: Audit ADDRESS comparison now skips sub-keys where WAMP value is empty (e.g. country) to avoid false pending results.

1.7.35

  • Add: Missing DB columns now available in Table Options (Title, Gender, Country, UniqID, ExtID, UUID, Status, SMS Valid, Group SMS, DelReason, Created, Updated).
  • Fix: Mailchimp audit comparison normalises ADDRESS sub-keys and trims whitespace to eliminate false "pending" results.
  • Fix: Audit diff message now shows exactly which merge tags differ instead of generic text.

1.7.34

  • Add: Immediate Mailchimp sync on every member create, update, or removal (admin and frontend).
  • Fix: Rewritten MC sync/remove callbacks to always fetch full member row from DB by ID — fixes broken hooks that only passed partial data.
  • Add: Double-membership awareness on removal — if one half is removed, MC is re-synced from the remaining active member instead of archiving.
  • Add: Frontend profile save, email update, and admin new-member creation now fire MC sync hooks.

1.7.33

  • Fix: Reduced batch auto-sync page size from 200 to 25 to prevent 504 gateway timeouts on shared hosting.

1.7.32

  • Add: Configurable match method for Mailchimp auto-sync (ID+EMAIL, UniqID+EMAIL, EMAIL+FNAME+LNAME) via dropdown on Audit page.
  • Add: Batch auto-sync AJAX now uses the selected match method.
  • Fix: Audit status table only shows members pending sync (hides 100% matched rows).
  • Fix: WAET sendqueue table existence check prevents DB error when WAET not installed.

1.7.31

  • Add: Mailchimp merge field mapping UI in Settings > Mailchimp — map MC tags to WAMP columns via dropdowns, fetched live from MC API.
  • Add: Merge field overview table on Mailchimp Audit page with mapping status and link to edit.
  • Add: CS_UNIQID and SPONSOR merge tag mappings (UniqID, meta2).
  • Add: Dashboard hides missing-payment box when count is 0.
  • Fix: PHP 8.4 deprecation — fgetcsv escape parameter in area CSV import.
  • Improve: Sync engine reads mapping from saved settings instead of hardcoded array.

1.7.30

  • Add: Configurable member list columns — select which columns to display via Settings > General.
  • Add: Custom column labels — rename any column (e.g. meta3 → Title) in settings.
  • Add: Default sort order setting — choose which column and direction the member list sorts by on load.
  • Add: All visible columns are now sortable (clickable headers).
  • Add: Optional member number assignment checkbox on CSV and MSSQL import.
  • Improve: SQL query now fetches all available fields (Birthday, Age, Area, meta1-4, YEAR, Phone, Note1).

1.7.29

  • Update: DomPDF library updated from 3.1.0 to 3.1.5 (bugfixes and security patches).
  • Update: FPDI library updated from 2.6.3 to 2.6.6 (security fix).
  • Fix: PHP 8.4 deprecation — added explicit $escape parameter to fputcsv() calls in paylog CSV export.
  • Fix: PHP 8.4 deprecation — added explicit $escape parameter to fgetcsv() call in CSV import helper.

1.7.28

  • Fix: MSSQL import now uses safe property reader — missing columns return defaults instead of PHP warnings, and DateTime objects are auto-converted to strings.
  • Fix: SQL injection in MSSQL import replaced with $wpdb->prepare().
  • Fix: MSSQL import tries both cs_medlemmer and cs_members table names.
  • Fix: Title from MSSQL mapped to meta3, Profession to meta4.
  • Add: meta4 field support in WP user sync (_wam_meta4 user meta) and Mailchimp sync (META4 merge field).

1.7.27

  • Fix: MSSQL database import AJAX handler no longer crashes on connection failure — sqlsrv_errors() array is now properly formatted as a readable string instead of causing a PHP 8 TypeError.
  • Fix: Failed MSSQL connections now return early with wp_send_json_error() instead of attempting SQL queries on a dead connection.
  • Fix: Added function_exists('sqlsrv_connect') guard in the AJAX handler to prevent fatal errors when the extension is missing.

1.7.26

  • Fix: Guard sqlsrv_connect() call with function_exists() check to prevent fatal error on servers without the PHP sqlsrv extension.
  • Improve: MSSQL connection failure now displays detailed error info (SQLSTATE, message, code) for easier debugging.

1.7.25

  • Feature: WooCommerce is now optional. The plugin activates and runs without WooCommerce, providing member management, categories, import/export, email templates, and native subscriptions. Payment-related features (gateway, QuickPay, checkout, order processing) are conditionally loaded only when WooCommerce is active.
  • Add: New wam_has_woocommerce() helper function for consistent WC availability checks throughout the plugin.
  • UI: Portal hides Cards, Payments, Kontingent, and Orders tabs when WooCommerce is not installed.
  • UI: Admin Settings hides Payments tab when WooCommerce is not installed.

1.7.24

  • Fix: 3DS enforcement now includes all valid QuickPay card types: creditcard → 3d-creditcard, visa-electron → 3d-visa-electron, and mastercard-debet → 3d-mastercard-debet. This resolves "The card type used is not supported" errors when users select debit card variants in payment settings.
  • Add: Admin QuickPay Card Types multiselect now includes Visa Electron (Debit) and Mastercard Debit options alongside existing credit card types.

1.7.23

  • Fix: Restored missing helper method WAM_Premium_Helper::get_order_category_code() used by order summary rendering. This removes the fatal error in member portal flows that trigger order summary/category mapping.

1.7.22

  • Improve: [wam_portal_tab_note] can now be used without parameters and resolves context/tab dynamically from active login/portal shortcode rendering.
  • Add: [wam_member_login] and [wam_member_portal] now support action/tab attributes to select initial tab and matching tab-note context.
  • Improve: Portal frontend now respects shortcode-selected initial tab via data-initial-tab before fallback URL/payment flow logic.

1.7.21

  • Fix: QuickPay order metabox manual capture now re-checks payment state via API on capture errors and treats already-captured payments as success, synchronizing order/payment status instead of showing a false failure.
  • Fix: QuickPay order metabox now shows detailed AJAX error text when manual capture fails, so admin sees the real cause.

1.7.20

  • Fix: Accepted kontingent/direct-payment callbacks no longer force orders to wc-failed when capture is not immediately confirmed. These now move to pending/on-hold with automatic QuickPay finalization scheduling.
  • Fix: Renewal cron flow now treats in-flight/pending QuickPay states as on-hold + finalization instead of failed.

1.7.19

  • Fix: Kontingent/renewal receipt sending is now enforced across all completion paths. When capture completes (portal immediate, async cron finalization, or Woo payment completion), the plugin now triggers renewal receipt delivery with WAET payment acknowledgment template priority and deduplicates with a per-order sent flag.

1.7.18

  • Feature: Added a dedicated Settings -> Tab texts tab with rich text editors for per-tab portal/login notes.
  • Feature: Added shortcode [wam_portal_tab_note context="portal|login" tab="..."] for explicit placement of tab notes.
  • UI: Notes are now rendered in both [wam_member_login] and [wam_member_portal] tab content using Gutenberg-compatible rendering (do_blocks + shortcode handling).

1.7.17

  • Fix: While a kontingent renewal capture is pending finalization, portal summary now suppresses overdue/due + Pay now actions to prevent duplicate charge attempts and misleading “not paid/due” messaging.

1.7.16

  • Fix: Portal kontingent capture now prioritizes immediate completion by charging with QuickPay autocapture enabled and extending synchronous capture confirmation before falling back to pending finalization.

1.7.15

  • Fix: Kontingent callback no longer reclassifies renewal orders as new-member orders. WAET routing now stays on payment acknowledgment templates for renewals, and kontingent year (member_year / description) is derived from _wam_order_title when it contains a year.

1.7.14

  • Fix: WooCommerce default "customer completed order" email is now disabled for kontingent/renewal orders, so members do not receive the generic completed-order wording. Renewal communication continues through the WAM payment email flow.

1.7.13

  • Fix: Portal kontingent payment no longer creates a new blank member row. create_members_from_order() now detects non-new-member orders (no _wam_is_new_member flag) and returns the existing member's DB id instead of INSERT-ing a new row. handle_payment_complete() has an additional guard to skip member creation entirely for renewal/kontingent orders.

1.7.12

  • Changes in the admin payment handling.

1.7.11

  • Refactor: Payments dashboard tab moved out of the view and into a dedicated admin class to match the shared /views loader structure.

1.7.10

1.7.9

  • Maintenance: Minor charges.

1.7.8

  • Fix: When a double membership shows Member 2 in the portal, Member 2 email and mobile can now be edited and saved from the same profile edit flow without email verification.

1.7.6

  • Fix: Portal profile address editing now resolves and saves Area through the same DAWA-based area lookup used in admin, using a hidden area field and persisting WAMP_MEMBER.Area when present.

1.7.5

  • Fix: Portal member-row selection no longer assumes a MemberID column when disambiguating or logging matched WAMP_MEMBER rows, so default installs using id stay deterministic.

1.7.4

  • Fix: Portal profile saves now resolve the real WAMP_MEMBER column names from the live table schema before updating, so default installs write Zip instead of the non-existent PostalCode column and only touch optional columns when they actually exist.

1.7.3

  • Fix: Portal profile updates now persist to the canonical memberid row in WAMP_MEMBER, avoiding writes through shared household identifiers.
  • Fix: New kontingent-order and post-capture member updates now prefer memberid over MemberNo when syncing WAMP_MEMBER data.

1.7.2

  • Fix: Portal kontingent QuickPay bridge now returns through the same post-capture completion path as the direct portal charge flow, including consistent paid/pending portal redirects and NextPayment sync.
  • Fix: Profile address save now strips any trailing DAWA postcode/city text from the address line before persisting, while keeping postcode and city in their own fields.
  • Fix: When changing profile email, the portal now shows the full new email in the verification message and auto-saves as soon as the verification code reaches its full length.

1.7.1

  • Add: Member portal profile card now supports inline editing for email, mobile, and address with an Edit/Save toggle in the top-right corner.
  • Add: Profile address editing now uses DAWA autocomplete in the portal edit flow.
  • Add: Changing profile email now sends a verification code to the new email address and only saves the profile after the code is verified.

1.7.00

  • Fix: Legacy Woo order QuickPay transaction meta is now healed from WAMP_PAYMENT.transid when old data mistakenly stored subscription_id in transaction-id fields. This repair now runs through the shared helper/meta sync path used by admin order views and payment resolution.

1.6.99

  • Fix: QuickPay refresh/admin sync now prioritizes refunded and cancelled states correctly, so refunded QP payments no longer fall back to processed in WAMP_PAYMENT or order meta.
  • UI: The WAM-Go column on the Members admin page now copies the generated link to the clipboard and shows copied status instead of opening a new tab.

1.6.98

  • Fix: When a cancelled order has no real QuickPay transaction, stale QP order meta is now cleared instead of lingering in admin. WAMP_PAYMENT still syncs to cancelled/refunded based on the actual payment row.

1.6.97

  • Fix: Completed-order admin save and bulk-complete now run the same QuickPay process as cancelled orders: fetch QP state, attempt capture when authorized/waiting, and update WAMP_PAYMENT to the actual QP result.

1.6.96

  • Fix: Added direct handling of WooCommerce bulk cancel actions from the admin orders list (handle_bulk_actions-edit-shop_order and HPOS equivalent), so bulk-cancelled orders now run the QuickPay refund/cancel logic and sync WAMP_PAYMENT per order.

1.6.95

  • Fix: WAM_Premium_Woo_Payments hooks are now initialized from plugin bootstrap for both admin and frontend. Previous admin order updates/cancellations had no effect because the payment hooks were only being registered from the frontend class.

1.6.94

  • Fix: Cancelled Woo orders now also auto-cancel non-captured QuickPay payments via the payment cancel endpoint; if the remote payment is already cancelled/refunded, WAMP_PAYMENT is synced to that state on admin update.

1.6.93

  • Fix: save_post_shop_order now also runs the cancelled-order QuickPay/WAMP_PAYMENT sync, so clicking "Update" on a cancelled Woo order triggers the refund/sync path even if Woo status hooks do not fire on that save.

1.6.92

  • Fix: When a Woo order is set to cancelled, the plugin now checks the QuickPay payment state first, auto-refunds captured payments, and syncs the real result to WAMP_PAYMENT.

1.6.91

  • Fix: Added save_post_shop_order hook — when admin clicks "Update" on a completed order, QP status is verified and WAMP_PAYMENT synced. Covers the case where woocommerce_order_status_completed does NOT fire because status didn’t change.

1.6.90

  • Debug: Full logging in sync_wamp_payment_status_via_qp() — logs every decision point (hook fired, no row, API call, result, rows_affected).
  • Debug: Bootstrap hook logs whether WAM_Premium_Helper is loaded when woocommerce_order_status_completed fires.

1.6.89

  • Refactor: New central helpers in WAM_Premium_Helper — qp_fetch_payment(), qp_resolve_payment_id_from_order(), sync_wamp_payment_status_via_qp().
  • Fix: woocommerce_order_status_completed hook now calls QuickPay API to verify actual payment status before syncing WAMP_PAYMENT.

1.6.88

  • Fix: Direct top-level hook on woocommerce_order_status_completed now unconditionally syncs WAMP_PAYMENT.status to 'processed' — no class chain, no init() dependency.

1.6.87

  • Fix: Portal kontingent CSS selectors corrected from #pane-kontingent to #pane-subscription so styles actually apply.
  • UI: "Overdue" label right-aligned above the "Pay now" button in kontingent summary.

1.6.86

  • Fix: Portal "Betal nu" kontingent payment now uses maybe_mark_order_completed_after_capture() — orders no longer stuck on "pending" or "processing".
  • Fix: QuickPay bridge (checkout return flow) now uses maybe_mark_order_completed_after_capture() instead of bare payment_complete() — same issue.

1.6.85

  • Fix: Manual WooCommerce order completion now bumps WAMP_MEMBER.NextPayment forward — the missing update_member_next_payment() method has been implemented.
  • UI: Portal kontingent "Overdue" label now bolder (700) and "Pay now" button spans full width instead of right-aligned.

1.6.84

  • Fix: Refund and cancellation now sync WAMP_PAYMENT.status to 'refunded'/'cancelled', so the member portal shows the correct status in both Kontingent and Betalinger.

1.6.73

  • Fix: WAMP_PAYMENT upsert now deduplicates by Woo order context (_wam_payment_id/orderno) in addition to transid, preventing duplicate rows for the same order.
  • Fix: Missing payer identity in WAMP_PAYMENT is now backfilled from Woo order billing meta and callback fields when member row data is not yet available.
  • Fix: Async finalize path now backfills and links missing WAMP_PAYMENT rows to the Woo order via _wam_payment_id.

1.6.72

  • Change: Members Dashboard redesigned with large KPI total card and per-category cards showing count, label, and category image/icon.
  • Fix: Category image (wam_cat_image_id) now loaded from WAMP_CATEGORY and displayed in dashboard cards.
  • i18n: All Danish source strings in __(), esc_html_e(), and esc_attr_e() wrappers converted to English across callbacks, shortcodes, import views, and admin classes.

1.6.83

  • Fix: When a WooCommerce order is manually set to "completed" (e.g. via Edit Order → Update), WAMP_PAYMENT.status is now always synced to "processed" immediately. Previously only worked for orders without product-line mapping.

1.6.82

  • Fix: WAMP_PAYMENT.status now synced to "processed" in maybe_mark_order_completed_after_capture() so all capture paths (callback, portal, renewals, finalization) update the payments table — not just the callback.

1.6.81

  • Fix: All 4 capture paths now respect the wam_premium_payment_quickpay_captured_completed setting.
  • Fix: Subscription callback, direct payment callback, portal charge, and renewal cron all use maybe_mark_order_completed_after_capture() instead of bare payment_complete() or hardcoded status.

1.6.80

  • Feature: Email subject lines for Membership receipt, Subscription receipt and New member receipt are now configurable in settings.
  • Feature: Subject settings support {{key}} placeholders (same variables available in WAET templates).
  • Fix: Payment acknowledgment emails now load intro_text from settings instead of sending empty.
  • Fix: Kontingent payment receipt no longer uses wrong subject ("Card linked" instead of "Renewal").

1.6.79

  • Feature: Added subject setting for Membership receipt and Subscription receipt email types.
  • Fix: intro_text loaded from settings in both payment acknowledgment methods.

1.6.78

  • Fix: Missing order meta writes — _wam_qp_transaction and _transaction_id now written in subscription payment callback.
  • Fix: Added _wam_qp_id and _wam_qp_transaction to portal kontingent capture and callback subscription capture paths.

1.6.77

  • Fix: WAM-Go now handles Mailchimp emails with unresolved merge tags (<<MemberNo>>/<<UniqueId>>) gracefully — redirects to post without login attempt.

1.6.76

  • Fix: WAMP_MEMBER.NextPayment now synced after cron renewal payments.
  • Fix: WAMP_PAYMENT.status synced to "captured" after successful capture in 4 locations.
  • Fix: Metabox refund button now visible for payments with status "processed".

1.6.75

  • Fix: Renewals cron broken reference to helper method.

1.6.74

  • Fix: QuickPay callback no longer confuses transid with subscription_id — 4 locations corrected.
  • Fix: orderno now uses wc_order_id instead of internal payment id.

1.6.71

  • Fix: QuickPay method details are now resolved from WAMP_SUBSCRIPTION when recurring charge payload lacks card metadata, even if memberno already exists.
  • Fix: Recurring payment flow now persists resolved card metadata to order meta (_wam_qp_brand, _wam_qp_last4, etc.) so shop_order list shows actual method instead of generic fallback.
  • Fix: Orders list rendering now backfills and saves missing card method details to both order meta and WAMP_PAYMENT from subscription data when available.

1.6.70

  • Fix: In post_type=shop_order QuickPay payment column, Method is now always shown with robust fallback (card brand from payment meta/order meta, otherwise gateway title/QuickPay Payment).
  • Fix: Added fallback for missing card brand/last4 from order meta (_wam_qp_brand, _wam_qp_last4) so partially populated orders still show method details.

1.6.69

  • Fix: WAMP_PAYMENT upsert no longer requires memberid for new-member recurring payments; payment rows are now created using reserved memberno when memberid is not available yet.
  • Fix: Prevented early validation failure in QuickPay payment upsert that previously skipped creating payment records for new members.

1.6.68

  • Change: Version bump for release packaging alignment.

1.6.67

  • Fix: New-member QuickPay callback no longer creates a duplicate copied Woo order.
  • Fix: Added callback finalization idempotency guard to skip duplicate callback finalization on the same order.

1.6.66

  • Fix: Removed final-order copy flow; setup order is now promoted directly to member_order to avoid duplicate paid orders.

1.6.61

  • Fix: Customer portal password-login errors no longer show WordPress username-oriented wording. Messages are now email/code based only.
  • Change: Replaced fallback raw WP_Error login output with user-friendly portal messages that do not refer to username login.

1.6.60

  • Fix: Password login in customer portal now resolves WP users by Gmail-equivalent email (dot-insensitive for gmail/googlemail), preventing false "unknown email" failures when users enter dotted/undotted variants.
  • Fix: Post-login member sync now prefers canonical member email from WAMP_MEMBER when Gmail aliases are used.

1.6.59

  • Fix: Customer portal member lookup now treats gmail.com/googlemail.com addresses as dot-insensitive in local part (e.g. flemmingaunel@gmail.com = flemming.aunel@gmail.com).
  • Fix: Token login fallback now accepts Gmail-equivalent member email and continues with canonical database email.
  • Fix: User creation/linking in portal now prefers canonical member email from WAMP_MEMBER to avoid mismatches/duplicates when user enters Gmail alias variants.

1.6.58

  • Fix: Resolved QuickPay error No available payment-method for transaction caused by array-valued cardtypelock being cast to string (Array) in payment window builders.
  • Fix: qp_enforce_3ds_payment_methods() now accepts both array and comma-separated string input and normalizes safely.
  • Fix: 3DS enforcement now only outputs valid/known card method tokens (dankort, 3d-visa, 3d-mastercard, 3d-maestro) and ignores unknown values instead of generating invalid 3d-* methods.

1.6.57

  • Fix: Enforced QuickPay payment_methods on all WAMP payment window builders (new-member setup, portal subscription setup, gateway subscription setup, and shortcode payment flow), so 3DS locking is always sent.
  • Fix: payment_methods now always passes through WAM_Premium_Helper::qp_enforce_3ds_payment_methods() (also when cardtypelock is empty), preventing non-3DS fallbacks.
  • Add: New generic filter wam_qp_payment_methods_default for overriding default enforced methods (dankort,3d-visa,3d-mastercard,3d-maestro), while keeping backward compatibility with wam_qp_subscription_payment_methods_default.

1.6.56

  • Fix: WAMP cron now also drains WAET's own sendqueue for WAMP-owned templates (payment request, payment ack, etc.) as a belt-and-suspenders fallback when WAET's waet_process_queue cron is delayed or not running.
  • Fix: "Send Payment Request" button was disabling the wrong element (ID mismatch), allowing multiple simultaneous batch submissions and duplicate email queuing for the same members.
  • Fix: Batch payment-request AJAX calls are now serialized (one at a time) instead of being fired in parallel, preventing server overload and database conflicts on large member selections.
  • Fix: Added server-side deduplication guard: if a payment request email was already queued for a member within the last 60 minutes, the request is skipped silently instead of inserting a duplicate queue entry.

1.6.55

  • Fix: Subscription payment windows now always use 3DS-enforced payment methods (PSD2 FraudFilter status 30100). Converts plain card type names (visa, mastercard) to their 3D counterparts (3d-visa, 3d-mastercard); leaves dankort as-is (inherently 3DS in DK). Default if no cardtypelock is set: dankort,3d-visa,3d-mastercard,3d-maestro.
  • Add: New helper WAM_Premium_Helper::qp_enforce_3ds_payment_methods() with filter wam_qp_subscription_payment_methods_default for overriding the default set.

1.6.54

  • Add: WAM-GO login handler now supports optional target query parameter for redirect override after successful member login.
  • Security: target is validated as same-site URL/path only; invalid targets automatically fall back to existing post_id permalink behavior.
  • Add: New helper wam_premium_build_wam_go_url() to build backward-compatible WAM-GO URLs with optional safe target override.

1.6.53

  • Fix: Disabled WooCommerce "order again" for MEP event orders (including direct ?order_again= links) to prevent incorrect 0,01 product base price reuse on reorders.

1.6.52

  • Fix: Corrected WAMP_PAYMENT inserts to use created_at (schema-aligned) in checkout, Woo payment sync, and renewal flows, preventing SQL fatals and missing payment rows.

1.6.51

  • Fix: QuickPay capture flow now resolves recurring payment stage from both state and last successful operation, improving reliability for approved/authorized transitions.
  • Fix: Async capture finalization now retries capture when recurring payment is approved/authorized and keeps retrying transient waiting states safely.
  • Add: QuickPay metabox now shows Capture button for all non-final states when a payment id exists, so manual capture is always available when auto-capture/finalization has not completed.
  • Change: Manual capture, refresh-confirmed capture, and async capture-finalization now use the existing setting wam_premium_payment_quickpay_captured_completed to set order status to completed (or processing when disabled).

1.6.50

  • Fix: QuickPay order metabox now backfills missing QuickPay meta from existing order meta and local payment rows before showing that no QuickPay payment was found.
  • Fix: Woo payment completion now restores missing QuickPay transaction/order metadata and computes missing NextPayment values for recurring membership orders.
  • Fix: New-member Woo orders now store NextPayment, title, SKU, and pay-category metadata at creation time.
  • Fix: Portal kontingent Woo order items now use the membership category image so thumbnails render correctly in Woo order views.

1.6.49

  • Fix: Subscription callback no longer forces orders to failed on transient QuickPay race/idempotency errors; moves to on-hold/pending finalization instead.
  • Fix: Added asynchronous finalization persistence so local order/payment metadata keeps QuickPay Transaction ID in sync when resolved later.
  • Add: Extended wam_payment_log schema with ordreid + index for reliable order-level tracing.
  • Add: Centralized payment logging helper used across callback/finalization/recovery flows with backward compatibility for older table schema.
  • Add: Admin recovery AJAX endpoint to bulk move failed subscription orders to on-hold and schedule automatic QuickPay finalization retries.
  • Change: Customer-facing payment messages are now softer during async capture/finalization to reduce uncertainty when payment is still being verified.

1.6.48

  • Maintenance release: internal prep for upcoming QuickPay callback/finalization hardening.

1.6.47

  • Maintenance release: plugin packaging consistency update.

1.6.10

  • Add: Manual member assignment in MobilePay import preview — allows overriding auto-matched members when MobilePay includes middle names or other variations
  • Add: Member picker input in import preview table for each payment row; updates staging table before merge
  • Add: AJAX handler wam_premium_update_manual_memberno to persist user selections from preview UI

1.6.9

  • Fix: QuickPay enabled setting can now be reliably toggled on/off in Payments settings.
  • Change: Normalize legacy QuickPay toggle values for backward compatibility.

1.6.8

  • Add: MobilePay CSV import — two-step flow on the Import Payments page (MobilePay tab)
  • Add: Automatic member matching via email (from message field), mobile last-4-digits, or full name
  • Add: Preview table shows matched/unmatched rows before committing; deduplicated by transaction ID

1.6.7

  • Fix: Load shared front assets on pages using [wam_new_member_form] so form styling and behavior match [quickpay_new_member_form]
  • Change: Apply DAWA address autocomplete container markup consistently to both new-member shortcodes
  • Fix: Import admin and AJAX class parsing/structure corrections for stable deployment

1.6.6

  • Add: CSV import supports format selection (new English-column format and legacy standard format)
  • Add: Missing MemberNo in import now uses standard automatic member-number allocation
  • Fix: Members import admin page conditional rendering to prevent PHP parse errors in deployment

1.6.5

  • Add: New shortcode [wam_new_member_form] — generic WooCommerce payment variant of [quickpay_new_member_form]; creates a pending WooCommerce order and redirects the user to the active payment gateway
  • Add: CSV member import now supports fallback member-number assignment when MemberNo is missing (uses standard auto-allocation setup)

1.6.4

  • Change: Maintenance release

1.6.3

  • Fix: Load WordPress media library correctly on Categories admin page so category images can be selected
  • Change: Hide category selection in quickpay_new_member_form when only one selectable membership category exists

1.6.2

  • Change: Declare WooCommerce feature compatibility explicitly (HPOS compatible, Cart/Checkout Blocks not supported)

1.6.1

  • Fix: Deployment packaging correction to include missing runtime class files (including WAM Go helper) in released builds

1.6.0

  • Change: WordPress login links now route members to the configured medlemsportal URL while preserving admin authentication flows
  • Change: Direct requests to wp-login.php now redirect guests to medlemsportal with redirect target preserved where relevant
  • Change: One-time login code expiry is now centralized and configurable in seconds via plugin settings/filter

1.5.3

  • Change: Always resolve {{org_email}} {{org_name}} • {{org_street}} • {{org_postal_code}} • {{org_city}} placeholders from settings when sending WAET emails

1.4.25

  • Fix: WAET email template settings — intro text uses rich text editor consistently
  • Fix: WAET template test-send works across templates (member no + email)
  • Change: Create/update WAET sendqueue table only via version bump table update

1.4.24

  • Fix: Self-healing BOM cleanup — scans all plugin PHP files on first load after update and strips UTF-8 BOMs at source
  • Fix: Eliminates BOM contamination permanently even when server-side files contain BOMs introduced during build/sync

1.4.23

  • Fix: ob_start callback now strips UTF-8 BOMs from output instead of just buffering them
  • Fix: Prevents BOM contamination of WordPress update and AJAX responses

1.4.22

  • Fix: Remove UTF-8 BOM contamination that corrupted AJAX/update JSON responses
  • Fix: Remove PHP close tags (?>) from class and include files (WordPress coding standard)
  • Fix: Add early AJAX output buffer in main plugin file to catch stray output from require_once
  • Fix: All AJAX handlers now flush ALL output buffer levels before sending JSON

1.4.21

  • Security: Rewrite Members_List_Table::get_table_data() with $wpdb->prepare() — removes esc_sql() and direct variable interpolation
  • Fix: CRITICAL bug in helpers.php update_max_ordernumber_from_input() — $wpdb->get_var() was misused as prepare(), %s never substituted
  • Fix: Replace die(json_encode()) with wp_send_json() in wa_members_ajax_response()
  • Fix: Guard $wpdb->prepare() with empty params in export_members and export_member_addresses
  • Fix: Remove trailing PHP close tag from Members_List_Table

1.4.20

  • Fix: Remove undefined $value reference in Members_List_Table constructor (PHP 8 warning)
  • Fix: Skip $wpdb->prepare() when no placeholders in Category and Payments get_total_count() (WP 6.2+ compat)
  • Fix: Add output buffer safety to all AJAX handlers to prevent stray PHP output corrupting JSON
  • Fix: Add error callback to members AJAX display for debugging
  • Fix: Category search/status event binding now reads from DOM instead of passing jQuery Event

1.4.19

  • Fix: Use dedicated AJAX action for WAET test emails to avoid handler collisions
  • Change: Align WAET queue table schema with production structure

1.4.18

  • Change: Simplify WAET template settings into accordion list
  • Change: Accordion closes other templates when opened
  • Fix: Test email buttons now target the correct template fields
  • Change: Use internal sequential order id for 0 DKK subscription setup and Woo order number for paid charge

1.4.17

  • Fix: New member flow creates order line item and keeps WAMP_PAYMENT linked to Woo order

1.4.16

  • Fix: Nonce handling returns user-friendly error instead of 403

1.4.15

  • Change: Standardize recurring charges on quickpay_payment_by_subscription

1.4.14

  • Change: Woo gateway redirects to QuickPay Payment Window if subscription is missing

1.4.13

  • Fix: Remove members area table fallback and fail fast when bootstrap/table is missing

1.4.12

  • Build: Version bump

1.4.11

  • Fix: Remove dbDelta inline comments in members area table

1.4.10

  • Build: Update available for injection validation

1.4.9

1.4.8

  • Fix: Member token login - create user if not exists
  • Fix: Database schema version now matches plugin version (WAMP_VERSION)
  • Fix: SQL syntax error in wam_members_area table creation (removed invalid comment)

1.4.7

  • Fix: Member token login undefined variable error in portal AJAX

1.4.6

  • Remove bundled update checker (WAUM handles updates)
  • Cards terminology and i18n keys for portal
  • Update POT template

1.4.5

  • Update language files

1.4.4

  • Change Subscription to Card

1.4.3

  • Build: Ensure PluginInstaller loader is packaged correctly from shared source

1.4.2

  • Update Checker: Make class generic and pass slug/server from bootstrap

1.4.1

  • Update Checker: Use dynamic slug via WAMP_SLUG

1.4.0

  • WebAlive Tab: Load shared installer loader in plugin bootstrap

1.3.9

  • Use new tab=webalive

1.3.8

  • Changed handling of wp login and members

1.3.7

  • JS updated for better apple handling

1.3.6

  • Debug: Added error logging for plugin-info.php failures to diagnose "Plugin not found" issues

1.3.5

  • WebAlive Tab: Ultra-minimal remote loader implementation (~50 lines inline)
  • WebAlive Tab: Shows all available WebAlive plugins (sales channel)
  • Architecture: All logic loaded from update server - no class files needed
  • Optimization: Reduced from 172 lines to 50 lines of integration code

1.3.4

  • Architecture: Removed PluginInstallerRemote - WebAlive tab now only in WAUM
  • Optimization: Reduced plugin size by removing duplicate installer code
  • Update: Relies on WAUM for plugin installation interface

1.3.2

  • Added WebAlive plugin installer tab integration
  • Users can now browse and install all WebAlive plugins from WordPress admin

1.3.1

  • Skiftet titel til WebAlive Members Premium
  • Plugin now checks for updates automatically via custom update server
  • Improved plugin update workflow and notifications

1.3.0

  • Added automatic update checker integration with updates.webalive.dk
  • Plugin now checks for updates automatically via custom update server
  • Improved plugin update workflow and notifications

1.2.0

  • Previous stable release with all membership management features

0.9.0

  • Added WP User sync logic with wa_members table
  • AJAX-powered modals for member actions (edit, remove, unremove)
  • Enhanced UI with Bootstrap styles
  • Optimized database queries and pagination
  • Added Mailchimp tab and API configuration
  • Modernized import workflow (CSV & MSSQL) with AJAX feedback and summary cards

0.8.0

  • Initial public release
© 2026 Webalive - live website.
  • Kundeportal
  • Privacy Policy
  • Kundeportal
  • Privacy Policy

© 2019