PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.12.9
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.12.9
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / includes / core / gateways / class-wpfnl-gateway-payfast.php

class-wpfnl-gateway-payfast.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.12.9, at includes/core/gateways/class-wpfnl-gateway-payfast.php

368 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPFunnels\Gateway;
3 /**
4 * @author [Saiduzzaman Tohin]
5 * @email [[email protected]]
6 * @create date 2022-05-26 11:12:24
7 * @modify date 2022-05-26 11:12:24
8 * @desc [Supporting PayFast Payment Gateway with WPFunnels one-click offer]
9 */
10
11 use WPFunnels\Wpfnl_functions;
12
13 class Wpfnl_Gateway_PayFast {
14
15 public $key = 'payfast';
16
17 function __construct()
18 {
19 // Convert offer product to subscription type
20 add_filter('woocommerce_gateway_payfast_payment_data_to_send', array( $this, 'make_subscription_type_true' ), 10 ,2);
21 // Store token for offer product
22 add_action('woocommerce_payfast_handle_itn_payment_complete', array($this, 'store_tokenization_on_meta_post'), 10, 3);
23 }
24
25
26 /**
27 * Making a product as subscription type
28 * @param mixed $result
29 * @param mixed $order
30 *
31 * @return bool
32 * @since 1.3.3
33 */
34 public function make_subscription_type_true($result, $order_id)
35 {
36
37 $checkout_id = Wpfnl_functions::get_checkout_id_from_order($order_id);
38 $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $checkout_id );
39 if($checkout_id && $funnel_id && $order_id) {
40 $result['subscription_type'] = '2';
41 }
42 return apply_filters( 'wpfunnels/maybe_enable_payfast_tokenization', $result);
43 }
44
45
46 /**
47 * Store the PayFast tokenization information
48 *
49 * @param mixed $data payfast payment information
50 * @param mixed $order_id order id
51 *
52 * @return void
53 * @since 1.3.3
54 */
55 public function store_tokenization_on_meta_post($data, $order, $subscriptions)
56 {
57 $order_id = $order->get_id();
58 $token = sanitize_text_field( $data['token'] );
59 $merchant_id = sanitize_text_field( $data['merchant_id'] );
60 $this->_set_order_token($token, $order);
61 $this->_set_order_merchant_d($merchant_id, $order);
62
63 }
64
65
66 /**
67 * Store the PayFast token
68 *
69 * @param string $token
70 * @param WC_Order $order
71 */
72 protected function _set_order_token( $token, $order ) {
73 update_post_meta( $order->get_id(), '_payfast_subscription_token', $token );
74 $order->update_meta_data('_payfast_subscription_token', $token );
75
76 }
77
78
79 /**
80 * Store the PayFast Merchant ID
81 *
82 * @param string $merchant_id
83 * @param WC_Order $order
84 */
85 protected function _set_order_merchant_d( $merchant_id, $order ) {
86 update_post_meta( $order->get_id(), '_payfast_merchant_id', $merchant_id );
87 $order->update_meta_data('_payfast_merchant_id', $merchant_id );
88 }
89
90
91 /**
92 * Retrieve the PayFast token for a given order id.
93 *
94 * @param WC_Order $order
95 * @return mixed
96 */
97 protected function _get_order_token( $order ) {
98 return $order->get_meta('_payfast_subscription_token' ) ? $order->get_meta('_payfast_subscription_token' ) : get_post_meta( $order->get_id(), '_payfast_subscription_token', true );
99 }
100
101
102 /**
103 * Retrieve the PayFast merchant ID for a given order id.
104 *
105 * @param WC_Order $order
106 * @return mixed
107 */
108 protected function _get_order_merchant_id( $order ) {
109 return $order->get_meta('_payfast_merchant_id' ) ? $order->get_meta('_payfast_merchant_id' ) : get_post_meta( $order->get_id(), '_payfast_merchant_id', true );
110 }
111
112
113
114 /**
115 * Getting woocommerce payfast settings information
116 * @param mixed $key payment setting key
117 *
118 * @return mixed
119 * @since 1.3.3
120 */
121 public function get_payfast_settings($key)
122 {
123 $payfast_settings = get_option('woocommerce_payfast_settings');
124 return isset($payfast_settings[$key]) ? $payfast_settings[$key] : '';
125 }
126
127
128 /**
129 * Processing of the offer payment
130 *
131 * @param $order
132 * @return bool|WP_Error
133 *
134 * @since 1.3.3
135 */
136 public function process_payment( $order )
137 {
138 $result = array(
139 'is_success' => false,
140 'message' => ''
141 );
142 if ( ! $this->has_token( $order ) ) {
143 return $result;
144 }
145
146 // Get Token from the database
147 $token = $this->_get_order_token( $order );
148 $step_id = isset( $_POST['step_id'] ) ? intval( $_POST['step_id'] ) : 0;
149 $product_id = isset( $_POST['product_id'] ) ? intval( $_POST['product_id'] ) : '';
150 $order_id = isset( $_POST['order_id'] ) ? intval($_POST['order_id']) : 0;
151
152 // Get Offer Product Information
153 $offer_product = Wpfnl_functions::get_offer_product_data( $step_id, $product_id, 0, $order_id );
154 if ( isset($offer_product['price']) && (floatval(0) === floatval( $offer_product['price'] ) || '' === trim($offer_product['price'])) ) {
155 wp_send_json(array(
156 'result' => 'fail',
157 'message' => __('Product price is less than 0', 'wpfnl-pro'),
158 ));
159 } else {
160 // Submition of tokenization offer product payment
161 $response = $this->submit_tokenization_payment($token, $offer_product, $order);
162 // Success response check
163 if($response){
164 return array(
165 'is_success' => true,
166 'message' => 'Success'
167 );
168 }
169
170 // Error response check
171 if ( is_wp_error( $response ) ) {
172 $result = array(
173 'is_success' => false,
174 'message' => sprintf( __( 'PayFast Pre-Order payment transaction failed (%1$s:%2$s)', 'woocommerce-gateway-payfast' ), $response->get_error_code() ,$response->get_error_message() )
175 );
176
177 return $result;
178 }
179
180 }
181
182 }
183
184
185
186 /**
187 * Submission of offer product tokenization payment
188 *
189 * @param mixed $token
190 * @param mixed $offer_product
191 * @param mixed $order
192 *
193 * @return bool|WP_Error
194 * @since 1.3.3
195 */
196 public function submit_tokenization_payment($token, $offer_product, $order)
197 {
198 $price = (int)$offer_product['price'];
199 $product_id = (int)$offer_product['id'];
200 $product = Wpfnl_functions::is_wc_active() ? wc_get_product( $product_id ) : null;
201 $description = '';
202 if( $product ){
203 if ( $product->get_type() == 'variation' ) {
204 $description = $product->get_description();
205 } else {
206 $description = $product->get_short_description();
207 }
208 }
209
210 $merchant_id = $this->get_payfast_settings( 'merchant_id' );
211 $args = array(
212 'body' => array(
213 'amount' => $price * 100, // convert to cents
214 'item_name' => $offer_product['name'],
215 'item_description' => $description,
216 ),
217 );
218 return $this->api_request( 'adhoc', $token, $args, $merchant_id );
219 }
220
221
222
223 /**
224 * Check if token is present in the order
225 *
226 * @param $order
227 * @since 1.3.3
228 */
229 private function has_token( $order ) {
230 $token = $order->get_meta('_payfast_subscription_token');
231
232 if ( empty( $token ) ) {
233 $token = get_post_meta($order->get_id(), '_payfast_subscription_token', true);
234 }
235 if ( ! empty( $token ) ) {
236 return true;
237 }
238 return false;
239 }
240
241 /**
242 * Send off API request.
243 *
244 * @param $command
245 * @param $token
246 * @param $api_args
247 * @param string $method POST
248 *
249 * @return bool|WP_Error
250 * @since 1.3.3
251 */
252 public function api_request( $command, $token, $api_args, $merchant_id, $method = 'POST' ) {
253 if ( empty( $token ) ) {
254 $this->log( "Error posting API request: No token supplied", true );
255 return new \WP_Error( '404', __( 'Can not submit PayFast request with an empty token', 'woocommerce-gateway-payfast' ), $results );
256 }
257 $settings = get_option( 'woocommerce_payfast_settings', [] );
258 $api_endpoint = "https://api.payfast.co.za/subscriptions/$token/$command";
259 $api_endpoint .= ( isset($settings['testmode']) && 'yes' === $settings['testmode'] ) ? '?testing=true' : '';
260 $timestamp = current_time( rtrim( \DateTime::ATOM, 'P' ) ) . '+02:00';
261 $api_args['timeout'] = 45;
262 $api_args['headers'] = array(
263 'merchant-id' => $merchant_id,
264 'timestamp' => $timestamp,
265 'version' => 'v1',
266 );
267
268 // generate signature
269 $all_api_variables = array_merge( $api_args['headers'], (array) $api_args['body'] );
270 $api_args['headers']['signature'] = md5( $this->_generate_parameter_string( $all_api_variables ) );
271 $api_args['method'] = strtoupper( $method );
272
273 $results = wp_remote_request( $api_endpoint, $api_args );
274
275 // Check PayFast server response
276 if ( 200 !== $results['response']['code'] ) {
277 $this->log( "Error posting API request:\n" . print_r( $results['response'], true ) );
278 return new \WP_Error( $results['response']['code'], json_decode( $results['body'] )->data->response, $results );
279 }
280
281 // Check adhoc bank charge response
282 $results_data = json_decode( $results['body'], true )['data'];
283
284 if ( $command == 'adhoc' && (true !== $results_data['response'] && 'true' !== $results_data['response']) ) {
285
286 $this->log( "Error posting API request:\n" . print_r( $results_data , true ) );
287
288 $code = is_array( $results_data['response'] ) ? $results_data['response']['code'] : $results_data['response'];
289 $message = is_array( $results_data['response'] ) ? $results_data['response']['reason'] : $results_data['message'];
290 // Use trim here to display it properly e.g. on an order note, since PayFast can include CRLF in a message.
291 return new \WP_Error( $code, trim( $message ), $results );
292 }
293
294 $maybe_json = json_decode( $results['body'], true );
295
296 if ( ! is_null( $maybe_json ) && isset( $maybe_json['status'] ) && 'failed' === $maybe_json['status'] ) {
297 $this->log( "Error posting API request:\n" . print_r( $results['body'], true ) );
298
299 // Use trim here to display it properly e.g. on an order note, since PayFast can include CRLF in a message.
300 return new \WP_Error( $maybe_json['code'], trim( $maybe_json['data']['message'] ), $results['body'] );
301 }
302
303 return true;
304 }
305
306
307
308 /**
309 * Generate signature for api request
310 *
311 * @param $api_data
312 * @param bool $sort_data_before_merge? default true.
313 * @param bool $skip_empty_values Should key value pairs be ignored when generating signature? Default true.
314 *
315 * @return string
316 * @since 1.3.3
317 */
318 protected function _generate_parameter_string( $api_data, $sort_data_before_merge = true, $skip_empty_values = true ) {
319
320 // if sorting is required the passphrase should be added in before sort.
321 if ( ! empty( $this->get_payfast_settings('pass_phrase') ) && $sort_data_before_merge ) {
322 $api_data['passphrase'] = $this->get_payfast_settings('pass_phrase');
323 }
324
325 if ( $sort_data_before_merge ) {
326 ksort( $api_data );
327 }
328
329 // concatenate the array key value pairs.
330 $parameter_string = '';
331 foreach ( $api_data as $key => $val ) {
332
333 if ( $skip_empty_values && empty( $val ) ) {
334 continue;
335 }
336
337 if ( 'signature' !== $key ) {
338 $val = urlencode( $val );
339 $parameter_string .= "$key=$val&";
340 }
341 }
342 // when not sorting passphrase should be added to the end before md5
343 if ( $sort_data_before_merge ) {
344 $parameter_string = rtrim( $parameter_string, '&' );
345 } elseif ( ! empty( $this->get_payfast_settings('pass_phrase') ) ) {
346 $parameter_string .= 'passphrase=' . urlencode( $this->get_payfast_settings('pass_phrase') );
347 } else {
348 $parameter_string = rtrim( $parameter_string, '&' );
349 }
350
351 return $parameter_string;
352 }
353
354
355 /**
356 * Log system processes.
357 * @since 1.3.3
358 */
359 public function log( $message ) {
360 if ( 'yes' === 'yes' || $this->enable_logging ) {
361 if ( empty( $this->logger ) ) {
362 $this->logger = new \WC_Logger();
363 }
364 $this->logger->add( 'payfast', $message );
365 }
366 }
367
368 }