/** * Plugin Name: LINE WooCommerce Subscription Bridge * Description: Sends LINE notifications for WooCommerce subscriptions. * Version: 3.2 * Author: Marie Kirton-Daike * Copyright 2025 Marie Kirton-Daike * License: This plugin is proprietary software. * Unauthorized use, modification, or distribution is prohibited. */ if (!defined('ABSPATH')) exit; class Line_WC_Bridge { private $settings; public function __construct() { $this->settings = get_option('line_wc_settings'); add_action('woocommerce_subscription_status_updated', [$this, 'on_subscription_status_update'], 10, 3); add_action('rest_api_init', [$this, 'register_line_webhook']); if (is_admin()) { $settings_file = plugin_dir_path(__FILE__) . 'admin/settings-page.php'; if (file_exists($settings_file)) require_once $settings_file; } do_action('qm/debug', "✅ LINE WooCommerce Subscription Bridge loaded."); } // Handle subscription status updates public function on_subscription_status_update($subscription, $new_status, $old_status) { if (!is_a($subscription, 'WC_Subscription')) return; $user_id = $subscription->get_user_id(); if (!$user_id) return; $line_user_id = get_user_meta($user_id, 'sll_lineid', true); if (empty($line_user_id)) return; // Only act if subscription contains the LINE product $has_line_product = false; foreach ($subscription->get_items() as $item) { if ($item->get_product_id() == $this->settings['line_product_id']) { $has_line_product = true; break; } } if (!$has_line_product) return; $subs_url = wc_get_endpoint_url('subscriptions', '', wc_get_page_permalink('myaccount')); // Treat trash as cancelled $status_to_use = ($new_status === 'trash') ? 'cancelled' : $new_status; // Skip active and pending-cancel if ($status_to_use === 'active' || $status_to_use === 'pending-cancel') return; // Map status to admin message $status_message_map = [ 'processing' => $this->settings['processing'] ?? '', 'on-hold' => $this->settings['on_hold'] ?? '', 'cancelled' => $this->settings['cancelled'] ?? '', 'expired' => $this->settings['expired'] ?? '', 'pending-cancel' => $this->settings['pending_cancel'] ?? '' ]; $message = $status_message_map[$status_to_use] ?: "ℹ️ Subscription status: " . ucfirst($status_to_use); // Add subscription URL for relevant statuses if (in_array($status_to_use, ['on-hold', 'expired'])) { $message .= " {$subs_url}"; } self::send_line_push($line_user_id, $message); } // Send LINE message public static function send_line_push($line_user_id, $message, $access_token = null) { $settings = get_option('line_wc_settings'); $access_token = $access_token ?: ($settings['access_token'] ?? ''); if (!$access_token) return; $url = 'https://api.line.me/v2/bot/message/push'; $data = [ 'to' => $line_user_id, 'messages' => [['type'=>'text','text'=>$message]] ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Content-Type: application/json", "Authorization: Bearer {$access_token}" ]); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_exec($ch); curl_close($ch); do_action('qm/debug', "📤 LINE message sent: {$message}"); } // Register webhook public function register_line_webhook() { register_rest_route('line-bridge/v1', '/webhook', [ 'methods' => 'POST', 'callback' => [$this, 'handle_line_webhook'], 'permission_callback' => '__return_true', ]); } // Handle LINE webhook public function handle_line_webhook($request) { $body = $request->get_body(); $data = json_decode($body, true); if (!isset($data['events'])) return rest_ensure_response(['status'=>'no events']); foreach ($data['events'] as $event) { $line_user_id = $event['source']['userId']; if ($event['type'] === 'follow') { $this->send_subscription_status_on_follow($line_user_id); } if ($event['type'] === 'message' && $event['message']['type'] === 'text') { handleMessageForWooCommerceSubscriptions($line_user_id, get_option('line_wc_settings')); } } return rest_ensure_response(['status'=>'ok']); } // Send subscription statuses when a user follows public function send_subscription_status_on_follow($line_user_id) { $users = get_users([ 'meta_key'=>'sll_lineid', 'meta_value'=>$line_user_id, 'number'=>1 ]); if (empty($users)) { $msg = $this->settings['not_linked'] ?: "You do not have a linked subscription."; self::send_line_push($line_user_id, $msg); return; } $user = $users[0]; $subscriptions = wcs_get_users_subscriptions($user->ID, [ 'order' => 'DESC', 'status' => 'any' ]); $line_subs = []; foreach ($subscriptions as $sub) { foreach ($sub->get_items() as $item) { if ($item->get_product_id() == $this->settings['line_product_id']) { $line_subs[] = $sub; } } } if (empty($line_subs)) { $msg = $this->settings['no_active'] ?: "You have no active LINE subscriptions."; self::send_line_push($line_user_id, $msg); return; } $status_message_map = [ 'on-hold' => $this->settings['on_hold'] ?? '', 'cancelled' => $this->settings['cancelled'] ?? '', 'expired' => $this->settings['expired'] ?? '', 'pending-cancel' => $this->settings['pending_cancel'] ?? '', 'trash' => $this->settings['cancelled'] ?? '' // trash as cancelled ]; foreach ($line_subs as $sub) { $status = $sub->get_status(); if ($status === 'active' || $status === 'pending-cancel') continue; $key = ($status === 'trash') ? 'trash' : $status; $message = $status_message_map[$key] ?: "ℹ️ LINE Subscription status: " . ucfirst($status); self::send_line_push($line_user_id, $message); } } } // Handle LINE messages function handleMessageForWooCommerceSubscriptions($line_user_id, $settings) { $users = get_users([ 'meta_key' => 'sll_lineid', 'meta_value' => $line_user_id, 'number' => 1 ]); if (empty($users)) { $msg = $settings['not_linked'] ?? "You do not have a linked subscription."; Line_WC_Bridge::send_line_push($line_user_id, $msg); return; } $user = $users[0]; $subs = wcs_get_users_subscriptions($user->ID, [ 'order' => 'DESC', 'status' => 'any' ]); if (empty($subs)) { $msg = $settings['no_active'] ?? "You have no active subscriptions."; Line_WC_Bridge::send_line_push($line_user_id, $msg); return; } $status_message_map = [ 'on-hold' => $settings['on_hold'] ?? '', 'cancelled' => $settings['cancelled'] ?? '', 'expired' => $settings['expired'] ?? '', 'pending-cancel' => $settings['pending_cancel'] ?? '', 'trash' => $settings['cancelled'] ?? '' // trash as cancelled ]; foreach ($subs as $sub) { $status = $sub->get_status(); if ($status === 'active' || $status === 'pending-cancel') continue; $key = ($status === 'trash') ? 'trash' : $status; $message = $status_message_map[$key] ?: "ℹ️ Subscription status: " . ucfirst($status); Line_WC_Bridge::send_line_push($line_user_id, $message); } } // Instantiate the plugin new Line_WC_Bridge(); /* * Copyright 2025 Marie Kirton-Daike * All rights reserved. * You may not copy, distribute, or use this code without permission. */