Ashley Gibson – Easy Digital Downloads Development https://easydigitaldownloads.com/development Official development blog for Easy Digital Downloads Tue, 13 Jul 2021 09:06:38 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.2 https://easydigitaldownloads.com/development/files/2015/11/icon-256x256-150x150.png Ashley Gibson – Easy Digital Downloads Development https://easydigitaldownloads.com/development 32 32 Easy Digital Downloads 3.0-beta3 now available https://easydigitaldownloads.com/development/2021/07/13/edd-3-0-beta3/ https://easydigitaldownloads.com/development/2021/07/13/edd-3-0-beta3/#comments Tue, 13 Jul 2021 09:06:37 +0000 https://easydigitaldownloads.com/development/?p=1091

Continue reading →

]]>
Our beta3 release is fairly small compared to the other two, but it contains some pretty important bug fixes, which is why we wanted to make it available to anyone testing 3.0.

Important Note: This beta release is NOT production ready. It should only be tested on development sites. Do not install it on your live site.

Migration fixes

We discovered that migrated order totals could be incorrect if the order had a fee applied that affected the entire order (as opposed to being linked to an individual item). This has now been resolved.

While fixing the fee issue, we also found a few problems when migrating data from very old versions of Easy Digital Downloads — we’re talking back to 2013. Back then, individual payments didn’t even store things like the subtotal or exact discount amount, and that led to unexpected results when the data was migrated to the 3.0 format.

Orders with discounts from that period still may not be perfect, but we’re confident we’ve done the best we can given the limited data from that era. It won’t be any worse than EDD 2.10, it’s just that EDD 3.0 exposes the problems that exist in 2.10 and are simply hidden. For example: if a discount amount wasn’t saved with an old EDD order, EDD 3.0 will show the discount amount as $0.00. That’s not technically correct; there was a discount amount, but because EDD never stored it we don’t know what it is.

Other migration fixes include:

  • Undefined index errors when migrating some order addresses.
  • When migrating tax rates via the CLI we now show the before and after count.
  • Some very old PayPal orders didn’t have their transaction IDs migrated. This is because old orders didn’t store transaction IDs the way we do now, and they only existed in order notes. These are now migrated over.
  • Some currency codes were saved (and thus also migrated) in lowercase, which caused unexpected results in our UIs. We now ensure all currency codes are uppercase when migrated.

The sales log has been removed

The sales log has historically existed because it was too difficult for us to query on payments directly, due to storing data in post meta. EDD 3.0 has made the sales log no longer necessary, as we can now very easily query order data. However, the UI still existed.

We made the decision to remove it when we found a bug: refund records were being included in the sales log incorrectly. Fixing that issue actually proved to be more complicated than expected, to the point where it would take less time to just remove the sales log and instead add the one missing feature to the main orders page: the ability to filter by download (product).

Product filter on the admin orders page

So there’s a shiny new feature for you!

Other bug fixes

  • EDD_Payments_Stats methods returning unexpected results.
  • edd_store_discount() had the default value for its second parameter changed from null to 0, which broke backwards compatibility. This has now been reverted.
  • The edd_post_insert_discount hook wasn’t firing after adding a discount code from the admin UI. This caused at least one add-on to stop working with EDD 3.0.
  • The Discount API endpoint wasn’t 3.0-ready.
  • Negative fees were showing as positive on the admin orders UI.
  • Adding a manual order now supports inclusive taxes.
  • Several methods in the EDD_Discount class were missing compared to 2.x. (Such as the get_name() method.)
  • The tool for re-counting download stats was incorrectly including refunds. It now counts gross earnings, and the label has been updated to better clarify that.
  • Reports: Manually adding an invalid date to the custom range field could cause a fatal error.
  • A few discount note functions that were introduced in 3.0 and ultimately never used (due to later refactoring) have been removed. Those functions are: edd_ajax_add_discount_note(), edd_delete_discount_note(), and edd_ajax_delete_discount_note(). These were all AJAX callback functions and were never designed for public use.
  • Order item meta arrays were incorrectly being double serialized.

