Form and flow: Claim Listing

Goal

Set up the Claim Listing flow so an app user can take ownership of a GeoDirectory listing: optionally request approval first, then complete payment (Stripe or in-app purchase), then have the listing’s post_author re-pointed at them with the claim’s chosen package applied.

This article documents three things:

  1. The Claim Listing tab bar button state machine (built in mam_gd_claim_listing_manager::tab_bar_button).
  2. The request → approve → paid AJAX flow (mam_claim_listing_request / mam_claim_listing_paid).
  3. The legacy claim-cancel form at includes/forms/claim-listing-form.php, which lets a user cancel the subscription on every listing they author.

Prerequisites

  • The third-party GeoDir Claim Manager plugin active. The button checks class_exists('GeoDir_Claim_Post'); without it the button never appears.
  • GeoDirectory pricing packages defined for the listing’s post type.
  • Settings configured on Mobile App Manager → Geodirectory → Settings:
    • mam_gd_claim_requires_approvalyes to require admin approval first; no to allow direct purchase.
    • mam_gd_claim_checkout_modeiap or stripe (string sent to the app to choose the checkout flow).
    • mam_gd_claim_cta — headline text on the claim purchase screen.
    • mam_gd_claim_features — newline-separated bullet list shown to the buyer.

The button states

mam_gd_claim_listing_manager::tab_bar_button($tabbar_button, $tab_key, $data_array):

The button is hidden entirely when:

  • The listing’s post type does not support claiming (GeoDir_Claim_Post::post_claim_allowed($post_id) returns false).
  • The listing is already claimed.
  • The viewer is the listing’s author.
  • No user is signed in.

When visible, the button takes one of three forms:

State Title Action When
Claim pending “Claim Pending” empty GeoDir_Claim_Post::is_claim_pending($post_id) is true.
Request to claim “Request to Claim” claim_request mam_gd_claim_requires_approval === 'yes' AND user has no _mam_claim_approved_{post_id} user meta.
Ready for purchase “Claim Listing” open_claim_purchase Otherwise. Source carries the package list, checkout mode, CTA, and features.

The “Ready for purchase” payload looks like:

$tabbar_button['source'] = [
    'mode'     => 'stripe',          // or 'iap'
    'packages' => [/* package data */],
    'post_id'  => '12345',
    'cta'      => 'Claim this listing to manage it',
    'features' => ['Manage your listing', 'Respond to reviews', ...],
];

Each package entry: {package_id, product_id, price, term, name, description, order} — sorted by display_order. term is a localised label like ' for one year' or ' per month', derived from the package’s recurring flag and time unit.


The AJAX endpoints

The plugin registers two actions on mam-main‘s mam_main_ajax pipeline:

  • mam_claim_listing_requesthandle_claim_request()
  • mam_claim_listing_paidhandle_claim_payment_complete()

Both are dispatched via mam_main_ajax with subaction=....

mam_claim_listing_request

The “Request to Claim” button calls this when approval is required. It:

  1. Reads post_id and the user’s comments from the request.
  2. Returns an error if the user already has a pending claim.
  3. Builds a claim record via GeoDir_Claim_Post::save() with status: 0 (pending) and the user’s IP and full name.
  4. Returns success with a refreshed jsonData (the user’s phone-data payload).

Admins approve the request out-of-band — typically by setting the _mam_claim_approved_{post_id} user meta on the requestor’s account, which the button’s state machine then picks up. (The admin-side UI for this is not included in mam-geodirectory; it is expected to be added per-deployment, e.g., as an admin tool that watches GeoDir_Claim_Post rows.)

mam_claim_listing_paid

The “Claim Listing” button calls this after the app has completed payment. It:

  1. Reads post_id and package_id.
  2. Finds the existing pending claim, or creates one inline if no prior request was made (direct-purchase path).
  3. Calls GeoDir_Claim_Post::approve_claim($claim->id) — this is the GeoDirectory primitive that flips claimed = 1 and re-points post_author.
  4. Updates the listing’s package via geodir_pricing_post_update_package when a package is supplied.
  5. Returns success with refreshed jsonData.

