woocommerce-subscriptions/includes/gateways/paypal/includes/class-wcs-paypal-reference-...

239 lines
7.0 KiB
PHP

<?php
/**
* PayPal Reference Transaction API Response Class
*
* Parses response string received from the PayPal Express Checkout API for Reference Transaction related requests, which is simply a URL-encoded string of parameters
*
* @link https://developer.paypal.com/docs/classic/api/NVPAPIOverview/#id084DN080HY4
*
* Heavily inspired by the WC_Paypal_Express_API_Payment_Response class developed by the masterful SkyVerge team
*
* @package WooCommerce Subscriptions
* @subpackage Gateways/PayPal
* @category Class
* @since 2.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WCS_PayPal_Reference_Transaction_API_Response extends WC_Gateway_Paypal_Response {
/** @var array URL-decoded and parsed parameters */
protected $parameters = array();
/**
* Parse the response parameters from the raw URL-encoded response string
*
* @link https://developer.paypal.com/docs/classic/api/NVPAPIOverview/#id084FBM0M0HS
*
* @param string $response the raw URL-encoded response string
* @since 2.0
*/
public function __construct( $response ) {
// URL decode the response string and parse it
wp_parse_str( urldecode( $response ), $this->parameters );
}
/**
* Checks if response contains an API error code
*
* @link https://developer.paypal.com/docs/classic/api/errorcodes/
*
* @return bool true if has API error, false otherwise
* @since 2.0
*/
public function has_api_error() {
// assume something went wrong if ACK is missing
if ( ! $this->has_parameter( 'ACK' ) ) {
return true;
}
// any non-success ACK is considered an error, see
// https://developer.paypal.com/docs/classic/api/NVPAPIOverview/#id09C2F0K30L7
return ( 'Success' !== $this->get_parameter( 'ACK' ) && 'SuccessWithWarning' !== $this->get_parameter( 'ACK' ) );
}
/**
* Checks if response contains an API error code or message relating to invalid credentails
*
* @link https://developer.paypal.com/docs/classic/api/errorcodes/
*
* @return bool true if has API error relating to incorrect credentials, false otherwise
* @since 2.1
*/
public function has_api_error_for_credentials() {
$has_api_error_for_credentials = false;
// assume something went wrong if ACK is missing
if ( $this->has_api_error() ) {
foreach ( range( 0, 9 ) as $index ) {
// Error codes refer to multiple errors, go figure, so we need to compare both error codes and error messages
$has_credentials_error_code = $this->has_parameter( "L_ERRORCODE{$index}" ) && in_array( $this->get_parameter( "L_ERRORCODE{$index}" ), array( 10002, 10008 ) );
$has_credentials_error_message = $this->has_parameter( "L_LONGMESSAGE{$index}" ) && in_array( $this->get_parameter( "L_LONGMESSAGE{$index}" ), array( 'Username/Password is incorrect', 'Security header is not valid' ) );
if ( $has_credentials_error_code && $has_credentials_error_message ) {
$has_api_error_for_credentials = true;
break;
}
}
}
return $has_api_error_for_credentials;
}
/**
* Gets the API error code
*
* Note that PayPal can return multiple error codes, which are merged here
* for convenience
*
* @link https://developer.paypal.com/docs/classic/api/errorcodes/
*
* @return string
* @since 2.0
*/
public function get_api_error_code() {
$error_codes = array();
foreach ( range( 0, 9 ) as $index ) {
if ( $this->has_parameter( "L_ERRORCODE{$index}" ) ) {
$error_codes[] = $this->get_parameter( "L_ERRORCODE{$index}" );
}
}
return empty( $error_codes ) ? 'N/A' : trim( implode( ', ', $error_codes ) );
}
/**
* Gets the API error message
*
* Note that PayPal can return multiple error messages, which are merged here
* for convenience
*
* @link https://developer.paypal.com/docs/classic/api/errorcodes/
*
* @return string
* @since 2.0
*/
public function get_api_error_message() {
$error_messages = array();
foreach ( range( 0, 9 ) as $index ) {
if ( $this->has_parameter( "L_SHORTMESSAGE{$index}" ) ) {
$error_message = sprintf( '%s: %s - %s',
$this->has_parameter( "L_SEVERITYCODE{$index}" ) ? $this->get_parameter( "L_SEVERITYCODE{$index}" ) : _x( 'Error', 'used in api error message if there is no severity code from PayPal', 'woocommerce-subscriptions' ),
$this->get_parameter( "L_SHORTMESSAGE{$index}" ),
$this->has_parameter( "L_LONGMESSAGE{$index}" ) ? $this->get_parameter( "L_LONGMESSAGE{$index}" ) : _x( 'Unknown error', 'used in api error message if there is no long message', 'woocommerce-subscriptions' )
);
// append additional info if available
if ( $this->has_parameter( "L_ERRORPARAMID{$index}" ) && $this->has_parameter( "L_ERRORPARAMVALUE{$index}" ) ) {
$error_message .= sprintf( ' (%s - %s)', $this->get_parameter( "L_ERRORPARAMID{$index}" ), $this->get_parameter( "L_ERRORPARAMVALUE{$index}" ) );
}
$error_messages[] = $error_message;
}
}
return empty( $error_messages ) ? _x( 'N/A', 'no information about something', 'woocommerce-subscriptions' ) : trim( implode( ', ', $error_messages ) );
}
/**
* Returns true if the parameter is not empty
*
* @param string $name parameter name
* @return bool
* @since 2.0
*/
protected function has_parameter( $name ) {
return ! empty( $this->parameters[ $name ] );
}
/**
* Gets the parameter value, or null if parameter is not set or empty
*
* @param string $name parameter name
* @return string|null
* @since 2.0
*/
protected function get_parameter( $name ) {
return $this->has_parameter( $name ) ? $this->parameters[ $name ] : null;
}
/**
* Returns a message appropriate for a frontend user. This should be used
* to provide enough information to a user to allow them to resolve an
* issue on their own, but not enough to help nefarious folks fishing for
* info.
*
* @link https://developer.paypal.com/docs/classic/api/errorcodes/
*
* @return string user message, if there is one
* @since 2.0
*/
public function get_user_message() {
$allowed_user_error_message_codes = array(
'10445',
'10474',
'12126',
'13113',
'13122',
'13112',
);
return in_array( $this->get_api_error_code(), $allowed_user_error_message_codes ) ? $this->get_api_error_message() : null;
}
/**
* Returns the string representation of this response
*
* @return string response
* @since 2.0
*/
public function to_string() {
return print_r( $this->parameters, true );
}
/**
* Returns the string representation of this response with any and all
* sensitive elements masked or removed
*
* @return string response safe for logging/displaying
* @since 2.0
*/
public function to_string_safe() {
// no sensitive data to mask
return $this->to_string();
}
/**
* Get the order for a request based on the 'custom' response field
*
* @see WC_Gateway_Paypal_Response::get_paypal_order()
* @param string $response the raw URL-encoded response string
* @since 2.0
*/
public function get_order() {
// assume something went wrong if ACK is missing
if ( $this->has_parameter( 'CUSTOM' ) ) {
return $this->get_paypal_order( $this->get_parameter( 'CUSTOM' ) );
}
}
}