Installing and testing 3.0-beta3

The 3.0-beta3 release can be downloaded from GitHub and installed as normal. If you encounter any bugs, please search through our existing issues. If there’s not already an open issue, you may create a new one (after reading our Contributor Guidelines).

]]>
https://easydigitaldownloads.com/development/2021/07/13/edd-3-0-beta3/feed/ 21
Easy Digital Downloads 3.0-beta2 now available https://easydigitaldownloads.com/development/2021/05/20/edd-3-0-beta2/ https://easydigitaldownloads.com/development/2021/05/20/edd-3-0-beta2/#comments Thu, 20 May 2021 14:30:26 +0000 https://easydigitaldownloads.com/development/?p=1062

Continue reading →

]]>
Since releasing 3.0-beta1 back in February, we’ve been hard at work updating our extensions for 3.0 compatibility and fixing any issues that popped up in core. We have since closed over 100 issues!

The beta2 release is now available for testing, and in this blog post we’ll go over three of the major changes.

  1. The new refunds API has been finalized
  2. Templates have been reworked
  3. The migration now has step recovery

Important Note: This beta release is NOT production ready. It should only be tested on development sites. Do not install it on your live site.

The new refunds API has been finalized

When we released 3.0-beta1, we made it clear that refunds were still a work in progress, but they have now been completed!

Modal for submitting a refund, with a checkbox to "Refund transaction in PayPal".

Here are a few notable changes:

  • Each order item and fee will have a listing in the modal, and you can select which items to refund. It’s possible to refund a fee without refunding the order items, and vice versa.
  • For each item, you can also specify a quantity (if applicable), and how much of that item you want to refund. As an example, for that $10 item I could choose to refund only $5 of it.
  • We have added a hook to the modal, which you can use to output custom fields below the table. You can see how we’ve added a checkbox that gives you the option to “Refund transaction in PayPal.”

Let’s talk about that last item for a moment. If you’ve built a custom gateway, you probably want to know how to add your own checkbox and execute custom code when the refund runs. Here’s our implementation for PayPal:

Adding the checkbox to the UI

Here’s what the code looks like to add a checkbox to the UI:

/**
 * Shows a checkbox to automatically refund payments in PayPal.
 *
 * @param \EDD\Orders\Order $order
 *
 * @since 3.0
 * @return void
 */
function edd_paypal_refund_checkbox( \EDD\Orders\Order $order ) {
	if ( 'paypal' !== $order->gateway ) {
		return;
	}

	// If our credentials are not set, return early.
	$key       = $order->mode;
	$username  = edd_get_option( 'paypal_' . $key . '_api_username' );
	$password  = edd_get_option( 'paypal_' . $key . '_api_password' );
	$signature = edd_get_option( 'paypal_' . $key . '_api_signature' );

	if ( empty( $username ) || empty( $password ) || empty( $signature ) ) {
		return;
	}
	?>
	<div class="edd-form-group edd-paypal-refund-transaction">
		<div class="edd-form-group__control">
			<input type="checkbox" id="edd-paypal-refund" name="edd-paypal-refund" class="edd-form-group__input" value="1">
			<label for="edd-paypal-refund" class="edd-form-group__label">
				<?php esc_html_e( 'Refund transaction in PayPal', 'easy-digital-downloads' ); ?>
			</label>
		</div>
	</div>
	<?php
}
add_action( 'edd_after_submit_refund_table', 'edd_paypal_refund_checkbox' );
  • Hook into edd_after_submit_refund_table. This hook provides one parameter: the \EDD\Orders\Order object.
  • Be sure to check that the order is using your gateway.
  • We only show the option if the API keys have been configured.
  • Then output markup to show the custom field.

The next step is to actually process the refund. Here’s the code to make that happen:

/**
 * If selected, refunds a transaction in PayPal when creating a new refund record.
 *
 * @param int $order_id ID of the order we're processing a refund for.
 * @param int $refund_id ID of the newly created refund record.
 * @param bool $all_refunded Whether or not this was a full refund.
 *
 * @since 3.0
 */
