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

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