The legacy claim-cancel form

mam_gd_manage_claim_listing::claim_listing() (in includes/forms/claim-listing-form.php) is unrelated to the new claim flow. It is a Gravity Forms result handler registered on mam_for_gravity_forms_form_result_form_{id} for the form ID stored in the option mam_geodirectory_claim_listing.

When the form is submitted with a checkbox set to ON, the handler walks every gd_place post owned by the current user and:

  • Sets the listing’s package_id post meta to 0.
  • Sets the listing’s expire_date to yesterday (date('Y-m-d', strtotime('-1 day'))).

This effectively cancels every paid subscription the user has on gd_place listings. The flow returns Subscription has been cancelled.

Heads up: this code path is sensitive to the mam_geodirectory_claim_listing option being non-empty. If the option is empty, the filter is registered as mam_for_gravity_forms_form_result_form_ (no ID), which is a no-op but undesirable. Make sure the option is set or remove the filter — the PLUGIN_AUDIT.md flagged this as a behavior bug; the code is unchanged at v2.1.5.


Steps to enable the new claim flow

1. Install the GeoDir Claim Manager plugin

The button silently disappears if GeoDir_Claim_Post is not loaded.

2. Configure claim toggles

Under Settings → Claim Listings:

  • Set mam_gd_claim_requires_approval to yes or no based on your policy.
  • Set mam_gd_claim_checkout_mode to iap (use App Store / Play Store IAP) or stripe (use Stripe IAP-equivalent on the device). The plugin does not validate this string; whatever you put there is sent verbatim to the app.
  • Set the CTA and features list.

3. Configure pricing packages

Each gd_place (or other claimable post type) should have at least one pricing package. The package’s woocommerce_product_id meta is sent as product_id — required for IAP flows.

4. Verify

  • Open the app as a non-author signed-in user. Pick a claimable, unclaimed listing.
  • With mam_gd_claim_requires_approval=yes: tap Request to Claim. Verify a pending row appears in GeoDir_Claim_Post.
  • After approval (set the user-meta flag manually), reopen the listing. The button should now read Claim Listing and open the purchase screen.
  • Complete a test purchase. Confirm the listing’s post_author is the new user and claim_status: claimed appears in subsequent app responses.

Variations

  • Direct purchase (no approval). Set mam_gd_claim_requires_approval=no. The button skips straight to purchase.
  • Custom features list. Edit the multi-line mam_gd_claim_features option. The plugin trims and filters empty lines.
  • Default features. When the option is empty, the plugin uses a built-in trio: “Manage your listing,” “Respond to reviews,” “Post special offers.”

Verification

This article was last verified against:

  • Plugin: mam-geodirectory v2.1.5
  • Source: includes/mam_gd_claim_listing_manager.php
  • Source: includes/forms/claim-listing-form.php (legacy cancel form)
  • Required: GeoDir Claim Manager plugin (GeoDir_Claim_Post)

Re-verify whenever the claim button state machine changes, the package field shape sent to the app changes, the AJAX subaction names change, or the legacy cancel-subscription form is removed.


  • Plugin: mam-geodirectory
  • Recipe: Configure the GeoDirectory settings page
  • Recipe: Bulk-invite venue owners
  • Listing tab bar buttons
  • Mobile listing data shapeclaim_status and can_claim keys
  • GeoDirectory notification types — claim invitation slug

Metadata

Field Value
Article type Recipe (Admin)
Plugin slug mam-geodirectory
Applies to plugin version 2.1.5+
Category Building Your App
Audience WordPress admin
Estimated time 15 minutes
Last verified 2026-05-01
Contents

    Need Support?

    Can’t find the answer you’re looking for? Don’t worry we’re here to help!