function edd_paypal_maybe_refund_transaction( $order_id, $refund_id, $all_refunded ) {
	if ( ! current_user_can( 'edit_shop_payments', $order_id ) ) {
		return;
	}

	if ( empty( $_POST['data'] ) ) {
		return;
	}

	$order = edd_get_order( $order_id );
	if ( empty( $order->gateway ) || 'paypal' !== $order->gateway ) {
		return;
	}

	// Get our data out of the serialized string.
	parse_str( $_POST['data'], $form_data );

	if ( empty( $form_data['edd-paypal-refund'] ) ) {
		// Checkbox was not checked.
		return;
	}

	$refund = edd_get_order( $refund_id );
	if ( empty( $refund->total ) ) {
		return;
	}

	edd_refund_paypal_purchase( $order, $refund );
}
add_action( 'edd_refund_order', 'edd_paypal_maybe_refund_transaction', 10, 3 );
  • Hook into edd_refund_order. This hook supplies three parameters: the ID of the order being refunded, the ID of the newly created refund object, and a boolean indicating if the entire order was refunded or not.
  • Be sure to include a permission check.
  • Be sure to check the relevant gateway.
  • To get the data from the form you’ll want to run: parse_str( $_POST['data'], $form_data ); Then the posted data will be available in $form_data.
  • As a sanity check, we make sure the newly created refund total isn’t empty (0.00).
  • Then, finally, you can execute your code to actually process the refund at the gateway. (We call edd_refund_paypal_purchase()).

You can see all our code for this in context in the PayPal Standard gateway file.

Templates have been reworked (but are still backwards compatible!)

We’ve updated the history-downloads.php and history-purchases.php templates to use all new 3.0 functions.

The main motivation behind this change was speed. Working with the legacy EDD_Payment object (and associated edd_get_payment() and edd_get_users_purchases() functions) is a performance hit. We have to navigate through multiple backwards compatibility layers to build up that object.

Prior to making the changes, the purchase history template was running approximately 300-400 queries — sometimes more. That definitely slowed the page down and resulted in a pretty negative experience. After reworking it, we’ve gotten it down to approximately 35 queries. That’s quite an improvement!

So what do these changes actually mean for you?

Every hook executed within those templates that passed through an EDD_Payment object (or array of objects) has been deprecated, and new replacements have been created. For example:

Here’s an example hook in the previous template version:

do_action( 'edd_before_purchase_history', $payments );

This hook has been deprecated because it was built to receive an array of EDD_Payment objects. It has been replaced with this new hook, which instead takes an array of \EDD\Orders\Order objects:

do_action( 'edd_before_order_history', $orders );

That being said, the old hook will still work. We hook into the new action to trigger the old:

add_action( 'edd_before_order_history', function( $orders ) {
	if ( ! has_action( 'edd_before_purchase_history' ) ) {
		return;
	}

	$payments = array();

	if ( ! empty( $orders ) ) {
		$order_ids = wp_list_pluck( $orders, 'id' );
		$payments  = edd_get_payments(
			array(
				'id__in'  => $order_ids,
				'orderby' => 'date',
			)
		);
	}

	do_action( 'edd_before_purchase_history', $payments );
} );

The only thing this means for you is that if you’re hooking into the old action you will see a performance hit, because we will need to load in the EDD_Payment objects for your hook.

Right now the old hooks will not trigger any deprecation notices, as it is still easiest to use them if you need to support both EDD 2.x and 3.0 at the same time. We are planning to add deprecation notices in EDD 3.1. We have no plans to stop the hooks from working entirely.

For a full list of all deprecated hooks, check out our new deprecated-hooks.php file.

The migration now has step recovery

The 3.0 migration has 8 different steps — or 9 if you include the removal of legacy data. In beta1, if the migration process was interrupted 50% through step 4 and you reloaded the page to start again, you would start over at 0% of step 4. So although your progress through the 8 (or 9) main steps could be recovered, the steps within each step could not. This has changed with beta2.

