Jak zbudować własne API w WooCommerce?

6 minut
How do I create an API in WooCommerce

Zanim napiszesz linijkę kodu, sprawdź, czy musisz. WooCommerce ma pełne REST API obejmujące produkty, zamówienia, klientów, kupony i raporty, wraz z zarządzaniem kluczami w panelu administracyjnym. Do podpięcia CRM-u, przewoźnika czy systemu magazynowego to zwykle cała odpowiedź. Dla bardziej złożonej granicy integracji zobacz, jakie API są niezbędne w integracji oprogramowania iGaming. Własny endpoint warto zbudować wtedy, gdy potrzebujesz logiki biznesowej, której wbudowane API nie wyraża, odpowiedzi przygotowanej pod konkretnego odbiorcę albo agregacji, która inaczej wymagałaby kilku zapytań. Poniżej obie ścieżki: kiedy wystarczy to, co już jest, i jak zbudować własne API, które przejdzie przegląd bezpieczeństwa.

Zacznij od wbudowanego API

WooCommerce udostępnia REST API pod adresem /wp-json/wc/v3/. Klucze generuje się w panelu, w sekcji WooCommerce → Ustawienia → Zaawansowane → REST API, przypisane do konkretnego użytkownika, z uprawnieniem do odczytu albo odczytu i zapisu.

Dwa wymagania bywają przeoczone: bezpośrednie odnośniki muszą być ustawione na cokolwiek innego niż domyślne, a połączenie musi działać po HTTPS.

Co już jest obsłużone:

  • produkty, warianty, kategorie, atrybuty
  • zamówienia, zwroty, notatki do zamówień
  • klienci, kupony, raporty
  • webhooki do integracji zdarzeniowych

Jeśli Twoja integracja da się wyrazić w tych pojęciach, na tym można poprzestać. Własny endpoint to endpoint, który utrzymujesz, zabezpieczasz i dokumentujesz przez cały okres życia sklepu.

Kiedy własny endpoint się opłaca

Warto go napisać, gdy:

  • liczy się kształt odpowiedzi. Aplikacja mobilna potrzebująca ośmiu pól nie powinna pobierać pełnych danych produktu przy każdym żądaniu.
  • między danymi a odpowiedzią leży logika biznesowa. Ceny progowe dla grup klientów, dostępność liczona z wielu magazynów, weryfikacja uprawnień.
  • agregujesz. Jedno żądanie zamiast czterech.
  • kontrakt narzuca system zewnętrzny i musisz się do niego dostosować, a nie go definiować.

Rejestracja endpointu

Kod trzymaj we własnej wtyczce, nie w motywie. Zmiana motywu nie powinna nigdy wyłączać integracji.

php

