Skip to content

Logic to support admin refunds.#4

Draft
kimcoleman wants to merge 1 commit intodevfrom
admin-track-refund
Draft

Logic to support admin refunds.#4
kimcoleman wants to merge 1 commit intodevfrom
admin-track-refund

Conversation

@kimcoleman
Copy link
Member

@kimcoleman kimcoleman commented Jul 19, 2024

All Submissions:

Changes proposed in this Pull Request:

Now loading our Google Analytics code in the WordPress admin on PMPro pages when orders are updated. Could use further testing, for example, when the order comes in from the gateway. This is hard to test with the GA4 testing tool. I did process a few so my dev site will be able to see this tomorrow potentially.

Let's also consider if we should or should not offer a feature to disable refund tracking.

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you successfully run tests with your changes locally?

Changelog entry

  • FEATURE: Added event tracking for order refunds.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds refund tracking functionality for Google Analytics by loading the GA script in the WordPress admin area on PMPro pages and sending refund events when orders are marked as refunded.

Changes:

  • Added admin-side Google Analytics script loading on PMPro pages
  • Implemented refund event tracking that fires when order status changes to refunded
  • Added helper functions to structure and output refund event data to GA4

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +133 to +139
extract( $pmproga4_settings = get_option( 'pmproga4_settings',
array(
'measurement_id' => '',
'track_levels' => array()
)
) );

Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of extract() is a discouraged practice in WordPress as it can lead to variable collision and make code harder to maintain. Consider accessing array values directly using $pmproga4_settings['measurement_id'] and $pmproga4_settings['track_levels'] instead.

Suggested change
extract( $pmproga4_settings = get_option( 'pmproga4_settings',
array(
'measurement_id' => '',
'track_levels' => array()
)
) );
$pmproga4_settings = get_option(
'pmproga4_settings',
array(
'measurement_id' => '',
'track_levels' => array(),
)
);
$measurement_id = isset( $pmproga4_settings['measurement_id'] ) ? $pmproga4_settings['measurement_id'] : '';
$track_levels = isset( $pmproga4_settings['track_levels'] ) ? $pmproga4_settings['track_levels'] : array();

Copilot uses AI. Check for mistakes.
?>
<!-- Paid Memberships Pro - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=<?php echo esc_attr( $measurement_id ); ?>"></script>
<script <?php echo esc_attr($script_atts); ?>>
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space before the opening brace on the script tag attribute. For consistency with WordPress coding standards, there should be a space before the PHP echo statement: <script >

Suggested change
<script <?php echo esc_attr($script_atts); ?>>
<script <?php echo esc_attr( $script_atts ); ?>>

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +218
* Enqueue the Google Analytics script for a refunded order.
*
* @since TBD
*/
function pmproga4_load_admin_order_refunded_script( $pmpro_invoice ) {
pmproga4_refund_event( $pmpro_invoice );
}

