Paid Memberships Pro - Version 2.0.5

Version Description

  • 2019-04-25 =
  • BUG FIX: Fixed fatal error on return from 2Checkout.
  • BUG FIX: Removed error when installing PMPro via WP-CLI.
  • BUG FIX: Fix database upgrade error on localhost environment. (Thanks, codezz on GitHub)
  • BUG FIX: Fixed issue where the credit card expiring email didn't include user info because the user ID wasn't passed in properly. (Thanks, David Cervantes Caballero)
  • BUG FIX: Fixed typo on edit level page. (Thanks, Theuns Coetzee)
  • BUG FIX: Fixed bug with daily revenue reports not showing up in some cases.
  • BUG FIX: Now checking before cancelling a Stripe subscription at the gateway to see if it has already been cancelled.
  • BUG FIX/ENHANCEMENT: Now caching the query results in pmpro_getMembershipLevelsForUser(). This improves performance, especially when there are many posts on one page to check membership for. (Thanks, Seagyn Davis)
  • BUG FIX/ENHANCEMENT: Now sending display_name to the $data array passed to PMPro email filters. (Thanks, David Cervantes Caballero)
  • BUG FIX/ENHANCEMENT: Now searching for the last order with "success" or "pending" status on the Billing page.
  • BUG FIX/ENHANCEMENT: Added pmpro_checkout_preheader_before_get_level_at_checkout and pmpro_checkout_preheader_after_get_level_at_checkout action hooks. Using pmpro_checkout_preheader_before_get_level_at_checkout to start the session earlier now.
  • BUG FIX/ENHANCEMENT: Removed the "membership_code_id" and "membership_code" as field options for the member shortcode. These weren't working and it's unclear what would be meant to ask for a user's discount code since a user could have several orders with or without discount codes. Added "membership_description" and "membership_confirmation" instead.
  • BUG FIX/ENHANCEMENT: Filtering the password reset message to make sure the link still works in all cases when we convert emails to HTML.
  • REFACTOR: Now running the pmpro_billing_preheader hook after the jquery.creditCardValidator script is enqueued in preheader/billing.php to match how we do it in preheader/checkout.php. (Thanks, Rafe Colton)
Download this release

Release Info

Developer strangerstudios
Plugin Icon 128x128 Paid Memberships Pro
Version 2.0.5
Comparing to
See all releases

Code changes from version 2.0.4 to 2.0.5

