initial docker setup

This commit is contained in:
GotPPay
2018-06-14 16:49:28 +02:00
parent bc80b7342e
commit b5f87f27f8
3023 changed files with 985078 additions and 1 deletions

View File

@@ -0,0 +1,101 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
abstract class WC_Gateway_PPEC_Client_Credential {
/**
* API username.
*
* @var string
*/
protected $_username;
/**
* API password.
*
* @var string
*/
protected $_password;
/**
* API subject.
*
* @var string
*/
protected $_subject;
/**
* Get API username.
*
* @return string API username
*/
public function get_username() {
return $this->_username;
}
/**
* Get API password.
*
* @return string API password
*/
public function get_password() {
return $this->_password;
}
/**
* Get API subject.
*
* @return string API subject
*/
public function get_subject() {
return $this->_subject;
}
/**
* Retrieves the subdomain of the endpoint which should be used for this type
* of credentials.
*
* @return string The appropriate endpoint, e.g. https://api.paypal.com/nvp
* in this case the subdomain is 'api'
*/
abstract public function get_endpoint_subdomain();
/**
* Retrieves a list of credentialing parameters that should be supplied to
* PayPal.
*
* @return array An array of name-value pairs containing the API credentials
* from this object.
*/
public function get_request_params() {
$params = array(
'USER' => $this->_username,
'PWD' => $this->_password,
);
if ( ! empty( $this->_subject ) ) {
$params['SUBJECT'] = $this->_subject;
}
return $params;
}
/**
* Allow certificate-based credential to configure cURL, especially
* to set CURLOPT_SSLCERT and CURLOPT_SSLCERTPASSWD.
*
* @throws Exception
*
* @param resource &$handle The cURL handle returned by curl_init().
* @param array $r The HTTP request arguments.
* @param string $url The request URL.
*
* @return void
*/
public function configure_curl( $handle, $r, $url ) {
curl_setopt( $handle, CURLOPT_CAINFO, wc_gateway_ppec()->includes_path . 'pem/bundle.pem' );
}
}

View File

@@ -0,0 +1,96 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Base class to handle request from PayPal.
*
* @since 1.1.2
*/
abstract class WC_Gateway_PPEC_PayPal_Request_Handler {
/**
* Gateway instance.
*
* @var WC_Gateway_PPEC
*/
protected $gateway;
/**
* Constructor.
*
* @param WC_Gateway_PPEC $gateway PPEC gateway instance
*/
public function __construct( WC_Gateway_PPEC $gateway ) {
$this->gateway = $gateway;
}
abstract protected function handle();
/**
* Get the order from the PayPal 'Custom' variable.
*
* @param string $raw_custom JSON Data passed back by PayPal
* @return bool|WC_Order Order object or false
*/
protected function get_paypal_order( $raw_custom ) {
// We have the data in the correct format, so get the order.
if ( ( $custom = json_decode( $raw_custom ) ) && is_object( $custom ) ) {
$order_id = $custom->order_id;
$order_key = $custom->order_key;
} else {
wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'Error: Order ID and key were not found in "custom".' ) );
return false;
}
if ( ! $order = wc_get_order( $order_id ) ) {
// We have an invalid $order_id, probably because invoice_prefix has changed.
$order_id = wc_get_order_id_by_order_key( $order_key );
$order = wc_get_order( $order_id );
}
if ( $order ) {
$order_key_from_order = version_compare( WC_VERSION, '3.0', '<' ) ? $order->order_key : $order->get_order_key();
} else {
$order_key_from_order = '';
}
if ( ! $order || $order_key_from_order !== $order_key ) {
wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'Error: Order Keys do not match.' ) );
return false;
}
return $order;
}
/**
* Complete order, add transaction ID and note.
*
* @param WC_Order $order Order object
* @param string $txn_id Transaction ID
* @param string $note Order note
*/
protected function payment_complete( $order, $txn_id = '', $note = '' ) {
$order->add_order_note( $note );
$order->payment_complete( $txn_id );
}
/**
* Hold order and add note.
*
* @param WC_Order $order Order object
* @param string $reason On-hold reason
*/
protected function payment_on_hold( $order, $reason = '' ) {
$order->update_status( 'on-hold', $reason );
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
if ( ! get_post_meta( $order->id, '_order_stock_reduced', true ) ) {
$order->reduce_order_stock();
}
} else {
wc_maybe_reduce_stock_levels( $order->get_id() );
}
WC()->cart->empty_cart();
}
}

View File

