Created
October 24, 2015 19:31
-
-
Save stracker-phil/04984c3424993526ec66 to your computer and use it in GitHub Desktop.
M2 helper plugin: Match PayPal IPN Messages when user deleted the original invoice
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: M2 PayPal Invoice Matching | |
* Description: Creates missing invoices for M2 PayPal subscriptions. This plugin extends the page "Membership 2 > Billing > Transaction Logs" | |
* Author: Philipp Stracker (WPMU DEV) | |
* Created: 21.10.2015 | |
* Version: 1.0.0 | |
* | |
* Addresses issue of this thread: | |
* http://premium.wpmudev.org/forums/topic/-of-my-members-now-have-expired-accounts-in-membership-pro-2 | |
* ---------------------------------------------------------------------------- | |
*/ | |
class M2_PP_Inv_Matching { | |
protected $match_result = ''; | |
static public function init() { | |
static $Inst = null; | |
if ( null === $Inst ) { | |
$Inst = new M2_PP_Inv_Matching(); | |
} | |
} | |
protected function __construct() { | |
add_action( | |
'load-membership-2_page_membership2-billing', | |
array( $this, 'billings_page' ) | |
); | |
add_filter( | |
'ms_helper_listtable_transactionlog-column_invoice', | |
array( $this, 'logs_invoice_column' ), | |
10, 2 | |
); | |
} | |
public function billings_page() { | |
if ( isset( $_GET['gateway_id'] ) || 'paypalstandard' == $_GET['gateway_id'] ) { | |
$this->process_action(); | |
} else { | |
$this->show_notice(); | |
} | |
} | |
protected function process_action() { | |
if ( empty( $_REQUEST['action'] ) ) { | |
return; | |
} | |
$action = $_REQUEST['action']; | |
switch ( $action ) { | |
case 'm2_pp_inv': | |
if ( ! empty( $_REQUEST['log'] ) ) { | |
$log_id = $_REQUEST['log']; | |
MS_Model_Import::retry_to_process( $log_id ); | |
add_filter( | |
'ms_view_render', | |
array( $this, 'show_inv_form' ), | |
10, 2 | |
); | |
} | |
break; | |
case 'm2_pp_match': | |
$this->create_matching(); | |
add_filter( | |
'ms_view_render', | |
array( $this, 'show_matching_result' ), | |
10, 2 | |
); | |
break; | |
} | |
} | |
protected function show_notice() { | |
$url = MS_Controller_Plugin::get_admin_url( | |
'billing', | |
array( | |
'show' => 'logs', | |
'state' => 'err', | |
'gateway_id' => 'paypalstandard', | |
) | |
); | |
lib3()->ui->admin_message( | |
'<b>M2 PayPal Invoice Matching</b>: Get a list of <a href="' . esc_url( $url ) . '">all PayPal transaction errors</a>' | |
); | |
} | |
public function show_inv_form( $html, $view ) { | |
if ( empty( $_REQUEST['log'] ) ) { return; } | |
$log_id = $_REQUEST['log']; | |
$log = MS_Factory::load( 'MS_Model_Transactionlog', $log_id ); | |
if ( 'err' != $log->state ) { | |
echo '<div class="updated notice is-dismissible"><p>This is an irrelevant transaction. Matching not possible.</p></div>'; | |
return $html; | |
} | |
$pp = $log->post; | |
lib3()->array->equip( | |
$pp, | |
'invoice', | |
'first_name', | |
'last_name', | |
'payer_email', | |
'item_name', | |
'item_number' | |
); | |
$inv_id = intval( $pp['invoice'] ); | |
$user_id = 0; | |
$mem_id = intval( $pp['item_number'] ); | |
$def_user = get_user_by( 'email', $pp['payer_email'] ); | |
if ( $def_user ) { $user_id = $def_user->ID; } | |
$meta_user_id = intval( get_post_meta( $inv_id, 'user_id', true ) ); | |
$meta_mem_id = intval( get_post_meta( $inv_id, 'membership_id', true ) ); | |
$meta_user = get_user_by( 'id', $meta_user_id ); | |
$meta_mem = MS_Factory::load( 'MS_Model_Membership', $meta_mem_id ); | |
if ( $meta_user_id ) { $user_id = $meta_user_id; } | |
if ( $meta_mem_id ) { $mem_id = $meta_mem_id; } | |
// Get user options. | |
$args = array( 'fields' => 'all', 'number' => 0 ); | |
$wp_user_search = new WP_User_Query( $args ); | |
$items = $wp_user_search->get_results(); | |
$user_options = array( '<option value="0">- Unknown -</option>'); | |
foreach ( $items as $user ) { | |
$user_options[] = sprintf( | |
'<option value="%s" %s>%s: %s (%s)</option>', | |
$user->ID, | |
selected( $user->ID, $user_id, false ), | |
$user->ID, | |
$user->first_name . ' ' . $user->last_name, | |
$user->user_email | |
); | |
} | |
// Get membership options. | |
$membership_options = array( '<option value="0">- Unknown -</option>' ); | |
$args = array( 'include_guest' => 0 ); | |
$items = MS_Model_Membership::get_membership_names( $args ); | |
foreach ( $items as $id => $name ) { | |
$membership_options[] = sprintf( | |
'<option value="%s" %s>%s: %s</option>', | |
$id, | |
selected( $id, $mem_id, false ), | |
$id, | |
$name | |
); | |
} | |
$name_match = strtolower( $meta_user->first_name ) == strtolower( $pp['first_name'] ) | |
&& strtolower( $meta_user->last_name ) == strtolower( $pp['last_name'] ); | |
$email_match = strtolower( $meta_user->user_email ) == strtolower( $pp['payer_email'] ); | |
$mem_match = $meta_mem_id == intval( $pp['item_number'] ); | |
$url = remove_query_arg( array( 'action', 'log' ) ); | |
?> | |
<form method="POST" action="<?php echo esc_url( $url ); ?>" class="matching-form"> | |
<input type="hidden" name="action" value="m2_pp_match" /> | |
<input type="hidden" name="log_id" value="<?php echo $log_id; ?>" /> | |
<h2>Transaction #<?php echo $log->id; ?></h2> | |
<h3>Details From PayPal</h3> | |
<p> | |
PayPal Invoice ID: <strong><?php echo $pp['invoice']; ?></strong><br> | |
Payer First/Lastname: <strong><?php echo $pp['first_name']; ?> <?php echo $pp['last_name']; ?></strong><br> | |
Payer Email: <strong><?php echo $pp['payer_email']; ?></strong><br> | |
Purchased Item ID: <strong><?php echo $pp['item_number']; ?></strong><br> | |
Purchased Item Name: <strong><?php echo $pp['item_name']; ?></strong><br> | |
</p> | |
<h3>Reconstructed From DB</h3> | |
<p> | |
User ID: <strong><?php echo $meta_user_id; ?></strong><br> | |
User Name: <strong><?php echo $meta_user->first_name; ?> <?php echo $meta_user->last_name; ?></strong> | |
<?php if ( $name_match ) : ?> | |
<i class="dashicons dashicons-yes" style="color:#080;"></i> | |
<?php else : ?> | |
<i class="dashicons dashicons-no" style="color:#C00;"></i> | |
<?php endif; ?> | |
<br> | |
User Email: <strong><?php echo $meta_user->user_email; ?></strong> | |
<?php if ( $email_match ) : ?> | |
<i class="dashicons dashicons-yes" style="color:#080;"></i> | |
<?php else : ?> | |
<i class="dashicons dashicons-no" style="color:#C00;"></i> | |
<?php endif; ?> | |
<br> | |
Membership ID: <strong><?php echo $meta_mem_id; ?> - <?php echo $meta_mem->name; ?></strong> | |
<?php if ( $mem_match ) : ?> | |
<i class="dashicons dashicons-yes" style="color:#080;"></i> | |
<?php else : ?> | |
<i class="dashicons dashicons-no" style="color:#C00;"></i> | |
<?php endif; ?> | |
<br> | |
</p> | |
<h3>M2 Matching Details</h3> | |
<p> | |
WordPress User: | |
<?php if ( $email_match && $name_match ) : ?> | |
<input type="hidden" name="user_id" value="<?php echo esc_attr( $meta_user_id ); ?>" /> | |
<strong><?php echo $meta_user_id; ?> - <?php echo $meta_user->first_name; ?> <?php echo $meta_user->last_name; ?></strong> | |
<?php else : ?> | |
<select name="user_id"><?php echo implode( '', $user_options ); ?></select> | |
<?php endif; ?> | |
<br> | |
Membership ID: | |
<?php if ( $mem_match ) : ?> | |
<input type="hidden" name="membership_id" value="<?php echo esc_attr( $meta_mem_id ); ?>" /> | |
<strong><?php echo $meta_mem_id; ?> - <?php echo $meta_mem->name; ?></strong> | |
<?php else : ?> | |
<select name="membership_id"><?php echo implode( '', $membership_options ); ?></select> | |
<?php endif; ?> | |
<br> | |
PayPal Invoice ID: | |
<?php if ( $inv_id ) : ?> | |
<input type="hidden" name="inv_id" value="<?php echo esc_attr( $inv_id ); ?>" /> | |
<strong><?php echo $inv_id; ?></strong> | |
<?php else : ?> | |
<input type="text" name="inv_id" value="" /> | |
<?php endif; ?><br> | |
</p> | |
<p> | |
<a href="<?php echo esc_url( $url ); ?>" class="button">Cancel</a> | |
<button class="button button-primary">Create missing invoice</button> | |
</p> | |
</form> | |
<script>jQuery(function(){ jQuery('.matching-form select').wpmuiSelect(); })</script> | |
<?php | |
return ''; | |
} | |
public function show_matching_result( $code, $log ) { | |
echo '<div class="updated notice is-dismissible"><p>' . $this->match_result . '</p></div>'; | |
return $code; | |
} | |
protected function create_matching() { | |
global $wpdb; | |
if ( empty( $_POST['inv_id'] ) || empty( $_POST['user_id'] ) || empty( $_POST['membership_id'] ) || empty( $_POST['log_id'] ) ) { | |
// Missing or invalid input data. | |
$this->match_result = 'Required data missing, submit the form again'; | |
return; | |
} | |
$inv_id = intval( $_POST['inv_id'] ); | |
$user_id = intval( $_POST['user_id'] ); | |
$membership_id = intval( $_POST['membership_id'] ); | |
$log_id = intval( $_POST['log_id'] ); | |
$sql = "SELECT 1 FROM {$wpdb->posts} WHERE ID=%s;"; | |
$sql = $wpdb->prepare( $sql, $inv_id ); | |
$res = $wpdb->get_var( $sql ); | |
if ( $res ) { | |
// This post ID already exists, do not overwrite it. | |
$this->match_result = 'This Invoice ID is already used by some other post/page/etc'; | |
return; | |
} | |
// Create entry in the POST table - needed to specify the POST ID! | |
$data = array( | |
'ID' => $inv_id, | |
'post_author' => $user_id, | |
'post_date' => date( 'Y-m-d' ), | |
'post_date_gmt' => gmdate( 'Y-m-d' ), | |
'post_title' => 'Invoice', | |
'post_name' => 'invoice-' . $inv_id, | |
'post_status' => 'private', | |
'post_parent' => 0, | |
'post_type' => MS_Model_Invoice::get_post_type(), | |
); | |
$wpdb->insert( | |
$wpdb->posts, | |
$data | |
); | |
// Load and re-save the whole invoice object to fill in missing fields. | |
$member = MS_Factory::load( 'MS_Model_Member', $user_id ); | |
$sub = $member->get_subscription( $membership_id ); | |
if ( $sub ) { | |
$member->add_membership( $membership_id ); | |
$sub = $member->get_subscription( $membership_id ); | |
} | |
$inv = MS_Factory::load( 'MS_Model_Invoice', $inv_id ); | |
$inv->gateway_id = 'paypalstandard'; | |
$inv->user_id = $user_id; | |
$inv->membership_id = $membership_id; | |
$inv->ms_relationship_id = $sub->id; | |
$inv->add_notes( 'Created by M2 PayPal Matching plugin' ); | |
$inv->save(); | |
// Re-Process the transaction. | |
if ( MS_Model_Import::retry_to_process( $log_id ) ) { | |
$this->match_result = 'Matching created and payment was processed correctly.'; | |
} else { | |
$this->match_result = 'Matching created but could not process payment.'; | |
} | |
} | |
public function logs_invoice_column( $code, $log ) { | |
if ( 'err' != $log->state ) { return $code; } | |
if ( 'paypalstandard' != $log->gateway_id ) { return $code; } | |
lib3()->array->equip( $log->post, 'invoice' ); | |
$inv_id = intval( $log->post['invoice'] ); | |
if ( ! $inv_id ) { | |
$code .= '<div>N/A</div>'; | |
return $code; | |
} | |
$meta_user = intval( get_post_meta( $inv_id, 'user_id', true ) ); | |
$user = get_user_by( 'id', $meta_user ); | |
if ( $user && $user->ID ) { | |
$label = 'Match'; | |
$class = 'button button-primary'; | |
} else { | |
$label = 'Check'; | |
$class = 'button'; | |
} | |
$url = add_query_arg( | |
array( | |
'action' => 'm2_pp_inv', | |
'log' => $log->id | |
) | |
); | |
$code .= sprintf( | |
'<div><a href="%s" class="%s" style="min-width:0;width:100%%">%s</a></div>', | |
esc_url( $url ), | |
esc_attr( $class ), | |
esc_html( $label ) | |
); | |
return $code; | |
} | |
}; // End of the M2_PP_Inv_Matching class | |
add_action( 'plugins_loaded', array( 'M2_PP_Inv_Matching', 'init' ) ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment