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

363 lines 11.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
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 $order_id = wc_clean( $resource->metadata['order_id'] );
41 $order = wc_get_order( $order_id );
42 if ( ! $order ) {
43 PayplugGateway::log( sprintf( 'Coudn\'t find order #%s (Transaction %s).', $order_id, wc_clean( $resource->id ) ), 'error' );
44
45 return;
46 }
47
48 PayplugGateway::log( sprintf( 'Order #%s : Begin processing payment IPN %s', $order_id, $resource->id ) );
49
50 // Ignore paid orders
51 if ( $order->is_paid() ) {
52 PayplugGateway::log( sprintf( 'Order #%s : Order is already complete. Ignoring IPN.', $order_id ) );
53
54 return;
55 }
56
57 // Ignore cancelled orders
58 if ( $order->has_status( 'refunded' ) ) {
59 PayplugGateway::log( sprintf( 'Order #%s : Order has been refunded. Ignoring IPN', $order_id ) );
60
61 return;
62 }
63
64 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $resource );
65 $order_metadata = $order->get_meta('_payplug_metadata', true);
66
67 if (is_array($order_metadata) && $order_metadata['transaction_in_progress']) {
68 PayplugGateway::log(sprintf('Order #%s : Order Oney IPN already in progress. Ignoring IPN', $order_id));
69 return;
70 }
71
72 if ( $resource->is_paid ) {
73 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true);
74 if ( ! $is_payment_with_token ) {
75 $this->maybe_save_card( $resource );
76 }
77
78 $this->maybe_save_address_hash( $resource );
79
80 $order->add_order_note( sprintf( __( 'PayPlug IPN OK | Transaction %s', 'payplug' ), wc_clean( $resource->id ) ) );
81 $order->payment_complete( wc_clean( $resource->id ) );
82 if ( PayplugWoocommerceHelper::is_pre_30() ) {
83 $order->reduce_order_stock();
84 }
85
86 /**
87 * Fires once a payment response has been processed.
88 *
89 * @param int $order_id Order ID
90 * @param PaymentResource $resource Payment resource
91 */
92 \do_action( 'payplug_gateway_payment_response_processed', $order_id, $resource );
93 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false);
94 PayplugGateway::log( sprintf( 'Order #%s : Payment IPN %s processing completed.', $order_id, $resource->id ) );
95
96 return;
97 }
98
99 if ( ! empty( $resource->failure ) ) {
100 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, true);
101 $order->update_status(
102 'failed',
103 sprintf( __( 'PayPlug IPN OK | Transaction %s failed : %s', 'payplug' ), $resource->id, wc_clean( $resource->failure->message ) )
104 );
105
106 /** This action is documented in src/Gateway/PayplugResponse */
107 \do_action( 'payplug_gateway_payment_response_processed', $order_id, $resource );
108 PayplugWoocommerceHelper::set_flag_ipn_order($order, $metadata, false);
109 PayplugGateway::log( sprintf( 'Order #%s : Payment IPN %s processing completed.', $order_id, $resource->id ) );
110
111 return;
112 }
113 }
114
115 /**
116 * Process refund.
117 *
118 * @param $resource
119 * @param bool $ignore_woocommerce_refund Flag to determine if IPN notification for refunds created by WooCommerce
120 * should be ignored or not. Default to true.
121 *
122 * @throws \Exception
123 */
124 public function process_refund( $resource, $ignore_woocommerce_refund = true ) {
125 $refund_id = wc_clean( $resource->id );
126 $transaction_id = wc_clean( $resource->payment_id );
127 $order = $this->get_order_from_transaction_id( $transaction_id );
128 if ( ! $order ) {
129 PayplugGateway::log( sprintf( 'Coudn\'t find order for transaction %s (Refund %s).', wc_clean( $resource->payment_id ), wc_clean( $resource->id ) ), 'error' );
130
131 return;
132 }
133 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
134
135 PayplugGateway::log( sprintf( 'Order #%s : Begin processing refund IPN %s', $order_id, $resource->id ) );
136
137 $refund_exist = $this->refund_exist_for_order( $order_id, $refund_id );
138 if ( $refund_exist ) {
139 PayplugGateway::log( sprintf( 'Order #%s : Refund has already been processed. Ignoring IPN.', $order_id ) );
140
141 return;
142 }
143
144 // Since refund notification doesn't contain the full resource, we need to retrieve
145 // the refund resource to access its metadata.
146 try {
147 $refund = $this->gateway->api->refund_retrieve( $transaction_id, $refund_id );
148 } catch ( \Exception $e ) {
149 PayplugGateway::log( sprintf( 'Order #%s : Fail to retrieve refund data with error %s', $order_id, $e->getMessage() ) );
150
151 return;
152 }
153
154 if (
155 $ignore_woocommerce_refund
156 && isset( $refund->metadata['refund_from'] )
157 && 'woocommerce' === $refund->metadata['refund_from']
158 ) {
159 PayplugGateway::log( sprintf( 'Order #%s : Refund created by WooCommerce. Ignoring IPN.', $order_id ) );
160
161 return;
162 }
163
164 $refund = wc_create_refund( [
165 'amount' => ( (int) $resource->amount ) / 100,
166 'reason' => isset( $resource->metadata['reason'] ) ? $resource->metadata['reason'] : null,
167 'order_id' => (int) $order_id,
168 'refund_id' => 0,
169 'refund_payment' => false,
170 ] );
171 if ( is_wp_error( $refund ) ) {
172 PayplugGateway::log( $refund->get_error_message() );
173 }
174
175 $refund_meta_key = sprintf( '_pr_%s', wc_clean( $resource->id ) );
176 if ( PayplugWoocommerceHelper::is_pre_30() ) {
177 update_post_meta( $order_id, $refund_meta_key, $resource->id );
178 } else {
179 $order->add_meta_data( $refund_meta_key, $resource->id, true );
180 $order->save();
181 }
182
183 $note = sprintf( __( 'Refund %s : Refunded %s', 'payplug' ), wc_clean( $resource->id ), wc_price( ( (int) $resource->amount ) / 100 ) );
184 if ( ! empty( $resource->metadata['reason'] ) ) {
185 $note .= sprintf( ' (%s)', esc_html( $resource->metadata['reason'] ) );
186 }
187 $order->add_order_note( $note );
188
189 /**
190 * Fires once a refund response has been processed.
191 *
192 * @param int $order_id Order ID
193 * @param RefundResource $resource Refund resource
194 */
195 \do_action( 'payplug_gateway_refund_response_processed', $order_id, $resource );
196
197 try {
198 $payment = $this->gateway->api->payment_retrieve( $transaction_id );
199 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
200 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
201 } catch ( \Exception $e ) {}
202
203 PayplugGateway::log( sprintf( 'Order #%s : Refund IPN %s processing completed.', $order_id, $resource->id ) );
204 }
205
206 /**
207 * Get an order from its transaction id.
208 *
209 * @param int $transaction_id
210 *
211 * @return bool|\WC_Order
212 */
213 protected function get_order_from_transaction_id( $transaction_id ) {
214 global $wpdb;
215
216 $order_id = $wpdb->get_var(
217 $wpdb->prepare(
218 "
219 SELECT post_id
220 FROM $wpdb->postmeta
221 WHERE meta_key = '_transaction_id'
222 AND meta_value = %s
223 ",
224 $transaction_id
225 )
226 );
227
228 return ! is_null( $order_id ) ? wc_get_order( $order_id ) : false;
229 }
230
231 /**
232 * Check if a refund id already exist for the order.
233 *
234 * @param string $refund_id
235 *
236 * @return bool
237 */
238 protected function refund_exist_for_order( $order_id, $refund_id ) {
239 global $wpdb;
240
241 $sql = "
242 SELECT p.ID
243 FROM $wpdb->posts p
244 INNER JOIN $wpdb->postmeta pm
245 ON p.ID = pm.post_id
246 WHERE 1=1
247 AND p.post_type = %s
248 AND p.ID = %d
249 AND pm.meta_key LIKE '_pr_" . esc_sql( $refund_id ) . "'
250 AND pm.meta_value = %s
251 LIMIT 1
252 ";
253
254 $results = $wpdb->get_col(
255 $wpdb->prepare(
256 $sql,
257 'shop_order',
258 (int) $order_id,
259 $refund_id
260 )
261 );
262
263 return ! empty( $results ) ? true : false;
264 }
265
266 /**
267 * Save card from the transaction.
268 *
269 * @param IVerifiableAPIResource $resource
270 *
271 * @return bool
272 */
273 protected function maybe_save_card( $resource ) {
274
275 if ( ! isset( $resource->card ) || empty( $resource->card->id ) ) {
276 return false;
277 }
278
279 if ( ! isset( $resource->metadata['customer_id'] ) ) {
280 return false;
281 }
282
283 $customer = get_user_by( 'id', $resource->metadata['customer_id'] );
284 if ( ! $customer || 0 === (int) $customer->ID ) {
285 return false;
286 }
287
288 $merchant_id = $this->gateway->get_merchant_id();
289 if ( empty( $merchant_id ) ) {
290 return false;
291 }
292
293 PayplugGateway::log( sprintf( 'Saving card from transaction %s for customer %s', wc_clean( $resource->id ), $customer->ID ) );
294
295 $token = new WC_Payment_Token_CC();
296 $existing_tokens = WC_Payment_Tokens::get_customer_tokens( $customer->ID , $this->gateway->id );
297 $set_token = wc_clean( $resource->card->id );
298 $set_last4 = wc_clean( $resource->card->last4 );
299 $set_expiry_year = wc_clean( $resource->card->exp_year ) ;
300 $set_expiry_month = zeroise( (int) wc_clean( $resource->card->exp_month ), 2 ) ;
301 $set_card_type = \strtolower( wc_clean( $resource->card->brand ) ) ;
302 if(!empty($existing_tokens)) {
303 foreach($existing_tokens as $token_id => $existing_token) {
304 $current_data = $existing_token->get_data();
305 if( $current_data['token'] === $set_token &&
306 $current_data['last4'] === $set_last4 &&
307 $current_data['expiry_year'] === $set_expiry_year &&
308 $current_data['expiry_month'] === $set_expiry_month &&
309 $current_data['card_type'] === $set_card_type) {
310 $token->set_id($current_data['id']);
311 }
312 }
313 }
314
315 $token->set_token( $set_token );
316 $token->set_gateway_id( 'payplug' );
317 $token->set_last4( $set_last4 );
318 $token->set_expiry_year( $set_expiry_year );
319 $token->set_expiry_month( $set_expiry_month );
320 $token->set_card_type( $set_card_type );
321 $token->set_user_id( $customer->ID );
322 $token->add_meta_data( 'mode', $resource->is_live ? 'live' : 'test', true );
323 $token->add_meta_data( 'payplug_account', \wc_clean( $merchant_id ), true );
324 $token->save();
325
326 PayplugGateway::log( sprintf( 'Payment card saved', wc_clean( $resource->id ), $customer->ID ) );
327
328 return true;
329 }
330
331 /**
332 * Save shipping address.
333 *
334 * @param IVerifiableAPIResource $resource
335 *
336 * @return bool
337 */
338 protected function maybe_save_address_hash( $resource ) {
339
340 if ( ! isset( $resource->metadata['customer_id'] ) ) {
341 return false;
342 }
343
344 $customer = get_user_by( 'id', $resource->metadata['customer_id'] );
345 if ( ! $customer || 0 === (int) $customer->ID ) {
346 return false;
347 }
348
349 $shipping = [];
350 foreach ( PayplugAddressData::$address_fields as $field ) {
351 $shipping[ $field ] = $resource->shipping->{$field};
352 }
353
354 $shipping_hash = PayplugAddressData::hash_address( $shipping );
355 $hash_list = PayplugAddressData::get_customer_addresses_hash( $customer->ID );
356 $hash_list[] = $shipping_hash;
357 $hash_list = array_unique( $hash_list );
358 PayplugAddressData::update_customer_addresses_hash( $customer->ID, $hash_list );
359
360 return true;
361 }
362 }
363