PluginProbe
PayPlug for WooCommerce (Official) / 1.10.0
PayPlug for WooCommerce (Official) v1.10.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) 1.10.0, at src/Gateway/PayplugResponse.php

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