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

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