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

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