/**
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function pmproga4_load_admin_order_refunded_script is defined but never used or hooked into WordPress. This appears to be dead code that should either be removed or properly integrated with an action hook.

Suggested change
* Enqueue the Google Analytics script for a refunded order.
*
* @since TBD
*/
function pmproga4_load_admin_order_refunded_script( $pmpro_invoice ) {
pmproga4_refund_event( $pmpro_invoice );
}
/**

Copilot uses AI. Check for mistakes.
}

/**
* Function for refund event.
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function documentation is incomplete. It should include a proper description of what the function does, document the @param tag for $pmpro_invoice parameter, and specify the @return type (which appears to be void).

Suggested change
* Function for refund event.
* Build and enqueue the Google Analytics 4 refund event for a given order.
*
* @param object $pmpro_invoice Paid Memberships Pro invoice/order object used to populate refund data.
*
* @return void

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +131
if ( empty( $_REQUEST['page'] ) || strpos( $_REQUEST['page'], 'pmpro' ) === false ) {
return;
}
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation: these lines use tabs while the surrounding code uses spaces. For consistency with WordPress coding standards and the rest of the file, use tabs for indentation.

Copilot uses AI. Check for mistakes.
Comment on lines +260 to +262
value: <?php echo $gtag_config_ecommerce_data['value']; ?>,
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo $gtag_config_ecommerce_data['tax']; ?>,
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Numeric values should be explicitly cast to ensure they are numeric types before output into JavaScript. Use floatval() for value and tax: value: and tax: . This prevents potential issues if the database values are unexpectedly non-numeric.

Suggested change
value: <?php echo $gtag_config_ecommerce_data['value']; ?>,
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo $gtag_config_ecommerce_data['tax']; ?>,
value: <?php echo floatval( $gtag_config_ecommerce_data['value'] ); ?>,
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo floatval( $gtag_config_ecommerce_data['tax'] ); ?>,

Copilot uses AI. Check for mistakes.
'send_page_view': false,
}
);
</script>
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation: this line uses a tab while the surrounding code uses spaces. For consistency with WordPress coding standards and the rest of the file, use a tab for indentation.

Suggested change
</script>
</script>

Copilot uses AI. Check for mistakes.
*/
function pmproga4_refund_event( $pmpro_invoice ) {

// Set the ecommerce dataLayer script if the order ID matches the session variable.
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions checking if the order ID matches a session variable, but no session variable is being checked in this code. The comment should be updated to accurately reflect what the code does: checking if the invoice object and its ID are not empty.

Suggested change
// Set the ecommerce dataLayer script if the order ID matches the session variable.
// Set the ecommerce dataLayer script if the invoice and its ID are not empty.

Copilot uses AI. Check for mistakes.
Comment on lines +227 to +271
// Set the ecommerce dataLayer script.
$gtag_config_ecommerce_data = array();
$gtag_config_ecommerce_data['transaction_id'] = $pmpro_invoice->code;
$gtag_config_ecommerce_data['value'] = $pmpro_invoice->membership_level->initial_payment;

if ( ! empty( $pmpro_invoice->tax ) ) {
$gtag_config_ecommerce_data['tax'] = $pmpro_invoice->tax;
} else {
$gtag_config_ecommerce_data['tax'] = 0;
}

if ( $pmpro_invoice->getDiscountCode() ) {
$gtag_config_ecommerce_data['coupon'] = $pmpro_invoice->discount_code->code;
} else {
$gtag_config_ecommerce_data['coupon'] = '';
}

// Build an array of product data.
$gtag_config_ecommerce_products = array();
$gtag_config_ecommerce_products['item_id'] = $pmpro_invoice->membership_level->id;
$gtag_config_ecommerce_products['item_name'] = $pmpro_invoice->membership_level->name;
$gtag_config_ecommerce_products['affiliation'] = get_bloginfo( 'name' );
if ( $pmpro_invoice->getDiscountCode() ) {
$gtag_config_ecommerce_products['coupon'] = $pmpro_invoice->discount_code->code;
}
$gtag_config_ecommerce_products['index'] = 0;
$gtag_config_ecommerce_products['price'] = $pmpro_invoice->membership_level->initial_payment;
$gtag_config_ecommerce_products['quantity'] = 1;
?>
<script>
jQuery(document).ready(function() {
gtag( 'event', 'refund', {
transaction_id: '<?php echo $gtag_config_ecommerce_data['transaction_id']; ?>',
value: <?php echo $gtag_config_ecommerce_data['value']; ?>,
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo $gtag_config_ecommerce_data['tax']; ?>,
<?php } ?>
<?php if( ! empty( $gtag_config_ecommerce_data['coupon'] ) ) { ?>
coupon: '<?php echo $gtag_config_ecommerce_data['coupon']; ?>',
<?php } ?>
items: [ <?php echo json_encode( $gtag_config_ecommerce_products ); ?> ]
});
});
</script>
<?php
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After calling getMembershipLevel(), there's no verification that the membership_level property was successfully populated. Add a check to ensure $pmpro_invoice->membership_level is not empty before accessing its properties like initial_payment, id, and name to prevent potential PHP warnings or errors.

