I saw a question in the SureCart Facebook group and I was interested in it from a technical perspective. SureCart has good API coverage and decent API docs and helpful support i only wish they had 100% coverage as there are a few areas I would love API coverage including an API for direct uploads of plugin uploads its the missing piece in me 100% automating SureCart releases inline with UUPD.
The question had more than one part but i focused “Is this possible in SureCart to setup all subscription renewals to happen on a certain date. Even if they for example signed up 6 months into the current year” I’ve seen similar questions previously and always wondered what is possible to do here so decided to investigate.
Looking at the API documents I had a pretty good idea that it was possible I just needed to work through the items, this was made easier by inspecting what the GUI did when you manually undertake these woks and the answer was subscription periods.
So I settled on a workflow
- Fetch Active Subscriptions: It calls GET https://api.surecart.com/v1/subscriptions using the stored API key to retrieve all subscriptions.
- Filter & Loop: It loops through all subscriptions, skipping those that are not active or lack a valid current_period.
- Compare & Update: For each active subscription, it checks if the current_period_end_at is already equal to the target expiry timestamp. If not, it updates it.
- Patch Period Endpoint: The snippet sends a PATCH request to https://api.surecart.com/v1/periods/{$period_id} with the new end_at timestamp to align the subscription expiry.
I then thought about how I’d like to control this and settled on it needing the following
- The ability to set then hide from the GUI an API token
- enable and disable debuggging
- Set a daily Cron to run and sync all subscription dates
- the date and time of expiry.
So I now had all the building blocks and build the following

