Hook: mam_main_iap_subscription_has_changed

Purpose

Fires when the in-app purchase subscription state shifts — a user upgrades, downgrades, cancels, or the receipt is renewed. Sibling plugins (typically mam-inapp-purchase-manager) update entitlements, gate features, or dispatch notifications.

The action fires from inside phase_content of the phone-data pipeline when receipt validation detects a change.


Signature

do_action(
    'mam_main_iap_subscription_has_changed',
    int    $user_id,
    string $product_id,
    string $previous_state,
    string $new_state
);
Parameter Type Description
$user_id int The user whose subscription changed
$product_id string The IAP product id
$previous_state string Previous state ('active', 'expired', 'cancelled', 'trial')
$new_state string New state

Example: persist new entitlement state

add_action( 'mam_main_iap_subscription_has_changed',
    function ( int $user_id, string $product_id, string $prev, string $new ) {

        update_user_meta( $user_id, "iap_state_{$product_id}", $new );
        update_user_meta( $user_id, "iap_state_{$product_id}_changed_at", current_time( 'mysql' ) );

        // Notify on cancellation.
        if ( $prev === 'active' && $new === 'cancelled' ) {
            do_action( 'mam_notification_send_message', array(
                'message_type' => 'mam-my-plugin-iap_cancelled',
                'recipient_id' => $user_id,
                'replacements' => array( 'product_id' => $product_id ),
            ) );
        }
    },
    10, 4
);

Pipeline integration

mam-inapp-purchase-manager::manage_phone_data (priority 10) reads the user’s IAP receipts on every phone-data build. When receipt validation detects a state shift, it fires this action. Subscribers see the change in (near) real-time.


Gotchas

  • Fires per-phone-data-request. A user with a long-running session may not see the action fire until they next launch the app or the cursor is invalidated.
  • No retry. If your subscriber’s persistence fails, the action doesn’t fire again unless the state changes again.
  • product_id shapes vary by store. Apple uses reverse-DNS (com.example.subscription_monthly); Google uses arbitrary strings. Don’t assume a format.
  • Trial → active is a state shift. Don’t assume only “downgrades” fire.
  • No transactional guarantee. Two simultaneous phone-data requests for the same user could both detect the same shift and both fire the action — guard your subscriber against double-application.

  • Phone data pipeline phases
  • Hook: mam_get_phone_data_before_send
  • Hook: mam_notification_send_message

Metadata

Field Value
Article type Hook Reference
Plugin slug mam-main
Applies to plugin version 2.1.11+
Hook type action
Audience PHP developer
Last verified 2026-05-02
Contents

    Need Support?

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