Created
September 6, 2017 17:06
-
-
Save brianhogg/9c6e10c59b85536240dada681c7c0ac4 to your computer and use it in GitHub Desktop.
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: EDD Webhooks | |
Description: Captures new sale notifications from 3rd party service and adds to Easy Digital Downloads | |
Version: 1.0 | |
Author: Brian Hogg | |
Author URI: https://brianhogg.com | |
Text Domain: edd-webhooks | |
License: GPL2 | |
*/ | |
abstract class EDD_Webhook_Handler { | |
/** | |
* Functions to get the name of this webhook (for display to the user) and an ID | |
*/ | |
abstract function get_hook_name(); | |
abstract function get_hook_id(); | |
function __construct() { | |
add_action( 'add_meta_boxes', array( $this, 'add_item_id_metabox' ) ); | |
add_action( 'edd_metabox_fields_save', array( $this, 'save_item_id_metabox_fields' ) ); | |
// register the REST API endpoint to handle our sale notifications | |
add_action( 'rest_api_init', array( $this, 'register_endpoints' ) ); | |
register_activation_hook( __FILE__, array( $this, 'flush_rewrite_rules' ) ); | |
} | |
/** | |
* Flush the rewrite rules on plugin activation so the rest API endpoint can take effect | |
*/ | |
function flush_rewrite_rules() { | |
flush_rewrite_rules(); | |
} | |
/** | |
* Functions to add a meta box so we can put in the product or item ID of the ecommerce service, and match it up to an EDD download | |
*/ | |
function add_item_id_metabox() { | |
if ( current_user_can( 'edit_product', get_the_ID() ) ) { | |
add_meta_box( 'edd_' . $this->get_hook_id() . '_item', sprintf( __( '%s Integration', 'edd-webhooks' ), $this->get_hook_name() ), array( $this, 'render_item_id_metabox' ), 'download', 'side' ); | |
} | |
} | |
function render_item_id_metabox() { | |
global $post; | |
$item_id = get_post_meta( $post->ID, $this->get_hook_id() . '_item_id', true ); | |
?> | |
<p><?php echo sprintf( esc_html__( 'Specify the %s product ID', $this->get_hook_id() . 'edd-webhooks' ), $this->get_hook_name() ); ?></p> | |
<input type="text" name="<?= $this->get_hook_id() ?>_item_id" value="<?php esc_attr_e( $item_id ) ?>" /> | |
<?php | |
} | |
function save_item_id_metabox_fields( $fields ) { | |
$fields[] = $this->get_hook_id() . '_item_id'; | |
return $fields; | |
} | |
/** | |
* Function to register any checks (ie. required parameters) for the endpoint | |
* | |
* @return array | |
*/ | |
function get_endpoint_args() { | |
return array(); | |
} | |
function register_endpoints() { | |
register_rest_route( | |
'edd-' . $this->get_hook_id() . '-webhook/v1', | |
'/purchase_notification', | |
array( | |
'methods' => 'POST,PUT', | |
'callback' => array( $this, 'handle_purchase_notification' ), | |
'args' => $this->get_endpoint_args(), | |
) | |
); | |
} | |
/** | |
* Get the EDD download ID for a given item id | |
* | |
* @param $item_id | |
* @return bool | EDD_Download | |
*/ | |
function get_download_by_item_id( $item_id ) { | |
if ( class_exists( 'EDD_Hide_Download' ) ) { | |
remove_filter( 'edd_downloads_query', array( EDD_Hide_Download::get_instance(), 'shortcode_query' ) ); | |
remove_action( 'pre_get_posts', array( EDD_Hide_Download::get_instance(), 'pre_get_posts' ), 9999 ); | |
} | |
$posts = get_posts( array( | |
'post_type' => 'download', | |
'post_status' => 'any', | |
'suppress_filters' => true, | |
'posts_per_page' => -1, | |
'meta_key' => $this->get_hook_id() . '_item_id', | |
'meta_value' => sanitize_text_field( $item_id ) | |
) | |
); | |
foreach ( $posts as $post ) { | |
$download = new EDD_Download( $post->ID ); | |
return $download; | |
} | |
return false; | |
} | |
/** | |
* Function to check that the request we're getting is really from the ecommerce service | |
* | |
* @param $request WP_REST_Request | |
* @return bool | |
*/ | |
abstract function verify_request( $request ); | |
/** | |
* Get the parameters the webhook sends to us. | |
* Override for webhooks that can't just get the params from the WP_REST_Request. | |
* | |
* @param WP_REST_Request | |
* @return array | |
*/ | |
function get_webhook_params( $request ) { | |
return $request->get_params(); | |
} | |
/** | |
* Run any checks on the webhook parameters | |
* | |
* @param $params array | |
* @return bool | |
*/ | |
abstract function verify_webhook_params( $params ); | |
/** | |
* Grab the customer email address from the parameters | |
* @param $params array | |
* @return string The email address | |
*/ | |
abstract function get_buyer_email_address( $params ); | |
/** | |
* Get the ordered item ID | |
* | |
* @param $params array | |
* @return string | |
*/ | |
abstract function get_item_id( $params ); | |
/** | |
* Get the unique order ID from the webhook params | |
* | |
* @param $params array | |
* @return string The order ID | |
*/ | |
abstract function get_order_id( $params ); | |
/** | |
* Get the price of the item (without tax) | |
* | |
* @param $params array | |
* @return string | |
*/ | |
abstract function get_item_price( $params ); | |
/** | |
* Get the tax charged on the item (if any) | |
* | |
* @param $params array | |
* @return string | |
*/ | |
abstract function get_item_tax( $params ); | |
/** | |
* Get the currency of the order. Defaults to the EDD currency but can override if needed. | |
* | |
* @param $params array | |
* @return string The currency code of the order | |
*/ | |
function get_order_currency( $params ) { | |
return edd_get_currency(); | |
} | |
/** | |
* Handle a purchase notification | |
* | |
* @param WP_REST_Request $request | |
*/ | |
function handle_purchase_notification( WP_REST_Request $request ) { | |
$params = $this->get_webhook_params( $request ); | |
// Verify the required data is present | |
if ( ! $this->verify_webhook_params( $params ) ) | |
return new WP_Error( 'invalid_alert', 'Invalid alert', array( 'status' => 404 ) ); | |
if ( ! $this->verify_request( $request ) ) { | |
return new WP_Error( 'invalid_signature', 'Invalid signature', array( 'status' => 401 ) ); | |
} | |
// Check and add the customer if needed | |
$email = $this->get_buyer_email_address( $params ); | |
$customer = new EDD_Customer( $email, false ); | |
$first = $this->get_hook_name(); | |
$last = 'Customer'; | |
$user_id = 0; | |
if ( ! $customer->id > 0 ) { | |
$user = get_user_by( 'email', $email ); | |
if ( $user ) { | |
$user_id = $user->ID; | |
$email = $user->user_email; | |
} | |
$customer->create( array( | |
'email' => $email, | |
'name' => $first . ' ' . $last, | |
'user_id' => $user_id | |
) ); | |
} else { | |
$email = $customer->email; | |
} | |
// See if there is a payment for the order ID already | |
$payment = new EDD_Payment; | |
foreach ( $customer->get_payments() as $customer_payment ) { | |
if ( $customer_payment->transaction_id == $this->get_order_id( $params ) ) { | |
$payment = $customer_payment; | |
break; | |
} | |
} | |
$payment->customer_id = $customer->id; | |
$payment->user_id = $user_id; | |
$payment->first_name = $first; | |
$payment->last_name = $last; | |
$payment->email = $email; | |
// Make sure the user info data is set | |
$payment->user_info = array( | |
'first_name' => $first, | |
'last_name' => $last, | |
'id' => $user_id, | |
'email' => $email, | |
); | |
$download = $this->get_download_by_item_id( $this->get_item_id( $params ) ); | |
if ( ! $download ) | |
return new WP_Error( 'invalid_download', 'Invalid download', array( 'status' => 500 ) ); | |
// Only a single price for now | |
$args = array( | |
'quantity' => 1, | |
'item_price' => edd_sanitize_amount( $this->get_item_price( $params ) ), | |
); | |
$args['tax'] = $this->get_item_tax( $params ); | |
// ensure download removed if already there | |
$payment->remove_download( $download->ID, array( 'quantity' => 1 ) ); | |
$payment->add_download( $download->ID, $args ); | |
$payment->date = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ); | |
$payment->status = 'pending'; | |
$payment->currency = $this->get_order_currency( $params ); | |
$payment->gateway = $this->get_hook_id(); | |
$payment->mode = 'live'; | |
$payment->transaction_id = $this->get_order_id( $params ); | |
$payment->save(); | |
// Now switch the status from pending to complete to trigger various actions, like generating a license key | |
$payment->status = 'complete'; | |
$payment->save(); | |
return new WP_REST_Response( array( 'success' => true ) ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment