| 1 |
<?php |
| 2 |
/** |
| 3 |
* Stripe Payment Gateway class. |
| 4 |
* |
| 5 |
* @package Charitable/Classes/Charitable_Gateway_Stripe_AM |
| 6 |
* @author David Bisset |
| 7 |
* @copyright Copyright (c) 2023, WP Charitable LLC |
| 8 |
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License |
| 9 |
* @since 1.0.0 |
| 10 |
* @version 1.6.55 |
| 11 |
*/ |
| 12 |
|
| 13 |
// Exit if accessed directly. |
| 14 |
if ( ! defined( 'ABSPATH' ) ) { |
| 15 |
exit; |
| 16 |
} |
| 17 |
|
| 18 |
if ( ! class_exists( 'Charitable_Gateway_Stripe_AM' ) ) : |
| 19 |
|
| 20 |
/** |
| 21 |
* Stripe Gateway. |
| 22 |
* |
| 23 |
* @since 1.0.0 |
| 24 |
*/ |
| 25 |
class Charitable_Gateway_Stripe_AM extends Charitable_Gateway { |
| 26 |
|
| 27 |
/** The gateway ID. */ |
| 28 |
const ID = 'stripe'; |
| 29 |
|
| 30 |
/** The Stripe API version we are using. */ |
| 31 |
const STRIPE_API_VERSION = '2019-03-14'; |
| 32 |
|
| 33 |
/** |
| 34 |
* Gateway badge. |
| 35 |
* |
| 36 |
* @var string |
| 37 |
*/ |
| 38 |
protected $badge = ''; |
| 39 |
|
| 40 |
/** |
| 41 |
* Instantiate the gateway class, defining its key values. |
| 42 |
* |
| 43 |
* @since 1.0.0 |
| 44 |
*/ |
| 45 |
public function __construct() { |
| 46 |
|
| 47 |
/** |
| 48 |
* Filter the gateway name |
| 49 |
* |
| 50 |
* @since 1.0.0 |
| 51 |
* |
| 52 |
* @param string $name Gateway name. |
| 53 |
*/ |
| 54 |
$this->name = apply_filters( 'charitable_gateway_stripe_name', __( 'Stripe', 'charitable' ) ); |
| 55 |
|
| 56 |
$this->defaults = [ |
| 57 |
'label' => __( 'Stripe', 'charitable' ), |
| 58 |
]; |
| 59 |
|
| 60 |
$this->badge = __( 'Recommended', 'charitable' ); |
| 61 |
|
| 62 |
$this->recommended = true; |
| 63 |
|
| 64 |
$this->supports = [ |
| 65 |
'1.3.0', |
| 66 |
'credit-card', |
| 67 |
'recurring', |
| 68 |
'refunds', |
| 69 |
]; |
| 70 |
|
| 71 |
/* Needed for backwards compatibility with Charitable < 1.3 */ |
| 72 |
$this->credit_card_form = true; |
| 73 |
|
| 74 |
add_filter( 'charitable_option_enable_stripe_checkout', array( $this, 'maybe_return_stripe_connect_setting' ), 10, 4 ); |
| 75 |
add_filter( 'charitable_option_enable_checkout_billing_address_collection', array( $this, 'maybe_return_stripe_connect_setting' ), 10, 4 ); |
| 76 |
add_filter( 'charitable_option_statement_descriptor', array( $this, 'maybe_return_stripe_connect_setting' ), 10, 4 ); |
| 77 |
add_filter( 'charitable_option_statement_descriptor_custom', array( $this, 'maybe_return_stripe_connect_setting' ), 10, 4 ); |
| 78 |
|
| 79 |
add_action( 'charitable_stripe_ipn_event', array( $this, 'maybe_process_webhook_with_stripe_connect' ), 10, 2 ); |
| 80 |
|
| 81 |
add_filter( 'charitable_minimum_donation_amount', array( $this, 'set_minimum_donation_amount' ), 10 ); |
| 82 |
} |
| 83 |
|
| 84 |
/** |
| 85 |
* Register the Stripe payment gateway class. |
| 86 |
* |
| 87 |
* @param string[] $gateways The list of registered gateways. |
| 88 |
* @return string[] |
| 89 |
* @since 1.7.0 |
| 90 |
*/ |
| 91 |
public static function register_gateway( $gateways ) { |
| 92 |
$gateways['stripe'] = 'Charitable_Gateway_Stripe'; |
| 93 |
return $gateways; |
| 94 |
} |
| 95 |
|
| 96 |
/** |
| 97 |
* Register gateway settings. |
| 98 |
* |
| 99 |
* @param array $settings The existing settings to display for the Stripe settings page. |
| 100 |
* @return array |
| 101 |
* @since 1.7.0 |
| 102 |
*/ |
| 103 |
public function gateway_settings( $settings ) { |
| 104 |
|
| 105 |
$settings = $settings + array( |
| 106 |
'cc_fields_format' => [ |
| 107 |
'title' => __( 'Credit Card Fields Format <span class="badge beta">Beta</span>', 'charitable' ), |
| 108 |
'type' => 'select', |
| 109 |
'priority' => 4, |
| 110 |
'class' => 'general-settings', |
| 111 |
'default' => 'below_amount_selection', |
| 112 |
'options' => array( |
| 113 |
'' => __( 'Single field', 'charitable' ), |
| 114 |
'multiple' => __( 'Multiple fields', 'charitable' ), |
| 115 |
), |
| 116 |
'help' => __( 'Single field (default) provides the card, expiration date, zip code, and CVC fields in one field. Multiple fields (beta) are seperated.', 'charitable' ), |
| 117 |
], |
| 118 |
); |
| 119 |
|
| 120 |
if ( ( charitable_is_debug() ) || charitable_is_admin_debug() ) { |
| 121 |
|
| 122 |
if ( charitable()->is_stripe_connect_addon() ) { |
| 123 |
|
| 124 |
$settings = $settings + array( |
| 125 |
'enable_stripe_checkout' => [ |
| 126 |
'type' => 'checkbox', |
| 127 |
'title' => __( 'Use Stripe Checkout', 'charitable' ), |
| 128 |
'priority' => 4, |
| 129 |
'help' => __( 'When you enable Stripe Checkout, donors enter their credit card details into a secure hosted payment page managed by Stripe. <a href="https://stripe.com/docs/checkout" target="_blank">https://stripe.com/docs/checkout</a>', 'charitable' ), |
| 130 |
], |
| 131 |
'section_statement_descriptor' => [ |
| 132 |
'type' => 'heading', |
| 133 |
'title' => __( 'Statement Descriptor', 'charitable' ), |
| 134 |
'priority' => 25, |
| 135 |
], |
| 136 |
'statement_descriptor' => [ |
| 137 |
'type' => 'select', |
| 138 |
'title' => __( 'Format', 'charitable' ), |
| 139 |
'priority' => 26, |
| 140 |
'help' => __( 'Customize the statement descriptor that will appear on donors\' bank statements.', 'charitable' ), |
| 141 |
'default' => 'auto', |
| 142 |
'options' => [ |
| 143 |
'auto' => __( 'Use campaign title', 'charitable' ), |
| 144 |
'custom' => __( 'Set a custom descriptor', 'charitable' ), |
| 145 |
], |
| 146 |
], |
| 147 |
'statement_descriptor_custom' => [ |
| 148 |
'type' => 'text', |
| 149 |
'title' => __( 'Custom Statement Descriptor', 'charitable' ), |
| 150 |
'priority' => 26, |
| 151 |
'help' => __( 'A custom statement descriptor up to 22 characters long.', 'charitable' ), |
| 152 |
'attrs' => [ |
| 153 |
'data-trigger-key' => '#charitable_settings_gateways_stripe_statement_descriptor', |
| 154 |
'data-trigger-value' => 'custom', |
| 155 |
'maxlength' => 22, |
| 156 |
], |
| 157 |
], |
| 158 |
); |
| 159 |
|
| 160 |
} |
| 161 |
|
| 162 |
return array_merge( |
| 163 |
$settings, |
| 164 |
array( |
| 165 |
'section_live_mode' => [ |
| 166 |
'title' => __( 'Live Settings', 'charitable' ), |
| 167 |
'type' => 'heading', |
| 168 |
'priority' => 5, |
| 169 |
], |
| 170 |
'live_secret_key' => [ |
| 171 |
'type' => 'text', |
| 172 |
'title' => __( 'Live Secret Key', 'charitable' ), |
| 173 |
'priority' => 6, |
| 174 |
'class' => 'wide', |
| 175 |
], |
| 176 |
'live_public_key' => [ |
| 177 |
'type' => 'text', |
| 178 |
'title' => __( 'Live Publishable Key', 'charitable' ), |
| 179 |
'priority' => 7, |
| 180 |
'class' => 'wide', |
| 181 |
], |
| 182 |
'section_test_mode' => [ |
| 183 |
'title' => __( 'Test Settings', 'charitable' ), |
| 184 |
'type' => 'heading', |
| 185 |
'priority' => 10, |
| 186 |
], |
| 187 |
'test_secret_key' => [ |
| 188 |
'type' => 'text', |
| 189 |
'title' => __( 'Test Secret Key', 'charitable' ), |
| 190 |
'priority' => 11, |
| 191 |
'class' => 'wide', |
| 192 |
], |
| 193 |
'test_public_key' => [ |
| 194 |
'type' => 'text', |
| 195 |
'title' => __( 'Test Publishable Key', 'charitable' ), |
| 196 |
'priority' => 12, |
| 197 |
'class' => 'wide', |
| 198 |
], |
| 199 |
'api_description' => array( |
| 200 |
'type' => 'content', |
| 201 |
'title' => __( 'Connection Status', 'charitable' ), |
| 202 |
'content' => $this->get_connection_status_content(), |
| 203 |
'priority' => 21, |
| 204 |
), |
| 205 |
) |
| 206 |
); |
| 207 |
|
| 208 |
} else { |
| 209 |
|
| 210 |
$stripe_settings = array( |
| 211 |
'api_description' => array( |
| 212 |
'type' => 'content', |
| 213 |
'title' => __( 'Connection Status', 'charitable' ), |
| 214 |
'content' => $this->get_connection_status_content(), |
| 215 |
'priority' => 5, |
| 216 |
), |
| 217 |
); |
| 218 |
|
| 219 |
if ( charitable_get_option( 'test_mode' ) || ( isset( $force_test_mode ) && $force_test_mode ) ) { |
| 220 |
$mode = 'test'; |
| 221 |
} else { |
| 222 |
$mode = 'live'; |
| 223 |
} |
| 224 |
|
| 225 |
if ( $this->check_keys_exist( $mode ) && false === charitable_using_stripe_connect() && charitable()->is_stripe_connect_addon() ) { |
| 226 |
// if there ARE keys and the Stripe addon is active... BUT WE ARE NOT USING 1.70 Stripe Connect... so this is most likely previous stripe addon keys. |
| 227 |
$stripe_settings = $stripe_settings + array( |
| 228 |
'stripe_modify_keys' => [ |
| 229 |
'title' => '', |
| 230 |
'type' => 'content', |
| 231 |
'class' => 'manually-modify', |
| 232 |
'content' => '<a href="#" data-charitable-args="mode=' . $mode . '" data-charitable-action="show-stripe-keys" class="manually-modify-keys" id="charitable-modify-keys">Manually Modify Keys</a>', |
| 233 |
'priority' => 15, |
| 234 |
], |
| 235 |
); |
| 236 |
} |
| 237 |
|
| 238 |
if ( charitable()->is_stripe_connect_addon() ) { |
| 239 |
|
| 240 |
$stripe_settings = $stripe_settings + array( |
| 241 |
'enable_stripe_checkout' => [ |
| 242 |
'type' => 'checkbox', |
| 243 |
'title' => __( 'Use Stripe Checkout', 'charitable' ), |
| 244 |
'priority' => 16, |
| 245 |
'help' => __( 'When you enable Stripe Checkout, donors enter their credit card details into a secure hosted payment page managed by Stripe. <a href="https://stripe.com/docs/checkout" target="_blank">https://stripe.com/docs/checkout</a>', 'charitable' ), |
| 246 |
], |
| 247 |
'enable_checkout_billing_address_collection' => [ |
| 248 |
'type' => 'checkbox', |
| 249 |
'title' => __( 'Require Billing Address in Checkout', 'charitable' ), |
| 250 |
'priority' => 17, |
| 251 |
'help' => __( 'If enabled, donors will always be asked to enter their billing address in Checkout.', 'charitable' ), |
| 252 |
'attrs' => [ |
| 253 |
'data-trigger-key' => '#charitable_settings_gateways_stripe_enable_stripe_checkout', |
| 254 |
'data-trigger-value' => 'checked', |
| 255 |
], |
| 256 |
], |
| 257 |
'section_statement_descriptor' => [ |
| 258 |
'type' => 'heading', |
| 259 |
'title' => __( 'Statement Descriptor', 'charitable' ), |
| 260 |
'priority' => 20, |
| 261 |
], |
| 262 |
'statement_descriptor' => [ |
| 263 |
'type' => 'select', |
| 264 |
'title' => __( 'Format', 'charitable' ), |
| 265 |
'priority' => 21, |
| 266 |
'help' => __( 'Customize the statement descriptor that will appear on donors\' bank statements.', 'charitable' ), |
| 267 |
'default' => 'auto', |
| 268 |
'options' => [ |
| 269 |
'auto' => __( 'Use campaign title', 'charitable' ), |
| 270 |
'custom' => __( 'Set a custom descriptor', 'charitable' ), |
| 271 |
], |
| 272 |
], |
| 273 |
'statement_descriptor_custom' => [ |
| 274 |
'type' => 'text', |
| 275 |
'title' => __( 'Custom Statement Descriptor', 'charitable' ), |
| 276 |
'priority' => 22, |
| 277 |
'help' => __( 'A custom statement descriptor up to 22 characters long.', 'charitable' ), |
| 278 |
'attrs' => [ |
| 279 |
'data-trigger-key' => '#charitable_settings_gateways_stripe_statement_descriptor', |
| 280 |
'data-trigger-value' => 'custom', |
| 281 |
'maxlength' => 22, |
| 282 |
], |
| 283 |
], |
| 284 |
); |
| 285 |
} |
| 286 |
|
| 287 |
if ( charitable_using_stripe_connect() ) { |
| 288 |
|
| 289 |
unset( $stripe_settings['section_live_mode'] ); |
| 290 |
unset( $stripe_settings['live_secret_key'] ); |
| 291 |
unset( $stripe_settings['live_public_key'] ); |
| 292 |
unset( $stripe_settings['section_test_mode'] ); |
| 293 |
unset( $stripe_settings['test_secret_key'] ); |
| 294 |
unset( $stripe_settings['test_public_key'] ); |
| 295 |
|
| 296 |
} |
| 297 |
|
| 298 |
if ( ( charitable_is_debug() ) || charitable_is_admin_debug() ) { |
| 299 |
$stripe_settings = $stripe_settings + array( |
| 300 |
'section_stripe_checkout' => [ |
| 301 |
'title' => __( 'Stripe Checkout INTERNAL', 'charitable' ), |
| 302 |
'type' => 'heading', |
| 303 |
'priority' => 4, |
| 304 |
], |
| 305 |
'section_live_mode' => [ |
| 306 |
'title' => __( 'Live Settings', 'charitable' ), |
| 307 |
'type' => 'heading', |
| 308 |
'priority' => 5, |
| 309 |
], |
| 310 |
'live_secret_key' => [ |
| 311 |
'type' => 'text', |
| 312 |
'title' => __( 'Live Secret Key', 'charitable' ), |
| 313 |
'priority' => 6, |
| 314 |
'class' => 'wide', |
| 315 |
], |
| 316 |
'live_public_key' => [ |
| 317 |
'type' => 'text', |
| 318 |
'title' => __( 'Live Publishable Key', 'charitable' ), |
| 319 |
'priority' => 7, |
| 320 |
'class' => 'wide', |
| 321 |
], |
| 322 |
'section_test_mode' => [ |
| 323 |
'title' => __( 'Test Settings', 'charitable' ), |
| 324 |
'type' => 'heading', |
| 325 |
'priority' => 10, |
| 326 |
], |
| 327 |
'test_secret_key' => [ |
| 328 |
'type' => 'text', |
| 329 |
'title' => __( 'Test Secret Key', 'charitable' ), |
| 330 |
'priority' => 11, |
| 331 |
'class' => 'wide', |
| 332 |
], |
| 333 |
'test_public_key' => [ |
| 334 |
'type' => 'text', |
| 335 |
'title' => __( 'Test Publishable Key', 'charitable' ), |
| 336 |
'priority' => 12, |
| 337 |
'class' => 'wide', |
| 338 |
], |
| 339 |
); |
| 340 |
} |
| 341 |
|
| 342 |
return array_merge( $settings, $stripe_settings ); |
| 343 |
|
| 344 |
} |
| 345 |
} |
| 346 |
|
| 347 |
/** |
| 348 |
* Get the Stripe logo. |
| 349 |
* |
| 350 |
* @since 1.8.7 |
| 351 |
* |
| 352 |
* @return string |
| 353 |
*/ |
| 354 |
public function get_logo() { |
| 355 |
return '<svg width="49" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.4718 10.3338c0-3.41791-1.6696-6.11484-4.8607-6.11484-3.2045 0-5.1434 2.69693-5.1434 6.08814 0 4.0187 2.289 6.048 5.5743 6.048 1.6023 0 2.8141-.3604 3.7296-.8678v-2.6702c-.9155.4539-1.9658.7343-3.2987.7343-1.3061 0-2.464-.4539-2.6121-2.0294h6.5841c0-.1735.0269-.8678.0269-1.1882Zm-6.6514-1.26838c0-1.50868.929-2.13618 1.7773-2.13618.8213 0 1.6965.6275 1.6965 2.13618h-3.4738Zm-8.5499-4.84646c-1.3195 0-2.1678.61415-2.639 1.04139l-.1751-.82777h-2.9621V20l3.3661-.7076.0134-3.7784c.4847.3471 1.1984.8411 2.3832.8411 2.4102 0 4.6048-1.9225 4.6048-6.1548-.0134-3.87186-2.235-5.98134-4.5913-5.98134Zm-.8079 9.19894c-.7944 0-1.2656-.2804-1.5888-.6275l-.0134-4.95328c.35-.38719.8348-.65421 1.6022-.65421 1.2253 0 2.0735 1.36182 2.0735 3.11079 0 1.7891-.8347 3.1242-2.0735 3.1242Zm-9.6001-9.98666 3.3796-.72096V0l-3.3796.70761v2.72363Zm0 1.01469h3.3796V16.1282h-3.3796V4.44593Zm-3.6219.98798-.2154-.98798h-2.9083V16.1282h3.3661V8.21095c.7944-1.02804 2.1408-.84112 2.5582-.69426V4.44593c-.4309-.16022-2.0062-.45394-2.8006.98798Zm-6.7322-3.88518-3.2853.69426-.01346 10.69421c0 1.976 1.49456 3.4313 3.48726 3.4313 1.1041 0 1.912-.2003 2.3563-.4406v-2.7103c-.4309.1736-2.5583.7877-2.5583-1.1882V7.28972h2.5583V4.44593h-2.5583l.0135-2.8972ZM3.40649 7.83712c0-.5207.43086-.72096 1.14447-.72096 1.0233 0 2.31588.30707 3.33917.85447V4.83311c-1.11755-.44059-2.22162-.61415-3.33917-.61415C1.81769 4.21896 0 5.63418 0 7.99733c0 3.68487 5.11647 3.09747 5.11647 4.68627 0 .6141-.53858.8144-1.29258.8144-1.11755 0-2.54477-.4539-3.675782-1.0681v3.1776c1.252192.534 2.517842.761 3.675782.761 2.80059 0 4.72599-1.3752 4.72599-3.765-.01346-3.97867-5.14339-3.27106-5.14339-4.76638Z" fill="#635BFF"/></svg>'; |
| 356 |
} |
| 357 |
|
| 358 |
/** |
| 359 |
* Determine if Stripe is connected. |
| 360 |
* |
| 361 |
* @since 1.8.1.12 |
| 362 |
* |
| 363 |
* @return int |
| 364 |
*/ |
| 365 |
public function maybe_stripe_connected() { |
| 366 |
return $this->check_keys_exist( 'live' ) || $this->check_keys_exist( 'test' ); |
| 367 |
} |
| 368 |
|
| 369 |
/** |
| 370 |
* Get connection status content. |
| 371 |
* |
| 372 |
* @since 1.8.1.12 |
| 373 |
* |
| 374 |
* @param bool $force_test_mode Whether to force test mode. |
| 375 |
* |
| 376 |
* @return bool |
| 377 |
*/ |
| 378 |
public function get_connection_status_content( $force_test_mode = false ) { |
| 379 |
|
| 380 |
if ( charitable_get_option( 'test_mode' ) || $force_test_mode ) { |
| 381 |
$mode = 'test'; |
| 382 |
} else { |
| 383 |
$mode = 'live'; |
| 384 |
} |
| 385 |
|
| 386 |
$html = ''; |
| 387 |
|
| 388 |
if ( ! $this->check_keys_exist( $mode ) || ! charitable_using_stripe_connect() ) { |
| 389 |
$html .= $this->get_stripe_connect_button(); |
| 390 |
} else { |
| 391 |
|
| 392 |
if ( ! charitable_is_pro() ) { |
| 393 |
$html .= '<div class="charitable-inline-notice info"> |
| 394 |
<p> |
| 395 |
<strong>' . esc_html__( 'Pay as you go pricing:', 'charitable' ) . '</strong> ' . |
| 396 |
sprintf( |
| 397 |
/* translators: %1$s: opening link tag, %2$s: closing link tag */ |
| 398 |
esc_html__( '3%% per transaction + Stripe fees. %1$sUpgrade to Pro%2$s for no added fees and priority support.', 'charitable' ), |
| 399 |
'<a target="_blank" href="' . esc_url( charitable_pro_upgrade_url( 'gateway-stripe' ) ) . '">', |
| 400 |
'</a>' |
| 401 |
) . '</p> |
| 402 |
</div>'; |
| 403 |
} |
| 404 |
|
| 405 |
$html .= '<p id="wpcharitable-stripe-auth-error-account-actions" style="display: block;">' . sprintf( |
| 406 |
/* translators: %1$s Stripe payment mode. %2$s Opening anchor tag for reconnecting to Stripe, do not translate. %3$s Opening anchor tag for disconnecting Stripe, do not translate. %4$s Closing anchor tag, do not translate. */ |
| 407 |
__( '%1$sDisconnect this account%2$s.', 'charitable' ), |
| 408 |
'<strong>Connected in ' . $mode . ' mode</strong>. <a href="' . esc_url( $this->get_stripe_disconnect_url() ) . '" class="wpcharitable-disconnect-link">', |
| 409 |
'</a>' |
| 410 |
) . '</p>'; |
| 411 |
|
| 412 |
$html .= '<p id="wpcharitable-stripe-activated-account-actions" style="display: none;">' . sprintf( |
| 413 |
/* translators: %1$s Stripe payment mode. %2$s Opening anchor tag for reconnecting to Stripe, do not translate. %3$s Opening anchor tag for disconnecting Stripe, do not translate. %4$s Closing anchor tag, do not translate. */ |
| 414 |
__( 'Your Stripe account is connected in %1$s mode. %2$sDisconnect this account%3$s.', 'charitable' ), |
| 415 |
'<strong>' . $mode . '</strong>', |
| 416 |
'<a href="' . esc_url( $this->get_stripe_disconnect_url() ) . '" class="wpcharitable-disconnect-link">', |
| 417 |
'</a>' |
| 418 |
) . '</p>'; |
| 419 |
|
| 420 |
$html .= '<p id="wpcharitable-stripe-unactivated-account-actions" style="display: none;"><a href="' . esc_url( $this->get_stripe_disconnect_url() ) . '">' . |
| 421 |
__( 'Disconnect temporary account', 'charitable' ) . |
| 422 |
'</a></p>'; |
| 423 |
} |
| 424 |
|
| 425 |
return $html; |
| 426 |
} |
| 427 |
|
| 428 |
/** |
| 429 |
* Generate the Stripe Connect button. |
| 430 |
* |
| 431 |
* @since 1.8.2 |
| 432 |
* |
| 433 |
* @param string $redirect_url The URL to redirect to after connecting. |
| 434 |
* @return string |
| 435 |
*/ |
| 436 |
public function get_stripe_connect_button( $redirect_url = '' ) { |
| 437 |
|
| 438 |
$url = $this->get_stripe_connect_url( $redirect_url ); |
| 439 |
|
| 440 |
ob_start(); |
| 441 |
?> |
| 442 |
|
| 443 |
<a href="<?php echo esc_url( $url ); ?>" aria-label="<?php echo esc_attr__( 'Connect with Stripe', 'charitable' ); ?>" class="wpcharitable-stripe-connect"> |
| 444 |
<span> |
| 445 |
<?php |
| 446 |
/* translators: Text before Stripe logo for "Connect with Stripe" button. */ |
| 447 |
esc_html_e( 'Connect with', 'charitable' ); |
| 448 |
?> |
| 449 |
</span> |
| 450 |
|
| 451 |
<svg width="49" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.4718 10.3338c0-3.41791-1.6696-6.11484-4.8607-6.11484-3.2045 0-5.1434 2.69693-5.1434 6.08814 0 4.0187 2.289 6.048 5.5743 6.048 1.6023 0 2.8141-.3604 3.7296-.8678v-2.6702c-.9155.4539-1.9658.7343-3.2987.7343-1.3061 0-2.464-.4539-2.6121-2.0294h6.5841c0-.1735.0269-.8678.0269-1.1882Zm-6.6514-1.26838c0-1.50868.929-2.13618 1.7773-2.13618.8213 0 1.6965.6275 1.6965 2.13618h-3.4738Zm-8.5499-4.84646c-1.3195 0-2.1678.61415-2.639 1.04139l-.1751-.82777h-2.9621V20l3.3661-.7076.0134-3.7784c.4847.3471 1.1984.8411 2.3832.8411 2.4102 0 4.6048-1.9225 4.6048-6.1548-.0134-3.87186-2.235-5.98134-4.5913-5.98134Zm-.8079 9.19894c-.7944 0-1.2656-.2804-1.5888-.6275l-.0134-4.95328c.35-.38719.8348-.65421 1.6022-.65421 1.2253 0 2.0735 1.36182 2.0735 3.11079 0 1.7891-.8347 3.1242-2.0735 3.1242Zm-9.6001-9.98666 3.3796-.72096V0l-3.3796.70761v2.72363Zm0 1.01469h3.3796V16.1282h-3.3796V4.44593Zm-3.6219.98798-.2154-.98798h-2.9083V16.1282h3.3661V8.21095c.7944-1.02804 2.1408-.84112 2.5582-.69426V4.44593c-.4309-.16022-2.0062-.45394-2.8006.98798Zm-6.7322-3.88518-3.2853.69426-.01346 10.69421c0 1.976 1.49456 3.4313 3.48726 3.4313 1.1041 0 1.912-.2003 2.3563-.4406v-2.7103c-.4309.1736-2.5583.7877-2.5583-1.1882V7.28972h2.5583V4.44593h-2.5583l.0135-2.8972ZM3.40649 7.83712c0-.5207.43086-.72096 1.14447-.72096 1.0233 0 2.31588.30707 3.33917.85447V4.83311c-1.11755-.44059-2.22162-.61415-3.33917-.61415C1.81769 4.21896 0 5.63418 0 7.99733c0 3.68487 5.11647 3.09747 5.11647 4.68627 0 .6141-.53858.8144-1.29258.8144-1.11755 0-2.54477-.4539-3.675782-1.0681v3.1776c1.252192.534 2.517842.761 3.675782.761 2.80059 0 4.72599-1.3752 4.72599-3.765-.01346-3.97867-5.14339-3.27106-5.14339-4.76638Z" fill="#fff"/></svg> |
| 452 |
</a> |
| 453 |
|
| 454 |
<style> |
| 455 |
.wpcharitable-stripe-connect { |
| 456 |
color: #fff; |
| 457 |
font-size: 15px; |
| 458 |
font-weight: bold; |
| 459 |
text-decoration: none; |
| 460 |
line-height: 1; |
| 461 |
background-color: #635bff; |
| 462 |
border-radius: 3px; |
| 463 |
padding: 10px 20px; |
| 464 |
display: inline-flex; |
| 465 |
align-items: center; |
| 466 |
} |
| 467 |
|
| 468 |
.wpcharitable-stripe-connect:focus, |
| 469 |
.wpcharitable-stripe-connect:hover { |
| 470 |
color: #fff; |
| 471 |
background-color: #0a2540; |
| 472 |
} |
| 473 |
|
| 474 |
.wpcharitable-stripe-connect:focus { |
| 475 |
outline: 0; |
| 476 |
box-shadow: inset 0 0 0 1px #fff, 0 0 0 1.5px #0a2540; |
| 477 |
} |
| 478 |
|
| 479 |
.wpcharitable-stripe-connect svg { |
| 480 |
margin-left: 5px; |
| 481 |
} |
| 482 |
</style> |
| 483 |
|
| 484 |
<?php |
| 485 |
return ob_get_clean(); |
| 486 |
} |
| 487 |
|
| 488 |
/** |
| 489 |
* Get the URL to connect to Stripe. |
| 490 |
* |
| 491 |
* @since 1.8.0 |
| 492 |
* |
| 493 |
* @param string $redirect_url The URL to redirect to after connecting. |
| 494 |
* @return string |
| 495 |
*/ |
| 496 |
public function get_stripe_connect_url( $redirect_url = '' ) { |
| 497 |
if ( empty( $redirect_url ) ) { |
| 498 |
|
| 499 |
$redirect_url = add_query_arg( |
| 500 |
array( |
| 501 |
'tab' => 'gateways', |
| 502 |
'page' => 'charitable-settings', |
| 503 |
'group' => 'gateways_stripe', |
| 504 |
), |
| 505 |
admin_url( 'admin.php' ) |
| 506 |
); |
| 507 |
|
| 508 |
} |
| 509 |
|
| 510 |
return add_query_arg( |
| 511 |
array( |
| 512 |
'live_mode' => (int) ! charitable_get_option( 'test_mode' ), // (int) ! $this->is_test_mode(), |
| 513 |
'state' => str_pad( wp_rand( wp_rand(), PHP_INT_MAX ), 100, wp_rand(), STR_PAD_BOTH ), |
| 514 |
'customer_site_url' => urlencode( $redirect_url ), |
| 515 |
), |
| 516 |
'https://wpcharitable.com/stripe-connect/?wpcharitable_gateway_connect_init=stripe_connect' // todo: filter market site url. |
| 517 |
); |
| 518 |
} |
| 519 |
|
| 520 |
/** |
| 521 |
* Get the URL to disconnect from Stripe. |
| 522 |
* |
| 523 |
* @since 1.8.0 |
| 524 |
* |
| 525 |
* @return string |
| 526 |
*/ |
| 527 |
public function get_stripe_disconnect_url() { |
| 528 |
return add_query_arg( |
| 529 |
array( |
| 530 |
'wpcharitable-stripe-disconnect' => true, |
| 531 |
'_wpnonce' => wp_create_nonce( |
| 532 |
'wpcharitable-stripe-connect-disconnect' |
| 533 |
), |
| 534 |
'tab' => 'gateways', |
| 535 |
'page' => 'charitable-settings', |
| 536 |
'group' => 'gateways_stripe', |
| 537 |
), |
| 538 |
admin_url( 'admin.php' ) |
| 539 |
); |
| 540 |
} |
| 541 |
|
| 542 |
/** |
| 543 |
* Returns the current gateway's ID. |
| 544 |
* |
| 545 |
* @return string |
| 546 |
* @since 1.0.3 |
| 547 |
*/ |
| 548 |
public static function get_gateway_id() { |
| 549 |
return self::ID; |
| 550 |
} |
| 551 |
|
| 552 |
/** |
| 553 |
* Return the keys to use. |
| 554 |
* |
| 555 |
* This will return the test keys if test mode is enabled. Otherwise, returns |
| 556 |
* the production keys. |
| 557 |
* |
| 558 |
* @since 1.7.0 |
| 559 |
* |
| 560 |
* @param boolean $force_test_mode Forces the test API keys to be used. |
| 561 |
* @return string[] |
| 562 |
*/ |
| 563 |
public function get_keys( $force_test_mode = false ) { |
| 564 |
$keys = []; |
| 565 |
|
| 566 |
if ( charitable_get_option( 'test_mode' ) || $force_test_mode ) { |
| 567 |
$keys['secret_key'] = trim( $this->get_value( 'test_secret_key' ) ); |
| 568 |
$keys['public_key'] = trim( $this->get_value( 'test_public_key' ) ); |
| 569 |
} else { |
| 570 |
$keys['secret_key'] = trim( $this->get_value( 'live_secret_key' ) ); |
| 571 |
$keys['public_key'] = trim( $this->get_value( 'live_public_key' ) ); |
| 572 |
} |
| 573 |
|
| 574 |
return $keys; |
| 575 |
} |
| 576 |
|
| 577 |
/** |
| 578 |
* Check if Stripe keys exist. |
| 579 |
* |
| 580 |
* @since 1.7.0 |
| 581 |
* @param string $mode The mode to check for keys. |
| 582 |
* @return boolean |
| 583 |
*/ |
| 584 |
public function check_keys_exist( $mode ) { |
| 585 |
|
| 586 |
$secret_key = trim( $this->get_value( $mode . '_secret_key' ) ); |
| 587 |
$publishable_key = trim( $this->get_value( $mode . '_public_key' ) ); |
| 588 |
|
| 589 |
if ( ! empty( $secret_key ) && ! empty( $publishable_key ) ) { |
| 590 |
return true; |
| 591 |
} |
| 592 |
|
| 593 |
return false; |
| 594 |
} |
| 595 |
|
| 596 |
/** |
| 597 |
* Return the submitted value for a gateway field. |
| 598 |
* |
| 599 |
* @since 1.7.0 |
| 600 |
* |
| 601 |
* @param string $key The key of the value we want to get. |
| 602 |
* @param mixed[] $values An values in which to search. |
| 603 |
* @return string|false |
| 604 |
*/ |
| 605 |
public function get_gateway_value( $key, $values ) { |
| 606 |
if ( isset( $values['gateways']['stripe'][ $key ] ) ) { |
| 607 |
return $values['gateways']['stripe'][ $key ]; |
| 608 |
} |
| 609 |
|
| 610 |
return false; |
| 611 |
} |
| 612 |
|
| 613 |
/** |
| 614 |
* Return the submitted value for a gateway field. |
| 615 |
* |
| 616 |
* @since 1.7.0 |
| 617 |
* |
| 618 |
* @param string $key The key of the value we want to get. |
| 619 |
* @param Charitable_Donation_Processor $processor The Donation Processor helper object. |
| 620 |
* @return string|false |
| 621 |
*/ |
| 622 |
public function get_gateway_value_from_processor( $key, Charitable_Donation_Processor $processor ) { |
| 623 |
return $this->get_gateway_value( $key, $processor->get_donation_data() ); |
| 624 |
} |
| 625 |
|
| 626 |
/** |
| 627 |
* Add the connected account & charge owner setting to the donation form's hidden fields. |
| 628 |
* |
| 629 |
* @since 1.4.0 |
| 630 |
* |
| 631 |
* @param string[] $fields The hidden fields as key=>value pairs. |
| 632 |
* @param Charitable_Donation_Form $form The donation form instance. |
| 633 |
* @return string[] |
| 634 |
*/ |
| 635 |
public static function add_hidden_stripe_account_field( $fields, $form ) { |
| 636 |
if ( ! class_exists( 'Charitable_Stripe_Connect' ) ) { |
| 637 |
return $fields; |
| 638 |
} |
| 639 |
|
| 640 |
$connected_account = charitable_stripe_get_connected_account_for_campaign( $form->get_campaign()->ID ); |
| 641 |
|
| 642 |
if ( $connected_account ) { |
| 643 |
$fields['connected_account'] = $connected_account; |
| 644 |
$fields['connect_charge_owner'] = charitable_get_option( [ 'gateways_stripe', 'charge_owner' ], 'direct' ); |
| 645 |
} |
| 646 |
|
| 647 |
return $fields; |
| 648 |
} |
| 649 |
|
| 650 |
/** |
| 651 |
* Returns an array of credit card fields. |
| 652 |
* |
| 653 |
* If the gateway requires different fields, this can simply be redefined |
| 654 |
* in the child class. |
| 655 |
* |
| 656 |
* @since 1.7.0 |
| 657 |
* @version 1.8.9.1 |
| 658 |
* |
| 659 |
* @return array[] |
| 660 |
*/ |
| 661 |
public function get_credit_card_fields() { |
| 662 |
/* If Stripe Checkout is enabled, remove the credit card fields. */ |
| 663 |
if ( $this->get_value( 'enable_stripe_checkout' ) ) { |
| 664 |
return []; |
| 665 |
} |
| 666 |
|
| 667 |
$fields = parent::get_credit_card_fields(); |
| 668 |
|
| 669 |
$cc_fields_format = empty( $this->get_value( 'cc_fields_format' ) ) ? 'standard' : $this->get_value( 'cc_fields_format' ); |
| 670 |
|
| 671 |
if ( 'multiple' === $cc_fields_format ) { |
| 672 |
/* Remove all fields except for the cc_name field. */ |
| 673 |
unset( |
| 674 |
$fields['cc_number'], |
| 675 |
$fields['cc_cvc'], |
| 676 |
$fields['cc_expiration'] |
| 677 |
); |
| 678 |
|
| 679 |
/* Check for an existing payment intent. */ |
| 680 |
$intent = Charitable_Stripe_Payment_Intent::init_from_session(); |
| 681 |
$intent = false; |
| 682 |
|
| 683 |
ob_start(); |
| 684 |
|
| 685 |
/** |
| 686 |
* Render a template with our custom CSS. NOTE: We pass a non-empty array |
| 687 |
* as the second parameter to ensure that the template is rendered immediately. |
| 688 |
* |
| 689 |
* @see charitable_template |
| 690 |
*/ |
| 691 |
charitable_template( |
| 692 |
'stripe-elements.css.php', |
| 693 |
[ 1 ], |
| 694 |
'Charitable_Stripe_Template' |
| 695 |
); |
| 696 |
?> |
| 697 |
|
| 698 |
<div class="charitable_stripe_multiple_field"> |
| 699 |
<label for="charitable_stripe_card_expiration_field"><?php esc_html_e( 'Expiration Date', 'charitable' ); ?></label> |
| 700 |
<div id="charitable_stripe_card_expiration_field"> |
| 701 |
<!-- A Stripe Element will be inserted here --> |
| 702 |
</div> |
| 703 |
</div> |
| 704 |
<div class="charitable_stripe_multiple_field"> |
| 705 |
<label for="charitable_stripe_card_number_field"><?php esc_html_e( 'Credit/Debit Number', 'charitable' ); ?></label> |
| 706 |
<div id="charitable_stripe_card_number_field"> |
| 707 |
<!-- A Stripe Element will be inserted here --> |
| 708 |
</div> |
| 709 |
</div> |
| 710 |
<div class="charitable_stripe_multiple_field"> |
| 711 |
<label for="charitable_stripe_card_cvc_field"><?php esc_html_e( 'CVC', 'charitable' ); ?></label> |
| 712 |
<div id="charitable_stripe_card_cvc_field"> |
| 713 |
<!-- A Stripe Element will be inserted here --> |
| 714 |
</div> |
| 715 |
</div> |
| 716 |
|
| 717 |
|
| 718 |
<div id="charitable_stripe_card_errors" role="alert"></div> |
| 719 |
<input type="hidden" name="stripe_payment_method" /> |
| 720 |
|
| 721 |
<?php |
| 722 |
|
| 723 |
$fields['cc_element'] = [ |
| 724 |
'type' => 'content', |
| 725 |
'content' => ob_get_clean(), |
| 726 |
'priority' => 2, |
| 727 |
]; |
| 728 |
|
| 729 |
} else { |
| 730 |
/* Remove all fields except for the cc_name field. */ |
| 731 |
unset( |
| 732 |
$fields['cc_number'], |
| 733 |
$fields['cc_cvc'], |
| 734 |
$fields['cc_expiration'] |
| 735 |
); |
| 736 |
|
| 737 |
/* Check for an existing payment intent. */ |
| 738 |
$intent = Charitable_Stripe_Payment_Intent::init_from_session(); |
| 739 |
$intent = false; |
| 740 |
|
| 741 |
ob_start(); |
| 742 |
|
| 743 |
/** |
| 744 |
* Render a template with our custom CSS. NOTE: We pass a non-empty array |
| 745 |
* as the second parameter to ensure that the template is rendered immediately. |
| 746 |
* |
| 747 |
* @see charitable_template |
| 748 |
*/ |
| 749 |
charitable_template( |
| 750 |
'stripe-elements.css.php', |
| 751 |
[ 1 ], |
| 752 |
'Charitable_Stripe_Template' |
| 753 |
); |
| 754 |
?> |
| 755 |
<label for="charitable_stripe_card_field"><?php esc_html_e( 'Credit/Debit Card', 'charitable' ); ?></label> |
| 756 |
<div id="charitable_stripe_card_field" data-secret="<?php echo esc_attr( $intent ? $intent->get( 'client_secret' ) : '' ); ?>" data-intent="<?php echo esc_attr( $intent ? $intent->get( 'id' ) : '' ); ?>"></div> |
| 757 |
<div id="charitable_stripe_card_errors" role="alert"></div> |
| 758 |
<input type="hidden" name="stripe_payment_method" /> |
| 759 |
<?php |
| 760 |
|
| 761 |
$fields['cc_element'] = [ |
| 762 |
'type' => 'content', |
| 763 |
'content' => ob_get_clean(), |
| 764 |
'priority' => 2, |
| 765 |
]; |
| 766 |
|
| 767 |
$fields['cc_name']['attrs'] = [ |
| 768 |
'data-input' => 'cc_name', |
| 769 |
]; |
| 770 |
} |
| 771 |
|
| 772 |
return $fields; |
| 773 |
} |
| 774 |
|
| 775 |
/** |
| 776 |
* Set up an output buffer before the donation form fields are displayed. |
| 777 |
* |
| 778 |
* @since 1.3.0 |
| 779 |
* @version 1.8.9.1 |
| 780 |
* |
| 781 |
* @param Charitable_Form $form Form object. |
| 782 |
* @return void |
| 783 |
*/ |
| 784 |
public static function setup_donation_form_field_output_buffer( Charitable_Form $form ) { |
| 785 |
if ( 'Charitable_Donation_Form' != get_class( $form ) ) { |
| 786 |
return; |
| 787 |
} |
| 788 |
|
| 789 |
ob_start(); |
| 790 |
|
| 791 |
echo '<noscript><div class="charitable-notice charitable-form-errors"> |
| 792 |
<ul class="charitable-notice-errors errors"> |
| 793 |
<li>' . esc_html__( 'For security reasons, credit card donations require Javascript. Please enable Javascript in your browser before continuing.', 'charitable' ) . '</li> |
| 794 |
</ul><!-- charitable-notice- --> |
| 795 |
</div></noscript>'; |
| 796 |
} |
| 797 |
|
| 798 |
/** |
| 799 |
* Get the donation form fields from the buffer and remove the name attribute |
| 800 |
* from cc fields. |
| 801 |
* |
| 802 |
* @since 1.3.0 |
| 803 |
* @version 1.8.9.1 |
| 804 |
* |
| 805 |
* @param Charitable_Form $form Form object. |
| 806 |
* @return void |
| 807 |
*/ |
| 808 |
public static function remove_name_attribute_from_cc_fields( Charitable_Form $form ) { |
| 809 |
if ( 'Charitable_Donation_Form' != get_class( $form ) ) { |
| 810 |
return; |
| 811 |
} |
| 812 |
|
| 813 |
$fields = ob_get_clean(); |
| 814 |
$fields = str_replace( 'name="cc_name"', '', $fields ); |
| 815 |
|
| 816 |
echo $fields; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 817 |
} |
| 818 |
|
| 819 |
/** |
| 820 |
* Validate the submitted credit card details. |
| 821 |
* |
| 822 |
* @since 1.7.0 |
| 823 |
* @since 1.4.0 Deprecated. |
| 824 |
* @since 1.4.7 Restored. No longer deprecated. |
| 825 |
* |
| 826 |
* @param boolean $valid Whether the donation is valid. |
| 827 |
* @param string $gateway The chosen gateway. |
| 828 |
* @param mixed[] $values The filtered values from the donation form submission. |
| 829 |
* @return boolean |
| 830 |
*/ |
| 831 |
public static function validate_donation( $valid, $gateway, $values ) { |
| 832 |
if ( 'stripe' !== $gateway ) { |
| 833 |
return $valid; |
| 834 |
} |
| 835 |
|
| 836 |
$gateway = new Charitable_Gateway_Stripe_AM(); |
| 837 |
$keys = $gateway->get_keys(); |
| 838 |
|
| 839 |
/* Make sure that the keys are set. */ |
| 840 |
if ( empty( $keys['secret_key'] ) || empty( $keys['public_key'] ) ) { |
| 841 |
charitable_get_notices()->add_error( __( 'Missing keys for Stripe payment gateway. Unable to proceed with payment.', 'charitable' ) ); |
| 842 |
return false; |
| 843 |
} |
| 844 |
|
| 845 |
/* If we're using Payment Intents, make sure we have a payment method. */ |
| 846 |
if ( ! $gateway->get_value( 'enable_stripe_checkout' ) ) { |
| 847 |
// phpcs:disable WordPress.Security.NonceVerification.Missing |
| 848 |
$stripe_payment_method = isset( $_POST['stripe_payment_method'] ) ? sanitize_text_field( wp_unslash( $_POST['stripe_payment_method'] ) ) : ''; |
| 849 |
if ( empty( $stripe_payment_method ) ) { |
| 850 |
// phpcs:enable WordPress.Security.NonceVerification.Missing |
| 851 |
charitable_get_notices()->add_error( |
| 852 |
__( '<strong>Missing payment details.</strong> <a href="#charitable-gateway-fields">Click here to double-check that all required payment fields are completed.</a>', 'charitable' ) |
| 853 |
); |
| 854 |
return false; |
| 855 |
} |
| 856 |
// phpcs:enable WordPress.Security.NonceVerification.Missing |
| 857 |
} |
| 858 |
|
| 859 |
return $valid; |
| 860 |
} |
| 861 |
|
| 862 |
/** |
| 863 |
* Checks whether the donation being processed is recurring. |
| 864 |
* |
| 865 |
* @since 1.3.0 |
| 866 |
* |
| 867 |
* @param Charitable_Donation_Processor $processor The Donation Processor helper. |
| 868 |
* @return boolean |
| 869 |
*/ |
| 870 |
public static function is_recurring_donation( Charitable_Donation_Processor $processor ) { |
| 871 |
return $processor->get_donation_data_value( 'donation_plan', false ); |
| 872 |
} |
| 873 |
|
| 874 |
/** |
| 875 |
* Process the donation with the gateway, seamlessly over the Stripe API. |
| 876 |
* |
| 877 |
* @since 1.7.0 |
| 878 |
* |
| 879 |
* @param mixed $return The result of the gateway processing. |
| 880 |
* @param int $donation_id The donation ID. |
| 881 |
* @param Charitable_Donation_Processor $processor The Donation Processor helper. |
| 882 |
* @return boolean |
| 883 |
*/ |
| 884 |
public static function process_donation( $return, $donation_id, Charitable_Donation_Processor $processor ) { |
| 885 |
if ( charitable_get_option( [ 'gateways_stripe', 'enable_stripe_checkout' ] ) && charitable()->is_stripe_connect_addon() ) { |
| 886 |
/** |
| 887 |
* Filter the processor used for handling Checkout donations. |
| 888 |
* |
| 889 |
* @since 1.4.0 |
| 890 |
* |
| 891 |
* @param string $class The name of the Stripe gateway processor class. |
| 892 |
* @param Charitable_Donation_Processor $processor The Donation Processor helper. |
| 893 |
*/ |
| 894 |
$processor_class = apply_filters( 'charitable_stripe_gateway_processor_checkout', 'Charitable_Stripe_Gateway_Processor_Checkout', $processor ); |
| 895 |
} else { |
| 896 |
/** |
| 897 |
* Filter the processor used for handling PaymentIntent donations. |
| 898 |
* |
| 899 |
* @since 1.4.0 |
| 900 |
* |
| 901 |
* @param string $class The name of the Stripe gateway processor class. |
| 902 |
* @param Charitable_Donation_Processor $processor The Donation Processor helper. |
| 903 |
*/ |
| 904 |
$processor_class = apply_filters( 'charitable_stripe_gateway_processor_payment_intents', 'Charitable_Stripe_Gateway_Processor_Payment_Intents', $processor ); |
| 905 |
} |
| 906 |
|
| 907 |
$gateway_processor = new $processor_class( $donation_id, $processor ); |
| 908 |
|
| 909 |
/* Ensure we have a valid processor. */ |
| 910 |
if ( ! $gateway_processor instanceof Charitable_Stripe_Gateway_Processor ) { |
| 911 |
$gateway_processor = new Charitable_Stripe_Gateway_Processor_Payment_Intents( $donation_id, $processor ); |
| 912 |
} |
| 913 |
|
| 914 |
return $gateway_processor->run(); |
| 915 |
} |
| 916 |
|
| 917 |
/** |
| 918 |
* Sets up the API. |
| 919 |
* |
| 920 |
* This sets the API key, specifies an API version to use, and also |
| 921 |
* sets the App info. |
| 922 |
* |
| 923 |
* @since 1.3.0 |
| 924 |
* |
| 925 |
* @param string|null $api_key The API key. If null, will use the secret key. |
| 926 |
* @return boolean |
| 927 |
*/ |
| 928 |
public function setup_api( $api_key = null ) { |
| 929 |
if ( is_null( $api_key ) ) { |
| 930 |
$keys = $this->get_keys(); |
| 931 |
|
| 932 |
if ( ! array_key_exists( 'secret_key', $keys ) ) { |
| 933 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 934 |
error_log( '[Charitable Stripe] setup_api FAILED: No secret_key in keys array.' ); // phpcs:ignore |
| 935 |
} |
| 936 |
return false; |
| 937 |
} |
| 938 |
|
| 939 |
$api_key = $keys['secret_key']; |
| 940 |
} |
| 941 |
|
| 942 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 943 |
$key_preview = ! empty( $api_key ) ? substr( $api_key, 0, 7 ) . '...' . substr( $api_key, -4 ) : 'EMPTY'; |
| 944 |
$test_mode = charitable_get_option( 'test_mode' ) ? 'test' : 'live'; |
| 945 |
error_log( sprintf( '[Charitable Stripe] setup_api called. Mode: %s, Key: %s', $test_mode, $key_preview ) ); // phpcs:ignore |
| 946 |
} |
| 947 |
|
| 948 |
/** |
| 949 |
* On certain versions of CentOS, curl requests to Stripe fail consistently. |
| 950 |
* The following filter provides a way to work around this with the following: |
| 951 |
* |
| 952 |
add_filter( 'charitable_stripe_http_curl_client', function( \Stripe\HttpClient\CurlClient $curl ) { |
| 953 |
$curl->setEnablePersistentConnections( false ); |
| 954 |
return $curl; |
| 955 |
} ); |
| 956 |
* |
| 957 |
* @see https://github.com/stripe/stripe-php/issues/918 |
| 958 |
* |
| 959 |
* @since 1.4.11 |
| 960 |
* |
| 961 |
* @param \Stripe\HttpClient\CurlClient $curl The CurlClient instance. |
| 962 |
*/ |
| 963 |
$curl = apply_filters( 'charitable_stripe_http_curl_client', new \Stripe\HttpClient\CurlClient() ); |
| 964 |
|
| 965 |
/* Allow the Curl client object to be changed. */ |
| 966 |
\Stripe\ApiRequestor::setHttpClient( $curl ); |
| 967 |
|
| 968 |
\Stripe\Stripe::setApiKey( $api_key ); |
| 969 |
\Stripe\Stripe::setApiVersion( self::STRIPE_API_VERSION ); |
| 970 |
\Stripe\Stripe::setAppInfo( |
| 971 |
'WordPress CharitablePlugin', |
| 972 |
charitable_stripe()->get_version(), |
| 973 |
'https://www.wpcharitable.com', |
| 974 |
'pp_partner_Ee8qdpxEQjiEne' |
| 975 |
); |
| 976 |
|
| 977 |
return true; |
| 978 |
} |
| 979 |
|
| 980 |
/** |
| 981 |
* Check whether a particular donation can be refunded automatically in Stripe. |
| 982 |
* |
| 983 |
* @since 1.3.0 |
| 984 |
* |
| 985 |
* @param Charitable_Donation $donation The donation object. |
| 986 |
* @return boolean |
| 987 |
*/ |
| 988 |
public function is_donation_refundable( Charitable_Donation $donation ) { |
| 989 |
$secret_key = $donation->get_test_mode( false ) ? 'test_secret_key' : 'live_secret_key'; |
| 990 |
|
| 991 |
if ( ! $this->get_value( $secret_key ) ) { |
| 992 |
return false; |
| 993 |
} |
| 994 |
|
| 995 |
// todo: determine why this function might not be available when this is called on the donation detail page. |
| 996 |
if ( function_exists( 'charitable_stripe_get_charge_id_for_donation' ) ) { |
| 997 |
return false !== charitable_stripe_get_charge_id_for_donation( $donation->ID ); |
| 998 |
} else { |
| 999 |
return false; |
| 1000 |
} |
| 1001 |
} |
| 1002 |
|
| 1003 |
/** |
| 1004 |
* Process a refund initiated in the WordPress dashboard. |
| 1005 |
* |
| 1006 |
* @since 1.3.0 |
| 1007 |
* |
| 1008 |
* @param int $donation_id The donation ID. |
| 1009 |
* @return boolean |
| 1010 |
*/ |
| 1011 |
public static function refund_donation_from_dashboard( $donation_id ) { |
| 1012 |
$donation = charitable_get_donation( $donation_id ); |
| 1013 |
|
| 1014 |
if ( ! $donation ) { |
| 1015 |
return false; |
| 1016 |
} |
| 1017 |
|
| 1018 |
$charge = charitable_stripe_get_charge_id_for_donation( $donation_id ); |
| 1019 |
|
| 1020 |
if ( ! $charge ) { |
| 1021 |
return false; |
| 1022 |
} |
| 1023 |
|
| 1024 |
$gateway = new Charitable_Gateway_Stripe_AM(); |
| 1025 |
$key = $donation->get_test_mode( false ) ? 'test_secret_key' : 'live_secret_key'; |
| 1026 |
$api_key = $gateway->get_value( $key ); |
| 1027 |
$account = charitable_stripe_get_account_id_for_donation( $donation->ID ); |
| 1028 |
$options = $account ? [ 'stripe_account' => $account ] : null; |
| 1029 |
|
| 1030 |
if ( ! $api_key ) { |
| 1031 |
return false; |
| 1032 |
} |
| 1033 |
|
| 1034 |
$gateway->setup_api( $api_key ); |
| 1035 |
|
| 1036 |
$data = 0 === strpos( $charge, 'pi_' ) ? [ 'payment_intent' => $charge ] : [ 'charge' => $charge ]; |
| 1037 |
|
| 1038 |
try { |
| 1039 |
$refund = \Stripe\Refund::create( $data, $options ); |
| 1040 |
|
| 1041 |
update_post_meta( $donation_id, '_stripe_refunded', true ); |
| 1042 |
update_post_meta( $donation_id, '_stripe_refund_id', $refund->id ); |
| 1043 |
|
| 1044 |
$donation->log()->add( |
| 1045 |
sprintf( |
| 1046 |
/* translators: %s: transaction reference. */ |
| 1047 |
__( 'Stripe refund transaction ID: %s', 'charitable' ), |
| 1048 |
$refund->id |
| 1049 |
) |
| 1050 |
); |
| 1051 |
|
| 1052 |
return true; |
| 1053 |
} catch ( \Stripe\Error\InvalidRequest $e ) { |
| 1054 |
$donation->log()->add( |
| 1055 |
sprintf( |
| 1056 |
/* translators: %s: error message. */ |
| 1057 |
__( 'Stripe refund failed: %s', 'charitable' ), |
| 1058 |
$e->getStripeCode() |
| 1059 |
) |
| 1060 |
); |
| 1061 |
return false; |
| 1062 |
} |
| 1063 |
} |
| 1064 |
|
| 1065 |
/** |
| 1066 |
* Check whether a recurring donation can be cancelled automatically in Stripe. |
| 1067 |
* |
| 1068 |
* @since 1.3.0 |
| 1069 |
* |
| 1070 |
* @param boolean $can_cancel Whether the subscription can be cancelled. |
| 1071 |
* @param Charitable_Recurring_Donation $donation The donation object. |
| 1072 |
* @return boolean |
| 1073 |
*/ |
| 1074 |
public static function is_subscription_cancellable( $can_cancel, Charitable_Recurring_Donation $donation ) { |
| 1075 |
if ( ! $can_cancel ) { |
| 1076 |
return $can_cancel; |
| 1077 |
} |
| 1078 |
|
| 1079 |
$secret_key = $donation->get_test_mode( false ) ? 'test_secret_key' : 'live_secret_key'; |
| 1080 |
|
| 1081 |
if ( ! charitable_get_option( [ 'gateways_stripe', $secret_key ] ) ) { |
| 1082 |
return false; |
| 1083 |
} |
| 1084 |
|
| 1085 |
return ! empty( $donation->get_gateway_subscription_id() ); |
| 1086 |
} |
| 1087 |
|
| 1088 |
/** |
| 1089 |
* Cancel a subscription. |
| 1090 |
* |
| 1091 |
* This can be triggered via the WordPress dashboard when editing a recurring |
| 1092 |
* donation, or via the user's own account area. |
| 1093 |
* |
| 1094 |
* @since 1.3.0 |
| 1095 |
* |
| 1096 |
* @param boolean $cancelled Whether the subscription was cancelled successfully in the gateway. |
| 1097 |
* @param Charitable_Recurring_Donation $donation The recurring donation object. |
| 1098 |
* @return boolean |
| 1099 |
*/ |
| 1100 |
public static function cancel_subscription( $cancelled, Charitable_Recurring_Donation $donation ) { |
| 1101 |
// Enhanced logging for cancellation debugging |
| 1102 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1103 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1104 |
error_log( sprintf( |
| 1105 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: Starting cancellation for donation #%d', |
| 1106 |
$donation->get_donation_id() |
| 1107 |
) ); |
| 1108 |
} |
| 1109 |
} |
| 1110 |
|
| 1111 |
$subscription_id = $donation->get_gateway_subscription_id(); |
| 1112 |
|
| 1113 |
if ( ! $subscription_id ) { |
| 1114 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1115 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1116 |
error_log( 'CHARITABLE_STRIPE_CANCEL_DEBUG: No subscription ID found - cancellation aborted' ); |
| 1117 |
} |
| 1118 |
} |
| 1119 |
$donation->log()->add( __( 'Cancellation failed: No gateway subscription ID found.', 'charitable' ) ); |
| 1120 |
return false; |
| 1121 |
} |
| 1122 |
|
| 1123 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1124 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1125 |
error_log( sprintf( |
| 1126 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: Attempting to cancel subscription %s', |
| 1127 |
$subscription_id |
| 1128 |
) ); |
| 1129 |
} |
| 1130 |
} |
| 1131 |
|
| 1132 |
$gateway = new Charitable_Gateway_Stripe_AM(); |
| 1133 |
$key = $donation->get_test_mode( false ) ? 'test_secret_key' : 'live_secret_key'; |
| 1134 |
$api_key = $gateway->get_value( $key ); |
| 1135 |
$account = charitable_stripe_get_account_id_for_donation( $donation->ID ); |
| 1136 |
$options = $account ? [ 'stripe_account' => $account ] : null; |
| 1137 |
|
| 1138 |
if ( ! $api_key ) { |
| 1139 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1140 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1141 |
error_log( sprintf( |
| 1142 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: No API key found for mode %s - cancellation aborted', |
| 1143 |
$donation->get_test_mode( false ) ? 'test' : 'live' |
| 1144 |
) ); |
| 1145 |
} |
| 1146 |
} |
| 1147 |
$donation->log()->add( __( 'Cancellation failed: No Stripe API key configured.', 'charitable' ) ); |
| 1148 |
return false; |
| 1149 |
} |
| 1150 |
|
| 1151 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1152 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1153 |
error_log( sprintf( |
| 1154 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: Using %s mode, account: %s', |
| 1155 |
$donation->get_test_mode( false ) ? 'test' : 'live', |
| 1156 |
$account ? $account : 'none' |
| 1157 |
) ); |
| 1158 |
} |
| 1159 |
} |
| 1160 |
|
| 1161 |
$gateway->setup_api( $api_key ); |
| 1162 |
|
| 1163 |
try { |
| 1164 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1165 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1166 |
error_log( sprintf( |
| 1167 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: Retrieving subscription %s from Stripe', |
| 1168 |
$subscription_id |
| 1169 |
) ); |
| 1170 |
} |
| 1171 |
} |
| 1172 |
|
| 1173 |
$subscription = \Stripe\Subscription::retrieve( $subscription_id, $options ); |
| 1174 |
|
| 1175 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1176 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1177 |
error_log( sprintf( |
| 1178 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: Retrieved subscription status: %s', |
| 1179 |
$subscription->status |
| 1180 |
) ); |
| 1181 |
} |
| 1182 |
} |
| 1183 |
|
| 1184 |
// Log current subscription state before cancellation |
| 1185 |
$donation->log()->add( sprintf( |
| 1186 |
__( 'Retrieved subscription from Stripe - Current status: %s', 'charitable' ), |
| 1187 |
$subscription->status |
| 1188 |
) ); |
| 1189 |
|
| 1190 |
$subscription->cancel( null, $options ); |
| 1191 |
|
| 1192 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1193 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1194 |
error_log( 'CHARITABLE_STRIPE_CANCEL_DEBUG: Stripe cancellation API call completed successfully' ); |
| 1195 |
} |
| 1196 |
} |
| 1197 |
|
| 1198 |
$donation->log()->add( __( 'Subscription cancelled in Stripe.', 'charitable' ) ); |
| 1199 |
|
| 1200 |
$cancelled = true; |
| 1201 |
|
| 1202 |
} catch ( Exception $e ) { |
| 1203 |
$body = $e->getJsonBody(); |
| 1204 |
$message = isset( $body['error']['message'] ) ? $body['error']['message'] : __( 'Unknown error.', 'charitable' ); |
| 1205 |
|
| 1206 |
$donation->log()->add( |
| 1207 |
sprintf( |
| 1208 |
/* translators: %s: error message */ |
| 1209 |
__( 'Stripe cancellation failed: %s', 'charitable' ), |
| 1210 |
$message |
| 1211 |
) |
| 1212 |
); |
| 1213 |
|
| 1214 |
$cancelled = false; |
| 1215 |
|
| 1216 |
} finally { |
| 1217 |
if ( defined( 'CHARITABLE_DEBUG' ) && CHARITABLE_DEBUG ) { |
| 1218 |
if ( charitable_is_debug( 'stripe' ) ) { |
| 1219 |
error_log( sprintf( |
| 1220 |
'CHARITABLE_STRIPE_CANCEL_DEBUG: Cancellation result: %s', |
| 1221 |
$cancelled ? 'SUCCESS' : 'FAILED' |
| 1222 |
) ); |
| 1223 |
} |
| 1224 |
} |
| 1225 |
return $cancelled; |
| 1226 |
} |
| 1227 |
} |
| 1228 |
|
| 1229 |
/** |
| 1230 |
* Cancel a subscription in Stripe after the recurring donation is marked as completed. |
| 1231 |
* |
| 1232 |
* @since 1.4.9 |
| 1233 |
* @since 1.8.11.3 Made static. It is registered as a static callback on |
| 1234 |
* charitable_recurring_donation_completed, which fataled |
| 1235 |
* on PHP 8+ while the method was non-static. The body only |
| 1236 |
* calls the already-static cancel_subscription(), so this |
| 1237 |
* is backwards compatible (instance calls to a static |
| 1238 |
* method still work). |
| 1239 |
* |
| 1240 |
* @param Charitable_Recurring_Donation $recurring_donation The recurring donation object. |
| 1241 |
* @return boolean |
| 1242 |
*/ |
| 1243 |
public static function process_completed_subscription( $recurring_donation ) { |
| 1244 |
return self::cancel_subscription( true, $recurring_donation ); |
| 1245 |
} |
| 1246 |
|
| 1247 |
/** |
| 1248 |
* Get the donation amount in the smallest common currency unit. |
| 1249 |
* |
| 1250 |
* @since 1.2.3 |
| 1251 |
* |
| 1252 |
* @param float $amount The donation amount in dollars. |
| 1253 |
* @param string|null $currency The currency of the donation. If null, the site currency will be used. |
| 1254 |
* @return int |
| 1255 |
*/ |
| 1256 |
public static function get_amount( $amount, $currency = null ) { |
| 1257 |
return Charitable_Stripe_Gateway_Processor::get_sanitized_donation_amount( $amount, $currency ); |
| 1258 |
} |
| 1259 |
|
| 1260 |
/** |
| 1261 |
* Returns whether the currency is a zero decimal currency. |
| 1262 |
* |
| 1263 |
* @since 1.2.3 |
| 1264 |
* |
| 1265 |
* @param string $currency The currency for the charge. If left blank, will check for the site currency. |
| 1266 |
* @return boolean |
| 1267 |
*/ |
| 1268 |
public static function is_zero_decimal_currency( $currency = null ) { |
| 1269 |
return Charitable_Stripe_Gateway_Processor::is_zero_decimal_currency( $currency ); |
| 1270 |
} |
| 1271 |
|
| 1272 |
/** |
| 1273 |
* Return all zero-decimal currencies supported by Stripe. |
| 1274 |
* |
| 1275 |
* @since 1.2.3 |
| 1276 |
* |
| 1277 |
* @return array |
| 1278 |
*/ |
| 1279 |
public static function get_zero_decimal_currencies() { |
| 1280 |
return Charitable_Stripe_Gateway_Processor::get_zero_decimal_currencies(); |
| 1281 |
} |
| 1282 |
|
| 1283 |
/** |
| 1284 |
* Attemps a Stripe charge and returns the status of the charge. |
| 1285 |
* |
| 1286 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1287 |
* |
| 1288 |
* @since 1.1.0 |
| 1289 |
* @since 1.3.0 Deprecated. |
| 1290 |
* |
| 1291 |
* @param array $charge_args The arguments for the charge API request. |
| 1292 |
* @param Charitable_Donor $donor The current user. |
| 1293 |
* @param Charitable_Donation_Processor $processor The Donation Processor helper object. |
| 1294 |
*/ |
| 1295 |
public function make_charge( $charge_args, $donor, $processor ) { |
| 1296 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1297 |
} |
| 1298 |
|
| 1299 |
/** |
| 1300 |
* Set the $charges property to empty. |
| 1301 |
* |
| 1302 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1303 |
* |
| 1304 |
* @since 1.1.0 |
| 1305 |
* @since 1.3.0 Deprecated. |
| 1306 |
* |
| 1307 |
* @return void |
| 1308 |
*/ |
| 1309 |
public function clear_charges() { |
| 1310 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1311 |
} |
| 1312 |
|
| 1313 |
/** |
| 1314 |
* Return the results of all charges. |
| 1315 |
* |
| 1316 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1317 |
* |
| 1318 |
* @since 1.1.0 |
| 1319 |
* @since 1.3.0 Deprecated. |
| 1320 |
*/ |
| 1321 |
public function get_charges() { |
| 1322 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1323 |
} |
| 1324 |
|
| 1325 |
/** |
| 1326 |
* Saves the results of a charge. |
| 1327 |
* |
| 1328 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1329 |
* |
| 1330 |
* @since 1.1.0 |
| 1331 |
* @since 1.3.0 Deprecated. |
| 1332 |
* |
| 1333 |
* @param array $result The result of the charge. |
| 1334 |
* @param string $status The status of the charge. |
| 1335 |
* |
| 1336 |
* @return void |
| 1337 |
*/ |
| 1338 |
public function save_charge_results( $result, $status ) { |
| 1339 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1340 |
} |
| 1341 |
|
| 1342 |
/** |
| 1343 |
* Returns a card ID for the customer. |
| 1344 |
* |
| 1345 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1346 |
* |
| 1347 |
* @since 1.7.0 |
| 1348 |
* @since 1.3.0 Deprecated. |
| 1349 |
* |
| 1350 |
* @param string $customer Stripe's customer ID. |
| 1351 |
* @param string $card The customer's card details or token. |
| 1352 |
* @return string|false Card ID or false if Stripe returns an error. |
| 1353 |
*/ |
| 1354 |
public function get_customer_card( $customer, $card ) { |
| 1355 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1356 |
|
| 1357 |
try { |
| 1358 |
|
| 1359 |
$cu = \Stripe\Customer::retrieve( $customer ); |
| 1360 |
$card = $cu->sources->create( [ 'source' => $card ] ); |
| 1361 |
$card_id = $card->id; |
| 1362 |
|
| 1363 |
} catch ( Exception $e ) { |
| 1364 |
|
| 1365 |
$body = $e->getJsonBody(); |
| 1366 |
$message = isset( $body['error']['message'] ) ? $body['error']['message'] : __( 'Something went wrong.', 'charitable' ); |
| 1367 |
charitable_get_notices()->add_error( $message ); |
| 1368 |
$card_id = false; |
| 1369 |
|
| 1370 |
} |
| 1371 |
|
| 1372 |
return $card_id; |
| 1373 |
} |
| 1374 |
|
| 1375 |
/** |
| 1376 |
* Return the Stripe Customer ID for the current customer. |
| 1377 |
* |
| 1378 |
* If the donor has donated previously through Stripe, this will return |
| 1379 |
* their ID from the database. If not, this will first set them up as a |
| 1380 |
* customer in Stripe, store their customer ID and then return it. |
| 1381 |
* |
| 1382 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1383 |
* |
| 1384 |
* @since 1.7.0 |
| 1385 |
* @since 1.3.0 Deprecated. |
| 1386 |
* |
| 1387 |
* @param Charitable_User|Charitable_Donor $donor The donor/user object for the logged in user. |
| 1388 |
* @param Charitable_Donation_Processor $processor The Donation Procesor helper. |
| 1389 |
* @return string|false |
| 1390 |
*/ |
| 1391 |
public function get_stripe_customer( $donor, Charitable_Donation_Processor $processor ) { |
| 1392 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1393 |
|
| 1394 |
$key = charitable_get_option( 'test_mode' ) ? self::STRIPE_CUSTOMER_ID_KEY_TEST : self::STRIPE_CUSTOMER_ID_KEY; |
| 1395 |
|
| 1396 |
/** |
| 1397 |
* Retrieve current customer ID and verify that the customer still exists |
| 1398 |
* in Stripe. |
| 1399 |
*/ |
| 1400 |
$stripe_customer_id = $donor->$key; |
| 1401 |
|
| 1402 |
if ( $stripe_customer_id ) { |
| 1403 |
|
| 1404 |
try { |
| 1405 |
/* Retrieve the customer object from Stripe. */ |
| 1406 |
$cu = \Stripe\Customer::retrieve( $stripe_customer_id ); |
| 1407 |
|
| 1408 |
} catch ( Stripe\Error\InvalidRequest $e ) { |
| 1409 |
$cu = null; |
| 1410 |
} |
| 1411 |
|
| 1412 |
if ( is_null( $cu ) || ( isset( $cu->deleted ) && $cu->deleted ) ) { |
| 1413 |
$stripe_customer_id = false; |
| 1414 |
} |
| 1415 |
} |
| 1416 |
|
| 1417 |
/* No Stripe Customer ID found, so we're going to create one. */ |
| 1418 |
if ( ! $stripe_customer_id ) { |
| 1419 |
|
| 1420 |
$stripe_customer_id = $this->create_stripe_customer( $donor, $processor ); |
| 1421 |
|
| 1422 |
/* Store the customer ID for logged in users. */ |
| 1423 |
if ( $stripe_customer_id && $donor->ID ) { |
| 1424 |
update_user_meta( $donor->ID, $key, $stripe_customer_id ); |
| 1425 |
} |
| 1426 |
} |
| 1427 |
|
| 1428 |
return $stripe_customer_id; |
| 1429 |
} |
| 1430 |
|
| 1431 |
/** |
| 1432 |
* Create a Stripe Customer object through the API. |
| 1433 |
* |
| 1434 |
* @deprecated 1.5.0 To be removed in 1.5.0 or 2.0.0. |
| 1435 |
* |
| 1436 |
* @since 1.2.2 |
| 1437 |
* @since 1.3.0 Deprecated. |
| 1438 |
* |
| 1439 |
* @param Charitable_Donor $donor The Donor object. |
| 1440 |
* @param Charitable_Donation_Processor $processor The Donation Procesor helper. |
| 1441 |
* @return string|false |
| 1442 |
*/ |
| 1443 |
public function create_stripe_customer( $donor, Charitable_Donation_Processor $processor ) { |
| 1444 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1445 |
|
| 1446 |
$stripe_customer_args = apply_filters( |
| 1447 |
'charitable_stripe_customer_args', |
| 1448 |
array( |
| 1449 |
'description' => sprintf( '%s %s', __( 'Donor for', 'charitable' ), $donor->get_email() ), |
| 1450 |
'email' => $donor->get_email(), |
| 1451 |
'metadata' => array( |
| 1452 |
'donor_id' => $processor->get_donor_id(), |
| 1453 |
'user_id' => $donor->ID, |
| 1454 |
), |
| 1455 |
), |
| 1456 |
$donor, |
| 1457 |
$processor |
| 1458 |
); |
| 1459 |
|
| 1460 |
try { |
| 1461 |
$customer = \Stripe\Customer::create( $stripe_customer_args ); |
| 1462 |
|
| 1463 |
return $customer->id; |
| 1464 |
|
| 1465 |
} catch ( Exception $e ) { |
| 1466 |
$body = $e->getJsonBody(); |
| 1467 |
$message = isset( $body['error']['message'] ) ? $body['error']['message'] : __( 'Something went wrong.', 'charitable' ); |
| 1468 |
charitable_get_notices()->add_error( $message ); |
| 1469 |
|
| 1470 |
return false; |
| 1471 |
} |
| 1472 |
} |
| 1473 |
|
| 1474 |
/** |
| 1475 |
* Returns the payment source. |
| 1476 |
* |
| 1477 |
* This may return a string, identifying the ID of a payment source such as |
| 1478 |
* a credit card. It may also be an associative array containing the user's |
| 1479 |
* credit card details. |
| 1480 |
* |
| 1481 |
* @deprecated 1.5.0 To be removed in 1.5.0. |
| 1482 |
* |
| 1483 |
* @since 1.7.0 |
| 1484 |
* @since 1.3.0 Deprecated. |
| 1485 |
* |
| 1486 |
* @param Charitable_User|Charitable_Donor $donor The donor/user object for the logged in user. |
| 1487 |
* @param Charitable_Donation_Processor $processor The Donation Procesor helper. |
| 1488 |
* @return string|array |
| 1489 |
*/ |
| 1490 |
public function get_source( $donor, Charitable_Donation_Processor $processor ) { |
| 1491 |
charitable_stripe_get_deprecated()->deprecated_function( __METHOD__, '1.3.0' ); |
| 1492 |
|
| 1493 |
$values = $processor->get_donation_data(); |
| 1494 |
|
| 1495 |
/** |
| 1496 |
* If the donation is made by a logged in user who selected |
| 1497 |
* a source (card), return that. |
| 1498 |
*/ |
| 1499 |
if ( $this->get_gateway_value( 'source', $values ) ) { |
| 1500 |
return $this->get_gateway_value( 'source', $values ); |
| 1501 |
} |
| 1502 |
|
| 1503 |
/** |
| 1504 |
* If we have a token available, return that. |
| 1505 |
*/ |
| 1506 |
if ( $this->get_gateway_value( 'token', $values ) ) { |
| 1507 |
return $this->get_gateway_value( 'token', $values ); |
| 1508 |
} |
| 1509 |
|
| 1510 |
charitable_get_notices()->add_error( __( 'Missing credit card details. Unable to proceed with payment.', 'charitable' ) ); |
| 1511 |
|
| 1512 |
return false; |
| 1513 |
} |
| 1514 |
|
| 1515 |
/** |
| 1516 |
* Load Stripe JS, as well as our handling scripts. |
| 1517 |
* |
| 1518 |
* @deprecated 1.6.0 |
| 1519 |
* |
| 1520 |
* @since 1.1.0 |
| 1521 |
* @since 1.4.0 Deprecated. Method moved to Charitable_Stripe class. |
| 1522 |
* |
| 1523 |
* @return boolean |
| 1524 |
*/ |
| 1525 |
public static function enqueue_scripts() { |
| 1526 |
charitable_stripe_get_deprecated()->deprecated_function( |
| 1527 |
__METHOD__, |
| 1528 |
'1.4.0', |
| 1529 |
'charitable_stripe()->enqueue_scripts()' |
| 1530 |
); |
| 1531 |
|
| 1532 |
return charitable_stripe()->enqueue_scripts(); |
| 1533 |
} |
| 1534 |
|
| 1535 |
/** |
| 1536 |
* Load Stripe JS or Stripe Checkout, as well as our handling scripts. |
| 1537 |
* |
| 1538 |
* @deprecated 1.6.0 |
| 1539 |
* |
| 1540 |
* @since 1.1.2 |
| 1541 |
* @since 1.4.0 Deprecated. Method moved to Charitable_Stripe class. |
| 1542 |
* |
| 1543 |
* @param Charitable_Donation_Form $form The current form object. |
| 1544 |
* @return boolean |
| 1545 |
*/ |
| 1546 |
public static function maybe_setup_scripts_in_donation_form( $form ) { |
| 1547 |
charitable_stripe_get_deprecated()->deprecated_function( |
| 1548 |
__METHOD__, |
| 1549 |
'1.4.0', |
| 1550 |
'charitable_stripe()->maybe_setup_scripts_in_donation_form( $form )' |
| 1551 |
); |
| 1552 |
|
| 1553 |
return charitable_stripe()->maybe_setup_scripts_in_donation_form( $form ); |
| 1554 |
} |
| 1555 |
|
| 1556 |
/** |
| 1557 |
* Enqueue the Stripe JS/Checkout scripts after a campaign loop if modal donations are in use. |
| 1558 |
* |
| 1559 |
* @deprecated 1.6.0 |
| 1560 |
* |
| 1561 |
* @since 1.1.2 |
| 1562 |
* @since 1.4.0 Deprecated. Method moved to Charitable_Stripe class. |
| 1563 |
* |
| 1564 |
* @return boolean |
| 1565 |
*/ |
| 1566 |
public static function maybe_setup_scripts_in_campaign_loop() { |
| 1567 |
charitable_stripe_get_deprecated()->deprecated_function( |
| 1568 |
__METHOD__, |
| 1569 |
'1.4.0', |
| 1570 |
'charitable_stripe()->maybe_setup_scripts_in_campaign_loop()' |
| 1571 |
); |
| 1572 |
|
| 1573 |
return charitable_stripe()->maybe_setup_scripts_in_campaign_loop(); |
| 1574 |
} |
| 1575 |
|
| 1576 |
|
| 1577 |
/** |
| 1578 |
* Checks if credit card details are required. |
| 1579 |
* |
| 1580 |
* Credit card details are required UNLESS the donation is being |
| 1581 |
* made by a logged in user who has donated before and is using |
| 1582 |
* one of their stored cards (stored on Stripe's server, not ours). |
| 1583 |
* |
| 1584 |
* @deprecated 1.6.0 |
| 1585 |
* |
| 1586 |
* @since 1.7.0 |
| 1587 |
* @since 1.4.0 Deprecated. |
| 1588 |
* |
| 1589 |
* @param array $values The filtered values from the donation form submission. |
| 1590 |
* @return boolean |
| 1591 |
*/ |
| 1592 |
public function require_credit_card_details( $values ) { |
| 1593 |
charitable_stripe_get_deprecated()->deprecated_function( |
| 1594 |
__METHOD__, |
| 1595 |
'1.4.0' |
| 1596 |
); |
| 1597 |
|
| 1598 |
return false; |
| 1599 |
} |
| 1600 |
|
| 1601 |
/** |
| 1602 |
* Determine if the Stripe addon should be shown on the frontend if no keys are found |
| 1603 |
* |
| 1604 |
* @param array[] $active_gateways The list of registered gateways. |
| 1605 |
* @return string[] |
| 1606 |
* @since 1.7.0 |
| 1607 |
*/ |
| 1608 |
public function maybe_active_public_gateway( $active_gateways ) { |
| 1609 |
|
| 1610 |
if ( ! isset( $active_gateways['stripe'] ) ) { |
| 1611 |
return $active_gateways; |
| 1612 |
} |
| 1613 |
|
| 1614 |
if ( is_admin() ) { |
| 1615 |
// if this is the admin backend don't mess with this list. |
| 1616 |
return $active_gateways; |
| 1617 |
} |
| 1618 |
|
| 1619 |
global $post; |
| 1620 |
|
| 1621 |
if ( false === $post || ! isset( $post->ID ) ) { |
| 1622 |
return $active_gateways; |
| 1623 |
} |
| 1624 |
|
| 1625 |
// ok, this is being loaded as an active gateway - but do we have any keys? |
| 1626 |
$keys = $this->get_keys(); |
| 1627 |
|
| 1628 |
/* Make sure that the keys are set, otherwise there will likely be JS error in the template that will effect other JS generated by the plugin on the page. */ |
| 1629 |
if ( empty( $keys['secret_key'] ) || empty( $keys['public_key'] ) ) { |
| 1630 |
charitable_get_notices()->add_error( __( 'Missing keys for Stripe payment gateway. Unable to proceed with payment.', 'charitable' ) ); |
| 1631 |
unset( $active_gateways['stripe'] ); |
| 1632 |
} |
| 1633 |
|
| 1634 |
return $active_gateways; |
| 1635 |
} |
| 1636 |
|
| 1637 |
/** |
| 1638 |
* Register the Stripe payment gateway class. |
| 1639 |
* |
| 1640 |
* @param string $setting Setting, which might have been stored in the database by an addon. |
| 1641 |
* @param string $original_key Oringial key. |
| 1642 |
* @param string $default Default setting. |
| 1643 |
* @return string |
| 1644 |
* @since 1.7.0 |
| 1645 |
*/ |
| 1646 |
public function maybe_return_stripe_connect_setting( $setting, $original_key, $default ) { |
| 1647 |
|
| 1648 |
if ( ! charitable()->is_stripe_connect_addon() ) { |
| 1649 |
// if the stripe connect addon isn't activated, then this setting shouldn't be retreavable (although it stays in the dataabase). |
| 1650 |
$setting = null; |
| 1651 |
} |
| 1652 |
|
| 1653 |
return $setting; |
| 1654 |
} |
| 1655 |
|
| 1656 |
|
| 1657 |
/** |
| 1658 |
* Stripe shows errors for amounts less tha $1, so set the min. amount to 1. This filter catches the ajax request. |
| 1659 |
* |
| 1660 |
* @param integer $minimum_amount The minimum donation amount. |
| 1661 |
* |
| 1662 |
* @return integer |
| 1663 |
* @since 1.7.0.2 |
| 1664 |
*/ |
| 1665 |
public function set_minimum_donation_amount( $minimum_amount ) { |
| 1666 |
|
| 1667 |
if ( isset( $_POST['gateway'] ) && false !== strpos( $_POST['gateway'], 'stripe' ) ) { // phpcs:ignore |
| 1668 |
// Stripe gateway detected - make sure the minimum aount is at least 1 (logic revised in 1.7.0.9). |
| 1669 |
if ( intval( $minimum_amount ) < 1 ) { |
| 1670 |
$minimum_amount = 1; |
| 1671 |
} |
| 1672 |
} |
| 1673 |
|
| 1674 |
return $minimum_amount; |
| 1675 |
} |
| 1676 |
|
| 1677 |
/** |
| 1678 |
* Use existing pre-1.7.0 hook to process webhooks that are for Stripe Connect AM accounts. |
| 1679 |
* |
| 1680 |
* @param string $event_type The type of event. |
| 1681 |
* @param object $event The event object. |
| 1682 |
* |
| 1683 |
* @since 1.7.0 |
| 1684 |
*/ |
| 1685 |
public function maybe_process_webhook_with_stripe_connect( $event_type, $event ) { |
| 1686 |
|
| 1687 |
// a reminder on this check: |
| 1688 |
// the option gets written when the stripe connect in the core plugin (starting in v1.7.0) is connected in gateway settings in the admin. |
| 1689 |
// the option is removed when, after the stripe connect is connected, the user clicks on the "disconnect" link is clicked in the settings. |
| 1690 |
if ( ! charitable_using_stripe_connect() ) { |
| 1691 |
return; |
| 1692 |
} |
| 1693 |
|
| 1694 |
if ( ( charitable_is_debug() ) ) { |
| 1695 |
error_log( 'maybe_process_webhook_with_stripe_connect' ); // phpcs:ignore |
| 1696 |
error_log( print_r( $event_type, true ) ); // phpcs:ignore |
| 1697 |
error_log( print_r( $event, true ) ); // phpcs:ignore |
| 1698 |
} |
| 1699 |
|
| 1700 |
$processor = new Charitable_Stripe_Webhook_Processor( $event ); |
| 1701 |
|
| 1702 |
$default_processors = apply_filters( |
| 1703 |
'charitable_stripe_default_event_processors', |
| 1704 |
[ |
| 1705 |
'charge.refunded' => [ $processor, 'process_refund' ], |
| 1706 |
'invoice.created' => [ $processor, 'process_invoice_created' ], |
| 1707 |
'invoice.payment_failed' => [ $processor, 'process_invoice_payment_failed' ], |
| 1708 |
'invoice.payment_succeeded' => [ $processor, 'process_invoice_payment_succeeded' ], |
| 1709 |
'customer.subscription.updated' => [ $processor, 'process_customer_subscription_updated' ], |
| 1710 |
'customer.subscription.deleted' => [ $processor, 'process_customer_subscription_deleted' ], |
| 1711 |
'payment_intent.payment_failed' => [ $processor, 'process_payment_intent_payment_failed' ], |
| 1712 |
'payment_intent.succeeded' => [ $processor, 'process_payment_intent_succeeded' ], |
| 1713 |
'checkout.session.completed' => [ $processor, 'process_checkout_session_completed' ], |
| 1714 |
] |
| 1715 |
); |
| 1716 |
|
| 1717 |
if ( array_key_exists( $event_type, $default_processors ) ) { |
| 1718 |
$processor = $default_processors[ $event_type ]; |
| 1719 |
if ( is_callable( $processor ) ) { |
| 1720 |
$message = $processor( $event ); |
| 1721 |
} else { |
| 1722 |
$message = 'Invalid processor'; |
| 1723 |
} |
| 1724 |
/* Kill processing with a message returned by the event processor. */ |
| 1725 |
die( $message ); // phpcs:ignore |
| 1726 |
} |
| 1727 |
} |
| 1728 |
} |
| 1729 |
|
| 1730 |
endif; |
| 1731 |
|