function mk_sym_products() {
$map = [];
if (MK_SYM_PRODUCT_ID > 0) $map[MK_SYM_PRODUCT_ID] = MK_SYM_SESSIONS;
if (MK_SYM_EXT_PRODUCT_ID > 0) $map[MK_SYM_EXT_PRODUCT_ID] = MK_SYM_EXT_SESSIONS;
if (MK_SYM_BUNDLE_PRODUCT_ID > 0) $map[MK_SYM_BUNDLE_PRODUCT_ID] = MK_SYM_BUNDLE_SESSIONS;
return $map;
}
// Suma sesji z produktow symulatora w zamowieniu (0 = brak produktu symulatora)
function mk_sym_order_sessions($order) {
$map = mk_sym_products();
$total = 0;
foreach ($order->get_items() as $item) {
$pid = (int) $item->get_product_id();
if (isset($map[$pid])) {
$total += $map[$pid] * max(1, (int) $item->get_quantity());
}
}
return $total;
}
// Fakturownia – uzupelnij po zdobyciu API tokena
if (!defined(‘MK_FAKTUROWNIA_TOKEN’)) define(‘MK_FAKTUROWNIA_TOKEN’, ‘hIjg6no2Q8DHgSqnu5Xv’);
if (!defined(‘MK_FAKTUROWNIA_DOMAIN’)) define(‘MK_FAKTUROWNIA_DOMAIN’, ‘czerwonka’);
// — REST endpoints —
add_action(‘rest_api_init’, function () {
register_rest_route(‘mk/v1’, ‘/symulator’, [
‘methods’ => ‘POST’,
‘callback’ => ‘mk_symulator_handler’,
‘permission_callback’ => ‘__return_true’,
]);
register_rest_route(‘mk/v1’, ‘/symulator-start’, [
‘methods’ => ‘POST’,
‘callback’ => ‘mk_symulator_start_handler’,
‘permission_callback’ => ‘__return_true’,
]);
register_rest_route(‘mk/v1’, ‘/symulator-report’, [
‘methods’ => ‘POST’,
‘callback’ => ‘mk_symulator_report_handler’,
‘permission_callback’ => ‘__return_true’,
]);
});
// — WooCommerce: inicjalizacja licznika sesji po oplacie —
add_action(‘woocommerce_order_status_completed’, ‘mk_sym_init_sessions’);
add_action(‘woocommerce_order_status_processing’, ‘mk_sym_init_sessions’);
function mk_sym_init_sessions($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
$sessions = mk_sym_order_sessions($order);
if ($sessions <= 0) return; $order_key = $order->get_order_key();
$opt_key = ‘mk_sym_sess_count_’ . $order_key;
// Inicjalizuj tylko raz (nie nadpisuj jesli juz ustawiono)
if (get_option($opt_key) === false) {
update_option($opt_key, $sessions, false);
}
// Fakturownia – wystaw fakture
if (MK_FAKTUROWNIA_TOKEN && MK_FAKTUROWNIA_DOMAIN) {
mk_sym_create_fakturownia_invoice($order);
}
}
function mk_sym_order_has_product($order) {
if (empty(mk_sym_products())) return true; // brak filtru produktu = wszystkie zamowienia
return mk_sym_order_sessions($order) > 0;
}
// — WooCommerce: redirect na symulator po zakupie —
add_action(‘woocommerce_thankyou’, ‘mk_sym_redirect_after_purchase’);
function mk_sym_redirect_after_purchase($order_id) {
if (MK_SYM_PRODUCT_ID <= 0) return; $order = wc_get_order($order_id); if (!$order || !mk_sym_order_has_product($order)) return; $order_key = $order->get_order_key();
$redirect_url = home_url(‘/symulator-rozmowy/?token=’ . urlencode($order_key));
?>
function mk_symulator_start_handler(WP_REST_Request $request) {
$order_key = sanitize_text_field($request->get_param(‘order_key’) ?? ”);
$dev_secret = sanitize_text_field($request->get_param(‘dev_secret’) ?? ”);
$is_dev = defined(‘MK_SYM_DEV_SECRET’) && $dev_secret === MK_SYM_DEV_SECRET;
if ($is_dev) {
$token = wp_generate_uuid4();
set_transient(‘mk_sym_token_’ . $token, [‘dev’ => true, ‘calls’ => 0], 6 * HOUR_IN_SECONDS);
return rest_ensure_response([‘session_token’ => $token, ‘sessions_left’ => 99]);
}
if (empty($order_key)) {
return new WP_Error(‘no_key’, “Brak klucza zam\u{f3}wienia.”, [‘status’ => 400]);
}
// Walidacja zamowienia WooCommerce
$order_id = wc_get_order_id_by_order_key($order_key);
if (!$order_id) {
return new WP_Error(‘invalid_order’, “Nieprawid\u{142}owy klucz zam\u{f3}wienia.”, [‘status’ => 403]);
}
$order = wc_get_order($order_id);
$status = $order->get_status();
if (!in_array($status, [‘completed’, ‘processing’], true)) {
return new WP_Error(‘unpaid’, “Zam\u{f3}wienie nie zosta\u{142}o op\u{142}acone.”, [‘status’ => 402]);
}
// Licznik sesji
$opt_key = ‘mk_sym_sess_count_’ . $order_key;
$remaining = (int) get_option($opt_key, -1);
if ($remaining < 0) { // Zamowienie oplacone ale inicjalizacja nie nastapila (edge case) – zainicjuj teraz $sessions = mk_sym_order_sessions($order); if ($sessions > 0) {
update_option($opt_key, $sessions – 1, false);
$remaining = $sessions – 1;
} else {
return new WP_Error(‘no_product’, “Zam\u{f3}wienie nie zawiera Symulatora.”, [‘status’ => 403]);
}
} elseif ($remaining <= 0) { return new WP_Error(‘no_sessions’, “Wszystkie sesje zosta\u{142}y wykorzystane. Kup nowy dost\u{119}p.”, [‘status’ => 403]);
} else {
update_option($opt_key, $remaining – 1, false);
$remaining = $remaining – 1;
}
$token = wp_generate_uuid4();
set_transient(‘mk_sym_token_’ . $token, [‘order_key’ => $order_key, ‘calls’ => 0], 6 * HOUR_IN_SECONDS);
return rest_ensure_response([‘session_token’ => $token, ‘sessions_left’ => $remaining]);
}
// — Endpoint: wywolanie Claude —
function mk_symulator_handler(WP_REST_Request $request) {
@set_time_limit(120);
$messages = $request->get_param(‘messages’);
$system = $request->get_param(‘system’) ?? ”;
$max_tokens = (int) ($request->get_param(‘max_tokens’) ?? 600);
$dev_secret = sanitize_text_field($request->get_param(‘dev_secret’) ?? ”);
$session_token = sanitize_text_field($request->get_param(‘session_token’) ?? ”);
$is_dev = defined(‘MK_SYM_DEV_SECRET’) && $dev_secret === MK_SYM_DEV_SECRET;
$is_valid_session = false;
if (!$is_dev && !empty($session_token)) {
$sess = get_transient(‘mk_sym_token_’ . $session_token);
if ($sess !== false) {
$is_valid_session = true;
$sess[‘calls’] = ($sess[‘calls’] ?? 0) + 1;
set_transient(‘mk_sym_token_’ . $session_token, $sess, 6 * HOUR_IN_SECONDS);
}
}
if (!is_array($messages) || empty($messages)) {
return new WP_Error(‘invalid’, “Brak wiadomo\u{15b}ci.”, [‘status’ => 400]);
}
if (!$is_dev && !$is_valid_session) {
return new WP_Error(‘unauthorized’, “Brak aktywnej sesji. Kup dost\u{119}p, aby korzysta\u{107} z Symulatora.”, [‘status’ => 401]);
}
$api_key = defined(‘MK_ANTHROPIC_KEY’) ? MK_ANTHROPIC_KEY : ”;
if (!$api_key) {
return new WP_Error(‘no_key’, ‘Brak konfiguracji API.’, [‘status’ => 500]);
}
$model = ($max_tokens >= 2000) ? ‘claude-sonnet-4-6’ : ‘claude-haiku-4-5-20251001’;
$max_tokens = min($max_tokens, 4000);
$body = [
‘model’ => $model,
‘max_tokens’ => $max_tokens,
‘messages’ => $messages,
];
if (!empty($system)) $body[‘system’] = (string) $system;
$anthropic_response = wp_remote_post(‘https://api.anthropic.com/v1/messages’, [
‘timeout’ => 90,
‘headers’ => [
‘Content-Type’ => ‘application/json’,
‘x-api-key’ => $api_key,
‘anthropic-version’ => ‘2023-06-01’,
],
‘body’ => wp_json_encode($body),
]);
if (is_wp_error($anthropic_response)) {
return new WP_Error(‘api_error’, “B\u{142}\u{105}d po\u{142}\u{105}czenia z API.”, [‘status’ => 502]);
}
$data = json_decode(wp_remote_retrieve_body($anthropic_response), true);
$code = wp_remote_retrieve_response_code($anthropic_response);
if ($code !== 200) {
$msg = $data[‘error’][‘message’] ?? “B\u{142}\u{105}d API Anthropic.”;
return new WP_Error(‘api_error’, $msg, [‘status’ => 502]);
}
$text = $data[‘content’][0][‘text’] ?? ”;
return rest_ensure_response([‘text’ => $text]);
}
// — Endpoint: wyslij raport emailem po sesji —
function mk_symulator_report_handler(WP_REST_Request $request) {
$session_token = sanitize_text_field($request->get_param(‘session_token’) ?? ”);
$dev_secret = sanitize_text_field($request->get_param(‘dev_secret’) ?? ”);
$user_email = sanitize_email($request->get_param(‘user_email’) ?? ”);
$eval_data = $request->get_param(‘eval_data’);
$role = sanitize_text_field($request->get_param(‘role’) ?? ”);
$level = sanitize_text_field($request->get_param(‘level’) ?? ”);
$is_dev = defined(‘MK_SYM_DEV_SECRET’) && $dev_secret === MK_SYM_DEV_SECRET;
if (!$is_dev && !empty($session_token)) {
$sess = get_transient(‘mk_sym_token_’ . $session_token);
if ($sess === false) {
return new WP_Error(‘unauthorized’, ‘Nieaktywna sesja.’, [‘status’ => 401]);
}
} elseif (!$is_dev) {
return new WP_Error(‘unauthorized’, ‘Brak tokenu sesji.’, [‘status’ => 401]);
}
if (!is_email($user_email)) {
return new WP_Error(‘no_email’, ‘Brak adresu email.’, [‘status’ => 400]);
}
if (!is_array($eval_data) || empty($eval_data[‘overall_score’])) {
return new WP_Error(‘no_eval’, ‘Brak danych ewaluacji.’, [‘status’ => 400]);
}
$sent = mk_sym_send_report_email($user_email, $eval_data, $role, $level);
if (!$sent) {
return new WP_Error(‘mail_error’, ‘Nie udalo sie wyslac emaila.’, [‘status’ => 500]);
}
return rest_ensure_response([‘ok’ => true]);
}
function mk_sym_send_report_email($email, $eval, $role, $level) {
$score = (int) ($eval[‘overall_score’] ?? 0);
$title = esc_html($eval[‘overall_title’] ?? ”);
$summary = esc_html($eval[‘overall_summary’] ?? ”);
$role_label = esc_html($role ?: ‘rekrutacja’);
$level_label = esc_html($level ?: ”);
$score_color = $score >= 80 ? ‘#10b981’ : ($score >= 60 ? ‘#f59e0b’ : ‘#ef4444’);
// Kategorie
$cats_html = ”;
foreach ((array) ($eval[‘categories’] ?? []) as $cat) {
$s = (int) ($cat[‘score’] ?? 0);
$c = $s >= 80 ? ‘#10b981’ : ($s >= 60 ? ‘#f59e0b’ : ‘#ef4444’);
$w = round($s * 0.9);
$cats_html .= ‘
‘ . esc_html($cat[‘name’] ?? ”) . ‘
‘
. ‘
‘
. ‘
‘ . $s . ‘
‘;
}
// Q&A feedback
$qa_html = ”;
foreach ((array) ($eval[‘qa_feedback’] ?? []) as $i => $qa) {
$q_score = (int) ($qa[‘score’] ?? 0);
$q_color = $q_score >= 80 ? ‘#10b981’ : ($q_score >= 60 ? ‘#f59e0b’ : ‘#ef4444’);
$qa_html .= ‘
. ‘
‘
. ‘
‘
. ‘
‘
. ‘
‘
. ‘
‘;
}
// Mocne / do wzmocnienia
$mocne_html = ”;
foreach ((array) ($eval[‘mocne’] ?? []) as $m) {
if ($m) $mocne_html .= ‘
- ‘ . esc_html($m) . ‘
‘;
}
$slabe_html = ”;
foreach ((array) ($eval[‘do_wzmocnienia’] ?? []) as $s) {
if ($s) $slabe_html .= ‘
- ‘ . esc_html($s) . ‘
‘;
}
$html = <<<HTML
|
HTML;
$subject = ‘Twoj raport z rozmowy rekrutacyjnej (‘ . $role_label . ‘) – ‘ . $score . ‘/100 pkt’;
$headers = [
‘Content-Type: text/html; charset=UTF-8’,
‘From: Manager Kariery <noreply@managerkariery.com>’,
];
return wp_mail($email, $subject, $html, $headers);
}
// — Fakturownia: wystaw fakture po zakupie —
function mk_sym_create_fakturownia_invoice($order) {
$token = MK_FAKTUROWNIA_TOKEN;
$domain = MK_FAKTUROWNIA_DOMAIN;
if (!$token || !$domain) return;
$total = number_format((float) $order->get_total(), 2, ‘.’, ”);
$net = number_format((float) $order->get_total() / 1.23, 2, ‘.’, ”); // VAT 23%
$vat_val = number_format((float) $order->get_total() – (float) $net, 2, ‘.’, ”);
$name = trim($order->get_billing_first_name() . ‘ ‘ . $order->get_billing_last_name());
$email = $order->get_billing_email();
$order_id = $order->get_id();
// Nazwa pozycji z realnych produktow zamowienia (nie hardcode “3 sesje”)
$item_names = [];
foreach ($order->get_items() as $item) {
$item_names[] = $item->get_name();
}
$position_name = $item_names ? implode(‘ + ‘, $item_names) : ‘Symulator Rozmowy Rekrutacyjnej’;
$invoice_data = [
‘api_token’ => $token,
‘invoice’ => [
‘kind’ => ‘vat’,
‘number’ => null,
‘sell_date’ => date(‘Y-m-d’),
‘issue_date’ => date(‘Y-m-d’),
‘payment_to’ => date(‘Y-m-d’),
‘payment_type’ => ‘transfer’,
‘status’ => ‘issued’,
‘buyer_name’ => $name,
‘buyer_email’ => $email,
‘buyer_city’ => $order->get_billing_city(),
‘buyer_post_code’ => $order->get_billing_postcode(),
‘buyer_street’ => $order->get_billing_address_1(),
‘buyer_country’ => $order->get_billing_country(),
‘description’ => ‘Zamowienie WooCommerce #’ . $order_id,
‘positions’ => [[
‘name’ => $position_name,
‘quantity’ => 1,
‘quantity_unit’ => ‘szt’,
‘tax’ => 23,
‘price_net’ => $net,
‘price_gross’ => $total,
‘total_price_gross’ => $total,
‘total_price_net’ => $net,
]],
],
];
// Nie wystawiaj drugiej faktury dla tego samego zamowienia
if ($order->get_meta(‘_mk_fakturownia_invoice_id’)) return;
$resp = wp_remote_post(‘https://’ . $domain . ‘.fakturownia.pl/invoices.json’, [
‘timeout’ => 30,
‘headers’ => [‘Content-Type’ => ‘application/json’],
‘body’ => wp_json_encode($invoice_data),
]);
// Zapisz wynik zamiast wysylac w ciemno (dotad bledy 401 znikaly po cichu)
if (is_wp_error($resp)) {
$order->add_order_note(‘Fakturownia: blad polaczenia – ‘ . $resp->get_error_message());
error_log(‘MK Fakturownia WP_Error: ‘ . $resp->get_error_message());
return;
}
$code = wp_remote_retrieve_response_code($resp);
$body = json_decode(wp_remote_retrieve_body($resp), true);
if ($code >= 200 && $code < 300 && !empty($body[‘id’])) { $invoice_id = $body[‘id’]; $order->update_meta_data(‘_mk_fakturownia_invoice_id’, $invoice_id);
$order->update_meta_data(‘_mk_fakturownia_invoice_number’, $body[‘number’] ?? ”);
$order->save();
$order->add_order_note(‘Fakturownia: faktura ‘ . ($body[‘number’] ?? $invoice_id) . ‘ wystawiona.’);
// Wyslij fakture emailem przez osobny endpoint (send_by_email w payload = 422)
wp_remote_post(‘https://’ . $domain . ‘.fakturownia.pl/invoices/’ . $invoice_id . ‘/send_by_email.json?api_token=’ . $token, [
‘timeout’ => 20,
‘headers’ => [‘Content-Type’ => ‘application/json’],
‘body’ => ‘{}’,
]);
} else {
$msg = is_array($body) ? wp_json_encode($body) : wp_remote_retrieve_body($resp);
$order->add_order_note(‘Fakturownia: BLAD ‘ . $code . ‘ – ‘ . mb_substr((string) $msg, 0, 300));
error_log(‘MK Fakturownia HTTP ‘ . $code . ‘: ‘ . (string) $msg);
}
}
