Skip to content

KSeF - National e-Invoice System

KSeF is a Polish Ministry of Finance platform for structured invoices. The plugin prepares your store for KSeF integration - it detects orders requiring a VAT invoice, adds a status column and hooks for integration with invoicing systems.

KSeF is in the implementation phase. The plugin does not issue invoices in KSeF, but makes integration easier with systems that do (e.g. Fakturownia, iFirma, wFirma, InFakt).

Main KSeF module features:

  1. Automatic detection of orders with a NIP number
  2. KSeF status column on the orders list
  3. Hooks for integration with external invoicing systems
  4. Order meta data ready for passing to the KSeF system

When a customer provides a NIP during order placement (the NIP field is part of the Checkout module), the plugin automatically:

  1. Validates the NIP format (10 digits, checksum verification)
  2. Marks the order as requiring a VAT invoice
  3. Saves the NIP in order meta data
  4. Optionally retrieves company data from the GUS/CEIDG API

The plugin checks NIP correctness at two levels:

  • Format - 10 digits, correct checksum (weights: 6, 5, 7, 2, 3, 4, 5, 6, 7)
  • Online verification - optional check in the VIES database (for EU NIPs) or GUS API

On the orders list (WooCommerce > Orders) a KSeF column appears with status icons:

IconStatusDescription
GrayNot applicableOrder without NIP, invoice not required
BluePendingOrder with NIP, invoice to be issued
GreenIssuedInvoice has been issued (status set by hook)
RedErrorA problem occurred with issuing the invoice

You can filter orders by KSeF status, e.g. display only those pending an invoice.

On the orders list you can bulk-mark multiple orders as “issued in KSeF”.

Triggered when an order with NIP is paid and ready for invoice issuance. The main hook for integration with invoicing systems.

/**
* @param int $order_id WooCommerce order ID.
* @param WC_Order $order Order object.
* @param string $nip Customer NIP number.
* @param array $invoice_data Invoice data (company name, address, NIP).
*/
add_action('polski/ksef/invoice_ready', function (int $order_id, WC_Order $order, string $nip, array $invoice_data): void {
// Example: send data to the Fakturownia API
$api_token = get_option('fakturownia_api_token');
$account = get_option('fakturownia_account');
$invoice_payload = [
'invoice' => [
'kind' => 'vat',
'number' => null, // auto-numbering
'sell_date' => $order->get_date_paid()->format('Y-m-d'),
'issue_date' => current_time('Y-m-d'),
'payment_type' => 'transfer',
'seller_name' => get_option('woocommerce_store_name'),
'buyer_name' => $invoice_data['company_name'],
'buyer_tax_no' => $nip,
'buyer_street' => $invoice_data['address'],
'buyer_city' => $invoice_data['city'],
'buyer_post_code' => $invoice_data['postcode'],
'positions' => [],
],
];
foreach ($order->get_items() as $item) {
$invoice_payload['invoice']['positions'][] = [
'name' => $item->get_name(),
'quantity' => $item->get_quantity(),
'total_price_gross' => $item->get_total() + $item->get_total_tax(),
'tax' => round(($item->get_total_tax() / $item->get_total()) * 100),
];
}
$response = wp_remote_post("https://{$account}.fakturownia.pl/invoices.json", [
'body' => wp_json_encode($invoice_payload),
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Token token=' . $api_token,
],
]);
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 201) {
$body = json_decode(wp_remote_retrieve_body($response), true);
update_post_meta($order_id, '_ksef_status', 'issued');
update_post_meta($order_id, '_ksef_invoice_id', $body['id'] ?? '');
} else {
update_post_meta($order_id, '_ksef_status', 'error');
}
}, 10, 4);

Filter allowing you to programmatically determine whether an order requires a KSeF invoice.

/**
* @param bool $is_required Whether a KSeF invoice is required.
* @param WC_Order $order Order object.
* @return bool
*/
add_filter('polski/ksef/is_required', function (bool $is_required, WC_Order $order): bool {
// Example: require KSeF invoice for orders above 450 PLN
if ($order->get_total() > 450) {
return true;
}
return $is_required;
}, 10, 2);

Example - automatic status marking after integration

Section titled “Example - automatic status marking after integration”
/**
* Update KSeF status after receiving a response from the invoicing system.
*/
add_action('my_invoicing/invoice_created', function (int $order_id, string $ksef_number): void {
$order = wc_get_order($order_id);
if (!$order) {
return;
}
$order->update_meta_data('_ksef_status', 'issued');
$order->update_meta_data('_ksef_number', $ksef_number);
$order->add_order_note(
sprintf('Invoice issued in KSeF. KSeF number: %s', $ksef_number)
);
$order->save();
}, 10, 2);

The KSeF module saves the following meta data in orders:

Meta keyDescription
_billing_nipCustomer NIP number
_billing_companyCompany name
_ksef_requiredWhether the order requires an invoice (yes/no)
_ksef_statusInvoice status (pending, issued, error)
_ksef_numberKSeF invoice number (after issuance)
_ksef_invoice_idInvoice ID in the external system

KSeF module settings: WooCommerce > Settings > Polski > KSeF.

OptionDescriptionDefault value
Enable KSeF moduleActivates detection and trackingYes
Online NIP validationCheck NIP in GUS/VIES APINo
Auto-fetch company dataFetch data from GUS after entering NIPNo
Hook trigger statusOrder status at which to trigger invoice_readyprocessing

KSeF column does not display on the orders list Click “Screen Options” and check the KSeF column. Make sure the module is enabled in settings.

NIP is not saved in the order Check that the NIP field is enabled in WooCommerce > Settings > Polski > Checkout. The field must be active for customers to fill it in.

The invoice_ready hook is not triggered Check the “Hook trigger status” setting. By default the hook fires at “Processing” status. For custom statuses, change this option.

This page is for informational purposes only and does not constitute legal advice. Consult a lawyer before implementation. Polski for WooCommerce is open source software (GPLv2) provided without warranty.