| @@ -12,8 +12,9 @@ | ||
| 12 | 12 | use Payplug\Resource\Payment as PaymentResource; |
| 13 | 13 | use Payplug\Resource\Refund as RefundResource; |
| 14 | 14 | use WC_Payment_Tokens; |
| 15 | 15 | use WC_Payment_Token_CC; |
| 16 | +use Automattic\WooCommerce\Utilities\OrderUtil; | |
| 16 | 17 | |
| 17 | 18 | /** |
| 18 | 19 | * Process responses from PayPlug. |
| 19 | 20 | * |
| @@ -35,85 +36,209 @@ | ||
| 35 | 36 | * |
| 36 | 37 | * @return void |
| 37 | 38 | * @throws \WC_Data_Exception |
| 38 | 39 | */ |
| 39 | - public function process_payment( $resource, $is_payment_with_token = false ) { | |
| 40 | - $order_id = wc_clean( $resource->metadata['order_id'] ); | |
| 41 | - $order = wc_get_order( $order_id ); | |
| 42 | - if ( ! $order ) { | |
| 43 | - PayplugGateway::log( sprintf( 'Coudn\'t find order #%s (Transaction %s).', $order_id, wc_clean( $resource->id ) ), 'error' ); | |
| 40 | + public function process_payment($resource, $is_payment_with_token = false, $source = null) | |
| 41 | + { | |
| 42 | + $order_id = wc_clean($resource->metadata['order_id']); | |
| 44 | 43 | |
| 44 | + $order = wc_get_order($order_id); | |
| 45 | + $gateway_id = $order->get_payment_method(); | |
| 46 | + $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($resource); | |
| 47 | + | |
| 48 | + // Ignore undefined orders | |
| 49 | + if (!$order) { | |
| 50 | + PayplugGateway::log(sprintf('Coudn\'t find order #%s (Transaction %s).', $order_id, wc_clean($resource->id)), 'error'); | |
| 45 | 51 | return; |
| 46 | 52 | } |
| 47 | 53 | |
| 48 | - PayplugGateway::log( sprintf( 'Order #%s : Begin processing payment IPN %s', $order_id, $resource->id ) ); | |
| 54 | + /** | |
| 55 | + * | |
| 56 | + * Checking if it is coming from the order confirmation page or the IPN | |
| 57 | + * | |
| 58 | + */ | |
| 49 | 59 | |
| 50 | - // Ignore paid orders | |
| 51 | - if ( $order->is_paid() ) { | |
| 52 | - PayplugGateway::log( sprintf( 'Order #%s : Order is already complete. Ignoring IPN.', $order_id ) ); | |
| 60 | + if (($gateway_id == $this->gateway->id) || (!empty($source) && ($source === "ipn"))) { | |
| 53 | 61 | |
| 54 | - return; | |
| 55 | - } | |
| 62 | + // Ignore cancelled orders | |
| 63 | + if ($order->has_status('refunded')) { | |
| 64 | + PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' order has been refunded. Ignoring IPN', $order_id)); | |
| 65 | + //$lock->deleteLock($resource->id); | |
| 66 | + return; | |
| 67 | + } | |
| 68 | + $paid_date = $order->get_date_paid(); | |
| 69 | + if( isset($paid_date) && !$order->is_paid()){ | |
| 70 | + $finished_status = wc_get_is_paid_statuses(); | |
| 71 | + $order->set_status($finished_status[0]); | |
| 72 | + } | |
| 56 | 73 | |
| 57 | - // Ignore cancelled orders | |
| 58 | - if ( $order->has_status( 'refunded' ) ) { | |
| 59 | - PayplugGateway::log( sprintf( 'Order #%s : Order has been refunded. Ignoring IPN', $order_id ) ); | |
| 74 | + // Ignore paid orders | |
| 75 | + if ( $order->is_paid() ) { | |
| 76 | + PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' order is already complete. Ignoring IPN. "' . !empty($source) ? '[IPN]' : ''. '"', $order_id)); | |
| 77 | + //$lock->deleteLock($resource->id); | |
| 78 | + return; | |
| 79 | + } | |
| 60 | 80 | |
| 61 | - return; | |
| 62 | - } | |
| 81 | + // Handle failed payments | |
| 82 | + if (!empty($resource->failure)) { | |
| 83 | + PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true); | |
| 84 | + $order->update_status( | |
| 85 | + 'failed', | |
| 86 | + sprintf(__('PayPlug IPN OK | Transaction %s failed : %s', 'payplug'), $resource->id, wc_clean($resource->failure->message)) | |
| 87 | + ); | |
| 88 | + /** This action is documented in src/Gateway/PayplugResponse */ | |
| 89 | + \do_action('payplug_gateway_payment_response_processed', $order_id, $resource); | |
| 90 | + PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false); | |
| 91 | + PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' payment IPN %s processing completed but failed.', $order_id, $resource->id)); | |
| 92 | + wc_increase_stock_levels($order); | |
| 63 | 93 | |
| 64 | - $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $resource ); | |
| 65 | - $order_metadata = $order->get_meta('_payplug_metadata', true); | |
| 94 | + //$lock->deleteLock($resource->id); | |
| 95 | + return; | |
| 96 | + } | |
| 66 | 97 | |
| 67 | - if (is_array($order_metadata) && $order_metadata['transaction_in_progress']) { | |
| 68 | - PayplugGateway::log(sprintf('Order #%s : Order Oney IPN already in progress. Ignoring IPN', $order_id)); | |
| 69 | - return; | |
| 70 | - } | |
| 98 | + elseif (( $resource->failure == null ) && !empty($resource->payment_method['is_pending']) | |
| 99 | + && ( $resource->payment_method['is_pending'] == true )) { | |
| 100 | + $this->handle_pending_oney( $order, $resource ); | |
| 101 | + } | |
| 71 | 102 | |
| 72 | - if ( $resource->is_paid ) { | |
| 73 | - PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true); | |
| 74 | - if ( ! $is_payment_with_token ) { | |
| 75 | - $this->maybe_save_card( $resource ); | |
| 76 | - } | |
| 103 | + // Save Logs of the payment for the different payment gateways: | |
| 104 | + // For Oney 4 gateways & Bancontact gateway: "$resource->payment_method" exists and is an array | |
| 105 | + // but not for Payplug credit card gateway (in this case we check the payment_method from the order itself not the $resource) | |
| 106 | + if (isset($resource->payment_method) && is_array($resource->payment_method)) { | |
| 107 | + $gateway_id = $resource->payment_method['type']; | |
| 77 | 108 | |
| 78 | - $this->maybe_save_address_hash( $resource ); | |
| 109 | + switch ($gateway_id) { | |
| 110 | + case substr( $gateway_id, 0, 5 ) === "oney_" : | |
| 111 | + $this->oney_ipn($resource); | |
| 112 | + break; | |
| 113 | + case "bancontact" : | |
| 114 | + $this->bancontact_ipn($resource); | |
| 115 | + break; | |
| 116 | + case "apple_pay" : | |
| 117 | + $this->apple_pay_ipn($resource); | |
| 118 | + break; | |
| 119 | + case "american_express" : | |
| 120 | + $this->amex_ipn($resource); | |
| 121 | + break; | |
| 122 | + } | |
| 79 | 123 | |
| 80 | - $order->add_order_note( sprintf( __( 'PayPlug IPN OK | Transaction %s', 'payplug' ), wc_clean( $resource->id ) ) ); | |
| 81 | - $order->payment_complete( wc_clean( $resource->id ) ); | |
| 82 | - if ( PayplugWoocommerceHelper::is_pre_30() ) { | |
| 83 | - $order->reduce_order_stock(); | |
| 124 | + } elseif ($gateway_id == "payplug") { | |
| 125 | + $this->payplug_ipn($resource); | |
| 84 | 126 | } |
| 85 | 127 | |
| 86 | - /** | |
| 87 | - * Fires once a payment response has been processed. | |
| 88 | - * | |
| 89 | - * @param int $order_id Order ID | |
| 90 | - * @param PaymentResource $resource Payment resource | |
| 91 | - */ | |
| 92 | - \do_action( 'payplug_gateway_payment_response_processed', $order_id, $resource ); | |
| 93 | - PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false); | |
| 94 | - PayplugGateway::log( sprintf( 'Order #%s : Payment IPN %s processing completed.', $order_id, $resource->id ) ); | |
| 128 | + // Handle successful payments | |
| 129 | + if ($resource->is_paid) { | |
| 130 | + PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true); | |
| 131 | + if (!$is_payment_with_token) { | |
| 132 | + $this->maybe_save_card($resource); | |
| 133 | + } | |
| 134 | + $this->maybe_save_address_hash($resource); | |
| 135 | + $order->add_order_note(sprintf(__('PayPlug IPN OK | Transaction %s', 'payplug'), wc_clean($resource->id))); | |
| 136 | + $order->payment_complete(wc_clean($resource->id)); | |
| 137 | + if (PayplugWoocommerceHelper::is_pre_30()) { | |
| 138 | + $order->reduce_order_stock(); | |
| 139 | + } | |
| 140 | + /** | |
| 141 | + * Fires once a payment response has been processed. | |
| 142 | + * | |
| 143 | + * @param int $order_id Order ID | |
| 144 | + * @param PaymentResource $resource Payment resource | |
| 145 | + */ | |
| 146 | + \do_action('payplug_gateway_payment_response_processed', $order_id, $resource); | |
| 147 | + PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false); | |
| 148 | + PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' payment IPN %s processing completed successfully.', $order_id, $resource->id)); | |
| 149 | + } | |
| 95 | 150 | |
| 96 | - return; | |
| 97 | 151 | } |
| 152 | + } | |
| 98 | 153 | |
| 99 | - if ( ! empty( $resource->failure ) ) { | |
| 100 | - PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true); | |
| 101 | - $order->update_status( | |
| 102 | - 'failed', | |
| 103 | - sprintf( __( 'PayPlug IPN OK | Transaction %s failed : %s', 'payplug' ), $resource->id, wc_clean( $resource->failure->message ) ) | |
| 104 | - ); | |
| 154 | + /** | |
| 155 | + * | |
| 156 | + * Handle pending oney payments orders | |
| 157 | + * | |
| 158 | + * @param $order | |
| 159 | + * @param $resource | |
| 160 | + * | |
| 161 | + * @return void | |
| 162 | + */ | |
| 105 | 163 | |
| 106 | - /** This action is documented in src/Gateway/PayplugResponse */ | |
| 107 | - \do_action( 'payplug_gateway_payment_response_processed', $order_id, $resource ); | |
| 108 | - PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false); | |
| 109 | - PayplugGateway::log( sprintf( 'Order #%s : Payment IPN %s processing completed.', $order_id, $resource->id ) ); | |
| 164 | + public function handle_pending_oney($order, $resource) { | |
| 165 | + $order->add_order_note(sprintf(__('PayPlug IPN OK | Transaction %s | Payment PENDING to be checked by an Oney agent', 'payplug'), wc_clean($resource->id))); | |
| 166 | + wc_reduce_stock_levels( $order ); | |
| 167 | + } | |
| 110 | 168 | |
| 111 | - return; | |
| 169 | + /** | |
| 170 | + * Payplug IPN | |
| 171 | + * | |
| 172 | + * @param $resource | |
| 173 | + */ | |
| 174 | + public function payplug_ipn($resource) { | |
| 175 | + $order_id = wc_clean( $resource->metadata['order_id'] ); | |
| 176 | + PayplugGateway::log( sprintf( 'Order #%s : Begin processing Payplug payment IPN %s', $order_id, $resource->id ) ); | |
| 177 | + } | |
| 178 | + | |
| 179 | + /** | |
| 180 | + * Bancontact IPN | |
| 181 | + * | |
| 182 | + * @param $resource | |
| 183 | + */ | |
| 184 | + public function bancontact_ipn($resource) { | |
| 185 | + $order_id = wc_clean( $resource->metadata['order_id'] ); | |
| 186 | + PayplugGateway::log( sprintf( 'Order #%s : Begin processing Bancontact payment IPN %s', $order_id, $resource->id ) ); | |
| 187 | + } | |
| 188 | + | |
| 189 | + /** | |
| 190 | + * Apple Pay IPN | |
| 191 | + * | |
| 192 | + * @param $resource | |
| 193 | + */ | |
| 194 | + public function apple_pay_ipn($resource) { | |
| 195 | + $order_id = wc_clean( $resource->metadata['order_id'] ); | |
| 196 | + PayplugGateway::log( sprintf( 'Order #%s : Begin processing Apple Pay payment IPN %s', $order_id, $resource->id ) ); | |
| 197 | + } | |
| 198 | + | |
| 199 | + /** | |
| 200 | + * American Express IPN | |
| 201 | + * | |
| 202 | + * @param $resource | |
| 203 | + */ | |
| 204 | + public function amex_ipn($resource) { | |
| 205 | + $order_id = wc_clean( $resource->metadata['order_id'] ); | |
| 206 | + PayplugGateway::log( sprintf( 'Order #%s : Begin processing Amex payment IPN %s', $order_id, $resource->id ) ); | |
| 207 | + } | |
| 208 | + | |
| 209 | + /** | |
| 210 | + * Oney IPN | |
| 211 | + * | |
| 212 | + * @param $resource | |
| 213 | + */ | |
| 214 | + public function oney_ipn( $resource ) { | |
| 215 | + $gateway_id = $resource->payment_method['type']; | |
| 216 | + $order_id = wc_clean( $resource->metadata['order_id'] ); | |
| 217 | + | |
| 218 | + if ( ( $resource->is_paid == true ) && ( $resource->failure == null ) ) { | |
| 219 | + PayplugGateway::log( sprintf( 'Order #%s : '. $this->gateway_name($gateway_id) .' payment SUCCESS', $order_id ) ); | |
| 112 | 220 | } |
| 221 | + | |
| 222 | + if ( ( $resource->is_paid == false ) && ( $resource->failure != null ) ) { | |
| 223 | + PayplugGateway::log( sprintf( 'Order #%s : '. $this->gateway_name($gateway_id) .' payment FAILED', $order_id ) ); | |
| 224 | + } | |
| 225 | + | |
| 226 | + if ( ( $resource->is_paid == false ) && ( $resource->failure == null ) && ( $resource->payment_method['is_pending'] == true ) ) { | |
| 227 | + PayplugGateway::log( sprintf( 'Order #%s : '. $this->gateway_name($gateway_id) .' payment PENDING and checked by an Oney agent', $order_id ) ); | |
| 228 | + } | |
| 113 | 229 | } |
| 114 | 230 | |
| 115 | 231 | /** |
| 232 | + * Get the gateway name from the gateway_id | |
| 233 | + * | |
| 234 | + * @param $gateway_id | |
| 235 | + */ | |
| 236 | + private function gateway_name($gateway_id){ | |
| 237 | + return ucwords(str_replace("_", " ", $gateway_id)); | |
| 238 | + } | |
| 239 | + | |
| 240 | + /** | |
| 116 | 241 | * Process refund. |
| 117 | 242 | * |
| 118 | 243 | * @param $resource |
| 119 | 244 | * @param bool $ignore_woocommerce_refund Flag to determine if IPN notification for refunds created by WooCommerce |
| @@ -212,20 +337,33 @@ | ||
| 212 | 337 | */ |
| 213 | 338 | protected function get_order_from_transaction_id( $transaction_id ) { |
| 214 | 339 | global $wpdb; |
| 215 | 340 | |
| 216 | - $order_id = $wpdb->get_var( | |
| 217 | - $wpdb->prepare( | |
| 218 | - " | |
| 219 | - SELECT post_id | |
| 220 | - FROM $wpdb->postmeta | |
| 221 | - WHERE meta_key = '_transaction_id' | |
| 222 | - AND meta_value = %s | |
| 223 | - ", | |
| 224 | - $transaction_id | |
| 225 | - ) | |
| 226 | - ); | |
| 341 | + if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { | |
| 227 | 342 | |
| 343 | + $orders = wc_get_orders( | |
| 344 | + array( | |
| 345 | + 'field_query' => array( | |
| 346 | + array( | |
| 347 | + 'key' => 'transaction_id', | |
| 348 | + 'comparison' => $transaction_id | |
| 349 | + ), | |
| 350 | + ), | |
| 351 | + ) | |
| 352 | + ); | |
| 353 | + $order = $orders[0]; | |
| 354 | + $order_id = $order->get_id(); | |
| 355 | + //$sql = "SELECT o.id FROM $wpdb->wc_orders o WHERE o.transaction_id = %s"; | |
| 356 | + | |
| 357 | + }else{ | |
| 358 | + $order_id = $wpdb->get_var( | |
| 359 | + $wpdb->prepare( | |
| 360 | + "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_transaction_id' AND meta_value = %s", | |
| 361 | + $transaction_id | |
| 362 | + ) | |
| 363 | + ); | |
| 364 | + } | |
| 365 | + | |
| 228 | 366 | return ! is_null( $order_id ) ? wc_get_order( $order_id ) : false; |
| 229 | 367 | } |
| 230 | 368 | |
| 231 | 369 | /** |
| @@ -237,29 +375,57 @@ | ||
| 237 | 375 | */ |
| 238 | 376 | protected function refund_exist_for_order( $order_id, $refund_id ) { |
| 239 | 377 | global $wpdb; |
| 240 | 378 | |
| 241 | - $sql = " | |
| 379 | + if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { | |
| 380 | + | |
| 381 | + $results = wc_get_orders( | |
| 382 | + array( | |
| 383 | + 'meta_query' => array( | |
| 384 | + array( | |
| 385 | + 'field_query' => array( | |
| 386 | + 'relation' => 'AND', | |
| 387 | + array( | |
| 388 | + 'field' => 'meta_key', | |
| 389 | + 'value' => '_pr_' . esc_sql( $refund_id ), | |
| 390 | + ), | |
| 391 | + array( | |
| 392 | + 'field' => 'meta_value', | |
| 393 | + 'value' => $refund_id | |
| 394 | + ), | |
| 395 | + array( | |
| 396 | + 'field' => 'order_id', | |
| 397 | + 'value' => (int) $order_id | |
| 398 | + ), | |
| 399 | + ) | |
| 400 | + ) | |
| 401 | + ), | |
| 402 | + ) | |
| 403 | + ); | |
| 404 | + | |
| 405 | + }else{ | |
| 406 | + $sql = " | |
| 242 | 407 | SELECT p.ID |
| 243 | - FROM $wpdb->posts p | |
| 244 | - INNER JOIN $wpdb->postmeta pm | |
| 245 | - ON p.ID = pm.post_id | |
| 246 | - WHERE 1=1 | |
| 247 | - AND p.post_type = %s | |
| 248 | - AND p.ID = %d | |
| 249 | - AND pm.meta_key LIKE '_pr_" . esc_sql( $refund_id ) . "' | |
| 250 | - AND pm.meta_value = %s | |
| 408 | + FROM $wpdb->posts p | |
| 409 | + INNER JOIN $wpdb->postmeta pm | |
| 410 | + ON p.ID = pm.post_id | |
| 411 | + WHERE 1=1 | |
| 412 | + AND p.post_type = %s | |
| 413 | + AND p.ID = %d | |
| 414 | + AND pm.meta_key LIKE '_pr_" . esc_sql( $refund_id ) . "' | |
| 415 | + AND pm.meta_value = %s | |
| 251 | 416 | LIMIT 1 |
| 252 | - "; | |
| 417 | + "; | |
| 253 | 418 | |
| 254 | - $results = $wpdb->get_col( | |
| 255 | - $wpdb->prepare( | |
| 256 | - $sql, | |
| 257 | - 'shop_order', | |
| 258 | - (int) $order_id, | |
| 259 | - $refund_id | |
| 260 | - ) | |
| 261 | - ); | |
| 419 | + $results = $wpdb->get_col( | |
| 420 | + $wpdb->prepare( | |
| 421 | + $sql, | |
| 422 | + 'shop_order', | |
| 423 | + (int) $order_id, | |
| 424 | + $refund_id | |
| 425 | + ) | |
| 426 | + ); | |
| 427 | + } | |
| 262 | 428 | |
| 263 | 429 | return ! empty( $results ) ? true : false; |
| 264 | 430 | } |
| 265 | 431 | |
| @@ -302,17 +468,17 @@ | ||
| 302 | 468 | if(!empty($existing_tokens)) { |
| 303 | 469 | foreach($existing_tokens as $token_id => $existing_token) { |
| 304 | 470 | $current_data = $existing_token->get_data(); |
| 305 | 471 | if( $current_data['token'] === $set_token && |
| 306 | - $current_data['last4'] === $set_last4 && | |
| 307 | - $current_data['expiry_year'] === $set_expiry_year && | |
| 308 | - $current_data['expiry_month'] === $set_expiry_month && | |
| 472 | + $current_data['last4'] === $set_last4 && | |
| 473 | + $current_data['expiry_year'] === $set_expiry_year && | |
| 474 | + $current_data['expiry_month'] === $set_expiry_month && | |
| 309 | 475 | $current_data['card_type'] === $set_card_type) { |
| 310 | 476 | $token->set_id($current_data['id']); |
| 311 | 477 | } |
| 312 | 478 | } |
| 313 | 479 | } |
| 314 | - | |
| 480 | + | |
| 315 | 481 | $token->set_token( $set_token ); |
| 316 | 482 | $token->set_gateway_id( 'payplug' ); |
| 317 | 483 | $token->set_last4( $set_last4 ); |
| 318 | 484 | $token->set_expiry_year( $set_expiry_year ); |