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 |