PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.6.8
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.6.8
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 / helpers / stripe_connect_helper.php
latepoint / lib / helpers Last commit date
activities_helper.php 4 months ago agent_helper.php 4 months ago analytics_helper.php 2 weeks ago auth_helper.php 1 week ago blocks_helper.php 3 weeks ago booking_helper.php 1 week ago bricks_helper.php 4 months ago bundles_helper.php 1 week ago calendar_helper.php 1 week ago carts_helper.php 4 months ago connector_helper.php 4 months ago csv_helper.php 4 months ago customer_helper.php 1 month ago customer_import_helper.php 1 month ago database_helper.php 1 day ago debug_helper.php 4 months ago defaults_helper.php 4 months ago elementor_helper.php 4 months ago email_helper.php 4 months ago encrypt_helper.php 4 months ago events_helper.php 3 months ago form_helper.php 4 months ago icalendar_helper.php 4 months ago image_helper.php 4 months ago invoices_helper.php 1 day ago license_helper.php 4 months ago location_helper.php 4 months ago marketing_systems_helper.php 4 months ago meeting_systems_helper.php 4 months ago menu_helper.php 3 weeks ago meta_helper.php 4 months ago migrations_helper.php 4 months ago money_helper.php 3 months ago notifications_helper.php 4 months ago nps_survey_helper.php 4 months ago order_intent_helper.php 2 months ago orders_helper.php 4 days ago otp_helper.php 3 months ago pages_helper.php 4 months ago params_helper.php 4 months ago payments_helper.php 4 days ago plugin_version_update_helper.php 2 months ago price_breakdown_helper.php 4 months ago process_jobs_helper.php 4 months ago processes_helper.php 4 months ago razorpay_connect_helper.php 1 day ago replacer_helper.php 4 months ago resource_helper.php 1 week ago roles_helper.php 3 weeks ago router_helper.php 4 months ago service_helper.php 4 months ago sessions_helper.php 4 months ago settings_helper.php 2 weeks ago short_links_systems_helper.php 4 months ago shortcodes_helper.php 3 weeks ago sms_helper.php 4 months ago steps_helper.php 1 day ago stripe_connect_helper.php 2 weeks ago styles_helper.php 4 months ago support_topics_helper.php 4 months ago time_helper.php 2 months ago timeline_helper.php 2 months ago transaction_helper.php 1 month ago transaction_intent_helper.php 4 months ago util_helper.php 1 month ago version_specific_updates_helper.php 1 day ago whatsapp_helper.php 1 month ago work_periods_helper.php 3 months ago wp_datetime.php 4 months ago wp_user_helper.php 4 months ago
stripe_connect_helper.php
806 lines
1 <?php
2
3 class OsStripeConnectHelper {
4 public static $default_currency_iso_code = 'usd';
5 public static $error = false;
6
7 public static $stripe = false;
8 public static $processor_code = 'stripe_connect';
9
10
11 public static function add_all_payment_methods_to_payment_times( array $payment_times ): array {
12 $payment_methods = self::get_supported_payment_methods();
13 foreach ( $payment_methods as $payment_method_code => $payment_method_info ) {
14 $payment_times[ LATEPOINT_PAYMENT_TIME_NOW ][ $payment_method_code ][ self::$processor_code ] = $payment_method_info;
15 }
16 return $payment_times;
17 }
18
19 public static function add_enabled_payment_methods_to_payment_times( array $payment_times ): array {
20 if ( OsPaymentsHelper::is_payment_processor_enabled( self::$processor_code ) ) {
21 $payment_times = self::add_all_payment_methods_to_payment_times( $payment_times );
22 }
23 return $payment_times;
24 }
25
26
27 public static function process_refund( $transaction_refund, OsTransactionModel $transaction, $custom_amount = null ) {
28 if ( $transaction->processor != self::$processor_code ) {
29 return $transaction_refund;
30 }
31
32 if ( ! $transaction->can_refund() ) {
33 throw new Exception( 'Invalid Transaction' );
34 }
35
36 $refund_data = [
37 'payment_intent_id' => $transaction->token,
38 ];
39 if ( $custom_amount ) {
40 $refund_data['custom_amount'] = self::convert_amount_to_specs( $custom_amount );
41 }
42
43 $response = self::do_account_request( 'refunds', OsSettingsHelper::get_payments_environment(), '', 'POST', $refund_data );
44
45 if ( empty( $response['data'] ) ) {
46 throw new Exception( __( 'Error Refunding', 'latepoint' ) );
47 }
48
49 $transaction_refund = new OsTransactionRefundModel();
50 $transaction_refund->transaction_id = $transaction->id;
51 $transaction_refund->amount = self::convert_amount_back_from_specs_to_db_format( $response['data']['amount'] );
52 $transaction_refund->token = $response['data']['id'];
53 if ( $transaction_refund->save() ) {
54 /**
55 * Transaction refund was issued
56 *
57 * @param {OsTransactionRefundModel} $transaction_refund instance of transaction refund model that was issued
58 *
59 * @since 5.1.0
60 * @hook latepoint_transaction_refund_created
61 *
62 */
63 do_action( 'latepoint_transaction_refund_created', $transaction_refund );
64 return $transaction_refund;
65 } else {
66 throw new Exception( implode( ', ', $transaction_refund->get_error_messages() ) );
67 }
68 }
69
70 public static function output_stripe_link_on_customer_quick_form( OsCustomerModel $customer ) {
71 $stripe_customer_id = self::get_stripe_customer_id( $customer );
72 if ( $stripe_customer_id ) {
73 echo '<div class="payment-processor-customer-link-wrapper">' . esc_html__( 'Stripe Customer', 'latepoint' ) . '<a target="_blank" href="' . esc_url( self::build_customer_profile_link( $stripe_customer_id, OsSettingsHelper::is_env_payments_dev() ) ) . '">' . esc_html__( 'Open in Stripe', 'latepoint' ) . '</a></div>';
74 }
75 }
76
77 public static function convert_transaction_intent_charge_amount_to_specs( $amount, OsTransactionIntentModel $transaction_intent ) {
78 if ( OsPaymentsHelper::should_processor_handle_payment_for_transaction_intent( self::$processor_code, $transaction_intent ) ) {
79 $amount = self::convert_amount_to_specs( $amount );
80 }
81 return $amount;
82 }
83
84 public static function process_payment_for_transaction_intent( $result, OsTransactionIntentModel $transaction_intent ) {
85 if ( OsPaymentsHelper::should_processor_handle_payment_for_transaction_intent( self::$processor_code, $transaction_intent ) ) {
86 switch ( $transaction_intent->get_payment_data_value( 'method' ) ) {
87 case 'payment_element':
88 if ( $transaction_intent->get_payment_data_value( 'token' ) ) {
89 // since the payment is already processed on the frontend - we need to retrieve payment intent and verify if its paid
90 $payment_intent_data = self::retrieve_payment_intent( $transaction_intent->get_payment_data_value( 'token' ) );
91 if ( in_array( $payment_intent_data['status'], [ 'succeeded', 'requires_capture' ] ) ) {
92 if ( ! self::validate_payment_intent_amount( $payment_intent_data, $transaction_intent->charge_amount ) ) {
93 $result['status'] = LATEPOINT_STATUS_ERROR;
94 $result['message'] = __( 'Payment amount mismatch', 'latepoint' );
95 OsDebugHelper::log( 'Stripe PI amount mismatch for transaction intent ' . $transaction_intent->id, 'stripe_connect_error' );
96 $transaction_intent->add_error( 'payment_error', $result['message'] );
97 break;
98 }
99 // success
100 $result['status'] = LATEPOINT_STATUS_SUCCESS;
101 $result['processor'] = self::$processor_code;
102 $result['charge_id'] = $payment_intent_data['id'];
103 $result['amount'] = $payment_intent_data['total'];
104 $result['kind'] = $payment_intent_data['status'] == 'requires_capture' ? LATEPOINT_TRANSACTION_KIND_AUTHORIZATION : LATEPOINT_TRANSACTION_KIND_CAPTURE;
105 } else {
106 // payment error
107 $result['status'] = LATEPOINT_STATUS_ERROR;
108 $result['message'] = __( 'Payment Error', 'latepoint' );
109 $transaction_intent->add_error( 'send_to_step', $result['message'], 'payment' );
110 }
111 } else {
112 // payment token missing
113 $result['status'] = LATEPOINT_STATUS_ERROR;
114 $result['message'] = __( 'Payment Error 23JDF38', 'latepoint' );
115 $transaction_intent->add_error( 'payment_error', $result['message'] );
116 }
117 break;
118 }
119 }
120
121 return $result;
122 }
123
124
125 public static function process_payment( $result, OsOrderIntentModel $order_intent ) {
126 if ( OsPaymentsHelper::should_processor_handle_payment_for_order_intent( self::$processor_code, $order_intent ) ) {
127 switch ( $order_intent->get_payment_data_value( 'method' ) ) {
128 case 'payment_element':
129 if ( $order_intent->get_payment_data_value( 'token' ) ) {
130 // since the payment is already processed on the frontend - we need to retrieve payment intent and verify if its paid
131 $payment_intent_data = self::retrieve_payment_intent( $order_intent->get_payment_data_value( 'token' ) );
132 if ( in_array( $payment_intent_data['status'], [ 'succeeded', 'requires_capture' ] ) ) {
133 if ( ! self::validate_payment_intent_amount( $payment_intent_data, $order_intent->charge_amount ) ) {
134 $result['status'] = LATEPOINT_STATUS_ERROR;
135 $result['message'] = __( 'Payment amount mismatch', 'latepoint' );
136 OsDebugHelper::log( 'Stripe PI amount mismatch for order intent ' . $order_intent->id, 'stripe_connect_error' );
137 $order_intent->add_error( 'payment_error', $result['message'] );
138 $order_intent->add_error( 'send_to_step', $result['message'], 'payment' );
139 break;
140 }
141 // success
142 $result['status'] = LATEPOINT_STATUS_SUCCESS;
143 $result['processor'] = self::$processor_code;
144 $result['charge_id'] = $payment_intent_data['id'];
145 $result['amount'] = $payment_intent_data['total'];
146 $result['kind'] = $payment_intent_data['status'] == 'requires_capture' ? LATEPOINT_TRANSACTION_KIND_AUTHORIZATION : LATEPOINT_TRANSACTION_KIND_CAPTURE;
147 } else {
148 // payment error
149 $result['status'] = LATEPOINT_STATUS_ERROR;
150 $result['message'] = __( 'Payment Error', 'latepoint' );
151 $order_intent->add_error( 'payment_error', $result['message'] );
152 $order_intent->add_error( 'send_to_step', $result['message'], 'payment' );
153 }
154 } else {
155 // payment token missing
156 $result['status'] = LATEPOINT_STATUS_ERROR;
157 $result['message'] = __( 'Payment Error 23JDF38', 'latepoint' );
158 $order_intent->add_error( 'payment_error', $result['message'] );
159 }
160 break;
161 }
162 }
163 return $result;
164 }
165
166
167 public static function convert_charge_amount_to_requirements( $charge_amount, OsCartModel $cart ) {
168 if ( OsPaymentsHelper::should_processor_handle_payment_for_cart( self::$processor_code, $cart ) ) {
169 $charge_amount = self::convert_amount_to_specs( $charge_amount );
170 }
171 return $charge_amount;
172 }
173
174 public static function convert_amount_to_specs( $charge_amount ) {
175 $iso_code = self::get_currency_iso_code();
176 if ( in_array( $iso_code, self::zero_decimal_currencies_list() ) ) {
177 $charge_amount = round( $charge_amount );
178 } else {
179 $number_of_decimals = OsSettingsHelper::get_settings_value( 'number_of_decimals', '2' );
180 $charge_amount = number_format( (float) $charge_amount, $number_of_decimals, '.', '' ) * pow( 10, $number_of_decimals );
181 }
182 return $charge_amount;
183 }
184
185 /**
186 * Converts amount from Stripe to database format
187 *
188 * @param $charge_amount
189 *
190 * @return mixed|string
191 */
192 public static function convert_amount_back_from_specs_to_db_format( $charge_amount ) {
193 $iso_code = self::get_currency_iso_code();
194 $number_of_decimals = OsSettingsHelper::get_settings_value( 'number_of_decimals', '2' );
195 if ( ! in_array( $iso_code, self::zero_decimal_currencies_list() ) && ! empty( $number_of_decimals ) ) {
196 $charge_amount = $charge_amount / pow( 10, $number_of_decimals );
197 $charge_amount = number_format( (float) $charge_amount, 4, '.', '' );
198 } else {
199 $charge_amount = OsMoneyHelper::pad_to_db_format( $charge_amount );
200 }
201 return $charge_amount;
202 }
203
204
205 public static function output_order_payment_pay_contents( OsTransactionIntentModel $transaction_intent ) {
206 if ( ! OsPaymentsHelper::should_processor_handle_payment_for_transaction_intent( self::$processor_code, $transaction_intent ) ) {
207 return;
208 }
209 echo '<div class="lp-payment-method-content" data-payment-method="payment_element">';
210 echo '<div class="lp-payment-method-content-i">';
211 echo '<div class="stripe-payment-element"></div>';
212 echo '</div>';
213 echo '</div>';
214 }
215
216 public static function output_payment_step_contents( OsCartModel $cart ) {
217 if ( ! OsPaymentsHelper::should_processor_handle_payment_for_cart( self::$processor_code, $cart ) ) {
218 return;
219 }
220 echo '<div class="lp-payment-method-content" data-payment-method="payment_element">';
221 echo '<div class="lp-payment-method-content-i">';
222 echo '<div class="stripe-payment-element"></div>';
223 echo '</div>';
224 echo '</div>';
225 }
226
227
228 public static function get_supported_payment_methods(): array {
229 return [
230 'payment_element' => [
231 'name' => __( 'Payment Element', 'latepoint' ),
232 'label' => __( 'Credit Card', 'latepoint' ),
233 'image_url' => LATEPOINT_IMAGES_URL . 'payment_cards.png',
234 ],
235 ];
236 }
237
238 public static function register_payment_processor( array $payment_processors ): array {
239 $payment_processors[ self::$processor_code ] = [
240 'code' => self::$processor_code,
241 'name' => __( 'Stripe Connect', 'latepoint' ),
242 'front_name' => __( 'Stripe', 'latepoint' ),
243 'image_url' => LATEPOINT_IMAGES_URL . 'processor-stripe-connect.png',
244 ];
245 return $payment_processors;
246 }
247
248 public static function add_settings_fields( $processor_code ) {
249 if ( $processor_code != self::$processor_code ) {
250 return false;
251 } ?>
252 <div class="sub-section-row">
253 <div class="sub-section-label">
254 <h3><?php esc_html_e( 'Connect (Live)', 'latepoint' ); ?></h3>
255 </div>
256 <div class="sub-section-content">
257 <div data-env="<?php echo esc_attr( LATEPOINT_PAYMENTS_ENV_LIVE ); ?>"
258 class="payment-processor-connect-status-wrapper stripe-connect-status-wrapper"
259 data-route-name="<?php echo esc_attr( OsRouterHelper::build_route_name( 'stripe_connect', 'check_connect_status' ) ); ?>">
260 <div class="os-loading-spinner"></div>
261 </div>
262 </div>
263 </div>
264 <div class="sub-section-row">
265 <div class="sub-section-label">
266 <h3><?php esc_html_e( 'Connect (Dev)', 'latepoint' ); ?></h3>
267 </div>
268 <div class="sub-section-content">
269 <div data-env="<?php echo esc_attr( LATEPOINT_PAYMENTS_ENV_DEV ); ?>"
270 class="payment-processor-connect-status-wrapper stripe-connect-status-wrapper"
271 data-route-name="<?php echo esc_attr( OsRouterHelper::build_route_name( 'stripe_connect', 'check_connect_status' ) ); ?>">
272 <div class="os-loading-spinner"></div>
273 </div>
274 </div>
275 </div>
276 <div class="sub-section-row">
277 <div class="sub-section-label">
278 <h3><?php esc_html_e( 'Other Settings', 'latepoint' ); ?></h3>
279 </div>
280 <div class="sub-section-content">
281 <?php
282 $selected_stripe_country_code = OsSettingsHelper::get_settings_value( 'stripe_connect_country_code', 'US' );
283 $selected_stripe_currency_iso_code = OsSettingsHelper::get_settings_value( 'stripe_connect_currency_iso_code', 'usd' ); ?>
284 <div class="os-row os-mb-2">
285 <div class="os-col-6">
286 <?php echo OsFormHelper::select_field( 'settings[stripe_connect_country_code]', __( 'Country', 'latepoint' ), self::load_countries_list(), $selected_stripe_country_code ); ?>
287 </div>
288 <div class="os-col-6">
289 <?php echo OsFormHelper::select_field( 'settings[stripe_connect_currency_iso_code]', __( 'Currency Code', 'latepoint' ), OsStripeConnectHelper::load_all_currencies_list(), $selected_stripe_currency_iso_code ); ?>
290 </div>
291 </div>
292 </div>
293 </div>
294 <?php
295 }
296
297 public static function get_stripe_customer_id( OsCustomerModel $customer ) {
298 return $customer->get_meta_by_key( OsSettingsHelper::append_payment_env_key( 'stripe_connect_customer_id' ), '' );
299 }
300
301 public static function save_stripe_customer_id( OsCustomerModel $customer, string $stripe_customer_id ) {
302 return $customer->save_meta_by_key( OsSettingsHelper::append_payment_env_key( 'stripe_connect_customer_id' ), $stripe_customer_id );
303 }
304
305
306 public static function get_customer( $stripe_customer_id ): \LatePoint\Misc\StripeConnectCustomer {
307 $response = self::do_account_request( "customers/{$stripe_customer_id}" );
308 $stripe_connect_customer = new \LatePoint\Misc\StripeConnectCustomer();
309 $stripe_connect_customer->id = $response['data']['id'];
310 return $stripe_connect_customer;
311 }
312
313 public static function update_customer( $stripe_customer_id, $customer, $values_to_update = array() ) {
314 $stripe_customer = self::get_customer( $stripe_customer_id );
315 if ( $stripe_customer && $values_to_update ) {
316 foreach ( $values_to_update as $key => $value ) {
317 if ( in_array( $key, self::get_properties_allowed_to_update() ) ) {
318 $stripe_customer->$key = $value;
319 }
320 }
321 $stripe_customer->save();
322 }
323 return $stripe_customer;
324 }
325
326 public static function create_customer( $customer ) {
327 $customer_data = [
328 'email' => $customer->email,
329 'name' => $customer->full_name,
330 ];
331 $response = self::do_account_request( 'customers', OsSettingsHelper::get_payments_environment(), '', 'POST', $customer_data );
332 $result = [ 'id' => $response['data']['customer_id'] ];
333 return $result;
334 }
335
336
337 public static function get_currency_iso_code() {
338 return OsSettingsHelper::get_settings_value( 'stripe_connect_currency_iso_code', self::$default_currency_iso_code );
339 }
340
341 public static function get_server_token( string $force_env = '' ): string {
342 $key = OsSettingsHelper::append_payment_env_key( 'server_token_for_stripe_connect', $force_env );
343 $server_token = OsSettingsHelper::get_settings_value( $key, '' );
344 if ( empty( $server_token ) ) {
345 $server_token = OsUtilHelper::generate_uuid();
346 OsSettingsHelper::save_setting_by_name( $key, $server_token );
347 }
348 return $server_token;
349 }
350
351 public static function reset_server_token( string $force_env = '' ): string {
352 $key = OsSettingsHelper::append_payment_env_key( 'server_token_for_stripe_connect', $force_env );
353 $new_server_token = OsUtilHelper::generate_uuid();
354 OsSettingsHelper::save_setting_by_name( $key, $new_server_token );
355 return $new_server_token;
356 }
357
358 public static function get_connect_url( string $env = '' ) {
359 $url = LATEPOINT_STRIPE_CONNECT_URL . '/wp/stripe-connection/' . $env . '/start/';
360 $url .= self::get_server_token( $env ) . '/' . base64_encode( implode( '|||', [ get_bloginfo( 'name' ), get_site_icon_url(), OsUtilHelper::get_site_url() ] ) );
361 return $url;
362 }
363
364
365 public static function do_account_request( string $path, string $env = '', string $connection_data = '', string $method = 'GET', array $vars = [], array $headers = [] ) {
366 if ( empty( $env ) ) {
367 $env = OsSettingsHelper::get_payments_environment();
368 }
369 $path = self::get_connect_account_id( $env ) . '/' . $path;
370 try {
371 return self::do_request( $path, $connection_data, $method, $vars, $headers );
372 } catch ( \Exception $e ) {
373 OsDebugHelper::log( 'Error processing request to Stripe: ' . $e->getMessage(), 'stripe_connect_error' );
374 return [];
375 }
376 }
377
378 public static function do_request( string $path, string $connection_data = '', string $method = 'GET', array $vars = [], array $headers = [], string $force_env = '' ) {
379
380 $default_vars = [];
381 $default_headers = [
382 'latepoint-version' => LATEPOINT_VERSION,
383 'latepoint-domain' => OsUtilHelper::get_site_url(),
384 'latepoint-license-key' => OsLicenseHelper::get_license_key(),
385 ];
386
387 if ( ! empty( $connection_data ) ) {
388 $default_headers['connection-data'] = $connection_data;
389 }
390
391
392 $args = array(
393 'timeout' => 15,
394 'headers' => array_merge( $default_headers, $headers ),
395 'body' => array_merge( $default_vars, $vars ),
396 'sslverify' => false,
397 'method' => $method,
398 );
399
400
401 // in our connect server we use test/live, while latepoint plugin uses dev/live
402 if ( ! empty( $force_env ) && in_array( $force_env, [ LATEPOINT_PAYMENTS_ENV_DEV, LATEPOINT_PAYMENTS_ENV_LIVE ] ) ) {
403 $env = ( $force_env == LATEPOINT_PAYMENTS_ENV_DEV ) ? 'test' : 'live';
404 } else {
405 $env = ( OsSettingsHelper::is_env_payments_dev() ? 'test' : 'live' );
406 }
407 $url = LATEPOINT_STRIPE_CONNECT_URL . "/api/wp/v1/stripe-connect/{$env}/{$path}";
408
409 $response = wp_remote_request( $url, $args );
410
411 if ( ! is_wp_error( $response ) ) {
412 $data = json_decode( wp_remote_retrieve_body( $response ), true );
413 $data['status'] = $response['response'];
414 return $data;
415 } else {
416 $error_message = $response->get_error_message();
417 throw new Exception( $error_message );
418 }
419 }
420
421
422 public static function get_connection_buttons_and_status( string $env = '' ) {
423 $stripe_connect_account_id = OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'stripe_connect_account_id', $env ), false );
424 $html = '';
425 $duplicate_token_activations = OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'stripe_connect_duplicate_token_activations', $env ), '' );
426 if ( ! empty( $duplicate_token_activations ) ) {
427 $html .= '<div class="latepoint-fix-records-warning"><i class="latepoint-icon latepoint-icon-info"></i><div>';
428 $html .= '<div>' . __( 'The following websites are using the same server token. This can happen if a site was cloned from one server to another. To fix this, disconnect each site and reconnect it. When you disconnect a connection, a new token is generated that can then be used safely.', 'latepoint' ) . '</div>';
429 $html .= '<div class="latepoint-fix-records-values">' . $duplicate_token_activations . '</div>';
430 $html .= '</div></div>';
431 }
432 $html .= '<div class="payment-processor-connect-status-inner">';
433 if ( $stripe_connect_account_id ) {
434 $charges_enabled = OsSettingsHelper::is_on( OsSettingsHelper::append_payment_env_key( 'stripe_connect_charges_enabled', $env ) );
435 $disconnect_link = '<a class="payment-processor-disconnect-link" href="#"
436 data-os-pass-response="yes"
437 data-os-pass-this="yes"
438 data-os-before-after="none"
439 data-os-after-call="latepointStripeConnectAdmin.reload_connect_status_wrapper"
440 data-os-params="' . esc_attr( OsUtilHelper::build_os_params( [ 'env' => $env ], 'stripe_disconnect_account' ) ) . '"
441 data-os-action="' . OsRouterHelper::build_route_name( 'stripe_connect', 'disconnect_connect_account' ) . '"
442 ><i class="latepoint-icon latepoint-icon-x"></i><span>' . __( 'disconnect', 'latepoint' ) . '</span></a>';
443 if ( $charges_enabled ) {
444 $html .= '<div class="payment-processor-status-connected"><i class="latepoint-icon latepoint-icon-check"></i><span>' . __( 'Connected', 'latepoint' ) . '</span></div>';
445 $html .= $disconnect_link;
446 $html .= '<div class="stripe-connect-account-info">' . __( 'Account: ', 'latepoint' ) . $stripe_connect_account_id . '</div>';
447 if ( $env == 'live' && ! empty( OsSettingsHelper::get_settings_value( 'stripe_connect_transaction_fee_info', '' ) ) ) {
448 $html .= '<div class="fee-disclosure-wrapper">
449 <div class="fee-disclosure">' . OsSettingsHelper::get_settings_value( 'stripe_connect_transaction_fee_info', '' ) . ' transaction fee. <a target="_blank" href="https://wpdocs.latepoint.com/understanding-payment-processing-fees/"><span>Pricing</span> <i class="latepoint-icon latepoint-icon-external-link"></i></a></div>
450 </div>';
451 }
452 } else {
453 $html .= '<div class="payment-processor-status-charges-disabled"><i class="latepoint-icon latepoint-icon-clock"></i><span>' . __( 'Pending Action', 'latepoint' ) . '</span></div>';
454 $html .= '<a data-env="' . $env . '" data-route-name="' . OsRouterHelper::build_route_name( 'stripe_connect', 'start_connect_process' ) . '" href="#" class="payment-start-connecting"><span>' . __( 'Continue Setup', 'latepoint' ) . '</span><i class="latepoint-icon latepoint-icon-arrow-right"></i></a>';
455 $html .= '<div class="stripe-connect-account-info">';
456 $html .= '<div>' . $stripe_connect_account_id . '</div>';
457 $html .= $disconnect_link;
458 $html .= '</div>';
459 }
460 } else {
461 $html .= '<a data-env="' . $env . '" data-route-name="' . OsRouterHelper::build_route_name( 'stripe_connect', 'start_connect_process' ) . '" href="#" class="payment-start-connecting"><span>' . __( 'Start Connecting', 'latepoint' ) . '</span><i class="latepoint-icon latepoint-icon-arrow-right"></i></a>';
462 }
463 $html .= '</div>';
464 return $html;
465 }
466
467 public static function retrieve_payment_intent( string $payment_intent_id ): array {
468 $payment_request_data = self::do_account_request( 'payment-intents/' . $payment_intent_id, OsSettingsHelper::get_payments_environment() );
469 $result = [
470 'id' => $payment_request_data['data']['id'],
471 'status' => $payment_request_data['data']['status'],
472 'total' => $payment_request_data['data']['total'],
473 ];
474 return $result;
475 }
476
477 public static function validate_payment_intent_amount( array $payment_intent_data, string $expected_charge_amount ): bool {
478 $expected_in_specs = (int) self::convert_amount_to_specs( $expected_charge_amount );
479 $actual_from_stripe = (int) $payment_intent_data['total'];
480 return abs( $expected_in_specs - $actual_from_stripe ) <= 1;
481 }
482
483 private static function get_properties_allowed_to_update( $roles = 'admin' ) {
484 return array( 'source', 'email', 'name' );
485 }
486
487 public static function get_connect_publishable_key(): string {
488 $key = OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'stripe_connect_publishable_key' ), '' );
489 if ( empty( $key ) ) {
490 $response = self::do_request( 'public-key/' );
491 $key = $response['data']['key'];
492 OsSettingsHelper::save_setting_by_name( OsSettingsHelper::append_payment_env_key( 'stripe_connect_publishable_key' ), $key );
493 }
494 return $key;
495 }
496
497 public static function zero_decimal_currencies_list() {
498 return array( 'bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga', 'pyg', 'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf' );
499 }
500
501
502 public static function load_countries_list() {
503 $country_codes = [
504 'AU' => 'Australia',
505 'AT' => 'Austria',
506 'BE' => 'Belgium',
507 'BR' => 'Brazil',
508 'BG' => 'Bulgaria',
509 'CA' => 'Canada',
510 'HR' => 'Croatia',
511 'CY' => 'Cyprus',
512 'CZ' => 'Czech Republic',
513 'DK' => 'Denmark',
514 'EE' => 'Estonia',
515 'FI' => 'Finland',
516 'FR' => 'France',
517 'DE' => 'Germany',
518 'GI' => 'Gibraltar',
519 'GR' => 'Greece',
520 'HK' => 'Hong Kong',
521 'HU' => 'Hungary',
522 'IN' => 'India',
523 'IE' => 'Ireland',
524 'IT' => 'Italy',
525 'JP' => 'Japan',
526 'LV' => 'Latvia',
527 'LI' => 'Liechtenstein',
528 'LT' => 'Lithuania',
529 'LU' => 'Luxembourg',
530 'MY' => 'Malaysia',
531 'MT' => 'Malta',
532 'MX' => 'Mexico',
533 'NL' => 'Netherlands',
534 'NZ' => 'New Zealand',
535 'NO' => 'Norway',
536 'PL' => 'Poland',
537 'PT' => 'Portugal',
538 'RO' => 'Romania',
539 'SG' => 'Singapore',
540 'SK' => 'Slovakia',
541 'SI' => 'Slovenia',
542 'ES' => 'Spain',
543 'SE' => 'Sweden',
544 'CH' => 'Switzerland',
545 'TH' => 'Thailand',
546 'AE' => 'United Arab Emirates',
547 'GB' => 'United Kingdom',
548 'US' => 'United States',
549 ];
550 return $country_codes;
551 }
552
553
554 public static function load_all_currencies_list(): array {
555 return [
556 'usd' => 'United States Dollar',
557 'aed' => 'United Arab Emirates Dirham',
558 'afn' => 'Afghan Afghani',
559 'all' => 'Albanian Lek',
560 'amd' => 'Armenian Dram',
561 'ang' => 'Netherlands Antillean Guilder',
562 'aoa' => 'Angolan Kwanza',
563 'ars' => 'Argentine Peso',
564 'aud' => 'Australian Dollar',
565 'awg' => 'Aruban Florin',
566 'azn' => 'Azerbaijani Manat',
567 'bam' => 'Bosnia-Herzegovina Convertible Mark',
568 'bbd' => 'Barbadian Dollar',
569 'bdt' => 'Bangladeshi Taka',
570 'bgn' => 'Bulgarian Lev',
571 'bif' => 'Burundian Franc',
572 'bmd' => 'Bermudian Dollar',
573 'bnd' => 'Brunei Dollar',
574 'bob' => 'Bolivian Boliviano',
575 'brl' => 'Brazilian Real',
576 'bsd' => 'Bahamian Dollar',
577 'bwp' => 'Botswana Pula',
578 'bzd' => 'Belize Dollar',
579 'cad' => 'Canadian Dollar',
580 'cdf' => 'Congolese Franc',
581 'chf' => 'Swiss Franc',
582 'clp' => 'Chilean Peso',
583 'cny' => 'Chinese Yuan',
584 'cop' => 'Colombian Peso',
585 'crc' => 'Costa Rican Colón',
586 'cve' => 'Cape Verdean Escudo',
587 'czk' => 'Czech Koruna',
588 'djf' => 'Djiboutian Franc',
589 'dkk' => 'Danish Krone',
590 'dop' => 'Dominican Peso',
591 'dzd' => 'Algerian Dinar',
592 'egp' => 'Egyptian Pound',
593 'etb' => 'Ethiopian Birr',
594 'eur' => 'Euro',
595 'fjd' => 'Fijian Dollar',
596 'fkp' => 'Falkland Islands Pound',
597 'gbp' => 'British Pound Sterling',
598 'gel' => 'Georgian Lari',
599 'gip' => 'Gibraltar Pound',
600 'gmd' => 'Gambian Dalasi',
601 'gnf' => 'Guinean Franc',
602 'gtq' => 'Guatemalan Quetzal',
603 'gyd' => 'Guyanese Dollar',
604 'hkd' => 'Hong Kong Dollar',
605 'hnl' => 'Honduran Lempira',
606 'hrk' => 'Croatian Kuna',
607 'htg' => 'Haitian Gourde',
608 'huf' => 'Hungarian Forint',
609 'idr' => 'Indonesian Rupiah',
610 'ils' => 'Israeli New Shekel',
611 'inr' => 'Indian Rupee',
612 'isk' => 'Icelandic Króna',
613 'jmd' => 'Jamaican Dollar',
614 'jpy' => 'Japanese Yen',
615 'kes' => 'Kenyan Shilling',
616 'kgs' => 'Kyrgyzstani Som',
617 'khr' => 'Cambodian Riel',
618 'kmf' => 'Comorian Franc',
619 'krw' => 'South Korean Won',
620 'kyd' => 'Cayman Islands Dollar',
621 'kzt' => 'Kazakhstani Tenge',
622 'lak' => 'Lao Kip',
623 'lbp' => 'Lebanese Pound',
624 'lkr' => 'Sri Lankan Rupee',
625 'lrd' => 'Liberian Dollar',
626 'lsl' => 'Lesotho Loti',
627 'mad' => 'Moroccan Dirham',
628 'mdl' => 'Moldovan Leu',
629 'mga' => 'Malagasy Ariary',
630 'mkd' => 'Macedonian Denar',
631 'mmk' => 'Myanmar Kyat',
632 'mnt' => 'Mongolian Tögrög',
633 'mop' => 'Macanese Pataca',
634 'mro' => 'Mauritanian Ouguiya (pre-2018)',
635 'mur' => 'Mauritian Rupee',
636 'mvr' => 'Maldivian Rufiyaa',
637 'mwk' => 'Malawian Kwacha',
638 'mxn' => 'Mexican Peso',
639 'myr' => 'Malaysian Ringgit',
640 'mzn' => 'Mozambican Metical',
641 'nad' => 'Namibian Dollar',
642 'ngn' => 'Nigerian Naira',
643 'nio' => 'Nicaraguan Córdoba',
644 'nok' => 'Norwegian Krone',
645 'npr' => 'Nepalese Rupee',
646 'nzd' => 'New Zealand Dollar',
647 'pab' => 'Panamanian Balboa',
648 'pen' => 'Peruvian Sol',
649 'pgk' => 'Papua New Guinean Kina',
650 'php' => 'Philippine Peso',
651 'pkr' => 'Pakistani Rupee',
652 'pln' => 'Polish Złoty',
653 'pyg' => 'Paraguayan Guarani',
654 'qar' => 'Qatari Riyal',
655 'ron' => 'Romanian Leu',
656 'rsd' => 'Serbian Dinar',
657 'rub' => 'Russian Ruble',
658 'rwf' => 'Rwandan Franc',
659 'sar' => 'Saudi Riyal',
660 'sbd' => 'Solomon Islands Dollar',
661 'scr' => 'Seychellois Rupee',
662 'sek' => 'Swedish Krona',
663 'sgd' => 'Singapore Dollar',
664 'shp' => 'Saint Helena Pound',
665 'sll' => 'Sierra Leonean Leone',
666 'sos' => 'Somali Shilling',
667 'srd' => 'Surinamese Dollar',
668 'std' => 'São Tomé and Príncipe Dobra (pre-2018)',
669 'svc' => 'Salvadoran Colón',
670 'szl' => 'Swazi Lilangeni',
671 'thb' => 'Thai Baht',
672 'tjs' => 'Tajikistani Somoni',
673 'top' => 'Tongan Paʻanga',
674 'try' => 'Turkish Lira',
675 'ttd' => 'Trinidad and Tobago Dollar',
676 'twd' => 'New Taiwan Dollar',
677 'tzs' => 'Tanzanian Shilling',
678 'uah' => 'Ukrainian Hryvnia',
679 'ugx' => 'Ugandan Shilling',
680 'uyu' => 'Uruguayan Peso',
681 'uzs' => 'Uzbekistani Som',
682 'vnd' => 'Vietnamese Đồng',
683 'vuv' => 'Vanuatu Vatu',
684 'wst' => 'Samoan Tālā',
685 'xaf' => 'Central African CFA Franc',
686 'xcd' => 'East Caribbean Dollar',
687 'xof' => 'West African CFA Franc',
688 'xpf' => 'CFP Franc',
689 'yer' => 'Yemeni Rial',
690 'zar' => 'South African Rand',
691 'zmw' => 'Zambian Kwacha',
692 ];
693 }
694
695
696 public static function get_connect_account_id( string $env = '' ) {
697 if ( empty( $env ) ) {
698 $env = OsSettingsHelper::get_payments_environment();
699 }
700 return OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'stripe_connect_account_id', $env ), '' );
701 }
702
703 public static function generate_payment_intent_id_and_secret_for_transaction_intent( OsTransactionIntentModel $transaction_intent ): array {
704 $order = new OsOrderModel( $transaction_intent->order_id );
705 $customer_data = [
706 'name' => $order->customer->full_name,
707 'email' => $order->customer->email,
708 ];
709 $options = [
710 'amount' => $transaction_intent->specs_charge_amount,
711 'currency' => self::get_currency_iso_code(),
712 'stripe_customer_id' => self::get_stripe_customer_id( $order->customer ),
713 'transaction_description' => esc_html__( 'Payment for Appointment', 'latepoint' ),
714 'metadata' => [
715 'transaction_intent_key' => $transaction_intent->intent_key,
716 ],
717 ];
718
719
720 // pass customer data in case it needs to be created
721 $result = self::do_account_request(
722 'payment-intents',
723 OsSettingsHelper::get_payments_environment(),
724 '',
725 'POST',
726 [
727 'payment_intent_options' => $options,
728 'customer_data' => $customer_data,
729 ]
730 );
731 if ( empty( $result['data'] ) ) {
732 // translators: %s is the payment error
733 $error_message = ! empty( $result['error'] ) ? sprintf( __( 'Payment Error: %s', 'latepoint' ), esc_html( $result['error'] ) ) : __( 'Error generating payment intent for transaction', 'latepoint' );
734 OsDebugHelper::log( $error_message );
735 throw new Exception( $error_message );
736 } else {
737 // make sure we use correct stripe customer id in case the one that was passed is invalid - a valid one will be returned in this call
738 if ( $result['data']['stripe_customer_id'] != self::get_stripe_customer_id( $order->customer ) ) {
739 self::save_stripe_customer_id( $order->customer, $result['data']['stripe_customer_id'] );
740 }
741
742 return [
743 'id' => $result['data']['id'],
744 'client_secret' => $result['data']['client_secret'],
745 ];
746 }
747 }
748
749 public static function generate_payment_intent_id_and_secret_for_order_intent( OsOrderIntentModel $order_intent ): array {
750 $customer_data = [
751 'name' => $order_intent->customer->full_name,
752 'email' => $order_intent->customer->email,
753 ];
754 $options = [
755 'amount' => $order_intent->specs_charge_amount,
756 'currency' => self::get_currency_iso_code(),
757 'stripe_customer_id' => self::get_stripe_customer_id( $order_intent->customer ),
758 'transaction_description' => esc_html__( 'Payment for Appointment', 'latepoint' ),
759 'metadata' => [
760 'order_intent_key' => $order_intent->intent_key,
761 ],
762 ];
763
764
765 // pass customer data in case it needs to be created
766 $result = self::do_account_request(
767 'payment-intents',
768 OsSettingsHelper::get_payments_environment(),
769 '',
770 'POST',
771 [
772 'payment_intent_options' => $options,
773 'customer_data' => $customer_data,
774 ]
775 );
776 if ( empty( $result['data'] ) ) {
777 // translators: %s is the payment error
778 $error_message = ! empty( $result['error'] ) ? sprintf( __( 'Payment Error: %s', 'latepoint' ), esc_html( $result['error'] ) ) : __( 'Error generating payment intent', 'latepoint' );
779 OsDebugHelper::log( $error_message );
780 throw new Exception( $error_message );
781 } else {
782 // make sure we use correct stripe customer id in case the one that was passed is invalid - a valid one will be returned in this call
783 if ( $result['data']['stripe_customer_id'] != self::get_stripe_customer_id( $order_intent->customer ) ) {
784 self::save_stripe_customer_id( $order_intent->customer, $result['data']['stripe_customer_id'] );
785 }
786
787 return [
788 'id' => $result['data']['id'],
789 'client_secret' => $result['data']['client_secret'],
790 ];
791 }
792 }
793
794 public static function build_customer_profile_link( string $stripe_customer_id, bool $test_env = false ): string {
795 return 'https://dashboard.stripe.com/' . ( $test_env ? 'test/' : '' ) . 'customers/' . $stripe_customer_id;
796 }
797
798
799 public static function transaction_is_refund_available( $result, OsTransactionModel $transaction_model ): bool {
800 if ( OsPaymentsHelper::is_payment_processor_enabled( self::$processor_code ) && $transaction_model->processor == self::$processor_code ) {
801 $result = true;
802 }
803 return $result;
804 }
805 }
806