first commit

This commit is contained in:
2024-07-15 11:28:08 +02:00
commit f52d538ea5
21891 changed files with 6161164 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
.wcppec-checkout-buttons {
text-align: center;
margin: 1em 0;
overflow: hidden;
}
.wcppec-checkout-buttons .woocommerce-error {
text-align: left;
}
.wcppec-checkout-buttons__separator {
display: block;
opacity: .5;
margin: 0 0 1em;
}
.wcppec-checkout-buttons__button {
display: inline-block;
text-decoration: none !important;
border: 0 !important;
padding-top: 1em;
}
.wcppec-checkout-buttons__button img {
margin: 0 auto;
}
.paypal-button-widget .paypal-button,
.paypal-button-widget .paypal-button:hover {
background: transparent;
box-shadow: none;
border: none;
}
.wcppec-cart-widget-button {
display: inline-block;
text-decoration: none !important;
border: 0 !important;
}
.site-header .widget_shopping_cart p.buttons.wcppec-cart-widget-spb {
padding: 0 1em 1em;
}
.site-header .widget_shopping_cart .woocommerce-mini-cart__empty-message + p.buttons.wcppec-cart-widget-spb {
display: none;
}
.payment_method_ppec_paypal img {
max-height: 68px !important;
border-radius: 0;
}
.wc-gateway-ppec-cancel {
display: block;
text-align: center;
padding: 10px;
}
#woo_pp_ec_button_checkout {
display: none;
}
#payment .place-order .button {
display: block;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

View File

@@ -0,0 +1,51 @@
;(function ( $, window, document ) {
'use strict';
var $wc_ppec = {
init: function() {
window.paypalCheckoutReady = function() {
paypal.checkout.setup(
wc_ppec_context.payer_id,
{
environment: wc_ppec_context.environment,
button: ['woo_pp_ec_button', 'woo_pp_ppc_button'],
locale: wc_ppec_context.locale,
container: ['woo_pp_ec_button', 'woo_pp_ppc_button']
}
);
}
}
}
var costs_updated = false;
$( '#woo_pp_ec_button' ).click( function( event ) {
if ( costs_updated ) {
costs_updated = false;
return;
}
event.stopPropagation();
var data = {
'nonce': wc_ppec_context.update_shipping_costs_nonce,
};
var href = $(this).attr( 'href' );
$.ajax( {
type: 'POST',
data: data,
url: wc_ppec_context.ajaxurl,
success: function( response ) {
costs_updated = true;
$( '#woo_pp_ec_button' ).click();
}
} );
} );
if ( wc_ppec_context.show_modal ) {
$wc_ppec.init();
}
})( jQuery, window, document );

View File

@@ -0,0 +1,126 @@
/* global wc_ppec_generate_cart_context */
;(function( $, window, document ) {
'use strict';
// This button state is only applicable to non-SPB click handler below.
var button_enabled = true;
$( '#woo_pp_ec_button_product' )
.on( 'enable.legacy', function() {
button_enabled = true;
} )
.on( 'disable.legacy', function() {
button_enabled = false;
} );
$( '#woo_pp_ec_button_product' )
.on( 'enable', function() {
$( '#woo_pp_ec_button_product' ).css( {
'cursor': '',
'-webkit-filter': '', // Safari 6.0 - 9.0
'filter': '',
} );
$( '#woo_pp_ec_button_product > *' ).css( 'pointer-events', '' );
} )
.on( 'disable', function() {
$( '#woo_pp_ec_button_product' ).css( {
'cursor': 'not-allowed',
'-webkit-filter': 'grayscale( 100% )', // Safari 6.0 - 9.0
'filter': 'grayscale( 100% )',
} );
$( '#woo_pp_ec_button_product > *' ).css( 'pointer-events', 'none' );
} );
// True if the product is simple or the user selected a valid variation. False on variable product without a valid variation selected
var variation_valid = true;
// True if all the fields of the product form are valid (such as required fields configured by Product Add-Ons). False otherwise
var fields_valid = true;
var form = $( 'form.cart' );
var update_button = function() {
$( '#woo_pp_ec_button_product' ).trigger( ( variation_valid && fields_valid ) ? 'enable' : 'disable' );
};
var validate_form = function() {
fields_valid = form.get( 0 ).checkValidity();
update_button();
};
// It's a variations form, button availability should depend on its events
if ( $( '.variations_form' ).length ) {
variation_valid = false;
$( '.variations_form' )
.on( 'show_variation', function( event, form, purchasable ) {
variation_valid = purchasable;
update_button();
} )
.on( 'hide_variation', function() {
variation_valid = false;
update_button();
} );
}
// Disable the button if there are invalid fields in the product page (like required fields from Product Addons)
form.on( 'change', 'select, input, textarea', function() {
// Hack: IE11 uses the previous field value for the checkValidity() check if it's called in the onChange handler
setTimeout( validate_form, 0 );
} );
validate_form();
var generate_cart = function( callback ) {
var data = {
'nonce': wc_ppec_generate_cart_context.generate_cart_nonce,
'attributes': {},
};
var field_pairs = form.serializeArray();
for ( var i = 0; i < field_pairs.length; i++ ) {
// Prevent the default WooCommerce PHP form handler from recognizing this as an "add to cart" call
if ( 'add-to-cart' === field_pairs[ i ].name ) {
field_pairs[ i ].name = 'ppec-add-to-cart';
}
// Save attributes as a separate prop in `data` object,
// so that `attributes` can be used later on when adding a variable product to cart
if ( -1 !== field_pairs[ i ].name.indexOf( 'attribute_' ) ) {
data.attributes[ field_pairs[ i ].name ] = field_pairs[ i ].value;
continue;
}
data[ field_pairs[ i ].name ] = field_pairs[ i ].value;
}
// If this is a simple product, the "Submit" button has the product ID as "value", we need to include it explicitly
data[ 'ppec-add-to-cart' ] = $( '[name=add-to-cart]' ).val();
$.ajax( {
type: 'POST',
data: data,
url: wc_ppec_generate_cart_context.ajaxurl,
success: callback,
} );
};
window.wc_ppec_generate_cart = generate_cart;
// Non-SPB mode click handler, namespaced as 'legacy' as it's replaced by `payment` callback of Button API.
$( '#woo_pp_ec_button_product' ).on( 'click.legacy', function( event ) {
event.preventDefault();
if ( ! button_enabled ) {
return;
}
$( '#woo_pp_ec_button_product' ).trigger( 'disable' );
var href = $(this).attr( 'href' );
generate_cart( function() {
window.location.href = href;
} );
} );
})( jQuery, window, document );

View File

@@ -0,0 +1,17 @@
;(function ( $, window, document ) {
'use strict';
$( 'form.checkout' ).on( 'click', 'input[name="payment_method"]', function() {
// Avoid toggling submit button if on confirmation screen
if ( $( '#payment' ).find( '.wc-gateway-ppec-cancel' ).length ) {
return;
}
var isPPEC = $( this ).is( '#payment_method_ppec_paypal' );
var togglePPEC = isPPEC ? 'show' : 'hide';
var toggleSubmit = isPPEC ? 'hide' : 'show';
$( '#woo_pp_ec_button_checkout' ).animate( { opacity: togglePPEC, height: togglePPEC, padding: togglePPEC }, 230 );
$( '#place_order' ).animate( { opacity: toggleSubmit, height: toggleSubmit, padding: toggleSubmit }, 230 );
} );
})( jQuery, window, document );

View File

@@ -0,0 +1,96 @@
;(function ( $, window, document ) {
'use strict';
var uploadField = {
frames: [],
init: function() {
$( 'button.image_upload' )
.on( 'click', this.onClickUploadButton );
$( 'button.image_remove' )
.on( 'click', this.removeProductImage );
},
onClickUploadButton: function( event ) {
event.preventDefault();
var data = $( event.target ).data();
// If the media frame already exists, reopen it.
if ( 'undefined' !== typeof uploadField.frames[ data.fieldId ] ) {
// Open frame.
uploadField.frames[ data.fieldId ].open();
return false;
}
// Create the media frame.
uploadField.frames[ data.fieldId ] = wp.media( {
title: data.mediaFrameTitle,
button: {
text: data.mediaFrameButton
},
multiple: false // Set to true to allow multiple files to be selected
} );
// When an image is selected, run a callback.
var context = {
fieldId: data.fieldId,
};
uploadField.frames[ data.fieldId ]
.on( 'select', uploadField.onSelectAttachment, context );
// Finally, open the modal.
uploadField.frames[ data.fieldId ].open();
},
onSelectAttachment: function() {
// We set multiple to false so only get one image from the uploader.
var attachment = uploadField.frames[ this.fieldId ]
.state()
.get( 'selection' )
.first()
.toJSON();
var $field = $( '#' + this.fieldId );
var $img = $( '<img />' )
.attr( 'src', getAttachmentUrl( attachment ) );
$field.siblings( '.image-preview-wrapper' )
.html( $img );
$field.val( attachment.id );
$field.siblings( 'button.image_remove' ).show();
$field.siblings( 'button.image_upload' ).hide();
},
removeProductImage: function( event ) {
event.preventDefault();
var $button = $( event.target );
var data = $button.data();
var $field = $( '#' + data.fieldId );
//update fields
$field.val( '' );
$field.siblings( '.image-preview-wrapper' ).html( ' ' );
$button.hide();
$field.siblings( 'button.image_upload' ).show();
},
};
function getAttachmentUrl( attachment ) {
if ( attachment.sizes && attachment.sizes.medium ) {
return attachment.sizes.medium.url;
}
if ( attachment.sizes && attachment.sizes.thumbnail ) {
return attachment.sizes.thumbnail.url;
}
return attachment.url;
}
function run() {
uploadField.init();
}
$( run );
}( jQuery ) );

View File

@@ -0,0 +1,171 @@
/* global wc_ppec_context */
;( function ( $, window, document ) {
'use strict';
// Show error notice at top of checkout form, or else within button container
var showError = function( errorMessage, selector ) {
var $container = $( '.woocommerce-notices-wrapper, form.checkout' );
if ( ! $container || ! $container.length ) {
$( selector ).prepend( errorMessage );
return;
} else {
$container = $container.first();
}
// Adapted from https://github.com/woocommerce/woocommerce/blob/ea9aa8cd59c9fa735460abf0ebcb97fa18f80d03/assets/js/frontend/checkout.js#L514-L529
$( '.woocommerce-NoticeGroup-checkout, .woocommerce-error, .woocommerce-message' ).remove();
$container.prepend( '<div class="woocommerce-NoticeGroup woocommerce-NoticeGroup-checkout">' + errorMessage + '</div>' );
$container.find( '.input-text, select, input:checkbox' ).trigger( 'validate' ).blur();
var scrollElement = $( '.woocommerce-NoticeGroup-checkout' );
if ( ! scrollElement.length ) {
scrollElement = $container;
}
if ( $.scroll_to_notices ) {
$.scroll_to_notices( scrollElement );
} else {
// Compatibility with WC <3.3
$( 'html, body' ).animate( {
scrollTop: ( $container.offset().top - 100 )
}, 1000 );
}
$( document.body ).trigger( 'checkout_error' );
}
// Map funding method settings to enumerated options provided by PayPal.
var getFundingMethods = function( methods ) {
if ( ! methods ) {
return undefined;
}
var paypal_funding_methods = [];
for ( var i = 0; i < methods.length; i++ ) {
var method = paypal.FUNDING[ methods[ i ] ];
if ( method ) {
paypal_funding_methods.push( method );
}
}
return paypal_funding_methods;
}
var render = function( isMiniCart ) {
var prefix = isMiniCart ? 'mini_cart_' : '';
var button_size = wc_ppec_context[ prefix + 'button_size' ];
var button_layout = wc_ppec_context[ prefix + 'button_layout' ];
var button_label = wc_ppec_context[ prefix + 'button_label' ];
var allowed = wc_ppec_context[ prefix + 'allowed_methods' ];
var disallowed = wc_ppec_context[ prefix + 'disallowed_methods' ];
var selector = isMiniCart ? '#woo_pp_ec_button_mini_cart' : '#woo_pp_ec_button_' + wc_ppec_context.page;
var fromCheckout = 'checkout' === wc_ppec_context.page && ! isMiniCart;
// Don't render if already rendered in DOM.
if ( $( selector ).children().length ) {
return;
}
paypal.Button.render( {
env: wc_ppec_context.environment,
locale: wc_ppec_context.locale,
commit: fromCheckout,
funding: {
allowed: getFundingMethods( allowed ),
disallowed: getFundingMethods( disallowed ),
},
style: {
color: wc_ppec_context.button_color,
shape: wc_ppec_context.button_shape,
layout: button_layout,
size: button_size,
label: button_label,
branding: true,
tagline: false,
},
validate: function( actions ) {
// Only enable on variable product page if purchasable variation selected.
$( '#woo_pp_ec_button_product' ).off( '.legacy' )
.on( 'enable', actions.enable )
.on( 'disable', actions.disable );
},
payment: function() {
// Clear any errors from previous attempt.
$( '.woocommerce-error', selector ).remove();
return new paypal.Promise( function( resolve, reject ) {
// First, generate cart if triggered from single product.
if ( 'product' === wc_ppec_context.page && ! isMiniCart ) {
window.wc_ppec_generate_cart( resolve );
} else {
resolve();
}
} ).then( function() {
// Make PayPal Checkout initialization request.
var data = $( selector ).closest( 'form' )
.add( $( '<input type="hidden" name="nonce" /> ' )
.attr( 'value', wc_ppec_context.start_checkout_nonce )
)
.add( $( '<input type="hidden" name="from_checkout" /> ' )
.attr( 'value', fromCheckout ? 'yes' : 'no' )
)
.serialize();
return paypal.request( {
method: 'post',
url: wc_ppec_context.start_checkout_url,
body: data,
} ).then( function( response ) {
if ( ! response.success ) {
var messageItems = response.data.messages.map( function( message ) {
return '<li>' + message + '</li>';
} ).join( '' );
showError( '<ul class="woocommerce-error" role="alert">' + messageItems + '</ul>', selector );
return null;
}
return response.data.token;
} );
} );
},
onAuthorize: function( data, actions ) {
if ( fromCheckout ) {
// Pass data necessary for authorizing payment to back-end.
$( 'form.checkout' )
.append( $( '<input type="hidden" name="paymentToken" /> ' ).attr( 'value', data.paymentToken ) )
.append( $( '<input type="hidden" name="payerID" /> ' ).attr( 'value', data.payerID ) )
.submit();
} else {
// Navigate to order confirmation URL specified in original request to PayPal from back-end.
return actions.redirect();
}
},
}, selector );
};
// Render cart, single product, or checkout buttons.
if ( wc_ppec_context.page ) {
if ( 'checkout' !== wc_ppec_context.page ) {
render();
}
$( document.body ).on( 'updated_cart_totals updated_checkout', render.bind( this, false ) );
}
// Render buttons in mini-cart if present.
$( document.body ).on( 'wc_fragments_loaded wc_fragments_refreshed', function() {
var $button = $( '.widget_shopping_cart #woo_pp_ec_button_mini_cart' );
if ( $button.length ) {
// Clear any existing button in container, and render.
$button.empty();
render( true );
}
} );
} )( jQuery, window, document );

View File

@@ -0,0 +1,243 @@
*** Changelog ***
= 1.6.17 - 2019-08-08 =
* Update - WooCommerce 3.7 compatibility
* Add - Filter to require display of billing agreement during checkout
* Add - Add CURRENCYCODE to capture_payment
* Add - Add filter for buttons on products
* Fix - Skip wasteful render on initial Checkout page load
* Fix - Appearance tweaks on Checkout screen
= 1.6.16 - 2019-07-18 =
* Fix - Don't require address for renewal of virtual subscriptions
* Fix - Avoid broken confirmation screen edge case after 10486 redirect
= 1.6.15 - 2019-06-19 =
* Fix - Prevent PHP errors when no billing details are present in PP response
* Fix - Require billing address for virtual products when enabled
* Add - Hook when a payment error occurs
= 1.6.14 - 2019-05-08 =
* Fix - Failing checkout when no addons are used
= 1.6.12 - 2019-05-08 =
* Fix - Better handling of virtual subscriptions when billing address is not required
* Fix - Prevent errors showing when purchasing a virtual product with WP_DEBUG enabled
= 1.6.11 - 2019-04-17 =
* Fix/Performance - Prevent db option updates during bootstrap on each page load
* Tweak = WC 3.6 compatibiliy.
= 1.6.10 - 2019-03-05 =
* Fix - Use only product attributes when adding to cart
= 1.6.9 - 2019-02-03 =
* Fix - Avoid SPB render error by tweaking 'allowed' funding methods' empty value
= 1.6.8 - 2019-01-25 =
* Fix - Guard against themes applying filter with too few params
= 1.6.7 - 2019-01-25 =
* Fix - Error 10413 when using coupons
* Fix: All variation details when using buttons on product pages are kept
* Fix: Always render the PayPal buttons in the mini cart
= 1.6.6 - 2019-01-09 =
* Fix - Discount items were not being included
* Add - Filter for order details to accept decimal quantities of products
* Fix - Unable to buy variation from product page
* Fix - Can use PayPal from product page without inputting required fields
* Add - Display PayPal fees under the totals on the order admin page
* Add - Prefill name, phone, and email info in PayPal Guest Checkout from checkout screen
= 1.6.5 - 2018-10-31 =
* Fix - Truncate the line item descriptions to avoid exceeding PayPal character limits.
* Update - WC 3.5 compatibility.
* Fix - checkout.js script loading when not needed.
* Fix - Missing shipping total and address when starting from checkout page.
= 1.6.4 - 2018-09-27 =
* Fix - Billing address from Checkout form not being passed to PayPal via Smart Payment Button.
* Fix - Checkout form not being validated until after Smart Payment Button payment flow.
= 1.6.3 - 2018-08-15 =
* Fix - Fatal error caused by a fix for Smart Payment Buttons.
= 1.6.2 - 2018-08-15 =
* Fix - Tax not applied on the (Confirm your PayPal order) page at the checkout.
= 1.6.1 - 2018-07-04 =
* Fix - GDPR Fatal error exporting user data when they have PPEC subscriptions.
* Fix - PayPal Credit still being disabled by default.
* Update - Rename 'PayPal Express Checkout' to 'PayPal Checkout'.
* Fix - Missing PayPal branding in "Buy Now" Smart Payment Button.
* Fix - PHP warning when PayPal Credit not supported and no funding methods hidden.
* Fix - Smart Payment Buttons gateway not inheriting IPN and subscription handling.
* Fix - Single product Smart Payment Button failing without existing session.
* Fix - When cart is empty, JS error on cart page and mini-cart payment buttons showing.
* Add - Locale filter.
= 1.6.0 - 2018-06-27 =
* Add - Smart Payment Buttons mode as alternative to directly embedded image links for all instances of PayPal button.
* Fix - Help tip alignment for image settings.
* Update - Enable PayPal Credit by default, and restrict its support by currency.
* Update - Omit 'Express Checkout' portion of default payment method title.
* Update - Enable Express Checkout on regular checkout page by default.
* Update - Enable Express Checkout on single product page by default.
= 1.5.6 - 2018-06-06 =
* Fix - Virtual products cause issues with billing details validation.
= 1.5.5 - 2018-05-23 =
* Update - WC 3.4 compatibility
* Update - Privacy policy notification.
* Update - Export/erasure hooks added.
= 1.5.4 - 2018-05-08 =
* Add - Hook to make billing address not required `woocommerce_paypal_express_checkout_address_not_required` (bool).
* Fix - Duplicate checkout settings when PP Credit option is enabled.
* Fix - Impossible to open API credentials after saving Settings.
* Fix - Prevent filtering if PPEC is not enabled.
* Fix - Single Product checkout: Quantity being duplicated due to multiple AJAX calls.
* Fix - When returning from PayPal, place order buttons says "proceed to payment".
* Tweak - Default billing address to be required.
= 1.5.3 - 2018-03-28 =
* Fix - wp_enqueue_media was not correctly loaded causing weird behavior with other parts of system wanting to use it.
* Fix - Typo in activation hook.
= 1.5.2 - 2018-02-20 =
* Tweak - Express checkout shouldn't display "Review your order before the payment".
* Fix - Compatibility with Subscriptions and Checkout from Single Product page.
* Fix - Make sure session object exists before use to prevent fatal error.
= 1.5.1 =
* Add - Hooks for Settings.
* Fix - Missing Settings link on Plugins page.
* Fix - Use correct image URL for PayPal image logo.
* Tweak - Default to signature method if certificate missing, rather than other way around.
= 1.5.0 =
* Add - PayPal credit is now available on checkout.
* Fix - WC 3.3 compatibility.
* Add - Ability to select existing / upload new image(s) for logo / header fields.
* Fix - Shipping address overridden when PayPal returns billing address.
= 1.4.7 =
* Fix - Issue with missing PayPal session information.
* Fix - Dependency error when using LibreSSL.
* Fix - Additional compatibility with shipping plugins
* Fix - Issue where deprecated `WC_Cart::get_cart_url` is being used.
* Tweak - Makes admin notification dismissible.
= 1.4.6 =
* Fix - Coupon related PayPal error 10413.
= 1.4.5 =
* Fix - Title/Description fields in the settings should appear based on Enable PayPal Express Checkout.
* Add - Invoice Prefix now has the ability to be empty.
* Fix - Additional compatibility fixes for line items.
* Fix - PHP notice for Subscription id.
= 1.4.4 =
* Fix - PayPal error (10431).
* Fix - PHP notices.
= 1.4.3 =
* Fix - Refunds not working on authorize then captured transactions.
* Fix - Checkout on single product available before variations are chosen.
* Fix - Not Returning PayPal Transaction Fee.
* Fix - 10431 (Item Amount Invalid at Checkout) error with discounts.
* Fix - Phone not returned and "Require Phone Number" setting not working.
= 1.4.2 =
* Fix - _paypal_status on Authorize transactions not updating to processing after capture.
* Fix - 10413 (The totals of the cart item amounts do not match order amounts) error with discounts.
* Fix - Shipping Address being required on Virtual products.
= 1.4.1 =
* Fix - Properly calculate whether Billing phone is required or not.
* Fix - Set NOSHIPPING based on product shipping requiredness (e.g. virtual products do not need shipping, etc).
= 1.4.0 =
* Tweak - Use shipping discount instead of tax when adjustment negative.
* Fix - Cannot process refunds on "authorize" transactions.
* Add - Option for displaying express checkout button on the product page.
* Fix - If there are no shipping options in WooCommerce, PayPal doesn't pass a shipping address to WC.
* Add - Option to set Billing phone number mandatory.
* Add - Option to disable checkout with PayPal button on Cart page.
* Fix - Trigger required shipping cost before checkout.
= 1.3.0 =
* Fix - Fatal Error calling is_main_query.
* Fix - Customer invoice email doesn't allow payment with PPEC.
* Fix - Double stock reduction.
* Fix - Payment automatically goes to complete when payment action set to Authorize.
= 1.2.1 =
* Fix - Avoid plugin links notice when WooCommerce is not active - props rellect
* Fix - Do not show this gateway when the cart amount is zero
* Fix - Fix 10413 error that prevents checking out with a coupon
* Fix - Filter default address fields to ensure they are not required
= 1.2.0 =
* Fix - Prevent conflict with other gateways.
* Fix - Compatibility with WooCommerce 3.0, including ensuring the customer address is saved correctly.
= 1.1.3 =
* Fix - Guest users can checkout without giving shipping information when required.
* Fix - Modal popup not working properly. Changed to full page redirect with a hook to add back the modal/popup.
* Tweak - Guest checkout is on by default. Should be turned off by using this filter: woocommerce_paypal_express_checkout_allow_guests.
= 1.1.2 =
* Fix - Make sure translations are loaded properly.
* Fix - Added IPN (Instant Payment Notification) handler.
* Fix - Make sure guest payment is enabled by default.
= 1.1.1 =
* Fixed fatal error prior to PHP 5.5 caused by passing empty() a non-variables.
= 1.1.0 =
* Improved flow after express checkout by removing billing and shipping fields and simply allowing shipping method selection.
* Fix - Fixed in-context checkout to work after ajax cart reload.
* Fix - Added missing 'large' button size.
* Fix - Prevent double stock reduction when payment complete.
* Fix - Allow PPE from pay page and don't use in-context checkout for PayPal Mark on checkout.
* Fix - Increase timeout to 30 to prevent error #3.
* Tweak - If the store owner decides to enable PayPal standard, respect that decision and remove EC from checkout screen.
* Tweak - Change place order button to "continue to payment".
* Tweak - Moved default button location to woocommerce_proceed_to_checkout hook.
* Tweak - Improved button appearance and look alongside regular checkout button.
= 1.0.4 =
* Fix - Wrong section slug / tab after redirected from connect.woocommerce.com
* Fix - Make sure to check if credentials were set in cart and checkout pages
* Fix - Removed configuration of chipers to use for TLS
= 1.0.3 =
* Fix - Issue where missing rounding two decimal digits of tax causing transaction being refused
* Fix - Issue where custom logo image URL is not saved
= 1.0.2 =
* Fix - Strip out HTML tags from item descriptions to prevent warning from PayPal
* Fix - Issue of incorrect plugin's setting link from admin plugins page when using WooCommerce 2.6
* Tweak - Make enabled option to default to true
* Fix - Issue of missing help icon when plugin directory is not the same as plugin's slug.
* Tweak - Add admin notice to setup / connect after plugin is activated.
= 1.0.1 =
* Fix - Make sure OpenSSL is installed with 1.0.1 as the minimum required version, otherwise display warning
* Fix - Make sure cURL transport is available for WP HTTP API, otherwise display warning
* Fix - Unhandled certificate-style API credential
* Fix - Fixed calculated tax and coupons data that sent over to PayPal
* Fix - Fixed calculated shipping discount data that sent over to PayPal
= 1.0.0 =
* Initial stable release
= 0.2.0 =
* Fix - Add cancel link on checkout page when session for PPEC is active
* Fix - In-context mini browser keeps spinning because failure xhr response is not handled properly
= 0.1.0 =
* Beta release

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,579 @@
<?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 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'] ) && 'yes' !== $this->get_option( 'use_spb' ) ) {
$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_order( $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(
'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 {
do_action( 'wc_gateway_ppec_process_payment_error', $e, $order );
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">
<label for="<?php echo esc_attr( $field_key ); ?>"><?php echo wp_kses_post( $data['title'] ); ?> <?php echo $this->get_tooltip_html( $data ); ?></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,772 @@
<?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,
$prefix . 'PHONENUM' => $this->_phoneNumber,
);
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;
}
/*
* Checks to see if the PayPal_Address object has all the
* required parameters when using a shipping address.
*
* The required parameters for a DoReferenceTransaction call are listed here:
* https://developer.paypal.com/docs/classic/api/merchant/DoReferenceTransaction-API-Operation-NVP/#ship-to-address-fields
*
* Other API operations have the same shipping parameter requirements.
* Here's a non-exhaustive list:
*
* DoExpressCheckoutPayment
* https://developer.paypal.com/docs/classic/api/merchant/DoExpressCheckoutPayment-API-Operation-NVP/
*
* SetExpressCheckout
* https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout-API-Operation-NVP/
*
* GetExpressCheckoutDetails
* https://developer.paypal.com/docs/classic/api/merchant/GetExpressCheckoutDetails-API-Operation-NVP/
*/
public function has_all_required_shipping_params() {
$has_name = ! empty( $this->getName() );
$has_street1 = ! empty( $this->getStreet1() );
$has_city = ! empty( $this->getCity() );
$has_country = ! empty( $this->getCountry() );
$has_zip = ! empty( $this->getZip() );
$has_state = ! empty( $this->getState() );
// If the country is the US, a zipcode is required
$has_zip_if_required = (
'US' === $this->getCountry()
? $has_zip
: true
);
// A state is required is the country is one of
// Argentina, Brazil, Canada, China, Indonesia,
// India, Japan, Mexico, Thailand or USA
$has_state_if_required = (
in_array(
$this->getCountry(),
array(
'AR', // Argentina
'BR', // Brazil
'CA', // Canada
'CN', // China
'ID', // Indonesia
'IN', // India
'JP', // Japan
'MX', // Mexico
'TH', // Thailand
'US', // USA
),
true
)
? $has_state
: true
);
return (
$has_name
&& $has_street1
&& $has_city
&& $has_country
&& $has_zip_if_required
&& $has_state_if_required
);
}
}

