PluginProbe
PayPlug for WooCommerce (Official) / 2.9.0
PayPlug for WooCommerce (Official) v2.9.0
3.0.0 2.18.0 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.0 1.10.0 1.10.1 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 All 101 releases
payplug / src / Gateway / PayplugResponse.php

PayplugResponse.php in PayPlug for WooCommerce (Official) 2.9.0, at src/Gateway/PayplugResponse.php

529 lines 16.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Payplug\PayplugWoocommerce\Gateway;
4
5 // Exit if accessed directly
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper;
11 use Payplug\Resource\IVerifiableAPIResource;
12 use Payplug\Resource\Payment as PaymentResource;
13 use Payplug\Resource\Refund as RefundResource;
14 use WC_Payment_Tokens;
15 use WC_Payment_Token_CC;
16 use Automattic\WooCommerce\Utilities\OrderUtil;
17
18 /**
19 * Process responses from PayPlug.
20 *
21 * @package Payplug\PayplugWoocommerce\Gateway
22 */
23 class PayplugResponse {
24
25 private $gateway;
26
27 public function __construct( PayplugGateway $gateway ) {
28 $this->gateway = $gateway;
29 }
30
31 /**
32 * Process payment.
33 *
34 * @param $resource
35 * @param $is_payment_with_token
36 *
37 * @return void
38 * @throws \WC_Data_Exception
39 */
40 public function process_payment($resource, $is_payment_with_token = false, $source = null)
41 {
42 $order_id = wc_clean($resource->metadata['order_id']);
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');
51 return;
52 }
53
54 /**
55 *
56 * Checking if it is coming from the order confirmation page or the IPN
57 *
58 */
59
60 if (($gateway_id == $this->gateway->id) || (!empty($source) && ($source === "ipn"))) {
61
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 }
73
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 }
80
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);
93
94 //$lock->deleteLock($resource->id);
95 return;
96 }
97
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 }
102
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'];
108
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 }
123
124 } elseif ($gateway_id == "payplug") {
125 $this->payplug_ipn($resource);
126 }
127
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 }
150
151 }
152 }
153
154 /**
155 *
156 * Handle pending oney payments orders
157 *
158 * @param $order
159 * @param $resource
160 *
161 * @return void
162 */
163
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 }
168
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 ) );
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 }
229 }
230
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 /**
241 * Process refund.
242 *
243 * @param $resource
244 * @param bool $ignore_woocommerce_refund Flag to determine if IPN notification for refunds created by WooCommerce
245 * should be ignored or not. Default to true.
246 *
247 * @throws \Exception
248 */
249 public function process_refund( $resource, $ignore_woocommerce_refund = true ) {
250 $refund_id = wc_clean( $resource->id );
251 $transaction_id = wc_clean( $resource->payment_id );
252 $order = $this->get_order_from_transaction_id( $transaction_id );
253 if ( ! $order ) {
254 PayplugGateway::log( sprintf( 'Coudn\'t find order for transaction %s (Refund %s).', wc_clean( $resource->payment_id ), wc_clean( $resource->id ) ), 'error' );
255
256 return;
257 }
258 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
259
260 PayplugGateway::log( sprintf( 'Order #%s : Begin processing refund IPN %s', $order_id, $resource->id ) );
261
262 $refund_exist = $this->refund_exist_for_order( $order_id, $refund_id );
263 if ( $refund_exist ) {
264 PayplugGateway::log( sprintf( 'Order #%s : Refund has already been processed. Ignoring IPN.', $order_id ) );
265
266 return;
267 }
268
269 // Since refund notification doesn't contain the full resource, we need to retrieve
270 // the refund resource to access its metadata.
271 try {
272 $refund = $this->gateway->api->refund_retrieve( $transaction_id, $refund_id );
273 } catch ( \Exception $e ) {
274 PayplugGateway::log( sprintf( 'Order #%s : Fail to retrieve refund data with error %s', $order_id, $e->getMessage() ) );
275
276 return;
277 }
278
279 if (
280 $ignore_woocommerce_refund
281 && isset( $refund->metadata['refund_from'] )
282 && 'woocommerce' === $refund->metadata['refund_from']
283 ) {
284 PayplugGateway::log( sprintf( 'Order #%s : Refund created by WooCommerce. Ignoring IPN.', $order_id ) );
285
286 return;
287 }
288
289 $refund = wc_create_refund( [
290 'amount' => ( (int) $resource->amount ) / 100,
291 'reason' => isset( $resource->metadata['reason'] ) ? $resource->metadata['reason'] : null,
292 'order_id' => (int) $order_id,
293 'refund_id' => 0,
294 'refund_payment' => false,
295 ] );
296 if ( is_wp_error( $refund ) ) {
297 PayplugGateway::log( $refund->get_error_message() );
298 }
299
300 $refund_meta_key = sprintf( '_pr_%s', wc_clean( $resource->id ) );
301 if ( PayplugWoocommerceHelper::is_pre_30() ) {
302 update_post_meta( $order_id, $refund_meta_key, $resource->id );
303 } else {
304 $order->add_meta_data( $refund_meta_key, $resource->id, true );
305 $order->save();
306 }
307
308 $note = sprintf( __( 'Refund %s : Refunded %s', 'payplug' ), wc_clean( $resource->id ), wc_price( ( (int) $resource->amount ) / 100 ) );
309 if ( ! empty( $resource->metadata['reason'] ) ) {
310 $note .= sprintf( ' (%s)', esc_html( $resource->metadata['reason'] ) );
311 }
312 $order->add_order_note( $note );
313
314 /**
315 * Fires once a refund response has been processed.
316 *
317 * @param int $order_id Order ID
318 * @param RefundResource $resource Refund resource
319 */
320 \do_action( 'payplug_gateway_refund_response_processed', $order_id, $resource );
321
322 try {
323 $payment = $this->gateway->api->payment_retrieve( $transaction_id );
324 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
325 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
326 } catch ( \Exception $e ) {}
327
328 PayplugGateway::log( sprintf( 'Order #%s : Refund IPN %s processing completed.', $order_id, $resource->id ) );
329 }
330
331 /**
332 * Get an order from its transaction id.
333 *
334 * @param int $transaction_id
335 *
336 * @return bool|\WC_Order
337 */
338 protected function get_order_from_transaction_id( $transaction_id ) {
339 global $wpdb;
340
341 if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
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
366 return ! is_null( $order_id ) ? wc_get_order( $order_id ) : false;
367 }
368
369 /**
370 * Check if a refund id already exist for the order.
371 *
372 * @param string $refund_id
373 *
374 * @return bool
375 */
376 protected function refund_exist_for_order( $order_id, $refund_id ) {
377 global $wpdb;
378
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 = "
407 SELECT p.ID
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
416 LIMIT 1
417 ";
418
419 $results = $wpdb->get_col(
420 $wpdb->prepare(
421 $sql,
422 'shop_order',
423 (int) $order_id,
424 $refund_id
425 )
426 );
427 }
428
429 return ! empty( $results ) ? true : false;
430 }
431
432 /**
433 * Save card from the transaction.
434 *
435 * @param IVerifiableAPIResource $resource
436 *
437 * @return bool
438 */
439 protected function maybe_save_card( $resource ) {
440
441 if ( ! isset( $resource->card ) || empty( $resource->card->id ) ) {
442 return false;
443 }
444
445 if ( ! isset( $resource->metadata['customer_id'] ) ) {
446 return false;
447 }
448
449 $customer = get_user_by( 'id', $resource->metadata['customer_id'] );
450 if ( ! $customer || 0 === (int) $customer->ID ) {
451 return false;
452 }
453
454 $merchant_id = $this->gateway->get_merchant_id();
455 if ( empty( $merchant_id ) ) {
456 return false;
457 }
458
459 PayplugGateway::log( sprintf( 'Saving card from transaction %s for customer %s', wc_clean( $resource->id ), $customer->ID ) );
460
461 $token = new WC_Payment_Token_CC();
462 $existing_tokens = WC_Payment_Tokens::get_customer_tokens( $customer->ID , $this->gateway->id );
463 $set_token = wc_clean( $resource->card->id );
464 $set_last4 = wc_clean( $resource->card->last4 );
465 $set_expiry_year = wc_clean( $resource->card->exp_year ) ;
466 $set_expiry_month = zeroise( (int) wc_clean( $resource->card->exp_month ), 2 ) ;
467 $set_card_type = \strtolower( wc_clean( $resource->card->brand ) ) ;
468 if(!empty($existing_tokens)) {
469 foreach($existing_tokens as $token_id => $existing_token) {
470 $current_data = $existing_token->get_data();
471 if( $current_data['token'] === $set_token &&
472 $current_data['last4'] === $set_last4 &&
473 $current_data['expiry_year'] === $set_expiry_year &&
474 $current_data['expiry_month'] === $set_expiry_month &&
475 $current_data['card_type'] === $set_card_type) {
476 $token->set_id($current_data['id']);
477 }
478 }
479 }
480
481 $token->set_token( $set_token );
482 $token->set_gateway_id( 'payplug' );
483 $token->set_last4( $set_last4 );
484 $token->set_expiry_year( $set_expiry_year );
485 $token->set_expiry_month( $set_expiry_month );
486 $token->set_card_type( $set_card_type );
487 $token->set_user_id( $customer->ID );
488 $token->add_meta_data( 'mode', $resource->is_live ? 'live' : 'test', true );
489 $token->add_meta_data( 'payplug_account', \wc_clean( $merchant_id ), true );
490 $token->save();
491
492 PayplugGateway::log( sprintf( 'Payment card saved', wc_clean( $resource->id ), $customer->ID ) );
493
494 return true;
495 }
496
497 /**
498 * Save shipping address.
499 *
500 * @param IVerifiableAPIResource $resource
501 *
502 * @return bool
503 */
504 protected function maybe_save_address_hash( $resource ) {
505
506 if ( ! isset( $resource->metadata['customer_id'] ) ) {
507 return false;
508 }
509
510 $customer = get_user_by( 'id', $resource->metadata['customer_id'] );
511 if ( ! $customer || 0 === (int) $customer->ID ) {
512 return false;
513 }
514
515 $shipping = [];
516 foreach ( PayplugAddressData::$address_fields as $field ) {
517 $shipping[ $field ] = $resource->shipping->{$field};
518 }
519
520 $shipping_hash = PayplugAddressData::hash_address( $shipping );
521 $hash_list = PayplugAddressData::get_customer_addresses_hash( $customer->ID );
522 $hash_list[] = $shipping_hash;
523 $hash_list = array_unique( $hash_list );
524 PayplugAddressData::update_customer_addresses_hash( $customer->ID, $hash_list );
525
526 return true;
527 }
528 }
529