<?php
/**
 * Plugin Name: Shop API
 * Description: Własne endpointy REST dla sklepu.
 * Version:     1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

add_action( 'rest_api_init', function () {
    register_rest_route( 'my-shop/v1', '/products/featured', array(
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => 'shop_api_get_featured_products',
        'permission_callback' => 'shop_api_permission_check',
        'args'                => array(
            'per_page' => array(
                'type'              => 'integer',
                'default'           => 10,
                'minimum'           => 1,
                'maximum'           => 100,
                'sanitize_callback' => 'absint',
            ),
            'page'     => array(
                'type'              => 'integer',
                'default'           => 1,
                'minimum'           => 1,
                'sanitize_callback' => 'absint',
            ),
        ),
    ) );
} );

Dwie rzeczy są tu istotne.

Schemat args wykonuje walidację za Ciebie. Zadeklarowanie typów, zakresów i funkcji oczyszczających sprawia, że WordPress odrzuca błędne dane, zanim uruchomi Twoją funkcję, i zwraca poprawnie sformułowany błąd. Ręcznie pisane warunki wewnątrz funkcji to miejsce, w którym powstają luki w walidacji.

permission_callback nigdy nie jest opcjonalne i nigdy nie zwraca true. WordPress zgłasza ostrzeżenie, gdy go brakuje, a endpoint zwracający true jest endpointem otwartym. Jeśli kusi Cię ustawienie true „na razie”, ustaw __return_false i doprowadź uwierzytelnianie do działania, zanim zajmiesz się resztą.

Uwierzytelnianie

Do komunikacji między serwerami korzystaj z mechanizmów, które WordPress i WooCommerce już mają: kluczy API WooCommerce albo haseł aplikacji WordPressa dla własnych przestrzeni nazw. Jedne i drugie są zarządzane w panelu, odwoływalne i powiązane z użytkownikiem, którego uprawnienia kontrolujesz.

Tam, gdzie wspólny sekret jest faktycznie właściwym rozwiązaniem, obowiązują trzy zasady.

php

function shop_api_permission_check( WP_REST_Request $request ) {

    // Zalogowany użytkownik WordPressa z odpowiednim uprawnieniem.
    if ( current_user_can( 'manage_woocommerce' ) ) {
        return true;
    }

    // Wspólny sekret do komunikacji między serwerami. Zdefiniowany w wp-config.php
    // albo w zmiennej środowiskowej, nigdy w tym pliku i nigdy w repozytorium.
    $expected = defined( 'SHOP_API_SECRET' ) ? SHOP_API_SECRET : '';
    $provided = $request->get_header( 'x-shop-api-secret' );

    if ( '' !== $expected && is_string( $provided ) && hash_equals( $expected, $provided ) ) {
        return true;
    }

    return new WP_Error(
        'shop_api_forbidden',
        __( 'Wymagane uwierzytelnienie.', 'shop-api' ),
        array( 'status' => rest_authorization_required_code() )
    );
}

Sekret mieszka poza kodem. W pliku wp-config.php albo w zmiennej środowiskowej. Dane uwierzytelniające wpisane do pliku wtyczki trafiają do repozytorium, do każdej kopii zapasowej i do każdej kopii serwisu, jaką ktokolwiek kiedykolwiek wykonał.

Porównuj przez hash_equals(), nie przez !==. Zwykłe porównanie łańcuchów kończy się w chwili napotkania pierwszej różnicy, a ta różnica czasowa jest mierzalna przy odpowiedniej liczbie żądań. hash_equals() porównuje w stałym czasie. To najczęściej pomijana linijka w kodzie API dla WordPressa.

Nagłówki czytaj przez $request->get_header(). Funkcja normalizuje nazwy nagłówków i trzyma Cię z dala od $_SERVER.

Zwróć uwagę na kolejność sprawdzeń. Samo sprawdzenie uprawnień nie uwierzytelni komunikacji między serwerami, bo w takim żądaniu nie ma zalogowanego użytkownika. Uwierzytelnianie i autoryzacja to dwa osobne kroki: najpierw ustalasz, kto dzwoni, potem co mu wolno.

Poza tym: wyłącznie HTTPS, najwęższe uprawnienie, które wystarcza, ograniczanie liczby żądań oraz logowanie nieudanych prób uwierzytelnienia.

Funkcja obsługująca żądanie

php

function shop_api_get_featured_products( WP_REST_Request $request ) {

    $products = wc_get_products( array(
        'status'   => 'publish',
        'featured' => true,
        'limit'    => $request['per_page'],
        'page'     => $request['page'],
    ) );

    $data = array();

    foreach ( $products as $product ) {
        $data[] = array(
            'id'            => $product->get_id(),
            'name'          => $product->get_name(),
            'price'         => wc_format_decimal( $product->get_price() ),
            'regular_price' => wc_format_decimal( $product->get_regular_price() ),
            'sale_price'    => wc_format_decimal( $product->get_sale_price() ),
            'image'         => wp_get_attachment_url( $product->get_image_id() ),
            'permalink'     => get_permalink( $product->get_id() ),
        );
    }

    return rest_ensure_response( array( 'products' => $data ) );
}

Ponieważ schemat już zwalidował i oczyścił parametry, funkcja pozostaje czytelna. wc_format_decimal() utrzymuje spójny format cen niezależnie od tego, co konsumuje odpowiedź.

Jeśli endpoint dotyka zamówień, korzystaj z warstwy CRUD. wc_get_order() i wc_get_orders(), nigdy get_post() ani get_post_meta() na identyfikatorach zamówień. Przy włączonym wydajnym przechowywaniu zamówień dane nie leżą już w tabelach wpisów, więc kod odczytujący je bezpośrednio zwraca pustkę albo dane nieaktualne. Pełny obraz tego, co i gdzie leży, znajdziesz w tekście gdzie WooCommerce przechowuje dane.

Dane osobowe

Endpoint zwracający dane klientów albo zamówień wyprowadza dane osobowe poza sklep. To wprowadza do gry RODO: podstawę prawną przekazania, umowę powierzenia z odbiorcą i ustalenie, jak długo odbiorca te dane przechowuje.

W praktyce przekłada się to na projekt. Zwracaj minimum tego, czego odbiorca faktycznie potrzebuje, trzymaj endpointy z danymi klientów w osobnej przestrzeni nazw z węższym sprawdzeniem uprawnień i loguj dostęp.

Cache

Cache’uj dane, nie obiekt odpowiedzi, i wyłącznie te endpointy, które zwracają to samo każdemu odpytującemu.

php

function shop_api_get_featured_products( WP_REST_Request $request ) {

    $cache_key = 'shop_api_featured_' . md5( wp_json_encode( $request->get_params() ) );
    $data      = get_transient( $cache_key );

    if ( false === $data ) {
        $data = shop_api_build_featured_payload( $request );
        set_transient( $cache_key, $data, HOUR_IN_SECONDS );
    }

    return rest_ensure_response( $data );
}

Nigdy nie cache’uj odpowiedzi zależnej od odpytującego pod kluczem, który go nie uwzględnia. Klucz oparty wyłącznie na parametrach żądania poda dane jednego klienta drugiemu w chwili, gdy zastosujesz go do endpointu wymagającego uwierzytelnienia.

CORS

Jeśli Twój endpoint wywołuje aplikacja przeglądarkowa z innej domeny, potrzebujesz nagłówków CORS. Ogranicz je do swojej przestrzeni nazw i do znanych źródeł.

php

add_filter( 'rest_pre_serve_request', function ( $served, $result, $request ) {

    if ( 0 !== strpos( $request->get_route(), '/my-shop/v1' ) ) {
        return $served;
    }

    $allowed = array( 'https://app.example.com' );
    $origin  = get_http_origin();

    if ( $origin && in_array( $origin, $allowed, true ) ) {
        header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
        header( 'Vary: Origin' );
    }

    return $served;
}, 10, 3 );

Dwóch rzeczy należy unikać. Nie wysyłaj Access-Control-Allow-Origin: * razem z Access-Control-Allow-Credentials: true. Przeglądarki odrzucają tę kombinację, a intencja, która za nią stoi, jest dokładnie tym, przed czym CORS ma chronić. Nie usuwaj globalnie własnego filtru CORS WordPressa, żeby rozwiązać problem na jednej trasie. To zmienia zachowanie całego REST API.

Testowanie

Postman albo Insomnia, na środowisku testowym, nigdy na produkcji.

Testuj ścieżki błędu, nie tylko tę działającą: brak sekretu, błędny sekret, parametry poza zakresem, nieznana trasa. Sprawdź, czy kody odpowiedzi są takie, na których odbiorca może się oprzeć.

  • 200 sukces
  • 400 błędne żądanie, nieprawidłowe parametry
  • 401 brak uwierzytelnienia
  • 403 uwierzytelniony, ale bez uprawnień
  • 404 nie znaleziono
  • 429 przekroczony limit żądań
  • 500 błąd serwera

Query Monitor pokazuje, ile zapytań generuje każde żądanie. Endpoint, który wygląda szybko przy dziesięciu produktach, przy dziesięciu tysiącach często już nie jest.

Zanim trafi na produkcję

  • sekret przechowywany poza kodem, wymienialny bez wdrożenia
  • permission_callback na każdej trasie, sprawdzone żądaniem, które ma się nie udać
  • wymuszone HTTPS
  • ograniczanie liczby żądań
  • logowanie i monitoring nieudanych prób uwierzytelnienia
  • wersjonowana przestrzeń nazw, żeby dało się zmienić kontrakt bez psucia odbiorców
  • spisana dokumentacja, choćby jednostronicowa: trasy, parametry, kody błędów

API nie jest zadaniem jednorazowym. To kontrakt, który utrzymujesz tak długo, jak długo coś od niego zależy.

Jeśli budujesz integracje przenoszące dane zamówień albo klientów, audyt bezpieczeństwa przed uruchomieniem kosztuje nieporównanie mniej niż incydent po nim.

Pwel Zmyslowski

Paweł Zmysłowski

CEO WLC.team

W WLC odpowiada za proces sprzedaży i zespół sprzedaży, a w przypadku bardziej złożonych projektów pełnię również rolę analityczną i doradczą.

Strona autora

Czy Twój WordPress „działa, ale jest wolny”?

WIĘCEJ ARTYKUŁÓW

Czytaj także

  • Full Site Editing and design systems in WordPress
    6 minut

    Full Site Editing i design system w WordPressie – jak działa

    Redakcja chce opublikować landing pod kampanię w czwartek. Projekt graficzny jest gotowy, treść jest gotowa, a zmiana i tak trafia do kolejki zespołu technicznego. To wzorzec, który widzimy w większości serwisów zbudowanych przed 2022 rokiem, niezależnie od tego, jak dobry jest zespół po obu stronach. WordPress rozwiązał ten problem na poziomie platformy. Nazywało się to…

    Czytaj

  • AI Search and WordPress How to prepare a large-scale platform for generative search
    13 minut

    AI Search a WordPress: jak przygotować dużą platformę do wyszukiwania generatywnego

    Duże platformy WordPress nie wypadają z odpowiedzi generowanych przez AI dlatego, że mają słabą treść. Wypadają, bo przez lata rozwoju nikt nie odpowiadał za architekturę informacji, a dostęp crawlerów bywa zablokowany na poziomie, którego nie widać w panelu WordPressa. Ten artykuł pokazuje, co da się w tym kontrolować, co jest udokumentowane, a co pozostaje hipotezą.…

    Czytaj

  • WordPress for Education in 2026
    10 minut

    WordPress dla edukacji w 2026 roku: architektura, narzędzia i decyzje, które zadecydują o sukcesie platformy

    WordPress obsługuje ponad 40% stron internetowych na świecie. W segmencie edukacyjnym ta dominacja jest jeszcze wyraźniejsza – platforma stała się de facto standardem dla instytucji, które chcą połączyć stronę szkoły z pełnoprawnym systemem zarządzania kursami bez kosztów licencyjnych rosnących wraz z bazą uczniów. Ale popularność nie oznacza automatycznego sukcesu. WordPress dla edukacji to nie instalacja…

    Czytaj