updated plugin Easy Digital Downloads version 3.1.1.2

This commit is contained in:
2023-03-17 22:34:04 +00:00
committed by Gitium
parent e8a66564bd
commit 19e086d1c4
647 changed files with 20986 additions and 27305 deletions

View File

@ -61,11 +61,7 @@ function edds_payments_column_data( $value, $payment_id, $column_name ) {
return $value;
}
if ( function_exists( 'edd_get_order_meta' ) ) {
$customer_id = edd_get_order_meta( $payment_id, '_edds_stripe_customer_id', true );
} else {
$customer_id = edd_get_payment_meta( $payment_id, '_edds_stripe_customer_id', true );
}
$customer_id = edd_get_order_meta( $payment_id, '_edds_stripe_customer_id', true );
if ( empty( $customer_id ) ) {
return $value;

View File

@ -23,7 +23,7 @@ if ( ! defined( 'ABSPATH' ) ) {
echo wp_kses(
sprintf(
/* translators: %1$s Opening strong tag, do not translate. %2$s Closing strong tag, do not translate. %3$s Opening code tag, do not translate. %4$s Closing code tag, do not translate. */
__( 'To continue accepting credit card payments with Stripe please update %1$sEasy Digital Downloads%2$s to version %3$s2.11%4$s or higher.', 'easy-digital-downloads' ),
__( 'To continue accepting credit card payments with Stripe please update %1$sEasy Digital Downloads%2$s to version %3$s3.1%4$s or higher.', 'easy-digital-downloads' ),
'<strong>',
'</strong>',
'<code>',

View File

@ -1,26 +0,0 @@
<?php
/**
* Reporting: Stripe
*
* @package EDD_Stripe
* @since 2.6
*/
/**
* Class EDD_Stripe_Reports
*
* Do nothing in 2.8.0
* The reports have not collected data since 2.7.0 and provide no tangible value.
*
* @since 2.6
* @deprecated 2.8.0
*/
class EDD_Stripe_Reports {
public function __construct() {
_doing_it_wrong(
__CLASS__,
__( 'Stripe-specific reports have been removed.', 'easy-digital-downloads' ),
'2.8.0'
);
}
}

View File

@ -1,10 +1,12 @@
<?php
/**
* Register our settings section
*
* @return array
*/
* Register our settings section
*
* @param array $sections The Registered EDD Sections array.
*
* @return array
*/
function edds_settings_section( $sections ) {
$sections['edd-stripe'] = __( 'Stripe', 'easy-digital-downloads' );
@ -17,185 +19,200 @@ add_filter( 'edd_settings_sections_gateways', 'edds_settings_section' );
*
* @access public
* @since 1.0
*
* @param array $settings The currently registered settings.
*
* @return array
*/
function edds_add_settings( $settings ) {
// Output a placeholder setting to help promote Stripe
// for non-Pro installs that do not meet PHP requirements.
if (
false === edds_has_met_requirements( 'php' ) &&
false === edds_is_pro()
) {
return array_merge(
$settings,
array(
'edd-stripe' => array(
'edds-requirements-not-met' => array(
'id' => 'edds-requirements-not-met',
'name' => __( 'Unmet Requirements', 'easy-digital-downloads' ),
'type' => 'stripe_requirements_not_met',
'class' => 'edds-requirements-not-met',
),
),
)
);
}
$stripe_settings = array(
'stripe_connect_button' => array(
'id' => 'stripe_connect_button',
'name' => __( 'Connection Status', 'easy-digital-downloads' ),
'desc' => edds_stripe_connect_setting_field(),
'type' => 'descriptive_text',
'stripe_connect_button' => array(
'id' => 'stripe_connect_button',
'name' => __( 'Connection Status', 'easy-digital-downloads' ),
'desc' => edds_stripe_connect_setting_field(),
'type' => 'descriptive_text',
'class' => 'edd-stripe-connect-row',
),
'test_publishable_key' => array(
'id' => 'test_publishable_key',
'test_publishable_key' => array(
'id' => 'test_publishable_key',
'name' => __( 'Test Publishable Key', 'easy-digital-downloads' ),
'desc' => __( 'Enter your test publishable key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
'type' => 'text',
'size' => 'regular',
'class' => 'edd-hidden edds-api-key-row',
),
'test_secret_key' => array(
'id' => 'test_secret_key',
'test_secret_key' => array(
'id' => 'test_secret_key',
'name' => __( 'Test Secret Key', 'easy-digital-downloads' ),
'desc' => __( 'Enter your test secret key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
'type' => 'text',
'size' => 'regular',
'class' => 'edd-hidden edds-api-key-row',
),
'live_publishable_key' => array(
'id' => 'live_publishable_key',
'live_publishable_key' => array(
'id' => 'live_publishable_key',
'name' => __( 'Live Publishable Key', 'easy-digital-downloads' ),
'desc' => __( 'Enter your live publishable key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
'type' => 'text',
'size' => 'regular',
'class' => 'edd-hidden edds-api-key-row',
),
'live_secret_key' => array(
'id' => 'live_secret_key',
'live_secret_key' => array(
'id' => 'live_secret_key',
'name' => __( 'Live Secret Key', 'easy-digital-downloads' ),
'desc' => __( 'Enter your live secret key, found in your Stripe Account Settings', 'easy-digital-downloads' ),
'type' => 'text',
'size' => 'regular',
'class' => 'edd-hidden edds-api-key-row',
),
'stripe_webhook_description' => array(
'id' => 'stripe_webhook_description',
'type' => 'descriptive_text',
'name' => __( 'Webhooks', 'easy-digital-downloads' ),
'desc' =>
'stripe_webhook_description' => array(
'id' => 'stripe_webhook_description',
'type' => 'descriptive_text',
'name' => __( 'Webhooks', 'easy-digital-downloads' ),
'desc' =>
'<p>' . sprintf(
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
__( 'In order for Stripe to function completely, you must configure your Stripe webhooks. Visit your %1$saccount dashboard%2$s to configure them. Please add a webhook endpoint for the URL below.', 'easy-digital-downloads' ),
'<a href="https://dashboard.stripe.com/account/webhooks" target="_blank" rel="noopener noreferrer">',
'</a>'
) . '</p>' .
'<p><strong>' . sprintf(
) .
'</p>' .
'<p><strong>' .
sprintf(
/* translators: %s Webhook URL. Do not translate. */
__( 'Webhook URL: %s', 'easy-digital-downloads' ),
home_url( 'index.php?edd-listener=stripe' )
) . '</strong></p>' .
'<p>' . sprintf(
) .
'</strong></p>' .
'<p>' .
sprintf(
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
__( 'See our %1$sdocumentation%2$s for more information.', 'easy-digital-downloads' ),
'<a href="' . esc_url( edds_documentation_route( 'stripe-webhooks' ) ) . '" target="_blank" rel="noopener noreferrer">',
'<a href="' . esc_url( edds_documentation_route( 'stripe' ) ) . '#webhook-configuration' . '" target="_blank" rel="noopener noreferrer">',
'</a>'
) . '</p>'
) .
'</p>',
),
'stripe_billing_fields' => array(
'id' => 'stripe_billing_fields',
'name' => __( 'Billing Address Display', 'easy-digital-downloads' ),
'desc' => __( 'Select how you would like to display the billing address fields on the checkout form. <p><strong>Notes</strong>:</p><p>If taxes are enabled, this option cannot be changed from "Full address".</p><p>If set to "No address fields", you <strong>must</strong> disable "zip code verification" in your Stripe account.</p>', 'easy-digital-downloads' ),
'type' => 'select',
'stripe_billing_fields' => array(
'id' => 'stripe_billing_fields',
'name' => __( 'Billing Address Display', 'easy-digital-downloads' ),
'desc' => __( 'Select how you would like to display the billing address fields on the checkout form. <p><strong>Notes</strong>:</p><p>If taxes are enabled, this option cannot be changed from "Full address".</p><p>If set to "No address fields", you <strong>must</strong> disable "zip code verification" in your Stripe account.</p>', 'easy-digital-downloads' ),
'type' => 'select',
'std' => 'full',
'options' => array(
'full' => __( 'Full address', 'easy-digital-downloads' ),
'zip_country' => __( 'Zip / Postal Code and Country only', 'easy-digital-downloads' ),
'none' => __( 'No address fields', 'easy-digital-downloads' )
'none' => __( 'No address fields', 'easy-digital-downloads' ),
),
'std' => 'full'
),
'stripe_statement_descriptor' => array(
'id' => 'stripe_statement_descriptor',
'name' => __( 'Statement Descriptor', 'easy-digital-downloads' ),
'desc' => __( 'Choose how charges will appear on customer\'s credit card statements. <em>Max 22 characters</em>', 'easy-digital-downloads' ),
'type' => 'text',
),
'stripe_use_existing_cards' => array(
'id' => 'stripe_use_existing_cards',
'name' => __( 'Show Previously Used Cards', 'easy-digital-downloads' ),
'desc' => __( 'Provides logged in customers with a list of previous used payment methods for faster checkout.', 'easy-digital-downloads' ),
'type' => 'checkbox'
'stripe_statement_descriptor' => array(
'id' => 'stripe_statement_descriptor',
'name' => __( 'Statement Descriptor', 'easy-digital-downloads' ),
'desc' => __( 'Choose how charges will appear on customer\'s credit card statements. <em>Max 22 characters</em>', 'easy-digital-downloads' ),
'type' => 'text',
),
'stripe_allow_prepaid' => array(
'stripe_restrict_assets' => array(
'id' => 'stripe_restrict_assets',
'name' => ( __( 'Restrict Stripe Assets', 'easy-digital-downloads' ) ),
'desc' => ( __( 'Only load Stripe.com hosted assets on pages that specifically utilize Stripe functionality.', 'easy-digital-downloads' ) ),
'type' => 'checkbox',
'tooltip_title' => __( 'Loading Javascript from Stripe', 'easy-digital-downloads' ),
'tooltip_desc' => __( 'Stripe advises that their Javascript library be loaded on every page to take advantage of their advanced fraud detection rules. If you are not concerned with this, enable this setting to only load the Javascript when necessary. Read more about Stripe\'s recommended setup here: https://stripe.com/docs/web/setup.', 'easy-digital-downloads' ),
),
);
if ( _edds_legacy_elements_enabled() ) {
$elements_mode = edds_get_elements_mode();
if ( ! edds_stripe_connect_can_manage_keys() ) {
$stripe_settings['stripe_elements_mode'] = array(
'id' => 'stripe_elements_mode',
'name' => __( 'Elements Mode', 'easy-digital-downloads' ),
'desc' => __( 'Toggle between using the legacy Card Elements Stripe integration and the new Payment Elements experience.', 'easy-digital-downloads' ),
'type' => 'select',
'options' => array(
'card-elements' => __( 'Card Element', 'easy-digital-downloads' ),
'payment-elements' => __( 'Payment Element', 'easy-digital-downloads' ),
),
'class' => 'stripe-elements-mode',
'tooltip_title' => __( 'Transitioning to Payment Elements', 'easy-digital-downloads' ),
'tooltip_desc' => __( 'You are seeing this option because your store has been using Card Elements prior to the EDD Stripe 2.9.0 update.<br /><br />To ensure that we do not affect your current checkout experience, you can use this setting to toggle between the Card Elements (legacy) and Payment Elements (updated version) to ensure that any customizations or theming you have done still function properly.<br /><br />Please be advised, that in a future version of the Stripe extension, we will deprecate the Card Elements, so take this time to update your store!', 'easy-digital-downloads' ),
);
}
$stripe_settings['stripe_allow_prepaid'] = array(
'id' => 'stripe_allow_prepaid',
'name' => __( 'Prepaid Cards', 'easy-digital-downloads' ),
'desc' => __( 'Allow prepaid cards as valid payment method.', 'easy-digital-downloads' ),
'type' => 'checkbox',
),
'stripe_split_payment_fields' => array(
'id' => 'stripe_split_payment_fields',
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
);
$radar_rules_url = sprintf(
'https://dashboard.stripe.com%s/settings/radar/rules',
edd_is_test_mode() ? '/test' : ''
);
$stripe_settings['stripe_allow_prepaid_elements_note'] = array(
'id' => 'stripe_allow_prepaid_elements_note',
'name' => __( 'Prepaid Cards', 'easy-digital-downloads' ),
'desc' => sprintf(
__( 'Prepaid card allowance can now be managed in your <a href="%s" target="_blank">Stripe Radar Rules</a>.', 'easy-digital-downloads' ),
$radar_rules_url
),
'type' => 'descriptive_text',
'class' => 'payment-elements' === $elements_mode ? 'payment-elements-feature' : 'edd-hidden payment-elements-feature',
);
$stripe_settings['stripe_split_payment_fields'] = array(
'id' => 'stripe_split_payment_fields',
'name' => __( 'Split Credit Card Form', 'easy-digital-downloads' ),
'desc' => __( 'Use separate card number, expiration, and CVC fields in payment forms.', 'easy-digital-downloads' ),
'type' => 'checkbox',
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
);
$stripe_settings['stripe_use_existing_cards'] = array(
'id' => 'stripe_use_existing_cards',
'name' => __( 'Show Previously Used Cards', 'easy-digital-downloads' ),
'desc' => __( 'Provides logged in customers with a list of previous used payment methods for faster checkout.', 'easy-digital-downloads' ),
'type' => 'checkbox',
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
);
$stripe_settings['stripe_use_existing_cards_elements_note'] = array(
'id' => 'stripe_use_existing_cards_elements_note',
'name' => __( 'Show Previously Used Cards', 'easy-digital-downloads' ),
'desc' => sprintf(
__( 'Previously used cards are now managed by <a href="%s" target="_blank">Link by Stripe</a>, for even better conversions and security.', 'easy-digital-downloads' ),
'https://link.co/'
),
'type' => 'descriptive_text',
'class' => 'payment-elements' === $elements_mode ? 'payment-elements-feature' : 'edd-hidden payment-elements-feature',
);
}
$settings['edd-stripe'] = $stripe_settings;
// Set up the new setting field for the Test Mode toggle notice.
$notice = array(
'stripe_connect_test_mode_toggle_notice' => array(
'id' => 'stripe_connect_test_mode_toggle_notice',
'desc' => '<p>' . __( 'You have disabled the "Test Mode" option. Once you have saved your changes, please verify your Stripe connection, especially if you have not previously connected in with "Test Mode" disabled.', 'easy-digital-downloads' ) . '</p>',
'type' => 'stripe_connect_notice',
'field_class' => 'edd-hidden',
),
'stripe_restrict_assets' => array(
'id' => 'stripe_restrict_assets',
'name' => ( __( 'Restrict Stripe Assets', 'easy-digital-downloads' ) ),
'desc' => ( __( 'Only load Stripe.com hosted assets on pages that specifically utilize Stripe functionality.', 'easy-digital-downloads' ) ),
'type' => 'checkbox',
'tooltip_title' => __( 'Loading Javascript from Stripe', 'easy-digital-downloads' ),
'tooltip_desc' => __( 'Stripe advises that their Javascript library be loaded on every page to take advantage of their advanced fraud detection rules. If you are not concerned with this, enable this setting to only load the Javascript when necessary. Read more about Stripe\'s recommended setup here: https://stripe.com/docs/web/setup.', 'easy-digital-downloads' ),
)
);
if ( edd_get_option( 'stripe_checkout' ) ) {
$stripe_settings['stripe_checkout'] = array(
'id' => 'stripe_checkout',
'name' => '<strong>' . __( 'Stripe Checkout', 'easy-digital-downloads' ) . '</strong>',
'type' => 'stripe_checkout_notice',
'desc' => wp_kses(
sprintf(
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
esc_html__( 'To ensure your website is compliant with the new %1$sStrong Customer Authentication%2$s (SCA) regulations, the legacy Stripe Checkout modal is no longer supported. Payments are still securely accepted through through Stripe on the standard Easy Digital Downloads checkout page. "Buy Now" buttons will also automatically redirect to the standard checkout page.', 'easy-digital-downloads' ),
'<a href="https://stripe.com/en-ca/guides/strong-customer-authentication" target="_blank" rel="noopener noreferrer">',
'</a>'
),
array(
'a' => array(
'href' => true,
'rel' => true,
'target' => true,
)
)
),
);
}
// Insert the new setting after the Test Mode checkbox.
$position = array_search( 'test_mode', array_keys( $settings['main'] ), true );
$settings = array_merge(
array_slice( $settings['main'], $position, 1, true ),
$notice,
$settings
);
if ( version_compare( EDD_VERSION, 2.5, '>=' ) ) {
$stripe_settings = array( 'edd-stripe' => $stripe_settings );
// Set up the new setting field for the Test Mode toggle notice
$notice = array(
'stripe_connect_test_mode_toggle_notice' => array(
'id' => 'stripe_connect_test_mode_toggle_notice',
'desc' => '<p>' . __( 'You have disabled the "Test Mode" option. Once you have saved your changes, please verify your Stripe connection, especially if you have not previously connected in with "Test Mode" disabled.', 'easy-digital-downloads' ) . '</p>',
'type' => 'stripe_connect_notice',
'field_class' => 'edd-hidden',
)
);
// Insert the new setting after the Test Mode checkbox
$position = array_search( 'test_mode', array_keys( $settings['main'] ), true );
$settings = array_merge(
array_slice( $settings['main'], $position, 1, true ),
$notice,
$settings
);
}
return array_merge( $settings, $stripe_settings );
return $settings;
}
add_filter( 'edd_settings_gateways', 'edds_add_settings' );
@ -204,11 +221,15 @@ add_filter( 'edd_settings_gateways', 'edds_add_settings' );
*
* @access public
* @since 2.5
*
* @param string $value The value currently set for the Stripe billing fields setting.
* @param string $key The Stripe setting key to detect, stripe_billing_fields.
*
* @return string
*/
function edd_stripe_sanitize_stripe_billing_fields_save( $value, $key ) {
if( 'stripe_billing_fields' == $key && edd_use_taxes() ) {
if ( 'stripe_billing_fields' === $key && edd_use_taxes() ) {
$value = 'full';
@ -223,8 +244,8 @@ add_filter( 'edd_settings_sanitize_select', 'edd_stripe_sanitize_stripe_billing_
* Filter the output of the statement descriptor option to add a max length to the text string
*
* @since 2.6
* @param $html string The full html for the setting output
* @param $args array The original arguments passed in to output the html
* @param string $html The full html for the setting output.
* @param array $args The original arguments passed in to output the html.
*
* @return string
*/
@ -264,7 +285,7 @@ function edd_stripe_connect_notice_callback( $args ) {
*
* @since 2.7.0
*
* @param array $args The setting field arguments
* @param array $args The setting field arguments.
*/
function edd_stripe_checkout_notice_callback( $args ) {
$value = isset( $args['desc'] ) ? $args['desc'] : '';
@ -280,7 +301,7 @@ function edd_stripe_checkout_notice_callback( $args ) {
* @since 2.8.1
*/
function edd_stripe_requirements_not_met_callback() {
$required_version = 5.6;
$required_version = 7.1;
$current_version = phpversion();
echo '<div class="notice inline notice-warning">';
@ -299,7 +320,7 @@ function edd_stripe_requirements_not_met_callback() {
),
array(
'code' => true,
'strong' => true
'strong' => true,
)
);
echo '</p>';
@ -338,7 +359,7 @@ function edd_stripe_requirements_not_met_callback() {
* @since 2.8.1
*
* @param string $html Setting HTML.
* @param array $args Setting arguments.
* @param array $args Setting arguments.
* @return string
*/
function edds_payment_gateways_notice( $html, $args ) {
@ -353,7 +374,7 @@ function edds_payment_gateways_notice( $html, $args ) {
return $html;
}
$required_version = 5.6;
$required_version = 7.1;
$current_version = phpversion();
$html .= '<div id="edds-payment-gateways-stripe-unmet-requirements" class="notice inline notice-info"><p>' .
@ -371,7 +392,7 @@ function edds_payment_gateways_notice( $html, $args ) {
),
array(
'code' => true,
'strong' => true
'strong' => true,
)
) .
'</p><p><strong>' .

View File

@ -13,21 +13,6 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Determines if the Stripe API keys can be managed manually.
*
* @since 2.8.0
*
* @return bool
*/
function edds_stripe_connect_can_manage_keys() {
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id', false );
$secret = edd_is_test_mode() ? edd_get_option( 'test_secret_key' ) : edd_get_option( 'live_secret_key' );
return empty( $stripe_connect_account_id ) && $secret;
}
/**
* Retrieves a URL to allow Stripe Connect via oAuth.
*
@ -158,12 +143,16 @@ function edds_process_gateway_connect_completion() {
$redirect_url = admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways&section=edd-stripe' );
if ( ! empty( $redirect_screen ) ) {
switch ( $redirect_screen ) {
case 'onboarding-wizard':
$redirect_url = admin_url( 'edit.php?post_type=download&page=edd-onboarding-wizard&current_step=payment_methods' );
break;
}
if ( ! empty( $redirect_screen ) && 'onboarding-wizard' === $redirect_screen ) {
$redirect_url = edd_get_admin_url(
array(
'page' => 'edd-onboarding-wizard',
'current_step' => 'payment_methods',
)
);
$gateways = edd_get_option( 'gateways', array() );
$gateways['stripe'] = true;
edd_update_option( 'gateways', $gateways );
}
wp_safe_redirect( esc_url_raw( $redirect_url ) );
@ -355,7 +344,7 @@ function edds_stripe_connect_setting_field() {
sprintf(
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
__( 'Have questions about connecting with Stripe? See the %1$sdocumentation%2$s.', 'easy-digital-downloads' ),
'<a href="' . esc_url( edds_documentation_route( 'stripe-connect' ) ) . '" target="_blank" rel="noopener noreferrer">',
'<a href="' . esc_url( edds_documentation_route( 'stripe' ) ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
),
array(
@ -572,6 +561,15 @@ function edds_stripe_connect_account_info_ajax_response() {
'Pay as you go pricing: 2% per-transaction fee + Stripe fees.',
'easy-digital-downloads'
)
. ' ' .
sprintf(
/** Translators: Replacements are for the html wrappers for the phrse Upgrade to Pro and should not be translated. */
esc_html__(
'%sUpgrade to Pro%s and install the Stripe Pro Gateway to remove transaction fees.', 'easy-digital-downloads'
),
'<span class="edd-pro-upgrade"><a href="' . edd_link_helper( 'https://easydigitaldownloads.com/pricing/', array( 'utm_medium' => 'stripe-settings', 'utm_content' => 'upgrade-to-pro' ) ) . '" target="_blank">',
'</a></span>'
)
)
: '';
@ -680,6 +678,20 @@ function edds_stripe_connect_admin_notices_register() {
);
try {
// Stripe Connect - Manually managed keys.
$registry->add(
'stripe-connect-manual',
array(
'message' => sprintf(
'<p>%s</p><p>%s</p>',
esc_html__( 'Your current Stripe payment connection is out of date. Enable more secure and reliable payments by clicking the button below to enable Stripe Connect.', 'easy-digital-downloads' ),
$connect_button
),
'type' => 'error',
'dismissible' => true,
)
);
// Stripe Connect.
$registry->add(
'stripe-connect',
@ -716,6 +728,7 @@ function edds_stripe_connect_admin_notices_register() {
'dismissible' => true,
)
);
} catch( Exception $e ) {
return new WP_Error( 'edds-invalid-notices-registration', esc_html__( $e->getMessage() ) );
};
@ -764,7 +777,11 @@ function edds_stripe_connect_admin_notices_print() {
// Stripe Connect.
if ( false === $mode_toggle ) {
$notices->output( 'stripe-connect' );
if ( edds_stripe_connect_can_manage_keys() ) {
$notices->output( 'stripe-connect-manual' );
} else {
$notices->output( 'stripe-connect' );
}
// Stripe Connect reconnect.
} else {
$notices->output( 'stripe-connect-reconnect' );

View File

@ -23,6 +23,38 @@ add_action( 'admin_init', function() {
}
} );
/**
* Automatic 'upgrade' to detect an existing Stripe install to keep them on card-elements.
*
* Since people can customize the checkout experiance, we need to keep existing installs on card
* elements, and let them manually migrate to the payment elements integration. This sets an option
* for the elements mode, and if the site is already set up, defines an option to label the fact that
* they still have access to swap between card and payment elements.
*
* @since 2.9.0
*/
add_action( 'admin_init', function() {
// If the elements mode exists, don't run this again.
if ( false !== edd_get_option( 'stripe_elements_mode' ) ) {
return;
}
$elements_mode = 'payment-elements';
if (
edds_stripe_connect_can_manage_keys() ||
! empty( edd_get_option( 'stripe_connect_account_id', false ) )
) {
$elements_mode = 'card-elements';
}
edd_update_option( 'stripe_elements_mode', $elements_mode );
if ( 'card-elements' === $elements_mode ) {
add_option( '_edds_legacy_elements_enabled', 1, false );
}
} );
/**
* Stripe Upgrade Notices
*

View File

@ -91,6 +91,10 @@ class EDD_Stripe {
require_once EDDS_PLUGIN_DIR . '/includes/class-stripe-api.php';
// We need this one to load early so we can use it in the upcoming includes.
require_once EDDS_PLUGIN_DIR . '/includes/elements/functions.php';
$elements_mode = edds_get_elements_mode();
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-stripe-api-unmet-requirements.php';
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-attribute-not-found.php';
require_once EDDS_PLUGIN_DIR . '/includes/utils/exceptions/class-stripe-object-not-found.php';
@ -108,15 +112,41 @@ class EDD_Stripe {
require_once EDDS_PLUGIN_DIR . '/includes/card-actions.php';
require_once EDDS_PLUGIN_DIR . '/includes/gateway-actions.php';
require_once EDDS_PLUGIN_DIR . '/includes/gateway-filters.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions.php';
// Payment Actions, separated by elements type.
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions/functions.php';
switch ( $elements_mode ) {
case 'card-elements':
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions/card-elements-actions.php';
break;
case 'payment-elements':
require_once EDDS_PLUGIN_DIR . '/includes/payment-actions/payment-elements-actions.php';
break;
}
require_once EDDS_PLUGIN_DIR . '/includes/webhooks.php';
require_once EDDS_PLUGIN_DIR . '/includes/elements.php';
require_once EDDS_PLUGIN_DIR . '/includes/scripts.php';
require_once EDDS_PLUGIN_DIR . '/includes/template-functions.php';
require_once EDDS_PLUGIN_DIR . '/includes/class-edd-stripe-rate-limiting.php';
// Load Apple Pay functions.
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/apple-pay.php';
// Stripe Elements, separated by elements type.
switch ( $elements_mode ) {
case 'card-elements':
require_once EDDS_PLUGIN_DIR . '/includes/elements/card-elements.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/index.php';
break;
case 'payment-elements':
require_once EDDS_PLUGIN_DIR . '/includes/elements/payment-elements.php';
break;
}
// Payment Methods.
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/index.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/buy-now/index.php';
if ( is_admin() ) {
@ -129,7 +159,6 @@ class EDD_Stripe {
require_once EDDS_PLUGIN_DIR . '/includes/admin/settings/stripe-connect.php';
require_once EDDS_PLUGIN_DIR . '/includes/admin/settings.php';
require_once EDDS_PLUGIN_DIR . '/includes/admin/upgrade-functions.php';
require_once EDDS_PLUGIN_DIR . '/includes/admin/reporting/class-stripe-reports.php';
}
if ( defined( 'WP_CLI' ) && WP_CLI ) {

View File

@ -158,15 +158,9 @@ function _edds_get_purchase_form_user( $valid_data = array() ) {
}
if ( ! empty( $user['user_id'] ) && $user['user_id'] > 0 && ! empty( $user['address'] ) ) {
// EDD3: Add the customer address if it doesn't already exist.
if ( function_exists( 'edd_maybe_add_customer_address' ) ) {
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
if ( $customer ) {
edd_maybe_add_customer_address( $customer->id, $user['address'] );
}
} else {
// Store the address in the user's meta so the cart can be pre-populated with it on return purchases.
update_user_meta( $user['user_id'], '_edd_user_address', $user['address'] );
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
if ( $customer ) {
edd_maybe_add_customer_address( $customer->id, $user['address'] );
}
}
@ -192,6 +186,9 @@ function _edds_get_purchase_form_user( $valid_data = array() ) {
* @since 2.7.0
*/
function _edds_process_purchase_form() {
// Unset any Errors so they aren't left over form other attempts.
edd_clear_errors();
// Catch exceptions at a high level.
try {
// `edd_process_purchase_form()` and subsequent code executions are written
@ -220,7 +217,7 @@ function _edds_process_purchase_form() {
do_action( 'edd_pre_process_purchase' );
// Make sure the cart isn't empty.
if ( ! edd_get_cart_contents() && ! edd_cart_has_fees() ) {
if ( empty( EDD()->cart->contents ) && empty( EDD()->cart->fees) ) {
throw new \Exception( esc_html__( 'Your cart is empty.', 'easy-digital-downloads' ) );
}
@ -305,15 +302,9 @@ function _edds_process_purchase_form() {
);
if ( ! empty( $user['user_id'] ) && $user['user_id'] > 0 && ! empty( $address ) ) {
// EDD3: Add the customer address if it doesn't already exist.
if ( function_exists( 'edd_maybe_add_customer_address' ) ) {
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
if ( $customer ) {
edd_maybe_add_customer_address( $customer->id, $user['address'] );
}
} else {
// Store the address in the user's meta so the cart can be pre-populated with it on return purchases.
update_user_meta( $user['user_id'], '_edd_user_address', $user['address'] );
$customer = edd_get_customer_by( 'user_id', $user['user_id'] );
if ( $customer ) {
edd_maybe_add_customer_address( $customer->id, $user['address'] );
}
}
@ -389,53 +380,72 @@ function _edds_process_purchase_form() {
add_action( 'wp_ajax_edds_process_purchase_form', '_edds_process_purchase_form' );
add_action( 'wp_ajax_nopriv_edds_process_purchase_form', '_edds_process_purchase_form' );
if ( ! function_exists( 'edd_is_dev_environment' ) ) {
/**
* WordPress core function polyfill for WordPress 5.4.
*
* @since 2.9.0
*/
if ( ! function_exists( 'wp_get_environment_type' ) ) {
/**
* Check the network site URL for signs of being a development environment.
* Retrieves the current environment type.
*
* @since 3.0
* The type can be set via the `WP_ENVIRONMENT_TYPE` global system variable,
* or a constant of the same name.
*
* @return bool $retval True if dev, false if not.
* Possible values are 'local', 'development', 'staging', and 'production'.
* If not set, the type defaults to 'production'.
*
* @return string The current environment type.
*/
function edd_is_dev_environment() {
function wp_get_environment_type() {
static $current_env = '';
// Assume not a development environment
$retval = false;
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && $current_env ) {
return $current_env;
}
// Get this one time and use it below
$network_url = network_site_url( '/' );
// Possible strings
$strings = array(
// Popular port suffixes
':8888', // This is common with MAMP on OS X
// Popular development TLDs
'.dev', // VVV
'.local', // Local
'.test', // IETF
'.example', // IETF
'.invalid', // IETF
'.localhost', // IETF
// Popular development subdomains
'dev.',
// Popular development domains
'localhost',
'example.com',
$wp_environments = array(
'local',
'development',
'staging',
'production',
);
// Loop through all strings
foreach ( $strings as $string ) {
if ( stristr( $network_url, $string ) ) {
$retval = $string;
break;
// Add a note about the deprecated WP_ENVIRONMENT_TYPES constant.
if ( defined( 'WP_ENVIRONMENT_TYPES' ) && function_exists( '_deprecated_argument' ) ) {
if ( function_exists( '__' ) ) {
/* translators: %s: WP_ENVIRONMENT_TYPES */
$message = sprintf( __( 'The %s constant is no longer supported.' ), 'WP_ENVIRONMENT_TYPES' );
} else {
$message = sprintf( 'The %s constant is no longer supported.', 'WP_ENVIRONMENT_TYPES' );
}
_deprecated_argument(
'define()',
'5.5.1',
$message
);
}
// Check if the environment variable has been set, if `getenv` is available on the system.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( false !== $has_env ) {
$current_env = $has_env;
}
}
// Filter & return
return apply_filters( 'edd_is_dev_environment', $retval );
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
$current_env = WP_ENVIRONMENT_TYPE;
}
// Make sure the environment is an allowed one, and not accidentally set to an invalid value.
if ( ! in_array( $current_env, $wp_environments, true ) ) {
$current_env = 'production';
}
return $current_env;
}
}

View File

@ -70,3 +70,111 @@ function edds_register_gateway( $gateways ) {
return edd_stripe()->register_gateway( $gateways );
}
/**
* Process refund in Stripe, in EDD 2.x
* For EDD 3.0, see `edd_stripe_maybe_refund_charge()`
* @see edd_stripe_maybe_refund_charge()
*
* @access public
* @since 1.8
* @deprecated 2.9.0
* @return void
*/
function edd_stripe_process_refund( $payment_id, $new_status, $old_status ) {
_edd_deprecated_function(
__FUNCTION__,
'2.9.0',
'edd_stripe_maybe_refund_charge',
debug_backtrace()
);
}
/**
* Load our admin javascript
*
* @access public
* @since 1.8
* @deprecated 2.9.0 - Deprecated as 2.9.0 requires EDD 3.1+
* @return void
*/
function edd_stripe_admin_js( $payment_id = 0 ) {
/**
* Since Stripe 2.9.0 requires EDD 3.1, we no longer need to load this.
*/
_edd_deprecated_function(
__FUNCTION__,
'2.9.0',
null,
debug_backtrace()
);
}
/**
* Display the payment status filters
*
* @since 1.6
* @deprecated 2.9.0
* @param array $views The array of views for the payments/orders table.
* @return array
*/
function edds_payment_status_filters( $views ) {
_edd_deprecated_function(
__FUNCTION__,
'2.9.0',
null,
debug_backtrace()
);
}
/**
* Renamed Apple Pay functions after we removed "prb" from them.
*
* Most of these were used internally only, and were hooked into display notices.
* No real documetnation is needed here.
*/
function edds_prb_apple_pay_admin_notices_register() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_admin_notices_register' );
return edds_apple_pay_admin_notices_register();
}
function edds_prb_apple_pay_get_fileinfo() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_get_fileinfo' );
return edds_apple_pay_get_fileinfo();
}
function edds_prb_apple_pay_is_valid() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_is_valid' );
return edds_apple_pay_is_valid();
}
function edds_prb_apple_pay_has_domain_verification_file() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_has_domain_verification_file' );
return edds_apple_pay_has_domain_verification_file();
}
function edds_prb_apple_pay_has_domain_verification() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_has_domain_verification' );
return edds_apple_pay_has_domain_verification();
}
function edds_prb_apple_pay_create_directory_and_move_file() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_create_directory_and_move_file' );
edds_apple_pay_create_directory_and_move_file();
}
function edds_prb_apple_pay_check_domain() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_check_domain' );
edds_apple_pay_check_domain();
}
function edds_prb_apple_pay_verify_domain() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_verify_domain' );
edds_apple_pay_verify_domain();
}
function edds_prb_apple_pay_admin_notices_print() {
_edd_deprecated_function( __FUNCTION__, '2.9.1', 'edds_apple_pay_admin_notices_print' );
edds_apple_pay_admin_notices_print();
}

View File

@ -1,11 +1,17 @@
<?php
/**
* Stripe Elements functionality.
* Stripe Card Elements functionality.
*
* @package EDD_Stripe
* @since 2.7.0
*/
/**
* Legacy Card Elements helper functions
*
* Note: These are not used in the Payment Elements integration.
*/
/**
* Retrieves the styles passed to the Stripe Elements instance.
*
@ -76,3 +82,21 @@ function edds_get_stripe_elements_options() {
return $elements_options;
}
/**
* Add the Card Elements values to the Stripe Localized variables.
*
* @since 2.9.0
*
* @param array $stripe_vars The array of values to localize for Stripe.
*
* @return array Includes any Card Elements values to the array of localized variables.
*/
function edds_card_element_js_vars( $stripe_vars ) {
$stripe_vars['elementsOptions'] = edds_get_stripe_elements_options();
$stripe_vars['elementsSplitFields'] = '1' === edd_get_option( 'stripe_split_payment_fields', false ) ? 'true' : 'false';
$stripe_vars['checkoutHasPaymentRequest'] = edds_prb_is_enabled( 'checkout' ) ? 'true' : 'false';
return $stripe_vars;
}
add_filter( 'edd_stripe_js_vars', 'edds_card_element_js_vars', 10, 1 );

View File

@ -0,0 +1,64 @@
<?php
/**
* Determines if the Stripe API keys can be managed manually.
*
* @since 2.8.0
*
* @return bool
*/
function edds_stripe_connect_can_manage_keys() {
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id', false );
$secret = edd_is_test_mode() ? edd_get_option( 'test_secret_key' ) : edd_get_option( 'live_secret_key' );
return empty( $stripe_connect_account_id ) && $secret;
}
/**
* Get the current elements mode.
*
* If the user is gated into the legacy mode, set the default to card-elements.
*
* @since 2.9.0
*
* @return string The elements mode string.
*/
function edds_get_elements_mode() {
$default = _edds_legacy_elements_enabled() ? 'card-elements' : 'payment-elements';
/**
* Because we use the deferred payment intents beta, only connected accounts can use Payment Elements
* for now, so we'll force them to be in `card-elements`.
*/
if ( edds_stripe_connect_can_manage_keys() ) {
return 'card-elements';
}
/**
* Recurring Subscription payment method updates need to still run card elements for now.
*/
if (
function_exists( 'edd_recurring' ) &&
( isset( $_GET['action'] ) && 'update' === $_GET['action'] ) &&
( isset( $_GET['subscription_id'] ) && is_numeric( $_GET['subscription_id'] ) )
) {
return 'card-elements';
}
return edd_get_option( 'stripe_elements_mode', $default );
}
/**
* INTERNAL ONLY: Determines if the user is gated into using the legacy card-elements.
*
* This is a transitionary function, intentded to allow us to later remove it. Do not
* use this function in any extending of EDD or Stripe.
*
* @since 2.9.0
*
* @return bool If the user is gated into using the legacy card-elements.
*/
function _edds_legacy_elements_enabled() {
return get_option( '_edds_legacy_elements_enabled', false );
}

View File

@ -0,0 +1,262 @@
<?php
/**
* Payments Elements helper functions
*
* Note: These are not used in the Card Elements integration.
*/
/**
* Retreives and allows filtering the payout elements theme.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_theme() {
$payment_elements_theme = 'none';
/**
* Filters the theme used for the Payment Elements object
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api#theme
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $payment_elements_theme The theme to use for the Payment Element object.
*/
return apply_filters( 'edds_stripe_payment_elements_theme', $payment_elements_theme );
}
/**
* Retreives and allows filtering the payout elements variables.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_variables() {
$payment_elements_variables = array();
/**
* Filters the variables used for the Payment Elements object
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api?platform=web#variables
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $payment_elements_variables Variables used for the Payment Elements.
*/
return apply_filters( 'edds_stripe_payment_elements_variables', $payment_elements_variables );
}
/**
* Retreives and allows filtering the payout elements rules.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_rules() {
$payment_elements_rules = array();
/**
* Filters the rules used for the Payment Elements object
*
* @example
* To match styles as closely as possible, EDD's imlementation does not use base 'border' declartations in our rules.
* If you want to customize the border behavior of inputs, you need to delcare a rule for each cardinal location:
* borderTop, borderRight, borderBottom, and borderLeft, with the full border color, width and style. The border radius
* is defined as borderRadius.
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api?platform=web#rules
*
* @param array $payment_elements_rules Rules used for the Payment Elements.
*/
return apply_filters( 'edds_stripe_payment_elements_rules', $payment_elements_rules );
}
/**
* Retreives and allows filtering the layout array sent to the Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_layout() {
$payment_elements_layout = array(
'type' => 'tabs',
'defaultCollapsed' => false,
);
/**
* Filters the layout variables passed to the Stripe Payment Elements.
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/elements_object/create_payment_element#payment_element_create-options-layout
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $payment_elements_layout Layout values used to create Stripe Elements object.
*/
return apply_filters( 'edds_stripe_payment_elements_layout', $payment_elements_layout );
}
/**
* Retreives and allows filtering the wallets sent to the Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_wallets() {
$default_wallet_behavior = 'auto';
/**
* Allows the ability to completely disable wallets (Google Pay, Apple Pay, etc) with a single filter.
*
* @example
* add_filter( 'edds_stripe_payment_elements_disable_wallets', '__return_true' );
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/elements_object/create_payment_element#payment_element_create-options-wallets
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param bool If wallets should be disabled.
*/
if ( apply_filters( 'edds_stripe_payment_elements_disable_wallets', false ) ) {
$default_wallet_behavior = 'never';
}
$enabled_wallets = array(
'applePay' => $default_wallet_behavior,
'googlePay' => $default_wallet_behavior,
);
/**
* Filters the wallets that the Payment Element will load.
*
* If you want to disable these, set their values to `never`
*
* @example
* array(
* 'applePay' => 'never',
* 'googlePay' => 'auto',
* )
*
* This uses an array_merge to ensure that if someone supplies a partial array we don't lose the default behavior.
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/elements_object/create_payment_element#payment_element_create-options-wallets
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $enabled_wallets Allowed wallets payment methods ot use on the Payment Element.
*/
$enabld_wallets = array_merge( $enabled_wallets, apply_filters( 'edds_stripe_payment_elements_wallets', $enabled_wallets ) );
return $enabld_wallets;
}
/**
* Retreives and allows filtering the label style sent to the Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_label_style() {
$label_style = 'above';
/**
* Filters the label appearance option.
*
* This can be set to either 'above' or 'floating'
*
* @since 2.9.0
*
* @link https://stripe.com/docs/elements/appearance-api?platform=web#others
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $label_style The style to use for the Payment Elements labels.
*/
return apply_filters( 'edds_stripe_payment_elements_label_style', $label_style );
}
/**
* Allows passing custom fonts into the Stripe Payment Elements.
*
* @since 2.9.0
*/
function edds_get_stripe_payment_elements_fonts() {
$fonts = array();
/**
* Allows passing custom font objects into the Stripe Elements.
*
* This either needs to be a CSS font soruce object, or a custom font source object.
* You can see the format and requiremnts for these in the links below. We default to none.
*
* @since 2.9.0
*
* @link https://stripe.com/docs/js/appendix/css_font_source_object
* @link https://stripe.com/docs/js/appendix/custom_font_source_object
*
* @see assets/js/src/frontend/payment-elements/index.php::generateElementStyles
*
* @param array $fonts The style to use for the Payment Elements labels.
*/
return apply_filters( 'edds_stripe_payment_elements_fonts', $fonts );
}
/**
* Gathers all the possible customizations for the Stripe Payment Elements.
*
* Pulls in the filtered customizations for the theme, variables, rules, and layout items
* for the Payment Elements instantiation. This allows developers to make customizations.
*
* EDD does attempt to match the input styles on the checkout already, in the Javascript.
*
* @since 2.9.0
*
* @returns array $customizations The array of customizations.
*/
function edds_gather_payment_element_customizations() {
$customizations = array(
'theme' => edds_get_stripe_payment_elements_theme(),
'variables' => edds_get_stripe_payment_elements_variables(),
'rules' => edds_get_stripe_payment_elements_rules(),
'layout' => edds_get_stripe_payment_elements_layout(),
'wallets' => edds_get_stripe_payment_elements_wallets(),
'labels' => edds_get_stripe_payment_elements_label_style(),
'fonts' => edds_get_stripe_payment_elements_fonts(),
'i18n' => array(
'errorMessages' => edds_get_localized_error_messages(),
),
);
if ( function_exists( 'edd_recurring' ) ) {
$customizations['cartHasSubscription'] = edd_recurring()->cart_contains_recurring() ? 'true' : 'false';
}
return $customizations;
}
/**
* Add the Payment Elements values to the Stripe Localized variables.
*
* @since 2.9.0
*
* @param array $stripe_vars The array of values to localize for Stripe.
*
* @return array Includes any Payment Elements values to the array of localized variables.
*/
function edds_payment_element_js_vars( $stripe_vars ) {
$stripe_vars['elementsCustomizations'] = edds_gather_payment_element_customizations();
return $stripe_vars;
}
add_filter( 'edd_stripe_js_vars', 'edds_payment_element_js_vars', 10, 1 );

View File

@ -75,11 +75,11 @@ function edds_is_gateway_active() {
function edds_has_met_requirements( $requirement = false ) {
$requirements = array(
'php' => (
version_compare( PHP_VERSION, '5.6.0', '>' )
version_compare( PHP_VERSION, '7.1', '>' )
),
'edd' => (
defined( 'EDD_VERSION' )
? version_compare( EDD_VERSION, '2.11', '>=' )
? version_compare( EDD_VERSION, '3.1', '>=' )
: true
),
'recurring' => (
@ -88,7 +88,7 @@ function edds_has_met_requirements( $requirement = false ) {
: true
),
'wp' => (
version_compare( get_bloginfo( 'version' ), '4.9', '>=' )
version_compare( get_bloginfo( 'version' ), '5.4', '>=' )
),
);
@ -147,6 +147,12 @@ function edds_truthy_to_bool( $truthy_value ) {
*/
function edd_stripe_existing_cards_enabled() {
$use_existing_cards = edd_get_option( 'stripe_use_existing_cards', false );
// Payment Elements doesn't support existing cards.
if ( 'payment-elements' === edds_get_elements_mode() ) {
$use_existing_cards = false;
}
return ! empty( $use_existing_cards );
}
@ -567,7 +573,7 @@ function edds_verify( $nonce = 'edd-process-checkout-nonce', $action = 'edd-proc
* @return string
*/
function edds_documentation_route( $type ) {
$base_url = 'https://docs.easydigitaldownloads.com/standard';
$base_url = 'https://easydigitaldownloads.com/docs';
/**
* Filter to change EDD-Stripe support url.
@ -713,8 +719,7 @@ function edd_refund_stripe_purchase( $order_id_or_object, $refund_object = null
edd_insert_payment_note( $order_id, $order_note );
// Add a negative transaction in EDD 3.0+.
if ( $refund_object instanceof Order && function_exists( 'edd_add_order_transaction' ) ) {
if ( $refund_object instanceof Order ) {
edd_add_order_transaction( array(
'object_id' => $refund_object->id,
'object_type' => 'order',
@ -771,4 +776,4 @@ function edds_get_tokenizer_input( $custom_id = '' ) {
esc_attr( $timestamp ),
esc_attr( \EDD\Utils\Tokenizer::tokenize( $timestamp ) )
);
}
}

View File

@ -49,13 +49,17 @@ function edds_process_post_data( $purchase_data ) {
return;
}
if ( isset( $_POST['edd_stripe_existing_card'] ) && 'new' !== $_POST['edd_stripe_existing_card'] ) {
return;
}
$elements_mode = edds_get_elements_mode();
// These are card-elements validations.
if ( 'card-elements' === $elements_mode ) {
if ( isset( $_POST['edd_stripe_existing_card'] ) && 'new' !== $_POST['edd_stripe_existing_card'] ) {
return;
}
// Require a name for new cards.
if ( ! isset( $_POST['card_name'] ) || strlen( trim( $_POST['card_name'] ) ) === 0 ) {
edd_set_error( 'no_card_name', __( 'Please enter a name for the credit card.', 'easy-digital-downloads' ) );
// Require a name for new cards.
if ( ! isset( $_POST['card_name'] ) || strlen( trim( $_POST['card_name'] ) ) === 0 ) {
edd_set_error( 'no_card_name', __( 'Please enter a name for the credit card.', 'easy-digital-downloads' ) );
}
}
}
add_action( 'edd_checkout_error_checks', 'edds_process_post_data' );

View File

@ -25,48 +25,51 @@ function edds_get_localized_error_messages() {
$generic_contact_issuer_message = __( 'There was an error processing your payment. Please contact your card issuer for more information.', 'easy-digital-downloads' );
$error_list = array(
'invalid_number' => __( 'The card number is not a valid credit card number.', 'easy-digital-downloads' ),
'invalid_expiry_month' => __( 'The card\'s expiration month is invalid.', 'easy-digital-downloads' ),
'invalid_expiry_year' => __( 'The card\'s expiration year is invalid.', 'easy-digital-downloads' ),
'invalid_cvc' => __( 'The card\'s security code is invalid.', 'easy-digital-downloads' ),
'incorrect_number' => __( 'The card number you provided is incorrect. Please check the number and try again.', 'easy-digital-downloads' ),
'incomplete_number' => __( 'The card number is incomplete.', 'easy-digital-downloads' ),
'incomplete_cvc' => __( 'The card\'s security code is incomplete.', 'easy-digital-downloads' ),
'incomplete_expiry' => __( 'The card\'s expiration date is incomplete.', 'easy-digital-downloads' ),
'incorrect_cvc' => __( 'The card\'s security code is incorrect.', 'easy-digital-downloads' ),
'incorrect_zip' => __( 'The card\'s zip code failed validation.', 'easy-digital-downloads' ),
'invalid_expiry_year_past' => __( 'The card\'s expiration year is in the past.', 'easy-digital-downloads' ),
'processing_error' => __( 'An error occurred while processing the card. Please try again.', 'easy-digital-downloads' ),
'email_invalid' => __( 'Invalid email address, please correct and try again.', 'easy-digital-downloads' ),
'authentication_required' => __( 'Your purchase may require additional authentication. Please try again and confirm any authentication requests.', 'easy-digital-downloads' ),
'approve_with_id' => __( 'There was an error processing your payment. Please try again, and if you continue to have problems, contact your card issuer.', 'easy-digital-downloads' ),
'currency_not_supported' => __( 'Your payment method is not authorized to make purchases in this currency. Please contact your card issuer.', 'easy-digital-downloads' ),
'expired_card' => __( 'The payment method you have provided is expired. Please try a different payment method.', 'easy-digital-downloads' ),
'try_again_later' => __( 'There was an error processing your payment. Please try again.', 'easy-digital-downloads' ),
'invalid_request_error' => $generic_different_payment_method,
'insufficient_funds' => $generic_different_payment_method,
'pin_try_exceeded' => $generic_different_payment_method,
'withdrawal_count_limit_exceeded' => $generic_different_payment_method,
'call_issuer' => $generic_contact_issuer_message,
'card_not_supported' => $generic_contact_issuer_message,
'card_velocity_exceeded' => $generic_contact_issuer_message,
'do_not_honor' => $generic_contact_issuer_message,
'do_not_try_again' => $generic_contact_issuer_message,
'invalid_account' => $generic_contact_issuer_message,
'invalid_amount' => $generic_contact_issuer_message,
'issuer_not_available' => $generic_contact_issuer_message,
'new_account_information_available' => $generic_contact_issuer_message,
'no_action_taken' => $generic_contact_issuer_message,
'not_permitted' => $generic_contact_issuer_message,
'pickup_card' => $generic_contact_issuer_message,
'restricted_card' => $generic_contact_issuer_message,
'revocation_of_all_authorizations' => $generic_contact_issuer_message,
'revocation_of_authorization' => $generic_contact_issuer_message,
'security_violation' => $generic_contact_issuer_message,
'service_not_allowed' => $generic_contact_issuer_message,
'stop_payment_order' => $generic_contact_issuer_message,
'transaction_not_allowed' => $generic_contact_issuer_message,
'card_declined' => $generic_contact_issuer_message,
'incomplete' => __( 'Payment processing cancelled; your order is not yet complete.', 'easy-digital-downloads' ),
'invalid_number' => __( 'The card number is not a valid credit card number.', 'easy-digital-downloads' ),
'invalid_expiry_month' => __( 'The card\'s expiration month is invalid.', 'easy-digital-downloads' ),
'invalid_expiry_year' => __( 'The card\'s expiration year is invalid.', 'easy-digital-downloads' ),
'invalid_cvc' => __( 'The card\'s security code is invalid.', 'easy-digital-downloads' ),
'incorrect_number' => __( 'The card number you provided is incorrect. Please check the number and try again.', 'easy-digital-downloads' ),
'incomplete_number' => __( 'The card number is incomplete.', 'easy-digital-downloads' ),
'incomplete_cvc' => __( 'The card\'s security code is incomplete.', 'easy-digital-downloads' ),
'incomplete_expiry' => __( 'The card\'s expiration date is incomplete.', 'easy-digital-downloads' ),
'incorrect_cvc' => __( 'The card\'s security code is incorrect.', 'easy-digital-downloads' ),
'incorrect_zip' => __( 'The card\'s zip code failed validation.', 'easy-digital-downloads' ),
'invalid_expiry_year_past' => __( 'The card\'s expiration year is in the past.', 'easy-digital-downloads' ),
'processing_error' => __( 'An error occurred while processing the card. Please try again.', 'easy-digital-downloads' ),
'email_invalid' => __( 'Invalid email address, please correct and try again.', 'easy-digital-downloads' ),
'authentication_required' => __( 'Your purchase may require additional authentication. Please try again and confirm any authentication requests.', 'easy-digital-downloads' ),
'approve_with_id' => __( 'There was an error processing your payment. Please try again, and if you continue to have problems, contact your card issuer.', 'easy-digital-downloads' ),
'currency_not_supported' => __( 'Your payment method is not authorized to make purchases in this currency. Please contact your card issuer.', 'easy-digital-downloads' ),
'expired_card' => __( 'The payment method you have provided is expired. Please try a different payment method.', 'easy-digital-downloads' ),
'try_again_later' => __( 'There was an error processing your payment. Please try again.', 'easy-digital-downloads' ),
'generic_decline' => $generic_different_payment_method,
'payment_intent_payment_attempt_failed' => $generic_different_payment_method,
'invalid_request_error' => $generic_different_payment_method,
'insufficient_funds' => $generic_different_payment_method,
'pin_try_exceeded' => $generic_different_payment_method,
'withdrawal_count_limit_exceeded' => $generic_different_payment_method,
'call_issuer' => $generic_contact_issuer_message,
'card_not_supported' => $generic_contact_issuer_message,
'card_velocity_exceeded' => $generic_contact_issuer_message,
'do_not_honor' => $generic_contact_issuer_message,
'do_not_try_again' => $generic_contact_issuer_message,
'invalid_account' => $generic_contact_issuer_message,
'invalid_amount' => $generic_contact_issuer_message,
'issuer_not_available' => $generic_contact_issuer_message,
'new_account_information_available' => $generic_contact_issuer_message,
'no_action_taken' => $generic_contact_issuer_message,
'not_permitted' => $generic_contact_issuer_message,
'pickup_card' => $generic_contact_issuer_message,
'restricted_card' => $generic_contact_issuer_message,
'revocation_of_all_authorizations' => $generic_contact_issuer_message,
'revocation_of_authorization' => $generic_contact_issuer_message,
'security_violation' => $generic_contact_issuer_message,
'service_not_allowed' => $generic_contact_issuer_message,
'stop_payment_order' => $generic_contact_issuer_message,
'transaction_not_allowed' => $generic_contact_issuer_message,
'card_declined' => $generic_contact_issuer_message,
);
/**
@ -96,14 +99,18 @@ function edds_get_localized_error_messages() {
function edds_get_localized_error_message( $error_code, $error_message, $decline_code = false ) {
$error_list = edds_get_localized_error_messages();
if ( 'card_declined' === $error_code && false !== $decline_code && ! empty( $error_list[ $decline_code ] ) ) {
if (
( 'card_declined' === $error_code || 'payment_intent_payment_attempt_failed' === $error_code || 'payment_intent_payment_attempt_failed' === $error_code ) &&
false !== $decline_code &&
! empty( $error_list[ $decline_code ] )
) {
$error_message = $error_list[ $decline_code ];
} elseif ( ! empty( $error_list[ $error_code ] ) ) {
$error_message = $error_list[ $error_code ];
}
// To make development and testing easier for store owners, append the error and decline codes.
if ( function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type() ) {
if ( 'local' === wp_get_environment_type() ) {
$error_message .= ' Error Code: ' . $error_code;
if ( false !== $decline_code ) {
$error_message .= ' Decline Code: ' . $decline_code;
@ -112,3 +119,17 @@ function edds_get_localized_error_message( $error_code, $error_message, $decline
return $error_message;
}
/**
* Returns a translatable string for informing the user that carts can only contain a single subscription and not be 'mixed'.
*
* @since 2.9.1
*
* @return string The translatable string for the single subscription requirement error.
*/
function edds_get_single_subscription_cart_error() {
return esc_html__(
'Subscriptions must be purchased individually. Please update your cart to only contain a single subscription.',
'easy-digital-downloads'
);
}

View File

@ -23,7 +23,7 @@ function edds_auto_register_login_user( $maybe_login ) {
return $maybe_login;
}
// If the request originated from the Stripe gateway on the Checkout, log in the registered user.
if ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'edds_create_payment', 'edds_complete_payment' ), true ) ) {
if ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'edds_create_payment', 'edds_complete_payment', 'edds_create_and_complete_order' ), true ) ) {
return true;
}

View File

@ -821,6 +821,7 @@ function edds_create_payment() {
// Ensure $_COOKIE is available without a new HTTP request.
if ( class_exists( 'EDD_Auto_Register' ) ) {
add_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
add_filter( 'edd_get_option_edd_auto_register_complete_orders_only', '__return_false' );
}
// Record the pending payment.
@ -1157,60 +1158,6 @@ function edds_complete_payment_authorization() {
add_action( 'wp_ajax_edds_complete_payment_authorization', 'edds_complete_payment_authorization' );
add_action( 'wp_ajax_nopriv_edds_complete_payment_authorization', 'edds_complete_payment_authorization' );
/**
* Sets up a \Stripe\Customer object based on the current purchase data.
*
* @param array $purchase_data {
*
* }
* @return \Stripe\Customer|false $customer Stripe Customer if one is created or false on error.
*/
function edds_checkout_setup_customer( $purchase_data ) {
$customer = false;
$stripe_customer_id = '';
if ( is_user_logged_in() ) {
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
}
if ( empty( $stripe_customer_id ) ) {
// No customer ID found, let's look one up based on the email.
$stripe_customer_id = edds_get_stripe_customer_id( $purchase_data['user_email'], false );
}
$customer_name = '';
if ( ! empty( $purchase_data['user_info']['first_name'] ) ) {
$customer_name .= sanitize_text_field( $purchase_data['user_info']['first_name'] );
}
if ( ! empty( $purchase_data['user_info']['last_name'] ) ) {
$customer_name .= ' ' . sanitize_text_field( $purchase_data['user_info']['last_name'] );
}
$customer_args = array(
'email' => $purchase_data['user_email'],
'description' => $purchase_data['user_email'],
'name' => $customer_name,
);
/**
* Filters the arguments used to create a Customer in Stripe.
*
* @since unknown
*
* @param array $customer_args {
* Arguments to create a Stripe Customer.
*
* @link https://stripe.com/docs/api/customers/create
* }
* @param array $purchase_data {
* Cart purchase data if in the checkout context. Empty otherwise.
* }
*/
$customer_args = apply_filters( 'edds_create_customer_args', $customer_args, $purchase_data );
$customer = edds_get_stripe_customer( $stripe_customer_id, $customer_args );
return $customer;
}
/**
* Generates a description based on the cart details.
*
@ -1244,222 +1191,3 @@ function edds_get_payment_description( $cart_details ) {
return html_entity_decode( $purchase_summary, ENT_COMPAT, 'UTF-8' );
}
/**
* Charge a preapproved payment
*
* @since 1.6
* @return bool
*/
function edds_charge_preapproved( $payment_id = 0 ) {
$retval = false;
if ( empty( $payment_id ) ) {
return $retval;
}
$payment = edd_get_payment( $payment_id );
$customer_id = $payment->get_meta( '_edds_stripe_customer_id' );
if ( empty( $customer_id ) ) {
return $retval;
}
if ( ! in_array( $payment->status, array( 'preapproval', 'preapproval_pending' ), true ) ) {
return $retval;
}
$setup_intent_id = $payment->get_meta( '_edds_stripe_setup_intent_id' );
try {
if ( edds_is_zero_decimal_currency() ) {
$amount = edd_get_payment_amount( $payment->ID );
} else {
$amount = edd_get_payment_amount( $payment->ID ) * 100;
}
$cart_details = edd_get_payment_meta_cart_details( $payment->ID );
$purchase_summary = edds_get_payment_description( $cart_details );
$statement_descriptor = edds_get_statement_descriptor();
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = substr( $purchase_summary, 0, 22 );
}
$statement_descriptor = apply_filters( 'edds_preapproved_statement_descriptor', $statement_descriptor, $payment->ID );
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = null;
}
// Create a PaymentIntent using SetupIntent data.
if ( ! empty( $setup_intent_id ) ) {
$setup_intent = edds_api_request( 'SetupIntent', 'retrieve', $setup_intent_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $setup_intent->payment_method,
'customer' => $setup_intent->customer,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => $setup_intent->metadata->toArray(),
'statement_descriptor' => $statement_descriptor,
);
// Process a legacy preapproval. Uses the Customer's default source.
} else {
$customer = \Stripe\Customer::retrieve( $customer_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $customer->default_source,
'customer' => $customer->id,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => array(
'email' => edd_get_payment_user_email( $payment->ID ),
'edd_payment_id' => $payment->ID,
),
'statement_descriptor' => $statement_descriptor,
);
}
/** This filter is documented in includes/payment-actions.php */
$intent_args = apply_filters( 'edds_create_payment_intent_args', $intent_args, array() );
$payment_intent = edds_api_request( 'PaymentIntent', 'create', $intent_args );
if ( 'succeeded' === $payment_intent->status ) {
$charge_id = current( $payment_intent->charges->data )->id;
$payment->status = 'publish';
$payment->add_note( 'Stripe Charge ID: ' . $charge_id );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $payment_intent->id );
$payment->add_meta( '_edds_stripe_payment_intent_id', $payment_intent->id );
$payment->transaction_id = $charge_id;
$retval = $payment->save();
}
} catch( \Stripe\Exception\ApiErrorException $e ) {
$error = $e->getJsonBody()['error'];
$payment->status = 'preapproval_pending';
$payment->add_note( esc_html(
edds_get_localized_error_message( $error['code'], $error['message'] )
) );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $error['payment_intent']['id'] );
$payment->add_meta( '_edds_stripe_payment_intent_id', $error['payment_intent']['id'] );
$payment->save();
/**
* Allows further processing when a Preapproved payment needs further action.
*
* @since 2.7.0
*
* @param int $payment_id ID of the payment.
*/
do_action( 'edds_preapproved_payment_needs_action', $payment_id );
} catch( \Exception $e ) {
$payment->add_note( esc_html( $e->getMessage() ) );
}
return $retval;
}
/**
* Process refund in Stripe, in EDD 2.x
* For EDD 3.0, see `edd_stripe_maybe_refund_charge()`
* @see edd_stripe_maybe_refund_charge()
*
* @access public
* @since 1.8
* @return void
*/
function edd_stripe_process_refund( $payment_id, $new_status, $old_status ) {
if ( empty( $_POST['edd_refund_in_stripe'] ) ) {
return;
}
$should_process_refund = 'publish' != $old_status && 'revoked' != $old_status ? false : true;
$should_process_refund = apply_filters( 'edds_should_process_refund', $should_process_refund, $payment_id, $new_status, $old_status );
if ( false === $should_process_refund ) {
return;
}
if ( 'refunded' != $new_status ) {
return;
}
try {
edd_refund_stripe_purchase( $payment_id );
} catch ( \Exception $e ) {
wp_die( $e->getMessage(), __( 'Error', 'easy-digital-downloads' ) , array( 'response' => 400 ) );
}
}
add_action( 'edd_update_payment_status', 'edd_stripe_process_refund', 200, 3 );
/**
* If selected, refunds a charge in Stripe when creating a new refund record.
* This handles refunds in EDD 3.0+. For EDD 2.x see `edd_stripe_process_refund()`
* @see edd_stripe_process_refund()
*
* @since 2.8.7
*
* @param int $order_id ID of the order we're processing a refund for.
* @param int $refund_id ID of the newly created refund record.
* @param bool $all_refunded Whether or not this was a full refund.
*/
function edd_stripe_maybe_refund_charge( $order_id, $refund_id, $all_refunded ) {
if ( ! current_user_can( 'edit_shop_payments', $order_id ) ) {
return;
}
if ( empty( $_POST['data'] ) ) {
return;
}
$order = edd_get_order( $order_id );
if ( empty( $order->gateway ) || 'stripe' !== $order->gateway ) {
return;
}
edd_debug_log( sprintf( 'Stripe - Maybe processing refund for order #%d.', $order_id ) );
// Get our data out of the serialized string.
parse_str( $_POST['data'], $form_data );
if ( empty( $form_data['edd-stripe-refund'] ) ) {
edd_debug_log( 'Stripe - Exiting refund process, as checkbox was not selected.' );
edd_add_note( array(
'object_id' => $order_id,
'object_type' => 'order',
'user_id' => is_admin() ? get_current_user_id() : 0,
'content' => __( 'Charge not refunded in Stripe, as checkbox was not selected.', 'easy-digital-downloads' )
) );
return;
}
edd_debug_log( 'Stripe - Refund checkbox was selected, proceeding to refund charge.' );
$refund = edd_get_order( $refund_id );
if ( empty( $refund->total ) ) {
edd_debug_log( sprintf(
'Stripe - Exiting refund for order #%d - refund total is empty.',
$order_id
) );
return;
}
try {
edd_refund_stripe_purchase( $order, $refund );
} catch ( \Exception $e ) {
edd_debug_log( sprintf( 'Exception thrown while refunding order #%d. Message: %s', $order_id, $e->getMessage() ) );
}
}
add_action( 'edd_refund_order', 'edd_stripe_maybe_refund_charge', 10, 3 );

View File

@ -0,0 +1,242 @@
<?php
/**
* Sets up a \Stripe\Customer object based on the current purchase data.
*
* @param array $purchase_data {
*
* }
* @return \Stripe\Customer|false $customer Stripe Customer if one is created or false on error.
*/
function edds_checkout_setup_customer( $purchase_data ) {
$customer = false;
$stripe_customer_id = '';
if ( is_user_logged_in() ) {
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
}
if ( empty( $stripe_customer_id ) ) {
// No customer ID found, let's look one up based on the email.
$stripe_customer_id = edds_get_stripe_customer_id( $purchase_data['user_email'], false );
}
$customer_name = '';
if ( ! empty( $purchase_data['user_info']['first_name'] ) ) {
$customer_name .= sanitize_text_field( $purchase_data['user_info']['first_name'] );
}
if ( ! empty( $purchase_data['user_info']['last_name'] ) ) {
$customer_name .= ' ' . sanitize_text_field( $purchase_data['user_info']['last_name'] );
}
$customer_args = array(
'email' => $purchase_data['user_email'],
'description' => $purchase_data['user_email'],
'name' => $customer_name,
);
/**
* Filters the arguments used to create a Customer in Stripe.
*
* @since unknown
*
* @param array $customer_args {
* Arguments to create a Stripe Customer.
*
* @link https://stripe.com/docs/api/customers/create
* }
* @param array $purchase_data {
* Cart purchase data if in the checkout context. Empty otherwise.
* }
*/
$customer_args = apply_filters( 'edds_create_customer_args', $customer_args, $purchase_data );
$customer = edds_get_stripe_customer( $stripe_customer_id, $customer_args );
return $customer;
}
/**
* Charge a preapproved payment
*
* @since 1.6
* @return bool
*/
function edds_charge_preapproved( $payment_id = 0 ) {
$retval = false;
if ( empty( $payment_id ) ) {
return $retval;
}
$payment = edd_get_payment( $payment_id );
$customer_id = $payment->get_meta( '_edds_stripe_customer_id' );
if ( empty( $customer_id ) ) {
return $retval;
}
if ( ! in_array( $payment->status, array( 'preapproval', 'preapproval_pending' ), true ) ) {
return $retval;
}
$setup_intent_id = $payment->get_meta( '_edds_stripe_setup_intent_id' );
try {
if ( edds_is_zero_decimal_currency() ) {
$amount = edd_get_payment_amount( $payment->ID );
} else {
$amount = edd_get_payment_amount( $payment->ID ) * 100;
}
$cart_details = edd_get_payment_meta_cart_details( $payment->ID );
$purchase_summary = edds_get_payment_description( $cart_details );
$statement_descriptor = edds_get_statement_descriptor();
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = substr( $purchase_summary, 0, 22 );
}
$statement_descriptor = apply_filters( 'edds_preapproved_statement_descriptor', $statement_descriptor, $payment->ID );
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = null;
}
// Create a PaymentIntent using SetupIntent data.
if ( ! empty( $setup_intent_id ) ) {
$setup_intent = edds_api_request( 'SetupIntent', 'retrieve', $setup_intent_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $setup_intent->payment_method,
'customer' => $setup_intent->customer,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => $setup_intent->metadata->toArray(),
'statement_descriptor' => $statement_descriptor,
);
// Process a legacy preapproval. Uses the Customer's default source.
} else {
$customer = \Stripe\Customer::retrieve( $customer_id );
$intent_args = array(
'amount' => $amount,
'currency' => edd_get_currency(),
'payment_method' => $customer->default_source,
'customer' => $customer->id,
'off_session' => true,
'confirm' => true,
'description' => $purchase_summary,
'metadata' => array(
'email' => edd_get_payment_user_email( $payment->ID ),
'edd_payment_id' => $payment->ID,
),
'statement_descriptor' => $statement_descriptor,
);
}
/** This filter is documented in includes/payment-actions.php */
$intent_args = apply_filters( 'edds_create_payment_intent_args', $intent_args, array() );
$payment_intent = edds_api_request( 'PaymentIntent', 'create', $intent_args );
if ( 'succeeded' === $payment_intent->status ) {
$charge_id = current( $payment_intent->charges->data )->id;
$payment->status = 'publish';
$payment->add_note( 'Stripe Charge ID: ' . $charge_id );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $payment_intent->id );
$payment->add_meta( '_edds_stripe_payment_intent_id', $payment_intent->id );
$payment->transaction_id = $charge_id;
$retval = $payment->save();
}
} catch( \Stripe\Exception\ApiErrorException $e ) {
$error = $e->getJsonBody()['error'];
$payment->status = 'preapproval_pending';
$payment->add_note( esc_html(
edds_get_localized_error_message( $error['code'], $error['message'] )
) );
$payment->add_note( 'Stripe PaymentIntent ID: ' . $error['payment_intent']['id'] );
$payment->add_meta( '_edds_stripe_payment_intent_id', $error['payment_intent']['id'] );
$payment->save();
/**
* Allows further processing when a Preapproved payment needs further action.
*
* @since 2.7.0
*
* @param int $payment_id ID of the payment.
*/
do_action( 'edds_preapproved_payment_needs_action', $payment_id );
} catch( \Exception $e ) {
$payment->add_note( esc_html( $e->getMessage() ) );
}
return $retval;
}
/**
* If selected, refunds a charge in Stripe when creating a new refund record.
* This handles refunds in EDD 3.0+. For EDD 2.x see `edd_stripe_process_refund()`
* @see edd_stripe_process_refund()
*
* @since 2.8.7
*
* @param int $order_id ID of the order we're processing a refund for.
* @param int $refund_id ID of the newly created refund record.
* @param bool $all_refunded Whether or not this was a full refund.
*/
function edd_stripe_maybe_refund_charge( $order_id, $refund_id, $all_refunded ) {
if ( ! current_user_can( 'edit_shop_payments', $order_id ) ) {
return;
}
if ( empty( $_POST['data'] ) ) {
return;
}
$order = edd_get_order( $order_id );
if ( empty( $order->gateway ) || 'stripe' !== $order->gateway ) {
return;
}
edd_debug_log( sprintf( 'Stripe - Maybe processing refund for order #%d.', $order_id ) );
// Get our data out of the serialized string.
parse_str( $_POST['data'], $form_data );
if ( empty( $form_data['edd-stripe-refund'] ) ) {
edd_debug_log( 'Stripe - Exiting refund process, as checkbox was not selected.' );
edd_add_note( array(
'object_id' => $order_id,
'object_type' => 'order',
'user_id' => is_admin() ? get_current_user_id() : 0,
'content' => __( 'Charge not refunded in Stripe, as checkbox was not selected.', 'easy-digital-downloads' )
) );
return;
}
edd_debug_log( 'Stripe - Refund checkbox was selected, proceeding to refund charge.' );
$refund = edd_get_order( $refund_id );
if ( empty( $refund->total ) ) {
edd_debug_log( sprintf(
'Stripe - Exiting refund for order #%d - refund total is empty.',
$order_id
) );
return;
}
try {
edd_refund_stripe_purchase( $order, $refund );
} catch ( \Exception $e ) {
edd_debug_log( sprintf( 'Exception thrown while refunding order #%d. Message: %s', $order_id, $e->getMessage() ) );
}
}
add_action( 'edd_refund_order', 'edd_stripe_maybe_refund_charge', 10, 3 );

View File

@ -0,0 +1,721 @@
<?php
/**
* Payment actions.
*
* @package EDD_Stripe
* @since 2.7.0
*/
function edds_maybe_disable_card_name() {
// We no longer need a card name field.
add_filter( 'edd_purchase_form_required_fields', function( $required_fields ) {
unset( $required_fields['card_name'] );
return $required_fields;
} );
remove_action( 'edd_checkout_error_checks', 'edds_process_post_data' );
}
add_action( 'edd_pre_process_purchase', 'edds_maybe_disable_card_name' );
/**
* Starts the process of completing a purchase with Stripe.
*
* Generates an intent that can require user authorization before proceeding.
*
* @link https://stripe.com/docs/payments/intents
* @since 2.7.0
*
* @param array $purchase_data {
* Purchase form data.
*
* }
*/
function edds_process_purchase_form( $purchase_data ) {
// Catch a straight to gateway request.
// Remove the error set by the "gateway mismatch" and allow the redirect.
if ( isset( $_REQUEST['edd_action'] ) && 'straight_to_gateway' === $_REQUEST['edd_action'] ) {
foreach ( $purchase_data['downloads'] as $download ) {
$options = isset( $download['options'] ) ? $download['options'] : array();
$options['quantity'] = isset( $download['quantity'] ) ? $download['quantity'] : 1;
edd_add_to_cart( $download['id'], $options );
}
edd_unset_error( 'edd-straight-to-gateway-error' );
edd_send_back_to_checkout();
return;
}
try {
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'We are unable to process your payment at this time, please try again later or contact support.',
'easy-digital-downloads'
)
);
}
/**
* Allows processing before an Intent is created.
*
* @since 2.7.0
*
* @param array $purchase_data Purchase data.
*/
do_action( 'edds_pre_process_purchase_form', $purchase_data );
/**
* We need to unhook some of the Recurring Payments actions here as we're handling captures ourselves.
*
* We're also going to attempt to restrict this to a single subscription and no mixed carts, for the time being.
*/
if ( function_exists( 'edd_recurring' ) ) {
if ( ( count( edd_get_cart_contents() ) > 1 && edd_recurring()->cart_contains_recurring() ) || edd_recurring()->cart_is_mixed() ) {
throw new \EDD_Stripe_Gateway_Exception( edds_get_single_subscription_cart_error() );
}
global $edd_recurring_stripe;
remove_filter( 'edds_create_payment_intent_args', array( $edd_recurring_stripe, 'create_payment_intent_args' ), 10, 2 );
remove_filter( 'edds_capture_payment_intent', array( $edd_recurring_stripe, 'capture_payment_intent' ) );
}
if ( edds_is_zero_decimal_currency() ) {
$amount = $purchase_data['price'];
} else {
$amount = round( $purchase_data['price'] * 100, 0 );
}
$existing_intent = false;
$customer = false;
if ( ! empty( $_REQUEST['intent_id'] ) && ! empty( $_REQUEST['intent_fingerprint'] ) ) {
$intent = edds_api_request( $_REQUEST['intent_type'], 'retrieve', $_REQUEST['intent_id'] );
if ( ! empty( $intent->customer ) ) {
$existing_intent = true;
$customer = edds_get_stripe_customer( $intent->customer, array() );
}
}
// We didn't have a customer on the existing intent. Make a new one.
if ( empty( $customer ) ) {
// Retrieves or creates a Stripe Customer.
$customer = edds_checkout_setup_customer( $purchase_data );
}
if ( ! $customer ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Unable to create customer. Please try again.',
'easy-digital-downloads'
)
);
}
/**
* Allows processing before an Intent is created, but
* after a \Stripe\Customer is available.
*
* @since 2.7.0
*
* @param array $purchase_data Purchase data.
* @param \Stripe\Customer $customer Stripe Customer object.
*/
do_action( 'edds_process_purchase_form_before_intent', $purchase_data, $customer );
// Create a list of {$download_id}_{$price_id}.
$payment_items = array();
foreach ( $purchase_data['cart_details'] as $item ) {
$price_id = isset( $item['item_number']['options']['price_id'] )
? $item['item_number']['options']['price_id']
: null;
$payment_items[] = $item['id'] . ( ! empty( $price_id ) ? ( '_' . $price_id ) : '' );
}
// Shared Intent arguments.
$intent_args = array(
'customer' => $customer->id,
'metadata' => array(
'email' => esc_html( $purchase_data['user_info']['email'] ),
'edd_payment_subtotal' => esc_html( $purchase_data['subtotal'] ),
'edd_payment_discount' => esc_html( $purchase_data['discount'] ),
'edd_payment_tax' => esc_html( $purchase_data['tax'] ),
'edd_payment_tax_rate' => esc_html( $purchase_data['tax_rate'] ),
'edd_payment_fees' => esc_html( edd_get_cart_fee_total() ),
'edd_payment_total' => esc_html( $purchase_data['price'] ),
'edd_payment_items' => esc_html( implode( ', ', $payment_items ) ),
),
);
if ( ! empty( $_REQUEST['payment_method_id'] ) ) {
$intent_args['payment_method'] = sanitize_text_field( $_REQUEST['payment_method_id'] );
} else {
$payment_method_types = array(
'card',
'link',
);
$intent_args['payment_method_types'] = $payment_method_types;
}
// Create a SetupIntent for a non-payment carts.
if ( edds_is_preapprove_enabled() || 0 === $amount ) {
$intent_args = array_merge(
array(
'description' => edds_get_payment_description( $purchase_data['cart_details'] ),
'usage' => 'off_session',
),
$intent_args
);
$intent_type = 'SetupIntent';
/**
* BETA Functionality.
*
* Sending the automatic_payment_methods flag to the SetupIntent is a beta feature that we have to enable via an API version
*
* @link https://stripe.com/docs/payments/defer-intent-creation?type=setup#create-intent
*/
add_action(
'edds_pre_stripe_api_request',
function() {
\Stripe\Stripe::setApiVersion( '2018-09-24;automatic_payment_methods_beta=v1' );
},
11
);
/**
* Filters the arguments used to create a SetupIntent.
*
* @since 2.7.0
*
* @param array $intent_args SetupIntent arguments.
* @param array $purchase_data {
* Purchase form data.
*
* }
*/
$intent_args = apply_filters( 'edds_create_setup_intent_args', $intent_args, $purchase_data );
} else {
$purchase_summary = edds_get_payment_description( $purchase_data['cart_details'] );
$statement_descriptor = edds_get_statement_descriptor();
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = substr( $purchase_summary, 0, 22 );
}
$statement_descriptor = apply_filters( 'edds_statement_descriptor', $statement_descriptor, $purchase_data );
$statement_descriptor = edds_sanitize_statement_descriptor( $statement_descriptor );
if ( empty( $statement_descriptor ) ) {
$statement_descriptor = null;
} elseif ( is_numeric( $statement_descriptor ) ) {
$statement_descriptor = edd_get_label_singular() . ' ' . $statement_descriptor;
}
$intent_args = array_merge(
array(
'amount' => $amount,
'currency' => edd_get_currency(),
'description' => $purchase_summary,
'statement_descriptor' => $statement_descriptor,
'setup_future_usage' => 'off_session',
),
$intent_args
);
$intent_type = 'PaymentIntent';
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id' );
if (
! empty( $stripe_connect_account_id ) &&
true === edds_stripe_connect_account_country_supports_application_fees()
) {
$intent_args['application_fee_amount'] = round( $amount * 0.02 );
}
/**
* Filters the arguments used to create a SetupIntent.
*
* @since 2.7.0
*
* @param array $intent_args SetupIntent arguments.
* @param array $purchase_data {
* Purchase form data.
*
* }
*/
$intent_args = apply_filters( 'edds_create_payment_intent_args', $intent_args, $purchase_data );
}
$new_fingerprint = md5( json_encode( $intent_args ) );
// Only update the intent, and process this further if we've made changes to the intent.
if ( ! empty( $_REQUEST['intent_id'] ) && ! empty( $_REQUEST['intent_fingerprint'] ) ) {
if ( hash_equals( $_REQUEST['intent_fingerprint'], $new_fingerprint ) ) {
return wp_send_json_success( array(
'intent_id' => $intent->id,
'client_secret' => $intent->client_secret,
'intent_type' => $intent_type,
'token' => wp_create_nonce( 'edd-process-checkout' ),
'intent_fingerprint' => $new_fingerprint,
'intent_changed' => 0,
) );
}
}
if ( ! empty( $existing_intent ) ) {
edds_api_request( $intent_type, 'update', $intent->id, $intent_args );
$intent = edds_api_request( $intent_type, 'retrieve', $intent->id );
} else {
$intent = edds_api_request( $intent_type, 'create', $intent_args );
}
/**
* Allows further processing after an Intent is created.
*
* @since 2.7.0
*
* @param array $purchase_data Purchase data.
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
* @param int $payment_id EDD Payment ID.
*/
do_action( 'edds_process_purchase_form', $purchase_data, $intent );
return wp_send_json_success( array(
'intent_id' => $intent->id,
'client_secret' => $intent->client_secret,
'intent_type' => $intent_type,
'token' => wp_create_nonce( 'edd-process-checkout' ),
'intent_fingerprint' => $new_fingerprint,
'intent_changed' => 1,
) );
} catch ( \Stripe\Exception\ApiErrorException $e ) {
$error = $e->getJsonBody()['error'];
// Record error in log.
edd_record_gateway_error(
esc_html__( 'Stripe Error 002', 'easy-digital-downloads' ),
sprintf(
esc_html__( 'There was an error while processing a Stripe payment. Order data: %s', 'easy-digital-downloads' ),
wp_json_encode( $error )
),
0
);
return wp_send_json_error( array(
'message' => esc_html(
edds_get_localized_error_message( $error['code'], $error['message'] )
),
) );
// Catch gateway processing errors.
} catch ( \EDD_Stripe_Gateway_Exception $e ) {
if ( true === $e->hasLogMessage() ) {
edd_record_gateway_error(
esc_html__( 'Stripe Error 003', 'easy-digital-downloads' ),
$e->getLogMessage(),
0
);
}
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
// Catch any remaining error.
} catch( \Exception $e ) {
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
}
}
add_action( 'edd_gateway_stripe', 'edds_process_purchase_form' );
/**
* Create an \EDD\Orders\Order.
*
* @since 2.9.0
*/
function edds_create_and_complete_order() {
// Map and merge serialized `form_data` to $_POST so it's accessible to other functions.
_edds_map_form_data_to_request( $_POST );
// Simulate being in an `edd_process_purchase_form()` request.
_edds_fake_process_purchase_step();
try {
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1001: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Rate limit reached during payment creation.'
);
}
// This must happen in the Checkout flow, so validate the Checkout nonce.
if ( false === edds_verify() ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1002: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Nonce verification failed during payment creation.'
);
}
$intent_id = isset( $_REQUEST['intent_id'] ) ? $_REQUEST['intent_id'] : '';
if ( ! isset( $intent_id ) ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1003: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to retrieve Intent data during payment creation.'
);
}
$purchase_data = edd_get_purchase_session();
if ( false === $purchase_data ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1004: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to retrieve purchase data during payment creation.'
);
}
// Ensure Intent has transitioned to the correct status.
if ( 'SetupIntent' === $_REQUEST['intent_type'] ) {
$intent = edds_api_request( 'SetupIntent', 'retrieve', $intent_id );
} else {
$intent = edds_api_request( 'PaymentIntent', 'retrieve', $intent_id );
}
if ( ! in_array( $intent->status, array( 'succeeded', 'requires_capture' ), true ) ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1005: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Invalid Intent status ' . $intent->status . ' during order creation.'
);
}
$order_data = array(
'price' => $purchase_data['price'],
'date' => $purchase_data['date'],
'user_email' => $purchase_data['user_email'],
'purchase_key' => $purchase_data['purchase_key'],
'currency' => edd_get_currency(),
'downloads' => $purchase_data['downloads'],
'cart_details' => $purchase_data['cart_details'],
'user_info' => $purchase_data['user_info'],
'status' => 'pending',
'gateway' => 'stripe',
);
// Ensure $_COOKIE is available without a new HTTP request.
if ( class_exists( 'EDD_Auto_Register' ) ) {
add_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
add_filter( 'edd_get_option_edd_auto_register_complete_orders_only', '__return_false' );
}
// Record the pending order.
$order_id = edd_build_order( $order_data );
if ( false === $order_id ) {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1006: An error occurred, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to insert order record.'
);
}
// Now get the newly created order.
$order = edd_get_order( $order_id );
// Retrieve the relevant Intent.
if ( 'setup_intent' === $intent->object ) {
$intent = edds_api_request( 'SetupIntent', 'update', $intent->id, array(
'metadata' => array(
'edd_payment_id' => $order->id,
),
) );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Payment Elements - Stripe SetupIntent ID: ' . $intent->id,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
edd_add_order_meta(
$order->id,
'_edds_stripe_setup_intent_id',
$intent->id
);
} else {
$intent = edds_api_request( 'PaymentIntent', 'update', $intent->id, array(
'metadata' => array(
'edd_payment_id' => $order->id,
),
) );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Payment Elements - Stripe PaymentIntent ID: ' . $intent->id,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
edd_add_order_meta(
$order->id,
'_edds_stripe_payment_intent_id',
$intent->id
);
}
// Use Intent ID for temporary transaction ID.
// It will be updated when a charge is available.
$order_transaction_id = edd_add_order_transaction(
array(
'object_id' => $order->id,
'object_type' => 'order',
'transaction_id' => sanitize_text_field( $intent->id ),
'gateway' => 'stripe',
'status' => 'pending',
'total' => $order->total,
)
);
// Retrieves or creates a Stripe Customer.
edd_update_order_meta( $order->id, '_edds_stripe_customer_id', $intent->customer );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Stripe Customer ID: ' . $intent->customer,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
// Attach the \Stripe\Customer ID to the \EDD_Customer meta if one exists.
$edd_customer = new EDD_Customer( $purchase_data['user_email'] );
if ( $edd_customer->id > 0 ) {
$edd_customer->update_meta( edd_stripe_get_customer_key(), $intent->customer );
}
if ( class_exists( 'EDD_Auto_Register' ) ) {
remove_action( 'set_logged_in_cookie', 'edds_set_logged_in_cookie_global' );
}
if ( has_action( 'edds_payment_created' ) ) {
// Load up an EDD Payment record here, in the event there is something hooking into it.
$payment = new EDD_Payment( $order->id );
/**
* Allows further processing after a payment is created.
*
* NOTE TO DEVELOPERS: Only hook into one of these complete hooks. Using both will result in
* unexpected double processing.
*
* @since 2.7.0
*
* @param \EDD_Payment $payment EDD Payment.
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
*/
do_action( 'edds_payment_created', $payment, $intent );
}
/**
* Allows further processing after a order is created.
*
* Sends back just the Intent ID to avoid needing always retrieve
* the intent in this step, which has been transformed via JSON,
* and is no longer a \Stripe\PaymentIntent
*
* @since 2.9.0
*
* @param \EDD\Orders\Order $order EDD Order Object.
* @param \Stripe\PaymentIntent|\Stripe\SetupIntent $intent Created Stripe Intent.
*/
do_action( 'edds_order_created', $order, $intent );
// Now we need to mark the order as complete.
$final_status = edds_is_preapprove_enabled() ? 'preapproval' : 'complete';
$updated = edd_update_order_status( $order->id, $final_status );
if ( $updated ) {
if ( 'setup_intent' !== $intent['object'] ) {
$charge_id = sanitize_text_field( current( $intent['charges']['data'] )['id'] );
edd_add_note(
array(
'object_id' => $order->id,
'content' => 'Stripe Charge ID: ' . $charge_id,
'user_id' => is_admin() ? get_current_user_id() : 0,
'object_type' => 'order',
)
);
edd_update_order_transaction(
$order_transaction_id,
array(
'transaction_id' => sanitize_text_field( $charge_id ),
'gateway' => 'stripe',
'status' => 'complete',
'total' => $order->total,
)
);
}
if ( has_action( 'edds_payment_complete' ) ) {
// Load up an EDD Payment record here, in the event there is something hooking into it.
$payment = new EDD_Payment( $order->id );
/**
* Allows further processing after a payment is completed.
*
* Sends back just the Intent ID to avoid needing always retrieve
* the intent in this step, which has been transformed via JSON,
* and is no longer a \Stripe\PaymentIntent
*
* NOTE TO DEVELOPERS: Only hook into one of these complete hooks. Using both will result in
* unexpected double processing.
*
* @since 2.7.0
*
* @param \EDD_Payment $payment EDD Payment.
* @param string $intent_id Stripe Intent ID.
*/
do_action( 'edds_payment_complete', $payment, $intent['id'] );
}
/**
* Allows further processing after a order is completed.
*
* Sends back just the Intent ID to avoid needing always retrieve
* the intent in this step, which has been transformed via JSON,
* and is no longer a \Stripe\PaymentIntent
*
* @since 2.9.0
*
* @param \EDD\Orders\Order $order The EDD Order object.
* @param string $intent_id Stripe Intent ID.
*/
do_action( 'edds_order_complete', $order, $intent['id'] );
// Empty cart.
edd_empty_cart();
} else {
throw new \EDD_Stripe_Gateway_Exception(
esc_html__(
'Error 1007: An error occurred completing the order, but your payment may have gone through. Please contact the site administrator.',
'easy-digital-downloads'
),
'Unable to insert order record.'
);
}
return wp_send_json_success( array(
'intent' => $intent,
'order' => $order,
// Send back a new nonce because the user might have logged in via Auto Register.
'nonce' => wp_create_nonce( 'edd-process-checkout' ),
) );
// Catch gateway processing errors.
} catch ( \EDD_Stripe_Gateway_Exception $e ) {
// Increase the rate limit count when something goes wrong mid-process.
edd_stripe()->rate_limiting->increment_card_error_count();
if ( true === $e->hasLogMessage() ) {
edd_record_gateway_error(
esc_html__( 'Stripe Error', 'easy-digital-downloads' ),
$e->getLogMessage(),
0
);
}
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
// Catch any remaining error.
} catch( \Exception $e ) {
return wp_send_json_error( array(
'message' => esc_html( $e->getMessage() ),
) );
}
}
add_action( 'wp_ajax_edds_create_and_complete_order', 'edds_create_and_complete_order' );
add_action( 'wp_ajax_nopriv_edds_create_and_complete_order', 'edds_create_and_complete_order' );
/**
* Uptick the rate limit card error count when a failure happens.
*
* @since 2.9.0
*/
function edds_payment_elements_rate_limit_tick() {
// Increase the card error count.
edd_stripe()->rate_limiting->increment_card_error_count();
exit();
}
add_action( 'wp_ajax_edds_payment_elements_rate_limit_tick', 'edds_payment_elements_rate_limit_tick' );
add_action( 'wp_ajax_nopriv_edds_payment_elements_rate_limit_tick', 'edds_payment_elements_rate_limit_tick' );
/**
* Generates a description based on the cart details.
*
* @param array $cart_details {
*
* }
* @return string
*/
function edds_get_payment_description( $cart_details ) {
$purchase_summary = '';
if( is_array( $cart_details ) && ! empty( $cart_details ) ) {
foreach( $cart_details as $item ) {
$purchase_summary .= $item['name'];
$price_id = isset( $item['item_number']['options']['price_id'] )
? absint( $item['item_number']['options']['price_id'] )
: false;
if ( false !== $price_id ) {
$purchase_summary .= ' - ' . edd_get_price_option_name( $item['id'], $item['item_number']['options']['price_id'] );
}
$purchase_summary .= ', ';
}
$purchase_summary = rtrim( $purchase_summary, ', ' );
}
// Stripe has a maximum of 999 characters in the charge description
$purchase_summary = substr( $purchase_summary, 0, 1000 );
return html_entity_decode( $purchase_summary, ENT_COMPAT, 'UTF-8' );
}