I then looked at the logic and code taking into account what i already knew from checking the API documentation.
<?php
/**
* Plugin Name: SureCart Auto Expiry Updater
* Description: Automatically updates SureCart subscription periods' end dates daily based on admin-defined settings.
* Version: 1.0
*
*/
add_action('surecart_auto_expiry_hook', 'surecart_check_and_update_expiry');
function surecart_check_and_update_expiry() {
$enabled = get_option('surecart_auto_update_enabled', false);
if (!$enabled) {
surecart_log('Cron skipped: auto update disabled.');
return;
}
$target_datetime = get_option('surecart_target_expiry', '2026-01-15T10:00');
$target_timestamp = strtotime($target_datetime);
$api_key = get_option('surecart_api_key');
surecart_log("Running cron at: " . current_time('mysql'));
surecart_log("Target expiry timestamp: {$target_timestamp}");
if (!$api_key) {
surecart_log("No API key set. Exiting.");
return;
}
$subscriptions = surecart_get_subscriptions($api_key);
if (!$subscriptions) {
surecart_log("No subscriptions returned or API error.");
return;
}
foreach ($subscriptions as $sub) {
$sub_id = $sub['id'];
$status = $sub['status'] ?? 'unknown';
if ($status !== 'active') {
surecart_log("Skipping subscription {$sub_id} (status: {$status})");
continue;
}
$period_id = $sub['current_period'] ?? null;
$period_end = $sub['current_period_end_at'] ?? 0;
if (!$period_id || !$period_end) {
surecart_log("No current_period or end date for subscription {$sub_id}. Skipping.");
continue;
}
if ($period_end == $target_timestamp) {
surecart_log("Subscription {$sub_id} already aligned. Skipping.");
continue;
}
surecart_log("Updating current_period {$period_id} for subscription {$sub_id} to timestamp {$target_timestamp}");
$success = surecart_update_period($api_key, $period_id, $target_timestamp);
if ($success) {
surecart_log("Successfully updated period {$period_id}");
} else {
surecart_log("Failed to update period {$period_id}");
}
}
}
function surecart_get_subscriptions($api_key) {
$url = 'https://api.surecart.com/v1/subscriptions';
surecart_log("Fetching subscriptions from: $url");
$response = wp_remote_get($url, [
'headers' => [
'Authorization' => 'Bearer ' . $api_key,
'Accept' => 'application/json',
]
]);
if (is_wp_error($response)) {
surecart_log("Error fetching subscriptions: " . $response->get_error_message());
return false;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
return $body['data'] ?? [];
}
function surecart_update_period($api_key, $period_id, $end_timestamp) {
$url = "https://api.surecart.com/v1/periods/{$period_id}";
surecart_log("Sending PATCH to {$url} with end_at {$end_timestamp}");
$response = wp_remote_request($url, [
'method' => 'PATCH',
'headers' => [
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
],
'body' => json_encode([
'end_at' => $end_timestamp
]),
]);
if (is_wp_error($response)) {
surecart_log("Error updating period {$period_id}: " . $response->get_error_message());
return false;
}
surecart_log("Update response code for {$period_id}: " . wp_remote_retrieve_response_code($response));
return true;
}
function surecart_log($message) {
if (!get_option('surecart_auto_debug_enabled')) return;
if (!defined('WP_DEBUG') || !WP_DEBUG) return;
if (is_array($message) || is_object($message)) {
$message = print_r($message, true);
}
error_log('[SureCart Debug] ' . $message);
}
register_activation_hook(__FILE__, function () {
$cron_time = get_option('surecart_cron_run_time', '00:00');
list($h, $m) = explode(':', $cron_time);
$timestamp = mktime((int)$h, (int)$m, 0);
if ($timestamp <= time()) {
$timestamp = strtotime('tomorrow ' . $cron_time);
}
if (!wp_next_scheduled('surecart_auto_expiry_hook')) {
wp_schedule_event($timestamp, 'daily', 'surecart_auto_expiry_hook');
}
});
register_deactivation_hook(__FILE__, function () {
wp_clear_scheduled_hook('surecart_auto_expiry_hook');
});
add_action('admin_menu', function () {
add_options_page('SureCart Auto Expiry', 'SureCart Auto Expiry', 'manage_options', 'surecart-auto-expiry', 'surecart_expiry_settings_page');
});
function surecart_expiry_settings_page() {
if (isset($_POST['submit'])) {
update_option('surecart_auto_update_enabled', isset($_POST['enabled']));
update_option('surecart_target_expiry', sanitize_text_field($_POST['expiry']));
update_option('surecart_cron_run_time', sanitize_text_field($_POST['cron_time']));
update_option('surecart_auto_debug_enabled', isset($_POST['debug_enabled']));
if (!empty($_POST['api_key'])) {
update_option('surecart_api_key', sanitize_text_field($_POST['api_key']));
update_option('surecart_api_key_hidden', true);
}
wp_clear_scheduled_hook('surecart_auto_expiry_hook');
$cron_time = sanitize_text_field($_POST['cron_time']);
list($h, $m) = explode(':', $cron_time);
$timestamp = mktime((int)$h, (int)$m, 0);
if ($timestamp <= time()) {
$timestamp = strtotime('tomorrow ' . $cron_time);
}
wp_schedule_event($timestamp, 'daily', 'surecart_auto_expiry_hook');
echo '<div class="updated"><p>Settings saved and cron rescheduled.</p></div>';
}
if (isset($_POST['reset_api_key'])) {
delete_option('surecart_api_key');
update_option('surecart_api_key_hidden', false);
echo '<div class="updated"><p>API Key has been reset.</p></div>';
}
$enabled = get_option('surecart_auto_update_enabled', false);
$expiry = get_option('surecart_target_expiry', '2026-01-15T10:00');
$cron_time = get_option('surecart_cron_run_time', '00:00');
$debug_enabled = get_option('surecart_auto_debug_enabled', false);
$api_key_hidden = get_option('surecart_api_key_hidden', false);
?>
<div class="wrap">
<h1>SureCart Auto Expiry Settings</h1>
<form method="post">
<label>
<input type="checkbox" name="enabled" <?php checked($enabled); ?>>
Enable Daily Subscription Expiry Update
</label><br><br>
<label>Target Expiry Date & Time:</label><br>
<input type="datetime-local" name="expiry" value="<?php echo esc_attr($expiry); ?>"><br><br>
<label>Daily Cron Run Time (server time):</label><br>
<input type="time" name="cron_time" value="<?php echo esc_attr($cron_time); ?>"><br><br>
<label>
<input type="checkbox" name="debug_enabled" <?php checked($debug_enabled); ?>>
Enable Debug Logging (to wp-content/debug.log)
</label><br><br>
<?php if (!$api_key_hidden): ?>
<label>SureCart API Key:</label><br>
<input type="text" name="api_key" size="50"><br><br>
<?php else: ?>
<p><strong>API Key is set and hidden.</strong></p>
<button name="reset_api_key" class="button">Reset API Key</button><br><br>
<?php endif; ?>
<input type="submit" name="submit" class="button-primary" value="Save Settings">
</form>
</div>
<?php
}
PHPThis codes entire perspective is to run a single CRON event daily to set all subscriptions to a renewal date as set in the admin interface. Its a proof of concept rather than a final solution. You will need to adapt and expand depending on your usecase
Expanding the code
Additional options to expand the code could be a setback date a point in time when you get 15 months for example rather than 12, the ability to only limit it to subscriptions containing certain products or price id’s there are endless possibilities once you start working directly with the API as long as you know the end point and have a logical idea of what you need to achieve.
You can view the accompanying video below: