Tribe__Tickets__Commerce__PayPal__Main::generate_tickets( string $payment_status = 'completed', bool $redirect = true )
Generate and store all the attendees information for a new order.
Contents
Parameters
- $redirect
-
(bool) (Optional) Whether the client should be redirected or not.
Default value: true
- $payment_status
-
(string) (Optional) The tickets payment status, defaults to completed.
Default value: 'completed'
Source
File: src/Tribe/Commerce/PayPal/Main.php
public function generate_tickets( $payment_status = 'completed', $redirect = true ) {
/** @var Tribe__Tickets__Commerce__PayPal__Gateway $gateway */
$gateway = tribe( 'tickets.commerce.paypal.gateway' );
$transaction_data = $gateway->get_transaction_data();
/** @var Tribe__Tickets__Commerce__PayPal__Cart__Interface $cart */
$cart = tribe( 'tickets.commerce.paypal.cart' );
/**
* The `invoice` variable is a passthrough one; if passed when adding items to the cart
* then it should be returned to us from PayPal. If we have it in the transaction data
* we can assume the cart associated with the invoice, if any, can be removed.
*
* @link https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/formbasics/#variations-on-basic-variables
*/
if ( ! empty( $transaction_data['custom'] ) ) {
$decoded_custom = Tribe__Tickets__Commerce__PayPal__Custom_Argument::decode( $transaction_data['custom'], true );
if ( isset( $decoded_custom['invoice'] ) ) {
$cart->set_id( $decoded_custom['invoice'] );
$cart->clear();
}
}
$raw_transaction_data = $gateway->get_raw_transaction_data();
if ( empty( $transaction_data ) || empty( $transaction_data['items'] ) ) {
return;
}
$has_tickets = $post_id = false;
/**
* PayPal Ticket specific action fired just before a PayPalTicket-driven attendee tickets for an order are generated
*
* @since 4.7
*
* @param array $transaction_data PayPal payment data
*/
do_action( 'tribe_tickets_tpp_before_order_processing', $transaction_data );
$order_id = $transaction_data['txn_id'];
$is_refund = Tribe__Tickets__Commerce__PayPal__Stati::$refunded === $payment_status
|| 'refund' === Tribe__Utils__Array::get( $transaction_data, 'reason_code', '' );
if ( $is_refund ) {
$transaction_data['payment_status'] = $payment_status = Tribe__Tickets__Commerce__PayPal__Stati::$refunded;
$refund_order_id = $order_id;
$order_id = Tribe__Utils__Array::get( $transaction_data, 'parent_txn_id', $order_id );
$order = Tribe__Tickets__Commerce__PayPal__Order::from_order_id( $order_id );
$order->refund_with( $refund_order_id );
unset( $transaction_data['txn_id'], $transaction_data['parent_txn_id'] );
$order->hydrate_from_transaction_data( $transaction_data );
} else {
$order = Tribe__Tickets__Commerce__PayPal__Order::from_transaction_data( $transaction_data );
}
$order->set_meta( 'transaction_data', $raw_transaction_data );
$custom = Tribe__Tickets__Commerce__PayPal__Custom_Argument::decode( $transaction_data['custom'], true );
/*
* This method might run during a POST (IPN) PayPal request hence the
* purchasing user ID, if any, will be stored in a custom PayPal var.
* Let's fallback on the current user ID for GET requests (PDT); it will be always `0`
* during a PayPal POST (IPN) request.
*/
$attendee_user_id = ! isset( $custom['user_id'] ) ? get_current_user_id() : absint( $custom['user_id'] );
$attendee_full_name = empty( $transaction_data['first_name'] ) && empty( $transaction_data['last_name'] )
? ''
: sanitize_text_field( "{$transaction_data['first_name']} {$transaction_data['last_name']}" );
$attendee_email = empty( $transaction_data['payer_email'] ) ? null : sanitize_email( $transaction_data['payer_email'] );
$attendee_email = is_email( $attendee_email ) ? $attendee_email : null;
if ( ! empty( $attendee_user_id ) ) {
$attendee = get_user_by( 'id', $attendee_user_id );
// Check if the user was found.
if ( $attendee ) {
// Check if the user has an email address.
if ( $attendee->user_email ) {
$attendee_email = $attendee->user_email;
}
$user_full_name = trim( "{$attendee->first_name} {$attendee->last_name}" );
// Check if the user has first/last name.
if ( ! empty( $user_full_name ) ) {
$attendee_full_name = $user_full_name;
}
}
}
/**
* This is an array of tickets IDs for which the user decided to opt-out.
*
* @see \Tribe__Tickets_Plus__Commerce__PayPal__Attendees::register_optout_choice()
*/
$attendee_optouts = Tribe__Utils__Array::list_to_array( Tribe__Utils__Array::get( $custom, 'oo', array() ), ',' );
if ( ! $attendee_email || ! $attendee_full_name ) {
$this->redirect_after_error( 101, $redirect, $post_id );
return;
}
// Iterate over each product
foreach ( (array) $transaction_data['items'] as $item ) {
$order_attendee_id = 0;
if ( empty( $item['ticket'] ) ) {
continue;
}
/** @var \Tribe__Tickets__Ticket_Object $ticket_type */
$ticket_type = $item['ticket'];
$product_id = $ticket_type->ID;
// Get the event this tickets is for
$post = $ticket_type->get_event();
if ( empty( $post ) ) {
continue;
}
$post_id = $post->ID;
// if there were no PayPal tickets for the product added to the cart, continue
if ( empty( $item['quantity'] ) ) {
continue;
}
// get the PayPal status `decrease_stock_by` value
$status_stock_size = 1;
$ticket_qty = (int) $item['quantity'];
// to avoid tickets from not being created on a status stock size of 0
// let's take the status stock size into account and create a number of tickets
// at least equal to the number of tickets the user requested
$ticket_qty = $status_stock_size < 1 ? $ticket_qty : $status_stock_size * $ticket_qty;
$qty = max( $ticket_qty, 0 );
// Throw an error if Qty is bigger then Remaining
if ( $ticket_type->managing_stock() && $payment_status === Tribe__Tickets__Commerce__PayPal__Stati::$completed ) {
$this->ignore_pending_stock_logic( true );
$inventory = (int) $ticket_type->inventory();
$this->ignore_pending_stock_logic( false );
$inventory_is_not_unlimited = - 1 !== $inventory;
if ( $inventory_is_not_unlimited && $qty > $inventory ) {
if ( ! $order->was_pending() ) {
$this->redirect_after_error( 102, $redirect, $post_id );
return;
}
/** @var Tribe__Tickets__Commerce__PayPal__Oversell__Policies $oversell_policies */
$oversell_policies = tribe( 'tickets.commerce.paypal.oversell.policies' );
$oversell_policy = $oversell_policies->for_post_ticket_order( $post_id, $ticket_type->ID, $order_id );
$qty = $oversell_policy->modify_quantity( $qty, $inventory );
if ( ! $oversell_policy->allows_overselling() ) {
$oversold_attendees = $this->get_attendees_by_order_id( $order_id );
$oversell_policy->handle_oversold_attendees( $oversold_attendees );
$this->redirect_after_error( 102, $redirect, $post_id );
return;
}
}
}
if ( $qty === 0 ) {
$this->redirect_after_error( 103, $redirect, $post_id );
return;
}
$has_tickets = true;
/**
* PayPal specific action fired just before a PayPal-driven attendee ticket for an event is generated
*
* @since 4.7
*
* @param int $post_id ID of event
* @param string $ticket_type Ticket Type object for the product
* @param array $data Parsed PayPal transaction data
*/
do_action( 'tribe_tickets_tpp_before_attendee_ticket_creation', $post_id, $ticket_type, $transaction_data );
$existing_attendees = $this->get_attendees_by_order_id( $order_id );
$has_generated_new_tickets = false;
/** @var Tribe__Tickets__Commerce__Currency $currency */
$currency = tribe( 'tickets.commerce.currency' );
$currency_symbol = $currency->get_currency_symbol( $product_id, true );
// Iterate over all the amount of tickets purchased (for this product)
for ( $i = 0; $i < $qty; $i ++ ) {
$attendee_id = null;
$updating_attendee = false;
// check if we already have an attendee or not
$post_title = $attendee_full_name . ' | ' . ( $i + 1 );
$criteria = array( 'post_title' => $post_title, 'product_id' => $product_id, 'event_id' => $post_id );
$existing_attendee = wp_list_filter( $existing_attendees, $criteria );
if ( ! empty( $existing_attendee ) ) {
$existing_attendee = reset( $existing_attendee );
$updating_attendee = true;
$attendee_id = $existing_attendee['attendee_id'];
} else {
$attendee = array(
'post_status' => 'publish',
'post_title' => $post_title,
'post_type' => $this->attendee_object,
'ping_status' => 'closed',
);
// Insert individual ticket purchased
$attendee_id = wp_insert_post( $attendee );
// since we are creating at least one
$has_generated_new_tickets = true;
}
$global_stock = new Tribe__Tickets__Global_Stock( $post_id );
$shared_capacity = false;
if ( $global_stock->is_enabled() ) {
$shared_capacity = true;
}
if ( $status_stock_size > 0 ) {
switch ( $payment_status ) {
case Tribe__Tickets__Commerce__PayPal__Stati::$completed:
$this->increase_ticket_sales_by( $product_id, 1, $shared_capacity, $global_stock );
break;
case Tribe__Tickets__Commerce__PayPal__Stati::$refunded:
$this->decrease_ticket_sales_by( $product_id, 1, $shared_capacity, $global_stock );
break;
default:
break;
}
}
$attendee_order_status = trim( strtolower( $payment_status ) );
if ( ! $updating_attendee ) {
update_post_meta( $attendee_id, $this->attendee_product_key, $product_id );
update_post_meta( $attendee_id, $this->attendee_event_key, $post_id );
update_post_meta( $attendee_id, $this->security_code, $this->generate_security_code( $attendee_id ) );
update_post_meta( $attendee_id, $this->order_key, $order_id );
$attendee_optout = Tribe__Utils__Array::get( $attendee_optouts, $product_id, false );
update_post_meta( $attendee_id, $this->attendee_optout_key, (bool) $attendee_optout );
update_post_meta( $attendee_id, $this->email, $attendee_email );
update_post_meta( $attendee_id, $this->full_name, $attendee_full_name );
update_post_meta( $attendee_id, '_paid_price', get_post_meta( $product_id, '_price', true ) );
update_post_meta( $attendee_id, '_price_currency_symbol', $currency_symbol );
}
update_post_meta( $attendee_id, $this->attendee_tpp_key, $attendee_order_status );
if ( Tribe__Tickets__Commerce__PayPal__Stati::$refunded === $payment_status ) {
$refund_order_id = Tribe__Utils__Array::get( $transaction_data, 'txn_id', '' );
update_post_meta( $attendee_id, $this->refund_order_key, $refund_order_id );
}
if ( ! $updating_attendee ) {
/**
* Action fired when an PayPal attendee ticket is created
*
* @since 4.7
*
* @param int $attendee_id Attendee post ID
* @param int $order_id PayPal Order ID
* @param int $product_id PayPal ticket post ID
* @param int $order_attendee_id Attendee number in submitted order
* @param string $attendee_order_status The order status for the attendee.
*/
do_action( 'event_tickets_tpp_attendee_created', $attendee_id, $order_id, $product_id, $order_attendee_id, $attendee_order_status );
}
/**
* Action fired when an PayPal attendee ticket is updated.
*
* This action will fire both when the attendee is created and
* when the attendee is updated.
* Hook into the `event_tickets_tpp_attendee_created` action to
* only act on the attendee creation.
*
* @since 4.7
*
* @param int $attendee_id Attendee post ID
* @param int $order_id PayPal Order ID
* @param int $product_id PayPal ticket post ID
* @param int $order_attendee_id Attendee number in submitted order
* @param string $attendee_order_status The order status for the attendee.
*/
do_action( 'event_tickets_tpp_attendee_updated', $attendee_id, $order_id, $product_id, $order_attendee_id, $attendee_order_status );
$order->add_attendee( $attendee_id );
$this->record_attendee_user_id( $attendee_id, $attendee_user_id );
$order_attendee_id++;
if ( ! empty( $existing_attendee ) ) {
$existing_attendees = wp_list_filter( $existing_attendees, array( 'attendee_id' => $existing_attendee['attendee_id'] ), 'NOT' );
}
}
if ( ! ( empty( $existing_attendees ) || empty( $oversell_policy ) ) ) {
// an oversell policy applied: what to do with existing oversold attendees?
$oversell_policy->handle_oversold_attendees( $existing_attendees );
}
if ( $has_generated_new_tickets ) {
/**
* Action fired when a PayPal has had attendee tickets generated for it.
*
* @since 4.7
*
* @param int $product_id PayPal ticket post ID
* @param int $order_id ID of the PayPal order
* @param int $qty Quantity ordered
*/
do_action( 'event_tickets_tpp_tickets_generated_for_product', $product_id, $order_id, $qty );
}
/**
* Action fired when a PayPal has had attendee tickets updated for it.
*
* This will fire even when tickets are initially created; if you need to hook on the
* creation process only use the 'event_tickets_tpp_tickets_generated_for_product' action.
*
* @since 4.7
*
* @param int $product_id PayPal ticket post ID
* @param int $order_id ID of the PayPal order
* @param int $qty Quantity ordered
*/
do_action( 'event_tickets_tpp_tickets_generated_for_product', $product_id, $order_id, $qty );
// After Adding the Values we Update the Transient
Tribe__Post_Transient::instance()->delete( $post_id, Tribe__Tickets__Tickets::ATTENDEES_CACHE );
}
$order->update();
/**
* Fires when an PayPal attendee tickets have been generated.
*
* @since 4.7
*
* @param int $order_id ID of the PayPal order
* @param int $post_id ID of the post the order was placed for
*/
do_action( 'event_tickets_tpp_tickets_generated', $order_id, $post_id );
/**
* Filters whether a confirmation email should be sent or not for PayPal tickets.
*
* This applies to attendance and non attendance emails.
*
* @since 4.7
*
* @param bool $send_mail Defaults to `true`.
*/
$send_mail = apply_filters( 'tribe_tickets_tpp_send_mail', true );
if (
$send_mail
&& $has_tickets
&& $attendee_order_status === Tribe__Tickets__Commerce__PayPal__Stati::$completed
) {
$this->send_tickets_email( $order_id, $post_id );
}
// Redirect to the same page to prevent double purchase on refresh
if ( ! empty( $post_id ) ) {
/** @var \Tribe__Tickets__Commerce__PayPal__Endpoints $endpoints */
$endpoints = tribe( 'tickets.commerce.paypal.endpoints' );
$url = $endpoints->success_url( $order_id, $post_id );
if ( $redirect ) {
wp_redirect( esc_url_raw( $url ) );
}
tribe_exit();
}
}
Changelog
| Version | Description |
|---|---|
| 4.7 | Introduced. |