adminpages/advancedsettings.php CHANGED
@@ -36,6 +36,7 @@
36
37
//captcha
38
pmpro_setOption("recaptcha");
39
pmpro_setOption("recaptcha_publickey");
40
pmpro_setOption("recaptcha_privatekey");
41
@@ -72,6 +73,7 @@
72
$redirecttosubscription = pmpro_getOption("redirecttosubscription");
73
74
$recaptcha = pmpro_getOption("recaptcha");
75
$recaptcha_publickey = pmpro_getOption("recaptcha_publickey");
76
$recaptcha_privatekey = pmpro_getOption("recaptcha_privatekey");
77
@@ -245,6 +247,13 @@ if(pmpro_displayAds())
245
<tr id="recaptcha_tr" <?php if(!$recaptcha) { ?>style="display: none;"<?php } ?>>
246
<th scope="row" valign="top">&nbsp;</th>
247
<td>
248
<label for="recaptcha_publickey"><?php _e('reCAPTCHA Site Key', 'paid-memberships-pro' );?>:</label>
249
<input type="text" id="recaptcha_publickey" name="recaptcha_publickey" size="60" value="<?php echo esc_attr($recaptcha_publickey);?>" />
250
<br /><br />
36
37
//captcha
38
pmpro_setOption("recaptcha");
39
+ pmpro_setOption("recaptcha_version");
40
pmpro_setOption("recaptcha_publickey");
41
pmpro_setOption("recaptcha_privatekey");
42
73
$redirecttosubscription = pmpro_getOption("redirecttosubscription");
74
75
$recaptcha = pmpro_getOption("recaptcha");
76
+ $recaptcha_version = pmpro_getOption("recaptcha_version");
77
$recaptcha_publickey = pmpro_getOption("recaptcha_publickey");
78
$recaptcha_privatekey = pmpro_getOption("recaptcha_privatekey");
79
247
<tr id="recaptcha_tr" <?php if(!$recaptcha) { ?>style="display: none;"<?php } ?>>
248
<th scope="row" valign="top">&nbsp;</th>
249
<td>
250
+ <label for="recaptcha_version"><?php _e( 'reCAPTCHA Version', 'paid-memberships-pro' );?>:</label>
251
+ <select id="recaptcha_version" name="recaptcha_version">
252
+ <option value="2_checkbox" <?php selected( '2_checkbox', $recaptcha_version ); ?>><?php _e( ' v2 - Checkbox', 'paid-memberships-pro' ); ?></option>
253
+ <option value="3_invisible" <?php selected( '3_invisible', $recaptcha_version ); ?>><?php _e( 'v3 - Invisible', 'paid-memberships-pro' ); ?></option>
254
+ </select>
255
+ <small><?php _e( 'Changing your version will require new API keys.', 'paid-memberships-pro' ); ?></small>
256
+ <br /><br />
257
<label for="recaptcha_publickey"><?php _e('reCAPTCHA Site Key', 'paid-memberships-pro' );?>:</label>
258
<input type="text" id="recaptcha_publickey" name="recaptcha_publickey" size="60" value="<?php echo esc_attr($recaptcha_publickey);?>" />
259
<br /><br />
adminpages/emailsettings.php CHANGED
@@ -51,8 +51,12 @@
51
{
52
$parsed = parse_url(home_url());
53
$hostname = $parsed["host"];
54
- $hostparts = explode(".", $hostname);
55
- $email_domain = $hostparts[count($hostparts) - 2] . "." . $hostparts[count($hostparts) - 1];
56
$from_email = "wordpress@" . $email_domain;
57
pmpro_setOption("from_email", $from_email);
58
}
51
{
52
$parsed = parse_url(home_url());
53
$hostname = $parsed["host"];
54
+ $host_parts = explode(".", $hostname);
55
+ if ( count( $host_parts ) > 1 ) {
56
+ $email_domain = $host_parts[count($host_parts) - 2] . "." . $host_parts[count($host_parts) - 1];
57
+ } else {
58
+ $email_domain = $parsed['host'];
59
+ }
60
$from_email = "wordpress@" . $email_domain;
61
pmpro_setOption("from_email", $from_email);
62
}
adminpages/membershiplevels.php CHANGED
@@ -387,7 +387,7 @@
387
}
388
?>
389
</div>
390
- <input id="confirmation_in_email" name="confirmation_in_email" type="checkbox" value="yes" <?php checked( $confirmation_in_email, 1); ?> /> <label for="confirmation_in_email"><?php _e('Check to include this message in the membership confiramtion email.', 'paid-memberships-pro' );?></label>
391
</td>
392
</tr>
393
</tbody>
387
}
388
?>
389
</div>
390
+ <input id="confirmation_in_email" name="confirmation_in_email" type="checkbox" value="yes" <?php checked( $confirmation_in_email, 1); ?> /> <label for="confirmation_in_email"><?php _e('Check to include this message in the membership confirmation email.', 'paid-memberships-pro' );?></label>
391
</td>
392
</tr>
393
</tbody>
adminpages/reports/sales.php CHANGED
@@ -152,7 +152,7 @@ function pmpro_report_sales_page()
152
if($period == "daily")
153
{
154
$startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
155
- $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
156
$date_function = 'DAY';
157
$currently_in_period = ( intval( date( 'Y' ) ) == $year && intval( date( 'n' ) ) == $month );
158
}
@@ -183,7 +183,7 @@ function pmpro_report_sales_page()
183
$sqlQuery .= "WHERE o.total > 0 AND o.timestamp >= '" . esc_sql( $startdate ) . "' AND o.status NOT IN('refunded', 'review', 'token', 'error') AND o.gateway_environment = '" . esc_sql( $gateway_environment ) . "' ";
184
185
if(!empty($enddate))
186
- $sqlQuery .= "AND o.timestamp < '" . esc_sql( $enddate ) . "' ";
187
188
if(!empty($l))
189
$sqlQuery .= "AND o.membership_id IN(" . esc_sql( $l ) . ") ";
@@ -615,4 +615,4 @@ function pmpro_report_sales_delete_transients()
615
delete_transient("pmpro_report_revenue");
616
}
617
add_action("pmpro_after_checkout", "pmpro_report_sales_delete_transients");
618
- add_action("pmpro_updated_order", "pmpro_report_sales_delete_transients");
152
if($period == "daily")
153
{
154
$startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
155
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-31';
156
$date_function = 'DAY';
157
$currently_in_period = ( intval( date( 'Y' ) ) == $year && intval( date( 'n' ) ) == $month );
158
}
183
$sqlQuery .= "WHERE o.total > 0 AND o.timestamp >= '" . esc_sql( $startdate ) . "' AND o.status NOT IN('refunded', 'review', 'token', 'error') AND o.gateway_environment = '" . esc_sql( $gateway_environment ) . "' ";
184
185
if(!empty($enddate))
186
+ $sqlQuery .= "AND o.timestamp <= '" . esc_sql( $enddate ) . "' ";
187
188
if(!empty($l))
189
$sqlQuery .= "AND o.membership_id IN(" . esc_sql( $l ) . ") ";
615
delete_transient("pmpro_report_revenue");
616
}
617
add_action("pmpro_after_checkout", "pmpro_report_sales_delete_transients");
618
+ add_action("pmpro_updated_order", "pmpro_report_sales_delete_transients");
classes/class.memberorder.php CHANGED
@@ -619,7 +619,7 @@
619
$this->gateway_environment = pmpro_getOption("gateway_environment");
620
621
if(empty($this->datetime) && empty($this->timestamp))
622
- $this->datetime = date("Y-m-d H:i:s", current_time("timestamp")); //use current time
623
elseif(empty($this->datetime) && !empty($this->timestamp) && is_numeric($this->timestamp))
624
$this->datetime = date("Y-m-d H:i:s", $this->timestamp); //get datetime from timestamp
625
elseif(empty($this->datetime) && !empty($this->timestamp))
619
$this->gateway_environment = pmpro_getOption("gateway_environment");
620
621
if(empty($this->datetime) && empty($this->timestamp))
622
+ $this->datetime = date("Y-m-d H:i:s", time());
623
elseif(empty($this->datetime) && !empty($this->timestamp) && is_numeric($this->timestamp))
624
$this->datetime = date("Y-m-d H:i:s", $this->timestamp); //get datetime from timestamp
625
elseif(empty($this->datetime) && !empty($this->timestamp))
classes/class.pmproemail.php CHANGED
@@ -142,7 +142,7 @@
142
$this->email = $user->user_email;
143
$this->subject = sprintf(__('Your membership at %s has been CANCELLED', 'paid-memberships-pro'), get_option("blogname"));
144
145
- $this->data = array("name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"));
146
147
if(!empty($old_level_id)) {
148
if(!is_array($old_level_id))
@@ -228,6 +228,7 @@
228
$this->data = array(
229
"subject" => $this->subject,
230
"name" => $user->display_name,
231
"user_login" => $user->user_login,
232
"sitename" => get_option("blogname"),
233
"siteemail" => pmpro_getOption("from_email"),
@@ -832,7 +833,7 @@
832
$this->email = $user->user_email;
833
$this->subject = sprintf(__("Your membership at %s has been changed", "paid-memberships-pro"), get_option("blogname"));
834
835
- $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_id" => $user->membership_level->id, "membership_level_name" => $user->membership_level->name, "siteemail" => pmpro_getOption("from_email"), "login_link" => wp_login_url());
836
837
if($user->membership_level->ID)
838
$this->data["membership_change"] = sprintf(__("The new level is %s", 'paid-memberships-pro' ), $user->membership_level->name);
@@ -879,7 +880,7 @@
879
$this->email = get_bloginfo("admin_email");
880
$this->subject = sprintf(__("Membership for %s at %s has been changed", "paid-memberships-pro"), $user->user_login, get_option("blogname"));
881
882
- $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_level_name" => $membership_level_name, "siteemail" => get_bloginfo("admin_email"), "login_link" => wp_login_url());
883
884
if(!empty($user->membership_level) && !empty($user->membership_level->ID)) {
885
$this->data["membership_change"] = sprintf(__("The new level is %s", 'paid-memberships-pro' ), $user->membership_level->name);
142
$this->email = $user->user_email;
143
$this->subject = sprintf(__('Your membership at %s has been CANCELLED', 'paid-memberships-pro'), get_option("blogname"));
144
145
+ $this->data = array("user_email" => $user->user_email, "display_name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"));
146
147
if(!empty($old_level_id)) {
148
if(!is_array($old_level_id))
228
$this->data = array(
229
"subject" => $this->subject,
230
"name" => $user->display_name,
231
+ "display_name" => $user->display_name,
232
"user_login" => $user->user_login,
233
"sitename" => get_option("blogname"),
234
"siteemail" => pmpro_getOption("from_email"),
833
$this->email = $user->user_email;
834
$this->subject = sprintf(__("Your membership at %s has been changed", "paid-memberships-pro"), get_option("blogname"));
835
836
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "display_name" => $user->display_name, "user_login" => $user->user_login, "user_email" => $user->user_email, "sitename" => get_option("blogname"), "membership_id" => $user->membership_level->id, "membership_level_name" => $user->membership_level->name, "siteemail" => pmpro_getOption("from_email"), "login_link" => wp_login_url());
837
838
if($user->membership_level->ID)
839
$this->data["membership_change"] = sprintf(__("The new level is %s", 'paid-memberships-pro' ), $user->membership_level->name);
880
$this->email = get_bloginfo("admin_email");
881
$this->subject = sprintf(__("Membership for %s at %s has been changed", "paid-memberships-pro"), $user->user_login, get_option("blogname"));
882
883
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "display_name" => $user->display_name, "user_login" => $user->user_login, "user_email" => $user->user_email, "sitename" => get_option("blogname"), "membership_level_name" => $membership_level_name, "siteemail" => get_bloginfo("admin_email"), "login_link" => wp_login_url());
884
885
if(!empty($user->membership_level) && !empty($user->membership_level->ID)) {
886
$this->data["membership_change"] = sprintf(__("The new level is %s", 'paid-memberships-pro' ), $user->membership_level->name);
classes/gateways/class.pmprogateway_paypal.php CHANGED
@@ -198,13 +198,13 @@
198
<?php if($gateway == "paypal" || $gateway == "paypalexpress" || $gateway == "paypalstandard") { ?>
199
<span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
200
<input type="hidden" name="submit-checkout" value="1" />
201
- <input type="image" class="pmpro_btn-submit-checkout" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> &raquo;" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif");?>" />
202
</span>
203
<?php } ?>
204
205
<span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
206
<input type="hidden" name="submit-checkout" value="1" />
207
- <input type="submit" class="pmpro_btn pmpro_btn-submit-checkout" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> &raquo;" />
208
</span>
209
<?php
210
198
<?php if($gateway == "paypal" || $gateway == "paypalexpress" || $gateway == "paypalstandard") { ?>
199
<span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
200
<input type="hidden" name="submit-checkout" value="1" />
201
+ <input type="image" id="pmpro_btn-submit-paypal" class="pmpro_btn-submit-checkout" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> &raquo;" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif");?>" />
202
</span>
203
<?php } ?>
204
205
<span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
206
<input type="hidden" name="submit-checkout" value="1" />
207
+ <input type="submit" id="pmpro_btn-submit" class="pmpro_btn pmpro_btn-submit-checkout" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> &raquo;" />
208
</span>
209
<?php
210
classes/gateways/class.pmprogateway_paypalexpress.php CHANGED
@@ -440,12 +440,12 @@
440
?>
441
<span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
442
<input type="hidden" name="submit-checkout" value="1" />
443
- <input type="image" class="pmpro_btn-submit-checkout" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> &raquo;" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif");?>" />
444
</span>
445
446
<span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
447
<input type="hidden" name="submit-checkout" value="1" />
448
- <input type="submit" class="pmpro_btn pmpro_btn-submit-checkout" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> &raquo;" />
449
</span>
450
<?php
451
440
?>
441
<span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
442
<input type="hidden" name="submit-checkout" value="1" />
443
+ <input type="image" id="pmpro_btn-submit-paypalexpress" class="pmpro_btn-submit-checkout" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> &raquo;" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif");?>" />
444
</span>
445
446
<span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>>
447
<input type="hidden" name="submit-checkout" value="1" />
448
+ <input type="submit" id="pmpro_btn-submit" class="pmpro_btn pmpro_btn-submit-checkout" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> &raquo;" />
449
</span>
450
<?php
451
classes/gateways/class.pmprogateway_stripe.php CHANGED
@@ -1,2048 +1,1939 @@
1
<?php
2
- // For compatibility with old library (Namespace Alias)
3
- use Stripe\Customer as Stripe_Customer;
4
- use Stripe\Invoice as Stripe_Invoice;
5
- use Stripe\Plan as Stripe_Plan;
6
- use Stripe\Charge as Stripe_Charge;
7
-
8
- define( "PMPRO_STRIPE_API_VERSION", "2017-08-15" );
9
-
10
- //include pmprogateway
11
- require_once(dirname(__FILE__) . "/class.pmprogateway.php");
12
-
13
- //load classes init method
14
- add_action('init', array('PMProGateway_stripe', 'init'));
15
-
16
- // loading plugin activation actions
17
- add_action('activate_paid-memberships-pro', array('PMProGateway_stripe', 'pmpro_activation'));
18
- add_action('deactivate_paid-memberships-pro', array('PMProGateway_stripe', 'pmpro_deactivation'));
19
-
20
/**
21
- * PMProGateway_stripe Class
22
- *
23
- * Handles Stripe integration.
24
*
25
- * @since 1.4
26
*/
27
- class PMProGateway_stripe extends PMProGateway
28
- {
29
- /**
30
- * @var bool Is the Stripe/PHP Library loaded
31
- */
32
- private static $is_loaded = false;
33
- /**
34
- * Stripe Class Constructor
35
- *
36
- * @since 1.4
37
- */
38
- function __construct($gateway = NULL)
39
- {
40
- $this->gateway = $gateway;
41
- $this->gateway_environment = pmpro_getOption("gateway_environment");
42
-
43
- if( true === $this->dependencies() ) {
44
- $this->loadStripeLibrary();
45
- Stripe\Stripe::setApiKey(pmpro_getOption("stripe_secretkey"));
46
- Stripe\Stripe::setAPIVersion( PMPRO_STRIPE_API_VERSION );
47
- self::$is_loaded = true;
48
- }
49
-
50
- return $this->gateway;
51
}
52
53
- /**
54
- * Warn if required extensions aren't loaded.
55
- *
56
- * @return bool
57
- * @since 1.8.6.8.1
58
- * @since 1.8.13.6 - Add json dependency
59
- */
60
- public static function dependencies()
61
- {
62
- global $msg, $msgt, $pmpro_stripe_error;
63
64
- if ( version_compare( PHP_VERSION, '5.3.29', '<' )) {
65
66
- $pmpro_stripe_error = true;
67
- $msg = -1;
68
- $msgt = sprintf(__("The Stripe Gateway requires PHP 5.3.29 or greater. We recommend upgrading to PHP %s or greater. Ask your host to upgrade.", "paid-memberships-pro" ), PMPRO_PHP_MIN_VERSION );
69
70
- if ( !is_admin() ) {
71
- pmpro_setMessage( $msgt, "pmpro_error" );
72
- }
73
74
- return false;
75
}
76
77
- $modules = array( 'curl', 'mbstring', 'json' );
78
79
- foreach($modules as $module){
80
- if(!extension_loaded($module)){
81
- $pmpro_stripe_error = true;
82
- $msg = -1;
83
- $msgt = sprintf(__("The %s gateway depends on the %s PHP extension. Please enable it, or ask your hosting provider to enable it.", 'paid-memberships-pro' ), 'Stripe', $module);
84
85
- //throw error on checkout page
86
- if(!is_admin())
87
- pmpro_setMessage($msgt, 'pmpro_error');
88
89
- return false;
90
- }
91
- }
92
93
- self::$is_loaded = true;
94
- return true;
95
- }
96
-
97
- /**
98
- * Load the Stripe API library.
99
- *
100
- * @since 1.8
101
- * Moved into a method in version 1.8 so we only load it when needed.
102
- */
103
- function loadStripeLibrary()
104
- {
105
- //load Stripe library if it hasn't been loaded already (usually by another plugin using Stripe)
106
- if(!class_exists("Stripe\Stripe")) {
107
- require_once( PMPRO_DIR . "/includes/lib/Stripe/init.php" );
108
}
109
}
110
111
- /**
112
- * Run on WP init
113
- *
114
- * @since 1.8
115
- */
116
- static function init()
117
- {
118
- //make sure Stripe is a gateway option
119
- add_filter('pmpro_gateways', array('PMProGateway_stripe', 'pmpro_gateways'));
120
-
121
- //add fields to payment settings
122
- add_filter('pmpro_payment_options', array('PMProGateway_stripe', 'pmpro_payment_options'));
123
- add_filter('pmpro_payment_option_fields', array('PMProGateway_stripe', 'pmpro_payment_option_fields'), 10, 2);
124
125
- //add some fields to edit user page (Updates)
126
- add_action('pmpro_after_membership_level_profile_fields', array('PMProGateway_stripe', 'user_profile_fields'));
127
- add_action('profile_update', array('PMProGateway_stripe', 'user_profile_fields_save'));
128
129
- //old global RE showing billing address or not
130
- global $pmpro_stripe_lite;
131
- $pmpro_stripe_lite = apply_filters("pmpro_stripe_lite", !pmpro_getOption("stripe_billingaddress")); //default is oposite of the stripe_billingaddress setting
132
- add_filter('pmpro_required_billing_fields', array('PMProGateway_stripe', 'pmpro_required_billing_fields'));
133
134
- //updates cron
135
- add_action('pmpro_cron_stripe_subscription_updates', array('PMProGateway_stripe', 'pmpro_cron_stripe_subscription_updates'));
136
137
- /*
138
- Filter pmpro_next_payment to get actual value
139
- via the Stripe API. This is disabled by default
140
- for performance reasons, but you can enable it
141
- by copying this line into a custom plugin or
142
- your active theme's functions.php and uncommenting
143
- it there.
144
- */
145
- //add_filter('pmpro_next_payment', array('PMProGateway_stripe', 'pmpro_next_payment'), 10, 3);
146
147
- //code to add at checkout if Stripe is the current gateway
148
- $default_gateway = pmpro_getOption('gateway');
149
- $current_gateway = pmpro_getGateway();
150
151
- if( ($default_gateway == "stripe" || $current_gateway == "stripe") && empty($_REQUEST['review'] ) ) //$_REQUEST['review'] means the PayPal Express review page
152
- {
153
- add_action('pmpro_checkout_preheader', array('PMProGateway_stripe', 'pmpro_checkout_preheader'));
154
- add_action('pmpro_billing_preheader', array('PMProGateway_stripe', 'pmpro_checkout_preheader'));
155
- add_filter('pmpro_checkout_order', array('PMProGateway_stripe', 'pmpro_checkout_order'));
156
- add_filter('pmpro_billing_order', array('PMProGateway_stripe', 'pmpro_checkout_order'));
157
- add_filter('pmpro_include_billing_address_fields', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
158
- add_filter('pmpro_include_cardtype_field', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
159
- add_filter('pmpro_include_payment_information_fields', array('PMProGateway_stripe', 'pmpro_include_payment_information_fields'));
160
-
161
- //make sure we clean up subs we will be cancelling after checkout before processing
162
- add_action('pmpro_checkout_before_processing', array('PMProGateway_stripe', 'pmpro_checkout_before_processing'));
163
- }
164
165
- add_action( 'init', array( 'PMProGateway_stripe', 'pmpro_clear_saved_subscriptions' ) );
166
}
167
168
- /**
169
- * Clear any saved (preserved) subscription IDs that should have been processed and are now timed out.
170
- */
171
- public static function pmpro_clear_saved_subscriptions() {
172
-
173
- if ( ! is_user_logged_in() ) {
174
- return;
175
- }
176
177
- global $current_user;
178
- $preserve = get_user_meta( $current_user->ID, 'pmpro_stripe_dont_cancel', true );
179
180
- // Clean up the subscription timeout values (if applicable)
181
- if ( !empty( $preserve ) ) {
182
183
- foreach ( $preserve as $sub_id => $timestamp ) {
184
-
185
- // Make sure the ID has "timed out" (more than 3 days since it was last updated/added.
186
- if ( intval( $timestamp ) >= ( current_time( 'timestamp' ) + ( 3 * DAY_IN_SECONDS ) ) ) {
187
- unset( $preserve[ $sub_id ] );
188
- }
189
- }
190
191
- update_user_meta( $current_user->ID, 'pmpro_stripe_dont_cancel', $preserve );
192
- }
193
}
194
195
- /**
196
- * Make sure Stripe is in the gateways list
197
- *
198
- * @since 1.8
199
- */
200
- static function pmpro_gateways($gateways)
201
- {
202
- if(empty($gateways['stripe']))
203
- $gateways['stripe'] = __('Stripe', 'paid-memberships-pro' );
204
-
205
- return $gateways;
206
- }
207
208
- /**
209
- * Get a list of payment options that the Stripe gateway needs/supports.
210
- *
211
- * @since 1.8
212
- */
213
- static function getGatewayOptions()
214
- {
215
- $options = array(
216
- 'sslseal',
217
- 'nuclear_HTTPS',
218
- 'gateway_environment',
219
- 'stripe_secretkey',
220
- 'stripe_publishablekey',
221
- 'stripe_billingaddress',
222
- 'currency',
223
- 'use_ssl',
224
- 'tax_state',
225
- 'tax_rate',
226
- 'accepted_credit_cards'
227
- );
228
229
- return $options;
230
- }
231
232
- /**
233
- * Set payment options for payment settings page.
234
- *
235
- * @since 1.8
236
- */
237
- static function pmpro_payment_options($options)
238
- {
239
- //get stripe options
240
- $stripe_options = self::getGatewayOptions();
241
242
- //merge with others.
243
- $options = array_merge($stripe_options, $options);
244
245
- return $options;
246
- }
247
248
- /**
249
- * Display fields for Stripe options.
250
- *
251
- * @since 1.8
252
- */
253
- static function pmpro_payment_option_fields($values, $gateway)
254
- {
255
- ?>
256
- <tr class="pmpro_settings_divider gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
257
- <td colspan="2">
258
- <?php _e('Stripe Settings', 'paid-memberships-pro' ); ?>
259
- </td>
260
- </tr>
261
- <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
262
- <th scope="row" valign="top">
263
- <label for="stripe_publishablekey"><?php _e('Publishable Key', 'paid-memberships-pro' );?>:</label>
264
- </th>
265
- <td>
266
- <input type="text" id="stripe_publishablekey" name="stripe_publishablekey" size="60" value="<?php echo esc_attr($values['stripe_publishablekey'])?>" />
267
- <?php
268
- $public_key_prefix = substr($values['stripe_publishablekey'] , 0, 3);
269
- if(!empty($values['stripe_publishablekey']) && $public_key_prefix != 'pk_') {
270
- ?>
271
- <br /><small class="pmpro_message pmpro_error"><?php _e('Your Publishable Key appears incorrect.', 'paid-memberships-pro');?></small>
272
- <?php
273
- }
274
?>
275
- </td>
276
- </tr>
277
- <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
278
- <th scope="row" valign="top">
279
- <label for="stripe_secretkey"><?php _e('Secret Key', 'paid-memberships-pro' );?>:</label>
280
- </th>
281
- <td>
282
- <input type="text" id="stripe_secretkey" name="stripe_secretkey" size="60" value="<?php echo esc_attr($values['stripe_secretkey'])?>" />
283
- </td>
284
- </tr>
285
- <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
286
- <th scope="row" valign="top">
287
- <label for="stripe_billingaddress"><?php _e('Show Billing Address Fields', 'paid-memberships-pro' );?>:</label>
288
- </th>
289
- <td>
290
- <select id="stripe_billingaddress" name="stripe_billingaddress">
291
- <option value="0" <?php if(empty($values['stripe_billingaddress'])) { ?>selected="selected"<?php } ?>><?php _e('No', 'paid-memberships-pro' );?></option>
292
- <option value="1" <?php if(!empty($values['stripe_billingaddress'])) { ?>selected="selected"<?php } ?>><?php _e('Yes', 'paid-memberships-pro' );?></option>
293
- </select>
294
- <small><?php _e("Stripe doesn't require billing address fields. Choose 'No' to hide them on the checkout page.<br /><strong>If No, make sure you disable address verification in the Stripe dashboard settings.</strong>", 'paid-memberships-pro' );?></small>
295
- </td>
296
- </tr>
297
- <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
298
- <th scope="row" valign="top">
299
- <label><?php _e('Web Hook URL', 'paid-memberships-pro' );?>:</label>
300
- </th>
301
- <td>
302
- <p><?php _e('To fully integrate with Stripe, be sure to set your Web Hook URL to', 'paid-memberships-pro' );?> <pre><?php echo admin_url("admin-ajax.php") . "?action=stripe_webhook";?></pre></p>
303
- </td>
304
- </tr>
305
-
306
- <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
307
- <th><?php _e( 'Stripe API Version', 'paid-memberships-pro' ); ?>:</th>
308
- <td><?php echo PMPRO_STRIPE_API_VERSION; ?></td>
309
- </tr>
310
- <?php
311
- }
312
-
313
- /**
314
- * Code added to checkout preheader.
315
- *
316
- * @since 1.8
317
- */
318
- static function pmpro_checkout_preheader()
319
- {
320
- global $gateway, $pmpro_level;
321
-
322
- $default_gateway = pmpro_getOption("gateway");
323
-
324
- if(($gateway == "stripe" || $default_gateway == "stripe") && !pmpro_isLevelFree($pmpro_level))
325
- {
326
- //stripe js library
327
- wp_enqueue_script("stripe", "https://js.stripe.com/v2/", array(), NULL);
328
-
329
- if ( ! function_exists( 'pmpro_stripe_javascript' ) ) {
330
331
- //stripe js code for checkout
332
- function pmpro_stripe_javascript()
333
- {
334
- global $pmpro_gateway, $pmpro_level, $pmpro_stripe_lite;
335
- ?>
336
- <script type="text/javascript">
337
- <!--
338
- // this identifies your website in the createToken call below
339
- Stripe.setPublishableKey('<?php echo pmpro_getOption("stripe_publishablekey"); ?>');
340
341
- pmpro_require_billing = true;
342
343
- var tokenNum = 0;
344
345
- jQuery(document).ready(function() {
346
- jQuery(".pmpro_form").submit(function(event) {
347
348
- // prevent the form from submitting with the default action
349
- event.preventDefault();
350
-
351
- //double check in case a discount code made the level free
352
- if(pmpro_require_billing) {
353
- //build array for creating token
354
- var args = {
355
- number: jQuery('#AccountNumber').val(),
356
- exp_month: jQuery('#ExpirationMonth').val(),
357
- exp_year: jQuery('#ExpirationYear').val()
358
- <?php
359
- $pmpro_stripe_verify_address = apply_filters("pmpro_stripe_verify_address", pmpro_getOption('stripe_billingaddress'));
360
- if(!empty($pmpro_stripe_verify_address))
361
- {
362
- ?>
363
- ,address_line1: jQuery('#baddress1').val(),
364
- address_line2: jQuery('#baddress2').val(),
365
- address_city: jQuery('#bcity').val(),
366
- address_state: jQuery('#bstate').val(),
367
- address_zip: jQuery('#bzipcode').val(),
368
- address_country: jQuery('#bcountry').val()
369
- <?php
370
- }
371
?>
372
- };
373
374
- //add CVC if not blank
375
- if(jQuery('#CVV').val().length)
376
- args['cvc'] = jQuery('#CVV').val();
377
378
- //add first and last name if not blank
379
- if (jQuery('#bfirstname').length && jQuery('#blastname').length)
380
- args['name'] = jQuery.trim(jQuery('#bfirstname').val() + ' ' + jQuery('#blastname').val());
381
382
- //create token(s)
383
- if (jQuery('#level').length) {
384
- var levelnums = jQuery("#level").val().split(",");
385
- for(var cnt = 0, len = levelnums.length; cnt < len; cnt++) {
386
- Stripe.createToken(args, stripeResponseHandler);
387
- }
388
- } else {
389
Stripe.createToken(args, stripeResponseHandler);
390
}
391
-
392
- // prevent the form from submitting with the default action
393
- return false;
394
} else {
395
- this.submit();
396
- return true; //not using Stripe anymore
397
}
398
- });
399
});
400
401
- function stripeResponseHandler(status, response) {
402
- if (response.error) {
403
- // re-enable the submit button
404
- jQuery('.pmpro_btn-submit-checkout,.pmpro_btn-submit').removeAttr("disabled");
405
406
- //hide processing message
407
- jQuery('#pmpro_processing_message').css('visibility', 'hidden');
408
409
- // show the errors on the form
410
- alert(response.error.message);
411
- jQuery(".payment-errors").text(response.error.message);
412
- } else {
413
- var form$ = jQuery("#pmpro_form, .pmpro_form");
414
- // token contains id, last4, and card type
415
- var token = response['id'];
416
- // insert the token into the form so it gets submitted to the server
417
- form$.append("<input type='hidden' name='stripeToken" + tokenNum + "' value='" + token + "'/>");
418
- tokenNum++;
419
-
420
- //console.log(response);
421
-
422
- //insert fields for other card fields
423
- if(jQuery('#CardType[name=CardType]').length)
424
- jQuery('#CardType').val(response['card']['brand']);
425
- else
426
- form$.append("<input type='hidden' name='CardType' value='" + response['card']['brand'] + "'/>");
427
- form$.append("<input type='hidden' name='AccountNumber' value='XXXXXXXXXXXX" + response['card']['last4'] + "'/>");
428
- form$.append("<input type='hidden' name='ExpirationMonth' value='" + ("0" + response['card']['exp_month']).slice(-2) + "'/>");
429
- form$.append("<input type='hidden' name='ExpirationYear' value='" + response['card']['exp_year'] + "'/>");
430
-
431
- // and submit
432
- form$.get(0).submit();
433
- }
434
- }
435
- -->
436
- </script>
437
- <?php
438
- }
439
- add_action("wp_head", "pmpro_stripe_javascript");
440
- }
441
442
- if ( ! function_exists( 'pmpro_stripe_dont_require_CVV' ) ) {
443
- //don't require the CVV
444
- function pmpro_stripe_dont_require_CVV($fields)
445
- {
446
- unset($fields['CVV']);
447
- return $fields;
448
}
449
- add_filter("pmpro_required_billing_fields", "pmpro_stripe_dont_require_CVV");
450
}
451
}
452
}
453
454
- /**
455
- * Don't require the CVV.
456
- * Don't require address fields if they are set to hide.
457
- */
458
- static function pmpro_required_billing_fields($fields)
459
- {
460
- global $pmpro_stripe_lite, $current_user, $bemail, $bconfirmemail;
461
-
462
- //CVV is not required if set that way at Stripe. The Stripe JS will require it if it is required.
463
- unset($fields['CVV']);
464
-
465
- //if using stripe lite, remove some fields from the required array
466
- if ($pmpro_stripe_lite) {
467
- //some fields to remove
468
- $remove = array('bfirstname', 'blastname', 'baddress1', 'bcity', 'bstate', 'bzipcode', 'bphone', 'bcountry', 'CardType');
469
- //if a user is logged in, don't require bemail either
470
- if (!empty($current_user->user_email)) {
471
- $remove[] = 'bemail';
472
- $bemail = $current_user->user_email;
473
- $bconfirmemail = $bemail;
474
- }
475
- //remove the fields
476
- foreach ($remove as $field)
477
- unset($fields[$field]);
478
}
479
-
480
- return $fields;
481
}
482
483
- /**
484
- * Filtering orders at checkout.
485
- *
486
- * @since 1.8
487
- */
488
- static function pmpro_checkout_order($morder)
489
{
490
- //load up token values
491
- if(isset($_REQUEST['stripeToken0']))
492
- {
493
- // find the highest one still around, and use it - then remove it from $_REQUEST.
494
- $thetoken = "";
495
- $tokennum = -1;
496
- foreach($_REQUEST as $key => $param) {
497
- if(preg_match('/stripeToken(\d+)/', $key, $matches)) {
498
- if(intval($matches[1])>$tokennum) {
499
- $thetoken = sanitize_text_field($param);
500
- $tokennum = intval($matches[1]);
501
- }
502
}
503
}
504
- $morder->stripeToken = $thetoken;
505
- unset($_REQUEST['stripeToken'.$tokennum]);
506
}
507
508
- //stripe lite code to get name from other sources if available
509
- global $pmpro_stripe_lite, $current_user;
510
- if(!empty($pmpro_stripe_lite) && empty($morder->FirstName) && empty($morder->LastName))
511
- {
512
- if(!empty($current_user->ID))
513
- {
514
- $morder->FirstName = get_user_meta($current_user->ID, "first_name", true);
515
- $morder->LastName = get_user_meta($current_user->ID, "last_name", true);
516
- }
517
- elseif(!empty($_REQUEST['first_name']) && !empty($_REQUEST['last_name']))
518
- {
519
- $morder->FirstName = sanitize_text_field($_REQUEST['first_name']);
520
- $morder->LastName = sanitize_text_field($_REQUEST['last_name']);
521
- }
522
}
523
-
524
- return $morder;
525
}
526
527
- /**
528
- * Code to run after checkout
529
- *
530
- * @since 1.8
531
- */
532
- static function pmpro_after_checkout($user_id, $morder)
533
- {
534
- global $gateway;
535
536
- if($gateway == "stripe")
537
- {
538
- if(self::$is_loaded && !empty($morder) && !empty($morder->Gateway) && !empty($morder->Gateway->customer) && !empty($morder->Gateway->customer->id))
539
- {
540
- update_user_meta($user_id, "pmpro_stripe_customerid", $morder->Gateway->customer->id);
541
- }
542
}
543
}
544
545
- /**
546
- * Check settings if billing address should be shown.
547
- * @since 1.8
548
- */
549
- static function pmpro_include_billing_address_fields($include)
550
- {
551
- //check settings RE showing billing address
552
- if(!pmpro_getOption("stripe_billingaddress"))
553
- $include = false;
554
555
- return $include;
556
- }
557
558
- /**
559
- * Use our own payment fields at checkout. (Remove the name attributes.)
560
- * @since 1.8
561
- */
562
- static function pmpro_include_payment_information_fields($include)
563
- {
564
- //global vars
565
- global $pmpro_requirebilling, $pmpro_show_discount_code, $discount_code, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
566
567
- //get accepted credit cards
568
- $pmpro_accepted_credit_cards = pmpro_getOption("accepted_credit_cards");
569
- $pmpro_accepted_credit_cards = explode(",", $pmpro_accepted_credit_cards);
570
- $pmpro_accepted_credit_cards_string = pmpro_implodeToEnglish($pmpro_accepted_credit_cards);
571
572
- //include ours
573
- ?>
574
- <div id="pmpro_payment_information_fields" class="pmpro_checkout" <?php if(!$pmpro_requirebilling || apply_filters("pmpro_hide_payment_information_fields", false) ) { ?>style="display: none;"<?php } ?>>
575
- <h3>
576
- <span class="pmpro_checkout-h3-name"><?php _e('Payment Information', 'paid-memberships-pro' );?></span>
577
- <span class="pmpro_checkout-h3-msg"><?php printf(__('We Accept %s', 'paid-memberships-pro' ), $pmpro_accepted_credit_cards_string);?></span>
578
- </h3>
579
- <?php $sslseal = pmpro_getOption("sslseal"); ?>
580
- <?php if(!empty($sslseal)) { ?>
581
- <div class="pmpro_checkout-fields-display-seal">
582
<?php } ?>
583
- <div class="pmpro_checkout-fields<?php if(!empty($sslseal)) { ?> pmpro_checkout-fields-leftcol<?php } ?>">
584
<?php
585
- $pmpro_include_cardtype_field = apply_filters('pmpro_include_cardtype_field', false);
586
- if($pmpro_include_cardtype_field) { ?>
587
- <div class="pmpro_checkout-field pmpro_payment-card-type">
588
- <label for="CardType"><?php _e('Card Type', 'paid-memberships-pro' );?></label>
589
- <select id="CardType" class=" <?php echo pmpro_getClassForField("CardType");?>">
590
- <?php foreach($pmpro_accepted_credit_cards as $cc) { ?>
591
- <option value="<?php echo $cc?>" <?php if($CardType == $cc) { ?>selected="selected"<?php } ?>><?php echo $cc?></option>
592
- <?php } ?>
593
- </select>
594
- </div>
595
- <?php } else { ?>
596
- <input type="hidden" id="CardType" name="CardType" value="<?php echo esc_attr($CardType);?>" />
597
- <script>
598
- <!--
599
- jQuery(document).ready(function() {
600
- jQuery('#AccountNumber').validateCreditCard(function(result) {
601
- var cardtypenames = {
602
- "amex":"American Express",
603
- "diners_club_carte_blanche":"Diners Club Carte Blanche",
604
- "diners_club_international":"Diners Club International",
605
- "discover":"Discover",
606
- "jcb":"JCB",
607
- "laser":"Laser",
608
- "maestro":"Maestro",
609
- "mastercard":"Mastercard",
610
- "visa":"Visa",
611
- "visa_electron":"Visa Electron"
612
- }
613
-
614
- if(result.card_type)
615
- jQuery('#CardType').val(cardtypenames[result.card_type.name]);
616
- else
617
- jQuery('#CardType').val('Unknown Card Type');
618
- });
619
- });
620
- -->
621
- </script>
622
- <?php } ?>
623
- <div class="pmpro_checkout-field pmpro_payment-account-number">
624
- <label for="AccountNumber"><?php _e('Card Number', 'paid-memberships-pro' );?></label>
625
- <input id="AccountNumber" class="input <?php echo pmpro_getClassForField("AccountNumber");?>" type="text" size="25" value="<?php echo esc_attr($AccountNumber)?>" autocomplete="off" />
626
</div>
627
- <div class="pmpro_checkout-field pmpro_payment-expiration">
628
- <label for="ExpirationMonth"><?php _e('Expiration Date', 'paid-memberships-pro' );?></label>
629
- <select id="ExpirationMonth" class=" <?php echo pmpro_getClassForField("ExpirationMonth");?>">
630
- <option value="01" <?php if($ExpirationMonth == "01") { ?>selected="selected"<?php } ?>>01</option>
631
- <option value="02" <?php if($ExpirationMonth == "02") { ?>selected="selected"<?php } ?>>02</option>
632
- <option value="03" <?php if($ExpirationMonth == "03") { ?>selected="selected"<?php } ?>>03</option>
633
- <option value="04" <?php if($ExpirationMonth == "04") { ?>selected="selected"<?php } ?>>04</option>
634
- <option value="05" <?php if($ExpirationMonth == "05") { ?>selected="selected"<?php } ?>>05</option>
635
- <option value="06" <?php if($ExpirationMonth == "06") { ?>selected="selected"<?php } ?>>06</option>
636
- <option value="07" <?php if($ExpirationMonth == "07") { ?>selected="selected"<?php } ?>>07</option>
637
- <option value="08" <?php if($ExpirationMonth == "08") { ?>selected="selected"<?php } ?>>08</option>
638
- <option value="09" <?php if($ExpirationMonth == "09") { ?>selected="selected"<?php } ?>>09</option>
639
- <option value="10" <?php if($ExpirationMonth == "10") { ?>selected="selected"<?php } ?>>10</option>
640
- <option value="11" <?php if($ExpirationMonth == "11") { ?>selected="selected"<?php } ?>>11</option>
641
- <option value="12" <?php if($ExpirationMonth == "12") { ?>selected="selected"<?php } ?>>12</option>
642
- </select>/<select id="ExpirationYear" class=" <?php echo pmpro_getClassForField("ExpirationYear");?>">
643
- <?php
644
- for($i = date_i18n("Y"); $i < date_i18n("Y") + 10; $i++) { ?>
645
- <option value="<?php echo $i?>" <?php if($ExpirationYear == $i) { ?>selected="selected"<?php } ?>><?php echo $i?></option>
646
- <?php } ?>
647
- </select>
648
</div>
649
- <?php
650
- $pmpro_show_cvv = apply_filters("pmpro_show_cvv", true);
651
- if($pmpro_show_cvv) { ?>
652
- <div class="pmpro_checkout-field pmpro_payment-cvv">
653
- <label for="CVV"><?php _e('Security Code (CVC)', 'paid-memberships-pro' );?></label>
654
- <input id="CVV" type="text" size="4" value="<?php if(!empty($_REQUEST['CVV'])) { echo esc_attr(sanitize_text_field($_REQUEST['CVV'])); }?>" class="input <?php echo pmpro_getClassForField("CVV");?>" /> <small>(<a href="javascript:void(0);" onclick="javascript:window.open('<?php echo pmpro_https_filter(PMPRO_URL)?>/pages/popup-cvv.html','cvv','toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=600, height=475');"><?php _e("what's this?", 'paid-memberships-pro' );?></a>)</small>
655
- </div>
656
- <?php } ?>
657
- <?php if($pmpro_show_discount_code) { ?>
658
- <div class="pmpro_checkout-field pmpro_payment-discount-code">
659
- <label for="discount_code"><?php _e('Discount Code', 'paid-memberships-pro' );?></label>
660
- <input class="input <?php echo pmpro_getClassForField("discount_code");?>" id="discount_code" name="discount_code" type="text" size="10" value="<?php echo esc_attr($discount_code)?>" />
661
- <input type="button" id="discount_code_button" name="discount_code_button" value="<?php _e('Apply', 'paid-memberships-pro' );?>" />
662
- <p id="discount_code_message" class="pmpro_message" style="display: none;"></p>
663
- </div>
664
- <?php } ?>
665
- </div> <!-- end pmpro_checkout-fields -->
666
- <?php if(!empty($sslseal)) { ?>
667
- <div class="pmpro_checkout-fields-rightcol pmpro_sslseal"><?php echo stripslashes($sslseal); ?></div>
668
- </div> <!-- end pmpro_checkout-fields-display-seal -->
669
<?php } ?>
670
- </div> <!-- end pmpro_payment_information_fields -->
671
- <?php
672
673
- //don't include the default
674
- return false;
675
- }
676
677
- /**
678
- * Fields shown on edit user page
679
- *
680
- * @since 1.8
681
- */
682
- static function user_profile_fields($user)
683
- {
684
- global $wpdb, $current_user, $pmpro_currency_symbol;
685
686
- $cycles = array( __('Day(s)', 'paid-memberships-pro' ) => 'Day', __('Week(s)', 'paid-memberships-pro' ) => 'Week', __('Month(s)', 'paid-memberships-pro' ) => 'Month', __('Year(s)', 'paid-memberships-pro' ) => 'Year' );
687
- $current_year = date_i18n("Y");
688
- $current_month = date_i18n("m");
689
690
- //make sure the current user has privileges
691
- $membership_level_capability = apply_filters("pmpro_edit_member_capability", "manage_options");
692
- if(!current_user_can($membership_level_capability))
693
- return false;
694
695
- //more privelges they should have
696
- $show_membership_level = apply_filters("pmpro_profile_show_membership_level", true, $user);
697
- if(!$show_membership_level)
698
- return false;
699
700
- //check that user has a current subscription at Stripe
701
- $last_order = new MemberOrder();
702
- $last_order->getLastMemberOrder($user->ID);
703
704
- //assume no sub to start
705
- $sub = false;
706
707
- //check that gateway is Stripe
708
- if($last_order->gateway == "stripe" && self::$is_loaded )
709
- {
710
- //is there a customer?
711
- $sub = $last_order->Gateway->getSubscription($last_order);
712
- }
713
714
- $customer_id = $user->pmpro_stripe_customerid;
715
716
- if(empty($sub))
717
- {
718
- //make sure we delete stripe updates
719
- update_user_meta($user->ID, "pmpro_stripe_updates", array());
720
721
- //if the last order has a sub id, let the admin know there is no sub at Stripe
722
- if(!empty($last_order) && $last_order->gateway == "stripe" && !empty($last_order->subscription_transaction_id) && strpos($last_order->subscription_transaction_id, "sub_") !== false)
723
- {
724
- ?>
725
- <p><?php printf( __('%1$sNote:%2$s Subscription %3$s%4$s%5$s could not be found at Stripe. It may have been deleted.', 'paid-memberships-pro'), '<strong>', '</strong>', '<strong>', esc_attr($last_order->subscription_transaction_id), '</strong>' ); ?></p>
726
- <?php
727
- }
728
- }
729
- elseif ( true === self::$is_loaded )
730
{
731
?>
732
- <h3><?php _e("Subscription Updates", 'paid-memberships-pro' ); ?></h3>
733
- <p>
734
- <?php
735
- if(empty($_REQUEST['user_id']))
736
- _e("Subscription updates, allow you to change the member's subscription values at predefined times. Be sure to click Update Profile after making changes.", 'paid-memberships-pro' );
737
- else
738
- _e("Subscription updates, allow you to change the member's subscription values at predefined times. Be sure to click Update User after making changes.", 'paid-memberships-pro' );
739
- ?>
740
- </p>
741
- <table class="form-table">
742
- <tr>
743
- <th><label for="membership_level"><?php _e("Update", 'paid-memberships-pro' ); ?></label></th>
744
- <td id="updates_td">
745
- <?php
746
- $old_updates = $user->pmpro_stripe_updates;
747
- if(is_array($old_updates))
748
- {
749
- $updates = array_merge(
750
- array(array('template'=>true, 'when'=>'now', 'date_month'=>'', 'date_day'=>'', 'date_year'=>'', 'billing_amount'=>'', 'cycle_number'=>'', 'cycle_period'=>'Month')),
751
- $old_updates
752
- );
753
- }
754
- else
755
- $updates = array(array('template'=>true, 'when'=>'now', 'date_month'=>'', 'date_day'=>'', 'date_year'=>'', 'billing_amount'=>'', 'cycle_number'=>'', 'cycle_period'=>'Month'));
756
-
757
- foreach($updates as $update)
758
- {
759
- ?>
760
- <div class="updates_update" <?php if(!empty($update['template'])) { ?>style="display: none;"<?php } ?>>
761
- <select class="updates_when" name="updates_when[]">
762
- <option value="now" <?php selected($update['when'], "now");?>>Now</option>
763
- <option value="payment" <?php selected($update['when'], "payment");?>>After Next Payment</option>
764
- <option value="date" <?php selected($update['when'], "date");?>>On Date</option>
765
- </select>
766
- <span class="updates_date" <?php if($update['when'] != "date") { ?>style="display: none;"<?php } ?>>
767
- <select name="updates_date_month[]">
768
- <?php
769
- for($i = 1; $i < 13; $i++)
770
- {
771
- ?>
772
- <option value="<?php echo str_pad($i, 2, "0", STR_PAD_LEFT);?>" <?php if(!empty($update['date_month']) && $update['date_month'] == $i) { ?>selected="selected"<?php } ?>>
773
- <?php echo date_i18n("M", strtotime($i . "/1/" . $current_year));?>
774
- </option>
775
- <?php
776
- }
777
?>
778
- </select>
779
- <input name="updates_date_day[]" type="text" size="2" value="<?php if(!empty($update['date_day'])) echo esc_attr($update['date_day']);?>" />
780
- <input name="updates_date_year[]" type="text" size="4" value="<?php if(!empty($update['date_year'])) echo esc_attr($update['date_year']);?>" />
781
- </span>
782
- <span class="updates_billing" <?php if($update['when'] == "now") { ?>style="display: none;"<?php } ?>>
783
- <?php echo $pmpro_currency_symbol?><input name="updates_billing_amount[]" type="text" size="10" value="<?php echo esc_attr($update['billing_amount']);?>" />
784
- <small><?php _e('per', 'paid-memberships-pro' );?></small>
785
- <input name="updates_cycle_number[]" type="text" size="5" value="<?php echo esc_attr($update['cycle_number']);?>" />
786
- <select name="updates_cycle_period[]">
787
- <?php
788
- foreach ( $cycles as $name => $value ) {
789
- echo "<option value='$value'";
790
- if(!empty($update['cycle_period']) && $update['cycle_period'] == $value) echo " selected='selected'";
791
- echo ">$name</option>";
792
}
793
- ?>
794
- </select>
795
- </span>
796
- <span>
797
- <a class="updates_remove" href="javascript:void(0);">Remove</a>
798
- </span>
799
- </div>
800
- <?php
801
- }
802
- ?>
803
- <p><a id="updates_new_update" href="javascript:void(0);">+ New Update</a></p>
804
- </td>
805
- </tr>
806
- </table>
807
- <script>
808
- <!--
809
- jQuery(document).ready(function() {
810
- //function to update dropdowns/etc based on when field
811
- function updateSubscriptionUpdateFields(when)
812
- {
813
- if(jQuery(when).val() == 'date')
814
- jQuery(when).parent().children('.updates_date').show();
815
- else
816
- jQuery(when).parent().children('.updates_date').hide();
817
818
- if(jQuery(when).val() == 'no')
819
- jQuery(when).parent().children('.updates_billing').hide();
820
- else
821
- jQuery(when).parent().children('.updates_billing').show();
822
- }
823
824
- //and update on page load
825
- jQuery('.updates_when').each(function() { if(jQuery(this).parent().css('display') != 'none') updateSubscriptionUpdateFields(this); });
826
827
- //add a new update when clicking to
828
- var num_updates_divs = <?php echo count($updates);?>;
829
- jQuery('#updates_new_update').click(function() {
830
- //get updates
831
- updates = jQuery('.updates_update').toArray();
832
833
- //clone the first one
834
- new_div = jQuery(updates[0]).clone();
835
836
- //append
837
- new_div.insertBefore('#updates_new_update');
838
839
- //update events
840
- addUpdateEvents()
841
842
- //unhide it
843
- new_div.show();
844
- updateSubscriptionUpdateFields(new_div.children('.updates_when'));
845
- });
846
847
- function addUpdateEvents()
848
- {
849
- //update when when changes
850
- jQuery('.updates_when').change(function() {
851
- updateSubscriptionUpdateFields(this);
852
- });
853
854
- //remove updates when clicking
855
- jQuery('.updates_remove').click(function() {
856
- jQuery(this).parent().parent().remove();
857
- });
858
- }
859
- addUpdateEvents();
860
- });
861
- -->
862
- </script>
863
- <?php
864
- }
865
}
866
867
- /**
868
- * Process fields from the edit user page
869
- *
870
- * @since 1.8
871
- */
872
- static function user_profile_fields_save($user_id)
873
- {
874
- global $wpdb;
875
-
876
- //check capabilities
877
- $membership_level_capability = apply_filters("pmpro_edit_member_capability", "manage_options");
878
- if(!current_user_can($membership_level_capability))
879
- return false;
880
881
- //make sure some value was passed
882
- if(!isset($_POST['updates_when']) || !is_array($_POST['updates_when']))
883
- return;
884
885
- //vars
886
- $updates = array();
887
- $next_on_date_update = "";
888
889
- //build array of updates (we skip the first because it's the template field for the JavaScript
890
- for($i = 1; $i < count($_POST['updates_when']); $i++)
891
- {
892
- $update = array();
893
894
- //all updates have these values
895
- $update['when'] = pmpro_sanitize_with_safelist($_POST['updates_when'][$i], array('now', 'payment', 'date'));
896
- $update['billing_amount'] = sanitize_text_field($_POST['updates_billing_amount'][$i]);
897
- $update['cycle_number'] = intval($_POST['updates_cycle_number'][$i]);
898
- $update['cycle_period'] = sanitize_text_field($_POST['updates_cycle_period'][$i]);
899
900
- //these values only for on date updates
901
- if($_POST['updates_when'][$i] == "date")
902
- {
903
- $update['date_month'] = str_pad(intval($_POST['updates_date_month'][$i]), 2, "0", STR_PAD_LEFT);
904
- $update['date_day'] = str_pad(intval($_POST['updates_date_day'][$i]), 2, "0", STR_PAD_LEFT);
905
- $update['date_year'] = intval($_POST['updates_date_year'][$i]);
906
- }
907
908
- //make sure the update is valid
909
- if(empty($update['cycle_number']))
910
- continue;
911
912
- //if when is now, update the subscription
913
- if($update['when'] == "now")
914
- {
915
- PMProGateway_stripe::updateSubscription($update, $user_id);
916
917
- continue;
918
- }
919
- elseif($update['when'] == 'date')
920
- {
921
- if(!empty($next_on_date_update))
922
- $next_on_date_update = min($next_on_date_update, $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day']);
923
- else
924
- $next_on_date_update = $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'];
925
- }
926
927
- //add to array
928
- $updates[] = $update;
929
}
930
931
- //save in user meta
932
- update_user_meta($user_id, "pmpro_stripe_updates", $updates);
933
-
934
- //save date of next on-date update to make it easier to query for these in cron job
935
- update_user_meta($user_id, "pmpro_stripe_next_on_date_update", $next_on_date_update);
936
}
937
938
- /**
939
- * Cron activation for subscription updates.
940
- *
941
- * @since 1.8
942
- */
943
- static function pmpro_activation()
944
- {
945
- pmpro_maybe_schedule_event(time(), 'daily', 'pmpro_cron_stripe_subscription_updates');
946
- }
947
948
- /**
949
- * Cron deactivation for subscription updates.
950
- *
951
- * @since 1.8
952
- */
953
- static function pmpro_deactivation()
954
- {
955
- wp_clear_scheduled_hook('pmpro_cron_stripe_subscription_updates');
956
- }
957
958
- /**
959
- * Cron job for subscription updates.
960
- *
961
- * @since 1.8
962
- */
963
- static function pmpro_cron_stripe_subscription_updates()
964
- {
965
- global $wpdb;
966
-
967
- //get all updates for today (or before today)
968
- $sqlQuery = "SELECT *
969
- FROM $wpdb->usermeta
970
- WHERE meta_key = 'pmpro_stripe_next_on_date_update'
971
- AND meta_value IS NOT NULL
972
- AND meta_value <> ''
973
- AND meta_value < '" . date_i18n("Y-m-d", strtotime("+1 day", current_time('timestamp'))) . "'";
974
- $updates = $wpdb->get_results($sqlQuery);
975
-
976
- if(!empty($updates))
977
- {
978
- //loop through
979
- foreach($updates as $update)
980
- {
981
- //pull values from update
982
- $user_id = $update->user_id;
983
984
- $user = get_userdata($user_id);
985
986
- //if user is missing, delete the update info and continue
987
- if(empty($user) || empty($user->ID))
988
- {
989
- delete_user_meta($user_id, "pmpro_stripe_updates");
990
- delete_user_meta($user_id, "pmpro_stripe_next_on_date_update");
991
992
- continue;
993
- }
994
995
- $user_updates = $user->pmpro_stripe_updates;
996
- $next_on_date_update = "";
997
998
- //loop through updates looking for updates happening today or earlier
999
- if(!empty($user_updates))
1000
- {
1001
- foreach($user_updates as $key => $ud)
1002
- {
1003
- if($ud['when'] == 'date' &&
1004
- $ud['date_year'] . "-" . $ud['date_month'] . "-" . $ud['date_day'] <= date_i18n("Y-m-d", current_time('timestamp') )
1005
- )
1006
- {
1007
- PMProGateway_stripe::updateSubscription($ud, $user_id);
1008
-
1009
- //remove update from list
1010
- unset($user_updates[$key]);
1011
- }
1012
- elseif($ud['when'] == 'date')
1013
- {
1014
- //this is an on date update for the future, update the next on date update
1015
- if(!empty($next_on_date_update))
1016
- $next_on_date_update = min($next_on_date_update, $ud['date_year'] . "-" . $ud['date_month'] . "-" . $ud['date_day']);
1017
- else
1018
- $next_on_date_update = $ud['date_year'] . "-" . $ud['date_month'] . "-" . $ud['date_day'];
1019
- }
1020
}
1021
}
1022
1023
- //save updates in case we removed some
1024
- update_user_meta($user_id, "pmpro_stripe_updates", $user_updates);
1025
1026
- //save date of next on-date update to make it easier to query for these in cron job
1027
- update_user_meta($user_id, "pmpro_stripe_next_on_date_update", $next_on_date_update);
1028
- }
1029
}
1030
}
1031
1032
/**
1033
- * Before processing a checkout, check for pending invoices we want to clean up.
1034
- * This prevents double billing issues in cases where Stripe has pending invoices
1035
- * because of an expired credit card/etc and a user checks out to renew their subscription
1036
- * instead of updating their billing information via the billing info page.
1037
*/
1038
- static function pmpro_checkout_before_processing() {
1039
- global $wpdb, $current_user;
1040
-
1041
- // we're only worried about cases where the user is logged in
1042
- if( ! is_user_logged_in() ) {
1043
- return;
1044
- }
1045
-
1046
- // make sure we're checking out with Stripe
1047
- $current_gateway = pmpro_getGateway();
1048
- if ( $current_gateway != 'stripe' ) {
1049
- return;
1050
- }
1051
-
1052
- //check the $pmpro_cancel_previous_subscriptions filter
1053
- //this is used in add ons like Gift Memberships to stop PMPro from cancelling old memberships
1054
- $pmpro_cancel_previous_subscriptions = true;
1055
- $pmpro_cancel_previous_subscriptions = apply_filters( 'pmpro_cancel_previous_subscriptions', $pmpro_cancel_previous_subscriptions );
1056
- if( ! $pmpro_cancel_previous_subscriptions ) {
1057
- return;
1058
- }
1059
-
1060
- //get user and membership level
1061
- $membership_level = pmpro_getMembershipLevelForUser($current_user->ID);
1062
-
1063
- //no level, then probably no subscription at Stripe anymore
1064
- if(empty($membership_level))
1065
- return;
1066
1067
- /**
1068
- * Filter which levels to cancel at the gateway.
1069
- * MMPU will set this to all levels that are going to be cancelled during this checkout.
1070
- * Others may want to display this by add_filter('pmpro_stripe_levels_to_cancel_before_checkout', __return_false);
1071
- */
1072
- $levels_to_cancel = apply_filters('pmpro_stripe_levels_to_cancel_before_checkout', array($membership_level->id), $current_user);
1073
-
1074
- foreach($levels_to_cancel as $level_to_cancel) {
1075
- //get the last order for this user/level
1076
- $last_order = new MemberOrder();
1077
- $last_order->getLastMemberOrder($current_user->ID, 'success', $level_to_cancel, 'stripe');
1078
-
1079
- //so let's cancel the user's susbcription
1080
- if(!empty($last_order) && !empty($last_order->subscription_transaction_id)) {
1081
- $subscription = $last_order->Gateway->getSubscription($last_order);
1082
- if(!empty($subscription)) {
1083
- $last_order->Gateway->cancelSubscriptionAtGateway($subscription, true);
1084
-
1085
- //Stripe was probably going to cancel this subscription 7 days past the payment failure (maybe just one hour, use a filter for sure)
1086
- $memberships_users_row = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $current_user->ID . "' AND membership_id = '" . $level_to_cancel . "' AND status = 'active' LIMIT 1");
1087
-
1088
- if(!empty($memberships_users_row) && (empty($memberships_users_row->enddate) || $memberships_users_row->enddate == '0000-00-00 00:00:00')) {
1089
- /**
1090
- * Filter graced period days when canceling existing subscriptions at checkout.
1091
- *
1092
- * @since 1.9.4
1093
- *
1094
- * @param int $days Grace period defaults to 3 days
1095
- * @param object $membership Membership row from pmpro_memberships_users including membership_id, user_id, and enddate
1096
- */
1097
- $days_grace = apply_filters('pmpro_stripe_days_grace_when_canceling_existing_subscriptions_at_checkout', 3, $memberships_users_row);
1098
- $new_enddate = date('Y-m-d H:i:s', current_time('timestamp')+3600*24*$days_grace);
1099
- $wpdb->update( $wpdb->pmpro_memberships_users, array('enddate'=>$new_enddate), array('user_id'=>$current_user->ID, 'membership_id'=>$level_to_cancel, 'status'=>'active'), array('%s'), array('%d', '%d', '%s') );
1100
- }
1101
}
1102
}
1103
}
1104
}
1105
1106
- /**
1107
- * Process checkout and decide if a charge and or subscribe is needed
1108
- *
1109
- * @since 1.4
1110
- */
1111
- function process(&$order)
1112
- {
1113
- //check for initial payment
1114
- if(floatval($order->InitialPayment) == 0)
1115
- {
1116
- //just subscribe
1117
- return $this->subscribe($order);
1118
- }
1119
- else
1120
- {
1121
- //charge then subscribe
1122
- if($this->charge($order))
1123
- {
1124
- if(pmpro_isLevelRecurring($order->membership_level))
1125
- {
1126
- if($this->subscribe($order))
1127
- {
1128
- //yay!
1129
- return true;
1130
- }
1131
- else
1132
- {
1133
- //try to refund initial charge
1134
- return false;
1135
- }
1136
- }
1137
- else
1138
- {
1139
- //only a one time charge
1140
- $order->status = "success"; //saved on checkout page
1141
return true;
1142
}
1143
}
1144
- else
1145
- {
1146
- if(empty($order->error)) {
1147
- if ( ! self::$is_loaded ) {
1148
1149
- $order->error = __( "Payment error: Please contact the webmaster (stripe-load-error)", 'paid-memberships-pro' );
1150
1151
- } else {
1152
1153
- $order->error = __( "Unknown error: Initial payment failed.", 'paid-memberships-pro' );
1154
- }
1155
}
1156
-
1157
- return false;
1158
}
1159
}
1160
}
1161
1162
- /**
1163
- * Make a one-time charge with Stripe
1164
- *
1165
- * @since 1.4
1166
- */
1167
- function charge(&$order)
1168
- {
1169
- global $pmpro_currency, $pmpro_currencies;
1170
- $currency_unit_multiplier = 100; //ie 100 cents per USD
1171
-
1172
- //account for zero-decimal currencies like the Japanese Yen
1173
- if(is_array($pmpro_currencies[$pmpro_currency]) && isset($pmpro_currencies[$pmpro_currency]['decimals']) && $pmpro_currencies[$pmpro_currency]['decimals'] == 0)
1174
- $currency_unit_multiplier = 1;
1175
1176
- //create a code for the order
1177
- if(empty($order->code))
1178
- $order->code = $order->getRandomCode();
1179
1180
- //what amount to charge?
1181
- $amount = $order->InitialPayment;
1182
1183
- //tax
1184
- $order->subtotal = $amount;
1185
- $tax = $order->getTax(true);
1186
- $amount = pmpro_round_price((float)$order->subtotal + (float)$tax);
1187
1188
- //create a customer
1189
- $result = $this->getCustomer($order);
1190
1191
- if(empty($result))
1192
- {
1193
- //failed to create customer
1194
- return false;
1195
- }
1196
1197
- //charge
1198
- try
1199
- {
1200
- $response = Stripe_Charge::create(array(
1201
- "amount" => $amount * $currency_unit_multiplier, # amount in cents, again
1202
- "currency" => strtolower($pmpro_currency),
1203
- "customer" => $this->customer->id,
1204
- "description" => apply_filters('pmpro_stripe_order_description', "Order #" . $order->code . ", " . trim($order->FirstName . " " . $order->LastName) . " (" . $order->Email . ")", $order)
1205
- )
1206
- );
1207
- }
1208
- catch (Exception $e)
1209
- {
1210
- //$order->status = "error";
1211
- $order->errorcode = true;
1212
- $order->error = "Error: " . $e->getMessage();
1213
- $order->shorterror = $order->error;
1214
- return false;
1215
- }
1216
-
1217
- if(empty($response["failure_message"]))
1218
- {
1219
- //successful charge
1220
- $order->payment_transaction_id = $response["id"];
1221
- $order->updateStatus("success");
1222
- $order->saveOrder();
1223
- return true;
1224
- }
1225
- else
1226
- {
1227
- //$order->status = "error";
1228
- $order->errorcode = true;
1229
- $order->error = $response['failure_message'];
1230
- $order->shorterror = $response['failure_message'];
1231
- return false;
1232
- }
1233
}
1234
1235
- /**
1236
- * Get a Stripe customer object.
1237
- *
1238
- * If $this->customer is set, it returns it.
1239
- * It first checks if the order has a subscription_transaction_id. If so, that's the customer id.
1240
- * If not, it checks for a user_id on the order and searches for a customer id in the user meta.
1241
- * If a customer id is found, it checks for a customer through the Stripe API.
1242
- * If a customer is found and there is a stripeToken on the order passed, it will update the customer.
1243
- * If no customer is found and there is a stripeToken on the order passed, it will create a customer.
1244
- *
1245
- * @since 1.4
1246
- * @return Stripe_Customer|false
1247
- */
1248
- function getCustomer(&$order = false, $force = false)
1249
- {
1250
- global $current_user;
1251
-
1252
- //already have it?
1253
- if(!empty($this->customer) && !$force)
1254
- return $this->customer;
1255
-
1256
- //figure out user_id and user
1257
- if(!empty($order->user_id))
1258
- $user_id = $order->user_id;
1259
1260
- //if no id passed, check the current user
1261
- if(empty($user_id) && !empty($current_user->ID))
1262
- $user_id = $current_user->ID;
1263
1264
- if(!empty($user_id))
1265
- $user = get_userdata($user_id);
1266
- else
1267
- $user = NULL;
1268
1269
- //transaction id?
1270
- if(!empty($order->subscription_transaction_id) && strpos($order->subscription_transaction_id, "cus_") !== false)
1271
- $customer_id = $order->subscription_transaction_id;
1272
- else
1273
- {
1274
- //try based on user id
1275
- if(!empty($user_id))
1276
- {
1277
- $customer_id = get_user_meta($user_id, "pmpro_stripe_customerid", true);
1278
}
1279
1280
- //look up by transaction id
1281
- if(empty($customer_id) && !empty($user_id))
1282
- {
1283
- //user id from this order or the user's last stripe order
1284
- if(!empty($order->payment_transaction_id))
1285
- $payment_transaction_id = $order->payment_transaction_id;
1286
- else
1287
- {
1288
- //find the user's last stripe order
1289
- $last_order = new MemberOrder();
1290
- $last_order->getLastMemberOrder($user_id, array('success', 'cancelled'), NULL, 'stripe', $order->Gateway->gateway_environment);
1291
- if(!empty($last_order->payment_transaction_id))
1292
- $payment_transaction_id = $last_order->payment_transaction_id;
1293
- }
1294
-
1295
- //we have a transaction id to look up
1296
- if(!empty($payment_transaction_id))
1297
- {
1298
- if(strpos($payment_transaction_id, "ch_") !== false)
1299
- {
1300
- //charge, look it up
1301
- try {
1302
- $charge = Stripe_Charge::retrieve($payment_transaction_id);
1303
- } catch( \Exception $exception ) {
1304
- $order->error = sprintf( __( 'Error: %s', 'paid-memberships-pro' ), $exception->getMessage() );
1305
- return false;
1306
- }
1307
-
1308
- if(!empty($charge) && !empty($charge->customer))
1309
- $customer_id = $charge->customer;
1310
}
1311
- else if(strpos($payment_transaction_id, "in_") !== false)
1312
- {
1313
- //invoice look it up
1314
- try {
1315
- $invoice = Stripe_Invoice::retrieve($payment_transaction_id);
1316
- } catch( \Exception $exception ) {
1317
- $order->error = sprintf( __( 'Error: %s', 'paid-memberships-pro' ), $exception->getMessage() );
1318
- return false;
1319
- }
1320
1321
- if(!empty($invoice) && !empty($invoice->customer))
1322
- $customer_id = $invoice->customer;
1323
}
1324
- }
1325
1326
- //if we found it, save to user meta for future reference
1327
- if(!empty($customer_id))
1328
- update_user_meta($user_id, "pmpro_stripe_customerid", $customer_id);
1329
}
1330
- }
1331
-
1332
- //get name and email values from order in case we update
1333
- if(!empty($order->FirstName) && !empty($order->LastName))
1334
- $name = trim($order->FirstName . " " . $order->LastName);
1335
- elseif(!empty($order->FirstName))
1336
- $name = $order->FirstName;
1337
- elseif(!empty($order->LastName))
1338
- $name = $order->LastName;
1339
1340
- if(empty($name) && !empty($user->ID))
1341
- {
1342
- $name = trim($user->first_name . " " . $user->last_name);
1343
-
1344
- //still empty?
1345
- if(empty($name))
1346
- $name = $user->user_login;
1347
- }
1348
- elseif(empty($name))
1349
- $name = "No Name";
1350
-
1351
- if(!empty($order->Email))
1352
- $email = $order->Email;
1353
- else
1354
- $email = "";
1355
- if(empty($email) && !empty($user->ID) && !empty($user->user_email))
1356
- {
1357
- $email = $user->user_email;
1358
}
1359
- elseif(empty($email))
1360
- $email = "No Email";
1361
-
1362
- //check for an existing stripe customer
1363
- if(!empty($customer_id))
1364
- {
1365
- try
1366
- {
1367
- $this->customer = Stripe_Customer::retrieve($customer_id);
1368
-
1369
- //update the customer description and card
1370
- if(!empty($order->stripeToken))
1371
- {
1372
- $this->customer->description = $name . " (" . $email . ")";
1373
- $this->customer->email = $email;
1374
- $this->customer->card = $order->stripeToken;
1375
- $this->customer->save();
1376
- }
1377
1378
- return $this->customer;
1379
- }
1380
- catch (Exception $e)
1381
- {
1382
- //assume no customer found
1383
}
1384
}
1385
1386
- //no customer id, create one
1387
- if(!empty($order->stripeToken))
1388
- {
1389
- try
1390
- {
1391
- $this->customer = Stripe_Customer::create(array(
1392
- "description" => $name . " (" . $email . ")",
1393
- "email" => $order->Email,
1394
- "card" => $order->stripeToken
1395
- ));
1396
- }
1397
- catch (Exception $e)
1398
- {
1399
- $order->error = __("Error creating customer record with Stripe:", 'paid-memberships-pro' ) . " " . $e->getMessage();
1400
- $order->shorterror = $order->error;
1401
- return false;
1402
- }
1403
1404
- if(!empty($user_id))
1405
- {
1406
- //user logged in/etc
1407
- update_user_meta($user_id, "pmpro_stripe_customerid", $this->customer->id);
1408
- }
1409
- else
1410
- {
1411
- //user not registered yet, queue it up
1412
- global $pmpro_stripe_customer_id;
1413
- $pmpro_stripe_customer_id = $this->customer->id;
1414
- if(! function_exists('pmpro_user_register_stripe_customerid')) {
1415
- function pmpro_user_register_stripe_customerid($user_id)
1416
- {
1417
- global $pmpro_stripe_customer_id;
1418
- update_user_meta($user_id, "pmpro_stripe_customerid", $pmpro_stripe_customer_id);
1419
- }
1420
- add_action("user_register", "pmpro_user_register_stripe_customerid");
1421
}
1422
}
1423
-
1424
- return apply_filters('pmpro_stripe_create_customer', $this->customer);
1425
}
1426
1427
- return false;
1428
}
1429
1430
- /**
1431
- * Get a Stripe subscription from a PMPro order
1432
- *
1433
- * @since 1.8
1434
- */
1435
- function getSubscription(&$order)
1436
- {
1437
- global $wpdb;
1438
1439
- //no order?
1440
- if(empty($order) || empty($order->code))
1441
- return false;
1442
1443
- $result = $this->getCustomer($order, true); //force so we don't get a cached sub for someone else
1444
1445
- //no customer?
1446
- if(empty($result))
1447
- return false;
1448
1449
- //is there a subscription transaction id pointing to a sub?
1450
- if(!empty($order->subscription_transaction_id) && strpos($order->subscription_transaction_id, "sub_") !== false)
1451
- {
1452
- try
1453
- {
1454
- $sub = $this->customer->subscriptions->retrieve($order->subscription_transaction_id);
1455
- }
1456
- catch (Exception $e)
1457
- {
1458
- $order->error = __("Error getting subscription with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
1459
- $order->shorterror = $order->error;
1460
- return false;
1461
- }
1462
1463
- return $sub;
1464
}
1465
1466
- //no subscriptions object in customer
1467
- if(empty($this->customer->subscriptions))
1468
- return false;
1469
1470
- //find subscription based on customer id and order/plan id
1471
- $subscriptions = $this->customer->subscriptions->all();
1472
1473
- //no subscriptions
1474
- if(empty($subscriptions) || empty($subscriptions->data))
1475
- return false;
1476
1477
- //we really want to test against the order codes of all orders with the same subscription_transaction_id (customer id)
1478
- $codes = $wpdb->get_col("SELECT code FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $order->user_id . "' AND subscription_transaction_id = '" . $order->subscription_transaction_id . "' AND status NOT IN('refunded', 'review', 'token', 'error')");
1479
1480
- //find the one for this order
1481
- foreach($subscriptions->data as $sub)
1482
- {
1483
- if(in_array($sub->plan->id, $codes))
1484
- {
1485
- return $sub;
1486
- }
1487
- }
1488
1489
- //didn't find anything yet
1490
- return false;
1491
}
1492
1493
- /**
1494
- * Create a new subscription with Stripe
1495
- *
1496
- * @since 1.4
1497
- */
1498
- function subscribe(&$order, $checkout = true)
1499
- {
1500
- global $pmpro_currency, $pmpro_currencies;
1501
1502
- $currency_unit_multiplier = 100; //ie 100 cents per USD
1503
1504
- //account for zero-decimal currencies like the Japanese Yen
1505
- if(is_array($pmpro_currencies[$pmpro_currency]) && isset($pmpro_currencies[$pmpro_currency]['decimals']) && $pmpro_currencies[$pmpro_currency]['decimals'] == 0)
1506
- $currency_unit_multiplier = 1;
1507
1508
- //create a code for the order
1509
- if(empty($order->code))
1510
- $order->code = $order->getRandomCode();
1511
1512
- //filter order before subscription. use with care.
1513
- $order = apply_filters("pmpro_subscribe_order", $order, $this);
1514
1515
- //figure out the user
1516
- if(!empty($order->user_id))
1517
- $user_id = $order->user_id;
1518
- else
1519
- {
1520
- global $current_user;
1521
- $user_id = $current_user->ID;
1522
- }
1523
1524
- //set up customer
1525
- $result = $this->getCustomer($order);
1526
- if(empty($result))
1527
- return false; //error retrieving customer
1528
1529
- //set subscription id to custom id
1530
- $order->subscription_transaction_id = $this->customer['id']; //transaction id is the customer id, we save it in user meta later too
1531
1532
- //figure out the amounts
1533
- $amount = $order->PaymentAmount;
1534
- $amount_tax = $order->getTaxForPrice($amount);
1535
- $amount = pmpro_round_price((float)$amount + (float)$amount_tax);
1536
1537
/*
1538
- There are two parts to the trial. Part 1 is simply the delay until the first payment
1539
- since we are doing the first payment as a separate transaction.
1540
- The second part is the actual "trial" set by the admin.
1541
1542
- Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
1543
*/
1544
- //figure out the trial length (first payment handled by initial charge)
1545
- if($order->BillingPeriod == "Year")
1546
- $trial_period_days = $order->BillingFrequency * 365; //annual
1547
- elseif($order->BillingPeriod == "Day")
1548
- $trial_period_days = $order->BillingFrequency * 1; //daily
1549
- elseif($order->BillingPeriod == "Week")
1550
- $trial_period_days = $order->BillingFrequency * 7; //weekly
1551
- else
1552
- $trial_period_days = $order->BillingFrequency * 30; //assume monthly
1553
-
1554
- //convert to a profile start date
1555
- $order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
1556
-
1557
- //filter the start date
1558
- $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
1559
-
1560
- //convert back to days
1561
- $trial_period_days = ceil(abs(strtotime(date_i18n("Y-m-d"), current_time("timestamp")) - strtotime($order->ProfileStartDate, current_time("timestamp"))) / 86400);
1562
-
1563
- //for free trials, just push the start date of the subscription back
1564
- if(!empty($order->TrialBillingCycles) && $order->TrialAmount == 0)
1565
- {
1566
- $trialOccurrences = (int)$order->TrialBillingCycles;
1567
- if($order->BillingPeriod == "Year")
1568
- $trial_period_days = $trial_period_days + (365 * $order->BillingFrequency * $trialOccurrences); //annual
1569
- elseif($order->BillingPeriod == "Day")
1570
- $trial_period_days = $trial_period_days + (1 * $order->BillingFrequency * $trialOccurrences); //daily
1571
- elseif($order->BillingPeriod == "Week")
1572
- $trial_period_days = $trial_period_days + (7 * $order->BillingFrequency * $trialOccurrences); //weekly
1573
- else
1574
- $trial_period_days = $trial_period_days + (30 * $order->BillingFrequency * $trialOccurrences); //assume monthly
1575
- }
1576
- elseif(!empty($order->TrialBillingCycles))
1577
- {
1578
- /*
1579
- Let's set the subscription to the trial and give the user an "update" to change the sub later to full price (since v2.0)
1580
1581
- This will force TrialBillingCycles > 1 to act as if they were 1
1582
- */
1583
- $new_user_updates = array();
1584
- $new_user_updates[] = array(
1585
- 'when' => 'payment',
1586
- 'billing_amount' => $order->PaymentAmount,
1587
- 'cycle_period' => $order->BillingPeriod,
1588
- 'cycle_number' => $order->BillingFrequency
1589
- );
1590
-
1591
- //now amount to equal the trial #s
1592
- $amount = $order->TrialAmount;
1593
- $amount_tax = $order->getTaxForPrice($amount);
1594
- $amount = pmpro_round_price((float)$amount + (float)$amount_tax);
1595
- }
1596
1597
- //create a plan
1598
- try
1599
- {
1600
- $plan = array(
1601
- "amount" => $amount * $currency_unit_multiplier,
1602
- "interval_count" => $order->BillingFrequency,
1603
- "interval" => strtolower($order->BillingPeriod),
1604
- "trial_period_days" => $trial_period_days,
1605
- "name" => $order->membership_name . " for order " . $order->code,
1606
- "currency" => strtolower($pmpro_currency),
1607
- "id" => $order->code
1608
- );
1609
-
1610
- $plan = Stripe_Plan::create(apply_filters('pmpro_stripe_create_plan_array', $plan));
1611
- }
1612
- catch (Exception $e)
1613
- {
1614
- $order->error = __("Error creating plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
1615
- $order->shorterror = $order->error;
1616
- return false;
1617
- }
1618
1619
- //before subscribing, let's clear out the updates so we don't trigger any during sub
1620
- if(!empty($user_id))
1621
- {
1622
- $old_user_updates = get_user_meta($user_id, "pmpro_stripe_updates", true);
1623
- update_user_meta($user_id, "pmpro_stripe_updates", array());
1624
- }
1625
1626
- if(empty($order->subscription_transaction_id) && !empty($this->customer['id']))
1627
- $order->subscription_transaction_id = $this->customer['id'];
1628
1629
- //subscribe to the plan
1630
- try
1631
- {
1632
- $subscription = array("plan" => $order->code);
1633
- $result = $this->customer->subscriptions->create(apply_filters('pmpro_stripe_create_subscription_array', $subscription));
1634
- }
1635
- catch (Exception $e)
1636
- {
1637
- //try to delete the plan
1638
- $plan->delete();
1639
1640
- //give the user any old updates back
1641
- if(!empty($user_id))
1642
- update_user_meta($user_id, "pmpro_stripe_updates", $old_user_updates);
1643
1644
- //return error
1645
- $order->error = __("Error subscribing customer to plan with Stripe:", 'paid-memberships-pro' ) . $e->getMessage();
1646
- $order->shorterror = $order->error;
1647
- return false;
1648
}
1649
1650
- //delete the plan
1651
- $plan = Stripe_Plan::retrieve($order->code);
1652
- $plan->delete();
1653
1654
- //if we got this far, we're all good
1655
- $order->status = "success";
1656
- $order->subscription_transaction_id = $result['id'];
1657
1658
- //save new updates if this is at checkout
1659
- if($checkout)
1660
- {
1661
- //empty out updates unless set above
1662
- if(empty($new_user_updates))
1663
- $new_user_updates = array();
1664
1665
- //update user meta
1666
- if(!empty($user_id))
1667
- update_user_meta($user_id, "pmpro_stripe_updates", $new_user_updates);
1668
- else
1669
- {
1670
- //need to remember the user updates to save later
1671
global $pmpro_stripe_updates;
1672
- $pmpro_stripe_updates = $new_user_updates;
1673
- function pmpro_user_register_stripe_updates($user_id)
1674
- {
1675
- global $pmpro_stripe_updates;
1676
- update_user_meta($user_id, "pmpro_stripe_updates", $pmpro_stripe_updates);
1677
- }
1678
- add_action("user_register", "pmpro_user_register_stripe_updates");
1679
}
1680
}
1681
- else
1682
- {
1683
- //give them their old updates back
1684
- update_user_meta($user_id, "pmpro_stripe_updates", $old_user_updates);
1685
- }
1686
-
1687
- return true;
1688
}
1689
1690
- /**
1691
- * Helper method to save the subscription ID to make sure the membership doesn't get cancelled by the webhook
1692
- */
1693
- static function ignoreCancelWebhookForThisSubscription($subscription_id, $user_id = NULL) {
1694
- if(empty($user_id)) {
1695
- global $current_user;
1696
- $user_id = $current_user->ID;
1697
- }
1698
-
1699
- $preserve = get_user_meta( $user_id, 'pmpro_stripe_dont_cancel', true );
1700
1701
- // No previous values found, init the array
1702
- if ( empty( $preserve ) ) {
1703
- $preserve = array();
1704
- }
1705
1706
- // Store or update the subscription ID timestamp (for cleanup)
1707
- $preserve[$subscription_id] = current_time( 'timestamp' );
1708
1709
- update_user_meta( $user_id, 'pmpro_stripe_dont_cancel', $preserve );
1710
}
1711
1712
- /**
1713
- * Helper method to process a Stripe subscription update
1714
- */
1715
- static function updateSubscription($update, $user_id) {
1716
- global $wpdb;
1717
1718
- //get level for user
1719
- $user_level = pmpro_getMembershipLevelForUser($user_id);
1720
1721
- //get current plan at Stripe to get payment date
1722
- $last_order = new MemberOrder();
1723
- $last_order->getLastMemberOrder($user_id);
1724
- $last_order->setGateway('stripe');
1725
- $last_order->Gateway->getCustomer($last_order);
1726
1727
- $subscription = $last_order->Gateway->getSubscription($last_order);
1728
1729
- if(!empty($subscription))
1730
- {
1731
- $end_timestamp = $subscription->current_period_end;
1732
1733
- //cancel the old subscription
1734
- if(!$last_order->Gateway->cancelSubscriptionAtGateway($subscription, true))
1735
- {
1736
- //throw error and halt save
1737
- if ( !function_exists( 'pmpro_stripe_user_profile_fields_save_error' )) {
1738
- //throw error and halt save
1739
- function pmpro_stripe_user_profile_fields_save_error( $errors, $update, $user ) {
1740
- $errors->add( 'pmpro_stripe_updates', __( 'Could not cancel the old subscription. Updates have not been processed.', 'paid-memberships-pro' ) );
1741
- }
1742
1743
- add_filter( 'user_profile_update_errors', 'pmpro_stripe_user_profile_fields_save_error', 10, 3 );
1744
}
1745
1746
- //stop processing updates
1747
- return;
1748
}
1749
}
1750
1751
- //if we didn't get an end date, let's set one one cycle out
1752
- if(empty($end_timestamp))
1753
- $end_timestamp = strtotime("+" . $update['cycle_number'] . " " . $update['cycle_period'], current_time('timestamp'));
1754
-
1755
- //build order object
1756
- $update_order = new MemberOrder();
1757
- $update_order->setGateway('stripe');
1758
- $update_order->user_id = $user_id;
1759
- $update_order->membership_id = $user_level->id;
1760
- $update_order->membership_name = $user_level->name;
1761
- $update_order->InitialPayment = 0;
1762
- $update_order->PaymentAmount = $update['billing_amount'];
1763
- $update_order->ProfileStartDate = date_i18n("Y-m-d", $end_timestamp);
1764
- $update_order->BillingPeriod = $update['cycle_period'];
1765
- $update_order->BillingFrequency = $update['cycle_number'];
1766
-
1767
- //need filter to reset ProfileStartDate
1768
- add_filter('pmpro_profile_start_date', create_function('$startdate, $order', 'return "' . $update_order->ProfileStartDate . 'T0:0:0";'), 10, 2);
1769
-
1770
- //update subscription
1771
- $update_order->Gateway->subscribe($update_order, false);
1772
-
1773
- //update membership
1774
- $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users
1775
- SET billing_amount = '" . esc_sql($update['billing_amount']) . "',
1776
- cycle_number = '" . esc_sql($update['cycle_number']) . "',
1777
- cycle_period = '" . esc_sql($update['cycle_period']) . "',
1778
- trial_amount = '',
1779
- trial_limit = ''
1780
- WHERE user_id = '" . esc_sql($user_id) . "'
1781
- AND membership_id = '" . esc_sql($last_order->membership_id) . "'
1782
- AND status = 'active'
1783
- LIMIT 1";
1784
-
1785
- $wpdb->query($sqlQuery);
1786
-
1787
- //save order so we know which plan to look for at stripe (order code = plan id)
1788
- $update_order->status = "success";
1789
- $update_order->saveOrder();
1790
}
1791
1792
- /**
1793
- * Helper method to update the customer info via getCustomer
1794
- *
1795
- * @since 1.4
1796
- */
1797
- function update(&$order)
1798
- {
1799
- //we just have to run getCustomer which will look for the customer and update it with the new token
1800
- $result = $this->getCustomer($order);
1801
1802
- if(!empty($result))
1803
- {
1804
- return true;
1805
- }
1806
- else
1807
- {
1808
- return false; //couldn't find the customer
1809
- }
1810
}
1811
1812
- /**
1813
- * Cancel a subscription at Stripe
1814
- *
1815
- * @since 1.4
1816
- */
1817
- function cancel(&$order, $update_status = true)
1818
- {
1819
- global $pmpro_stripe_event;
1820
1821
- //no matter what happens below, we're going to cancel the order in our system
1822
- if($update_status)
1823
- $order->updateStatus("cancelled");
1824
1825
- //require a subscription id
1826
- if(empty($order->subscription_transaction_id))
1827
- return false;
1828
1829
- //find the customer
1830
- $result = $this->getCustomer($order);
1831
1832
- if(!empty($result))
1833
- {
1834
- //find subscription with this order code
1835
- $subscription = $this->getSubscription($order);
1836
1837
- if(!empty($subscription)
1838
- && ( empty( $pmpro_stripe_event ) || empty( $pmpro_stripe_event->type ) || $pmpro_stripe_event->type != 'customer.subscription.deleted' ) )
1839
- {
1840
- if($this->cancelSubscriptionAtGateway($subscription))
1841
- {
1842
- //we're okay, going to return true later
1843
- }
1844
- else
1845
- {
1846
- $order->error = __("Could not cancel old subscription.", 'paid-memberships-pro' );
1847
- $order->shorterror = $order->error;
1848
1849
- return false;
1850
- }
1851
}
1852
-
1853
- /*
1854
- Clear updates for this user. (But not if checking out, we would have already done that.)
1855
- */
1856
- if(empty($_REQUEST['submit-checkout']))
1857
- update_user_meta($order->user_id, "pmpro_stripe_updates", array());
1858
-
1859
- return true;
1860
}
1861
- else
1862
- {
1863
- $order->error = __("Could not find the customer.", 'paid-memberships-pro' );
1864
- $order->shorterror = $order->error;
1865
- return false; //no customer found
1866
}
1867
- }
1868
1869
- /**
1870
- * Helper method to cancel a subscription at Stripe and also clear up any upaid invoices.
1871
- *
1872
- * @since 1.8
1873
- */
1874
- function cancelSubscriptionAtGateway($subscription, $preserve_local_membership = false)
1875
- {
1876
- //need a valid sub
1877
- if(empty($subscription->id))
1878
- return false;
1879
1880
- //make sure we get the customer for this subscription
1881
- $order = new MemberOrder();
1882
- $order->getLastMemberOrderBySubscriptionTransactionID($subscription->id);
1883
1884
- //no order?
1885
- if(empty($order))
1886
- {
1887
- //lets cancel anyway, but this is suspicious
1888
- $r = $subscription->cancel();
1889
1890
- return true;
1891
- }
1892
1893
- //okay have an order, so get customer so we can cancel invoices too
1894
- $this->getCustomer($order);
1895
1896
- //get open invoices
1897
- $invoices = $this->customer->invoices();
1898
- $invoices = $invoices->all();
1899
1900
- //found it, cancel it
1901
- try
1902
- {
1903
- //find any open invoices for this subscription and forgive them
1904
- if(!empty($invoices))
1905
- {
1906
- foreach($invoices->data as $invoice)
1907
- {
1908
- if(!$invoice->closed && $invoice->subscription == $subscription->id)
1909
- {
1910
- $invoice->closed = true;
1911
- $invoice->save();
1912
- }
1913
}
1914
}
1915
1916
- //sometimes we don't want to cancel the local membership when Stripe sends its webhook
1917
- if($preserve_local_membership)
1918
- PMProGateway_stripe::ignoreCancelWebhookForThisSubscription($subscription->id, $order->user_id);
1919
1920
- //cancel
1921
- $r = $subscription->cancel();
1922
1923
- return true;
1924
- }
1925
- catch(Exception $e)
1926
- {
1927
- return false;
1928
- }
1929
}
1930
1931
- /**
1932
- * Filter pmpro_next_payment to get date via API if possible
1933
- *
1934
- * @since 1.8.6
1935
- */
1936
- static function pmpro_next_payment($timestamp, $user_id, $order_status)
1937
- {
1938
- //find the last order for this user
1939
- if(!empty($user_id))
1940
- {
1941
- //get last order
1942
- $order = new MemberOrder();
1943
- $order->getLastMemberOrder($user_id, $order_status);
1944
-
1945
- //check if this is a Stripe order with a subscription transaction id
1946
- if(!empty($order->id) && !empty($order->subscription_transaction_id) && $order->gateway == "stripe")
1947
- {
1948
- //get the subscription and return the current_period end or false
1949
- $subscription = $order->Gateway->getSubscription($order);
1950
-
1951
- if( !empty( $subscription ) ) {
1952
- $customer = $order->Gateway->getCustomer();
1953
- if( ! $customer->delinquent && ! empty ( $subscription->current_period_end ) ) {
1954
- return $subscription->current_period_end;
1955
- } elseif ( $customer->delinquent && ! empty( $subscription->current_period_start ) ) {
1956
- return $subscription->current_period_start;
1957
- } else {
1958
- return $false; // shouldn't really get here
1959
- }
1960
- }
1961
}
1962
}
1963
-
1964
- return $timestamp;
1965
}
1966
1967
- /**
1968
- * Refund a payment or invoice
1969
- * @param object &$order Related PMPro order object.
1970
- * @param string $transaction_id Payment or Invoice id to void.
1971
- * @return bool True or false if the void worked
1972
- */
1973
- function void(&$order, $transaction_id = null)
1974
- {
1975
- //stripe doesn't differentiate between voids and refunds, so let's just pass on to the refund function
1976
- return $this->refund($order, $transaction_id);
1977
- }
1978
1979
- /**
1980
- * Refund a payment or invoice
1981
- * @param object &$order Related PMPro order object.
1982
- * @param string $transaction_id Payment or invoice id to void.
1983
- * @return bool True or false if the refund worked.
1984
- */
1985
- function refund(&$order, $transaction_id = NULL)
1986
- {
1987
- //default to using the payment id from the order
1988
- if(empty($transaction_id) && !empty($order->payment_transaction_id))
1989
- $transaction_id = $order->payment_transaction_id;
1990
1991
- //need a transaction id
1992
- if(empty($transaction_id))
1993
- return false;
1994
1995
- //if an invoice ID is passed, get the charge/payment id
1996
- if(strpos($transaction_id, "in_") !== false) {
1997
- $invoice = Stripe_Invoice::retrieve($transaction_id);
1998
1999
- if(!empty($invoice) && !empty($invoice->charge))
2000
- $transaction_id = $invoice->charge;
2001
- }
2002
2003
- //get the charge
2004
- try {
2005
- $charge = Stripe_Charge::retrieve($transaction_id);
2006
- } catch (Exception $e) {
2007
- $charge = false;
2008
}
2009
2010
- //can't find the charge?
2011
- if(empty($charge)) {
2012
- $order->status = "error";
2013
- $order->errorcode = "";
2014
- $order->error = "";
2015
- $order->shorterror = "";
2016
2017
- return false;
2018
- }
2019
2020
- //attempt refund
2021
- try
2022
- {
2023
- $refund = $charge->refund();
2024
- }
2025
- catch (Exception $e)
2026
- {
2027
- //$order->status = "error";
2028
- $order->errorcode = true;
2029
- $order->error = __("Error: ", 'paid-memberships-pro' ) . $e->getMessage();
2030
- $order->shorterror = $order->error;
2031
- return false;
2032
- }
2033
2034
- if($refund->status == "succeeded") {
2035
- $order->status = "refunded";
2036
- $order->saveOrder();
2037
2038
- return true;
2039
- } else {
2040
- $order->status = "error";
2041
- $order->errorcode = true;
2042
- $order->error = sprintf(__("Error: Unkown error while refunding charge #%s", 'paid-memberships-pro' ), $transaction_id);
2043
- $order->shorterror = $order->error;
2044
2045
- return false;
2046
- }
2047
}
2048
}
1
<?php
2
+ // For compatibility with old library (Namespace Alias)
3
+ use Stripe\Customer as Stripe_Customer;
4
+ use Stripe\Invoice as Stripe_Invoice;
5
+ use Stripe\Plan as Stripe_Plan;
6
+ use Stripe\Charge as Stripe_Charge;
7
+
8
+ define( "PMPRO_STRIPE_API_VERSION", "2017-08-15" );
9
+
10
+ //include pmprogateway
11
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
12
+
13
+ //load classes init method
14
+ add_action('init', array('PMProGateway_stripe', 'init'));
15
+
16
+ // loading plugin activation actions
17
+ add_action('activate_paid-memberships-pro', array('PMProGateway_stripe', 'pmpro_activation'));
18
+ add_action('deactivate_paid-memberships-pro', array('PMProGateway_stripe', 'pmpro_deactivation'));
19
+
20
+ /**
21
+ * PMProGateway_stripe Class
22
+ *
23
+ * Handles Stripe integration.
24
+ *
25
+ * @since 1.4
26
+ */
27
+ class PMProGateway_stripe extends PMProGateway
28
+ {
29
/**
30
+ * @var bool Is the Stripe/PHP Library loaded
31
+ */
32
+ private static $is_loaded = false;
33
+ /**
34
+ * Stripe Class Constructor
35
*
36
+ * @since 1.4
37
*/
38
+ function __construct($gateway = NULL) {
39
+ $this->gateway = $gateway;
40
+ $this->gateway_environment = pmpro_getOption("gateway_environment");
41
+
42
+ if( true === $this->dependencies() ) {
43
+ $this->loadStripeLibrary();
44
+ Stripe\Stripe::setApiKey(pmpro_getOption("stripe_secretkey"));
45
+ Stripe\Stripe::setAPIVersion( PMPRO_STRIPE_API_VERSION );
46
+ self::$is_loaded = true;
47
}
48
49
+ return $this->gateway;
50
+ }
51
52
+ /**
53
+ * Warn if required extensions aren't loaded.
54
+ *
55
+ * @return bool
56
+ * @since 1.8.6.8.1
57
+ * @since 1.8.13.6 - Add json dependency
58
+ */
59
+ public static function dependencies() {
60
+ global $msg, $msgt, $pmpro_stripe_error;
61
62
+ if ( version_compare( PHP_VERSION, '5.3.29', '<' )) {
63
64
+ $pmpro_stripe_error = true;
65
+ $msg = -1;
66
+ $msgt = sprintf(__("The Stripe Gateway requires PHP 5.3.29 or greater. We recommend upgrading to PHP %s or greater. Ask your host to upgrade.", "paid-memberships-pro" ), PMPRO_PHP_MIN_VERSION );
67
68
+ if ( !is_admin() ) {
69
+ pmpro_setMessage( $msgt, "pmpro_error" );
70
}
71
72
+ return false;
73
+ }
74
75
+ $modules = array( 'curl', 'mbstring', 'json' );
76
77
+ foreach($modules as $module){
78
+ if(!extension_loaded($module)){
79
+ $pmpro_stripe_error = true;
80
+ $msg = -1;
81
+ $msgt = sprintf(__("The %s gateway depends on the %s PHP extension. Please enable it, or ask your hosting provider to enable it.", 'paid-memberships-pro' ), 'Stripe', $module);
82
83
+ //throw error on checkout page
84
+ if(!is_admin())
85
+ pmpro_setMessage($msgt, 'pmpro_error');
86
87
+ return false;
88
}
89
}
90
91
+ self::$is_loaded = true;
92
+ return true;
93
+ }
94
95
+ /**
96
+ * Load the Stripe API library.
97
+ *
98
+ * @since 1.8
99
+ * Moved into a method in version 1.8 so we only load it when needed.
100
+ */
101
+ function loadStripeLibrary() {
102
+ //load Stripe library if it hasn't been loaded already (usually by another plugin using Stripe)
103
+ if(!class_exists("Stripe\Stripe")) {
104
+ require_once( PMPRO_DIR . "/includes/lib/Stripe/init.php" );
105
+ }
106
+ }
107
108
+ /**
109
+ * Run on WP init
110
+ *
111
+ * @since 1.8
112
+ */
113
+ static function init() {
114
+ //make sure Stripe is a gateway option
115
+ add_filter('pmpro_gateways', array('PMProGateway_stripe', 'pmpro_gateways'));
116
+
117
+ //add fields to payment settings
118
+ add_filter('pmpro_payment_options', array('PMProGateway_stripe', 'pmpro_payment_options'));
119
+ add_filter('pmpro_payment_option_fields', array('PMProGateway_stripe', 'pmpro_payment_option_fields'), 10, 2);
120
+
121
+ //add some fields to edit user page (Updates)
122
+ add_action('pmpro_after_membership_level_profile_fields', array('PMProGateway_stripe', 'user_profile_fields'));
123
+ add_action('profile_update', array('PMProGateway_stripe', 'user_profile_fields_save'));
124
+
125
+ //old global RE showing billing address or not
126
+ global $pmpro_stripe_lite;
127
+ $pmpro_stripe_lite = apply_filters("pmpro_stripe_lite", !pmpro_getOption("stripe_billingaddress")); //default is oposite of the stripe_billingaddress setting
128
+ add_filter('pmpro_required_billing_fields', array('PMProGateway_stripe', 'pmpro_required_billing_fields'));
129
+
130
+ //updates cron
131
+ add_action('pmpro_cron_stripe_subscription_updates', array('PMProGateway_stripe', 'pmpro_cron_stripe_subscription_updates'));
132
+
133
+ /*
134
+ Filter pmpro_next_payment to get actual value
135
+ via the Stripe API. This is disabled by default
136
+ for performance reasons, but you can enable it
137
+ by copying this line into a custom plugin or
138
+ your active theme's functions.php and uncommenting
139
+ it there.
140
+ */
141
+ //add_filter('pmpro_next_payment', array('PMProGateway_stripe', 'pmpro_next_payment'), 10, 3);
142
143
+ //code to add at checkout if Stripe is the current gateway
144
+ $default_gateway = pmpro_getOption('gateway');
145
+ $current_gateway = pmpro_getGateway();
146
147
+ if( ($default_gateway == "stripe" || $current_gateway == "stripe") && empty($_REQUEST['review'] ) ) //$_REQUEST['review'] means the PayPal Express review page
148
+ {
149
+ add_action('pmpro_checkout_preheader', array('PMProGateway_stripe', 'pmpro_checkout_preheader'));
150
+ add_action('pmpro_billing_preheader', array('PMProGateway_stripe', 'pmpro_checkout_preheader'));
151
+ add_filter('pmpro_checkout_order', array('PMProGateway_stripe', 'pmpro_checkout_order'));
152
+ add_filter('pmpro_billing_order', array('PMProGateway_stripe', 'pmpro_checkout_order'));
153
+ add_filter('pmpro_include_billing_address_fields', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
154
+ add_filter('pmpro_include_cardtype_field', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
155
+ add_filter('pmpro_include_payment_information_fields', array('PMProGateway_stripe', 'pmpro_include_payment_information_fields'));
156
+
157
+ //make sure we clean up subs we will be cancelling after checkout before processing
158
+ add_action('pmpro_checkout_before_processing', array('PMProGateway_stripe', 'pmpro_checkout_before_processing'));
159
+ }
160
161
+ add_action( 'init', array( 'PMProGateway_stripe', 'pmpro_clear_saved_subscriptions' ) );
162