PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / 4.0.3
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress v4.0.3
4.17.2 4.17.1 4.17.0 4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / src / Membership / Controllers / CheckoutController.php
wp-user-avatar / src / Membership / Controllers Last commit date
BaseController.php 4 years ago CheckoutController.php 4 years ago CheckoutResponse.php 4 years ago CheckoutSessionData.php 4 years ago CheckoutTrait.php 4 years ago FrontendController.php 4 years ago SubscriptionPlanController.php 4 years ago index.php 4 years ago
CheckoutController.php
446 lines
1 <?php
2
3 namespace ProfilePress\Core\Membership\Controllers;
4
5 use ProfilePress\Core\Classes\LoginAuth;
6 use ProfilePress\Core\Membership\Models\Coupon\CouponFactory;
7 use ProfilePress\Core\Membership\Models\Order\OrderFactory;
8 use ProfilePress\Core\Membership\Models\Order\OrderType;
9 use ProfilePress\Core\Membership\Models\Plan\PlanEntity;
10 use ProfilePress\Core\Membership\Models\Subscription\SubscriptionFactory;
11 use ProfilePress\Core\Membership\PaymentMethods\PaymentMethods;
12 use ProfilePress\Core\Membership\PaymentMethods\StoreGateway;
13 use ProfilePress\Core\Membership\Repositories\OrderRepository;
14 use ProfilePress\Core\Membership\Repositories\SubscriptionRepository;
15 use ProfilePress\Core\Membership\Services\EUVATChecker\EuVatApi;
16 use ProfilePress\Core\Membership\Services\OrderService;
17 use ProfilePress\Core\Membership\Services\TaxService;
18
19 class CheckoutController extends BaseController
20 {
21 use CheckoutTrait;
22
23 public function __construct()
24 {
25 add_action('wp_ajax_nopriv_ppress_process_checkout_login', [$this, 'process_checkout_login']);
26
27 add_action('wp_ajax_ppress_process_checkout', [$this, 'process_checkout']);
28 add_action('wp_ajax_nopriv_ppress_process_checkout', [$this, 'process_checkout']);
29
30 add_action('wp_ajax_ppress_checkout_apply_discount', [$this, 'apply_discount']);
31 add_action('wp_ajax_nopriv_ppress_checkout_apply_discount', [$this, 'apply_discount']);
32
33 add_action('wp_ajax_ppress_checkout_remove_discount', [$this, 'remove_discount']);
34 add_action('wp_ajax_nopriv_ppress_checkout_remove_discount', [$this, 'remove_discount']);
35
36 add_action('wp_ajax_ppress_update_order_review', [$this, 'update_order_review']);
37 add_action('wp_ajax_nopriv_ppress_update_order_review', [$this, 'update_order_review']);
38
39 add_action('wp_ajax_ppress_contextual_state_field', [$this, 'contextual_state_field']);
40 add_action('wp_ajax_nopriv_ppress_contextual_state_field', [$this, 'contextual_state_field']);
41 }
42
43
44 public function contextual_state_field()
45 {
46 check_ajax_referer('ppress_process_checkout', 'csrf');
47
48 $country = ppressPOST_var('country', '');
49 $nameAttr = ppressPOST_var('name', '');
50 $idAttr = ppressPOST_var('id', '');
51 $classAttr = ppressPOST_var('class', '');
52
53 $states = ! empty($country) ? ppress_array_of_world_states(sanitize_text_field($country)) : [];
54
55 ob_start();
56
57 if ( ! empty($states)) {
58
59 printf('<select name="%s" id="%s" class="%s" autocomplete="address-level1" required="required">', $nameAttr, $idAttr, $classAttr);
60 echo '<option value="">&mdash;&mdash;&mdash;</option>';
61 foreach ($states as $id => $label) {
62 printf('<option value="%s">%s</option>', $id, $label);
63 }
64 echo '</select>';
65
66 } else {
67
68 printf(
69 '<input name="%s" type="text" id="%s" class="%s" autocomplete="address-level1" required="required">',
70 $nameAttr, $idAttr, $classAttr
71 );
72 }
73
74 wp_send_json_success(ob_get_clean());
75 }
76
77 public function process_checkout_login()
78 {
79 $nonce_check = check_ajax_referer('ppress_process_checkout', 'ppress_checkout_nonce', false);
80
81 if (false === $nonce_check) {
82 wp_send_json_error(
83 $this->alert_message(
84 esc_html__('Error processing login. Nonce failed', 'wp-user-avatar')
85 )
86 );
87 }
88
89 $response = LoginAuth::login_auth(
90 trim($_POST['ppmb_user_login']),
91 $_POST['ppmb_user_pass'],
92 true,
93 ppress_plan_checkout_url(absint($_GET['plan']))
94 );
95
96 if (is_wp_error($response)) {
97 wp_send_json_error($this->alert_message($response->get_error_message()));
98 }
99
100 wp_send_json_success();
101 }
102
103 public function apply_discount()
104 {
105 try {
106
107 $nonce_check = check_ajax_referer('ppress_process_checkout', 'ppress_checkout_nonce', false);
108
109 if (false === $nonce_check) {
110
111 throw new \Exception(
112 esc_html__('Error applying coupon code. Nonce failed', 'wp-user-avatar')
113 );
114 }
115
116 if (empty($_POST['coupon_code'])) {
117
118 throw new \Exception(
119 esc_html__('Please enter a coupon code.', 'wp-user-avatar')
120 );
121 }
122
123 if (empty($_POST['plan_id'])) {
124
125 throw new \Exception(
126 esc_html__('Please enter a plan ID.', 'wp-user-avatar')
127 );
128 }
129
130 $plan_id = absint($_POST['plan_id']);
131 $coupon_code = sanitize_text_field($_POST['coupon_code']);
132
133 $coupon = CouponFactory::fromCode($coupon_code);
134
135 if ( ! $coupon->exists()) {
136
137 throw new \Exception(
138 sprintf(esc_html__('Coupon code "%s" not found.', 'wp-user-avatar'), $coupon_code)
139 );
140 }
141
142 if ( ! $coupon->is_valid($plan_id, OrderType::NEW_ORDER)) {
143
144 throw new \Exception(
145 esc_html__('Sorry, this coupon is not valid.', 'wp-user-avatar')
146 );
147 }
148
149 ppress_session()->set(CheckoutSessionData::COUPON_CODE, [
150 'plan_id' => $plan_id,
151 'coupon_code' => $coupon->code,
152 ]);
153
154 wp_send_json_success();
155
156 } catch (\Exception $e) {
157
158 wp_send_json_error(
159 $this->alert_message($e->getMessage())
160 );
161 }
162 }
163
164 public function remove_discount()
165 {
166 try {
167
168 check_ajax_referer('ppress_process_checkout', 'ppress_checkout_nonce');
169
170 if (empty($_POST['plan_id'])) {
171
172 throw new \Exception(
173 esc_html__('Please enter a plan ID.', 'wp-user-avatar')
174 );
175 }
176
177 $plan_id = absint($_POST['plan_id']);
178
179 $session_coupon = ppress_session()->get(CheckoutSessionData::COUPON_CODE);
180
181 if (isset($session_coupon['plan_id'], $session_coupon['coupon_code']) && $plan_id == $session_coupon['plan_id']) {
182 ppress_session()->set(CheckoutSessionData::COUPON_CODE, null);
183 }
184
185 wp_send_json_success();
186
187 } catch (\Exception $e) {
188
189 wp_send_json_error(
190 $this->alert_message($e->getMessage())
191 );
192 }
193 }
194
195 public function process_checkout()
196 {
197 try {
198
199 $nonce_check = check_ajax_referer('ppress_process_checkout', 'ppress_checkout_nonce', false);
200
201 if (false === $nonce_check) {
202 throw new \Exception(esc_html__('Error processing checkout. Nonce failed', 'wp-user-avatar'));
203 }
204
205 $_POST = $this->cleanup_posted_data($_POST);
206
207 $plan_id = (int)$_POST['plan_id'];
208
209 if ( ! empty(ppress_settings_by_key('terms_page_id')) && empty($_POST['ppress-terms'])) {
210 throw new \Exception(
211 esc_html__('Please read and accept the terms and conditions to proceed with your order.', 'wp-user-avatar')
212 );
213 }
214
215 $cart_vars = OrderService::init()->checkout_order_calculation([
216 'plan_id' => $plan_id,
217 'coupon_code' => CheckoutSessionData::get_coupon_code($plan_id),
218 'tax_rate' => CheckoutSessionData::get_tax_rate($plan_id)
219 ]);
220
221 $is_free_checkout = OrderService::init()->is_free_checkout($cart_vars);
222
223 $payment_method = PaymentMethods::get_instance()->get_by_id(ppressPOST_var('ppress_payment_method', ''));
224
225 if ((empty($_POST['ppress_payment_method']) || ! $payment_method) && $is_free_checkout === false) {
226
227 throw new \Exception(
228 esc_html__('No payment method selected. Please try again.', 'wp-user-avatar')
229 );
230 }
231
232 if ($is_free_checkout) {
233 add_filter('ppress_checkout_billing_validation', '__return_false');
234 } else {
235
236 $validation_response = $payment_method->validate_fields();
237
238 if (is_wp_error($validation_response)) {
239 throw new \Exception($validation_response->get_error_message());
240 }
241 }
242
243 $customer_id = $this->register_update_user();
244
245 if (is_wp_error($customer_id)) {
246 throw new \Exception(json_encode($customer_id->get_error_messages()));
247 }
248
249 $order_id = $this->create_order($customer_id, $cart_vars);
250
251 if (is_wp_error($order_id)) {
252 throw new \Exception($order_id->get_error_message());
253 }
254
255 $subscription_id = $this->create_subscription($customer_id, $cart_vars);
256
257 if (is_wp_error($subscription_id)) {
258 throw new \Exception($subscription_id->get_error_message());
259 }
260
261 SubscriptionRepository::init()->updateColumn($subscription_id, 'parent_order_id', $order_id);
262 OrderRepository::init()->updateColumn($order_id, 'subscription_id', $subscription_id);
263
264 if ( ! $payment_method || ! $payment_method->get_id()) {
265 $payment_method = StoreGateway::get_instance();
266 }
267
268 $this->save_eu_vat_details($payment_method->id, $order_id);
269
270 if ($is_free_checkout) {
271 OrderFactory::fromId($order_id)->complete_order();
272 SubscriptionFactory::fromId($subscription_id)->activate_subscription();
273
274 $process_payment = (new CheckoutResponse())->set_is_success(true);
275
276 } else {
277
278 /** @var CheckoutResponse $process_payment */
279 $process_payment = $payment_method->process_payment(
280 $order_id,
281 $subscription_id,
282 $customer_id
283 );
284 }
285
286 $order = OrderFactory::fromId($order_id);
287
288 wp_send_json([
289 'success' => $process_payment->is_success,
290 'redirect_url' => $process_payment->redirect_url,
291 'gateway_response' => $process_payment->gateway_response,
292 'error_message' => $this->alert_message($process_payment->error_message),
293 'order_success_url' => add_query_arg(['payment_method' => $order->payment_method], ppress_get_success_url($order->order_key)),
294 ]);
295
296 } catch (\Exception $e) {
297
298 $error_message = ppress_is_json($e->getMessage()) ? json_decode($e->getMessage(), true) : $e->getMessage();
299
300 ppress_log_error($error_message);
301
302 wp_send_json_error(
303 $this->alert_message($error_message)
304 );
305 }
306 }
307
308 /**
309 * @param $country_code
310 * @param $country_state_code
311 * @param $vat_number
312 * @param PlanEntity $planObj
313 *
314 * @return float|int|string
315 * @throws \Exception
316 */
317 private function get_checkout_tax_rate($country_code, $country_state_code, $vat_number, $planObj)
318 {
319 if ( ! TaxService::init()->is_tax_enabled()) return 0;
320
321 if (TaxService::init()->calculate_tax_based_on_setting() == 'base') {
322 $base_country = ppress_business_country();
323 if ( ! empty($base_country)) {
324 $country_code = $base_country;
325 $country_state_code = ppress_business_state();
326 }
327 }
328
329 $tax_rate = TaxService::init()->get_country_tax_rate($country_code, $country_state_code);
330
331 if (TaxService::init()->is_eu_vat_enabled() && TaxService::init()->is_eu_countries($country_code)) {
332
333 $business_country = ppress_business_country();
334 $same_country_rule_setting = TaxService::init()->eu_vat_same_country_rule_setting();
335
336 if ($business_country == $country_code && $same_country_rule_setting == 'charge_always') {
337 return $tax_rate;
338 }
339
340 if ($business_country == $country_code && $same_country_rule_setting == 'no_charge') {
341 return 0;
342 }
343
344 if (empty($vat_number)) return $tax_rate;
345
346 $session_data = [
347 'plan_id' => $planObj->id,
348 'vat_number' => $vat_number,
349 'country_code' => $country_code
350 ];
351
352 if (TaxService::init()->is_vat_number_validation_active()) {
353
354 $response = EuVatApi::check_vat($vat_number, $country_code);
355
356 if ( ! $response->is_valid()) {
357 throw new \Exception($response->get_error_message(), $response->error);
358 }
359
360 $session_data['company_name'] = $response->name;
361 $session_data['company_address'] = $response->address;
362 $session_data['is_valid'] = $response->is_valid();
363 }
364
365 $session_data['reverse_charged'] = true;
366
367 ppress_session()->set(CheckoutSessionData::EU_VAT_NUMBER, $session_data);
368
369 $tax_rate = 0;
370 }
371
372 return $tax_rate;
373 }
374
375 public function update_order_review()
376 {
377 check_ajax_referer('ppress_process_checkout', 'csrf');
378
379 try {
380
381 if (empty($_POST['plan_id'])) {
382
383 throw new \Exception(
384 esc_html__('Please enter a plan ID.', 'wp-user-avatar')
385 );
386 }
387
388 $planObj = ppress_get_plan(absint($_POST['plan_id']));
389
390 $country_code = sanitize_text_field(ppressPOST_var('country', '', true));
391 $country_state_code = sanitize_text_field(ppressPOST_var('state', '', true));
392 $vat_number = sanitize_text_field(ppressPOST_var('vat_number', '', true));
393
394 $tax_rate = $this->get_checkout_tax_rate($country_code, $country_state_code, $vat_number, $planObj);
395
396 ppress_session()->set(CheckoutSessionData::TAX_RATE, [
397 'plan_id' => $planObj->id,
398 'tax_rate' => $tax_rate,
399 'country' => $country_code,
400 'state' => $country_state_code
401 ]);
402
403 $cart_vars = OrderService::init()->checkout_order_calculation([
404 'plan_id' => $planObj->id,
405 'coupon_code' => CheckoutSessionData::get_coupon_code($planObj->id),
406 'tax_rate' => CheckoutSessionData::get_tax_rate($planObj->id)
407 ]);
408
409 ob_start();
410 ppress_render_view(
411 'checkout/form-checkout-sidebar', [
412 'plan' => $planObj,
413 'cart_vars' => $cart_vars
414 ]
415 );
416 $checkout_sidebar_html = ob_get_clean();
417
418 ob_start();
419 ppress_render_view('checkout/form-payment-methods', [
420 'plan' => $planObj,
421 'cart_vars' => $cart_vars
422 ]);
423 $checkout_payment_methods_html = ob_get_clean();
424
425 ob_start();
426 ppress_render_view('checkout/form-checkout-submit-btn', ['order_total' => $cart_vars->total, 'plan' => $planObj]);
427 $checkout_submit_btn = ob_get_clean();
428
429 wp_send_json_success(
430 apply_filters('ppress_update_order_review_response', [
431 'fragments' => apply_filters('ppress_update_order_review_fragments', [
432 '.ppress-checkout_order_summary-wrap' => $checkout_sidebar_html,
433 '.ppress-checkout_payment_methods-wrap' => $checkout_payment_methods_html,
434 '.ppress-checkout-submit' => $checkout_submit_btn
435 ])
436 ], $cart_vars, $planObj)
437 );
438
439 } catch (\Exception $e) {
440
441 wp_send_json_error(
442 $this->alert_message($e->getMessage())
443 );
444 }
445 }
446 }