PluginProbe
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.4.2
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.4.2
4.7.2 4.7.1 4.7.0 4.6.6 4.6.5 4.6.4 4.6.3 4.6.2 4.6.1 4.6.0 4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 All 281 releases
surecart / app / src / Controllers / Rest / CheckoutsController.php
CheckoutsController.php
400 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SureCart\Controllers\Rest;
4
5 use SureCart\Models\Checkout;
6 use SureCart\Models\Form;
7 use SureCart\Models\User;
8 use SureCart\WordPress\Users\CustomerLinkService;
9 use SureCart\WordPress\RecaptchaValidationService;
10
11 /**
12 * Handle price requests through the REST API
13 */
14 class CheckoutsController extends RestController {
15 /**
16 * Class to make the requests.
17 *
18 * @var string
19 */
20 protected $class = Checkout::class;
21
22 /**
23 * Middleware before we make the request.
24 *
25 * @param \SureCart\Models\Model $class Model class instance.
26 * @param \WP_REST_Request $request Request object.
27 *
28 * @return \SureCart\Models\Model|\WP_Error
29 */
30 protected function middleware( $class, \WP_REST_Request $request ) {
31 // Set the return url.
32 $request->set_param( 'external_url', esc_url_raw( get_home_url( null, 'surecart/redirect' ) ) );
33
34 // if this is an open invoice, we don't set the user.
35 if ( 'open_invoice' === $request->get_param( 'type' ) ) {
36 return apply_filters( 'surecart/request/model', $class, $request );
37 }
38
39 $request->set_param(
40 'metadata',
41 array_merge(
42 $request->get_param( 'metadata' ) ?? [],
43 [
44 // this must always be set to ensure the user cannot override the user role.
45 'wp_user_role' => is_user_logged_in() ? wp_get_current_user()->roles : null,
46 ]
47 )
48 );
49
50 // set the user.
51 $class = $this->maybeSetUser( $class, $request );
52
53 // return the class.
54 return apply_filters( 'surecart/request/model', $class, $request );
55 }
56
57 /**
58 * Edit model.
59 *
60 * @param \WP_REST_Request $request Rest Request.
61 *
62 * @return \WP_REST_Response|\WP_Error
63 */
64 public function edit( \WP_REST_Request $request ) {
65 // if we have a password, hash it and set it in a transient.
66 // we need to do this because some processors will redirect and we will lose this form data.
67 if ( ! empty( $request->get_param( 'password' ) ) ) {
68 set_transient( 'sc_checkout_password_hash_' . $request['id'], wp_hash_password( $request->get_param( 'password' ) ), DAY_IN_SECONDS );
69 }
70
71 // edit the checkout.
72 $response = parent::edit( $request );
73
74 // check if the email exists and set on record.
75 if ( apply_filters( 'surecart/checkout/finduser', true ) ) {
76 if ( ! empty( $response->email ) ) {
77 $response->email_exists = (bool) email_exists( $response->email );
78 }
79 }
80
81 return $response;
82 }
83
84 /**
85 * Let's set the customer's email and name if they are already logged in.
86 *
87 * @param \SureCart\Models\Model $class Model class instance.
88 * @param \WP_REST_Request $request Request object.
89 *
90 * @return \SureCart\Models\Model|\WP_Error
91 */
92 protected function maybeSetUser( \SureCart\Models\Model $class, \WP_REST_Request $request ) {
93 // get current user.
94 $user = User::current();
95
96 // must be logged in.
97 if ( ! $user ) {
98 return $class;
99 }
100
101 // set the email.
102 $class['email'] = $user->user_email;
103
104 // force the customer id, if it exists.
105 $customer_id = $user->customerId( ! empty( $request['live_mode'] ) ? 'live' : 'test' );
106 if ( ! empty( $customer_id ) ) {
107 $class['customer'] = $customer_id;
108 }
109
110 // if this is a new session, populate the name and phone from the user data.
111 if ( $request->get_method() === 'POST' ) {
112 $class['name'] = $user->display_name;
113 $class['first_name'] = $user->first_name;
114 $class['last_name'] = $user->last_name;
115 $class['phone'] = $user->phone;
116 }
117
118 return $class;
119 }
120
121 /**
122 * Get the form mode
123 *
124 * @param integer $id ID of the form.
125 * @return string Mode of the form.
126 */
127 protected function getFormMode( $id ) {
128 return Form::getMode( (int) $id );
129 }
130
131 /**
132 * Manually pay an order.
133 *
134 * @param \WP_REST_Request $request Rest Request.
135 *
136 * @return \SureCart\Models\Checkout|\WP_Error
137 */
138 public function manuallyPay( \WP_REST_Request $request ) {
139 $checkout = $this->middleware( new $this->class( $request['id'] ), $request );
140 if ( is_wp_error( $checkout ) ) {
141 return $checkout;
142 }
143
144 if ( ! empty( $this->with ) ) {
145 $checkout = $checkout->with( $this->with );
146 }
147
148 $paid = $checkout->where( $request->get_query_params() )->with(
149 [
150 'purchases', // Important: we need to make sure we expand the purchase to provide access.
151 ]
152 )->manuallyPay();
153
154 // purchase created.
155 if ( ! empty( $paid->purchases->data ) ) {
156 foreach ( $paid->purchases->data as $purchase ) {
157 if ( empty( $purchase->revoked ) ) {
158 // broadcast the webhook.
159 do_action( 'surecart/purchase_created', $purchase );
160 }
161 }
162 }
163 return $paid;
164 }
165
166 /**
167 * Finalize an order.
168 *
169 * @param \WP_REST_Request $request Rest Request.
170 *
171 * @return \SureCart\Models\Checkout|\WP_Error
172 */
173 public function finalize( \WP_REST_Request $request ) {
174 $args = $request->get_params();
175
176 // validate form fields and password input.
177 $errors = $this->validate( $args, $request );
178
179 // return early if errors.
180 if ( $errors->has_errors() ) {
181 return $errors;
182 }
183
184 // finalize the order.
185 $checkout = new $this->class( [ 'id' => $request['id'] ] );
186 $finalized = $checkout->where( $request->get_query_params() )
187 ->finalize( $request->get_body_params() );
188
189 // bail if error.
190 if ( is_wp_error( $finalized ) ) {
191 return $finalized;
192 }
193
194 // return the order.
195 return $finalized;
196 }
197
198 /**
199 * Confirm an order.
200 *
201 * This force-fetches the order from the API, runs any automations
202 * and creates the user account tied to the customer.
203 *
204 * @param \WP_REST_Request $request Rest Request.
205 *
206 * @return \SureCart\Models\Checkout|\WP_Error
207 */
208 public function confirm( \WP_REST_Request $request ) {
209 $checkout = $this->middleware( new $this->class(), $request );
210 if ( is_wp_error( $checkout ) ) {
211 return $checkout;
212 }
213
214 $checkout = $checkout->where(
215 array_merge(
216 $request->get_query_params(),
217 [ 'refresh_status' => true ] // Important: Do not remove. This will force syncing with the processor.
218 )
219 )->with(
220 [
221 'purchases', // Important: we need to make sure we expand the purchase to provide access.
222 'customer', // Important: we need to use this to create the WP User with the same info.
223 'manual_payment_method', // Important: we need to use this to display manual payment instructions.
224 ]
225 )->find( $request['id'] );
226
227 // bail if error.
228 if ( is_wp_error( $checkout ) ) {
229 return $checkout;
230 }
231
232 // Create a user account for the customer.
233 $linked = $this->linkCustomerId( $checkout );
234 if ( is_wp_error( $linked ) ) {
235 return $linked;
236 }
237
238 // purchase created.
239 if ( ! empty( $checkout->purchases->data ) ) {
240 foreach ( $checkout->purchases->data as $purchase ) {
241 if ( empty( $purchase->revoked ) ) {
242 // broadcast the webhook.
243 do_action( 'surecart/purchase_created', $purchase );
244 }
245 }
246 }
247
248 // the order is confirmed.
249 do_action( 'surecart/checkout_confirmed', $checkout, $request );
250
251 // return the order.
252 return $checkout;
253 }
254
255 /**
256 * Link the customer id to the order.
257 *
258 * @param \SureCart\Models\Checkout $checkout Checkout model.
259 * @return \WP_User|\WP_Error
260 */
261 public function linkCustomerId( $checkout ) {
262 // get transient.
263 $password_hash = get_transient( 'sc_checkout_password_hash_' . $checkout->id );
264 // delete transient.
265 delete_transient( 'sc_checkout_password_hash_' . $checkout->id );
266 // link customer.
267 $service = new CustomerLinkService( $checkout, $password_hash );
268 return $service->link();
269 }
270
271 /**
272 * Validate the form.
273 *
274 * @param array $args Arguments.
275 * @param object $request Request.
276 * @return \WP_Error Errors.
277 */
278 public function validate( $args, $request ) {
279 $errors = new \WP_Error();
280
281 // Check if honeypot checkbox checked or not.
282 $metadata = $request->get_param( 'metadata' );
283 if ( $metadata && ! empty( $metadata['get_feedback'] ) ) {
284 $errors->add( 'invalid', __( 'Spam check failed. Please try again.', 'surecart' ) );
285 }
286
287 // check recaptcha.
288 $service = new RecaptchaValidationService();
289 if ( $service->isEnabled() ) {
290 $recaptcha = $service->validate( $request->get_param( 'grecaptcha' ) );
291 if ( is_wp_error( $recaptcha ) ) {
292 $errors->add( $recaptcha->get_error_code(), $recaptcha->get_error_message() );
293 }
294 }
295
296 return apply_filters( 'surecart/checkout/validate', $errors, $args, $request );
297 }
298
299 /**
300 * Cancel an checkout
301 *
302 * @param \WP_REST_Request $request Rest Request.
303 *
304 * @return \SureCart\Models\Checkout|\WP_Error
305 */
306 public function cancel( \WP_REST_Request $request ) {
307 $order = $this->middleware( new $this->class( $request['id'] ), $request );
308 if ( is_wp_error( $order ) ) {
309 return $order;
310 }
311 return $order->where( $request->get_query_params() )->cancel();
312 }
313
314 /**
315 * Offer the bump (used for analytics).
316 *
317 * @param \WP_REST_Request $request Rest Request.
318 *
319 * @return \SureCart\Models\Checkout|\WP_Error
320 */
321 public function offerBump( \WP_REST_Request $request ) {
322 $order = $this->middleware( new $this->class( $request['id'] ), $request );
323 if ( is_wp_error( $order ) ) {
324 return $order;
325 }
326 return $order->where( $request->get_query_params() )->offerBump( $request['bump_id'] );
327 }
328
329 /**
330 * Offer the bump (used for analytics).
331 *
332 * @param \WP_REST_Request $request Rest Request.
333 *
334 * @return \SureCart\Models\Checkout|\WP_Error
335 */
336 public function offerUpsell( \WP_REST_Request $request ) {
337 $order = $this->middleware( new $this->class( $request['id'] ), $request );
338 if ( is_wp_error( $order ) ) {
339 return $order;
340 }
341 return $order->where( $request->get_query_params() )->offerUpsell( $request['upsell_id'] );
342 }
343
344 /**
345 * Offer the bump (used for analytics).
346 *
347 * @param \WP_REST_Request $request Rest Request.
348 *
349 * @return \SureCart\Models\Checkout|\WP_Error
350 */
351 public function declineUpsell( \WP_REST_Request $request ) {
352 $order = $this->middleware( new $this->class( $request['id'] ), $request );
353 if ( is_wp_error( $order ) ) {
354 return $order;
355 }
356 return $order->where( $request->get_query_params() )->declineUpsell( $request['upsell_id'] );
357 }
358
359 /**
360 * Check if the user is trying to sign in.
361 * If so, validate credentials before finalizing.
362 *
363 * @param string $email Email.
364 * @param string $password Password.
365 *
366 * @return true|\WP_Error
367 */
368 public function maybeValidateLoginCreds( $email = '', $password = '' ) {
369 // check if the person is signing in using a password and sign them in.
370 if ( $password && $email ) {
371 // user exists, try signing in with password.
372 $user = get_user_by( 'email', $email );
373 // if there's a user, check the username and password before we submit the order.
374 if ( false !== $user ) {
375 return wp_authenticate_username_password( null, $user->user_login, $password );
376 }
377 }
378 return true;
379 }
380
381 /**
382 * Create or login the user.
383 *
384 * @param string $user_email Username.
385 * @param string $password User password.
386 * @return \WP_Error|true
387 */
388 protected function maybeLoginUser( $user_email, $password = '' ) {
389 if ( empty( $password ) ) {
390 return;
391 }
392 return wp_signon(
393 [
394 'user_login' => $user_email,
395 'user_password' => $password,
396 ]
397 );
398 }
399 }
400