View File

@ -2,8 +2,6 @@
/**
* Payment Request: Apple Pay
*
* @link https://stripe.com/docs/stripe-js/elements/payment-request-button#verifying-your-domain-with-apple-pay
*
* @package EDD_Stripe
* @since 2.8.0
*/
@ -15,7 +13,7 @@
*
* @return true|WP_Error True if all notices are registered, otherwise WP_Error.
*/
function edds_prb_apple_pay_admin_notices_register() {
function edds_apple_pay_admin_notices_register() {
$registry = edds_get_registry( 'admin-notices' );
if ( ! $registry ) {
@ -25,8 +23,8 @@ function edds_prb_apple_pay_admin_notices_register() {
try {
// General error message.
$message = (
'<strong>' . esc_html__( 'Apple Pay domain verification error.', 'easy-digital-downloads' ) . '</strong><br />' .
edd_get_option( 'stripe_prb_apple_pay_domain_error', '' )
'<strong>' . esc_html__( 'Apple Pay domain verification error.', 'easy-digital-downloads' ) . '</strong><br />' .
edd_get_option( 'stripe_apple_pay_domain_error', '' )
);
$registry->add(
@ -56,14 +54,14 @@ function edds_prb_apple_pay_admin_notices_register() {
return true;
}
add_action( 'admin_init', 'edds_prb_apple_pay_admin_notices_register', 30 );
add_action( 'admin_init', 'edds_apple_pay_admin_notices_register', 30 );
/**
* Conditionally prints registered notices.
*
* @since 2.8.0
*/
function edds_prb_apple_pay_admin_notices_print() {
function edds_apple_pay_admin_notices_print() {
// Current user needs capability to dismiss notices.
if ( ! current_user_can( 'manage_options' ) ) {
return;
@ -80,15 +78,16 @@ function edds_prb_apple_pay_admin_notices_print() {
wp_enqueue_script( 'edds-admin-notices' );
try {
$error = edd_get_option( 'stripe_prb_apple_pay_domain_error', '' );
$test_mode = edd_is_test_mode();
$is_connected = edd_get_option( 'stripe_connect_account_id', '' );
$error = edd_get_option( 'stripe_apple_pay_domain_error', '' );
$test_mode = edd_is_test_mode();
if ( ! empty( $error ) && false === $test_mode ) {
if ( ! empty( $is_connected ) && ! empty( $error ) && false === $test_mode ) {
$notices->output( 'apple-pay-' . $_SERVER['HTTP_HOST'] );
}
} catch( Exception $e ) {}
}
add_action( 'admin_notices', 'edds_prb_apple_pay_admin_notices_print' );
add_action( 'admin_notices', 'edds_apple_pay_admin_notices_print' );
/**
* Returns information associated with the name/location of the domain verification file.
@ -97,7 +96,7 @@ add_action( 'admin_notices', 'edds_prb_apple_pay_admin_notices_print' );
*
* @return array Domain verification file information.
*/
function edds_prb_apple_pay_get_fileinfo() {
function edds_apple_pay_get_fileinfo() {
$path = untrailingslashit( $_SERVER['DOCUMENT_ROOT'] );
$dir = '.well-known';
$file = 'apple-developer-merchantid-domain-association';
@ -117,10 +116,10 @@ function edds_prb_apple_pay_get_fileinfo() {
*
* @return bool True if the domain has been verified and the association file exists.
*/
function edds_prb_apple_pay_is_valid() {
function edds_apple_pay_is_valid() {
return (
edds_prb_apple_pay_has_domain_verification_file() &&
edds_prb_apple_pay_has_domain_verification()
edds_apple_pay_has_domain_verification_file() &&
edds_apple_pay_has_domain_verification()
);
}
@ -131,8 +130,8 @@ function edds_prb_apple_pay_is_valid() {
*
* @return bool True if the domain verification file exists.
*/
function edds_prb_apple_pay_has_domain_verification_file() {
$fileinfo = edds_prb_apple_pay_get_fileinfo();
function edds_apple_pay_has_domain_verification_file() {
$fileinfo = edds_apple_pay_get_fileinfo();
if ( ! @file_exists( $fileinfo['fullpath'] ) ) {
return false;
@ -148,7 +147,7 @@ function edds_prb_apple_pay_has_domain_verification_file() {
*
* @return bool True if the saved verified domain matches the current site.
*/
function edds_prb_apple_pay_has_domain_verification() {
function edds_apple_pay_has_domain_verification() {
return edd_get_option( 'stripe_prb_apple_pay_domain' ) === $_SERVER['HTTP_HOST'];
}
@ -159,14 +158,14 @@ function edds_prb_apple_pay_has_domain_verification() {
*
* @throws \Exception If the directory or file cannot be created.
*/
function edds_prb_apple_pay_create_directory_and_move_file() {
$file = edds_prb_apple_pay_has_domain_verification_file();
function edds_apple_pay_create_directory_and_move_file() {
$file = edds_apple_pay_has_domain_verification_file();
if ( true === $file ) {
return;
}
$fileinfo = edds_prb_apple_pay_get_fileinfo();
$fileinfo = edds_apple_pay_get_fileinfo();
// Create directory if it does not exist.
if ( ! file_exists( trailingslashit( $fileinfo['path'] ) . $fileinfo['dir'] ) ) {
@ -176,7 +175,7 @@ function edds_prb_apple_pay_create_directory_and_move_file() {
}
// Move file if needed.
if ( ! edds_prb_apple_pay_has_domain_verification_file() ) {
if ( ! edds_apple_pay_has_domain_verification_file() ) {
if ( ! @copy( trailingslashit( EDDS_PLUGIN_DIR ) . $fileinfo['file'], $fileinfo['fullpath'] ) ) { // @codingStandardsIgnoreLine
throw new \Exception( __( 'Unable to copy domain association file to domain .well-known directory.', 'easy-digital-downloads' ) );
}
@ -189,8 +188,13 @@ function edds_prb_apple_pay_create_directory_and_move_file() {
*
* @since 2.8.0
*/
function edds_prb_apple_pay_check_domain() {
$error = edd_get_option( 'stripe_prb_apple_pay_domain_error', '' );
function edds_apple_pay_check_domain() {
$is_connected = edd_get_option( 'stripe_connect_account_id', '' );
if ( empty( $is_connected ) ) {
return;
}
$error = edd_get_option( 'stripe_apple_pay_domain_error', '' );
if ( empty( $error ) ) {
return;
@ -201,26 +205,35 @@ function edds_prb_apple_pay_check_domain() {
foreach ( $domains->autoPagingIterator() as $domain ) {
if ( $domain->domain_name === $_SERVER['HTTP_HOST'] ) {
edd_delete_option( 'stripe_prb_apple_pay_domain_error' );
edd_delete_option( 'stripe_apple_pay_domain_error' );
edd_update_option( 'stripe_prb_apple_pay_domain', $_SERVER['HTTP_HOST'] );
break;
}
}
} catch ( \Exception $e ) {}
}
add_action( 'admin_init', 'edds_prb_apple_pay_check_domain', 10 );
add_action( 'admin_init', 'edds_apple_pay_check_domain', 10 );
/**
* Verifies the current domain.
*
* @since 2.8.0
*/
function edds_prb_apple_pay_verify_domain() {
// Payment Request Button is not enabled, do nothing.
if ( false === edds_prb_is_enabled() ) {
function edds_apple_pay_verify_domain() {
// If Stripe isn't connected, just return.
if ( empty( edd_get_option( 'stripe_connect_account_id', '' ) ) ) {
return;
}
// Payment Request Button is not enabled, and card-elements is used.
$elements_mode = edds_get_elements_mode();
if ( 'card-elements' === $elements_mode ) {
if ( function_exists( 'edds_prb_is_enabled' ) && false === edds_prb_is_enabled() ) {
return;
}
}
// Avoid getting caught in AJAX requests.
if ( defined( 'DOING_AJAX' ) && true === DOING_AJAX ) {
return;
@ -237,19 +250,21 @@ function edds_prb_apple_pay_verify_domain() {
}
// Current domain matches and the file exists, do nothing.
if ( true === edds_prb_apple_pay_is_valid() ) {
if ( true === edds_apple_pay_is_valid() ) {
return;
}
try {
// Create directory and move file if needed.
edds_prb_apple_pay_create_directory_and_move_file();
edds_apple_pay_create_directory_and_move_file();
$stripe_connect_account_id = edd_get_option( 'stripe_connect_account_id', '' );
// Automatically verify when using "real" API keys.
if ( empty( $stripe_connect_account_id ) ) {
$verification = edds_api_request(
if (
empty( $stripe_connect_account_id ) || // If we don't have a stripe connect account ID
( ! empty( $stripe_connect_account_id ) && empty( edd_get_option( 'stripe_prb_apple_pay_domain') ) ) // Or if we do have a stripe connect account ID, but we haven't registered the domain for Apple Pay
) {
edds_api_request(
'ApplePayDomain',
'create',
array(
@ -274,7 +289,7 @@ function edds_prb_apple_pay_verify_domain() {
}
} catch ( \Exception $e ) {
// Set error if something went wrong.
edd_update_option( 'stripe_prb_apple_pay_domain_error', $e->getMessage() );
edd_update_option( 'stripe_apple_pay_domain_error', $e->getMessage() );
}
}
add_action( 'admin_init', 'edds_prb_apple_pay_verify_domain', 20 );
add_action( 'admin_init', 'edds_apple_pay_verify_domain', 20 );

View File

@ -14,33 +14,30 @@
function edds_buy_now_ajax_add_to_cart() {
$data = $_POST;
if ( ! isset( $data['download_id'] ) || ! isset( $data['nonce'] ) ) {
if ( empty( $data['download_id'] ) ) {
return wp_send_json_error( array(
'message' => __( 'Unable to add item to cart.', 'easy-digital-downloads' ),
) );
}
$download_id = absint( $data['download_id'] );
$price_id = absint( $data['price_id'] );
$quantity = absint( $data['quantity'] );
$nonce = sanitize_text_field( $data['nonce'] );
$valid_nonce = wp_verify_nonce( $nonce, 'edd-add-to-cart-' . $download_id );
if ( false === $valid_nonce ) {
if ( false === edds_verify( 'nonce', 'edd-add-to-cart-' . $download_id ) ) {
return wp_send_json_error( array(
'message' => __( 'Unable to add item to cart.', 'easy-digital-downloads' ),
) );
}
// Empty cart.
edd_empty_cart();
$args = array(
'quantity' => absint( $data['quantity'] ),
'price_id' => null,
);
if ( edd_has_variable_prices( $download_id ) ) {
$args['price_id'] = absint( $data['price_id'] );
}
// Add individual item.
edd_add_to_cart( $download_id, array(
'quantity' => $quantity,
'price_id' => $price_id,
) );
edd_add_to_cart( $download_id, $args );
return wp_send_json_success( array(
'checkout' => edds_buy_now_checkout(),
@ -55,7 +52,9 @@ add_action( 'wp_ajax_nopriv_edds_add_to_cart', 'edds_buy_now_ajax_add_to_cart' )
* @since 2.8.0
*/
function edds_buy_now_ajax_empty_cart() {
edd_empty_cart();
if ( ! empty( EDD()->cart->contents ) ) {
EDD()->cart->empty_cart();
}
return wp_send_json_success();
}

View File

@ -26,14 +26,8 @@ function edds_buy_now_modal() {
// Enqueue core scripts.
add_filter( 'edd_is_checkout', '__return_true' );
if ( function_exists( 'edd_enqueue_scripts' ) ) {
// https://github.com/easydigitaldownloads/easy-digital-downloads/issues/7847
edd_enqueue_scripts();
edd_localize_scripts();
} else {
edd_load_scripts();
edd_agree_to_terms_js();
}
edd_enqueue_scripts();
edd_localize_scripts();
remove_filter( 'edd_is_checkout', '__return_true' );
@ -63,6 +57,7 @@ function edds_buy_now_checkout() {
$form_mode = $total > 0
? 'payment-mode=stripe'
: 'payment-mode=manual';
$form_action = edd_get_checkout_uri( $form_mode );
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
@ -104,6 +99,9 @@ function edds_buy_now_checkout() {
action="<?php echo esc_url( $form_action ); ?>"
method="POST"
>
<?php if ( is_user_logged_in() && ! empty( $customer['email'] ) ) : ?>
<input type="hidden" name="edd_email" id="edd-email" value="<?php echo esc_attr( $customer['email'] ); ?>" required/>
<?php else: ?>
<p>
<label class="edd-label" for="edd-email">
<?php esc_html_e( 'Email Address', 'easy-digital-downloads' ); ?>
@ -123,6 +121,7 @@ function edds_buy_now_checkout() {
<?php endif; ?>
/>
</p>
<?php endif; ?>
<?php if ( $total > 0 ) : ?>
@ -185,13 +184,15 @@ function edds_buy_now_checkout_purchase_label( $label ) {
return $label;
}
return sprintf(
$label = sprintf(
'%s - %s',
edd_currency_filter(
edd_format_amount( $total )
),
$label
);
return $label;
}
/**

View File

@ -15,6 +15,14 @@
* @return array Filtered gateway settings.
*/
function edds_prb_add_settings( $settings ) {
/**
* In Version 2.9.0, PRBs are no longer necessary as they are part of the
* Payment Element.
*/
if ( false === _edds_legacy_elements_enabled() ) {
return $settings;
}
// Prevent adding the extra settings if the requirements are not met.
// The `edd_settings_gateways` filter runs regardless of the short circuit
// inside of `edds_add_settings()`
@ -34,7 +42,8 @@ function edds_prb_add_settings( $settings ) {
),
);
} else {
$prb_settings = array(
$elements_mode = edds_get_elements_mode();
$prb_settings = array(
array(
'id' => 'stripe_prb',
'name' => __( 'Apple Pay/Google Pay', 'easy-digital-downloads' ),
@ -51,7 +60,7 @@ function edds_prb_add_settings( $settings ) {
? '<br /><strong>' . __( 'Apple Pay is not available in Test Mode.', 'easy-digital-downloads' ) . '</strong> ' . sprintf(
/* translators: %1$s Opening anchor tag, do not translate. %2$s Opening anchor tag, do not translate. */
__( 'See our %1$sdocumentation%2$s for more information.', 'easy-digital-downloads' ),
'<a href="' . esc_url( edds_documentation_route( 'stripe-express-checkout' ) ) . '" target="_blank" rel="noopener noreferrer">',
'<a href="' . esc_url( edds_documentation_route( 'stripe-express-checkout-apple-pay-google-pay' ) ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
)
: ''
@ -81,12 +90,21 @@ function edds_prb_add_settings( $settings ) {
),
'checkout' => __( 'Checkout', 'easy-digital-downloads' ),
),
)
'class' => 'payment-elements' === $elements_mode ? 'edd-hidden card-elements-feature' : 'card-elements-feature',
),
array(
'id' => 'stripe_prb_elements_note',
'name' => __( 'Apple Pay/Google Pay', 'easy-digital-downloads' ),
'desc' => __( 'Apple Pay and Google Pay support is now provided via the Payment Elements integration.', 'easy-digital-downloads' ),
'type' => 'descriptive_text',
'class' => 'payment-elements' === $elements_mode ? 'payment-elements-feature' : 'edd-hidden payment-elements-feature',
),
);
}
$position = array_search(
'stripe_statement_descriptor',
'stripe_use_existing_cards',
array_values( wp_list_pluck( $settings['edd-stripe'], 'id' ) ),
true
);
@ -119,7 +137,7 @@ function edd_edds_stripe_prb_taxes_callback() {
'See the %1$sExpress Checkout documentation%2$s for more information.',
'easy-digital-downloads'
),
'<a href="' . esc_url( edds_documentation_route( 'stripe-express-checkout' ) ) . '#edds-prb-faqs" target="_blank" rel="noopener noreferrer">',
'<a href="' . esc_url( edds_documentation_route( 'stripe-express-checkout-apple-pay-google-pay' ) ) . '#edds-prb-faqs" target="_blank" rel="noopener noreferrer">',
'</a>'
),
array(

View File

@ -21,6 +21,10 @@ function edds_prb_is_enabled( $context = array() ) {
return false;
}
if ( 'payment-elements' === edds_get_elements_mode() ) {
return false;
}
// Gather allowed and enabled contexts.
$allowed_contexts = array( 'single', 'archive', 'checkout' );
$enabled_contexts = array_keys(

View File

@ -11,7 +11,6 @@ require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/checko
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/functions.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/template.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/shortcode.php';
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/apple-pay.php';
if ( is_admin() ) {
require_once EDDS_PLUGIN_DIR . '/includes/payment-methods/payment-request/admin/settings.php';

View File

@ -26,83 +26,96 @@ function edd_stripe_js( $force_load_scripts = false ) {
return;
}
if ( function_exists( 'edd_is_checkout' ) ) {
wp_register_script(
'sandhills-stripe-js-v3',
'https://js.stripe.com/v3/',
array(),
'v3'
);
$publishable_key = NULL;
$is_checkout = edd_is_checkout() && 0 < edd_get_cart_total();
$restrict_assets = edd_get_option( 'stripe_restrict_assets', false );
if ( edd_is_test_mode() ) {
$publishable_key = edd_get_option( 'test_publishable_key', '' );
} else {
$publishable_key = edd_get_option( 'live_publishable_key', '' );
}
if ( $is_checkout || $force_load_scripts || false === $restrict_assets ) {
wp_enqueue_script( 'sandhills-stripe-js-v3' );
}
wp_register_script(
if ( $is_checkout || $force_load_scripts ) {
$publishable_key_option = edd_is_test_mode() ? 'test_publishable_key' : 'live_publishable_key';
$publishable_key = edd_get_option( $publishable_key_option, '' );
// We're going to assume Payment Elements needs to load...
$script_source = EDDSTRIPE_PLUGIN_URL . 'assets/js/build/paymentelements.min.js';
$script_deps = array(
'sandhills-stripe-js-v3',
'https://js.stripe.com/v3/',
array(),
'v3'
'jquery',
'edd-ajax',
);
// But if the user has Card Elements, we need to load that instead.
$elements_mode = edds_get_elements_mode();
if ( 'card-elements' === $elements_mode ) {
$script_source = EDDSTRIPE_PLUGIN_URL . 'assets/js/build/cardelements.min.js';
$script_deps[] = 'jQuery.payment';
}
wp_register_script(
'edd-stripe-js',
EDDSTRIPE_PLUGIN_URL . 'assets/js/build/app.min.js',
array(
'sandhills-stripe-js-v3',
'jquery',
'edd-ajax'
),
EDD_STRIPE_VERSION,
$script_source,
$script_deps,
EDD_STRIPE_VERSION . '-' . $elements_mode,
true
);
$is_checkout = edd_is_checkout() && 0 < edd_get_cart_total();
$restrict_assets = edd_get_option( 'stripe_restrict_assets', false );
wp_enqueue_script( 'edd-stripe-js' );
if ( $is_checkout || $force_load_scripts || false === $restrict_assets ) {
wp_enqueue_script( 'sandhills-stripe-js-v3' );
}
$stripe_localized_vars = array(
'publishable_key' => trim( $publishable_key ),
'isTestMode' => edd_is_test_mode() ? 'true' : 'false',
'elementsMode' => $elements_mode,
'is_ajaxed' => edd_is_ajax_enabled() ? 'true' : 'false',
'currency' => edd_get_currency(),
// @todo Replace with country code derived from Stripe Account information if available.
// @link https://github.com/easydigitaldownloads/edd-stripe/issues/654
'country' => edd_get_option( 'base_country', 'US' ),
'locale' => edds_get_stripe_checkout_locale(),
'is_zero_decimal' => edds_is_zero_decimal_currency() ? 'true' : 'false',
'checkout' => edd_get_option( 'stripe_checkout' ) ? 'true' : 'false',
'store_name' => ! empty( edd_get_option( 'entity_name' ) ) ? edd_get_option( 'entity_name' ) : get_bloginfo( 'name' ),
'submit_text' => edd_get_option( 'stripe_checkout_button_text', __( 'Next', 'easy-digital-downloads' ) ),
'image' => edd_get_option( 'stripe_checkout_image' ),
'zipcode' => edd_get_option( 'stripe_checkout_zip_code', false ) ? 'true' : 'false',
'billing_address' => edd_get_option( 'stripe_checkout_billing', false ) ? 'true' : 'false',
'remember_me' => edd_get_option( 'stripe_checkout_remember', false ) ? 'true' : 'false',
'no_key_error' => __( 'Stripe publishable key missing. Please enter your publishable key in Settings.', 'easy-digital-downloads' ),
'checkout_required_fields_error' => __( 'Please fill out all required fields to continue your purchase.', 'easy-digital-downloads' ),
'checkout_agree_to_terms' => __( 'Please agree to the terms to complete your purchase.', 'easy-digital-downloads' ),
'checkout_agree_to_privacy' => __( 'Please agree to the privacy policy to complete your purchase.', 'easy-digital-downloads' ),
'generic_error' => __( 'Unable to complete your request. Please try again.', 'easy-digital-downloads' ),
'prepaid' => edd_get_option( 'stripe_allow_prepaid', false ) ? 'true' : 'false',
'successPageUri' => edd_get_success_page_uri(),
'failurePageUri' => edd_get_failed_transaction_uri(),
'debuggingEnabled' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? 'true' : 'false',
'formLoadingText' => __( 'Please wait...', 'easy-digital-downloads' ),
'cartHasSubscription' => function_exists( 'edd_recurring' ) && edd_recurring()->cart_contains_recurring() ? 'true' : 'false',
);
if ( $is_checkout || $force_load_scripts ) {
wp_enqueue_script( 'edd-stripe-js' );
wp_enqueue_script( 'jQuery.payment' );
$stripe_vars = apply_filters(
'edd_stripe_js_vars',
$stripe_localized_vars
);
$stripe_vars = apply_filters( 'edd_stripe_js_vars', array(
'publishable_key' => trim( $publishable_key ),
'is_ajaxed' => edd_is_ajax_enabled() ? 'true' : 'false',
'currency' => edd_get_currency(),
// @todo Replace with country code derived from Stripe Account information if available.
// @link https://github.com/easydigitaldownloads/edd-stripe/issues/654
'country' => edd_get_option( 'base_country', 'US' ),
'locale' => edds_get_stripe_checkout_locale(),
'is_zero_decimal' => edds_is_zero_decimal_currency() ? 'true' : 'false',
'checkout' => edd_get_option( 'stripe_checkout' ) ? 'true' : 'false',
'store_name' => get_bloginfo( 'name' ),
'alipay' => edd_get_option( 'stripe_alipay' ) ? 'true' : 'false',
'submit_text' => edd_get_option( 'stripe_checkout_button_text', __( 'Next', 'easy-digital-downloads' ) ),
'image' => edd_get_option( 'stripe_checkout_image' ),
'zipcode' => edd_get_option( 'stripe_checkout_zip_code', false ) ? 'true' : 'false',
'billing_address' => edd_get_option( 'stripe_checkout_billing', false ) ? 'true' : 'false',
'remember_me' => edd_get_option( 'stripe_checkout_remember', false ) ? 'true' : 'false',
'no_key_error' => __( 'Stripe publishable key missing. Please enter your publishable key in Settings.', 'easy-digital-downloads' ),
'checkout_required_fields_error' => __( 'Please fill out all required fields to continue your purchase.', 'easy-digital-downloads' ),
'checkout_agree_to_terms' => __( 'Please agree to the terms to complete your purchase.', 'easy-digital-downloads' ),
'checkout_agree_to_privacy' => __( 'Please agree to the privacy policy to complete your purchase.', 'easy-digital-downloads' ),
'generic_error' => __( 'Unable to complete your request. Please try again.', 'easy-digital-downloads' ),
'successPageUri' => edd_get_success_page_uri(),
'failurePageUri' => edd_get_failed_transaction_uri(),
'elementsOptions' => edds_get_stripe_elements_options(),
'elementsSplitFields' => '1' === edd_get_option( 'stripe_split_payment_fields', false ) ? 'true' : 'false',
'isTestMode' => edd_is_test_mode() ? 'true' : 'false',
'checkoutHasPaymentRequest' => edds_prb_is_enabled( 'checkout' ) ? 'true' : 'false',
) );
wp_localize_script( 'edd-stripe-js', 'edd_stripe_vars', $stripe_vars );
wp_localize_script( 'edd-stripe-js', 'edd_stripe_vars', $stripe_vars );
}
}
}
add_action( 'wp_enqueue_scripts', 'edd_stripe_js', 100 );
/**
* Conditionally load the Stripe CSS
*
* @param bool $force_load_scripts If we should forece loading the scripts outside of checkout.
*/
function edd_stripe_css( $force_load_scripts = false ) {
if ( false === edds_is_gateway_active() ) {
return;
@ -115,57 +128,27 @@ function edd_stripe_css( $force_load_scripts = false ) {
$deps = array();
}
wp_register_style( 'edd-stripe', EDDSTRIPE_PLUGIN_URL . 'assets/css/build/app.min.css', $deps, EDD_STRIPE_VERSION );
// We're going to assume Payment Elements needs to load...
$style_src = EDDSTRIPE_PLUGIN_URL . 'assets/css/build/paymentelements.min.css';
// But if the user has Card Elements, we need to load that instead.
$elements_mode = edds_get_elements_mode();
if ( 'card-elements' === $elements_mode ) {
$style_src = EDDSTRIPE_PLUGIN_URL . 'assets/css/build/cardelements.min.css';
}
wp_register_style(
'edd-stripe',
$style_src,
$deps,
EDD_STRIPE_VERSION . '-' . $elements_mode
);
wp_enqueue_style( 'edd-stripe' );
}
}
add_action( 'wp_enqueue_scripts', 'edd_stripe_css', 100 );
/**
* Load our admin javascript
*
* @access public
* @since 1.8
* @return void
*/
function edd_stripe_admin_js( $payment_id = 0 ) {
// Bail if on EDD 3.0+.
if ( function_exists( 'edd_get_order' ) ) {
return;
}
if( 'stripe' !== edd_get_payment_gateway( $payment_id ) ) {
return;
}
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('select[name=edd-payment-status]').change(function() {
if( 'refunded' == $(this).val() ) {
// Localize refund label
var edd_stripe_refund_charge_label = "<?php echo esc_js( __( 'Refund Charge in Stripe', 'easy-digital-downloads' ) ); ?>";
$(this).parent().parent().append( '<input type="checkbox" id="edd_refund_in_stripe" name="edd_refund_in_stripe" value="1" style="margin-top: 0;" />' );
$(this).parent().parent().append( '<label for="edd_refund_in_stripe">' + edd_stripe_refund_charge_label + '</label>' );
} else {
$('#edd_refund_in_stripe').remove();
$('label[for="edd_refund_in_stripe"]').remove();
}
});
});
</script>
<?php
}
add_action( 'edd_view_order_details_before', 'edd_stripe_admin_js', 100 );
/**
* Loads the javascript for the Stripe Connect functionality in the settings page.
*
@ -173,7 +156,7 @@ add_action( 'edd_view_order_details_before', 'edd_stripe_admin_js', 100 );
*/
function edd_stripe_connect_admin_script( $hook ) {
if( 'download_page_edd-settings' !== $hook ) {
if ( 'download_page_edd-settings' !== $hook ) {
return;
}
@ -188,11 +171,11 @@ function edd_stripe_connect_admin_script( $hook ) {
'edd-stripe-admin-scripts',
'edd_stripe_admin',
array(
'stripe_enabled' => array_key_exists( 'stripe', edd_get_enabled_payment_gateways() ),
'test_mode' => (int) edd_is_test_mode(),
'stripe_enabled' => array_key_exists( 'stripe', edd_get_enabled_payment_gateways() ),
'test_mode' => (int) edd_is_test_mode(),
'test_key_exists' => ! empty( $test_key ) ? 'true' : 'false',
'live_key_exists' => ! empty( $live_key ) ? 'true' : 'false',
'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
'ajaxurl' => esc_url( admin_url( 'admin-ajax.php' ) ),
)
);
}

View File

@ -20,8 +20,6 @@ add_action( 'edd_after_cc_fields', 'edds_add_stripe_errors', 999 );
*/
function edds_credit_card_form( $echo = true ) {
global $edd_options;
if ( edd_stripe()->rate_limiting->has_hit_card_error_limit() ) {
edd_set_error( 'edd_stripe_error_limit', __( 'We are unable to process your payment at this time, please try again later or contact support.', 'easy-digital-downloads' ) );
return;
@ -29,37 +27,34 @@ function edds_credit_card_form( $echo = true ) {
ob_start(); ?>
<?php if ( ! wp_script_is ( 'edd-stripe-js' ) ) : ?>
<?php if ( ! wp_script_is( 'edd-stripe-js' ) ) : ?>
<?php edd_stripe_js( true ); ?>
<?php endif; ?>
<?php do_action( 'edd_before_cc_fields' ); ?>
<fieldset id="edd_cc_fields" class="edd-do-validate">
<legend><?php _e( 'Credit Card Info', 'easy-digital-downloads' ); ?></legend>
<?php if( is_ssl() ) : ?>
<?php $elements_mode = edds_get_elements_mode(); ?>
<?php if ( 'card-elements' === $elements_mode ) : ?>
<legend><?php esc_html_e( 'Credit Card Info', 'easy-digital-downloads' ); ?></legend>
<?php else: ?>
<legend><?php esc_html_e( 'Payment Info', 'easy-digital-downloads' ); ?></legend>
<?php endif; ?>
<?php if ( is_ssl() ) : ?>
<div id="edd_secure_site_wrapper">
<span class="padlock">
<?php
if ( function_exists( 'edd_get_payment_icon' ) ) {
echo edd_get_payment_icon(
array(
'icon' => 'lock',
'width' => 18,
'height' => 28,
'classes' => array(
'edd-icon',
'edd-icon-lock',
),
)
);
} else {
?>
<svg class="edd-icon edd-icon-lock" xmlns="http://www.w3.org/2000/svg" width="18" height="28" viewBox="0 0 18 28" aria-hidden="true">
<path d="M5 12h8V9c0-2.203-1.797-4-4-4S5 6.797 5 9v3zm13 1.5v9c0 .828-.672 1.5-1.5 1.5h-15C.672 24 0 23.328 0 22.5v-9c0-.828.672-1.5 1.5-1.5H2V9c0-3.844 3.156-7 7-7s7 3.156 7 7v3h.5c.828 0 1.5.672 1.5 1.5z"/>
</svg>
<?php
}
echo edd_get_payment_icon(
array(
'icon' => 'lock',
'width' => 18,
'height' => 28,
'classes' => array(
'edd-icon',
'edd-icon-lock',
),
)
);
?>
</span>
<span><?php _e( 'This is a secure SSL encrypted payment.', 'easy-digital-downloads' ); ?></span>
@ -67,9 +62,11 @@ function edds_credit_card_form( $echo = true ) {
<?php endif; ?>
<?php
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
?>
<?php if ( ! empty( $existing_cards ) ) { edd_stripe_existing_card_field_radio( get_current_user_id() ); } ?>
if ( 'card-elements' === $elements_mode ) {
$existing_cards = edd_stripe_get_existing_cards( get_current_user_id() );
?>
<?php if ( ! empty( $existing_cards ) ) { edd_stripe_existing_card_field_radio( get_current_user_id() ); } ?>
<?php } ?>
<div class="edd-stripe-new-card" <?php if ( ! empty( $existing_cards ) ) { echo 'style="display: none;"'; } ?>>
<?php do_action( 'edd_stripe_new_card_form' ); ?>
@ -105,19 +102,63 @@ function edd_stripe_new_card_form() {
return;
}
$elements_mode = edds_get_elements_mode();
if ( 'payment-elements' === $elements_mode ) {
edds_output_payment_elements_form();
} else {
edds_output_legacy_new_card_form();
}
}
add_action( 'edd_stripe_new_card_form', 'edd_stripe_new_card_form' );
/**
* Add the element for the Stripe Payment Elements to attach to.
*
* @since 2.9.0
*/
function edds_output_payment_elements_form() {
// Payment Elements needs to not allow checking out with mixed carts or multiple subscriptions.
if ( function_exists( 'edd_recurring' ) ) {
if ( ( count( edd_get_cart_contents() ) > 1 && edd_recurring()->cart_contains_recurring() ) || edd_recurring()->cart_is_mixed() ) {
?>
<div class="edd_errors edd-alert edd-alert-info">
<p class="edd_error" id="edd_error_edd-stripe-incompatible-cart"><?php echo edds_get_single_subscription_cart_error(); ?></p>
</div>
<?php
return;
}
}
// Clear any errors that might be sitting from a previous AJAX loading of errors.
edd_clear_errors();
?>
<div id="edd-card-wrap">
<div id="edd-stripe-payment-element"></div>
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
</div>
<?php
}
/**
* Add the legacy Card Element fields for users who are still on Card Elements.
*
* @since 2.9.0
*/
function edds_output_legacy_new_card_form() {
$split = edd_get_option( 'stripe_split_payment_fields', false );
?>
?>
<p id="edd-card-name-wrap">
<label for="card_name" class="edd-label">
<?php esc_html_e( 'Name on the Card', 'easy-digital-downloads' ); ?>
<span class="edd-required-indicator">*</span>
</label>
<span class="edd-description"><?php esc_html_e( 'The name printed on the front of your credit card.', 'easy-digital-downloads' ); ?></span>
<input type="text" name="card_name" id="card_name" class="card-name edd-input required" placeholder="<?php esc_attr_e( 'Card name', 'easy-digital-downloads' ); ?>" autocomplete="cc-name" />
</p>
<p id="edd-card-name-wrap">
<label for="card_name" class="edd-label">
<?php esc_html_e( 'Name on the Card', 'easy-digital-downloads' ); ?>
<span class="edd-required-indicator">*</span>
</label>
<span class="edd-description"><?php esc_html_e( 'The name printed on the front of your credit card.', 'easy-digital-downloads' ); ?></span>
<input type="text" name="card_name" id="card_name" class="card-name edd-input required" placeholder="<?php esc_attr_e( 'Card name', 'easy-digital-downloads' ); ?>" autocomplete="cc-name" />
</p>
<div id="edd-card-wrap">
<div id="edd-card-wrap">
<label for="edd-card-element" class="edd-label">
<?php
if ( '1' === $split ) :
@ -138,11 +179,11 @@ function edd_stripe_new_card_form() {
</div>
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
</div>
</div>
<?php if ( '1' === $split ) : ?>
<?php if ( '1' === $split ) : ?>
<div id="edd-card-details-wrap">
<div id="edd-card-details-wrap">
<p class="edds-field-spacer-shim"></p><!-- Extra spacing -->
<div id="edd-card-exp-wrap">
@ -162,13 +203,13 @@ function edd_stripe_new_card_form() {
<div id="edd-stripe-card-cvc-element" class="edd-stripe-card-cvc-element"></div>
</div>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
<div id="edd-stripe-card-errors" role="alert"></div>
<div id="edd-stripe-card-errors" role="alert"></div>
<?php
<?php
/**
* Allow output of extra content before the credit card expiration field.
*
@ -180,7 +221,6 @@ function edd_stripe_new_card_form() {
*/
do_action( 'edd_before_cc_expiration' );
}
add_action( 'edd_stripe_new_card_form', 'edd_stripe_new_card_form' );
/**
* Show the checkbox for updating the billing information on an existing Stripe card
@ -800,38 +840,40 @@ function edd_stripe_zip_and_country() {
* @return void
*/
function edd_stripe_setup_billing_address_fields() {
if( ! function_exists( 'edd_use_taxes' ) ) {
if ( 'stripe' !== edd_get_chosen_gateway() || ! edd_get_cart_total() > 0 ) {
return;
}
if( edd_use_taxes() || 'stripe' !== edd_get_chosen_gateway() || ! edd_get_cart_total() > 0 ) {
$hook = 'payment-elements' === edds_get_elements_mode() || apply_filters( 'edds_address_before_payment', false ) ? 'edd_before_cc_fields' : 'edd_after_cc_fields';
if ( edd_use_taxes() ) {
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields' );
add_action( $hook, 'edd_default_cc_address_fields' );
return;
}
$display = edd_get_option( 'stripe_billing_fields', 'full' );
switch( $display ) {
switch ( $display ) {
case 'full' :
// Make address fields required
case 'full':
// Make address fields required.
add_filter( 'edd_require_billing_address', '__return_true' );
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields' );
add_action( $hook, 'edd_default_cc_address_fields' );
break;
case 'zip_country' :
case 'zip_country':
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
add_action( 'edd_after_cc_fields', 'edd_stripe_zip_and_country', 9 );
add_action( $hook, 'edd_stripe_zip_and_country', 9 );
// Make Zip required
// Make Zip required.
add_filter( 'edd_purchase_form_required_fields', 'edd_stripe_require_zip_and_country' );
break;
case 'none' :
case 'none':
remove_action( 'edd_after_cc_fields', 'edd_default_cc_address_fields', 10 );
break;