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_callbackna 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.



