Integration
Edd Sl Sdk Guide
Drop-in integration guide for the official Easy Digital Downloads Software Licensing SDK. Built so AI agents (Claude, Gemini, Codex, Hermes, OpenClaw, …) and WordPress plugin developers can ship a working license + auto-updater in minutes.
Configuration Example
{
"repositories": {
"edd-sl-sdk": {
"type": "vcs",
"url": "https://github.com/awesomemotive/edd-sl-sdk"
}
},
"require": {
"easy-digital-downloads/edd-sl-sdk": "^1.0.3"
}
}
README
# EDD Software Licensing SDK — Integration Guide
A drop-in integration guide for the official [Easy Digital Downloads Software Licensing SDK](https://github.com/awesomemotive/edd-sl-sdk). Built so AI agents (Claude, Gemini, Codex, Hermes, OpenClaw, …) and WordPress plugin developers can ship a working license activation + auto-updater **in minutes**, without inventing custom UI or hand-rolling API calls.
> **Why this guide exists**: the SDK is excellent but the official docs are scattered across three pages, the SDK is not on Packagist (so vanilla `composer require` fails), and the README only shows a happy-path snippet. This guide consolidates everything you need into one focused document — verified against a real production integration.
## What is the EDD-SL SDK?
A WordPress library that adds three things to your plugin or theme **automatically**:
1. A **"Manage License"** action link on the WordPress Plugins screen (or a "Theme License" menu item for themes), which opens a modal for the user to paste their license key.
2. **License activation/deactivation** against your EDD store (`activate_license`, `deactivate_license`, `check_license` endpoints) — no JSON API code on your side.
3. **Auto-update delivery** — when you bump the product version on your store, WordPress shows the update banner; one click installs the new ZIP.
You write ~10 lines of glue. The SDK does the rest.
## 60-Second Integration
### 1. `composer.json`
The SDK is **not on Packagist**. Add it as a VCS repository:
```json
{
"repositories": {
"edd-sl-sdk": {
"type": "vcs",
"url": "https://github.com/awesomemotive/edd-sl-sdk"
}
},
"require": {
"easy-digital-downloads/edd-sl-sdk": "^1.0.3"
}
}
```
Then install:
```bash
composer install --no-dev --optimize-autoloader
```
### 2. Your plugin's main file
```php
<?php
/**
* Plugin Name: Your Plugin
* Version: 1.0.0
*/
defined( 'ABSPATH' ) || exit;
// Composer autoloader.
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require_once __DIR__ . '/vendor/autoload.php';
}
// EDD-SL SDK bootstrap — separate file from the autoloader, easy to forget.
if ( file_exists( __DIR__ . '/vendor/easy-digital-downloads/edd-sl-sdk/edd-sl-sdk.php' ) ) {
require_once __DIR__ . '/vendor/easy-digital-downloads/edd-sl-sdk/edd-sl-sdk.php';
}
add_action( 'edd_sl_sdk_registry', function ( $init ) {
$init->register( array(
'id' => 'your-plugin-slug', // your plugin slug
'url' => 'https://store.example.com', // your EDD store URL
'item_id' => 123, // EDD download post ID
'version' => '1.0.0', // current plugin version
'file' => __FILE__, // path to main plugin file
) );
} );
```
### 3. Release ZIP must include `vendor/`
Your distribution workflow has to run `composer install --no-dev` before zipping, and the resulting `vendor/` directory must end up inside the ZIP. Most CI setups need an explicit step. Example GitHub Actions snippet:
```yaml
- name: Install production dependencies
run: composer install --no-dev --optimize-autoloader --no-interaction
- name: Build release ZIP
run: |
rsync -a --exclude='.git' --exclude='.github' --exclude='tests' \
--exclude='node_modules' --exclude='composer.lock' \
--exclude='*.zip' ./ "release/your-plugin/"
cd release && zip -r "../your-plugin-${VERSION}.zip" "your-plugin"
```
**Do NOT** add `vendor/` to your `rsync --exclude` list. That ships a broken ZIP with no SDK.
That's the whole integration. There is no admin page to build, no AJAX handler to write, no settings field to register.
## Registry Arguments — Full Reference
```php
$init->register( array(
// Required
'id' => 'your-plugin-slug', // string — plugin slug
'url' => 'https://store.example', // string — EDD store base URL
'item_id' => 123, // int — EDD download post ID
'version' => '1.0.0', // string — current version
'file' => __FILE__, // string — main plugin file (plugins only)
// Optional
'type' => 'plugin', // 'plugin' (default) or 'theme'
'weekly_check' => true, // bool — background license verify (default true)
'messenger_class' => MyMessenger::class, // FQCN extending EasyDigitalDownloads\Updater\Messenger
) );
```
For themes, drop `file` and add `'type' => 'theme'`. The SDK injects a "Theme License" submenu under Appearance instead of a plugin row action.
## What NOT to Build (Common Mistakes)
If you are an AI agent reading this to implement EDD-SL: avoid the following anti-patterns.
| Don't | Why |
|---|---|
| Add a license-key text field to your plugin's own settings page | The SDK injects its own "Manage License" link + modal. A second input causes confusion and never syncs with the SDK's internal state. |
| Pass `'license' => …` in the `register()` array | The SDK manages license storage in its own option keys (`edd_sl_…`). Passing `license` here does nothing useful. |
| Call `edd_action=activate_license` from your plugin yourself | The SDK already does this from its modal. Calling it again can leave state out of sync, or hit `no_activations_left`. |
| Ship without `vendor/` in the release ZIP | The bootstrap file (`require_once`) silently no-ops via the `file_exists()` guard, and your users see no update banners, ever. Hardest bug to spot in this whole integration. |
| Add `vendor/` to your `.gitignore` AND forget the CI install step | Same as above — vendor is missing in the ZIP. Either commit `vendor/`, or make CI install it before zipping. Most teams pick CI install. |
| Use the older single-file `EDD_SL_Plugin_Updater.php` from `easydigitaldownloads/edd-sample-plugin` | The SDK is the current recommendation (per EDD's 2025 docs). The old single-file approach still works but you re-write activate/deactivate UI yourself. |
| Forget to bump the version when releasing | Both the plugin header (`Version:`) and any version constant should match the new tag. The SDK compares this against `new_version` from the store. |
## License Lifecycle (What the SDK Does for You)
Once installed, here is what happens on each user action:
| User action | SDK behaviour |
|---|---|
| Visits **Plugins** screen | Detects whether SDK has a stored license key for this product. Shows "Manage License" link. |
| Clicks "Manage License" | Opens modal with input field. |
| Pastes key + clicks **Activate** | POSTs to `{store_url}/?edd_action=activate_license&item_id=…&license=…&url=…`. On `license=valid`, stores key in `edd_sl_…` option and marks active. On any error, shows the localized error message. |
| Clicks **Deactivate** | POSTs to `deactivate_license`, drops the local active flag, optionally keeps the key for re-activation later. |
| Periodically (weekly by default) | POSTs to `check_license` to confirm the license hasn't expired/been revoked/etc. |
| WordPress polls for updates | SDK intercepts the `pre_set_site_transient_update_plugins` hook, calls `get_version` with the active license, injects `new_version` + `download_link` so the WP core update flow takes over. |
The SDK gracefully handles every license state your store can return: `valid`, `invalid`, `disabled`, `expired`, `key_mismatch`, `item_name_mismatch`, `site_inactive`, plus `no_activations_left`.
## Verify Without wp-admin (curl + wp-cli)
After deploying your plugin to a test site, you can verify each layer of the chain from CLI before opening a browser:
```bash
# 1. SDK class is loaded into WP runtime
wp eval 'echo class_exists("\\EasyDigitalDownloads\\Updater\\Registry") ? "loaded\n" : "missing\n";'
# 2. Store's get_version endpoint answers correctly
curl "https://store.example/?edd_action=get_version&item_id=123&license=YOUR_KEY&url=https://your-site.example" | jq .
# 3. Store's activate_license endpoint accepts your key
curl -X POST "https://store.example/?edd_action=activate_license&item_id=123&license=YOUR_KEY&url=https://your-site.example" | jq .
```
Expected `activate_license` success response:
```json
{
"success": true,
"license": "valid",
"item_id": 123,
"item_name": "Your Plugin",
"expires": "lifetime",
"site_count": 1,
"activations_left": "unlimited"
}
```
If the API responds correctly but the SDK still doesn't activate from wp-admin, the problem is **almost always** one of: SDK bootstrap not loaded (forgot the `require_once`), `vendor/` missing from ZIP, or product `_edd_sl_enabled` not set to `1` on the store. Check those three first.
## Customizing Translations
The SDK uses its own text domain (`edd-sl-sdk`) by default. To use your own:
### Option A — Custom messenger class
```php
use EasyDigitalDownloads\Updater\Messenger;
class MyPluginMessenger extends Messenger {
protected $text_domain = 'my-plugin';
public function get_activate_button_label() {
return $this->filter_string( __( 'Activate License', $this->text_domain ), 'activate_button' );
}
}
add_action( 'edd_sl_sdk_registry', function ( $registry ) {
$registry->register( array(
'id' => 'my-plugin',
'url' => 'https://store.example',
'item_id' => 123,
'version' => '1.0.0',
'file' => __FILE__,
'messenger_class' => MyPluginMessenger::class,
) );
} );
```
All overrideable methods are listed in [the SDK README](https://github.com/awesomemotive/edd-sl-sdk#available-messenger-methods).
### Option B — Filter hook
```php
add_filter( 'edd_sl_sdk_translate_string', function ( $string, $key, $text_domain ) {
if ( 'my-text-domain' === $text_domain && 'activate_button' === $key ) {
return __( 'Enable
... (truncated)
integration
Comments
Sign in to leave a comment