PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.6.10
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.6.10
5.6.10 5.6.9 5.6.8 5.6.7 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 / controllers / paypal_connect_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 3 months ago auth_controller.php 5 months ago booking_form_settings_controller.php 5 months ago bookings_controller.php 1 month ago calendars_controller.php 5 months ago carts_controller.php 1 month ago controller.php 5 months ago customer_cabinet_controller.php 1 month ago customers_controller.php 1 month ago dashboard_controller.php 3 months ago default_agent_controller.php 5 months ago events_controller.php 5 months ago form_fields_controller.php 1 month ago integrations_controller.php 5 months ago invoices_controller.php 1 month ago manage_booking_by_key_controller.php 5 months ago manage_order_by_key_controller.php 5 months ago notifications_controller.php 5 months ago orders_controller.php 1 month ago paypal_connect_controller.php 3 weeks ago pro_controller.php 1 month ago process_jobs_controller.php 5 months ago processes_controller.php 3 weeks ago razorpay_connect_controller.php 1 month ago search_controller.php 5 months ago services_controller.php 5 months ago settings_controller.php 3 months ago steps_controller.php 1 month ago stripe_connect_controller.php 1 month ago support_topics_controller.php 5 months ago todos_controller.php 5 months ago transactions_controller.php 1 month ago wizard_controller.php 1 month ago
paypal_connect_controller.php
378 lines
1 <?php
2 /*
3 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit; // Exit if accessed directly.
8 }
9
10
11 if ( ! class_exists( 'OsPaypalConnectController' ) ) :
12
13
14 class OsPaypalConnectController extends OsController {
15
16
17 function __construct() {
18 parent::__construct();
19
20 $this->action_access['public'] = array_merge( $this->action_access['public'], [ 'webhook', 'create_order_for_transaction', 'create_order', 'capture_order' ] );
21 $this->action_access['customer'] = array_merge( $this->action_access['customer'], [] );
22 $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'paypal_connect/';
23 }
24
25 public function create_order_for_transaction() {
26 if ( ! filter_var( $this->params['invoice_id'], FILTER_VALIDATE_INT ) ) {
27 exit();
28 }
29 try {
30
31 $invoice = new OsInvoiceModel( $this->params['invoice_id'] );
32
33 $transaction_intent = OsTransactionIntentHelper::create_or_update_transaction_intent( $invoice, $this->params );
34
35 if ( OsPaypalConnectHelper::get_connect_merchant_id() ) {
36 $order_data = OsPaypalConnectHelper::create_order_for_transaction_intent( $transaction_intent );
37 $paypal_order_id = $order_data['order_id'];
38 } else {
39 throw new Exception( __( 'PayPal connect merchant ID not set', 'latepoint' ) );
40 }
41
42 $transaction_intent->set_payment_data_value( 'token', $paypal_order_id, false );
43 if ( ! $transaction_intent->save() ) {
44 throw new Exception( __( 'Unable to save transaction intent', 'latepoint' ) );
45 }
46
47 if ( $this->get_return_format() == 'json' ) {
48 $this->send_json(
49 [
50 'status' => LATEPOINT_STATUS_SUCCESS,
51 'continue_transaction_intent_url' => OsTransactionIntentHelper::generate_continue_intent_url( $transaction_intent->intent_key ),
52 'paypal_order_id' => $paypal_order_id,
53 'transaction_intent_key' => $transaction_intent->intent_key,
54 ]
55 );
56 }
57 } catch ( Exception $e ) {
58 if ( $this->get_return_format() == 'json' ) {
59 $this->send_json(
60 array(
61 'status' => LATEPOINT_STATUS_ERROR,
62 'message' => $e->getMessage(),
63 )
64 );
65 }
66 }
67 }
68
69 public function create_order() {
70 try {
71 OsStepsHelper::set_required_objects( $this->params );
72
73 $booking_form_page_url = $this->params['booking_form_page_url'] ?? OsUtilHelper::get_referrer();
74 $order_intent = OsOrderIntentHelper::create_or_update_order_intent( OsStepsHelper::$cart_object, OsStepsHelper::$restrictions, OsStepsHelper::$presets, $booking_form_page_url, OsStepsHelper::get_customer_object_id() );
75
76 if ( ! $order_intent->is_bookable() ) {
77 throw new Exception( empty( $order_intent->get_error_messages() ) ? __( 'Booking slot is not available anymore.', 'latepoint' ) : implode( ', ', $order_intent->get_error_messages() ) );
78 }
79
80 if ( OsPaypalConnectHelper::get_connect_merchant_id() ) {
81 $payment_method = OsStepsHelper::$cart_object->payment_method ?? 'paypal_buttons';
82 $order_data = OsPaypalConnectHelper::create_order_for_order_intent( $order_intent, $payment_method );
83 $paypal_order_id = $order_data['order_id'];
84 } else {
85 throw new Exception( __( 'PayPal connect merchant ID not set', 'latepoint' ) );
86 }
87
88 // update cart with paypal order id
89 OsStepsHelper::$cart_object->payment_token = $paypal_order_id;
90
91 // cart_item_data might be changed after filters run, make sure to get the latest version
92 $cart_items_data = json_decode( $order_intent->cart_items_data, true );
93 $payment_data = json_decode( $order_intent->payment_data, true );
94
95 $payment_data['token'] = $paypal_order_id;
96 $order_intent->update_attributes(
97 [
98 'cart_items_data' => wp_json_encode( $cart_items_data ),
99 'payment_data' => wp_json_encode( $payment_data ),
100 ]
101 );
102 if ( $this->get_return_format() == 'json' ) {
103 $this->send_json(
104 [
105 'status' => LATEPOINT_STATUS_SUCCESS,
106 'continue_order_intent_url' => OsOrderIntentHelper::generate_continue_intent_url( $order_intent->intent_key ),
107 'paypal_order_id' => $paypal_order_id,
108 'order_intent_key' => $order_intent->intent_key,
109 ]
110 );
111 }
112 } catch ( Exception $e ) {
113 if ( $this->get_return_format() == 'json' ) {
114 $this->send_json(
115 array(
116 'status' => LATEPOINT_STATUS_ERROR,
117 'message' => $e->getMessage(),
118 )
119 );
120 }
121 }
122 }
123
124 public function capture_order() {
125 try {
126 $paypal_order_id = sanitize_text_field( $this->params['paypal_order_id'] ?? '' );
127 if ( empty( $paypal_order_id ) ) {
128 throw new Exception( __( 'PayPal order ID is required', 'latepoint' ) );
129 }
130
131 $capture_data = OsPaypalConnectHelper::capture_order( $paypal_order_id );
132
133 if ( $this->get_return_format() == 'json' ) {
134 $this->send_json(
135 [
136 'status' => LATEPOINT_STATUS_SUCCESS,
137 'capture_id' => $capture_data['capture_id'] ?? '',
138 'order_id' => $capture_data['order_id'] ?? '',
139 'payment_source_type' => $capture_data['payment_source_type'] ?? 'paypal',
140 ]
141 );
142 }
143 } catch ( Exception $e ) {
144 if ( $this->get_return_format() == 'json' ) {
145 $this->send_json(
146 array(
147 'status' => LATEPOINT_STATUS_ERROR,
148 'message' => $e->getMessage(),
149 )
150 );
151 }
152 }
153 }
154
155 public function webhook() {
156 $payload = @file_get_contents( 'php://input' );
157 $data = json_decode( $payload, true );
158 if ( empty( $data['server_token'] ) || $data['server_token'] != OsPaypalConnectHelper::get_server_token() || $data['paypal_merchant_id'] != OsPaypalConnectHelper::get_connect_merchant_id() ) {
159 http_response_code( 400 );
160 echo 'Validation issue with webhook';
161 exit();
162 }
163 $event = $data['event'];
164 // Handle the event
165 switch ( $event['type'] ) {
166 case 'PAYMENT.CAPTURE.COMPLETED':
167 if ( ! empty( $event['data']['order_intent_key'] ) ) {
168 $order_intent = OsOrderIntentHelper::get_order_intent_by_intent_key( $event['data']['order_intent_key'] );
169 if ( $order_intent->is_new_record() ) {
170 OsDebugHelper::log( 'Error processing paypal connect webhook: Order intent not found for key' );
171 http_response_code( 400 );
172 exit();
173 }
174
175 $stored_order_id = $order_intent->get_other_data_value( 'paypal_order_id' );
176 if ( $stored_order_id !== ( $event['data']['paypal_order_id'] ?? '' ) ) {
177 OsDebugHelper::log( 'PayPal order ID mismatch in webhook for order intent ' . $order_intent->id, 'paypal_connect_webhook' );
178 http_response_code( 400 );
179 exit();
180 }
181
182 if ( $order_intent->convert_to_order() ) {
183 http_response_code( 200 );
184 } else {
185 http_response_code( 400 );
186 OsDebugHelper::log( 'Error converting order intent', 'paypal_connect_webhook', $order_intent->get_error_messages() );
187 }
188 }
189 if ( ! empty( $event['data']['transaction_intent_key'] ) ) {
190 $transaction_intent = OsTransactionIntentHelper::get_transaction_intent_by_intent_key( $event['data']['transaction_intent_key'] );
191 if ( $transaction_intent->is_new_record() ) {
192 OsDebugHelper::log( 'Error processing paypal connect webhook: Transaction intent not found for key' );
193 http_response_code( 400 );
194 exit();
195 }
196
197 $stored_order_id = $transaction_intent->get_payment_data_value( 'paypal_order_id' );
198 if ( $stored_order_id !== ( $event['data']['paypal_order_id'] ?? '' ) ) {
199 OsDebugHelper::log( 'PayPal order ID mismatch in webhook for transaction intent ' . $transaction_intent->id, 'paypal_connect_webhook' );
200 http_response_code( 400 );
201 exit();
202 }
203
204 if ( $transaction_intent->convert_to_transaction() ) {
205 http_response_code( 200 );
206 } else {
207 http_response_code( 400 );
208 OsDebugHelper::log( 'Error converting transaction intent' );
209 }
210 }
211 break;
212 }
213 exit();
214 }
215
216 private function get_env_from_params(): string {
217 return ( ! empty(
218 $this->params['env'] && in_array(
219 $this->params['env'],
220 [
221 LATEPOINT_PAYMENTS_ENV_LIVE,
222 LATEPOINT_PAYMENTS_ENV_DEV,
223 ]
224 )
225 ) ) ? $this->params['env'] : OsSettingsHelper::get_payments_environment();
226 }
227
228 public function start_connect_process() {
229 $env = $this->get_env_from_params();
230 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'enable_payment_processor_paypal_connect', $env ), LATEPOINT_VALUE_ON );
231 $url = OsPaypalConnectHelper::get_connect_url( $env );
232 $this->send_json(
233 array(
234 'status' => LATEPOINT_STATUS_SUCCESS,
235 'url' => $url,
236 'message' => __( 'Redirecting to PayPal', 'latepoint' ),
237 )
238 );
239 }
240
241 public function disconnect_connect_account() {
242 $env = $this->get_env_from_params();
243 try {
244 $path = 'server-tokens/' . OsPaypalConnectHelper::get_server_token( $env ) . '/disconnect/';
245 $response = OsPaypalConnectHelper::do_account_request( $path, $env, '', 'DELETE' );
246 if ( $response['status']['code'] == 200 ) {
247 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_payments_enabled' ) );
248 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_merchant_id' ) );
249 OsPaypalConnectHelper::reset_server_token( $env );
250 } else {
251 OsDebugHelper::log( 'PayPal Connect Error', 'paypal_connect_disconnect_error', $response );
252 }
253 } catch ( Exception $e ) {
254 OsDebugHelper::log( 'Error getting status of a paypal connection', 'paypal_ppcp', [ 'error_message' => $e->getMessage() ] );
255 $this->send_json(
256 array(
257 'status' => LATEPOINT_STATUS_ERROR,
258 'message' => $e->getMessage(),
259 )
260 );
261 }
262 $this->send_json(
263 array(
264 'status' => LATEPOINT_STATUS_SUCCESS,
265 'message' => OsPaypalConnectHelper::get_connection_buttons_and_status( $env ),
266 )
267 );
268 }
269
270
271 public function check_connect_status() {
272 $env = $this->get_env_from_params();
273 try {
274 $response = OsPaypalConnectHelper::do_request( 'server-tokens/' . OsPaypalConnectHelper::get_server_token( $env ) . '/status/', '', 'GET', [], [], $env );
275 $data = $response['data'] ?? [];
276
277 if ( $env == 'live' ) {
278 if ( empty( $data['transaction_fee_info'] ) ) {
279 OsSettingsHelper::save_setting_by_name( 'paypal_connect_transaction_fee_info', '0' );
280 } else {
281 OsSettingsHelper::save_setting_by_name( 'paypal_connect_transaction_fee_info', $data['transaction_fee_info'] );
282 }
283 }
284 if ( ! empty( $data ) && ! empty( $data['merchant_id'] ) ) {
285
286 // Save granular status flags so the admin UI and payment gate can use them.
287 $merchant_id = sanitize_text_field( $data['merchant_id'] );
288 $payments_receivable = ! empty( $data['payments_receivable'] );
289 $primary_email_confirmed = ! empty( $data['primary_email_confirmed'] );
290 $oauth_integrations_valid = ! empty( $data['oauth_integrations_valid'] );
291 $acdc_vetting_status = sanitize_text_field( $data['acdc_vetting_status'] ?? '' );
292 $acdc_capability_active = ! empty( $data['acdc_capability_active'] );
293
294 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_merchant_id', $env ), $merchant_id );
295 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_payments_receivable', $env ), $payments_receivable ? LATEPOINT_VALUE_ON : '' );
296 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_primary_email_confirmed', $env ), $primary_email_confirmed ? LATEPOINT_VALUE_ON : '' );
297 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_oauth_integrations_valid', $env ), $oauth_integrations_valid ? LATEPOINT_VALUE_ON : '' );
298 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_acdc_vetting_status', $env ), $acdc_vetting_status );
299 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_acdc_capability_active', $env ), $acdc_capability_active ? LATEPOINT_VALUE_ON : '' );
300
301 // Payments are enabled when PayPal confirms the merchant can transact.
302 // oauth_integrations_valid is advisory (shown in admin UI) but does not block payments.
303 if ( $payments_receivable && $primary_email_confirmed ) {
304 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_payments_enabled', $env ), LATEPOINT_VALUE_ON );
305 } else {
306 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_payments_enabled', $env ) );
307 }
308 } else {
309 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_payments_enabled', $env ) );
310 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_merchant_id', $env ) );
311 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_payments_receivable', $env ) );
312 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_primary_email_confirmed', $env ) );
313 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_oauth_integrations_valid', $env ) );
314 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_acdc_vetting_status', $env ) );
315 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_acdc_capability_active', $env ) );
316 }
317 if ( ! empty( $data['active_site_urls'] ) ) {
318 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_duplicate_token_activations', $env ), $data['active_site_urls'] );
319 } else {
320 OsSettingsHelper::remove_setting_by_name( OsSettingsHelper::append_payment_env_key( 'paypal_connect_duplicate_token_activations', $env ) );
321 }
322 if ( ! empty( $data['error'] ) ) {
323 OsDebugHelper::log( 'Error checking status of server token', 'paypal_connect_error', [ 'error_message' => $data['error'] ] );
324 }
325 } catch ( Exception $e ) {
326 OsDebugHelper::log( 'Error getting status of a paypal connection', 'paypal_connect_error', [ 'error_message' => $e->getMessage() ] );
327 $this->send_json(
328 array(
329 'status' => LATEPOINT_STATUS_ERROR,
330 'message' => $e->getMessage(),
331 )
332 );
333 }
334 $this->send_json(
335 array(
336 'status' => LATEPOINT_STATUS_SUCCESS,
337 'message' => OsPaypalConnectHelper::get_connection_buttons_and_status( $env ),
338 )
339 );
340 }
341
342
343 public function heartbeat() {
344 $payload = @file_get_contents( 'php://input' );
345 $data = json_decode( $payload, true );
346
347 if ( empty( $data['wp_latepoint_server_token'] ) ) {
348 $this->send_json(
349 array(
350 'status' => LATEPOINT_STATUS_ERROR,
351 'message' => 'Token is missing',
352 ),
353 404
354 );
355 }
356 if ( $data['wp_latepoint_server_token'] != OsPaypalConnectHelper::get_server_token() ) {
357 $this->send_json(
358 array(
359 'status' => LATEPOINT_STATUS_ERROR,
360 'message' => 'Invalid Token',
361 ),
362 404
363 );
364 }
365
366 $this->send_json(
367 array(
368 'status' => LATEPOINT_STATUS_SUCCESS,
369 'message' => 'Heartbeat detected',
370 ),
371 200
372 );
373 }
374 }
375
376
377 endif;
378