| 1 |
<?php |
| 2 |
namespace WPFunnels\Gateway; |
| 3 |
|
| 4 |
use WPFunnels\Wpfnl; |
| 5 |
use WPFunnels\Wpfnl_functions; |
| 6 |
use WPFunnels\Gateway\API\Wpfnl_Gateway; |
| 7 |
use WPFunnels\Gateway\Exception\Wpfnl_Payment_Gateway_Exception; |
| 8 |
|
| 9 |
class Wpfnl_Gateway_Paypal_Express extends Wpfnl_Gateway { |
| 10 |
|
| 11 |
/** |
| 12 |
* Live API url |
| 13 |
*/ |
| 14 |
const PRODUCTION_ENDPOINT = 'https://api-3t.paypal.com/nvp'; |
| 15 |
|
| 16 |
/** |
| 17 |
* Sandbox api url |
| 18 |
*/ |
| 19 |
const SANDBOX_ENDPOINT = 'https://api-3t.sandbox.paypal.com/nvp'; |
| 20 |
|
| 21 |
protected $key = 'ppec_paypal'; |
| 22 |
|
| 23 |
/** |
| 24 |
* The request parameters |
| 25 |
* |
| 26 |
* @var array |
| 27 |
*/ |
| 28 |
public $parameters = array(); |
| 29 |
|
| 30 |
public $response_params = array(); |
| 31 |
|
| 32 |
|
| 33 |
/** |
| 34 |
* List of locales supported by PayPal. |
| 35 |
* |
| 36 |
* @var array |
| 37 |
*/ |
| 38 |
protected $_supported_locales = array( |
| 39 |
'da_DK', |
| 40 |
'de_DE', |
| 41 |
'en_AU', |
| 42 |
'en_GB', |
| 43 |
'en_US', |
| 44 |
'es_ES', |
| 45 |
'fr_CA', |
| 46 |
'fr_FR', |
| 47 |
'he_IL', |
| 48 |
'id_ID', |
| 49 |
'it_IT', |
| 50 |
'ja_JP', |
| 51 |
'nl_NL', |
| 52 |
'no_NO', |
| 53 |
'pl_PL', |
| 54 |
'pt_BR', |
| 55 |
'pt_PT', |
| 56 |
'ru_RU', |
| 57 |
'sv_SE', |
| 58 |
'th_TH', |
| 59 |
'tr_TR', |
| 60 |
'zh_CN', |
| 61 |
'zh_HK', |
| 62 |
'zh_TW', |
| 63 |
); |
| 64 |
|
| 65 |
public function __construct() { |
| 66 |
add_filter( 'woocommerce_paypal_express_checkout_request_body', array( $this, 'modify_request_body' ), 999 ); |
| 67 |
add_action( 'wp_enqueue_scripts', array( $this, 'load_payment_scripts' ), 9999 ); |
| 68 |
// add_filter( 'woocommerce_paypal_refund_request', array( $this, 'wpfnl_offer_refund_request' ), 10, 3 ); |
| 69 |
} |
| 70 |
|
| 71 |
|
| 72 |
/** |
| 73 |
* modify paypal express request body params |
| 74 |
* |
| 75 |
* @param $request_body |
| 76 |
* @return mixed |
| 77 |
* |
| 78 |
* @since 1.0.0 |
| 79 |
*/ |
| 80 |
public function modify_request_body( $data ) { |
| 81 |
|
| 82 |
if(!$this->is_enabled()) return $data; |
| 83 |
|
| 84 |
|
| 85 |
|
| 86 |
|
| 87 |
if ( true === $this->is_reference_transaction_enabled() ) { |
| 88 |
|
| 89 |
// // translators: blog name. |
| 90 |
// $description = sprintf( _x( 'Orders with %s', 'data sent to PayPal', 'wpfunnels' ), get_bloginfo( 'name' ) ); |
| 91 |
|
| 92 |
// $description = html_entity_decode( $description, ENT_NOQUOTES, 'UTF-8' ); |
| 93 |
|
| 94 |
if ( $data && isset( $data['METHOD'] ) && 'SetExpressCheckout' == $data['METHOD'] && ! isset( $data['L_BILLINGTYPE0'] ) ) { |
| 95 |
$data['RETURNURL'] = add_query_arg( array( 'create-billing-agreement' => true ), $data['RETURNURL'] ); |
| 96 |
$data['L_BILLINGTYPE0'] = 'MerchantInitiatedBillingSingleAgreement'; |
| 97 |
$data['L_BILLINGAGREEMENTDESCRIPTION0'] = $this->get_billing_agreement_description(); |
| 98 |
$data['L_BILLINGAGREEMENTCUSTOM0'] = ''; |
| 99 |
} |
| 100 |
|
| 101 |
if ( $data && isset( $data['METHOD'] ) && 'DoReferenceTransaction' == $data['METHOD'] ) { |
| 102 |
|
| 103 |
$step_id = isset( $_POST['step_id'] ) ? (int) $_POST['step_id'] : 0; |
| 104 |
$order_id = isset( $_POST['order_id'] ) ? (int) $_POST['order_id'] : 0; |
| 105 |
|
| 106 |
// Return if the step id is not in the query string. |
| 107 |
if ( $step_id < 1 ) { |
| 108 |
return $data; |
| 109 |
} |
| 110 |
|
| 111 |
// Return if the order id is not in the query string. |
| 112 |
if ( $order_id < 1 ) { |
| 113 |
return $data; |
| 114 |
} |
| 115 |
|
| 116 |
$order = wc_get_order( $order_id ); |
| 117 |
$variation_id = isset( $_POST['variation_id'] ) ? intval( $_POST['variation_id'] ) : ''; |
| 118 |
$input_qty = isset( $_POST['input_qty'] ) ? intval( $_POST['input_qty'] ) : ''; |
| 119 |
$offer_package = Wpfnl_functions::get_offer_product_data($step_id); |
| 120 |
|
| 121 |
$data['AMT'] = $offer_package['total']; |
| 122 |
$data['ITEMAMT'] = $offer_package['total']; |
| 123 |
|
| 124 |
// shippingamt shoud be 0. |
| 125 |
if ( ( isset( $offer_package['shipping'] ) && isset( $offer_package['shipping']['diff'] ) ) && 0 < $offer_package['shipping']['diff'] ) { |
| 126 |
$data['SHIPPINGAMT'] = 0; |
| 127 |
$data['SHIPDISCAMT'] = ( isset( $offer_package['shipping'] ) && isset( $offer_package['shipping']['diff'] ) ) ? $offer_package['shipping']['diff']['cost'] : 0; |
| 128 |
|
| 129 |
} else { |
| 130 |
$data['SHIPPINGAMT'] = ( isset( $offer_package['shipping'] ) && isset( $offer_package['shipping']['diff'] ) ) ? $offer_package['shipping']['diff']['cost'] : 0; |
| 131 |
$data['SHIPDISCAMT'] = 0; |
| 132 |
} |
| 133 |
|
| 134 |
$data['TAXAMT'] = ( isset( $offer_package['taxes'] ) ) ? $offer_package['taxes'] : 0; |
| 135 |
$data['INVNUM'] = 'WC-' . $order_id . '_' . $step_id; |
| 136 |
$data['INSURANCEAMT'] = 0; |
| 137 |
$data['HANDLINGAMT'] = 0; |
| 138 |
$data = $this->remove_previous_line_items( $data ); |
| 139 |
|
| 140 |
$data['L_NAME0'] = $offer_package['name']; |
| 141 |
$data['L_DESC0'] = $offer_package['desc']; |
| 142 |
$data['L_AMT0'] = wc_format_decimal( $offer_package['unit_price_tax'], 2 ); |
| 143 |
$data['L_QTY0'] = $offer_package['qty']; |
| 144 |
|
| 145 |
$item_amt = $offer_package['total']; |
| 146 |
|
| 147 |
$data['ITEMAMT'] = $item_amt; |
| 148 |
} |
| 149 |
|
| 150 |
if ( isset( $data['METHOD'] ) && 'DoExpressCheckoutPayment' == $data['METHOD'] ) { |
| 151 |
|
| 152 |
if ( isset( $data['PAYMENTREQUEST_0_CUSTOM'] ) ) { |
| 153 |
$get_custom_attrs = json_decode( $data['PAYMENTREQUEST_0_CUSTOM'] ); |
| 154 |
if ( isset( $get_custom_attrs->order_id ) ) { |
| 155 |
$get_order = wc_get_order( $get_custom_attrs->order_id ); |
| 156 |
|
| 157 |
try { |
| 158 |
$checkout = wc_gateway_ppec()->checkout; |
| 159 |
$checkout_details = $checkout->get_checkout_details( $data['TOKEN'] ); |
| 160 |
|
| 161 |
$checkout->create_billing_agreement( $get_order, $checkout_details ); |
| 162 |
|
| 163 |
$token = $get_order->get_meta( '_ppec_billing_agreement_id' ); |
| 164 |
|
| 165 |
if ( ! empty( $token ) ) { |
| 166 |
|
| 167 |
// Saving meta by our own. |
| 168 |
$get_order->update_meta_data('_ppec_billing_agreement_id', $token ); |
| 169 |
} |
| 170 |
} catch ( \Exception $e ) { |
| 171 |
print_r($e->getMessage()); |
| 172 |
} |
| 173 |
} |
| 174 |
} |
| 175 |
} |
| 176 |
} |
| 177 |
|
| 178 |
return $data; |
| 179 |
} |
| 180 |
|
| 181 |
|
| 182 |
/** |
| 183 |
* Remove line items |
| 184 |
* |
| 185 |
* @since 1.0.0 |
| 186 |
* |
| 187 |
* @param array $array object. |
| 188 |
* |
| 189 |
* @return array |
| 190 |
*/ |
| 191 |
public function remove_previous_line_items( $array ) { |
| 192 |
|
| 193 |
if ( is_array( $array ) && count( $array ) > 0 ) { |
| 194 |
foreach ( $array as $key => $val ) { |
| 195 |
if ( false !== strpos( strtoupper( $key ), 'L_' ) ) { |
| 196 |
unset( $array[ $key ] ); |
| 197 |
} |
| 198 |
} |
| 199 |
} |
| 200 |
|
| 201 |
return $array; |
| 202 |
} |
| 203 |
|
| 204 |
|
| 205 |
/** |
| 206 |
* Get billing agreement description to be passed to PayPal. |
| 207 |
* |
| 208 |
* @return string Billing agreement description |
| 209 |
* @since 1.0.0 |
| 210 |
* |
| 211 |
*/ |
| 212 |
protected function get_billing_agreement_description() { |
| 213 |
/* translators: placeholder is blogname */ |
| 214 |
$description = sprintf( _x( 'Orders with %s', 'data sent to PayPal', 'wpfunnels' ), get_bloginfo( 'name' ) ); |
| 215 |
|
| 216 |
if ( strlen( $description ) > 127 ) { |
| 217 |
$description = substr( $description, 0, 124 ) . '...'; |
| 218 |
} |
| 219 |
|
| 220 |
return html_entity_decode( $description, ENT_NOQUOTES, 'UTF-8' ); |
| 221 |
} |
| 222 |
|
| 223 |
|
| 224 |
|
| 225 |
/** |
| 226 |
* check if paypal scripts should load or not |
| 227 |
* |
| 228 |
* @return bool |
| 229 |
* |
| 230 |
* @since 1.0.0 |
| 231 |
*/ |
| 232 |
private function may_be_load_paypal_scripts() { |
| 233 |
|
| 234 |
if ( (Wpfnl_functions::check_if_this_is_step_type('upsell') || Wpfnl_functions::check_if_this_is_step_type('downsell')) && $this->has_paypal_gateway()) { |
| 235 |
return true; |
| 236 |
} |
| 237 |
return false; |
| 238 |
} |
| 239 |
|
| 240 |
|
| 241 |
/** |
| 242 |
* load paypal payment js |
| 243 |
*/ |
| 244 |
public function load_payment_scripts() { |
| 245 |
|
| 246 |
if ( $this->may_be_load_paypal_scripts() ) { |
| 247 |
wp_enqueue_script( |
| 248 |
'wpfnl-paypal-express-script', |
| 249 |
'https://www.paypalobjects.com/api/checkout.js', |
| 250 |
array( 'jquery' ), |
| 251 |
WPFNL_PRO_VERSION, |
| 252 |
true |
| 253 |
); |
| 254 |
$script = $this->paypal_script(); |
| 255 |
wp_add_inline_script( 'wpfnl-paypal-express-script', $script ); |
| 256 |
} |
| 257 |
} |
| 258 |
|
| 259 |
/** |
| 260 |
* paypal script |
| 261 |
* |
| 262 |
*/ |
| 263 |
public function paypal_script(){ |
| 264 |
|
| 265 |
ob_start(); |
| 266 |
?> |
| 267 |
|
| 268 |
(function($){ $( function($) { |
| 269 |
var $wpfnl_paypal_checkout = { |
| 270 |
init: function () { |
| 271 |
var getButtons = [ |
| 272 |
'upsell_funnel_target', |
| 273 |
]; |
| 274 |
window.paypalCheckoutReady = function () { |
| 275 |
paypal.checkout.setup( |
| 276 |
'<?php echo esc_js($this->get_payer_id()); ?>', |
| 277 |
{ |
| 278 |
environment: 'sandbox', |
| 279 |
buttons: getButtons, |
| 280 |
container: 'myContainer', |
| 281 |
locale: '<?php echo esc_js( $this->get_paypal_locale() ); ?>', |
| 282 |
click: function () { |
| 283 |
var variation_id = 0; |
| 284 |
var postData = { |
| 285 |
step_id: window.WPFunnelsOfferVars.step_id, |
| 286 |
funnel_id: window.WPFunnelsOfferVars.funnel_id, |
| 287 |
order_id: window.WPFunnelsOfferVars.order_id, |
| 288 |
order_key: window.WPFunnelsOfferVars.order_key, |
| 289 |
variation_id: 0, |
| 290 |
input_qty: 0, |
| 291 |
action: 'wpfunnels_create_paypal_express_checkout_token' |
| 292 |
}; |
| 293 |
|
| 294 |
paypal.checkout.initXO(); |
| 295 |
var action = $.post(window.WPFunnelsOfferVars.ajaxUrl, postData); |
| 296 |
|
| 297 |
action.done(function (data) { |
| 298 |
paypal.checkout.startFlow(data.token); |
| 299 |
}); |
| 300 |
|
| 301 |
action.fail(function () { |
| 302 |
paypal.checkout.closeFlow(); |
| 303 |
}); |
| 304 |
} |
| 305 |
}); |
| 306 |
} |
| 307 |
} |
| 308 |
}; |
| 309 |
|
| 310 |
$wpfnl_paypal_checkout.init(); |
| 311 |
}); |
| 312 |
})(jQuery); |
| 313 |
<?php return ob_get_clean(); |
| 314 |
} |
| 315 |
|
| 316 |
|
| 317 |
/** |
| 318 |
* Get locale data for PayPal. |
| 319 |
* |
| 320 |
* @return string |
| 321 |
* |
| 322 |
* @since 1.0.0 |
| 323 |
*/ |
| 324 |
public function get_paypal_locale() { |
| 325 |
$locale = get_locale(); |
| 326 |
if ( ! in_array( $locale, $this->_supported_locales, true ) ) { |
| 327 |
$locale = 'en_US'; |
| 328 |
} |
| 329 |
|
| 330 |
return $locale; |
| 331 |
} |
| 332 |
|
| 333 |
/** |
| 334 |
* check id reference transaction is enabled |
| 335 |
* |
| 336 |
* @return bool |
| 337 |
*/ |
| 338 |
public function is_reference_transaction_enabled() { |
| 339 |
return false; |
| 340 |
} |
| 341 |
|
| 342 |
|
| 343 |
/** |
| 344 |
* check if api creds are saved or not |
| 345 |
* |
| 346 |
* @return bool |
| 347 |
*/ |
| 348 |
public function has_api_credentials_set() { |
| 349 |
$credentials_are_set = false; |
| 350 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'production'; |
| 351 |
|
| 352 |
$api_creds_prefix = ''; |
| 353 |
if ('sandbox' === $environment) { |
| 354 |
$api_creds_prefix = 'sandbox_'; |
| 355 |
} |
| 356 |
|
| 357 |
if ('' !== $this->get_wc_gateway()->get_option($api_creds_prefix . 'api_username') && '' !== $this->get_wc_gateway()->get_option($api_creds_prefix . 'api_password') && '' !== $this->get_wc_gateway()->get_option($api_creds_prefix . 'api_signature')) { |
| 358 |
$credentials_are_set = true; |
| 359 |
} |
| 360 |
|
| 361 |
return $credentials_are_set; |
| 362 |
} |
| 363 |
|
| 364 |
|
| 365 |
/** |
| 366 |
* Check if current order has paypal gatway |
| 367 |
* |
| 368 |
* @return bool |
| 369 |
*/ |
| 370 |
public function has_paypal_gateway() { |
| 371 |
|
| 372 |
$order_id = isset( $_GET['wpfnl-order'] ) ? $_GET['wpfnl-order'] : ''; |
| 373 |
|
| 374 |
if ( empty( $order_id ) ) { |
| 375 |
return false; |
| 376 |
} |
| 377 |
|
| 378 |
$order = wc_get_order( $order_id ); |
| 379 |
|
| 380 |
$gateway = $order->get_payment_method(); |
| 381 |
|
| 382 |
if ( $this->get_key() === $gateway ) { |
| 383 |
return true; |
| 384 |
} |
| 385 |
|
| 386 |
return false; |
| 387 |
} |
| 388 |
|
| 389 |
|
| 390 |
public function process_payment( $offer_product, $order_id, $order_key ) { |
| 391 |
|
| 392 |
} |
| 393 |
|
| 394 |
/** |
| 395 |
* get payer id |
| 396 |
* |
| 397 |
* @return bool|mixed|void |
| 398 |
*/ |
| 399 |
public function get_payer_id() { |
| 400 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'production'; |
| 401 |
|
| 402 |
$api_creds_prefix = ''; |
| 403 |
if ('sandbox' === $environment) { |
| 404 |
$api_creds_prefix = 'sandbox_'; |
| 405 |
} |
| 406 |
|
| 407 |
$option_key = 'woocommerce_ppec_payer_id_' . $environment . '_' . md5($this->get_wc_gateway()->get_option($api_creds_prefix . 'api_username') . ':' . $this->get_wc_gateway()->get_option($api_creds_prefix . 'api_password')); |
| 408 |
$payer_id = get_option($option_key); |
| 409 |
if ($payer_id) { |
| 410 |
return $payer_id; |
| 411 |
} else { |
| 412 |
$result = $this->get_pal_details(); |
| 413 |
|
| 414 |
if (!empty($result['PAL'])) { |
| 415 |
update_option($option_key, wc_clean($result['PAL'])); |
| 416 |
return $payer_id; |
| 417 |
} |
| 418 |
} |
| 419 |
|
| 420 |
return false; |
| 421 |
} |
| 422 |
|
| 423 |
|
| 424 |
/** |
| 425 |
* get the paypal id, including the merchant account number |
| 426 |
* |
| 427 |
* @return object|API\SV_WC_API_Response |
| 428 |
*/ |
| 429 |
public function get_pal_details() { |
| 430 |
|
| 431 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'production'; |
| 432 |
$api_creds_prefix = ''; |
| 433 |
if ('sandbox' === $environment) { |
| 434 |
$api_creds_prefix = 'sandbox_'; |
| 435 |
} |
| 436 |
$this->set_api_credentials( |
| 437 |
$this->get_key(), |
| 438 |
$environment, |
| 439 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_username'), |
| 440 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_password'), |
| 441 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_signature') |
| 442 |
); |
| 443 |
$this->add_parameter('METHOD', 'GetPalDetails'); |
| 444 |
$this->set_credentials_params( |
| 445 |
$this->api_username, |
| 446 |
$this->api_password, |
| 447 |
$this->api_signature, |
| 448 |
124 |
| 449 |
); |
| 450 |
|
| 451 |
|
| 452 |
$request = new \stdClass(); |
| 453 |
$request->path = ''; |
| 454 |
$request->method = 'POST'; |
| 455 |
$request->body = $this->to_string(); |
| 456 |
return $this->perform_request($request); |
| 457 |
|
| 458 |
} |
| 459 |
|
| 460 |
|
| 461 |
/** |
| 462 |
* Create express checkout token ajax action. |
| 463 |
* It will return checkout token for express checkout |
| 464 |
*/ |
| 465 |
public function create_express_checkout_token() { |
| 466 |
|
| 467 |
$step_id = isset( $_POST['step_id'] ) ? intval( $_POST['step_id'] ) : 0; |
| 468 |
$funnel_id = isset( $_POST['funnel_id'] ) ? intval( $_POST['funnel_id'] ) : 0; |
| 469 |
$order_id = isset( $_POST['order_id'] ) ? intval( $_POST['order_id'] ) : 0; |
| 470 |
$order_key = isset( $_POST['order_key'] ) ? sanitize_text_field( wp_unslash( $_POST['order_key'] ) ) : ''; |
| 471 |
$session_key = isset( $_POST['session_key'] ) ? sanitize_text_field( wp_unslash( $_POST['session_key'] ) ) : ''; |
| 472 |
$variation_id = isset( $_POST['variation_id'] ) ? intval( $_POST['variation_id'] ) : ''; |
| 473 |
$input_qty = isset( $_POST['input_qty'] ) ? intval( $_POST['input_qty'] ) : ''; |
| 474 |
|
| 475 |
$is_valid_order = true; |
| 476 |
|
| 477 |
if ( $is_valid_order ) { |
| 478 |
$order = wc_get_order( $order_id ); |
| 479 |
$response = $this->init_express_checkout( |
| 480 |
array( |
| 481 |
'currency' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 482 |
'return_url' => $this->get_callback_url( |
| 483 |
array( |
| 484 |
'action' => 'wpfunnels_paypal_express_return', |
| 485 |
'step_id' => $step_id, |
| 486 |
'order_id' => $order->get_id(), |
| 487 |
'funnel_id' => $funnel_id, |
| 488 |
'order_key' => $order_key, |
| 489 |
) |
| 490 |
), |
| 491 |
'cancel_url' => $this->get_callback_url( |
| 492 |
array( |
| 493 |
'action' => 'wpfunnels_paypal_express_cancel', |
| 494 |
'step_id' => $step_id, |
| 495 |
'funnel_id' => $funnel_id, |
| 496 |
'order_id' => $order->get_id(), |
| 497 |
) |
| 498 |
), |
| 499 |
'notify_url' => $this->get_callback_url( 'wpfunnels_notify_url' ), |
| 500 |
'order' => $order, |
| 501 |
'step_id' => $step_id, |
| 502 |
'variation_id' => $variation_id, |
| 503 |
'input_qty' => $input_qty, |
| 504 |
), |
| 505 |
true |
| 506 |
); |
| 507 |
|
| 508 |
|
| 509 |
if ( isset( $response['TOKEN'] ) && '' !== $response['TOKEN'] ) { |
| 510 |
wp_send_json( |
| 511 |
array( |
| 512 |
'result' => 'success', |
| 513 |
'token' => $response['TOKEN'], |
| 514 |
) |
| 515 |
); |
| 516 |
} |
| 517 |
} |
| 518 |
|
| 519 |
wp_send_json( |
| 520 |
array( |
| 521 |
'result' => 'error', |
| 522 |
'response' => $response, |
| 523 |
) |
| 524 |
); |
| 525 |
} |
| 526 |
|
| 527 |
|
| 528 |
/** |
| 529 |
* Initiate express checkout request |
| 530 |
* |
| 531 |
* @param $args |
| 532 |
* @param bool $is_upsell |
| 533 |
* @return object|API\SV_WC_API_Response |
| 534 |
*/ |
| 535 |
public function init_express_checkout( $args, $is_upsell = false ) { |
| 536 |
|
| 537 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'production'; |
| 538 |
|
| 539 |
$api_creds_prefix = ''; |
| 540 |
if ('sandbox' === $environment) { |
| 541 |
$api_creds_prefix = 'sandbox_'; |
| 542 |
} |
| 543 |
|
| 544 |
$this->set_api_credentials( |
| 545 |
$this->key, |
| 546 |
$environment, |
| 547 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_username'), |
| 548 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_password'), |
| 549 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_signature') |
| 550 |
); |
| 551 |
|
| 552 |
$this->set_express_checkout_args( $args, $is_upsell ); |
| 553 |
|
| 554 |
$this->set_credentials_params( |
| 555 |
$this->api_username, |
| 556 |
$this->api_password, |
| 557 |
$this->api_signature, |
| 558 |
124 |
| 559 |
); |
| 560 |
$request = new \stdClass(); |
| 561 |
$request->path = ''; |
| 562 |
$request->method = 'POST'; |
| 563 |
$request->body = $this->to_string(); |
| 564 |
|
| 565 |
WC()->session->set( 'paypal_request' , $this->get_parameters() ); |
| 566 |
|
| 567 |
return $this->perform_request($request); |
| 568 |
} |
| 569 |
|
| 570 |
|
| 571 |
/** |
| 572 |
* Sets up DoExpressCheckoutPayment API Call arguments |
| 573 |
* |
| 574 |
* @param string $token Unique token of the payment initiated |
| 575 |
* @param \WC_Order $order |
| 576 |
* @param array $args |
| 577 |
*/ |
| 578 |
public function set_do_express_checkout_args($token, $order, $args) |
| 579 |
{ |
| 580 |
$this->set_method('DoExpressCheckoutPayment'); |
| 581 |
|
| 582 |
// set base params |
| 583 |
$this->add_parameters(array( |
| 584 |
'TOKEN' => $token, |
| 585 |
'PAYERID' => $args['payer_id'], |
| 586 |
'BUTTONSOURCE' => 'WooThemes_Cart', |
| 587 |
'RETURNFMFDETAILS' => 1, |
| 588 |
)); |
| 589 |
|
| 590 |
$this->add_payment_details_parameters($order, $args['payment_action']); |
| 591 |
} |
| 592 |
|
| 593 |
|
| 594 |
/** |
| 595 |
* Do express checkout |
| 596 |
* |
| 597 |
* @param $token |
| 598 |
* @param $order |
| 599 |
* @param $args |
| 600 |
* @return object|string |
| 601 |
* |
| 602 |
* @since 1.0.0 |
| 603 |
*/ |
| 604 |
private function do_express_checkout($token, $order, $args) { |
| 605 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'production'; |
| 606 |
|
| 607 |
$api_creds_prefix = ''; |
| 608 |
if ('sandbox' === $environment) { |
| 609 |
$api_creds_prefix = 'sandbox_'; |
| 610 |
} |
| 611 |
|
| 612 |
$this->set_api_credentials( |
| 613 |
$this->key, |
| 614 |
$environment, |
| 615 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_username'), |
| 616 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_password'), |
| 617 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_signature') |
| 618 |
); |
| 619 |
|
| 620 |
$this->set_do_express_checkout_args($token, $order, $args); |
| 621 |
|
| 622 |
$this->set_credentials_params( |
| 623 |
$this->api_username, |
| 624 |
$this->api_password, |
| 625 |
$this->api_signature, |
| 626 |
124 |
| 627 |
); |
| 628 |
$request = new \stdClass(); |
| 629 |
$request->path = ''; |
| 630 |
$request->method = 'POST'; |
| 631 |
$request->body = $this->to_string(); |
| 632 |
return $this->perform_request($request); |
| 633 |
} |
| 634 |
|
| 635 |
|
| 636 |
/** |
| 637 |
* Sets up the API credentials for API request |
| 638 |
* |
| 639 |
* @param $gateway_id |
| 640 |
* @param $api_environment |
| 641 |
* @param $api_username |
| 642 |
* @param $api_password |
| 643 |
* @param $api_signature |
| 644 |
* |
| 645 |
* @since 1.0.0 |
| 646 |
*/ |
| 647 |
private function set_api_credentials( $gateway_id, $api_environment, $api_username, $api_password, $api_signature ) { |
| 648 |
// tie API to gateway |
| 649 |
$this->gateway_id = $gateway_id; |
| 650 |
|
| 651 |
// request URI does not vary per-request |
| 652 |
$this->request_uri = ('production' === $api_environment) ? self::PRODUCTION_ENDPOINT : self::SANDBOX_ENDPOINT; |
| 653 |
|
| 654 |
// PayPal requires HTTP 1.1 |
| 655 |
$this->request_http_version = '1.1'; |
| 656 |
|
| 657 |
$this->api_username = $api_username; |
| 658 |
$this->api_password = $api_password; |
| 659 |
$this->api_signature = $api_signature; |
| 660 |
} |
| 661 |
|
| 662 |
|
| 663 |
/** |
| 664 |
* Sets up the express checkout transaction |
| 665 |
* |
| 666 |
* @link https://developer.paypal.com/docs/classic/express-checkout/integration-guide/ECGettingStarted/#id084RN060BPF |
| 667 |
* @link https://developer.paypal.com/webapps/developer/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/ |
| 668 |
* |
| 669 |
* @param array $args { |
| 670 |
* @type string 'currency' (Optional) A 3-character currency code (default is store's currency). |
| 671 |
* @type string 'billing_type' (Optional) Type of billing agreement for reference transactions. You must have permission from PayPal to use this field. This field must be set to one of the following values: MerchantInitiatedBilling - PayPal creates a billing agreement for each transaction associated with buyer. You must specify version 54.0 or higher to use this option; MerchantInitiatedBillingSingleAgreement - PayPal creates a single billing agreement for all transactions associated with buyer. Use this value unless you need per-transaction billing agreements. You must specify version 58.0 or higher to use this option. |
| 672 |
* @type string 'billing_description' (Optional) Description of goods or services associated with the billing agreement. This field is required for each recurring payment billing agreement if using MerchantInitiatedBilling as the billing type, that means you can use a different agreement for each subscription/order. PayPal recommends that the description contain a brief summary of the billing agreement terms and conditions (but this only makes sense when the billing type is MerchantInitiatedBilling, otherwise the terms will be incorrectly displayed for all agreements). For example, buyer is billed at "9.99 per month for 2 years". |
| 673 |
* @type string 'maximum_amount' (Optional) The expected maximum total amount of the complete order and future payments, including shipping cost and tax charges. If you pass the expected average transaction amount (default 25.00). PayPal uses this value to validate the buyer's funding source. |
| 674 |
* @type string 'no_shipping' (Optional) Determines where or not PayPal displays shipping address fields on the PayPal pages. For digital goods, this field is required, and you must set it to 1. It is one of the following values: 0 – PayPal displays the shipping address on the PayPal pages; 1 – PayPal does not display shipping address fields whatsoever (default); 2 – If you do not pass the shipping address, PayPal obtains it from the buyer's account profile. |
| 675 |
* @type string 'page_style' (Optional) Name of the Custom Payment Page Style for payment pages associated with this button or link. It corresponds to the HTML variable page_style for customizing payment pages. It is the same name as the Page Style Name you chose to add or edit the page style in your PayPal Account profile. |
| 676 |
* @type string 'brand_name' (Optional) A label that overrides the business name in the PayPal account on the PayPal hosted checkout pages. Default: store name. |
| 677 |
* @type string 'landing_page' (Optional) Type of PayPal page to display. It is one of the following values: 'login' – PayPal account login (default); 'Billing' – Non-PayPal account. |
| 678 |
* @type string 'payment_action' (Optional) How you want to obtain payment. If the transaction does not include a one-time purchase, this field is ignored. Default 'Sale' – This is a final sale for which you are requesting payment (default). Alternative: 'Authorization' – This payment is a basic authorization subject to settlement with PayPal Authorization and Capture. You cannot set this field to Sale in SetExpressCheckout request and then change the value to Authorization or Order in the DoExpressCheckoutPayment request. If you set the field to Authorization or Order in SetExpressCheckout, you may set the field to Sale. |
| 679 |
* @type string 'return_url' (Required) URL to which the buyer's browser is returned after choosing to pay with PayPal. |
| 680 |
* @type string 'cancel_url' (Required) URL to which the buyer is returned if the buyer does not approve the use of PayPal to pay you. |
| 681 |
* @type string 'custom' (Optional) A free-form field for up to 256 single-byte alphanumeric characters |
| 682 |
* } |
| 683 |
* @since 1.0.0 |
| 684 |
* @source https://github.com/wp-premium/woocommerce-subscriptions/blob/master/includes/gateways/paypal/includes/class-wcs-paypal-reference-transaction-api-request.php#L68 |
| 685 |
*/ |
| 686 |
public function set_express_checkout_args( $args, $is_upsell = false ) { |
| 687 |
|
| 688 |
// translators: placeholder is blogname |
| 689 |
$default_description = sprintf( _x( 'Orders with %s', 'data sent to paypal', 'wpfnl-pro' ), get_bloginfo( 'name' ) ); |
| 690 |
|
| 691 |
$defaults = array( |
| 692 |
'currency' => get_woocommerce_currency(), |
| 693 |
'billing_type' => 'MerchantInitiatedBillingSingleAgreement', |
| 694 |
// translators: placeholder is for blog name |
| 695 |
'billing_description' => html_entity_decode( apply_filters( 'woocommerce_subscriptions_paypal_billing_agreement_description', $default_description, $args ), ENT_NOQUOTES, 'UTF-8' ), |
| 696 |
'maximum_amount' => null, |
| 697 |
'no_shipping' => 1, |
| 698 |
'page_style' => null, |
| 699 |
'brand_name' => html_entity_decode( get_bloginfo( 'name' ), ENT_NOQUOTES, 'UTF-8' ), |
| 700 |
'landing_page' => 'login', |
| 701 |
'payment_action' => 'Sale', |
| 702 |
'custom' => '', |
| 703 |
); |
| 704 |
|
| 705 |
$args = wp_parse_args( $args, $defaults ); |
| 706 |
|
| 707 |
$this->set_method( 'SetExpressCheckout' ); |
| 708 |
|
| 709 |
$this->add_parameters( array( |
| 710 |
'RETURNURL' => $args['return_url'], |
| 711 |
'CANCELURL' => $args['cancel_url'], |
| 712 |
'PAGESTYLE' => $args['page_style'], |
| 713 |
'BRANDNAME' => $args['brand_name'], |
| 714 |
'LANDINGPAGE' => ( 'login' == $args['landing_page'] ) ? 'Login' : 'Billing', |
| 715 |
'NOSHIPPING' => $args['no_shipping'], |
| 716 |
'MAXAMT' => $args['maximum_amount'], |
| 717 |
) ); |
| 718 |
|
| 719 |
if ( false === $is_upsell ) { |
| 720 |
$this->add_parameter( 'L_BILLINGTYPE0', $args['billing_type'] ); |
| 721 |
$this->add_parameter( 'L_BILLINGAGREEMENTDESCRIPTION0', get_bloginfo( 'name' ) ); |
| 722 |
$this->add_parameter( 'L_BILLINGAGREEMENTCUSTOM0', '' ); |
| 723 |
} |
| 724 |
|
| 725 |
// if we have an order, the request is to create a subscription/process a payment (not just check if the PayPal account supports Reference Transactions) |
| 726 |
if ( isset( $args['order'] ) ) { |
| 727 |
|
| 728 |
if ( true === $is_upsell ) { |
| 729 |
$this->add_payment_details_parameters( $args['order'], $args['step_id'], $args['payment_action'], false, true, $args['variation_id'], $args['input_qty'] ); |
| 730 |
|
| 731 |
} else { |
| 732 |
$this->add_payment_details_parameters( $args['order'], $args['payment_action'], false, false ); |
| 733 |
|
| 734 |
} |
| 735 |
} |
| 736 |
|
| 737 |
$args['no_shipping'] = 0; |
| 738 |
if ( empty( $args['no_shipping'] ) ) { |
| 739 |
$this->maybe_add_shipping_address_params( $args['order'] ); |
| 740 |
|
| 741 |
} |
| 742 |
$set_express_checkout_params = apply_filters( 'wpfunnels/paypal_param_setexpresscheckout', $this->get_parameters(), $is_upsell ); |
| 743 |
if ( isset( $set_express_checkout_params['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'] ) && 2 === strlen( $set_express_checkout_params['PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE'] ) ) { |
| 744 |
$set_express_checkout_params['ADDRESSOVERRIDE'] = '1'; |
| 745 |
} |
| 746 |
|
| 747 |
$this->clean_params(); |
| 748 |
$this->add_parameters( $set_express_checkout_params ); |
| 749 |
} |
| 750 |
|
| 751 |
|
| 752 |
/** |
| 753 |
* Set the method for the request, currently using: |
| 754 |
* |
| 755 |
* + `SetExpressCheckout` - setup transaction |
| 756 |
* + `GetExpressCheckout` - gets buyers info from PayPal |
| 757 |
* + `DoExpressCheckoutPayment` - completes the transaction |
| 758 |
* + `DoCapture` - captures a previously authorized transaction |
| 759 |
* |
| 760 |
* @param string $method |
| 761 |
* @since 1.0.0 |
| 762 |
*/ |
| 763 |
private function set_method( $method ) { |
| 764 |
$this->add_parameter( 'METHOD', $method ); |
| 765 |
} |
| 766 |
|
| 767 |
|
| 768 |
/** |
| 769 |
* Set up the payment details for a DoExpressCheckoutPayment or DoReferenceTransaction request |
| 770 |
* |
| 771 |
* @param \WC_Order $order |
| 772 |
* @param $step_id |
| 773 |
* @param $type |
| 774 |
* @param bool $use_deprecated_params whether to use deprecated PayPal NVP parameters (required for DoReferenceTransaction API calls) |
| 775 |
* @param false $is_offer_charge |
| 776 |
* @param string $variation_id |
| 777 |
* @param string $input_qty |
| 778 |
* |
| 779 |
* @since 1.0.0 |
| 780 |
*/ |
| 781 |
protected function add_payment_details_parameters( \WC_Order $order, $step_id, $type, $use_deprecated_params = false, $is_offer_charge = false, $variation_id = '', $input_qty = '' ) { |
| 782 |
|
| 783 |
$calculated_total = 0; |
| 784 |
$order_subtotal = 0; |
| 785 |
$item_count = 0; |
| 786 |
$order_items = array(); |
| 787 |
|
| 788 |
$offer_data = Wpfnl_functions::get_offer_product_data( $step_id ); |
| 789 |
if ( true === $is_offer_charge ) { |
| 790 |
$order_items[] = array( |
| 791 |
'NAME' => $offer_data['name'], |
| 792 |
'DESC' => $offer_data['desc'], |
| 793 |
'AMT' => $this->round( $offer_data['price'] ), |
| 794 |
'QTY' => 1, |
| 795 |
'ITEMURL' => $offer_data['url'], |
| 796 |
); |
| 797 |
$order_subtotal += $offer_data['total_unit_price_amount']; |
| 798 |
} |
| 799 |
else { |
| 800 |
// add line items |
| 801 |
foreach ( $order->get_items() as $item ) { |
| 802 |
|
| 803 |
$product = new \WC_Product( $item['product_id'] ); |
| 804 |
|
| 805 |
$order_items[] = array( |
| 806 |
'NAME' => $product->get_title(), |
| 807 |
'DESC' => $this->get_item_description( $product ), |
| 808 |
'AMT' => $this->round( $order->get_item_subtotal( $item ) ), |
| 809 |
'QTY' => ( ! empty( $item['qty'] ) ) ? absint( $item['qty'] ) : 1, |
| 810 |
'ITEMURL' => $product->get_permalink(), |
| 811 |
); |
| 812 |
|
| 813 |
$order_subtotal += $item['line_total']; |
| 814 |
} |
| 815 |
|
| 816 |
// add fees |
| 817 |
foreach ( $order->get_fees() as $fee ) { |
| 818 |
|
| 819 |
$order_items[] = array( |
| 820 |
'NAME' => ( $fee['name'] ), |
| 821 |
'AMT' => $this->round( $fee['line_total'] ), |
| 822 |
'QTY' => 1, |
| 823 |
); |
| 824 |
|
| 825 |
$order_subtotal += $fee['line_total']; |
| 826 |
} |
| 827 |
if ( $order->get_total_discount() > 0 ) { |
| 828 |
|
| 829 |
$order_items[] = array( |
| 830 |
'NAME' => __( 'Total Discount', 'wpfnl' ), |
| 831 |
'QTY' => 1, |
| 832 |
'AMT' => - $this->round( $order->get_total_discount() ), |
| 833 |
); |
| 834 |
} |
| 835 |
} |
| 836 |
|
| 837 |
|
| 838 |
|
| 839 |
|
| 840 |
/**Do things for the main order **/ |
| 841 |
if ( false === $is_offer_charge ) { |
| 842 |
if ( $this->skip_line_items( $order ) ) { |
| 843 |
|
| 844 |
$total_amount = $this->round( $order->get_total() ); |
| 845 |
|
| 846 |
// calculate the total as PayPal would |
| 847 |
$calculated_total += $this->round( $order_subtotal + $order->get_cart_tax() ) + $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ); |
| 848 |
|
| 849 |
// offset the discrepancy between the WooCommerce cart total and PayPal's calculated total by adjusting the order subtotal |
| 850 |
if ( $this->price_format( $total_amount ) !== $this->price_format( $calculated_total ) ) { |
| 851 |
$order_subtotal = $order_subtotal - ( $calculated_total - $total_amount ); |
| 852 |
} |
| 853 |
|
| 854 |
$item_names = array(); |
| 855 |
|
| 856 |
foreach ( $order_items as $item ) { |
| 857 |
$item_names[] = sprintf( '%1$s x %2$s', $item['NAME'], $item['QTY'] ); |
| 858 |
} |
| 859 |
|
| 860 |
// add a single item for the entire order |
| 861 |
$this->add_line_item_parameters( array( |
| 862 |
// translators: placeholder is blogname |
| 863 |
'NAME' => sprintf( __( '%s - Order', 'wpfnl' ), get_option( 'blogname' ) ), |
| 864 |
'DESC' => $this->get_item_description( implode( ', ', $item_names ) ), |
| 865 |
'AMT' => $this->round( $order_subtotal + $order->get_cart_tax() ), |
| 866 |
'QTY' => 1, |
| 867 |
), 0, $use_deprecated_params ); |
| 868 |
|
| 869 |
// add order-level parameters |
| 870 |
// - Do not send the TAXAMT due to rounding errors |
| 871 |
if ( $use_deprecated_params ) { |
| 872 |
$this->add_parameters( array( |
| 873 |
'AMT' => $total_amount, |
| 874 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 875 |
'ITEMAMT' => $this->round( $order_subtotal + $order->get_cart_tax() ), |
| 876 |
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ), |
| 877 |
'INVNUM' => $this->get_wc_gateway()->get_option( 'invoice_prefix' ) . Wpfnl_functions::str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'wpfnl-pro' ) ) ), |
| 878 |
'PAYMENTACTION' => $type, |
| 879 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 880 |
'CUSTOM' => wp_json_encode( array( |
| 881 |
'_wpfunnels_o_id' => $order->get_id(), |
| 882 |
) ), |
| 883 |
) ); |
| 884 |
} else { |
| 885 |
$this->add_payment_parameters( array( |
| 886 |
'AMT' => $total_amount, |
| 887 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 888 |
'ITEMAMT' => $this->round( $order_subtotal + $order->get_cart_tax() ), |
| 889 |
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ), |
| 890 |
'INVNUM' => $this->get_wc_gateway()->get_option( 'invoice_prefix' ) . Wpfnl_functions::str_to_ascii( ltrim( $order->get_order_number(), _x( '#', 'hash before the order number. Used as a character to remove from the actual order number', 'wpfnl' ) ) ), |
| 891 |
'PAYMENTACTION' => $type, |
| 892 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 893 |
'CUSTOM' => wp_json_encode( array( |
| 894 |
'_wpfunnels_o_id' => $order->get_id(), |
| 895 |
|
| 896 |
) ), |
| 897 |
) ); |
| 898 |
} |
| 899 |
} else { |
| 900 |
|
| 901 |
// add individual order items |
| 902 |
foreach ( $order_items as $item ) { |
| 903 |
$this->add_line_item_parameters( $item, $item_count ++, $use_deprecated_params ); |
| 904 |
} |
| 905 |
|
| 906 |
$total_amount = $this->round( $order->get_total() ); |
| 907 |
// add order-level parameters |
| 908 |
if ( $use_deprecated_params ) { |
| 909 |
$this->add_parameters( array( |
| 910 |
'AMT' => $total_amount, |
| 911 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 912 |
'ITEMAMT' => $this->round( $order_subtotal ), |
| 913 |
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() ), |
| 914 |
'TAXAMT' => $this->round( $order->get_total_tax() ), |
| 915 |
'INVNUM' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 916 |
'PAYMENTACTION' => $type, |
| 917 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 918 |
|
| 919 |
) ); |
| 920 |
} else { |
| 921 |
$this->add_payment_parameters( array( |
| 922 |
'AMT' => $total_amount, |
| 923 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 924 |
'ITEMAMT' => $this->round( $order_subtotal ), |
| 925 |
'SHIPPINGAMT' => $this->round( $order->get_total_shipping() ), |
| 926 |
'TAXAMT' => $this->round( $order->get_total_tax() ), |
| 927 |
'INVNUM' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 928 |
'PAYMENTACTION' => $type, |
| 929 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 930 |
'CUSTOM' => wp_json_encode( array( |
| 931 |
'_wpfunnels_o_id' => $order->get_id(), |
| 932 |
) ), |
| 933 |
) ); |
| 934 |
} |
| 935 |
|
| 936 |
} |
| 937 |
} /** Handle paypal data setup for the offers */ |
| 938 |
|
| 939 |
else { |
| 940 |
|
| 941 |
/** |
| 942 |
* Code for reference transaction |
| 943 |
*/ |
| 944 |
$total_amount = $offer_data['total']; |
| 945 |
|
| 946 |
$item_names = array(); |
| 947 |
|
| 948 |
foreach ( $order_items as $item ) { |
| 949 |
$item_names[] = sprintf( '%1$s x %2$s', $item['NAME'], $item['QTY'] ); |
| 950 |
} |
| 951 |
$item_count = 0; |
| 952 |
// add individual order items |
| 953 |
foreach ( $order_items as $item ) { |
| 954 |
$this->add_line_item_parameters( $item, $item_count ++, $use_deprecated_params ); |
| 955 |
} |
| 956 |
|
| 957 |
/** |
| 958 |
* Check if this is a referencetransaction call then send depreceated params |
| 959 |
*/ |
| 960 |
if ( true === $use_deprecated_params && true === $is_offer_charge ) { |
| 961 |
/** |
| 962 |
* When shipping amount is a negative number, means user opted for free shipping offer |
| 963 |
* In this case we setup shippingamt as 0 and shipping discount amount is that negative amount that is coming. |
| 964 |
*/ |
| 965 |
if ( ( isset( $offer_data['shipping'] ) && isset( $offer_data['shipping']['diff'] ) ) && 0 > $offer_data['shipping']['diff']['cost'] ) { |
| 966 |
$this->add_parameters( array( |
| 967 |
'AMT' => $total_amount, |
| 968 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 969 |
'ITEMAMT' => $this->round( $order_subtotal ), |
| 970 |
'SHIPPINGAMT' => 0, |
| 971 |
'SHIPDISCAMT' => ( isset( $offer_data['shipping'] ) && isset( $offer_data['shipping']['diff'] ) ) ? $offer_data['shipping']['diff']['cost'] : 0, |
| 972 |
'INVNUM' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 973 |
'PAYMENTACTION' => $type, |
| 974 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 975 |
'TAXAMT' => ( isset( $offer_data['taxes'] ) ) ? $offer_data['taxes'] : 0, |
| 976 |
'CUSTOM' => wp_json_encode( array( |
| 977 |
'_wpfunnels_o_id' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 978 |
) ), |
| 979 |
) ); |
| 980 |
} else { |
| 981 |
$this->add_parameters( array( |
| 982 |
'AMT' => $total_amount, |
| 983 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 984 |
'ITEMAMT' => $this->round( $order_subtotal ), |
| 985 |
'SHIPPINGAMT' => ( isset( $offer_data['shipping'] ) && isset( $offer_data['shipping']['diff'] ) ) ? $offer_data['shipping']['diff']['cost'] : 0, |
| 986 |
'INVNUM' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 987 |
'PAYMENTACTION' => $type, |
| 988 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 989 |
'TAXAMT' => ( isset( $offer_data['taxes'] ) ) ? $offer_data['taxes'] : 0, |
| 990 |
'CUSTOM' => wp_json_encode( array( |
| 991 |
'_wpfunnels_o_id' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 992 |
) ), |
| 993 |
) ); |
| 994 |
} |
| 995 |
} else { |
| 996 |
if ( ( isset( $offer_data['shipping'] ) && isset( $offer_data['shipping']['diff'] ) ) && 0 > $offer_data['shipping']['diff']['cost'] ) { |
| 997 |
$this->add_payment_parameters( array( |
| 998 |
'AMT' => $total_amount, |
| 999 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 1000 |
'ITEMAMT' => $this->round( $order_subtotal ), |
| 1001 |
'SHIPPINGAMT' => 0, |
| 1002 |
'SHIPDISCAMT' => ( isset( $offer_data['shipping'] ) && isset( $offer_data['shipping']['diff'] ) ) ? $offer_data['shipping']['diff']['cost'] : 0, |
| 1003 |
'INVNUM' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 1004 |
'PAYMENTACTION' => $type, |
| 1005 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 1006 |
'TAXAMT' => ( isset( $offer_data['taxes'] ) ) ? $offer_data['taxes'] : 0, |
| 1007 |
'NOTIFYURL' => WC()->api_request_url( 'WC_Gateway_Paypal' ), |
| 1008 |
'CUSTOM' => wp_json_encode( array( |
| 1009 |
'_wpfunnels_o_id' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 1010 |
) ), |
| 1011 |
) ); |
| 1012 |
} else { |
| 1013 |
$this->add_payment_parameters( array( |
| 1014 |
'AMT' => $total_amount, |
| 1015 |
'CURRENCYCODE' => $order ? $order->get_currency() : get_woocommerce_currency(), |
| 1016 |
'ITEMAMT' => $this->round( $order_subtotal ), |
| 1017 |
'SHIPPINGAMT' => ( isset( $offer_data['shipping'] ) && isset( $offer_data['shipping']['diff'] ) ) ? $offer_data['shipping']['diff']['cost'] : 0, |
| 1018 |
'INVNUM' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 1019 |
'PAYMENTACTION' => $type, |
| 1020 |
'NOTIFYURL' => WC()->api_request_url( 'WC_Gateway_Paypal' ), |
| 1021 |
'PAYMENTREQUESTID' => $order->get_id(), |
| 1022 |
'TAXAMT' => ( isset( $offer_data['taxes'] ) ) ? $offer_data['taxes'] : 0, |
| 1023 |
'CUSTOM' => wp_json_encode( array( |
| 1024 |
'_wpfunnels_o_id' => $this->get_wc_gateway()->get_option('invoice_prefix') . $this->get_order_number($order, $offer_data['step_id']), |
| 1025 |
) ), |
| 1026 |
) ); |
| 1027 |
} |
| 1028 |
} |
| 1029 |
} |
| 1030 |
|
| 1031 |
|
| 1032 |
} |
| 1033 |
|
| 1034 |
|
| 1035 |
/** |
| 1036 |
* Adds a line item parameters to the request, auto-prefixes the parameter key |
| 1037 |
* with `L_PAYMENTREQUEST_0_` for convenience and readability |
| 1038 |
* |
| 1039 |
* @param array $params |
| 1040 |
* @param int $item_count current item count |
| 1041 |
* |
| 1042 |
* @since 2.0 |
| 1043 |
*/ |
| 1044 |
private function add_line_item_parameters( array $params, $item_count, $use_deprecated_params = false ) { |
| 1045 |
foreach ( $params as $key => $value ) { |
| 1046 |
if ( $use_deprecated_params ) { |
| 1047 |
$this->add_parameter( "L_{$key}{$item_count}", $value ); |
| 1048 |
} else { |
| 1049 |
$this->add_parameter( "L_PAYMENTREQUEST_0_{$key}{$item_count}", $value ); |
| 1050 |
} |
| 1051 |
} |
| 1052 |
} |
| 1053 |
|
| 1054 |
|
| 1055 |
/** |
| 1056 |
* Add payment parameters, auto-prefixes the parameter key with `PAYMENTREQUEST_0_` |
| 1057 |
* for convenience and readability |
| 1058 |
* |
| 1059 |
* @param array $params |
| 1060 |
* |
| 1061 |
* @since 1.0.0 |
| 1062 |
*/ |
| 1063 |
private function add_payment_parameters( array $params ) { |
| 1064 |
foreach ( $params as $key => $value ) { |
| 1065 |
$this->add_parameter( "PAYMENTREQUEST_0_{$key}", $value ); |
| 1066 |
} |
| 1067 |
} |
| 1068 |
|
| 1069 |
|
| 1070 |
|
| 1071 |
/** |
| 1072 |
* PayPal cannot properly calculate order totals when prices include tax (due |
| 1073 |
* to rounding issues), so line items are skipped and the order is sent as |
| 1074 |
* a single item |
| 1075 |
* |
| 1076 |
* @since 2.0.9 |
| 1077 |
* @param \WC_Order $order Optional. The WC_Order object. Default null. |
| 1078 |
* @return bool true if line items should be skipped, false otherwise |
| 1079 |
*/ |
| 1080 |
private function skip_line_items( $order = null, $order_items = null ) { |
| 1081 |
|
| 1082 |
$skip_line_items = false; |
| 1083 |
|
| 1084 |
// Also check actual totals add up just in case totals have been manually modified to amounts that can not round correctly, see https://github.com/Prospress/woocommerce-subscriptions/issues/2213 |
| 1085 |
if ( ! is_null( $order ) ) { |
| 1086 |
|
| 1087 |
$rounded_total = 0; |
| 1088 |
$items = array(); |
| 1089 |
foreach ( $order_items as $item ) { |
| 1090 |
$amount = round( $item['line_subtotal'] / $item['qty'], 2 ); |
| 1091 |
$rounded_total += round( $amount * $item['qty'], 2 ); |
| 1092 |
$calculated_total += $this->round( $item['AMT'] * $item['QTY'] ); |
| 1093 |
|
| 1094 |
$amount = round( $item['line_subtotal'] / $item['qty'], 2 ); |
| 1095 |
$item = array( |
| 1096 |
'name' => $item['name'], |
| 1097 |
'quantity' => $item['qty'], |
| 1098 |
'amount' => $amount, |
| 1099 |
); |
| 1100 |
|
| 1101 |
$items[] = $item; |
| 1102 |
} |
| 1103 |
|
| 1104 |
$discounts = $order->get_total_discount(); |
| 1105 |
|
| 1106 |
$details = array( |
| 1107 |
'total_item_amount' => round( $order->get_subtotal(), 2 ) + $discounts, |
| 1108 |
'order_tax' => round( $order->get_total_tax(), 2 ), |
| 1109 |
'shipping' => round( $order->get_shipping_total(), 2 ), |
| 1110 |
'items' => $items, |
| 1111 |
); |
| 1112 |
$details['order_total'] = round( $details['total_item_amount'] + $details['order_tax'] + $details['shipping'], 2 ); |
| 1113 |
if ( (float) $details['order_total'] !== (float) $order->get_total() ) { |
| 1114 |
$skip_line_items = true; |
| 1115 |
} |
| 1116 |
if ( $details['total_item_amount'] !== $rounded_total ) { |
| 1117 |
$skip_line_items = true; |
| 1118 |
} |
| 1119 |
} |
| 1120 |
|
| 1121 |
/** |
| 1122 |
* Filter whether line items should be skipped or not |
| 1123 |
* |
| 1124 |
* @since 3.3.0 |
| 1125 |
* @param bool $skip_line_items True if line items should be skipped, false otherwise |
| 1126 |
* @param \WC_Order/null $order The WC_Order object or null. |
| 1127 |
*/ |
| 1128 |
return apply_filters( 'wpfunnels/paypal_skip_line_items', $skip_line_items, $order ); |
| 1129 |
} |
| 1130 |
|
| 1131 |
|
| 1132 |
/** |
| 1133 |
* @param \WC_Order $order |
| 1134 |
*/ |
| 1135 |
function maybe_add_shipping_address_params( \WC_Order $order, $prefix = 'PAYMENTREQUEST_0_SHIPTO' ) { |
| 1136 |
|
| 1137 |
if ( $order->has_shipping_address() ) { |
| 1138 |
$shipping_first_name = $order->get_shipping_first_name(); |
| 1139 |
$shipping_last_name = $order->get_shipping_last_name(); |
| 1140 |
$shipping_address_1 = $order->get_shipping_address_1(); |
| 1141 |
$shipping_address_2 = $order->get_shipping_address_2(); |
| 1142 |
$shipping_city = $order->get_shipping_city(); |
| 1143 |
$shipping_state = $order->get_shipping_state(); |
| 1144 |
$shipping_postcode = $order->get_shipping_postcode(); |
| 1145 |
$shipping_country = $order->get_shipping_country(); |
| 1146 |
} else { |
| 1147 |
$shipping_first_name = $order->get_billing_first_name(); |
| 1148 |
$shipping_last_name = $order->get_billing_last_name(); |
| 1149 |
$shipping_address_1 = $order->get_billing_address_1(); |
| 1150 |
$shipping_address_2 = $order->get_billing_address_2(); |
| 1151 |
$shipping_city = $order->get_billing_city(); |
| 1152 |
$shipping_state = $order->get_billing_state(); |
| 1153 |
$shipping_postcode = $order->get_billing_postcode(); |
| 1154 |
$shipping_country = $order->get_billing_country(); |
| 1155 |
} |
| 1156 |
if ( empty( $shipping_country ) ) { |
| 1157 |
$shipping_country = WC()->countries->get_base_country(); |
| 1158 |
} |
| 1159 |
|
| 1160 |
$shipping_phone = $order->get_billing_phone(); |
| 1161 |
|
| 1162 |
$params = array( |
| 1163 |
$prefix . 'NAME' => $shipping_first_name . ' ' . $shipping_last_name, |
| 1164 |
$prefix . 'STREET' => $shipping_address_1, |
| 1165 |
$prefix . 'STREET2' => $shipping_address_2, |
| 1166 |
$prefix . 'CITY' => $shipping_city, |
| 1167 |
$prefix . 'STATE' => $shipping_state, |
| 1168 |
$prefix . 'ZIP' => $shipping_postcode, |
| 1169 |
$prefix . 'COUNTRYCODE' => $this->get_country( $shipping_country ), |
| 1170 |
$prefix . 'PHONENUM' => $shipping_phone, |
| 1171 |
); |
| 1172 |
foreach ( $params as $key => $val ) { |
| 1173 |
$this->add_parameter( $key, $val ); |
| 1174 |
} |
| 1175 |
|
| 1176 |
} |
| 1177 |
|
| 1178 |
|
| 1179 |
private function get_country( $country ) { |
| 1180 |
if ( 2 === strlen( $country ) ) { |
| 1181 |
return $country; |
| 1182 |
} |
| 1183 |
|
| 1184 |
return substr( $country, 0, 2 ); |
| 1185 |
} |
| 1186 |
|
| 1187 |
|
| 1188 |
/** |
| 1189 |
* Add api credentials parameters |
| 1190 |
* |
| 1191 |
* @param string $api_username API username. |
| 1192 |
* @param string $api_password API password. |
| 1193 |
* @param string $api_signature API signature. |
| 1194 |
* @param string $api_version API version. |
| 1195 |
* @return void |
| 1196 |
*/ |
| 1197 |
public function set_credentials_params( $api_username, $api_password, $api_signature, $api_version ) { |
| 1198 |
|
| 1199 |
$this->add_parameters( |
| 1200 |
array( |
| 1201 |
'USER' => $api_username, |
| 1202 |
'PWD' => $api_password, |
| 1203 |
'SIGNATURE' => $api_signature, |
| 1204 |
'VERSION' => $api_version, |
| 1205 |
) |
| 1206 |
); |
| 1207 |
} |
| 1208 |
|
| 1209 |
|
| 1210 |
/** |
| 1211 |
* Add multiple parameters |
| 1212 |
* |
| 1213 |
* @param array $params |
| 1214 |
* |
| 1215 |
* @since 2.0 |
| 1216 |
*/ |
| 1217 |
public function add_parameters(array $params) |
| 1218 |
{ |
| 1219 |
foreach ($params as $key => $value) { |
| 1220 |
$this->add_parameter($key, $value); |
| 1221 |
} |
| 1222 |
} |
| 1223 |
|
| 1224 |
|
| 1225 |
/** |
| 1226 |
* Add a parameter |
| 1227 |
* |
| 1228 |
* @param string $key |
| 1229 |
* @param string|int $value |
| 1230 |
* |
| 1231 |
* @since 2.0 |
| 1232 |
*/ |
| 1233 |
public function add_parameter($key, $value) |
| 1234 |
{ |
| 1235 |
$this->parameters[$key] = $value; |
| 1236 |
} |
| 1237 |
|
| 1238 |
|
| 1239 |
/** |
| 1240 |
* get call back url of paypal request |
| 1241 |
* |
| 1242 |
* @param $args |
| 1243 |
* @return string |
| 1244 |
* |
| 1245 |
* @since 1.0.0 |
| 1246 |
*/ |
| 1247 |
public function get_callback_url($args) |
| 1248 |
{ |
| 1249 |
$api_request_url = WC()->api_request_url( 'wpfunnels_paypal_express' ); |
| 1250 |
if ( is_array( $args ) ) { |
| 1251 |
return add_query_arg( $args, $api_request_url ); |
| 1252 |
} else { |
| 1253 |
return add_query_arg( 'action', $args, $api_request_url ); |
| 1254 |
} |
| 1255 |
} |
| 1256 |
|
| 1257 |
|
| 1258 |
/** |
| 1259 |
* get paypal payment params |
| 1260 |
* |
| 1261 |
* @return array|mixed|void |
| 1262 |
* @throws \Exception |
| 1263 |
*/ |
| 1264 |
public function get_parameters() { |
| 1265 |
|
| 1266 |
$this->parameters = apply_filters('wcs_paypal_request_params', $this->parameters, $this); |
| 1267 |
// validate parameters. |
| 1268 |
foreach ( $this->parameters as $key => $value ) { |
| 1269 |
|
| 1270 |
// remove unused params. |
| 1271 |
if ( '' === $value || is_null( $value ) ) { |
| 1272 |
unset( $this->parameters[ $key ] ); |
| 1273 |
} |
| 1274 |
|
| 1275 |
// format and check amounts. |
| 1276 |
if ( false !== strpos( $key, 'AMT' ) ) { |
| 1277 |
|
| 1278 |
// amounts must be 10,000.00 or less for USD. |
| 1279 |
if ( isset( $this->parameters['PAYMENTREQUEST_0_CURRENCYCODE'] ) && 'USD' == $this->parameters['PAYMENTREQUEST_0_CURRENCYCODE'] && $value > 10000 ) { |
| 1280 |
|
| 1281 |
throw new \Exception( sprintf( '%s amount of %s must be less than $10,000.00', $key, $value ) ); |
| 1282 |
} |
| 1283 |
|
| 1284 |
$this->parameters[ $key ] = $this->price_format( $value ); |
| 1285 |
} |
| 1286 |
} |
| 1287 |
return $this->parameters; |
| 1288 |
} |
| 1289 |
|
| 1290 |
|
| 1291 |
/** |
| 1292 |
* Return the parsed response object for the request |
| 1293 |
* |
| 1294 |
* @since 1.0.0 |
| 1295 |
* |
| 1296 |
* @param string $raw_response_body response body. |
| 1297 |
* |
| 1298 |
* @return object |
| 1299 |
*/ |
| 1300 |
protected function get_parsed_response( $raw_response_body ) { |
| 1301 |
|
| 1302 |
wp_parse_str( urldecode( $raw_response_body ), $this->response_params ); |
| 1303 |
|
| 1304 |
return $this->response_params; |
| 1305 |
} |
| 1306 |
|
| 1307 |
|
| 1308 |
/** |
| 1309 |
* Get Express Checkout Details |
| 1310 |
* |
| 1311 |
* @param $token |
| 1312 |
* @return object |
| 1313 |
* |
| 1314 |
* @since 1.0.0 |
| 1315 |
*/ |
| 1316 |
public function get_express_checkout_details( $token ) { |
| 1317 |
|
| 1318 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'production'; |
| 1319 |
$api_creds_prefix = ''; |
| 1320 |
if ('sandbox' === $environment) { |
| 1321 |
$api_creds_prefix = 'sandbox_'; |
| 1322 |
} |
| 1323 |
$this->set_api_credentials( |
| 1324 |
$this->get_key(), |
| 1325 |
$environment, |
| 1326 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_username'), |
| 1327 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_password'), |
| 1328 |
$this->get_wc_gateway()->get_option($api_creds_prefix . 'api_signature') |
| 1329 |
); |
| 1330 |
$this->set_express_checkout_details_args($token); |
| 1331 |
$this->set_credentials_params( |
| 1332 |
$this->api_username, |
| 1333 |
$this->api_password, |
| 1334 |
$this->api_signature, |
| 1335 |
124 |
| 1336 |
); |
| 1337 |
$request = new \stdClass(); |
| 1338 |
$request->path = ''; |
| 1339 |
$request->method = 'POST'; |
| 1340 |
$request->body = $this->to_string(); |
| 1341 |
return $this->perform_request($request); |
| 1342 |
} |
| 1343 |
|
| 1344 |
|
| 1345 |
/** |
| 1346 |
* Sets up GetExpressCheckoutDetails API call arguments |
| 1347 |
* |
| 1348 |
* @param string $token |
| 1349 |
* |
| 1350 |
* @since 1.0.0 |
| 1351 |
*/ |
| 1352 |
public function set_express_checkout_details_args($token) |
| 1353 |
{ |
| 1354 |
|
| 1355 |
$this->set_method('GetExpressCheckoutDetails'); |
| 1356 |
$this->add_parameter('TOKEN', $token); |
| 1357 |
} |
| 1358 |
|
| 1359 |
|
| 1360 |
/** |
| 1361 |
* @hooked woocommerce_api_wpfunnels_paypal_express |
| 1362 |
*/ |
| 1363 |
public function maybe_create_billing() { |
| 1364 |
if (!isset($_GET['action'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 1365 |
return; |
| 1366 |
} |
| 1367 |
$token = esc_attr( sanitize_text_field( wp_unslash( $_GET['token'] ) ) ); |
| 1368 |
$order_id = isset( $_GET['order_id'] ) ? intval( $_GET['order_id'] ) : 0; |
| 1369 |
$step_id = isset( $_GET['step_id'] ) ? intval( $_GET['step_id'] ) : 0; |
| 1370 |
$funnel_id = isset( $_GET['funnel_id'] ) ? intval( $_GET['funnel_id'] ) : 0; |
| 1371 |
|
| 1372 |
switch ($_GET['action']) { |
| 1373 |
|
| 1374 |
// called when the customer is returned from PayPal after authorizing their payment, used for retrieving the customer's checkout details |
| 1375 |
case 'wpfunnels_paypal_express_create_billing_agreement': |
| 1376 |
|
| 1377 |
// return if no token |
| 1378 |
if (!isset($_GET['token'])) { |
| 1379 |
return; |
| 1380 |
} |
| 1381 |
|
| 1382 |
// get token to retrieve checkout details with |
| 1383 |
$token = wc_clean( $_GET['token'] ); |
| 1384 |
$order = null; |
| 1385 |
try { |
| 1386 |
$express_checkout_details_response = $this->get_express_checkout_details($token); |
| 1387 |
$order = wc_get_order($order_id); |
| 1388 |
|
| 1389 |
// Make sure the billing agreement was accepted |
| 1390 |
if (1 === $express_checkout_details_response['BILLINGAGREEMENTACCEPTEDSTATUS'] || '1' === $express_checkout_details_response['BILLINGAGREEMENTACCEPTEDSTATUS']) { |
| 1391 |
|
| 1392 |
if (is_null($order)) { |
| 1393 |
throw new Wpfnl_Payment_Gateway_Exception(__('Unable to find order for PayPal billing agreement.', 'wpfnl-pro'), 101, $this->get_key()); |
| 1394 |
} |
| 1395 |
|
| 1396 |
// we need to process an initial payment |
| 1397 |
if (Wpfnl_functions::get_amount_for_comparisons($order->get_total()) > 0) { |
| 1398 |
$billing_agreement_response = $this->do_express_checkout($token, $order, array( |
| 1399 |
'payment_action' => 'Sale', |
| 1400 |
'payer_id' => $this->get_value_from_response($express_checkout_details_response, 'PAYERID'), |
| 1401 |
)); |
| 1402 |
} else { |
| 1403 |
throw new Wpfnl_Payment_Gateway_Exception(__('Order total is not greater than zero.', 'wpfnl'), 101, $this->get_key()); |
| 1404 |
} |
| 1405 |
|
| 1406 |
if ($this->has_api_error($billing_agreement_response)) { |
| 1407 |
throw new Wpfnl_Payment_Gateway_Exception($this->get_api_error($billing_agreement_response), 101, $this->get_key()); |
| 1408 |
} |
| 1409 |
|
| 1410 |
$order->set_payment_method('paypal'); |
| 1411 |
$order->update_meta_data('_paypal_subscription_id', $this->get_value_from_response($billing_agreement_response, 'BILLINGAGREEMENTID')); |
| 1412 |
|
| 1413 |
/** |
| 1414 |
* mark primary payment as completed |
| 1415 |
*/ |
| 1416 |
$order->payment_complete($billing_agreement_response['PAYMENTINFO_0_TRANSACTIONID']); |
| 1417 |
|
| 1418 |
$next_step = Wpfnl_functions::get_next_step( $funnel_id, $step_id ); |
| 1419 |
$custom_url = Wpfnl_functions::custom_url_for_thankyou_page( $next_step['step_id'] ); |
| 1420 |
if( $custom_url ){ |
| 1421 |
$redirect_url = $custom_url; |
| 1422 |
}else{ |
| 1423 |
$redirect_url = add_query_arg('utm_nooverride', '1', get_permalink($next_step['step_id'])); |
| 1424 |
} |
| 1425 |
|
| 1426 |
//redirect customer to order received page |
| 1427 |
wp_safe_redirect(esc_url_raw($redirect_url)); |
| 1428 |
exit; |
| 1429 |
|
| 1430 |
} |
| 1431 |
else { |
| 1432 |
$this->handle_api_failures($order); |
| 1433 |
} |
| 1434 |
} |
| 1435 |
catch (\Exception $e) { |
| 1436 |
$this->handle_api_failures($order, $e); |
| 1437 |
} |
| 1438 |
} |
| 1439 |
} |
| 1440 |
|
| 1441 |
/** |
| 1442 |
* check if api response has any error |
| 1443 |
* |
| 1444 |
* @param $response |
| 1445 |
* @return bool |
| 1446 |
*/ |
| 1447 |
public function has_api_error($response) { |
| 1448 |
// assume something went wrong if ACK is missing |
| 1449 |
if (!isset($response['ACK'])) { |
| 1450 |
return true; |
| 1451 |
} |
| 1452 |
|
| 1453 |
// any non-success ACK is considered an error, see |
| 1454 |
// https://developer.paypal.com/docs/classic/api/NVPAPIOverview/#id09C2F0K30L7 |
| 1455 |
return ('Success' !== $this->get_value_from_response($response, 'ACK') && 'SuccessWithWarning' !== $this->get_value_from_response($response, 'ACK')); |
| 1456 |
|
| 1457 |
} |
| 1458 |
|
| 1459 |
|
| 1460 |
/** |
| 1461 |
* get order from response |
| 1462 |
* |
| 1463 |
* @param $response |
| 1464 |
* @return bool|\WC_Order|\WC_Order_Refund |
| 1465 |
*/ |
| 1466 |
public function get_order_from_response($response) { |
| 1467 |
|
| 1468 |
if ($response && isset($response['CUSTOM'])) { |
| 1469 |
$getjson = json_decode($response['CUSTOM'], true); |
| 1470 |
return wc_get_order($getjson['_wpfunnels_o_id']); |
| 1471 |
} |
| 1472 |
} |
| 1473 |
|
| 1474 |
|
| 1475 |
/** |
| 1476 |
* Handles Payment Gateway API error |
| 1477 |
* |
| 1478 |
* @param $order |
| 1479 |
* @param $e |
| 1480 |
*/ |
| 1481 |
protected function handle_api_failures($order, $e = '') { |
| 1482 |
if ($order instanceof \WC_Order) { |
| 1483 |
|
| 1484 |
if ($e instanceof Wpfnl_Payment_Gateway_Exception) { |
| 1485 |
|
| 1486 |
$order->add_order_note($e->getMessage()); |
| 1487 |
} |
| 1488 |
$redirect = $order->get_checkout_order_received_url(); |
| 1489 |
wp_redirect($redirect); |
| 1490 |
exit; |
| 1491 |
} |
| 1492 |
wp_die('Unable to process further. Please contact to the store admin & enquire about the status of your order.'); |
| 1493 |
exit; |
| 1494 |
} |
| 1495 |
|
| 1496 |
|
| 1497 |
public function handle_api_calls() { |
| 1498 |
|
| 1499 |
if (!isset($_GET['action'])) { |
| 1500 |
return; |
| 1501 |
} |
| 1502 |
|
| 1503 |
$step_id = isset( $_GET['step_id'] ) ? intval( $_GET['step_id'] ) : 0; |
| 1504 |
$funnel_id = isset( $_GET['funnel_id'] ) ? intval( $_GET['funnel_id'] ) : 0; |
| 1505 |
$order_id = isset( $_GET['order_id'] ) ? intval( $_GET['order_id'] ) : 0; |
| 1506 |
$order_key = isset( $_GET['order_key'] ) ? sanitize_text_field( wp_unslash( $_GET['order_key'] ) ) : ''; |
| 1507 |
$variation_id = isset( $_GET['variation_id'] ) ? intval( $_GET['variation_id'] ) : ''; |
| 1508 |
$input_qty = isset( $_GET['input_qty'] ) ? intval( $_GET['input_qty'] ) : ''; |
| 1509 |
|
| 1510 |
$order = null; |
| 1511 |
try { |
| 1512 |
switch ($_GET['action']) { |
| 1513 |
case 'wpfunnels_paypal_express_return': |
| 1514 |
|
| 1515 |
$offer_product = Wpfnl_functions::get_offer_product_data($step_id); |
| 1516 |
|
| 1517 |
$order = wc_get_order( $order_id ); |
| 1518 |
|
| 1519 |
|
| 1520 |
if ( isset( $_GET['token'] ) && ! empty( $_GET['token'] ) ) { |
| 1521 |
$api_response_result = false; |
| 1522 |
$express_checkout_details_response = $this->get_express_checkout_details( wp_unslash( $_GET['token'] ) ); //phpcs:ignore |
| 1523 |
|
| 1524 |
$get_paypal_data = WC()->session->get('paypal_request'); |
| 1525 |
|
| 1526 |
/** |
| 1527 |
* Check if product total is greater than 0. |
| 1528 |
*/ |
| 1529 |
if ( $offer_product['total'] > 0 ) { |
| 1530 |
/** |
| 1531 |
* Prepare DoExpessCheckout Call to finally charge the user. |
| 1532 |
*/ |
| 1533 |
$do_express_checkout_data = array( |
| 1534 |
'TOKEN' => $express_checkout_details_response['TOKEN'], |
| 1535 |
'PAYERID' => $express_checkout_details_response['PAYERID'], |
| 1536 |
'METHOD' => 'DoExpressCheckoutPayment', |
| 1537 |
); |
| 1538 |
|
| 1539 |
$do_express_checkout_data = wp_parse_args( $do_express_checkout_data, $get_paypal_data ); |
| 1540 |
$environment = ( 'sandbox' === $this->get_wc_gateway()->environment ) ? 'sandbox' : 'live'; |
| 1541 |
|
| 1542 |
$api_prefix = ''; |
| 1543 |
|
| 1544 |
if ( 'sandbox' === $environment ) { |
| 1545 |
$api_prefix = 'sandbox_'; |
| 1546 |
} |
| 1547 |
|
| 1548 |
/** |
| 1549 |
* Setup & perform DoExpressCheckout API Call. |
| 1550 |
*/ |
| 1551 |
$this->set_api_credentials( |
| 1552 |
$this->key, |
| 1553 |
$environment, |
| 1554 |
$this->get_wc_gateway()->get_option( $api_prefix . 'api_username' ), |
| 1555 |
$this->get_wc_gateway()->get_option( $api_prefix . 'api_password' ), |
| 1556 |
$this->get_wc_gateway()->get_option( $api_prefix . 'api_signature' ) |
| 1557 |
); |
| 1558 |
|
| 1559 |
$this->add_parameters( $do_express_checkout_data ); |
| 1560 |
$this->set_credentials_params( $this->api_username, $this->api_password, $this->api_signature, 124 ); |
| 1561 |
|
| 1562 |
$request = new \stdClass(); |
| 1563 |
$request->path = ''; |
| 1564 |
$request->method = 'POST'; |
| 1565 |
$request->body = $this->to_string(); |
| 1566 |
$response_checkout = $this->perform_request( $request ); |
| 1567 |
|
| 1568 |
if ( false === $this->has_api_error( $response_checkout ) ) { |
| 1569 |
$api_response_result = true; |
| 1570 |
$data = $this->handle_offer_charge($order, $api_response_result, $offer_product); |
| 1571 |
} |
| 1572 |
} |
| 1573 |
else { |
| 1574 |
$api_response_result = true; |
| 1575 |
$data = $this->handle_offer_charge($order, $api_response_result, $offer_product); |
| 1576 |
} |
| 1577 |
|
| 1578 |
$result = Wpfnl_functions::after_offer_charged( $step_id, $order_id, $order_key, $api_response_result, $variation_id, $input_qty, $offer_product ); |
| 1579 |
$next_step = Wpfnl_functions::get_next_step($funnel_id, $step_id); |
| 1580 |
|
| 1581 |
$next_step_url = get_permalink($next_step['step_id']); |
| 1582 |
$custom_url = Wpfnl_functions::custom_url_for_thankyou_page( $next_step['step_id'] ); |
| 1583 |
if( $custom_url ){ |
| 1584 |
$redirect_url = $custom_url; |
| 1585 |
}else{ |
| 1586 |
$redirect_url = add_query_arg( |
| 1587 |
array( |
| 1588 |
'wpfnl-order' => $order_id, |
| 1589 |
'wpfnl-key' => $order_key, |
| 1590 |
'key' => $order_key, |
| 1591 |
), |
| 1592 |
$next_step_url |
| 1593 |
); |
| 1594 |
} |
| 1595 |
|
| 1596 |
wp_safe_redirect( $redirect_url ); |
| 1597 |
exit; |
| 1598 |
|
| 1599 |
} else { |
| 1600 |
|
| 1601 |
$result = Wpfnl_functions::after_offer_charged( $step_id, $order_id, $order_key, $api_response_result, $variation_id, $input_qty, $offer_product ); |
| 1602 |
$next_step = Wpfnl_functions::get_next_step($funnel_id, $step_id); |
| 1603 |
|
| 1604 |
$custom_url = Wpfnl_functions::custom_url_for_thankyou_page( $next_step['step_id'] ); |
| 1605 |
if( $custom_url ){ |
| 1606 |
$redirect_url = $custom_url; |
| 1607 |
}else{ |
| 1608 |
$next_step_url = get_permalink($next_step['step_id']); |
| 1609 |
$redirect_url = add_query_arg( |
| 1610 |
array( |
| 1611 |
'wpfnl-order' => $order_id, |
| 1612 |
'wpfnl-key' => $order_key, |
| 1613 |
'key' => $order_key, |
| 1614 |
), |
| 1615 |
$next_step_url |
| 1616 |
); |
| 1617 |
} |
| 1618 |
|
| 1619 |
|
| 1620 |
wp_safe_redirect( $redirect_url ); |
| 1621 |
exit; |
| 1622 |
} |
| 1623 |
|
| 1624 |
break; |
| 1625 |
|
| 1626 |
case 'cancel_url': |
| 1627 |
|
| 1628 |
/** |
| 1629 |
* Getting the current URL from the session and loading the same offer again. |
| 1630 |
* User needs to chose "no thanks" if he want to move to upsell/order received. |
| 1631 |
*/ |
| 1632 |
$url = get_permalink( $step_id ); |
| 1633 |
|
| 1634 |
$args = array( |
| 1635 |
'wpfnl-order' => $order_id, |
| 1636 |
'wpfnl-key' => $order_key, |
| 1637 |
'key' => $order_key, |
| 1638 |
); |
| 1639 |
|
| 1640 |
$url = add_query_arg( $args, $url ); |
| 1641 |
wp_redirect($url); |
| 1642 |
exit; |
| 1643 |
|
| 1644 |
} |
| 1645 |
|
| 1646 |
} catch (Exception $e) { |
| 1647 |
$this->handle_api_failures($order, $e); |
| 1648 |
} |
| 1649 |
} |
| 1650 |
|
| 1651 |
|
| 1652 |
/** |
| 1653 |
* Store Offer Trxn Charge. |
| 1654 |
* |
| 1655 |
* @param \WC_Order $order The order that is being paid for. |
| 1656 |
* @param Object $response The response that is send from the payment gateway. |
| 1657 |
* @param array $product Product data. |
| 1658 |
*/ |
| 1659 |
public function handle_offer_charge( \WC_Order $order, $response, $product ) { |
| 1660 |
|
| 1661 |
$order_id = $order->get_id(); |
| 1662 |
$txn_id = ''; |
| 1663 |
|
| 1664 |
if ( ! isset( $response['PAYMENTINFO_0_TRANSACTIONID'] ) ) { |
| 1665 |
if(is_array($response)){ |
| 1666 |
$txn_id = $response['TRANSACTIONID']; |
| 1667 |
} |
| 1668 |
|
| 1669 |
} else { |
| 1670 |
if(is_array($response)){ |
| 1671 |
$txn_id = $response['PAYMENTINFO_0_TRANSACTIONID']; |
| 1672 |
} |
| 1673 |
} |
| 1674 |
$order->update_meta_data( '_wpfunnels_offer_txn_resp_' . $product['step_id'], $txn_id ); |
| 1675 |
$order->save(); |
| 1676 |
} |
| 1677 |
|
| 1678 |
|
| 1679 |
/** |
| 1680 |
* Modify offer refund data. |
| 1681 |
* |
| 1682 |
* @param array $request request. |
| 1683 |
* @param object $order the order object. |
| 1684 |
* @param float $amount refund amount. |
| 1685 |
* |
| 1686 |
* @return object |
| 1687 |
*/ |
| 1688 |
public function wpfnl_offer_refund_request( $request, $order, $amount ) { |
| 1689 |
|
| 1690 |
$payment_method = $order->get_payment_method(); |
| 1691 |
|
| 1692 |
if ( $this->key !== $payment_method ) { |
| 1693 |
return $request; |
| 1694 |
} |
| 1695 |
|
| 1696 |
if ( isset( $order->data['transaction_id'] ) && ! empty( $order->data['transaction_id'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing |
| 1697 |
$request['TRANSACTIONID'] = wc_clean( $order->data['transaction_id'] ); //phpcs:ignore WordPress.Security.NonceVerification.Missing |
| 1698 |
|
| 1699 |
$environment = ('sandbox' === $this->get_wc_gateway()->environment) ? 'sandbox' : 'live'; |
| 1700 |
|
| 1701 |
if ( 'live' === $environment ) { |
| 1702 |
$request['USER'] = $this->get_wc_gateway()->get_option( 'api_username' ); |
| 1703 |
$request['PWD'] = $this->get_wc_gateway()->get_option( 'api_password' ); |
| 1704 |
$request['SIGNATURE'] = $this->get_wc_gateway()->get_option( 'api_signature' ); |
| 1705 |
} else { |
| 1706 |
$request['USER'] = $this->get_wc_gateway()->get_option( 'sandbox_api_username' ); |
| 1707 |
$request['PWD'] = $this->get_wc_gateway()->get_option( 'sandbox_api_password' ); |
| 1708 |
$request['SIGNATURE'] = $this->get_wc_gateway()->get_option( 'sandbox_api_signature' ); |
| 1709 |
|
| 1710 |
} |
| 1711 |
} |
| 1712 |
return $request; |
| 1713 |
} |
| 1714 |
|
| 1715 |
|
| 1716 |
} |
| 1717 |
|