PluginProbe
Gift Cards (Gift Vouchers and Packages) (WooCommerce Supported) / trunk
Gift Cards (Gift Vouchers and Packages) (WooCommerce Supported) vtrunk
4.7.5 4.7.4 4.7.3 4.7.2 4.7.1 4.7.0 4.6.9 4.6.8 4.6.7 4.3.7 4.3.8 4.3.9 4.4.0 4.4.1 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1 4.5.2 All 104 releases
gift-voucher / gift-voucher.php

gift-voucher.php in Gift Cards (Gift Vouchers and Packages) (WooCommerce Supported) trunk, at gift-voucher.php

1,942 lines 74.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Plugin Name: Gift Cards (Gift Vouchers and Packages) (WooCommerce Supported)
5 * Description: Let your customers buy gift cards/certificates for your services & products directly on your website.
6 * Plugin URI: https://wp-giftcard.com/
7 * Author: Codemenschen GmbH
8 * Author URI: https://www.codemenschen.at/
9 * Version: 4.7.5
10 * Text Domain: gift-voucher
11 * Domain Path: /languages
12 * License: GNU General Public License v2.0 or later
13 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
14 *
15 * Plugin Variable: wpgiftv
16 *
17 * @package Gift Cards
18 * @author Patrick Fuchshofer
19 * @copyright Copyright (c) 2020
20 *
21 */
22
23 if (!defined('ABSPATH')) exit; // Exit if accessed directly
24
25 define('WPGIFT_VERSION', '4.7.5');
26 define('WPGIFT__MINIMUM_WP_VERSION', '4.0');
27 define('WPGIFT__PLUGIN_DIR', untrailingslashit(plugin_dir_path(__FILE__)));
28 define('WPGIFT__PLUGIN_URL', untrailingslashit(plugins_url(basename(plugin_dir_path(__FILE__)), basename(__FILE__))));
29 define('WPGIFT_PLUGIN_ROOT', plugin_dir_path(__FILE__));
30 define('WPGIFT_SESSION_KEY', 'wpgv-gift-voucher-data');
31 define('WPGIFT_INSTALL_DATE', 'wpgv-install-date');
32 define('WPGIFT_ADMIN_NOTICE_KEY', 'wpgv-hide-notice');
33 define('WPGV_PRODUCT_TYPE_SLUG', 'gift_voucher');
34 define('WPGV_PRODUCT_TYPE_NAME', 'Gift Voucher');
35 define('WPGV_DENOMINATION_ATTRIBUTE_SLUG', 'gift-voucher-amount');
36 define('WPGV_MAX_MESSAGE_CHARACTERS', 500);
37 define('WPGV_RECIPIENT_LIMIT', 999);
38 define('WPGV_STRIPE_WEBHOOK_SECRET_SYNC_VERSION', 1);
39 define('WPGV_GIFT_VOUCHER_NUMBER_META_KEY', 'wpgv_gift_voucher_number');
40 define('WPGV_AMOUNT_META_KEY', 'wpgv_gift_voucher_amount');
41 define('WPGV_YOUR_NAME_META_KEY', 'wpgv_your_name');
42 define('WPGV_RECIPIENT_NAME_META_KEY', 'wpgv_recipient_name');
43 define('WPGV_RECIPIENT_EMAIL_META_KEY', 'wpgv_recipient_email');
44 define('WPGV_YOUR_EMAIL_META_KEY', 'wpgv_your_email');
45 define('WPGV_MESSAGE_META_KEY', 'wpgv_message');
46
47 // Load the Cart/Checkout Blocks integration before WooCommerce initializes its
48 // block registries, while keeping classic pages on the existing flow.
49 function wpgv_load_blocks_integration()
50 {
51 if (!wpgv_is_woocommerce_enable()) {
52 return;
53 }
54
55 require_once WPGIFT__PLUGIN_DIR . '/include/class-wpgv-blocks-integration.php';
56 if (function_exists('wpgv_register_blocks_integration')) {
57 wpgv_register_blocks_integration();
58 }
59 }
60
61 if (did_action('woocommerce_blocks_loaded')) {
62 wpgv_load_blocks_integration();
63 } else {
64 add_action('woocommerce_blocks_loaded', 'wpgv_load_blocks_integration', 10);
65 }
66
67 if (!class_exists('WP_List_Table')) {
68 require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
69 }
70 function wpgv_db_table_exists($table_name)
71 {
72 global $wpdb;
73
74 return (bool) $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($table_name)));
75 }
76
77 function wpgv_db_column_exists($table_name, $column_name)
78 {
79 global $wpdb;
80
81 if (!wpgv_db_table_exists($table_name)) {
82 return false;
83 }
84
85 return (bool) $wpdb->get_var("SHOW COLUMNS FROM `{$table_name}` LIKE '{$column_name}'");
86 }
87
88 function wpgv_db_has_unique_index_for_column($table_name, $column_name)
89 {
90 global $wpdb;
91
92 if (!wpgv_db_table_exists($table_name)) {
93 return false;
94 }
95
96 $column_name = sanitize_key($column_name);
97 if ($column_name === '') {
98 return false;
99 }
100
101 return (bool) $wpdb->get_var(
102 $wpdb->prepare(
103 "SHOW INDEX FROM `{$table_name}` WHERE Column_name = %s AND Non_unique = %d",
104 $column_name,
105 0
106 )
107 );
108 }
109
110 function wpgv_couponcode_has_duplicates()
111 {
112 global $wpdb;
113
114 $table_name = $wpdb->prefix . 'giftvouchers_list';
115 if (!wpgv_db_table_exists($table_name) || !wpgv_db_column_exists($table_name, 'couponcode')) {
116 return false;
117 }
118
119 $duplicate_code = $wpdb->get_var(
120 "SELECT couponcode
121 FROM `{$table_name}`
122 GROUP BY couponcode
123 HAVING COUNT(*) > 1
124 LIMIT 1"
125 );
126
127 return !empty($duplicate_code);
128 }
129
130 function wpgv_normalize_decimal_amount($raw_value)
131 {
132 if (is_int($raw_value) || is_float($raw_value)) {
133 return round((float) $raw_value, 2);
134 }
135
136 $raw_value = sanitize_text_field(wp_unslash((string) $raw_value));
137 $raw_value = preg_replace('/\s+/', '', $raw_value);
138 $raw_value = preg_replace('/[^0-9,.\-]/', '', $raw_value);
139
140 if ($raw_value === '' || $raw_value === null) {
141 return null;
142 }
143
144 if (strpos($raw_value, ',') !== false && strpos($raw_value, '.') !== false) {
145 $raw_value = str_replace(',', '', $raw_value);
146 } elseif (strpos($raw_value, ',') !== false) {
147 $raw_value = str_replace(',', '.', $raw_value);
148 }
149
150 if (!is_numeric($raw_value)) {
151 return null;
152 }
153
154 return round((float) $raw_value, 2);
155 }
156
157 function wpgv_get_stripe_amount_minor_units($raw_amount)
158 {
159 $amount = wpgv_normalize_decimal_amount($raw_amount);
160 if ($amount === null) {
161 return null;
162 }
163
164 return (int) round($amount * 100);
165 }
166
167 function wpgv_get_stripe_order_binding_metadata($voucher_id, $order_key, $raw_amount, $currency_code)
168 {
169 $amount_minor = wpgv_get_stripe_amount_minor_units($raw_amount);
170
171 return array(
172 'voucher_id' => (string) absint($voucher_id),
173 'order_key' => sanitize_text_field((string) $order_key),
174 'amount_minor' => $amount_minor === null ? '' : (string) $amount_minor,
175 'currency' => strtoupper(sanitize_text_field((string) $currency_code)),
176 );
177 }
178
179 function wpgv_normalize_stripe_metadata($metadata)
180 {
181 if ($metadata instanceof \Stripe\StripeObject) {
182 $metadata = $metadata->toArray();
183 } elseif (is_object($metadata)) {
184 $metadata = get_object_vars($metadata);
185 }
186
187 if (!is_array($metadata)) {
188 return array();
189 }
190
191 return array_map(
192 static function ($value) {
193 return sanitize_text_field((string) $value);
194 },
195 $metadata
196 );
197 }
198
199 function wpgv_stripe_metadata_matches_expected($metadata, $expected_metadata)
200 {
201 $metadata = wpgv_normalize_stripe_metadata($metadata);
202
203 foreach ($expected_metadata as $key => $expected_value) {
204 $expected_value = sanitize_text_field((string) $expected_value);
205 if ($expected_value === '' || !isset($metadata[$key]) || !hash_equals($expected_value, (string) $metadata[$key])) {
206 return false;
207 }
208 }
209
210 return true;
211 }
212
213 function wpgv_is_stripe_checkout_session_bound_to_voucher($checkout_session, $expected_metadata)
214 {
215 if (!is_object($checkout_session) || !method_exists($checkout_session, 'jsonSerialize')) {
216 return false;
217 }
218
219 $session_data = $checkout_session->jsonSerialize();
220 $expected_amount_minor = isset($expected_metadata['amount_minor']) ? (int) $expected_metadata['amount_minor'] : null;
221 $expected_currency = isset($expected_metadata['currency']) ? strtoupper($expected_metadata['currency']) : '';
222 $expected_voucher_id = isset($expected_metadata['voucher_id']) ? (string) $expected_metadata['voucher_id'] : '';
223
224 if (($session_data['mode'] ?? '') !== 'payment') {
225 return false;
226 }
227
228 if (($session_data['payment_status'] ?? '') !== 'paid') {
229 return false;
230 }
231
232 if (isset($session_data['status']) && $session_data['status'] !== 'complete') {
233 return false;
234 }
235
236 if (
237 isset($session_data['client_reference_id']) &&
238 $expected_voucher_id !== '' &&
239 (string) $session_data['client_reference_id'] !== $expected_voucher_id
240 ) {
241 return false;
242 }
243
244 if ($expected_amount_minor === null || !isset($session_data['amount_total']) || (int) $session_data['amount_total'] !== $expected_amount_minor) {
245 return false;
246 }
247
248 if ($expected_currency === '' || strtoupper((string) ($session_data['currency'] ?? '')) !== $expected_currency) {
249 return false;
250 }
251
252 return wpgv_stripe_metadata_matches_expected($session_data['metadata'] ?? array(), $expected_metadata);
253 }
254
255 function wpgv_is_stripe_payment_intent_bound_to_voucher($payment_intent, $expected_metadata)
256 {
257 if (!is_object($payment_intent)) {
258 return false;
259 }
260
261 $expected_amount_minor = isset($expected_metadata['amount_minor']) ? (int) $expected_metadata['amount_minor'] : null;
262 $expected_currency = isset($expected_metadata['currency']) ? strtoupper($expected_metadata['currency']) : '';
263
264 if (($payment_intent->status ?? '') !== 'succeeded') {
265 return false;
266 }
267
268 if ($expected_amount_minor === null || !isset($payment_intent->amount) || (int) $payment_intent->amount !== $expected_amount_minor) {
269 return false;
270 }
271
272 if ($expected_currency === '' || strtoupper((string) ($payment_intent->currency ?? '')) !== $expected_currency) {
273 return false;
274 }
275
276 return wpgv_stripe_metadata_matches_expected($payment_intent->metadata ?? array(), $expected_metadata);
277 }
278
279 /**
280 * Validate a legacy Stripe Checkout Session which predates binding metadata.
281 * This is used only by the customer success-page fallback, never by webhooks.
282 */
283 function wpgv_is_legacy_stripe_checkout_session_bound_to_voucher($checkout_session, $amount_minor, $currency)
284 {
285 if (!is_object($checkout_session) || !method_exists($checkout_session, 'jsonSerialize')) {
286 return false;
287 }
288
289 $session_data = $checkout_session->jsonSerialize();
290 return ($session_data['mode'] ?? '') === 'payment'
291 && ($session_data['payment_status'] ?? '') === 'paid'
292 && (!isset($session_data['status']) || $session_data['status'] === 'complete')
293 && isset($session_data['amount_total'])
294 && (int) $session_data['amount_total'] === (int) $amount_minor
295 && strtoupper((string) ($session_data['currency'] ?? '')) === strtoupper((string) $currency);
296 }
297
298 function wpgv_is_legacy_stripe_payment_intent_bound_to_voucher($payment_intent, $amount_minor, $currency)
299 {
300 return is_object($payment_intent)
301 && ($payment_intent->status ?? '') === 'succeeded'
302 && isset($payment_intent->amount)
303 && (int) $payment_intent->amount === (int) $amount_minor
304 && strtoupper((string) ($payment_intent->currency ?? '')) === strtoupper((string) $currency);
305 }
306
307 function wpgv_is_stripe_webhook_fulfillment_enabled()
308 {
309 return (bool) get_option('wpgv_stripe_webhook_fulfillment_enabled', 1);
310 }
311
312 function wpgv_get_public_voucher_value_limits($setting_options)
313 {
314 $min_amount = isset($setting_options->voucher_min_value) && $setting_options->voucher_min_value !== ''
315 ? max(0, (float) $setting_options->voucher_min_value)
316 : 1.0;
317 $max_amount = isset($setting_options->voucher_max_value) && $setting_options->voucher_max_value !== ''
318 ? max(0, (float) $setting_options->voucher_max_value)
319 : 10000.0;
320
321 if ($max_amount > 0 && $max_amount < $min_amount) {
322 $max_amount = $min_amount;
323 }
324
325 return array($min_amount, $max_amount);
326 }
327
328 function wpgv_validate_public_voucher_amount($raw_value, $setting_options)
329 {
330 $amount = wpgv_normalize_decimal_amount($raw_value);
331 if ($amount === null) {
332 return new WP_Error('wpgv_invalid_amount', __('Invalid voucher amount supplied.', 'gift-voucher'));
333 }
334
335 list($min_amount, $max_amount) = wpgv_get_public_voucher_value_limits($setting_options);
336
337 if ($amount <= 0) {
338 return new WP_Error('wpgv_invalid_amount', __('Voucher amount must be greater than zero.', 'gift-voucher'));
339 }
340
341 if ($min_amount > 0 && $amount < $min_amount) {
342 return new WP_Error(
343 'wpgv_amount_too_low',
344 sprintf(
345 /* translators: %s: minimum voucher value */
346 __('Voucher amount must be at least %s.', 'gift-voucher'),
347 wpgv_price_format($min_amount)
348 )
349 );
350 }
351
352 if ($max_amount > 0 && $amount > $max_amount) {
353 return new WP_Error(
354 'wpgv_amount_too_high',
355 sprintf(
356 /* translators: %s: maximum voucher value */
357 __('Voucher amount must not exceed %s.', 'gift-voucher'),
358 wpgv_price_format($max_amount)
359 )
360 );
361 }
362
363 return $amount;
364 }
365
366 function wpgv_get_configured_extra_charge($option_name)
367 {
368 $extra_charge = get_option($option_name);
369 $amount = wpgv_normalize_decimal_amount($extra_charge);
370
371 return ($amount === null || $amount < 0) ? 0.0 : $amount;
372 }
373
374 function wpgv_get_shipping_charge_amount($shipping_type, $shipping_method, $setting_options)
375 {
376 $shipping_type = sanitize_text_field((string) $shipping_type);
377 $shipping_method = sanitize_text_field((string) $shipping_method);
378
379 if ($shipping_type !== 'shipping_as_post') {
380 return 0.0;
381 }
382
383 if ($shipping_method === '') {
384 return new WP_Error('wpgv_invalid_shipping_method', __('Invalid shipping method selected.', 'gift-voucher'));
385 }
386
387 $configured_methods = isset($setting_options->shipping_method) ? (string) $setting_options->shipping_method : '';
388 foreach (explode(',', $configured_methods) as $method) {
389 if ($method === '') {
390 continue;
391 }
392
393 $parts = explode(':', $method, 2);
394 $configured_amount = wpgv_normalize_decimal_amount($parts[0] ?? '');
395 $configured_label = trim(stripslashes($parts[1] ?? ''));
396
397 if ($configured_label !== '' && hash_equals($configured_label, $shipping_method)) {
398 return ($configured_amount === null || $configured_amount < 0) ? 0.0 : $configured_amount;
399 }
400 }
401
402 return new WP_Error('wpgv_invalid_shipping_method', __('Invalid shipping method selected.', 'gift-voucher'));
403 }
404
405 /**
406 * Validate a payment method posted by an unauthenticated buyer.
407 *
408 * The public order handlers write the posted value straight into
409 * `giftvouchers_list.pay_method`, and that column decides how the success page
410 * verifies the order. 'Per Invoice' in particular skips payment verification by
411 * design, so an unvalidated value lets a buyer select a gateway the shop owner
412 * has switched off and receive the voucher PDF without paying.
413 *
414 * @param string $payment_method Raw payment method from the request.
415 * @param object $setting_options Row from the giftvouchers_setting table.
416 * @return string|WP_Error The accepted payment method, or WP_Error when disabled.
417 */
418 function wpgv_validate_public_payment_method($payment_method, $setting_options)
419 {
420 $payment_method = trim(sanitize_text_field((string) $payment_method));
421
422 $gateway_settings = array(
423 'Paypal' => 'paypal',
424 'Sofort' => 'sofort',
425 'Stripe' => 'stripe',
426 'Per Invoice' => 'per_invoice',
427 );
428
429 if (!isset($gateway_settings[$payment_method])) {
430 return new WP_Error('wpgv_invalid_payment_method', __('Invalid payment method selected.', 'gift-voucher'));
431 }
432
433 // The rest of the plugin reads these columns as truthy flags, so keep the
434 // same semantics here instead of requiring an exact integer 1.
435 $setting_key = $gateway_settings[$payment_method];
436 $is_enabled = is_object($setting_options) && !empty($setting_options->$setting_key);
437
438 if (!$is_enabled) {
439 return new WP_Error('wpgv_payment_method_disabled', __('The selected payment method is not available.', 'gift-voucher'));
440 }
441
442 return $payment_method;
443 }
444
445 function wpgv_couponcode_exists($couponcode, $exclude_id = 0)
446 {
447 global $wpdb;
448
449 $couponcode = sanitize_text_field((string) $couponcode);
450 if ($couponcode === '') {
451 return false;
452 }
453
454 $table_name = $wpdb->prefix . 'giftvouchers_list';
455 if ($exclude_id > 0) {
456 $existing_id = $wpdb->get_var(
457 $wpdb->prepare(
458 "SELECT id FROM `{$table_name}` WHERE couponcode = %s AND id != %d LIMIT 1",
459 $couponcode,
460 absint($exclude_id)
461 )
462 );
463 } else {
464 $existing_id = $wpdb->get_var(
465 $wpdb->prepare(
466 "SELECT id FROM `{$table_name}` WHERE couponcode = %s LIMIT 1",
467 $couponcode
468 )
469 );
470 }
471
472 return !empty($existing_id);
473 }
474
475 function wpgv_generate_unique_couponcode($max_attempts = 50)
476 {
477 $max_attempts = max(1, (int) $max_attempts);
478
479 for ($attempt = 0; $attempt < $max_attempts; $attempt++) {
480 try {
481 $couponcode = (string) random_int(1000000000000000, 9999999999999999);
482 } catch (Exception $exception) {
483 return new WP_Error('wpgv_couponcode_rng_failed', __('Unable to generate a secure voucher code.', 'gift-voucher'));
484 }
485
486 if (!wpgv_couponcode_exists($couponcode)) {
487 return $couponcode;
488 }
489 }
490
491 return new WP_Error('wpgv_couponcode_generation_failed', __('Unable to generate a unique voucher code.', 'gift-voucher'));
492 }
493
494 function wpgv_sanitize_voucher_pdf_basename($value)
495 {
496 $value = sanitize_file_name((string) $value);
497 $value = preg_replace('/[^A-Za-z0-9_-]/', '', $value);
498
499 return trim((string) $value, '._-');
500 }
501
502 function wpgv_get_voucher_pdf_filename($voucherpdf_link, $suffix = '')
503 {
504 $voucherpdf_link = wpgv_sanitize_voucher_pdf_basename($voucherpdf_link);
505 $suffix = preg_replace('/[^A-Za-z0-9_-]/', '', (string) $suffix);
506
507 if ($voucherpdf_link === '') {
508 return '';
509 }
510
511 return $voucherpdf_link . $suffix . '.pdf';
512 }
513
514 function wpgv_get_voucher_pdf_url($voucherpdf_link, $suffix = '')
515 {
516 $filename = wpgv_get_voucher_pdf_filename($voucherpdf_link, $suffix);
517 if ($filename === '') {
518 return '';
519 }
520
521 $upload = wp_get_upload_dir();
522
523 return trailingslashit($upload['baseurl']) . 'voucherpdfuploads/' . rawurlencode($filename);
524 }
525
526 function wpgv_get_voucher_pdf_path($voucherpdf_link, $suffix = '')
527 {
528 $filename = wpgv_get_voucher_pdf_filename($voucherpdf_link, $suffix);
529 if ($filename === '') {
530 return '';
531 }
532
533 return wpgv_pdf_get_upload_path($filename);
534 }
535
536 function wpgv_get_voucher_row_by_id($voucher_id)
537 {
538 global $wpdb;
539
540 $voucher_id = absint($voucher_id);
541 if ($voucher_id <= 0) {
542 return null;
543 }
544
545 return $wpdb->get_row(
546 $wpdb->prepare(
547 "SELECT * FROM `{$wpdb->prefix}giftvouchers_list` WHERE id = %d",
548 $voucher_id
549 )
550 );
551 }
552
553 function wpgv_current_user_can_view_voucher_details($voucher_id)
554 {
555 $voucher = wpgv_get_voucher_row_by_id($voucher_id);
556 if (!$voucher) {
557 return false;
558 }
559
560 if (current_user_can('manage_options') || current_user_can('manage_woocommerce')) {
561 return true;
562 }
563
564 if (!is_user_logged_in()) {
565 return false;
566 }
567
568 $current_user = wp_get_current_user();
569 $current_email = isset($current_user->user_email) ? strtolower(trim((string) $current_user->user_email)) : '';
570 if ($current_email === '') {
571 return false;
572 }
573
574 $owner_emails = array_filter(array(
575 strtolower(trim((string) $voucher->email)),
576 strtolower(trim((string) $voucher->shipping_email)),
577 ));
578
579 return in_array($current_email, $owner_emails, true);
580 }
581
582 function wpgv_is_woocommerce_enable()
583 {
584 global $wpdb;
585 // Defensive: during activation/upgrade the plugin tables may not exist yet.
586 // Check for the settings table before querying it to avoid warnings / output.
587 $table_name = $wpdb->prefix . 'giftvouchers_setting';
588
589 if (!wpgv_db_table_exists($table_name)) {
590 // Table doesn't exist yet (activation / install context). Treat WooCommerce as disabled so
591 // the conditional requires below won't include admin front-end files that may produce output.
592 return false;
593 }
594
595 $options = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table_name} WHERE id = %d", 1));
596 if (!$options) {
597 return false;
598 }
599
600 return !empty($options->is_woocommerce_enable);
601 }
602
603 /**
604 * True only when the plugin voucher form is enabled within active WooCommerce integration.
605 *
606 * @return bool
607 */
608 function wpgv_is_woocommerce_redeem_form_enabled()
609 {
610 return wpgv_is_woocommerce_enable() && (bool) get_option('wpgv_enable_woocommerce_redeem_form', 0);
611 }
612
613 /**
614 * True when plugin vouchers may be entered through WooCommerce's native coupon UI.
615 *
616 * @return bool
617 */
618 function wpgv_is_native_coupon_voucher_redemption_enabled()
619 {
620 return wpgv_is_woocommerce_enable() && !wpgv_is_woocommerce_redeem_form_enabled();
621 }
622
623 /**
624 * Ensure the Stripe webhook signing secret has a canonical option.
625 *
626 * This is intentionally a one-time admin migration. The secret cannot be
627 * fetched from Stripe automatically; an administrator must enter it in the
628 * Payment settings page. Existing non-empty values are never overwritten.
629 */
630 function wpgv_sync_stripe_webhook_secret_option()
631 {
632 $sync_version = (int) get_option('wpgv_stripe_webhook_secret_sync_version', 0);
633 if ($sync_version >= WPGV_STRIPE_WEBHOOK_SECRET_SYNC_VERSION) {
634 return;
635 }
636
637 if (get_option('wpgv_stripe_webhook_key', null) === null) {
638 add_option('wpgv_stripe_webhook_key', '', '', false);
639 }
640
641 update_option(
642 'wpgv_stripe_webhook_secret_sync_version',
643 WPGV_STRIPE_WEBHOOK_SECRET_SYNC_VERSION,
644 false
645 );
646 }
647
648 // Load translations and plugin files on init to avoid early translation notice (WP 6.7+)
649 add_action('init', function() {
650 load_plugin_textdomain('gift-voucher', false, dirname(plugin_basename(__FILE__)) . '/languages');
651
652 require_once(WPGIFT__PLUGIN_DIR . '/vendor/autoload.php');
653 require_once(WPGIFT__PLUGIN_DIR . '/vendor/sofort/payment/sofortLibSofortueberweisung.inc.php');
654 require_once(WPGIFT__PLUGIN_DIR . '/admin.php');
655 require_once(WPGIFT__PLUGIN_DIR . '/front.php');
656 require_once(WPGIFT__PLUGIN_DIR . '/giftitems.php');
657 require_once(WPGIFT__PLUGIN_DIR . '/include/pdf-wrapper.php');
658 require_once(WPGIFT__PLUGIN_DIR . '/classes/voucher.php');
659 require_once(WPGIFT__PLUGIN_DIR . '/classes/template.php');
660 require_once(WPGIFT__PLUGIN_DIR . '/classes/page_template.php');
661 require_once(WPGIFT__PLUGIN_DIR . '/include/wpgv_voucher_pdf.php');
662 require_once(WPGIFT__PLUGIN_DIR . '/include/wpgv_item_pdf.php');
663 require_once(WPGIFT__PLUGIN_DIR . '/include/voucher_posttype.php');
664 // If we are including this file during 'init' after priority 0 executed, the
665 // post type registration hooks added in voucher_posttype.php won't have run
666 // on this request. Register them immediately if needed so admin pages work.
667 if (!post_type_exists('wpgv_voucher_product') && function_exists('wpgv_voucher_product_function')) {
668 wpgv_voucher_product_function();
669 }
670 if (!taxonomy_exists('wpgv_voucher_category') && function_exists('wpgv_voucher_category_function')) {
671 wpgv_voucher_category_function();
672 }
673 if (!post_type_exists('voucher_template') && function_exists('codemenschen_voucher_template')) {
674 codemenschen_voucher_template();
675 }
676 if (!taxonomy_exists('category_voucher_template') && function_exists('codemenschen_voucher_template_category')) {
677 codemenschen_voucher_template_category();
678 }
679
680 require_once(WPGIFT__PLUGIN_DIR . '/include/voucher_metabox.php');
681 require_once(WPGIFT__PLUGIN_DIR . '/include/voucher-shortcodes.php');
682 require_once(WPGIFT__PLUGIN_DIR . '/include/stripewebhook.php');
683 require_once(WPGIFT__PLUGIN_DIR . '/classes/wpgv-gift-voucher.php');
684 require_once(WPGIFT__PLUGIN_DIR . '/classes/wpgv-gift-voucher-activity.php');
685 require_once(WPGIFT__PLUGIN_DIR . '/include/stripe-fulfillment.php');
686 require_once(WPGIFT__PLUGIN_DIR . '/giftcard.php');
687 require_once(WPGIFT__PLUGIN_DIR . '/include/wpgv_giftcard_pdf.php');
688 require_once(WPGIFT__PLUGIN_DIR . '/include/edit-order-voucher.php');
689 require_once(WPGIFT__PLUGIN_DIR . '/include/receipt-functions.php');
690 require_once(WPGIFT__PLUGIN_DIR . '/include/admin_regenerate_modern_pdf.php');
691 require_once(WPGIFT__PLUGIN_DIR . '/include/admin_regenerate_standard_pdf.php');
692 require_once(WPGIFT__PLUGIN_DIR . '/include/db-transfer-common.php');
693 require_once(WPGIFT__PLUGIN_DIR . '/include/export_giftcard_db.php');
694 require_once(WPGIFT__PLUGIN_DIR . '/include/import_giftcard_db.php');
695
696 if (wpgv_is_woocommerce_enable()) {
697 require_once(WPGIFT__PLUGIN_DIR . '/classes/wpgv-voucher-product-list.php');
698 require_once(WPGIFT__PLUGIN_DIR . '/include/wc_wpgv_voucher_pdf.php');
699 require_once(WPGIFT__PLUGIN_DIR . '/classes/wpgv-check-plugin-active.php');
700 }
701 }, 1);
702
703 add_action('init', function () {
704 WPGiftVoucherAdminPages::get_instance();
705 });
706
707
708 add_action('admin_init', function () {
709
710 wpgv_sync_stripe_webhook_secret_option();
711
712 global $wpdb;
713 $giftvouchers_list = $wpdb->prefix . 'giftvouchers_list';
714 $giftvouchers_setting = $wpdb->prefix . 'giftvouchers_setting';
715
716 if (wpgv_db_table_exists($giftvouchers_list)) {
717 if (!wpgv_db_column_exists($giftvouchers_list, 'check_send_mail')) {
718 $wpdb->query("ALTER TABLE `{$giftvouchers_list}` ADD check_send_mail VARCHAR(30) NOT NULL DEFAULT 'unsent'");
719 }
720
721 if (!wpgv_db_column_exists($giftvouchers_list, 'product_id')) {
722 $wpdb->query("ALTER TABLE `{$giftvouchers_list}` ADD product_id BIGINT(20) UNSIGNED DEFAULT NULL");
723 }
724
725 if (!wpgv_db_column_exists($giftvouchers_list, 'order_id')) {
726 $wpdb->query("ALTER TABLE `{$giftvouchers_list}` ADD order_id BIGINT(20) UNSIGNED DEFAULT NULL");
727 }
728
729 if (!wpgv_db_column_exists($giftvouchers_list, 'note_order')) {
730 $wpdb->query("ALTER TABLE `{$giftvouchers_list}` ADD note_order VARCHAR(255) NOT NULL");
731 }
732
733 if (!wpgv_db_has_unique_index_for_column($giftvouchers_list, 'couponcode') && !wpgv_couponcode_has_duplicates()) {
734 $wpdb->query("ALTER TABLE `{$giftvouchers_list}` ADD UNIQUE KEY `wpgv_couponcode_unique` (`couponcode`)");
735 }
736 }
737
738 if (wpgv_db_table_exists($giftvouchers_setting)) {
739 if (!wpgv_db_column_exists($giftvouchers_setting, 'is_order_form_enable')) {
740 $wpdb->query("ALTER TABLE `{$giftvouchers_setting}` ADD is_order_form_enable TINYINT(1) DEFAULT 1");
741 }
742
743 if (wpgv_db_column_exists($giftvouchers_setting, 'portrait_mode_templates')) {
744 $portrait_mode_templates = $wpdb->get_var("SELECT portrait_mode_templates FROM `{$giftvouchers_setting}` LIMIT 1");
745
746 if (empty($portrait_mode_templates) || $portrait_mode_templates === '0') {
747 $template_portail = 'template-voucher-portail-1.png, template-voucher-portail-2.png, template-voucher-portail-6.png';
748
749 $wpdb->query($wpdb->prepare("
750 UPDATE `{$giftvouchers_setting}`
751 SET portrait_mode_templates = %s
752 WHERE portrait_mode_templates = '' OR portrait_mode_templates IS NULL OR portrait_mode_templates = '0'
753 ", $template_portail));
754 }
755 }
756 }
757
758 // Seed default quotes JSON if not exists
759 $existing_quotes = get_option('wpgv_quotes', '');
760 if ($existing_quotes === '' || $existing_quotes === false) {
761 $default_quotes = array(
762 __('Happy Birthday! Wishing you a day filled with joy and laughter.', 'gift-voucher'),
763 __('Congratulations on your special day! Enjoy this little treat.', 'gift-voucher'),
764 __('Thank you for everything you do. Hope you love this gift!', 'gift-voucher'),
765 __('Wishing you all the best—may this gift bring a smile to your face!', 'gift-voucher'),
766 __('With love and warm wishes—enjoy every moment!', 'gift-voucher'),
767 );
768 update_option('wpgv_quotes', wp_json_encode($default_quotes));
769 }
770
771 if (!current_user_can('manage_options')) {
772 return false;
773 }
774
775 require_once(WPGIFT__PLUGIN_DIR . '/classes/class-nag.php');
776
777 // Setup nag
778 $nag = new WPGIFT_Nag();
779 $nag->setup();
780 });
781
782
783
784 add_action('woocommerce_init', 'wpgv_files_loaded', 10, 1);
785 function wpgv_files_loaded()
786 {
787 global $wpdb;
788 $options = $wpdb->get_row("SELECT * FROM `{$wpdb->prefix}giftvouchers_setting` WHERE id = 1");
789
790 if ($options->is_woocommerce_enable) {
791 require_once(WPGIFT__PLUGIN_DIR . '/include/redeem-voucher.php');
792 require_once(WPGIFT__PLUGIN_DIR . '/classes/wc-order-item-wpgv-gift-voucher.php');
793 require_once(WPGIFT__PLUGIN_DIR . '/classes/data-stores/wc-order-item-wpgv-gift-voucher-data-store.php');
794
795 require_once(WPGIFT__PLUGIN_DIR . '/classes/wpgv-gift-voucher-product.php');
796 require_once(WPGIFT__PLUGIN_DIR . '/classes/wpgv-wc-product-gift-voucher.php');
797 require_once(WPGIFT__PLUGIN_DIR . '/include/wpgv-product-settings.php');
798
799 if (is_admin()) {
800 require_once(WPGIFT__PLUGIN_DIR . '/admin/wpgv-gift-voucher-admin.php');
801 }
802 }
803 }
804
805 add_action('init', 'wpgv_voucher_imagesize_setup');
806 function wpgv_voucher_imagesize_setup()
807 {
808 add_image_size('voucher-thumb', 300);
809 add_image_size('voucher-medium', 450);
810 }
811
812 /** Setting menu link */
813 add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'wpgv_settings_page_link');
814 function wpgv_settings_page_link($links)
815 {
816 $links[] = '<a href="' . admin_url('admin.php?page=voucher-setting') . '">' . __('Settings', 'gift-voucher') . '</a>';
817 $links[] = '<a href="https://www.wp-giftcard.com/docs/documentation/" target="_blank">' . __('Documentation', 'gift-voucher') . '</a>';
818 return $links;
819 }
820
821 function wpgv_front_enqueue()
822 {
823
824 global $wpdb;
825 $setting_options = $wpdb->get_row("SELECT * FROM `{$wpdb->prefix}giftvouchers_setting` WHERE id = 1");
826
827
828 $translations = array(
829 'ajaxurl' => admin_url('admin-ajax.php'),
830 'not_equal_email' => __('This email must be different from above email.', 'gift-voucher'),
831 'select_template' => __('Please select voucher template', 'gift-voucher'),
832 'accept_terms' => __('Please accept the terms and conditions', 'gift-voucher'),
833 'finish' => __('Finish', 'gift-voucher'),
834 'next' => __('Continue', 'gift-voucher'),
835 'previous' => __('Back', 'gift-voucher'),
836 'submitted' => __('Submitted!', 'gift-voucher'),
837 'error_occur' => __('Error occurred', 'gift-voucher'),
838 'total_character' => __('Total Characters', 'gift-voucher'),
839 'via_post' => __('Shipping via Post', 'gift-voucher'),
840 'via_email' => __('Shipping via Email', 'gift-voucher'),
841 'checkemail' => __('Please check email address.', 'gift-voucher'),
842 'required' => __('This field is required.', 'gift-voucher'),
843 'remote' => __('Please fix this field.', 'gift-voucher'),
844 'maxlength' => __('Please enter no more than {0} characters.', 'gift-voucher'),
845 'email' => __('Please enter a valid email address.', 'gift-voucher'),
846 'max' => __('Please enter a value less than or equal to {0}.', 'gift-voucher'),
847 'min' => __('Please enter a value greater than or equal to {0}.', 'gift-voucher'),
848 'preview' => __('This is Preview!', 'gift-voucher'),
849 'text_value' => __('Value', 'gift-voucher'),
850 'nonce' => wp_create_nonce('wpgv_nonce_action'),
851 'gift_voucher_session_nonce' => wp_create_nonce('wpgv_gift_voucher_session'),
852 );
853 wp_register_style('wpgv-voucher-style', WPGIFT__PLUGIN_URL . '/assets/css/voucher-style.css');
854 wp_register_style('wpgv-item-style', WPGIFT__PLUGIN_URL . '/assets/css/item-style.css');
855 wp_register_style('wpgv-slick-css', WPGIFT__PLUGIN_URL . '/assets/css/slick.css');
856 wp_register_style('wpgv-fontawesome-css', WPGIFT__PLUGIN_URL . '/assets/css/font-awesome.min.css');
857 wp_register_style('wpgv-voucher-template-fonts-css', WPGIFT__PLUGIN_URL . '/assets/css/voucher-template-fonts.css');
858 wp_register_style('wpgv-voucher-template-style-css', WPGIFT__PLUGIN_URL . '/assets/css/voucher-template-style.css');
859 wp_register_script('wpgv-konva-min-js', WPGIFT__PLUGIN_URL . '/assets/js/konva.min.js', array('jquery'), '1.17.0', true);
860 wp_register_script('wpgv-jspdf-js', WPGIFT__PLUGIN_URL . '/assets/js/jspdf.debug.js', array('jquery'), '1.5.3', true);
861 wp_register_script('wpgv-jquery-validate', WPGIFT__PLUGIN_URL . '/assets/js/jquery.validate.min.js', array('jquery'), '1.17.0', true);
862 wp_register_script('wpgv-jquery-steps', WPGIFT__PLUGIN_URL . '/assets/js/jquery.steps.min.js', array('jquery'), '1.1.0', true);
863 wp_register_script('wpgv-stripe-js', WPGIFT__PLUGIN_URL . '/assets/js/stripe-v3.js', array('jquery'), '3.0.0', true);
864 wp_register_script('wpgv-voucher-script', WPGIFT__PLUGIN_URL . '/assets/js/voucher-script.js', array('jquery'), '3.3.9.1', true);
865 wp_register_script('wpgv-item-script', WPGIFT__PLUGIN_URL . '/assets/js/item-script.js', array('jquery'), '3.3.9.1', true);
866 wp_register_script('wpgv-woocommerce-script', WPGIFT__PLUGIN_URL . '/assets/js/woocommerce-script.js', array('jquery'), '3.3.9.1', true);
867 wp_register_script('wpgv-voucher-product', WPGIFT__PLUGIN_URL . '/assets/js/wpgv-voucher-product.js', array('jquery'), WPGIFT_VERSION, true);
868 wp_register_script('wpgv-slick-script', WPGIFT__PLUGIN_URL . '/assets/js/slick.min.js', array('jquery'), WPGIFT_VERSION, true);
869 wp_register_script('wpgv-voucher-template-script', WPGIFT__PLUGIN_URL . '/assets/js/voucher-template-script.js', array('jquery'), WPGIFT_VERSION, true);
870 if ($setting_options->test_mode) {
871 wp_register_script('wpgv-paypal-js', 'https://www.paypal.com/sdk/js?client-id=sb&currency=' . $setting_options->currency_code, array('jquery'), '5.0.0', true);
872 } else {
873 $wpgv_paypal_client_id = get_option('wpgv_paypal_client_id') ? get_option('wpgv_paypal_client_id') : '';
874 wp_register_script('wpgv-paypal-js', 'https://www.paypal.com/sdk/js?client-id=' . $wpgv_paypal_client_id . '&currency=' . $setting_options->currency_code, array('jquery'), '5.0.0', true);
875 }
876 if (wpgv_is_woocommerce_enable()) {
877 $check_plugin = new WPGV_Check_Plugin_Active();
878 if ($check_plugin->wpgv_check_woo_active()) {
879 wp_localize_script('wpgv-voucher-product', 'wpgv', array(
880 'ajaxurl' => admin_url('admin-ajax.php', 'relative'),
881 'denomination_attribute_slug' => WPGV_DENOMINATION_ATTRIBUTE_SLUG,
882 'decimal_places' => wc_get_price_decimals(),
883 'max_message_characters' => WPGV_MAX_MESSAGE_CHARACTERS,
884 'i18n' => array(
885 'custom_amount_required_error' => __('Required', 'gift-voucher'),
886 // translators: %s is the currency symbol.
887 'min_amount_error' => sprintf(__('Minimum amount is %s', 'gift-voucher'), get_woocommerce_currency_symbol()),
888 // translators: %s is the currency symbol.
889 'max_amount_error' => sprintf(__('Maximum amount is %s', 'gift-voucher'), get_woocommerce_currency_symbol()),
890 'invalid_recipient_error' => __('The "To" field should only contain email addresses. The following recipients do not look like valid email addresses:', 'gift-voucher'),
891 ),
892 'nonces' => array(
893 'check_balance' => wp_create_nonce('wpgv-gift-cards-check-balance'),
894 'apply_gift_card' => wp_create_nonce('wpgv-gift-cards-apply-gift-card'),
895 'remove_card' => wp_create_nonce('wpgv-gift-cards-remove-card'),
896 )
897 ));
898 }
899 }
900 wp_localize_script('wpgv-voucher-script', 'frontend_ajax_object', $translations);
901 wp_localize_script('wpgv-voucher-template-script', 'frontend_ajax_object', $translations);
902 wp_localize_script('wpgv-item-script', 'frontend_ajax_object', $translations);
903 wp_localize_script('wpgv-woocommerce-script', 'frontend_ajax_object', $translations);
904 }
905
906 add_action('wp_enqueue_scripts', 'wpgv_front_enqueue');
907
908 function wpgv_plugin_activation()
909 {
910 global $wpdb;
911 global $jal_db_version;
912
913 $giftvouchers_setting = $wpdb->prefix . 'giftvouchers_setting';
914 $giftvouchers_list = $wpdb->prefix . 'giftvouchers_list';
915 $giftvouchers_template = $wpdb->prefix . 'giftvouchers_template';
916 $giftvouchers_activity = $wpdb->prefix . 'giftvouchers_activity';
917
918 $charset_collate = $wpdb->get_charset_collate();
919
920 $giftvouchers_setting_sql = "CREATE TABLE $giftvouchers_setting (
921 id int(11) NOT NULL AUTO_INCREMENT,
922 is_woocommerce_enable int(1) DEFAULT 0,
923 is_style_choose_enable int(1) DEFAULT 0,
924 is_order_form_enable int(1) DEFAULT 1,
925 voucher_style varchar(100) DEFAULT 0,
926 company_name varchar(255) DEFAULT NULL,
927 currency_code varchar(10) DEFAULT NULL,
928 currency varchar(10) DEFAULT NULL,
929 currency_position varchar(10) DEFAULT NULL,
930 voucher_bgcolor varchar(6) DEFAULT NULL,
931 voucher_color varchar(6) DEFAULT NULL,
932 template_col int(2) DEFAULT 3,
933 voucher_min_value int(4) DEFAULT NULL,
934 voucher_max_value int(6) DEFAULT NULL,
935 voucher_expiry_type varchar(6) DEFAULT NULL,
936 voucher_expiry varchar(10) DEFAULT NULL,
937 voucher_terms_note text DEFAULT NULL,
938 custom_loader text DEFAULT NULL,
939 pdf_footer_url varchar(255) DEFAULT NULL,
940 pdf_footer_email varchar(255) DEFAULT NULL,
941 post_shipping int(1) DEFAULT NULL,
942 shipping_method text DEFAULT NULL,
943 preview_button int(1) DEFAULT 1,
944 paypal int(11) DEFAULT NULL,
945 sofort int(11) DEFAULT NULL,
946 stripe int(11) DEFAULT NULL,
947 paypal_email varchar(100) DEFAULT NULL,
948 sofort_configure_key varchar(100) DEFAULT NULL,
949 reason_for_payment varchar(100) DEFAULT NULL,
950 stripe_publishable_key varchar(255) DEFAULT NULL,
951 stripe_secret_key varchar(255) DEFAULT NULL,
952 sender_name varchar(100) DEFAULT NULL,
953 sender_email varchar(100) DEFAULT NULL,
954 test_mode int(10) NOT NULL,
955 per_invoice int(10) NOT NULL,
956 bank_info longtext,
957 landscape_mode_templates text DEFAULT NULL,
958 portrait_mode_templates text DEFAULT NULL,
959 PRIMARY KEY (id)
960 ) $charset_collate;";
961
962 $giftvouchers_list_sql = "CREATE TABLE $giftvouchers_list (
963 id int(11) NOT NULL AUTO_INCREMENT,
964 order_type enum('items', 'vouchers', 'gift_voucher_product') NOT NULL DEFAULT 'vouchers',
965 template_id int(11) NOT NULL,
966 product_id int(11) NOT NULL,
967 order_id int(11) NOT NULL,
968 itemcat_id int(11) NOT NULL,
969 item_id int(11) NOT NULL,
970 buying_for enum('someone_else', 'yourself') NOT NULL DEFAULT 'someone_else',
971 from_name varchar(255) NOT NULL,
972 to_name varchar(255) NOT NULL,
973 amount float NOT NULL,
974 message text NOT NULL,
975 firstname varchar(255) NOT NULL,
976 lastname varchar(255) NOT NULL,
977 email varchar(255) NOT NULL,
978 address text NOT NULL,
979 postcode varchar(30) NOT NULL,
980 pay_method varchar(255) NOT NULL,
981 shipping_type enum('shipping_as_email', 'shipping_as_post') NOT NULL DEFAULT 'shipping_as_email',
982 shipping_email varchar(255) NOT NULL,
983 shipping_method varchar(255) NOT NULL,
984 expiry varchar(100) NOT NULL,
985 couponcode bigint(25) NOT NULL,
986 voucherpdf_link text NOT NULL,
987 voucheradd_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
988 status varchar(10) NOT NULL DEFAULT 'unused',
989 payment_status varchar(10) NOT NULL DEFAULT 'Not Pay',
990 check_send_mail varchar(30) NOT NULL DEFAULT 'unsent',
991 PRIMARY KEY (id),
992 UNIQUE KEY wpgv_couponcode_unique (couponcode)
993 ) $charset_collate;";
994
995 $giftvouchers_template_sql = "CREATE TABLE $giftvouchers_template (
996 id int(11) NOT NULL AUTO_INCREMENT,
997 title text NOT NULL,
998 image int(11) DEFAULT NULL,
999 image_style varchar(100) DEFAULT NULL,
1000 orderno int(11) NOT NULL DEFAULT '0',
1001 active int(11) NOT NULL DEFAULT '0',
1002 templateadd_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
1003 PRIMARY KEY (id)
1004 ) $charset_collate;";
1005
1006 $giftvouchers_activity_sql = "CREATE TABLE $giftvouchers_activity (
1007 id int(11) NOT NULL AUTO_INCREMENT,
1008 voucher_id int(11) NOT NULL,
1009 user_id int(11) NOT NULL,
1010 action varchar(60) DEFAULT NULL,
1011 amount decimal(15,6),
1012 note text NOT NULL,
1013 activity_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
1014 PRIMARY KEY (id)
1015 ) $charset_collate;";
1016
1017 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1018 dbDelta($giftvouchers_setting_sql);
1019 dbDelta($giftvouchers_list_sql);
1020 dbDelta($giftvouchers_template_sql);
1021 dbDelta($giftvouchers_activity_sql);
1022
1023 add_option('jal_db_version', $jal_db_version);
1024
1025 $demoimageurl = get_option('wpgv_demoimageurl') ? get_option('wpgv_demoimageurl') : WPGIFT__PLUGIN_URL . '/assets/img/demo.png';
1026 update_option('wpgv_demoimageurl', $demoimageurl);
1027
1028 $company_name = get_bloginfo('name');
1029 $paypal_email = get_option('admin_email');
1030 $template_lanscape = 'template-voucher-lanscape-4.png, template-voucher-lanscape-8.png, template-voucher-lanscape-10.png';
1031 $template_portail = 'template-voucher-portail-1.png, template-voucher-portail-2.png, template-voucher-portail-6.png';
1032 if (
1033 !$wpdb->get_var(
1034 $wpdb->prepare(
1035 "SELECT id FROM `{$giftvouchers_setting}` WHERE id = %d",
1036 1
1037 )
1038 )
1039 ) {
1040 $wpdb->insert(
1041 $giftvouchers_setting,
1042 array(
1043 'is_woocommerce_enable' => 0,
1044 'is_style_choose_enable' => 0,
1045 'is_order_form_enable' => 1,
1046 'voucher_style' => 0,
1047 'company_name' => $company_name,
1048 'paypal_email' => $paypal_email,
1049 'reason_for_payment' => 'Payment for Gift Cards',
1050 'sender_name' => $company_name,
1051 'sender_email' => $paypal_email,
1052 'currency_code' => 'USD',
1053 'currency' => '$',
1054 'paypal' => 1,
1055 'sofort' => 0,
1056 'stripe' => 0,
1057 'voucher_bgcolor' => '81c6a9',
1058 'voucher_color' => '555555',
1059 'template_col' => 4,
1060 'voucher_min_value' => 0,
1061 'voucher_max_value' => 10000,
1062 'voucher_expiry_type' => 'days',
1063 'voucher_expiry' => 60,
1064 'voucher_terms_note' => 'Note: The voucher is valid for 60 days and can be redeemed at ' . $company_name . '. A cash payment is not possible.',
1065 'custom_loader' => WPGIFT__PLUGIN_URL . '/assets/img/loader.gif',
1066 'pdf_footer_url' => get_site_url(),
1067 'pdf_footer_email' => $paypal_email,
1068 'post_shipping' => 1,
1069 'shipping_method' => '5.99 : Express Shipping - $5.99, 3.99 : Standard Shipping - $3.99',
1070 'preview_button' => 1,
1071 'currency_position' => 'Left',
1072 'test_mode' => 0,
1073 'per_invoice' => 0,
1074 'landscape_mode_templates' => $template_lanscape,
1075 'portrait_mode_templates' => $template_portail,
1076 ),
1077 array('%d', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%d', '%s', '%s', '%d', '%d', '%d', '%s', '%d', '%s', '%s', '%s', '%s', '%d', '%s', '%d', '%s', '%d', '%d', '%s', '%s',)
1078 );
1079 $wpdb->insert(
1080 $giftvouchers_template,
1081 array(
1082 'title' => "Demo Template",
1083 'active' => 1,
1084 ),
1085 array('%s', '%d')
1086 );
1087 }
1088 $data_setting = $wpdb->get_row(
1089 $wpdb->prepare(
1090 "SELECT * FROM `{$giftvouchers_setting}` WHERE id = %d",
1091 1
1092 )
1093 );
1094 if (empty($data_setting->landscape_mode_templates) || empty($data_setting->portrait_mode_templates)) {
1095 // Use update() function from $wpdb
1096 $wpdb->update(
1097 $giftvouchers_setting,
1098 array(
1099 'landscape_mode_templates' => $template_lanscape,
1100 'portrait_mode_templates' => $template_portail,
1101 ),
1102 array('id' => 1),
1103 array('%s', '%s'),
1104 array('%d')
1105 );
1106 }
1107 require_once ABSPATH . 'wp-admin/includes/file.php';
1108 WP_Filesystem();
1109
1110 $upload = wp_upload_dir();
1111 $upload_dir = trailingslashit($upload['basedir']) . 'voucherpdfuploads';
1112
1113 global $wp_filesystem;
1114 if (!$wp_filesystem->is_dir($upload_dir)) {
1115 $wp_filesystem->mkdir($upload_dir, 0755);
1116 $wp_filesystem->put_contents($upload_dir . '/index.html', 'Silence is golden.', 0755);
1117 }
1118
1119
1120 if (!wp_next_scheduled('wpgv_check_voucher_status')) {
1121 wp_schedule_event(time(), 'hourly', 'wpgv_check_voucher_status');
1122 }
1123 if (!wp_next_scheduled('wpgv_cleanup_unpaid_cron')) {
1124 wp_schedule_event(time(), 'hourly', 'wpgv_cleanup_unpaid_cron');
1125 }
1126 set_transient('wpgv_activated', 1);
1127 require_once(WPGIFT__PLUGIN_DIR . '/classes/class-nag.php');
1128 WPGIFT_Nag::insert_install_date();
1129 }
1130 register_activation_hook(__FILE__, 'wpgv_plugin_activation');
1131
1132 function wpgv_upgrade_completed($upgrader_object, $options)
1133 {
1134 // The path to our plugin's main file
1135 $our_plugin = plugin_basename(__FILE__);
1136 // If an update has taken place and the updated type is plugins and the plugins element exists
1137 if (isset($options['action'], $options['type']) && $options['action'] == 'update' && $options['type'] == 'plugin' && isset($options['plugins'])) {
1138 // Iterate through the plugins being updated and check if ours is there
1139 foreach ($options['plugins'] as $plugin) {
1140 if ($plugin == $our_plugin) {
1141
1142 global $wpdb;
1143
1144 $giftvouchers_setting = $wpdb->prefix . 'giftvouchers_setting';
1145 $giftvouchers_list = $wpdb->prefix . 'giftvouchers_list';
1146 $giftvouchers_template = $wpdb->prefix . 'giftvouchers_template';
1147 $giftvouchers_activity = $wpdb->prefix . 'giftvouchers_activity';
1148
1149 $charset_collate = $wpdb->get_charset_collate();
1150 $giftvouchers_activity_sql = "CREATE TABLE IF NOT EXISTS $giftvouchers_activity (
1151 id int(11) NOT NULL AUTO_INCREMENT,
1152 voucher_id int(11) NOT NULL,
1153 user_id int(11) NOT NULL,
1154 action varchar(60) DEFAULT NULL,
1155 amount decimal(15,6),
1156 note text NOT NULL,
1157 activity_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
1158 PRIMARY KEY (id)
1159 ) $charset_collate;";
1160
1161 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1162 dbDelta($giftvouchers_activity_sql);
1163
1164 $table_exists = function ($table) use ($wpdb) {
1165 return (bool) $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
1166 };
1167
1168 $column_exists = function ($table, $column) use ($wpdb, $table_exists) {
1169 if (!$table_exists($table)) {
1170 return false;
1171 }
1172
1173 return (bool) $wpdb->get_row(
1174 $wpdb->prepare(
1175 "SHOW COLUMNS FROM `{$table}` LIKE %s",
1176 $column
1177 )
1178 );
1179 };
1180
1181 $add_column_if_not_exists = function ($table, $column, $definition) use ($column_exists, $wpdb) {
1182 if (!$column_exists($table, $column)) {
1183 $wpdb->query("ALTER TABLE `{$table}` ADD `{$column}` {$definition}");
1184 }
1185 };
1186
1187 $modify_column_if_exists = function ($table, $column, $definition) use ($column_exists, $wpdb) {
1188 if ($column_exists($table, $column)) {
1189 $wpdb->query("ALTER TABLE `{$table}` MODIFY COLUMN `{$column}` {$definition}");
1190 }
1191 };
1192
1193 $add_column_if_not_exists($giftvouchers_template, 'image_style', "varchar(100) DEFAULT NULL");
1194 $add_column_if_not_exists($giftvouchers_setting, 'is_woocommerce_enable', "int(1) DEFAULT 0");
1195 $add_column_if_not_exists($giftvouchers_setting, 'post_shipping', "int(1) DEFAULT 0");
1196 $add_column_if_not_exists($giftvouchers_setting, 'preview_button', "int(1) DEFAULT 1");
1197 $add_column_if_not_exists($giftvouchers_setting, 'pdf_footer_url', "varchar(255) DEFAULT NULL");
1198 $add_column_if_not_exists($giftvouchers_setting, 'pdf_footer_email', "varchar(255) DEFAULT NULL");
1199 $add_column_if_not_exists($giftvouchers_setting, 'is_style_choose_enable', "int(1) DEFAULT 0");
1200 $add_column_if_not_exists($giftvouchers_setting, 'voucher_style', "varchar(100) DEFAULT NULL");
1201 $add_column_if_not_exists($giftvouchers_setting, 'currency', "varchar(10) DEFAULT NULL");
1202 $add_column_if_not_exists($giftvouchers_setting, 'currency_code', "varchar(10) DEFAULT NULL");
1203 $add_column_if_not_exists($giftvouchers_list, 'check_send_mail', "varchar(30) NOT NULL DEFAULT 'unsent'");
1204
1205 $modify_column_if_exists($giftvouchers_setting, 'stripe_publishable_key', "varchar(255) DEFAULT NULL");
1206 $modify_column_if_exists($giftvouchers_setting, 'stripe_secret_key', "varchar(255) DEFAULT NULL");
1207
1208 if ($table_exists($giftvouchers_list) && $table_exists($giftvouchers_activity)) {
1209 $orders = $wpdb->get_results("SELECT id,from_name,amount FROM $giftvouchers_list WHERE id NOT IN (SELECT voucher_id FROM $giftvouchers_activity) AND `status` = 'unused' AND `payment_status` = 'Paid'");
1210
1211 foreach ($orders as $order) {
1212 WPGV_Gift_Voucher_Activity::record($order->id, 'create', '', 'Voucher ordered by ' . $order->from_name);
1213 WPGV_Gift_Voucher_Activity::record($order->id, 'transaction', $order->amount, 'Voucher payment recieved.');
1214 }
1215 }
1216
1217 if ($table_exists($giftvouchers_template) && $column_exists($giftvouchers_template, 'image_style')) {
1218 $templates = $wpdb->get_results("SELECT id,image FROM $giftvouchers_template WHERE `image_style` IS NULL");
1219 foreach ($templates as $template) {
1220 $wpdb->update(
1221 $giftvouchers_template,
1222 array(
1223 'image_style' => '["' . $template->image . '","",""]',
1224 ),
1225 array('id' => $template->id)
1226 );
1227 }
1228 }
1229
1230 $items = get_posts(array('posts_per_page' => -1, 'post_type' => 'wpgv_voucher_product'));
1231 foreach ($items as $item) {
1232 update_post_meta($item->ID, 'style1_image', get_post_thumbnail_id($item->ID));
1233 }
1234
1235 // Set a transient to record that our plugin has just been updated
1236 set_transient('wpgv_updated', 1);
1237 }
1238 }
1239 }
1240 }
1241 add_action('upgrader_process_complete', 'wpgv_upgrade_completed', 10, 2);
1242
1243 function wpgv_display_update_notice()
1244 {
1245 if (get_transient('wpgv_updated')) {
1246 $class = 'notice notice-info';
1247 $message = sprintf(
1248 'Thanks for Updating <b>Gift Cards</b> plugin. Please see the new plugin settings features from <a href="%s" target="_blank">here</a>. We upgraded PayPal (New Checkout) and Stripe (SCA-ready) payment process so you need to update the fields of these payment methods in the settings page. Please see here the documentation of new payment settings <a href="%s" target="_blank">here</a>.<br><br>We have noticed that you have been using Gift Cards plugin for a long time. We hope you love it, and we would really appreciate it if you would <a href="%s" target="_blank">give us a 5 stars rating</a>.',
1249 esc_url(admin_url('admin.php') . '?page=voucher-setting'),
1250 esc_url('https://www.wp-giftcard.com/docs/documentation/plugin-settings/payment-settings/'),
1251 esc_url('https://wordpress.org/support/plugin/gift-voucher/reviews/#new-post')
1252 );
1253
1254 printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), wp_kses_post($message));
1255 delete_transient('wpgv_updated');
1256 }
1257 }
1258 add_action('admin_notices', 'wpgv_display_update_notice');
1259
1260 function wpgv_display_install_notice()
1261 {
1262 if (get_transient('wpgv_activated')) {
1263 $class = 'notice notice-info';
1264 $message = sprintf(
1265 'Thanks for Installing <b>Gift Cards</b> plugin. Please setup your plugin settings from <a href="%s" target="_blank">here</a>.',
1266 esc_url(admin_url('admin.php') . '?page=voucher-setting')
1267 );
1268
1269 printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), wp_kses_post($message));
1270 delete_transient('wpgv_activated');
1271 }
1272 }
1273 add_action('admin_notices', 'wpgv_display_install_notice');
1274
1275 function wpgv_plugin_deactivation()
1276 {
1277 wp_clear_scheduled_hook('wpgv_check_voucher_status');
1278 wp_clear_scheduled_hook('wpgv_cleanup_unpaid_cron');
1279 }
1280 register_deactivation_hook(__FILE__, 'wpgv_plugin_deactivation');
1281
1282 add_action('init', 'wpgv_do_output_buffer');
1283 function wpgv_do_output_buffer()
1284 {
1285 ob_start();
1286 global $wpdb;
1287 $giftvouchers_list = $wpdb->prefix . 'giftvouchers_list';
1288
1289 if (wpgv_db_table_exists($giftvouchers_list) && !wpgv_db_column_exists($giftvouchers_list, 'check_send_mail')) {
1290 $wpdb->query("ALTER TABLE $giftvouchers_list ADD check_send_mail varchar(30) NOT NULL DEFAULT 'unsent'");
1291 }
1292 }
1293
1294 // Filter page template
1295 add_filter('page_template', 'wpgv_catch_plugin_template');
1296
1297 // Page template filter callback
1298 function wpgv_catch_plugin_template($template)
1299 {
1300 if (is_page_template('wpgv_voucher_pdf.php')) {
1301 $template = WPGIFT__PLUGIN_DIR . '/templates/wpgv_voucher_pdf.php';
1302 } elseif (is_page_template('wpgv_item_pdf.php')) {
1303 $template = WPGIFT__PLUGIN_DIR . '/templates/wpgv_item_pdf.php';
1304 }
1305
1306 return $template;
1307 }
1308
1309 function wpgv_hex2rgb($color)
1310 {
1311 if ($color[0] == '#')
1312 $color = substr($color, 1);
1313
1314 if (strlen($color) == 6)
1315 list($r, $g, $b) = array(
1316 $color[0] . $color[1],
1317 $color[2] . $color[3],
1318 $color[4] . $color[5]
1319 );
1320 elseif (strlen($color) == 3)
1321 list($r, $g, $b) = array($color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2]);
1322 else
1323 return false;
1324
1325 $r = hexdec($r);
1326 $g = hexdec($g);
1327 $b = hexdec($b);
1328
1329 return array($r, $g, $b);
1330 }
1331
1332 //conversion pixel -> millimeter at 72 dpi
1333 function wpgv_px2mm($px)
1334 {
1335 return $px * 25.4 / 72;
1336 }
1337
1338 function wpgv_txtentities($html)
1339 {
1340 $trans = get_html_translation_table(HTML_ENTITIES);
1341 $trans = array_flip($trans);
1342 return strtr($html, $trans);
1343 }
1344
1345 function wpgv_em($word)
1346 {
1347 if (is_null($word)) {
1348 return '';
1349 }
1350
1351 // Undo slashes safely (use WP helper)
1352 if (function_exists('wp_unslash')) {
1353 $word = wp_unslash($word);
1354 } else {
1355 $word = stripslashes($word);
1356 }
1357
1358 // Remove any HTML tags
1359 $word = wp_strip_all_tags($word);
1360
1361 // Decode HTML entities into UTF-8. Use HTML5 flag to support wider entity set.
1362 $word = html_entity_decode($word, ENT_QUOTES | ENT_HTML5, 'UTF-8');
1363
1364 // Fix invalid UTF-8 sequences (WP helper if available)
1365 if (function_exists('wp_check_invalid_utf8')) {
1366 $word = wp_check_invalid_utf8($word);
1367 } elseif (function_exists('mb_convert_encoding')) {
1368 $word = mb_convert_encoding($word, 'UTF-8', 'UTF-8');
1369 }
1370
1371 // Normalize Unicode (NFC) if ext-intl is available
1372 if (function_exists('normalizer_normalize')) {
1373 $normalized = normalizer_normalize($word, Normalizer::FORM_C);
1374 if ($normalized !== false) {
1375 $word = $normalized;
1376 }
1377 }
1378
1379 // Trim whitespace and return; preserve full UTF-8 (including emoji and non-Latin)
1380 return trim($word);
1381 }
1382
1383 function wpgv_mailvarstr_multiple($string, $setting_options, $voucher_options_results, $voucherpdf_link)
1384 {
1385
1386 $get_link_pdf = array();
1387 $get_order_number = array();
1388 $from_name = null;
1389 $to_name = null;
1390 $email = null;
1391 $amount = null;
1392
1393 // Normalize input: ensure we can iterate and access an example item safely.
1394 if (empty($voucher_options_results)) {
1395 return $string;
1396 }
1397
1398 foreach ($voucher_options_results as $get_value) {
1399 $get_link_pdf[] = wpgv_get_voucher_pdf_url(isset($get_value->voucherpdf_link) ? $get_value->voucherpdf_link : '');
1400 $get_order_number[] = isset($get_value->id) ? $get_value->id : '';
1401 $from_name = isset($get_value->from_name) ? $get_value->from_name : '';
1402 $to_name = isset($get_value->to_name) ? $get_value->to_name : '';
1403 if (!empty($get_value->email)) {
1404 $email = $get_value->email;
1405 } else {
1406 $email = isset($get_value->shipping_email) ? $get_value->shipping_email : '';
1407 }
1408 $amount = isset($get_value->amount) ? $get_value->amount : '';
1409 }
1410
1411 // Use the first record as a representative for order-level fields if necessary.
1412 $first = is_array($voucher_options_results) ? reset($voucher_options_results) : $voucher_options_results;
1413
1414 $vars = array(
1415 '{order_type}' => (!empty($first->order_type)) ? $first->order_type : 'vouchers',
1416 '{company_name}' => (!empty($setting_options->company_name)) ? stripslashes($setting_options->company_name) : '',
1417 '{website_url}' => get_site_url(),
1418 '{sender_email}' => isset($setting_options->sender_email) ? $setting_options->sender_email : '',
1419 '{sender_name}' => isset($setting_options->sender_name) ? stripslashes($setting_options->sender_name) : '',
1420 '{order_number}' => isset($first->id) ? $first->id : '',
1421 '{amount}' => $amount,
1422 '{customer_name}' => stripslashes($from_name),
1423 '{recipient_name}' => stripslashes($to_name),
1424 '{customer_email}' => $email,
1425 '{customer_address}' => isset($first->address) ? $first->address : '',
1426 '{customer_postcode}' => isset($first->postcode) ? $first->postcode : '',
1427 '{coupon_code}' => isset($first->couponcode) ? $first->couponcode : '',
1428 '{payment_method}' => isset($first->pay_method) ? $first->pay_method : '',
1429 '{payment_status}' => isset($first->payment_status) ? $first->payment_status : '',
1430 '{pdf_link}' => wpgv_get_voucher_pdf_url($voucherpdf_link),
1431 '{receipt_link}' => wpgv_get_voucher_pdf_url(isset($first->voucherpdf_link) ? $first->voucherpdf_link : '', '-receipt'),
1432 );
1433 return strtr($string, $vars);
1434 }
1435
1436 // Provide admin variant fallback if not defined elsewhere. This keeps backward compatibility
1437 if (! function_exists('wpgv_mailvarstr_multiple_admin')) {
1438 function wpgv_mailvarstr_multiple_admin($string, $setting_options, $voucher_options_results)
1439 {
1440 // Try to use the same logic as wpgv_mailvarstr_multiple; admin templates typically do not need a specific pdf link.
1441 $first = !empty($voucher_options_results) && is_array($voucher_options_results) ? reset($voucher_options_results) : $voucher_options_results;
1442 $voucherpdf_link = isset($first->voucherpdf_link) ? $first->voucherpdf_link : '';
1443 return wpgv_mailvarstr_multiple($string, $setting_options, $voucher_options_results, $voucherpdf_link);
1444 }
1445 }
1446 function wpgv_mailvarstr($string, $setting_options, $voucher_options)
1447 {
1448 $vars = array(
1449 '{order_type}' => ($voucher_options->order_type) ? $voucher_options->order_type : 'vouchers',
1450 '{company_name}' => ($setting_options->company_name) ? $setting_options->company_name : '',
1451 '{website_url}' => get_site_url(),
1452 '{sender_email}' => $setting_options->sender_email,
1453 '{sender_name}' => $setting_options->sender_name,
1454 '{order_number}' => $voucher_options->id,
1455 '{amount}' => $voucher_options->amount,
1456 '{customer_name}' => $voucher_options->from_name,
1457 '{recipient_name}' => $voucher_options->to_name,
1458 '{customer_email}' => ($voucher_options->email) ? $voucher_options->email : $voucher_options->shipping_email,
1459 '{customer_address}' => $voucher_options->address,
1460 '{customer_postcode}' => $voucher_options->postcode,
1461 '{coupon_code}' => $voucher_options->couponcode,
1462 '{payment_method}' => $voucher_options->pay_method,
1463 '{payment_status}' => $voucher_options->payment_status,
1464 '{pdf_link}' => wpgv_get_voucher_pdf_url($voucher_options->voucherpdf_link),
1465 '{receipt_link}' => wpgv_get_voucher_pdf_url($voucher_options->voucherpdf_link, '-receipt'),
1466 );
1467
1468 return strtr($string, $vars);
1469 }
1470
1471 // This function is use for Multisite WP environment
1472 function wpgv_new_blog($blog_id, $user_id, $domain, $path, $site_id, $meta)
1473 {
1474 if (is_plugin_active_for_network('gift-voucher-pro/gift-voucher-pro.php')) {
1475 switch_to_blog($blog_id);
1476 wpgv_plugin_activation();
1477 restore_current_blog();
1478 }
1479 }
1480
1481 add_action('wpmu_new_blog', 'wpgv_new_blog', 10, 6);
1482
1483 add_action('wpgv_check_voucher_status', 'do_wpgv_check_voucher_status');
1484
1485 function do_wpgv_check_voucher_status()
1486 {
1487 global $wpdb;
1488 $setting_table_name = $wpdb->prefix . 'giftvouchers_setting';
1489 $options = $wpdb->get_row("SELECT * FROM $setting_table_name WHERE id = 1");
1490 if ($options->is_woocommerce_enable) {
1491 $vouchers = $wpdb->get_results("SELECT id, couponcode FROM {$wpdb->prefix}giftvouchers_list WHERE `status` = 'unused' AND `payment_status` = 'Paid'");
1492 foreach ($vouchers as $voucher) {
1493 $gift_voucher = new WPGV_Gift_Voucher($voucher->couponcode);
1494 $balance = $gift_voucher->get_balance();
1495 if (empty($balance) || ($balance == 0)) {
1496 $wpdb->update(
1497 "{$wpdb->prefix}giftvouchers_list",
1498 array('id' => $voucher->id, 'status' => 'used'),
1499 array('id' => $voucher->id)
1500 );
1501 }
1502 }
1503 }
1504 }
1505
1506 add_action('wp_ajax_wpgv_redeem_voucher', 'wpgv_redeem_voucher');
1507
1508 function wpgv_redeem_voucher()
1509 {
1510 global $wpdb;
1511
1512 if (!current_user_can('manage_options')) {
1513 wp_send_json_error(array('message' => __('You are not allowed to redeem vouchers.', 'gift-voucher')), 403);
1514 }
1515
1516 check_ajax_referer('wpgv_redeem_voucher', 'nonce');
1517
1518 $setting_table_name = $wpdb->prefix . 'giftvouchers_setting';
1519 $setting_options = $wpdb->get_row("SELECT * FROM $setting_table_name WHERE id = 1");
1520 $voucher_id = isset($_POST['voucher_id']) ? absint(wp_unslash($_POST['voucher_id'])) : 0;
1521 $raw_voucher_amount = isset($_POST['voucher_amount']) ? sanitize_text_field(wp_unslash($_POST['voucher_amount'])) : '';
1522 $normalized_amount = preg_replace('/[^0-9.,]/', '', $raw_voucher_amount);
1523
1524 if ($voucher_id <= 0) {
1525 wp_send_json_error(array('message' => __('Invalid voucher ID.', 'gift-voucher')), 400);
1526 }
1527
1528 if ($normalized_amount === '') {
1529 wp_send_json_error(array('message' => __('Voucher amount is required.', 'gift-voucher')), 400);
1530 }
1531
1532 if (strpos($normalized_amount, ',') !== false && strpos($normalized_amount, '.') !== false) {
1533 $normalized_amount = str_replace(',', '', $normalized_amount);
1534 } else {
1535 $normalized_amount = str_replace(',', '.', $normalized_amount);
1536 }
1537
1538 $voucher_amount = round((float) $normalized_amount, 2);
1539
1540 if ($voucher_amount <= 0) {
1541 wp_send_json_error(array('message' => __('Voucher amount must be greater than zero.', 'gift-voucher')), 400);
1542 }
1543
1544 $gift_voucher = WPGV_Gift_Voucher::get_by_id($voucher_id);
1545 if (!$gift_voucher || !$gift_voucher->get_id()) {
1546 wp_send_json_error(array('message' => __('Gift Voucher does not exist.', 'gift-voucher')), 404);
1547 }
1548
1549 if ($gift_voucher->get_payment_status() !== 'Paid') {
1550 wp_send_json_error(array('message' => __('Only paid vouchers can be redeemed.', 'gift-voucher')), 400);
1551 }
1552
1553 if ($gift_voucher->get_active() === 'used') {
1554 wp_send_json_error(array('message' => __('This voucher is already marked as used.', 'gift-voucher')), 400);
1555 }
1556
1557 if ($gift_voucher->has_expired()) {
1558 wp_send_json_error(array('message' => __('This voucher has expired and cannot be redeemed.', 'gift-voucher')), 400);
1559 }
1560
1561 $current_balance = (float) $gift_voucher->get_balance();
1562 if ($current_balance <= 0) {
1563 wp_send_json_error(array('message' => __('This voucher has no remaining balance.', 'gift-voucher')), 400);
1564 }
1565
1566 if ($voucher_amount > $current_balance) {
1567 wp_send_json_error(array('message' => sprintf(
1568 /* translators: %s: current balance */
1569 __('Redeem amount exceeds current balance of %s.', 'gift-voucher'),
1570 wpgv_price_format($current_balance)
1571 )), 400);
1572 }
1573
1574 $result = WPGV_Gift_Voucher_Activity::record(
1575 $voucher_id,
1576 'transaction',
1577 -$voucher_amount,
1578 'Voucher amount ' . $setting_options->currency . $voucher_amount . ' used directly by administrator.'
1579 );
1580
1581 if (!$result) {
1582 wp_send_json_error(array('message' => __('Unable to record voucher redemption.', 'gift-voucher')), 500);
1583 }
1584
1585 $updated_voucher = WPGV_Gift_Voucher::get_by_id($voucher_id);
1586 $updated_balance = $updated_voucher ? (float) $updated_voucher->get_balance() : max(0, $current_balance - $voucher_amount);
1587
1588 wp_send_json_success(array(
1589 'message' => __('Successful', 'gift-voucher'),
1590 'balance' => $updated_balance,
1591 ));
1592 }
1593
1594 function wpgv_price_format($price)
1595 {
1596 global $wpdb;
1597 $setting_options = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}giftvouchers_setting WHERE id = %d", 1));
1598
1599 $price = html_entity_decode(wp_strip_all_tags(stripslashes($price)), ENT_NOQUOTES, 'UTF-8');
1600 // Keep UTF-8 for front-end and general usage.
1601 // Do not convert to windows-1252 here because this function is used
1602 // in both HTML (UTF-8) and PDF contexts. PDF-specific conversion
1603 // is handled by `wpgv_text_to_pdf_safe` when rendering PDFs.
1604 if (function_exists('wp_check_invalid_utf8')) {
1605 $price = wp_check_invalid_utf8($price);
1606 } elseif (function_exists('mb_convert_encoding')) {
1607 $price = mb_convert_encoding($price, 'UTF-8', 'UTF-8');
1608 }
1609 // number format new
1610 $wpgv_select_number_format = get_option('wpgv_select_number_format') ? get_option('wpgv_select_number_format') : '';
1611 if ($wpgv_select_number_format == "comma") {
1612 $price = number_format((float)$price, 2, '.', ',');
1613 } elseif ($wpgv_select_number_format == "dot") {
1614 $price = number_format((float)$price, 2, ',', '.');
1615 }
1616 $currency = ($setting_options->currency_position == 'Left') ? $setting_options->currency . ' ' . $price : $price . ' ' . $setting_options->currency;
1617 return $currency;
1618 }
1619
1620 function wpgv_find_existing_public_page($post_title, $post_content = '', $page_template = '')
1621 {
1622 global $wpdb;
1623
1624 $posts_table = $wpdb->posts;
1625 $postmeta_table = $wpdb->postmeta;
1626
1627 $post_title = sanitize_text_field($post_title);
1628 $post_content = is_string($post_content) ? trim($post_content) : '';
1629 $page_template = sanitize_text_field($page_template);
1630
1631 if ($page_template !== '' && $post_title !== '') {
1632 $existing_id = $wpdb->get_var(
1633 $wpdb->prepare(
1634 "SELECT p.ID
1635 FROM $posts_table p
1636 INNER JOIN $postmeta_table pm ON p.ID = pm.post_id
1637 WHERE p.post_type = 'page'
1638 AND p.post_status = 'publish'
1639 AND p.post_title = %s
1640 AND pm.meta_key = '_wp_page_template'
1641 AND pm.meta_value = %s
1642 LIMIT 1",
1643 $post_title,
1644 $page_template
1645 )
1646 );
1647
1648 if ($existing_id) {
1649 return absint($existing_id);
1650 }
1651 }
1652
1653 if ($post_content !== '') {
1654 $existing_id = $wpdb->get_var(
1655 $wpdb->prepare(
1656 "SELECT ID
1657 FROM $posts_table
1658 WHERE post_type = 'page'
1659 AND post_status = 'publish'
1660 AND post_content = %s
1661 LIMIT 1",
1662 $post_content
1663 )
1664 );
1665
1666 if ($existing_id) {
1667 return absint($existing_id);
1668 }
1669 }
1670
1671 if ($post_title !== '') {
1672 $existing_id = $wpdb->get_var(
1673 $wpdb->prepare(
1674 "SELECT ID
1675 FROM $posts_table
1676 WHERE post_type = 'page'
1677 AND post_status = 'publish'
1678 AND post_title = %s
1679 LIMIT 1",
1680 $post_title
1681 )
1682 );
1683
1684 if ($existing_id) {
1685 return absint($existing_id);
1686 }
1687 }
1688
1689 return 0;
1690 }
1691
1692 function wpgv_create_or_reuse_public_page($page_args, $page_template = '')
1693 {
1694 $post_title = isset($page_args['post_title']) ? $page_args['post_title'] : '';
1695 $post_content = isset($page_args['post_content']) ? $page_args['post_content'] : '';
1696
1697 $page_id = wpgv_find_existing_public_page($post_title, $post_content, $page_template);
1698
1699 if (!$page_id) {
1700 $page_id = wp_insert_post($page_args, '');
1701 }
1702
1703 $page_id = absint($page_id);
1704
1705 if ($page_id && $page_template !== '') {
1706 update_post_meta($page_id, '_wp_page_template', $page_template);
1707 }
1708
1709 return $page_id;
1710 }
1711
1712 function wpgv_create_plugin_pages()
1713 {
1714
1715 // Create Pages
1716 $giftCardPage = array(
1717 'post_title' => 'Gift Cards',
1718 'post_content' => '[wpgv_giftcard]',
1719 'post_status' => 'publish',
1720 'post_author' => strval(wp_get_current_user()->ID),
1721 'post_type' => 'page',
1722 );
1723 $voucherPage = array(
1724 'post_title' => 'Gift Voucher',
1725 'post_content' => '[wpgv_giftvoucher]',
1726 'post_status' => 'publish',
1727 'post_author' => strval(wp_get_current_user()->ID),
1728 'post_type' => 'page',
1729 );
1730 $giftItemsPage = array(
1731 'post_title' => 'Gift Items',
1732 'post_content' => '[wpgv_giftitems]',
1733 'post_status' => 'publish',
1734 'post_author' => strval(wp_get_current_user()->ID),
1735 'post_type' => 'page',
1736 );
1737 $voucherPDFPage = array(
1738 'post_title' => 'Voucher PDF Preview',
1739 'post_content' => ' ',
1740 'post_status' => 'publish',
1741 'post_author' => strval(wp_get_current_user()->ID),
1742 'post_type' => 'page',
1743 'comment_status' => 'closed',
1744 'ping_status' => 'closed',
1745 );
1746 $giftItemPDFPage = array(
1747 'post_title' => 'Gift Item PDF Preview',
1748 'post_content' => ' ',
1749 'post_status' => 'publish',
1750 'post_author' => strval(wp_get_current_user()->ID),
1751 'post_type' => 'page',
1752 'comment_status' => 'closed',
1753 'ping_status' => 'closed',
1754 );
1755 $voucherSuccessPage = array(
1756 'post_title' => 'Voucher Payment Successful',
1757 'post_content' => '[wpgv_giftvouchersuccesspage]',
1758 'post_status' => 'publish',
1759 'post_author' => strval(wp_get_current_user()->ID),
1760 'post_type' => 'page',
1761 'comment_status' => 'closed',
1762 'ping_status' => 'closed',
1763 );
1764 $voucherCancelPage = array(
1765 'post_title' => 'Voucher Payment Cancel',
1766 'post_content' => '[wpgv_giftvouchercancelpage]',
1767 'post_status' => 'publish',
1768 'post_author' => strval(wp_get_current_user()->ID),
1769 'post_type' => 'page',
1770 'comment_status' => 'closed',
1771 'ping_status' => 'closed',
1772 );
1773 $stripeSuccessPage = array(
1774 'post_title' => 'Stripe Payment Success Page',
1775 'post_content' => '[wpgv_stripesuccesspage]',
1776 'post_status' => 'publish',
1777 'post_author' => strval(wp_get_current_user()->ID),
1778 'post_type' => 'page',
1779 'comment_status' => 'closed',
1780 'ping_status' => 'closed',
1781 );
1782 $voucherBalancePage = array(
1783 'post_title' => 'Voucher Balance Check',
1784 'post_content' => '[wpgv-check-voucher-balance]',
1785 'post_status' => 'publish',
1786 'post_author' => strval(wp_get_current_user()->ID),
1787 'post_type' => 'page',
1788 'comment_status' => 'closed',
1789 'ping_status' => 'closed',
1790 );
1791 $lastpageIds[0] = wpgv_create_or_reuse_public_page($voucherPage);
1792 $lastpageIds[1] = wpgv_create_or_reuse_public_page($giftItemsPage);
1793 $lastpageIds[2] = wpgv_create_or_reuse_public_page($voucherPDFPage, 'wpgv_voucher_pdf.php');
1794 $lastpageIds[3] = wpgv_create_or_reuse_public_page($giftItemPDFPage, 'wpgv_item_pdf.php');
1795 $lastpageIds[4] = wpgv_create_or_reuse_public_page($voucherSuccessPage);
1796 $lastpageIds[5] = wpgv_create_or_reuse_public_page($voucherCancelPage);
1797 $lastpageIds[6] = wpgv_create_or_reuse_public_page($giftCardPage);
1798 $lastpageIds[7] = wpgv_create_or_reuse_public_page($stripeSuccessPage);
1799 $lastpageIds[8] = wpgv_create_or_reuse_public_page($voucherBalancePage);
1800
1801 if (!empty($lastpageIds[7])) {
1802 update_option('wpgv_stripesuccesspage', $lastpageIds[7]);
1803 }
1804
1805 $lastCategoryID = wp_insert_term(
1806 'Demo Category',
1807 'wpgv_voucher_category',
1808 array(
1809 'description' => 'Demo Category Description',
1810 'slug' => 'demo-category',
1811 )
1812 );
1813 $demoItem = array(
1814 'post_title' => 'Demo Item',
1815 'post_status' => 'publish',
1816 'post_author' => get_current_user_id(),
1817 'post_type' => 'wpgv_voucher_product',
1818 );
1819 $lastItemID = wp_insert_post($demoItem);
1820 add_post_meta($lastItemID, 'description', 'Demo Description');
1821 add_post_meta($lastItemID, 'price', '100');
1822 add_post_meta($lastItemID, 'special_price', '80');
1823 if (!isset($lastCategoryID)) {
1824 wp_set_object_terms($lastItemID, $lastCategoryID, 'wpgv_voucher_category');
1825 }
1826
1827 if (!$lastpageIds[2])
1828 wp_die('Error creating template page');
1829
1830 if (!$lastpageIds[3])
1831 wp_die('Error creating template page');
1832
1833 return array($lastpageIds);
1834 }
1835
1836 function wpgv_display_testmode_notice()
1837 {
1838
1839 global $wpdb;
1840 $setting_table = $wpdb->prefix . 'giftvouchers_setting';
1841 $setting_options = $wpdb->get_row("SELECT * FROM $setting_table WHERE id = 1");
1842
1843 $wpgv_paypal_client_id = get_option('wpgv_paypal_client_id') ? get_option('wpgv_paypal_client_id') : '';
1844 $wpgv_paypal_secret_key = get_option('wpgv_paypal_secret_key') ? get_option('wpgv_paypal_secret_key') : '';
1845
1846 if ($setting_options->paypal && $setting_options->test_mode) {
1847 $class = 'notice notice-info';
1848 $message = sprintf(
1849 'PayPal Testmode has enabled in the <a href="%s" target="_blank">plugin settings</a> in <b>Gift Cards</b> plugin.',
1850 esc_url(admin_url('admin.php') . '?page=voucher-setting#payment')
1851 );
1852 printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), wp_kses_post($message));
1853 }
1854
1855 if ($setting_options->paypal && (!$wpgv_paypal_client_id || !$wpgv_paypal_secret_key)) {
1856 $class = 'notice notice-info';
1857 $message = sprintf(
1858 'PayPal has enabled but empty client id or secret key in the <a href="%s" target="_blank">plugin settings</a> in <b>Gift Cards</b> plugin.',
1859 esc_url(admin_url('admin.php') . '?page=voucher-setting#payment')
1860 );
1861 printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), wp_kses_post($message));
1862 }
1863
1864 if ($setting_options->stripe && (!$setting_options->stripe_publishable_key || !$setting_options->stripe_secret_key)) {
1865 $class = 'notice notice-info';
1866 $message = sprintf(
1867 'Stripe has enabled but empty Publishable key or Secret key in the <a href="%s" target="_blank">plugin settings</a> in <b>Gift Cards</b> plugin.',
1868 esc_url(admin_url('admin.php') . '?page=voucher-setting#payment')
1869 );
1870 printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), wp_kses_post($message));
1871 }
1872
1873 if ($setting_options->sofort && !$setting_options->sofort_configure_key) {
1874 $class = 'notice notice-info';
1875 $message = sprintf(
1876 'Sofort has enabled but empty Configuration Key in the <a href="%s" target="_blank">plugin settings</a> in <b>Gift Cards</b> plugin.',
1877 esc_url(admin_url('admin.php') . '?page=voucher-setting#payment')
1878 );
1879 printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), wp_kses_post($message));
1880 }
1881 }
1882 add_action('admin_notices', 'wpgv_display_testmode_notice');
1883
1884
1885 // Apply gift card code if valid and update cart totals
1886 function wpgv_handle_gift_voucher_application($err, $err_code, $coupon)
1887 {
1888 // Native Coupon code / Add coupons redemption is available only when the
1889 // plugin redemption form is disabled and WooCommerce integration is enabled.
1890 if (!wpgv_is_native_coupon_voucher_redemption_enabled()) {
1891 return $err;
1892 }
1893
1894 // WooCommerce deprecated direct property access on coupon objects.
1895 $coupon_code = is_object($coupon) && method_exists($coupon, 'get_code')
1896 ? $coupon->get_code()
1897 : (is_object($coupon) && isset($coupon->code) ? $coupon->code : '');
1898
1899 if ($coupon_code === '') {
1900 return $err;
1901 }
1902
1903 $gift_voucher = new WPGV_Gift_Voucher($coupon_code);
1904
1905 if (!$gift_voucher->get_id() || $gift_voucher->get_payment_status() !== 'Paid') {
1906 return $err;
1907 }
1908
1909 $balance = $gift_voucher->get_balance();
1910
1911 if (empty($balance) || $balance <= 0) {
1912 $message = esc_html__('This gift voucher has a zero balance.', 'gift-voucher');
1913 return $message;
1914 }
1915
1916 if ($gift_voucher->has_expired()) {
1917 $message = esc_html__('Your voucher has expired.', 'gift-voucher');
1918 return $message;
1919 }
1920
1921 if (!WC()->session->has_session()) {
1922 WC()->session->set_customer_session_cookie(true);
1923 }
1924
1925 $session_data = (array) WC()->session->get(WPGIFT_SESSION_KEY);
1926 $session_data['gift_voucher'][$coupon_code] = 0;
1927 WC()->session->set(WPGIFT_SESSION_KEY, $session_data);
1928
1929 wc_add_notice(esc_html__('Gift voucher applied successfully.', 'gift-voucher'), 'success');
1930
1931 WC()->cart->calculate_totals();
1932
1933 if (is_checkout() && !is_admin()) {
1934 wc_enqueue_js("jQuery('body').trigger('update_checkout');");
1935 }
1936
1937 // Suppress WooCommerce's "invalid coupon" message because this code was
1938 // accepted as a plugin voucher and stored in the voucher session instead.
1939 return '';
1940 }
1941 add_filter('woocommerce_coupon_error', 'wpgv_handle_gift_voucher_application', 10, 3);
1942