Now, if the migration is interrupted 50% through step 4 and you reload the page to initiate it again, you will resume at 50% through step 4.

Installing and testing 3.0-beta2

The 3.0-beta2 release can be downloaded from GitHub and installed as normal. If you encounter any bugs, please search through our existing issues. If there’s not already an open issue, you may create a new one (after reading our Contributor Guidelines).

What’s next for 3.0?

Our next major goal is to upgrade to 3.0 on one of our own Sandhills product sites. We’ve been hard at work updating extensions — first prioritizing the ones we use internally — so we can upgrade to 3.0. We will be doing this prior to the official 3.0 release.

We don’t have an exact date for this yet, and there are still a few extensions we need to update first, but I suspect you’ll see us with another blog post when it happens so we can tell you all about how it went!

]]>
https://easydigitaldownloads.com/development/2021/05/20/edd-3-0-beta2/feed/ 2
Easy Digital Downloads 3.0-beta1 now ready for testing https://easydigitaldownloads.com/development/2021/02/16/edd-3-0-beta1/ https://easydigitaldownloads.com/development/2021/02/16/edd-3-0-beta1/#comments Tue, 16 Feb 2021 14:20:14 +0000 https://easydigitaldownloads.com/development/?p=963

Continue reading →

]]>
We’re pleased to announce that the first beta for Easy Digital Downloads 3.0 is now available! This has been a long time coming, and we want to thank you all for your patience and understanding while it’s been in development.

Important Note: This beta release is NOT production ready. It should only be tested on development sites. Do not install it on your live site.

Our goals for the first 3.0 beta

Easy Digital Downloads 3.0 is not yet a finished product. The point of this first beta release is not to test the plugin as a whole, but to focus on these key things:

  1. Early testing on our migration process, to ensure all necessary data is migrated to its new place and that there are no issues with the data transfer or backwards compatibility.
  2. Giving third party developers a chance to see how the code base has changed in 3.0, and the opportunity to update their extensions to be compatible.

Official extensions that are compatible with 3.0:

The following extensions have been already updated to work with 3.0 and may be used in testing:

  • Auto Register, version 1.3.11+
  • Braintree Gateway, version 1.1.6+
  • Commissions, version 3.4.11+
  • Gateway Fees
  • PDF Invoices, version 2.2.27+
  • Recurring, version 2.10.1+
  • Software Licensing, version 3.7+
  • Stripe Gateway, version 2.8+
    * When checking out with Stripe, you may see a debug notice about addresses being stored incorrectly. This will be addressed in version 2.8.1.

What’s not ready for testing

Order refunds are still undergoing some changes, particularly with regards to refunding fees, so we do not recommend testing refunds or updating any of your own refund-related code. We hope for this to be ready for the beta2 release.

System requirements

In order to run EDD 3.0 you’ll need to have the following minimum versions:

  • PHP 5.6+
  • WordPress 4.9+

Note: If you installed EDD 3.0 back in 2018 but have not pulled the release branch since then, we recommend starting with an entirely fresh install. This is because we’ve removed some incremental database upgrades from the 3.0 development branch that were introduced in 2018. If you’re installing or upgrading to 3.0 for the first time then this does not affect you.

Installing 3.0-beta1

The 3.0 beta release can be downloaded from GitHub and installed as normal. After installation, new database tables will be installed in the background and you will be prompted to run the 3.0 migration.

Testing the custom tables migration

In 3.0 we migrate the bulk of EDD data out of the WordPress core tables (posts, postmeta, etc.) and into our new custom tables. This includes:

  • Discounts
  • Payments
  • Customer addresses
  • Customer email addresses
  • Logs
  • Order notes
  • Customer notes

The migration can either be performed through an admin UI, or run through WP-CLI using this command:

wp edd v30_migration
EDD 3.0 migration UI

While testing the migration, double check that all your data has been migrated successfully and that none has been lost — this is particularly true of any custom data you may have added.

