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 / razorpay_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
razorpay_connect_helper.php
661 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 class OsRazorpayConnectHelper {
11 public static $default_currency_iso_code = 'INR';
12 public static $processor_code = 'razorpay_connect';
13
14 // -------------------------------------------------------------------------
15 // Payment processor registration
16 // -------------------------------------------------------------------------
17
18 public static function register_payment_processor( array $payment_processors ): array {
19 $payment_processors[ self::$processor_code ] = [
20 'code' => self::$processor_code,
21 'name' => __( 'Razorpay Checkout', 'latepoint' ),
22 'front_name' => __( 'Razorpay Checkout', 'latepoint' ),
23 'image_url' => LATEPOINT_IMAGES_URL . 'processor-razorpay-logo.png',
24 ];
25 return $payment_processors;
26 }
27
28 public static function add_settings_fields( $processor_code ) {
29 if ( $processor_code !== self::$processor_code ) {
30 return false;
31 }
32 ?>
33 <div class="sub-section-row">
34 <div class="sub-section-label">
35 <h3><?php esc_html_e( 'Connect (Live)', 'latepoint' ); ?></h3>
36 </div>
37 <div class="sub-section-content">
38 <div data-env="<?php echo esc_attr( LATEPOINT_PAYMENTS_ENV_LIVE ); ?>"
39 class="payment-processor-connect-status-wrapper razorpay-connect-status-wrapper"
40 data-route-name="<?php echo esc_attr( OsRouterHelper::build_route_name( 'razorpay_connect', 'check_connect_status' ) ); ?>">
41 <div class="os-loading-spinner"></div>
42 </div>
43 </div>
44 </div>
45 <div class="sub-section-row">
46 <div class="sub-section-label">
47 <h3><?php esc_html_e( 'Connect (Dev)', 'latepoint' ); ?></h3>
48 </div>
49 <div class="sub-section-content">
50 <div data-env="<?php echo esc_attr( LATEPOINT_PAYMENTS_ENV_DEV ); ?>"
51 class="payment-processor-connect-status-wrapper razorpay-connect-status-wrapper"
52 data-route-name="<?php echo esc_attr( OsRouterHelper::build_route_name( 'razorpay_connect', 'check_connect_status' ) ); ?>">
53 <div class="os-loading-spinner"></div>
54 </div>
55 </div>
56 </div>
57 <div class="sub-section-row">
58 <div class="sub-section-label">
59 <h3><?php esc_html_e( 'Currency Settings', 'latepoint' ); ?></h3>
60 </div>
61 <div class="sub-section-content">
62 <?php
63 $selected_country_code = OsSettingsHelper::get_settings_value( 'razorpay_connect_country_code', 'IN' );
64 $selected_currency_code = OsSettingsHelper::get_settings_value( 'razorpay_connect_currency_iso_code', self::$default_currency_iso_code );
65 ?>
66 <div class="os-row">
67 <div class="os-col-6">
68 <?php echo OsFormHelper::select_field( 'settings[razorpay_connect_country_code]', __( 'Country', 'latepoint' ), self::load_countries_list(), $selected_country_code ); ?>
69 </div>
70 <div class="os-col-6">
71 <?php echo OsFormHelper::select_field( 'settings[razorpay_connect_currency_iso_code]', __( 'Currency Code', 'latepoint' ), self::load_currencies_list(), $selected_currency_code ); ?>
72 </div>
73 </div>
74 </div>
75 </div>
76 <div class="sub-section-row">
77 <div class="sub-section-label">
78 <h3><?php esc_html_e( 'Other Settings', 'latepoint' ); ?></h3>
79 </div>
80 <div class="sub-section-content">
81 <div class="os-row os-mb-1">
82 <div class="os-col-6">
83 <?php echo OsFormHelper::text_field( 'settings[razorpay_connect_company_name]', __( 'Company Name (Appears on Payment Modal)', 'latepoint' ), OsSettingsHelper::get_settings_value( 'razorpay_connect_company_name' ), [ 'theme' => 'simple' ] ); ?>
84 </div>
85 <div class="os-col-6">
86 <?php echo OsFormHelper::color_picker( 'settings[razorpay_connect_theme_color]', __( 'Color for Payment Modal', 'latepoint' ), OsSettingsHelper::get_settings_value( 'razorpay_connect_theme_color', '#4366ff' ) ); ?>
87 </div>
88 </div>
89 <div class="os-row os-mb-2">
90 <div class="os-col-12">
91 <?php echo OsFormHelper::media_uploader_field( 'settings[razorpay_connect_logo_image_id]', 0, __( 'Logo for Payment Modal', 'latepoint' ), __( 'Remove Logo', 'latepoint' ), OsSettingsHelper::get_settings_value( 'razorpay_connect_logo_image_id' ) ); ?>
92 </div>
93 </div>
94 </div>
95 </div>
96 <?php
97 }
98
99 // -------------------------------------------------------------------------
100 // Payment methods
101 // -------------------------------------------------------------------------
102
103 public static function get_supported_payment_methods(): array {
104 return [
105 'razorpay_checkout' => [
106 'name' => __( 'Razorpay Checkout', 'latepoint' ),
107 'label' => __( 'Pay with Razorpay', 'latepoint' ),
108 'image_url' => LATEPOINT_IMAGES_URL . 'processor-razorpay-logo.png',
109 ],
110 ];
111 }
112
113 public static function add_all_payment_methods_to_payment_times( array $payment_times ): array {
114 $payment_methods = self::get_supported_payment_methods();
115 foreach ( $payment_methods as $payment_method_code => $payment_method_info ) {
116 $payment_times[ LATEPOINT_PAYMENT_TIME_NOW ][ $payment_method_code ][ self::$processor_code ] = $payment_method_info;
117 }
118 return $payment_times;
119 }
120
121 public static function add_enabled_payment_methods_to_payment_times( array $payment_times ): array {
122 if ( OsPaymentsHelper::is_payment_processor_enabled( self::$processor_code ) ) {
123 $payment_times = self::add_all_payment_methods_to_payment_times( $payment_times );
124 }
125 return $payment_times;
126 }
127
128 // -------------------------------------------------------------------------
129 // Payment step UI
130 // -------------------------------------------------------------------------
131
132 public static function output_payment_step_contents( OsCartModel $cart ) {
133 if ( ! OsPaymentsHelper::should_processor_handle_payment_for_cart( self::$processor_code, $cart ) ) {
134 return;
135 }
136 echo '<div class="lp-payment-method-content" data-payment-method="razorpay_checkout">';
137 echo '<div class="lp-payment-method-content-i">';
138 echo '<div class="razorpay-payment-trigger"></div>';
139 echo '</div>';
140 echo '</div>';
141 }
142
143 public static function output_order_payment_pay_contents( OsTransactionIntentModel $transaction_intent ) {
144 if ( ! OsPaymentsHelper::should_processor_handle_payment_for_transaction_intent( self::$processor_code, $transaction_intent ) ) {
145 return;
146 }
147 echo '<div class="lp-payment-method-content" data-payment-method="razorpay_checkout">';
148 echo '<div class="lp-payment-method-content-i">';
149 echo '<div class="razorpay-payment-trigger"></div>';
150 echo '</div>';
151 echo '</div>';
152 }
153
154 // -------------------------------------------------------------------------
155 // Amount conversion
156 // -------------------------------------------------------------------------
157
158 public static function convert_charge_amount_to_requirements( $charge_amount, OsCartModel $cart ) {
159 if ( OsPaymentsHelper::should_processor_handle_payment_for_cart( self::$processor_code, $cart ) ) {
160 $charge_amount = self::convert_amount_to_specs( $charge_amount );
161 }
162 return $charge_amount;
163 }
164
165 public static function convert_transaction_intent_charge_amount_to_specs( $amount, OsTransactionIntentModel $transaction_intent ) {
166 if ( OsPaymentsHelper::should_processor_handle_payment_for_transaction_intent( self::$processor_code, $transaction_intent ) ) {
167 $amount = self::convert_amount_to_specs( $amount );
168 }
169 return $amount;
170 }
171
172 public static function convert_amount_to_specs( $charge_amount ) {
173 $iso_code = self::get_currency_iso_code();
174 if ( in_array( strtolower( $iso_code ), self::zero_decimal_currencies_list() ) ) {
175 $charge_amount = round( $charge_amount );
176 } else {
177 $number_of_decimals = OsSettingsHelper::get_settings_value( 'number_of_decimals', '2' );
178 $charge_amount = number_format( (float) $charge_amount, $number_of_decimals, '.', '' ) * pow( 10, $number_of_decimals );
179 }
180 return (int) $charge_amount;
181 }
182
183 public static function convert_amount_back_from_specs_to_db_format( $charge_amount ) {
184 $iso_code = self::get_currency_iso_code();
185 $number_of_decimals = OsSettingsHelper::get_settings_value( 'number_of_decimals', '2' );
186 if ( ! in_array( strtolower( $iso_code ), self::zero_decimal_currencies_list() ) && ! empty( $number_of_decimals ) ) {
187 $charge_amount = $charge_amount / pow( 10, $number_of_decimals );
188 $charge_amount = number_format( (float) $charge_amount, 4, '.', '' );
189 } else {
190 $charge_amount = OsMoneyHelper::pad_to_db_format( $charge_amount );
191 }
192 return $charge_amount;
193 }
194
195 public static function zero_decimal_currencies_list(): array {
196 return array( 'bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga', 'pyg', 'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf' );
197 }
198
199 // -------------------------------------------------------------------------
200 // Payment verification
201 // -------------------------------------------------------------------------
202
203 // Reject a payment whose amount doesn't match the expected charge (blocks underpayment). Mirrors Stripe's validate_payment_intent_amount().
204 public static function validate_payment_amount( array $payment, string $expected_charge_amount ): bool {
205 $expected_in_specs = (int) self::convert_amount_to_specs( $expected_charge_amount );
206 $actual_from_razorpay = isset( $payment['amount'] ) ? (int) $payment['amount'] : 0;
207 return abs( $expected_in_specs - $actual_from_razorpay ) <= 1;
208 }
209
210 // Reject a payment whose currency doesn't match the configured currency; fails closed when currency is missing.
211 public static function validate_payment_currency( array $payment ): bool {
212 $expected = strtoupper( self::get_currency_iso_code() );
213 $actual = isset( $payment['currency'] ) ? strtoupper( (string) $payment['currency'] ) : '';
214 return ! empty( $actual ) && $actual === $expected;
215 }
216
217 // Reject a payment that isn't bound to THIS intent. When creating the Razorpay order/checkout we stamp the intent
218 // key into the notes ($order_intent->intent_key under 'order_intent_key' / 'transaction_intent_key'); Razorpay
219 // echoes those notes back on the retrieved payment, so matching notes[$note_key] against the local intent key
220 // binds the payment to this specific booking/invoice. A same-amount/same-currency payment from an unrelated
221 // order carries a different (or no) note and is rejected. Fails closed when the note is absent.
222 public static function validate_payment_order_binding( array $payment, string $note_key, string $expected_intent_key ): bool {
223 if ( empty( $expected_intent_key ) ) {
224 return false;
225 }
226 $notes = ( isset( $payment['notes'] ) && is_array( $payment['notes'] ) ) ? $payment['notes'] : [];
227 $actual_intent_key = isset( $notes[ $note_key ] ) ? (string) $notes[ $note_key ] : '';
228 return ! empty( $actual_intent_key ) && hash_equals( $expected_intent_key, $actual_intent_key );
229 }
230
231 // -------------------------------------------------------------------------
232 // Payment processing
233 // -------------------------------------------------------------------------
234
235 public static function process_payment( $result, OsOrderIntentModel $order_intent ) {
236 if ( ! OsPaymentsHelper::should_processor_handle_payment_for_order_intent( self::$processor_code, $order_intent ) ) {
237 return $result;
238 }
239 switch ( $order_intent->get_payment_data_value( 'method' ) ) {
240 case 'razorpay_checkout':
241 if ( $order_intent->get_payment_data_value( 'token' ) ) {
242 $payment_data = self::retrieve_payment( $order_intent->get_payment_data_value( 'token' ) );
243 if ( ! empty( $payment_data['data'] ) && in_array( $payment_data['data']['status'], [ 'authorized', 'captured' ] ) ) {
244 // Verify amount, currency and that the payment's Razorpay notes reference this intent — not
245 // just status. Fail closed on any mismatch (blocks underpayment and cross-order reuse).
246 if ( self::validate_payment_amount( $payment_data['data'], $order_intent->charge_amount )
247 && self::validate_payment_currency( $payment_data['data'] )
248 && self::validate_payment_order_binding( $payment_data['data'], 'order_intent_key', $order_intent->intent_key ) ) {
249 $result['status'] = LATEPOINT_STATUS_SUCCESS;
250 $result['processor'] = self::$processor_code;
251 $result['charge_id'] = $payment_data['data']['id'];
252 $result['amount'] = $payment_data['data']['amount'];
253 $result['kind'] = $payment_data['data']['status'] === 'authorized' ? LATEPOINT_TRANSACTION_KIND_AUTHORIZATION : LATEPOINT_TRANSACTION_KIND_CAPTURE;
254 } else {
255 $result['status'] = LATEPOINT_STATUS_ERROR;
256 $result['message'] = __( 'Payment verification failed', 'latepoint' );
257 OsDebugHelper::log( 'Razorpay payment verification failed (amount/currency/notes binding) for order intent ' . $order_intent->id, 'razorpay_connect_error' );
258 $order_intent->add_error( 'payment_error', $result['message'] );
259 $order_intent->add_error( 'send_to_step', $result['message'], 'payment' );
260 }
261 } else {
262 $result['status'] = LATEPOINT_STATUS_ERROR;
263 $result['message'] = __( 'Payment Error', 'latepoint' );
264 $order_intent->add_error( 'payment_error', $result['message'] );
265 $order_intent->add_error( 'send_to_step', $result['message'], 'payment' );
266 }
267 } else {
268 $result['status'] = LATEPOINT_STATUS_ERROR;
269 $result['message'] = __( 'Payment Error — token missing', 'latepoint' );
270 $order_intent->add_error( 'payment_error', $result['message'] );
271 }
272 break;
273 }
274 return $result;
275 }
276
277 public static function process_payment_for_transaction_intent( $result, OsTransactionIntentModel $transaction_intent ) {
278 if ( ! OsPaymentsHelper::should_processor_handle_payment_for_transaction_intent( self::$processor_code, $transaction_intent ) ) {
279 return $result;
280 }
281 switch ( $transaction_intent->get_payment_data_value( 'method' ) ) {
282 case 'razorpay_checkout':
283 if ( $transaction_intent->get_payment_data_value( 'token' ) ) {
284 $payment_data = self::retrieve_payment( $transaction_intent->get_payment_data_value( 'token' ) );
285 if ( ! empty( $payment_data['data'] ) && in_array( $payment_data['data']['status'], [ 'authorized', 'captured' ] ) ) {
286 // Verify amount, currency and that the payment's Razorpay notes reference this intent — not
287 // just status. Fail closed on any mismatch (blocks underpayment and cross-order reuse).
288 if ( self::validate_payment_amount( $payment_data['data'], $transaction_intent->charge_amount )
289 && self::validate_payment_currency( $payment_data['data'] )
290 && self::validate_payment_order_binding( $payment_data['data'], 'transaction_intent_key', $transaction_intent->intent_key ) ) {
291 $result['status'] = LATEPOINT_STATUS_SUCCESS;
292 $result['processor'] = self::$processor_code;
293 $result['charge_id'] = $payment_data['data']['id'];
294 $result['amount'] = $payment_data['data']['amount'];
295 $result['kind'] = $payment_data['data']['status'] === 'authorized' ? LATEPOINT_TRANSACTION_KIND_AUTHORIZATION : LATEPOINT_TRANSACTION_KIND_CAPTURE;
296 } else {
297 $result['status'] = LATEPOINT_STATUS_ERROR;
298 $result['message'] = __( 'Payment verification failed', 'latepoint' );
299 OsDebugHelper::log( 'Razorpay payment verification failed (amount/currency/notes binding) for transaction intent ' . $transaction_intent->id, 'razorpay_connect_error' );
300 $transaction_intent->add_error( 'send_to_step', $result['message'], 'payment' );
301 }
302 } else {
303 $result['status'] = LATEPOINT_STATUS_ERROR;
304 $result['message'] = __( 'Payment Error', 'latepoint' );
305 $transaction_intent->add_error( 'send_to_step', $result['message'], 'payment' );
306 }
307 } else {
308 $result['status'] = LATEPOINT_STATUS_ERROR;
309 $result['message'] = __( 'Payment Error — token missing', 'latepoint' );
310 $transaction_intent->add_error( 'payment_error', $result['message'] );
311 }
312 break;
313 }
314 return $result;
315 }
316
317 public static function transaction_is_refund_available( $result, OsTransactionModel $transaction_model ): bool {
318 if ( OsPaymentsHelper::is_payment_processor_enabled( self::$processor_code ) && $transaction_model->processor === self::$processor_code ) {
319 $result = true;
320 }
321 return $result;
322 }
323
324 public static function process_refund( $transaction_refund, OsTransactionModel $transaction, $custom_amount = null ) {
325 if ( $transaction->processor !== self::$processor_code ) {
326 return $transaction_refund;
327 }
328 if ( ! $transaction->can_refund() ) {
329 throw new Exception( __( 'Invalid Transaction', 'latepoint' ) );
330 }
331
332 $refund_data = [
333 'payment_id' => $transaction->token,
334 ];
335 if ( $custom_amount ) {
336 $refund_data['custom_amount'] = self::convert_amount_to_specs( $custom_amount );
337 }
338
339 $response = self::do_account_request( 'refunds', OsSettingsHelper::get_payments_environment(), '', 'POST', $refund_data );
340
341 if ( empty( $response['data'] ) ) {
342 throw new Exception( __( 'Error Refunding', 'latepoint' ) );
343 }
344
345 $transaction_refund = new OsTransactionRefundModel();
346 $transaction_refund->transaction_id = $transaction->id;
347 $transaction_refund->amount = self::convert_amount_back_from_specs_to_db_format( $response['data']['amount'] );
348 $transaction_refund->token = $response['data']['id'];
349 if ( $transaction_refund->save() ) {
350 do_action( 'latepoint_transaction_refund_created', $transaction_refund );
351 return $transaction_refund;
352 } else {
353 throw new Exception( implode( ', ', $transaction_refund->get_error_messages() ) );
354 }
355 }
356
357 // -------------------------------------------------------------------------
358 // Middleware requests
359 // -------------------------------------------------------------------------
360
361 public static function generate_razorpay_order_for_order_intent( OsOrderIntentModel $order_intent ): array {
362 $options = [
363 'amount' => $order_intent->specs_charge_amount,
364 'currency' => strtoupper( self::get_currency_iso_code() ),
365 'notes' => [
366 'order_intent_key' => $order_intent->intent_key,
367 ],
368 ];
369 $result = self::do_account_request(
370 'orders',
371 OsSettingsHelper::get_payments_environment(),
372 '',
373 'POST',
374 [ 'order_options' => $options ]
375 );
376 if ( empty( $result['data'] ) ) {
377 $error_message = ! empty( $result['error'] ) ? sprintf( __( 'Payment Error: %s', 'latepoint' ), esc_html( $result['error'] ) ) : __( 'Error generating Razorpay order', 'latepoint' );
378 OsDebugHelper::log( $error_message );
379 throw new Exception( $error_message );
380 }
381 return $result['data'];
382 }
383
384 public static function generate_razorpay_order_for_transaction_intent( OsTransactionIntentModel $transaction_intent ): array {
385 $options = [
386 'amount' => $transaction_intent->specs_charge_amount,
387 'currency' => strtoupper( self::get_currency_iso_code() ),
388 'notes' => [
389 'transaction_intent_key' => $transaction_intent->intent_key,
390 ],
391 ];
392 $result = self::do_account_request(
393 'orders',
394 OsSettingsHelper::get_payments_environment(),
395 '',
396 'POST',
397 [ 'order_options' => $options ]
398 );
399 if ( empty( $result['data'] ) ) {
400 $error_message = ! empty( $result['error'] ) ? sprintf( __( 'Payment Error: %s', 'latepoint' ), esc_html( $result['error'] ) ) : __( 'Error generating Razorpay order for transaction', 'latepoint' );
401 OsDebugHelper::log( $error_message );
402 throw new Exception( $error_message );
403 }
404 return $result['data'];
405 }
406
407 public static function retrieve_payment( string $payment_id ): array {
408 $env = OsSettingsHelper::get_payments_environment();
409 return self::do_account_request( 'payments/' . $payment_id, $env );
410 }
411
412 public static function do_account_request( string $path, string $env = '', string $connection_data = '', string $method = 'GET', array $vars = [], array $headers = [] ) {
413 if ( empty( $env ) ) {
414 $env = OsSettingsHelper::get_payments_environment();
415 }
416 $path = self::get_connect_account_id( $env ) . '/' . $path;
417 try {
418 return self::do_request( $path, $connection_data, $method, $vars, $headers, $env );
419 } catch ( Exception $e ) {
420 OsDebugHelper::log( 'Error processing request to Razorpay middleware: ' . $e->getMessage(), 'razorpay_connect_error' );
421 return [];
422 }
423 }
424
425 public static function do_request( string $path, string $connection_data = '', string $method = 'GET', array $vars = [], array $headers = [], string $force_env = '' ) {
426 $default_headers = [
427 'latepoint-version' => LATEPOINT_VERSION,
428 'latepoint-domain' => OsUtilHelper::get_site_url(),
429 'latepoint-license-key' => OsLicenseHelper::get_license_key(),
430 ];
431
432 if ( ! empty( $connection_data ) ) {
433 $default_headers['connection-data'] = $connection_data;
434 }
435
436 $args = array(
437 'timeout' => 15,
438 'headers' => array_merge( $default_headers, $headers ),
439 'body' => $vars,
440 'sslverify' => false,
441 'method' => $method,
442 );
443
444 if ( ! empty( $force_env ) && in_array( $force_env, [ LATEPOINT_PAYMENTS_ENV_DEV, LATEPOINT_PAYMENTS_ENV_LIVE ] ) ) {
445 $env = ( $force_env === LATEPOINT_PAYMENTS_ENV_DEV ) ? 'test' : 'live';
446 } else {
447 $env = OsSettingsHelper::is_env_payments_dev() ? 'test' : 'live';
448 }
449
450 $url = LATEPOINT_RAZORPAY_CONNECT_URL . "/api/wp/v1/razorpay-connect/{$env}/{$path}";
451 $response = wp_remote_request( $url, $args );
452
453 if ( ! is_wp_error( $response ) ) {
454 $data = json_decode( wp_remote_retrieve_body( $response ), true );
455 $data['status'] = $response['response'];
456 return $data;
457 } else {
458 $error_message = $response->get_error_message();
459 throw new Exception( $error_message );
460 }
461 }
462
463 // -------------------------------------------------------------------------
464 // Connection management
465 // -------------------------------------------------------------------------
466
467 public static function get_server_token( string $force_env = '' ): string {
468 $key = OsSettingsHelper::append_payment_env_key( 'server_token_for_razorpay_connect', $force_env );
469 $server_token = OsSettingsHelper::get_settings_value( $key, '' );
470 if ( empty( $server_token ) ) {
471 $server_token = OsUtilHelper::generate_uuid();
472 OsSettingsHelper::save_setting_by_name( $key, $server_token );
473 }
474 return $server_token;
475 }
476
477 public static function reset_server_token( string $force_env = '' ): string {
478 $key = OsSettingsHelper::append_payment_env_key( 'server_token_for_razorpay_connect', $force_env );
479 $new_server_token = OsUtilHelper::generate_uuid();
480 OsSettingsHelper::save_setting_by_name( $key, $new_server_token );
481 return $new_server_token;
482 }
483
484 public static function get_connect_url( string $env = '' ): string {
485 $url = LATEPOINT_RAZORPAY_CONNECT_URL . '/wp/razorpay-connection/' . $env . '/start/';
486 $url .= self::get_server_token( $env ) . '/' . base64_encode( implode( '|||', [ get_bloginfo( 'name' ), get_site_icon_url(), OsUtilHelper::get_site_url() ] ) );
487 return $url;
488 }
489
490 public static function get_connect_account_id( string $env = '' ): string {
491 if ( empty( $env ) ) {
492 $env = OsSettingsHelper::get_payments_environment();
493 }
494 return OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'razorpay_connect_account_id', $env ), '' );
495 }
496
497 public static function get_connect_public_token( string $env = '' ): string {
498 if ( empty( $env ) ) {
499 $env = OsSettingsHelper::get_payments_environment();
500 }
501 return OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'razorpay_connect_public_token', $env ), '' );
502 }
503
504 public static function get_connection_buttons_and_status( string $env = '' ): string {
505 $razorpay_connect_account_id = OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'razorpay_connect_account_id', $env ), false );
506 $html = '';
507 $duplicate_token_activations = OsSettingsHelper::get_settings_value( OsSettingsHelper::append_payment_env_key( 'razorpay_connect_duplicate_token_activations', $env ), '' );
508
509 if ( ! empty( $duplicate_token_activations ) ) {
510 $html .= '<div class="latepoint-fix-records-warning"><i class="latepoint-icon latepoint-icon-info"></i><div>';
511 $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.', 'latepoint' ) . '</div>';
512 $html .= '<div class="latepoint-fix-records-values">' . esc_html( $duplicate_token_activations ) . '</div>';
513 $html .= '</div></div>';
514 }
515
516 $html .= '<div class="payment-processor-connect-status-inner">';
517 if ( $razorpay_connect_account_id ) {
518 $charges_enabled = OsSettingsHelper::is_on( OsSettingsHelper::append_payment_env_key( 'razorpay_connect_charges_enabled', $env ) );
519 $disconnect_link = '<a class="payment-processor-disconnect-link" href="#"
520 data-os-pass-response="yes"
521 data-os-pass-this="yes"
522 data-os-before-after="none"
523 data-os-after-call="latepointRazorpayConnectAdmin.reload_connect_status_wrapper"
524 data-os-params="' . esc_attr( OsUtilHelper::build_os_params( [ 'env' => $env ], 'razorpay_disconnect_account' ) ) . '"
525 data-os-action="' . OsRouterHelper::build_route_name( 'razorpay_connect', 'disconnect_connect_account' ) . '"
526 ><i class="latepoint-icon latepoint-icon-x"></i><span>' . __( 'disconnect', 'latepoint' ) . '</span></a>';
527 if ( $charges_enabled ) {
528 $html .= '<div class="payment-processor-status-connected"><i class="latepoint-icon latepoint-icon-check"></i><span>' . __( 'Connected', 'latepoint' ) . '</span></div>';
529 $html .= $disconnect_link;
530 $html .= '<div class="razorpay-connect-account-info">' . __( 'Account: ', 'latepoint' ) . esc_html( $razorpay_connect_account_id ) . '</div>';
531 if ( $env === LATEPOINT_PAYMENTS_ENV_LIVE && ! empty( OsSettingsHelper::get_settings_value( 'razorpay_connect_transaction_fee_info', '' ) ) ) {
532 $html .= '<div class="fee-disclosure-wrapper">
533 <div class="fee-disclosure">' . esc_html( OsSettingsHelper::get_settings_value( 'razorpay_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>
534 </div>';
535 }
536 } else {
537 $html .= '<div class="payment-processor-status-charges-disabled"><i class="latepoint-icon latepoint-icon-clock"></i><span>' . __( 'Pending Action', 'latepoint' ) . '</span></div>';
538 $html .= '<a data-env="' . esc_attr( $env ) . '" data-route-name="' . esc_attr( OsRouterHelper::build_route_name( 'razorpay_connect', 'start_connect_process' ) ) . '" href="#" class="payment-start-connecting"><span>' . __( 'Continue Setup', 'latepoint' ) . '</span><i class="latepoint-icon latepoint-icon-arrow-right"></i></a>';
539 $html .= '<div class="razorpay-connect-account-info">';
540 $html .= '<div>' . esc_html( $razorpay_connect_account_id ) . '</div>';
541 $html .= $disconnect_link;
542 $html .= '</div>';
543 }
544 } else {
545 $html .= '<a data-env="' . esc_attr( $env ) . '" data-route-name="' . esc_attr( OsRouterHelper::build_route_name( 'razorpay_connect', 'start_connect_process' ) ) . '" href="#" class="payment-start-connecting"><span>' . __( 'Start Connecting', 'latepoint' ) . '</span><i class="latepoint-icon latepoint-icon-arrow-right"></i></a>';
546 }
547 $html .= '</div>';
548 return $html;
549 }
550
551 // -------------------------------------------------------------------------
552 // Settings helpers
553 // -------------------------------------------------------------------------
554
555 public static function get_currency_iso_code(): string {
556 return OsSettingsHelper::get_settings_value( 'razorpay_connect_currency_iso_code', self::$default_currency_iso_code );
557 }
558
559 public static function load_countries_list(): array {
560 return [ 'IN' => 'India' ];
561 }
562
563 public static function load_currencies_list(): array {
564 return [
565 'AED' => 'United Arab Emirates Dirham',
566 'ALL' => 'Albanian Lek',
567 'AMD' => 'Armenian Dram',
568 'ARS' => 'Argentine Peso',
569 'AUD' => 'Australian Dollar',
570 'AWG' => 'Aruban Florin',
571 'BBD' => 'Barbadian Dollar',
572 'BDT' => 'Bangladeshi Taka',
573 'BMD' => 'Bermudian Dollar',
574 'BND' => 'Brunei Dollar',
575 'BOB' => 'Bolivian Boliviano',
576 'BSD' => 'Bahamian Dollar',
577 'BWP' => 'Botswana Pula',
578 'BZD' => 'Belize Dollar',
579 'CAD' => 'Canadian Dollar',
580 'CHF' => 'Swiss Franc',
581 'CNY' => 'Chinese Yuan',
582 'COP' => 'Colombian Peso',
583 'CRC' => 'Costa Rican Colón',
584 'CUP' => 'Cuban Peso',
585 'CZK' => 'Czech Koruna',
586 'DKK' => 'Danish Krone',
587 'DOP' => 'Dominican Peso',
588 'DZD' => 'Algerian Dinar',
589 'EGP' => 'Egyptian Pound',
590 'ETB' => 'Ethiopian Birr',
591 'EUR' => 'Euro',
592 'FJD' => 'Fijian Dollar',
593 'GBP' => 'Pound Sterling',
594 'GHS' => 'Ghanaian Cedi',
595 'GIP' => 'Gibraltar Pound',
596 'GMD' => 'Gambian Dalasi',
597 'GTQ' => 'Guatemalan Quetzal',
598 'GYD' => 'Guyanese Dollar',
599 'HKD' => 'Hong Kong Dollar',
600 'HNL' => 'Honduran Lempira',
601 'HRK' => 'Croatian Kuna',
602 'HTG' => 'Haitian Gourde',
603 'HUF' => 'Hungarian Forint',
604 'IDR' => 'Indonesian Rupiah',
605 'ILS' => 'Israeli New Shekel',
606 'INR' => 'Indian Rupee',
607 'JMD' => 'Jamaican Dollar',
608 'KES' => 'Kenyan Shilling',
609 'KGS' => 'Kyrgyzstani Som',
610 'KHR' => 'Cambodian Riel',
611 'KYD' => 'Cayman Islands Dollar',
612 'KZT' => 'Kazakhstani Tenge',
613 'LAK' => 'Lao Kip',
614 'LBP' => 'Lebanese Pound',
615 'LKR' => 'Sri Lankan Rupee',
616 'LRD' => 'Liberian Dollar',
617 'LSL' => 'Lesotho Loti',
618 'MAD' => 'Moroccan Dirham',
619 'MDL' => 'Moldovan Leu',
620 'MKD' => 'Macedonian Denar',
621 'MMK' => 'Myanmar Kyat',
622 'MNT' => 'Mongolian Tögrög',
623 'MOP' => 'Macanese Pataca',
624 'MUR' => 'Mauritian Rupee',
625 'MVR' => 'Maldivian Rufiyaa',
626 'MWK' => 'Malawian Kwacha',
627 'MXN' => 'Mexican Peso',
628 'MYR' => 'Malaysian Ringgit',
629 'NAD' => 'Namibian Dollar',
630 'NGN' => 'Nigerian Naira',
631 'NIO' => 'Nicaraguan Córdoba',
632 'NOK' => 'Norwegian Krone',
633 'NPR' => 'Nepalese Rupee',
634 'NZD' => 'New Zealand Dollar',
635 'PEN' => 'Peruvian Sol',
636 'PGK' => 'Papua New Guinean Kina',
637 'PHP' => 'Philippine Peso',
638 'PKR' => 'Pakistani Rupee',
639 'QAR' => 'Qatari Riyal',
640 'RUB' => 'Russian Ruble',
641 'SAR' => 'Saudi Riyal',
642 'SCR' => 'Seychellois Rupee',
643 'SEK' => 'Swedish Krona',
644 'SGD' => 'Singapore Dollar',
645 'SLL' => 'Sierra Leonean Leone',
646 'SOS' => 'Somali Shilling',
647 'SSP' => 'South Sudanese Pound',
648 'SVC' => 'Salvadoran Colón',
649 'SZL' => 'Swazi Lilangeni',
650 'THB' => 'Thai Baht',
651 'TTD' => 'Trinidad and Tobago Dollar',
652 'TZS' => 'Tanzanian Shilling',
653 'USD' => 'United States Dollar',
654 'UYU' => 'Uruguayan Peso',
655 'UZS' => 'Uzbekistani Som',
656 'YER' => 'Yemeni Rial',
657 'ZAR' => 'South African Rand',
658 ];
659 }
660 }
661