PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.6.3
LatePoint – Calendar Booking Plugin for Appointments and Events v5.6.3
5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / models / transaction_intent_model.php
latepoint / lib / models Last commit date
activity_model.php 3 months ago agent_meta_model.php 3 months ago agent_model.php 3 months ago booking_meta_model.php 3 months ago booking_model.php 1 week ago bundle_meta_model.php 3 months ago bundle_model.php 1 week ago cart_item_model.php 3 months ago cart_meta_model.php 3 months ago cart_model.php 2 weeks ago connector_model.php 3 months ago customer_meta_model.php 3 months ago customer_model.php 1 month ago invoice_model.php 2 weeks ago join_bundles_services_model.php 3 months ago location_category_model.php 3 months ago location_model.php 3 months ago meta_model.php 3 months ago model.php 3 months ago off_period_model.php 3 months ago order_intent_meta_model.php 3 months ago order_intent_model.php 1 week ago order_item_model.php 3 months ago order_meta_model.php 3 months ago order_model.php 1 month ago otp_model.php 3 months ago payment_request_model.php 3 months ago process_job_model.php 3 months ago process_model.php 1 month ago recurrence_model.php 3 months ago service_category_model.php 3 months ago service_meta_model.php 3 months ago service_model.php 3 months ago session_model.php 3 months ago settings_model.php 3 months ago step_settings_model.php 3 months ago transaction_intent_model.php 3 months ago transaction_model.php 3 months ago transaction_refund_model.php 3 months ago work_period_model.php 3 months ago
transaction_intent_model.php
378 lines
1 <?php
2 /*
3 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
4 */
5
6 class OsTransactionIntentModel extends OsModel {
7 var $id,
8 $intent_key,
9 $order_id,
10 $customer_id,
11 $invoice_id,
12 $transaction_id,
13 $payment_data = '',
14 $payment_data_arr,
15 $other_data,
16 $charge_amount,
17 $specs_charge_amount,
18 $status,
19 $order_form_page_url,
20 $updated_at,
21 $created_at;
22
23 function __construct( $id = false ) {
24 parent::__construct();
25 $this->table_name = LATEPOINT_TABLE_TRANSACTION_INTENTS;
26
27 if ( $id ) {
28 $this->load_by_id( $id );
29 }
30 }
31
32 public function calculate_specs_charge_amount() {
33 /**
34 * Convert transaction intent charge amount to specs
35 *
36 * @param {string} $charge_amount original charge amount of a transaction intent
37 * @param {OsTransactionIntentModel} $transaction_intent transaction intent model
38 *
39 * @returns {string} The filtered to specs charge amount
40 *
41 * @since 5.1.3
42 * @hook latepoint_transaction_intent_specs_charge_amount
43 *
44 */
45 $this->specs_charge_amount = apply_filters( 'latepoint_transaction_intent_specs_charge_amount', $this->charge_amount, $this );
46 }
47
48 protected function params_to_sanitize() {
49 return [
50 'charge_amount' => 'money',
51 ];
52 }
53
54
55 public function get_payment_data_value( string $key ): string {
56 if ( ! isset( $this->payment_data_arr ) ) {
57 $this->payment_data_arr = json_decode( $this->payment_data, true );
58 }
59
60 return $this->payment_data_arr[ $key ] ?? '';
61 }
62
63 public function set_payment_data_value( string $key, string $value, bool $save = true ) {
64 $this->payment_data_arr = json_decode( $this->payment_data, true );
65 $this->payment_data_arr[ $key ] = $value;
66 $this->payment_data = wp_json_encode( $this->payment_data_arr );
67 if ( $save ) {
68 $this->update_attributes( [ 'payment_data' => $this->payment_data ] );
69 }
70 }
71
72
73 public function is_processing(): bool {
74 return $this->status == LATEPOINT_TRANSACTION_INTENT_STATUS_PROCESSING;
75 }
76 public function is_failed(): bool {
77 return $this->status == LATEPOINT_TRANSACTION_INTENT_STATUS_FAILED;
78 }
79
80 public function wait_for_transaction_completion(): OsTransactionIntentModel {
81 $attempts = 0;
82 $max_attempts = 6;
83 $delay_seconds = 2;
84
85 while ( $attempts < $max_attempts ) {
86 if ( ! $this->is_processing() ) {
87 return $this;
88 }
89 sleep( $delay_seconds );
90 $attempts++;
91 $this->load_by_id( $this->id );
92 }
93 if ( $this->is_processing() ) {
94 // if it's still processing after waiting - mark as failed
95 $this->mark_as_failed();
96 }
97 return $this;
98 }
99
100 public function convert_to_transaction() {
101 if ( $this->is_processing() ) {
102 $this->wait_for_transaction_completion();
103 if ( $this->is_failed() ) {
104 $this->add_error( 'transaction_intent_error', __( 'Can not convert to transaction, because transaction intent conversion is being processed', 'latepoint' ) );
105 return false;
106 }
107 }
108
109 if ( $this->is_converted() ) {
110 return $this->transaction_id;
111 }
112
113
114 $this->mark_as_processing();
115
116 try {
117
118 // process payment if there is amount due
119 $transaction = OsPaymentsHelper::process_payment_for_transaction_intent( $this );
120 if ( ! $transaction || $transaction->status != LATEPOINT_TRANSACTION_STATUS_SUCCEEDED ) {
121 if ( ! $transaction ) {
122 $this->add_error( 'transaction_intent_error', __( 'No payment processor available to process this transaction intent', 'latepoint' ) );
123 } else {
124 if ( $transaction->get_error() ) {
125 $this->add_error( 'transaction_intent_error', $transaction->get_error_messages() );
126 }
127 }
128 $this->mark_as_new();
129 return false;
130 }
131
132 /**
133 * Filters transaction right before it's about to be saved when converting from a transaction intent
134 *
135 * @param {OsTransactionModel} $transaction Transaction to be filtered
136 * @returns {OsTransactionModel} The filtered transaction
137 *
138 * @since 5.0.0
139 * @hook latepoint_before_transaction_save_from_transaction_intent
140 *
141 */
142 $transaction = apply_filters( 'latepoint_before_transaction_save_from_transaction_intent', $transaction );
143
144
145 if ( $transaction->save() ) {
146 $this->mark_as_converted( $transaction );
147
148 /**
149 * Transaction was created
150 *
151 * @param {OsTransactionModel} $transaction instance of transaction model that was created
152 *
153 * @since 5.0.0
154 * @hook latepoint_transaction_created
155 *
156 */
157 do_action( 'latepoint_transaction_created', $transaction );
158
159 if ( $transaction->invoice_id ) {
160 $invoice = new OsInvoiceModel( $transaction->invoice_id );
161 if ( $invoice && ! $invoice->is_new_record() ) {
162 $invoice->change_status( LATEPOINT_INVOICE_STATUS_PAID );
163 OsOrdersHelper::check_if_order_invoices_paid_full_balance( $this->order_id );
164 }
165 }
166
167 return $transaction->id;
168 } else {
169 $this->add_error( 'transaction_intent_error', $transaction->get_error_messages() );
170
171 $this->mark_as_new();
172 return false;
173 }
174 } catch ( Exception $e ) {
175 $this->mark_as_new();
176 // translators: %s is the error description
177 $this->add_error( 'transaction_intent_error', sprintf( __( 'Error: %s', 'latepoint' ), $e->getMessage() ) );
178 OsDebugHelper::log( 'Error converting transaction intent to a transaction', 'transaction_intent_error', $e->getMessage() );
179 return false;
180 }
181 }
182
183 public function get_by_intent_key( $intent_key ) {
184 return $this->where( [ 'intent_key' => $intent_key ] )->set_limit( 1 )->get_results_as_models();
185 }
186
187 public function mark_as_converted( OsTransactionModel $transaction ): bool {
188 if ( empty( $transaction->id ) ) {
189 return false;
190 }
191
192 $this->transaction_id = $transaction->id;
193 $this->status = LATEPOINT_TRANSACTION_INTENT_STATUS_CONVERTED;
194
195 if ( $this->save() ) {
196 /**
197 * Transaction intent is converted to transaction
198 *
199 * @param {OsTransactionIntentModel} $transaction_intent Instance of transaction intent model that has been converted to transaction
200 * @param {OsTransactionModel} $transaction Instance of transaction model that transaction intent was converted to
201 *
202 * @since 5.0.0
203 * @hook latepoint_transaction_intent_converted
204 *
205 */
206 do_action( 'latepoint_transaction_intent_converted', $this, $transaction );
207 return true;
208 } else {
209 return false;
210 }
211 }
212
213 public function mark_as_failed() {
214 $this->update_attributes( [ 'status' => LATEPOINT_TRANSACTION_INTENT_STATUS_FAILED ] );
215 /**
216 * Transaction intent is marked as failed
217 *
218 * @param {OsTransactionIntentModel} $transaction_intent Instance of order intent model that has failed
219 *
220 * @since 5.2.0
221 * @hook latepoint_transaction_intent_failed
222 *
223 */
224 do_action( 'latepoint_transaction_intent_failed', $this );
225 }
226
227 public function mark_as_processing() {
228 $this->update_attributes( [ 'status' => LATEPOINT_TRANSACTION_INTENT_STATUS_PROCESSING ] );
229 /**
230 * Transaction intent is marked as processing
231 *
232 * @param {OsTransactionIntentModel} $transaction_intent Instance of order intent model that has started processing
233 *
234 * @since 5.0.0
235 * @hook latepoint_transaction_intent_processing
236 *
237 */
238 do_action( 'latepoint_transaction_intent_processing', $this );
239 }
240
241 public function mark_as_new() {
242 $this->update_attributes( [ 'status' => LATEPOINT_TRANSACTION_INTENT_STATUS_NEW ] );
243 /**
244 * Order intent is marked as new
245 *
246 * @param {OsTransactionIntentModel} $transaction_intent Instance of order intent model that is being marked as new
247 *
248 * @since 5.0.0
249 * @hook latepoint_transaction_intent_new
250 *
251 */
252 do_action( 'latepoint_transaction_intent_new', $this );
253 }
254
255 // Determines if order intent has been converted into a order already
256 public function is_converted(): bool {
257 if ( empty( $this->transaction_id ) ) {
258 return false;
259 } else {
260 return true;
261 }
262 }
263
264 public function generate_data_vars(): array {
265 $vars = [
266 'id' => $this->id,
267 'intent_key' => $this->intent_key,
268 'payment_data' => ! empty( $this->payment_data ) ? json_decode( $this->payment_data, true ) : [],
269 'order_id' => $this->order_id,
270 'transaction_id' => $this->transaction_id,
271 'order_form_page_url' => $this->order_form_page_url,
272 'updated_at' => $this->updated_at,
273 'created_at' => $this->created_at,
274 ];
275
276 return $vars;
277 }
278
279 public function get_page_url_with_intent() {
280 $order_form_page_url = $this->order_form_page_url;
281 $existing_var_position = strpos( $order_form_page_url, 'latepoint_transaction_intent_key=' );
282 if ( $existing_var_position === false ) {
283 // no intent variable in url
284 $question_position = strpos( $order_form_page_url, '?' );
285 if ( $question_position === false ) {
286 // no ?query params
287 $hash_position = strpos( $order_form_page_url, '#' );
288 if ( $hash_position === false ) {
289 // no hashtag in url
290 $order_form_page_url = $order_form_page_url . '?latepoint_transaction_intent_key=' . $this->intent_key;
291 } else {
292 // hashtag in url and no ?query, prepend the hashtag with query
293 $order_form_page_url = substr_replace( $order_form_page_url, '?latepoint_transaction_intent_key=' . $this->intent_key . '#', $hash_position, 1 );
294 }
295 } else {
296 // ?query string exists, add intent key to it
297 $order_form_page_url = substr_replace( $order_form_page_url, '?latepoint_transaction_intent_key=' . $this->intent_key . '&', $question_position, 1 );
298 }
299 } else {
300 // intent key variable exist in url
301 preg_match( '/latepoint_transaction_intent_key=([\d,\w]*)/', $order_form_page_url, $matches );
302 if ( isset( $matches[1] ) ) {
303 $order_form_page_url = str_replace( 'latepoint_transaction_intent_key=' . $matches[1], 'latepoint_transaction_intent_key=' . $this->intent_key, $order_form_page_url );
304 }
305 }
306
307 return $order_form_page_url;
308 }
309
310 public function generate_intent_key() {
311 $this->intent_key = bin2hex( openssl_random_pseudo_bytes( 10 ) );
312 }
313
314
315 public function get_customer(): OsCustomerModel {
316 if ( $this->customer_id ) {
317 if ( ! isset( $this->customer ) || ( isset( $this->customer ) && ( $this->customer->id != $this->customer_id ) ) ) {
318 $this->customer = new OsCustomerModel( $this->customer_id );
319 }
320 } else {
321 $this->customer = new OsCustomerModel();
322 }
323
324 return $this->customer;
325 }
326
327
328 protected function before_create() {
329 if ( empty( $this->intent_key ) ) {
330 $this->intent_key = bin2hex( openssl_random_pseudo_bytes( 10 ) );
331 }
332 if ( empty( $this->status ) ) {
333 $this->status = LATEPOINT_TRANSACTION_INTENT_STATUS_NEW;
334 }
335 }
336
337 protected function allowed_params( $role = 'admin' ) {
338 $allowed_params = array(
339 'payment_data',
340 'intent_key',
341 'order_id',
342 'customer_id',
343 'invoice_id',
344 'transaction_id',
345 'order_form_page_url',
346 'status',
347 );
348
349 return $allowed_params;
350 }
351
352
353 protected function params_to_save( $role = 'admin' ) {
354 $params_to_save = array(
355 'payment_data',
356 'intent_key',
357 'charge_amount',
358 'specs_charge_amount',
359 'order_id',
360 'customer_id',
361 'invoice_id',
362 'transaction_id',
363 'status',
364 );
365
366 return $params_to_save;
367 }
368
369
370 protected function properties_to_validate() {
371 $validations = array(
372 'order_id' => array( 'presence' ),
373 );
374
375 return $validations;
376 }
377 }
378