Custom payment meta to watch out for

_edd_payment_meta

If you’ve added any custom keys to the _edd_payment_meta array then those will be inserted into our new wp_edd_ordermeta table using the key payment_meta. Just like before, this will be saved as a serialized array.

Note: Calls to edd_get_payment_meta( $payment_id, '_edd_payment_meta' ) are still fully backwards compatible and will include your custom keys/values. However, the backwards compatibility layers do mean that this old method will be slower than our newer edd_get_order_meta() functions so we recommend updating if possible.

Cart item options

In 2.9, _edd_payment_meta included an array that looked like this:

array(
    'cart_details' => array(
        array(
            'item_number' => array(
                'options' => array(
                    'price_id' => 1,
                    'quantity' => 1,
                    // More
                )
            )
        )
    )
)

Any keys added to that options array will be migrated to our new wp_edd_order_itemmeta table. This is meta associated with an individual order item, rather than the order as a whole.

Note: Retrieving this information via edd_get_payment_meta() or edd_get_payment_meta_cart_details() is still fully backwards compatible in 3.0. However, the backwards compatibility layers do mean that this old method will be slower than our newer edd_get_order_meta() functions so we recommend updating if possible.

Custom meta keys

Any custom pieces of meta data associated with a payment will be transferred exactly over to our new wp_edd_ordermeta table. Only the storage location has changed; the data and format remains exactly the same.

Updating your integrations for 3.0

Plugins can be compatible with both EDD 2.9 and 3.0 at the same time. However, a few methods are no longer compatible with 3.0 and your code may need to be updated accordingly.

Ensure you’re not using “post” helper functions on payments, discounts, or logs

In 3.0 you can no longer use “post”-related helper functions on payment, log, or discount code objects. This includes get_post(), get_post_meta(), and more. You will need to update your code to use our wrapper functions instead.

Here’s a detailed list of examples:

Payments/orders

Note: in 3.0, payments are now referred to as “orders”, which is reflected in the new function names.

Retrieving a payment/order

// NO LONGER VALID
$payment = get_post( $payment_id );

// Valid in both 2.9 and 3.0
$payment = edd_get_payment( $payment_id );

// Valid in 3.0 only
$payment = edd_get_order( $payment_id );

Retrieving meta attached to a payment/order

// NO LONGER VALID
$meta = get_post_meta( $payment_id, 'your_meta_key_here', true );

// Valid in both 2.9 and 3.0
$meta = edd_get_payment_meta( $payment_id, 'your_meta_key_here', true );

// Valid in 3.0 only
$meta = edd_get_order_meta( $payment_id, 'your_meta_key_here', true );

Adding or updating payment/order meta

// NO LONGER VALID
update_post_meta( $payment_id, 'your_meta_key_here', 'your meta value' );

// Valid in both 2.9 and 3.0
edd_update_payment_meta( $payment_id, 'your_meta_key_here', 'your meta value' );

// Valid in 3.0 only
edd_update_order_meta( $payment_id, 'your_meta_key_here', 'your meta value' );

Querying for payment/order records

// NO LONGER VALID
$payments = get_posts( array(
    'post_type'   => 'edd_payment',
    'post_status' => 'publish'
) );