@@ -0,0 +1,580 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* WC_Gateway_PPEC
*/
abstract class WC_Gateway_PPEC extends WC_Payment_Gateway {
/**
* Constructor.
*/
public function __construct() {
$this->has_fields = false;
$this->supports[] = 'refunds';
$this->method_title = __( 'PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' );
$this->method_description = __( 'Allow customers to conveniently checkout directly with PayPal.', 'woocommerce-gateway-paypal-express-checkout' );
if ( empty( $_GET['woo-paypal-return'] ) ) {
$this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-paypal-express-checkout' );
}
wc_gateway_ppec()->ips->maybe_received_credentials();
$this->init_form_fields();
$this->init_settings();
$this->title = $this->method_title;
$this->description = '';
$this->enabled = $this->get_option( 'enabled', 'yes' );
$this->button_size = $this->get_option( 'button_size', 'large' );
$this->environment = $this->get_option( 'environment', 'live' );
$this->mark_enabled = 'yes' === $this->get_option( 'mark_enabled', 'no' );
if ( 'live' === $this->environment ) {
$this->api_username = $this->get_option( 'api_username' );
$this->api_password = $this->get_option( 'api_password' );
$this->api_signature = $this->get_option( 'api_signature' );
$this->api_certificate = $this->get_option( 'api_certificate' );
$this->api_subject = $this->get_option( 'api_subject' );
} else {
$this->api_username = $this->get_option( 'sandbox_api_username' );
$this->api_password = $this->get_option( 'sandbox_api_password' );
$this->api_signature = $this->get_option( 'sandbox_api_signature' );
$this->api_certificate = $this->get_option( 'sandbox_api_certificate' );
$this->api_subject = $this->get_option( 'sandbox_api_subject' );
}
$this->debug = 'yes' === $this->get_option( 'debug', 'no' );
$this->invoice_prefix = $this->get_option( 'invoice_prefix', '' );
$this->instant_payments = 'yes' === $this->get_option( 'instant_payments', 'no' );
$this->require_billing = 'yes' === $this->get_option( 'require_billing', 'no' );
$this->paymentaction = $this->get_option( 'paymentaction', 'sale' );
$this->subtotal_mismatch_behavior = $this->get_option( 'subtotal_mismatch_behavior', 'add' );
$this->use_ppc = false;
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
// Change gateway name if session is active
if ( ! is_admin() ) {
if ( wc_gateway_ppec()->checkout->is_started_from_checkout_page() ) {
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
}
} else {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
}
add_filter( 'woocommerce_ajax_get_endpoint', array( $this, 'pass_return_args_to_ajax' ), 10, 2 );
}
/**
* Pass woo return args to AJAX endpoint when the checkout updates from the frontend
* so that the order button gets set correctly.
*
* @param string $request Optional.
* @return string
*/
public function pass_return_args_to_ajax( $request ) {
if ( isset( $_GET['woo-paypal-return'] ) ) {
$request .= '&woo-paypal-return=1';
}
return $request;
}
/**
* Enqueues admin scripts.
*
* @since 1.5.2
*/
public function enqueue_scripts() {
// Image upload.
wp_enqueue_media();
wp_enqueue_script( 'wc-gateway-ppec-settings', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-settings.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
}
/**
* Initialise Gateway Settings Form Fields.
*/
public function init_form_fields() {
$this->form_fields = include( dirname( dirname( __FILE__ ) ) . '/settings/settings-ppec.php' );
}
/**
* Process payments.
*
* @param int $order_id Order ID
*
* @return array
*/
public function process_payment( $order_id ) {
$checkout = wc_gateway_ppec()->checkout;
$order = wc_get_order( $order_id );
$session = WC()->session->get( 'paypal' );
// Redirect them over to PayPal if they have no current session (this
// is for PayPal Mark).
if ( $checkout->is_started_from_checkout_page() ) {
try {
return array(
'result' => 'success',
'redirect' => $checkout->start_checkout_from_checkout( $order_id, $this->use_ppc ),
);
} catch ( PayPal_API_Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
}
} else {
try {
// Get details
$checkout_details = $checkout->get_checkout_details( $session->token );
$checkout_context = array(
'start_from' => 'checkout',
'order_id' => $order_id,
);
if ( $checkout->needs_billing_agreement_creation( $checkout_context ) ) {
$checkout->create_billing_agreement( $order, $checkout_details );
}
// Complete the payment now.
$checkout->do_payment( $order, $session->token, $session->payer_id );
// Clear Cart
WC()->cart->empty_cart();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order ),
);
} catch ( PayPal_Missing_Session_Exception $e ) {
// For some reason, our session data is missing. Generally,
// if we've made it this far, this shouldn't happen.
wc_add_notice( __( 'Sorry, an error occurred while trying to process your payment. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ), 'error' );
} catch ( PayPal_API_Exception $e ) {
// Did we get a 10486 or 10422 back from PayPal? If so, this
// means we need to send the buyer back over to PayPal to have
// them pick out a new funding method.
$error_codes = wp_list_pluck( $e->errors, 'error_code' );
if ( in_array( '10486', $error_codes ) || in_array( '10422', $error_codes ) ) {
$session->checkout_completed = false;
$session->source = 'order';
$session->order_id = $order_id;
WC()->session->set( 'paypal', $session );
return array(
'result' => 'success',
'redirect' => wc_gateway_ppec()->settings->get_paypal_redirect_url( $session->token, true ),
);
} else {
wc_add_notice( $e->getMessage(), 'error' );
}
}
}
}
/**
* Get info about uploaded certificate.
* @param string $cert_string
* @return string
*/
private function get_certificate_info( $cert_string ) {
if ( ! strlen( $cert_string ) ) {
return __( 'No API certificate on file.', 'woocommerce-gateway-paypal-express-checkout' );
}
$cert = @openssl_x509_read( $cert_string ); // @codingStandardsIgnoreLine
$out = '';
if ( false !== $cert ) {
$certinfo = openssl_x509_parse( $cert );
if ( false !== $certinfo ) {
$valid_until = $certinfo['validTo_time_t'];
if ( $valid_until < time() ) {
// Display in red if the cert is already expired
$expires = '<span style="color: red;">' . __( 'expired on %s', 'woocommerce-gateway-paypal-express-checkout' ) . '</span>';
} elseif ( $valid_until < ( time() - 2592000 ) ) {
// Also display in red if the cert is going to expire in the next 30 days
$expires = '<span style="color: red;">' . __( 'expires on %s', 'woocommerce-gateway-paypal-express-checkout' ) . '</span>';
} else {
// Otherwise just display a normal message
$expires = __( 'expires on %s', 'woocommerce-gateway-paypal-express-checkout' );
}
$expires = sprintf( $expires, date_i18n( get_option( 'date_format' ), $valid_until ) );
$out = sprintf( __( 'Certificate belongs to API username %1$s; %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $certinfo['subject']['CN'], $expires );
} else {
$out = __( 'The certificate on file is not valid.', 'woocommerce-gateway-paypal-express-checkout' );
}
}
return $out;
}
/**
* Do some additonal validation before saving options via the API.
*/
public function process_admin_options() {
// If a certificate has been uploaded, read the contents and save that string instead.
if ( array_key_exists( 'woocommerce_ppec_paypal_api_certificate', $_FILES )
&& array_key_exists( 'tmp_name', $_FILES['woocommerce_ppec_paypal_api_certificate'] )
&& array_key_exists( 'size', $_FILES['woocommerce_ppec_paypal_api_certificate'] )
&& $_FILES['woocommerce_ppec_paypal_api_certificate']['size'] ) {
$_POST['woocommerce_ppec_paypal_api_certificate'] = base64_encode( file_get_contents( $_FILES['woocommerce_ppec_paypal_api_certificate']['tmp_name'] ) );
unlink( $_FILES['woocommerce_ppec_paypal_api_certificate']['tmp_name'] );
unset( $_FILES['woocommerce_ppec_paypal_api_certificate'] );
} else {
$_POST['woocommerce_ppec_paypal_api_certificate'] = $this->get_option( 'api_certificate' );
}
if ( array_key_exists( 'woocommerce_ppec_paypal_sandbox_api_certificate', $_FILES )
&& array_key_exists( 'tmp_name', $_FILES['woocommerce_ppec_paypal_sandbox_api_certificate'] )
&& array_key_exists( 'size', $_FILES['woocommerce_ppec_paypal_sandbox_api_certificate'] )
&& $_FILES['woocommerce_ppec_paypal_sandbox_api_certificate']['size'] ) {
$_POST['woocommerce_ppec_paypal_sandbox_api_certificate'] = base64_encode( file_get_contents( $_FILES['woocommerce_ppec_paypal_sandbox_api_certificate']['tmp_name'] ) );
unlink( $_FILES['woocommerce_ppec_paypal_sandbox_api_certificate']['tmp_name'] );
unset( $_FILES['woocommerce_ppec_paypal_sandbox_api_certificate'] );
} else {
$_POST['woocommerce_ppec_paypal_sandbox_api_certificate'] = $this->get_option( 'sandbox_api_certificate' );
}
parent::process_admin_options();
// Validate credentials.
$this->validate_active_credentials();
}
/**
* Validate the provided credentials.
*/
protected function validate_active_credentials() {
$settings = wc_gateway_ppec()->settings->load( true );
$creds = $settings->get_active_api_credentials();
$username = $creds->get_username();
$password = $creds->get_password();
if ( ! empty( $username ) ) {
if ( empty( $password ) ) {
WC_Admin_Settings::add_error( __( 'Error: You must enter API password.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
if ( is_a( $creds, 'WC_Gateway_PPEC_Client_Credential_Signature' ) && $creds->get_signature() ) {
try {
$payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $settings->get_environment() );
if ( ! $payer_id ) {
WC_Admin_Settings::add_error( __( 'Error: The API credentials you provided are not valid. Please double-check that you entered them correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
} catch ( PayPal_API_Exception $ex ) {
WC_Admin_Settings::add_error( __( 'An error occurred while trying to validate your API credentials. Unable to verify that your API credentials are correct.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
} elseif ( is_a( $creds, 'WC_Gateway_PPEC_Client_Credential_Certificate' ) && $creds->get_certificate() ) {
$cert = @openssl_x509_read( $creds->get_certificate() ); // @codingStandardsIgnoreLine
if ( false === $cert ) {
WC_Admin_Settings::add_error( __( 'Error: The API certificate is not valid.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
$cert_info = openssl_x509_parse( $cert );
$valid_until = $cert_info['validTo_time_t'];
if ( $valid_until < time() ) {
WC_Admin_Settings::add_error( __( 'Error: The API certificate has expired.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
if ( $cert_info['subject']['CN'] != $creds->get_username() ) {
WC_Admin_Settings::add_error( __( 'Error: The API username does not match the name in the API certificate. Make sure that you have the correct API certificate.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
try {
$payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $settings->get_environment() );
if ( ! $payer_id ) {
WC_Admin_Settings::add_error( __( 'Error: The API credentials you provided are not valid. Please double-check that you entered them correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
} catch ( PayPal_API_Exception $ex ) {
WC_Admin_Settings::add_error( __( 'An error occurred while trying to validate your API credentials. Unable to verify that your API credentials are correct.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
} else {
WC_Admin_Settings::add_error( __( 'Error: You must provide API signature or certificate.', 'woocommerce-gateway-paypal-express-checkout' ) );
return false;
}
$settings_array = (array) get_option( 'woocommerce_ppec_paypal_settings', array() );
if ( 'yes' === $settings_array['require_billing'] ) {
$is_account_enabled_for_billing_address = false;
try {
$is_account_enabled_for_billing_address = wc_gateway_ppec()->client->test_for_billing_address_enabled( $creds, $settings->get_environment() );
} catch ( PayPal_API_Exception $ex ) {
$is_account_enabled_for_billing_address = false;
}
if ( ! $is_account_enabled_for_billing_address ) {
$settings_array['require_billing'] = 'no';
update_option( 'woocommerce_ppec_paypal_settings', $settings_array );
WC_Admin_Settings::add_error( __( 'The "require billing address" option is not enabled by your account and has been disabled.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
}
}
}
/**
* Process refund.
*
* @param int $order_id Order ID
* @param float $amount Order amount
* @param string $reason Refund reason
*
* @return boolean True or false based on success, or a WP_Error object.
*/
public function process_refund( $order_id, $amount = null, $reason = '' ) {
$order = wc_get_order( $order_id );
if ( 0 == $amount || null == $amount ) {
return new WP_Error( 'paypal_refund_error', __( 'Refund Error: You need to specify a refund amount.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
// load up refundable_txns from Post Meta
// loop through each transaction to compile list of txns that are able to be refunded
// process refunds against each txn in the list until full amount of refund is reached
// first loop through, try to find a transaction that equals the refund amount being requested
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$txn_data = $old_wc ? get_post_meta( $order_id, '_woo_pp_txnData', true ) : $order->get_meta( '_woo_pp_txnData', true );
$order_currency = $old_wc ? $order->order_currency : $order->get_currency();
foreach ( $txn_data['refundable_txns'] as $key => $value ) {
$refundable_amount = $value['amount'] - $value['refunded_amount'];
if ( $amount == $refundable_amount ) {
$refund_type = ( 0 == $value['refunded_amount'] ) ? 'Full' : 'Partial';
try {
$refund_txn_id = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, $refund_type, $reason, $order_currency );
$txn_data['refundable_txns'][ $key ]['refunded_amount'] += $amount;
$order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refund_txn_id ) );
if ( $old_wc ) {
update_post_meta( $order_id, '_woo_pp_txnData', $txn_data );
} else {
$order->update_meta_data( '_woo_pp_txnData', $txn_data );
}
return true;
} catch ( PayPal_API_Exception $e ) {
return new WP_Error( 'paypal_refund_error', $e->getMessage() );
}
}
}
foreach ( $txn_data['refundable_txns'] as $key => $value ) {
$refundable_amount = $value['amount'] - $value['refunded_amount'];
if ( $amount < $refundable_amount ) {
try {
$refund_txn_id = WC_Gateway_PPEC_Refund::refund_order( $order, $amount, 'Partial', $reason, $order_currency );
$txn_data['refundable_txns'][ $key ]['refunded_amount'] += $amount;
$order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refund_txn_id ) );
if ( $old_wc ) {
update_post_meta( $order_id, '_woo_pp_txnData', $txn_data );
} else {
$order->update_meta_data( '_woo_pp_txnData', $txn_data );
}
return true;
} catch ( PayPal_API_Exception $e ) {
return new WP_Error( 'paypal_refund_error', $e->getMessage() );
}
}
}
$total_refundable_amount = 0;
foreach ( $txn_data['refundable_txns'] as $key => $value ) {
$refundable_amount = $value['amount'] - $value['refunded_amount'];
$total_refundable_amount += $refundable_amount;
}
if ( $total_refundable_amount < $amount ) {
if ( 0 == $total_refundable_amount ) {
return new WP_Error( 'paypal_refund_error', __( 'Refund Error: All transactions have been fully refunded. There is no amount left to refund', 'woocommerce-gateway-paypal-express-checkout' ) );
} else {
return new WP_Error( 'paypal_refund_error', sprintf( __( 'Refund Error: The requested refund amount is too large. The refund amount must be less than or equal to %s.', 'woocommerce-gateway-paypal-express-checkout' ), html_entity_decode( get_woocommerce_currency_symbol() ) . $total_refundable_amount ) );
}
} else {
$total_to_refund = $amount;
foreach ( $txn_data['refundable_txns'] as $key => $value ) {
$refundable_amount = $value['amount'] - $value['refunded_amount'];
if ( $refundable_amount > $total_to_refund ) {
$amount_to_refund = $total_to_refund;
} else {
$amount_to_refund = $refundable_amount;
}
if ( 0 < $amount_to_refund ) {
$refund_type = 'Partial';
if ( 0 == $value['refunded_amount'] && $amount_to_refund == $value['amount'] ) {
$refund_type = 'Full';
}
try {
$refund_txn_id = WC_Gateway_PPEC_Refund::refund_order( $order, $amount_to_refund, $refund_type, $reason, $order_currency );
$total_to_refund -= $amount_to_refund;
$txn_data['refundable_txns'][ $key ]['refunded_amount'] += $amount_to_refund;
$order->add_order_note( sprintf( __( 'PayPal refund completed; transaction ID = %s', 'woocommerce-gateway-paypal-express-checkout' ), $refund_txn_id ) );
if ( $old_wc ) {
update_post_meta( $order_id, '_woo_pp_txnData', $txn_data );
} else {
$order->update_meta_data( '_woo_pp_txnData', $txn_data );
}
return true;
} catch ( PayPal_API_Exception $e ) {
return new WP_Error( 'paypal_refund_error', $e->getMessage() );
}
}
}
}
}
/**
* Get the transaction URL.
*
* @param WC_Order $order
* @return string
*/
public function get_transaction_url( $order ) {
if ( 'sandbox' === $this->environment ) {
$this->view_transaction_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_view-a-trans&id=%s';
} else {
$this->view_transaction_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=_view-a-trans&id=%s';
}
return parent::get_transaction_url( $order );
}
/**
* Check if this gateway is enabled.
*
* @return bool
*/
public function is_available() {
return 'yes' === $this->enabled;
}
/**
* Generate Image HTML.
*
* @param mixed $key
* @param mixed $data
* @since 1.5.0
* @return string
*/
public function generate_image_html( $key, $data ) {
$field_key = $this->get_field_key( $key );
$defaults = array(
'title' => '',
'disabled' => false,
'class' => '',
'css' => '',
'placeholder' => '',
'type' => 'text',
'desc_tip' => false,
'description' => '',
'custom_attributes' => array(),
);
$data = wp_parse_args( $data, $defaults );
$value = $this->get_option( $key );
// Hide show add remove buttons.
$maybe_hide_add_style = '';
$maybe_hide_remove_style = '';
// For backwards compatibility (customers that already have set a url)
$value_is_url = filter_var( $value, FILTER_VALIDATE_URL ) !== false;
if ( empty( $value ) || $value_is_url ) {
$maybe_hide_remove_style = 'display: none;';
} else {
$maybe_hide_add_style = 'display: none;';
}
ob_start();
?>
<tr valign="top">
<th scope="row" class="titledesc">
<?php echo $this->get_tooltip_html( $data ); ?>
<label for="<?php echo esc_attr( $field_key ); ?>"><?php echo wp_kses_post( $data['title'] ); ?></label>
</th>
<td class="image-component-wrapper">
<div class="image-preview-wrapper">
<?php
if ( ! $value_is_url ) {
echo wp_get_attachment_image( $value, 'thumbnail' );
} else {
echo sprintf( __( 'Already using URL as image: %s', 'woocommerce-gateway-paypal-express-checkout' ), $value );
}
?>
</div>
<button
class="button image_upload"
data-field-id="<?php echo esc_attr( $field_key ); ?>"
data-media-frame-title="<?php echo esc_attr( __( 'Select a image to upload', 'woocommerce-gateway-paypal-express-checkout' ) ); ?>"
data-media-frame-button="<?php echo esc_attr( __( 'Use this image', 'woocommerce-gateway-paypal-express-checkout' ) ); ?>"
data-add-image-text="<?php echo esc_attr( __( 'Add image', 'woocommerce-gateway-paypal-express-checkout' ) ); ?>"
style="<?php echo esc_attr( $maybe_hide_add_style ); ?>"
>
<?php echo esc_html__( 'Add image', 'woocommerce-gateway-paypal-express-checkout' ); ?>
</button>
<button
class="button image_remove"
data-field-id="<?php echo esc_attr( $field_key ); ?>"
style="<?php echo esc_attr( $maybe_hide_remove_style ); ?>"
>
<?php echo esc_html__( 'Remove image', 'woocommerce-gateway-paypal-express-checkout' ); ?>
</button>
<input type="hidden"
name="<?php echo esc_attr( $field_key ); ?>"
id="<?php echo esc_attr( $field_key ); ?>"
value="<?php echo esc_attr( $value ); ?>"
/>
</td>
</tr>
<?php
return ob_get_clean();
}
}

View File

@@ -0,0 +1,702 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class PayPal_Address {
protected $_name;
protected $_street1;
protected $_street2;
protected $_city;
protected $_state;
protected $_zip;
protected $_country;
protected $_phoneNumber;
protected $_addressOwner;
protected $_addressStatus;
const AddressStatusNone = 'none';
const AddressStatusConfirmed = 'Confirmed';
const AddressStatusUnconfirmed = 'Unconfirmed';
public function setName( $name ) {
$this->_name = $name;
}
public function getName() {
return $this->_name;
}
public function setStreet1( $street1 ) {
$this->_street1 = $street1;
}
public function getStreet1() {
return $this->_street1;
}
public function setStreet2( $street2 ) {
$this->_street2 = $street2;
}
public function getStreet2() {
return $this->_street2;
}
public function setCity( $city ) {
$this->_city = $city;
}
public function getCity() {
return $this->_city;
}
public function setState( $state ) {
$this->_state = $state;
}
public function getState() {
return $this->_state;
}
public function setZip( $zip ) {
$this->_zip = $zip;
}
public function getZip() {
return $this->_zip;
}
public function setCountry( $country ) {
$this->_country = $country;
}
public function getCountry() {
return $this->_country;
}
public function setPhoneNumber( $phoneNumber ) {
$this->_phoneNumber = $phoneNumber;
}
public function getPhoneNumber() {
return $this->_phoneNumber;
}
public function setAddressOwner( $addressOwner ) {
$this->_addressOwner = $addressOwner;
}
public function getAddressOwner() {
return $this->_addressOwner;
}
public function setAddressStatus( $addressStatus ) {
$this->_addressStatus = $addressStatus;
}
public function getAddressStatus() {
return $this->_addressStatus;
}
public function normalizeAddress() {
$this->normalizeCountry();
$this->normalizeState();
$this->normalizeZip();
}
public function normalizeCountry() {
// Since many shopping carts might use the full country name for their internal representation of
// the country, and since PayPal expects the ISO 3166-1 alpha-2 identifier, we'll attempt to
// translate from the various internal representations that might be used. Child classes can
// override this method to provide language-specific or cart-specific translations. Many Bothans
// died to bring us this information...
// This list was taken from https://developer.paypal.com/docs/classic/api/country_codes/.
$translation_table = array(
'albania' => 'AL',
'algeria' => 'DZ',
'andorra' => 'AD',
'angola' => 'AO',
'anguilla' => 'AI',
'antigua and barbuda' => 'AG',
'argentina' => 'AR',
'armenia' => 'AM',
'aruba' => 'AW',
'australia' => 'AU',
'austria' => 'AT',
'azerbaijan' => 'AZ',
'bahamas' => 'BS',
'bahrain' => 'BH',
'barbados' => 'BB',
'belgium' => 'BE',
'belize' => 'BZ',
'benin' => 'BJ',
'bermuda' => 'BM',
'bhutan' => 'BT',
'bolivia' => 'BO',
'bosnia-herzegovina' => 'BA',
'botswana' => 'BW',
'brazil' => 'BR',
'brunei darussalam' => 'BN',
'bulgaria' => 'BG',
'burkina faso' => 'BF',
'burundi' => 'BI',
'cambodia' => 'KH',
'canada' => 'CA',
'cape verde' => 'CV',
'cayman islands' => 'KY',
'chad' => 'TD',
'chile' => 'CL',
'china' => 'CN',
'colombia' => 'CO',
'comoros' => 'KM',
'democratic republic of congo' => 'CD',
'congo' => 'CG',
'cook islands' => 'CK',
'costa rica' => 'CR',
'croatia' => 'HR',
'cyprus' => 'CY',
'czech republic' => 'CZ',
'denmark' => 'DK',
'djibouti' => 'DJ',
'dominica' => 'DM',
'dominican republic' => 'DO',
'ecuador' => 'EC',
'egypt' => 'EG',
'el salvador' => 'SV',
'eriteria' => 'ER',
'estonia' => 'EE',
'ethiopia' => 'ET',
'falkland islands (malvinas)' => 'FK',
// This derivation doesn't show up in the list, but seems obvious
'falkland islands' => 'FK',
'fiji' => 'FJ',
'finland' => 'FI',
'france' => 'FR',
'french guiana' => 'GF',
'french polynesia' => 'PF',
'gabon' => 'GA',
'gambia' => 'GM',
'georgia' => 'GE',
'germany' => 'DE',
'gibraltar' => 'GI',
'greece' => 'GR',
'greenland' => 'GL',
'grenada' => 'GD',
'guadeloupe' => 'GP',
'guam' => 'GU',
'guatemala' => 'GT',
'guinea' => 'GN',
'guinea bissau' => 'GW',
'guyana' => 'GY',
'holy see (vatican city state)' => 'VA',
// This derivation doesn't show up in the list, but seems obvious
'holy see' => 'VA',
'honduras' => 'HN',
'hong kong' => 'HK',
'hungary' => 'HU',
'iceland' => 'IS',
'india' => 'IN',
'indonesia' => 'ID',
'ireland' => 'IE',
'israel' => 'IL',
'italy' => 'IT',
'jamaica' => 'JM',
'japan' => 'JP',
'jordan' => 'JO',
'kazakhstan' => 'KZ',
'kenya' => 'KE',
'kiribati' => 'KI',
'korea, republic of' => 'KR',
// This derivation doesn't show up in the list, but seems obvious
'republic of korea' => 'KR',
'kuwait' => 'KW',
'kyrgyzstan' => 'KG',
'laos' => 'LA',
'latvia' => 'LV',
'lesotho' => 'LS',
'liechtenstein' => 'LI',
'lithuania' => 'LT',
'luxembourg' => 'LU',
'madagascar' => 'MG',
'malawi' => 'MW',
'malaysia' => 'MY',
'maldives' => 'MV',
'mali' => 'ML',
'malta' => 'MT',
'marshall islands' => 'MH',
'martinique' => 'MQ',
'mauritania' => 'MR',
'mauritius' => 'MU',
'mayotte' => 'YT',
'mexico' => 'MX',
'micronesia, federated states of' => 'FM',
// The next two derivations don't show up in the list, but seem obvious
'federated states of micronesia' => 'FM',
'micronesia' => 'FM',
'mongolia' => 'MN',
'montserrat' => 'MS',
'morocco' => 'MA',
'mozambique' => 'MZ',
'namibia' => 'NA',
'nauru' => 'NR',
'nepal' => 'NP',
'netherlands' => 'NL',
'netherlands antilles' => 'AN',
'new caledonia' => 'NC',
'new zealand' => 'NZ',
'nicaragua' => 'NI',
'niger' => 'NE',
'niue' => 'NU',
'norfolk island' => 'NF',
'norway' => 'NO',
'oman' => 'OM',
'palau' => 'PW',
'panama' => 'PA',
'papau new guinea' => 'PG',
'peru' => 'PE',
'philippines' => 'PH',
'pitcairn' => 'PN',
'poland' => 'PL',
'portugal' => 'PT',
'qatar' => 'QA',
'reunion' => 'RE',
'romania' => 'RO',
'russian federation' => 'RU',
// This derivation doesn't show up in the list, but seems obvious
'russia' => 'RU',
'rwanda' => 'RW',
'saint helena' => 'SH',
'saint kitts and nevis' => 'KN',
'saint lucia' => 'LC',
'saint pierre and miquelon' => 'PM',
'saint vincent and the grenadines' => 'VC',
'samoa' => 'WS',
'san marino' => 'SM',
'sao tome and principe' => 'ST',
'saudi arabia' => 'SA',
'senegal' => 'SN',
'serbia' => 'RS',
'seychelles' => 'SC',
'sierra leone' => 'SL',
'singapore' => 'SG',
'slovakia' => 'SK',
'slovenia' => 'SI',
'solomon islands' => 'SB',
'somalia' => 'SO',
'south africa' => 'ZA',
'south korea' => 'KR',
'spain' => 'ES',
'sri lanka' => 'LK',
'suriname' => 'SR',
'svalbard and jan mayen' => 'SJ',
'swaziland' => 'SZ',
'sweden' => 'SE',
'switzerland' => 'CH',
'taiwan, province of china' => 'TW',
// This derivation doesn't show up in the list, but seems obvious
'taiwan' => 'TW',
'tajikistan' => 'TJ',
'tanzania, united republic of' => 'TZ',
// The next two derivations don't show up in the list, but seem obvious
'united republic of tanzania' => 'TZ',
'tanzania' => 'TZ',
'thailand' => 'TH',
'togo' => 'TG',
'tonga' => 'TO',
'trinidad and tobago' => 'TT',
'tunisia' => 'TN',
'turkey' => 'TR',
'turkmenistan' => 'TM',
'turks and caicos islands' => 'TC',
// This derivation doesn't show up in the list, but seems obvious
'turks and caicos' => 'TC',
'tuvalu' => 'TV',
'uganda' => 'UG',
'ukraine' => 'UA',
'united arab emirates' => 'AE',
'united kingdom' => 'GB',
'united states' => 'US',
// This derivation doesn't show up in the list, but seems obvious
'united states of america' => 'US',
'uruguay' => 'UY',
'vanuatu' => 'VU',
'venezuela' => 'VE',
'vietnam' => 'VN',
'virgin islands, british' => 'VG',
// This derivation doesn't show up in the list, but seems obvious
'british virgin islands' => 'VG',
'wallis and futana' => 'WF',
'yemen' => 'YE',
'zambia' => 'ZM',
// This one is here because some carts will make the mistake of using 'uk' instead of 'gb'.
'uk' => 'GB'
);
// And now, the actual translation is as simple as...
if ( array_key_exists( strtolower( trim( $this->_country ) ), $translation_table ) ) {
$this->_country = $translation_table[ strtolower( trim( $this->_country ) ) ];
}
}
public function normalizeState() {
// Since some shopping carts might use the full state name for their internal representation of the
// state, and since PayPal expects the 2-character state/province abbreviation (for US/Canada
// addresses, at least), we'll attempt to translate from the various internal representations
// that might be used. Child classes can override this method to provide additional
// language-specific or cart-specific translations.
// This call should be made AFTER normalizeCountry() has been called, so that the country can be
// properly detected.
// PayPal's documentation also defines state codes for Italy and the Netherlands, so we'll provide
// translations for those as well.
$translation_table = array();
if ( 'US' == $this->_country ) {
$translation_table = array(
'alabama' => 'AL',
'alaska' => 'AK',
'arizona' => 'AZ',
'arkansas' => 'AR',
'california' => 'CA',
'colorado' => 'CO',
'connecticut' => 'CT',
'deleware' => 'DE',
'district of columbia (washington, d.c.)' => 'DC',
// The next several derivations don't show up in the list, but seem obvious
'district of columbia' => 'DC',
'washington, d.c.' => 'DC',
'washington d.c.' => 'DC',
'washington, dc' => 'DC',
'washington dc' => 'DC',
'washington, d. c.' => 'DC',
'washington d. c.' => 'DC',
'washington, d c' => 'DC',
'washington d c' => 'DC',
'florida' => 'FL',
'georgia' => 'GA',
'hawaii' => 'HI',
'idaho' => 'ID',
'illinois' => 'IL',
'indiana' => 'IN',
'iowa' => 'IA',
'kansas' => 'KS',
'kentucky' => 'KY',
'louisiana' => 'LA',
'maine' => 'ME',
'maryland' => 'MD',
'massachusetts' => 'MA',
'michigan' => 'MI',
'minnesota' => 'MN',
'mississippi' => 'MS',
'missouri' => 'MO',
'montana' => 'MT',
'nebraska' => 'NE',
'nevada' => 'NV',
'new hampshire' => 'NH',
'new jersey' => 'NJ',
'new mexico' => 'NM',
'new jersey' => 'NJ',
'new mexico' => 'NM',
'new york' => 'NY',
'north carolina' => 'NC',
'north dakota' => 'ND',
'ohio' => 'OH',
'oklahoma' => 'OK',
'oregon' => 'OR',
'pennsylvania' => 'PA',
'puerto rico' => 'PR',
'rhode island' => 'RI',
'south carolina' => 'SC',
'south dakota' => 'SD',
'tennessee' => 'TN',
'texas' => 'TX',
'utah' => 'UT',
'vermont' => 'VT',
'virginia' => 'VA',
'washington' => 'WA',
'west virginia' => 'WV',
'wisconsin' => 'WI',
'wyoming' => 'WY',
'armed forces americas' => 'AA',
'armed forces' => 'AE',
'armed forces pacific' => 'AP',
'american samoa' => 'AS',
'guam' => 'GU',
'northern mariana islands' => 'MP',
'virgin islands' => 'VI',
// The next few derivations don't show up on the list, but seem obvious
'us virgin islands' => 'VI',
'u.s. virgin islands' => 'VI',
'u s virgin islands' => 'VI',
'u. s. virgin islands' => 'VI'
);
} elseif ( 'CA' == $this->_country ) {
$translation_table = array(
'alberta' => 'AB',
'british columbia' => 'BC',
'manitoba' => 'MB',
'new brunswick' => 'NB',
'newfoundland' => 'NL',
'northwest territories' => 'NT',
'nova scotia' => 'NS',
'nunavut' => 'NU',
'ontario' => 'ON',
'prince edward island' => 'PE',
'quebec' => 'QC',
'saskatchewan' => 'SK',
'yukon' => 'YT',
// This derivation doesn't show up on the list, but seems obvious
'yukon territory' => 'YT'
);
} elseif ( 'IT' == $this->_country ) {
$translation_table = array(
'agrigento' => 'AG',
'alessandria' => 'AL',
'ancona' => 'AN',
'aosta' => 'AO',
'arezzo' => 'AR',
'ascoli piceno' => 'AP',
'asti' => 'AT',
'avellino' => 'AV',
'bari' => 'BA',
'belluno' => 'BL',
'benevento' => 'BN',
'bergamo' => 'BG',
'biella' => 'BI',
'bologna' => 'BO',
'bolzano' => 'BZ',
'brescia' => 'BS',
'brindisi' => 'BR',
'cagliari' => 'CA',
'caltanissetta' => 'CL',
'campobasso' => 'CB',
'caserta' => 'CE',
'catania' => 'CT',
'catanzaro' => 'CZ',
'chieti' => 'CH',
'como' => 'CO',
'cosenza' => 'CS',
'cremona' => 'CR',
'crotone' => 'KR',
'cuneo' => 'CN',
'enna' => 'EN',
'ferrara' => 'FE',
'firenze' => 'FI',
'foggia' => 'FG',
'forli-cesena' => 'FO',
'frosinone' => 'FR',
'genova' => 'GE',
'gorizia' => 'GO',
'grosseto' => 'GR',
'imperia' => 'IM',
'isernia' => 'IS',
'la spezia' => 'SP',
'l\'aquila' => 'AQ',
'latina' => 'LT',
'lecce' => 'LE',
'lecco' => 'LC',
'livorno' => 'LI',
'lodi' => 'LO',
'lucca' => 'LU',
'macerata' => 'MC',
'mantova' => 'MN',
'massa-carrara' => 'MS',
'matera' => 'MT',
'messina' => 'ME',
'milano' => 'MI',
'modena' => 'MO',
'monza e brianza' => 'MB',
// The next couple of derivations are based off information from Wikipedia
'monza and brianza' => 'MB',
'monza e della brianza' => 'MB',
'napoli' => 'NA',
'novara' => 'NO',
'nuoro' => 'NU',
'oristano' => 'OR',
'padova' => 'PD',
'palermo' => 'PA',
'parma' => 'PR',
'pavia' => 'PV',
'perugia' => 'PG',
'pesaro' => 'PS',
'pescara' => 'PE',
'piacenza' => 'PC',
'pisa' => 'PI',
'pistoia' => 'PT',
'pordenone' => 'PN',
'potenza' => 'PZ',
'prato' => 'PO',
'ragusa' => 'RG',
'ravenna' => 'RA',
'reggio calabria' => 'RC',
'reggio emilia' => 'RE',
'rieti' => 'RI',
'rimini' => 'RN',
'roma' => 'RM',
'rovigo' => 'RO',
'salerno' => 'SA',
'sassari' => 'SS',
'savona' => 'SV',
'siena' => 'SI',
'siracusa' => 'SR',
'sondrio' => 'SO',
'taranto' => 'TA',
'teramo' => 'TE',
'terni' => 'TR',
'torino' => 'TO',
'trapani' => 'TP',
'trento' => 'TN',
'treviso' => 'TV',
'trieste' => 'TS',
'udine' => 'UD',
'varese' => 'VA',
'venezia' => 'VE',
'verbania-cusio-ossola' => 'VB',
// This derivation doesn't appear in the list, but seems obvious
'verbania cusio ossola' => 'VB',
'vercelli' => 'VC',
'verona' => 'VR',
'vibo valentia' => 'VV',
'vicenza' => 'VI',
'viterbo' => 'VT'
);
} elseif ( 'NL' == $this->_country ) {
$translation_table = array(
'drenthe' => 'DR',
'flevoland' => 'FL',
'friesland' => 'FR',
'gelderland' => 'GE',
'groningen' => 'GR',
'limburg' => 'LI',
'noord-brabant' => 'NB',
'noord-holland' => 'NH',
'overijssel' => 'OV',
'utrecht' => 'UT',
'zeeland' => 'ZE',
'zuid-holland' => 'ZH'
);
} elseif ( 'IE' == $this->_country ) {
$translation_table = array(
'co clare' => 'CE',
'co cork' => 'CK',
'co cavan' => 'CN',
'co carlow' => 'CW',
'co donegal' => 'DL',
// All of these should be mapped to Dublin start
'co dublin' => 'DN',
'dublin 1' => 'DN',
'dublin 2' => 'DN',
'dublin 3' => 'DN',
'dublin 4' => 'DN',
'dublin 5' => 'DN',
'dublin 6' => 'DN',
'dublin 6w' => 'DN',
'dublin 7' => 'DN',
'dublin 8' => 'DN',
'dublin 9' => 'DN',
'dublin 10' => 'DN',
'dublin 11' => 'DN',
'dublin 12' => 'DN',
'dublin 13' => 'DN',
'dublin 14' => 'DN',
'dublin 15' => 'DN',
'dublin 16' => 'DN',
'dublin 17' => 'DN',
'dublin 18' => 'DN',
'dublin 20' => 'DN',
'dublin 22' => 'DN',
'dublin 24' => 'DN',
// All of these should be mapped to Dublin end
'co galway' => 'GY',
'co kildare' => 'KE',
'co kilkenny' => 'KK',
'co kerry' => 'KY',
'co longford' => 'LD',
'co louth' => 'LH',
'co limerick' => 'LK',
'co leitrim' => 'LM',
'co laois' => 'LS',
'co meath' => 'MH',
'co monaghan' => 'MN',
'co mayo' => 'MO',
'co offaly' => 'OY',
'co roscommon' => 'RN',
'co sligo' => 'SO',
'co tipperary' => 'TY',
'co waterford' => 'WD',
'co westmeath' => 'WH',
'co wicklow' => 'WW',
'co wexford' => 'WX',
);
}
if ( array_key_exists( strtolower( trim( $this->_state ) ), $translation_table ) ) {
$this->_state = $translation_table[ strtolower( trim( $this->_state ) ) ];
}
}
public function normalizeZip() {
// TODO: Try to do some ZIP code normalization
}
public function getAddressParams( $prefix = '' ) {
$params = array(
$prefix . 'NAME' => $this->_name,
$prefix . 'STREET' => $this->_street1,
$prefix . 'STREET2' => $this->_street2,
$prefix . 'CITY' => $this->_city,
$prefix . 'STATE' => $this->_state,
$prefix . 'ZIP' => $this->_zip,
$prefix . 'COUNTRYCODE' => $this->_country
);
return $params;
}
public function loadFromGetECResponse( $getECResponse, $prefix, $isBillingAddress = false ) {
$map = array(
'NAME' => '_name',
'STREET' => '_street1',
'STREET2' => '_street2',
'CITY' => '_city',
'STATE' => '_state',
'ZIP' => '_zip',
'PHONENUM' => '_phoneNumber',
'ADDRESSSTATUS' => '_addressStatus',
'ADDRESSOWNER' => '_addressOwner'
);
if ( $isBillingAddress ) {
$map['COUNTRY'] = '_country';
} else {
$map['COUNTRYCODE'] = '_country';
}
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = $prefix . $index;
if ( array_key_exists( $var_name, $getECResponse ) ) {
$this->$value = $getECResponse[ $var_name ];
// ADDRESSSTATUS is returned whether or not a billing address is requested, so we don't want
// the presence of this variable alone be enough to trigger recognition of a complete
// billing address.
if ( 'ADDRESSSTATUS' != $index || ! $isBillingAddress ) {
$found_any = true;
}
}
}
// After the state has been set, attempt to normalize (in case it comes from a PayPal response)
$this->normalizeState();
return $found_any;
}
}

View File

@@ -0,0 +1,300 @@
<?php
/**
* Plugin bootstrapper.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_Admin_Handler {
/**
* Constructor.
*/
public function __construct() {
add_action( 'woocommerce_update_options_general', array( $this, 'force_zero_decimal' ) );
add_action( 'admin_notices', array( $this, 'show_decimal_warning' ) );
// defer this until for next release.
// add_filter( 'woocommerce_get_sections_checkout', array( $this, 'filter_checkout_sections' ) );
add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) );
add_action( 'woocommerce_order_status_on-hold_to_completed', array( $this, 'capture_payment' ) );
add_action( 'woocommerce_order_status_on-hold_to_cancelled', array( $this, 'cancel_payment' ) );
add_action( 'woocommerce_order_status_on-hold_to_refunded', array( $this, 'cancel_payment' ) );
add_filter( 'woocommerce_order_actions', array( $this, 'add_capture_charge_order_action' ) );
add_action( 'woocommerce_order_action_ppec_capture_charge', array( $this, 'maybe_capture_charge' ) );
add_action( 'load-woocommerce_page_wc-settings', array( $this, 'maybe_redirect_to_ppec_settings' ) );
add_action( 'load-woocommerce_page_wc-settings', array( $this, 'maybe_reset_api_credentials' ) );
}
public function add_capture_charge_order_action( $actions ) {
if ( ! isset( $_REQUEST['post'] ) ) {
return $actions;
}
$order = wc_get_order( $_REQUEST['post'] );
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$order_id = $old_wc ? $order->id : $order->get_id();
$payment_method = $old_wc ? $order->payment_method : $order->get_payment_method();
$paypal_status = $old_wc ? get_post_meta( $order_id, '_paypal_status', true ) : $order->get_meta( '_paypal_status', true );
// bail if the order wasn't paid for with this gateway
if ( 'ppec_paypal' !== $payment_method || 'pending' !== $paypal_status ) {
return $actions;
}
if ( ! is_array( $actions ) ) {
$actions = array();
}
$actions['ppec_capture_charge'] = esc_html__( 'Capture Charge', 'woocommerce-gateway-paypal-express-checkout' );
return $actions;
}
/**
* Force zero decimal on specific currencies.
*/
public function force_zero_decimal() {
$settings = wc_gateway_ppec()->settings;
if ( $settings->currency_has_decimal_restriction() ) {
update_option( 'woocommerce_price_num_decimals', 0 );
update_option( 'wc_gateway_ppce_display_decimal_msg', true );
}
}
/**
* Show decimal warning.
*/
public function show_decimal_warning() {
if ( get_option( 'wc_gateway_ppce_display_decimal_msg', false ) ) {
?>
<div class="updated fade">
<p>
<strong><?php _e( 'NOTE: PayPal does not accept decimal places for the currency in which you are transacting. The "Number of Decimals" option in WooCommerce has automatically been set to 0 for you.', 'woocommerce-gateway-paypal-express-checkout' ); ?></strong>
</p>
</div>
<?php
delete_option( 'wc_gateway_ppce_display_decimal_msg' );
}
}
/**
* Prevent PPEC Credit showing up in the admin, because it shares its settings
* with the PayPal Express Checkout class.
*
* @param array $sections List of sections in checkout
*
* @return array Sections in checkout
*/
public function filter_checkout_sections( $sections ) {
$paypal_sections = array(
'wc_gateway_ppec_with_paypal',
);
$card_sections = array(
'wc_gateway_ppec_with_paypal_credit',
);
$current_section = isset( $_GET['section'] ) ? $_GET['section'] : '';
// If the current section is a paypal section, remove the card section,
// otherwise, remove the paypal section
$sections_to_remove = in_array( $current_section, $paypal_sections ) ? $card_sections : $paypal_sections;
// And, let's also remove simplify commerce from the sections if it is not enabled and it is not the
// current section. (Note: The option will be empty if it has never been enabled)
$simplify_commerce_options = get_option( 'woocommerce_simplify_commerce_settings', array() );
if ( empty( $simplify_commerce_options ) || ( 'no' === $simplify_commerce_options['enabled'] ) ) {
if ( 'wc_gateway_simplify_commerce' !== $current_section ) {
$sections_to_remove[] = 'wc_gateway_simplify_commerce';
}
if ( 'wc_addons_gateway_simplify_commerce' !== $current_section ) {
$sections_to_remove[] = 'wc_addons_gateway_simplify_commerce';
}
}
foreach ( $sections_to_remove as $section_to_remove ) {
unset( $sections[ $section_to_remove ] );
}
return $sections;
}
public function maybe_capture_charge( $order ) {
if ( ! is_object( $order ) ) {
$order = wc_get_order( $order );
}
$order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
$this->capture_payment( $order_id );
return true;
}
/**
* Capture payment when the order is changed from on-hold to complete or processing
*
* @param int $order_id
*/
public function capture_payment( $order_id ) {
$order = wc_get_order( $order_id );
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$payment_method = $old_wc ? $order->payment_method : $order->get_payment_method();
if ( 'ppec_paypal' === $payment_method ) {
$trans_id = get_post_meta( $order_id, '_transaction_id', true );
$trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
$order_total = $old_wc ? $order->order_total : $order->get_total();
$params['AUTHORIZATIONID'] = $trans_id;
$params['AMT'] = floatval( $order_total );
$params['COMPLETETYPE'] = 'Complete';
$result = wc_gateway_ppec()->client->do_express_checkout_capture( $params );
if ( is_wp_error( $result ) ) {
$order->add_order_note( __( 'Unable to capture charge!', 'woocommerce-gateway-paypal-express-checkout' ) . ' ' . $result->get_error_message() );
} else {
update_post_meta( $order_id, '_paypal_status', ! empty( $trans_details['PAYMENTSTATUS'] ) ? $trans_details['PAYMENTSTATUS'] : 'completed' );
if ( ! empty( $result['TRANSACTIONID'] ) ) {
update_post_meta( $order_id, '_transaction_id', $result['TRANSACTIONID'] );
}
$order->add_order_note( sprintf( __( 'PayPal Express Checkout charge complete (Charge ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $trans_id ) );
}
}
}
}
/**
* Checks to see if the transaction can be captured
*
* @param array $trans_details
*/
public function is_authorized_only( $trans_details = array() ) {
if ( ! is_wp_error( $trans_details ) && ! empty( $trans_details ) ) {
if ( 'Pending' === $trans_details['PAYMENTSTATUS'] && 'authorization' === $trans_details['PENDINGREASON'] ) {
return true;
}
}
return false;
}
/**
* Cancel authorization
*
* @param int $order_id
*/
public function cancel_payment( $order_id ) {
$order = wc_get_order( $order_id );
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$payment_method = $old_wc ? $order->payment_method : $order->get_payment_method();
if ( 'ppec_paypal' === $payment_method ) {
$trans_id = get_post_meta( $order_id, '_transaction_id', true );
$trans_details = wc_gateway_ppec()->client->get_transaction_details( array( 'TRANSACTIONID' => $trans_id ) );
if ( $trans_id && $this->is_authorized_only( $trans_details ) ) {
$params['AUTHORIZATIONID'] = $trans_id;
$result = wc_gateway_ppec()->client->do_express_checkout_void( $params );
if ( is_wp_error( $result ) ) {
$order->add_order_note( __( 'Unable to void charge!', 'woocommerce-gateway-paypal-express-checkout' ) . ' ' . $result->get_error_message() );
} else {
$order->add_order_note( sprintf( __( 'PayPal Express Checkout charge voided (Charge ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $trans_id ) );
}
}
}
}
/**
* Get admin URL for this gateway setting.
*
* @deprecated
*
* @return string URL
*/
public function gateway_admin_url( $gateway_class ) {
_deprecated_function( 'WC_Gateway_PPEC_Admin_Handler::gateway_admin_url', '1.0.4', 'wc_gateway_ppec()->get_admin_setting_link' );
return wc_gateway_ppec()->get_admin_setting_link();
}
/**
* Maybe redirect to wc_gateway_ppec_with_paypal from PayPal standard
* checkout settings.
*
* @return void
*/
public function maybe_redirect_to_ppec_settings() {
if ( ! wc_gateway_ppec()->settings->enabled ) {
return;
}
if ( empty( $_GET['tab'] ) || empty( $_GET['section'] ) ) {
return;
}
if ( 'checkout' === $_GET['tab'] && 'wc_gateway_paypal' === $_GET['section'] ) {
$redirect = add_query_arg( array( 'section' => 'wc_gateway_ppec_with_paypal' ) );
wp_safe_redirect( $redirect );
}
}
/**
* Reset API credentials if merchant clicked the reset credential link.
*
* When API credentials empty, the connect button will be displayed again,
* allowing merchant to reconnect with other account.
*
* When WooCommerce Branding is active, this handler may not be invoked as
* screen ID may evaluates to something else.
*
* @since 1.2.0
*/
public function maybe_reset_api_credentials() {
if ( empty( $_GET['reset_ppec_api_credentials'] ) ) {
return;
}
if ( empty( $_GET['reset_nonce'] ) || ! wp_verify_nonce( $_GET['reset_nonce'], 'reset_ppec_api_credentials' ) ) {
return;
}
$settings = wc_gateway_ppec()->settings;
$env = $settings->_environment;
if ( ! empty( $_GET['environment'] ) ) {
$env = $_GET['environment'];
}
$prefix = 'sandbox' === $env ? 'sandbox_' : '';
foreach ( array( 'api_username', 'api_password', 'api_signature', 'api_certificate' ) as $key ) {
$key = $prefix . $key;
$settings->{$key} = '';
}
// Save environment too as when it switches to another env and merchant
// click the reset they'd expect to save the environment too.
$settings->environment = 'sandbox' === $env ? 'sandbox' : 'live';
$settings->save();
wp_safe_redirect( wc_gateway_ppec()->get_admin_setting_link() );
}
}

View File

@@ -0,0 +1,58 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class PayPal_API_Error {
public $error_code;
public $short_message;
public $long_message;
public $severity_code;
public function __construct( $error_code, $short_message, $long_message, $severity_code ) {
$this->error_code = $error_code;
$this->short_message = $short_message;
$this->long_message = $long_message;
$this->severity_code = $severity_code;
}
public function mapToBuyerFriendlyError() {
switch ( $this->error_code ) {
case '-1': return __( 'Unable to communicate with PayPal. Please try your payment again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10407': return __( 'PayPal rejected your email address because it is not valid. Please double-check your email address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10409':
case '10421':
case '10410': return __( 'Your PayPal checkout session is invalid. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10411': return __( 'Your PayPal checkout session has expired. Please check out again.', 'woocommerce-gateway-paypal-express-checkout' );
case '11607':
case '10415': return __( 'Your PayPal payment has already been completed. Please contact the store owner for more information.', 'woocommerce-gateway-paypal-express-checkout' );
case '10416': return __( 'Your PayPal payment could not be processed. Please check out again or contact PayPal for assistance.', 'woocommerce-gateway-paypal-express-checkout' );
case '10417': return __( 'Your PayPal payment could not be processed. Please select an alternative method of payment or contact PayPal for assistance.', 'woocommerce-gateway-paypal-express-checkout' );
case '10486':
case '10422': return __( 'Your PayPal payment could not be processed. Please return to PayPal and select a new method of payment.', 'woocommerce-gateway-paypal-express-checkout' );
case '10485':
case '10435': return __( 'You have not approved this transaction on the PayPal website. Please check out again and be sure to complete all steps of the PayPal checkout process.', 'woocommerce-gateway-paypal-express-checkout' );
case '10474': return __( 'Your shipping address may not be in a different country than your country of residence. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10537': return __( 'This store does not accept transactions from buyers in your country. Please contact the store owner for assistance.', 'woocommerce-gateway-paypal-express-checkout' );
case '10538': return __( 'The transaction is over the threshold allowed by this store. Please contact the store owner for assistance.', 'woocommerce-gateway-paypal-express-checkout' );
case '11611':
case '10539': return __( 'Your transaction was declined. Please contact the store owner for assistance.', 'woocommerce-gateway-paypal-express-checkout' );
case '10725': return __( 'The country in your shipping address is not valid. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10727': return __( 'The street address in your shipping address is not valid. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10728': return __( 'The city in your shipping address is not valid. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10729': return __( 'The state in your shipping address is not valid. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10730': return __( 'The ZIP code or postal code in your shipping address is not valid. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10731': return __( 'The country in your shipping address is not valid. Please double-check your shipping address and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '10736': return __( 'PayPal rejected your shipping address because the city, state, and/or ZIP code are incorrect. Please double-check that they are all spelled correctly and try again.', 'woocommerce-gateway-paypal-express-checkout' );
case '13113':
case '11084': return __( 'Your PayPal payment could not be processed. Please contact PayPal for assistance.', 'woocommerce-gateway-paypal-express-checkout' );
case '12126':
case '12125': return __( 'The redemption code(s) you entered on PayPal cannot be used at this time. Please return to PayPal and remove them.', 'woocommerce-gateway-paypal-express-checkout' );
case '17203':
case '17204':
case '17200': return __( 'Your funding instrument is invalid. Please check out again and select a new funding source.', 'woocommerce-gateway-paypal-express-checkout' );
default: return sprintf( __( 'An error (%s) occurred while processing your PayPal payment. Please contact the store owner for assistance.', 'woocommerce-gateway-paypal-express-checkout' ), $this->error_code );
}
}
}

View File

@@ -0,0 +1,259 @@
<?php
/**
* Cart handler.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Gateway_PPEC_Cart_Handler handles button display in the cart.
*/
class WC_Gateway_PPEC_Cart_Handler {
/**
* Constructor.
*/
public function __construct() {
if ( ! wc_gateway_ppec()->settings->is_enabled() ) {
return;
}
add_action( 'woocommerce_before_cart_totals', array( $this, 'before_cart_totals' ) );
add_action( 'woocommerce_widget_shopping_cart_buttons', array( $this, 'display_mini_paypal_button' ), 20 );
add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_paypal_button' ), 20 );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
if ( 'yes' === wc_gateway_ppec()->settings->checkout_on_single_product_enabled ) {
add_action( 'woocommerce_after_add_to_cart_form', array( $this, 'display_paypal_button_product' ), 1 );
add_action( 'wc_ajax_wc_ppec_generate_cart', array( $this, 'wc_ajax_generate_cart' ) );
}
add_action( 'wc_ajax_wc_ppec_update_shipping_costs', array( $this, 'wc_ajax_update_shipping_costs' ) );
}
/**
* Start checkout handler when cart is loaded.
*/
public function before_cart_totals() {
// If there then call start_checkout() else do nothing so page loads as normal.
if ( ! empty( $_GET['startcheckout'] ) && 'true' === $_GET['startcheckout'] ) {
// Trying to prevent auto running checkout when back button is pressed from PayPal page.
$_GET['startcheckout'] = 'false';
woo_pp_start_checkout();
}
}
/**
* Generates the cart for express checkout on a product level.
*
* @since 1.4.0
*/
public function wc_ajax_generate_cart() {
global $post;
if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_ppec_generate_cart_nonce' ) ) {
wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-paypal-express-checkout' ) );
}
if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
}
WC()->shipping->reset_shipping();
$product = wc_get_product( $post->ID );
if ( ! empty( $_POST['add-to-cart'] ) ) {
$product = wc_get_product( absint( $_POST['add-to-cart'] ) );
}
/**
* If this page is single product page, we need to simulate
* adding the product to the cart taken account if it is a
* simple or variable product.
*/
if ( $product ) {
$qty = ! isset( $_POST['qty'] ) ? 1 : absint( $_POST['qty'] );
wc_empty_cart();
if ( $product->is_type( 'variable' ) ) {
$attributes = array_map( 'wc_clean', $_POST['attributes'] );
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
$variation_id = $product->get_matching_variation( $attributes );
} else {
$data_store = WC_Data_Store::load( 'product' );
$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
}
WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
} else {
WC()->cart->add_to_cart( $product->get_id(), $qty );
}
WC()->cart->calculate_totals();
}
wp_send_json( new stdClass() );
}
/**
* Update shipping costs. Trigger this update before checking out to have total costs up to date.
*
* @since 1.4.0
*/
public function wc_ajax_update_shipping_costs() {
if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_ppec_update_shipping_costs_nonce' ) ) {
wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-paypal-express-checkout' ) );
}
if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
}
WC()->shipping->reset_shipping();
WC()->cart->calculate_totals();
wp_send_json( new stdClass() );
}
/**
* Display paypal button on the product page.
*
* @since 1.4.0
*/
public function display_paypal_button_product() {
$gateways = WC()->payment_gateways->get_available_payment_gateways();
if ( ! is_product() || ! isset( $gateways['ppec_paypal'] ) ) {
return;
}
$settings = wc_gateway_ppec()->settings;
$express_checkout_img_url = apply_filters( 'woocommerce_paypal_express_checkout_button_img_url', sprintf( 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-%s.png', $settings->button_size ) );
?>
<div class="wcppec-checkout-buttons woo_pp_cart_buttons_div">
<a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ec_button_product" class="wcppec-checkout-buttons__button">
<img src="<?php echo esc_url( $express_checkout_img_url ); ?>" alt="<?php _e( 'Check out with PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
</a>
</div>
<?php
}
/**
* Display paypal button on the cart page.
*/
public function display_paypal_button() {
$gateways = WC()->payment_gateways->get_available_payment_gateways();
$settings = wc_gateway_ppec()->settings;
// billing details on checkout page to calculate shipping costs
if ( ! isset( $gateways['ppec_paypal'] ) || 'no' === $settings->cart_checkout_enabled ) {
return;
}
$express_checkout_img_url = apply_filters( 'woocommerce_paypal_express_checkout_button_img_url', sprintf( 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-%s.png', $settings->button_size ) );
$paypal_credit_img_url = apply_filters( 'woocommerce_paypal_express_checkout_credit_button_img_url', sprintf( 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppcredit-logo-%s.png', $settings->button_size ) );
?>
<div class="wcppec-checkout-buttons woo_pp_cart_buttons_div">
<?php if ( has_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout' ) ) : ?>
<div class="wcppec-checkout-buttons__separator">
<?php _e( '&mdash; or &mdash;', 'woocommerce-gateway-paypal-express-checkout' ); ?>
</div>
<?php endif; ?>
<a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ec_button" class="wcppec-checkout-buttons__button">
<img src="<?php echo esc_url( $express_checkout_img_url ); ?>" alt="<?php _e( 'Check out with PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
</a>
<?php if ( $settings->is_credit_enabled() ) : ?>
<a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true', 'use-ppc' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ppc_button" class="wcppec-checkout-buttons__button">
<img src="<?php echo esc_url( $paypal_credit_img_url ); ?>" alt="<?php _e( 'Pay with PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
</a>
<?php endif; ?>
</div>
<?php
}
/**
* Display paypal button on the cart widget
*/
public function display_mini_paypal_button() {
$gateways = WC()->payment_gateways->get_available_payment_gateways();
$settings = wc_gateway_ppec()->settings;
// billing details on checkout page to calculate shipping costs
if ( ! isset( $gateways['ppec_paypal'] ) || 'no' === $settings->cart_checkout_enabled ) {
return;
}
?>
<a href="<?php echo esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ); ?>" id="woo_pp_ec_button" class="wcppec-cart-widget-button">
<img src="<?php echo esc_url( 'https://www.paypalobjects.com/webstatic/en_US/i/btn/png/gold-rect-paypalcheckout-26px.png' ); ?>" alt="<?php _e( 'Check out with PayPal', 'woocommerce-gateway-paypal-express-checkout' ); ?>" style="width: auto; height: auto;">
</a>
<?php
}
/**
* Frontend scripts
*/
public function enqueue_scripts() {
$settings = wc_gateway_ppec()->settings;
$client = wc_gateway_ppec()->client;
wp_enqueue_style( 'wc-gateway-ppec-frontend-cart', wc_gateway_ppec()->plugin_url . 'assets/css/wc-gateway-ppec-frontend-cart.css' );
if ( is_cart() ) {
wp_enqueue_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
wp_enqueue_script( 'wc-gateway-ppec-frontend-in-context-checkout', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-frontend-in-context-checkout.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
wp_localize_script( 'wc-gateway-ppec-frontend-in-context-checkout', 'wc_ppec_context',
array(
'payer_id' => $client->get_payer_id(),
'environment' => $settings->get_environment(),
'locale' => $settings->get_paypal_locale(),
'start_flow' => esc_url( add_query_arg( array( 'startcheckout' => 'true' ), wc_get_page_permalink( 'cart' ) ) ),
'show_modal' => apply_filters( 'woocommerce_paypal_express_checkout_show_cart_modal', true ),
'update_shipping_costs_nonce' => wp_create_nonce( '_wc_ppec_update_shipping_costs_nonce' ),
'ajaxurl' => WC_AJAX::get_endpoint( 'wc_ppec_update_shipping_costs' ),
)
);
}
if ( is_product() ) {
wp_enqueue_script( 'wc-gateway-ppec-generate-cart', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-generate-cart.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
wp_localize_script( 'wc-gateway-ppec-generate-cart', 'wc_ppec_context',
array(
'generate_cart_nonce' => wp_create_nonce( '_wc_ppec_generate_cart_nonce' ),
'ajaxurl' => WC_AJAX::get_endpoint( 'wc_ppec_generate_cart' ),
)
);
}
}
/**
* @deprecated
*/
public function loadCartDetails() {
_deprecated_function( __METHOD__, '1.2.0', '' );
}
/**
* @deprecated
*/
public function loadOrderDetails( $order_id ) {
_deprecated_function( __METHOD__, '1.2.0', '' );
}
/**
* @deprecated
*/
public function setECParams() {
_deprecated_function( __METHOD__, '1.2.0', '' );
}
}

View File

@@ -0,0 +1,520 @@
<?php
/**
* TODO: Move each class into its own file and group them under one dir, checkout-details.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
$includes_path = wc_gateway_ppec()->includes_path;
require_once( $includes_path . 'class-wc-gateway-ppec-address.php' );
class PayPal_Checkout_Details {
public $raw_response = array();
public $token = false;
public $custom = false;
public $invnum = false;
public $phone_number = false;
public $billing_agreement_accepted = false;
const BillingAgreementNotAccepted = '0';
const BillingAgreementAccepted = '1';
public $paypal_adjustment = false;
public $redirect_required_after_payment = false;
public $checkout_status = false;
const PaymentNotAttempted = 'PaymentActionNotInitiated';
const PaymentFailed = 'PaymentActionFailed';
const PaymentInProgress = 'PaymentActionInProgress';
const PaymentCompleted = 'PaymentActionCompleted';
public $gift_details = false;
public $buyer_marketing_email = false;
public $survey_question = false;
public $survey_choice_selected = false;
public $payer_details = false;
public $wallets = false;
public $instrument_details = false;
public $shipping_option_details = false;
public $payments = false;
public function loadFromGetECResponse( $getECResponse ) {
$this->raw_response = $getECResponse;
$map = array(
'TOKEN' => 'token',
'CUSTOM' => 'custom',
'INVNUM' => 'invnum',
'PHONENUM' => 'phone_number',
'BILLINGAGREEMENTACCEPTEDSTATUS' => 'billing_agreement_accepted',
'PAYPALADJUSTMENT' => 'paypal_adjustment',
'REDIRECTREQUIRED' => 'redirect_required_after_payment',
'CHECKOUTSTATUS' => 'checkout_status',
'BUYERMARKETINGEMAIL' => 'buyer_marketing_email',
'SURVEYQUESTION' => 'survey_question',
'SURVEYCHOICESELECTED' => 'survey_choice_selected'
);
foreach ( $getECResponse as $index => $value ) {
if ( array_key_exists( $index, $map ) ) {
$this->{ $map[ $index ] } = $value;
}
}
$this->gift_details = new PayPal_Checkout_Gift_Details();
if ( ! $this->gift_details->loadFromGetECResponse( $getECResponse ) ) {
$this->gift_details = false;
}
$this->payer_details = new PayPal_Checkout_Payer_Details();
if ( ! $this->payer_details->loadFromGetECResponse( $getECResponse ) ) {
$this->payer_details = false;
}
$this->instrument_details = new PayPal_Checkout_Instrument_Details();
if ( ! $this->instrument_details->loadFromGetECResponse( $getECResponse ) ) {
$this->instrument_details = false;
}
$this->shipping_option_details = new PayPal_Checkout_Shipping_Option_Details();
if ( ! $this->shipping_option_details->loadFromGetECResponse( $getECResponse ) ) {
$this->shipping_option_details = false;
}
$max_wallet_num = -1;
$max_payment_num = -1;
foreach ( $getECResponse as $index => $value ) {
if ( preg_match( '/^(WALLETTYPE|WALLETID|WALLETDESCRIPTION)(\d+)$/', $index, $matches ) ) {
if ( $matches[2] > $max_wallet_num ) {
$max_wallet_num = $matches[2];
}
} elseif ( preg_match( '/^PAYMENTREQUEST_(\d)_(AMT|CURRENCYCODE|ITEMAMT|SHIPPINGAMT|INSURANCEAMT|SHIPDISCAMT|INSURANCEOPTIONOFFERED|HANDLINGAMT|TAXAMT|DESC|CUSTOM|INVNUM|NOTIFYURL|NOTETEXT|TRANSACTIONID|ALLOWEDPAYMENTMETHOD|PAYMENTREQUESTID|BUCKETCATEGORYTYPE)$/', $index, $matches )
|| preg_match( '/^L_PAYMENTREQUEST_(\d)_(NAME|DESC|AMT|NUMBER|QTY|TAXAMT|ITEMWEIGHTVALUE|ITEMWEIGHTUNIT|ITEMLENGTHVALUE|ITEMLENGTHUNIT|ITEMWIDTHVALUE|ITEMWIDTHUNIT|ITEMHEIGHTVALUE|ITEMHEIGHTUNIT)\d+$/', $index, $matches ) ) {
if ( $matches[1] > $max_payment_num ) {
$max_payment_num = $matches[1];
}
}
}
if ( $max_wallet_num > -1 ) {
$this->wallets = array();
for ( $i = 0; $i <= $max_wallet_num; $i++ ) {
$this->wallets[ $i ] = new PayPal_Checkout_Wallet_Details();
$this->wallets[ $i ]->loadFromGetECResponse( $getECResponse, $i );
}
}
if ( $max_payment_num > -1 ) {
$this->payments = array();
for ( $i = 0; $i <= $max_payment_num; $i++ ) {
$this->payments[ $i ] = new PayPal_Checkout_Payment_Details();
$this->payments[ $i ]->loadFromGetECResponse( $getECResponse, $i );
}
}
}
}
class PayPal_Checkout_Payment_Details {
public $shipping_address = false;
public $shipping_address_confirmed = false;
public $shipping_address_normalization_status = false;
const AddressNormalizationNone = 'None';
const AddressNormalizationNormalized = 'Normalized';
const AddressNormalizationUnnormalized = 'Unnormalized';
const AddressNormalizationUserPreferred = 'UserPreferred';
public $amount = false;
public $currency_code = false;
public $item_amount = false;
public $shipping_amount = false;
public $insurance_amount = false;
public $shipping_discount_amount = false;
public $insurance_option_offered = false;
public $handling_amount = false;
public $tax_amount = false;
public $description = false;
public $custom = false;
public $invoice_number = false;
public $notify_url = false;
public $note_text = false;
public $transaction_id = false;
public $allowed_payment_method = false;
const AllowedPaymentMethodInstantPaymentOnly = 'InstantPaymentOnly';
public $payment_request_id = false;
public $bucket_category_type = false;
const BucketCategoryInternationalShipping = '1';
const BucketCategoryLocalDelivery = '2';
public $items = false;
public function loadFromGetECResponse( $getECResponse, $bucketNum ) {
$map = array(
'AMT' => 'amount',
'CURRENCYCODE' => 'currency_code',
'ITEMAMT' => 'item_subtotal',
'SHIPPINGAMT' => 'shipping_amount',
'INSURANCEAMT' => 'insurance_amount',
'SHIPDISCAMT' => 'shipping_discount_amount',
'INSURANCEOPTIONOFFERED' => 'insurance_option_offered',
'HANDLINGAMT' => 'handling_amount',
'TAXAMT' => 'tax_amount',
'DESC' => 'description',
'CUSTOM' => 'custom',
'INVNUM' => 'invoice_number',
'NOTIFYURL' => 'notify_url',
'NOTETEXT' => 'note_text',
'TRANSACTIONID' => 'transaction_id',
'ALLOWEDPAYMENTMETHOD' => 'allowed_payment_method',
'PAYMENTREQUESTID' => 'payment_request_id',
'BUCKETCATEGORYTYPE' => 'bucket_category_type',
'ADDRESSNORMALIZATIONSTATUS' => 'shipping_address_normalization_status'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'PAYMENTREQUEST_' . $bucketNum . '_' . $index;
if ( array_key_exists( $var_name, $getECResponse ) ) {
$this->{ $value } = $getECResponse[ $var_name ];
$found_any = true;
}
}
// See if we have any line items that need to be parsed
$max_line_item_num = -1;
foreach ( $getECResponse as $index => $value ) {
if ( preg_match( '/^L_PAYMENTREQUEST_' . $bucketNum . '_(NAME|DESC|AMT|NUMBER|QTY|TAXAMT|ITEMWEIGHTVALUE|ITEMWEIGHTUNIT|ITEMLENGTHVALUE|ITEMLENGTHUNIT|ITEMWIDTHVALUE|ITEMWIDTHUNIT|ITEMHEIGHTVALUE|ITEMHEIGHTUNIT|ITEMCATEGORY|EBAYITEMNUMBER|EBAYITEMAUCTIONTXNID|EBAYITEMORDERID|EBAYITEMCARTID)(\d+)$/', $index, $matches ) ) {
if ( isset( $matches[2] ) && $matches[2] > $max_line_item_num ) {
$max_line_item_num = $matches[2];
}
}
}
if ( $max_line_item_num > -1 ) {
$found_any = true;
$this->items = array();
for ( $i = 0; $i <= $max_line_item_num; $i++ ) {
$items[ $i ] = new PayPal_Checkout_Payment_Item_Details();
$items[ $i ]->loadFromGetECResponse( $getECResponse, $bucketNum, $i );
}
}
$this->shipping_address = new PayPal_Address();
if ( ! $this->shipping_address->loadFromGetECResponse( $getECResponse, 'PAYMENTREQUEST_' . $bucketNum . '_SHIPTO' ) ) {
$this->shipping_address = false;
} else {
$found_any = true;
}
return $found_any;
}
}
class PayPal_Checkout_Payment_Item_Details {
public $name = false;
public $description = false;
public $amount = false;
public $item_number = false;
public $quantity = false;
public $tax_amount = false;
public $physical_details = false;
public $ebay_item_details = false;
public function loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) {
$map = array(
'NAME' => 'name',
'DESC' => 'description',
'AMT' => 'amount',
'NUMBER' => 'item_number',
'QTY' => 'quantity',
'TAXAMT' => 'tax_amount',
);
foreach ( $map as $index => $value ) {
$var_name = 'L_PAYMENTREQUEST_' . $bucketNum . '_' . $index . $itemNum;
if ( array_key_exists( $var_name, $getECResponse ) ) {
$this->{ $value } = $getECResponse[ $var_name ];
}
}
$this->physical_details = new PayPal_Checkout_Payment_Item_Physical_Details();
if ( ! $this->physical_details->loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) ) {
$this->physical_details = false;
}
$this->ebay_item_details = new PayPal_Checkout_Payment_Item_Ebay_Item_Details();
if ( ! $this->ebay_item_details->loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) ) {
$this->ebay_item_details = false;
}
}
}
class PayPal_Checkout_Payment_Item_Physical_Details {
public $weight;
public $weight_units;
public $length;
public $length_units;
public $width;
public $width_units;
public $height;
public $height_units;
public function loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) {
$map = array(
'WEIGHTVALUE' => 'weight',
'WEIGHTUNIT' => 'weight_units',
'LENGTHVALUE' => 'length',
'LENGTHUNIT' => 'length_units',
'WIDTHVALUE' => 'width',
'WIDTHUNIT' => 'width_units',
'HEIGHTVALUE' => 'height',
'HEIGHTUNIT' => 'height_units'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'L_PAYMENTREQUEST_' . $bucketNum . '_ITEM' . $index . $itemNum;
if ( array_key_exists( $var_name, $getECResponse ) ) {
$this->{ $value } = $getECResponse[ $var_name ];
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Checkout_Payment_Item_Ebay_Item_Details {
public $item_number = false;
public $auction_transaction_id = false;
public $order_id = false;
public $cart_id = false;
public function loadFromGetECResponse( $getECResponse, $bucketNum, $itemNum ) {
$map = array(
'ITEMNUMBER' => 'item_number',
'AUCTIONTXNID' => 'auction_transaction_id',
'ORDERID' => 'order_id',
'CARTID' => 'cart_id'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'L_PAYMENTREQUEST_' . $bucketNum . '_' . $index . $itemNum;
if ( array_key_exists( $var_name, $getECResponse ) ) {
$this->{ $value } = $getECResponse[ $var_name ];
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Checkout_Shipping_Option_Details {
public $calculation_mode = false;
const CalculationModeCallback = 'Callback';
const CalculationModeFlatrate = 'FlatRate';
public $insurance_option_selected = false;
public $shipping_option_is_default = false;
public $shipping_option_amount = false;
public $shipping_option_name = false;
// Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
// If not, it returns false to indicate that the caller can destroy this object.
public function loadFromGetECResponse( $getECResponse ) {
$map = array(
'SHIPPINGCALCULATIONMODE' => 'calculation_mode',
'INSURANCEOPTIONSELECTED' => 'insurance_option_selected',
'SHIPPINGOPTIONISDEFAULT' => 'shipping_option_is_default',
'SHIPPINGOPTIONAMOUNT' => 'shipping_option_amount',
'SHIPPINGOPTIONNAME' => 'shipping_option_name'
);
$found_any = false;
foreach ( $getECResponse as $index => $value ) {
if ( array_key_exists( $index, $map ) ) {
$this->{ $map[ $index ] } = $value;
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Checkout_Instrument_Details {
public $instrument_category = false;
const InstrumentCategoryPayPalCredit = '1';
const InstrumentCategoryPrivateCard = '2';
public $instrument_id = false;
// Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
// If not, it returns false to indicate that the caller can destroy this object.
public function loadFromGetECResponse( $getECResponse ) {
$map = array(
'INSTRUMENTCATEGORY' => 'instrument_category',
'INSTRUMENTID' => 'instrument_id'
);
$found_any = false;
foreach ( $getECResponse as $index => $value ) {
if ( array_key_exists( $index, $map ) ) {
$this->{ $map[ $index ] } = $value;
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Checkout_Wallet_Details {
public $wallet_type = false;
const WalletTypeLoyaltyCard = 'LOYALTY_CARD';
const WalletTypeMerchantCoupon = 'MERCHANT_COUPON';
const WalletTypeMerchantClosedLoopOffer = 'MERCHANT_CLOSED_LOOP_OFFER';
public $wallet_id = false;
public $wallet_description = false;
public function __construct( $wallet_type = false, $wallet_id = false, $wallet_description = false ) {
$this->wallet_type = $wallet_type;
$this->wallet_id = $wallet_id;
$this->wallet_description = $wallet_description;
}
// Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
// If not, it returns false to indicate that the caller can destroy this object.
public function loadFromGetECResponse( $getECResponse, $wallet_num ) {
$found_any = false;
foreach ( $getECResponse as $index => $value ) {
if ( ( 'WALLETTYPE' . $wallet_num ) == $index ) {
$this->wallet_type = $value;
$found_any = true;
} elseif ( ( 'WALLETID' . $wallet_num ) == $index ) {
$this->wallet_id = $value;
$found_any = true;
} elseif ( ( 'WALLETDESCRIPTION' . $wallet_num ) == $index ) {
$this->wallet_description = $value;
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Checkout_Payer_Details {
public $phone_number = false;
public $email = false;
public $payer_id = false;
public $payer_status = false;
const PayerStatusVerified = 'verified';
const PayerStatusUnverified = 'unverified';
public $country = false;
public $business_name = false;
public $first_name = false;
public $last_name = false;
public $middle_name = false;
public $suffix = false;
public $billing_address = false;
// Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
// If not, it returns false to indicate that the caller can destroy this object.
public function loadFromGetECResponse( $getECResponse ) {
$map = array(
'PHONENUM' => 'phone_number',
'EMAIL' => 'email',
'PAYERID' => 'payer_id',
'PAYERSTATUS' => 'payer_status',
'COUNTRYCODE' => 'country',
'BUSINESS' => 'business_name',
'FIRSTNAME' => 'first_name',
'MIDDLENAME' => 'middle_name',
'LASTNAME' => 'last_name',
'SUFFIX' => 'suffix'
);
$found_any = false;
// At the same time, see if we have a billing address that needs to be parsed out.
$billing_address_present = false;
foreach ( $getECResponse as $index => $value ) {
if ( array_key_exists( $index, $map ) ) {
$this->{ $map[ $index ] } = $value;
$found_any = true;
}
if ( preg_match( '/^BILLTONAME|STREET|STREET2|CITY|STATE|ZIP|COUNTRY|COUNTRYNAME|ADDRESSOWNER|ADDRESSSTATUS$/', $index ) ) {
$billing_address_present = true;
}
}
if ( $billing_address_present ) {
$this->billing_address = new PayPal_Address();
if ( $this->billing_address->loadFromGetECResponse( $getECResponse, '', true ) ) {
$found_any = true;
} else {
$this->billing_address = false;
}
}
return $found_any;
}
}
class PayPal_Checkout_Gift_Details {
public $gift_message = false;
public $gift_receipt_enabled = false;
public $gift_wrap_name = false;
public $gift_wrap_amount = false;
// Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
// If not, it returns false to indicate that the caller can destroy this object.
public function loadFromGetECResponse( $getECResponse ) {
$map = array(
'GIFTMESSAGE' => 'gift_message',
'GIFTWRAPNAME' => 'gift_wrap_name',
'GIFTRECEIPTENABLE' => 'gift_receipt_enabled',
'GIFTWRAPAMOUNT' => 'gift_wrap_amount'
);
$found_any = false;
foreach ( $getECResponse as $index => $value ) {
if ( array_key_exists( $index, $map ) ) {
$this->{ $map[ $index ] } = $value;
$found_any = true;
}
}
return $found_any;
}
}

View File

@@ -0,0 +1,146 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_Client_Credential_Certificate extends WC_Gateway_PPEC_Client_Credential {
/**
* Certificate string.
*
* @var string
*/
protected $_certificate;
/**
* Creates a new instance of certificate-based credential.
*
* @param string $username The API username that will be set on this object.
* @param string $password The API password that will be set on this object.
* @param string $certificate The API certificate that will be set on this object.
* @param string $subject The API subject that will be set on this object, or false if there is no subject.
*/
public function __construct( $username, $password, $certificate, $subject = '' ) {
$this->_username = $username;
$this->_password = $password;
$this->_certificate = $certificate;
$this->_subject = $subject;
}
/**
* {@inheritdoc}
*/
public function get_endpoint_subdomain() {
return 'api';
}
/**
* Get certificate.
*
* @return string
*/
public function get_certificate() {
return $this->_certificate;
}
/**
* Allow certificate-based credential to configure cURL, especially
* to set CURLOPT_SSLCERT and CURLOPT_SSLCERTPASSWD.
*
* @throws Exception
*
* @param resource &$handle The cURL handle returned by curl_init().
* @param array $r The HTTP request arguments.
* @param string $url The request URL.
*
* @return void
*/
public function configure_curl( $handle, $r, $url ) {
parent::configure_curl( $handle, $r, $url );
$password = uniqid();
$certificate_file = $this->_maybe_create_certificate_file( $password );
if ( false === curl_setopt( $handle, CURLOPT_SSLCERT, $certificate_file ) ) {
throw new Exception( __( 'Unable to accept certificate during cURL configuration', 'woocommerce-gateway-paypal-express-checkout' ), WC_Gateway_PPEC_Client::INVALID_ENVIRONMENT_ERROR );
}
if ( $this->_use_secure_transport() && false === curl_setopt( $handle, CURLOPT_SSLCERTPASSWD, $password ) ) {
throw new Exception( __( 'Unable to accept certificate password during cURL configuration', 'woocommerce-gateway-paypal-express-checkout' ), WC_Gateway_PPEC_Client::INVALID_ENVIRONMENT_ERROR );
}
}
/**
* Dump the certificate out to a temporary file, because cURL can't accept
* it any other way.
*
* @throws Exception
*
* @param string $password Password for certificate when using secure transport
*
* @return string Filepath of certificate file
*/
protected function _maybe_create_certificate_file( $password ) {
$temp_file = tempnam( sys_get_temp_dir(), 'pptmp_' );
if ( ! $temp_file ) {
throw new Exception( sprintf( __( 'Unable to write certificate file %s during cURL configuration', 'woocommerce-gateway-paypal-express-checkout' ), $temp_file ), WC_Gateway_PPEC_Client::INVALID_ENVIRONMENT_ERROR );
}
if ( $this->_use_secure_transport() ) {
$this->_maybe_create_secure_certificate_file( $temp_file, $password );
} else {
$this->_maybe_create_non_secure_certificate_file( $temp_file );
}
return $temp_file;
}
/**
* If we're using SecureTransport, we have to translate the certificate to
* PKCS12 before passing it to cURL.
*
* @throws Exception
*
* @param string $temp_file Filepath to temporary certificate file
*
* @return void
*/
protected function _maybe_create_secure_certificate_file( $temp_file, $password ) {
$private_key = openssl_pkey_get_private( $this->_certificate );
if ( false === $private_key ) {
throw new Exception( __( 'Failed to retrieve private key during cURL configuration', 'woocommerce-gateway-paypal-express-checkout' ), WC_Gateway_PPEC_Client::INVALID_ENVIRONMENT_ERROR );
}
if ( ! openssl_pkcs12_export_to_file( $this->_certificate, $temp_file, $private_key, $password ) ) {
throw new Exception( __( 'Failed to export PKCS12 file during cURL configuration', 'woocommerce-gateway-paypal-express-checkout' ), WC_Gateway_PPEC_Client::INVALID_ENVIRONMENT_ERROR );
}
}
/**
* Create non-password certificate file. Basically just dump the certificate
* string to temporary file.
*
* @throws Exception
*
* @param string $temp_file Filepath to temporary certificate file
*
* @return void
*/
protected function _maybe_create_non_secure_certificate_file( $temp_file ) {
if ( false === file_put_contents( $temp_file, $this->_certificate ) ) {
throw new Exception( sprintf( __( 'Unable to write certificate file %s during cURL configuration', 'woocommerce-gateway-paypal-express-checkout' ), $temp_file ), WC_Gateway_PPEC_Client::INVALID_ENVIRONMENT_ERROR );
}
}
/**
* Returns true if secure transport is available in current cURL.
*
* @return bool
*/
protected function _use_secure_transport() {
$curl_version = curl_version();
return false !== strpos( $curl_version['ssl_version'], 'SecureTransport' );
}
}

View File

@@ -0,0 +1,56 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_Client_Credential_Signature extends WC_Gateway_PPEC_Client_Credential {
/**
* Signature string.
*
* @var string
*/
protected $_signature;
/**
* Creates a new instance of signature-based credential.
*
* @param string $username The API username that will be set on this object.
* @param string $password The API password that will be set on this object.
* @param string $signature The API signature that will be set on this object.
* @param string $subject The API subject that will be set on this object, or false if there is no subject.
*/
public function __construct( $username, $password, $signature, $subject = '' ) {
$this->_username = $username;
$this->_password = $password;
$this->_signature = $signature;
$this->_subject = $subject;
}
/**
* {@inheritdoc}
*/
public function get_request_params() {
$params = parent::get_request_params();
$params['SIGNATURE'] = $this->_signature;
return $params;
}
/**
* {@inheritdoc}
*/
public function get_endpoint_subdomain() {
return 'api-3t';
}
/**
* Get signature.
*
* @return string
*/
public function get_signature() {
return $this->_signature;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Plugin bootstrapper.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class WC_Gateway_PPEC_Gateway_Loader {
/**
* Constructor.
*/
public function __construct() {
$includes_path = wc_gateway_ppec()->includes_path;
require_once( $includes_path . 'class-wc-gateway-ppec-refund.php' );
require_once( $includes_path . 'abstracts/abstract-wc-gateway-ppec.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal-credit.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-with-paypal-addons.php' );
add_filter( 'woocommerce_payment_gateways', array( $this, 'payment_gateways' ) );
}
/**
* Register the PPEC payment methods.
*
* @param array $methods Payment methods.
*
* @return array Payment methods
*/
public function payment_gateways( $methods ) {
$settings = wc_gateway_ppec()->settings;
if ( $this->can_use_addons() ) {
$methods[] = 'WC_Gateway_PPEC_With_PayPal_Addons';
} else {
$methods[] = 'WC_Gateway_PPEC_With_PayPal';
}
if ( $settings->is_credit_enabled() ) {
$methods[] = 'WC_Gateway_PPEC_With_PayPal_Credit';
}
return $methods;
}
/**
* Checks whether gateway addons can be used.
*
* @since 1.2.0
*
* @return bool Returns true if gateway addons can be used
*/
public function can_use_addons() {
return ( class_exists( 'WC_Subscriptions_Order' ) && function_exists( 'wcs_create_renewal_order' ) );
}
}

View File

@@ -0,0 +1,355 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* PayPal Instant Payment Notification handler.
*
* @see https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/
* @since 1.1.2
*/
class WC_Gateway_PPEC_IPN_Handler extends WC_Gateway_PPEC_PayPal_Request_Handler {
/**
* Handle the IPN request.
*/
public function handle() {
add_action( 'woocommerce_api_wc_gateway_ppec', array( $this, 'check_request' ) );
add_action( 'woocommerce_paypal_express_checkout_valid_ipn_request', array( $this, 'handle_valid_ipn' ) );
}
/**
* Check request.
*/
public function check_request() {
try {
if ( empty( $_POST ) ) {
throw new Exception( esc_html__( 'Empty POST data.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
if ( $this->is_valid_ipn_request( $_POST ) ) {
wc_gateway_ppec_log( 'IPN request is valid according to PayPal.' );
do_action( 'woocommerce_paypal_express_checkout_valid_ipn_request', wp_unslash( $_POST ) );
exit;
} else {
wc_gateway_ppec_log( 'IPN request is NOT valid according to PayPal.' );
throw new Exception( esc_html__( 'Invalid IPN request.' , 'woocommerce-gateway-paypal-express-checkout' ) );
}
} catch ( Exception $e ) {
wp_die( $e->getMessage(), esc_html__( 'PayPal IPN Request Failure', 'woocommerce-gateway-paypal-express-checkout' ), array( 'response' => 500 ) );
}
}
/**
* Check with PayPal whether posted data is valid IPN request.
*
* @throws Exception
*
* @param array $posted_data Posted data
* @return bool True if posted_data is valid IPN request
*/
public function is_valid_ipn_request( array $posted_data ) {
wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'Checking IPN request validity' ) );
$ipn_request = array(
'cmd' => '_notify-validate',
);
$ipn_request += wp_unslash( $posted_data );
$params = array(
'body' => $ipn_request,
'timeout' => 60,
'httpversion' => '1.1',
'compress' => false,
'decompress' => false,
'user-agent' => get_class( $this->gateway ),
);
// Post back to PayPal to check validity of IPN request.
$response = wp_safe_remote_post( $this->get_validator_url(), $params );
wc_gateway_ppec_log( sprintf( '%s: %s: %s', __FUNCTION__, 'Verify IPN request', print_r( $params, true ) ) );
wc_gateway_ppec_log( sprintf( '%s: %s: %s', __FUNCTION__, 'Response for the IPN request', print_r( $response, true ) ) );
if ( is_wp_error( $response ) ) {
throw new Exception( $response->get_error_message() );
}
return (
$response['response']['code'] >= 200
&&
$response['response']['code'] < 300
&&
strstr( $response['body'], 'VERIFIED' )
);
}
/**
* Handle valid IPN request.
*
* @param array $posted_data Posted data
*/
public function handle_valid_ipn( $posted_data ) {
if ( ! empty( $posted_data['custom'] ) && ( $order = $this->get_paypal_order( $posted_data['custom'] ) ) ) {
// Lowercase returned variables.
$posted_data['payment_status'] = strtolower( $posted_data['payment_status'] );
// Sandbox fix.
if ( ( empty( $posted_data['pending_reason'] ) || 'authorization' !== $posted_data['pending_reason'] ) && isset( $posted_data['test_ipn'] ) && 1 == $posted_data['test_ipn'] && 'pending' == $posted_data['payment_status'] ) {
$posted_data['payment_status'] = 'completed';
}
$order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
wc_gateway_ppec_log( 'Found order #' . $order_id );
wc_gateway_ppec_log( 'Payment status: ' . $posted_data['payment_status'] );
if ( method_exists( $this, 'payment_status_' . $posted_data['payment_status'] ) ) {
call_user_func( array( $this, 'payment_status_' . $posted_data['payment_status'] ), $order, $posted_data );
}
} else {
wc_gateway_ppec_log( sprintf( '%s: %s', __FUNCTION__, 'No order data being passed' ) );
}
}
/**
* Check for a valid transaction type.
*
* @param string $txn_type Transaction type
*/
protected function validate_transaction_type( $txn_type ) {
$accepted_types = array( 'cart', 'instant', 'express_checkout', 'web_accept', 'masspay', 'send_money' );
if ( ! in_array( strtolower( $txn_type ), $accepted_types ) ) {
wc_gateway_ppec_log( 'Aborting, Invalid type:' . $txn_type );
exit;
}
}
/**
* Check currency from IPN matches the order.
*
* @param WC_Order $order Order object
* @param string $currency Currency
*/
protected function validate_currency( $order, $currency ) {
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$order_currency = $old_wc ? $order->order_currency : $order->get_currency();
if ( $order_currency !== $currency ) {
wc_gateway_ppec_log( 'Payment error: Currencies do not match (sent "' . $order_currency . '" | returned "' . $currency . '")' );
// Put this order on-hold for manual checking.
$order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal currencies do not match (code %s).', 'woocommerce-gateway-paypal-express-checkout' ), $currency ) );
exit;
}
}
/**
* Check payment amount from IPN matches the order.
*
* @param WC_Order $order Order object
* @param int $amount Amount
*/
protected function validate_amount( $order, $amount ) {
if ( number_format( $order->get_total(), 2, '.', '' ) != number_format( $amount, 2, '.', '' ) ) {
wc_gateway_ppec_log( 'Payment error: Amounts do not match (gross ' . $amount . ')' );
// Put this order on-hold for manual checking.
$order->update_status( 'on-hold', sprintf( __( 'Validation error: PayPal amounts do not match (gross %s).', 'woocommerce-gateway-paypal-express-checkout' ), $amount ) );
exit;
}
}
/**
* Handle a completed payment.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_completed( $order, $posted_data ) {
$order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
if ( $order->has_status( array( 'processing', 'completed' ) ) ) {
wc_gateway_ppec_log( 'Aborting, Order #' . $order_id . ' is already complete.' );
exit;
}
$this->validate_transaction_type( $posted_data['txn_type'] );
$this->validate_currency( $order, $posted_data['mc_currency'] );
$this->validate_amount( $order, $posted_data['mc_gross'] );
$this->save_paypal_meta_data( $order, $posted_data );
if ( 'completed' === $posted_data['payment_status'] ) {
$this->payment_complete( $order, ( ! empty( $posted_data['txn_id'] ) ? wc_clean( $posted_data['txn_id'] ) : '' ), __( 'IPN payment completed', 'woocommerce-gateway-paypal-express-checkout' ) );
if ( ! empty( $posted_data['mc_fee'] ) ) {
// Log paypal transaction fee.
$transaction_fee = wc_clean( $posted_data['mc_fee'] );
update_post_meta( $order_id, 'PayPal Transaction Fee', $transaction_fee );
}
} else {
if ( 'authorization' === $posted_data['pending_reason'] ) {
$this->payment_on_hold( $order, __( 'Payment authorized. Change payment status to processing or complete to capture funds.', 'woocommerce-gateway-paypal-express-checkout' ) );
} else {
$this->payment_on_hold( $order, sprintf( __( 'Payment pending (%s).', 'woocommerce-gateway-paypal-express-checkout' ), $posted_data['pending_reason'] ) );
}
}
}
/**
* Handle a pending payment.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_pending( $order, $posted_data ) {
$this->payment_status_completed( $order, $posted_data );
}
/**
* Handle a failed payment.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_failed( $order, $posted_data ) {
$order->update_status( 'failed', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), wc_clean( $posted_data['payment_status'] ) ) );
}
/**
* Handle a denied payment.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_denied( $order, $posted_data ) {
$this->payment_status_failed( $order, $posted_data );
}
/**
* Handle an expired payment.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_expired( $order, $posted_data ) {
$this->payment_status_failed( $order, $posted_data );
}
/**
* Handle a voided payment.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_voided( $order, $posted_data ) {
$this->payment_status_failed( $order, $posted_data );
}
/**
* Handle a refunded order.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_refunded( $order, $posted_data ) {
// Only handle full refunds, not partial.
$order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
if ( $order->get_total() == ( $posted_data['mc_gross'] * -1 ) ) {
// Mark order as refunded.
$order->update_status( 'refunded', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), strtolower( $posted_data['payment_status'] ) ) );
$this->send_ipn_email_notification(
sprintf( __( 'Payment for order %s refunded', 'woocommerce-gateway-paypal-express-checkout' ), '<a class="link" href="' . esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ) . '">' . $order->get_order_number() . '</a>' ),
sprintf( __( 'Order #%1$s has been marked as refunded - PayPal reason code: %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), $posted_data['reason_code'] )
);
}
}
/**
* Handle a reveral.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_reversed( $order, $posted_data ) {
$order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
$order->update_status( 'on-hold', sprintf( __( 'Payment %s via IPN.', 'woocommerce-gateway-paypal-express-checkout' ), wc_clean( $posted_data['payment_status'] ) ) );
$this->send_ipn_email_notification(
sprintf( __( 'Payment for order %s reversed', 'woocommerce-gateway-paypal-express-checkout' ), '<a class="link" href="' . esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ) . '">' . $order->get_order_number() . '</a>' ),
sprintf( __( 'Order #%1$s has been marked on-hold due to a reversal - PayPal reason code: %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), wc_clean( $posted_data['reason_code'] ) )
);
}
/**
* Handle a cancelled reveral.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function payment_status_canceled_reversal( $order, $posted_data ) {
$order_id = version_compare( WC_VERSION, '3.0', '<' ) ? $order->id : $order->get_id();
$this->send_ipn_email_notification(
sprintf( __( 'Reversal cancelled for order #%s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number() ),
sprintf( __( 'Order #%1$s has had a reversal cancelled. Please check the status of payment and update the order status accordingly here: %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $order->get_order_number(), esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ) )
);
}
/**
* Save important data from the IPN to the order.
*
* @param WC_Order $order Order object
* @param array $posted_data Posted data
*/
protected function save_paypal_meta_data( $order, $posted_data ) {
// A map of PayPal $POST keys to order meta keys
$mapped_keys = array(
'payer_email' => 'Payer PayPal address',
'first_name' => 'Payer first name',
'last_name' => 'Payer last name',
'payment_type' => 'Payment type',
'payment_status' => '_paypal_status'
);
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
foreach ( $mapped_keys as $post_key => $meta_key ) {
if ( ! empty( $posted_data[ $post_key ] ) ) {
$value = wc_clean( $posted_data[ $post_key ] );
if ( $old_wc ) {
update_post_meta( $order->id, $meta_key, $value );
} else {
$order->update_meta_data( $meta_key, $value );
}
}
}
if ( ! empty( $posted_data['txn_id'] ) ) {
update_post_meta( $old_wc ? $order->id : $order->get_id(), '_transaction_id', wc_clean( $posted_data['txn_id'] ) );
}
}
/**
* Send a notification to the user handling orders.
*
* @param string $subject Email subject
* @param string $message Email message
*/
protected function send_ipn_email_notification( $subject, $message ) {
$new_order_settings = get_option( 'woocommerce_new_order_settings', array() );
$mailer = WC()->mailer();
$message = $mailer->wrap_message( $subject, $message );
$mailer->send( ! empty( $new_order_settings['recipient'] ) ? $new_order_settings['recipient'] : get_option( 'admin_email' ), strip_tags( $subject ), $message );
}
/**
* Get IPN request validator URL.
*
* @return string PayPal IPN request validator URL
*/
public function get_validator_url() {
$url = 'https://www.paypal.com/cgi-bin/webscr';
if ( 'sandbox' === $this->gateway->environment ) {
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}
return $url;
}
}

View File

@@ -0,0 +1,212 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* PayPal Express Integrated PayPal Signup Handler.
*/
class WC_Gateway_PPEC_IPS_Handler {
const MIDDLEWARE_BASE_URL = 'https://connect.woocommerce.com';
/**
* Countries that support IPS.
*
* @var array
*/
// @codingStandardsIgnoreStart
private $_supported_countries = array(
'AL', 'DZ', 'AO', 'AI', 'AG', 'AR', 'AM', 'AW', 'AU', 'AT', 'AZ', 'BS',
'BH', 'BB', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BA', 'BW', 'VG', 'BN',
'BG', 'BF', 'BI', 'KH', 'CA', 'CV', 'KY', 'TD', 'CL', 'CN', 'C2', 'CO',
'KM', 'CG', 'CK', 'CR', 'HR', 'CY', 'CZ', 'CD', 'DK', 'DJ', 'DM', 'DO',
'EC', 'EG', 'SV', 'ER', 'EE', 'ET', 'FK', 'FM', 'FJ', 'FI', 'FR', 'GF',
'PF', 'GA', 'GM', 'GE', 'DE', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT',
'GN', 'GW', 'GY', 'VA', 'HN', 'HK', 'HU', 'IS', 'ID', 'IE', 'IT', 'JM',
'JO', 'KZ', 'KE', 'KI', 'KW', 'KG', 'LA', 'LV', 'LS', 'LI', 'LT', 'LU',
'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', 'MX',
'MN', 'MS', 'MA', 'MZ', 'NA', 'NR', 'NP', 'NL', 'AN', 'NC', 'NZ', 'NI',
'NE', 'NU', 'NF', 'NO', 'OM', 'PW', 'PA', 'PG', 'PE', 'PH', 'PN', 'PL',
'PT', 'QA', 'RE', 'RO', 'RU', 'RW', 'SH', 'KN', 'LC', 'PM', 'VC', 'WS',
'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SK', 'SI', 'SB', 'SO',
'ZA', 'KR', 'ES', 'LK', 'SR', 'SJ', 'SZ', 'SE', 'CH', 'TW', 'TJ', 'TH',
'TG', 'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB',
'TZ', 'US', 'UY', 'VU', 'VE', 'VN', 'WF', 'YE', 'ZM',
);
// @codingStandardsIgnoreEnd
/**
* Get merchant redirect URL for IPS.
*
* This is store URL that will be redirected from middleware.
*
* @param string $env Environment
*
* @return string Redirect URL
*/
public function get_redirect_url( $env ) {
if ( ! in_array( $env, array( 'live', 'sandbox' ) ) ) {
$env = 'live';
}
return add_query_arg(
array(
'env' => $env,
'wc_ppec_ips_admin_nonce' => wp_create_nonce( 'wc_ppec_ips' ),
),
wc_gateway_ppec()->get_admin_setting_link()
);
}
/**
* Get login URL to WC middleware.
*
* @param string $env Environment
*
* @return string Signup URL
*/
public function get_middleware_login_url( $env ) {
$service = 'ppe';
if ( 'sandbox' === $env ) {
$service = 'ppesandbox';
}
return self::MIDDLEWARE_BASE_URL . '/login/' . $service;
}
/**
* Get signup URL to WC middleware.
*
* @param string $env Environment
*
* @return string Signup URL
*/
public function get_signup_url( $env ) {
$query_args = array(
'redirect' => urlencode( $this->get_redirect_url( $env ) ),
'countryCode' => WC()->countries->get_base_country(),
'merchantId' => md5( site_url( '/' ) . time() ),
);
return add_query_arg( $query_args, $this->get_middleware_login_url( $env ) );
}
/**
* Check if base location country supports IPS.
*
* @return bool Returns true of base country in supported countries
*/
public function is_supported() {
return in_array( WC()->countries->get_base_country(), $this->_supported_countries );
}
/**
* Redirect with messages.
*
* @return void
*/
protected function _redirect_with_messages( $error_msg ) {
if ( ! is_array( $error_msg ) ) {
$error_msgs = array( array( 'error' => $error_msg ) );
} else {
$error_msgs = $error_msg;
}
add_option( 'woo_pp_admin_error', $error_msgs );
wp_safe_redirect( wc_gateway_ppec()->get_admin_setting_link() );
exit;
}
/**
* Maybe received credentials after successfully returned from IPS flow.
*
* @return mixed
*/
public function maybe_received_credentials() {
if ( ! is_admin() || ! is_user_logged_in() ) {
return false;
}
// Require the nonce.
if ( empty( $_GET['wc_ppec_ips_admin_nonce'] ) || empty( $_GET['env'] ) ) {
return false;
}
$env = in_array( $_GET['env'], array( 'live', 'sandbox' ) ) ? $_GET['env'] : 'live';
// Verify the nonce.
if ( ! wp_verify_nonce( $_GET['wc_ppec_ips_admin_nonce'], 'wc_ppec_ips' ) ) {
wp_die( __( 'Invalid connection request', 'woocommerce-gateway-paypal-express-checkout' ) );
}
wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow with parameters: %s', __METHOD__, print_r( $_GET, true ) ) );
// Check if error.
if ( ! empty( $_GET['error'] ) ) {
$error_message = ! empty( $_GET['error_message'] ) ? $_GET['error_message'] : '';
wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow with error: %s', __METHOD__, $error_message ) );
$this->_redirect_with_messages( __( 'Sorry, Easy Setup encountered an error. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
// Make sure credentials present in query string.
foreach ( array( 'api_style', 'api_username', 'api_password', 'signature' ) as $param ) {
if ( empty( $_GET[ $param ] ) ) {
wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow but missing parameter %s', __METHOD__, $param ) );
$this->_redirect_with_messages( __( 'Sorry, Easy Setup encountered an error. Please try again.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
}
$creds = new WC_Gateway_PPEC_Client_Credential_Signature(
$_GET['api_username'],
$_GET['api_password'],
$_GET['signature']
);
$error_msgs = array();
try {
$payer_id = wc_gateway_ppec()->client->test_api_credentials( $creds, $env );
if ( ! $payer_id ) {
$this->_redirect_with_messages( __( 'Easy Setup was able to obtain your API credentials, but was unable to verify that they work correctly. Please make sure your PayPal account is set up properly and try Easy Setup again.', 'woocommerce-gateway-paypal-express-checkout' ) );
}
} catch ( PayPal_API_Exception $ex ) {
$error_msgs[] = array(
'warning' => __( 'Easy Setup was able to obtain your API credentials, but an error occurred while trying to verify that they work correctly. Please try Easy Setup again.', 'woocommerce-gateway-paypal-express-checkout' ),
);
}
$error_msgs[] = array(
'success' => __( 'Success! Your PayPal account has been set up successfully.', 'woocommerce-gateway-paypal-express-checkout' ),
);
if ( ! empty( $error_msgs ) ) {
wc_gateway_ppec_log( sprintf( '%s: returned back from IPS flow: %s', __METHOD__, print_r( $error_msgs, true ) ) );
}
// Save credentials to settings API
$settings_array = (array) get_option( 'woocommerce_ppec_paypal_settings', array() );
if ( 'live' === $env ) {
$settings_array['environment'] = 'live';
$settings_array['api_username'] = $creds->get_username();
$settings_array['api_password'] = $creds->get_password();
$settings_array['api_signature'] = is_callable( array( $creds, 'get_signature' ) ) ? $creds->get_signature() : '';
$settings_array['api_certificate'] = is_callable( array( $creds, 'get_certificate' ) ) ? $creds->get_certificate() : '';
$settings_array['api_subject'] = $creds->get_subject();
} else {
$settings_array['environment'] = 'sandbox';
$settings_array['sandbox_api_username'] = $creds->get_username();
$settings_array['sandbox_api_password'] = $creds->get_password();
$settings_array['sandbox_api_signature'] = is_callable( array( $creds, 'get_signature' ) ) ? $creds->get_signature() : '';
$settings_array['sandbox_api_certificate'] = is_callable( array( $creds, 'get_certificate' ) ) ? $creds->get_certificate() : '';
$settings_array['sandbox_api_subject'] = $creds->get_subject();
}
update_option( 'woocommerce_ppec_paypal_settings', $settings_array );
$this->_redirect_with_messages( $error_msgs );
}
}

View File

@@ -0,0 +1,452 @@
<?php
/**
* TODO: Move each class into its own file and group them under one dir, payment-details.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class PayPal_Payment_Details {
public $token = false;
public $billing_agreement_id = false;
public $redirect_required = false;
public $redirect_requested = false;
public $note = false;
public $payments = false;
public $shipping_option_details = false;
public function loadFromDoECResponse( $doECResponse ) {
$map = array(
'TOKEN' => 'token',
'BILLINGAGREEMENTID' => 'billing_agreement_id',
'REDIRECTREQUIRED' => 'redirect_required',
'SUCCESSPAGEREDIRECTREQUESTED' => 'redirect_requested',
'NOTE' => 'note'
);
$max_payment_num = -1;
foreach ( $doECResponse as $index => $value ) {
if ( array_key_exists( $index, $map ) ) {
$key = $map[ $index ];
$this->$key = $value;
}
// Figure out the highest payment number
if ( preg_match( '/^PAYMENTINFO_(\d)_(TRANSACTIONID|EBAYITEMAUCTIONTXNID|PARENTTRANSACTIONID|RECEIPTID|TRANSACTIONTYPE|PAYMENTTYPE|EXPECTEDECHECKCLEARDATE|ORDERTIME|AMT|CURRENCYCODE|FEEAMT|SETTLEAMT|TAXAMT|EXCHANGERATE|PAYMENTSTATUS|PENDINGREASON|REASONCODE|HOLDDECISION|SHIPPINGMETHOD|PROTECTIONELIGIBILITY|PROTECTIONELIGIBILITYTYPE|RECEIPTREFERENCENUMBER|SHIPPINGAMT|HANDLINGAMT|PAYMENTREQUESTID|INSTRUMENTCATEGORY|INSTRUMENTID|OFFERCODE|OFFERTRACKINGID|SHORTMESSAGE|LONGMESSAGE|ERRORCODE|SEVERITYCODE|ACK|SELLERPAYPALACCOUNTID|SECUREMERCHANTACCOUNTID|SELLERID|SELLERUSERNAME|SELLERREGISTRATIONDATE)$/', $index, $matches ) ) {
if ( $matches[1] > $max_payment_num ) {
$max_payment_num = $matches[1];
}
}
}
if ( $max_payment_num >= 0 ) {
$this->payments = array();
for ( $i = 0; $i <= $max_payment_num; $i++ ) {
$this->payments[ $i ] = new PayPal_Payment_Payment_Details();
$this->payments[ $i ]->loadFromDoECResponse( $doECResponse, $i );
}
}
$this->shipping_option_details = new PayPal_Payment_Shipping_Option_Details();
if ( ! $this->shipping_option_details->loadFromDoECResponse( $doECResponse ) ) {
$this->shipping_option_details = false;
}
}
}
class PayPal_Payment_Payment_FMF_Details {
public $filters = false;
function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$max_filter_num = array(
'PENDING' => -1,
'REPORT' => -1,
'DENY' => -1,
'ACCEPT' => -1
);
$found_any = false;
foreach ( $doECResponse as $index => $value ) {
if ( preg_match( '/^L_PAYMENTINFO_' . $bucketNum . '_FMF(PENDING|REPORT|DENY|ACCEPT)(ID|NAME)(\d+)$/', $index, $matches ) ) {
$found_any = true;
if ( $matches[3] > $max_filter_num[ $matches[1] ] ) {
$max_filter_num[ $matches[1] ] = $matches[3];
}
}
}
// If we didn't find anything in the initial scan, bail out now.
if ( ! $found_any ) {
return false;
}
$this->filters = array();
foreach ( $max_filter_num as $index => $value ) {
for ( $i = 0; $i <= $value; $i++ ) {
$prefix = 'L_PAYMENTINFO_' . $bucketNum . '_FMF' . $index;
if ( array_key_exists( $prefix . 'NAME' . $i, $doECResponse ) && array_key_exists( $prefix . 'ID' . $i, $doECResponse ) ) {
$filters[] = new PayPal_Payment_Fraud_Management_Filter( $doECResponse[ $prefix . 'NAME' . $i ], $doECResponse[ $prefix . 'ID' . $i ], $index );
}
}
}
return true;
}
}
class PayPal_Payment_Fraud_Management_Filter {
public $name;
public $id;
public $status;
const FraudManagementFilterPending = 'PENDING';
const FraudManagementFilterReport = 'REPORT';
const FraudManagementFilterDeny = 'DENY';
const FraudManagementFilterAccept = 'ACCEPT';
public function __construct( $name, $id, $status ) {
$this->name = $name;
$this->id = $id;
$this->status = $status;
}
}
class PayPal_Payment_Shipping_Option_Details {
public $calculation_mode = false;
public $insurance_option_selected = false;
public $shipping_option_is_default = false;
public $shipping_option_amount = false;
public $shipping_option_name = false;
public function loadFromDoECResponse( $doECResponse ) {
$map = array(
'SHIPPINGCALCULATIONMODE' => 'calculation_mode',
'INSURANCEOPTIONSELECTED' => 'insurance_option_selected',
'SHIPPINGOPTIONISDEFAULT' => 'shipping_option_is_default',
'SHIPPINGOPTIONAMOUNT' => 'shipping_option_amount',
'SHIPPINGOPTIONNAME' => 'shipping_option_name'
);
$found_any = false;
foreach ( $map as $index => $value ) {
if ( array_key_exists( $index, $doECResponse ) ) {
$this->$value = $doECResponse[ $index ];
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Payment_Payment_Details {
public $transaction_id = false;
public $ebay_item_auction_transaction_id = false;
public $parent_transaction_id = false;
public $receipt_id = false;
public $transaction_type = false;
const TransactionTypeCart = 'cart';
const TransactionTypeExpressCheckout = 'express-checkout';
public $payment_type = false;
const PaymentTypeNone = 'none';
const PaymentTypeEcheck = 'echeck';
const PaymentTypeInstant = 'instant';
public $expected_echeck_clear_date = false;
public $order_time = false;
public $amount = false;
public $currency_code = false;
public $fee_amount = false;
public $settlement_amount = false;
public $tax_amount = false;
public $exchange_rate = false;
public $payment_status = false;
const PaymentStatusNone = 'None';
const PaymentStatusCanceledReversal = 'Canceled-Reversal';
const PaymentStatusCompleted = 'Completed';
const PaymentStatusDenied = 'Denied';
const PaymentStatusExpired = 'Expired';
const PaymentStatusFailed = 'Failed';
const PaymentStatusInProgress = 'In-Progress';
const PaymentStatusPartiallyRefunded = 'Partially-Refunded';
const PaymentStatusPending = 'Pending';
const PaymentStatusRefunded = 'Refunded';
const PaymentStatusReversed = 'Reversed';
const PaymentStatusProcessed = 'Processed';
const PaymentStatusVoided = 'Voided';
const PaymentStatusCompletedFundsHeld = 'Completed-Funds-Held';
public $pending_reason = false;
const PendingReasonNone = 'none';
const PendingReasonAddress = 'address';
const PendingReasonAuthorization = 'authorization';
const PendingReasonEcheck = 'echeck';
const PendingReasonInternational = 'intl';
const PendingReasonMultiCurrency = 'multi-currency';
const PendingReasonOrder = 'order';
const PendingReasonPaymentReview = 'payment-review';
const PendingReasonRegulatoryReview = 'regulatory-review';
const PendingReasonUnilateral = 'unilateral';
const PendingReasonVerify = 'verify';
const PendingReasonOther = 'other';
public $reason_code = false;
const ReasonCodeNone = 'none';
const ReasonCodeChargeback = 'chargeback';
const ReasonCodeGuarantee = 'guarantee';
const ReasonCodeBuyerComplaint = 'buyer-complaint';
const ReasonCodeRefund = 'refund';
const ReasonCodeOther = 'other';
public $hold_decision = false;
const HoldDecisionNewSellerPaymentHold = 'newsellerpaymenthold';
const HoldDecisionPaymentHold = 'paymenthold';
public $shipping_method = false;
public $protection_eligibility_details = false;
public $receipt_reference_number = false;
public $shipping_amount = false;
public $handling_amount = false;
public $payment_request_id = false;
public $instrument_details = false;
public $offer_details = false;
public $error_details = false;
public $seller_details = false;
public $fmf_details = false;
public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$map = array(
'TRANSACTIONID' => 'transaction_id',
'EBAYITEMAUCTIONTXNID' => 'ebay_item_auction_transaction_id',
'PARENTTRANSACTIONID' => 'parent_transaction_id',
'RECEIPTID' => 'receipt_id',
'TRANSACTIONTYPE' => 'transaction_type',
'PAYMENTTYPE' => 'payment_type',
'EXPECTEDECHECKCLEARDATE' => 'expected_echeck_clear_date',
'ORDERTIME' => 'order_time',
'AMT' => 'amount',
'CURRENCYCODE' => 'currency_code',
'FEEAMT' => 'fee_amount',
'SETTLEAMT' => 'settlement_amount',
'TAXAMT' => 'tax_amount',
'EXCHANGERATE' => 'exchange_rate',
'PAYMENTSTATUS' => 'payment_status',
'PENDINGREASON' => 'pending_reason',
'REASONCODE' => 'reason_code',
'HOLDDECISION' => 'hold_decision',
'SHIPPINGMETHOD' => 'shipping_method',
'RECEIPTREFERENCENUMBER' => 'receipt_reference_number',
'SHIPPINGAMT' => 'shipping_amount',
'HANDLINGAMT' => 'handling_amount',
'PAYMENTREQUESTID' => 'payment_request_id'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
if ( array_key_exists( $var_name, $doECResponse ) ) {
$this->$value = $doECResponse[ $var_name ];
$found_any = true;
}
}
$this->protection_eligibility_details = new PayPal_Payment_Payment_Protection_Eligibility_Details();
if ( ! $this->protection_eligibility_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
$this->protection_eligibility_details = false;
}
$this->instrument_details = new PayPal_Payment_Payment_Instrument_Details();
if ( ! $this->instrument_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
$this->instrument_details = false;
}
$this->offer_details = new PayPal_Payment_Payment_Offer_Details();
if ( ! $this->offer_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
$this->offer_details = false;
}
$this->error_details = new PayPal_Payment_Payment_Error_Details();
if ( ! $this->error_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
$this->error_details = false;
}
$this->seller_details = new PayPal_Payment_Payment_Seller_Details();
if ( ! $this->seller_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
$this->seller_details = false;
}
$this->fmf_details = new PayPal_Payment_Payment_FMF_Details();
if ( ! $this->fmf_details->loadFromDoECResponse( $doECResponse, $bucketNum ) ) {
$this->fmf_details = false;
}
}
}
class PayPal_Payment_Payment_Protection_Eligibility_Details {
public $protection_eligibility = false;
const ProtectionEligibilityEligible = 'Eligible';
const ProtectionEligibilityPartiallyEligible = 'PartiallyEligible';
const ProtectionEligibilityIneligible = 'Ineligible';
public $protection_eligibility_type = false;
const ProtectionEligibilityTypeItemNotReceivedEligible = 'ItemNotReceivedEligible';
const ProtectionEligibilityTypeUnauthorizedPaymentEligible = 'UnauthorizedPaymentEligible';
const ProtectionEligibilityTypeIneligible = 'Ineligible';
public function isItemNotReceivedEligible() {
$types = explode( ',', $this->protection_eligibility_type );
foreach ( $types as $value ) {
if ( self::ProtectionEligibilityTypeItemNotReceivedEligible == $value ) {
return true;
}
}
return false;
}
public function isUnauthorizedPaymentEligible() {
$types = explode( ',', $this->protection_eligibility_type );
foreach ( $types as $value ) {
if ( self::ProtectionEligibilityTypeUnauthorizedPaymentEligible == $value ) {
return true;
}
}
return false;
}
public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$map = array(
'PROTECTIONELIGIBILITY' => 'protection_eligibility',
'PROTECTIONELIGIBILITYTYPE' => 'protection_eligibility_type'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
if ( array_key_exists( $var_name, $doECResponse ) ) {
$this->$value = $doECResponse[ $var_name ];
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Payment_Payment_Instrument_Details {
public $instrument_category = false;
const InstrumentCategoryPayPalCredit = '1';
const InstrumentCategoryPrivateCard = '2';
public $instrument_id = false;
// Returns true to indicate that the getECResponse array contained variables that were pertinent to this object.
// If not, it returns false to indicate that the caller can destroy this object.
public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$map = array(
'INSTRUMENTCATEGORY' => 'instrument_category',
'INSTRUMENTID' => 'instrument_id'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
if ( array_key_exists( $var_name, $doECResponse ) ) {
$this->$value = $doECResponse[ $var_name ];
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Payment_Payment_Offer_Details {
public $offer_code = false;
public $offer_tracking_id = false;
public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$map = array(
'OFFERCODE' => 'offer_code',
'OFFERTRACKINGID' => 'offer_tracking_id'
);
}
}
class PayPal_Payment_Payment_Error_Details {
public $short_message = false;
public $long_message = false;
public $error_code = false;
public $severity_code = false;
public $ack = false;
public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$map = array(
'SHORTMESSAGE' => 'short_message',
'LONGMESSAGE' => 'long_message',
'ERRORCODE' => 'error_code',
'SEVERITYCODE' => 'severity_code',
'ACK' => 'ack'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
if ( array_key_exists( $var_name, $doECResponse ) ) {
$this->$value = $doECResponse[ $var_name ];
$found_any = true;
}
}
return $found_any;
}
}
class PayPal_Payment_Payment_Seller_Details {
public $paypal_account_id = false;
public $secure_merchant_account_id = false;
public $seller_id = false;
public $user_name = false;
public $registration_date = false;
public function loadFromDoECResponse( $doECResponse, $bucketNum ) {
$map = array(
'SELLERPAYPALACCOUNTID' => 'paypal_account_id',
'SECUREMERCHANTACCOUNTID' => 'secure_merchant_account_id',
'SELLERID' => 'seller_id',
'SELLERUSERNAME' => 'user_name',
'SELLERREGISTRATIONDATE' => 'registration_date'
);
$found_any = false;
foreach ( $map as $index => $value ) {
$var_name = 'PAYMENTINFO_' . $bucketNum . '_' . $index;
if ( array_key_exists( $var_name, $doECResponse ) ) {
$this->$value = $doECResponse[ $var_name ];
$found_any = true;
}
}
return $found_any;
}
}

View File

@@ -0,0 +1,446 @@
<?php
/**
* PayPal Express Checkout Plugin.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_Plugin {
const ALREADY_BOOTSTRAPED = 1;
const DEPENDENCIES_UNSATISFIED = 2;
const NOT_CONNECTED = 3;
/**
* Filepath of main plugin file.
*
* @var string
*/
public $file;
/**
* Plugin version.
*
* @var string
*/
public $version;
/**
* Absolute plugin path.
*
* @var string
*/
public $plugin_path;
/**
* Absolute plugin URL.
*
* @var string
*/
public $plugin_url;
/**
* Absolute path to plugin includes dir.
*
* @var string
*/
public $includes_path;
/**
* Flag to indicate the plugin has been boostrapped.
*
* @var bool
*/
private $_bootstrapped = false;
/**
* Instance of WC_Gateway_PPEC_Settings.
*
* @var WC_Gateway_PPEC_Settings
*/
public $settings;
/**
* Constructor.
*
* @param string $file Filepath of main plugin file
* @param string $version Plugin version
*/
public function __construct( $file, $version ) {
$this->file = $file;
$this->version = $version;
// Path.
$this->plugin_path = trailingslashit( plugin_dir_path( $this->file ) );
$this->plugin_url = trailingslashit( plugin_dir_url( $this->file ) );
$this->includes_path = $this->plugin_path . trailingslashit( 'includes' );
// Updates
if ( version_compare( $version, get_option( 'wc_ppec_version' ), '>' ) ) {
$this->run_updater( $version );
}
}
/**
* Handle updates.
* @param [type] $new_version [description]
* @return [type] [description]
*/
private function run_updater( $new_version ) {
// Map old settings to settings API
if ( get_option( 'pp_woo_enabled' ) ) {
$settings_array = (array) get_option( 'woocommerce_ppec_paypal_settings', array() );
$settings_array['enabled'] = get_option( 'pp_woo_enabled' ) ? 'yes' : 'no';
$settings_array['logo_image_url'] = get_option( 'pp_woo_logoImageUrl' );
$settings_array['paymentAction'] = strtolower( get_option( 'pp_woo_paymentAction', 'sale' ) );
$settings_array['subtotal_mismatch_behavior'] = 'addLineItem' === get_option( 'pp_woo_subtotalMismatchBehavior' ) ? 'add' : 'drop';
$settings_array['environment'] = get_option( 'pp_woo_environment' );
$settings_array['button_size'] = get_option( 'pp_woo_button_size' );
$settings_array['instant_payments'] = get_option( 'pp_woo_blockEChecks' );
$settings_array['require_billing'] = get_option( 'pp_woo_requireBillingAddress' );
$settings_array['debug'] = get_option( 'pp_woo_logging_enabled' ) ? 'yes' : 'no';
// Make sure button size is correct.
if ( ! in_array( $settings_array['button_size'], array( 'small', 'medium', 'large' ) ) ) {
$settings_array['button_size'] = 'medium';
}
// Load client classes before `is_a` check on credentials instance.
$this->_load_client();
$live = get_option( 'pp_woo_liveApiCredentials' );
$sandbox = get_option( 'pp_woo_sandboxApiCredentials' );
if ( $live && is_a( $live, 'WC_Gateway_PPEC_Client_Credential' ) ) {
$settings_array['api_username'] = $live->get_username();
$settings_array['api_password'] = $live->get_password();
$settings_array['api_signature'] = is_callable( array( $live, 'get_signature' ) ) ? $live->get_signature() : '';
$settings_array['api_certificate'] = is_callable( array( $live, 'get_certificate' ) ) ? $live->get_certificate() : '';
$settings_array['api_subject'] = $live->get_subject();
}
if ( $sandbox && is_a( $sandbox, 'WC_Gateway_PPEC_Client_Credential' ) ) {
$settings_array['sandbox_api_username'] = $sandbox->get_username();
$settings_array['sandbox_api_password'] = $sandbox->get_password();
$settings_array['sandbox_api_signature'] = is_callable( array( $sandbox, 'get_signature' ) ) ? $sandbox->get_signature() : '';
$settings_array['sandbox_api_certificate'] = is_callable( array( $sandbox, 'get_certificate' ) ) ? $sandbox->get_certificate() : '';
$settings_array['sandbox_api_subject'] = $sandbox->get_subject();
}
update_option( 'woocommerce_ppec_paypal_settings', $settings_array );
delete_option( 'pp_woo_enabled' );
}
update_option( 'wc_ppec_version', $new_version );
}
/**
* Maybe run the plugin.
*/
public function maybe_run() {
register_activation_hook( $this->file, array( $this, 'activate' ) );
add_action( 'plugins_loaded', array( $this, 'bootstrap' ) );
add_filter( 'allowed_redirect_hosts' , array( $this, 'whitelist_paypal_domains_for_redirect' ) );
add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
add_filter( 'plugin_action_links_' . plugin_basename( $this->file ), array( $this, 'plugin_action_links' ) );
add_action( 'wp_ajax_ppec_dismiss_notice_message', array( $this, 'ajax_dismiss_notice' ) );
}
public function bootstrap() {
try {
if ( $this->_bootstrapped ) {
throw new Exception( __( '%s in WooCommerce Gateway PayPal Express Checkout plugin can only be called once', 'woocommerce-gateway-paypal-express-checkout' ), self::ALREADY_BOOTSTRAPED );
}
$this->_check_dependencies();
$this->_run();
$this->_check_credentials();
$this->_bootstrapped = true;
delete_option( 'wc_gateway_ppce_bootstrap_warning_message' );
delete_option( 'wc_gateway_ppce_prompt_to_connect' );
} catch ( Exception $e ) {
if ( in_array( $e->getCode(), array( self::ALREADY_BOOTSTRAPED, self::DEPENDENCIES_UNSATISFIED ) ) ) {
update_option( 'wc_gateway_ppce_bootstrap_warning_message', $e->getMessage() );
}
if ( self::NOT_CONNECTED === $e->getCode() ) {
update_option( 'wc_gateway_ppce_prompt_to_connect', $e->getMessage() );
}
add_action( 'admin_notices', array( $this, 'show_bootstrap_warning' ) );
}
}
public function show_bootstrap_warning() {
$dependencies_message = get_option( 'wc_gateway_ppce_bootstrap_warning_message', '' );
if ( ! empty( $dependencies_message ) && 'yes' !== get_option( 'wc_gateway_ppec_bootstrap_warning_message_dismissed', 'no' ) ) {
?>
<div class="notice notice-warning is-dismissible ppec-dismiss-bootstrap-warning-message">
<p>
<strong><?php echo esc_html( $dependencies_message ); ?></strong>
</p>
</div>
<script>
( function( $ ) {
$( '.ppec-dismiss-bootstrap-warning-message' ).on( 'click', '.notice-dismiss', function() {
jQuery.post( "<?php echo admin_url( 'admin-ajax.php' ); ?>", {
action: "ppec_dismiss_notice_message",
dismiss_action: "ppec_dismiss_bootstrap_warning_message",
nonce: "<?php echo esc_js( wp_create_nonce( 'ppec_dismiss_notice' ) ); ?>"
} );
} );
} )( jQuery );
</script>
<?php
}
$prompt_connect = get_option( 'wc_gateway_ppce_prompt_to_connect', '' );
if ( ! empty( $prompt_connect ) && 'yes' !== get_option( 'wc_gateway_ppec_prompt_to_connect_message_dismissed', 'no' ) ) {
?>
<div class="notice notice-warning is-dismissible ppec-dismiss-prompt-to-connect-message">
<p>
<strong><?php echo wp_kses( $prompt_connect, array( 'a' => array( 'href' => array() ) ) ); ?></strong>
</p>
</div>
<script>
( function( $ ) {
$( '.ppec-dismiss-prompt-to-connect-message' ).on( 'click', '.notice-dismiss', function() {
jQuery.post( "<?php echo admin_url( 'admin-ajax.php' ); ?>", {
action: "ppec_dismiss_notice_message",
dismiss_action: "ppec_dismiss_prompt_to_connect",
nonce: "<?php echo esc_js( wp_create_nonce( 'ppec_dismiss_notice' ) ); ?>"
} );
} );
} )( jQuery );
</script>
<?php
}
}
/**
* AJAX handler for dismiss notice action.
*
* @since 1.4.7
* @version 1.4.7
*/
public function ajax_dismiss_notice() {
if ( empty( $_POST['dismiss_action'] ) ) {
return;
}
check_ajax_referer( 'ppec_dismiss_notice', 'nonce' );
switch ( $_POST['dismiss_action'] ) {
case 'ppec_dismiss_bootstrap_warning_message':
update_option( 'wc_gateway_ppec_bootstrap_warning_message_dismissed', 'yes' );
break;
case 'ppec_dismiss_prompt_to_connect':
update_option( 'wc_gateway_ppec_prompt_to_connect_message_dismissed', 'yes' );
break;
}
wp_die();
}
/**
* Check dependencies.
*
* @throws Exception
*/
protected function _check_dependencies() {
if ( ! function_exists( 'WC' ) ) {
throw new Exception( __( 'WooCommerce Gateway PayPal Express Checkout requires WooCommerce to be activated', 'woocommerce-gateway-paypal-express-checkout' ), self::DEPENDENCIES_UNSATISFIED );
}
if ( version_compare( WC()->version, '2.5', '<' ) ) {
throw new Exception( __( 'WooCommerce Gateway PayPal Express Checkout requires WooCommerce version 2.5 or greater', 'woocommerce-gateway-paypal-express-checkout' ), self::DEPENDENCIES_UNSATISFIED );
}
if ( ! function_exists( 'curl_init' ) ) {
throw new Exception( __( 'WooCommerce Gateway PayPal Express Checkout requires cURL to be installed on your server', 'woocommerce-gateway-paypal-express-checkout' ), self::DEPENDENCIES_UNSATISFIED );
}
$openssl_warning = __( 'WooCommerce Gateway PayPal Express Checkout requires OpenSSL >= 1.0.1 to be installed on your server', 'woocommerce-gateway-paypal-express-checkout' );
if ( ! defined( 'OPENSSL_VERSION_TEXT' ) ) {
throw new Exception( $openssl_warning, self::DEPENDENCIES_UNSATISFIED );
}
preg_match( '/^(?:Libre|Open)SSL ([\d.]+)/', OPENSSL_VERSION_TEXT, $matches );
if ( empty( $matches[1] ) ) {
throw new Exception( $openssl_warning, self::DEPENDENCIES_UNSATISFIED );
}
if ( ! version_compare( $matches[1], '1.0.1', '>=' ) ) {
throw new Exception( $openssl_warning, self::DEPENDENCIES_UNSATISFIED );
}
}
/**
* Check credentials. If it's not client credential it means it's not set
* and will prompt admin to connect.
*
* @see https://github.com/woothemes/woocommerce-gateway-paypal-express-checkout/issues/112
*
* @throws Exception
*/
protected function _check_credentials() {
$credential = $this->settings->get_active_api_credentials();
if ( ! is_a( $credential, 'WC_Gateway_PPEC_Client_Credential' ) || '' === $credential->get_username() ) {
$setting_link = $this->get_admin_setting_link();
throw new Exception( sprintf( __( 'PayPal Express Checkout is almost ready. To get started, <a href="%s">connect your PayPal account</a>.', 'woocommerce-gateway-paypal-express-checkout' ), esc_url( $setting_link ) ), self::NOT_CONNECTED );
}
}
/**
* Run the plugin.
*/
protected function _run() {
require_once( $this->includes_path . 'functions.php' );
$this->_load_handlers();
}
/**
* Callback for activation hook.
*/
public function activate() {
if ( ! isset( $this->settings ) ) {
require_once( $this->includes_path . 'class-wc-gateway-ppec-settings.php' );
$settings = new WC_Gateway_PPEC_Settings();
} else {
$settings = $this->settings;
}
// Force zero decimal on specific currencies.
if ( $settings->currency_has_decimal_restriction() ) {
update_option( 'woocommerce_price_num_decimals', 0 );
update_option( 'wc_gateway_ppce_display_decimal_msg', true );
}
}
/**
* Load handlers.
*/
protected function _load_handlers() {
// Client.
$this->_load_client();
// Load handlers.
require_once( $this->includes_path . 'class-wc-gateway-ppec-privacy.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-settings.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-gateway-loader.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-admin-handler.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-checkout-handler.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-cart-handler.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-ips-handler.php' );
require_once( $this->includes_path . 'abstracts/abstract-wc-gateway-ppec-paypal-request-handler.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-ipn-handler.php' );
$this->settings = new WC_Gateway_PPEC_Settings();
$this->gateway_loader = new WC_Gateway_PPEC_Gateway_Loader();
$this->admin = new WC_Gateway_PPEC_Admin_Handler();
$this->checkout = new WC_Gateway_PPEC_Checkout_Handler();
$this->cart = new WC_Gateway_PPEC_Cart_Handler();
$this->ips = new WC_Gateway_PPEC_IPS_Handler();
$this->client = new WC_Gateway_PPEC_Client( $this->settings->get_active_api_credentials(), $this->settings->environment );
}
/**
* Load client.
*
* @since 1.1.0
*/
protected function _load_client() {
require_once( $this->includes_path . 'abstracts/abstract-wc-gateway-ppec-client-credential.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-client-credential-certificate.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-client-credential-signature.php' );
require_once( $this->includes_path . 'class-wc-gateway-ppec-client.php' );
}
/**
* Link to settings screen.
*/
public function get_admin_setting_link() {
if ( version_compare( WC()->version, '2.6', '>=' ) ) {
$section_slug = 'ppec_paypal';
} else {
$section_slug = strtolower( 'WC_Gateway_PPEC_With_PayPal' );
}
return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
}
/**
* Allow PayPal domains for redirect.
*
* @since 1.0.0
*
* @param array $domains Whitelisted domains for `wp_safe_redirect`
*
* @return array $domains Whitelisted domains for `wp_safe_redirect`
*/
public function whitelist_paypal_domains_for_redirect( $domains ) {
$domains[] = 'www.paypal.com';
$domains[] = 'paypal.com';
$domains[] = 'www.sandbox.paypal.com';
$domains[] = 'sandbox.paypal.com';
return $domains;
}
/**
* Load localisation files.
*
* @since 1.1.2
*/
public function load_plugin_textdomain() {
load_plugin_textdomain( 'woocommerce-gateway-paypal-express-checkout', false, plugin_basename( $this->plugin_path ) . '/languages' );
}
/**
* Add relevant links to plugins page.
*
* @since 1.2.0
*
* @param array $links Plugin action links
*
* @return array Plugin action links
*/
public function plugin_action_links( $links ) {
$plugin_links = array();
if ( function_exists( 'WC' ) ) {
$setting_url = $this->get_admin_setting_link();
$plugin_links[] = '<a href="' . esc_url( $setting_url ) . '">' . esc_html__( 'Settings', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
}
$plugin_links[] = '<a href="https://docs.woocommerce.com/document/paypal-express-checkout/">' . esc_html__( 'Docs', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
return array_merge( $plugin_links, $links );
}
/**
* Check if shipping is needed for PayPal. This only checks for virtual products (#286),
* but skips the check if there are no shipping methods enabled (#249).
*
* @since 1.4.1
* @version 1.4.1
*
* @return bool
*/
public static function needs_shipping() {
$cart_contents = WC()->cart->cart_contents;
$needs_shipping = false;
if ( ! empty( $cart_contents ) ) {
foreach ( $cart_contents as $cart_item_key => $values ) {
if ( $values['data']->needs_shipping() ) {
$needs_shipping = true;
break;
}
}
}
return apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
}
}

View File

@@ -0,0 +1,272 @@
<?php
if ( ! class_exists( 'WC_Abstract_Privacy' ) ) {
return;
}
class WC_Gateway_PPEC_Privacy extends WC_Abstract_Privacy {
/**
* Constructor
*
*/
public function __construct() {
parent::__construct( __( 'PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ) );
$this->add_exporter( 'woocommerce-gateway-paypal-express-checkout-order-data', __( 'WooCommerce PPEC Order Data', 'woocommerce-gateway-paypal-express-checkout' ), array( $this, 'order_data_exporter' ) );
if ( class_exists( 'WC_Subscriptions' ) ) {
$this->add_exporter( 'woocommerce-gateway-paypal-express-checkout-subscriptions-data', __( 'WooCommerce PPEC Subscriptions Data', 'woocommerce-gateway-paypal-express-checkout' ), array( $this, 'subscriptions_data_exporter' ) );
}
$this->add_eraser( 'woocommerce-gateway-paypal-express-checkout-order-data', __( 'WooCommerce PPEC Data', 'woocommerce-gateway-paypal-express-checkout' ), array( $this, 'order_data_eraser' ) );
}
/**
* Returns a list of orders that are using one of PPEC's payment methods.
*
* @param string $email_address
* @param int $page
*
* @return array WP_Post
*/
protected function get_ppec_orders( $email_address, $page ) {
$user = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data.
$order_query = array(
'payment_method' => array( 'ppec_paypal' ),
'limit' => 10,
'page' => $page,
);
if ( $user instanceof WP_User ) {
$order_query['customer_id'] = (int) $user->ID;
} else {
$order_query['billing_email'] = $email_address;
}
return wc_get_orders( $order_query );
}
/**
* Gets the message of the privacy to display.
*
*/
public function get_privacy_message() {
return wpautop( sprintf( __( 'By using this extension, you may be storing personal data or sharing data with an external service. <a href="%s" target="_blank">Learn more about how this works, including what you may want to include in your privacy policy.</a>', 'woocommerce-gateway-paypal-express-checkout' ), 'https://docs.woocommerce.com/document/privacy-payments/#woocommerce-gateway-paypal-express-checkout' ) );
}
/**
* Handle exporting data for Orders.
*
* @param string $email_address E-mail address to export.
* @param int $page Pagination of data.
*
* @return array
*/
public function order_data_exporter( $email_address, $page = 1 ) {
$done = false;
$data_to_export = array();
$orders = $this->get_ppec_orders( $email_address, (int) $page );
$done = true;
if ( 0 < count( $orders ) ) {
foreach ( $orders as $order ) {
$data_to_export[] = array(
'group_id' => 'woocommerce_orders',
'group_label' => __( 'Orders', 'woocommerce-gateway-paypal-express-checkout' ),
'item_id' => 'order-' . $order->get_id(),
'data' => array(
array(
'name' => __( 'PPEC Refundable transaction data', 'woocommerce-gateway-paypal-express-checkout' ),
'value' => json_encode( get_post_meta( $order->get_id(), '_woo_pp_txnData', true ) ),
),
array(
'name' => __( 'PPEC Billing agreement id', 'woocommerce-gateway-paypal-express-checkout' ),
'value' => get_post_meta( $order->get_id(), '_ppec_billing_agreement_id', true ),
),
),
);
}
$done = 10 > count( $orders );
}
return array(
'data' => $data_to_export,
'done' => $done,
);
}
/**
* Handle exporting data for Subscriptions.
*
* @param string $email_address E-mail address to export.
* @param int $page Pagination of data.
*
* @return array
*/
public function subscriptions_data_exporter( $email_address, $page = 1 ) {
$done = false;
$page = (int) $page;
$data_to_export = array();
$meta_query = array(
'relation' => 'AND',
array(
'key' => '_payment_method',
'value' => array( 'ppec_paypal' ),
'compare' => 'IN',
),
array(
'key' => '_billing_email',
'value' => $email_address,
'compare' => '=',
),
);
$subscription_query = array(
'posts_per_page' => 10,
'page' => $page,
'meta_query' => $meta_query,
);
$subscriptions = wcs_get_subscriptions( $subscription_query );
$done = true;
if ( 0 < count( $subscriptions ) ) {
foreach ( $subscriptions as $subscription ) {
$data_to_export[] = array(
'group_id' => 'woocommerce_subscriptions',
'group_label' => __( 'Subscriptions', 'woocommerce-gateway-paypal-express-checkout' ),
'item_id' => 'subscription-' . $subscription->get_id(),
'data' => array(
array(
'name' => __( 'PPEC Refundable transaction data', 'woocommerce-gateway-paypal-express-checkout' ),
'value' => json_encode( get_post_meta( $order->get_id(), '_woo_pp_txnData', true ) ),
),
array(
'name' => __( 'PPEC Billing agreement id', 'woocommerce-gateway-paypal-express-checkout' ),
'value' => get_post_meta( $order->get_id(), '_ppec_billing_agreement_id', true ),
),
),
);
}
$done = 10 > count( $subscriptions );
}
return array(
'data' => $data_to_export,
'done' => $done,
);
}
/**
* Finds and erases order data by email address.
*
* @since 3.4.0
* @param string $email_address The user email address.
* @param int $page Page.
* @return array An array of personal data in name value pairs
*/
public function order_data_eraser( $email_address, $page ) {
$orders = $this->get_ppec_orders( $email_address, (int) $page );
$items_removed = false;
$items_retained = false;
$messages = array();
foreach ( (array) $orders as $order ) {
$order = wc_get_order( $order->get_id() );
list( $removed, $retained, $msgs ) = $this->maybe_handle_order( $order );
$items_removed |= $removed;
$items_retained |= $retained;
$messages = array_merge( $messages, $msgs );
list( $removed, $retained, $msgs ) = $this->maybe_handle_subscription( $order );
$items_removed |= $removed;
$items_retained |= $retained;
$messages = array_merge( $messages, $msgs );
}
// Tell core if we have more orders to work on still
$done = count( $orders ) < 10;
return array(
'items_removed' => $items_removed,
'items_retained' => $items_retained,
'messages' => $messages,
'done' => $done,
);
}
/**
* Handle eraser of data tied to Subscriptions
*
* @param WC_Order $order
* @return array
*/
protected function maybe_handle_subscription( $order ) {
if ( ! class_exists( 'WC_Subscriptions' ) ) {
return array( false, false, array() );
}
if ( ! wcs_order_contains_subscription( $order ) ) {
return array( false, false, array() );
}
$subscription = current( wcs_get_subscriptions_for_order( $order->get_id() ) );
$subscription_id = $subscription->get_id();
$ppec_billing = get_post_meta( $subscription_id, '_ppec_billing_agreement_id', true );
if ( empty( $ppec_billing ) ) {
return array( false, false, array() );
}
if ( $subscription->has_status( apply_filters( 'woocommerce_paypal_express_checkout_privacy_eraser_subs_statuses', array( 'on-hold', 'active' ) ) ) ) {
return array( false, true, array( sprintf( __( 'Order ID %d contains an active Subscription' ), $order->get_id() ) ) );
}
$renewal_orders = WC_Subscriptions_Renewal_Order::get_renewal_orders( $order->get_id() );
foreach ( $renewal_orders as $renewal_order_id ) {
delete_post_meta( $renewal_order_id, '_woo_pp_txnData' );
delete_post_meta( $renewal_order_id, '_ppec_billing_agreement_id' );
delete_post_meta( $renewal_order_id, '_paypal_status' );
}
delete_post_meta( $subscription_id, '_woo_pp_txnData' );
delete_post_meta( $subscription_id, '_ppec_billing_agreement_id' );
delete_post_meta( $subscription_id, '_paypal_status' );
return array( true, false, array( __( 'PayPal Express Checkout Subscriptions Data Erased.', 'woocommerce-gateway-paypal-express-checkout' ) ) );
}
/**
* Handle eraser of data tied to Orders
*
* @param WC_Order $order
* @return array
*/
protected function maybe_handle_order( $order ) {
$order_id = $order->get_id();
$ppec_txn_data = get_post_meta( $order_id, '_woo_pp_txnData', true );
$ppec_billing = get_post_meta( $order_id, '_ppec_billing_agreement_id', true );
$ppec_status = get_post_meta( $order_id, '_paypal_status', true );
if ( empty( $ppec_txn_data ) && empty( $ppec_billing ) && empty( $ppec_status ) ) {
return array( false, false, array() );
}
delete_post_meta( $order_id, '_woo_pp_txnData' );
delete_post_meta( $order_id, '_ppec_billing_agreement_id' );
delete_post_meta( $order_id, '_paypal_status' );
return array( true, false, array( __( 'PayPal Express Checkout Order Data Erased.', 'woocommerce-gateway-paypal-express-checkout' ) ) );
}
}
new WC_Gateway_PPEC_Privacy();

View File

@@ -0,0 +1,46 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_Refund {
/**
* Refund an order.
*
* @throws \PayPal_API_Exception
*
* @param WC_Order $order Order to refund
* @param float $amount Amount to refund
* @param string $refundType Type of refund (Partial or Full)
* @param string $reason Reason to refund
* @param string $current Currency of refund
*
* @return null|string If exception is thrown, null is returned. Otherwise
* ID of refund transaction is returned.
*/
public static function refund_order( $order, $amount, $refundType, $reason, $currency ) {
// add refund params
$params['TRANSACTIONID'] = $order->get_transaction_id();
$params['REFUNDTYPE'] = $refundType;
$params['AMT'] = $amount;
$params['CURRENCYCODE'] = $currency;
$params['NOTE'] = $reason;
// do API call
$response = wc_gateway_ppec()->client->refund_transaction( $params );
// look at ACK to see if success or failure
// if success return the transaction ID of the refund
// if failure then do 'throw new PayPal_API_Exception( $response );'
if ( 'Success' == $response['ACK'] || 'SuccessWithWarning' == $response['ACK'] ) {
return $response['REFUNDTRANSACTIONID'];
} else {
throw new PayPal_API_Exception( $response );
}
}
}

View File

@@ -0,0 +1,98 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* PayPal Express Checkout session wrapper.
*/
class WC_Gateway_PPEC_Session_Data {
/**
* Source where the session is set from. Valid value is either 'cart' or
* 'order'.
*
* If source is 'cart' then buyer starts it from cart page, otherwise it
* starts from checkout page or flow that has order created (e.g. from cart
* then the process_payment encountered error).
*
* @var string
*/
public $source;
/**
* WooCommerce Order ID.
*
* If self::$source is 'order', this must be set to order ID.
*
* @var int
*/
public $order_id;
/**
* Whether the buyer has returned from PayPal.
*
* If checkout_completed is true PPEC should be selected as the payment
* method.
*
* @var bool
*/
public $checkout_completed = false;
/**
* Express checkout token.
*
* @var string
*/
public $token;
/**
* The buyer's payer ID.
*
* Retrieved after buyer comes back from PayPal in-context dialog.
*
* @var string
*/
public $payer_id;
/**
* How long the token will expires (in seconds).
*
* @var int
*/
public $expiry_time;
/**
* Whether the buyer is checking out with PayPal Credit.
*
* @since 1.2.0
*
* @var bool
*/
public $use_paypal_credit;
/**
* Constructor.
*
* @param array $args Arguments for session data
*/
public function __construct( $args = array() ) {
$args = wp_parse_args( $args, array(
'token' => '',
'source' => 'cart',
'order_id' => false,
'expires_in' => 10800,
'use_paypal_credit' => false,
) );
$this->token = $args['token'];
$this->source = $args['source'];
$this->expiry_time = time() + $args['expires_in'];
$this->use_paypal_credit = $args['use_paypal_credit'];
if ( 'order' === $this->source ) {
$this->order_id = $args['order_id'];
}
}
}

View File

@@ -0,0 +1,376 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles settings retrieval from the settings API.
*/
class WC_Gateway_PPEC_Settings {
/**
* Setting values from get_option.
*
* @var array
*/
protected $_settings = array();
/**
* List of locales supported by PayPal.
*
* @var array
*/
protected $_supported_locales = array(
'da_DK',
'de_DE',
'en_AU',
'en_GB',
'en_US',
'es_ES',
'fr_CA',
'fr_FR',
'he_IL',
'id_ID',
'it_IT',
'ja_JP',
'nl_NL',
'no_NO',
'pl_PL',
'pt_BR',
'pt_PT',
'ru_RU',
'sv_SE',
'th_TH',
'tr_TR',
'zh_CN',
'zh_HK',
'zh_TW',
);
/**
* Flag to indicate setting has been loaded from DB.
*
* @var bool
*/
private $_is_setting_loaded = false;
public function __set( $key, $value ) {
if ( array_key_exists( $key, $this->_settings ) ) {
$this->_settings[ $key ] = $value;
}
}
public function __get( $key ) {
if ( array_key_exists( $key, $this->_settings ) ) {
return $this->_settings[ $key ];
}
return null;
}
public function __isset( $key ) {
return array_key_exists( $key, $this->_settings );
}
public function __construct() {
$this->load();
}
/**
* Load settings from DB.
*
* @since 1.2.0
*
* @param bool $force_reload Force reload settings
*
* @return WC_Gateway_PPEC_Settings Instance of WC_Gateway_PPEC_Settings
*/
public function load( $force_reload = false ) {
if ( $this->_is_setting_loaded && ! $force_reload ) {
return $this;
}
$this->_settings = (array) get_option( 'woocommerce_ppec_paypal_settings', array() );
$this->_is_setting_loaded = true;
return $this;
}
/**
* Load settings from DB.
*
* @deprecated
*/
public function load_settings( $force_reload = false ) {
_deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Settings::load' );
return $this->load( $force_reload );
}
/**
* Save current settings.
*
* @since 1.2.0
*/
public function save() {
update_option( 'woocommerce_ppec_paypal_settings', $this->_settings );
}
/**
* Get API credentials for live envionment.
*
* @return WC_Gateway_PPEC_Client_Credential_Signature|WC_Gateway_PPEC_Client_Credential_Certificate
*/
public function get_live_api_credentials() {
if ( $this->api_certificate ) {
return new WC_Gateway_PPEC_Client_Credential_Certificate( $this->api_username, $this->api_password, $this->api_certificate, $this->api_subject );
}
return new WC_Gateway_PPEC_Client_Credential_Signature( $this->api_username, $this->api_password, $this->api_signature, $this->api_subject );
}
/**
* Get API credentials for sandbox envionment.
*
* @return WC_Gateway_PPEC_Client_Credential_Signature|WC_Gateway_PPEC_Client_Credential_Certificate
*/
public function get_sandbox_api_credentials() {
if ( $this->sandbox_api_certificate ) {
return new WC_Gateway_PPEC_Client_Credential_Certificate( $this->sandbox_api_username, $this->sandbox_api_password, $this->sandbox_api_certificate, $this->sandbox_api_subject );
}
return new WC_Gateway_PPEC_Client_Credential_Signature( $this->sandbox_api_username, $this->sandbox_api_password, $this->sandbox_api_signature, $this->sandbox_api_subject );
}
/**
* Get API credentials for the current envionment.
*
* @return object
*/
public function get_active_api_credentials() {
return 'live' === $this->get_environment() ? $this->get_live_api_credentials() : $this->get_sandbox_api_credentials();
}
/**
* Get PayPal redirect URL.
*
* @param string $token Token
* @param bool $commit If set to true, 'useraction' parameter will be set
* to 'commit' which makes PayPal sets the button text
* to **Pay Now** ont the PayPal _Review your information_
* page.
* @param bool $ppc Whether to use PayPal credit.
*
* @return string PayPal redirect URL
*/
public function get_paypal_redirect_url( $token, $commit = false, $ppc = false ) {
$url = 'https://www.';
if ( 'live' !== $this->environment ) {
$url .= 'sandbox.';
}
$url .= 'paypal.com/checkoutnow?token=' . urlencode( $token );
if ( $commit ) {
$url .= '&useraction=commit';
}
if ( $ppc ) {
$url .= '#/checkout/chooseCreditOffer';
}
return $url;
}
public function get_set_express_checkout_shortcut_params( $buckets = 1 ) {
_deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Client::get_set_express_checkout_params' );
return wc_gateway_ppec()->client->get_set_express_checkout_params( array( 'start_from' => 'cart' ) );
}
public function get_set_express_checkout_mark_params( $buckets = 1 ) {
_deprecated_function( __METHOD__, '1.2.0', 'WC_Gateway_PPEC_Client::get_set_express_checkout_params' );
// Still missing order_id in args.
return wc_gateway_ppec()->client->get_set_express_checkout_params( array(
'start_from' => 'checkout',
) );
}
/**
* Get base parameters, based on settings instance, for DoExpressCheckoutCheckout NVP call.
*
* @see https://developer.paypal.com/docs/classic/api/merchant/DoExpressCheckoutPayment_API_Operation_NVP/
*
* @param WC_Order $order Order object
* @param int|array $buckets Number of buckets or list of bucket
*
* @return array DoExpressCheckoutPayment parameters
*/
public function get_do_express_checkout_params( WC_Order $order, $buckets = 1 ) {
$params = array();
if ( ! is_array( $buckets ) ) {
$num_buckets = $buckets;
$buckets = array();
for ( $i = 0; $i < $num_buckets; $i++ ) {
$buckets[] = $i;
}
}
foreach ( $buckets as $bucket_num ) {
$params[ 'PAYMENTREQUEST_' . $bucket_num . '_NOTIFYURL' ] = WC()->api_request_url( 'WC_Gateway_PPEC' );
$params[ 'PAYMENTREQUEST_' . $bucket_num . '_PAYMENTACTION' ] = $this->get_paymentaction();
$params[ 'PAYMENTREQUEST_' . $bucket_num . '_INVNUM' ] = $this->invoice_prefix . $order->get_order_number();
$params[ 'PAYMENTREQUEST_' . $bucket_num . '_CUSTOM' ] = json_encode( array( 'order_id' => $order->id, 'order_key' => $order->order_key ) );
}
return $params;
}
/**
* Is PPEC enabled.
*
* @return bool
*/
public function is_enabled() {
return 'yes' === $this->enabled;
}
/**
* Is logging enabled.
*
* @return bool
*/
public function is_logging_enabled() {
return 'yes' === $this->debug;
}
/**
* Get payment action from setting.
*
* @return string
*/
public function get_paymentaction() {
return 'authorization' === $this->paymentaction ? 'authorization' : 'sale';
}
/**
* Get active environment from setting.
*
* @return string
*/
public function get_environment() {
return 'sandbox' === $this->environment ? 'sandbox' : 'live';
}
/**
* Subtotal mismatches.
*
* @return string
*/
public function get_subtotal_mismatch_behavior() {
return 'drop' === $this->subtotal_mismatch_behavior ? 'drop' : 'add';
}
/**
* Get session length.
*
* @todo Map this to a merchant-configurable setting
*
* @return int
*/
public function get_token_session_length() {
return 10800; // 3h
}
/**
* Whether currency has decimal restriction for PPCE to functions?
*
* @return bool True if it has restriction otherwise false
*/
public function currency_has_decimal_restriction() {
return (
'yes' === $this->enabled
&&
in_array( get_woocommerce_currency(), array( 'HUF', 'TWD', 'JPY' ) )
&&
0 !== absint( get_option( 'woocommerce_price_num_decimals', 2 ) )
);
}
/**
* Get locale for PayPal.
*
* @return string
*/
public function get_paypal_locale() {
$locale = get_locale();
if ( ! in_array( $locale, $this->_supported_locales ) ) {
$locale = 'en_US';
}
return $locale;
}
/**
* Get brand name form settings.
*
* Default to site's name if brand_name in settings empty.
*
* @since 1.2.0
*
* @return string
*/
public function get_brand_name() {
$brand_name = $this->brand_name ? $this->brand_name : get_bloginfo( 'name', 'display' );
/**
* Character length and limitations for this parameter is 127 single-byte
* alphanumeric characters.
*
* @see https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout_API_Operation_NVP/
*/
if ( ! empty( $brand_name ) ) {
$brand_name = substr( $brand_name, 0, 127 );
}
/**
* Filters the brand name in PayPal hosted checkout pages.
*
* @since 1.2.0
*
* @param string Brand name
*/
return apply_filters( 'woocommerce_paypal_express_checkout_get_brand_name', $brand_name );
}
/**
* Checks whether PayPal Credit is enabled.
*
* @since 1.2.0
*
* @return bool Returns true if PayPal Credit is enabled and supported
*/
public function is_credit_enabled() {
return 'yes' === $this->credit_enabled && wc_gateway_ppec_is_credit_supported();
}
/**
* Checks if currency in setting supports 0 decimal places.
*
* @since 1.2.0
*
* @return bool Returns true if currency supports 0 decimal places
*/
public function is_currency_supports_zero_decimal() {
return in_array( get_woocommerce_currency(), array( 'HUF', 'JPY', 'TWD' ) );
}
/**
* Get number of digits after the decimal point.
*
* @since 1.2.0
*
* @return int Number of digits after the decimal point. Either 2 or 0
*/
public function get_number_of_decimal_digits() {
return $this->is_currency_supports_zero_decimal() ? 0 : 2;
}
}

View File

@@ -0,0 +1,204 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_With_PayPal_Addons extends WC_Gateway_PPEC_With_PayPal {
public function __construct() {
parent::__construct();
$this->supports = array_merge(
$this->supports,
array(
'subscriptions',
'subscription_cancellation',
'subscription_reactivation',
'subscription_suspension',
'multiple_subscriptions',
'subscription_payment_method_change_customer',
'subscription_payment_method_change_admin',
'subscription_amount_changes',
'subscription_date_changes',
)
);
$this->_maybe_register_callback_in_subscriptions();
}
/**
* Maybe register callback in WooCommerce Subscription hooks.
*
* @since 1.2.0
*/
protected function _maybe_register_callback_in_subscriptions() {
if ( ! class_exists( 'WC_Subscriptions_Order' ) ) {
return;
}
add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
add_action( 'woocommerce_subscription_failing_payment_method_' . $this->id, array( $this, 'update_failing_payment_method' ) );
}
/**
* Checks whether order is part of subscription.
*
* @since 1.2.0
*
* @param int $order_id Order ID
*
* @return bool Returns true if order is part of subscription
*/
public function is_subscription( $order_id ) {
return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
}
/**
* Process payment.
*
* @since 1.2.0
*
* @param int $order_id Order ID
*
* @return array
*/
public function process_payment( $order_id ) {
if ( $this->is_subscription( $order_id ) ) {
return $this->process_subscription( $order_id );
}
return parent::process_payment( $order_id );
}
/**
* Process initial subscription.
*
* @since 1.2.0
*
* @param int $order_id Order ID
*
* @return array
*/
public function process_subscription( $order_id ) {
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$resp = parent::process_payment( $order_id );
$order = wc_get_order( $order_id );
$subscriptions = array();
if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
$subscriptions = wcs_get_subscriptions_for_order( $order_id );
} elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) {
$subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
}
$billing_agreement_id = $old_wc ? get_post_meta( $order_id, '_ppec_billing_agreement_id', true ) : $order->get_meta( '_ppec_billing_agreement_id', true );
// Shipping / billing addresses and billing agreement were not copied
// because it's not available during subscription creation.
foreach ( $subscriptions as $subscription ) {
wcs_copy_order_address( $order, $subscription );
update_post_meta( is_callable( array( $subscription, 'get_id' ) ) ? $subscription->get_id() : $subscription->id, '_ppec_billing_agreement_id', $billing_agreement_id );
}
return $resp;
}
/**
* Process scheduled subscription payment.
*
* @since 1.2.0
*
* @param float $amount Subscription amount
* @param int|WC_Order $order Order ID or order object
*/
public function scheduled_subscription_payment( $amount, $order ) {
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$order = wc_get_order( $order );
$order_id = $old_wc ? $order->id : $order->get_id();
$billing_agreement_id = $old_wc ? get_post_meta( $order_id, '_ppec_billing_agreement_id', true ) : $order->get_meta( '_ppec_billing_agreement_id', true );
if ( empty( $billing_agreement_id ) ) {
wc_gateway_ppec_log( sprintf( '%s: Could not found billing agreement. Skip reference transaction', __METHOD__ ) );
return;
}
if ( 0 == $amount ) {
$order->payment_complete();
return;
}
$client = wc_gateway_ppec()->client;
$params = $client->get_do_reference_transaction_params( array(
'reference_id' => $billing_agreement_id,
'amount' => $amount,
'order_id' => $order_id,
) );
$resp = $client->do_reference_transaction( $params );
$this->_process_reference_transaction_response( $order, $resp );
}
/**
* Process reference transaction response used when creating payment for
* scheduled subscription.
*
* @since 1.2.0
*
* @param WC_Order $order Order object
* @param array $response Response from DoReferenceTransaction
*/
protected function _process_reference_transaction_response( $order, $response ) {
$client = wc_gateway_ppec()->client;
try {
if ( ! $client->response_has_success_status( $response ) ) {
throw new Exception( __( 'PayPal API error', 'woocommerce-gateway-paypal-express-checkout' ) );
}
$status = ! empty( $response['PAYMENTSTATUS'] ) ? $response['PAYMENTSTATUS'] : '';
switch ( $status ) {
case 'Pending':
/* translators: placeholder is pending reason from PayPal API. */
$order_note = sprintf( __( 'PayPal transaction held: %s', 'woocommerce-gateway-paypal-express-checkout' ), $response['PENDINGREASON'] );
if ( ! $order->has_status( 'on-hold' ) ) {
$order->update_status( 'on-hold', $order_note );
} else {
$order->add_order_note( $order_note );
}
break;
case 'Completed':
case 'Processed':
case 'In-Progress':
$transaction_id = $response['TRANSACTIONID'];
$order->add_order_note( sprintf( __( 'PayPal payment approved (ID: %s)', 'woocommerce-gateway-paypal-express-checkout' ), $transaction_id ) );
$order->payment_complete( $transaction_id );
break;
default:
throw new Exception( __( 'PayPal payment declined', 'woocommerce-gateway-paypal-express-checkout' ) );
}
} catch ( Exception $e ) {
$order->update_status( 'failed', $e->getMessage() );
}
}
/**
* Update billing agreement ID for a subscription after using PPEC to complete
* a payment to make up for an automatic renewal payment which previously
* failed.
*
* @since 1.2.0
*
* @param WC_Subscription $subscription The subscription for which the failing
* payment method relates
* @param WC_Order $renewal_order The order which recorded the successful
* payment (to make up for the failed
* automatic payment)
*/
public function update_failing_payment_method( $subscription, $renewal_order ) {
update_post_meta( is_callable( array( $subscription, 'get_id' ) ) ? $subscription->get_id() : $subscription->id, '_ppec_billing_agreement_id', $renewal_order->ppec_billing_agreement_id );
}
}

View File

@@ -0,0 +1,21 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_With_PayPal_Credit extends WC_Gateway_PPEC_With_PayPal {
public function __construct() {
$this->icon = 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/ppc-acceptance-small.png';
parent::__construct();
if ( ! is_admin() ) {
if ( wc_gateway_ppec()->checkout->is_started_from_checkout_page() ) {
$this->title = __( 'PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' );;
}
}
$this->use_ppc = true;
}
}

View File

@@ -0,0 +1,19 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_Gateway_PPEC_With_PayPal extends WC_Gateway_PPEC {
public function __construct() {
$this->id = 'ppec_paypal';
$this->icon = 'https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png';
parent::__construct();
if ( $this->is_available() ) {
$ipn_handler = new WC_Gateway_PPEC_IPN_Handler( $this );
$ipn_handler->handle();
}
}
}

View File

@@ -0,0 +1,73 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* PayPal API Exception.
*/
class PayPal_API_Exception extends Exception {
/**
* List of errors from PayPal API.
*
* @var array
*/
public $errors;
/**
* Unique identifier of PayPal transaction.
*
* This identifies the PayPal application that processed the request and
* must be provided to Merchant Technical Support if you need their assistance
* with a specific transaction.
*
* @var string
*/
public $correlation_id;
/**
* Constructor.
*
* This constructor takes the API response received from PayPal, parses out the
* errors in the response, then places those errors into the $errors property.
* It also captures correlation ID and places that in the $correlation_id property.
*
* @param array $response Response from PayPal API
*/
public function __construct( $response ) {
parent::__construct( __( 'An error occurred while calling the PayPal API.', 'woocommerce-gateway-paypal-express-checkout' ) );
$errors = array();
foreach ( $response as $index => $value ) {
if ( preg_match( '/^L_ERRORCODE(\d+)$/', $index, $matches ) ) {
$errors[ $matches[1] ]['code'] = $value;
} elseif ( preg_match( '/^L_SHORTMESSAGE(\d+)$/', $index, $matches ) ) {
$errors[ $matches[1] ]['message'] = $value;
} elseif ( preg_match( '/^L_LONGMESSAGE(\d+)$/', $index, $matches ) ) {
$errors[ $matches[1] ]['long'] = $value;
} elseif ( preg_match( '/^L_SEVERITYCODE(\d+)$/', $index, $matches ) ) {
$errors[ $matches[1] ]['severity'] = $value;
} elseif ( 'CORRELATIONID' == $index ) {
$this->correlation_id = $value;
}
}
$this->errors = array();
$error_messages = array();
foreach ( $errors as $value ) {
$error = new PayPal_API_Error( $value['code'], $value['message'], $value['long'], $value['severity'] );
$this->errors[] = $error;
/* translators: placeholders are error code and message from PayPal */
$error_messages[] = sprintf( __( 'PayPal error (%1$s): %2$s', 'woocommerce-gateway-paypal-express-checkout' ), $error->error_code, $error->maptoBuyerFriendlyError() );
}
if ( empty( $error_messages ) ) {
$error_messages[] = __( 'An error occurred while calling the PayPal API.', 'woocommerce-gateway-paypal-express-checkout' );
}
$this->message = implode( PHP_EOL, $error_messages );
}
}

View File

@@ -0,0 +1,24 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Missing session exception.
*/
class PayPal_Missing_Session_Exception extends Exception {
/**
* Constructor.
*
* @param string $message Exception message
*/
public function __construct( $message = '' ) {
if ( empty( $message ) ) {
$message = __( 'The buyer\'s session information could not be found.', 'woocommerce-gateway-paypal-express-checkout' );
}
parent::__construct( $message );
}
}

View File

@@ -0,0 +1,93 @@
<?php
function woo_pp_start_checkout() {
$checkout = wc_gateway_ppec()->checkout;
try {
$redirect_url = $checkout->start_checkout_from_cart();
wp_safe_redirect( $redirect_url );
exit;
} catch( PayPal_API_Exception $e ) {
wc_add_notice( $e->getMessage(), 'error' );
$redirect_url = wc_get_cart_url();
$settings = wc_gateway_ppec()->settings;
$client = wc_gateway_ppec()->client;
if ( $settings->is_enabled() && $client->get_payer_id() ) {
ob_end_clean();
?>
<script type="text/javascript">
if( ( window.opener != null ) && ( window.opener !== window ) &&
( typeof window.opener.paypal != "undefined" ) &&
( typeof window.opener.paypal.checkout != "undefined" ) ) {
window.opener.location.assign( "<?php echo $redirect_url; ?>" );
window.close();
} else {
window.location.assign( "<?php echo $redirect_url; ?>" );
}
</script>
<?php
exit;
} else {
wp_safe_redirect( $redirect_url );
exit;
}
}
}
/**
* @deprecated
*/
function wc_gateway_ppec_format_paypal_api_exception( $errors ) {
_deprecated_function( 'wc_gateway_ppec_format_paypal_api_exception', '1.2.0', '' );
}
/**
* Log a message via WC_Logger.
*
* @param string $message Message to log
*/
function wc_gateway_ppec_log( $message ) {
static $wc_ppec_logger;
// No need to write to log file if logging is disabled.
if ( ! wc_gateway_ppec()->settings->is_logging_enabled() ) {
return false;
}
if ( ! isset( $wc_ppec_logger ) ) {
$wc_ppec_logger = new WC_Logger();
}
$wc_ppec_logger->add( 'wc_gateway_ppec', $message );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( $message );
}
}
/**
* Whether PayPal credit is supported.
*
* @since 1.5.0
*
* @return bool Returns true if PayPal credit is supported
*/
function wc_gateway_ppec_is_credit_supported() {
$base = wc_get_base_location();
return 'US' === $base['country'];
}
/**
* Checks whether buyer is checking out with PayPal Credit.
*
* @since 1.2.0
*
* @return bool Returns true if buyer is checking out with PayPal Credit
*/
function wc_gateway_ppec_is_using_credit() {
return ! empty( $_GET['use-ppc'] ) && 'true' === $_GET['use-ppc'];
}

View File

@@ -0,0 +1,117 @@
-----BEGIN CERTIFICATE-----
MIIFWDCCBECgAwIBAgIQB8o+TSd/21eGoIdLI1qp6zANBgkqhkiG9w0BAQUFADCB
tTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm
VmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTMwODAx
MDAwMDAwWhcNMTUwOTE4MjM1OTU5WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
CkNhbGlmb3JuaWExETAPBgNVBAcUCFNhbiBKb3NlMRUwEwYDVQQKFAxQYXlQYWws
IEluYy4xGjAYBgNVBAsUEVBheVBhbCBQcm9kdWN0aW9uMSIwIAYDVQQDFBlhcGkt
M3Quc2FuZGJveC5wYXlwYWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAqpUTPy8lOveTpJd8RBUWI73HcyLtNw+RbS2sxXa+7t3XTc6IEF0bF6dS
55d4r4GjU+eiFKYXx9atIO7aNnIp7ebVtPJTBBdRLFHwzpKmOhNPbwOozrThfY5F
KKVAjniUS+5qnxrRVGB3c42W+EVLu8xagQvRnTKr5DYA1lSLHmiXYUcuGgpvSF6p
OcHy5iKM3FIVTc98rxgUdlbh4L3Sv0HdGaPbx4UPJ41ft7ZsjHqoCIjNP9tZuMBn
6m/l+eCIiTvZYVJgHXkwz6pm5WP92hqsl3Ts+V/7jyJYJIqg+aWUc/DjZVkU9JND
PqQUuj88uM695MZyzS2pdbPAeQZ57wIDAQABo4IBiTCCAYUwJAYDVR0RBB0wG4IZ
YXBpLTN0LnNhbmRib3gucGF5cGFsLmNvbTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQE
AwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwQwYDVR0gBDwwOjA4
BgpghkgBhvhFAQc2MCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWdu
LmNvbS9jcHMwHwYDVR0jBBgwFoAUDURcFlNEwYJ+HSCrJfQBY9i+eaUwRQYDVR0f
BD4wPDA6oDigNoY0aHR0cDovL1NWUlNlY3VyZS1HMy1jcmwudmVyaXNpZ24uY29t
L1NWUlNlY3VyZUczLmNybDB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0
dHA6Ly9vY3NwLnZlcmlzaWduLmNvbTBABggrBgEFBQcwAoY0aHR0cDovL1NWUlNl
Y3VyZS1HMy1haWEudmVyaXNpZ24uY29tL1NWUlNlY3VyZUczLmNlcjANBgkqhkiG
9w0BAQUFAAOCAQEASUdxPQFgUi6sSDJ4Aw071gf0sHRiiFMdZSJazmVxQA8/bpiD
CFDq5rYEMR8jqQMx4bPfeUjAZVwmT0q7YlLRGnDEyur6fd7yv1pbCCN0anfwORE2
tn/6yxgBp9JPaOc4k8mWbYr/1p94V/tlvaqZ52Cz8gQ7pMuvmWLgJuAhk0hdtNXq
vktqDfGCxdv+8CgoUdTQOdqQEkcz7HueAQ7DLCXum530p+QpQQy0xKT2+EzIXnHC
erln4DHogkj8dWt+oehtRZwChX5Aczig1P3zBW8SBuZwS1b8XZKudr7kFpZg1/S3
U30gP7d3TEvbnVtXSjTl3xLT3848FbJbfGmNKg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF7DCCBNSgAwIBAgIQbsx6pacDIAm4zrz06VLUkTANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggHfMIIB
2zA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlz
aWduLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4
RQEHFwMwVjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nw
czAqBggrBgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQG
A1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUu
Y3JsMA4GA1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglp
bWFnZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNo
dHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0w
GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+
HSCrJfQBY9i+eaUwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJ
KoZIhvcNAQEFBQADggEBAAyDJO/dwwzZWJz+NrbrioBL0aP3nfPMU++CnqOh5pfB
WJ11bOAdG0z60cEtBcDqbrIicFXZIDNAMwfCZYP6j0M3m+oOmmxw7vacgDvZN/R6
bezQGH1JSsqZxxkoor7YdyT3hSaGbYcFQEFn0Sc67dxIHSLNCwuLvPSxe/20majp
dirhGi2HbnTTiN0eIsbfFrYrghQKlFzyUOyvzv9iNw2tZdMGQVPtAhTItVgooazg
W+yzf5VK+wPIrSbb5mZ4EkrZn0L74ZjmQoObj49nJOhhGbXdzbULJgWOw27EyHW4
Rs/iGAZeqa6ogZpHFt4MKGwlJ7net4RYxh84HqTEy2Y=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFyTCCBTKgAwIBAgIQek8uV8VnNIp7IFhGnUesRTANBgkqhkiG9w0BAQUFADCB
wTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTwwOgYDVQQL
EzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1
dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
cmswHhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMC
VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAt
IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFz
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07c
fLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs
70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP
6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06
tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnk
jWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUV
AgMBAAGjggIxMIICLTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA9
BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
L3ZzbG9nby5naWYwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2ln
bi5jb20vcGNhMy1nMi5jcmwwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MIHnBgNVHSMEgd8wgdyhgcekgcQwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5W
ZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBW
ZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrghB92f4Hz6getxB5Z/uniTTGMA0GCSqG
SIb3DQEBBQUAA4GBACca3K/R32nu+7tGBJ42k7SZ2f8Qk1+WLePXvICJmPKoaa87
LDVJgn/0lLMfmSON8O9Y1Z3ERVtwScITLo8C3ycnXngGNOQgRQJ9+pGhjE7rPGhF
Gx57i4p49zVt5Xh9uE50iML9njdENzUc6Tgi7e3Pp9Z3RDZqA0WjAQ3vq/uC
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
oJ2daZH9
-----END CERTIFICATE-----

View File

@@ -0,0 +1,403 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$api_username = $this->get_option( 'api_username' );
$sandbox_api_username = $this->get_option( 'sandbox_api_username' );
$needs_creds = empty( $api_username );
$needs_sandbox_creds = empty( $sandbox_api_username );
$enable_ips = wc_gateway_ppec()->ips->is_supported();
if ( $enable_ips && $needs_creds ) {
$ips_button = '<a href="' . esc_url( wc_gateway_ppec()->ips->get_signup_url( 'live' ) ) . '" class="button button-primary">' . __( 'Setup or link an existing PayPal account', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
$api_creds_text = sprintf( __( '%s or <a href="#" class="ppec-toggle-settings">click here to toggle manual API credential input</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $ips_button );
} else {
$reset_link = add_query_arg(
array(
'reset_ppec_api_credentials' => 'true',
'environment' => 'live',
'reset_nonce' => wp_create_nonce( 'reset_ppec_api_credentials' ),
),
wc_gateway_ppec()->get_admin_setting_link()
);
$api_creds_text = sprintf( __( 'To reset current credentials and use other account <a href="%1$s" title="%2$s">click here</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $reset_link, __( 'Reset current credentials', 'woocommerce-gateway-paypal-express-checkout' ) );
}
if ( $enable_ips && $needs_sandbox_creds ) {
$sandbox_ips_button = '<a href="' . esc_url( wc_gateway_ppec()->ips->get_signup_url( 'sandbox' ) ) . '" class="button button-primary">' . __( 'Setup or link an existing PayPal Sandbox account', 'woocommerce-gateway-paypal-express-checkout' ) . '</a>';
$sandbox_api_creds_text = sprintf( __( '%s or <a href="#" class="ppec-toggle-sandbox-settings">click here to toggle manual API credential input</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $sandbox_ips_button );
} else {
$reset_link = add_query_arg(
array(
'reset_ppec_api_credentials' => 'true',
'environment' => 'sandbox',
'reset_nonce' => wp_create_nonce( 'reset_ppec_api_credentials' ),
),
wc_gateway_ppec()->get_admin_setting_link()
);
$sandbox_api_creds_text = sprintf( __( 'Your account setting is set to sandbox, no real charging takes place. To accept live payments, switch your environment to live and connect your PayPal account. To reset current credentials and use other sandbox account <a href="%1$s" title="%2$s">click here</a>.', 'woocommerce-gateway-paypal-express-checkout' ), $reset_link, __( 'Reset current sandbox credentials', 'woocommerce-gateway-paypal-express-checkout' ) );
}
$credit_enabled_label = __( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' );
if ( ! wc_gateway_ppec_is_credit_supported() ) {
$credit_enabled_label .= '<p><em>' . __( 'This option is disabled. Currently PayPal Credit only available for U.S. merchants.', 'woocommerce-gateway-paypal-express-checkout' ) . '</em></p>';
}
wc_enqueue_js( "
jQuery( function( $ ) {
var ppec_mark_fields = '#woocommerce_ppec_paypal_title, #woocommerce_ppec_paypal_description';
var ppec_live_fields = '#woocommerce_ppec_paypal_api_username, #woocommerce_ppec_paypal_api_password, #woocommerce_ppec_paypal_api_signature, #woocommerce_ppec_paypal_api_certificate, #woocommerce_ppec_paypal_api_subject';
var ppec_sandbox_fields = '#woocommerce_ppec_paypal_sandbox_api_username, #woocommerce_ppec_paypal_sandbox_api_password, #woocommerce_ppec_paypal_sandbox_api_signature, #woocommerce_ppec_paypal_sandbox_api_certificate, #woocommerce_ppec_paypal_sandbox_api_subject';
var enable_toggle = $( 'a.ppec-toggle-settings' ).length > 0;
var enable_sandbox_toggle = $( 'a.ppec-toggle-sandbox-settings' ).length > 0;
$( '#woocommerce_ppec_paypal_environment' ).change(function(){
$( ppec_sandbox_fields + ',' + ppec_live_fields ).closest( 'tr' ).hide();
if ( 'live' === $( this ).val() ) {
$( '#woocommerce_ppec_paypal_api_credentials, #woocommerce_ppec_paypal_api_credentials + p' ).show();
$( '#woocommerce_ppec_paypal_sandbox_api_credentials, #woocommerce_ppec_paypal_sandbox_api_credentials + p' ).hide();
if ( ! enable_toggle ) {
$( ppec_live_fields ).closest( 'tr' ).show();
}
} else {
$( '#woocommerce_ppec_paypal_api_credentials, #woocommerce_ppec_paypal_api_credentials + p' ).hide();
$( '#woocommerce_ppec_paypal_sandbox_api_credentials, #woocommerce_ppec_paypal_sandbox_api_credentials + p' ).show();
if ( ! enable_sandbox_toggle ) {
$( ppec_sandbox_fields ).closest( 'tr' ).show();
}
}
}).change();
$( '#woocommerce_ppec_paypal_enabled' ).change(function(){
if ( $( this ).is( ':checked' ) ) {
$( ppec_mark_fields ).closest( 'tr' ).show();
} else {
$( ppec_mark_fields ).closest( 'tr' ).hide();
}
}).change();
$( '#woocommerce_ppec_paypal_paymentaction' ).change(function(){
if ( 'sale' === $( this ).val() ) {
$( '#woocommerce_ppec_paypal_instant_payments' ).closest( 'tr' ).show();
} else {
$( '#woocommerce_ppec_paypal_instant_payments' ).closest( 'tr' ).hide();
}
}).change();
if ( enable_toggle ) {
$( document ).off( 'click', '.ppec-toggle-settings' );
$( document ).on( 'click', '.ppec-toggle-settings', function( e ) {
$( ppec_live_fields ).closest( 'tr' ).toggle( 'fast' );
e.preventDefault();
} );
}
if ( enable_sandbox_toggle ) {
$( document ).off( 'click', '.ppec-toggle-sandbox-settings' );
$( document ).on( 'click', '.ppec-toggle-sandbox-settings', function( e ) {
$( ppec_sandbox_fields ).closest( 'tr' ).toggle( 'fast' );
e.preventDefault();
} );
}
});
" );
/**
* Settings for PayPal Gateway.
*/
return apply_filters( 'woocommerce_paypal_express_checkout_settings', array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ),
'description' => __( 'This enables PayPal Express Checkout which allows customers to checkout directly via PayPal from your cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
'desc_tip' => true,
'default' => 'yes',
),
'title' => array(
'title' => __( 'Title', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => __( 'PayPal Express Checkout', 'woocommerce-gateway-paypal-express-checkout' ),
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'desc_tip' => true,
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => __( 'Pay via PayPal; you can pay with your credit card if you don\'t have a PayPal account.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'account_settings' => array(
'title' => __( 'Account Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => '',
),
'environment' => array(
'title' => __( 'Environment', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'description' => __( 'This setting specifies whether you will process live transactions, or whether you will process simulated transactions using the PayPal Sandbox.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'live',
'desc_tip' => true,
'options' => array(
'live' => __( 'Live', 'woocommerce-gateway-paypal-express-checkout' ),
'sandbox' => __( 'Sandbox', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'api_credentials' => array(
'title' => __( 'API Credentials', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => $api_creds_text,
),
'api_username' => array(
'title' => __( 'Live API Username', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
),
'api_password' => array(
'title' => __( 'Live API Password', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'password',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
),
'api_signature' => array(
'title' => __( 'Live API Signature', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional if you provide a certificate below', 'woocommerce-gateway-paypal-express-checkout' ),
),
'api_certificate' => array(
'title' => __( 'Live API Certificate', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'file',
'description' => $this->get_certificate_info( $this->get_option( 'api_certificate' ) ),
'default' => '',
),
'api_subject' => array(
'title' => __( 'Live API Subject', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
),
'sandbox_api_credentials' => array(
'title' => __( 'Sandbox API Credentials', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => $sandbox_api_creds_text,
),
'sandbox_api_username' => array(
'title' => __( 'Sandbox API Username', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
),
'sandbox_api_password' => array(
'title' => __( 'Sandbox API Password', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'password',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
),
'sandbox_api_signature' => array(
'title' => __( 'Sandbox API Signature', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
),
'sandbox_api_certificate' => array(
'title' => __( 'Sandbox API Certificate', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'file',
'description' => __( 'Get your API credentials from PayPal.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
),
'sandbox_api_subject' => array(
'title' => __( 'Sandbox API Subject', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'If you\'re processing transactions on behalf of someone else\'s PayPal account, enter their email address or Secure Merchant Account ID (also known as a Payer ID) here. Generally, you must have API permissions in place with the other account in order to process anything other than "sale" transactions for them.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
),
'display_settings' => array(
'title' => __( 'Display Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => __( 'Customize the appearance of Express Checkout in your store.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'brand_name' => array(
'title' => __( 'Brand Name', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'A label that overrides the business name in the PayPal account on the PayPal hosted checkout pages.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => get_bloginfo( 'name', 'display' ),
'desc_tip' => true,
),
'button_size' => array(
'title' => __( 'Button Size', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'description' => __( 'PayPal offers different sizes of the "PayPal Checkout" buttons, allowing you to select a size that best fits your site\'s theme. This setting will allow you to choose which size button(s) appear on your cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'large',
'desc_tip' => true,
'options' => array(
'small' => __( 'Small', 'woocommerce-gateway-paypal-express-checkout' ),
'medium' => __( 'Medium', 'woocommerce-gateway-paypal-express-checkout' ),
'large' => __( 'Large', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'cart_checkout_enabled' => array(
'title' => __( 'Checkout on cart page', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal checkout on the cart page', 'woocommerce-gateway-paypal-express-checkout' ),
'description' => __( 'This shows or hides the PayPal checkout button on the cart page.', 'woocommerce-gateway-paypal-express-checkout' ),
'desc_tip' => true,
'default' => 'yes',
),
'mark_enabled' => array(
'title' => __( 'PayPal Mark', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Enable the PayPal Mark on regular checkout', 'woocommerce-gateway-paypal-express-checkout' ),
'description' => __( 'This enables the PayPal mark, which can be shown on regular WooCommerce checkout to use PayPal Express Checkout like a regular WooCommerce gateway.', 'woocommerce-gateway-paypal-express-checkout' ),
'desc_tip' => true,
'default' => 'no',
),
'logo_image_url' => array(
'title' => __( 'Logo Image (190×60)', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'image',
'description' => __( 'If you want PayPal to co-brand the checkout page with your logo, enter the URL of your logo image here.<br/>The image must be no larger than 190x60, GIF, PNG, or JPG format, and should be served over HTTPS.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
),
'header_image_url' => array(
'title' => __( 'Header Image (750×90)', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'image',
'description' => __( 'If you want PayPal to co-brand the checkout page with your header, enter the URL of your header image here.<br/>The image must be no larger than 750x90, GIF, PNG, or JPG format, and should be served over HTTPS.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
),
'page_style' => array(
'title' => __( 'Page Style', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'Optionally enter the name of the page style you wish to use. These are defined within your PayPal account.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => '',
'desc_tip' => true,
'placeholder' => __( 'Optional', 'woocommerce-gateway-paypal-express-checkout' ),
),
'landing_page' => array(
'title' => __( 'Landing Page', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'description' => __( 'Type of PayPal page to display.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'Login',
'desc_tip' => true,
'options' => array(
'Billing' => _x( 'Billing (Non-PayPal account)', 'Type of PayPal page', 'woocommerce-gateway-paypal-express-checkout' ),
'Login' => _x( 'Login (PayPal account login)', 'Type of PayPal page', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'credit_enabled' => array(
'title' => __( 'Enable PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => $credit_enabled_label,
'disabled' => ! wc_gateway_ppec_is_credit_supported(),
'default' => 'no',
'desc_tip' => true,
'description' => __( 'This enables PayPal Credit, which displays a PayPal Credit button next to the Express Checkout button. PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button shows up next to the PayPal Button, and lets customers pay quickly with PayPal Credit®.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'checkout_on_single_product_enabled' => array(
'title' => __( 'Checkout on Single Product', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Checkout on Single Product', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'no',
'description' => __( 'Enable Express checkout on Single Product view.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'advanced' => array(
'title' => __( 'Advanced Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => '',
),
'debug' => array(
'title' => __( 'Debug Log', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Enable Logging', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'no',
'desc_tip' => true,
'description' => __( 'Log PayPal events, such as IPN requests.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'invoice_prefix' => array(
'title' => __( 'Invoice Prefix', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'text',
'description' => __( 'Please enter a prefix for your invoice numbers. If you use your PayPal account for multiple stores ensure this prefix is unique as PayPal will not allow orders with the same invoice number.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'WC-',
'desc_tip' => true,
),
'require_billing' => array(
'title' => __( 'Billing Addresses', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Require Billing Address', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'no',
'description' => sprintf( __( 'PayPal only returns a shipping address back to the website. To make sure billing address is returned as well, please enable this functionality on your PayPal account by calling %1$sPayPal Technical Support%2$s.', 'woocommerce-gateway-paypal-express-checkout' ), '<a href="https://www.paypal.com/us/selfhelp/contact/call">', '</a>' ),
),
'require_phone_number' => array(
'title' => __( 'Require Phone Number', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Require Phone Number', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'no',
'description' => __( 'Require buyer to enter their telephone number during checkout if none is provided by PayPal', 'woocommerce-gateway-paypal-express-checkout' ),
),
'paymentaction' => array(
'title' => __( 'Payment Action', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'description' => __( 'Choose whether you wish to capture funds immediately or authorize payment only.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'sale',
'desc_tip' => true,
'options' => array(
'sale' => __( 'Sale', 'woocommerce-gateway-paypal-express-checkout' ),
'authorization' => __( 'Authorize', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'instant_payments' => array(
'title' => __( 'Instant Payments', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Require Instant Payment', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'no',
'desc_tip' => true,
'description' => __( 'If you enable this setting, PayPal will be instructed not to allow the buyer to use funding sources that take additional time to complete (for example, eChecks). Instead, the buyer will be required to use an instant funding source, such as an instant transfer, a credit/debit card, or PayPal Credit.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'subtotal_mismatch_behavior' => array(
'title' => __( 'Subtotal Mismatch Behavior', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'description' => __( 'Internally, WC calculates line item prices and taxes out to four decimal places; however, PayPal can only handle amounts out to two decimal places (or, depending on the currency, no decimal places at all). Occasionally, this can cause discrepancies between the way WooCommerce calculates prices versus the way PayPal calculates them. If a mismatch occurs, this option controls how the order is dealt with so payment can still be taken.', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'add',
'desc_tip' => true,
'options' => array(
'add' => __( 'Add another line item', 'woocommerce-gateway-paypal-express-checkout' ),
'drop' => __( 'Do not send line items to PayPal', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
) );