Integrations / WooCommerce

WooCommerce integration guide

WooCommerce doesn’t know anything about Serian ShipKit out of the box — but because it’s a WordPress plugin, you can call our API from a tiny companion plugin or from a code snippet in functions.php. We don’t currently ship an official plugin; this guide shows you the minimum code to roll your own.

1. Get an API key

In your Serian ShipKit dashboard, go to API Keys and create one. Start with a sk_test_key while you’re developing — it never charges you. Copy it into your WordPress secrets (wp-config.php, Vault, or a secrets manager).

2. Add a shipping method that asks us for rates

Drop this into a small MU-plugin (wp-content/mu-plugins/serian.php). It registers a new shipping method that hits POST /api/v1/rates and returns the quotes to WooCommerce at checkout.

php
<?php
/*
 * Plugin Name: Serian ShipKit Shipping
 */
add_action('woocommerce_shipping_init', function () {
  class WC_Shipping_Serian extends WC_Shipping_Method {
    public function __construct() {
      $this->id = 'serian';
      $this->method_title = 'Serian ShipKit';
      $this->init();
    }

    public function init() {
      $this->init_form_fields();
      $this->init_settings();
      $this->title = $this->get_option('title', 'Serian ShipKit');
    }

    public function calculate_shipping($package = []) {
      $body = [
        'shipper' => [
          'name' => get_bloginfo('name'),
          'street1' => get_option('serian_ship_from_street1'),
          'city' => get_option('serian_ship_from_city'),
          'state' => get_option('serian_ship_from_state'),
          'zip' => get_option('serian_ship_from_zip'),
          'country' => get_option('serian_ship_from_country', 'GH'),
        ],
        'recipient' => [
          'name' => $package['destination']['first_name'] ?? 'Recipient',
          'street1' => $package['destination']['address_1'],
          'city' => $package['destination']['city'],
          'state' => $package['destination']['state'],
          'zip' => $package['destination']['postcode'],
          'country' => $package['destination']['country'],
        ],
        'parcels' => [[
          'length' => 20, 'width' => 15, 'height' => 10,
          'weight' => 1, 'dimension_unit' => 'CM', 'weight_unit' => 'KG',
        ]],
      ];

      $res = wp_remote_post('https://api.serianshipkit.com/api/v1/rates', [
        'headers' => [
          'Authorization' => 'Bearer ' . SERIAN_API_KEY,
          'Content-Type' => 'application/json',
        ],
        'body' => wp_json_encode($body),
        'timeout' => 10,
      ]);

      if (is_wp_error($res)) return;
      $data = json_decode(wp_remote_retrieve_body($res), true);
      foreach (($data['rates'] ?? []) as $rate) {
        $this->add_rate([
          'id' => 'serian_' . $rate['service_type'],
          'label' => $rate['service_name'],
          'cost' => $rate['total_charge'],
          'meta_data' => [ 'serian_rate_id' => $rate['rate_id'] ],
        ]);
      }
    }
  }
});

add_filter('woocommerce_shipping_methods', fn($m) => $m + ['serian' => 'WC_Shipping_Serian']);

3. Create the label when the order is paid

Hook into woocommerce_order_status_processing (or woocommerce_payment_complete) and call POST /api/v1/shipments using the rate_idwe stored in the shipping item’s meta:

php
add_action('woocommerce_order_status_processing', function ($order_id) {
  $order = wc_get_order($order_id);
  foreach ($order->get_shipping_methods() as $item) {
    $rate_id = $item->get_meta('serian_rate_id');
    if (!$rate_id) continue;

    $res = wp_remote_post('https://api.serianshipkit.com/api/v1/shipments', [
      'headers' => [
        'Authorization' => 'Bearer ' . SERIAN_API_KEY,
        'Content-Type' => 'application/json',
        'Idempotency-Key' => 'order-' . $order_id,
      ],
      'body' => wp_json_encode([
        'rate_id' => $rate_id,
        'reference' => 'WC-' . $order_id,
      ]),
      'timeout' => 20,
    ]);

    $data = json_decode(wp_remote_retrieve_body($res), true);
    $order->update_meta_data('_serian_tracking', $data['tracking_number']);
    $order->update_meta_data('_serian_label_url', $data['label_url']);
    $order->save();
  }
});

4. Get tracking updates back into WooCommerce

Register a webhook endpoint in your Serian ShipKit dashboard pointing at https://yourstore.com/?rest_route=/serian/v1/webhook, subscribed to tracking.updated and shipment.delivered. Verify the X-Webhook-Signature header (HMAC-SHA256 of the raw body, keyed with the webhook secret), then update the order status:

php
add_action('rest_api_init', function () {
  register_rest_route('serian/v1', '/webhook', [
    'methods' => 'POST',
    'permission_callback' => '__return_true',
    'callback' => function (WP_REST_Request $req) {
      $raw = $req->get_body();
      $sig = $req->get_header('X-Webhook-Signature');
      $expected = hash_hmac('sha256', $raw, SERIAN_WEBHOOK_SECRET);
      if (!hash_equals($expected, $sig)) return new WP_Error('bad_sig', '', ['status' => 401]);

      $event = json_decode($raw, true);
      if ($event['type'] === 'shipment.delivered') {
        $orders = wc_get_orders([
          'meta_key' => '_serian_tracking',
          'meta_value' => $event['data']['tracking_number'],
        ]);
        foreach ($orders as $o) $o->update_status('completed', 'Delivered by carrier');
      }
      return ['ok' => true];
    },
  ]);
});

Troubleshooting

  • Rates never appear at checkout:check WordPress’s error log. Our API returns JSON errors with a request_id you can share with support.
  • Labels get duplicated: always pass Idempotency-Key (we used order-{id} above).
  • Test safely: use a sk_test_ key — all labels and tracking events are simulated and never cost anything.