View File

@@ -0,0 +1,351 @@
<?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_processing', array( $this, 'capture_payment' ) );
add_action( 'woocommerce_order_status_completed', array( $this, 'capture_payment' ) );
add_action( 'woocommerce_order_status_cancelled', array( $this, 'cancel_authorization' ) );
add_action( 'woocommerce_order_status_refunded', array( $this, 'cancel_authorization' ) );
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' ) );
add_action( 'woocommerce_admin_order_totals_after_total', array( $this, 'display_order_fee_and_payout' ) );
}
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 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['CURRENCYCODE'] = $old_wc ? $order->order_currency : $order->get_currency();
$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 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 (if one is present)
*
* @param int $order_id
*/
public function cancel_authorization( $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 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() );
}
/**
* Displays the PayPal fee and the net total of the transaction without the PayPal charges
*
* @since 1.6.6
*
* @param int $order_id
*/
public function display_order_fee_and_payout( $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();
$paypal_fee = wc_gateway_ppec_get_transaction_fee( $order );
$order_currency = $old_wc ? $order->order_currency : $order->get_currency();
$order_total = $old_wc ? $order->order_total : $order->get_total();
if ( 'ppec_paypal' !== $payment_method || ! is_numeric( $paypal_fee ) ) {
return;
}
$net = $order_total - $paypal_fee;
?>
<tr>
<td class="label ppec-fee">
<?php echo wc_help_tip( __( 'This represents the fee PayPal collects for the transaction.', 'woocommerce-gateway-paypal-express-checkout' ) ); ?>
<?php esc_html_e( 'PayPal Fee:', 'woocommerce-gateway-paypal-express-checkout' ); ?>
</td>
<td width="1%"></td>
<td class="total">
-&nbsp;<?php echo wc_price( $paypal_fee, array( 'currency' => $order_currency ) ); ?>
</td>
</tr>
<tr>
<td class="label ppec-payout">
<?php echo wc_help_tip( __( 'This represents the net total that will be credited to your PayPal account. This may be in a different currency than is set in your PayPal account.', 'woocommerce-gateway-paypal-express-checkout' ) ); ?>
<?php esc_html_e( 'PayPal Payout:', 'woocommerce-gateway-paypal-express-checkout' ); ?>
</td>
<td width="1%"></td>
<td class="total">
<?php echo wc_price( $net, array( 'currency' => $order_currency ) ); ?>
</td>
</tr>
<?php
}
}

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,543 @@
<?php
/**
* Cart handler.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WC_Gateway_PPEC_Cart_Handler handles button display in the frontend.
*/
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_proceed_to_checkout', array( $this, 'display_paypal_button' ), 20 );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
if ( 'yes' === wc_gateway_ppec()->settings->use_spb ) {
add_action( 'woocommerce_after_mini_cart', array( $this, 'display_mini_paypal_button' ), 20 );
} else {
add_action( 'woocommerce_widget_shopping_cart_buttons', array( $this, 'display_mini_paypal_button' ), 20 );
}
add_action( 'widget_title', array( $this, 'maybe_enqueue_checkout_js' ), 10, 3 );
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( 'wp', array( $this, 'ensure_session' ) ); // Ensure there is a customer session so that nonce is not invalidated by new session created on AJAX POST request.
}
add_action( 'wc_ajax_wc_ppec_update_shipping_costs', array( $this, 'wc_ajax_update_shipping_costs' ) );
add_action( 'wc_ajax_wc_ppec_start_checkout', array( $this, 'wc_ajax_start_checkout' ) );
}
/**
* 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 PayPal Checkout on a product level.
* TODO: Why not let the default "add-to-cart" PHP form handler insert the product into the cart? Investigate.
*
* @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['ppec-add-to-cart'] ) ) {
$product = wc_get_product( absint( $_POST['ppec-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['quantity'] ) ? 1 : absint( $_POST['quantity'] );
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() );
}
/**
* Handle AJAX request to start checkout flow, first triggering form validation if necessary.
*
* @since 1.6.0
*/
public function wc_ajax_start_checkout() {
if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_ppec_start_checkout_nonce' ) ) {
wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-paypal-express-checkout' ) );
}
if ( isset( $_POST['from_checkout'] ) && 'yes' === $_POST['from_checkout'] ) {
// Intercept process_checkout call to exit after validation.
add_action( 'woocommerce_after_checkout_validation', array( $this, 'maybe_start_checkout' ), 10, 2 );
WC()->checkout->process_checkout();
} else {
$this->start_checkout( true );
}
}
/**
* Report validation errors if any, or else save form data in session and proceed with checkout flow.
*
* @since 1.6.4
*/
public function maybe_start_checkout( $data, $errors = null ) {
if ( is_null( $errors ) ) {
// Compatibility with WC <3.0: get notices and clear them so they don't re-appear.
$error_messages = wc_get_notices( 'error' );
wc_clear_notices();
} else {
$error_messages = $errors->get_error_messages();
}
if ( empty( $error_messages ) ) {
$this->set_customer_data( $_POST );
$this->start_checkout( false );
} else {
wp_send_json_error( array( 'messages' => $error_messages ) );
}
exit;
}
/**
* Set Express Checkout and return token in response.
*
* @param bool $skip_checkout Whether checkout screen is being bypassed.
*
* @since 1.6.4
*/
protected function start_checkout( $skip_checkout ) {
try {
wc_gateway_ppec()->checkout->start_checkout_from_cart( $skip_checkout );
wp_send_json_success( array( 'token' => WC()->session->paypal->token ) );
} catch( PayPal_API_Exception $e ) {
wp_send_json_error( array( 'messages' => array( $e->getMessage() ) ) );
}
}
/**
* Store checkout form data in customer session.
*
* @since 1.6.4
*/
protected function set_customer_data( $data ) {
$customer = WC()->customer;
$billing_first_name = empty( $data[ 'billing_first_name' ] ) ? '' : wc_clean( $data[ 'billing_first_name' ] );
$billing_last_name = empty( $data[ 'billing_last_name' ] ) ? '' : wc_clean( $data[ 'billing_last_name' ] );
$billing_country = empty( $data[ 'billing_country' ] ) ? '' : wc_clean( $data[ 'billing_country' ] );
$billing_address_1 = empty( $data[ 'billing_address_1' ] ) ? '' : wc_clean( $data[ 'billing_address_1' ] );
$billing_address_2 = empty( $data[ 'billing_address_2' ] ) ? '' : wc_clean( $data[ 'billing_address_2' ] );
$billing_city = empty( $data[ 'billing_city' ] ) ? '' : wc_clean( $data[ 'billing_city' ] );
$billing_state = empty( $data[ 'billing_state' ] ) ? '' : wc_clean( $data[ 'billing_state' ] );
$billing_postcode = empty( $data[ 'billing_postcode' ] ) ? '' : wc_clean( $data[ 'billing_postcode' ] );
$billing_phone = empty( $data[ 'billing_phone' ] ) ? '' : wc_clean( $data[ 'billing_phone' ] );
$billing_email = empty( $data[ 'billing_email' ] ) ? '' : wc_clean( $data[ 'billing_email' ] );
if ( isset( $data['ship_to_different_address'] ) ) {
$shipping_first_name = empty( $data[ 'shipping_first_name' ] ) ? '' : wc_clean( $data[ 'shipping_first_name' ] );
$shipping_last_name = empty( $data[ 'shipping_last_name' ] ) ? '' : wc_clean( $data[ 'shipping_last_name' ] );
$shipping_country = empty( $data[ 'shipping_country' ] ) ? '' : wc_clean( $data[ 'shipping_country' ] );
$shipping_address_1 = empty( $data[ 'shipping_address_1' ] ) ? '' : wc_clean( $data[ 'shipping_address_1' ] );
$shipping_address_2 = empty( $data[ 'shipping_address_2' ] ) ? '' : wc_clean( $data[ 'shipping_address_2' ] );
$shipping_city = empty( $data[ 'shipping_city' ] ) ? '' : wc_clean( $data[ 'shipping_city' ] );
$shipping_state = empty( $data[ 'shipping_state' ] ) ? '' : wc_clean( $data[ 'shipping_state' ] );
$shipping_postcode = empty( $data[ 'shipping_postcode' ] ) ? '' : wc_clean( $data[ 'shipping_postcode' ] );
} else {
$shipping_first_name = $billing_first_name;
$shipping_last_name = $billing_last_name;
$shipping_country = $billing_country;
$shipping_address_1 = $billing_address_1;
$shipping_address_2 = $billing_address_2;
$shipping_city = $billing_city;
$shipping_state = $billing_state;
$shipping_postcode = $billing_postcode;
}
$customer->set_shipping_country( $shipping_country );
$customer->set_shipping_address( $shipping_address_1 );
$customer->set_shipping_address_2( $shipping_address_2 );
$customer->set_shipping_city( $shipping_city );
$customer->set_shipping_state( $shipping_state );
$customer->set_shipping_postcode( $shipping_postcode );
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
$customer->shipping_first_name = $shipping_first_name;
$customer->shipping_last_name = $shipping_last_name;
$customer->billing_first_name = $billing_first_name;
$customer->billing_last_name = $billing_last_name;
$customer->set_country( $billing_country );
$customer->set_address( $billing_address_1 );
$customer->set_address_2( $billing_address_2 );
$customer->set_city( $billing_city );
$customer->set_state( $billing_state );
$customer->set_postcode( $billing_postcode );
$customer->billing_phone = $billing_phone;
$customer->billing_email = $billing_email;
} else {
$customer->set_shipping_first_name( $shipping_first_name );
$customer->set_shipping_last_name( $shipping_last_name );
$customer->set_billing_first_name( $billing_first_name );
$customer->set_billing_last_name( $billing_last_name );
$customer->set_billing_country( $billing_country );
$customer->set_billing_address_1( $billing_address_1 );
$customer->set_billing_address_2( $billing_address_2 );
$customer->set_billing_city( $billing_city );
$customer->set_billing_state( $billing_state );
$customer->set_billing_postcode( $billing_postcode );
$customer->set_billing_phone( $billing_phone );
$customer->set_billing_email( $billing_email );
}
}
/**
* 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;
}
if ( apply_filters( 'woocommerce_paypal_express_checkout_hide_button_on_product_page', false ) ) {
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">
<?php if ( 'yes' === $settings->use_spb ) :
wp_enqueue_script( 'wc-gateway-ppec-smart-payment-buttons' ); ?>
<div id="woo_pp_ec_button_product"></div>
<?php else : ?>
<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>
<?php endif; ?>
</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; ?>
<?php if ( 'yes' === $settings->use_spb ) :
wp_enqueue_script( 'wc-gateway-ppec-smart-payment-buttons' ); ?>
<div id="woo_pp_ec_button_cart"></div>
<?php else : ?>
<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; ?>
<?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;
}
?>
<?php if ( 'yes' === $settings->use_spb ) : ?>
<p class="woocommerce-mini-cart__buttons buttons wcppec-cart-widget-spb">
<span id="woo_pp_ec_button_mini_cart"></span>
</p>
<?php else : ?>
<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 endif; ?>
<?php
}
public function maybe_enqueue_checkout_js( $widget_title, $widget_instance = array(), $widget_id = null ) {
if ( 'woocommerce_widget_cart' === $widget_id ) {
$gateways = WC()->payment_gateways->get_available_payment_gateways();
$settings = wc_gateway_ppec()->settings;
if ( isset( $gateways['ppec_paypal'] ) && 'yes' === $settings->cart_checkout_enabled && 'yes' === $settings->use_spb ) {
wp_enqueue_script( 'wc-gateway-ppec-smart-payment-buttons' );
}
}
return $widget_title;
}
/**
* Convert from settings to values expected by PayPal Button API:
* - 'small' button size only allowed if layout is 'vertical'.
* - 'label' only allowed if layout is 'vertical'.
* - 'disallowed' funding methods if layout is 'vertical'.
* - 'allowed' funding methods if layout is 'horizontal'.
* - Only allow PayPal Credit if supported.
*
* @param array Raw settings.
*
* @return array Same array adapted to include data suitable for client-side rendering.
*
* @since 1.6.0
*/
protected function get_button_settings( $settings, $context = '' ) {
$prefix = $context ? $context . '_' : $context;
$data = array(
'button_layout' => $settings->{ $prefix . 'button_layout' },
'button_size' => $settings->{ $prefix . 'button_size' },
'hide_funding_methods' => $settings->{ $prefix . 'hide_funding_methods' },
'credit_enabled' => $settings->{ $prefix . 'credit_enabled' },
);
$button_layout = $data['button_layout'];
$data['button_label'] = 'horizontal' === $button_layout ? 'buynow' : null;
$data['button_size'] = 'vertical' === $button_layout && 'small' === $data['button_size']
? 'medium'
: $data['button_size'];
if ( ! wc_gateway_ppec_is_credit_supported() ) {
$data['credit_enabled'] = 'no';
if ( ! is_array( $data['hide_funding_methods'] ) ) {
$data['hide_funding_methods'] = array( 'CREDIT' );
} elseif ( ! in_array( 'CREDIT', $data['hide_funding_methods'] ) ) {
$data['hide_funding_methods'][] = 'CREDIT';
}
}
if ( 'vertical' === $button_layout ) {
$data['disallowed_methods'] = $data['hide_funding_methods'];
} else {
$data['allowed_methods'] = 'yes' === $data['credit_enabled'] ? array( 'CREDIT' ) : array();
}
unset( $data['hide_funding_methods'], $data['credit_enabled'] );
return $data;
}
/**
* Frontend scripts
*/
public function enqueue_scripts() {
$settings = wc_gateway_ppec()->settings;
$client = wc_gateway_ppec()->client;
wp_enqueue_style( 'wc-gateway-ppec-frontend', wc_gateway_ppec()->plugin_url . 'assets/css/wc-gateway-ppec-frontend.css' );
$is_cart = is_cart() && ! WC()->cart->is_empty() && 'yes' === $settings->cart_checkout_enabled;
$is_product = is_product() && 'yes' === $settings->checkout_on_single_product_enabled;
$is_checkout = is_checkout() && 'yes' === $settings->mark_enabled && ! wc_gateway_ppec()->checkout->has_active_session();
$page = $is_cart ? 'cart' : ( $is_product ? 'product' : ( $is_checkout ? 'checkout' : null ) );
if ( 'yes' !== $settings->use_spb && $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' ),
)
);
} elseif ( 'yes' === $settings->use_spb ) {
wp_register_script( 'paypal-checkout-js', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
wp_register_script( 'wc-gateway-ppec-smart-payment-buttons', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-smart-payment-buttons.js', array( 'jquery', 'paypal-checkout-js' ), wc_gateway_ppec()->version, true );
$data = array(
'environment' => 'sandbox' === $settings->get_environment() ? 'sandbox' : 'production',
'locale' => $settings->get_paypal_locale(),
'page' => $page,
'button_color' => $settings->button_color,
'button_shape' => $settings->button_shape,
'start_checkout_nonce' => wp_create_nonce( '_wc_ppec_start_checkout_nonce' ),
'start_checkout_url' => WC_AJAX::get_endpoint( 'wc_ppec_start_checkout' ),
);
if ( ! is_null( $page ) ) {
if ( 'product' === $page && 'yes' === $settings->single_product_settings_toggle ) {
$button_settings = $this->get_button_settings( $settings, 'single_product' );
} elseif ( 'checkout' === $page && 'yes' === $settings->mark_settings_toggle ) {
$button_settings = $this->get_button_settings( $settings, 'mark' );
} else {
$button_settings = $this->get_button_settings( $settings );
}
$data = array_merge( $data, $button_settings );
}
$settings_toggle = 'yes' === $settings->mini_cart_settings_toggle;
$mini_cart_data = $this->get_button_settings( $settings, $settings_toggle ? 'mini_cart' : '' );
foreach( $mini_cart_data as $key => $value ) {
unset( $mini_cart_data[ $key ] );
$mini_cart_data[ 'mini_cart_' . $key ] = $value;
}
$data = array_merge( $data, $mini_cart_data );
wp_localize_script( 'wc-gateway-ppec-smart-payment-buttons', 'wc_ppec_context', $data );
}
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_generate_cart_context',
array(
'generate_cart_nonce' => wp_create_nonce( '_wc_ppec_generate_cart_nonce' ),
'ajaxurl' => WC_AJAX::get_endpoint( 'wc_ppec_generate_cart' ),
)
);
}
}
/**
* Creates a customer session if one is not already active.
*/
public function ensure_session() {
$frontend = ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! defined( 'REST_REQUEST' );
if ( ! $frontend ) {
return;
}
if ( ! WC()->session->has_session() ) {
WC()->session->set_customer_session_cookie( true );
}
}
/**
* @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,521 @@
<?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 ) ) {
wc_gateway_ppec_log( sprintf( 'PayPal response did not include the payer billing details: %s', print_r( $getECResponse, true ) ) );
$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,72 @@
<?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' );
require_once( $includes_path . 'class-wc-gateway-ppec-with-spb.php' );
require_once( $includes_path . 'class-wc-gateway-ppec-with-spb-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 ( 'yes' === $settings->use_spb ) {
if ( $this->can_use_addons() ) {
$methods[] = 'WC_Gateway_PPEC_With_SPB_Addons';
} else {
$methods[] = 'WC_Gateway_PPEC_With_SPB';
}
return $methods;
}
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,359 @@
<?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 ) {
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
$order_id = $old_wc ? $order->id : $order->get_id();
if ( $order->has_status( array( '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.
wc_gateway_ppec_set_transaction_fee( $order, $posted_data['mc_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 ( ! $old_wc ) {
$order->save_meta_data();
}
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,483 @@
<?php
/**
* PayPal 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 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;
} catch ( Exception $e ) {
if ( in_array( $e->getCode(), array( self::ALREADY_BOOTSTRAPED, self::DEPENDENCIES_UNSATISFIED ) ) ) {
$this->bootstrap_warning_message = $e->getMessage();
}
if ( self::NOT_CONNECTED === $e->getCode() ) {
$this->prompt_to_connect = $e->getMessage();
}
add_action( 'admin_notices', array( $this, 'show_bootstrap_warning' ) );
}
}
public function show_bootstrap_warning() {
$dependencies_message = isset( $this->bootstrap_warning_message ) ? $this->bootstrap_warning_message : null;
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 esc_url( 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 = isset( $this->prompt_to_connect ) ? $this->prompt_to_connect : null;
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 esc_url( 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
}
}
public function show_spb_notice() {
// Should only show when PPEC is enabled but not in SPB mode.
if ( 'yes' !== $this->settings->enabled || 'yes' === $this->settings->use_spb ) {
return;
}
// Should only show on WooCommerce screens, the main dashboard, and on the plugins screen (as in WC_Admin_Notices).
$screen = get_current_screen();
$screen_id = $screen ? $screen->id : '';
if ( ! in_array( $screen_id, wc_get_screen_ids(), true ) && 'dashboard' !== $screen_id && 'plugins' !== $screen_id ) {
return;
}
if ( 'yes' !== get_option( 'wc_gateway_ppec_spb_notice_dismissed', 'no' ) ) {
$setting_link = $this->get_admin_setting_link();
$message = sprintf( __( '<p>PayPal&nbsp;Checkout with new <strong>Smart&nbsp;Payment&nbsp;Buttons™</strong> gives your customers the power to pay the way they want without leaving your site.</p><p>The <strong>existing buttons will be deprecated and removed</strong> in future releases. Upgrade to Smart&nbsp;Payment&nbsp;Buttons in the <a href="%s">PayPal&nbsp;Checkout settings</a>.</p>', 'woocommerce-gateway-paypal-express-checkout' ), esc_url( $setting_link ) );
?>
<div class="notice notice-warning is-dismissible ppec-dismiss-spb-notice">
<?php echo wp_kses( $message, array( 'a' => array( 'href' => array() ), 'strong' => array(), 'p' => array() ) ); ?>
</div>
<script>
( function( $ ) {
$( '.ppec-dismiss-spb-notice' ).on( 'click', '.notice-dismiss', function() {
jQuery.post( "<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>", {
action: "ppec_dismiss_notice_message",
dismiss_action: "ppec_dismiss_spb_notice",
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;
case 'ppec_dismiss_spb_notice':
update_option( 'wc_gateway_ppec_spb_notice_dismissed', 'yes' );
break;
}
wp_die();
}
/**
* Check dependencies.
*
* @throws Exception
*/
protected function _check_dependencies() {
if ( ! function_exists( 'WC' ) ) {
throw new Exception( __( 'WooCommerce Gateway PayPal 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 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 Checkout requires cURL to be installed on your server', 'woocommerce-gateway-paypal-express-checkout' ), self::DEPENDENCIES_UNSATISFIED );
}
$openssl_warning = __( 'WooCommerce Gateway PayPal 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 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();
add_action( 'admin_notices', array( $this, 'show_spb_notice' ) );
}
/**
* 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 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( $subscription->get_id(), '_woo_pp_txnData', true ) ),
),
array(
'name' => __( 'PPEC Billing agreement id', 'woocommerce-gateway-paypal-express-checkout' ),
'value' => get_post_meta( $subscription->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 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 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 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( 'skip_checkout' => true ) );
}
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(
'skip_checkout' => false,
) );
}
/**
* 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 apply_filters( 'woocommerce_paypal_express_checkout_paypal_locale', $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,61 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Duplicate of implementation in WC_Gateway_PPEC_With_SPB.
*/
class WC_Gateway_PPEC_With_SPB_Addons extends WC_Gateway_PPEC_With_PayPal_Addons {
public function __construct() {
parent::__construct();
add_action( 'woocommerce_review_order_after_submit', array( $this, 'display_paypal_button' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
}
/**
* Display PayPal button on the checkout page order review.
*/
public function display_paypal_button() {
wp_enqueue_script( 'wc-gateway-ppec-smart-payment-buttons' );
?>
<div id="woo_pp_ec_button_checkout"></div>
<?php
}
/**
* Output script for conditionally showing Smart Payment Buttons on regular checkout.
*
* @since 1.6.0
*/
public function payment_scripts() {
if ( ! wc_gateway_ppec()->checkout->has_active_session() ) {
wp_enqueue_script( 'wc-gateway-ppec-order-review', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-order-review.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
}
}
/**
* Save data necessary for authorizing payment to session, in order to
* go ahead with processing payment and bypass redirecting to PayPal.
*
* @param int $order_id Order ID
*
* @return array
*/
public function process_payment( $order_id ) {
if ( isset( $_POST['payerID'] ) && isset( $_POST['paymentToken'] ) ) {
$session = WC()->session->get( 'paypal' );
$session->checkout_completed = true;
$session->payer_id = $_POST['payerID'];
$session->token = $_POST['paymentToken'];
WC()->session->set( 'paypal', $session );
}
return parent::process_payment( $order_id );
}
}

View File

@@ -0,0 +1,61 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Implementation is duplicated in WC_Gateway_PPEC_With_SPB.
*/
class WC_Gateway_PPEC_With_SPB extends WC_Gateway_PPEC_With_PayPal {
public function __construct() {
parent::__construct();
add_action( 'woocommerce_review_order_after_submit', array( $this, 'display_paypal_button' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
}
/**
* Display PayPal button on the checkout page order review.
*/
public function display_paypal_button() {
wp_enqueue_script( 'wc-gateway-ppec-smart-payment-buttons' );
?>
<div id="woo_pp_ec_button_checkout"></div>
<?php
}
/**
* Output script for conditionally showing Smart Payment Buttons on regular checkout.
*
* @since 1.6.0
*/
public function payment_scripts() {
if ( ! wc_gateway_ppec()->checkout->has_active_session() ) {
wp_enqueue_script( 'wc-gateway-ppec-order-review', wc_gateway_ppec()->plugin_url . 'assets/js/wc-gateway-ppec-order-review.js', array( 'jquery' ), wc_gateway_ppec()->version, true );
}
}
/**
* Save data necessary for authorizing payment to session, in order to
* go ahead with processing payment and bypass redirecting to PayPal.
*
* @param int $order_id Order ID
*
* @return array
*/
public function process_payment( $order_id ) {
if ( isset( $_POST['payerID'] ) && isset( $_POST['paymentToken'] ) ) {
$session = WC()->session->get( 'paypal' );
$session->checkout_completed = true;
$session->payer_id = $_POST['payerID'];
$session->token = $_POST['paymentToken'];
WC()->session->set( 'paypal', $session );
}
return parent::process_payment( $order_id );
}
}

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,156 @@
<?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'] && 'USD' === get_woocommerce_currency();
}
/**
* 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'];
}
const PPEC_FEE_META_NAME_OLD = 'PayPal Transaction Fee';
const PPEC_FEE_META_NAME_NEW = '_paypal_transaction_fee';
/**
* Sets the PayPal Fee in the order metadata
*
* @since 1.6.6
*
* @param object $order Order to modify
* @param string $fee Fee to save
*/
function wc_gateway_ppec_set_transaction_fee( $order, $fee ) {
if ( empty( $fee ) ) {
return;
}
$fee = wc_clean( $fee );
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
update_post_meta( $order->id, PPEC_FEE_META_NAME_NEW, $fee );
} else {
$order->update_meta_data( PPEC_FEE_META_NAME_NEW, $fee );
$order->save_meta_data();
}
}
/**
* Gets the PayPal Fee from the order metadata, migrates if the fee was saved under a legacy key
*
* @since 1.6.6
*
* @param object $order Order to read
* @return string Returns the fee or an empty string if the fee has not been set on the order
*/
function wc_gateway_ppec_get_transaction_fee( $order ) {
$old_wc = version_compare( WC_VERSION, '3.0', '<' );
//retrieve the fee using the new key
if ( $old_wc ) {
$fee = get_post_meta( $order->id, PPEC_FEE_META_NAME_NEW, true );
} else {
$fee = $order->get_meta( PPEC_FEE_META_NAME_NEW, true );
}
//if the fee was found, return
if ( is_numeric( $fee ) ) {
return $fee;
}
//attempt to retrieve the old meta, delete its old key, and migrate it to the new one
if ( $old_wc ) {
$fee = get_post_meta( $order->id, PPEC_FEE_META_NAME_OLD, true );
delete_post_meta( $order->id, PPEC_FEE_META_NAME_OLD );
} else {
$fee = $order->get_meta( PPEC_FEE_META_NAME_OLD, true );
$order->delete_meta_data( PPEC_FEE_META_NAME_OLD );
$order->save_meta_data();
}
if ( is_numeric( $fee ) ) {
wc_gateway_ppec_set_transaction_fee( $order, $fee );
}
return $fee;
}

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,693 @@
<?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 using USD currency.', 'woocommerce-gateway-paypal-express-checkout' ) . '</em></p>';
}
$credit_enabled_description = __( 'This enables PayPal Credit, which displays a PayPal Credit button next to the primary PayPal Checkout button. PayPal 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®. (Should be unchecked for stores involved in Real Money Gaming.)', 'woocommerce-gateway-paypal-express-checkout' );
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();
} );
}
$( '.woocommerce_ppec_paypal_button_layout' ).change( function( event ) {
if ( ! $( '#woocommerce_ppec_paypal_use_spb' ).is( ':checked' ) ) {
return;
}
// Show settings that pertain to selected layout in same section
var isVertical = 'vertical' === $( event.target ).val();
var table = $( event.target ).closest( 'table' );
table.find( '.woocommerce_ppec_paypal_vertical' ).closest( 'tr' ).toggle( isVertical );
table.find( '.woocommerce_ppec_paypal_horizontal' ).closest( 'tr' ).toggle( ! isVertical );
// Disable 'small' button size option in vertical layout only
var button_size = table.find( '.woocommerce_ppec_paypal_button_size' );
var button_size_option = button_size.find( 'option[value=\"small\"]' );
if ( button_size_option.prop( 'disabled' ) !== isVertical ) {
button_size.removeClass( 'enhanced' )
button_size_option.prop( 'disabled', isVertical );
$( document.body ).trigger( 'wc-enhanced-select-init' );
! button_size.val() && button_size.val( 'responsive' ).change();
}
} ).change();
// Hide default layout and size settings if they'll be overridden anyway.
function showHideDefaultButtonSettings() {
var display =
$( '#woocommerce_ppec_paypal_cart_checkout_enabled' ).is( ':checked' ) ||
( $( '#woocommerce_ppec_paypal_checkout_on_single_product_enabled' ).is( ':checked' ) && ! $( '#woocommerce_ppec_paypal_single_product_settings_toggle' ).is( ':checked' ) ) ||
( $( '#woocommerce_ppec_paypal_mark_enabled' ).is( ':checked' ) && ! $( '#woocommerce_ppec_paypal_mark_settings_toggle' ).is( ':checked' ) );
$( '#woocommerce_ppec_paypal_button_layout, #woocommerce_ppec_paypal_button_size, #woocommerce_ppec_paypal_hide_funding_methods, #woocommerce_ppec_paypal_credit_enabled' ).closest( 'tr' ).toggle( display );
display && $( '#woocommerce_ppec_paypal_button_layout' ).change();
}
// Toggle mini-cart section based on whether checkout on cart page is enabled
$( '#woocommerce_ppec_paypal_cart_checkout_enabled' ).change( function( event ) {
if ( ! $( '#woocommerce_ppec_paypal_use_spb' ).is( ':checked' ) ) {
return;
}
var checked = $( event.target ).is( ':checked' );
$( '#woocommerce_ppec_paypal_mini_cart_settings_toggle, .woocommerce_ppec_paypal_mini_cart' )
.closest( 'tr' )
.add( '#woocommerce_ppec_paypal_mini_cart_settings' ) // Select title.
.next( 'p' ) // Select description if present.
.addBack()
.toggle( checked );
checked && $( '#woocommerce_ppec_paypal_mini_cart_settings_toggle' ).change();
showHideDefaultButtonSettings();
} ).change();
$( '#woocommerce_ppec_paypal_mini_cart_settings_toggle' ).change( function( event ) {
// Only show settings specific to mini-cart if configured to override global settings.
var checked = $( event.target ).is( ':checked' );
$( '.woocommerce_ppec_paypal_mini_cart' ).closest( 'tr' ).toggle( checked );
checked && $( '#woocommerce_ppec_paypal_mini_cart_button_layout' ).change();
showHideDefaultButtonSettings();
} ).change();
$( '#woocommerce_ppec_paypal_checkout_on_single_product_enabled, #woocommerce_ppec_paypal_single_product_settings_toggle' ).change( function( event ) {
if ( ! $( '#woocommerce_ppec_paypal_use_spb' ).is( ':checked' ) ) {
return;
}
if ( ! $( '#woocommerce_ppec_paypal_checkout_on_single_product_enabled' ).is( ':checked' ) ) {
// If product page button is disabled, hide remaining settings in section.
$( '#woocommerce_ppec_paypal_single_product_settings_toggle, .woocommerce_ppec_paypal_single_product' ).closest( 'tr' ).hide();
} else if ( ! $( '#woocommerce_ppec_paypal_single_product_settings_toggle' ).is( ':checked' ) ) {
// If product page button is enabled but not configured to override global settings, hide remaining settings in section.
$( '#woocommerce_ppec_paypal_single_product_settings_toggle' ).closest( 'tr' ).show();
$( '.woocommerce_ppec_paypal_single_product' ).closest( 'tr' ).hide();
} else {
// Show all settings in section.
$( '#woocommerce_ppec_paypal_single_product_settings_toggle, .woocommerce_ppec_paypal_single_product' ).closest( 'tr' ).show();
$( '#woocommerce_ppec_paypal_single_product_button_layout' ).change();
}
showHideDefaultButtonSettings();
} ).change();
$( '#woocommerce_ppec_paypal_mark_enabled, #woocommerce_ppec_paypal_mark_settings_toggle' ).change( function() {
if ( ! $( '#woocommerce_ppec_paypal_use_spb' ).is( ':checked' ) ) {
return;
}
if ( ! $( '#woocommerce_ppec_paypal_mark_enabled' ).is( ':checked' ) ) {
// If checkout page button is disabled, hide remaining settings in section.
$( '#woocommerce_ppec_paypal_mark_settings_toggle, .woocommerce_ppec_paypal_mark' ).closest( 'tr' ).hide();
} else if ( ! $( '#woocommerce_ppec_paypal_mark_settings_toggle' ).is( ':checked' ) ) {
// If checkout page button is enabled but not configured to override global settings, hide remaining settings in section.
$( '#woocommerce_ppec_paypal_mark_settings_toggle' ).closest( 'tr' ).show();
$( '.woocommerce_ppec_paypal_mark' ).closest( 'tr' ).hide();
} else {
// Show all settings in section.
$( '#woocommerce_ppec_paypal_mark_settings_toggle, .woocommerce_ppec_paypal_mark' ).closest( 'tr' ).show();
$( '#woocommerce_ppec_paypal_mark_button_layout' ).change();
}
showHideDefaultButtonSettings();
} ).change();
// Make sure handlers are only attached once if script is loaded multiple times.
$( '#woocommerce_ppec_paypal_use_spb' ).off( 'change' );
$( '#woocommerce_ppec_paypal_use_spb' ).change( function( event ) {
var checked = $( event.target ).is( ':checked' );
// Show settings specific to Smart Payment Buttons only if enabled.
$( '.woocommerce_ppec_paypal_spb' ).not( 'h3 ').closest( 'tr' ).toggle( checked );
$( '.woocommerce_ppec_paypal_spb' ).filter( 'h3' ).next( 'p' ).addBack().toggle( checked );
if ( checked ) {
// Trigger all logic that controls visibility of other settings.
$( '.woocommerce_ppec_paypal_visibility_toggle' ).change();
} else {
// If non-SPB mode is enabled, show all settings that may have been hidden.
$( '#woocommerce_ppec_paypal_button_size, #woocommerce_ppec_paypal_credit_enabled' ).closest( 'tr' ).show();
}
// Hide 'Responsive' button size option in SPB mode, and make sure to show 'Small' option.
var button_size = $( '#woocommerce_ppec_paypal_button_size' ).removeClass( 'enhanced' );
button_size.find( 'option[value=\"responsive\"]' ).prop( 'disabled', ! checked );
! checked && button_size.find( 'option[value=\"small\"]' ).prop( 'disabled', false );
$( document.body ).trigger( 'wc-enhanced-select-init' );
} ).change();
// Reset button size values to default when switching modes.
$( '#woocommerce_ppec_paypal_use_spb' ).change( function( event ) {
if ( $( event.target ).is( ':checked' ) ) {
// In SPB mode, set to recommended 'Responsive' value so it is not missed.
$( '#woocommerce_ppec_paypal_button_size' ).val( 'responsive' ).change();
} else if ( ! $( '#woocommerce_ppec_paypal_button_size' ).val() ) {
// Set back to original default for non-SPB mode.
$( '#woocommerce_ppec_paypal_button_size' ).val( 'large' ).change();
}
} );
});
" );
/**
* Settings for PayPal Gateway.
*/
$settings = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'label' => __( 'Enable PayPal Checkout', 'woocommerce-gateway-paypal-express-checkout' ),
'description' => __( 'This enables PayPal 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', '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' ),
),
'paypal_hosted_settings' => array(
'title' => __( 'PayPal-hosted Checkout Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => __( 'Customize the appearance of PayPal Checkout on the PayPal side.', '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,
),
'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' ),
),
),
'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' ),
),
),
'button_settings' => array(
'title' => __( 'Button Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'description' => __( 'Customize the appearance of PayPal Checkout on your site.', 'woocommerce-gateway-paypal-express-checkout' ),
),
'use_spb' => array(
'title' => __( 'Smart Payment Buttons', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'default' => $this->get_option( 'button_size' ) ? 'no' : 'yes', // A 'button_size' value having been set indicates that settings have been initialized before, requiring merchant opt-in to SPB.
'label' => __( 'Use Smart Payment Buttons', 'woocommerce-gateway-paypal-express-checkout' ),
'description' => sprintf( __( 'PayPal Checkout\'s Smart Payment Buttons provide a variety of button customization options, such as color, language, shape, and multiple button layout. <a href="%s">Learn more about Smart Payment Buttons</a>.', 'woocommerce-gateway-paypal-express-checkout' ), 'https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/#smart-payment-buttons' ),
),
'button_color' => array(
'title' => __( 'Button Color', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select woocommerce_ppec_paypal_spb',
'default' => 'gold',
'desc_tip' => true,
'description' => __( 'Controls the background color of the primary button. Use "Gold" to leverage PayPal\'s recognition and preference, or change it to match your site design or aesthetic.', 'woocommerce-gateway-paypal-express-checkout' ),
'options' => array(
'gold' => __( 'Gold (Recommended)', 'woocommerce-gateway-paypal-express-checkout' ),
'blue' => __( 'Blue', 'woocommerce-gateway-paypal-express-checkout' ),
'silver' => __( 'Silver', 'woocommerce-gateway-paypal-express-checkout' ),
'black' => __( 'Black', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'button_shape' => array(
'title' => __( 'Button Shape', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select woocommerce_ppec_paypal_spb',
'default' => 'rect',
'desc_tip' => true,
'description' => __( 'The pill-shaped button\'s unique and powerful shape signifies PayPal in people\'s minds. Use the rectangular button as an alternative when pill-shaped buttons might pose design challenges.', 'woocommerce-gateway-paypal-express-checkout' ),
'options' => array(
'pill' => __( 'Pill', 'woocommerce-gateway-paypal-express-checkout' ),
'rect' => __( 'Rectangle', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
);
/**
* Settings that are copied to context-specific sections.
*/
$per_context_settings = array(
'button_layout' => array(
'title' => __( 'Button Layout', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select woocommerce_ppec_paypal_spb woocommerce_ppec_paypal_button_layout',
'default' => 'vertical',
'desc_tip' => true,
'description' => __( 'If additional funding sources are available to the buyer through PayPal, such as Venmo, then multiple buttons are displayed in the space provided. Choose "vertical" for a dynamic list of alternative and local payment options, or "horizontal" when space is limited.', 'woocommerce-gateway-paypal-express-checkout' ),
'options' => array(
'vertical' => __( 'Vertical', 'woocommerce-gateway-paypal-express-checkout' ),
'horizontal' => __( 'Horizontal', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'button_size' => array(
'title' => __( 'Button Size', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'select',
'class' => 'wc-enhanced-select woocommerce_ppec_paypal_button_size',
'default' => 'yes' === $this->get_option( 'use_spb', 'yes' ) ? 'responsive' : 'large',
'desc_tip' => true,
'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. (The "Responsive" option adjusts to container size, and is available and recommended for Smart Payment Buttons.)', 'woocommerce-gateway-paypal-express-checkout' ),
'options' => array(
'responsive' => __( 'Responsive', 'woocommerce-gateway-paypal-express-checkout' ),
'small' => __( 'Small', 'woocommerce-gateway-paypal-express-checkout' ),
'medium' => __( 'Medium', 'woocommerce-gateway-paypal-express-checkout' ),
'large' => __( 'Large', 'woocommerce-gateway-paypal-express-checkout' ),
),
),
'hide_funding_methods' => array(
'title' => 'Hide Funding Method(s)',
'type' => 'multiselect',
'class' => 'wc-enhanced-select woocommerce_ppec_paypal_spb woocommerce_ppec_paypal_vertical',
'default' => array( 'CARD' ),
'desc_tip' => true,
'description' => __( 'Hides the specified funding methods.', 'woocommerce-gateway-paypal-express-checkout' ),
'options' => array(
'CREDIT' => __( 'PayPal Credit', 'woocommerce-gateway-paypal-express-checkout' ),
'ELV' => __( 'ELV', 'woocommerce-gateway-paypal-express-checkout' ),
'CARD' => __( 'Credit Card', '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(),
'class' => 'woocommerce_ppec_paypal_horizontal',
'default' => 'yes',
'desc_tip' => true,
'description' => $credit_enabled_description,
),
);
/**
* Cart / global button settings.
*/
$settings = array_merge( $settings, $per_context_settings );
$per_context_settings['button_size']['class'] .= ' woocommerce_ppec_paypal_spb';
$per_context_settings['credit_enabled']['class'] .= ' woocommerce_ppec_paypal_spb';
$settings['cart_checkout_enabled'] = array(
'title' => __( 'Checkout on cart page', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'class' => 'woocommerce_ppec_paypal_visibility_toggle',
'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',
);
/**
* Mini-cart button settings.
*/
$settings['mini_cart_settings'] = array(
'title' => __( 'Mini-cart Button Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'class' => 'woocommerce_ppec_paypal_spb',
);
$settings['mini_cart_settings_toggle'] = array(
'title' => __( 'Configure Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'label' => __( 'Configure settings specific to mini-cart', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'class' => 'woocommerce_ppec_paypal_spb woocommerce_ppec_paypal_visibility_toggle',
'default' => 'no',
'desc_tip' => true,
'description' => __( 'Optionally override global button settings above and configure buttons for this context.', 'woocommerce-gateway-paypal-express-checkout' ),
);
foreach( $per_context_settings as $key => $value ) {
$value['class'] .= ' woocommerce_ppec_paypal_mini_cart';
$settings[ 'mini_cart_' . $key ] = $value;
}
/**
* Single product button settings.
*/
$settings['single_product_settings'] = array(
'title' => __( 'Single Product Button Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'class' => 'woocommerce_ppec_paypal_spb',
);
$settings['checkout_on_single_product_enabled'] = array(
'title' => __( 'Checkout on Single Product', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'class' => 'woocommerce_ppec_paypal_visibility_toggle',
'label' => __( 'Checkout on Single Product', 'woocommerce-gateway-paypal-express-checkout' ),
'default' => 'yes',
'desc_tip' => true,
'description' => __( 'Enable PayPal Checkout on Single Product view.', 'woocommerce-gateway-paypal-express-checkout' ),
);
$settings['single_product_settings_toggle'] = array(
'title' => __( 'Configure Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'label' => __( 'Configure settings specific to Single Product view', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'class' => 'woocommerce_ppec_paypal_spb woocommerce_ppec_paypal_visibility_toggle',
'default' => 'yes',
'desc_tip' => true,
'description' => __( 'Optionally override global button settings above and configure buttons for this context.', 'woocommerce-gateway-paypal-express-checkout' ),
);
foreach( $per_context_settings as $key => $value ) {
$value['class'] .= ' woocommerce_ppec_paypal_single_product';
$settings[ 'single_product_' . $key ] = $value;
}
$settings['single_product_button_layout']['default'] = 'horizontal';
/**
* Regular checkout button settings.
*/
$settings['mark_settings'] = array(
'title' => __( 'Regular Checkout Button Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'title',
'class' => 'woocommerce_ppec_paypal_spb',
);
$settings['mark_enabled'] = array(
'title' => __( 'PayPal Mark', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'class' => 'woocommerce_ppec_paypal_visibility_toggle',
'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 Checkout like a regular WooCommerce gateway.', 'woocommerce-gateway-paypal-express-checkout' ),
'desc_tip' => true,
'default' => 'yes',
);
$settings['mark_settings_toggle'] = array(
'title' => __( 'Configure Settings', 'woocommerce-gateway-paypal-express-checkout' ),
'label' => __( 'Configure settings specific to regular checkout', 'woocommerce-gateway-paypal-express-checkout' ),
'type' => 'checkbox',
'class' => 'woocommerce_ppec_paypal_spb woocommerce_ppec_paypal_visibility_toggle',
'default' => 'no',
'desc_tip' => true,
'description' => __( 'Optionally override global button settings above and configure buttons for this context.', 'woocommerce-gateway-paypal-express-checkout' ),
);
foreach( $per_context_settings as $key => $value ) {
$value['class'] .= ' woocommerce_ppec_paypal_mark';
$settings[ 'mark_' . $key ] = $value;
}
return apply_filters( 'woocommerce_paypal_express_checkout_settings', $settings );

View File

@@ -0,0 +1,845 @@
# Copyright (C) 2016 Automattic
# This file is distributed under the GNU General Public License v3.0.
msgid ""
msgstr ""
"Project-Id-Version: WooCommerce PayPal Express Checkout Gateway 1.1.1\n"
"Report-Msgid-Bugs-To: "
"https://github.com/woothemes/woocommerce-gateway-paypal-express-checkout/"
"issues\n"
"POT-Creation-Date: 2016-08-20 20:23:39+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"X-Generator: grunt-wp-i18n 0.5.4\n"
#: includes/abstracts/abstract-wc-gateway-ppec.php:19
#: includes/settings/settings-ppec.php:135
msgid "PayPal Express Checkout"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:20
msgid "Allow customers to conveniently checkout directly with PayPal."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:23
msgid "Continue to payment"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:120
msgid ""
"Sorry, an error occurred while trying to process your payment. Please try "
"again."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:150
msgid "No API certificate on file."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:162
msgid "expired on %s"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:165
#: includes/abstracts/abstract-wc-gateway-ppec.php:168
msgid "expires on %s"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:172
msgid "Certificate belongs to API username %1$s; %2$s"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:174
msgid "The certificate on file is not valid."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:189
msgid "Error: The logo image URL you provided is not valid and cannot be used."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:237
msgid "Error: You must enter API password."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:248
#: includes/abstracts/abstract-wc-gateway-ppec.php:283
msgid ""
"Error: The %s credentials you provided are not valid. Please double-check "
"that you entered them correctly and try again."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:253
#: includes/abstracts/abstract-wc-gateway-ppec.php:288
msgid ""
"An error occurred while trying to validate your %s API credentials. Unable "
"to verify that your API credentials are correct."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:261
msgid "Error: The %s API certificate is not valid."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:269
msgid "Error: The %s API certificate has expired."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:274
msgid ""
"Error: The API username does not match the name in the API certificate. "
"Make sure that you have the correct API certificate."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:293
msgid "Error: You must provide a %s API signature or certificate."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:311
msgid ""
"The \"require billing address\" option is not enabled by your account and "
"has been disabled."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:324
msgid "Refund Error: You need to specify a refund amount."
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:347
#: includes/abstracts/abstract-wc-gateway-ppec.php:370
#: includes/abstracts/abstract-wc-gateway-ppec.php:421
msgid "PayPal refund completed; transaction ID = %s"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:354
#: includes/abstracts/abstract-wc-gateway-ppec.php:377
#: includes/abstracts/abstract-wc-gateway-ppec.php:427
msgid "Error: %1$s - %2$s"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:394
msgid ""
"Refund Error: All transactions have been fully refunded. There is no amount "
"left to refund"
msgstr ""
#: includes/abstracts/abstract-wc-gateway-ppec.php:396
msgid ""
"Refund Error: The requested refund amount is too large. The refund amount "
"must be less than or equal to %s."
msgstr ""
#: includes/class-wc-gateway-ppec-admin-handler.php:49
msgid "Capture Charge"
msgstr ""
#: includes/class-wc-gateway-ppec-admin-handler.php:73
msgid ""
"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."
msgstr ""
#: includes/class-wc-gateway-ppec-admin-handler.php:156
msgid "Unable to capture charge!"
msgstr ""
#: includes/class-wc-gateway-ppec-admin-handler.php:158
msgid "PayPal Express Checkout charge complete (Charge ID: %s)"
msgstr ""
#: includes/class-wc-gateway-ppec-admin-handler.php:197
msgid "Unable to void charge!"
msgstr ""
#: includes/class-wc-gateway-ppec-admin-handler.php:199
msgid "PayPal Express Checkout charge voided (Charge ID: %s)"
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:22
msgid "Unable to communicate with PayPal. Please try your payment again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:23
msgid ""
"PayPal rejected your email address because it is not valid. Please "
"double-check your email address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:26
msgid "Your PayPal checkout session is invalid. Please check out again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:27
msgid "Your PayPal checkout session has expired. Please check out again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:29
msgid ""
"Your PayPal payment has already been completed. Please contact the store "
"owner for more information."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:30
msgid ""
"Your PayPal payment could not be processed. Please check out again or "
"contact PayPal for assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:31
msgid ""
"Your PayPal payment could not be processed. Please select an alternative "
"method of payment or contact PayPal for assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:33
msgid ""
"Your PayPal payment could not be processed. Please return to PayPal and "
"select a new method of payment."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:35
msgid ""
"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."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:36
msgid ""
"Your shipping address may not be in a different country than your country "
"of residence. Please double-check your shipping address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:37
msgid ""
"This store does not accept transactions from buyers in your country. "
"Please contact the store owner for assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:38
msgid ""
"The transaction is over the threshold allowed by this store. Please "
"contact the store owner for assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:40
msgid ""
"Your transaction was declined. Please contact the store owner for "
"assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:41
#: includes/class-wc-gateway-ppec-api-error.php:46
msgid ""
"The country in your shipping address is not valid. Please double-check "
"your shipping address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:42
msgid ""
"The street address in your shipping address is not valid. Please "
"double-check your shipping address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:43
msgid ""
"The city in your shipping address is not valid. Please double-check your "
"shipping address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:44
msgid ""
"The state in your shipping address is not valid. Please double-check your "
"shipping address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:45
msgid ""
"The ZIP code or postal code in your shipping address is not valid. Please "
"double-check your shipping address and try again."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:47
msgid ""
"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."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:49
msgid ""
"Your PayPal payment could not be processed. Please contact PayPal for "
"assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:51
msgid ""
"The redemption code(s) you entered on PayPal cannot be used at this time. "
"Please return to PayPal and remove them."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:54
msgid ""
"Your funding instrument is invalid. Please check out again and select a "
"new funding source."
msgstr ""
#: includes/class-wc-gateway-ppec-api-error.php:55
msgid ""
"An error (%s) occurred while processing your PayPal payment. Please "
"contact the store owner for assistance."
msgstr ""
#: includes/class-wc-gateway-ppec-cart-handler.php:74
msgid "&mdash; or &mdash;"
msgstr ""
#: includes/class-wc-gateway-ppec-cart-handler.php:79
#: includes/class-wc-gateway-ppec-cart-handler.php:97
msgid "Check out with PayPal"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:66
msgid "Confirm your PayPal order"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:100
msgid "Billing details"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:103
msgid "Address:"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:105
msgid "Name:"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:109
msgid "Email:"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:113
msgid "Tel:"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:131
msgid "Shipping details"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:198
#: includes/class-wc-gateway-ppec-checkout-handler.php:235
msgid "Your PayPal checkout session has expired. Please check out again."
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:230
msgid ""
"Sorry, an error occurred while trying to retrieve your information from "
"PayPal. Please try again."
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:282
msgid "Cancel"
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:290
#: includes/class-wc-gateway-ppec-checkout-handler.php:291
msgid ""
"You have cancelled Checkout with PayPal. Please try to process your order "
"again."
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:644
#: includes/class-wc-gateway-ppec-ipn-handler.php:189
msgid ""
"Payment authorized. Change payment status to processing or complete to "
"capture funds."
msgstr ""
#: includes/class-wc-gateway-ppec-checkout-handler.php:646
#: includes/class-wc-gateway-ppec-ipn-handler.php:191
msgid "Payment pending (%s)."
msgstr ""
#: includes/class-wc-gateway-ppec-client-credential-certificate.php:66
msgid "Unable to accept certificate during cURL configuration"
msgstr ""
#: includes/class-wc-gateway-ppec-client-credential-certificate.php:70
msgid "Unable to accept certificate password during cURL configuration"
msgstr ""
#: includes/class-wc-gateway-ppec-client-credential-certificate.php:87
#: includes/class-wc-gateway-ppec-client-credential-certificate.php:133
msgid "Unable to write certificate file %s during cURL configuration"
msgstr ""
#: includes/class-wc-gateway-ppec-client-credential-certificate.php:113
msgid "Failed to retrieve private key during cURL configuration"
msgstr ""
#: includes/class-wc-gateway-ppec-client-credential-certificate.php:117
msgid "Failed to export PKCS12 file during cURL configuration"
msgstr ""
#: includes/class-wc-gateway-ppec-client.php:121
msgid "Missing credential"
msgstr ""
#: includes/class-wc-gateway-ppec-client.php:125
msgid "Invalid credential object"
msgstr ""
#: includes/class-wc-gateway-ppec-client.php:129
msgid "Invalid environment"
msgstr ""
#: includes/class-wc-gateway-ppec-client.php:152
msgid "An error occurred while trying to connect to PayPal: %s"
msgstr ""
#: includes/class-wc-gateway-ppec-client.php:158
msgid "Malformed response received from PayPal"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:30
msgid "Empty POST data."
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:39
msgid "Invalid IPN request."
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:44
msgid "PayPal IPN Request Failure"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:144
msgid "Validation error: PayPal currencies do not match (code %s)."
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:159
msgid "Validation error: PayPal amounts do not match (gross %s)."
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:182
msgid "IPN payment completed"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:213
#: includes/class-wc-gateway-ppec-ipn-handler.php:256
#: includes/class-wc-gateway-ppec-ipn-handler.php:271
msgid "Payment %s via IPN."
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:258
msgid "Payment for order %s refunded"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:259
msgid "Order #%s has been marked as refunded - PayPal reason code: %s"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:273
msgid "Payment for order %s reversed"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:274
msgid "Order #%s has been marked on-hold due to a reversal - PayPal reason code: %s"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:286
msgid "Reversal cancelled for order #%s"
msgstr ""
#: includes/class-wc-gateway-ppec-ipn-handler.php:287
msgid ""
"Order #%s has had a reversal cancelled. Please check the status of payment "
"and update the order status accordingly here: %s"
msgstr ""
#: includes/class-wc-gateway-ppec-ips-handler.php:140
msgid "Invalid connection request"
msgstr ""
#: includes/class-wc-gateway-ppec-ips-handler.php:150
#: includes/class-wc-gateway-ppec-ips-handler.php:158
msgid "Sorry, Easy Setup encountered an error. Please try again."
msgstr ""
#: includes/class-wc-gateway-ppec-ips-handler.php:173
msgid ""
"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."
msgstr ""
#: includes/class-wc-gateway-ppec-ips-handler.php:178
msgid ""
"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."
msgstr ""
#: includes/class-wc-gateway-ppec-ips-handler.php:183
msgid "Success! Your PayPal account has been set up successfully."
msgstr ""
#: includes/class-wc-gateway-ppec-plugin.php:153
msgid ""
"%s in WooCommerce Gateway PayPal Express Checkout plugin can only be called "
"once"
msgstr ""
#: includes/class-wc-gateway-ppec-plugin.php:208
msgid ""
"WooCommerce Gateway PayPal Express Checkout requires WooCommerce to be "
"activated"
msgstr ""
#: includes/class-wc-gateway-ppec-plugin.php:212
msgid ""
"WooCommerce Gateway PayPal Express Checkout requires WooCommerce version "
"2.5 or greater"
msgstr ""
#: includes/class-wc-gateway-ppec-plugin.php:216
msgid ""
"WooCommerce Gateway PayPal Express Checkout requires cURL to be installed "
"on your server"
msgstr ""
#: includes/class-wc-gateway-ppec-plugin.php:219
msgid ""
"WooCommerce Gateway PayPal Express Checkout requires OpenSSL >= 1.0.1 to be "
"installed on your server"
msgstr ""
#: includes/functions.php:45
msgid "Payment error:"
msgstr ""
#: includes/settings/settings-ppec.php:15
msgid "Setup or link an existing PayPal account"
msgstr ""
#: includes/settings/settings-ppec.php:16
msgid ""
"%s or <a href=\"#\" class=\"ppec-toggle-settings\">click here to toggle "
"manual API credential input</a>."
msgstr ""
#: includes/settings/settings-ppec.php:22
msgid "Setup or link an existing PayPal Sandbox account"
msgstr ""
#: includes/settings/settings-ppec.php:23
msgid ""
"%s or <a href=\"#\" class=\"ppec-toggle-sandbox-settings\">click here to "
"toggle manual API credential input</a>."
msgstr ""
#: includes/settings/settings-ppec.php:91
msgid "Enable/Disable"
msgstr ""
#: includes/settings/settings-ppec.php:93
msgid "Enable PayPal Express Checkout"
msgstr ""
#: includes/settings/settings-ppec.php:94
msgid ""
"This enables PayPal Express Checkout which allows customers to checkout "
"directly via PayPal from your cart page."
msgstr ""
#: includes/settings/settings-ppec.php:99
msgid "Button Size"
msgstr ""
#: includes/settings/settings-ppec.php:102
msgid ""
"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."
msgstr ""
#: includes/settings/settings-ppec.php:106
msgid "Small"
msgstr ""
#: includes/settings/settings-ppec.php:107
msgid "Medium"
msgstr ""
#: includes/settings/settings-ppec.php:108
msgid "Large"
msgstr ""
#: includes/settings/settings-ppec.php:112
msgid "Environment"
msgstr ""
#: includes/settings/settings-ppec.php:115
msgid ""
"This setting specifies whether you will process live transactions, or "
"whether you will process simulated transactions using the PayPal Sandbox."
msgstr ""
#: includes/settings/settings-ppec.php:119
msgid "Live"
msgstr ""
#: includes/settings/settings-ppec.php:120
msgid "Sandbox"
msgstr ""
#: includes/settings/settings-ppec.php:124
msgid "PayPal Mark"
msgstr ""
#: includes/settings/settings-ppec.php:126
msgid "Enable the PayPal Mark on regular checkout"
msgstr ""
#: includes/settings/settings-ppec.php:127
msgid ""
"This enables the PayPal mark, which can be shown on regular WooCommerce "
"checkout to use PayPal Express Checkout like a regular WooCommerce gateway."
msgstr ""
#: includes/settings/settings-ppec.php:132
msgid "Title"
msgstr ""
#: includes/settings/settings-ppec.php:134
msgid "This controls the title which the user sees during checkout."
msgstr ""
#: includes/settings/settings-ppec.php:139
msgid "Description"
msgstr ""
#: includes/settings/settings-ppec.php:142
msgid "This controls the description which the user sees during checkout."
msgstr ""
#: includes/settings/settings-ppec.php:143
msgid ""
"Pay using either your PayPal account or credit card. All credit card "
"payments will be processed by PayPal."
msgstr ""
#: includes/settings/settings-ppec.php:147
msgid "API Credentials"
msgstr ""
#: includes/settings/settings-ppec.php:152
msgid "Live API Username"
msgstr ""
#: includes/settings/settings-ppec.php:154
#: includes/settings/settings-ppec.php:161
#: includes/settings/settings-ppec.php:168
#: includes/settings/settings-ppec.php:196
#: includes/settings/settings-ppec.php:203
#: includes/settings/settings-ppec.php:210
#: includes/settings/settings-ppec.php:217
msgid "Get your API credentials from PayPal."
msgstr ""
#: includes/settings/settings-ppec.php:159
msgid "Live API Password"
msgstr ""
#: includes/settings/settings-ppec.php:166
msgid "Live API Signature"
msgstr ""
#: includes/settings/settings-ppec.php:171
msgid "Optional if you provide a certificate below"
msgstr ""
#: includes/settings/settings-ppec.php:174
msgid "Live API Certificate"
msgstr ""
#: includes/settings/settings-ppec.php:180
msgid "Live API Subject"
msgstr ""
#: includes/settings/settings-ppec.php:182
#: includes/settings/settings-ppec.php:224
msgid ""
"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."
msgstr ""
#: includes/settings/settings-ppec.php:185
#: includes/settings/settings-ppec.php:227
#: includes/settings/settings-ppec.php:284
msgid "Optional"
msgstr ""
#: includes/settings/settings-ppec.php:189
msgid "Sandbox API Credentials"
msgstr ""
#: includes/settings/settings-ppec.php:194
msgid "Sandbox API Username"
msgstr ""
#: includes/settings/settings-ppec.php:201
msgid "Sandbox API Password"
msgstr ""
#: includes/settings/settings-ppec.php:208
msgid "Sandbox API Signature"
msgstr ""
#: includes/settings/settings-ppec.php:215
msgid "Sandbox API Certificate"
msgstr ""
#: includes/settings/settings-ppec.php:222
msgid "Sandbox API Subject"
msgstr ""
#: includes/settings/settings-ppec.php:231
msgid "Advanced Settings"
msgstr ""
#: includes/settings/settings-ppec.php:236
msgid "Debug Log"
msgstr ""
#: includes/settings/settings-ppec.php:238
msgid "Enable Logging"
msgstr ""
#: includes/settings/settings-ppec.php:241
msgid "Log PayPal events, such as IPN requests."
msgstr ""
#: includes/settings/settings-ppec.php:244
msgid "Invoice Prefix"
msgstr ""
#: includes/settings/settings-ppec.php:246
msgid ""
"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."
msgstr ""
#: includes/settings/settings-ppec.php:251
msgid "Billing Addresses"
msgstr ""
#: includes/settings/settings-ppec.php:253
msgid "Require Billing Address"
msgstr ""
#: includes/settings/settings-ppec.php:256
msgid ""
"PayPal does not share buyer billing details with you. However, there are "
"times when you must collect the buyer billing address to fulfill an "
"essential business function (such as determining whether you must charge "
"the buyer tax). Enable this function to collect the address before payment "
"is taken."
msgstr ""
#: includes/settings/settings-ppec.php:259
msgid "Payment Action"
msgstr ""
#: includes/settings/settings-ppec.php:262
msgid ""
"Choose whether you wish to capture funds immediately or authorize payment "
"only."
msgstr ""
#: includes/settings/settings-ppec.php:266
msgid "Sale"
msgstr ""
#: includes/settings/settings-ppec.php:267
msgid "Authorize"
msgstr ""
#: includes/settings/settings-ppec.php:271
msgid "Instant Payments"
msgstr ""
#: includes/settings/settings-ppec.php:273
msgid "Require Instant Payment"
msgstr ""
#: includes/settings/settings-ppec.php:276
msgid ""
"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."
msgstr ""
#: includes/settings/settings-ppec.php:279
msgid "Logo Image URL"
msgstr ""
#: includes/settings/settings-ppec.php:281
msgid ""
"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."
msgstr ""
#: includes/settings/settings-ppec.php:287
msgid "Subtotal Mismatch Behavior"
msgstr ""
#: includes/settings/settings-ppec.php:290
msgid ""
"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."
msgstr ""
#: includes/settings/settings-ppec.php:294
msgid "Add another line item"
msgstr ""
#: includes/settings/settings-ppec.php:295
msgid "Do not send line items to PayPal"
msgstr ""
#. Plugin Name of the plugin/theme
msgid "WooCommerce PayPal Express Checkout Gateway"
msgstr ""
#. Plugin URI of the plugin/theme
msgid ""
"https://www.woothemes.com/products/woocommerce-gateway-paypal-express-"
"checkout/"
msgstr ""
#. Description of the plugin/theme
msgid ""
"A payment gateway for PayPal Express Checkout "
"(https://www.paypal.com/us/webapps/mpp/express-checkout)."
msgstr ""
#. Author of the plugin/theme
msgid "Automattic"
msgstr ""
#. Author URI of the plugin/theme
msgid "https://woocommerce.com"
msgstr ""

View File

@@ -0,0 +1,215 @@
=== WooCommerce PayPal Checkout Payment Gateway ===
Contributors: automattic, woothemes, akeda, dwainm, royho, allendav, slash1andy, woosteve, spraveenitpro, mikedmoore, fernashes, shellbeezy, danieldudzic, mikaey, fullysupportedphil, dsmithweb, corsonr, bor0, zandyring, pauldechov, robobot3000
Tags: ecommerce, e-commerce, commerce, woothemes, wordpress ecommerce, store, sales, sell, shop, shopping, cart, checkout, configurable, paypal
Requires at least: 4.4
Tested up to: 5.2.2
Stable tag: 1.6.17
License: GPLv3
License URI: http://www.gnu.org/licenses/gpl-3.0.html
Accept PayPal, Credit Cards and Debit Cards on your WooCommerce store.
== Description ==
This is a PayPal Checkout Payment Gateway for WooCommerce.
PayPal Checkout allows you to securely sell your products and subscriptions online using In-Context Checkout to help you meet security requirements without causing your theme to suffer. In-Context Checkout uses a modal window, hosted on PayPal's servers, that overlays the checkout form and provides a secure means for your customers to enter their account information.
Also, with Integrated PayPal Setup (Easy Setup), connecting to PayPal is as simple as clicking a button - no complicated API keys to cut and paste.
== Installation ==
= Minimum Requirements =
* WordPress 4.4 or greater
= Automatic installation =
Automatic installation is the easiest option as WordPress handles the file transfers itself and you dont need to leave your web browser. To do an automatic install of, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New.
In the search field type "WooCommerce PayPal Checkout" and click Search Plugins. Once youve found our plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by simply clicking “Install Now”.
= Manual installation =
The manual installation method involves downloading our plugin and uploading it to your webserver via your favorite FTP application. The
WordPress codex contains [instructions on how to do this here](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
= Updating =
Automatic updates should work like a charm; as always though, ensure you backup your site just in case.
If on the off-chance you do encounter issues with the shop/category pages after an update you simply need to flush the permalinks by going to WordPress > Settings > Permalinks and hitting 'save'. That should return things to normal.
== Frequently Asked Questions ==
= Does this plugin work with credit cards or just PayPal? =
This plugin supports payments using both credit and debit cards as well as PayPal. The new Smart Payment Buttons feature dynamically displays PayPal, Venmo (US Only), PayPal Credit, or other local payment options* in a single stack—without needing to leave the merchant's website.
*PayPal Checkout features may not be available in all countries.
= Does this support Checkout with PayPal from the cart view? =
Yes!
= Does this support both production mode and sandbox mode for testing? =
Yes it does - production and sandbox mode is driven by how you connect. You may choose to connect in either mode, and disconnect and reconnect in the other mode whenever you want.
= Where can I find documentation? =
For help setting up and configuring, please refer to our [user guide](https://docs.woocommerce.com/document/paypal-express-checkout/)
= Where can I get support or talk to other users? =
If you get stuck, you can ask for help in the Plugin Forum.
= Will this plugin work with my theme? =
Yes, this plugin will work with any theme, but may require some styling to make it match nicely. If you're
looking for a theme with built in WooCommerce integration we recommend [Storefront](http://www.woothemes.com/storefront/).
= Where can I request new features or report bugs? =
New feature requests and bugs reports can be made in the plugin forum.
= How to remove 'Proceed to Checkout' button from cart page? =
If PayPal Checkout is the only enabled payment gateway and you want to remove the 'Proceed to Checkout' button from the cart, you can use this snippet:
https://gist.github.com/mikejolley/ad2ecc286c9ad6cefbb7065ba6dfef48
= Where can I contribute? =
The GitHub repository for PayPal Checkout is here:
https://github.com/woocommerce/woocommerce-gateway-paypal-express-checkout
Please use this to inform us about bugs, or make contributions via PRs.
== Screenshots ==
1. Click the "Setup or link an existing PayPal account" button. If you want to test before going live, you can switch the Environment, above the button, to Sandbox.
2. API credentials will be set after linking, or you can set them manually.
3. See PayPal button settings below.
4. Checkout with PayPal directly from the Cart.
5. And without leaving the store.
6. Confirm details after clicking "Continue".
7. Choose PayPal from regular checkout page.
8. Choose PayPal from single product page.
9. Initiate checkout from mini-cart.
== Changelog ==
= 1.6.17 - 2019-08-08 =
* Update - WooCommerce 3.7 compatibility
* Add - Filter to require display of billing agreement during checkout
* Add - Add CURRENCYCODE to capture_payment
* Add - Add filter for buttons on products
* Fix - Skip wasteful render on initial Checkout page load
* Fix - Appearance tweaks on Checkout screen
= 1.6.16 - 2019-07-18 =
* Fix - Don't require address for renewal of virtual subscriptions
* Fix - Avoid broken confirmation screen edge case after 10486 redirect
= 1.6.15 - 2019-06-19 =
* Fix - Prevent PHP errors when no billing details are present in PP response
* Fix - Require billing address for virtual products when enabled
* Add - Hook when a payment error occurs
= 1.6.14 - 2019-05-08 =
* Fix - Failing checkout when no addons are used
= 1.6.12 - 2019-05-08 =
* Fix - Better handling of virtual subscriptions when billing address is not required
* Fix - Prevent errors showing when purchasing a virtual product with WP_DEBUG enabled
= 1.6.11 - 2019-04-17 =
* Fix/Performance - Prevent db option updates during bootstrap on each page load
* Tweak = WC 3.6 compatibiliy.
= 1.6.10 - 2019-03-05 =
* Fix - Use only product attributes when adding to cart
= 1.6.9 - 2019-02-03 =
* Fix - Avoid SPB render error by tweaking 'allowed' funding methods' empty value
= 1.6.8 - 2019-01-25 =
* Fix - Guard against themes applying filter with too few params
= 1.6.7 - 2019-01-25 =
* Fix - Error 10413 when using coupons
* Fix: All variation details when using buttons on product pages are kept
* Fix: Always render the PayPal buttons in the mini cart
= 1.6.6 - 2019-01-09 =
* Fix - Discount items were not being included
* Add - Filter for order details to accept decimal quantities of products
* Fix - Unable to buy variation from product page
* Fix - Can use PayPal from product page without inputting required fields
* Add - Display PayPal fees under the totals on the order admin page
* Add - Prefill name, phone, and email info in PayPal Guest Checkout from checkout screen
= 1.6.5 - 2018-10-31 =
* Fix - Truncate the line item descriptions to avoid exceeding PayPal character limits.
* Update - WC 3.5 compatibility.
* Fix - checkout.js script loading when not needed.
* Fix - Missing shipping total and address when starting from checkout page.
= 1.6.4 - 2018-09-27 =
* Fix - Billing address from Checkout form not being passed to PayPal via Smart Payment Button.
* Fix - Checkout form not being validated until after Smart Payment Button payment flow.
= 1.6.3 - 2018-08-15 =
* Fix - Fatal error caused by a fix for Smart Payment Buttons.
= 1.6.2 - 2018-08-15 =
* Fix - Tax not applied on the (Confirm your PayPal order) page at the checkout.
= 1.6.1 - 2018-07-04 =
* Fix - GDPR Fatal error exporting user data when they have PPEC subscriptions.
* Fix - PayPal Credit still being disabled by default.
* Update - Rename 'PayPal Express Checkout' to 'PayPal Checkout'.
* Fix - Missing PayPal branding in "Buy Now" Smart Payment Button.
* Fix - PHP warning when PayPal Credit not supported and no funding methods hidden.
* Fix - Smart Payment Buttons gateway not inheriting IPN and subscription handling.
* Fix - Single product Smart Payment Button failing without existing session.
* Fix - When cart is empty, JS error on cart page and mini-cart payment buttons showing.
* Add - Locale filter.
= 1.6.0 - 2018-06-27 =
* Add - Smart Payment Buttons mode as alternative to directly embedded image links for all instances of PayPal button.
* Fix - Help tip alignment for image settings.
* Update - Enable PayPal Credit by default, and restrict its support by currency.
* Update - Omit 'Express Checkout' portion of default payment method title.
* Update - Enable Express Checkout on regular checkout page by default.
* Update - Enable Express Checkout on single product page by default.
= 1.5.6 - 2018-06-06 =
* Fix - Virtual products cause issues with billing details validation.
= 1.5.5 - 2018-05-23 =
* Update - WC 3.4 compatibility
* Update - Privacy policy notification.
* Update - Export/erasure hooks added.
= 1.5.4 - 2018-05-08 =
* Add - Hook to make billing address not required `woocommerce_paypal_express_checkout_address_not_required` (bool).
* Fix - Duplicate checkout settings when PP Credit option is enabled.
* Fix - Impossible to open API credentials after saving Settings.
* Fix - Prevent filtering if PPEC is not enabled.
* Fix - Single Product checkout: Quantity being duplicated due to multiple AJAX calls.
* Fix - When returning from PayPal, place order buttons says "proceed to payment".
* Tweak - Default billing address to be required.
= 1.5.3 - 2018-03-28 =
* Fix - wp_enqueue_media was not correctly loaded causing weird behavior with other parts of system wanting to use it.
* Fix - Typo in activation hook.
= 1.5.2 - 2018-02-20 =
* Tweak - Express checkout shouldn't display "Review your order before the payment".
* Fix - Compatibility with Subscriptions and Checkout from Single Product page.
* Fix - Make sure session object exists before use to prevent fatal error.
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-paypal-express-checkout/master/changelog.txt).

View File

@@ -0,0 +1,49 @@
<?php
/**
* Plugin Name: WooCommerce PayPal Checkout Gateway
* Plugin URI: https://woocommerce.com/products/woocommerce-gateway-paypal-express-checkout/
* Description: A payment gateway for PayPal Checkout (https://www.paypal.com/us/webapps/mpp/paypal-checkout).
* Version: 1.6.17
* Author: WooCommerce
* Author URI: https://woocommerce.com
* Copyright: © 2019 WooCommerce / PayPal.
* License: GNU General Public License v3.0
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
* Text Domain: woocommerce-gateway-paypal-express-checkout
* Domain Path: /languages
* WC tested up to: 3.7
* WC requires at least: 2.6
*/
/**
* Copyright (c) 2019 PayPal, Inc.
*
* The name of the PayPal may not be used to endorse or promote products derived from this
* software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
* WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
define( 'WC_GATEWAY_PPEC_VERSION', '1.6.17' );
/**
* Return instance of WC_Gateway_PPEC_Plugin.
*
* @return WC_Gateway_PPEC_Plugin
*/
function wc_gateway_ppec() {
static $plugin;
if ( ! isset( $plugin ) ) {
require_once( 'includes/class-wc-gateway-ppec-plugin.php' );
$plugin = new WC_Gateway_PPEC_Plugin( __FILE__, WC_GATEWAY_PPEC_VERSION );
}
return $plugin;
}
wc_gateway_ppec()->maybe_run();