ReturnKit

Developer reference

Code lives under the ReturnKit\ namespace in includes/. The prefix for options, tables, hooks, query parameters and CSS classes is rtkt_ (rtkt- in CSS).

Shortcodes

Shortcode Attributes What it shows
[returnkit_portal] none The Returns page: the guest look-up, the return form for an order link, and request pages. Logged-in visitors get a pointer to My Account → Returns.
[returnkit_withdrawal] none The withdrawal page: the order picker (logged in), the statement form, the review and confirm step, and withdrawal request pages.
[returnkit_withdrawal_button] label, class A "Withdraw from contract here" link to the withdrawal page, for footers, menus and legal pages. label overrides the Button label setting; class adds CSS classes to the rtkt-withdraw-button link. Prints nothing while the withdrawal function is off.
[returnkit_withdrawal_button label="Cancel a purchase" class="footer-link"]

Templates

Every customer-facing template can be overridden by the theme. Copy it from wp-content/plugins/returnkit/templates/ to yourtheme/woocommerce/returnkit/, keeping the sub-folder, and edit the copy. Templates are loaded with wc_get_template(), so a child theme's copy wins over the parent's. Each template's header lists the variables it receives, and its @version.

Template Used for
portal/lookup.php The guest look-up on the Returns page.
portal/return-form.php The return form for one order.
portal/request.php A request's page: status, acknowledgement, instructions, items, timeline, cancel.
portal/not-found.php An invalid or expired link.
portal/withdrawal-orders.php The order picker for logged-in customers.
portal/withdrawal-form.php The withdrawal statement form. Keep the fields the law requires: name, order identification and email.
portal/withdrawal-review.php The review step. Keep the confirm button: the law requires a separate confirmation step.
myaccount/returns.php My Account → Returns.
myaccount/order-panel.php The Returns panel on the order page and the order-received page.
emails/*.php, emails/plain/*.php The five emails and the shared request-items.php. Keep the statement and the date and time in customer-withdrawal.php.

Filters

rtkt_deadline

Change the last moment a return or withdrawal can be requested.

// Give returns until the end of January for December orders.
add_filter( 'rtkt_deadline', function ( $deadline, $order, $type ) {
	$created = $order->get_date_created();
	if ( 'return' === $type && $created && '12' === $created->date( 'm' ) ) {
		return max( $deadline, strtotime( ( (int) $created->date( 'Y' ) + 1 ) . '-01-31 23:59:59' ) );
	}
	return $deadline;
}, 10, 3 );
Parameter Type
$deadline int Unix timestamp: the end of the last day, site time.
$order WC_Order
$type string return or withdrawal. Exchanges use return.

Not called while the period hasn't started (with When the order is completed, before completion).

rtkt_order_eligibility

Refuse a return or withdrawal for an order that passed every built-in check.

Parameter Type
$result array ok (bool), code (disabled, status, window or nothing_left), deadline (int or null), outside (bool: a withdrawal accepted after the period).
$order WC_Order
$type string return or withdrawal.
add_filter( 'rtkt_order_eligibility', function ( $result, $order, $type ) {
	if ( 'return' === $type && $order->get_meta( '_wholesale' ) ) {
		$result['ok']   = false;
		$result['code'] = 'status';
	}
	return $result;
}, 10, 3 );

The filter only runs when the order is eligible; a refusal returns before it. The customer sees the message for code.

rtkt_item_excluded

Exclude an order line, or allow one that's excluded. Return the reason shown to the customer, or '' to allow it.

Parameter Type
$reason string '' when the line can be returned or withdrawn.
$item WC_Order_Item_Product
$product WC_Product or null Null if the product was deleted.
$type string return or withdrawal.
add_filter( 'rtkt_item_excluded', function ( $reason, $item, $product, $type ) {
	if ( 'return' === $type && $product && $product->get_meta( '_final_sale' ) ) {
		return __( 'Final sale items can\'t be returned.', 'my-theme' );
	}
	return $reason;
}, 10, 4 );

Only exclude from withdrawal what the law allows.

rtkt_reasons

Change the reasons offered, as key => label. Keys are stored on the items.

add_filter( 'rtkt_reasons', function ( $reasons ) {
	$reasons['gift'] = 'Unwanted gift';
	return $reasons;
} );

rtkt_client_ip

Change the visitor IP address ReturnKit uses for the rate limit, Turnstile and the IP stored with each request. Runs after Visitor IP address from has been applied. Return a valid IP, or '' for unknown.

add_filter( 'rtkt_client_ip', function ( $ip ) {
	// e.g. a header set by your own trusted edge.
	return isset( $_SERVER['HTTP_X_MY_EDGE_IP'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_MY_EDGE_IP'] ) ) : $ip;
} );

The value is shortened before it's stored when Shorten IP addresses is on.

rtkt_request_number

Change the request number shown to customers and in wp-admin. Default R-{id}.

add_filter( 'rtkt_request_number', function ( $number, $request ) {
	return 'RMA-' . str_pad( (string) $request->id(), 6, '0', STR_PAD_LEFT );
}, 10, 2 );

The search box recognizes R-123 and bare numbers only.

rtkt_request_items

Inspect or change a new request's item rows before it's saved, or return a WP_Error to refuse it with that message. Pro uses it to validate and price exchanges.

Parameter Type
$items array[] Rows with order_item_id, product_id, variation_id, name, qty, unit_value, reason, exchange_product_id.
$order WC_Order
$type string return, exchange or withdrawal.
$args array name, email, note, source, resolution, scope, statement, order_number, flags.
add_filter( 'rtkt_request_items', function ( $items, $order, $type ) {
	if ( 'withdrawal' !== $type && count( $items ) > 10 ) {
		return new WP_Error( 'too_many', 'Please contact us to return more than 10 items.' );
	}
	return $items;
}, 10, 3 );

Don't refuse withdrawals with this filter: a valid withdrawal statement must be accepted.

rtkt_new_request_flags

Add flags to a new request. Flags show on the request, in the store email and under the Flagged filter. Pro adds frequent and risky here.

Parameter Type
$flags string[] Built-in: outside_window, email_mismatch.
$order WC_Order
$email string The customer's email, lower case.
add_filter( 'rtkt_new_request_flags', function ( $flags, $order, $email ) {
	if ( $order->get_total() > 1000 ) {
		$flags[] = 'high_value';
	}
	return $flags;
}, 10, 3 );

Unknown flag keys are displayed as the key itself. Not called for unmatched withdrawals, which always get unmatched.

rtkt_customer_timeline

Change the timeline entries customers see on their request page.

Parameter Type
$entries array[] Log rows: id, request_id, created_at (UTC), actor, user_id, event, message, data (array). By default events created, approved, received, completed, rejected, cancelled, message, credited and exchange_order.
$request ReturnKit\Domain\Request

rtkt_email_status_change

Return false to skip the customer email for a status change.

Parameter Type
$send bool Default true.
$request ReturnKit\Domain\Request After the change.
$to string approved, received, completed or rejected.
// No "we've received your items" email.
add_filter( 'rtkt_email_status_change', function ( $send, $request, $to ) {
	return 'received' === $to ? false : $send;
}, 10, 3 );

rtkt_approved_email_attachments

Files to attach to the "approved" update email. Pro adds the return label.

Parameter Type
$attachments string[] Absolute file paths.
$request ReturnKit\Domain\Request

rtkt_exchange_options (Pro)

Change the replacements offered for an order line.

Parameter Type
$options array Product ID => [ label, price incl. tax ].
$line array The eligibility line: item_id, item, product, name, ordered, refunded, held, available, unit_value, excluded, note.
add_filter( 'rtkt_exchange_options', function ( $options, $line ) {
	unset( $options[ 123 ] ); // never offer product 123.
	return $options;
}, 10, 2 );

A replacement you add must still be published, purchasable and in stock to be accepted.

Admin filters

Filter Parameters Use
rtkt_admin_screens $screens: key => [ slug suffix, title ] Add a screen to the ReturnKit menu and header tabs. Render it on rtkt_admin_screen_{key}. Pro adds insights.
rtkt_admin_flash $map: key => [ text, is_error ] Messages shown after a redirect with ?rtkt_msg={key}.
rtkt_admin_request_action $result (null), $do (string), $request Handle a custom action posted from the request screen (do field). Return a WP_Error, a Request, or a flash key string; return $result unchanged for actions that aren't yours. Capability and nonce are already checked.

Actions

Action Arguments Fires when
rtkt_request_created $request A request was saved: customer emails (priority 10) and Pro's automatic approval (20) hook in here. Also fires for unmatched withdrawals.
rtkt_status_changed $request (after), $from, $to, $message, $data Any status change. $data can hold outcome (refunded, credited, exchanged, completed) and amount.
rtkt_customer_message $request, $text Staff sent a message to the customer.
rtkt_send_access_link $order A guest look-up matched an order. ReturnKit emails the link from here.
rtkt_create_tables $collate Tables are created or upgraded. Pro adds the store-credit ledger.
rtkt_portal_assets none The front-end stylesheet and script were enqueued.
rtkt_admin_assets none The admin stylesheet and script were enqueued on a ReturnKit screen.
rtkt_admin_screen_{key} none Render a screen added through rtkt_admin_screens.
rtkt_settings_sections $settings, $stored, $helpers Print extra cards inside the settings form. $helpers holds the row printers rtkt_label, rtkt_toggle, rtkt_number, rtkt_segment, rtkt_textarea, rtkt_text, rtkt_checklist. Only keys from Settings::defaults() are saved.
rtkt_admin_request_main $request After the items on the admin request screen.
rtkt_admin_request_actions $request, $fields Inside Next step on an open request. Call $fields( 'my_action' ) inside your <form> to print the nonce and hidden fields, then handle it with rtkt_admin_request_action.
rtkt_admin_request_side $request, $fields Side cards on the admin request screen.
rtkt_fs_loaded none The Freemius SDK (or the built-in stub) is ready.

Template actions:

Action Arguments Where
rtkt_return_form_item_fields $line, $order, $old Inside each returnable item in the return form. $old is the posted input for that line after an error.
rtkt_request_view_after_instructions $request, $token On the customer's request page, after the return instructions. $token is the access token, or '' inside My Account.
rtkt_account_returns_after none At the end of My Account → Returns.
rtkt_email_update_after $request, $event, $plain In the update email, after the main text. $event is approved, received, completed, rejected or message.
// Post every new withdrawal to an internal system.
add_action( 'rtkt_request_created', function ( $request ) {
	if ( $request->is_withdrawal() ) {
		wp_remote_post( 'https://erp.example.com/withdrawals', array(
			'body' => array(
				'number'    => $request->number(),
				'order'     => $request->order_id(),
				'submitted' => $request->meta( 'submitted_at' ),
			),
		) );
	}
} );

The request object

Hooks pass ReturnKit\Domain\Request. Useful methods: id(), number(), type() (return, exchange, withdrawal), is_withdrawal(), status(), is_closed(), resolution() (refund, credit, exchange), order_id(), order(), customer_id(), email(), name(), note(), currency(), total(), refunded(), credited(), created_gmt(), flags(), has_flag(), items(), qty(), meta( $key ) and get( $column ).

For withdrawals, meta( 'statement' ) holds the frozen statement: name, email, order_number, scope (full or items), items (name and qty), details, note, submitted_at (Unix time) and submitted (ISO 8601, UTC).

Creating requests from code

Every request, from any form, goes through ReturnKit\Domain\Service::create(), which re-checks eligibility and quantities under a per-order lock:

use ReturnKit\Domain\Service;

$request = Service::create(
	$order,                                  // WC_Order
	'return',                                // return | exchange | withdrawal
	array( $item_id => array( 'qty' => 1, 'reason' => 'damaged-or-defective' ) ),
	array( 'source' => 'admin', 'note' => 'Reported by phone.' )
);
if ( is_wp_error( $request ) ) {
	// ->get_error_message() is customer-ready.
}

With source set to admin, eligibility, exclusions and the required reason are skipped (quantities are still checked), the history records a staff action, and the source shows as Created by staff. The usual emails are sent. The other sources are account, guest and form (the general withdrawal form).

Query parameters

Useful when building links. Links that show an order or a request need the secret part (rtkt_key or rtkt_token) unless the visitor is the logged-in customer.

Parameter Meaning
rtkt_order + rtkt_key An order and its WooCommerce order key: opens the return form (on the Returns page) or the withdrawal form (on the withdrawal page).
rtkt_type=withdrawal With rtkt_order on the Returns page or in My Account: open the withdrawal form instead.
rtkt_request + rtkt_token A request's page.
rtkt_claim + rtkt_token (Pro) In My Account: attach a guest request's store credit to the logged-in account.
rtkt_general=1 On the withdrawal page, skip the logged-in order picker and show the general form.

Order numbers

Wherever a customer or staff member types an order number (the guest look-up, the general withdrawal form, Link order, search), ReturnKit runs it through WooCommerce's woocommerce_shortcode_order_tracking_order_id filter, the same one WooCommerce's order tracking uses. Sequential order number plugins that support order tracking work without extra code.

Crash protection

Every hook ReturnKit registers goes through ReturnKit\Core\Safe:

Hook type When it throws
Action Does nothing.
Filter Returns its input unchanged.
Shortcode Shows a polite message (the withdrawal page asks the customer to email their withdrawal instead).
Admin screen Shows a notice instead of a critical-error page.

Caught errors are logged (ReturnKit caught an error … and let the request continue) and the latest one is stored in the rtkt_last_error option for a wp-admin notice, shown for 24 hours. Callbacks you add to rtkt_* hooks usually run inside one of these guards, so an exception in them is caught too, but the rest of that step may be skipped. Keep your callbacks defensive.

Because the hooks are registered through closures, remove_action() / remove_filter() can't target them by callback. Use the settings or filters instead.

Constants

Constant Where Effect
RETURNKIT_FS_PLUGIN_ID, RETURNKIT_FS_PUBLIC_KEY wp-config.php Override the Freemius product credentials (for staging or development).
RTKT_DEV_UNLOCK wp-config.php Unlocks Pro for local development, only when WP_DEBUG is also true and the Pro code is present. It has no effect in the free build.

The plugin defines RTKT_VERSION, RTKT_FILE, RTKT_DIR, RTKT_URL, RTKT_BASENAME, RTKT_DOCS_URL and RTKT_SUPPORT_URL.

Data the plugin stores

What Name
Settings option rtkt_settings
Versions options rtkt_version, rtkt_db_version, rtkt_pro_db_version (Pro)
Last caught error option rtkt_last_error
Requests table {prefix}rtkt_requests (below)
Request items table {prefix}rtkt_request_items (below)
History (audit log) table {prefix}rtkt_log: id, request_id, created_at (UTC), actor (customer, admin, system), user_id, event, message, data (JSON)
Store-credit ledger (Pro) table {prefix}rtkt_credit: id, email, user_id, amount, balance, type (refund, bonus, exchange, spent, restored, manual), request_id, order_id, note, created_at. Balances belong to user_id; rows with user_id = 0 are a guest's unclaimed credit, held by email until claimed.
Product rules product meta _rtkt_no_return, _rtkt_no_withdrawal (yes)
Exchange order link (Pro) order meta _rtkt_exchange_for (request ID)
Credit on an order (Pro) fee item meta _rtkt_credit; order meta _rtkt_credit_spent, _rtkt_credit_restored
Email settings options woocommerce_rtkt_{email id}_settings
"Save my credit for later" (Pro) user meta rtkt_credit_off
Rate limits transients rtkt_rl_{form}_{md5 of IP}, one hour
Return labels (Pro) wp-content/uploads/returnkit-labels/
Pages the Returns and withdrawal pages, IDs in the settings

rtkt_requests: id, order_id (0 = unmatched withdrawal), customer_id, email, name, type (return, exchange, withdrawal), status, resolution (refund, credit, exchange), note, source (account, guest, form, admin), currency, total, refunded, credited, exchange_order_id, flags (comma separated), ip, meta (JSON), created_at, updated_at (UTC).

rtkt_request_items: id, request_id, order_item_id, product_id, variation_id, name, qty, unit_value (incl. tax, after discounts), reason (key), exchange_product_id, exchange_name, exchange_price, received_qty, restocked_qty, refunded_qty.

Request meta (JSON): scope, submitted_at, secret (signs the request's links), statement (withdrawals), received (when items were marked received), refunds (WooCommerce refund IDs), label (Pro), anonymized.

Request flags: outside_window, unmatched, email_mismatch, and with Pro frequent and risky.

Log events: created, flagged, approved, received, completed, rejected, cancelled, refunded, refund_failed, restocked, note, message, email_sent, email_failed, email_off, linked, anonymized, and with Pro rule, credited, credit_claimed, exchange_order, exchange_paid, label.

Orders are referenced by ID, so HPOS and legacy order storage both work.

Code map

Folder Contents
includes/Core/ Plugin (wiring), Settings, Installer, Uninstaller, Privacy, Safe
includes/Domain/ Eligibility, Service (creating requests), Workflow (statuses, refunds, restocking), Request, Reasons, Labels
includes/Frontend/ Portal, Account, Withdrawal, Forms (form handling), Access (who may see what, and links), AntiSpam, Template, Assets
includes/Emails/ The five WooCommerce email classes and Emails (wiring and logging)
includes/Admin/ Admin (screens), Actions (form handlers, CSV), OrderBox, ProductFields
includes/Integration/ CoreWithdrawal: living alongside WooCommerce's own order withdrawal feature
includes/Store/ Requests, Log (custom tables)
includes/Freemius/ Gate (plan checks), Stub
includes/Pro/ Premium-only modules. Freemius strips this folder from the free build.
templates/ Customer-facing templates (overridable)
views/ Admin screen templates; views/pro/ is premium-only