Paid Memberships Pro - Version 1.8.1

Version Description

  • BUG: Fixed typos in pmpro_memberslist_csv and pmpro_orderscsv capabilities. (Thanks, Arnaud Devic)
  • BUG: Only loading the Braintree API when using it now.
  • BUG: Fixed fatal error that would occur at checkout if PayPal Standard were used with a discount code. (Thanks, John Zeiger)
  • BUG: Fixed issue where discount codes would not work if billing address fields were hidden. (e.g. paying by PayPal or check)
  • BUG: Fixed issue with the logic around sending emails when admin's change a member's level or expiration date. Admins will always get an email. Members will only get an email if the checkbox is checked.
  • ENHANCEMENT: No longer showing check instructions at checkout if the level is free.
  • ENHANCEMENT: Added pmpro_stripe_create_subscription filter. (Thanks, nickd32 on GitHub)
  • ENHANCEMENT: Added Czech (cs_CZ) language files and support for using decimals as separators. (Thanks, Martin "shr3k" Koke on GitHub)
Download this release

Release Info

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

Code changes from version 1.8 to 1.8.1

adminpages/orders-csv.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  //only admins can get this
3
- if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_orders_csv")))
4
  {
5
  die(__("You do not have permissions to perform this action.", "pmpro"));
6
  }
1
  <?php
2
  //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_orderscsv")))
4
  {
5
  die(__("You do not have permissions to perform this action.", "pmpro"));
6
  }
classes/gateways/class.pmprogateway_braintree.php CHANGED
@@ -3,17 +3,17 @@
3
  require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
 
5
  //load classes init method
6
- add_action('init', array('PMProGateway_braintree', 'init'));
7
 
8
- if(!class_exists("Braintree"))
9
- require_once(dirname(__FILE__) . "/../../includes/lib/Braintree/Braintree.php");
10
  class PMProGateway_braintree extends PMProGateway
11
  {
12
  function PMProGateway_braintree($gateway = NULL)
13
- {
14
  $this->gateway = $gateway;
15
  $this->gateway_environment = pmpro_getOption("gateway_environment");
16
 
 
 
17
  //convert to braintree nomenclature
18
  $environment = $this->gateway_environment;
19
  if($environment == "live")
@@ -27,6 +27,19 @@
27
  return $this->gateway;
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  /**
31
  * Run on WP init
32
  *
3
  require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
 
5
  //load classes init method
6
+ add_action('init', array('PMProGateway_braintree', 'init'));
7
 
 
 
8
  class PMProGateway_braintree extends PMProGateway
9
  {
10
  function PMProGateway_braintree($gateway = NULL)
11
+ {
12
  $this->gateway = $gateway;
13
  $this->gateway_environment = pmpro_getOption("gateway_environment");
14
 
15
+ $this->loadBraintreeLibrary();
16
+
17
  //convert to braintree nomenclature
18
  $environment = $this->gateway_environment;
19
  if($environment == "live")
27
  return $this->gateway;
28
  }
29
 
30
+ /**
31
+ * Load the Braintree API library.
32
+ *
33
+ * @since 1.8.1
34
+ * Moved into a method in version 1.8.1 so we only load it when needed.
35
+ */
36
+ function loadBraintreeLibrary()
37
+ {
38
+ //load Braintree library if it hasn't been loaded already (usually by another plugin using Braintree)
39
+ if(!class_exists("Braintree"))
40
+ require_once(dirname(__FILE__) . "/../../includes/lib/Braintree/Braintree.php");
41
+ }
42
+
43
  /**
44
  * Run on WP init
45
  *
classes/gateways/class.pmprogateway_paypalstandard.php CHANGED
@@ -220,7 +220,7 @@
220
  */
221
  static function pmpro_checkout_before_change_membership_level($user_id, $morder)
222
  {
223
- global $discount_code_id;
224
 
225
  //if no order, no need to pay
226
  if(empty($morder))
220
  */
221
  static function pmpro_checkout_before_change_membership_level($user_id, $morder)
222
  {
223
+ global $discount_code_id, $wpdb;
224
 
225
  //if no order, no need to pay
226
  if(empty($morder))
classes/gateways/class.pmprogateway_stripe.php CHANGED
@@ -1,10 +1,10 @@
1
- <?php
2
  //include pmprogateway
3
- require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
-
5
  //load classes init method
6
  add_action('init', array('PMProGateway_stripe', 'init'));
7
-
8
  /**
9
  * PMProGateway_stripe Class
10
  *
@@ -16,25 +16,25 @@
16
  {
17
  /**
18
  * Stripe Class Constructor
19
- *
20
  * @since 1.4
21
  */
22
  function PMProGateway_stripe($gateway = NULL)
23
  {
24
  $this->gateway = $gateway;
25
  $this->gateway_environment = pmpro_getOption("gateway_environment");
26
-
27
- $this->loadStripeLibrary();
28
  Stripe::setApiKey(pmpro_getOption("stripe_secretkey"));
29
-
30
  return $this->gateway;
31
- }
32
-
33
  /**
34
  * Load the Stripe API library.
35
- *
36
  * @since 1.8
37
- * Moved into a method in version 2.0 so we only load it when needed.
38
  */
39
  function loadStripeLibrary()
40
  {
@@ -42,66 +42,66 @@
42
  if(!class_exists("Stripe"))
43
  require_once(dirname(__FILE__) . "/../../includes/lib/Stripe/Stripe.php");
44
  }
45
-
46
  /**
47
  * Run on WP init
48
- *
49
  * @since 1.8
50
  */
51
  static function init()
52
- {
53
  //make sure Stripe is a gateway option
54
  add_filter('pmpro_gateways', array('PMProGateway_stripe', 'pmpro_gateways'));
55
-
56
  //add fields to payment settings
57
  add_filter('pmpro_payment_options', array('PMProGateway_stripe', 'pmpro_payment_options'));
58
  add_filter('pmpro_payment_option_fields', array('PMProGateway_stripe', 'pmpro_payment_option_fields'), 10, 2);
59
-
60
  //add some fields to edit user page (Updates)
61
  add_action('pmpro_after_membership_level_profile_fields', array('PMProGateway_stripe', 'user_profile_fields'));
62
  add_action('profile_update', array('PMProGateway_stripe', 'user_profile_fields_save'));
63
-
64
  //old global RE showing billing address or not
65
  global $pmpro_stripe_lite;
66
  $pmpro_stripe_lite = apply_filters("pmpro_stripe_lite", !pmpro_getOption("stripe_billingaddress")); //default is oposite of the stripe_billingaddress setting
67
-
68
  //updates cron
69
  add_action('pmpro_activation', array('PMProGateway_stripe', 'pmpro_activation'));
70
  add_action('pmpro_deactivation', array('PMProGateway_stripe', 'pmpro_deactivation'));
71
  add_action('pmpro_cron_stripe_subscription_updates', array('PMProGateway_stripe', 'pmpro_cron_stripe_subscription_updates'));
72
-
73
  //code to add at checkout if Stripe is the current gateway
74
  $gateway = pmpro_getOption("gateway");
75
  if($gateway == "stripe")
76
  {
77
- add_action('pmpro_checkout_preheader', array('PMProGateway_stripe', 'pmpro_checkout_preheader'));
78
  add_filter('pmpro_checkout_order', array('PMProGateway_stripe', 'pmpro_checkout_order'));
79
  add_filter('pmpro_include_billing_address_fields', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
80
  add_filter('pmpro_include_cardtype_field', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
81
  add_filter('pmpro_include_payment_information_fields', array('PMProGateway_stripe', 'pmpro_include_payment_information_fields'));
82
  }
83
  }
84
-
85
  /**
86
  * Make sure Stripe is in the gateways list
87
- *
88
  * @since 1.8
89
  */
90
  static function pmpro_gateways($gateways)
91
  {
92
  if(empty($gateways['stripe']))
93
  $gateways['stripe'] = __('Stripe', 'pmpro');
94
-
95
  return $gateways;
96
  }
97
-
98
  /**
99
  * Get a list of payment options that the Stripe gateway needs/supports.
100
- *
101
  * @since 1.8
102
  */
103
  static function getGatewayOptions()
104
- {
105
  $options = array(
106
  'sslseal',
107
  'nuclear_HTTPS',
@@ -115,35 +115,35 @@
115
  'tax_rate',
116
  'accepted_credit_cards'
117
  );
118
-
119
  return $options;
120
  }
121
-
122
  /**
123
  * Set payment options for payment settings page.
124
- *
125
  * @since 1.8
126
  */
127
  static function pmpro_payment_options($options)
128
- {
129
  //get stripe options
130
  $stripe_options = PMProGateway_stripe::getGatewayOptions();
131
-
132
  //merge with others.
133
  $options = array_merge($stripe_options, $options);
134
-
135
  return $options;
136
  }
137
-
138
  /**
139
  * Display fields for Stripe options.
140
- *
141
  * @since 1.8
142
  */
143
  static function pmpro_payment_option_fields($values, $gateway)
144
  {
145
  ?>
146
- <tr class="pmpro_settings_divider gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
147
  <td colspan="2">
148
  <?php _e('Stripe Settings', 'pmpro'); ?>
149
  </td>
@@ -171,7 +171,7 @@
171
  <td>
172
  <select id="stripe_billingaddress" name="stripe_billingaddress">
173
  <option value="0" <?php if(empty($values['stripe_billingaddress'])) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
174
- <option value="1" <?php if(!empty($values['stripe_billingaddress'])) { ?>selected="selected"<?php } ?>><?php _e('Yes', 'pmpro');?></option>
175
  </select>
176
  <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>", 'pmpro');?></small>
177
  </td>
@@ -183,39 +183,39 @@
183
  <td>
184
  <p><?php _e('To fully integrate with Stripe, be sure to set your Web Hook URL to', 'pmpro');?> <pre><?php echo admin_url("admin-ajax.php") . "?action=stripe_webhook";?></pre></p>
185
  </td>
186
- </tr>
187
  <?php
188
  }
189
-
190
  /**
191
  * Code added to checkout preheader.
192
- *
193
  * @since 1.8
194
  */
195
  static function pmpro_checkout_preheader()
196
- {
197
  global $gateway, $pmpro_level;
198
-
199
  if($gateway == "stripe" && !pmpro_isLevelFree($pmpro_level))
200
  {
201
  //stripe js library
202
  wp_enqueue_script("stripe", "https://js.stripe.com/v2/", array(), NULL);
203
-
204
  //stripe js code for checkout
205
  function pmpro_stripe_javascript()
206
  {
207
  global $pmpro_gateway, $pmpro_level, $pmpro_stripe_lite;
208
  ?>
209
  <script type="text/javascript">
210
- // this identifies your website in the createToken call below
211
  Stripe.setPublishableKey('<?php echo pmpro_getOption("stripe_publishablekey"); ?>');
212
-
213
- var pmpro_require_billing = true;
214
-
215
  jQuery(document).ready(function() {
216
  jQuery("#pmpro_form, .pmpro_form").submit(function(event) {
217
-
218
- //double check in case a discount code made the level free
219
  if(pmpro_require_billing)
220
  {
221
  //build array for creating token
@@ -231,18 +231,18 @@
231
  ?>
232
  ,address_line1: jQuery('#baddress1').val(),
233
  address_line2: jQuery('#baddress2').val(),
234
- address_city: jQuery('#bcity').val(),
235
- address_state: jQuery('#bstate').val(),
236
- address_zip: jQuery('#bzipcode').val(),
237
  address_country: jQuery('#bcountry').val()
238
  <?php
239
  }
240
- ?>
241
  };
242
-
243
  if (jQuery('#bfirstname').length && jQuery('#blastname').length)
244
  args['name'] = jQuery.trim(jQuery('#bfirstname').val() + ' ' + jQuery('#blastname').val());
245
-
246
  //create token
247
  Stripe.createToken(args, stripeResponseHandler);
248
 
@@ -261,19 +261,19 @@
261
 
262
  //hide processing message
263
  jQuery('#pmpro_processing_message').css('visibility', 'hidden');
264
-
265
  // show the errors on the form
266
  alert(response.error.message);
267
  jQuery(".payment-errors").text(response.error.message);
268
  } else {
269
- var form$ = jQuery("#pmpro_form, .pmpro_form");
270
  // token contains id, last4, and card type
271
- var token = response['id'];
272
  // insert the token into the form so it gets submitted to the server
273
  form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
274
-
275
  console.log(response);
276
-
277
  //insert fields for other card fields
278
  if(jQuery('#CardType[name=CardType]').length)
279
  jQuery('#CardType').val(response['card']['brand']);
@@ -281,8 +281,8 @@
281
  form$.append("<input type='hidden' name='CardType' value='" + response['card']['brand'] + "'/>");
282
  form$.append("<input type='hidden' name='AccountNumber' value='XXXXXXXXXXXXX" + response['card']['last4'] + "'/>");
283
  form$.append("<input type='hidden' name='ExpirationMonth' value='" + ("0" + response['card']['exp_month']).slice(-2) + "'/>");
284
- form$.append("<input type='hidden' name='ExpirationYear' value='" + response['card']['exp_year'] + "'/>");
285
-
286
  // and submit
287
  form$.get(0).submit();
288
  }
@@ -291,20 +291,20 @@
291
  <?php
292
  }
293
  add_action("wp_head", "pmpro_stripe_javascript");
294
-
295
  //don't require the CVV
296
  function pmpro_stripe_dont_require_CVV($fields)
297
  {
298
- unset($fields['CVV']);
299
  return $fields;
300
  }
301
  add_filter("pmpro_required_billing_fields", "pmpro_stripe_dont_require_CVV");
302
  }
303
  }
304
-
305
  /**
306
  * Filtering orders at checkout.
307
- *
308
  * @since 1.8
309
  */
310
  static function pmpro_checkout_order($morder)
@@ -314,13 +314,13 @@
314
  {
315
  $morder->stripeToken = $_REQUEST['stripeToken'];
316
  }
317
-
318
  //stripe lite code to get name from other sources if available
319
  global $pmpro_stripe_lite, $current_user;
320
  if(!empty($pmpro_stripe_lite) && empty($morder->FirstName) && empty($morder->LastName))
321
  {
322
  if(!empty($current_user->ID))
323
- {
324
  $morder->FirstName = get_user_meta($current_user->ID, "first_name", true);
325
  $morder->LastName = get_user_meta($current_user->ID, "last_name", true);
326
  }
@@ -330,19 +330,19 @@
330
  $morder->LastName = $_REQUEST['last_name'];
331
  }
332
  }
333
-
334
  return $morder;
335
  }
336
-
337
  /**
338
  * Code to run after checkout
339
- *
340
  * @since 1.8
341
  */
342
- static function pmpro_after_checkout($user_id, $morder)
343
  {
344
  global $gateway;
345
-
346
  if($gateway == "stripe")
347
  {
348
  if(!empty($morder) && !empty($morer->Gateway) && !empty($morder->Gateway->customer) && !empty($morder->Gateway->customer->id))
@@ -351,7 +351,7 @@
351
  }
352
  }
353
  }
354
-
355
  /**
356
  * Check settings if billing address should be shown.
357
  * @since 1.8
@@ -361,24 +361,24 @@
361
  //check settings RE showing billing address
362
  if(!pmpro_getOption("stripe_billingaddress"))
363
  $include = false;
364
-
365
  return $include;
366
  }
367
-
368
  /**
369
  * Use our own payment fields at checkout. (Remove the name attributes.)
370
  * @since 1.8
371
  */
372
  static function pmpro_include_payment_information_fields($include)
373
- {
374
  //global vars
375
  global $pmpro_requirebilling, $pmpro_show_discount_code, $discount_code, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
376
-
377
  //get accepted credit cards
378
  $pmpro_accepted_credit_cards = pmpro_getOption("accepted_credit_cards");
379
  $pmpro_accepted_credit_cards = explode(",", $pmpro_accepted_credit_cards);
380
- $pmpro_accepted_credit_cards_string = pmpro_implodeToEnglish($pmpro_accepted_credit_cards);
381
-
382
  //include ours
383
  ?>
384
  <table id="pmpro_payment_information_fields" class="pmpro_checkout top1em" width="100%" cellpadding="0" cellspacing="0" border="0" <?php if(!$pmpro_requirebilling || apply_filters("pmpro_hide_payment_information_fields", false) ) { ?>style="display: none;"<?php } ?>>
@@ -388,8 +388,8 @@
388
  </tr>
389
  </thead>
390
  <tbody>
391
- <tr valign="top">
392
- <td>
393
  <?php
394
  $sslseal = pmpro_getOption("sslseal");
395
  if($sslseal)
@@ -409,7 +409,7 @@
409
  <select id="CardType" class=" <?php echo pmpro_getClassForField("CardType");?>">
410
  <?php foreach($pmpro_accepted_credit_cards as $cc) { ?>
411
  <option value="<?php echo $cc?>" <?php if($CardType == $cc) { ?>selected="selected"<?php } ?>><?php echo $cc?></option>
412
- <?php } ?>
413
  </select>
414
  </div>
415
  <?php
@@ -419,8 +419,8 @@
419
  ?>
420
  <input type="hidden" id="CardType" name="CardType" value="<?php echo esc_attr($CardType);?>" />
421
  <script>
422
- jQuery(document).ready(function() {
423
- jQuery('#AccountNumber').validateCreditCard(function(result) {
424
  var cardtypenames = {
425
  "amex":"American Express",
426
  "diners_club_carte_blanche":"Diners Club Carte Blanche",
@@ -433,23 +433,23 @@
433
  "visa":"Visa",
434
  "visa_electron":"Visa Electron"
435
  }
436
-
437
  if(result.card_type)
438
  jQuery('#CardType').val(cardtypenames[result.card_type.name]);
439
  else
440
  jQuery('#CardType').val('Unknown Card Type');
441
- });
442
  });
443
  </script>
444
  <?php
445
  }
446
  ?>
447
-
448
  <div class="pmpro_payment-account-number">
449
  <label for="AccountNumber"><?php _e('Card Number', 'pmpro');?></label>
450
  <input id="AccountNumber" class="input <?php echo pmpro_getClassForField("AccountNumber");?>" type="text" size="25" value="<?php echo esc_attr($AccountNumber)?>" autocomplete="off" />
451
  </div>
452
-
453
  <div class="pmpro_payment-expiration">
454
  <label for="ExpirationMonth"><?php _e('Expiration Date', 'pmpro');?></label>
455
  <select id="ExpirationMonth" class=" <?php echo pmpro_getClassForField("ExpirationMonth");?>">
@@ -474,9 +474,9 @@
474
  <?php
475
  }
476
  ?>
477
- </select>
478
  </div>
479
-
480
  <?php
481
  $pmpro_show_cvv = apply_filters("pmpro_show_cvv", true);
482
  if($pmpro_show_cvv)
@@ -489,7 +489,7 @@
489
  <?php
490
  }
491
  ?>
492
-
493
  <?php if($pmpro_show_discount_code) { ?>
494
  <div class="pmpro_payment-discount-code">
495
  <label for="discount_code"><?php _e('Discount Code', 'pmpro');?></label>
@@ -498,20 +498,20 @@
498
  <p id="discount_code_message" class="pmpro_message" style="display: none;"></p>
499
  </div>
500
  <?php } ?>
501
-
502
- </td>
503
  </tr>
504
  </tbody>
505
  </table>
506
  <?php
507
-
508
  //don't include the default
509
  return false;
510
  }
511
-
512
  /**
513
  * Fields shown on edit user page
514
- *
515
  * @since 1.8
516
  */
517
  static function user_profile_fields($user)
@@ -521,48 +521,48 @@
521
  $cycles = array( __('Day(s)', 'pmpro') => 'Day', __('Week(s)', 'pmpro') => 'Week', __('Month(s)', 'pmpro') => 'Month', __('Year(s)', 'pmpro') => 'Year' );
522
  $current_year = date("Y");
523
  $current_month = date("m");
524
-
525
  //make sure the current user has privileges
526
  $membership_level_capability = apply_filters("pmpro_edit_member_capability", "manage_options");
527
  if(!current_user_can($membership_level_capability))
528
  return false;
529
 
530
- //more privelges they should have
531
  $show_membership_level = apply_filters("pmpro_profile_show_membership_level", true, $user);
532
  if(!$show_membership_level)
533
  return false;
534
-
535
  //check that user has a current subscription at Stripe
536
  $last_order = new MemberOrder();
537
  $last_order->getLastMemberOrder($user->ID);
538
-
539
  //assume no sub to start
540
  $sub = false;
541
-
542
  //check that gateway is Stripe
543
  if($last_order->gateway == "stripe")
544
- {
545
  //is there a customer?
546
- $sub = $last_order->Gateway->getSubscription($last_order);
547
- }
548
-
549
  $customer_id = $user->pmpro_stripe_customerid;
550
-
551
  if(empty($sub))
552
  {
553
  //make sure we delete stripe updates
554
  update_user_meta($user->ID, "pmpro_stripe_updates", array());
555
-
556
  //if the last order has a sub id, let the admin know there is no sub at Stripe
557
  if(!empty($last_order) && $last_order->gateway == "stripe" && !empty($last_order->subscription_transaction_id) && strpos($last_order->subscription_transaction_id, "sub_") !== false)
558
  {
559
  ?>
560
  <p><strong>Note:</strong> Subscription <strong><?php echo $last_order->subscription_transaction_id;?></strong> could not be found at Stripe. It might have been deleted.</p>
561
  <?php
562
- }
563
  }
564
- else
565
- {
566
  ?>
567
  <h3><?php _e("Subscription Updates", "pmpro"); ?></h3>
568
  <p>
@@ -578,7 +578,7 @@
578
  <th><label for="membership_level"><?php _e("Update", "pmpro"); ?></label></th>
579
  <td id="updates_td">
580
  <?php
581
- $old_updates = $user->pmpro_stripe_updates;
582
  if(is_array($old_updates))
583
  {
584
  $updates = array_merge(
@@ -588,19 +588,19 @@
588
  }
589
  else
590
  $updates = array(array('template'=>true, 'when'=>'now', 'date_month'=>'', 'date_day'=>'', 'date_year'=>'', 'billing_amount'=>'', 'cycle_number'=>'', 'cycle_period'=>'Month'));
591
-
592
  foreach($updates as $update)
593
  {
594
  ?>
595
  <div class="updates_update" <?php if(!empty($update['template'])) { ?>style="display: none;"<?php } ?>>
596
- <select class="updates_when" name="updates_when[]">
597
  <option value="now" <?php selected($update['when'], "now");?>>Now</option>
598
  <option value="payment" <?php selected($update['when'], "payment");?>>After Next Payment</option>
599
  <option value="date" <?php selected($update['when'], "date");?>>On Date</option>
600
  </select>
601
  <span class="updates_date" <?php if($uwhen != "date") { ?>style="display: none;"<?php } ?>>
602
  <select name="updates_date_month[]">
603
- <?php
604
  for($i = 1; $i < 13; $i++)
605
  {
606
  ?>
@@ -619,7 +619,7 @@
619
  <small><?php _e('per', 'pmpro');?></small>
620
  <input name="updates_cycle_number[]" type="text" size="5" value="<?php echo esc_attr($update['cycle_number']);?>" />
621
  <select name="updates_cycle_period[]">
622
- <?php
623
  foreach ( $cycles as $name => $value ) {
624
  echo "<option value='$value'";
625
  if(!empty($update['cycle_period']) && $update['cycle_period'] == $value) echo " selected='selected'";
@@ -627,18 +627,18 @@
627
  }
628
  ?>
629
  </select>
630
- </span>
631
  <span>
632
- <a class="updates_remove" href="javascript:void(0);">Remove</a>
633
  </span>
634
  </div>
635
  <?php
636
  }
637
- ?>
638
  <p><a id="updates_new_update" href="javascript:void(0);">+ New Update</a></p>
639
  </td>
640
- </tr>
641
- </table>
642
  <script>
643
  jQuery(document).ready(function() {
644
  //function to update dropdowns/etc based on when field
@@ -648,45 +648,45 @@
648
  jQuery(when).parent().children('.updates_date').show();
649
  else
650
  jQuery(when).parent().children('.updates_date').hide();
651
-
652
  if(jQuery(when).val() == 'no')
653
  jQuery(when).parent().children('.updates_billing').hide();
654
  else
655
  jQuery(when).parent().children('.updates_billing').show();
656
- }
657
 
658
  //and update on page load
659
  jQuery('.updates_when').each(function() { if(jQuery(this).parent().css('display') != 'none') updateSubscriptionUpdateFields(this); });
660
-
661
  //add a new update when clicking to
662
  var num_updates_divs = <?php echo count($updates);?>;
663
  jQuery('#updates_new_update').click(function() {
664
  //get updates
665
  updates = jQuery('.updates_update').toArray();
666
-
667
  //clone the first one
668
- new_div = jQuery(updates[0]).clone();
669
-
670
  //append
671
  new_div.insertBefore('#updates_new_update');
672
-
673
  //update events
674
  addUpdateEvents()
675
-
676
  //unhide it
677
  new_div.show();
678
- updateSubscriptionUpdateFields(new_div.children('.updates_when'));
679
  });
680
-
681
  function addUpdateEvents()
682
  {
683
  //update when when changes
684
  jQuery('.updates_when').change(function() {
685
  updateSubscriptionUpdateFields(this);
686
  });
687
-
688
  //remove updates when clicking
689
- jQuery('.updates_remove').click(function() {
690
  jQuery(this).parent().parent().remove();
691
  });
692
  }
@@ -696,40 +696,40 @@
696
  <?php
697
  }
698
  }
699
-
700
  /**
701
  * Process fields from the edit user page
702
- *
703
  * @since 1.8
704
  */
705
  static function user_profile_fields_save($user_id)
706
  {
707
  global $wpdb;
708
-
709
  //check capabilities
710
  $membership_level_capability = apply_filters("pmpro_edit_member_capability", "manage_options");
711
  if(!current_user_can($membership_level_capability))
712
  return false;
713
-
714
  //make sure some value was passed
715
  if(!isset($_POST['updates_when']) || !is_array($_POST['updates_when']))
716
  return;
717
-
718
  //vars
719
  $updates = array();
720
  $next_on_date_update = "";
721
-
722
  //build array of updates (we skip the first because it's the template field for the JavaScript
723
  for($i = 1; $i < count($_POST['updates_when']); $i++)
724
  {
725
  $update = array();
726
-
727
  //all updates have these values
728
  $update['when'] = $_POST['updates_when'][$i];
729
  $update['billing_amount'] = $_POST['updates_billing_amount'][$i];
730
  $update['cycle_number'] = $_POST['updates_cycle_number'][$i];
731
  $update['cycle_period'] = $_POST['updates_cycle_period'][$i];
732
-
733
  //these values only for on date updates
734
  if($_POST['updates_when'][$i] == "date")
735
  {
@@ -737,17 +737,17 @@
737
  $update['date_day'] = str_pad($_POST['updates_date_day'][$i], 2, "0", STR_PAD_LEFT);
738
  $update['date_year'] = $_POST['updates_date_year'][$i];
739
  }
740
-
741
  //make sure the update is valid
742
  if(empty($update['cycle_number']))
743
  continue;
744
-
745
  //if when is now, update the subscription
746
  if($update['when'] == "now")
747
  {
748
  //get level for user
749
  $user_level = pmpro_getMembershipLevelForUser($user_id);
750
-
751
  //get current plan at Stripe to get payment date
752
  $last_order = new MemberOrder();
753
  $last_order->getLastMemberOrder($user_id);
@@ -755,30 +755,30 @@
755
  $last_order->Gateway->getCustomer($last_order);
756
 
757
  $subscription = $last_order->Gateway->getSubscription($last_order);
758
-
759
  if(!empty($subscription))
760
- {
761
  $end_timestamp = $subscription->current_period_end;
762
-
763
- //cancel the old subscription
764
  if(!$last_order->Gateway->cancelSubscriptionAtGateway($subscription))
765
  {
766
  //throw error and halt save
767
- function pmpro_stripe_user_profile_fields_save_error($errors, $update, $user)
768
  {
769
  $errors->add('pmpro_stripe_updates',__('Could not cancel the old subscription. Updates have not been processed.', 'pmpro'));
770
  }
771
  add_filter('user_profile_update_errors', 'pmpro_stripe_user_profile_fields_save_error', 10, 3);
772
-
773
  //stop processing updates
774
  return;
775
  }
776
  }
777
-
778
  //if we didn't get an end date, let's set one one cycle out
779
  if(empty($end_timestamp))
780
  $end_timestamp = strtotime("+" . $update['cycle_number'] . " " . $update['cycle_period'], current_time('timestamp'));
781
-
782
  //build order object
783
  $update_order = new MemberOrder();
784
  $update_order->setGateway('stripe');
@@ -789,14 +789,14 @@
789
  $update_order->PaymentAmount = $update['billing_amount'];
790
  $update_order->ProfileStartDate = date("Y-m-d", $end_timestamp);
791
  $update_order->BillingPeriod = $update['cycle_period'];
792
- $update_order->BillingFrequency = $update['cycle_number'];
793
-
794
  //need filter to reset ProfileStartDate
795
  add_filter('pmpro_profile_start_date', create_function('$startdate, $order', 'return "' . $update_order->ProfileStartDate . 'T0:0:0";'), 10, 2);
796
-
797
  //update subscription
798
  $update_order->Gateway->subscribe($update_order, false);
799
-
800
  //update membership
801
  $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users
802
  SET billing_amount = '" . esc_sql($update['billing_amount']) . "',
@@ -808,13 +808,13 @@
808
  AND membership_id = '" . esc_sql($last_order->membership_id) . "'
809
  AND status = 'active'
810
  LIMIT 1";
811
-
812
  $wpdb->query($sqlQuery);
813
-
814
  //save order so we know which plan to look for at stripe (order code = plan id)
815
  $update_order->status = "success";
816
  $update_order->saveOrder();
817
-
818
  continue;
819
  }
820
  elseif($update['when'] == 'date')
@@ -824,41 +824,41 @@
824
  else
825
  $next_on_date_update = $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'];
826
  }
827
-
828
- //add to array
829
- $updates[] = $update;
830
  }
831
-
832
  //save in user meta
833
  update_user_meta($user_id, "pmpro_stripe_updates", $updates);
834
-
835
  //save date of next on-date update to make it easier to query for these in cron job
836
  update_user_meta($user_id, "pmpro_stripe_next_on_date_update", $next_on_date_update);
837
  }
838
-
839
  /**
840
  * Cron activation for subscription updates.
841
- *
842
  * @since 1.8
843
  */
844
  static function pmpro_activation()
845
  {
846
  wp_schedule_event(time(), 'daily', 'pmpro_cron_stripe_subscription_updates');
847
  }
848
-
849
  /**
850
  * Cron deactivation for subscription updates.
851
- *
852
  * @since 1.8
853
  */
854
  static function pmpro_deactivation()
855
  {
856
  wp_clear_scheduled_hook('pmpro_cron_stripe_subscription_updates');
857
  }
858
-
859
  /**
860
  * Cron job for subscription updates.
861
- *
862
  * @since 1.8
863
  */
864
  static function pmpro_cron_stripe_subscription_updates()
@@ -866,41 +866,41 @@
866
  global $wpdb;
867
 
868
  //get all updates for today (or before today)
869
- $sqlQuery = "SELECT *
870
- FROM $wpdb->usermeta
871
- WHERE meta_key = 'pmpro_stripe_next_on_date_update'
872
- AND meta_value IS NOT NULL
873
- AND meta_value < '" . date("Y-m-d", strtotime("+1 day")) . "'";
874
  $updates = $wpdb->get_results($sqlQuery);
875
-
876
  if(!empty($updates))
877
- {
878
  //loop through
879
  foreach($updates as $update)
880
- {
881
  //pull values from update
882
  $user_id = $update->user_id;
883
-
884
  $user = get_userdata($user_id);
885
  $user_updates = $user->pmpro_stripe_updates;
886
- $next_on_date_update = "";
887
-
888
  //loop through updates looking for updates happening today or earlier
889
  foreach($user_updates as $key => $update)
890
- {
891
  if($update['when'] == 'date' &&
892
  $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'] <= date("Y-m-d")
893
  )
894
  {
895
  //get level for user
896
  $user_level = pmpro_getMembershipLevelForUser($user_id);
897
-
898
  //get current plan at Stripe to get payment date
899
  $last_order = new MemberOrder();
900
  $last_order->getLastMemberOrder($user_id);
901
  $last_order->setGateway('stripe');
902
  $last_order->Gateway->getCustomer($last_order);
903
-
904
  if(!empty($last_order->Gateway->customer))
905
  {
906
  //find the first subscription
@@ -910,10 +910,10 @@
910
  $end_timestamp = $first_sub['current_period_end'];
911
  }
912
  }
913
-
914
  //if we didn't get an end date, let's set one one cycle out
915
  $end_timestamp = strtotime("+" . $update['cycle_number'] . " " . $update['cycle_period']);
916
-
917
  //build order object
918
  $update_order = new MemberOrder();
919
  $update_order->setGateway('stripe');
@@ -925,28 +925,28 @@
925
  $update_order->ProfileStartDate = date("Y-m-d", $end_timestamp);
926
  $update_order->BillingPeriod = $update['cycle_period'];
927
  $update_order->BillingFrequency = $update['cycle_number'];
928
-
929
  //update subscription
930
  $update_order->Gateway->subscribe($update_order, false);
931
-
932
  //update membership
933
- $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users
934
- SET billing_amount = '" . esc_sql($update['billing_amount']) . "',
935
- cycle_number = '" . esc_sql($update['cycle_number']) . "',
936
- cycle_period = '" . esc_sql($update['cycle_period']) . "'
937
- WHERE user_id = '" . esc_sql($user_id) . "'
938
- AND membership_id = '" . esc_sql($last_order->membership_id) . "'
939
- AND status = 'active'
940
  LIMIT 1";
941
-
942
  $wpdb->query($sqlQuery);
943
-
944
  //save order
945
  $update_order->status = "success";
946
  $update_order->save();
947
-
948
  //remove update from list
949
- unset($user_updates[$key]);
950
  }
951
  elseif($update['when'] == 'date')
952
  {
@@ -957,19 +957,19 @@
957
  $next_on_date_update = $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'];
958
  }
959
  }
960
-
961
  //save updates in case we removed some
962
  update_user_meta($user_id, "pmpro_stripe_updates", $user_updates);
963
-
964
  //save date of next on-date update to make it easier to query for these in cron job
965
  update_user_meta($user_id, "pmpro_stripe_next_on_date_update", $next_on_date_update);
966
  }
967
  }
968
  }
969
-
970
  /**
971
  * Process checkout and decide if a charge and or subscribe is needed
972
- *
973
  * @since 1.4
974
  */
975
  function process(&$order)
@@ -986,7 +986,7 @@
986
  if($this->charge($order))
987
  {
988
  if(pmpro_isLevelRecurring($order->membership_level))
989
- {
990
  if($this->subscribe($order))
991
  {
992
  //yay!
@@ -1001,7 +1001,7 @@
1001
  else
1002
  {
1003
  //only a one time charge
1004
- $order->status = "success"; //saved on checkout page
1005
  return true;
1006
  }
1007
  }
@@ -1011,39 +1011,39 @@
1011
  $order->error = __("Unknown error: Initial payment failed.", "pmpro");
1012
  return false;
1013
  }
1014
- }
1015
- }
1016
-
1017
  /**
1018
  * Make a one-time charge with Stripe
1019
- *
1020
  * @since 1.4
1021
  */