Suggested change
// Set the ecommerce dataLayer script.
$gtag_config_ecommerce_data = array();
$gtag_config_ecommerce_data['transaction_id'] = $pmpro_invoice->code;
$gtag_config_ecommerce_data['value'] = $pmpro_invoice->membership_level->initial_payment;
if ( ! empty( $pmpro_invoice->tax ) ) {
$gtag_config_ecommerce_data['tax'] = $pmpro_invoice->tax;
} else {
$gtag_config_ecommerce_data['tax'] = 0;
}
if ( $pmpro_invoice->getDiscountCode() ) {
$gtag_config_ecommerce_data['coupon'] = $pmpro_invoice->discount_code->code;
} else {
$gtag_config_ecommerce_data['coupon'] = '';
}
// Build an array of product data.
$gtag_config_ecommerce_products = array();
$gtag_config_ecommerce_products['item_id'] = $pmpro_invoice->membership_level->id;
$gtag_config_ecommerce_products['item_name'] = $pmpro_invoice->membership_level->name;
$gtag_config_ecommerce_products['affiliation'] = get_bloginfo( 'name' );
if ( $pmpro_invoice->getDiscountCode() ) {
$gtag_config_ecommerce_products['coupon'] = $pmpro_invoice->discount_code->code;
}
$gtag_config_ecommerce_products['index'] = 0;
$gtag_config_ecommerce_products['price'] = $pmpro_invoice->membership_level->initial_payment;
$gtag_config_ecommerce_products['quantity'] = 1;
?>
<script>
jQuery(document).ready(function() {
gtag( 'event', 'refund', {
transaction_id: '<?php echo $gtag_config_ecommerce_data['transaction_id']; ?>',
value: <?php echo $gtag_config_ecommerce_data['value']; ?>,
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo $gtag_config_ecommerce_data['tax']; ?>,
<?php } ?>
<?php if( ! empty( $gtag_config_ecommerce_data['coupon'] ) ) { ?>
coupon: '<?php echo $gtag_config_ecommerce_data['coupon']; ?>',
<?php } ?>
items: [ <?php echo json_encode( $gtag_config_ecommerce_products ); ?> ]
});
});
</script>
<?php
if ( ! empty( $pmpro_invoice->membership_level ) ) {
// Set the ecommerce dataLayer script.
$gtag_config_ecommerce_data = array();
$gtag_config_ecommerce_data['transaction_id'] = $pmpro_invoice->code;
$gtag_config_ecommerce_data['value'] = $pmpro_invoice->membership_level->initial_payment;
if ( ! empty( $pmpro_invoice->tax ) ) {
$gtag_config_ecommerce_data['tax'] = $pmpro_invoice->tax;
} else {
$gtag_config_ecommerce_data['tax'] = 0;
}
if ( $pmpro_invoice->getDiscountCode() ) {
$gtag_config_ecommerce_data['coupon'] = $pmpro_invoice->discount_code->code;
} else {
$gtag_config_ecommerce_data['coupon'] = '';
}
// Build an array of product data.
$gtag_config_ecommerce_products = array();
$gtag_config_ecommerce_products['item_id'] = $pmpro_invoice->membership_level->id;
$gtag_config_ecommerce_products['item_name'] = $pmpro_invoice->membership_level->name;
$gtag_config_ecommerce_products['affiliation'] = get_bloginfo( 'name' );
if ( $pmpro_invoice->getDiscountCode() ) {
$gtag_config_ecommerce_products['coupon'] = $pmpro_invoice->discount_code->code;
}
$gtag_config_ecommerce_products['index'] = 0;
$gtag_config_ecommerce_products['price'] = $pmpro_invoice->membership_level->initial_payment;
$gtag_config_ecommerce_products['quantity'] = 1;
?>
<script>
jQuery(document).ready(function() {
gtag( 'event', 'refund', {
transaction_id: '<?php echo $gtag_config_ecommerce_data['transaction_id']; ?>',
value: <?php echo $gtag_config_ecommerce_data['value']; ?>,
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo $gtag_config_ecommerce_data['tax']; ?>,
<?php } ?>
<?php if( ! empty( $gtag_config_ecommerce_data['coupon'] ) ) { ?>
coupon: '<?php echo $gtag_config_ecommerce_data['coupon']; ?>',
<?php } ?>
items: [ <?php echo json_encode( $gtag_config_ecommerce_products ); ?> ]
});
});
</script>
<?php
}

Copilot uses AI. Check for mistakes.
<?php if ( ! empty( $gtag_config_ecommerce_data['tax'] ) ) { ?>
tax: <?php echo $gtag_config_ecommerce_data['tax']; ?>,
<?php } ?>
<?php if( ! empty( $gtag_config_ecommerce_data['coupon'] ) ) { ?>
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after 'if' keyword. WordPress coding standards require a space after control structure keywords: if ( ! empty( ... ) )

Suggested change
<?php if( ! empty( $gtag_config_ecommerce_data['coupon'] ) ) { ?>
<?php if ( ! empty( $gtag_config_ecommerce_data['coupon'] ) ) { ?>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant