'https://api-3t.sandbox.paypal.com/nvp',
'live' => 'https://api-3t.paypal.com/nvp'
);
/**
* @var array URLs for sandbox and live
*/
private $_url = array(
'sandbox' => 'https://www.sandbox.paypal.com/webscr',
'live' => 'https://www.paypal.com/webscr'
);
/**
* @access private
* @var string Query var for listener to watch for
*/
private $_listener_query_var = 'paypalListener';
/**
* @access private
* @var string Value that query var must be for listener to take overs
*/
private $_listener_query_var_value = 'IPN';
private $_currencies = array();
/**
* This is our constructor, which is private to force the use of
* getInstance() to make this a Singleton
*
* @return wpPayPalFramework
*/
private function __construct() {
$this->_getSettings();
$this->_fixDebugEmails();
$this->_currencies = array(
'AUD' => __( 'Australian Dollar', 'paypal-framework' ),
'CAD' => __( 'Canadian Dollar', 'paypal-framework' ),
'CZK' => __( 'Czech Koruna', 'paypal-framework' ),
'DKK' => __( 'Danish Krone', 'paypal-framework' ),
'EUR' => __( 'Euro', 'paypal-framework' ),
'HKD' => __( 'Hong Kong Dollar', 'paypal-framework' ),
'HUF' => __( 'Hungarian Forint', 'paypal-framework' ),
'ILS' => __( 'Israeli New Sheqel', 'paypal-framework' ),
'JPY' => __( 'Japanese Yen', 'paypal-framework' ),
'MXN' => __( 'Mexican Peso', 'paypal-framework' ),
'NOK' => __( 'Norwegian Krone', 'paypal-framework' ),
'NZD' => __( 'New Zealand Dollar', 'paypal-framework' ),
'PLN' => __( 'Polish Zloty', 'paypal-framework' ),
'GBP' => __( 'Pound Sterling', 'paypal-framework' ),
'SGD' => __( 'Singapore Dollar', 'paypal-framework' ),
'SEK' => __( 'Swedish Krona', 'paypal-framework' ),
'CHF' => __( 'Swiss Franc', 'paypal-framework' ),
'USD' => __( 'U.S. Dollar', 'paypal-framework' )
);
/**
* Add filters and actions
*/
add_action( 'admin_init', array($this,'registerOptions') );
add_action( 'admin_menu', array($this,'adminMenu') );
add_action( 'wp_ajax_nopriv_paypal_listener', array( $this, 'listener' ) );
add_action( 'template_redirect', array( $this, 'template_redirect' ) );
add_filter( 'query_vars', array( $this, 'addPaypalListenerVar' ) );
add_filter( 'init', array( $this, 'init_locale' ) );
if ( 'on' == $this->_settings['legacy_support'] )
add_action( 'init', 'paypalFramework_legacy_function' );
}
/**
* If an instance exists, this returns it. If not, it creates one and
* retuns it.
*
* @return wpPayPalFramework
*/
public static function getInstance() {
if ( !self::$instance )
self::$instance = new self;
return self::$instance;
}
public function init_locale() {
load_plugin_textdomain( 'paypal-framework', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
private function _getSettings() {
if (empty($this->_settings))
$this->_settings = get_option( $this->_optionsName );
if ( !is_array( $this->_settings ) )
$this->_settings = array();
$defaults = array(
'sandbox' => 'sandbox',
'username-sandbox' => '',
'password-sandbox' => '',
'signature-sandbox' => '',
'username-live' => '',
'password-live' => '',
'signature-live' => '',
'version' => '58.0',
'currency' => 'USD',
'debugging' => 'on',
'debugging_email' => '',
'legacy_support' => 'off',
);
$this->_settings = wp_parse_args( $this->_settings, $defaults );
}
public function getSetting( $settingName, $default = false ) {
if (empty($this->_settings))
$this->_getSettings();
if ( isset($this->_settings[$settingName]) )
return $this->_settings[$settingName];
else
return $default;
}
public function registerOptions() {
register_setting( $this->_optionsGroup, $this->_optionsName );
}
public function adminMenu() {
$page = add_options_page( __( 'PayPal Settings', 'paypal-framework' ), __( 'PayPal', 'paypal-framework' ), 'manage_options', 'PayPalFramework', array( $this, 'options' ) );
add_action( 'admin_print_styles-' . $page, array( $this, 'admin_css' ) );
}
public function admin_css() {
wp_enqueue_style( 'paypal-framework', plugin_dir_url( __FILE__ ) . 'paypal-framework.css', array(), '0.0.1' );
}
/**
* This is used to display the options page for this plugin
*/
public function options() {
?>
' . __( 'Help', 'paypal-framework' ) . '';
switch ( $help ) {
case 'live':
?>
-
sign up for one.', 'paypal-framework'), 'https://www.paypal.com/us/mrb/pal=TJ287296FD8KW'); ?>
-
sign up for it.', 'paypal-framework'), 'https://www.paypal.com/us/cgi-bin/webscr?cmd=_wp-pro-overview'); ?>
-
set it up.", 'paypal-framework' ), 'https://www.paypal.com/cgi-bin/webscr?cmd=xpt/cps/general/DPRPLaunch-outside'); ?>
-
"Profile" -> "Request API credentials" -> "PayPal API" -> "Set up PayPal API credentials and permissions". If asked, you want to request an "API signature" not a certificate. All the data that you are given should easily fit in this form.', 'paypal-framework'); ?>
PayPal sandbox account.', 'paypal-framework'), 'https://developer.paypal.com/'); ?>
-
Profile on the My Account tab.', 'paypal-framework'); ?>
-
Instant Payment Notification Preferences in the Selling Preferences column.', 'paypal-framework'); ?>
-
Edit IPN Settings to specify your listener's URL and activate the listener.", 'paypal-framework'); ?>
-
-
-
Save.', 'paypal-framework'); ?>
-
Back to Profile Summary to return to the Profile after activating your listener.", 'paypal-framework'); ?>
$this->_settings['version'],
'PWD' => $this->_settings["password-{$this->_settings['sandbox']}"],
'USER' => $this->_settings["username-{$this->_settings['sandbox']}"],
'SIGNATURE' => $this->_settings["signature-{$this->_settings['sandbox']}"],
'CURRENCYCODE' => $this->_settings['currency'],
);
return wp_parse_args( $req, $defaults );
}
/**
* Convert an associative array into an NVP string
*
* @param array Associative array to create NVP string from
* @param string[optional] Used to separate arguments (defaults to &)
*
* @return string NVP string
*/
public function makeNVP( $reqArray, $sep = '&' ) {
if ( !is_array($reqArray) )
return $reqArray;
return http_build_query( $reqArray, '', $sep );
}
/**
* hashCall: Function to perform the API call to PayPal using API signature
* @param string|array $args Parameters needed for call
*
* @return array On success return associtive array containing the response from the server.
*/
public function hashCall( $args ) {
$params = array(
'body' => $this->_prepRequest($args),
'sslverify' => apply_filters( 'paypal_framework_sslverify', false ),
'timeout' => 30,
);
// Send the request
$resp = wp_remote_post( $this->_endpoint[$this->_settings['sandbox']], $params );
// If the response was valid, decode it and return it. Otherwise return a WP_Error
if ( !is_wp_error($resp) && $resp['response']['code'] >= 200 && $resp['response']['code'] < 300 ) {
// Used for debugging.
$request = $this->_sanitizeRequest($params['body']);
$message = __( 'Request:', 'paypal-framework' );
$message .= "\r\n".print_r($request, true)."\r\n\r\n";
$message .= __( 'Response:', 'paypal-framework' );
$message .= "\r\n".print_r(wp_parse_args( $resp['body'] ), true)."\r\n\r\n";
$this->_debug_mail( __( 'PayPal Framework - hashCall sent successfully', 'paypal-framework' ), $message );
return wp_parse_args($resp['body']);
} else {
$request = $this->_sanitizeRequest($params['body']);
$message = __( 'Request:', 'paypal-framework' );
$message .= "\r\n".print_r($request, true)."\r\n\r\n";
$message .= __( 'Response:', 'paypal-framework' );
$message .= "\r\n".print_r($resp, true)."\r\n\r\n";
$this->_debug_mail( __( 'PayPal Framework - hashCall failed', 'paypal-framework' ), $message );
if ( !is_wp_error($resp) )
$resp = new WP_Error('http_request_failed', $resp['response']['message'], $resp['response']);
return $resp;
}
}
private function _sanitizeRequest($request) {
/**
* If this is a live request, hide sensitive data in the debug
* E-Mails we send
*/
if ( $this->_settings['sandbox'] != 'sandbox' ) {
if ( !empty( $request['ACCT'] ) )
$request['ACCT'] = str_repeat('*', strlen($request['ACCT'])-4) . substr($request['ACCT'], -4);
if ( !empty( $request['EXPDATE'] ) )
$request['EXPDATE'] = str_repeat('*', strlen($request['EXPDATE']));
if ( !empty( $request['CVV2'] ) )
$request['CVV2'] = str_repeat('*', strlen($request['CVV2']));
}
return $request;
}
/**
* Used to direct the user to the Express Checkout
*
* @param string|array $args Parameters needed for call. *token is REQUIRED*
*/
public function sendToExpressCheckout($args) {
$args['cmd'] = '_express-checkout';
$nvpString = $this->makeNVP($args);
wp_redirect($this->_url[$this->_settings['sandbox']] . "?{$nvpString}");
exit;
}
public function template_redirect() {
// Check that the query var is set and is the correct value.
if ( get_query_var( $this->_listener_query_var ) == $this->_listener_query_var_value )
$this->listener();
}
/**
* This is our listener. If the proper query var is set correctly it will
* attempt to handle the response.
*/
public function listener() {
$_POST = stripslashes_deep($_POST);
// Try to validate the response to make sure it's from PayPal
if ($this->_validateMessage())
$this->_processMessage();
// Stop WordPress entirely
exit;
}
/**
* Get the PayPal URL based on current setting for sandbox vs live
*/
public function getUrl() {
return $this->_url[$this->_settings['sandbox']];
}
public function _fixDebugEmails() {
$this->_settings['debugging_email'] = preg_split('/\s*,\s*/', $this->_settings['debugging_email']);
$this->_settings['debugging_email'] = array_filter($this->_settings['debugging_email'], 'is_email');
$this->_settings['debugging_email'] = implode(',', $this->_settings['debugging_email']);
}
private function _debug_mail( $subject, $message ) {
// Used for debugging.
if ( $this->_settings['debugging'] == 'on' && !empty($this->_settings['debugging_email']) )
wp_mail( $this->_settings['debugging_email'], $subject, $message );
}
/**
* Validate the message by checking with PayPal to make sure they really
* sent it
*/
private function _validateMessage() {
// Set the command that is used to validate the message
$_POST['cmd'] = "_notify-validate";
// We need to send the message back to PayPal just as we received it
$params = array(
'body' => $_POST,
'sslverify' => apply_filters( 'paypal_framework_sslverify', false ),
'timeout' => 30,
);
// Send the request
$resp = wp_remote_post( $this->_url[$this->_settings['sandbox']], $params );
// Put the $_POST data back to how it was so we can pass it to the action
unset( $_POST['cmd'] );
$message = __('URL:', 'paypal-framework' );
$message .= "\r\n".print_r($this->_url[$this->_settings['sandbox']], true)."\r\n\r\n";
$message .= __('Options:', 'paypal-framework' );
$message .= "\r\n".print_r($this->_settings, true)."\r\n\r\n";
$message .= __('Response:', 'paypal-framework' );
$message .= "\r\n".print_r($resp, true)."\r\n\r\n";
$message .= __('Post:', 'paypal-framework' );
$message .= "\r\n".print_r($_POST, true);
// If the response was valid, check to see if the request was valid
if ( !is_wp_error($resp) && $resp['response']['code'] >= 200 && $resp['response']['code'] < 300 && (strcmp( $resp['body'], "VERIFIED") == 0)) {
$this->_debug_mail( __( 'IPN Listener Test - Validation Succeeded', 'paypal-framework' ), $message );
return true;
} else {
// If we can't validate the message, assume it's bad
$this->_debug_mail( __( 'IPN Listener Test - Validation Failed', 'paypal-framework' ), $message );
return false;
}
}
/**
* Add our query var to the list of query vars
*/
public function addPaypalListenerVar($public_query_vars) {
$public_query_vars[] = $this->_listener_query_var;
return $public_query_vars;
}
/**
* Throw an action based off the transaction type of the message
*/
private function _processMessage() {
do_action( 'paypal-ipn', $_POST );
$actions = array( 'paypal-ipn' );
$subject = sprintf( __( 'IPN Listener Test - %s', 'paypal-framework' ), '_processMessage()' );
if ( !empty($_POST['txn_type']) ) {
do_action("paypal-{$_POST['txn_type']}", $_POST);
$actions[] = "paypal-{$_POST['txn_type']}";
}
$message = sprintf( __( 'Actions thrown: %s', 'paypal-framework' ), implode( ', ', $actions ) );
$message .= "\r\n\r\n";
$message .= sprintf( __( 'Passed to actions: %s', 'paypal-framework' ), "\r\n" . print_r($_POST, true) );
$this->_debug_mail( $subject, $message );
}
}
/**
* Helper functions
*/
function hashCall ($args) {
$wpPayPalFramework = wpPayPalFramework::getInstance();
return $wpPayPalFramework->hashCall($args);
}
function paypalFramework_legacy_function() {
//Only load if the function doesn't already exist
if ( !function_exists('hash_call') ) {
/**
* Support the old method of using hash_call
*/
function hash_call($methodName, $nvpStr) {
_deprecated_function(__FUNCTION__, '0.1', 'wpPayPalFramework::hashCall()');
$nvpStr = wp_parse_args( $nvpStr );
$nvpStr['METHOD'] = $methodName;
$nvpStr = array_map('urldecode', $nvpStr);
$wpPayPalFramework = wpPayPalFramework::getInstance();
return $wpPayPalFramework->hashCall($nvpStr);
}
}
}
// Instantiate our class
$wpPayPalFramework = wpPayPalFramework::getInstance();