PluginProbe
PayPlug for WooCommerce (Official) / 2.8.2
PayPlug for WooCommerce (Official) v2.8.2
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.8.2, at src/Gateway/PayplugResponse.php

525 lines 16.0 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
70 // Ignore paid orders
71 if ($order->is_paid() || isset($paid_date) ) {
72 PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' order is already complete. Ignoring IPN.', $order_id));
73 //$lock->deleteLock($resource->id);
74 return;
75 }
76
77 // Handle failed payments
78 if (!empty($resource->failure)) {
79 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true);
80 $order->update_status(
81 'failed',
82 sprintf(__('PayPlug IPN OK | Transaction %s failed : %s', 'payplug'), $resource->id, wc_clean($resource->failure->message))
83 );
84 /** This action is documented in src/Gateway/PayplugResponse */
85 \do_action('payplug_gateway_payment_response_processed', $order_id, $resource);
86 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false);
87 PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' payment IPN %s processing completed but failed.', $order_id, $resource->id));
88 wc_increase_stock_levels($order);
89
90 //$lock->deleteLock($resource->id);
91 return;
92 }
93
94 elseif (( $resource->failure == null ) && !empty($resource->payment_method['is_pending'])
95 && ( $resource->payment_method['is_pending'] == true )) {
96 $this->handle_pending_oney( $order, $resource );
97 }
98
99 // Save Logs of the payment for the different payment gateways:
100 // For Oney 4 gateways & Bancontact gateway: "$resource->payment_method" exists and is an array
101 // but not for Payplug credit card gateway (in this case we check the payment_method from the order itself not the $resource)
102 if (isset($resource->payment_method) && is_array($resource->payment_method)) {
103 $gateway_id = $resource->payment_method['type'];
104
105 switch ($gateway_id) {
106 case substr( $gateway_id, 0, 5 ) === "oney_" :
107 $this->oney_ipn($resource);
108 break;
109 case "bancontact" :
110 $this->bancontact_ipn($resource);
111 break;
112 case "apple_pay" :
113 $this->apple_pay_ipn($resource);
114 break;
115 case "american_express" :
116 $this->amex_ipn($resource);
117 break;
118 }
119
120 } elseif ($gateway_id == "payplug") {
121 $this->payplug_ipn($resource);
122 }
123
124 // Handle successful payments
125 if ($resource->is_paid) {
126 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true);
127 if (!$is_payment_with_token) {
128 $this->maybe_save_card($resource);
129 }
130 $this->maybe_save_address_hash($resource);
131 $order->add_order_note(sprintf(__('PayPlug IPN OK | Transaction %s', 'payplug'), wc_clean($resource->id)));
132 $order->payment_complete(wc_clean($resource->id));
133 if (PayplugWoocommerceHelper::is_pre_30()) {
134 $order->reduce_order_stock();
135 }
136 /**
137 * Fires once a payment response has been processed.
138 *
139 * @param int $order_id Order ID
140 * @param PaymentResource $resource Payment resource
141 */
142 \do_action('payplug_gateway_payment_response_processed', $order_id, $resource);
143 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false);
144 PayplugGateway::log(sprintf('Order #%s : '. $this->gateway_name($gateway_id) .' payment IPN %s processing completed successfully.', $order_id, $resource->id));
145 }
146
147 }
148 }
149
150 /**
151 *
152 * Handle pending oney payments orders
153 *
154 * @param $order
155 * @param $resource
156 *
157 * @return void
158 */
159
160 public function handle_pending_oney($order, $resource) {
161 $order->add_order_note(sprintf(__('PayPlug IPN OK | Transaction %s | Payment PENDING to be checked by an Oney agent', 'payplug'), wc_clean($resource->id)));
162 wc_reduce_stock_levels( $order );
163 }
164
165 /**
166 * Payplug IPN
167 *
168 * @param $resource
169 */
170 public function payplug_ipn($resource) {
171 $order_id = wc_clean( $resource->metadata['order_id'] );
172 PayplugGateway::log( sprintf( 'Order #%s : Begin processing Payplug payment IPN %s', $order_id, $resource->id ) );
173 }
174
175 /**
176 * Bancontact IPN
177 *
178 * @param $resource
179 */
180 public function bancontact_ipn($resource) {
181 $order_id = wc_clean( $resource->metadata['order_id'] );
182 PayplugGateway::log( sprintf( 'Order #%s : Begin processing Bancontact payment IPN %s', $order_id, $resource->id ) );
183 }
184
185 /**
186 * Apple Pay IPN
187 *
188 * @param $resource
189 */
190 public function apple_pay_ipn($resource) {
191 $order_id = wc_clean( $resource->metadata['order_id'] );
192 PayplugGateway::log( sprintf( 'Order #%s : Begin processing Apple Pay payment IPN %s', $order_id, $resource->id ) );
193 }
194
195 /**
196 * American Express IPN
197 *
198 * @param $resource
199 */
200 public function amex_ipn($resource) {
201 $order_id = wc_clean( $resource->metadata['order_id'] );
202 PayplugGateway::log( sprintf( 'Order #%s : Begin processing Amex payment IPN %s', $order_id, $resource->id ) );
203 }
204
205 /**
206 * Oney IPN
207 *
208 * @param $resource
209 */
210 public function oney_ipn( $resource ) {
211 $gateway_id = $resource->payment_method['type'];
212 $order_id = wc_clean( $resource->metadata['order_id'] );
213
214 if ( ( $resource->is_paid == true ) && ( $resource->failure == null ) ) {
215 PayplugGateway::log( sprintf( 'Order #%s : '. $this->gateway_name($gateway_id) .' payment SUCCESS', $order_id ) );
216 }
217
218 if ( ( $resource->is_paid == false ) && ( $resource->failure != null ) ) {
219 PayplugGateway::log( sprintf( 'Order #%s : '. $this->gateway_name($gateway_id) .' payment FAILED', $order_id ) );
220 }
221
222 if ( ( $resource->is_paid == false ) && ( $resource->failure == null ) && ( $resource->payment_method['is_pending'] == true ) ) {
223 PayplugGateway::log( sprintf( 'Order #%s : '. $this->gateway_name($gateway_id) .' payment PENDING and checked by an Oney agent', $order_id ) );
224 }
225 }
226
227 /**
228 * Get the gateway name from the gateway_id
229 *
230 * @param $gateway_id
231 */
232 private function gateway_name($gateway_id){
233 return ucwords(str_replace("_", " ", $gateway_id));
234 }
235
236 /**
237 * Process refund.
238 *
239 * @param $resource
240 * @param bool $ignore_woocommerce_refund Flag to determine if IPN notification for refunds created by WooCommerce
241 * should be ignored or not. Default to true.
242 *
243 * @throws \Exception
244 */
245 public function process_refund( $resource, $ignore_woocommerce_refund = true ) {
246 $refund_id = wc_clean( $resource->id );
247 $transaction_id = wc_clean( $resource->payment_id );
248 $order = $this->get_order_from_transaction_id( $transaction_id );
249 if ( ! $order ) {
250 PayplugGateway::log( sprintf( 'Coudn\'t find order for transaction %s (Refund %s).', wc_clean( $resource->payment_id ), wc_clean( $resource->id ) ), 'error' );
251
252 return;
253 }
254 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
255
256 PayplugGateway::log( sprintf( 'Order #%s : Begin processing refund IPN %s', $order_id, $resource->id ) );
257
258 $refund_exist = $this->refund_exist_for_order( $order_id, $refund_id );
259 if ( $refund_exist ) {
260 PayplugGateway::log( sprintf( 'Order #%s : Refund has already been processed. Ignoring IPN.', $order_id ) );
261
262 return;
263 }
264
265 // Since refund notification doesn't contain the full resource, we need to retrieve
266 // the refund resource to access its metadata.
267 try {
268 $refund = $this->gateway->api->refund_retrieve( $transaction_id, $refund_id );
269 } catch ( \Exception $e ) {
270 PayplugGateway::log( sprintf( 'Order #%s : Fail to retrieve refund data with error %s', $order_id, $e->getMessage() ) );
271
272 return;
273 }
274
275 if (
276 $ignore_woocommerce_refund
277 && isset( $refund->metadata['refund_from'] )
278 && 'woocommerce' === $refund->metadata['refund_from']
279 ) {
280 PayplugGateway::log( sprintf( 'Order #%s : Refund created by WooCommerce. Ignoring IPN.', $order_id ) );
281
282 return;
283 }
284
285 $refund = wc_create_refund( [
286 'amount' => ( (int) $resource->amount ) / 100,
287 'reason' => isset( $resource->metadata['reason'] ) ? $resource->metadata['reason'] : null,
288 'order_id' => (int) $order_id,
289 'refund_id' => 0,
290 'refund_payment' => false,
291 ] );
292 if ( is_wp_error( $refund ) ) {
293 PayplugGateway::log( $refund->get_error_message() );
294 }
295
296 $refund_meta_key = sprintf( '_pr_%s', wc_clean( $resource->id ) );
297 if ( PayplugWoocommerceHelper::is_pre_30() ) {
298 update_post_meta( $order_id, $refund_meta_key, $resource->id );
299 } else {
300 $order->add_meta_data( $refund_meta_key, $resource->id, true );
301 $order->save();
302 }
303
304 $note = sprintf( __( 'Refund %s : Refunded %s', 'payplug' ), wc_clean( $resource->id ), wc_price( ( (int) $resource->amount ) / 100 ) );
305 if ( ! empty( $resource->metadata['reason'] ) ) {
306 $note .= sprintf( ' (%s)', esc_html( $resource->metadata['reason'] ) );
307 }
308 $order->add_order_note( $note );
309
310 /**
311 * Fires once a refund response has been processed.
312 *
313 * @param int $order_id Order ID
314 * @param RefundResource $resource Refund resource
315 */
316 \do_action( 'payplug_gateway_refund_response_processed', $order_id, $resource );
317
318 try {
319 $payment = $this->gateway->api->payment_retrieve( $transaction_id );
320 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
321 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
322 } catch ( \Exception $e ) {}
323
324 PayplugGateway::log( sprintf( 'Order #%s : Refund IPN %s processing completed.', $order_id, $resource->id ) );
325 }
326
327 /**
328 * Get an order from its transaction id.
329 *
330 * @param int $transaction_id
331 *
332 * @return bool|\WC_Order
333 */
334 protected function get_order_from_transaction_id( $transaction_id ) {
335 global $wpdb;
336
337 if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
338
339 $orders = wc_get_orders(
340 array(
341 'field_query' => array(
342 array(
343 'key' => 'transaction_id',
344 'comparison' => $transaction_id
345 ),
346 ),
347 )
348 );
349 $order = $orders[0];
350 $order_id = $order->get_id();
351 //$sql = "SELECT o.id FROM $wpdb->wc_orders o WHERE o.transaction_id = %s";
352
353 }else{
354 $order_id = $wpdb->get_var(
355 $wpdb->prepare(
356 "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_transaction_id' AND meta_value = %s",
357 $transaction_id
358 )
359 );
360 }
361
362 return ! is_null( $order_id ) ? wc_get_order( $order_id ) : false;
363 }
364
365 /**
366 * Check if a refund id already exist for the order.
367 *
368 * @param string $refund_id
369 *
370 * @return bool
371 */
372 protected function refund_exist_for_order( $order_id, $refund_id ) {
373 global $wpdb;
374
375 if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
376
377 $results = wc_get_orders(
378 array(
379 'meta_query' => array(
380 array(
381 'field_query' => array(
382 'relation' => 'AND',
383 array(
384 'field' => 'meta_key',
385 'value' => '_pr_' . esc_sql( $refund_id ),
386 ),
387 array(
388 'field' => 'meta_value',
389 'value' => $refund_id
390 ),
391 array(
392 'field' => 'order_id',
393 'value' => (int) $order_id
394 ),
395 )
396 )
397 ),
398 )
399 );
400
401 }else{
402 $sql = "
403 SELECT p.ID
404 FROM $wpdb->posts p
405 INNER JOIN $wpdb->postmeta pm
406 ON p.ID = pm.post_id
407 WHERE 1=1
408 AND p.post_type = %s
409 AND p.ID = %d
410 AND pm.meta_key LIKE '_pr_" . esc_sql( $refund_id ) . "'
411 AND pm.meta_value = %s
412 LIMIT 1
413 ";
414
415 $results = $wpdb->get_col(
416 $wpdb->prepare(
417 $sql,
418 'shop_order',
419 (int) $order_id,
420 $refund_id
421 )
422 );
423 }
424
425 return ! empty( $results ) ? true : false;
426 }
427
428 /**
429 * Save card from the transaction.
430 *
431 * @param IVerifiableAPIResource $resource
432 *
433 * @return bool
434 */
435 protected function maybe_save_card( $resource ) {
436
437 if ( ! isset( $resource->card ) || empty( $resource->card->id ) ) {
438 return false;
439 }
440
441 if ( ! isset( $resource->metadata['customer_id'] ) ) {
442 return false;
443 }
444
445 $customer = get_user_by( 'id', $resource->metadata['customer_id'] );
446 if ( ! $customer || 0 === (int) $customer->ID ) {
447 return false;
448 }
449
450 $merchant_id = $this->gateway->get_merchant_id();
451 if ( empty( $merchant_id ) ) {
452 return false;
453 }
454
455 PayplugGateway::log( sprintf( 'Saving card from transaction %s for customer %s', wc_clean( $resource->id ), $customer->ID ) );
456
457 $token = new WC_Payment_Token_CC();
458 $existing_tokens = WC_Payment_Tokens::get_customer_tokens( $customer->ID , $this->gateway->id );
459 $set_token = wc_clean( $resource->card->id );
460 $set_last4 = wc_clean( $resource->card->last4 );
461 $set_expiry_year = wc_clean( $resource->card->exp_year ) ;
462 $set_expiry_month = zeroise( (int) wc_clean( $resource->card->exp_month ), 2 ) ;
463 $set_card_type = \strtolower( wc_clean( $resource->card->brand ) ) ;
464 if(!empty($existing_tokens)) {
465 foreach($existing_tokens as $token_id => $existing_token) {
466 $current_data = $existing_token->get_data();
467 if( $current_data['token'] === $set_token &&
468 $current_data['last4'] === $set_last4 &&
469 $current_data['expiry_year'] === $set_expiry_year &&
470 $current_data['expiry_month'] === $set_expiry_month &&
471 $current_data['card_type'] === $set_card_type) {
472 $token->set_id($current_data['id']);
473 }
474 }
475 }
476
477 $token->set_token( $set_token );
478 $token->set_gateway_id( 'payplug' );
479 $token->set_last4( $set_last4 );
480 $token->set_expiry_year( $set_expiry_year );
481 $token->set_expiry_month( $set_expiry_month );
482 $token->set_card_type( $set_card_type );
483 $token->set_user_id( $customer->ID );
484 $token->add_meta_data( 'mode', $resource->is_live ? 'live' : 'test', true );
485 $token->add_meta_data( 'payplug_account', \wc_clean( $merchant_id ), true );
486 $token->save();
487
488 PayplugGateway::log( sprintf( 'Payment card saved', wc_clean( $resource->id ), $customer->ID ) );
489
490 return true;
491 }
492
493 /**
494 * Save shipping address.
495 *
496 * @param IVerifiableAPIResource $resource
497 *
498 * @return bool
499 */
500 protected function maybe_save_address_hash( $resource ) {
501
502 if ( ! isset( $resource->metadata['customer_id'] ) ) {
503 return false;
504 }
505
506 $customer = get_user_by( 'id', $resource->metadata['customer_id'] );
507 if ( ! $customer || 0 === (int) $customer->ID ) {
508 return false;
509 }
510
511 $shipping = [];
512 foreach ( PayplugAddressData::$address_fields as $field ) {
513 $shipping[ $field ] = $resource->shipping->{$field};
514 }
515
516 $shipping_hash = PayplugAddressData::hash_address( $shipping );
517 $hash_list = PayplugAddressData::get_customer_addresses_hash( $customer->ID );
518 $hash_list[] = $shipping_hash;
519 $hash_list = array_unique( $hash_list );
520 PayplugAddressData::update_customer_addresses_hash( $customer->ID, $hash_list );
521
522 return true;
523 }
524 }
525