Skip to content

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.

  • 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

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:

<?php
defined( '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 );
}
}
}

  1. Activate your plugin
  2. Add a product to cart
  3. Enable debug logging: Settings → Swft Checkout → Debug Logging → Yes
  4. Navigate to the WooCommerce checkout page
  5. Check the debug log — you should see the session creation call succeed
  6. In the Swft dashboard or via api.swft.co.uk, inspect the session — extensions.gifts should be present

You can also test directly via wp-cli:

Terminal window
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):

my-swft-gifts.js
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.


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 );

StepHookWhat it does
Session creationswft_session_extensionsAdds gifts data to the session payload
Checkout frontendswftcheckout:ready + SwftCheckout.addOrderRowRenders gift wrap fee in order summary
Post-paymentwoocommerce_checkout_order_createdReads _swft_ext_gifts from order meta
Cart drawerswftcart_cart_dataPasses 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.