Building a Module
This guide walks through building a complete Swft module — a standalone WordPress plugin that extends the checkout using the Extensions API. We will build a simplified version of SwftGifts: a gift message and gift wrap option that appears in the cart and checkout.
What we are building
Section titled “What we are building”- A settings field in WooCommerce where merchants configure the gift wrap price
- Cart-side: gift message textarea and gift wrap toggle in the Swft Cart drawer
- Checkout-side: a custom order row showing the gift wrap fee, and the message shown in the order summary
- Post-order: gift data written to the WooCommerce order
Step 1: Plugin boilerplate
Section titled “Step 1: Plugin boilerplate”Create /wp-content/plugins/my-swft-gifts/my-swft-gifts.php:
<?php/** * Plugin Name: My Swft Gifts * Description: Gift message and gift wrap for Swft Checkout. * Version: 1.0.0 * Requires at least: 6.0 * Requires PHP: 8.0 * Author: Your Name */
defined( 'ABSPATH' ) || exit;
define( 'MY_SWFT_GIFTS_VERSION', '1.0.0' );define( 'MY_SWFT_GIFTS_DIR', plugin_dir_path( __FILE__ ) );
add_action( 'plugins_loaded', function(): void { if ( ! class_exists( 'WooCommerce' ) ) { return; } require_once MY_SWFT_GIFTS_DIR . 'includes/class-gifts-hooks.php'; My_Swft_Gifts_Hooks::init();} );Step 2: Add the swft_session_extensions filter
Section titled “Step 2: Add the swft_session_extensions filter”Create /wp-content/plugins/my-swft-gifts/includes/class-gifts-hooks.php:
<?phpdefined( 'ABSPATH' ) || exit;
class My_Swft_Gifts_Hooks {
public static function init(): void { // Pass gift data into the Swft session add_filter( 'swft_session_extensions', [ self::class, 'add_gifts_extension' ], 10, 2 );
// Write gift data to WooCommerce order after payment add_action( 'woocommerce_checkout_order_created', [ self::class, 'write_order_meta' ] ); }
/** * Add gift options to the Swft session extensions. * This data flows: WP → API → KV → window.__SWFT_SESSION__.extensions.gifts * and then to WooCommerce order meta as _swft_ext_gifts. */ public static function add_gifts_extension( array $extensions, WC_Cart $cart ): array { $session = WC()->session;
$message = sanitize_textarea_field( $session->get( 'gift_message', '' ) ); $wrap = (bool) $session->get( 'gift_wrap', false ); $wrap_fee = (int) get_option( 'my_swft_gifts_wrap_price', 299 ); // pence
$extensions['gifts'] = [ 'message' => $message, 'wrap' => $wrap, 'wrap_fee' => $wrap, // only charge if wrap is selected 'price' => $wrap ? $wrap_fee : 0, ];
return $extensions; }
/** * When WooCommerce creates an order (triggered by Swft's WC sync), * the extension data is already in _swft_ext_gifts. Here we also * set a human-readable order note. */ public static function write_order_meta( WC_Order $order ): void { $ext = $order->get_meta( '_swft_ext_gifts', true ); if ( ! $ext ) { return; } $data = is_string( $ext ) ? json_decode( $ext, true ) : (array) $ext;
if ( ! empty( $data['message'] ) ) { $order->add_order_note( 'Gift message: ' . esc_html( $data['message'] ), false // not customer-facing ); }
if ( ! empty( $data['wrap'] ) ) { $order->add_order_note( 'Gift wrap requested.', false ); } }}Step 3: Test that data flows through
Section titled “Step 3: Test that data flows through”- Activate your plugin
- Add a product to cart
- Enable debug logging: Settings → Swft Checkout → Debug Logging → Yes
- Navigate to the WooCommerce checkout page
- Check the debug log — you should see the session creation call succeed
- In the Swft dashboard or via
api.swft.co.uk, inspect the session —extensions.giftsshould be present
You can also test directly via wp-cli:
wp eval 'WC()->session->set("gift_message", "Happy birthday!"); WC()->session->set("gift_wrap", true); echo json_encode((new Swft_Cart())->build_payload()["extensions"]);'Step 4: Listen for swftcheckout:ready on the frontend
Section titled “Step 4: Listen for swftcheckout:ready on the frontend”Add a JavaScript file that your plugin enqueues on all pages (or just the checkout page via a URL check):
document.addEventListener('swftcheckout:ready', function() { const gifts = window.SwftCheckout.getExtension('gifts') if (!gifts) return
// If gift wrap was selected, add a fee row to the order summary if (gifts.wrap && gifts.price > 0) { const formatted = new Intl.NumberFormat('en-GB', { style: 'currency', currency: window.__SWFT_SESSION__.cart.currency, }).format(gifts.price / 100)
window.SwftCheckout.addOrderRow({ id: 'gift-wrap-fee', label: 'Gift wrap', value: formatted, pos: 'after-subtotal', }) }
// If there is a gift message, show it as a custom field (read-only display) if (gifts.message) { window.SwftCheckout.addOrderRow({ id: 'gift-message-display', label: 'Gift message', value: gifts.message, pos: 'after-subtotal', }) }})Enqueue it:
add_action( 'wp_enqueue_scripts', function(): void { wp_enqueue_script( 'my-swft-gifts-frontend', plugin_dir_url( MY_SWFT_GIFTS_DIR . 'my-swft-gifts.php' ) . 'assets/my-swft-gifts.js', [], MY_SWFT_GIFTS_VERSION, true );} );Because the script is enqueued on the WordPress side and the checkout runs on checkout.swft.co.uk, this script will not run on the checkout domain. For frontend checkout behaviour, the correct approach is to pass all necessary data via swft_session_extensions and use addOrderRow / addCustomField from a script loaded via the Swft Cart drawer (which runs on the WooCommerce store domain).
If you need JavaScript to run on the checkout frontend itself, contact Swft about the hosted script injection feature.
Step 5: Handle cart-side UI
Section titled “Step 5: Handle cart-side UI”If you want a gift message field and wrap toggle in the Swft Cart drawer, hook into swftcart_cart_data to add the fields as module configuration:
add_filter( 'swftcart_cart_data', function( array $cart_data ): array { $cart_data['gift_options'] = [ 'enabled' => true, 'wrap_price' => (int) get_option( 'my_swft_gifts_wrap_price', 299 ), 'currency' => get_woocommerce_currency(), 'message' => WC()->session->get( 'gift_message', '' ) ?? '', 'wrap' => (bool) ( WC()->session->get( 'gift_wrap', false ) ?? false ), ]; return $cart_data;} );The Swft Cart frontend reads cart_data.gift_options and renders the UI if your module is registered with the Swft Cart module system. See Building a Swft Cart Module for the cart module registration API.
Step 6: Handle post-order in woocommerce_checkout_order_created
Section titled “Step 6: Handle post-order in woocommerce_checkout_order_created”The woocommerce_checkout_order_created hook fires when Swft syncs the order to WooCommerce via the REST API. By this point, all extension data is already written to order meta as _swft_ext_{key}.
The hook in Step 2 already handles this. In more complex modules, you might:
- Send a confirmation email with gift-specific content
- Trigger a fulfilment system
- Update a loyalty points balance
add_action( 'woocommerce_checkout_order_created', function( WC_Order $order ): void { $gifts_raw = $order->get_meta( '_swft_ext_gifts', true ); if ( ! $gifts_raw ) return;
$gifts = is_string( $gifts_raw ) ? json_decode( $gifts_raw, true ) : (array) $gifts_raw;
if ( ! empty( $gifts['wrap'] ) ) { // Notify packing team wp_mail( 'Gift wrap required — Order #' . $order->get_order_number(), 'Gift message: ' . esc_html( $gifts['message'] ?? '(none)' ) ); }}, 10, 1 );Summary
Section titled “Summary”| Step | Hook | What it does |
|---|---|---|
| Session creation | swft_session_extensions | Adds gifts data to the session payload |
| Checkout frontend | swftcheckout:ready + SwftCheckout.addOrderRow | Renders gift wrap fee in order summary |
| Post-payment | woocommerce_checkout_order_created | Reads _swft_ext_gifts from order meta |
| Cart drawer | swftcart_cart_data | Passes gift option state to cart UI |
The key insight: all extension data passes through the Swft session as a JSON blob. The WordPress plugin writes it in, the checkout frontend reads it out, and the WooCommerce order stores it permanently. Your module never needs to store anything separately — the session is the source of truth.