update
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Widget: Carei Reservation form in modal.
|
||||
*/
|
||||
class Carei_Reservation_Widget extends \Elementor\Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'carei-reservation';
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return 'Carei Reservation';
|
||||
}
|
||||
|
||||
public function get_icon() {
|
||||
return 'eicon-form-horizontal';
|
||||
}
|
||||
|
||||
public function get_categories() {
|
||||
return array( 'general' );
|
||||
}
|
||||
|
||||
public function get_style_depends() {
|
||||
return array( 'carei-reservation-css' );
|
||||
}
|
||||
|
||||
public function get_script_depends() {
|
||||
return array( 'carei-reservation-js' );
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
$this->start_controls_section( 'content_section', array(
|
||||
'label' => 'Przycisk rezerwacji',
|
||||
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
|
||||
) );
|
||||
|
||||
$this->add_control( 'button_text', array(
|
||||
'label' => 'Tekst przycisku',
|
||||
'type' => \Elementor\Controls_Manager::TEXT,
|
||||
'default' => 'Złóż zapytanie o rezerwację',
|
||||
) );
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
$button_text = esc_html( $settings['button_text'] );
|
||||
?>
|
||||
<button type="button" class="carei-reservation-trigger" data-carei-open-modal>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||
<?php echo $button_text; ?>
|
||||
</button>
|
||||
|
||||
<div class="carei-modal-overlay" data-carei-modal>
|
||||
<div class="carei-modal">
|
||||
<button type="button" class="carei-modal-close" data-carei-close-modal>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<h2 class="carei-modal-title">Wypełnij formularz rezerwacji<span>.</span></h2>
|
||||
|
||||
<form class="carei-form" id="carei-reservation-form" novalidate>
|
||||
|
||||
<!-- Dane wynajmu -->
|
||||
<div class="carei-form__section">
|
||||
<div class="carei-form__field carei-form__field--full">
|
||||
<div class="carei-form__select-wrap">
|
||||
<select id="carei-segment" name="segment" required>
|
||||
<option value="" disabled selected>Wybierz segment pojazdu</option>
|
||||
</select>
|
||||
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="carei-form__row carei-form__row--dates">
|
||||
<div class="carei-form__field">
|
||||
<label class="carei-form__label-small" for="carei-date-from">Od kiedy?</label>
|
||||
<input type="datetime-local" id="carei-date-from" name="dateFrom" class="carei-form__input" required>
|
||||
</div>
|
||||
<div class="carei-form__field">
|
||||
<label class="carei-form__label-small" for="carei-date-to">
|
||||
<svg class="carei-form__icon-calendar" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="3" width="12" height="11" rx="1" stroke="currentColor" stroke-width="1.5"/><path d="M2 6h12M5 1v3M11 1v3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||
Do kiedy?
|
||||
</label>
|
||||
<input type="datetime-local" id="carei-date-to" name="dateTo" class="carei-form__input" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="carei-form__days-count" id="carei-days-count">Wybrano: <strong>0 dni</strong></div>
|
||||
|
||||
<div class="carei-form__field carei-form__field--full">
|
||||
<div class="carei-form__select-wrap carei-form__select-wrap--icon">
|
||||
<svg class="carei-form__icon-pin" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
||||
<select id="carei-pickup-branch" name="pickupBranch" required>
|
||||
<option value="" disabled selected>Miejsce odbioru</option>
|
||||
</select>
|
||||
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="carei-form__checkbox-label">
|
||||
<input type="checkbox" id="carei-same-return" name="sameReturn" checked>
|
||||
<span class="carei-form__checkbox-box">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</span>
|
||||
<span class="carei-form__checkbox-text">Zwrot w tej samej lokalizacji</span>
|
||||
</label>
|
||||
|
||||
<div class="carei-form__field carei-form__field--full carei-form__return-wrap" id="carei-return-wrap" style="display:none;">
|
||||
<div class="carei-form__select-wrap carei-form__select-wrap--icon">
|
||||
<svg class="carei-form__icon-pin" width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 1C5.24 1 3 3.24 3 6c0 3.75 5 9 5 9s5-5.25 5-9c0-2.76-2.24-5-5-5zm0 7a2 2 0 110-4 2 2 0 010 4z" fill="currentColor"/></svg>
|
||||
<select id="carei-return-branch" name="returnBranch">
|
||||
<option value="" disabled selected>Miejsce zwrotu</option>
|
||||
</select>
|
||||
<svg class="carei-form__select-arrow" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opcje dodatkowe -->
|
||||
<div class="carei-form__divider"><span>Opcje dodatkowe</span></div>
|
||||
|
||||
<div class="carei-form__section">
|
||||
<div class="carei-form__row" id="carei-extras-container">
|
||||
<div class="carei-form__extra-card">
|
||||
<label class="carei-form__checkbox-label carei-form__checkbox-label--card">
|
||||
<input type="checkbox" name="extras[]" value="insurance">
|
||||
<span class="carei-form__checkbox-box">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</span>
|
||||
<span class="carei-form__extra-content">
|
||||
<strong>Rozszerzone ubezpieczenie</strong>
|
||||
<span class="carei-form__extra-desc">Obejmuje brak odpowiedzialności najemcy za wszelki szkody poniesione na aucie.</span>
|
||||
<span class="carei-form__extra-price">300 zł</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="carei-form__extra-card">
|
||||
<label class="carei-form__checkbox-label carei-form__checkbox-label--card">
|
||||
<input type="checkbox" name="extras[]" value="child-seat">
|
||||
<span class="carei-form__checkbox-box">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</span>
|
||||
<span class="carei-form__extra-content">
|
||||
<strong>Fotelik dla dziecka</strong>
|
||||
<span class="carei-form__extra-desc">Prosimy zawrzeć wiek dziecka w wiadomości.</span>
|
||||
<span class="carei-form__extra-price">50 zł</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dane najemcy -->
|
||||
<div class="carei-form__divider"><span>Dane najemcy</span></div>
|
||||
|
||||
<div class="carei-form__section">
|
||||
<div class="carei-form__row">
|
||||
<div class="carei-form__field">
|
||||
<label class="carei-form__label-small" for="carei-firstname">Imię</label>
|
||||
<input type="text" id="carei-firstname" name="firstName" class="carei-form__input" required>
|
||||
</div>
|
||||
<div class="carei-form__field">
|
||||
<label class="carei-form__label-small" for="carei-lastname">Nazwisko</label>
|
||||
<input type="text" id="carei-lastname" name="lastName" class="carei-form__input" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="carei-form__row">
|
||||
<div class="carei-form__field">
|
||||
<label class="carei-form__label-small" for="carei-email">Adres e-mail</label>
|
||||
<input type="email" id="carei-email" name="email" class="carei-form__input" required>
|
||||
</div>
|
||||
<div class="carei-form__field">
|
||||
<label class="carei-form__label-small" for="carei-phone">Nr telefonu</label>
|
||||
<div class="carei-form__phone-wrap">
|
||||
<div class="carei-form__phone-prefix">
|
||||
<span class="carei-form__phone-flag">🇵🇱</span>
|
||||
<span class="carei-form__phone-code">+48</span>
|
||||
</div>
|
||||
<input type="tel" id="carei-phone" name="phone" class="carei-form__input carei-form__input--phone" placeholder="___ ___ ___" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="carei-form__field carei-form__field--full">
|
||||
<textarea id="carei-message" name="message" class="carei-form__textarea" placeholder="Twoja wiadomość dotycząca rezerwacji" rows="4"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stopka -->
|
||||
<div class="carei-form__footer">
|
||||
<label class="carei-form__checkbox-label carei-form__checkbox-label--privacy">
|
||||
<input type="checkbox" id="carei-privacy" name="privacy" required>
|
||||
<span class="carei-form__checkbox-box">
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7l3.5 3.5L12 4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</span>
|
||||
<span class="carei-form__checkbox-text">Zgadzam się na <a href="/polityka-prywatnosci/" target="_blank">Politykę Prywatności</a></span>
|
||||
</label>
|
||||
<button type="submit" class="carei-form__submit">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
|
||||
Wyślij
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="carei-form__error-summary" id="carei-error-summary" style="display:none;">
|
||||
Uzupełnij wymagane pola zaznaczone na czerwono.
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP REST API proxy to Softra Rent API.
|
||||
* Namespace: carei/v1
|
||||
*/
|
||||
class Carei_REST_Proxy {
|
||||
|
||||
const NAMESPACE = 'carei/v1';
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
|
||||
}
|
||||
|
||||
public function register_routes() {
|
||||
// GET /branches
|
||||
register_rest_route( self::NAMESPACE, '/branches', array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_branches' ),
|
||||
'permission_callback' => '__return_true',
|
||||
) );
|
||||
|
||||
// GET /car-classes-all (all defined classes, no params needed)
|
||||
register_rest_route( self::NAMESPACE, '/car-classes-all', array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_all_car_classes' ),
|
||||
'permission_callback' => '__return_true',
|
||||
) );
|
||||
|
||||
// POST /car-classes
|
||||
register_rest_route( self::NAMESPACE, '/car-classes', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'get_car_classes' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
'args' => array(
|
||||
'dateFrom' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'dateTo' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'branchName' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
),
|
||||
) );
|
||||
|
||||
// POST /car-models
|
||||
register_rest_route( self::NAMESPACE, '/car-models', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'get_car_models' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
'args' => array(
|
||||
'dateFrom' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'dateTo' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'branchName' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'category' => array( 'required' => false, 'sanitize_callback' => 'sanitize_text_field', 'default' => '' ),
|
||||
),
|
||||
) );
|
||||
|
||||
// POST /pricelist
|
||||
register_rest_route( self::NAMESPACE, '/pricelist', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'get_pricelist' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
'args' => array(
|
||||
'category' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'dateFrom' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'dateTo' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
'pickUpLocation' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
),
|
||||
) );
|
||||
|
||||
// POST /pricing-summary
|
||||
register_rest_route( self::NAMESPACE, '/pricing-summary', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'get_pricing_summary' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
) );
|
||||
|
||||
// POST /customer
|
||||
register_rest_route( self::NAMESPACE, '/customer', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'add_customer' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
) );
|
||||
|
||||
// POST /booking
|
||||
register_rest_route( self::NAMESPACE, '/booking', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'make_booking' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
) );
|
||||
|
||||
// POST /booking/confirm
|
||||
register_rest_route( self::NAMESPACE, '/booking/confirm', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'confirm_booking' ),
|
||||
'permission_callback' => array( $this, 'check_nonce' ),
|
||||
'args' => array(
|
||||
'reservationId' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ),
|
||||
),
|
||||
) );
|
||||
|
||||
// GET /agreements
|
||||
register_rest_route( self::NAMESPACE, '/agreements', array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_agreements' ),
|
||||
'permission_callback' => '__return_true',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify WP REST nonce for POST requests.
|
||||
*/
|
||||
public function check_nonce( WP_REST_Request $request ) {
|
||||
$nonce = $request->get_header( 'X-WP-Nonce' );
|
||||
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
|
||||
return new WP_Error( 'rest_forbidden', 'Invalid nonce.', array( 'status' => 403 ) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API instance or return error.
|
||||
*/
|
||||
private function api() {
|
||||
$api = Carei_Softra_API::get_instance();
|
||||
if ( null === $api ) {
|
||||
return new WP_Error( 'carei_not_configured', 'Softra API not configured.', array( 'status' => 500 ) );
|
||||
}
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap API response.
|
||||
*/
|
||||
private function respond( $result ) {
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
return new WP_REST_Response( $result, 200 );
|
||||
}
|
||||
|
||||
// ─── Callbacks ────────────────────────────────────────────────
|
||||
|
||||
public function get_all_car_classes( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->get_all_car_classes() );
|
||||
}
|
||||
|
||||
public function get_branches( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->get_branches() );
|
||||
}
|
||||
|
||||
public function get_car_classes( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->get_car_classes(
|
||||
$request->get_param( 'dateFrom' ),
|
||||
$request->get_param( 'dateTo' ),
|
||||
$request->get_param( 'branchName' )
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_car_models( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->get_car_models(
|
||||
$request->get_param( 'dateFrom' ),
|
||||
$request->get_param( 'dateTo' ),
|
||||
$request->get_param( 'branchName' ),
|
||||
$request->get_param( 'category' )
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_pricelist( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->get_pricelist(
|
||||
$request->get_param( 'category' ),
|
||||
$request->get_param( 'dateFrom' ),
|
||||
$request->get_param( 'dateTo' ),
|
||||
$request->get_param( 'pickUpLocation' )
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_pricing_summary( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
$params = $request->get_json_params();
|
||||
return $this->respond( $api->get_pricing_summary( $params ) );
|
||||
}
|
||||
|
||||
public function add_customer( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
$data = $request->get_json_params();
|
||||
return $this->respond( $api->add_customer( $data ) );
|
||||
}
|
||||
|
||||
public function make_booking( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
$data = $request->get_json_params();
|
||||
return $this->respond( $api->make_booking( $data ) );
|
||||
}
|
||||
|
||||
public function confirm_booking( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->confirm_booking(
|
||||
$request->get_param( 'reservationId' )
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_agreements( WP_REST_Request $request ) {
|
||||
$api = $this->api();
|
||||
if ( is_wp_error( $api ) ) {
|
||||
return $api;
|
||||
}
|
||||
return $this->respond( $api->get_agreements() );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Softra Rent API client with JWT token caching.
|
||||
* Uses native cURL (matching softra-test.php) instead of WP HTTP API.
|
||||
*/
|
||||
class Carei_Softra_API {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
private $base_url;
|
||||
private $username;
|
||||
private $password;
|
||||
|
||||
const TOKEN_TRANSIENT = 'carei_softra_token';
|
||||
const TOKEN_EXPIRY = 3000; // 50 minutes (token valid 60 min)
|
||||
|
||||
private function __construct( $base_url, $username, $password ) {
|
||||
$this->base_url = rtrim( $base_url, '/' );
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
public static function init( $base_url, $username, $password ) {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self( $base_url, $username, $password );
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function get_instance() {
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Native cURL request — mirrors softra-test.php requestJson().
|
||||
*/
|
||||
private function curl_request( $method, $url, $payload = null, $extra_headers = array() ) {
|
||||
$ch = curl_init( $url );
|
||||
if ( false === $ch ) {
|
||||
return new WP_Error( 'carei_curl_init', 'cURL init failed' );
|
||||
}
|
||||
|
||||
$headers = array_merge( array( 'Accept: application/json' ), $extra_headers );
|
||||
if ( null !== $payload ) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
}
|
||||
|
||||
curl_setopt_array( $ch, array(
|
||||
CURLOPT_CUSTOMREQUEST => strtoupper( $method ),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_CONNECTTIMEOUT => 15,
|
||||
) );
|
||||
|
||||
if ( null !== $payload ) {
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
|
||||
}
|
||||
|
||||
$raw = curl_exec( $ch );
|
||||
$err = curl_error( $ch );
|
||||
$status = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
curl_close( $ch );
|
||||
|
||||
if ( false === $raw ) {
|
||||
return new WP_Error( 'carei_curl_error', 'cURL error: ' . $err );
|
||||
}
|
||||
|
||||
$decoded = json_decode( $raw, true );
|
||||
if ( ! is_array( $decoded ) ) {
|
||||
$decoded = null;
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => $status,
|
||||
'body' => $decoded,
|
||||
'raw' => $raw,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JWT token (cached in WP transient).
|
||||
*/
|
||||
public function get_token() {
|
||||
$token = get_transient( self::TOKEN_TRANSIENT );
|
||||
if ( false !== $token ) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
$res = $this->curl_request( 'POST', $this->base_url . '/account/auth', array(
|
||||
'login' => $this->username,
|
||||
'password' => $this->password,
|
||||
) );
|
||||
|
||||
if ( is_wp_error( $res ) ) {
|
||||
return new WP_Error( 'carei_auth_failed', 'Softra API auth failed: ' . $res->get_error_message() );
|
||||
}
|
||||
|
||||
if ( 200 !== $res['status'] || empty( $res['body']['token'] ) ) {
|
||||
$detail = isset( $res['raw'] ) ? $res['raw'] : '';
|
||||
return new WP_Error( 'carei_auth_failed', 'Softra API auth failed (HTTP ' . $res['status'] . '): ' . $detail );
|
||||
}
|
||||
|
||||
$token = $res['body']['token'];
|
||||
set_transient( self::TOKEN_TRANSIENT, $token, self::TOKEN_EXPIRY );
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic API request with auth.
|
||||
*/
|
||||
public function request( $method, $endpoint, $body = null, $query_params = array() ) {
|
||||
$token = $this->get_token();
|
||||
if ( is_wp_error( $token ) ) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
$url = $this->base_url . $endpoint;
|
||||
if ( ! empty( $query_params ) ) {
|
||||
$url .= '?' . http_build_query( $query_params );
|
||||
}
|
||||
|
||||
$res = $this->curl_request(
|
||||
$method,
|
||||
$url,
|
||||
$body,
|
||||
array( 'Authorization: Bearer ' . $token )
|
||||
);
|
||||
|
||||
if ( is_wp_error( $res ) ) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
if ( $res['status'] < 200 || $res['status'] >= 300 ) {
|
||||
return new WP_Error(
|
||||
'carei_api_error',
|
||||
'Softra API error: HTTP ' . $res['status'],
|
||||
array( 'status' => $res['status'], 'body' => $res['body'] )
|
||||
);
|
||||
}
|
||||
|
||||
return $res['body'];
|
||||
}
|
||||
|
||||
// ─── Public API Methods ───────────────────────────────────────
|
||||
|
||||
public function get_branches() {
|
||||
return $this->request( 'GET', '/branch/list' );
|
||||
}
|
||||
|
||||
public function get_all_car_classes() {
|
||||
return $this->request( 'GET', '/car/class/listAll' );
|
||||
}
|
||||
|
||||
public function get_car_classes( $date_from, $date_to, $branch_name ) {
|
||||
return $this->request( 'POST', '/car/class/list', array(
|
||||
'dateFrom' => $date_from,
|
||||
'dateTo' => $date_to,
|
||||
'branchName' => $branch_name,
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_car_models( $date_from, $date_to, $branch_name, $category = '' ) {
|
||||
return $this->request( 'POST', '/car/model/list', array(
|
||||
'dateFrom' => $date_from,
|
||||
'dateTo' => $date_to,
|
||||
'branchName' => $branch_name,
|
||||
'category' => $category,
|
||||
), array( 'includeBrandDetails' => 'true' ) );
|
||||
}
|
||||
|
||||
public function get_pricelist( $category, $date_from, $date_to, $pickup_location, $language = 'pl', $currency = 'PLN' ) {
|
||||
return $this->request( 'POST', '/pricelist/list', array(
|
||||
'category' => $category,
|
||||
'dateFrom' => $date_from,
|
||||
'dateTo' => $date_to,
|
||||
'pickUpLocation' => $pickup_location,
|
||||
'language' => $language,
|
||||
'currency' => $currency,
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_pricing_summary( $params ) {
|
||||
return $this->request( 'POST', '/rent/princingSummary', $params );
|
||||
}
|
||||
|
||||
public function add_customer( $data ) {
|
||||
return $this->request( 'POST', '/customer/add', $data );
|
||||
}
|
||||
|
||||
public function make_booking( $data ) {
|
||||
return $this->request( 'POST', '/rent/makebooking', $data );
|
||||
}
|
||||
|
||||
public function confirm_booking( $reservation_id ) {
|
||||
return $this->request( 'POST', '/rent/confirm', array(
|
||||
'reservationId' => $reservation_id,
|
||||
) );
|
||||
}
|
||||
|
||||
public function get_agreements() {
|
||||
return $this->request( 'GET', '/agreement/def/list' );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user