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