0 && $action=='refund')
ticketdrum_purchase_refund();
elseif ($id>0 && $action=='resend_emails')
ticketdrum_purchases_resend_emails();
elseif ($action=='edit' && $id>0)
ticketdrum_purchase_edit();
elseif ($id>0 || $action=='add')
ticketdrum_purchases_view();
else
ticketdrum_purchases_list();
}
function ticketdrum_export_purchases() {
if (sanitize_key(wp_unslash($_GET['action'] ?? ''))=='ticketdrum_export_purchases')
{
if (!current_user_can('manage_options')) return;
check_admin_referer('ticketdrum_export');
global $wpdb;
$return_array = [];
$all_custom_fields = $event_custom_fields = [];
$custom_field_rows = $wpdb->get_results($wpdb->prepare("SELECT custom_fields FROM %i",TICKETDRUM_PURCHASES_TABLE),ARRAY_A);
foreach ($custom_field_rows as $custom_field_row)
{
$custom_fields = json_decode($custom_field_row['custom_fields'],true);
#echo('
'.print_r($custom_fields,true).'
');
foreach ($custom_fields as $custom_field_key=>$custom_field_value)
if (strpos($custom_field_key,'event')===0)
foreach ($custom_field_value as $custom_field_sub_key=>$custom_field_sub_value)
$event_custom_fields[] = $custom_field_sub_key;
if (isset($custom_fields['checkout']))
foreach ($custom_fields['checkout'] as $cf_row_key=>$cf_row_val)
$all_custom_fields[] = $cf_row_key;
}
$event_custom_fields = array_values(array_unique($event_custom_fields));
$all_custom_fields = array_values(array_unique($all_custom_fields));
$purchases = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i WHERE status!='pending' ORDER BY added ASC",TICKETDRUM_PURCHASES_TABLE),ARRAY_A);
foreach ($purchases AS $purchase)
{
$ticket_data = json_decode($purchase['ticket_data'],true);
$custom_fields = json_decode($purchase['custom_fields'],true);
$tickets_description = '';
$attendee_count = 0;
foreach ($ticket_data['tickets'] as $ticket_data_row)
{
$ticket_details = $wpdb->get_row($wpdb->prepare("SELECT id,title,price,attendee_count FROM %i WHERE active=1 AND id=%d",TICKETDRUM_TICKETS_TABLE,(int)$ticket_data_row['id']),ARRAY_A );
if ($ticket_data_row['quantity']>0)
{
$event_details = $wpdb->get_row($wpdb->prepare("SELECT id,title FROM %i WHERE id=%d",TICKETDRUM_EVENTS_TABLE,(int)$ticket_data_row['event_id']),ARRAY_A );
$tickets_description.=$ticket_data_row['description']." (".$event_details['title']."), ";
}
$attendee_count+= $ticket_data_row['quantity']*$ticket_details['attendee_count'];
}
$tickets_description = trim($tickets_description,', ');
$row = [];
$row[__('Purchase ID','ticketdrum')] = sprintf('%04d', $purchase['id']);
$row[__('Date','ticketdrum')] = $purchase['added'];
$row[__('Name','ticketdrum')] = $purchase['name'];
$row[__('Email','ticketdrum')] = $purchase['email'];
$row[__('Subtotal','ticketdrum')] = $purchase['subtotal'];
$row[get_option('ticketdrum_tax_title',__('Tax','ticketdrum'))] = $purchase['tax'];
$row[__('Total','ticketdrum')] = $purchase['total'];
$row[__('Status','ticketdrum')] = $purchase['status'];
$row[__('Tickets','ticketdrum')] = $tickets_description;
$row[__('Attendee Count','ticketdrum')] = $attendee_count;
foreach ($all_custom_fields as $custom_field_row)
if (isset($custom_fields['checkout'][$custom_field_row]))
$row[$custom_field_row] = stripslashes($custom_fields['checkout'][$custom_field_row]);
else
$row[$custom_field_row] = '';
#echo(''.print_r($custom_fields,true).'
');
foreach ($event_custom_fields as $custom_field_row)
foreach ($custom_fields as $cf_key=>$custom_field)
if (strpos($cf_key,'event')===0)
{
if (isset($custom_field[$custom_field_row]))
$row[$custom_field_row] = stripslashes($custom_field[$custom_field_row]);
else
$row[$custom_field_row] = '';
}
$return_array[] = $row;
}
#die(''.print_r($return_array,true).'
');
//The buffer is opened and closed within these few lines, before a single header is sent,
//so it cannot be left open for anything else to trip over: fputcsv() writes to
//php://output, ob_get_clean() takes that back as a string, and the download is echoed
//afterwards from the variable.
ob_start();
$df = fopen("php://output", 'w'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- streaming CSV to php://output
fputcsv($df, array_keys(end($return_array)));
foreach ($return_array as $row)
fputcsv($df, $row);
fclose($df); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- streaming CSV to php://output
$csv_output = ob_get_clean();
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment;filename=purchases_export_".gmdate("Ymd").".csv");
header("Content-Transfer-Encoding: binary");
echo $csv_output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV file download, not HTML
exit;
}
}
function ticketdrum_export_tickets() {
if (sanitize_key(wp_unslash($_GET['action'] ?? ''))=='ticketdrum_export_tickets')
{
if (!current_user_can('manage_options')) return;
check_admin_referer('ticketdrum_export');
global $wpdb;
$return_array = [];
$all_custom_fields = [];
$custom_fields = $wpdb->get_results($wpdb->prepare("SELECT custom_fields FROM %i",TICKETDRUM_PURCHASES_TABLE),ARRAY_A);
foreach ($custom_fields as $cf)
{
$cf_decoded = json_decode($cf['custom_fields'],true);
foreach ($cf_decoded as $cf_decoded_type)
foreach ($cf_decoded_type as $cf_row_key=>$cf_row_val)
{
if (is_array(($cf_row_val)))
{
foreach ($cf_row_val as $cftk=>$cftv)
$all_custom_fields[] = $cftk;
}
else
$all_custom_fields[] = $cf_row_key;
}
}
$all_custom_fields = array_unique($all_custom_fields);
sort($all_custom_fields);
$purchases = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i WHERE status!='pending' ORDER BY added ASC",TICKETDRUM_PURCHASES_TABLE),ARRAY_A);
foreach ($purchases AS $purchase)
{
$ticket_data = json_decode($purchase['ticket_data'],true);
$custom_fields = json_decode($purchase['custom_fields'],true);
foreach ($ticket_data['tickets'] as $ticket)
{
for ($ticket_index=0;$ticket_index<$ticket['quantity']*$ticket['attendee_count'];$ticket_index++)
{
$row = [];
$row[__('Purchase ID','ticketdrum')] = sprintf('%04d', $purchase['id']);
$row[__('Date','ticketdrum')] = $purchase['added'];
$row[__('Name','ticketdrum')] = $purchase['name'];
$row[__('Email','ticketdrum')] = $purchase['email'];
$row[__('Ticket','ticketdrum')] = $ticket['title'];
foreach ($all_custom_fields as $custom_field)
{
if (isset($custom_fields['ticket'.$ticket['id']][$ticket_index][$custom_field]))
$row[$custom_field] = $custom_fields['ticket'.$ticket['id']][$ticket_index][$custom_field];
elseif (isset($custom_fields['checkout'][$custom_field]))
$row[$custom_field] = $custom_fields['checkout'][$custom_field];
else
$row[$custom_field] = "";
}
$return_array[] = $row;
}
}
}
//The buffer is opened and closed within these few lines, before a single header is sent,
//so it cannot be left open for anything else to trip over: fputcsv() writes to
//php://output, ob_get_clean() takes that back as a string, and the download is echoed
//afterwards from the variable.
ob_start();
$df = fopen("php://output", 'w'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- streaming CSV to php://output
fputcsv($df, array_keys(end($return_array)));
foreach ($return_array as $row)
fputcsv($df, $row);
fclose($df); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- streaming CSV to php://output
$csv_output = ob_get_clean();
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment;filename=purchases_export_".gmdate("Ymd").".csv");
header("Content-Transfer-Encoding: binary");
echo $csv_output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV file download, not HTML
exit;
}
}
function ticketdrum_purchase_refund() {
ticketdrum_require_admin();
global $wpdb,$ticketdrum_payment_data,$ticketdrum_stripe_mode;
$id = absint(wp_unslash($_GET['id'] ?? 0));
if (!check_admin_referer('ticketdrum_refund_'.$id)) return;
$purchase = $wpdb->get_row($wpdb->prepare("SELECT * FROM %i WHERE id=%d AND status='paid' LIMIT 1",TICKETDRUM_PURCHASES_TABLE,$id),ARRAY_A);
if (!$purchase || $purchase['payment_intent']=='')
{
echo ''.esc_html__('Purchase not found or not refundable.','ticketdrum').'
';
ticketdrum_purchases_view();
return;
}
$refundable = round($purchase['total']-$purchase['refunded_amount'],2);
$refund_amount = round((float)sanitize_text_field(wp_unslash($_POST['refund_amount'] ?? $refundable)),2);
if ($refund_amount<=0 || $refund_amount>$refundable)
{
/* translators: %s: maximum refundable amount, with currency sign */
echo ''.wp_kses_post(sprintf(__('Invalid refund amount - up to %s can be refunded on this order.','ticketdrum'),ticketdrum_currency_format($refundable))).'
';
ticketdrum_purchases_view();
return;
}
$purchase_meta = json_decode($purchase['payment_data'],true);
$mode = (isset($purchase_meta['mode']) && $purchase_meta['mode']=='live')?'live':(isset($purchase_meta['livemode']) && $purchase_meta['livemode']?'live':'test');
if (($ticketdrum_payment_data[$mode.'_stripe_access_token'] ?? '')=='')
$mode = $ticketdrum_stripe_mode;
try {
$TicketDrumStripe = new \TicketDrumStripe\TicketDrumStripeClient($ticketdrum_payment_data[$mode.'_stripe_access_token']);
$refund = $TicketDrumStripe->refunds->create(['payment_intent'=>$purchase['payment_intent'],'amount'=>(int)round($refund_amount*100)]);
} catch (Exception $e) {
/* translators: %s: error message from Stripe */
echo ''.esc_html(sprintf(__('Refund failed: %s','ticketdrum'),$e->getMessage())).'
';
ticketdrum_purchases_view();
return;
}
$new_refunded = round($purchase['refunded_amount']+$refund_amount,2);
$fully_refunded = $new_refunded>=round($purchase['total'],2)-0.005;
$wpdb->update(TICKETDRUM_PURCHASES_TABLE,['refunded_amount'=>$new_refunded,'status'=>$fully_refunded?'refunded':'paid'],['id'=>$id]);
//Spaces just came free - tell the waitlist
if ($fully_refunded)
foreach (array_filter(explode(',',$purchase['event_ids'])) as $freed_event_id)
ticketdrum_waitlist_notify((int)$freed_event_id);
$refund_message = $fully_refunded
/* translators: 1: refund amount with currency sign, 2: buyer name */
? __('Full refund of %1$s issued to %2$s. The buyer will see it in a few working days.','ticketdrum')
/* translators: 1: refund amount with currency sign, 2: buyer name */
: __('Partial refund of %1$s issued to %2$s. The buyer will see it in a few working days.','ticketdrum');
echo ''.wp_kses_post(sprintf($refund_message,ticketdrum_currency_format($refund_amount),esc_html(stripslashes($purchase['name'])))).'
';
ticketdrum_purchases_view();
}
function ticketdrum_purchases_list() {
global $wpdb;
$pagenum = isset( $_GET['pagenum'] ) ? absint( $_GET['pagenum'] ) : 1;
//Three fixed query shapes rather than a query assembled from pieces. Every statement below
//is a string literal handed straight to $wpdb->prepare(), so there is no path by which
//anything from the request reaches the SQL text. Pending rows are in-progress or abandoned
//checkouts rather than orders, so they are excluded throughout.
$limit = 50; // number of rows in page
$offset = ($pagenum - 1) * $limit;
$search_string = trim(sanitize_text_field(wp_unslash($_GET['s'] ?? '')));
$search_like = '%'.$wpdb->esc_like($search_string).'%';
$search_like_ids = '%,'.$wpdb->esc_like($search_string).',%';
if ($search_string=='')
{
$total = (int)$wpdb->get_var($wpdb->prepare(
"SELECT COUNT(id) FROM %i WHERE status!='pending'",
TICKETDRUM_PURCHASES_TABLE));
}
elseif (strlen($search_string)>2)
{
$total = (int)$wpdb->get_var($wpdb->prepare(
"SELECT COUNT(id) FROM %i WHERE status!='pending'
AND (name LIKE %s OR email LIKE %s OR custom_fields LIKE %s OR event_ids LIKE %s OR ticket_ids LIKE %s)",
TICKETDRUM_PURCHASES_TABLE,$search_like,$search_like,$search_like,$search_like_ids,$search_like_ids));
}
else
{
//Very short searches only match whole ids, never free text, so they stay useful
$total = (int)$wpdb->get_var($wpdb->prepare(
"SELECT COUNT(id) FROM %i WHERE status!='pending'
AND (event_ids LIKE %s OR ticket_ids LIKE %s)",
TICKETDRUM_PURCHASES_TABLE,$search_like_ids,$search_like_ids));
}
$num_of_pages = ceil( $total / $limit );
$page_links = paginate_links( array(
'base' => add_query_arg( 'pagenum', '%#%' ),
'format' => '',
'prev_text' => __( '«', 'ticketdrum' ),
'next_text' => __( '»', 'ticketdrum' ),
'total' => $num_of_pages,
'current' => $pagenum
) );
?>
()
get_results($wpdb->prepare(
"SELECT * FROM %i WHERE status!='pending' ORDER BY added DESC LIMIT %d, %d",
TICKETDRUM_PURCHASES_TABLE,$offset,$limit),ARRAY_A);
elseif (strlen($search_string)>2)
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM %i WHERE status!='pending'
AND (name LIKE %s OR email LIKE %s OR custom_fields LIKE %s OR event_ids LIKE %s OR ticket_ids LIKE %s)
ORDER BY added DESC LIMIT %d, %d",
TICKETDRUM_PURCHASES_TABLE,$search_like,$search_like,$search_like,$search_like_ids,$search_like_ids,$offset,$limit),ARRAY_A);
else
$rows = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM %i WHERE status!='pending'
AND (event_ids LIKE %s OR ticket_ids LIKE %s)
ORDER BY added DESC LIMIT %d, %d",
TICKETDRUM_PURCHASES_TABLE,$search_like_ids,$search_like_ids,$offset,$limit),ARRAY_A);
?>
|
|
|
|
|
|
|
|
|
get_row($wpdb->prepare("SELECT id,title,price,attendee_count FROM %i WHERE active=1 AND id=%d",TICKETDRUM_TICKETS_TABLE,(int)$ticket_data_row['id']),ARRAY_A );
if ($ticket_data_row['quantity']>0)
{
$event_details = $wpdb->get_row($wpdb->prepare("SELECT id,title FROM %i WHERE id=%d",TICKETDRUM_EVENTS_TABLE,(int)$ticket_data_row['event_id']),ARRAY_A );
if ($event_details && !in_array(stripslashes($event_details['title']),$event_names)) $event_names[] = stripslashes($event_details['title']);
$tickets_description.=$ticket_data_row['description'].", ";
}
$attendee_count+= $ticket_data_row['quantity']*$ticket_details['attendee_count'];
}
$tickets_description = trim($tickets_description,', ');
?>
|
|
|
|
|
|
|
|
|
' . wp_kses_post($page_links) . '
'; ?>
get_row($wpdb->prepare("SELECT * FROM %i WHERE id=%d",TICKETDRUM_PURCHASES_TABLE,$id),ARRAY_A );
$ticket_data = json_decode($purchase['ticket_data'],true);
$custom_fields = json_decode($purchase['custom_fields'],true);
$attendees = json_decode($purchase['attendees'],true);
$payment_data = json_decode($purchase['payment_data'],true);
$tickets_description = '';
$attendee_count = 0;
foreach ($ticket_data['tickets'] as $ticket_data_row)
{
$ticket_details = $wpdb->get_row($wpdb->prepare("SELECT id,title,price,attendee_count FROM %i WHERE active=1 AND id=%d",TICKETDRUM_TICKETS_TABLE,(int)$ticket_data_row['id']),ARRAY_A );
if ($ticket_data_row['quantity']>0)
{
$event_details = $wpdb->get_row($wpdb->prepare("SELECT id,title FROM %i WHERE id=%d",TICKETDRUM_EVENTS_TABLE,(int)$ticket_data_row['event_id']),ARRAY_A );
$tickets_description.=$ticket_data_row['description']." (".$event_details['title']."), ";
}
$attendee_count+= $ticket_data_row['quantity']*$ticket_details['attendee_count'];
}
$tickets_description = trim($tickets_description,', ');
$event_custom_fields = [];
foreach ($custom_fields as $custom_field_key=>$custom_field_value)
if (strpos($custom_field_key,'event')===0)
{
$event_details = $wpdb->get_row($wpdb->prepare("SELECT id,title FROM %i WHERE id=%d",TICKETDRUM_EVENTS_TABLE,(int)str_replace('event','',$custom_field_key)),ARRAY_A );
$event_custom_fields[] = [
'title' => $event_details['title'],
'fields' => $custom_field_value
];
}
$discount = (float)($ticket_data['discount'] ?? 0);
$promo_code = $ticket_data['promo_code'] ?? '';
$buyer_paid_fee = ((float)$purchase['fee']>0 && round($purchase['total'],2)>round($purchase['subtotal']+$purchase['tax']-$discount,2));
?>
__('paid','ticketdrum'),'pending'=>__('pending','ticketdrum'),'refunded'=>__('refunded','ticketdrum')]; echo esc_html($status_labels[$purchase['status']] ?? __('unknown','ticketdrum'));?>
| | | |
|
$ticket_custom_fields) { ?>
$value) echo ' '.esc_html($title).': '.esc_html($value); ?>
|
|
|
|
| |
0): ?>
| - |
0): ?>
| |
0): ?>
| | |
| |
0): ?>
| - |
| |
0) { ?>
$value) echo '
'.esc_html($title).': '.esc_html($value); ?>
«
get_row($wpdb->prepare("SELECT * FROM %i WHERE id=%d",TICKETDRUM_PURCHASES_TABLE,$id),ARRAY_A );
$custom_fields = json_decode($purchase['custom_fields'],true);
$ticket_data = json_decode($purchase['ticket_data'],true);
// Update basic purchase info
$custom_fields['checkout'] = map_deep(wp_unslash($_POST['custom_fields'] ?? []),'sanitize_text_field');
// Update tickets if provided
if (isset($_POST['tickets'])) {
// Clear old ticket data and rebuild
$new_tickets = [];
$new_subtotal = 0;
$new_tax = 0;
//Ticket IDs and quantities only, so absint on every posted value is the whole of
//the validation this needs; anything non-numeric becomes 0 and is skipped below.
$posted_tickets = map_deep(wp_unslash($_POST['tickets'] ?? []),'absint');
foreach ((array)$posted_tickets as $ticket_index => $ticket_info) {
if (!is_array($ticket_info)) continue;
if ((int)($ticket_info['quantity'] ?? 0) > 0) {
// Get ticket details from database
$ticket_details = $wpdb->get_row($wpdb->prepare("SELECT t.*, e.id as event_id FROM %i t LEFT JOIN %i e ON t.event_id = e.id WHERE t.id=%d",TICKETDRUM_TICKETS_TABLE,TICKETDRUM_EVENTS_TABLE,(int)($ticket_info['ticket_id'] ?? 0)), ARRAY_A);
if ($ticket_details) {
$quantity = (int)$ticket_info['quantity'];
$ticket_price = (float)$ticket_details['price'];
$line_subtotal = $quantity * $ticket_price;
// Calculate tax for this ticket
$tax_rate = $ticket_details['tax'] !== null ? (float)$ticket_details['tax'] : (float)get_option('ticketdrum_tax_rate', 0);
$line_tax = 0;
if (get_option('ticketdrum_tax_mode','none') != 'none' && $tax_rate > 0) {
if (get_option('ticketdrum_tax_mode','none') == 'excluded') {
$line_tax = $line_subtotal * ($tax_rate / 100);
} elseif (get_option('ticketdrum_tax_mode','none') == 'included') {
$line_tax = $line_subtotal - ($line_subtotal / (1 + $tax_rate / 100));
}
}
$new_tickets[] = [
'id' => $ticket_details['id'],
'quantity' => $quantity,
'title' => $ticket_details['title'],
'description' => $ticket_details['title'],
'event_id' => $ticket_details['event_id'],
'attendee_count' => $ticket_details['attendee_count']
];
$new_subtotal += $line_subtotal;
$new_tax += $line_tax;
}
}
}
$ticket_data['tickets'] = $new_tickets;
// Calculate new total
$new_total = $new_subtotal;
if (get_option('ticketdrum_tax_mode','none') == 'excluded') {
$new_total = $new_subtotal + $new_tax;
}
// Update purchase totals
$purchase_data_updates = [
'subtotal' => round($new_subtotal, 2),
'tax' => round($new_tax, 2),
'total' => round($new_total, 2)
];
} else {
$purchase_data_updates = [];
}
$custom_fields = json_encode($custom_fields);
$ticket_data = json_encode($ticket_data);
// Prepare update data
$update_data = array(
'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
'email' => sanitize_text_field(wp_unslash($_POST['email'] ?? '')),
'custom_fields' => sanitize_text_field($custom_fields),
'ticket_data' => sanitize_text_field($ticket_data)
);
// Add recalculated totals if tickets were updated
if (!empty($purchase_data_updates)) {
$update_data = array_merge($update_data, $purchase_data_updates);
}
//echo ''.print_r($_POST,true).'
';die();
$wpdb->update(
TICKETDRUM_PURCHASES_TABLE, //table
$update_data, //data
array('ID' => $id) //where
);
} else {//selecting value to update
$purchase = $wpdb->get_row($wpdb->prepare("SELECT * FROM %i WHERE id=%d",TICKETDRUM_PURCHASES_TABLE,$id),ARRAY_A );
$ticket_data = json_decode($purchase['ticket_data'],true);
$custom_fields = json_decode($purchase['custom_fields'],true);
$attendees = json_decode($purchase['attendees'],true);
$payment_data = json_decode($purchase['payment_data'],true);
//echo ''.print_r($ticket_data,true).'
';
}
?>