1022
  function charge(&$order)
1023
  {
1024
  global $pmpro_currency;
1025
-
1026
  //create a code for the order
1027
  if(empty($order->code))
1028
  $order->code = $order->getRandomCode();
1029
-
1030
- //what amount to charge?
1031
  $amount = $order->InitialPayment;
1032
-
1033
  //tax
1034
  $order->subtotal = $amount;
1035
  $tax = $order->getTax(true);
1036
  $amount = round((float)$order->subtotal + (float)$tax, 2);
1037
-
1038
  //create a customer
1039
  $result = $this->getCustomer($order);
1040
-
1041
  if(empty($result))
1042
- {
1043
  //failed to create customer
1044
  return false;
1045
- }
1046
-
1047
  //charge
1048
  try
1049
  {
@@ -1063,13 +1063,13 @@
1063
  $order->shorterror = $order->error;
1064
  return false;
1065
  }
1066
-
1067
  if(empty($response["failure_message"]))
1068
  {
1069
  //successful charge
1070
  $order->payment_transaction_id = $response["id"];
1071
- $order->updateStatus("success");
1072
- return true;
1073
  }
1074
  else
1075
  {
@@ -1078,12 +1078,12 @@
1078
  $order->error = $response['failure_message'];
1079
  $order->shorterror = $response['failure_message'];
1080
  return false;
1081
- }
1082
  }
1083
-
1084
  /**
1085
  * Get a Stripe customer object.
1086
- *
1087
  * If $this->customer is set, it returns it.
1088
  * It first checks if the order has a subscription_transaction_id. If so, that's the customer id.
1089
  * If not, it checks for a user_id on the order and searches for a customer id in the user meta.
@@ -1097,42 +1097,42 @@
1097
  function getCustomer(&$order = false, $force = false)
1098
  {
1099
  global $current_user;
1100
-
1101
  //already have it?
1102
  if(!empty($this->customer) && !$force)
1103
  return $this->customer;
1104
-
1105
  //figure out user_id and user
1106
  if(!empty($order->user_id))
1107
  $user_id = $order->user_id;
1108
-
1109
  //if no id passed, check the current user
1110
  if(empty($user_id) && !empty($current_user->ID))
1111
  $user_id = $current_user->ID;
1112
-
1113
  if(!empty($user_id))
1114
  $user = get_userdata($user_id);
1115
  else
1116
  $user = NULL;
1117
-
1118
  //transaction id?
1119
  if(!empty($order->subscription_transaction_id) && strpos($order->subscription_transaction_id, "cus_") !== false)
1120
  $customer_id = $order->subscription_transaction_id;
1121
  else
1122
  {
1123
- //try based on user id
1124
  if(!empty($user_id))
1125
- {
1126
- $customer_id = get_user_meta($user_id, "pmpro_stripe_customerid", true);
1127
  }
1128
- }
1129
-
1130
  //get name and email values from order in case we update
1131
  $name = trim($order->FirstName . " " . $order->LastName);
1132
  if(empty($name) && !empty($user->ID))
1133
  {
1134
  $name = trim($user->first_name . " " . $user->last_name);
1135
-
1136
  //still empty?
1137
  if(empty($name))
1138
  $name = $user->user_login;
@@ -1140,41 +1140,41 @@
1140
  elseif(empty($name))
1141
  $name = "No Name";
1142
 
1143
- $email = $order->Email;
1144
  if(empty($email) && !empty($user->ID))
1145
  {
1146
  $email = $user->user_email;
1147
  }
1148
  elseif(empty($email))
1149
  $email = "No Email";
1150
-
1151
  //check for an existing stripe customer
1152
  if(!empty($customer_id))
1153
  {
1154
  try
1155
  {
1156
  $this->customer = Stripe_Customer::retrieve($customer_id);
1157
-
1158
  //update the customer description and card
1159
  if(!empty($order->stripeToken))
1160
- {
1161
  $this->customer->description = $name . " (" . $email . ")";
1162
  $this->customer->email = $email;
1163
  $this->customer->card = $order->stripeToken;
1164
  $this->customer->save();
1165
  }
1166
-
1167
  return $this->customer;
1168
  }
1169
  catch (Exception $e)
1170
  {
1171
- //assume no customer found
1172
  }
1173
  }
1174
-
1175
  //no customer id, create one
1176
  if(!empty($order->stripeToken))
1177
- {
1178
  try
1179
  {
1180
  $this->customer = Stripe_Customer::create(array(
@@ -1189,11 +1189,11 @@
1189
  $order->shorterror = $order->error;
1190
  return false;
1191
  }
1192
-
1193
  if(!empty($user_id))
1194
  {
1195
  //user logged in/etc
1196
- update_user_meta($user_id, "pmpro_stripe_customerid", $this->customer->id);
1197
  }
1198
  else
1199
  {
@@ -1210,29 +1210,29 @@
1210
 
1211
  return apply_filters('pmpro_stripe_create_customer', $this->customer);
1212
  }
1213
-
1214
- return false;
1215
  }
1216
-
1217
  /**
1218
  * Get a Stripe subscription from a PMPro order
1219
- *
1220
  * @since 1.8
1221
  */
1222
- function getSubscription(&$order)
1223
  {
1224
  global $wpdb;
1225
-
1226
  //no order?
1227
  if(empty($order) || empty($order->code))
1228
- return false;
1229
-
1230
  $result = $this->getCustomer($order, true); //force so we don't get a cached sub for someone else
1231
-
1232
  //no customer?
1233
  if(empty($result))
1234
  return false;
1235
-
1236
  //is there a subscription transaction id pointing to a sub?
1237
  if(!empty($order->subscription_transaction_id) && strpos($order->subscription_transaction_id, "sub_") !== false)
1238
  {
@@ -1246,49 +1246,49 @@
1246
  $order->shorterror = $order->error;
1247
  return false;
1248
  }
1249
-
1250
  return $sub;
1251
- }
1252
-
1253
  //find subscription based on customer id and order/plan id
1254
- $subscriptions = $this->customer->subscriptions->all();
1255
-
1256
  //no subscriptions
1257
  if(empty($subscriptions) || empty($subscriptions->data))
1258
- return false;
1259
-
1260
  //we really want to test against the order codes of all orders with the same subscription_transaction_id (customer id)
1261
  $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')");
1262
-
1263
  //find the one for this order
1264
  foreach($subscriptions->data as $sub)
1265
- {
1266
  if(in_array($sub->plan->id, $codes))
1267
  {
1268
  return $sub;
1269
  }
1270
  }
1271
-
1272
- //didn't find anything yet
1273
  return false;
1274
  }
1275
-
1276
  /**
1277
  * Create a new subscription with Stripe
1278
- *
1279
  * @since 1.4
1280
  */
1281
  function subscribe(&$order, $checkout = true)
1282
  {
1283
  global $pmpro_currency;
1284
-
1285
  //create a code for the order
1286
  if(empty($order->code))
1287
  $order->code = $order->getRandomCode();
1288
-
1289
  //filter order before subscription. use with care.
1290
  $order = apply_filters("pmpro_subscribe_order", $order, $this);
1291
-
1292
  //figure out the user
1293
  if(!empty($order->user_id))
1294
  $user_id = $order->user_id;
@@ -1297,28 +1297,28 @@
1297
  global $current_user;
1298
  $user_id = $current_user->ID;
1299
  }
1300
-
1301
  //setup customer
1302
  $result = $this->getCustomer($order);
1303
  if(empty($result))
1304
  return false; //error retrieving customer
1305
-
1306
  //set subscription id to custom id
1307
  $order->subscription_transaction_id = $this->customer['id']; //transaction id is the customer id, we save it in user meta later too
1308
-
1309
  //figure out the amounts
1310
  $amount = $order->PaymentAmount;
1311
- $amount_tax = $order->getTaxForPrice($amount);
1312
  $amount = round((float)$amount + (float)$amount_tax, 2);
1313
 
1314
  /*
1315
  There are two parts to the trial. Part 1 is simply the delay until the first payment
1316
  since we are doing the first payment as a separate transaction.
1317
  The second part is the actual "trial" set by the admin.
1318
-
1319
  Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
1320
  */
1321
- //figure out the trial length (first payment handled by initial charge)
1322
  if($order->BillingPeriod == "Year")
1323
  $trial_period_days = $order->BillingFrequency * 365; //annual
1324
  elseif($order->BillingPeriod == "Day")
@@ -1327,18 +1327,18 @@
1327
  $trial_period_days = $order->BillingFrequency * 7; //weekly
1328
  else
1329
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
1330
-
1331
  //convert to a profile start date
1332
  $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
1333
-
1334
  //filter the start date
1335
- $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
1336
-
1337
- //convert back to days
1338
  $trial_period_days = ceil(abs(strtotime(date("Y-m-d"), current_time("timestamp")) - strtotime($order->ProfileStartDate, current_time("timestamp"))) / 86400);
1339
 
1340
  //for free trials, just push the start date of the subscription back
1341
- if(!empty($order->TrialBillingCycles) && $order->TrialAmount == 0)
1342
  {
1343
  $trialOccurrences = (int)$order->TrialBillingCycles;
1344
  if($order->BillingPeriod == "Year")
@@ -1354,25 +1354,25 @@
1354
  {
1355
  /*
1356
  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)
1357
-
1358
  This will force TrialBillingCycles > 1 to act as if they were 1
1359
- */
1360
  $new_user_updates = array();
1361
  $new_user_updates[] = array(
1362
  'when' => 'payment',
1363
  'billing_amount' => $order->PaymentAmount,
1364
  'cycle_period' => $order->BillingPeriod,
1365
  'cycle_number' => $order->BillingFrequency
1366
- );
1367
-
1368
  //now amount to equal the trial #s
1369
  $amount = $order->TrialAmount;
1370
  $amount_tax = $order->getTaxForPrice($amount);
1371
  $amount = round((float)$amount + (float)$amount_tax, 2);
1372
- }
1373
-
1374
  //create a plan
1375
- try
1376
  {
1377
  $plan = array(
1378
  "amount" => $amount * 100,
@@ -1384,7 +1384,7 @@
1384
  "id" => $order->code
1385
  );
1386
 
1387
- $plan = Stripe_Plan::create(apply_filters('pmpro_stripe_create_plan_array', $plan));
1388
  }
1389
  catch (Exception $e)
1390
  {
@@ -1392,54 +1392,55 @@
1392
  $order->shorterror = $order->error;
1393
  return false;
1394
  }
1395
-
1396
  //before subscribing, let's clear out the updates so we don't trigger any during sub
1397
  if(!empty($user_id))
1398
- {
1399
  $old_user_updates = get_user_meta($user_id, "pmpro_stripe_updates", true);
1400
- update_user_meta($user_id, "pmpro_stripe_updates", array());
1401
- }
1402
-
1403
  if(empty($order->subscription_transaction_id) && !empty($this->customer['id']))
1404
  $order->subscription_transaction_id = $this->customer['id'];
1405
-
1406
  //subscribe to the plan
1407
  try
1408
  {
1409
- $result = $this->customer->subscriptions->create(array("plan" => $order->code));
 
1410
  }
1411
  catch (Exception $e)
1412
  {
1413
  //try to delete the plan
1414
  $plan->delete();
1415
-
1416
  //give the user any old updates back
1417
  if(!empty($user_id))
1418
  update_user_meta($user_id, "pmpro_stripe_updates", $old_user_updates);
1419
-
1420
  //return error
1421
  $order->error = __("Error subscribing customer to plan with Stripe:", "pmpro") . $e->getMessage();
1422
  $order->shorterror = $order->error;
1423
  return false;
1424
  }
1425
-
1426
  //delete the plan
1427
  $plan = Stripe_Plan::retrieve($order->code);
1428
- $plan->delete();
1429
 
1430
- //if we got this far, we're all good
1431
- $order->status = "success";
1432
  $order->subscription_transaction_id = $result['id'];
1433
-
1434
  //save new updates if this is at checkout
1435
  if($checkout)
1436
- {
1437
  //empty out updates unless set above
1438
  if(empty($new_user_updates))
1439
  $new_user_updates = array();
1440
-
1441
  //update user meta
1442
- if(!empty($user_id))
1443
  update_user_meta($user_id, "pmpro_stripe_updates", $new_user_updates);
1444
  else
1445
  {
@@ -1459,33 +1460,33 @@
1459
  //give them their old updates back
1460
  update_user_meta($user_id, "pmpro_stripe_updates", $old_user_updates);
1461
  }
1462
-
1463
  return true;
1464
- }
1465
-
1466
  /**
1467
  * Helper method to update the customer info via getCustomer
1468
- *
1469
  * @since 1.4
1470
  */
1471
  function update(&$order)
1472
  {
1473
  //we just have to run getCustomer which will look for the customer and update it with the new token
1474
  $result = $this->getCustomer($order);
1475
-
1476
  if(!empty($result))
1477
  {
1478
  return true;
1479
- }
1480
  else
1481
  {
1482
  return false; //couldn't find the customer
1483
  }
1484
  }
1485
-
1486
  /**
1487
  * Cancel a subscription at Stripe
1488
- *
1489
  * @since 1.4
1490
  */
1491
  function cancel(&$order, $update_status = true)
@@ -1493,21 +1494,21 @@
1493
  //no matter what happens below, we're going to cancel the order in our system
1494
  if($update_status)
1495
  $order->updateStatus("cancelled");
1496
-
1497
  //require a subscription id
1498
  if(empty($order->subscription_transaction_id))
1499
  return false;
1500
-
1501
  //find the customer
1502
- $result = $this->getCustomer($order);
1503
-
1504
  if(!empty($result))
1505
  {
1506
  //find subscription with this order code
1507
- $subscription = $this->getSubscription($order);
1508
 
1509
  if(!empty($subscription))
1510
- {
1511
  if($this->cancelSubscriptionAtGateway($subscription))
1512
  {
1513
  //we're okay, going to return true later
@@ -1516,17 +1517,17 @@
1516
  {
1517
  $order->error = __("Could not cancel old subscription.", "pmpro");
1518
  $order->shorterror = $order->error;
1519
-
1520
- return false;
1521
- }
1522
- }
1523
-
1524
  /*
1525
  Clear updates for this user. (But not if checking out, we would have already done that.)
1526
  */
1527
  if(empty($_REQUEST['submit-checkout']))
1528
  update_user_meta($order->user_id, "pmpro_stripe_updates", array());
1529
-
1530
  return true;
1531
  }
1532
  else
@@ -1534,12 +1535,12 @@
1534
  $order->error = __("Could not find the customer.", "pmpro");
1535
  $order->shorterror = $order->error;
1536
  return false; //no customer found
1537
- }
1538
- }
1539
-
1540
  /**
1541
  * Helper method to cancel a subscription at Stripe and also clear up any upaid invoices.
1542
- *
1543
  * @since 1.8
1544
  */
1545
  function cancelSubscriptionAtGateway($subscription)
@@ -1547,50 +1548,50 @@
1547
  //need a valid sub
1548
  if(empty($subscription->id))
1549
  return false;
1550
-
1551
  //make sure we get the customer for this subscription
1552
  $order = new MemberOrder();
1553
- $order->getLastMemberOrderBySubscriptionTransactionID($subscription->id);
1554
-
1555
  //no order?
1556
  if(empty($order))
1557
  {
1558
  //lets cancel anyway, but this is suspicious
1559
  $r = $subscription->cancel();
1560
-
1561
  return true;
1562
  }
1563
-
1564
  //okay have an order, so get customer so we can cancel invoices too
1565
  $this->getCustomer($order);
1566
-
1567
  //get open invoices
1568
  $invoices = $this->customer->invoices();
1569
  $invoices = $invoices->all();
1570
-
1571
  //found it, cancel it
1572
  try
1573
- {
1574
  //find any open invoices for this subscription and forgive them
1575
  if(!empty($invoices))
1576
  {
1577
  foreach($invoices->data as $invoice)
1578
- {
1579
  if(!$invoice->closed && $invoice->subscription == $subscription->id)
1580
- {
1581
  $invoice->closed = true;
1582
  $invoice->save();
1583
  }
1584
  }
1585
- }
1586
-
1587
  //cancel
1588
  $r = $subscription->cancel();
1589
-
1590
  return true;
1591
  }
1592
  catch(Exception $e)
1593
- {
1594
  return false;
1595
  }
1596
  }
1
+ <?php
2
  //include pmprogateway
3
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
+
5
  //load classes init method
6
  add_action('init', array('PMProGateway_stripe', 'init'));
7
+
8
  /**
9
  * PMProGateway_stripe Class
10
  *
16
  {
17
  /**
18
  * Stripe Class Constructor
19
+ *
20
  * @since 1.4
21
  */
22
  function PMProGateway_stripe($gateway = NULL)
23
  {
24
  $this->gateway = $gateway;
25
  $this->gateway_environment = pmpro_getOption("gateway_environment");
26
+
27
+ $this->loadStripeLibrary();
28
  Stripe::setApiKey(pmpro_getOption("stripe_secretkey"));
29
+
30
  return $this->gateway;
31
+ }
32
+
33
  /**
34
  * Load the Stripe API library.
35
+ *
36
  * @since 1.8
37
+ * Moved into a method in version 1.8 so we only load it when needed.
38
  */
39
  function loadStripeLibrary()
40
  {
42
  if(!class_exists("Stripe"))
43
  require_once(dirname(__FILE__) . "/../../includes/lib/Stripe/Stripe.php");
44
  }
45
+
46
  /**
47
  * Run on WP init
48
+ *
49
  * @since 1.8
50
  */
51
  static function init()
52
+ {
53
  //make sure Stripe is a gateway option
54
  add_filter('pmpro_gateways', array('PMProGateway_stripe', 'pmpro_gateways'));
55
+
56
  //add fields to payment settings
57
  add_filter('pmpro_payment_options', array('PMProGateway_stripe', 'pmpro_payment_options'));
58
  add_filter('pmpro_payment_option_fields', array('PMProGateway_stripe', 'pmpro_payment_option_fields'), 10, 2);
59
+
60
  //add some fields to edit user page (Updates)
61
  add_action('pmpro_after_membership_level_profile_fields', array('PMProGateway_stripe', 'user_profile_fields'));
62
  add_action('profile_update', array('PMProGateway_stripe', 'user_profile_fields_save'));
63
+
64
  //old global RE showing billing address or not
65
  global $pmpro_stripe_lite;
66
  $pmpro_stripe_lite = apply_filters("pmpro_stripe_lite", !pmpro_getOption("stripe_billingaddress")); //default is oposite of the stripe_billingaddress setting
67
+
68
  //updates cron
69
  add_action('pmpro_activation', array('PMProGateway_stripe', 'pmpro_activation'));
70
  add_action('pmpro_deactivation', array('PMProGateway_stripe', 'pmpro_deactivation'));
71
  add_action('pmpro_cron_stripe_subscription_updates', array('PMProGateway_stripe', 'pmpro_cron_stripe_subscription_updates'));
72
+
73
  //code to add at checkout if Stripe is the current gateway
74
  $gateway = pmpro_getOption("gateway");
75
  if($gateway == "stripe")
76
  {
77
+ add_action('pmpro_checkout_preheader', array('PMProGateway_stripe', 'pmpro_checkout_preheader'));
78
  add_filter('pmpro_checkout_order', array('PMProGateway_stripe', 'pmpro_checkout_order'));
79
  add_filter('pmpro_include_billing_address_fields', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
80
  add_filter('pmpro_include_cardtype_field', array('PMProGateway_stripe', 'pmpro_include_billing_address_fields'));
81
  add_filter('pmpro_include_payment_information_fields', array('PMProGateway_stripe', 'pmpro_include_payment_information_fields'));
82
  }
83
  }
84
+
85
  /**
86
  * Make sure Stripe is in the gateways list
87
+ *
88
  * @since 1.8
89
  */
90
  static function pmpro_gateways($gateways)
91
  {
92
  if(empty($gateways['stripe']))
93
  $gateways['stripe'] = __('Stripe', 'pmpro');
94
+
95
  return $gateways;
96
  }
97
+
98
  /**
99
  * Get a list of payment options that the Stripe gateway needs/supports.
100
+ *
101
  * @since 1.8
102
  */
103
  static function getGatewayOptions()
104
+ {
105
  $options = array(
106
  'sslseal',
107
  'nuclear_HTTPS',
115
  'tax_rate',
116
  'accepted_credit_cards'
117
  );
118
+
119
  return $options;
120
  }
121
+
122
  /**
123
  * Set payment options for payment settings page.
124
+ *
125
  * @since 1.8
126
  */
127
  static function pmpro_payment_options($options)
128
+ {
129
  //get stripe options
130
  $stripe_options = PMProGateway_stripe::getGatewayOptions();
131
+
132
  //merge with others.
133
  $options = array_merge($stripe_options, $options);
134
+
135
  return $options;
136
  }
137
+
138
  /**
139
  * Display fields for Stripe options.
140
+ *
141
  * @since 1.8
142
  */
143
  static function pmpro_payment_option_fields($values, $gateway)
144
  {
145
  ?>
146
+ <tr class="pmpro_settings_divider gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
147
  <td colspan="2">
148
  <?php _e('Stripe Settings', 'pmpro'); ?>
149
  </td>
171
  <td>
172
  <select id="stripe_billingaddress" name="stripe_billingaddress">
173
  <option value="0" <?php if(empty($values['stripe_billingaddress'])) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
174
+ <option value="1" <?php if(!empty($values['stripe_billingaddress'])) { ?>selected="selected"<?php } ?>><?php _e('Yes', 'pmpro');?></option>
175
  </select>
176
  <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>", 'pmpro');?></small>
177
  </td>
183
  <td>
184
  <p><?php _e('To fully integrate with Stripe, be sure to set your Web Hook URL to', 'pmpro');?> <pre><?php echo admin_url("admin-ajax.php") . "?action=stripe_webhook";?></pre></p>
185
  </td>
186
+ </tr>
187
  <?php
188
  }
189
+
190
  /**
191
  * Code added to checkout preheader.
192
+ *
193
  * @since 1.8
194
  */
195
  static function pmpro_checkout_preheader()
196
+ {
197
  global $gateway, $pmpro_level;
198
+
199
  if($gateway == "stripe" && !pmpro_isLevelFree($pmpro_level))
200
  {
201
  //stripe js library
202
  wp_enqueue_script("stripe", "https://js.stripe.com/v2/", array(), NULL);
203
+
204
  //stripe js code for checkout
205
  function pmpro_stripe_javascript()
206
  {
207
  global $pmpro_gateway, $pmpro_level, $pmpro_stripe_lite;
208
  ?>
209
  <script type="text/javascript">
210
+ // this identifies your website in the createToken call below
211
  Stripe.setPublishableKey('<?php echo pmpro_getOption("stripe_publishablekey"); ?>');
212
+
213
+ var pmpro_require_billing = true;
214
+
215
  jQuery(document).ready(function() {
216
  jQuery("#pmpro_form, .pmpro_form").submit(function(event) {
217
+
218
+ //double check in case a discount code made the level free
219
  if(pmpro_require_billing)
220
  {
221
  //build array for creating token
231
  ?>
232
  ,address_line1: jQuery('#baddress1').val(),
233
  address_line2: jQuery('#baddress2').val(),
234
+ address_city: jQuery('#bcity').val(),
235
+ address_state: jQuery('#bstate').val(),
236
+ address_zip: jQuery('#bzipcode').val(),
237
  address_country: jQuery('#bcountry').val()
238
  <?php
239
  }
240
+ ?>
241
  };
242
+
243
  if (jQuery('#bfirstname').length && jQuery('#blastname').length)
244
  args['name'] = jQuery.trim(jQuery('#bfirstname').val() + ' ' + jQuery('#blastname').val());
245
+
246
  //create token
247
  Stripe.createToken(args, stripeResponseHandler);
248
 
261
 
262
  //hide processing message
263
  jQuery('#pmpro_processing_message').css('visibility', 'hidden');
264
+
265
  // show the errors on the form
266
  alert(response.error.message);
267
  jQuery(".payment-errors").text(response.error.message);
268
  } else {
269
+ var form$ = jQuery("#pmpro_form, .pmpro_form");
270
  // token contains id, last4, and card type
271
+ var token = response['id'];
272
  // insert the token into the form so it gets submitted to the server
273
  form$.append("<input type='hidden' name='stripeToken' value='" + token + "'/>");
274
+
275
  console.log(response);
276
+
277
  //insert fields for other card fields
278
  if(jQuery('#CardType[name=CardType]').length)
279
  jQuery('#CardType').val(response['card']['brand']);
281
  form$.append("<input type='hidden' name='CardType' value='" + response['card']['brand'] + "'/>");
282
  form$.append("<input type='hidden' name='AccountNumber' value='XXXXXXXXXXXXX" + response['card']['last4'] + "'/>");
283
  form$.append("<input type='hidden' name='ExpirationMonth' value='" + ("0" + response['card']['exp_month']).slice(-2) + "'/>");
284
+ form$.append("<input type='hidden' name='ExpirationYear' value='" + response['card']['exp_year'] + "'/>");
285
+
286
  // and submit
287
  form$.get(0).submit();
288
  }
291
  <?php
292
  }
293
  add_action("wp_head", "pmpro_stripe_javascript");
294
+
295
  //don't require the CVV
296
  function pmpro_stripe_dont_require_CVV($fields)
297
  {
298
+ unset($fields['CVV']);
299
  return $fields;
300
  }
301
  add_filter("pmpro_required_billing_fields", "pmpro_stripe_dont_require_CVV");
302
  }
303
  }
304
+
305
  /**
306
  * Filtering orders at checkout.
307
+ *
308
  * @since 1.8
309
  */
310
  static function pmpro_checkout_order($morder)
314
  {
315
  $morder->stripeToken = $_REQUEST['stripeToken'];
316
  }
317
+
318
  //stripe lite code to get name from other sources if available
319
  global $pmpro_stripe_lite, $current_user;
320
  if(!empty($pmpro_stripe_lite) && empty($morder->FirstName) && empty($morder->LastName))
321
  {
322
  if(!empty($current_user->ID))
323
+ {
324
  $morder->FirstName = get_user_meta($current_user->ID, "first_name", true);
325
  $morder->LastName = get_user_meta($current_user->ID, "last_name", true);
326
  }
330
  $morder->LastName = $_REQUEST['last_name'];
331
  }
332
  }
333
+
334
  return $morder;
335
  }
336
+
337
  /**
338
  * Code to run after checkout
339
+ *
340
  * @since 1.8
341
  */
342
+ static function pmpro_after_checkout($user_id, $morder)
343
  {
344
  global $gateway;
345
+
346
  if($gateway == "stripe")
347
  {
348
  if(!empty($morder) && !empty($morer->Gateway) && !empty($morder->Gateway->customer) && !empty($morder->Gateway->customer->id))
351
  }
352
  }
353
  }
354
+
355
  /**
356
  * Check settings if billing address should be shown.
357
  * @since 1.8
361
  //check settings RE showing billing address
362
  if(!pmpro_getOption("stripe_billingaddress"))
363
  $include = false;
364
+
365
  return $include;
366
  }
367
+
368
  /**
369
  * Use our own payment fields at checkout. (Remove the name attributes.)
370
  * @since 1.8
371
  */
372
  static function pmpro_include_payment_information_fields($include)
373
+ {
374
  //global vars
375
  global $pmpro_requirebilling, $pmpro_show_discount_code, $discount_code, $CardType, $AccountNumber, $ExpirationMonth, $ExpirationYear;
376
+
377
  //get accepted credit cards
378
  $pmpro_accepted_credit_cards = pmpro_getOption("accepted_credit_cards");
379
  $pmpro_accepted_credit_cards = explode(",", $pmpro_accepted_credit_cards);
380
+ $pmpro_accepted_credit_cards_string = pmpro_implodeToEnglish($pmpro_accepted_credit_cards);
381
+
382
  //include ours
383
  ?>
384
  <table id="pmpro_payment_information_fields" class="pmpro_checkout top1em" width="100%" cellpadding="0" cellspacing="0" border="0" <?php if(!$pmpro_requirebilling || apply_filters("pmpro_hide_payment_information_fields", false) ) { ?>style="display: none;"<?php } ?>>
388
  </tr>
389
  </thead>
390
  <tbody>
391
+ <tr valign="top">
392
+ <td>
393
  <?php
394
  $sslseal = pmpro_getOption("sslseal");
395
  if($sslseal)
409
  <select id="CardType" class=" <?php echo pmpro_getClassForField("CardType");?>">
410
  <?php foreach($pmpro_accepted_credit_cards as $cc) { ?>
411
  <option value="<?php echo $cc?>" <?php if($CardType == $cc) { ?>selected="selected"<?php } ?>><?php echo $cc?></option>
412
+ <?php } ?>
413
  </select>
414
  </div>
415
  <?php
419
  ?>
420
  <input type="hidden" id="CardType" name="CardType" value="<?php echo esc_attr($CardType);?>" />
421
  <script>
422
+ jQuery(document).ready(function() {
423
+ jQuery('#AccountNumber').validateCreditCard(function(result) {
424
  var cardtypenames = {
425
  "amex":"American Express",
426
  "diners_club_carte_blanche":"Diners Club Carte Blanche",
433
  "visa":"Visa",
434
  "visa_electron":"Visa Electron"
435
  }
436
+
437
  if(result.card_type)
438
  jQuery('#CardType').val(cardtypenames[result.card_type.name]);
439
  else
440
  jQuery('#CardType').val('Unknown Card Type');
441
+ });
442
  });
443
  </script>
444
  <?php
445
  }
446
  ?>
447
+
448
  <div class="pmpro_payment-account-number">
449
  <label for="AccountNumber"><?php _e('Card Number', 'pmpro');?></label>
450
  <input id="AccountNumber" class="input <?php echo pmpro_getClassForField("AccountNumber");?>" type="text" size="25" value="<?php echo esc_attr($AccountNumber)?>" autocomplete="off" />
451
  </div>
452
+
453
  <div class="pmpro_payment-expiration">
454
  <label for="ExpirationMonth"><?php _e('Expiration Date', 'pmpro');?></label>
455
  <select id="ExpirationMonth" class=" <?php echo pmpro_getClassForField("ExpirationMonth");?>">
474
  <?php
475
  }
476
  ?>
477
+ </select>
478
  </div>
479
+
480
  <?php
481
  $pmpro_show_cvv = apply_filters("pmpro_show_cvv", true);
482
  if($pmpro_show_cvv)
489
  <?php
490
  }
491
  ?>
492
+
493
  <?php if($pmpro_show_discount_code) { ?>
494
  <div class="pmpro_payment-discount-code">
495
  <label for="discount_code"><?php _e('Discount Code', 'pmpro');?></label>
498
  <p id="discount_code_message" class="pmpro_message" style="display: none;"></p>
499
  </div>
500
  <?php } ?>
501
+
502
+ </td>
503
  </tr>
504
  </tbody>
505
  </table>
506
  <?php
507
+
508
  //don't include the default
509
  return false;
510
  }
511
+
512
  /**
513
  * Fields shown on edit user page
514
+ *
515
  * @since 1.8
516
  */
517
  static function user_profile_fields($user)
521
  $cycles = array( __('Day(s)', 'pmpro') => 'Day', __('Week(s)', 'pmpro') => 'Week', __('Month(s)', 'pmpro') => 'Month', __('Year(s)', 'pmpro') => 'Year' );
522
  $current_year = date("Y");
523
  $current_month = date("m");
524
+
525
  //make sure the current user has privileges
526
  $membership_level_capability = apply_filters("pmpro_edit_member_capability", "manage_options");
527
  if(!current_user_can($membership_level_capability))
528
  return false;
529
 
530
+ //more privelges they should have
531
  $show_membership_level = apply_filters("pmpro_profile_show_membership_level", true, $user);
532
  if(!$show_membership_level)
533
  return false;
534
+
535
  //check that user has a current subscription at Stripe
536
  $last_order = new MemberOrder();
537
  $last_order->getLastMemberOrder($user->ID);
538
+
539
  //assume no sub to start
540
  $sub = false;
541
+
542
  //check that gateway is Stripe
543
  if($last_order->gateway == "stripe")
544
+ {
545
  //is there a customer?
546
+ $sub = $last_order->Gateway->getSubscription($last_order);
547
+ }
548
+
549
  $customer_id = $user->pmpro_stripe_customerid;
550
+
551
  if(empty($sub))
552
  {
553
  //make sure we delete stripe updates
554
  update_user_meta($user->ID, "pmpro_stripe_updates", array());
555
+
556
  //if the last order has a sub id, let the admin know there is no sub at Stripe
557
  if(!empty($last_order) && $last_order->gateway == "stripe" && !empty($last_order->subscription_transaction_id) && strpos($last_order->subscription_transaction_id, "sub_") !== false)
558
  {
559
  ?>
560
  <p><strong>Note:</strong> Subscription <strong><?php echo $last_order->subscription_transaction_id;?></strong> could not be found at Stripe. It might have been deleted.</p>
561
  <?php
562
+ }
563
  }
564
+ else
565
+ {
566
  ?>
567
  <h3><?php _e("Subscription Updates", "pmpro"); ?></h3>
568
  <p>
578
  <th><label for="membership_level"><?php _e("Update", "pmpro"); ?></label></th>
579
  <td id="updates_td">
580
  <?php
581
+ $old_updates = $user->pmpro_stripe_updates;
582
  if(is_array($old_updates))
583
  {
584
  $updates = array_merge(
588
  }
589
  else
590
  $updates = array(array('template'=>true, 'when'=>'now', 'date_month'=>'', 'date_day'=>'', 'date_year'=>'', 'billing_amount'=>'', 'cycle_number'=>'', 'cycle_period'=>'Month'));
591
+
592
  foreach($updates as $update)
593
  {
594
  ?>
595
  <div class="updates_update" <?php if(!empty($update['template'])) { ?>style="display: none;"<?php } ?>>
596
+ <select class="updates_when" name="updates_when[]">
597
  <option value="now" <?php selected($update['when'], "now");?>>Now</option>
598
  <option value="payment" <?php selected($update['when'], "payment");?>>After Next Payment</option>
599
  <option value="date" <?php selected($update['when'], "date");?>>On Date</option>
600
  </select>
601
  <span class="updates_date" <?php if($uwhen != "date") { ?>style="display: none;"<?php } ?>>
602
  <select name="updates_date_month[]">
603
+ <?php
604
  for($i = 1; $i < 13; $i++)
605
  {
606
  ?>
619
  <small><?php _e('per', 'pmpro');?></small>
620
  <input name="updates_cycle_number[]" type="text" size="5" value="<?php echo esc_attr($update['cycle_number']);?>" />
621
  <select name="updates_cycle_period[]">
622
+ <?php
623
  foreach ( $cycles as $name => $value ) {
624
  echo "<option value='$value'";
625
  if(!empty($update['cycle_period']) && $update['cycle_period'] == $value) echo " selected='selected'";
627
  }
628
  ?>
629
  </select>
630
+ </span>
631
  <span>
632
+ <a class="updates_remove" href="javascript:void(0);">Remove</a>
633
  </span>
634
  </div>
635
  <?php
636
  }
637
+ ?>
638
  <p><a id="updates_new_update" href="javascript:void(0);">+ New Update</a></p>
639
  </td>
640
+ </tr>
641
+ </table>
642
  <script>
643
  jQuery(document).ready(function() {
644
  //function to update dropdowns/etc based on when field
648
  jQuery(when).parent().children('.updates_date').show();
649
  else
650
  jQuery(when).parent().children('.updates_date').hide();
651
+
652
  if(jQuery(when).val() == 'no')
653
  jQuery(when).parent().children('.updates_billing').hide();
654
  else
655
  jQuery(when).parent().children('.updates_billing').show();
656
+ }
657
 
658
  //and update on page load
659
  jQuery('.updates_when').each(function() { if(jQuery(this).parent().css('display') != 'none') updateSubscriptionUpdateFields(this); });
660
+
661
  //add a new update when clicking to
662
  var num_updates_divs = <?php echo count($updates);?>;
663
  jQuery('#updates_new_update').click(function() {
664
  //get updates
665
  updates = jQuery('.updates_update').toArray();
666
+
667
  //clone the first one
668
+ new_div = jQuery(updates[0]).clone();
669
+
670
  //append
671
  new_div.insertBefore('#updates_new_update');
672
+
673
  //update events
674
  addUpdateEvents()
675
+
676
  //unhide it
677
  new_div.show();
678
+ updateSubscriptionUpdateFields(new_div.children('.updates_when'));
679
  });
680
+
681
  function addUpdateEvents()
682
  {
683
  //update when when changes
684
  jQuery('.updates_when').change(function() {
685
  updateSubscriptionUpdateFields(this);
686
  });
687
+
688
  //remove updates when clicking
689
+ jQuery('.updates_remove').click(function() {
690
  jQuery(this).parent().parent().remove();
691
  });
692
  }
696
  <?php
697
  }
698
  }
699
+
700
  /**
701
  * Process fields from the edit user page
702
+ *
703
  * @since 1.8
704
  */
705
  static function user_profile_fields_save($user_id)
706
  {
707
  global $wpdb;
708
+
709
  //check capabilities
710
  $membership_level_capability = apply_filters("pmpro_edit_member_capability", "manage_options");
711
  if(!current_user_can($membership_level_capability))
712
  return false;
713
+
714
  //make sure some value was passed
715
  if(!isset($_POST['updates_when']) || !is_array($_POST['updates_when']))
716
  return;
717
+
718
  //vars
719
  $updates = array();
720
  $next_on_date_update = "";
721
+
722
  //build array of updates (we skip the first because it's the template field for the JavaScript
723
  for($i = 1; $i < count($_POST['updates_when']); $i++)
724
  {
725
  $update = array();
726
+
727
  //all updates have these values
728
  $update['when'] = $_POST['updates_when'][$i];
729
  $update['billing_amount'] = $_POST['updates_billing_amount'][$i];
730
  $update['cycle_number'] = $_POST['updates_cycle_number'][$i];
731
  $update['cycle_period'] = $_POST['updates_cycle_period'][$i];
732
+
733
  //these values only for on date updates
734
  if($_POST['updates_when'][$i] == "date")
735
  {
737
  $update['date_day'] = str_pad($_POST['updates_date_day'][$i], 2, "0", STR_PAD_LEFT);
738
  $update['date_year'] = $_POST['updates_date_year'][$i];
739
  }
740
+
741
  //make sure the update is valid
742
  if(empty($update['cycle_number']))
743
  continue;
744
+
745
  //if when is now, update the subscription
746
  if($update['when'] == "now")
747
  {
748
  //get level for user
749
  $user_level = pmpro_getMembershipLevelForUser($user_id);
750
+
751
  //get current plan at Stripe to get payment date
752
  $last_order = new MemberOrder();
753
  $last_order->getLastMemberOrder($user_id);
755
  $last_order->Gateway->getCustomer($last_order);
756
 
757
  $subscription = $last_order->Gateway->getSubscription($last_order);
758
+
759
  if(!empty($subscription))
760
+ {
761
  $end_timestamp = $subscription->current_period_end;
762
+
763
+ //cancel the old subscription
764
  if(!$last_order->Gateway->cancelSubscriptionAtGateway($subscription))
765
  {
766
  //throw error and halt save
767
+ function pmpro_stripe_user_profile_fields_save_error($errors, $update, $user)
768
  {
769
  $errors->add('pmpro_stripe_updates',__('Could not cancel the old subscription. Updates have not been processed.', 'pmpro'));
770
  }
771
  add_filter('user_profile_update_errors', 'pmpro_stripe_user_profile_fields_save_error', 10, 3);
772
+
773
  //stop processing updates
774
  return;
775
  }
776
  }
777
+
778
  //if we didn't get an end date, let's set one one cycle out
779
  if(empty($end_timestamp))
780
  $end_timestamp = strtotime("+" . $update['cycle_number'] . " " . $update['cycle_period'], current_time('timestamp'));
781
+
782
  //build order object
783
  $update_order = new MemberOrder();
784
  $update_order->setGateway('stripe');
789
  $update_order->PaymentAmount = $update['billing_amount'];
790
  $update_order->ProfileStartDate = date("Y-m-d", $end_timestamp);
791
  $update_order->BillingPeriod = $update['cycle_period'];
792
+ $update_order->BillingFrequency = $update['cycle_number'];
793
+
794
  //need filter to reset ProfileStartDate
795
  add_filter('pmpro_profile_start_date', create_function('$startdate, $order', 'return "' . $update_order->ProfileStartDate . 'T0:0:0";'), 10, 2);
796
+
797
  //update subscription
798
  $update_order->Gateway->subscribe($update_order, false);
799
+
800
  //update membership
801
  $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users
802
  SET billing_amount = '" . esc_sql($update['billing_amount']) . "',
808
  AND membership_id = '" . esc_sql($last_order->membership_id) . "'
809
  AND status = 'active'
810
  LIMIT 1";
811
+
812
  $wpdb->query($sqlQuery);
813
+
814
  //save order so we know which plan to look for at stripe (order code = plan id)
815
  $update_order->status = "success";
816
  $update_order->saveOrder();
817
+
818
  continue;
819
  }
820
  elseif($update['when'] == 'date')
824
  else
825
  $next_on_date_update = $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'];
826
  }
827
+
828
+ //add to array
829
+ $updates[] = $update;
830
  }
831
+
832
  //save in user meta
833
  update_user_meta($user_id, "pmpro_stripe_updates", $updates);
834
+
835
  //save date of next on-date update to make it easier to query for these in cron job
836
  update_user_meta($user_id, "pmpro_stripe_next_on_date_update", $next_on_date_update);
837
  }
838
+
839
  /**
840
  * Cron activation for subscription updates.
841
+ *
842
  * @since 1.8
843
  */
844
  static function pmpro_activation()
845
  {
846
  wp_schedule_event(time(), 'daily', 'pmpro_cron_stripe_subscription_updates');
847
  }
848
+
849
  /**
850
  * Cron deactivation for subscription updates.
851
+ *
852
  * @since 1.8
853
  */
854
  static function pmpro_deactivation()
855
  {
856
  wp_clear_scheduled_hook('pmpro_cron_stripe_subscription_updates');
857
  }
858
+
859
  /**
860
  * Cron job for subscription updates.
861
+ *
862
  * @since 1.8
863
  */
864
  static function pmpro_cron_stripe_subscription_updates()
866
  global $wpdb;
867
 
868
  //get all updates for today (or before today)
869
+ $sqlQuery = "SELECT *
870
+ FROM $wpdb->usermeta
871
+ WHERE meta_key = 'pmpro_stripe_next_on_date_update'
872
+ AND meta_value IS NOT NULL
873
+ AND meta_value < '" . date("Y-m-d", strtotime("+1 day")) . "'";
874
  $updates = $wpdb->get_results($sqlQuery);
875
+
876
  if(!empty($updates))
877
+ {
878
  //loop through
879
  foreach($updates as $update)
880
+ {
881
  //pull values from update
882
  $user_id = $update->user_id;
883
+
884
  $user = get_userdata($user_id);
885
  $user_updates = $user->pmpro_stripe_updates;
886
+ $next_on_date_update = "";
887
+
888
  //loop through updates looking for updates happening today or earlier
889
  foreach($user_updates as $key => $update)
890
+ {
891
  if($update['when'] == 'date' &&
892
  $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'] <= date("Y-m-d")
893
  )
894
  {
895
  //get level for user
896
  $user_level = pmpro_getMembershipLevelForUser($user_id);
897
+
898
  //get current plan at Stripe to get payment date
899
  $last_order = new MemberOrder();
900
  $last_order->getLastMemberOrder($user_id);
901
  $last_order->setGateway('stripe');
902
  $last_order->Gateway->getCustomer($last_order);
903
+
904
  if(!empty($last_order->Gateway->customer))
905
  {
906
  //find the first subscription
910
  $end_timestamp = $first_sub['current_period_end'];
911
  }
912
  }
913
+
914
  //if we didn't get an end date, let's set one one cycle out
915
  $end_timestamp = strtotime("+" . $update['cycle_number'] . " " . $update['cycle_period']);
916
+
917
  //build order object
918
  $update_order = new MemberOrder();
919
  $update_order->setGateway('stripe');
925
  $update_order->ProfileStartDate = date("Y-m-d", $end_timestamp);
926
  $update_order->BillingPeriod = $update['cycle_period'];
927
  $update_order->BillingFrequency = $update['cycle_number'];
928
+
929
  //update subscription
930
  $update_order->Gateway->subscribe($update_order, false);
931
+
932
  //update membership
933
+ $sqlQuery = "UPDATE $wpdb->pmpro_memberships_users
934
+ SET billing_amount = '" . esc_sql($update['billing_amount']) . "',
935
+ cycle_number = '" . esc_sql($update['cycle_number']) . "',
936
+ cycle_period = '" . esc_sql($update['cycle_period']) . "'
937
+ WHERE user_id = '" . esc_sql($user_id) . "'
938
+ AND membership_id = '" . esc_sql($last_order->membership_id) . "'
939
+ AND status = 'active'
940
  LIMIT 1";
941
+
942
  $wpdb->query($sqlQuery);
943
+
944
  //save order
945
  $update_order->status = "success";
946
  $update_order->save();
947
+
948
  //remove update from list
949
+ unset($user_updates[$key]);
950
  }
951
  elseif($update['when'] == 'date')
952
  {
957
  $next_on_date_update = $update['date_year'] . "-" . $update['date_month'] . "-" . $update['date_day'];
958
  }
959
  }
960
+
961
  //save updates in case we removed some
962
  update_user_meta($user_id, "pmpro_stripe_updates", $user_updates);
963
+
964
  //save date of next on-date update to make it easier to query for these in cron job
965
  update_user_meta($user_id, "pmpro_stripe_next_on_date_update", $next_on_date_update);
966
  }
967
  }
