flush_response(); $unprocessed_event_ids = FrmSquareLiteConnectHelper::get_unprocessed_event_ids(); if ( $unprocessed_event_ids ) { $this->process_event_ids( $unprocessed_event_ids ); } wp_send_json_success(); } /** * @since 6.22 * * @param array $event_ids * @return void */ private function process_event_ids( $event_ids ) { foreach ( $event_ids as $event_id ) { if ( $this->should_skip_event( $event_id ) ) { continue; } set_transient( 'frm_square_last_process_' . $event_id, time(), 60 ); $this->event = FrmSquareLiteConnectHelper::get_event( $event_id ); if ( is_object( $this->event ) ) { $this->handle_event(); $this->track_handled_event( $event_id ); FrmSquareLiteConnectHelper::process_event( $event_id ); } else { $this->count_failed_event( $event_id ); } } } /** * @since 6.22 * * @param string $event_id * @return bool True if the event should be skipped. */ private function should_skip_event( $event_id ) { if ( $this->last_attempt_to_process_event_is_too_recent( $event_id ) ) { return true; } $option = get_option( self::$events_to_skip_option_name ); if ( ! is_array( $option ) ) { return false; } return in_array( $event_id, $option, true ); } /** * @param string $event_id * @return bool */ private function last_attempt_to_process_event_is_too_recent( $event_id ) { $last_process_attempt = get_transient( 'frm_square_last_process_' . $event_id ); return is_numeric( $last_process_attempt ) && $last_process_attempt > time() - 60; } /** * @since 6.22 * * @param string $event_id * @return void */ private function count_failed_event( $event_id ) { $transient_name = 'frm_square_failed_event_' . $event_id; $transient = get_transient( $transient_name ); if ( is_int( $transient ) ) { $failed_count = $transient + 1; } else { $failed_count = 1; } $maximum_retries = 3; if ( $failed_count >= $maximum_retries ) { $this->track_handled_event( $event_id ); } else { set_transient( $transient_name, $failed_count, 4 * DAY_IN_SECONDS ); } } /** * Track an event to no longer process. * This is called for successful events, and also for failed events after a number of retries. * * @since 6.22 * * @param string $event_id * @return void */ private function track_handled_event( $event_id ) { $option = get_option( self::$events_to_skip_option_name ); if ( is_array( $option ) ) { if ( count( $option ) > 1000 ) { // Prevent the option from getting too big by removing the front item before adding the next. array_shift( $option ); } } else { $option = array(); } $option[] = $event_id; update_option( self::$events_to_skip_option_name, $option, false ); } /** * @return void */ private function handle_event() { switch ( $this->event->type ) { case 'payment.created': $payment_id = $this->event->data->id; $subscription = FrmSquareLiteConnectHelper::get_subscription_id_for_payment( $payment_id ); if ( is_object( $subscription ) && isset( $subscription->id ) ) { $subscription_id = $subscription->id; $this->add_subscription_payment( $subscription_id ); } break; case 'payment.updated': $payment_id = $this->event->data->id; $frm_payment = new FrmTransLitePayment(); $payment = $frm_payment->get_one_by( $payment_id, 'receipt_id' ); if ( $payment ) { $status = $this->event->data->object->payment->status; if ( 'COMPLETED' === $status && 'complete' !== $payment->status ) { $payment_values = (array) $payment; $payment_values['status'] = 'complete'; $frm_payment->update( $payment->id, $payment_values ); FrmTransLiteActionsController::trigger_payment_status_change( array( 'status' => 'complete', 'payment' => $payment, ) ); } return; } break; case 'subscription.updated': $subscription_id = $this->event->data->id; $frm_sub = new FrmTransLiteSubscription(); $sub = $frm_sub->get_one_by( $subscription_id, 'sub_id' ); if ( $sub ) { $status = $this->event->data->object->subscription->status; if ( 'DEACTIVATED' === $status ) { $status = 'canceled'; FrmTransLiteSubscriptionsController::change_subscription_status( array( 'status' => $status, 'sub' => $sub, ) ); } } break; }//end switch } /** * Add a payment row for the payments table. * * @param string $subscription_id The Square ID for the current subscription. * @return void */ private function add_subscription_payment( $subscription_id ) { $payment_id = $this->event->data->id; $frm_payment = new FrmTransLitePayment(); $payment = $frm_payment->get_one_by( $payment_id, 'receipt_id' ); if ( $payment ) { // Avoid adding the same payment twice. return; } $frm_sub = new FrmTransLiteSubscription(); $sub = $frm_sub->get_one_by( $subscription_id, 'sub_id' ); if ( ! $sub ) { return; } $payment_object = $this->event->data->object->payment; if ( 'JPY' === $payment_object->amount_money->currency ) { // Japanese does not include the additional 2 digits. $amount = $payment_object->amount_money->amount; } else { $amount = number_format( floatval( $payment_object->amount_money->amount ) / 100, 2 ); } $begin_date = gmdate( 'Y-m-d' ); $expire_date = '0000-00-00'; $subscription = FrmSquareLiteConnectHelper::get_subscription( $sub->sub_id ); if ( is_object( $subscription ) ) { if ( ! empty( $subscription->start_date ) ) { $begin_date = gmdate( 'Y-m-d', strtotime( $subscription->start_date ) ); } if ( ! empty( $subscription->charged_through_date ) ) { $expire_date = gmdate( 'Y-m-d', strtotime( $subscription->charged_through_date ) ); $frm_sub->update( $sub->id, array( 'next_bill_date' => gmdate( 'Y-m-d', strtotime( $expire_date ) ) ) ); } } $frm_payment = new FrmTransLitePayment(); $frm_payment->create( array( 'paysys' => 'square', 'amount' => $amount, 'status' => 'authorized', 'item_id' => $sub->item_id, 'action_id' => $sub->action_id, 'receipt_id' => $payment_id, 'sub_id' => $sub->id, 'test' => 'test' === FrmSquareLiteAppHelper::active_mode() ? 1 : 0, 'begin_date' => $begin_date, 'expire_date' => $expire_date, ) ); } }