eventbrite-for-the-events-c.../includes/class-eb4tec-ticket-manager.php
Laurence Horrocks-Barlow f3bc795d9a Initial release of Eventbrite for The Events Calendar (v1.0.0)
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.
2026-05-17 08:48:04 +01:00

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;
}
}