968
  }
969
+
970
  /**
971
  * Process checkout and decide if a charge and or subscribe is needed
972
+ *
973
  * @since 1.4
974
  */
975
  function process(&$order)
986
  if($this->charge($order))
987
  {
988
  if(pmpro_isLevelRecurring($order->membership_level))
989
+ {
990
  if($this->subscribe($order))
991
  {
992
  //yay!
1001
  else
1002
  {
1003
  //only a one time charge
1004
+ $order->status = "success"; //saved on checkout page
1005
  return true;
1006
  }
1007
  }
1011
  $order->error = __("Unknown error: Initial payment failed.", "pmpro");
1012
  return false;
1013
  }
1014
+ }
1015
+ }
1016
+
1017
  /**
1018
  * Make a one-time charge with Stripe
1019
+ *
1020
  * @since 1.4
1021
  */
1022
  function charge(&$order)
1023
  {
1024
  global $pmpro_currency;
1025
+
1026
  //create a code for the order
1027
  if(empty($order->code))
1028
  $order->code = $order->getRandomCode();
1029
+
1030
+ //what amount to charge?
1031
  $amount = $order->InitialPayment;
1032
+
1033
  //tax
1034
  $order->subtotal = $amount;
1035
  $tax = $order->getTax(true);
1036
  $amount = round((float)$order->subtotal + (float)$tax, 2);
1037
+
1038
  //create a customer
1039
  $result = $this->getCustomer($order);
1040
+
1041
  if(empty($result))
1042
+ {
1043
  //failed to create customer
1044
  return false;
1045
+ }
1046
+
1047
  //charge
1048
  try
1049
  {
1063
  $order->shorterror = $order->error;
1064
  return false;
1065
  }
1066
+
1067
  if(empty($response["failure_message"]))
1068
  {
1069
  //successful charge
1070
  $order->payment_transaction_id = $response["id"];
1071
+ $order->updateStatus("success");
1072
+ return true;
1073
  }
1074
  else
1075
  {
1078
  $order->error = $response['failure_message'];
1079
  $order->shorterror = $response['failure_message'];
1080
  return false;
1081
+ }
1082
  }
1083
+
1084
  /**
1085
  * Get a Stripe customer object.
1086
+ *
1087
  * If $this->customer is set, it returns it.
1088
  * It first checks if the order has a subscription_transaction_id. If so, that's the customer id.
1089
  * If not, it checks for a user_id on the order and searches for a customer id in the user meta.
1097
  function getCustomer(&$order = false, $force = false)
1098
  {
1099
  global $current_user;
1100
+
1101
  //already have it?
1102
  if(!empty($this->customer) && !$force)
1103
  return $this->customer;
1104
+
1105
  //figure out user_id and user
1106
  if(!empty($order->user_id))
1107
  $user_id = $order->user_id;
1108
+
1109
  //if no id passed, check the current user
1110
  if(empty($user_id) && !empty($current_user->ID))
1111
  $user_id = $current_user->ID;
1112
+
1113
  if(!empty($user_id))
1114
  $user = get_userdata($user_id);
1115
  else
1116
  $user = NULL;
1117
+
1118
  //transaction id?
1119
  if(!empty($order->subscription_transaction_id) && strpos($order->subscription_transaction_id, "cus_") !== false)
1120
  $customer_id = $order->subscription_transaction_id;
1121
  else
1122
  {
1123
+ //try based on user id
1124
  if(!empty($user_id))
1125
+ {
1126
+ $customer_id = get_user_meta($user_id, "pmpro_stripe_customerid", true);
1127
  }
1128
+ }
1129
+
1130
  //get name and email values from order in case we update
1131
  $name = trim($order->FirstName . " " . $order->LastName);
1132
  if(empty($name) && !empty($user->ID))
1133
  {
1134
  $name = trim($user->first_name . " " . $user->last_name);
1135
+
1136
  //still empty?
1137
  if(empty($name))
1138
  $name = $user->user_login;
1140
  elseif(empty($name))
1141
  $name = "No Name";
1142
 
1143
+ $email = $order->Email;
1144
  if(empty($email) && !empty($user->ID))
1145
  {
1146
  $email = $user->user_email;
1147
  }
1148
  elseif(empty($email))
1149
  $email = "No Email";
1150
+
1151
  //check for an existing stripe customer
1152
  if(!empty($customer_id))
1153
  {
1154
  try
1155
  {
1156
  $this->customer = Stripe_Customer::retrieve($customer_id);
1157
+
1158
  //update the customer description and card
1159
  if(!empty($order->stripeToken))
1160
+ {
1161
  $this->customer->description = $name . " (" . $email . ")";
1162
  $this->customer->email = $email;
1163
  $this->customer->card = $order->stripeToken;
1164
  $this->customer->save();
1165
  }
1166
+
1167
  return $this->customer;
1168
  }
1169
  catch (Exception $e)
1170
  {
1171
+ //assume no customer found
1172
  }
1173
  }
1174
+
1175
  //no customer id, create one
1176
  if(!empty($order->stripeToken))
1177
+ {
1178
  try
1179
  {
1180
  $this->customer = Stripe_Customer::create(array(
1189
  $order->shorterror = $order->error;
1190
  return false;
1191
  }
1192
+
1193
  if(!empty($user_id))
1194
  {
1195
  //user logged in/etc
1196
+ update_user_meta($user_id, "pmpro_stripe_customerid", $this->customer->id);
1197
  }
1198
  else
1199
  {
1210
 
1211
  return apply_filters('pmpro_stripe_create_customer', $this->customer);
1212
  }
1213
+
1214
+ return false;
1215
  }
1216
+
1217
  /**
1218
  * Get a Stripe subscription from a PMPro order
1219
+ *
1220
  * @since 1.8
1221
  */
1222
+ function getSubscription(&$order)
1223
  {
1224
  global $wpdb;
1225
+
1226
  //no order?
1227
  if(empty($order) || empty($order->code))
1228
+ return false;
1229
+
1230
  $result = $this->getCustomer($order, true); //force so we don't get a cached sub for someone else
1231
+
1232
  //no customer?
1233
  if(empty($result))
1234
  return false;
1235
+
1236
  //is there a subscription transaction id pointing to a sub?
1237
  if(!empty($order->subscription_transaction_id) && strpos($order->subscription_transaction_id, "sub_") !== false)
1238
  {
1246
  $order->shorterror = $order->error;
1247
  return false;
1248
  }
1249
+
1250
  return $sub;
1251
+ }
1252
+
1253
  //find subscription based on customer id and order/plan id
1254
+ $subscriptions = $this->customer->subscriptions->all();
1255
+
1256
  //no subscriptions
1257
  if(empty($subscriptions) || empty($subscriptions->data))
1258
+ return false;
1259
+
1260
  //we really want to test against the order codes of all orders with the same subscription_transaction_id (customer id)
1261
  $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')");
1262
+
1263
  //find the one for this order
1264
  foreach($subscriptions->data as $sub)
1265
+ {
1266
  if(in_array($sub->plan->id, $codes))
1267
  {
1268
  return $sub;
1269
  }
1270
  }
1271
+
1272
+ //didn't find anything yet
1273
  return false;
1274
  }
1275
+
1276
  /**
1277
  * Create a new subscription with Stripe
1278
+ *
1279
  * @since 1.4
1280
  */
1281
  function subscribe(&$order, $checkout = true)
1282
  {
1283
  global $pmpro_currency;
1284
+
1285
  //create a code for the order
1286
  if(empty($order->code))
1287
  $order->code = $order->getRandomCode();
1288
+
1289
  //filter order before subscription. use with care.
1290
  $order = apply_filters("pmpro_subscribe_order", $order, $this);
1291
+
1292
  //figure out the user
1293
  if(!empty($order->user_id))
1294
  $user_id = $order->user_id;
1297
  global $current_user;
1298
  $user_id = $current_user->ID;
1299
  }
1300
+
1301
  //setup customer
1302
  $result = $this->getCustomer($order);
1303
  if(empty($result))
1304
  return false; //error retrieving customer
1305
+
1306
  //set subscription id to custom id
1307
  $order->subscription_transaction_id = $this->customer['id']; //transaction id is the customer id, we save it in user meta later too
1308
+
1309
  //figure out the amounts
1310
  $amount = $order->PaymentAmount;
1311
+ $amount_tax = $order->getTaxForPrice($amount);
1312
  $amount = round((float)$amount + (float)$amount_tax, 2);
1313
 
1314
  /*
1315
  There are two parts to the trial. Part 1 is simply the delay until the first payment
1316
  since we are doing the first payment as a separate transaction.
1317
  The second part is the actual "trial" set by the admin.
1318
+
1319
  Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
1320
  */
1321
+ //figure out the trial length (first payment handled by initial charge)
1322
  if($order->BillingPeriod == "Year")
1323
  $trial_period_days = $order->BillingFrequency * 365; //annual
1324
  elseif($order->BillingPeriod == "Day")
1327
  $trial_period_days = $order->BillingFrequency * 7; //weekly
1328
  else
1329
  $trial_period_days = $order->BillingFrequency * 30; //assume monthly
1330
+
1331
  //convert to a profile start date
1332
  $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
1333
+
1334
  //filter the start date
1335
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
1336
+
1337
+ //convert back to days
1338
  $trial_period_days = ceil(abs(strtotime(date("Y-m-d"), current_time("timestamp")) - strtotime($order->ProfileStartDate, current_time("timestamp"))) / 86400);
1339
 
1340
  //for free trials, just push the start date of the subscription back
