PluginProbe
MakeCommerce for WooCommerce / trunk
MakeCommerce for WooCommerce vtrunk
4.1.0 4.0.8 trunk 1.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 2.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.0 2.2.1 2.2.2 All 96 releases
makecommerce / payment / payment.php

payment.php in MakeCommerce for WooCommerce trunk, at payment/payment.php

406 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The payment functionality of the plugin.
5 *
6 * @link https://makecommerce.net/
7 * @since 3.0.0
8 *
9 * @package Makecommerce
10 * @subpackage Makecommerce/admin
11 */
12
13 namespace MakeCommerce;
14
15 use Automattic\WooCommerce\Utilities\OrderUtil;
16 /**
17 * The payment functionality of the plugin.
18 *
19 * Defines the plugin name, version, and two examples hooks for how to
20 * enqueue the admin-specific stylesheet and JavaScript.
21 *
22 * @package Makecommerce
23 * @subpackage Makecommerce/payment
24 * @author Maksekeskus AS <support@maksekeskus.ee>
25 */
26 class Payment {
27
28 /**
29 * The ID of this plugin.
30 *
31 * @since 3.0.0
32 * @access private
33 * @var string $plugin_name The ID of this plugin.
34 */
35 private $plugin_name;
36
37 /**
38 * The version of this plugin.
39 *
40 * @since 3.0.0
41 * @access private
42 * @var string $version The current version of this plugin.
43 */
44 private $version;
45
46 private Loader $loader;
47
48 /**
49 * Initialize the class and set its properties.
50 *
51 * @since 3.0.0
52 * @param string $plugin_name The name of this plugin.
53 * @param string $version The version of this plugin.
54 */
55 public function __construct( $plugin_name, $version, Loader $loader ) {
56
57 $this->plugin_name = $plugin_name;
58 $this->version = $version;
59 $this->loader = $loader;
60
61 $this->loader->add_action( 'plugins_loaded', $this, 'initialize_payment_gateways' );
62 $this->loader->add_action( 'woocommerce_before_checkout_form', $this, 'check_payment_status' );
63 }
64
65 /**
66 * Initialize all payment gateways
67 *
68 * @since 3.0.0
69 */
70 public function initialize_payment_gateways() {
71 //WooCommerce
72 new Payment\Gateway\WooCommerce( true );
73 }
74
75 /**
76 * Returns price excluding taxes
77 *
78 * @since 3.0.0
79 */
80 public static function get_rate_without_taxes( $method, $rate ) {
81
82 if ( empty( $rate[$method] ) ) {
83 return 0;
84 }
85
86 $rate = $rate[$method];
87
88 return $rate->cost;
89 }
90
91 /**
92 * Returns price including taxes
93 *
94 * @since 3.0.0
95 */
96 public static function get_rate_with_taxes( $method, $rate ) {
97
98 if ( empty( $rate[$method] ) ) {
99 return 0;
100 }
101
102 $rate = $rate[$method];
103 $price = $rate->cost;
104
105 if ( empty( $rate->taxes ) ) {
106 return $price;
107 }
108
109 foreach ( $rate->taxes as $tax ) {
110 $price += $tax;
111 }
112
113 return $price;
114 }
115
116 /**
117 * Update banklinks
118 *
119 * @since 3.0.0
120 */
121 public static function update_banklinks() {
122
123 $obj = new Payment\Gateway\WooCommerce( true );
124
125 $obj->mc_banklinks_reload( true );
126
127 }
128
129 /**
130 * Add check for unsuccesful payment redirects
131 * Displays notice if something went wrong
132 *
133 * @since 3.0.12
134 */
135 public function check_payment_status()
136 {
137 if ( isset ( $_GET['mc_payment_status'] ) ) {
138 switch( $_GET['mc_payment_status'] ) {
139 case 'failed':
140 wc_add_notice( __( 'Payment failed', 'wc_makecommerce_domain' ), 'error' );
141 break;
142 case 'cancelled':
143 wc_add_notice( __( 'Payment transaction cancelled', 'wc_makecommerce_domain' ), 'error' );
144 break;
145 case 'expired':
146 wc_add_notice( __( 'Payment transaction expired', 'wc_makecommerce_domain' ), 'error' );
147 break;
148 }
149
150 // Avoid multiple error messages
151 unset($_GET['mc_payment_status']);
152 }
153 }
154
155 /**
156 * Check payment and process order if completed, canceled
157 *
158 * @since 3.0.0
159 */
160 public static function check_payment( $settings = array() ) {
161
162 global $woocommerce;
163
164 $api = \MakeCommerce::get_api();
165
166 //set the correct language
167 if ( isset( $_GET["lang1"] ) ) {
168 \MakeCommerce\i18n::switch_language( $_GET["lang1"] );
169 }
170
171 $returnUrl = home_url();
172
173 $request = stripslashes_deep( $_POST );
174
175 if ( $api->verifyMac( $request ) ) {
176
177 $data = $api->extractRequestData( $request );
178 $transactionId = false;
179 $reference = false;
180
181 if ( !empty( $data['error'] ) ) {
182
183 //add status and send client back to checkout
184 return add_query_arg( 'mc_payment_status', 'failed', wc_get_checkout_url() );
185 }
186
187 if ( !empty( $data['message_type'] ) ) {
188
189 if ( $data['message_type'] === 'payment_return' ) {
190
191 $transactionId = $data['transaction'];
192 $reference = $data['reference'];
193 $paymentStatus = $data['status'];
194 $totalAmount = $data['amount'];
195 }
196
197 if ( $data['message_type'] === 'token_return' ) {
198 if ( !empty( $data['transaction']['reference'] ) ) {
199 $reference = $data['transaction']['reference'];
200 }
201
202 $paymentStatus = $data['transaction']['status'];
203 $transactionId = $data['transaction']['id'];
204 }
205 }
206 }
207
208 // if we didn't find reference to an Order, we send user to shop landing-page. TODO: could be improved
209 if ( empty( $transactionId ) ) {
210 return $returnUrl;
211 }
212
213 //get the post id using transactionid
214 $orderId = self::get_postid_using_metakey( '_makecommerce_transaction_id', $transactionId );
215
216 $order = wc_get_order( $orderId );
217
218 // if we didn't find the Order, we send user to shop landing-page. TODO: could be improved
219 if ( !$order || !$order->get_id() ) {
220 return $returnUrl;
221 }
222
223 $returnUrl = $order->get_meta( '_makecommerce_order_received_url', true );
224 if ( !$returnUrl ) {
225 $returnUrl = $order->get_checkout_order_received_url();
226 }
227
228 // Disregard with subscription method change as amounts do not match (0 vs order total)
229 // Only check sums if $totalAmount is set
230 // Trx total and order total did not match
231 if ( $order->get_status() !== 'active' && isset( $totalAmount ) && $totalAmount != $order->get_total() ) {
232
233 if ( $paymentStatus == 'COMPLETED' ) {
234 $notes[] = sprintf('%s: %s.',
235 __( 'Order total and transaction amount did not match! Please contact us at', 'wc_makecommerce_domain' ),
236 __( 'support@maksekeskus.ee', 'wc_makecommerce_domain' )
237 );
238 $notes[] = sprintf('%s: %s.',
239 __( 'Transaction ID that was assigned to this order', 'wc_makecommerce_domain' ),
240 '<a target=_blank href="'.$api->getEnvUrls()->merchantUrl.'merchant/shop/deals/detail.html?id='. $transactionId .'">'.$transactionId.'</a>'
241 );
242 $notes[] = sprintf('%s: %s€',
243 __( 'Amount that was paid', 'wc_makecommerce_domain' ),
244 $totalAmount
245 );
246
247 $order->add_order_note( implode( "\r\n", $notes ) );
248
249 // Empty the cart so the customer is not tempted to pay again
250 $woocommerce->cart->empty_cart();
251
252 return $returnUrl;
253 }
254
255 // If the payment was not complete then send the customer back to checkout
256 return wc_get_checkout_url();
257 }
258
259 if ( $paymentStatus == 'EXPIRED' || $paymentStatus == 'CANCELLED' || $paymentStatus == 'COMPLETED' ) {
260
261 //get transaction to update the payment method.
262 $transaction = $api->getTransaction( $transactionId );
263
264 //make sure the payment method is the same for simplecheckout and normal version.
265 $paymentMethod = "";
266 if ( isset( $transaction->type ) && isset( $transaction->method ) ) {
267 $paymentMethod = $transaction->channel;
268 }
269
270 //update _makecommerce_preselected_method
271 $order->update_meta_data( '_makecommerce_preselected_method', $paymentMethod );
272 $order->save();
273 }
274
275 if ( $paymentStatus == 'CANCELLED' || ( $paymentStatus == 'EXPIRED' && $order->get_status() == "pending" ) ) {
276 //send client back to checkout
277 $returnUrl = wc_get_checkout_url();
278 }
279
280 // Avoid duplicate method changes for subscriptions
281 $check_status = true;
282
283 if ( !empty( $data['token']['id'] ) && !empty( $data['token']['multiuse'] ) ) {
284 // Order is a subscription and saved token is different from response token
285 if ( $order->get_meta( '_makecommerce_payment_token', true) !== $data['token']['id'] ) {
286 $check_status = false;
287 }
288 }
289
290 // Lock processing for this order+status to avoid race conditions between callback and redirect
291 $lock_key = '_makecommerce_payment_lock_status_' . $paymentStatus;
292 $locked = add_post_meta( $orderId, $lock_key, time(), true );
293 if ( ! $locked ) {
294 return $returnUrl;
295 }
296
297 //check if we already processed this status in the past.
298 if ( $check_status && $order->get_meta( '_makecommerce_payment_processed_status', true ) == $paymentStatus ) {
299 return $returnUrl;
300 }
301
302 if ( $paymentStatus === 'COMPLETED' ) {
303 $order->update_meta_data( '_makecommerce_payment_processed_status', $paymentStatus );
304 $order->save();
305 }
306
307 switch( $paymentStatus ) {
308
309 case 'CANCELLED':
310 // Do not update status when disabled in settings
311 if ( ( $settings['disable_cancelled_payment_update'] ?? '' ) !== 'yes' ) {
312 // Update automatically
313 $order->update_status( 'cancelled' );
314 $order->update_meta_data( '_makecommerce_payment_processed_status', $paymentStatus );
315 }
316
317 $returnUrl = add_query_arg( 'mc_payment_status', 'cancelled', $returnUrl );
318
319 break;
320 case 'EXPIRED':
321 //only update if order status is pending payment
322 if ( $order->get_status() == "pending" ) {
323
324 // Do not update order status if disabled in settings
325 if ( ( $settings['disable_expired_payment_update'] ?? '' ) !== 'yes' ) {
326 // Update automatically
327 $order->update_status( 'cancelled' );
328 $order->update_meta_data( '_makecommerce_payment_processed_status', $paymentStatus );
329 }
330
331 $returnUrl = add_query_arg( 'mc_payment_status', 'expired', $returnUrl );
332 }
333
334 break;
335 case 'COMPLETED':
336 $orderNote = array();
337 $transactionIdText = __( 'Transaction ID', 'wc_makecommerce_domain' );
338 $paymentOptionText = __( 'Payment option', 'wc_makecommerce_domain' );
339 $transactionIdText = \MakeCommerce\i18n::get_string_from_mo( 'Transaction ID', 'wc_makecommerce_domain', \MakeCommerce\i18n::get_site_default_language() );
340 $paymentOptionText = \MakeCommerce\i18n::get_string_from_mo( 'Payment option', 'wc_makecommerce_domain', \MakeCommerce\i18n::get_site_default_language() );
341
342 $orderNote[] = $transactionIdText . ': <a target=_blank href="'.$api->getEnvUrls()->merchantUrl.'merchant/shop/deals/detail.html?id='. $transactionId .'">'.$transactionId.'</a>';
343 $orderNote[] = $paymentOptionText . ': ' . $order->get_meta( '_makecommerce_preselected_method', true );
344 $order->add_order_note( implode( "\r\n", $orderNote ) );
345
346 if ( !empty( $data['token'] ) && !empty( $data['token']['multiuse'] ) ) {
347 $order->update_meta_data( '_makecommerce_payment_token', $data['token']['id'] );
348 $order->update_meta_data( '_makecommerce_payment_token_valid_until', $data['token']['valid_until'] );
349 }
350
351 if ( isset( $_GET['lang1'] ) ) {
352 $order->update_meta_data( 'wpml_language', $_GET["lang1"] );
353 }
354
355 $order->payment_complete( $transactionId );
356 $woocommerce->cart->empty_cart();
357
358 break;
359 }
360
361 // Save all changes to order
362 $order->save();
363
364 return $returnUrl;
365 }
366
367 /**
368 * Returns post_id using transaction id or false if not found
369 *
370 * @since 3.0.4
371 */
372 public static function get_postid_using_metakey( $meta_key, $transactionId ) {
373 global $wpdb;
374
375 if ( class_exists( OrderUtil::class )
376 && OrderUtil::custom_orders_table_usage_is_enabled()
377 ) {
378 // HPOS
379 $results = $wpdb->get_results(
380 $wpdb->prepare("
381 SELECT {$wpdb->prefix}wc_orders.id
382 FROM {$wpdb->prefix}wc_orders
383 INNER JOIN {$wpdb->prefix}wc_orders_meta AS meta ON meta.order_id = {$wpdb->prefix}wc_orders.id AND meta.meta_key = %s
384 WHERE meta.meta_value = %s
385 ",
386 $meta_key,
387 $transactionId
388 )
389 );
390
391 if ( isset( $results[0]->id ) ) {
392 return $results[0]->id;
393 }
394 } else {
395 // Legacy DB
396 $wpdb->query("SELECT `post_id` FROM $wpdb->postmeta WHERE `meta_key` = '". $meta_key ."' AND `meta_value` = '" . $transactionId . "'");
397
398 if ( isset( $wpdb->last_result[0]->post_id ) ) {
399 return $wpdb->last_result[0]->post_id;
400 }
401 }
402
403 return false;
404 }
405 }
406