2026-04-16 13:32:32 +02:00
|
|
|
|
<?php
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Plugin Name: AZA License Bridge
|
|
|
|
|
|
* Description: Verknüpft WooCommerce-Abo-Käufe mit dem AZA Hetzner-Lizenz-Backend.
|
2026-05-20 00:09:28 +02:00
|
|
|
|
* Version: 1.0.2
|
2026-04-16 13:32:32 +02:00
|
|
|
|
* Author: AZA MedWork
|
|
|
|
|
|
* Requires PHP: 7.4
|
|
|
|
|
|
* Requires at least: 5.8
|
|
|
|
|
|
*
|
|
|
|
|
|
* Nach erfolgreicher Aktivierung eines Abonnements wird automatisch ein
|
|
|
|
|
|
* Lizenzschlüssel auf dem Hetzner-Backend erzeugt und per E-Mail an den
|
|
|
|
|
|
* Kunden versendet.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Konfiguration: WordPress Admin → Einstellungen → AZA License Bridge
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
|
|
|
|
exit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Settings Page ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
add_action( 'admin_menu', function () {
|
|
|
|
|
|
add_options_page(
|
|
|
|
|
|
'AZA License Bridge',
|
|
|
|
|
|
'AZA License Bridge',
|
|
|
|
|
|
'manage_options',
|
|
|
|
|
|
'aza-license-bridge',
|
|
|
|
|
|
'aza_lb_settings_page'
|
|
|
|
|
|
);
|
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
add_action( 'admin_init', function () {
|
|
|
|
|
|
register_setting( 'aza_lb_settings', 'aza_lb_api_url' );
|
|
|
|
|
|
register_setting( 'aza_lb_settings', 'aza_lb_wc_secret' );
|
|
|
|
|
|
register_setting( 'aza_lb_settings', 'aza_lb_lookup_key' );
|
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_settings_page() {
|
|
|
|
|
|
$api_url = get_option( 'aza_lb_api_url', 'https://api.aza-medwork.ch' );
|
|
|
|
|
|
$wc_secret = get_option( 'aza_lb_wc_secret', '' );
|
|
|
|
|
|
$lookup_key = get_option( 'aza_lb_lookup_key', 'aza_basic_monthly' );
|
|
|
|
|
|
?>
|
|
|
|
|
|
<div class="wrap">
|
|
|
|
|
|
<h1>AZA License Bridge</h1>
|
|
|
|
|
|
|
|
|
|
|
|
<form method="post" action="options.php">
|
|
|
|
|
|
<?php settings_fields( 'aza_lb_settings' ); ?>
|
|
|
|
|
|
<table class="form-table">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th scope="row">Hetzner API URL</th>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<input type="url" name="aza_lb_api_url" value="<?php echo esc_attr( $api_url ); ?>" class="regular-text" />
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th scope="row">WC Provision Secret</th>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<input type="text" name="aza_lb_wc_secret" value="<?php echo esc_attr( $wc_secret ); ?>" class="regular-text" />
|
|
|
|
|
|
<p class="description">Muss identisch sein mit WC_PROVISION_SECRET auf Hetzner.</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th scope="row">Standard lookup_key</th>
|
|
|
|
|
|
<td>
|
|
|
|
|
|
<input type="text" name="aza_lb_lookup_key" value="<?php echo esc_attr( $lookup_key ); ?>" class="regular-text" />
|
|
|
|
|
|
<p class="description">z. B. aza_basic_monthly, aza_basic_yearly</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
<?php submit_button(); ?>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
|
|
<hr>
|
|
|
|
|
|
|
|
|
|
|
|
<h2>Status</h2>
|
|
|
|
|
|
<p><strong>API URL:</strong> <?php echo esc_html( trailingslashit( $api_url ) . 'wc/provision' ); ?></p>
|
|
|
|
|
|
<p><strong>Secret gesetzt:</strong> <?php echo $wc_secret ? '✅ Ja' : '❌ Nein'; ?></p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<?php
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_log( $message ) {
|
|
|
|
|
|
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
|
|
|
|
|
error_log( '[AZA-License-Bridge] ' . $message );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$upload = wp_upload_dir();
|
|
|
|
|
|
$base = isset( $upload['basedir'] ) ? $upload['basedir'] : '';
|
|
|
|
|
|
|
|
|
|
|
|
if ( empty( $base ) ) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$log_dir = trailingslashit( $base ) . 'aza-logs';
|
|
|
|
|
|
if ( ! is_dir( $log_dir ) ) {
|
|
|
|
|
|
wp_mkdir_p( $log_dir );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$log_file = trailingslashit( $log_dir ) . 'license-bridge.log';
|
|
|
|
|
|
$timestamp = current_time( 'Y-m-d H:i:s' );
|
|
|
|
|
|
|
|
|
|
|
|
@file_put_contents( $log_file, '[' . $timestamp . '] ' . $message . PHP_EOL, FILE_APPEND | LOCK_EX );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_get_lookup_key_from_subscription( $subscription ) {
|
|
|
|
|
|
if ( ! is_object( $subscription ) || ! method_exists( $subscription, 'get_items' ) ) {
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$items = $subscription->get_items();
|
|
|
|
|
|
if ( empty( $items ) || ! is_array( $items ) ) {
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach ( $items as $item ) {
|
|
|
|
|
|
if ( ! is_object( $item ) || ! method_exists( $item, 'get_product' ) ) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$product = $item->get_product();
|
|
|
|
|
|
if ( ! $product || ! is_object( $product ) || ! method_exists( $product, 'get_meta' ) ) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$lookup_key = trim( (string) $product->get_meta( '_aza_lookup_key' ) );
|
|
|
|
|
|
if ( $lookup_key !== '' ) {
|
|
|
|
|
|
return $lookup_key;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_get_parent_order_id_from_subscription( $subscription ) {
|
|
|
|
|
|
if ( ! is_object( $subscription ) ) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_parent_id' ) ) {
|
|
|
|
|
|
return (int) $subscription->get_parent_id();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_get_billing_email_from_subscription( $subscription ) {
|
|
|
|
|
|
if ( ! is_object( $subscription ) ) {
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_billing_email' ) ) {
|
|
|
|
|
|
$email = trim( (string) $subscription->get_billing_email() );
|
|
|
|
|
|
if ( $email !== '' ) {
|
|
|
|
|
|
return $email;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_parent' ) ) {
|
|
|
|
|
|
$parent_order = $subscription->get_parent();
|
|
|
|
|
|
if ( $parent_order && is_object( $parent_order ) && method_exists( $parent_order, 'get_billing_email' ) ) {
|
|
|
|
|
|
$email = trim( (string) $parent_order->get_billing_email() );
|
|
|
|
|
|
if ( $email !== '' ) {
|
|
|
|
|
|
return $email;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 00:09:28 +02:00
|
|
|
|
// ── Phase 1f: Abrechnungsperiode → Hetzner (ohne Woo /wc/v3/subscriptions von AZA aus) ──
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Liest nächste Zahlung / Billing aus Subscription-Objekt oder Post-Meta.
|
|
|
|
|
|
* Kompatibel mit WooCommerce-Subscriptions-APIs und WP Desk Flexible Subscriptions (u. a. billing_period M/D/W/Y).
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param object $subscription
|
|
|
|
|
|
* @return array Teilmenge für POST /wc/subscription_period
|
|
|
|
|
|
*/
|
|
|
|
|
|
function aza_lb_collect_period_fields_from_subscription( $subscription ) {
|
|
|
|
|
|
$fields = array();
|
|
|
|
|
|
if ( ! is_object( $subscription ) || ! method_exists( $subscription, 'get_id' ) ) {
|
|
|
|
|
|
return $fields;
|
|
|
|
|
|
}
|
|
|
|
|
|
$sid = (int) $subscription->get_id();
|
|
|
|
|
|
if ( $sid <= 0 ) {
|
|
|
|
|
|
return $fields;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_date' ) ) {
|
|
|
|
|
|
$next = $subscription->get_date( 'next_payment' );
|
|
|
|
|
|
if ( is_numeric( $next ) ) {
|
|
|
|
|
|
$fields['next_payment_date'] = (int) $next;
|
|
|
|
|
|
} elseif ( is_string( $next ) && $next !== '' && $next !== '0' ) {
|
|
|
|
|
|
$fields['next_payment_date'] = $next;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( empty( $fields['next_payment_date'] ) ) {
|
|
|
|
|
|
$meta_keys = array( '_schedule_next_payment', 'wps_next_payment_date' );
|
|
|
|
|
|
foreach ( $meta_keys as $meta_key ) {
|
|
|
|
|
|
$raw = get_post_meta( $sid, $meta_key, true );
|
|
|
|
|
|
if ( is_string( $raw ) && $raw !== '' ) {
|
|
|
|
|
|
$fields['next_payment_date'] = $raw;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( is_numeric( $raw ) && (int) $raw > 0 ) {
|
|
|
|
|
|
$fields['next_payment_date'] = (int) $raw;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_billing_period' ) ) {
|
|
|
|
|
|
$fields['billing_period'] = (string) $subscription->get_billing_period();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$bp = get_post_meta( $sid, '_billing_period', true );
|
|
|
|
|
|
if ( $bp !== '' && $bp !== null ) {
|
|
|
|
|
|
$fields['billing_period'] = (string) $bp;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_billing_interval' ) ) {
|
|
|
|
|
|
$fields['billing_interval'] = (int) $subscription->get_billing_interval();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$bi = get_post_meta( $sid, '_billing_interval', true );
|
|
|
|
|
|
if ( $bi !== '' && $bi !== null ) {
|
|
|
|
|
|
$fields['billing_interval'] = (int) $bi;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( method_exists( $subscription, 'get_status' ) ) {
|
|
|
|
|
|
$fields['subscription_status'] = (string) $subscription->get_status();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $fields;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Sendet nur Periodenfelder an Hetzner (POST /wc/subscription_period, X-WC-Secret).
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param object $subscription
|
|
|
|
|
|
* @return void
|
|
|
|
|
|
*/
|
|
|
|
|
|
function aza_lb_push_subscription_period_to_backend( $subscription ) {
|
|
|
|
|
|
$api_url = rtrim( (string) get_option( 'aza_lb_api_url', 'https://api.aza-medwork.ch' ), '/' );
|
|
|
|
|
|
$wc_secret = (string) get_option( 'aza_lb_wc_secret', '' );
|
|
|
|
|
|
if ( $wc_secret === '' || ! is_object( $subscription ) || ! method_exists( $subscription, 'get_id' ) ) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$sub_numeric = (int) $subscription->get_id();
|
|
|
|
|
|
if ( $sub_numeric <= 0 ) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$order_id = 0;
|
|
|
|
|
|
if ( function_exists( 'aza_lb_get_parent_order_id_from_subscription' ) ) {
|
|
|
|
|
|
$order_id = (int) aza_lb_get_parent_order_id_from_subscription( $subscription );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$period = aza_lb_collect_period_fields_from_subscription( $subscription );
|
|
|
|
|
|
if ( empty( $period['next_payment_date'] ) ) {
|
|
|
|
|
|
aza_lb_log( "PERIOD PUSH SKIP: sub={$sub_numeric} — kein next_payment ermittelbar" );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$payload = array_merge(
|
|
|
|
|
|
array(
|
|
|
|
|
|
'wc_subscription_id' => $sub_numeric,
|
|
|
|
|
|
'wc_order_id' => $order_id,
|
|
|
|
|
|
),
|
|
|
|
|
|
$period
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
$url = $api_url . '/wc/subscription_period';
|
|
|
|
|
|
$response = wp_remote_post(
|
|
|
|
|
|
$url,
|
|
|
|
|
|
array(
|
|
|
|
|
|
'timeout' => 20,
|
|
|
|
|
|
'headers' => array(
|
|
|
|
|
|
'Content-Type' => 'application/json',
|
|
|
|
|
|
'X-WC-Secret' => $wc_secret,
|
|
|
|
|
|
'User-Agent' => 'AZA-License-Bridge/1.0-flex-period',
|
|
|
|
|
|
),
|
|
|
|
|
|
'body' => wp_json_encode( $payload ),
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
|
|
|
|
aza_lb_log( 'PERIOD PUSH ERR: ' . $response->get_error_message() );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$code = (int) wp_remote_retrieve_response_code( $response );
|
|
|
|
|
|
aza_lb_log( "PERIOD PUSH sub={$sub_numeric} HTTP {$code}" );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 13:32:32 +02:00
|
|
|
|
// ── Provisioning Request ──────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Sendet den Lizenz-Provisioning-Request an das Hetzner-Backend.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $subscription_id WooCommerce Subscription ID
|
|
|
|
|
|
* @param int $order_id WooCommerce Order ID
|
|
|
|
|
|
* @param string $email Kunden-E-Mail
|
|
|
|
|
|
* @param string $lookup_key Produkt-/Planschlüssel
|
|
|
|
|
|
* @return array|WP_Error
|
|
|
|
|
|
*/
|
|
|
|
|
|
function aza_lb_provision_license( $subscription_id, $order_id, $email, $lookup_key = '' ) {
|
|
|
|
|
|
$api_url = rtrim( (string) get_option( 'aza_lb_api_url', 'https://api.aza-medwork.ch' ), '/' );
|
|
|
|
|
|
$wc_secret = (string) get_option( 'aza_lb_wc_secret', '' );
|
|
|
|
|
|
|
|
|
|
|
|
if ( $wc_secret === '' ) {
|
|
|
|
|
|
aza_lb_log( "SKIP: WC_PROVISION_SECRET nicht konfiguriert (sub={$subscription_id})" );
|
|
|
|
|
|
return new WP_Error( 'aza_lb_no_secret', 'WC Provision Secret nicht konfiguriert.' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( $lookup_key === '' ) {
|
|
|
|
|
|
$lookup_key = (string) get_option( 'aza_lb_lookup_key', 'aza_basic_monthly' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$url = $api_url . '/wc/provision';
|
|
|
|
|
|
|
|
|
|
|
|
$payload = array(
|
|
|
|
|
|
'customer_email' => (string) $email,
|
|
|
|
|
|
'wc_order_id' => (int) $order_id,
|
|
|
|
|
|
'wc_subscription_id' => (int) $subscription_id,
|
|
|
|
|
|
'lookup_key' => (string) $lookup_key,
|
|
|
|
|
|
'allowed_users' => 1,
|
|
|
|
|
|
'devices_per_user' => 2,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
$body = wp_json_encode( $payload );
|
|
|
|
|
|
|
|
|
|
|
|
aza_lb_log(
|
|
|
|
|
|
sprintf(
|
|
|
|
|
|
'POST %s (sub=%d, order=%d, email=%s, lookup_key=%s)',
|
|
|
|
|
|
$url,
|
|
|
|
|
|
(int) $subscription_id,
|
|
|
|
|
|
(int) $order_id,
|
|
|
|
|
|
(string) $email,
|
|
|
|
|
|
(string) $lookup_key
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
$response = wp_remote_post(
|
|
|
|
|
|
$url,
|
|
|
|
|
|
array(
|
|
|
|
|
|
'timeout' => 20,
|
|
|
|
|
|
'headers' => array(
|
|
|
|
|
|
'Content-Type' => 'application/json',
|
|
|
|
|
|
'X-WC-Secret' => $wc_secret,
|
|
|
|
|
|
'User-Agent' => 'AZA-License-Bridge/1.0.1',
|
|
|
|
|
|
),
|
|
|
|
|
|
'body' => $body,
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
|
|
|
|
aza_lb_log( 'FEHLER: ' . $response->get_error_message() );
|
|
|
|
|
|
return $response;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$code = (int) wp_remote_retrieve_response_code( $response );
|
|
|
|
|
|
$resp_body = (string) wp_remote_retrieve_body( $response );
|
|
|
|
|
|
|
|
|
|
|
|
aza_lb_log( "Response HTTP {$code}: {$resp_body}" );
|
|
|
|
|
|
|
|
|
|
|
|
if ( $code >= 200 && $code < 300 ) {
|
|
|
|
|
|
$data = json_decode( $resp_body, true );
|
|
|
|
|
|
return is_array( $data ) ? $data : array( 'raw' => $resp_body );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new WP_Error( 'aza_lb_provision_failed', "HTTP {$code}: {$resp_body}" );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── WooCommerce Hook ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Hook: Subscription wird aktiv.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Ziel:
|
|
|
|
|
|
* - Neukauf sauber provisionieren
|
|
|
|
|
|
* - Mehrfachauslösung durch Meta-Flag verhindern
|
|
|
|
|
|
*/
|
|
|
|
|
|
add_action( 'woocommerce_subscription_status_active', 'aza_lb_on_subscription_active', 10, 1 );
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_on_subscription_active( $subscription ) {
|
|
|
|
|
|
if ( ! is_object( $subscription ) ) {
|
|
|
|
|
|
aza_lb_log( 'SKIP: Hook erhielt kein Objekt.' );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! method_exists( $subscription, 'get_id' ) ) {
|
|
|
|
|
|
aza_lb_log( 'SKIP: Hook-Objekt ohne get_id().' );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$sub_id = (int) $subscription->get_id();
|
|
|
|
|
|
if ( $sub_id <= 0 ) {
|
|
|
|
|
|
aza_lb_log( 'SKIP: ungültige Subscription-ID.' );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$already = (string) get_post_meta( $sub_id, '_aza_license_provisioned', true );
|
|
|
|
|
|
if ( $already === 'yes' ) {
|
|
|
|
|
|
aza_lb_log( "SKIP: sub={$sub_id} bereits provisioniert" );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$email = aza_lb_get_billing_email_from_subscription( $subscription );
|
|
|
|
|
|
if ( $email === '' ) {
|
|
|
|
|
|
aza_lb_log( "SKIP: sub={$sub_id} – keine E-Mail gefunden" );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$order_id = aza_lb_get_parent_order_id_from_subscription( $subscription );
|
|
|
|
|
|
$lookup_key = aza_lb_get_lookup_key_from_subscription( $subscription );
|
|
|
|
|
|
|
|
|
|
|
|
if ( $lookup_key === '' ) {
|
|
|
|
|
|
$lookup_key = (string) get_option( 'aza_lb_lookup_key', 'aza_basic_monthly' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$result = aza_lb_provision_license( $sub_id, $order_id, $email, $lookup_key );
|
|
|
|
|
|
|
|
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
|
|
|
|
aza_lb_log( "PROVISION FEHLGESCHLAGEN: sub={$sub_id} – " . $result->get_error_message() );
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$license_key = '';
|
|
|
|
|
|
if ( isset( $result['license_key'] ) ) {
|
|
|
|
|
|
$license_key = trim( (string) $result['license_key'] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
update_post_meta( $sub_id, '_aza_license_provisioned', 'yes' );
|
|
|
|
|
|
update_post_meta( $sub_id, '_aza_license_provisioned_at', current_time( 'mysql' ) );
|
|
|
|
|
|
update_post_meta( $sub_id, '_aza_license_lookup_key', $lookup_key );
|
|
|
|
|
|
|
|
|
|
|
|
if ( $license_key !== '' ) {
|
|
|
|
|
|
update_post_meta( $sub_id, '_aza_license_key', $license_key );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( $order_id > 0 && $license_key !== '' ) {
|
|
|
|
|
|
update_post_meta( $order_id, '_aza_license_key', $license_key );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$status = isset( $result['status'] ) ? (string) $result['status'] : 'unknown';
|
|
|
|
|
|
|
|
|
|
|
|
aza_lb_log( "PROVISION OK: sub={$sub_id}, order={$order_id}, key={$license_key}, status={$status}" );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 00:09:28 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Nach Provision (oder bei reaktivem „active“): Perioden an Hetzner senden.
|
|
|
|
|
|
* Läuft mit Priorität 20 nach aza_lb_on_subscription_active (10).
|
|
|
|
|
|
*/
|
|
|
|
|
|
add_action( 'woocommerce_subscription_status_active', 'aza_lb_after_subscription_active_push_period', 20, 1 );
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_after_subscription_active_push_period( $subscription ) {
|
|
|
|
|
|
aza_lb_push_subscription_period_to_backend( $subscription );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Subscription-Objekt laden (WCS oder Flexible Subscriptions / shop_subscription Order).
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param int $sub_id
|
|
|
|
|
|
* @return object|null
|
|
|
|
|
|
*/
|
|
|
|
|
|
function aza_lb_get_subscription_object( $sub_id ) {
|
|
|
|
|
|
$sub_id = (int) $sub_id;
|
|
|
|
|
|
if ( $sub_id <= 0 ) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( function_exists( 'wcs_get_subscription' ) ) {
|
|
|
|
|
|
$sub = wcs_get_subscription( $sub_id );
|
|
|
|
|
|
if ( $sub ) {
|
|
|
|
|
|
return $sub;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( function_exists( 'wc_get_order' ) ) {
|
|
|
|
|
|
$order = wc_get_order( $sub_id );
|
|
|
|
|
|
if ( $order && method_exists( $order, 'get_type' ) ) {
|
|
|
|
|
|
$type = (string) $order->get_type();
|
|
|
|
|
|
if ( $type === 'shop_subscription' || $type === 'subscription' ) {
|
|
|
|
|
|
return $order;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Admin: manueller Perioden-Push (keine Zahlung, nur Hetzner-Sync) ─────────
|
|
|
|
|
|
|
|
|
|
|
|
add_action( 'admin_menu', function () {
|
|
|
|
|
|
add_management_page(
|
|
|
|
|
|
'AZA Period Sync',
|
|
|
|
|
|
'AZA Period Sync',
|
|
|
|
|
|
'manage_options',
|
|
|
|
|
|
'aza-lb-period-sync',
|
|
|
|
|
|
'aza_lb_period_sync_admin_page'
|
|
|
|
|
|
);
|
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
function aza_lb_period_sync_admin_page() {
|
|
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
$notice = '';
|
|
|
|
|
|
if ( isset( $_POST['aza_lb_period_sync_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['aza_lb_period_sync_nonce'] ) ), 'aza_lb_period_sync' ) ) {
|
|
|
|
|
|
$sub_id = isset( $_POST['wc_subscription_id'] ) ? (int) $_POST['wc_subscription_id'] : 0;
|
|
|
|
|
|
$sub = aza_lb_get_subscription_object( $sub_id );
|
|
|
|
|
|
if ( ! $sub ) {
|
|
|
|
|
|
$notice = 'Subscription nicht gefunden oder kein Abo-Typ.';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$result = aza_lb_push_subscription_period_to_backend( $sub );
|
|
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
|
|
|
|
$notice = 'Fehler: ' . esc_html( $result->get_error_message() );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$notice = 'Perioden-Push ausgelöst (sub=' . (int) $sub_id . '). Log: uploads/aza-logs/license-bridge.log';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
?>
|
|
|
|
|
|
<div class="wrap">
|
|
|
|
|
|
<h1>AZA Period Sync (Hetzner)</h1>
|
|
|
|
|
|
<p>Sendet nur <code>current_period_*</code> an <code>POST /wc/subscription_period</code>. Keine Zahlung, keine Kundenänderung.</p>
|
|
|
|
|
|
<?php if ( $notice !== '' ) : ?>
|
|
|
|
|
|
<div class="notice notice-info"><p><?php echo esc_html( $notice ); ?></p></div>
|
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
<form method="post">
|
|
|
|
|
|
<?php wp_nonce_field( 'aza_lb_period_sync', 'aza_lb_period_sync_nonce' ); ?>
|
|
|
|
|
|
<table class="form-table">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th scope="row"><label for="wc_subscription_id">Woo Subscription ID</label></th>
|
|
|
|
|
|
<td><input type="number" name="wc_subscription_id" id="wc_subscription_id" min="1" required class="regular-text" /></td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
<?php submit_button( 'Perioden an Hetzner senden' ); ?>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<?php
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 13:32:32 +02:00
|
|
|
|
// ── Admin-Anzeige ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Zeigt den Lizenzschlüssel in der Admin-Bestellung an.
|
|
|
|
|
|
* Primär aus der Order, sekundär aus verknüpften Subscriptions.
|
|
|
|
|
|
*/
|
|
|
|
|
|
add_action( 'woocommerce_admin_order_data_after_billing_address', function ( $order ) {
|
|
|
|
|
|
if ( ! $order || ! is_object( $order ) || ! method_exists( $order, 'get_id' ) ) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$order_id = (int) $order->get_id();
|
|
|
|
|
|
$key = (string) get_post_meta( $order_id, '_aza_license_key', true );
|
|
|
|
|
|
|
|
|
|
|
|
if ( $key === '' && function_exists( 'wcs_get_subscriptions_for_order' ) ) {
|
|
|
|
|
|
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
|
|
|
|
|
|
if ( is_array( $subscriptions ) ) {
|
|
|
|
|
|
foreach ( $subscriptions as $subscription ) {
|
|
|
|
|
|
if ( ! is_object( $subscription ) || ! method_exists( $subscription, 'get_id' ) ) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$sub_key = (string) get_post_meta( (int) $subscription->get_id(), '_aza_license_key', true );
|
|
|
|
|
|
if ( $sub_key !== '' ) {
|
|
|
|
|
|
$key = $sub_key;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ( $key !== '' ) {
|
|
|
|
|
|
echo '<p><strong>AZA Lizenzschlüssel:</strong> <code>' . esc_html( $key ) . '</code></p>';
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 10, 1 );
|