369 lines
11 KiB
PHP
369 lines
11 KiB
PHP
<?php
|
|
namespace ElementorPro\Modules\Payments\Widgets;
|
|
|
|
use Elementor\Widget_Base;
|
|
use Elementor\Controls_Manager;
|
|
use Elementor\Plugin;
|
|
use Elementor\Utils;
|
|
use ElementorPro\Modules\Payments\Classes\Payment_Button;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit; // Exit if accessed directly
|
|
}
|
|
|
|
class Paypal_Button extends Payment_Button {
|
|
|
|
// API integration types.
|
|
const API_TYPE_SIMPLE = 'simple';
|
|
const API_TYPE_ADVANCED = 'advanced';
|
|
|
|
// PayPal constants.
|
|
const PROD_URL = 'https://www.paypal.com/cgi-bin/webscr';
|
|
const SANDBOX_URL = 'https://sandbox.paypal.com/cgi-bin/webscr';
|
|
const CMD_CHECKOUT = '_xclick';
|
|
const CMD_DONATION = '_donations';
|
|
const CMD_SUBSCRIPTION = '_xclick-subscriptions';
|
|
|
|
const BILLING_CYCLE_TYPES = [
|
|
self::BILLING_CYCLE_DAYS => 'D',
|
|
self::BILLING_CYCLE_WEEKS => 'W',
|
|
self::BILLING_CYCLE_MONTHS => 'M',
|
|
self::BILLING_CYCLE_YEARS => 'Y',
|
|
];
|
|
|
|
public function get_name() {
|
|
return 'paypal-button';
|
|
}
|
|
|
|
public function get_title() {
|
|
return esc_html__( 'PayPal Button', 'elementor-pro' );
|
|
}
|
|
|
|
public function get_icon() {
|
|
return 'eicon-paypal-button';
|
|
}
|
|
|
|
public function get_keywords() {
|
|
return [ 'paypal', 'payment', 'sell', 'donate' ];
|
|
}
|
|
|
|
protected function get_merchant_name() {
|
|
return 'PayPal';
|
|
}
|
|
|
|
// Retrieve a numerical field from settings, and default to $min if it's too small.
|
|
protected function get_numeric_setting( $key, $min = 0 ) {
|
|
$num = doubleval( $this->get_settings_for_display( $key ) );
|
|
|
|
return ( $min > $num ) ? $min : $num;
|
|
}
|
|
|
|
// Print a numerical field from settings, using `get_numeric_setting`.
|
|
protected function print_numeric_setting( $key, $min = 0 ) {
|
|
// PHPCS - the get_numeric_setting function is safe.
|
|
echo $this->get_numeric_setting( $key, $min ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
}
|
|
|
|
// Get the currently selected API communication method ( legacy / SDK ).
|
|
protected function get_api_method() {
|
|
$settings = $this->get_settings_for_display();
|
|
|
|
return ( self::API_TYPE_ADVANCED === $settings['merchant_account'] ) ? 'sdk' : 'legacy';
|
|
}
|
|
|
|
// Get validation errors.
|
|
protected function get_errors( $squash_errors = true ) {
|
|
$settings = $this->get_settings_for_display();
|
|
$errors = [];
|
|
|
|
// Don't render errors in the editor.
|
|
if ( Plugin::instance()->editor->is_edit_mode() ) {
|
|
return $errors;
|
|
}
|
|
|
|
// No payment method provided.
|
|
if ( 'legacy' === $this->get_api_method() ) {
|
|
$empty_email = empty( $settings['email'] );
|
|
$empty_sandbox_email = $this->is_sandbox() && empty( $settings['sandbox_email'] );
|
|
|
|
if ( $empty_email || $empty_sandbox_email ) {
|
|
$errors[ self::ERROR_MESSAGE_PAYMENT_METHOD ] = $this->get_custom_message( self::ERROR_MESSAGE_PAYMENT_METHOD );
|
|
}
|
|
}
|
|
|
|
// Other errors.
|
|
$empty_product_price = ( self::PAYMENT_TYPE_DONATION !== $settings['type'] && empty( $settings['product_price'] ) );
|
|
$empty_donation_amount = ( self::DONATION_TYPE_FIXED === $settings['donation_type'] && empty( $settings['donation_amount'] ) );
|
|
$empty_tax = ( ! empty( $settings['tax_type'] ) && empty( $settings['tax_rate'] ) );
|
|
|
|
if ( $empty_product_price || $empty_donation_amount || $empty_tax ) {
|
|
$errors[ self::ERROR_MESSAGE_GLOBAL ] = $this->get_custom_message( self::ERROR_MESSAGE_GLOBAL );
|
|
}
|
|
|
|
// Squash errors to show only a global error.
|
|
if ( $squash_errors && 1 < count( $errors ) ) {
|
|
return [
|
|
self::ERROR_MESSAGE_GLOBAL => $this->get_custom_message( self::ERROR_MESSAGE_GLOBAL ),
|
|
];
|
|
}
|
|
|
|
return $errors;
|
|
}
|
|
|
|
// Render PayPal's legacy checkout form.
|
|
protected function render_legacy_form() {
|
|
$settings = $this->get_settings_for_display();
|
|
|
|
// Handle sandbox mode.
|
|
if ( ! $this->is_sandbox() ) {
|
|
$form_action = self::PROD_URL;
|
|
$email = $settings['email'];
|
|
} else {
|
|
$form_action = self::SANDBOX_URL;
|
|
$email = $settings['sandbox_email'];
|
|
$this->add_render_attribute( 'button', 'class', 'elementor-payment-sandbox-mode' );
|
|
}
|
|
|
|
if ( 'yes' === $settings['open_in_new_window'] ) {
|
|
$target = '_blank';
|
|
} else {
|
|
$target = '_top';
|
|
}
|
|
|
|
// Set PayPal payment settings by payment type.
|
|
switch ( $settings['type'] ) {
|
|
case self::PAYMENT_TYPE_CHECKOUT:
|
|
$cmd = self::CMD_CHECKOUT;
|
|
$price_field = [
|
|
'name' => 'amount',
|
|
'value' => $settings['product_price'],
|
|
];
|
|
break;
|
|
|
|
case self::PAYMENT_TYPE_DONATION:
|
|
$cmd = self::CMD_DONATION;
|
|
$donation_amount = '';
|
|
|
|
// phpcs:ignore
|
|
if ( self::DONATION_TYPE_FIXED === $settings['donation_type'] ) {
|
|
$donation_amount = $settings['donation_amount'];
|
|
}
|
|
|
|
$price_field = [
|
|
'name' => 'amount',
|
|
'value' => $donation_amount,
|
|
];
|
|
break;
|
|
|
|
case self::PAYMENT_TYPE_SUBSCRIPTION:
|
|
$cmd = self::CMD_SUBSCRIPTION;
|
|
$price_field = [
|
|
'name' => 'a3',
|
|
'value' => $settings['product_price'],
|
|
];
|
|
$auto_renewal = ( 'yes' === $settings['auto_renewal'] ) ? 1 : 0;
|
|
$billing_cycle = self::BILLING_CYCLE_TYPES[ $settings['billing_cycle'] ];
|
|
break;
|
|
}
|
|
|
|
// PayPal HTML reference:
|
|
// https://developer.paypal.com/docs/paypal-payments-standard/integration-guide/html-reference-landing/
|
|
|
|
?>
|
|
<form action="<?php echo esc_attr( $form_action ); ?>" method="post" target="<?php echo esc_attr( $target ); ?>">
|
|
<input type="hidden" name="cmd" value="<?php echo esc_attr( $cmd ); ?>" />
|
|
<input type="hidden" name="business" value="<?php echo esc_attr( $email ); ?>" />
|
|
<input type="hidden" name="lc" value="US" />
|
|
<input type="hidden" name="item_name" value="<?php echo esc_attr( $settings['product_name'] ); ?>" />
|
|
<input type="hidden" name="item_number" value="<?php echo esc_attr( $settings['product_sku'] ); ?>" />
|
|
<input type="hidden" name="currency_code" value="<?php echo esc_attr( $settings['currency'] ); ?>" />
|
|
<input type="hidden" name="<?php echo esc_attr( $price_field['name'] ); ?>" value="<?php echo esc_attr( $price_field['value'] ); ?>" />
|
|
<input type="hidden" name="no_note" value="1">
|
|
|
|
<?php if ( self::PAYMENT_TYPE_CHECKOUT === $settings['type'] ) { ?>
|
|
<input type="hidden" name="shipping" value="<?php $this->print_numeric_setting( 'shipping_price' ); ?>" />
|
|
<input type="hidden" name="tax_rate" value="<?php $this->print_numeric_setting( 'tax_rate' ); ?>" />
|
|
<input type="hidden" name="quantity" value="<?php $this->print_numeric_setting( 'quantity', 1 ); ?>" />
|
|
<?php
|
|
} elseif ( self::PAYMENT_TYPE_SUBSCRIPTION === $settings['type'] ) { ?>
|
|
<?php // PHPCS - the $auto_renewal variable is a safe. ?>
|
|
<input type="hidden" name="src" value="<?php echo $auto_renewal; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" />
|
|
<input type="hidden" name="p3" value="1" />
|
|
<?php // PHPCS - the $billing_cycle variable is a constant value from self::BILLING_CYCLE_TYPES. ?>
|
|
<input type="hidden" name="t3" value="<?php echo $billing_cycle; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" />
|
|
<input type="hidden" name="no-shipping" value="1" />
|
|
<?php
|
|
}
|
|
|
|
if ( ! empty( $settings['redirect_after_success']['url'] ) ) { ?>
|
|
<input type="hidden" name="return" value="<?php echo esc_url( $settings['redirect_after_success']['url'] ); ?>">
|
|
<?php
|
|
}
|
|
|
|
$this->add_render_attribute( 'button', 'type', 'submit' );
|
|
$this->add_render_attribute( 'button', 'class', 'elementor-paypal-legacy' );
|
|
parent::render_button( null, 'button' );
|
|
|
|
foreach ( $this->get_errors() as $type => $message ) {
|
|
?>
|
|
<div class="elementor-message elementor-message-danger elementor-hidden elementor-error-message-<?php Utils::print_unescaped_internal_string( $type ); ?>">
|
|
<?php echo esc_html( $message ); ?>
|
|
</div>
|
|
<?php
|
|
}
|
|
?>
|
|
</form>
|
|
<?php
|
|
}
|
|
|
|
// Render the payment button.
|
|
protected function render_button( Widget_Base $instance = null, $tag = 'a' ) {
|
|
switch ( $this->get_api_method() ) {
|
|
case 'legacy':
|
|
$this->render_legacy_form();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Account details section.
|
|
protected function register_account_section() {
|
|
$this->start_controls_section(
|
|
'section_account',
|
|
[
|
|
'label' => esc_html__( 'Pricing & Payments', 'elementor-pro' ),
|
|
]
|
|
);
|
|
|
|
$this->add_control(
|
|
'merchant_account',
|
|
[
|
|
'label' => esc_html__( 'Merchant Account', 'elementor-pro' ),
|
|
'type' => Controls_Manager::HIDDEN,
|
|
'default' => self::API_TYPE_SIMPLE,
|
|
'options' => [
|
|
self::API_TYPE_SIMPLE => esc_html__( 'Default (Simple)', 'elementor-pro' ),
|
|
self::API_TYPE_ADVANCED => esc_html__( 'Custom (Advanced)', 'elementor-pro' ),
|
|
],
|
|
'frontend_available' => true,
|
|
]
|
|
);
|
|
|
|
$this->add_control(
|
|
'email',
|
|
[
|
|
'label' => esc_html__( 'PayPal Account', 'elementor-pro' ),
|
|
'type' => Controls_Manager::TEXT,
|
|
'dynamic' => [
|
|
'active' => true,
|
|
],
|
|
'ai' => [
|
|
'active' => false,
|
|
],
|
|
'description' => esc_html__( 'Transactions made through your PayPal button will be registered under this account.', 'elementor-pro' ),
|
|
'label_block' => true,
|
|
'condition' => [
|
|
'merchant_account' => self::API_TYPE_SIMPLE,
|
|
],
|
|
'placeholder' => 'yours@email.com',
|
|
]
|
|
);
|
|
|
|
$this->add_control(
|
|
'sdk_token',
|
|
[
|
|
'label' => esc_html__( 'SDK Token', 'elementor-pro' ),
|
|
'type' => Controls_Manager::TEXT,
|
|
'dynamic' => [
|
|
'active' => true,
|
|
],
|
|
'ai' => [
|
|
'active' => false,
|
|
],
|
|
'label_block' => true,
|
|
'condition' => [
|
|
'merchant_account' => self::API_TYPE_ADVANCED,
|
|
],
|
|
]
|
|
);
|
|
|
|
$this->register_product_controls();
|
|
|
|
$this->end_controls_section();
|
|
}
|
|
|
|
/**
|
|
* Updates Button tab controls in 'Style' tab
|
|
*
|
|
* @since 3.7.0
|
|
*/
|
|
public function register_paypal_button_controls() {
|
|
parent::register_controls();
|
|
|
|
$this->update_control( 'selected_icon', [
|
|
'default' => [
|
|
'value' => 'fab fa-paypal',
|
|
'library' => 'fa-brands',
|
|
],
|
|
] );
|
|
|
|
$this->update_control( 'background_color', [
|
|
'default' => '#032E82',
|
|
] );
|
|
}
|
|
|
|
/**
|
|
* Edit button control initial UI
|
|
*
|
|
* @since 3.7.0
|
|
*
|
|
*/
|
|
protected function register_controls() {
|
|
$this->register_paypal_button_controls();
|
|
}
|
|
|
|
// Custom sandbox controls.
|
|
protected function register_sandbox_controls() {
|
|
$this->add_control(
|
|
'sandbox_email',
|
|
[
|
|
'label' => esc_html__( 'Sandbox Email Account', 'elementor-pro' ),
|
|
'type' => Controls_Manager::TEXT,
|
|
'dynamic' => [
|
|
'active' => true,
|
|
],
|
|
'ai' => [
|
|
'active' => false,
|
|
],
|
|
'description' => esc_html__( 'This is the address given to you by PayPal when you set up a sandbox with your developer account. You can use the sandbox to test your purchase flow.', 'elementor-pro' ),
|
|
'label_block' => true,
|
|
'condition' => [
|
|
'sandbox_mode' => 'yes',
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
// This widget extends the button core widget and therefore needs to overwrite the widget-base core CSS config.
|
|
public function get_css_config() {
|
|
$widget_name = 'payments';
|
|
|
|
$direction = is_rtl() ? '-rtl' : '';
|
|
|
|
$css_file_path = 'css/widget-' . $widget_name . $direction . '.min.css';
|
|
|
|
/*
|
|
* Currently this widget does not support custom-breakpoints in its CSS file.
|
|
* In order to support it, this widget needs to get the CSS config from the base-widget-trait.php.
|
|
* But to make sure that it implements the Pro assets-path due to the fact that it extends a Core widget.
|
|
*/
|
|
return [
|
|
'key' => $widget_name,
|
|
'version' => ELEMENTOR_PRO_VERSION,
|
|
'file_path' => ELEMENTOR_PRO_ASSETS_PATH . $css_file_path,
|
|
'data' => [
|
|
'file_url' => ELEMENTOR_PRO_ASSETS_URL . $css_file_path,
|
|
],
|
|
];
|
|
}
|
|
}
|