Bidirectional sync between Eventbrite and The Events Calendar, with WooCommerce ticket purchasing that bypasses Eventbrite's processing fees by registering buyers as free attendees via API. Includes venue/ organizer sync, QR code ticket generation, attendee management with CSV export, scheduled sync via WP-Cron, and real-time Eventbrite webhooks.
113 lines
3.8 KiB
PHP
113 lines
3.8 KiB
PHP
<?php
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
final class EB4TEC_Ticket_Manager {
|
|
|
|
public function __construct( private readonly EB4TEC_API_Client $api ) {}
|
|
|
|
/**
|
|
* Ensure the hidden "WordPress Purchase" free ticket class exists on Eventbrite.
|
|
* Creates it if absent; updates capacity if it has changed.
|
|
* Returns the Eventbrite ticket class ID.
|
|
*/
|
|
public function ensure_wp_ticket_class( string $eb_event_id, int $post_id, int $capacity ): string|WP_Error {
|
|
$existing_class_id = (string) get_post_meta( $post_id, '_eb4tec_wp_ticket_class_id', true );
|
|
|
|
if ( $existing_class_id ) {
|
|
// Update capacity in case it changed.
|
|
$result = $this->api->update_ticket_class( $eb_event_id, $existing_class_id, $this->build_ticket_class_body( $capacity, $eb_event_id ) );
|
|
if ( is_wp_error( $result ) ) {
|
|
// Stale ID — try creating a fresh one.
|
|
delete_post_meta( $post_id, '_eb4tec_wp_ticket_class_id' );
|
|
return $this->create_wp_ticket_class( $eb_event_id, $post_id, $capacity );
|
|
}
|
|
return $existing_class_id;
|
|
}
|
|
|
|
return $this->create_wp_ticket_class( $eb_event_id, $post_id, $capacity );
|
|
}
|
|
|
|
private function create_wp_ticket_class( string $eb_event_id, int $post_id, int $capacity ): string|WP_Error {
|
|
$result = $this->api->create_ticket_class( $eb_event_id, $this->build_ticket_class_body( $capacity, $eb_event_id ) );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
return $result;
|
|
}
|
|
|
|
$class_id = $result['id'] ?? '';
|
|
if ( empty( $class_id ) ) {
|
|
return new WP_Error( 'eb4tec_no_class_id', __( 'Eventbrite did not return a ticket class ID.', 'eb4tec' ) );
|
|
}
|
|
|
|
update_post_meta( $post_id, '_eb4tec_wp_ticket_class_id', sanitize_text_field( $class_id ) );
|
|
return $class_id;
|
|
}
|
|
|
|
/**
|
|
* Sync capacity to both the Eventbrite ticket class and the WooCommerce product stock.
|
|
*/
|
|
public function sync_capacity( int $post_id, int $new_capacity ): bool {
|
|
update_post_meta( $post_id, '_eb4tec_capacity', $new_capacity );
|
|
update_post_meta( $post_id, '_EventCapacity', $new_capacity );
|
|
|
|
// Update WC product stock.
|
|
$product_id = (int) get_post_meta( $post_id, '_eb4tec_wp_product_id', true );
|
|
if ( $product_id ) {
|
|
$product = wc_get_product( $product_id );
|
|
if ( $product ) {
|
|
$product->set_stock_quantity( $new_capacity );
|
|
$product->save();
|
|
}
|
|
}
|
|
|
|
// Update EB ticket class.
|
|
$eb_event_id = (string) get_post_meta( $post_id, '_eb4tec_event_id', true );
|
|
$class_id = (string) get_post_meta( $post_id, '_eb4tec_wp_ticket_class_id', true );
|
|
if ( $eb_event_id && $class_id ) {
|
|
$result = $this->api->update_ticket_class( $eb_event_id, $class_id, $this->build_ticket_class_body( $new_capacity, $eb_event_id ) );
|
|
return ! is_wp_error( $result );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Remaining available spots based on WooCommerce stock.
|
|
*/
|
|
public function get_available_capacity( int $post_id ): int {
|
|
$product_id = (int) get_post_meta( $post_id, '_eb4tec_wp_product_id', true );
|
|
if ( ! $product_id ) {
|
|
return (int) get_post_meta( $post_id, '_eb4tec_capacity', true );
|
|
}
|
|
|
|
$product = wc_get_product( $product_id );
|
|
if ( ! $product || ! $product->managing_stock() ) {
|
|
return (int) get_post_meta( $post_id, '_eb4tec_capacity', true );
|
|
}
|
|
|
|
return max( 0, (int) $product->get_stock_quantity() );
|
|
}
|
|
|
|
private function build_ticket_class_body( int $capacity, string $eb_event_id ): array {
|
|
$label = (string) get_option( 'eb4tec_wp_ticket_label', 'WordPress Purchase' );
|
|
$hidden = get_option( 'eb4tec_wp_ticket_visible', 'hidden' ) === 'hidden';
|
|
|
|
$body = [
|
|
'ticket_class' => [
|
|
'name' => $label,
|
|
'free' => true,
|
|
'minimum_quantity' => 1,
|
|
'maximum_quantity' => 10,
|
|
'hidden' => $hidden,
|
|
],
|
|
];
|
|
|
|
if ( $capacity > 0 ) {
|
|
$body['ticket_class']['quantity_total'] = $capacity;
|
|
}
|
|
|
|
return $body;
|
|
}
|
|
}
|