1341
+ if(!empty($order->TrialBillingCycles) && $order->TrialAmount == 0)
1342
  {
1343
  $trialOccurrences = (int)$order->TrialBillingCycles;
1344
  if($order->BillingPeriod == "Year")
1354
  {
1355
  /*
1356
  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)
1357
+
1358
  This will force TrialBillingCycles > 1 to act as if they were 1
1359
+ */
1360
  $new_user_updates = array();
1361
  $new_user_updates[] = array(
1362
  'when' => 'payment',
1363
  'billing_amount' => $order->PaymentAmount,
1364
  'cycle_period' => $order->BillingPeriod,
1365
  'cycle_number' => $order->BillingFrequency
1366
+ );
1367
+
1368
  //now amount to equal the trial #s
1369
  $amount = $order->TrialAmount;
1370
  $amount_tax = $order->getTaxForPrice($amount);
1371
  $amount = round((float)$amount + (float)$amount_tax, 2);
1372
+ }
1373
+
1374
  //create a plan
1375
+ try
1376
  {
1377
  $plan = array(
1378
  "amount" => $amount * 100,
1384
  "id" => $order->code
1385
  );
1386
 
1387
+ $plan = Stripe_Plan::create(apply_filters('pmpro_stripe_create_plan_array', $plan));
1388
  }
1389
  catch (Exception $e)
1390
  {
1392
  $order->shorterror = $order->error;
1393
  return false;
1394
  }
1395
+
1396
  //before subscribing, let's clear out the updates so we don't trigger any during sub
1397
  if(!empty($user_id))
1398
+ {
1399
  $old_user_updates = get_user_meta($user_id, "pmpro_stripe_updates", true);
1400
+ update_user_meta($user_id, "pmpro_stripe_updates", array());
1401
+ }
1402
+
1403
  if(empty($order->subscription_transaction_id) && !empty($this->customer['id']))
1404
  $order->subscription_transaction_id = $this->customer['id'];
1405
+
1406
  //subscribe to the plan
1407
  try
1408
  {
1409
+ $subscription = array("plan" => $order->code);
1410
+ $result = $this->customer->subscriptions->create(apply_filters('pmpro_stripe_create_subscription_array', $subscription));
1411
  }
1412
  catch (Exception $e)
1413
  {
1414
  //try to delete the plan
1415
  $plan->delete();
1416
+
1417
  //give the user any old updates back
1418
  if(!empty($user_id))
1419
  update_user_meta($user_id, "pmpro_stripe_updates", $old_user_updates);
1420
+
1421
  //return error
1422
  $order->error = __("Error subscribing customer to plan with Stripe:", "pmpro") . $e->getMessage();
1423
  $order->shorterror = $order->error;
1424
  return false;
1425
  }
1426
+
1427
  //delete the plan
1428
  $plan = Stripe_Plan::retrieve($order->code);
1429
+ $plan->delete();
1430
 
1431
+ //if we got this far, we're all good
1432
+ $order->status = "success";
1433
  $order->subscription_transaction_id = $result['id'];
1434
+
1435
  //save new updates if this is at checkout
1436
  if($checkout)
1437
+ {
1438
  //empty out updates unless set above
1439
  if(empty($new_user_updates))
1440
  $new_user_updates = array();
1441
+
1442
  //update user meta
1443
+ if(!empty($user_id))
1444
  update_user_meta($user_id, "pmpro_stripe_updates", $new_user_updates);
1445
  else
1446
  {
1460
  //give them their old updates back
1461
  update_user_meta($user_id, "pmpro_stripe_updates", $old_user_updates);
1462
  }
1463
+
1464
  return true;
1465
+ }
1466
+
1467
  /**
1468
  * Helper method to update the customer info via getCustomer
1469
+ *
1470
  * @since 1.4
1471
  */
1472
  function update(&$order)
1473
  {
1474
  //we just have to run getCustomer which will look for the customer and update it with the new token
1475
  $result = $this->getCustomer($order);
1476
+
1477
  if(!empty($result))
1478
  {
1479
  return true;
1480
+ }
1481
  else
1482
  {
1483
  return false; //couldn't find the customer
1484
  }
1485
  }
1486
+
1487
  /**
1488
  * Cancel a subscription at Stripe
1489
+ *
1490
  * @since 1.4
1491
  */
1492
  function cancel(&$order, $update_status = true)
1494
  //no matter what happens below, we're going to cancel the order in our system
1495
  if($update_status)
1496
  $order->updateStatus("cancelled");
1497
+
1498
  //require a subscription id
1499
  if(empty($order->subscription_transaction_id))
1500
  return false;
1501
+
1502
  //find the customer
1503
+ $result = $this->getCustomer($order);
1504
+
1505
  if(!empty($result))
1506
  {
1507
  //find subscription with this order code
1508
+ $subscription = $this->getSubscription($order);
1509
 
1510
  if(!empty($subscription))
1511
+ {
1512
  if($this->cancelSubscriptionAtGateway($subscription))
1513
  {
1514
  //we're okay, going to return true later
1517
  {
1518
  $order->error = __("Could not cancel old subscription.", "pmpro");
1519
  $order->shorterror = $order->error;
1520
+
1521
+ return false;
1522
+ }
1523
+ }
1524
+
1525
  /*
1526
  Clear updates for this user. (But not if checking out, we would have already done that.)
1527
  */
1528
  if(empty($_REQUEST['submit-checkout']))
1529
  update_user_meta($order->user_id, "pmpro_stripe_updates", array());
1530
+
1531
  return true;
1532
  }
1533
  else
1535
  $order->error = __("Could not find the customer.", "pmpro");
1536
  $order->shorterror = $order->error;
1537
  return false; //no customer found
1538
+ }
1539
+ }
1540
+
1541
  /**
1542
  * Helper method to cancel a subscription at Stripe and also clear up any upaid invoices.
1543
+ *
1544
  * @since 1.8
1545
  */
1546
  function cancelSubscriptionAtGateway($subscription)
1548
  //need a valid sub
1549
  if(empty($subscription->id))
1550
  return false;
1551
+
1552
  //make sure we get the customer for this subscription
1553
  $order = new MemberOrder();
1554
+ $order->getLastMemberOrderBySubscriptionTransactionID($subscription->id);
1555
+
1556
  //no order?
1557
  if(empty($order))
1558
  {
1559
  //lets cancel anyway, but this is suspicious
1560
  $r = $subscription->cancel();
1561
+
1562
  return true;
1563
  }
1564
+
1565
  //okay have an order, so get customer so we can cancel invoices too
1566
  $this->getCustomer($order);
1567
+
1568
  //get open invoices
1569
  $invoices = $this->customer->invoices();
1570
  $invoices = $invoices->all();
1571
+
1572
  //found it, cancel it
1573
  try
1574
+ {
1575
  //find any open invoices for this subscription and forgive them
1576
  if(!empty($invoices))
1577
  {
1578
  foreach($invoices->data as $invoice)
1579
+ {
1580
  if(!$invoice->closed && $invoice->subscription == $subscription->id)
1581
+ {
1582
  $invoice->closed = true;
1583
  $invoice->save();
1584
  }
1585
  }
1586
+ }
1587
+
1588
  //cancel
1589
  $r = $subscription->cancel();
1590
+
1591
  return true;
1592
  }
1593
  catch(Exception $e)
1594
+ {
1595
  return false;
1596
  }
1597
  }
email/admin_change_admin.html CHANGED
@@ -1,4 +1,4 @@
1
- <p>An administrator at !!sitename!! has changed a membership level.</p>
2
 
3
  <p>!!membership_change!!.</p>
4
 
1
+ <p>An administrator at !!sitename!! has changed a membership level for !!name!!.</p>
2
 
3
  <p>!!membership_change!!.</p>
4
 
includes/currencies.php CHANGED
@@ -23,7 +23,14 @@
23
  ),
24
  'CAD' => __('Canadian Dollars (&#36;)', 'pmpro'),
25
  'CNY' => __('Chinese Yuan', 'pmpro'),
26
- 'CZK' => __('Czech Koruna', 'pmpro'),
 
 
 
 
 
 
 
27
  'DKK' => __('Danish Krone', 'pmpro'),
28
  'HKD' => __('Hong Kong Dollar (&#36;)', 'pmpro'),
29
  'HUF' => __('Hungarian Forint', 'pmpro'),
23
  ),
24
  'CAD' => __('Canadian Dollars (&#36;)', 'pmpro'),
25
  'CNY' => __('Chinese Yuan', 'pmpro'),
26
+ 'CZK' => array(
27
+ 'name' => __('Czech Koruna', 'pmpro'),
28
+ 'decimals' => '0',
29
+ 'thousands_separator' => '&nbsp;',
30
+ 'decimal_separator' => ',',
31
+ 'symbol' => '&nbsp;Kč',
32
+ 'position' => 'right',
33
+ ),
34
  'DKK' => __('Danish Krone', 'pmpro'),
35
  'HKD' => __('Hong Kong Dollar (&#36;)', 'pmpro'),
36
  'HUF' => __('Hungarian Forint', 'pmpro'),
includes/filters.php CHANGED
@@ -147,4 +147,12 @@ function pmpro_required_billing_fields_stripe_lite($fields)
147
 
148
  //ship it!
149
  return $fields;
 
 
 
 
 
 
 
 
150
  }
147
 
148
  //ship it!
149
  return $fields;
150
+ }
151
+
152
+ //copy other discount code to discount code if latter is not set
153
+ if(empty($_REQUEST['discount_code']) && !empty($_REQUEST['other_discount_code']))
154
+ {
155
+ $_REQUEST['discount_code'] = $_REQUEST['other_discount_code'];
156
+ $_POST['discount_code'] = $_POST['other_discount_code'];
157
+ $_GET['discount_code'] = $_GET['other_discount_code'];
158
  }
includes/functions.php CHANGED
@@ -237,7 +237,7 @@ function pmpro_getLevelCost(&$level, $tags = true, $short = false)
237
  if(!$short)
238
  $r = sprintf(__('The price for membership is <strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
239
  else
240
- $r = sprintf(__('<strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
241
  }
242
  } else {
243
  if($level->cycle_number == '1')
@@ -422,6 +422,7 @@ if(!function_exists("formatPhone"))
422
 
423
  function pmpro_showRequiresMembershipMessage()
424
  {
 
425
  //get the correct message
426
  if(is_feed())
427
  {
@@ -636,7 +637,7 @@ function pmpro_changeMembershipLevel($level, $user_id = NULL, $old_level_status
636
 
637
  if(!empty($c_order->error))
638
  $pmpro_error = $c_order->error;
639
- }
640
  }
641
 
642
  //insert current membership
@@ -1804,15 +1805,18 @@ function pmpro_formatPrice($price)
1804
  //settings stored in array?
1805
  if(!empty($pmpro_currencies[$pmpro_currency]) && is_array($pmpro_currencies[$pmpro_currency]))
1806
  {
 
 
 
 
 
 
 
1807
  //which side is the symbol on?
1808
  if(!empty($pmpro_currencies[$pmpro_currency]['position']) && $pmpro_currencies[$pmpro_currency]['position']== 'left')
1809
  $formatted = $pmpro_currency_symbol . $formatted;
1810
  else
1811
  $formatted = $formatted . $pmpro_currency_symbol;
1812
-
1813
- //commas or periods?
1814
- if(!empty($pmpro_currencies[$pmpro_currency]['separator']) && $pmpro_currencies[$pmpro_currency]['separator'])
1815
- $formatted = str_replace(array(".",","), $pmpro_currencies[$pmpro_currency]['separator'], $formatted);
1816
  }
1817
  else
1818
  $formatted = $pmpro_currency_symbol . $formatted; //default to symbol on the left
@@ -1834,7 +1838,7 @@ function pmpro_getCurrencyPosition()
1834
  return $pmpro_currencies[$pmpro_currency]['position'];
1835
  else
1836
  return "left";
1837
- }
1838
 
1839
  /*
1840
  * What gateway should we be using?
@@ -1865,4 +1869,4 @@ function pmpro_getGateway()
1865
  $gateway = apply_filters('pmpro_get_gateway', $gateway, $valid_gateways);
1866
 
1867
  return $gateway;
1868
- }
237
  if(!$short)
238
  $r = sprintf(__('The price for membership is <strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
239
  else
240
+ $r = sprintf(__('<strong>%s every %d %s</strong>.', 'pmpro'), pmpro_formatPrice($level->initial_payment), $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number) );
241
  }
242
  } else {
243
  if($level->cycle_number == '1')
422
 
423
  function pmpro_showRequiresMembershipMessage()
424
  {
425
+ //TODO $current_user $post_membership_levels_names are undefined variables
426
  //get the correct message
427
  if(is_feed())
428
  {
637
 
638
  if(!empty($c_order->error))
639
  $pmpro_error = $c_order->error;
640
+ }
641
  }
642
 
643
  //insert current membership
1805
  //settings stored in array?
1806
  if(!empty($pmpro_currencies[$pmpro_currency]) && is_array($pmpro_currencies[$pmpro_currency]))
1807
  {
1808
+ //format number do decimals, with decimal_separator and thousands_separator
1809
+ $formatted = number_format($price,
1810
+ (isset($pmpro_currencies[$pmpro_currency]['decimals']) ? (int)$pmpro_currencies[$pmpro_currency]['decimals'] : 2),
1811
+ $pmpro_currencies[$pmpro_currency]['decimal_separator'],
1812
+ $pmpro_currencies[$pmpro_currency]['thousands_separator']
1813
+ );
1814
+
1815
  //which side is the symbol on?
1816
  if(!empty($pmpro_currencies[$pmpro_currency]['position']) && $pmpro_currencies[$pmpro_currency]['position']== 'left')
1817
  $formatted = $pmpro_currency_symbol . $formatted;
1818
  else
</