// NO LONGER VALID
$payments = new WP_Query( ( array(
    'post_type'   => 'edd_payment',
    'post_status' => 'publish'
) );

// Valid in both 2.9 and 3.0
$payments = edd_get_payments( array(
    'status' => 'publish'
) );

// Valid in 3.0 only
$payments = edd_get_orders( array(
    'status' => 'complete'
) );

Discounts

Retrieving a discount code

// NO LONGER VALID
$discount = get_post( $discount_id );

// Valid in both 2.9 and 3.0
// Get by ID
$discount = edd_get_discount( $discount_id );

// Get by code
$discount = edd_get_discount_by_code( 'BFCM2021' );

Querying for discount codes

// NO LONGER VALID
$discounts = get_posts( array(
    'post_type'   => 'edd_discount',
    'post_status' => array( 'active', 'inactive' )
) );

// Valid in both 2.9 and 3.0
$discounts = edd_get_discounts( array(
    'post_status' => array( 'active', 'inactive' )
) );

Retrieving meta attached to a discount

// NO LONGER VALID
$meta = get_post_meta( $discount_id, 'your_meta_key_here', true );

// Valid in both 2.9 and 3.0
$discount = edd_get_discount( $discount_id );
$discount->get_meta( 'your_meta_key_here', true );

// Valid in 3.0 only
edd_get_adjustment_meta( $discount_id, 'your_meta_key_here', true );

Adding or updating meta

// NO LONGER VALID
update_post_meta( $discount_id, 'your_meta_key_here', 'your meta value' );

// Valid in both 2.9 and 3.0
$discount = edd_get_discount( $discount_id );
$discount->update_meta( 'your_meta_key_here', 'your meta value' );

// Valid in 3.0 only
edd_update_adjustment_meta( $discount_id, 'your_meta_key_here', 'your meta value' );

Logs

EDD has a class called EDD_Logging, which is available in 2.9 and fully backwards compatible in 3.0. If your code needs to be compatible with both versions, ensure you use this class for all querying and inserting.

Querying logs:

global $edd_logs;
$edd_logs->get_logs( $object_id, $log_type, $page_number );

Inserting a log:

global $edd_logs;
$edd_logs->insert( array(
    'log_type'    => 'gateway_error',
    'post_parent' => $download_id
) );

EDD 3.0 also comes with several new functions. Logs are split up into different database tables according to type: file download logs, API request logs, and generic logs. Here are the new functions for generic logs:

Querying logs:

$logs = edd_get_logs( array(
    'type'    => 'gateway_error',
    'user_id' => $user_id
) );

Inserting a log:

edd_add_log( array(
    'object_id'   => $object_id,
    'object_type' => $object_type,
    'user_id'     => $user_id,
    'type'        => $log_type,
    'title'       => $log_title,
    'content'     => $log_content
) );

Update raw queries on the wp_posts database table

In 3.0, most data has been migrated out of wp_posts and wp_postmeta. As a result, if you’re performing any raw queries on those tables, you may need to update your integration to query on our new tables instead in 3.0.

Here’s a before and after example of one of our own queries for download logs:

2.9 query on the posts/postmeta tables:

SELECT post_parent as downloaded_product_id, COUNT(post_parent) AS number_of_downloads, COUNT(DISTINCT meta_value) AS unique_passes_used
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID
WHERE meta_key = '_edd_log_all_access_pass_id'
AND meta_value LIKE '%_{$all_access_product_id}_%'
AND post_date_gmt > %s
AND post_date_gmt < %s
GROUP BY post_parent
ORDER BY COUNT(post_parent) DESC;

3.0 query on the file_downloads/file_downloadmeta tables:

SELECT product_id as downloaded_product_id, COUNT(product_id) as number_of_downloads, COUNT(DISTINCT meta_value) as unique_passes_used
FROM {$wpdb->edd_logs_file_downloads} l
INNER JOIN {$wpdb->edd_logs_file_downloadmeta} lm ON l.id = lm.edd_logs_file_download_id
WHERE meta_key = '_edd_log_all_access_pass_id'
AND meta_value LIKE '%_{$all_access_product_id}_%'
AND date_created > %s
AND date_created < %s
GROUP BY product_id
ORDER BY COUNT(product_id) DESC

With custom queries like this, you will need to load one version if the site is running EDD 2.9 or lower, and another version if the site is running 3.0+.

Reporting issues

EDD 3.0 is still in active development on GitHub. If you encounter any bugs, please search through our existing issues. If there’s not already an open issue, you may create a new one (after reading our Contributor Guidelines). Please prefix all issues with “3.0 -” so we know it pertains to the 3.0 release.

]]>
https://easydigitaldownloads.com/development/2021/02/16/edd-3-0-beta1/feed/ 26