mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-05 08:59:14 +08:00
🔀 Merge branch 'trunk'
# Conflicts: # modules/ppcp-applepay/resources/js/ApplepayButton.js # modules/ppcp-applepay/resources/js/boot-admin.js # modules/ppcp-button/resources/js/modules/Preview/PreviewButtonManager.js # modules/ppcp-googlepay/resources/js/GooglepayButton.js # modules/ppcp-googlepay/resources/js/boot-admin.js
This commit is contained in:
commit
a128228c86
303 changed files with 21512 additions and 5378 deletions
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
36
modules/ppcp-googlepay/assets/images/googlepay.svg
Normal file
36
modules/ppcp-googlepay/assets/images/googlepay.svg
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" width="92px" height="50px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="160 160 774 422" enable-background="new 0 0 1094 742" xml:space="preserve">
|
||||
<path id="Base_1_" fill="#FFFFFF" d="M722.7,170h-352c-110,0-200,90-200,200l0,0c0,110,90,200,200,200h352c110,0,200-90,200-200l0,0
|
||||
C922.7,260,832.7,170,722.7,170z"/>
|
||||
<path id="Outline" fill="#3C4043" d="M722.7,186.2c24.7,0,48.7,4.9,71.3,14.5c21.9,9.3,41.5,22.6,58.5,39.5
|
||||
c16.9,16.9,30.2,36.6,39.5,58.5c9.6,22.6,14.5,46.6,14.5,71.3s-4.9,48.7-14.5,71.3c-9.3,21.9-22.6,41.5-39.5,58.5
|
||||
c-16.9,16.9-36.6,30.2-58.5,39.5c-22.6,9.6-46.6,14.5-71.3,14.5h-352c-24.7,0-48.7-4.9-71.3-14.5c-21.9-9.3-41.5-22.6-58.5-39.5
|
||||
c-16.9-16.9-30.2-36.6-39.5-58.5c-9.6-22.6-14.5-46.6-14.5-71.3s4.9-48.7,14.5-71.3c9.3-21.9,22.6-41.5,39.5-58.5
|
||||
c16.9-16.9,36.6-30.2,58.5-39.5c22.6-9.6,46.6-14.5,71.3-14.5L722.7,186.2 M722.7,170h-352c-110,0-200,90-200,200l0,0
|
||||
c0,110,90,200,200,200h352c110,0,200-90,200-200l0,0C922.7,260,832.7,170,722.7,170L722.7,170z"/>
|
||||
<g id="G_Pay_Lockup_1_">
|
||||
<g id="Pay_Typeface_3_">
|
||||
<path id="Letter_p_3_" fill="#3C4043" d="M529.3,384.2v60.5h-19.2V295.3H561c12.9,0,23.9,4.3,32.9,12.9
|
||||
c9.2,8.6,13.8,19.1,13.8,31.5c0,12.7-4.6,23.2-13.8,31.7c-8.9,8.5-19.9,12.7-32.9,12.7h-31.7V384.2z M529.3,313.7v52.1h32.1
|
||||
c7.6,0,14-2.6,19-7.7c5.1-5.1,7.7-11.3,7.7-18.3c0-6.9-2.6-13-7.7-18.1c-5-5.3-11.3-7.9-19-7.9h-32.1V313.7z"/>
|
||||
<path id="Letter_a_3_" fill="#3C4043" d="M657.9,339.1c14.2,0,25.4,3.8,33.6,11.4c8.2,7.6,12.3,18,12.3,31.2v63h-18.3v-14.2h-0.8
|
||||
c-7.9,11.7-18.5,17.5-31.7,17.5c-11.3,0-20.7-3.3-28.3-10s-11.4-15-11.4-25c0-10.6,4-19,12-25.2c8-6.3,18.7-9.4,32-9.4
|
||||
c11.4,0,20.8,2.1,28.1,6.3v-4.4c0-6.7-2.6-12.3-7.9-17c-5.3-4.7-11.5-7-18.6-7c-10.7,0-19.2,4.5-25.4,13.6l-16.9-10.6
|
||||
C625.9,345.8,639.7,339.1,657.9,339.1z M633.1,413.3c0,5,2.1,9.2,6.4,12.5c4.2,3.3,9.2,5,14.9,5c8.1,0,15.3-3,21.6-9
|
||||
s9.5-13,9.5-21.1c-6-4.7-14.3-7.1-25-7.1c-7.8,0-14.3,1.9-19.5,5.6C635.7,403.1,633.1,407.8,633.1,413.3z"/>
|
||||
<path id="Letter_y_3_" fill="#3C4043" d="M808.2,342.4l-64,147.2h-19.8l23.8-51.5L706,342.4h20.9l30.4,73.4h0.4l29.6-73.4H808.2z"
|
||||
/>
|
||||
</g>
|
||||
<g id="G_Mark_1_">
|
||||
<path id="Blue_500" fill="#4285F4" d="M452.93,372c0-6.26-0.56-12.25-1.6-18.01h-80.48v33L417.2,387
|
||||
c-1.88,10.98-7.93,20.34-17.2,26.58v21.41h27.59C443.7,420.08,452.93,398.04,452.93,372z"/>
|
||||
<path id="Green_500_1_" fill="#34A853" d="M400.01,413.58c-7.68,5.18-17.57,8.21-29.14,8.21c-22.35,0-41.31-15.06-48.1-35.36
|
||||
h-28.46v22.08c14.1,27.98,43.08,47.18,76.56,47.18c23.14,0,42.58-7.61,56.73-20.71L400.01,413.58z"/>
|
||||
<path id="Yellow_500_1_" fill="#FABB05" d="M320.09,370.05c0-5.7,0.95-11.21,2.68-16.39v-22.08h-28.46
|
||||
c-5.83,11.57-9.11,24.63-9.11,38.47s3.29,26.9,9.11,38.47l28.46-22.08C321.04,381.26,320.09,375.75,320.09,370.05z"/>
|
||||
<path id="Red_500" fill="#E94235" d="M370.87,318.3c12.63,0,23.94,4.35,32.87,12.85l24.45-24.43
|
||||
c-14.85-13.83-34.21-22.32-57.32-22.32c-33.47,0-62.46,19.2-76.56,47.18l28.46,22.08C329.56,333.36,348.52,318.3,370.87,318.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3 KiB |
|
@ -4,7 +4,7 @@
|
|||
"description": "Googlepay module for PPCP",
|
||||
"license": "GPL-2.0",
|
||||
"require": {
|
||||
"php": "^7.2 | ^8.0",
|
||||
"php": "^7.4 | ^8.0",
|
||||
"dhii/module-interface": "^0.3.0-alpha1"
|
||||
},
|
||||
"autoload": {
|
||||
|
|
|
@ -18,7 +18,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
|
||||
return array(
|
||||
|
||||
'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array {
|
||||
'wcgateway.settings.fields' => function ( array $fields, ContainerInterface $container ): array {
|
||||
|
||||
// Used in various places to mark fields for the preview button.
|
||||
$apm_name = 'GooglePay';
|
||||
|
@ -72,7 +72,7 @@ return array(
|
|||
'googlepay_button_enabled' => array(
|
||||
'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/googlepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
'<img src="%sassets/images/googlepay.svg" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Google Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
|
@ -117,7 +117,7 @@ return array(
|
|||
'googlepay_button_enabled' => array(
|
||||
'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ),
|
||||
'title_html' => sprintf(
|
||||
'<img src="%sassets/images/googlepay.png" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
'<img src="%sassets/images/googlepay.svg" alt="%s" style="max-width: 150px; max-height: 45px;" />',
|
||||
$module_url,
|
||||
__( 'Google Pay', 'woocommerce-paypal-payments' )
|
||||
),
|
||||
|
@ -166,7 +166,7 @@ return array(
|
|||
'classes' => array( 'ppcp-field-indent' ),
|
||||
'class' => array(),
|
||||
'input_class' => array( 'wc-enhanced-select' ),
|
||||
'default' => 'pay',
|
||||
'default' => 'plain',
|
||||
'options' => PropertiesDictionary::button_types(),
|
||||
'screens' => array( State::STATE_ONBOARDED ),
|
||||
'gateway' => 'dcc',
|
||||
|
|
|
@ -9,8 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Googlepay;
|
||||
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
|
||||
return static function (): ModuleInterface {
|
||||
return static function (): GooglepayModule {
|
||||
return new GooglepayModule();
|
||||
};
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
/* Front end display */
|
||||
.ppcp-button-apm .gpay-card-info-container-fill .gpay-card-info-container {
|
||||
outline-offset: -1px;
|
||||
border-radius: var(--apm-button-border-radius);
|
||||
}
|
||||
|
||||
/* Admin preview */
|
||||
.ppcp-button-googlepay {
|
||||
min-height: 40px;
|
||||
|
||||
|
@ -18,3 +25,7 @@
|
|||
min-width: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
#ppc-button-ppcp-googlepay {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import CheckoutActionHandler from '../../../../ppcp-button/resources/js/modules/
|
|||
import FormValidator from '../../../../ppcp-button/resources/js/modules/Helper/FormValidator';
|
||||
|
||||
class CheckoutHandler extends BaseHandler {
|
||||
transactionInfo() {
|
||||
validateForm() {
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const spinner = new Spinner();
|
||||
|
@ -23,7 +23,7 @@ class CheckoutHandler extends BaseHandler {
|
|||
: null;
|
||||
|
||||
if ( ! formValidator ) {
|
||||
resolve( super.transactionInfo() );
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ class CheckoutHandler extends BaseHandler {
|
|||
|
||||
reject();
|
||||
} else {
|
||||
resolve( super.transactionInfo() );
|
||||
resolve();
|
||||
}
|
||||
} );
|
||||
} catch ( error ) {
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
import { GooglePayStorage } from '../Helper/GooglePayStorage';
|
||||
import {
|
||||
getWooCommerceCustomerDetails,
|
||||
setPayerData,
|
||||
} from '../../../../ppcp-button/resources/js/modules/Helper/PayerData';
|
||||
|
||||
const CHECKOUT_FORM_SELECTOR = 'form.woocommerce-checkout';
|
||||
|
||||
export class CheckoutBootstrap {
|
||||
/**
|
||||
* @type {GooglePayStorage}
|
||||
*/
|
||||
#storage;
|
||||
|
||||
/**
|
||||
* @type {HTMLFormElement|null}
|
||||
*/
|
||||
#checkoutForm;
|
||||
|
||||
/**
|
||||
* @param {GooglePayStorage} storage
|
||||
*/
|
||||
constructor( storage ) {
|
||||
this.#storage = storage;
|
||||
this.#checkoutForm = CheckoutBootstrap.getCheckoutForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the current page contains a checkout form.
|
||||
*
|
||||
* @return {boolean} True if a checkout form is present.
|
||||
*/
|
||||
static isPageWithCheckoutForm() {
|
||||
return null !== CheckoutBootstrap.getCheckoutForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the WooCommerce checkout form element.
|
||||
*
|
||||
* @return {HTMLFormElement|null} The form, or null if not a checkout page.
|
||||
*/
|
||||
static getCheckoutForm() {
|
||||
return document.querySelector( CHECKOUT_FORM_SELECTOR );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WooCommerce checkout form element.
|
||||
*
|
||||
* @return {HTMLFormElement|null} The form, or null if not a checkout page.
|
||||
*/
|
||||
get checkoutForm() {
|
||||
return this.#checkoutForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the checkout process.
|
||||
*
|
||||
* @throws {Error} If called on a page without a checkout form.
|
||||
*/
|
||||
init() {
|
||||
if ( ! this.#checkoutForm ) {
|
||||
throw new Error(
|
||||
'Checkout form not found. Cannot initialize CheckoutBootstrap.'
|
||||
);
|
||||
}
|
||||
|
||||
this.#populateCheckoutFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates checkout fields with stored or customer data.
|
||||
*/
|
||||
#populateCheckoutFields() {
|
||||
const loggedInData = getWooCommerceCustomerDetails();
|
||||
|
||||
if ( loggedInData ) {
|
||||
// If customer is logged in, we use the details from the customer profile.
|
||||
return;
|
||||
}
|
||||
|
||||
const billingData = this.#storage.getPayer();
|
||||
|
||||
if ( ! billingData ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPayerData( billingData, true );
|
||||
this.checkoutForm.addEventListener(
|
||||
'submit',
|
||||
this.#onFormSubmit.bind( this )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean-up when checkout form is submitted.
|
||||
*
|
||||
* Immediately removes the payer details from the localStorage.
|
||||
*/
|
||||
#onFormSubmit() {
|
||||
this.#storage.clearPayer();
|
||||
}
|
||||
}
|
|
@ -1,242 +1,412 @@
|
|||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import { setVisible } from '../../../ppcp-button/resources/js/modules/Helper/Hiding';
|
||||
import { setEnabled } from '../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler';
|
||||
import {
|
||||
combineStyles,
|
||||
combineWrapperIds,
|
||||
} from '../../../ppcp-button/resources/js/modules/Helper/PaymentButtonHelpers';
|
||||
import PaymentButton from '../../../ppcp-button/resources/js/modules/Renderer/PaymentButton';
|
||||
import widgetBuilder from '../../../ppcp-button/resources/js/modules/Renderer/WidgetBuilder';
|
||||
import UpdatePaymentData from './Helper/UpdatePaymentData';
|
||||
import { apmButtonsInit } from '../../../ppcp-button/resources/js/modules/Helper/ApmButtons';
|
||||
import { PaymentMethods } from '../../../ppcp-button/resources/js/modules/Helper/CheckoutMethodState';
|
||||
import { setPayerData } from '../../../ppcp-button/resources/js/modules/Helper/PayerData';
|
||||
import moduleStorage from './Helper/GooglePayStorage';
|
||||
|
||||
class GooglepayButton {
|
||||
constructor( context, externalHandler, buttonConfig, ppcpConfig ) {
|
||||
apmButtonsInit( ppcpConfig );
|
||||
/**
|
||||
* Plugin-specific styling.
|
||||
*
|
||||
* Note that most properties of this object do not apply to the Google Pay button.
|
||||
*
|
||||
* @typedef {Object} PPCPStyle
|
||||
* @property {string} shape - Outline shape.
|
||||
* @property {?number} height - Button height in pixel.
|
||||
*/
|
||||
|
||||
this.isInitialized = false;
|
||||
/**
|
||||
* Style options that are defined by the Google Pay SDK and are required to render the button.
|
||||
*
|
||||
* @typedef {Object} GooglePayStyle
|
||||
* @property {string} type - Defines the button label.
|
||||
* @property {string} color - Button color
|
||||
* @property {string} language - The locale; an empty string will apply the user-agent's language.
|
||||
*/
|
||||
|
||||
this.context = context;
|
||||
this.externalHandler = externalHandler;
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
/**
|
||||
* Google Pay JS SDK
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects
|
||||
* @typedef {Object} GooglePaySDK
|
||||
* @property {typeof PaymentsClient} PaymentsClient - Main API client for payment actions.
|
||||
*/
|
||||
|
||||
this.paymentsClient = null;
|
||||
/**
|
||||
* The Payments Client class, generated by the Google Pay SDK.
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/client
|
||||
* @typedef {Object} PaymentsClient
|
||||
* @property {Function} createButton - The convenience method is used to
|
||||
* generate a Google Pay payment button styled with the latest Google Pay branding for
|
||||
* insertion into a webpage.
|
||||
* @property {Function} isReadyToPay - Use the isReadyToPay(isReadyToPayRequest)
|
||||
* method to determine a user's ability to return a form of payment from the Google Pay API.
|
||||
* @property {(Object) => Promise} loadPaymentData - This method presents a Google Pay payment
|
||||
* sheet that allows selection of a payment method and optionally configured parameters
|
||||
* @property {Function} onPaymentAuthorized - This method is called when a payment is
|
||||
* authorized in the payment sheet.
|
||||
* @property {Function} onPaymentDataChanged - This method handles payment data changes
|
||||
* in the payment sheet such as shipping address and shipping options.
|
||||
*/
|
||||
|
||||
this.contextHandler = ContextHandlerFactory.create(
|
||||
this.context,
|
||||
this.buttonConfig,
|
||||
this.ppcpConfig,
|
||||
this.externalHandler
|
||||
/**
|
||||
* This object describes the transaction details.
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo
|
||||
* @typedef {Object} TransactionInfo
|
||||
* @property {string} currencyCode - Required. The ISO 4217 alphabetic currency code.
|
||||
* @property {string} countryCode - Optional. required for EEA countries,
|
||||
* @property {string} transactionId - Optional. A unique ID that identifies a facilitation
|
||||
* attempt. Highly encouraged for troubleshooting.
|
||||
* @property {string} totalPriceStatus - Required. [ESTIMATED|FINAL] The status of the total price
|
||||
* used:
|
||||
* @property {string} totalPrice - Required. Total monetary value of the transaction with an
|
||||
* optional decimal precision of two decimal places.
|
||||
* @property {Array} displayItems - Optional. A list of cart items shown in the payment sheet
|
||||
* (e.g. subtotals, sales taxes, shipping charges, discounts etc.).
|
||||
* @property {string} totalPriceLabel - Optional. Custom label for the total price within the
|
||||
* display items.
|
||||
* @property {string} checkoutOption - Optional. Affects the submit button text displayed in the
|
||||
* Google Pay payment sheet.
|
||||
*/
|
||||
|
||||
function payerDataFromPaymentResponse( response ) {
|
||||
const raw = response?.paymentMethodData?.info?.billingAddress;
|
||||
|
||||
return {
|
||||
email_address: response?.email,
|
||||
name: {
|
||||
given_name: raw.name.split( ' ' )[ 0 ], // Assuming first name is the first part
|
||||
surname: raw.name.split( ' ' ).slice( 1 ).join( ' ' ), // Assuming last name is the rest
|
||||
},
|
||||
address: {
|
||||
country_code: raw.countryCode,
|
||||
address_line_1: raw.address1,
|
||||
address_line_2: raw.address2,
|
||||
admin_area_1: raw.administrativeArea,
|
||||
admin_area_2: raw.locality,
|
||||
postal_code: raw.postalCode,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class GooglepayButton extends PaymentButton {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static methodId = PaymentMethods.GOOGLEPAY;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static cssClass = 'google-pay';
|
||||
|
||||
/**
|
||||
* Client reference, provided by the Google Pay JS SDK.
|
||||
*/
|
||||
#paymentsClient = null;
|
||||
|
||||
/**
|
||||
* Details about the processed transaction, provided to the Google SDK.
|
||||
*
|
||||
* @type {?TransactionInfo}
|
||||
*/
|
||||
#transactionInfo = null;
|
||||
|
||||
googlePayConfig = null;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static getWrappers( buttonConfig, ppcpConfig ) {
|
||||
return combineWrapperIds(
|
||||
buttonConfig?.button?.wrapper || '',
|
||||
buttonConfig?.button?.mini_cart_wrapper || '',
|
||||
ppcpConfig?.button?.wrapper || '',
|
||||
'ppc-button-googlepay-container',
|
||||
'ppc-button-ppcp-googlepay'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
static getStyles( buttonConfig, ppcpConfig ) {
|
||||
const styles = combineStyles(
|
||||
ppcpConfig?.button || {},
|
||||
buttonConfig?.button || {}
|
||||
);
|
||||
|
||||
this.log = function () {
|
||||
if ( this.buttonConfig.is_debug ) {
|
||||
//console.log('[GooglePayButton]', ...arguments);
|
||||
if ( 'buy' === styles.MiniCart.type ) {
|
||||
styles.MiniCart.type = 'pay';
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
constructor(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
) {
|
||||
// Disable debug output in the browser console:
|
||||
// buttonConfig.is_debug = false;
|
||||
|
||||
super(
|
||||
context,
|
||||
externalHandler,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
|
||||
this.init = this.init.bind( this );
|
||||
this.onPaymentAuthorized = this.onPaymentAuthorized.bind( this );
|
||||
this.onPaymentDataChanged = this.onPaymentDataChanged.bind( this );
|
||||
this.onButtonClick = this.onButtonClick.bind( this );
|
||||
|
||||
this.log( 'Create instance' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
get requiresShipping() {
|
||||
return super.requiresShipping && this.buttonConfig.shipping?.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google Pay API.
|
||||
*
|
||||
* @return {?GooglePaySDK} API for the Google Pay JS SDK, or null when SDK is not ready yet.
|
||||
*/
|
||||
get googlePayApi() {
|
||||
return window.google?.payments?.api;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Google Pay PaymentsClient instance created by this button.
|
||||
* @see https://developers.google.com/pay/api/web/reference/client
|
||||
*
|
||||
* @return {?PaymentsClient} The SDK object, or null when SDK is not ready yet.
|
||||
*/
|
||||
get paymentsClient() {
|
||||
return this.#paymentsClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Details about the processed transaction.
|
||||
*
|
||||
* This object defines the price that is charged, and text that is displayed inside the
|
||||
* payment sheet.
|
||||
*
|
||||
* @return {?TransactionInfo} The TransactionInfo object.
|
||||
*/
|
||||
get transactionInfo() {
|
||||
return this.#transactionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the new transaction details to the payment button.
|
||||
*
|
||||
* @param {TransactionInfo} newTransactionInfo - Transaction details.
|
||||
*/
|
||||
set transactionInfo( newTransactionInfo ) {
|
||||
this.#transactionInfo = newTransactionInfo;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
validateConfiguration( silent = false ) {
|
||||
const validEnvs = [ 'PRODUCTION', 'TEST' ];
|
||||
|
||||
const isInvalid = ( ...args ) => {
|
||||
if ( ! silent ) {
|
||||
this.error( ...args );
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
init( config ) {
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
this.isInitialized = true;
|
||||
|
||||
if ( ! this.validateConfig() ) {
|
||||
return;
|
||||
if ( ! validEnvs.includes( this.buttonConfig.environment ) ) {
|
||||
return isInvalid(
|
||||
'Invalid environment:',
|
||||
this.buttonConfig.environment
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler.validateContext() ) {
|
||||
return;
|
||||
// Preview buttons only need a valid environment.
|
||||
if ( this.isPreview ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.googlePayConfig = config;
|
||||
this.allowedPaymentMethods = config.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
|
||||
this.initClient();
|
||||
this.initEventHandlers();
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
config
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
if ( response.result ) {
|
||||
this.addButton( this.baseCardPaymentMethod );
|
||||
}
|
||||
} )
|
||||
.catch( function ( err ) {
|
||||
console.error( err );
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
return;
|
||||
return isInvalid(
|
||||
'No API configuration - missing configure() call?'
|
||||
);
|
||||
}
|
||||
|
||||
this.isInitialized = false;
|
||||
this.init( this.googlePayConfig );
|
||||
}
|
||||
|
||||
validateConfig() {
|
||||
if (
|
||||
[ 'PRODUCTION', 'TEST' ].indexOf(
|
||||
this.buttonConfig.environment
|
||||
) === -1
|
||||
) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid environment.',
|
||||
this.buttonConfig.environment
|
||||
if ( ! this.transactionInfo ) {
|
||||
return isInvalid(
|
||||
'No transactionInfo - missing configure() call?'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! this.contextHandler ) {
|
||||
console.error(
|
||||
'[GooglePayButton] Invalid context handler.',
|
||||
this.contextHandler
|
||||
);
|
||||
return false;
|
||||
if ( ! typeof this.contextHandler?.validateContext() ) {
|
||||
return isInvalid( 'Invalid context handler.', this.contextHandler );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configurations relative to this button context.
|
||||
* Configures the button instance. Must be called before the initial `init()`.
|
||||
*
|
||||
* @param {Object} apiConfig - API configuration.
|
||||
* @param {Object} transactionInfo - Transaction details; required before "init" call.
|
||||
*/
|
||||
contextConfig() {
|
||||
const config = {
|
||||
wrapper: this.buttonConfig.button.wrapper,
|
||||
ppcpStyle: this.ppcpConfig.button.style,
|
||||
buttonStyle: this.buttonConfig.button.style,
|
||||
ppcpButtonWrapper: this.ppcpConfig.button.wrapper,
|
||||
};
|
||||
configure( apiConfig, transactionInfo ) {
|
||||
this.googlePayConfig = apiConfig;
|
||||
this.#transactionInfo = transactionInfo;
|
||||
|
||||
if ( this.context === 'mini-cart' ) {
|
||||
config.wrapper = this.buttonConfig.button.mini_cart_wrapper;
|
||||
config.ppcpStyle = this.ppcpConfig.button.mini_cart_style;
|
||||
config.buttonStyle = this.buttonConfig.button.mini_cart_style;
|
||||
config.ppcpButtonWrapper = this.ppcpConfig.button.mini_cart_wrapper;
|
||||
|
||||
// Handle incompatible types.
|
||||
if ( config.buttonStyle.type === 'buy' ) {
|
||||
config.buttonStyle.type = 'pay';
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
[ 'cart-block', 'checkout-block' ].indexOf( this.context ) !== -1
|
||||
) {
|
||||
config.ppcpButtonWrapper =
|
||||
'#express-payment-method-ppcp-gateway-paypal';
|
||||
}
|
||||
|
||||
return config;
|
||||
this.allowedPaymentMethods = this.googlePayConfig.allowedPaymentMethods;
|
||||
this.baseCardPaymentMethod = this.allowedPaymentMethods[ 0 ];
|
||||
}
|
||||
|
||||
initClient() {
|
||||
const callbacks = {
|
||||
onPaymentAuthorized: this.onPaymentAuthorized.bind( this ),
|
||||
};
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
callbacks.onPaymentDataChanged =
|
||||
this.onPaymentDataChanged.bind( this );
|
||||
init() {
|
||||
// Use `reinit()` to force a full refresh of an initialized button.
|
||||
if ( this.isInitialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentsClient = new google.payments.api.PaymentsClient( {
|
||||
// Stop, if configuration is invalid.
|
||||
if ( ! this.validateConfiguration() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.init();
|
||||
this.#paymentsClient = this.createPaymentsClient();
|
||||
|
||||
if ( ! this.isPresent ) {
|
||||
this.log( 'Payment wrapper not found', this.wrapperId );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! this.paymentsClient ) {
|
||||
this.log( 'Could not initialize the payments client' );
|
||||
return;
|
||||
}
|
||||
|
||||
this.paymentsClient
|
||||
.isReadyToPay(
|
||||
this.buildReadyToPayRequest(
|
||||
this.allowedPaymentMethods,
|
||||
this.googlePayConfig
|
||||
)
|
||||
)
|
||||
.then( ( response ) => {
|
||||
this.log( 'PaymentsClient.isReadyToPay response:', response );
|
||||
this.isEligible = !! response.result;
|
||||
} )
|
||||
.catch( ( err ) => {
|
||||
this.error( err );
|
||||
this.isEligible = false;
|
||||
} );
|
||||
}
|
||||
|
||||
reinit() {
|
||||
// Missing (invalid) configuration indicates, that the first `init()` call did not happen yet.
|
||||
if ( ! this.validateConfiguration( true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.reinit();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an object with relevant paymentDataCallbacks for the current button instance.
|
||||
*
|
||||
* @return {Object} An object containing callbacks for the current scope & configuration.
|
||||
*/
|
||||
preparePaymentDataCallbacks() {
|
||||
const callbacks = {};
|
||||
|
||||
// We do not attach any callbacks to preview buttons.
|
||||
if ( this.isPreview ) {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
callbacks.onPaymentAuthorized = this.onPaymentAuthorized;
|
||||
|
||||
if ( this.requiresShipping ) {
|
||||
callbacks.onPaymentDataChanged = this.onPaymentDataChanged;
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
createPaymentsClient() {
|
||||
if ( ! this.googlePayApi ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const callbacks = this.preparePaymentDataCallbacks();
|
||||
|
||||
/**
|
||||
* Consider providing merchant info here:
|
||||
*
|
||||
* @see https://developers.google.com/pay/api/web/reference/request-objects#PaymentOptions
|
||||
*/
|
||||
return new this.googlePayApi.PaymentsClient( {
|
||||
environment: this.buttonConfig.environment,
|
||||
// add merchant info maybe
|
||||
paymentDataCallbacks: callbacks,
|
||||
} );
|
||||
}
|
||||
|
||||
initEventHandlers() {
|
||||
const { wrapper, ppcpButtonWrapper } = this.contextConfig();
|
||||
|
||||
if ( wrapper === ppcpButtonWrapper ) {
|
||||
throw new Error(
|
||||
`[GooglePayButton] "wrapper" and "ppcpButtonWrapper" values must differ to avoid infinite loop. Current value: "${ wrapper }"`
|
||||
);
|
||||
}
|
||||
|
||||
const syncButtonVisibility = () => {
|
||||
const $ppcpButtonWrapper = jQuery( ppcpButtonWrapper );
|
||||
setVisible( wrapper, $ppcpButtonWrapper.is( ':visible' ) );
|
||||
setEnabled(
|
||||
wrapper,
|
||||
! $ppcpButtonWrapper.hasClass( 'ppcp-disabled' )
|
||||
);
|
||||
};
|
||||
|
||||
jQuery( document ).on(
|
||||
'ppcp-shown ppcp-hidden ppcp-enabled ppcp-disabled',
|
||||
( ev, data ) => {
|
||||
if ( jQuery( data.selector ).is( ppcpButtonWrapper ) ) {
|
||||
syncButtonVisibility();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
syncButtonVisibility();
|
||||
}
|
||||
|
||||
buildReadyToPayRequest( allowedPaymentMethods, baseRequest ) {
|
||||
this.log( 'Ready To Pay request', baseRequest, allowedPaymentMethods );
|
||||
|
||||
return Object.assign( {}, baseRequest, {
|
||||
allowedPaymentMethods,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Google Pay purchase button
|
||||
* @param baseCardPaymentMethod
|
||||
* Creates the payment button and calls `this.insertButton()` to make the button visible in the
|
||||
* correct wrapper.
|
||||
*/
|
||||
addButton( baseCardPaymentMethod ) {
|
||||
this.log( 'addButton', this.context );
|
||||
addButton() {
|
||||
if ( ! this.paymentsClient ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { wrapper, ppcpStyle, buttonStyle } = this.contextConfig();
|
||||
const baseCardPaymentMethod = this.baseCardPaymentMethod;
|
||||
const { color, type, language } = this.style;
|
||||
|
||||
this.waitForWrapper( wrapper, () => {
|
||||
const $wrapper = jQuery( wrapper );
|
||||
|
||||
$wrapper.removeClass( 'ppcp-button-rect ppcp-button-pill' );
|
||||
$wrapper.addClass( 'ppcp-button-' + ppcpStyle.shape );
|
||||
|
||||
if ( ppcpStyle.height ) {
|
||||
$wrapper.css( 'height', `${ ppcpStyle.height }px` );
|
||||
}
|
||||
|
||||
const button = this.paymentsClient.createButton( {
|
||||
onClick: this.onButtonClick.bind( this ),
|
||||
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
||||
buttonColor: buttonStyle.color || 'black',
|
||||
buttonType: buttonStyle.type || 'pay',
|
||||
buttonLocale: buttonStyle.language || 'en',
|
||||
buttonSizeMode: 'fill',
|
||||
} );
|
||||
|
||||
$wrapper.append( button );
|
||||
/**
|
||||
* @see https://developers.google.com/pay/api/web/reference/client#createButton
|
||||
*/
|
||||
const button = this.paymentsClient.createButton( {
|
||||
onClick: this.onButtonClick,
|
||||
allowedPaymentMethods: [ baseCardPaymentMethod ],
|
||||
buttonColor: color || 'black',
|
||||
buttonType: type || 'pay',
|
||||
buttonLocale: language || 'en',
|
||||
buttonSizeMode: 'fill',
|
||||
} );
|
||||
}
|
||||
|
||||
waitForWrapper( selector, callback, delay = 100, timeout = 2000 ) {
|
||||
const startTime = Date.now();
|
||||
const interval = setInterval( () => {
|
||||
const el = document.querySelector( selector );
|
||||
const timeElapsed = Date.now() - startTime;
|
||||
|
||||
if ( el ) {
|
||||
clearInterval( interval );
|
||||
callback( el );
|
||||
} else if ( timeElapsed > timeout ) {
|
||||
clearInterval( interval );
|
||||
}
|
||||
}, delay );
|
||||
this.insertButton( button );
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
@ -246,53 +416,78 @@ class GooglepayButton {
|
|||
/**
|
||||
* Show Google Pay payment sheet when Google Pay payment button is clicked
|
||||
*/
|
||||
async onButtonClick() {
|
||||
this.log( 'onButtonClick', this.context );
|
||||
onButtonClick() {
|
||||
this.log( 'onButtonClick' );
|
||||
|
||||
const paymentDataRequest = await this.paymentDataRequest();
|
||||
this.log(
|
||||
'onButtonClick: paymentDataRequest',
|
||||
paymentDataRequest,
|
||||
this.context
|
||||
);
|
||||
const initiatePaymentRequest = () => {
|
||||
window.ppcpFundingSource = 'googlepay';
|
||||
const paymentDataRequest = this.paymentDataRequest();
|
||||
|
||||
window.ppcpFundingSource = 'googlepay'; // Do this on another place like on create order endpoint handler.
|
||||
this.log(
|
||||
'onButtonClick: paymentDataRequest',
|
||||
paymentDataRequest,
|
||||
this.context
|
||||
);
|
||||
|
||||
this.paymentsClient.loadPaymentData( paymentDataRequest );
|
||||
return this.paymentsClient.loadPaymentData( paymentDataRequest );
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
if ( 'function' !== typeof this.contextHandler.validateForm ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.contextHandler.validateForm().catch( ( error ) => {
|
||||
this.error( 'Form validation failed:', error );
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
const getTransactionInfo = () => {
|
||||
if ( 'function' !== typeof this.contextHandler.transactionInfo ) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.contextHandler
|
||||
.transactionInfo()
|
||||
.then( ( transactionInfo ) => {
|
||||
this.transactionInfo = transactionInfo;
|
||||
} )
|
||||
.catch( ( error ) => {
|
||||
this.error( 'Failed to get transaction info:', error );
|
||||
throw error;
|
||||
} );
|
||||
};
|
||||
|
||||
validateForm()
|
||||
.then( getTransactionInfo )
|
||||
.then( initiatePaymentRequest );
|
||||
}
|
||||
|
||||
async paymentDataRequest() {
|
||||
paymentDataRequest() {
|
||||
const baseRequest = {
|
||||
apiVersion: 2,
|
||||
apiVersionMinor: 0,
|
||||
};
|
||||
|
||||
const googlePayConfig = this.googlePayConfig;
|
||||
const paymentDataRequest = Object.assign( {}, baseRequest );
|
||||
paymentDataRequest.allowedPaymentMethods =
|
||||
googlePayConfig.allowedPaymentMethods;
|
||||
paymentDataRequest.transactionInfo =
|
||||
await this.contextHandler.transactionInfo();
|
||||
paymentDataRequest.merchantInfo = googlePayConfig.merchantInfo;
|
||||
const useShippingCallback = this.requiresShipping;
|
||||
const callbackIntents = [ 'PAYMENT_AUTHORIZATION' ];
|
||||
|
||||
if (
|
||||
this.buttonConfig.shipping.enabled &&
|
||||
this.contextHandler.shippingAllowed()
|
||||
) {
|
||||
paymentDataRequest.callbackIntents = [
|
||||
'SHIPPING_ADDRESS',
|
||||
'SHIPPING_OPTION',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
];
|
||||
paymentDataRequest.shippingAddressRequired = true;
|
||||
paymentDataRequest.shippingAddressParameters =
|
||||
this.shippingAddressParameters();
|
||||
paymentDataRequest.shippingOptionRequired = true;
|
||||
} else {
|
||||
paymentDataRequest.callbackIntents = [ 'PAYMENT_AUTHORIZATION' ];
|
||||
if ( useShippingCallback ) {
|
||||
callbackIntents.push( 'SHIPPING_ADDRESS', 'SHIPPING_OPTION' );
|
||||
}
|
||||
|
||||
return paymentDataRequest;
|
||||
return {
|
||||
...baseRequest,
|
||||
allowedPaymentMethods: this.googlePayConfig.allowedPaymentMethods,
|
||||
transactionInfo: this.transactionInfo,
|
||||
merchantInfo: this.googlePayConfig.merchantInfo,
|
||||
callbackIntents,
|
||||
emailRequired: true,
|
||||
shippingAddressRequired: useShippingCallback,
|
||||
shippingOptionRequired: useShippingCallback,
|
||||
shippingAddressParameters: this.shippingAddressParameters(),
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------
|
||||
|
@ -307,47 +502,54 @@ class GooglepayButton {
|
|||
}
|
||||
|
||||
onPaymentDataChanged( paymentData ) {
|
||||
this.log( 'onPaymentDataChanged', this.context );
|
||||
this.log( 'paymentData', paymentData );
|
||||
this.log( 'onPaymentDataChanged', paymentData );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
const paymentDataRequestUpdate = {};
|
||||
try {
|
||||
const paymentDataRequestUpdate = {};
|
||||
|
||||
const updatedData = await new UpdatePaymentData(
|
||||
this.buttonConfig.ajax.update_payment_data
|
||||
).update( paymentData );
|
||||
const transactionInfo = await this.contextHandler.transactionInfo();
|
||||
const updatedData = await new UpdatePaymentData(
|
||||
this.buttonConfig.ajax.update_payment_data
|
||||
).update( paymentData );
|
||||
const transactionInfo = this.transactionInfo;
|
||||
|
||||
this.log( 'onPaymentDataChanged:updatedData', updatedData );
|
||||
this.log( 'onPaymentDataChanged:transactionInfo', transactionInfo );
|
||||
this.log( 'onPaymentDataChanged:updatedData', updatedData );
|
||||
this.log(
|
||||
'onPaymentDataChanged:transactionInfo',
|
||||
transactionInfo
|
||||
);
|
||||
|
||||
updatedData.country_code = transactionInfo.countryCode;
|
||||
updatedData.currency_code = transactionInfo.currencyCode;
|
||||
updatedData.total_str = transactionInfo.totalPrice;
|
||||
updatedData.country_code = transactionInfo.countryCode;
|
||||
updatedData.currency_code = transactionInfo.currencyCode;
|
||||
updatedData.total_str = transactionInfo.totalPrice;
|
||||
|
||||
// Handle unserviceable address.
|
||||
if ( ! updatedData.shipping_options?.shippingOptions?.length ) {
|
||||
paymentDataRequestUpdate.error =
|
||||
this.unserviceableShippingAddressError();
|
||||
resolve( paymentDataRequestUpdate );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( paymentData.callbackTrigger ) {
|
||||
case 'INITIALIZE':
|
||||
case 'SHIPPING_ADDRESS':
|
||||
paymentDataRequestUpdate.newShippingOptionParameters =
|
||||
updatedData.shipping_options;
|
||||
paymentDataRequestUpdate.newTransactionInfo =
|
||||
this.calculateNewTransactionInfo( updatedData );
|
||||
break;
|
||||
case 'SHIPPING_OPTION':
|
||||
paymentDataRequestUpdate.newTransactionInfo =
|
||||
this.calculateNewTransactionInfo( updatedData );
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle unserviceable address.
|
||||
if ( ! updatedData.shipping_options?.shippingOptions?.length ) {
|
||||
paymentDataRequestUpdate.error =
|
||||
this.unserviceableShippingAddressError();
|
||||
resolve( paymentDataRequestUpdate );
|
||||
return;
|
||||
} catch ( error ) {
|
||||
this.error( 'Error during onPaymentDataChanged:', error );
|
||||
reject( error );
|
||||
}
|
||||
|
||||
switch ( paymentData.callbackTrigger ) {
|
||||
case 'INITIALIZE':
|
||||
case 'SHIPPING_ADDRESS':
|
||||
paymentDataRequestUpdate.newShippingOptionParameters =
|
||||
updatedData.shipping_options;
|
||||
paymentDataRequestUpdate.newTransactionInfo =
|
||||
this.calculateNewTransactionInfo( updatedData );
|
||||
break;
|
||||
case 'SHIPPING_OPTION':
|
||||
paymentDataRequestUpdate.newTransactionInfo =
|
||||
this.calculateNewTransactionInfo( updatedData );
|
||||
break;
|
||||
}
|
||||
|
||||
resolve( paymentDataRequestUpdate );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -373,84 +575,111 @@ class GooglepayButton {
|
|||
//------------------------
|
||||
|
||||
onPaymentAuthorized( paymentData ) {
|
||||
this.log( 'onPaymentAuthorized', this.context );
|
||||
this.log( 'onPaymentAuthorized', paymentData );
|
||||
|
||||
return this.processPayment( paymentData );
|
||||
}
|
||||
|
||||
async processPayment( paymentData ) {
|
||||
this.log( 'processPayment', this.context );
|
||||
this.logGroup( 'processPayment' );
|
||||
|
||||
return new Promise( async ( resolve, reject ) => {
|
||||
try {
|
||||
const id = await this.contextHandler.createOrder();
|
||||
const payer = payerDataFromPaymentResponse( paymentData );
|
||||
|
||||
this.log( 'processPayment: createOrder', id, this.context );
|
||||
const paymentError = ( reason ) => {
|
||||
this.error( reason );
|
||||
|
||||
const confirmOrderResponse = await widgetBuilder.paypal
|
||||
.Googlepay()
|
||||
.confirmOrder( {
|
||||
orderId: id,
|
||||
paymentMethodData: paymentData.paymentMethodData,
|
||||
} );
|
||||
return this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
reason
|
||||
);
|
||||
};
|
||||
|
||||
this.log(
|
||||
'processPayment: confirmOrder',
|
||||
confirmOrderResponse,
|
||||
this.context
|
||||
);
|
||||
const checkPayPalApproval = async ( orderId ) => {
|
||||
const confirmationData = {
|
||||
orderId,
|
||||
paymentMethodData: paymentData.paymentMethodData,
|
||||
};
|
||||
|
||||
/** Capture the Order on the Server */
|
||||
if ( confirmOrderResponse.status === 'APPROVED' ) {
|
||||
let approveFailed = false;
|
||||
await this.contextHandler.approveOrder(
|
||||
{
|
||||
orderID: id,
|
||||
},
|
||||
{
|
||||
// actions mock object.
|
||||
restart: () =>
|
||||
new Promise( ( resolve, reject ) => {
|
||||
approveFailed = true;
|
||||
resolve();
|
||||
} ),
|
||||
order: {
|
||||
get: () =>
|
||||
new Promise( ( resolve, reject ) => {
|
||||
resolve( null );
|
||||
} ),
|
||||
},
|
||||
}
|
||||
);
|
||||
const confirmOrderResponse = await widgetBuilder.paypal
|
||||
.Googlepay()
|
||||
.confirmOrder( confirmationData );
|
||||
|
||||
if ( ! approveFailed ) {
|
||||
resolve( this.processPaymentResponse( 'SUCCESS' ) );
|
||||
} else {
|
||||
resolve(
|
||||
this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
'FAILED TO APPROVE'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
resolve(
|
||||
this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
'TRANSACTION FAILED'
|
||||
)
|
||||
);
|
||||
this.log( 'confirmOrder', confirmOrderResponse );
|
||||
|
||||
return 'APPROVED' === confirmOrderResponse?.status;
|
||||
};
|
||||
|
||||
/**
|
||||
* This approval mainly confirms that the orderID is valid.
|
||||
*
|
||||
* It's still needed because this handler redirects to the checkout page if the server-side
|
||||
* approval was successful.
|
||||
*
|
||||
* @param {string} orderID
|
||||
*/
|
||||
const approveOrderServerSide = async ( orderID ) => {
|
||||
let isApproved = true;
|
||||
|
||||
this.log( 'approveOrder', orderID );
|
||||
|
||||
await this.contextHandler.approveOrder(
|
||||
{ orderID, payer },
|
||||
{
|
||||
restart: () =>
|
||||
new Promise( ( resolve ) => {
|
||||
isApproved = false;
|
||||
resolve();
|
||||
} ),
|
||||
order: {
|
||||
get: () =>
|
||||
new Promise( ( resolve ) => {
|
||||
resolve( null );
|
||||
} ),
|
||||
},
|
||||
}
|
||||
} catch ( err ) {
|
||||
resolve(
|
||||
this.processPaymentResponse(
|
||||
'ERROR',
|
||||
'PAYMENT_AUTHORIZATION',
|
||||
err.message
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
return isApproved;
|
||||
};
|
||||
|
||||
const processPaymentPromise = async ( resolve ) => {
|
||||
const id = await this.contextHandler.createOrder();
|
||||
|
||||
this.log( 'createOrder', id );
|
||||
|
||||
const isApprovedByPayPal = await checkPayPalApproval( id );
|
||||
|
||||
if ( ! isApprovedByPayPal ) {
|
||||
resolve( paymentError( 'TRANSACTION FAILED' ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// This must be the last step in the process, as it initiates a redirect.
|
||||
const success = await approveOrderServerSide( id );
|
||||
|
||||
if ( success ) {
|
||||
resolve( this.processPaymentResponse( 'SUCCESS' ) );
|
||||
} else {
|
||||
resolve( paymentError( 'FAILED TO APPROVE' ) );
|
||||
}
|
||||
};
|
||||
|
||||
const addBillingDataToSession = () => {
|
||||
moduleStorage.setPayer( payer );
|
||||
setPayerData( payer );
|
||||
};
|
||||
|
||||
return new Promise( async ( resolve ) => {
|
||||
try {
|
||||
addBillingDataToSession();
|
||||
await processPaymentPromise( resolve );
|
||||
} catch ( err ) {
|
||||
resolve( paymentError( err.message ) );
|
||||
}
|
||||
|
||||
this.logGroup();
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -466,7 +695,7 @@ class GooglepayButton {
|
|||
};
|
||||
}
|
||||
|
||||
this.log( 'processPaymentResponse', response, this.context );
|
||||
this.log( 'processPaymentResponse', response );
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -1,39 +1,92 @@
|
|||
import buttonModuleWatcher from '../../../ppcp-button/resources/js/modules/ButtonModuleWatcher';
|
||||
import GooglepayButton from './GooglepayButton';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
|
||||
class GooglepayManager {
|
||||
constructor( buttonConfig, ppcpConfig ) {
|
||||
this.buttonConfig = buttonConfig;
|
||||
this.ppcpConfig = ppcpConfig;
|
||||
this.googlePayConfig = null;
|
||||
this.transactionInfo = null;
|
||||
this.contextHandler = null;
|
||||
|
||||
this.buttons = [];
|
||||
|
||||
buttonModuleWatcher.watchContextBootstrap( ( bootstrap ) => {
|
||||
const button = new GooglepayButton(
|
||||
buttonModuleWatcher.watchContextBootstrap( async ( bootstrap ) => {
|
||||
this.contextHandler = ContextHandlerFactory.create(
|
||||
bootstrap.context,
|
||||
buttonConfig,
|
||||
ppcpConfig,
|
||||
bootstrap.handler
|
||||
);
|
||||
|
||||
const button = GooglepayButton.createButton(
|
||||
bootstrap.context,
|
||||
bootstrap.handler,
|
||||
buttonConfig,
|
||||
ppcpConfig
|
||||
ppcpConfig,
|
||||
this.contextHandler
|
||||
);
|
||||
|
||||
this.buttons.push( button );
|
||||
|
||||
if ( this.googlePayConfig ) {
|
||||
button.init( this.googlePayConfig );
|
||||
const initButton = () => {
|
||||
button.configure( this.googlePayConfig, this.transactionInfo );
|
||||
button.init();
|
||||
};
|
||||
|
||||
// Initialize button only if googlePayConfig and transactionInfo are already fetched.
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
initButton();
|
||||
} else {
|
||||
await this.init();
|
||||
|
||||
if ( this.googlePayConfig && this.transactionInfo ) {
|
||||
initButton();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
init() {
|
||||
( async () => {
|
||||
// Gets GooglePay configuration of the PayPal merchant.
|
||||
this.googlePayConfig = await paypal.Googlepay().config();
|
||||
|
||||
for ( const button of this.buttons ) {
|
||||
button.init( this.googlePayConfig );
|
||||
async init() {
|
||||
try {
|
||||
if ( ! this.googlePayConfig ) {
|
||||
// Gets GooglePay configuration of the PayPal merchant.
|
||||
this.googlePayConfig = await paypal.Googlepay().config();
|
||||
}
|
||||
} )();
|
||||
|
||||
if ( ! this.transactionInfo ) {
|
||||
this.transactionInfo = await this.fetchTransactionInfo();
|
||||
}
|
||||
|
||||
if ( ! this.googlePayConfig ) {
|
||||
console.error( 'No GooglePayConfig received during init' );
|
||||
} else if ( ! this.transactionInfo ) {
|
||||
console.error( 'No transactionInfo found during init' );
|
||||
} else {
|
||||
for ( const button of this.buttons ) {
|
||||
button.configure(
|
||||
this.googlePayConfig,
|
||||
this.transactionInfo
|
||||
);
|
||||
button.init();
|
||||
}
|
||||
}
|
||||
} catch ( error ) {
|
||||
console.error( 'Error during initialization:', error );
|
||||
}
|
||||
}
|
||||
|
||||
async fetchTransactionInfo() {
|
||||
try {
|
||||
if ( ! this.contextHandler ) {
|
||||
throw new Error( 'ContextHandler is not initialized' );
|
||||
}
|
||||
return await this.contextHandler.transactionInfo();
|
||||
} catch ( error ) {
|
||||
console.error( 'Error fetching transaction info:', error );
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
reinit() {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import PreviewButton from '../../../ppcp-button/resources/js/modules/Renderer/PreviewButton';
|
||||
import ContextHandlerFactory from './Context/ContextHandlerFactory';
|
||||
import GooglepayButton from './GooglepayButton';
|
||||
|
||||
/**
|
||||
* A single GooglePay preview button instance.
|
||||
*/
|
||||
export default class GooglePayPreviewButton extends PreviewButton {
|
||||
/**
|
||||
* Instance of the preview button.
|
||||
*
|
||||
* @type {?PaymentButton}
|
||||
*/
|
||||
#button = null;
|
||||
|
||||
constructor( args ) {
|
||||
super( args );
|
||||
|
||||
this.selector = `${ args.selector }GooglePay`;
|
||||
this.defaultAttributes = {
|
||||
button: {
|
||||
style: {
|
||||
type: 'pay',
|
||||
color: 'black',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createNewWrapper() {
|
||||
const element = super.createNewWrapper();
|
||||
element.addClass( 'ppcp-button-googlepay' );
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createButton( buttonConfig ) {
|
||||
const contextHandler = ContextHandlerFactory.create(
|
||||
'preview',
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
null
|
||||
);
|
||||
|
||||
if ( ! this.#button ) {
|
||||
/* Intentionally using `new` keyword, instead of the `.createButton()` factory,
|
||||
* as the factory is designed to only create a single button per context, while a single
|
||||
* page can contain multiple instances of a preview button.
|
||||
*/
|
||||
this.#button = new GooglepayButton(
|
||||
'preview',
|
||||
null,
|
||||
buttonConfig,
|
||||
this.ppcpConfig,
|
||||
contextHandler
|
||||
);
|
||||
}
|
||||
|
||||
this.#button.configure( this.apiConfig, null );
|
||||
this.#button.applyButtonStyles( buttonConfig, this.ppcpConfig );
|
||||
this.#button.reinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge form details into the config object for preview.
|
||||
* Mutates the previewConfig object; no return value.
|
||||
*
|
||||
* @param {Object} buttonConfig
|
||||
* @param {Object} ppcpConfig
|
||||
*/
|
||||
dynamicPreviewConfig( buttonConfig, ppcpConfig ) {
|
||||
// Merge the current form-values into the preview-button configuration.
|
||||
if ( ppcpConfig.button && buttonConfig.button ) {
|
||||
Object.assign( buttonConfig.button.style, ppcpConfig.button.style );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { LocalStorage } from '../../../../ppcp-button/resources/js/modules/Helper/LocalStorage';
|
||||
|
||||
export class GooglePayStorage extends LocalStorage {
|
||||
static PAYER = 'payer';
|
||||
static PAYER_TTL = 900; // 15 minutes in seconds
|
||||
|
||||
constructor() {
|
||||
super( 'ppcp-googlepay' );
|
||||
}
|
||||
|
||||
getPayer() {
|
||||
return this.get( GooglePayStorage.PAYER );
|
||||
}
|
||||
|
||||
setPayer( data ) {
|
||||
/*
|
||||
* The payer details are deleted on successful checkout, or after the TTL is reached.
|
||||
* This helps to remove stale data from the browser, in case the customer chooses to
|
||||
* use a different method to complete the purchase.
|
||||
*/
|
||||
this.set( GooglePayStorage.PAYER, data, GooglePayStorage.PAYER_TTL );
|
||||
}
|
||||
|
||||
clearPayer() {
|
||||
this.clear( GooglePayStorage.PAYER );
|
||||
}
|
||||
}
|
||||
|
||||
const moduleStorage = new GooglePayStorage();
|
||||
|
||||
export default moduleStorage;
|
|
@ -1,28 +1,62 @@
|
|||
/**
|
||||
* Initialize the GooglePay module in the front end.
|
||||
* In some cases, this module is loaded when the `window.PayPalCommerceGateway` object is not
|
||||
* present. In that case, the page does not contain a Google Pay button, but some other logic
|
||||
* that is related to Google Pay (e.g., the CheckoutBootstrap module)
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
import { loadCustomScript } from '@paypal/paypal-js';
|
||||
import { loadPaypalScript } from '../../../ppcp-button/resources/js/modules/Helper/ScriptLoading';
|
||||
import GooglepayManager from './GooglepayManager';
|
||||
import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Helper/ButtonRefreshHelper';
|
||||
import { CheckoutBootstrap } from './ContextBootstrap/CheckoutBootstrap';
|
||||
import moduleStorage from './Helper/GooglePayStorage';
|
||||
|
||||
( function ( { buttonConfig, ppcpConfig, jQuery } ) {
|
||||
let manager;
|
||||
( function ( { buttonConfig, ppcpConfig = {} } ) {
|
||||
const context = ppcpConfig.context;
|
||||
|
||||
const bootstrap = function () {
|
||||
manager = new GooglepayManager( buttonConfig, ppcpConfig );
|
||||
manager.init();
|
||||
};
|
||||
|
||||
setupButtonEvents( function () {
|
||||
if ( manager ) {
|
||||
manager.reinit();
|
||||
function bootstrapPayButton() {
|
||||
if ( ! buttonConfig || ! ppcpConfig ) {
|
||||
return;
|
||||
}
|
||||
} );
|
||||
|
||||
const manager = new GooglepayManager( buttonConfig, ppcpConfig );
|
||||
manager.init();
|
||||
|
||||
setupButtonEvents( function () {
|
||||
manager.reinit();
|
||||
} );
|
||||
}
|
||||
|
||||
function bootstrapCheckout() {
|
||||
if ( context && ! [ 'continuation', 'checkout' ].includes( context ) ) {
|
||||
// Context must be missing/empty, or "continuation"/"checkout" to proceed.
|
||||
return;
|
||||
}
|
||||
if ( ! CheckoutBootstrap.isPageWithCheckoutForm() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const checkoutBootstrap = new CheckoutBootstrap( moduleStorage );
|
||||
checkoutBootstrap.init();
|
||||
}
|
||||
|
||||
function bootstrap() {
|
||||
bootstrapPayButton();
|
||||
bootstrapCheckout();
|
||||
}
|
||||
|
||||
document.addEventListener( 'DOMContentLoaded', () => {
|
||||
if (
|
||||
typeof buttonConfig === 'undefined' ||
|
||||
typeof ppcpConfig === 'undefined'
|
||||
) {
|
||||
// No PayPal buttons present on this page.
|
||||
if ( ! buttonConfig || ! ppcpConfig ) {
|
||||
/*
|
||||
* No PayPal buttons present on this page, but maybe a bootstrap module needs to be
|
||||
* initialized. Skip loading the SDK or gateway configuration, and directly initialize
|
||||
* the module.
|
||||
*/
|
||||
bootstrap();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -52,5 +86,4 @@ import { setupButtonEvents } from '../../../ppcp-button/resources/js/modules/Hel
|
|||
} )( {
|
||||
buttonConfig: window.wc_ppcp_googlepay,
|
||||
ppcpConfig: window.PayPalCommerceGateway,
|
||||
jQuery: window.jQuery,
|
||||
} );
|
||||
|
|
|
@ -25,23 +25,24 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
|||
return array(
|
||||
|
||||
// If GooglePay can be configured.
|
||||
'googlepay.eligible' => static function ( ContainerInterface $container ): bool {
|
||||
'googlepay.eligible' => static function ( ContainerInterface $container ): bool {
|
||||
$apm_applies = $container->get( 'googlepay.helpers.apm-applies' );
|
||||
assert( $apm_applies instanceof ApmApplies );
|
||||
|
||||
return $apm_applies->for_country_currency();
|
||||
return $apm_applies->for_country() && $apm_applies->for_currency();
|
||||
},
|
||||
|
||||
'googlepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
'googlepay.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
|
||||
return new ApmApplies(
|
||||
$container->get( 'googlepay.supported-country-currency-matrix' ),
|
||||
$container->get( 'googlepay.supported-countries' ),
|
||||
$container->get( 'googlepay.supported-currencies' ),
|
||||
$container->get( 'api.shop.currency' ),
|
||||
$container->get( 'api.shop.country' )
|
||||
);
|
||||
},
|
||||
|
||||
// If GooglePay is configured and onboarded.
|
||||
'googlepay.available' => static function ( ContainerInterface $container ): bool {
|
||||
'googlepay.available' => static function ( ContainerInterface $container ): bool {
|
||||
if ( apply_filters( 'woocommerce_paypal_payments_googlepay_validate_product_status', true ) ) {
|
||||
$status = $container->get( 'googlepay.helpers.apm-product-status' );
|
||||
assert( $status instanceof ApmProductStatus );
|
||||
|
@ -54,14 +55,14 @@ return array(
|
|||
},
|
||||
|
||||
// We assume it's a referral if we can check product status without API request failures.
|
||||
'googlepay.is_referral' => static function ( ContainerInterface $container ): bool {
|
||||
'googlepay.is_referral' => static function ( ContainerInterface $container ): bool {
|
||||
$status = $container->get( 'googlepay.helpers.apm-product-status' );
|
||||
assert( $status instanceof ApmProductStatus );
|
||||
|
||||
return ! $status->has_request_failure();
|
||||
},
|
||||
|
||||
'googlepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice {
|
||||
'googlepay.availability_notice' => static function ( ContainerInterface $container ): AvailabilityNotice {
|
||||
return new AvailabilityNotice(
|
||||
$container->get( 'googlepay.helpers.apm-product-status' ),
|
||||
$container->get( 'wcgateway.is-wc-gateways-list-page' ),
|
||||
|
@ -69,7 +70,7 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'googlepay.helpers.apm-product-status' => SingletonDecorator::make(
|
||||
'googlepay.helpers.apm-product-status' => SingletonDecorator::make(
|
||||
static function( ContainerInterface $container ): ApmProductStatus {
|
||||
return new ApmProductStatus(
|
||||
$container->get( 'wcgateway.settings' ),
|
||||
|
@ -81,772 +82,93 @@ return array(
|
|||
),
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* The list of which countries can be used for GooglePay.
|
||||
*/
|
||||
'googlepay.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array {
|
||||
'googlepay.supported-countries' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which countries and currency combinations can be used for GooglePay.
|
||||
* Returns which countries can be used for GooglePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_googlepay_supported_country_currency_matrix',
|
||||
'woocommerce_paypal_payments_googlepay_supported_countries',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'AT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'BG' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CA' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CY' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'CZ' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'EE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'FR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'DE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GR' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'HU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'IT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LV' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'LU' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'MT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'NL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PL' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'PT' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'RO' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SK' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SI' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'ES' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'SE' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'GB' => array(
|
||||
'AUD',
|
||||
'BRL',
|
||||
'CAD',
|
||||
'CHF',
|
||||
'CZK',
|
||||
'DKK',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'HKD',
|
||||
'HUF',
|
||||
'ILS',
|
||||
'JPY',
|
||||
'MXN',
|
||||
'NOK',
|
||||
'NZD',
|
||||
'PHP',
|
||||
'PLN',
|
||||
'SEK',
|
||||
'SGD',
|
||||
'THB',
|
||||
'TWD',
|
||||
'USD',
|
||||
),
|
||||
'US' => array(
|
||||
'AUD',
|
||||
'CAD',
|
||||
'EUR',
|
||||
'GBP',
|
||||
'JPY',
|
||||
'USD',
|
||||
),
|
||||
'AU', // Australia
|
||||
'AT', // Austria
|
||||
'BE', // Belgium
|
||||
'BG', // Bulgaria
|
||||
'CA', // Canada
|
||||
'CN', // China
|
||||
'CY', // Cyprus
|
||||
'CZ', // Czech Republic
|
||||
'DK', // Denmark
|
||||
'EE', // Estonia
|
||||
'FI', // Finland
|
||||
'FR', // France
|
||||
'DE', // Germany
|
||||
'GR', // Greece
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
'LV', // Latvia
|
||||
'LI', // Liechtenstein
|
||||
'LT', // Lithuania
|
||||
'LU', // Luxembourg
|
||||
'MT', // Malta
|
||||
'NL', // Netherlands
|
||||
'NO', // Norway
|
||||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SK', // Slovakia
|
||||
'SI', // Slovenia
|
||||
'ES', // Spain
|
||||
'SE', // Sweden
|
||||
'US', // United States
|
||||
'GB', // United Kingdom
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
'googlepay.button' => static function ( ContainerInterface $container ): ButtonInterface {
|
||||
/**
|
||||
* The list of which currencies can be used for GooglePay.
|
||||
*/
|
||||
'googlepay.supported-currencies' => static function ( ContainerInterface $container ) : array {
|
||||
/**
|
||||
* Returns which currencies can be used for GooglePay.
|
||||
*/
|
||||
return apply_filters(
|
||||
'woocommerce_paypal_payments_googlepay_supported_currencies',
|
||||
// phpcs:disable Squiz.Commenting.InlineComment
|
||||
array(
|
||||
'AUD', // Australian Dollar
|
||||
'BRL', // Brazilian Real
|
||||
'CAD', // Canadian Dollar
|
||||
'CHF', // Swiss Franc
|
||||
'CZK', // Czech Koruna
|
||||
'DKK', // Danish Krone
|
||||
'EUR', // Euro
|
||||
'GBP', // British Pound Sterling
|
||||
'HKD', // Hong Kong Dollar
|
||||
'HUF', // Hungarian Forint
|
||||
'ILS', // Israeli New Shekel
|
||||
'JPY', // Japanese Yen
|
||||
'MXN', // Mexican Peso
|
||||
'NOK', // Norwegian Krone
|
||||
'NZD', // New Zealand Dollar
|
||||
'PHP', // Philippine Peso
|
||||
'PLN', // Polish Zloty
|
||||
'SEK', // Swedish Krona
|
||||
'SGD', // Singapore Dollar
|
||||
'THB', // Thai Baht
|
||||
'TWD', // New Taiwan Dollar
|
||||
'USD', // United States Dollar
|
||||
)
|
||||
// phpcs:enable Squiz.Commenting.InlineComment
|
||||
);
|
||||
},
|
||||
|
||||
'googlepay.button' => static function ( ContainerInterface $container ): ButtonInterface {
|
||||
return new Button(
|
||||
$container->get( 'googlepay.url' ),
|
||||
$container->get( 'googlepay.sdk_url' ),
|
||||
|
@ -860,7 +182,7 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'googlepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface {
|
||||
'googlepay.blocks-payment-method' => static function ( ContainerInterface $container ): PaymentMethodTypeInterface {
|
||||
return new BlocksPaymentMethod(
|
||||
'ppcp-googlepay',
|
||||
$container->get( 'googlepay.url' ),
|
||||
|
@ -870,7 +192,7 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'googlepay.url' => static function ( ContainerInterface $container ): string {
|
||||
'googlepay.url' => static function ( ContainerInterface $container ): string {
|
||||
$path = realpath( __FILE__ );
|
||||
if ( false === $path ) {
|
||||
return '';
|
||||
|
@ -881,26 +203,26 @@ return array(
|
|||
);
|
||||
},
|
||||
|
||||
'googlepay.sdk_url' => static function ( ContainerInterface $container ): string {
|
||||
'googlepay.sdk_url' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://pay.google.com/gp/p/js/pay.js';
|
||||
},
|
||||
|
||||
'googlepay.endpoint.update-payment-data' => static function ( ContainerInterface $container ): UpdatePaymentDataEndpoint {
|
||||
'googlepay.endpoint.update-payment-data' => static function ( ContainerInterface $container ): UpdatePaymentDataEndpoint {
|
||||
return new UpdatePaymentDataEndpoint(
|
||||
$container->get( 'button.request-data' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
|
||||
'googlepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string {
|
||||
'googlepay.enable-url-sandbox' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://www.sandbox.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY';
|
||||
},
|
||||
|
||||
'googlepay.enable-url-live' => static function ( ContainerInterface $container ): string {
|
||||
'googlepay.enable-url-live' => static function ( ContainerInterface $container ): string {
|
||||
return 'https://www.paypal.com/bizsignup/add-product?product=payment_methods&capabilities=GOOGLE_PAY';
|
||||
},
|
||||
|
||||
'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
|
||||
'googlepay.settings.connection.status-text' => static function ( ContainerInterface $container ): string {
|
||||
$state = $container->get( 'onboarding.state' );
|
||||
if ( $state->current_state() < State::STATE_ONBOARDED ) {
|
||||
return '';
|
||||
|
@ -938,5 +260,15 @@ return array(
|
|||
esc_html( $button_text )
|
||||
);
|
||||
},
|
||||
|
||||
'googlepay.wc-gateway' => static function ( ContainerInterface $container ): GooglePayGateway {
|
||||
return new GooglePayGateway(
|
||||
$container->get( 'wcgateway.order-processor' ),
|
||||
$container->get( 'api.factory.paypal-checkout-url' ),
|
||||
$container->get( 'wcgateway.processor.refunds' ),
|
||||
$container->get( 'wcgateway.transaction-url-provider' ),
|
||||
$container->get( 'session.handler' ),
|
||||
$container->get( 'googlepay.url' ),
|
||||
$container->get( 'woocommerce.logger.woocommerce' )
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -13,7 +13,9 @@ use Exception;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use WC_Countries;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\GooglePayGateway;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\Environment;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
|
||||
|
@ -25,6 +27,8 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
|||
*/
|
||||
class Button implements ButtonInterface {
|
||||
|
||||
use ContextTrait;
|
||||
|
||||
/**
|
||||
* The URL to the module.
|
||||
*
|
||||
|
@ -286,6 +290,7 @@ class Button implements ButtonInterface {
|
|||
$render_placeholder,
|
||||
function () {
|
||||
$this->googlepay_button();
|
||||
$this->hide_gateway_until_eligible();
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -299,6 +304,7 @@ class Button implements ButtonInterface {
|
|||
$render_placeholder,
|
||||
function () {
|
||||
$this->googlepay_button();
|
||||
$this->hide_gateway_until_eligible();
|
||||
},
|
||||
21
|
||||
);
|
||||
|
@ -331,6 +337,23 @@ class Button implements ButtonInterface {
|
|||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an inline CSS style that hides the Google Pay gateway (on Classic Checkout).
|
||||
* The style is removed by `PaymentButton.js` once the eligibility of the payment method
|
||||
* is confirmed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function hide_gateway_until_eligible() : void {
|
||||
?>
|
||||
<style data-hide-gateway='<?php echo esc_attr( GooglePayGateway::ID ); ?>'>
|
||||
.wc_payment_method.payment_method_ppcp-googlepay {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues scripts/styles.
|
||||
*/
|
||||
|
@ -409,7 +432,7 @@ class Button implements ButtonInterface {
|
|||
*/
|
||||
public function script_data(): array {
|
||||
$shipping = array(
|
||||
'enabled' => $this->settings->has( 'googlepay_button_shipping_enabled' )
|
||||
'enabled' => $this->settings->has( 'googlepay_button_shipping_enabled' )
|
||||
? boolval( $this->settings->get( 'googlepay_button_shipping_enabled' ) )
|
||||
: false,
|
||||
'configured' => wc_shipping_enabled() && wc_get_shipping_method_count( false, true ) > 0,
|
||||
|
@ -421,19 +444,23 @@ class Button implements ButtonInterface {
|
|||
|
||||
$is_enabled = $this->settings->has( 'googlepay_button_enabled' ) && $this->settings->get( 'googlepay_button_enabled' );
|
||||
|
||||
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||
$is_wc_gateway_enabled = isset( $available_gateways[ GooglePayGateway::ID ] );
|
||||
|
||||
return array(
|
||||
'environment' => $this->environment->current_environment_is( Environment::SANDBOX ) ? 'TEST' : 'PRODUCTION',
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
'is_enabled' => $is_enabled,
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'button' => array(
|
||||
'environment' => $this->environment->current_environment_is( Environment::SANDBOX ) ? 'TEST' : 'PRODUCTION',
|
||||
'is_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
|
||||
'is_enabled' => $is_enabled,
|
||||
'is_wc_gateway_enabled' => $is_wc_gateway_enabled,
|
||||
'sdk_url' => $this->sdk_url,
|
||||
'button' => array(
|
||||
'wrapper' => '#ppc-button-googlepay-container',
|
||||
'style' => $this->button_styles_for_context( 'cart' ), // For now use cart. Pass the context if necessary.
|
||||
'mini_cart_wrapper' => '#ppc-button-googlepay-container-minicart',
|
||||
'mini_cart_style' => $this->button_styles_for_context( 'mini-cart' ),
|
||||
),
|
||||
'shipping' => $shipping,
|
||||
'ajax' => array(
|
||||
'shipping' => $shipping,
|
||||
'ajax' => array(
|
||||
'update_payment_data' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( UpdatePaymentDataEndpoint::ENDPOINT ),
|
||||
'nonce' => wp_create_nonce( UpdatePaymentDataEndpoint::nonce() ),
|
||||
|
|
254
modules/ppcp-googlepay/src/GooglePayGateway.php
Normal file
254
modules/ppcp-googlepay/src/GooglePayGateway.php
Normal file
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
/**
|
||||
* The Google Pay Payment Gateway
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Googlepay
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Googlepay;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WC_Order;
|
||||
use WC_Payment_Gateway;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||
use WooCommerce\PayPalCommerce\Session\SessionHandler;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\GatewayGenericException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Exception\PayPalOrderMissingException;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\Messages;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\ProcessPaymentTrait;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor;
|
||||
|
||||
/**
|
||||
* Class GooglePayGateway
|
||||
*/
|
||||
class GooglePayGateway extends WC_Payment_Gateway {
|
||||
use ProcessPaymentTrait;
|
||||
|
||||
const ID = 'ppcp-googlepay';
|
||||
|
||||
/**
|
||||
* The processor for orders.
|
||||
*
|
||||
* @var OrderProcessor
|
||||
*/
|
||||
protected $order_processor;
|
||||
|
||||
/**
|
||||
* The function return the PayPal checkout URL for the given order ID.
|
||||
*
|
||||
* @var callable(string):string
|
||||
*/
|
||||
private $paypal_checkout_url_factory;
|
||||
|
||||
/**
|
||||
* The Refund Processor.
|
||||
*
|
||||
* @var RefundProcessor
|
||||
*/
|
||||
private $refund_processor;
|
||||
|
||||
/**
|
||||
* Service able to provide transaction url for an order.
|
||||
*
|
||||
* @var TransactionUrlProvider
|
||||
*/
|
||||
protected $transaction_url_provider;
|
||||
|
||||
/**
|
||||
* The Session Handler.
|
||||
*
|
||||
* @var SessionHandler
|
||||
*/
|
||||
protected $session_handler;
|
||||
|
||||
/**
|
||||
* The URL to the module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* GooglePayGateway constructor.
|
||||
*
|
||||
* @param OrderProcessor $order_processor The Order Processor.
|
||||
* @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID.
|
||||
* @param RefundProcessor $refund_processor The Refund Processor.
|
||||
* @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order.
|
||||
* @param SessionHandler $session_handler The Session Handler.
|
||||
* @param string $module_url The URL to the module.
|
||||
* @param LoggerInterface $logger The logger.
|
||||
*/
|
||||
public function __construct(
|
||||
OrderProcessor $order_processor,
|
||||
callable $paypal_checkout_url_factory,
|
||||
RefundProcessor $refund_processor,
|
||||
TransactionUrlProvider $transaction_url_provider,
|
||||
SessionHandler $session_handler,
|
||||
string $module_url,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->id = self::ID;
|
||||
|
||||
$this->supports = array(
|
||||
'refunds',
|
||||
'products',
|
||||
);
|
||||
|
||||
$this->method_title = __( 'Google Pay (via PayPal) ', 'woocommerce-paypal-payments' );
|
||||
$this->method_description = __( 'The separate payment gateway with the Google Pay button. If disabled, the button is included in the PayPal gateway.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$this->title = $this->get_option( 'title', __( 'Google Pay', 'woocommerce-paypal-payments' ) );
|
||||
$this->description = $this->get_option( 'description', '' );
|
||||
|
||||
$this->module_url = $module_url;
|
||||
$this->icon = esc_url( $this->module_url ) . 'assets/images/googlepay.svg';
|
||||
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
$this->order_processor = $order_processor;
|
||||
$this->paypal_checkout_url_factory = $paypal_checkout_url_factory;
|
||||
$this->refund_processor = $refund_processor;
|
||||
$this->transaction_url_provider = $transaction_url_provider;
|
||||
$this->session_handler = $session_handler;
|
||||
$this->logger = $logger;
|
||||
|
||||
add_action(
|
||||
'woocommerce_update_options_payment_gateways_' . $this->id,
|
||||
array(
|
||||
$this,
|
||||
'process_admin_options',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the form fields.
|
||||
*/
|
||||
public function init_form_fields() {
|
||||
$this->form_fields = array(
|
||||
'enabled' => array(
|
||||
'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Google Pay', 'woocommerce-paypal-payments' ),
|
||||
'default' => 'no',
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'Enable/Disable Google Pay payment gateway.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'title' => array(
|
||||
'title' => __( 'Title', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => $this->title,
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'description' => array(
|
||||
'title' => __( 'Description', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'text',
|
||||
'default' => $this->description,
|
||||
'desc_tip' => true,
|
||||
'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process payment for a WooCommerce order.
|
||||
*
|
||||
* @param int $order_id The WooCommerce order id.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function process_payment( $order_id ) {
|
||||
$wc_order = wc_get_order( $order_id );
|
||||
if ( ! is_a( $wc_order, WC_Order::class ) ) {
|
||||
return $this->handle_payment_failure(
|
||||
null,
|
||||
new GatewayGenericException( new Exception( 'WC order was not found.' ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the WC_Order is paid through the approved webhook.
|
||||
*/
|
||||
//phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
if ( isset( $_REQUEST['ppcp-resume-order'] ) && $wc_order->has_status( 'processing' ) ) {
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
}
|
||||
//phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_process_order', $wc_order );
|
||||
|
||||
try {
|
||||
try {
|
||||
$this->order_processor->process( $wc_order );
|
||||
|
||||
do_action( 'woocommerce_paypal_payments_before_handle_payment_success', $wc_order );
|
||||
|
||||
return $this->handle_payment_success( $wc_order );
|
||||
} catch ( PayPalOrderMissingException $exc ) {
|
||||
$order = $this->order_processor->create_order( $wc_order );
|
||||
|
||||
return array(
|
||||
'result' => 'success',
|
||||
'redirect' => ( $this->paypal_checkout_url_factory )( $order->id() ),
|
||||
);
|
||||
}
|
||||
} catch ( PayPalApiException $error ) {
|
||||
return $this->handle_payment_failure(
|
||||
$wc_order,
|
||||
new Exception(
|
||||
Messages::generic_payment_error_message() . ' ' . $error->getMessage(),
|
||||
$error->getCode(),
|
||||
$error
|
||||
)
|
||||
);
|
||||
} catch ( Exception $error ) {
|
||||
return $this->handle_payment_failure( $wc_order, $error );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process refund.
|
||||
*
|
||||
* If the gateway declares 'refunds' support, this will allow it to refund.
|
||||
* a passed in amount.
|
||||
*
|
||||
* @param int $order_id Order ID.
|
||||
* @param float $amount Refund 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 ( ! is_a( $order, \WC_Order::class ) ) {
|
||||
return false;
|
||||
}
|
||||
return $this->refund_processor->process( $order, (float) $amount, (string) $reason );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return transaction url for this gateway and given order.
|
||||
*
|
||||
* @param \WC_Order $order WC order to get transaction url by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_transaction_url( $order ): string {
|
||||
$this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order );
|
||||
|
||||
return parent::get_transaction_url( $order );
|
||||
}
|
||||
}
|
|
@ -10,35 +10,43 @@ declare(strict_types=1);
|
|||
namespace WooCommerce\PayPalCommerce\Googlepay;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry;
|
||||
use WC_Payment_Gateway;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\ButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\Endpoint\UpdatePaymentDataEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\Helper\ApmProductStatus;
|
||||
use WooCommerce\PayPalCommerce\Googlepay\Helper\AvailabilityNotice;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExecutableModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ExtendingModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ModuleClassNameIdTrait;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Module\ServiceModule;
|
||||
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||
|
||||
/**
|
||||
* Class GooglepayModule
|
||||
*/
|
||||
class GooglepayModule implements ModuleInterface {
|
||||
class GooglepayModule implements ServiceModule, ExtendingModule, ExecutableModule {
|
||||
use ModuleClassNameIdTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setup(): ServiceProviderInterface {
|
||||
return new ServiceProvider(
|
||||
require __DIR__ . '/../services.php',
|
||||
require __DIR__ . '/../extensions.php'
|
||||
);
|
||||
public function services(): array {
|
||||
return require __DIR__ . '/../services.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): void {
|
||||
public function extensions(): array {
|
||||
return require __DIR__ . '/../extensions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run( ContainerInterface $c ): bool {
|
||||
|
||||
// Clears product status when appropriate.
|
||||
add_action(
|
||||
|
@ -92,11 +100,21 @@ class GooglepayModule implements ModuleInterface {
|
|||
static function () use ( $c, $button ) {
|
||||
$smart_button = $c->get( 'button.smart-button' );
|
||||
assert( $smart_button instanceof SmartButtonInterface );
|
||||
|
||||
if ( $smart_button->should_load_ppcp_script() ) {
|
||||
$button->enqueue();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checkout page, but no PPCP scripts were loaded. Most likely in continuation mode.
|
||||
* Need to enqueue some Google Pay scripts to populate the billing form with details
|
||||
* provided by Google Pay.
|
||||
*/
|
||||
if ( is_checkout() ) {
|
||||
$button->enqueue();
|
||||
}
|
||||
|
||||
if ( has_block( 'woocommerce/checkout' ) || has_block( 'woocommerce/cart' ) ) {
|
||||
/**
|
||||
* Should add this to the ButtonInterface.
|
||||
|
@ -159,13 +177,47 @@ class GooglepayModule implements ModuleInterface {
|
|||
},
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the module.
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function getKey() {
|
||||
add_filter(
|
||||
'woocommerce_payment_gateways',
|
||||
/**
|
||||
* Param types removed to avoid third-party issues.
|
||||
*
|
||||
* @psalm-suppress MissingClosureParamType
|
||||
*/
|
||||
static function ( $methods ) use ( $c ): array {
|
||||
if ( ! is_array( $methods ) ) {
|
||||
return $methods;
|
||||
}
|
||||
|
||||
$settings = $c->get( 'wcgateway.settings' );
|
||||
assert( $settings instanceof Settings );
|
||||
|
||||
if ( $settings->has( 'googlepay_button_enabled' ) && $settings->get( 'googlepay_button_enabled' ) ) {
|
||||
$googlepay_gateway = $c->get( 'googlepay.wc-gateway' );
|
||||
assert( $googlepay_gateway instanceof WC_Payment_Gateway );
|
||||
|
||||
$methods[] = $googlepay_gateway;
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_review_order_after_submit',
|
||||
function () {
|
||||
echo '<div id="ppc-button-' . esc_attr( GooglePayGateway::ID ) . '"></div>';
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'woocommerce_pay_order_after_submit',
|
||||
function () {
|
||||
echo '<div id="ppc-button-' . esc_attr( GooglePayGateway::ID ) . '"></div>';
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,18 @@ namespace WooCommerce\PayPalCommerce\Googlepay\Helper;
|
|||
class ApmApplies {
|
||||
|
||||
/**
|
||||
* The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* The list of which countries can be used for GooglePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_country_currency_matrix;
|
||||
private $allowed_countries;
|
||||
|
||||
/**
|
||||
* The list of which currencies can be used for GooglePay.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $allowed_currencies;
|
||||
|
||||
/**
|
||||
* 3-letter currency code of the shop.
|
||||
|
@ -39,30 +46,39 @@ class ApmApplies {
|
|||
/**
|
||||
* DccApplies constructor.
|
||||
*
|
||||
* @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for GooglePay.
|
||||
* @param array $allowed_countries The list of which countries can be used for GooglePay.
|
||||
* @param array $allowed_currencies The list of which currencies can be used for GooglePay.
|
||||
* @param string $currency 3-letter currency code of the shop.
|
||||
* @param string $country 2-letter country code of the shop.
|
||||
*/
|
||||
public function __construct(
|
||||
array $allowed_country_currency_matrix,
|
||||
array $allowed_countries,
|
||||
array $allowed_currencies,
|
||||
string $currency,
|
||||
string $country
|
||||
) {
|
||||
$this->allowed_country_currency_matrix = $allowed_country_currency_matrix;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
$this->allowed_countries = $allowed_countries;
|
||||
$this->allowed_currencies = $allowed_currencies;
|
||||
$this->currency = $currency;
|
||||
$this->country = $country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether GooglePay can be used in the current country and the current currency used.
|
||||
* Returns whether GooglePay can be used in the current country used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_country_currency(): bool {
|
||||
if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
|
||||
public function for_country(): bool {
|
||||
return in_array( $this->country, $this->allowed_countries, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether GooglePay can be used in the current currency used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function for_currency(): bool {
|
||||
return in_array( $this->currency, $this->allowed_currencies, true );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue