Paid Memberships Pro - Version 1.7.3

Version Description

  • pmpro_longform_address and pmpro_international_addresses now default to true. See this gist to go back to US-specific address format: https://gist.github.com/strangerstudios/6478242
  • Added "Show Billing Address Fields" option for the Stripe payment gateway. Set to 'No' to hide billing address fields. Replaces the functionality of the "Stripe Lite" plugin.
  • Added language files for es_ES, es_CL, and es_PE. Thanks, Javier Monorov (zillionsk8 on GitHub).
  • Added language files for fr_FR. Thanks, Jrmy De la casa.
  • NOTE: Language files may not be complete. The get involved, join us on GitHub.
  • Added Cybersource and 2Checkout gateways in beta.
  • Added pmpro_paypal_button_image filter so you can override the URL of the PayPal button image.
  • Added a new report showing signups vs. cancellations, monthly recurring revenue, and lifetime value.
  • Fixed issue where the checkout_paid template was always being used for emails. It now checks if the level at checkout is free and sends either the checkout_free, checkout_paid, or checkout_trial templates accordingly. (Thanks, inator on GitHub)
  • Fixed sales report to not show $ when hovering over bars for "sales" vs. "revenue".
  • Fixed issue where PayPal Standard levels using a billing frequency > 1 (e.g. every 3 months) would have an extra payment charged after one period.
  • Fixed SQL error in discount code admin page that could result in cycle_periods of code levels saving incorrectly. (Thanks, Sam D'Amico)
  • Removed note that Payflow gateway doesn't support recurring payments. It does.
  • Now passing the membership level id in the $data var for checkout and cancellation emails. The key is "membership_id" so use $data['membership_id'] to check/access it.
  • No longer setting the subtotal property of orders when the "subscribe" method of the gateways is called. This will fix cases where an initial order or a subscription with a free trial showed a charge amount > $0.
  • Clicking enter in discount code box at checkout will no longer submit form and will click the "apply" button.
  • Hiding the "Apply" button on the checkout page if a discount code was passed in. Showing it if the text field is changed.
  • Authorize.net now supports CAD, GBP, and EUR currencies (for US merchants only) http://community.developer.authorize.net/t5/The-Authorize-Net-Developer-Blog/Authorize-Net-Expansion-into-Canada-the-United-Kingdom-and/ba-p/33690
  • Fixed notice in getfile.php
  • Fixed notices and expiration dates in login report.
  • Fixed notices in includes/notifications.php (Thanks, Nilesh)
  • Allowing dashes (-) in discount codes now.
Download this release

Release Info

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

Version 1.7.3

Files changed (233) hide show
  1. adminpages/addons.php +142 -0
  2. adminpages/addons/hide-admin-bar-from-non-admins.php +32 -0
  3. adminpages/addons/images/pmpro-aweber.gif +0 -0
  4. adminpages/addons/images/pmpro-infusionsoft.jpg +0 -0
  5. adminpages/addons/images/pmpro-mailchimp.jpg +0 -0
  6. adminpages/addons/images/pmpro-network.gif +0 -0
  7. adminpages/addons/images/pmpro-post-affiliate-pro.jpg +0 -0
  8. adminpages/addons/images/pmpro-register-helper.gif +0 -0
  9. adminpages/addons/images/pmpro-series.gif +0 -0
  10. adminpages/addons/images/pmpro-wp-affiliate-platform.jpg +0 -0
  11. adminpages/addons/images/wp-bouncer.gif +0 -0
  12. adminpages/addons/pmpro-addon-packages.php +30 -0
  13. adminpages/addons/pmpro-affiliates.php +30 -0
  14. adminpages/addons/pmpro-aweber.php +31 -0
  15. adminpages/addons/pmpro-bbpress.php +28 -0
  16. adminpages/addons/pmpro-disable-emails.php +28 -0
  17. adminpages/addons/pmpro-expiration.php +28 -0
  18. adminpages/addons/pmpro-freeaddress.php +28 -0
  19. adminpages/addons/pmpro-infusionsoft.php +31 -0
  20. adminpages/addons/pmpro-international-addresses.php +31 -0
  21. adminpages/addons/pmpro-level-cost-text.php +30 -0
  22. adminpages/addons/pmpro-mailchimp.php +31 -0
  23. adminpages/addons/pmpro-network.php +31 -0
  24. adminpages/addons/pmpro-post-affiliate-pro.php +31 -0
  25. adminpages/addons/pmpro-register-helper.php +31 -0
  26. adminpages/addons/pmpro-require-code-to-register.php +28 -0
  27. adminpages/addons/pmpro-series.php +31 -0
  28. adminpages/addons/pmpro-shipping.php +31 -0
  29. adminpages/addons/pmpro-stripe-lite.php +30 -0
  30. adminpages/addons/pmpro-user-pages.php +30 -0
  31. adminpages/addons/pmpro-wp-affiliate.php +29 -0
  32. adminpages/addons/tml.php +32 -0
  33. adminpages/addons/wp-bouncer.php +31 -0
  34. adminpages/admin_footer.php +2 -0
  35. adminpages/admin_header.php +135 -0
  36. adminpages/advancedsettings.php +290 -0
  37. adminpages/dashboard.php +127 -0
  38. adminpages/discountcodes.php +631 -0
  39. adminpages/emailsettings.php +152 -0
  40. adminpages/functions.php +157 -0
  41. adminpages/membershiplevels.php +580 -0
  42. adminpages/memberslist-csv.php +180 -0
  43. adminpages/memberslist.php +210 -0
  44. adminpages/orders-csv.php +189 -0
  45. adminpages/orders.php +691 -0
  46. adminpages/pagesettings.php +215 -0
  47. adminpages/paymentsettings.php +579 -0
  48. adminpages/reports.php +59 -0
  49. adminpages/reports/login.php +412 -0
  50. adminpages/reports/memberships.php +637 -0
  51. adminpages/reports/sales.php +399 -0
  52. classes/class.memberorder.php +558 -0
  53. classes/class.mimetype.php +239 -0
  54. classes/class.pmproemail.php +747 -0
  55. classes/gateways/class.pmprogateway.php +204 -0
  56. classes/gateways/class.pmprogateway_authorizenet.php +872 -0
  57. classes/gateways/class.pmprogateway_braintree.php +422 -0
  58. classes/gateways/class.pmprogateway_check.php +208 -0
  59. classes/gateways/class.pmprogateway_cybersource.php +765 -0
  60. classes/gateways/class.pmprogateway_payflowpro.php +445 -0
  61. classes/gateways/class.pmprogateway_paypal.php +454 -0
  62. classes/gateways/class.pmprogateway_paypalexpress.php +358 -0
  63. classes/gateways/class.pmprogateway_paypalstandard.php +285 -0
  64. classes/gateways/class.pmprogateway_stripe.php +357 -0
  65. classes/gateways/class.pmprogateway_twocheckout.php +123 -0
  66. css/admin.css +97 -0
  67. css/frontend.css +211 -0
  68. css/print.css +5 -0
  69. email/admin_change.html +7 -0
  70. email/admin_change_admin.html +5 -0
  71. email/billing.html +19 -0
  72. email/billing_admin.html +17 -0
  73. email/billing_failure.html +15 -0
  74. email/billing_failure_admin.html +15 -0
  75. email/cancel.html +3 -0
  76. email/cancel_admin.html +8 -0
  77. email/checkout_check.html +17 -0
  78. email/checkout_check_admin.html +17 -0
  79. email/checkout_express.html +14 -0
  80. email/checkout_express_admin.html +14 -0
  81. email/checkout_free.html +8 -0
  82. email/checkout_free_admin.html +8 -0
  83. email/checkout_freetrial.html +22 -0
  84. email/checkout_freetrial_admin.html +22 -0
  85. email/checkout_paid.html +26 -0
  86. email/checkout_paid_admin.html +26 -0
  87. email/checkout_trial.html +26 -0
  88. email/checkout_trial_admin.html +26 -0
  89. email/credit_card_expiring.html +15 -0
  90. email/default.html +1 -0
  91. email/footer.html +4 -0
  92. email/header.html +1 -0
  93. email/invoice.html +23 -0
  94. email/membership_expired.html +7 -0
  95. email/membership_expiring.html +6 -0
  96. email/trial_ending.html +8 -0
  97. images/CCV-back.jpg +0 -0
  98. images/CCV-front.jpg +0 -0
  99. images/Paid-Memberships-Pro.png +0 -0
  100. images/Paid-Memberships-Pro_watermark.png +0 -0
  101. images/PaidMembershipsPro-grey.gif +0 -0
  102. images/PaidMembershipsPro.gif +0 -0
  103. images/bg_grad-chrome.gif +0 -0
  104. images/bg_grad-grey.gif +0 -0
  105. images/creditcards.gif +0 -0
  106. images/delete.gif +0 -0
  107. images/enlarge.gif +0 -0
  108. images/icon-pmproadmin16-sprite.png +0 -0
  109. images/icon-pmproadmin16-sprite_2x.png +0 -0
  110. images/icon-pmproadmin32.png +0 -0
  111. images/icon-pmproadmin32_2x.png +0 -0
  112. images/icon_alert.gif +0 -0
  113. images/icon_comment.gif +0 -0
  114. images/icon_comments.gif +0 -0
  115. images/icon_continue.gif +0 -0
  116. images/icon_delete.gif +0 -0
  117. images/icon_email.gif +0 -0
  118. images/icon_error.gif +0 -0
  119. images/icon_information.gif +0 -0
  120. images/icon_phone.gif +0 -0
  121. images/icon_search.gif +0 -0
  122. images/icon_success.gif +0 -0
  123. images/menu_users.png +0 -0
  124. images/printer.gif +0 -0
  125. images/spacer.gif +0 -0
  126. images/tag_sale.png +0 -0
  127. includes/adminpages.php +145 -0
  128. includes/cleanup.php +51 -0
  129. includes/content.php +370 -0
  130. includes/countries.php +259 -0
  131. includes/currencies.php +42 -0
  132. includes/email.php +79 -0
  133. includes/filters.php +150 -0
  134. includes/functions.php +1622 -0
  135. includes/https.php +150 -0
  136. includes/init.php +195 -0
  137. includes/lib/Braintree/Braintree.php +172 -0
  138. includes/lib/Braintree/Braintree/AddOn.php +15 -0
  139. includes/lib/Braintree/Braintree/Address.php +352 -0
  140. includes/lib/Braintree/Braintree/Collection.php +159 -0
  141. includes/lib/Braintree/Braintree/Configuration.php +346 -0
  142. includes/lib/Braintree/Braintree/CreditCard.php +591 -0
  143. includes/lib/Braintree/Braintree/CreditCardVerification.php +41 -0
  144. includes/lib/Braintree/Braintree/CreditCardVerificationSearch.php +34 -0
  145. includes/lib/Braintree/Braintree/Customer.php +562 -0
  146. includes/lib/Braintree/Braintree/CustomerSearch.php +31 -0
  147. includes/lib/Braintree/Braintree/Descriptor.php +4 -0
  148. includes/lib/Braintree/Braintree/Digest.php +59 -0
  149. includes/lib/Braintree/Braintree/Discount.php +15 -0
  150. includes/lib/Braintree/Braintree/EqualityNode.php +10 -0
  151. includes/lib/Braintree/Braintree/Error/Codes.php +206 -0
  152. includes/lib/Braintree/Braintree/Error/ErrorCollection.php +118 -0
  153. includes/lib/Braintree/Braintree/Error/Validation.php +64 -0
  154. includes/lib/Braintree/Braintree/Error/ValidationErrorCollection.php +135 -0
  155. includes/lib/Braintree/Braintree/Exception.php +20 -0
  156. includes/lib/Braintree/Braintree/Exception/Authentication.php +21 -0
  157. includes/lib/Braintree/Braintree/Exception/Authorization.php +23 -0
  158. includes/lib/Braintree/Braintree/Exception/Configuration.php +20 -0
  159. includes/lib/Braintree/Braintree/Exception/DownForMaintenance.php +20 -0
  160. includes/lib/Braintree/Braintree/Exception/ForgedQueryString.php +23 -0
  161. includes/lib/Braintree/Braintree/Exception/InvalidSignature.php +5 -0
  162. includes/lib/Braintree/Braintree/Exception/NotFound.php +20 -0
  163. includes/lib/Braintree/Braintree/Exception/SSLCaFileNotFound.php +20 -0
  164. includes/lib/Braintree/Braintree/Exception/SSLCertificate.php +20 -0
  165. includes/lib/Braintree/Braintree/Exception/ServerError.php +20 -0
  166. includes/lib/Braintree/Braintree/Exception/Unexpected.php +21 -0
  167. includes/lib/Braintree/Braintree/Exception/UpgradeRequired.php +12 -0
  168. includes/lib/Braintree/Braintree/Exception/ValidationsFailed.php +21 -0
  169. includes/lib/Braintree/Braintree/Http.php +99 -0
  170. includes/lib/Braintree/Braintree/Instance.php +70 -0
  171. includes/lib/Braintree/Braintree/IsNode.php +22 -0
  172. includes/lib/Braintree/Braintree/KeyValueNode.php +22 -0
  173. includes/lib/Braintree/Braintree/Modification.php +23 -0
  174. includes/lib/Braintree/Braintree/MultipleValueNode.php +37 -0
  175. includes/lib/Braintree/Braintree/MultipleValueOrTextNode.php +46 -0
  176. includes/lib/Braintree/Braintree/PartialMatchNode.php +16 -0
  177. includes/lib/Braintree/Braintree/Plan.php +55 -0
  178. includes/lib/Braintree/Braintree/RangeNode.php +38 -0
  179. includes/lib/Braintree/Braintree/ResourceCollection.php +148 -0
  180. includes/lib/Braintree/Braintree/Result/CreditCardVerification.php +86 -0
  181. includes/lib/Braintree/Braintree/Result/Error.php +107 -0
  182. includes/lib/Braintree/Braintree/Result/Successful.php +78 -0
  183. includes/lib/Braintree/Braintree/SettlementBatchSummary.php +74 -0
  184. includes/lib/Braintree/Braintree/Subscription.php +256 -0
  185. includes/lib/Braintree/Braintree/SubscriptionSearch.php +64 -0
  186. includes/lib/Braintree/Braintree/SubscriptionStatus.php +0 -0
  187. includes/lib/Braintree/Braintree/Test/CreditCardNumbers.php +76 -0
  188. includes/lib/Braintree/Braintree/Test/TransactionAmounts.php +24 -0
  189. includes/lib/Braintree/Braintree/TextNode.php +10 -0
  190. includes/lib/Braintree/Braintree/Transaction.php +664 -0
  191. includes/lib/Braintree/Braintree/Transaction/AddressDetails.php +32 -0
  192. includes/lib/Braintree/Braintree/Transaction/CreditCardDetails.php +43 -0
  193. includes/lib/Braintree/Braintree/Transaction/CustomerDetails.php +29 -0
  194. includes/lib/Braintree/Braintree/Transaction/StatusDetails.php +25 -0
  195. includes/lib/Braintree/Braintree/Transaction/SubscriptionDetails.php +22 -0
  196. includes/lib/Braintree/Braintree/TransactionSearch.php +124 -0
  197. includes/lib/Braintree/Braintree/TransparentRedirect.php +327 -0
  198. includes/lib/Braintree/Braintree/Util.php +290 -0
  199. includes/lib/Braintree/Braintree/Version.php +39 -0
  200. includes/lib/Braintree/Braintree/WebhookNotification.php +66 -0
  201. includes/lib/Braintree/Braintree/WebhookTesting.php +52 -0
  202. includes/lib/Braintree/Braintree/Xml.php +43 -0
  203. includes/lib/Braintree/Braintree/Xml/Generator.php +144 -0
  204. includes/lib/Braintree/Braintree/Xml/Parser.php +179 -0
  205. includes/lib/Braintree/ssl/sandbox_braintreegateway_com.ca.crt +19 -0
  206. includes/lib/Braintree/ssl/www_braintreegateway_com.ca.crt +202 -0
  207. includes/lib/CyberSource/cyber_source_soap_client.php +87 -0
  208. includes/lib/Stripe/Stripe.php +45 -0
  209. includes/lib/Stripe/Stripe/Account.php +16 -0
  210. includes/lib/Stripe/Stripe/ApiConnectionError.php +5 -0
  211. includes/lib/Stripe/Stripe/ApiError.php +5 -0
  212. includes/lib/Stripe/Stripe/ApiRequestor.php +200 -0
  213. includes/lib/Stripe/Stripe/ApiResource.php +104 -0
  214. includes/lib/Stripe/Stripe/AuthenticationError.php +5 -0
  215. includes/lib/Stripe/Stripe/CardError.php +11 -0
  216. includes/lib/Stripe/Stripe/Charge.php +55 -0
  217. includes/lib/Stripe/Stripe/Coupon.php +34 -0
  218. includes/lib/Stripe/Stripe/Customer.php +102 -0
  219. includes/lib/Stripe/Stripe/Error.php +27 -0
  220. includes/lib/Stripe/Stripe/Event.php +22 -0
  221. includes/lib/Stripe/Stripe/InvalidRequestError.php +10 -0
  222. includes/lib/Stripe/Stripe/Invoice.php +51 -0
  223. includes/lib/Stripe/Stripe/InvoiceItem.php +40 -0
  224. includes/lib/Stripe/Stripe/List.php +17 -0
  225. includes/lib/Stripe/Stripe/Object.php +144 -0
  226. includes/lib/Stripe/Stripe/Plan.php +40 -0
  227. includes/lib/Stripe/Stripe/SingletonApiResource.php +24 -0
  228. includes/lib/Stripe/Stripe/Stripe.php +27 -0
  229. includes/lib/Stripe/Stripe/Token.php +22 -0
  230. includes/lib/Stripe/Stripe/Transfer.php +22 -0
  231. includes/lib/Stripe/Stripe/Util.php +61 -0
  232. includes/lib/Stripe/Stripe/Util/Set.php +34 -0
  233. includes/lib/Stripe/data/ca-certificates.crt +2126 -0
adminpages/addons.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || !current_user_can("manage_options"))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb, $msg, $msgt, $pmpro_addons;
9
+
10
+ /*
11
+ Addon lists
12
+ */
13
+ $pmpro_addon_lists = array(
14
+ 'repo' => array('Plugins in the WordPress Repository', 'These official PMPro plugins are available in the WordPress repository and can be installed through Plugins --> Add New.'),
15
+ 'thirdparty' => array('Third-party Integration', 'These official PMPro plugins integrate with specific third-party tools and software.'),
16
+ 'recommended' => array('Recommended Plugins', 'These plugins are not developed by the PMPro team, but are recommended for sites running PMPro.'),
17
+ 'github' => array('Plugins on GitHub', 'These official PMPro plugins must be downloaded from GitHub and installed through Plugins --> Add New --> Upload, then activated. These plugins cannot be automatically updated and may require more developer input.'),
18
+ 'gists' => array('Code Gists', 'These are bits of code that generally must be added to your active theme\'s functions.php file or included in a custom plugin. Most gists require customization and are recommended for developers only.')
19
+ );
20
+
21
+ /*
22
+ Function to add an addon
23
+ */
24
+ function pmpro_add_addon($list, $addon)
25
+ {
26
+ global $pmpro_addons;
27
+
28
+ //make sure we have the base array
29
+ if(empty($pmpro_addons))
30
+ $pmpro_addons = array();
31
+
32
+ //make sure we have an array for the list
33
+ if(empty($pmpro_addons[$list]))
34
+ $pmpro_addons[$list] = array();
35
+
36
+ //add addon to list
37
+ $pmpro_addons[$list][] = $addon;
38
+ }
39
+
40
+ /*
41
+ Load All Addons
42
+ */
43
+ $pmpro_addons_dir = dirname(__FILE__) . "/../adminpages/addons/";
44
+ $cwd = getcwd();
45
+ chdir($pmpro_addons_dir);
46
+ $count = 0;
47
+ foreach (glob("*.php") as $filename)
48
+ {
49
+ $count++;
50
+ require_once($filename);
51
+ }
52
+ chdir($cwd);
53
+
54
+ require_once(dirname(__FILE__) . "/admin_header.php");
55
+ ?>
56
+
57
+ <h2>Add Ons</h2>
58
+ <ul id="addon-filters" class="subsubsub">
59
+ <li id="addon-filters-all"><a href="javascript:void(0);" class="current all tab">All</a> <span>(<?php echo $count;?>)</span></li>
60
+ <?php foreach($pmpro_addon_lists as $list => $list_info) { ?>
61
+ <li id="addon-filters-<?php echo $list;?>"> | <a href="javascript:void(0);>" class="tab"><?php echo $list_info[0];?></a> <span>(<?php echo count($pmpro_addons[$list]);?>)</span></li>
62
+ <?php } ?>
63
+ </ul>
64
+
65
+ <?php foreach($pmpro_addon_lists as $list => $list_info) { ?>
66
+ <div id="pmpro-<?php echo $list;?>" class="pmpro-addon-list widgets-holder-wrap">
67
+
68
+ <h3 class="section-title"><?php echo $list_info[0];?></h3>
69
+ <p class="description"><?php echo $list_info[1];?></p>
70
+ <br class="clear" />
71
+
72
+ <div id="addons-list-<?php echo $list;?>" class="addon-list">
73
+
74
+ <?php foreach($pmpro_addons[$list] as $slug => $addon) { ?>
75
+ <div id="addon-<?php echo $slug;?>" class="widget <?php if($addon['enabled']) echo "enabled"; else echo "disabled";?>">
76
+ <div class="widget-top">
77
+ <div class="widget-title">
78
+ <h4>
79
+ <span class="status-label"><?php if($addon['enabled']) echo __("Enabled", "pmpro"); else echo __("Disabled", "pmpro");?></span>
80
+ <span class="title"><?php echo $addon['title'];?></span>
81
+ <span class="version pmpro_tag-grey"><?php echo $addon['version'];?></span>
82
+ <span class="in-widget-title"></span>
83
+ </h4>
84
+ </div> <!-- end widget-title -->
85
+ </div> <!-- end widget-top -->
86
+ <div class="widget-inside">
87
+ <?php call_user_func($addon['widget'], $addon);?>
88
+ </div> <!-- end addon-inside -->
89
+ </div> <!-- end widget -->
90
+ <?php } ?>
91
+
92
+ <br class="clear" />
93
+ </div> <!-- end addon-list -->
94
+
95
+ </div> <!-- end pmpro-<?php echo $list;?> -->
96
+ <?php } ?>
97
+
98
+ <script>
99
+ //tabs
100
+ jQuery(document).ready(function() {
101
+ jQuery('#addon-filters a.tab').click(function() {
102
+ //which tab?
103
+ var tab = jQuery(this).parent().attr('id').replace('addon-filters-', '');
104
+
105
+ //un select tabs
106
+ jQuery('#addon-filters a.tab').removeClass('current');
107
+
108
+ //select this tab
109
+ jQuery('#addon-filters-'+tab+' a').addClass('current');
110
+
111
+ //show all?
112
+ if(tab == 'all')
113
+ jQuery('div.pmpro-addon-list').show();
114
+ else
115
+ {
116
+ //hide all
117
+ jQuery('div.pmpro-addon-list').hide();
118
+
119
+ //show this one
120
+ jQuery('#pmpro-'+tab).show();
121
+ }
122
+ });
123
+ });
124
+
125
+ //resize addon boxes
126
+ jQuery(document).ready(function() {
127
+ jQuery('.addon-list').each(function() {
128
+ //what's the tallest p in the list?
129
+ var tallest = 32;
130
+ jQuery(this).find('div.info p').each(function() {
131
+ tallest = Math.max(tallest, jQuery(this).height());
132
+ });
133
+
134
+ //set all p's to match
135
+ jQuery(this).find('div.info p').css('height', tallest);
136
+ });
137
+ });
138
+ </script>
139
+
140
+ <?php
141
+ require_once(dirname(__FILE__) . "/admin_footer.php");
142
+ ?>
adminpages/addons/hide-admin-bar-from-non-admins.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: Hide Admin Bar From Non-Admins
4
+ Slug: hide-admin-bar-from-non-admins
5
+ */
6
+ pmpro_add_addon('repo', array(
7
+ 'title' => 'Hide Admin Bar From Non-Admins',
8
+ 'version' => '1.0',
9
+ 'widget' => 'pmpro_addon_hide_admin_bar_from_non_admins_widget',
10
+ 'enabled' => function_exists('habfna_disable_admin_bar')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_hide_admin_bar_from_non_admins_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Perfect for sites where there is only one admin who needs access to the dashboard and the admin bar. When activated only administrators will see the admin bar.</p>
19
+ <div class="actions">
20
+ <form method="post" name="component-actions" action="">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="<?php echo admin_url("plugins.php");?>" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../hide-admin-bar-from-non-admins/hide-admin-bar-from-non-admins.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=hide-admin-bar-from-non-admins/hide-admin-bar-from-non-admins.php'), 'activate-plugin_hide-admin-bar-from-non-admins/hide-admin-bar-from-non-admins.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="<?php echo wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=hide-admin-bar-from-non-admins'), 'install-plugin_hide-admin-bar-from-non-admins'); ?>" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </form>
29
+ </div>
30
+ </div> <!-- end info -->
31
+ <?php
32
+ }
adminpages/addons/images/pmpro-aweber.gif ADDED
Binary file
adminpages/addons/images/pmpro-infusionsoft.jpg ADDED
Binary file
adminpages/addons/images/pmpro-mailchimp.jpg ADDED
Binary file
adminpages/addons/images/pmpro-network.gif ADDED
Binary file
adminpages/addons/images/pmpro-post-affiliate-pro.jpg ADDED
Binary file
adminpages/addons/images/pmpro-register-helper.gif ADDED
Binary file
adminpages/addons/images/pmpro-series.gif ADDED
Binary file
adminpages/addons/images/pmpro-wp-affiliate-platform.jpg ADDED
Binary file
adminpages/addons/images/wp-bouncer.gif ADDED
Binary file
adminpages/addons/pmpro-addon-packages.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Addon Packages
4
+ Slug: pmpro-addon-packages
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Addon Packages',
8
+ 'version' => '.1.3',
9
+ 'widget' => 'pmpro_addon_pmpro_addon_packages_widget',
10
+ 'enabled' => function_exists('pmproap_post_meta')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_addon_packages_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Sell access to individual pages or posts for a flat fee. This is a workaround if you would like to allow multiple membership levels per user.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a href="https://github.com/strangerstudios/pmpro-addon-packages/blob/master/readme.txt" class="button">Enabled</a>
22
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-addon-packages/pmpro-addon-packages.php")) { ?>
23
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-addon-packages/pmpro-addon-packages.php'), 'activate-plugin_pmpro-addon-packages/pmpro-addon-packages.php')?>" class="button button-primary">Activate</a>
24
+ <?php } else { ?>
25
+ <a href="https://github.com/strangerstudios/pmpro-addon-packages/archive/master.zip" class="button button-primary">Download</a>
26
+ <?php } ?>
27
+ </div>
28
+ </div> <!-- end info -->
29
+ <?php
30
+ }
adminpages/addons/pmpro-affiliates.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Affiliates
4
+ Slug: pmpro-affiliates
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Affiliates',
8
+ 'version' => '.2.2',
9
+ 'widget' => 'pmpro_addon_pmpro_affiliates_widget',
10
+ 'enabled' => function_exists('pmpro_affiliates_dependencies')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_affiliates_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Lightweight Affiliate system. Create affiliate accounts and codes; tracks checkouts by affiliate account.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a href="https://github.com/strangerstudios/pmpro-affiliates/blob/master/readme.txt" class="button">Enabled</a>
22
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-affiliates/pmpro-affiliates.php")) { ?>
23
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-affiliates/pmpro-affiliates.php'), 'activate-plugin_pmpro-affiliates/pmpro-affiliates.php')?>" class="button button-primary">Activate</a>
24
+ <?php } else { ?>
25
+ <a href="https://github.com/strangerstudios/pmpro-affiliates/archive/master.zip" class="button button-primary">Download</a>
26
+ <?php } ?>
27
+ </div>
28
+ </div> <!-- end info -->
29
+ <?php
30
+ }
adminpages/addons/pmpro-aweber.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro AWeber Integration
4
+ Slug: pmpro-aweber
5
+ */
6
+ pmpro_add_addon('thirdparty', array(
7
+ 'title' => 'PMPro AWeber Integration',
8
+ 'version' => '.2',
9
+ 'widget' => 'pmpro_addon_pmpro_aweber_widget',
10
+ 'enabled' => function_exists('pmproaw_init')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_aweber_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-aweber.gif" />
18
+ <div class="info">
19
+ <p>Integrate User Registrations with AWeber. Adds members to lists based on their membership level. (Note: works without PMPro as well.)</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-aweber/" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-aweber/pmpro-aweber.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-aweber/pmpro-aweber.php'), 'activate-plugin_pmpro-aweber/pmpro-aweber.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="<?php echo wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=pmpro-aweber'), 'install-plugin_pmpro-aweber'); ?>" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-bbpress.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro bbPress
4
+ Slug: pmpro-bbpress
5
+ */
6
+ pmpro_add_addon('gists', array(
7
+ 'title' => 'PMPro bbPress',
8
+ 'version' => '.1',
9
+ 'widget' => 'pmpro_addon_pmpro_bbpress_widget',
10
+ 'enabled' => function_exists('pmpro_check_forum')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_bbpress_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Locking down bbPress Forums by Membership Level and Forum ID.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a target="_blank" href="https://gist.github.com/strangerstudios/1633637" class="button">Enabled</a>
22
+ <?php } else { ?>
23
+ <a target="_blank" href="https://gist.github.com/strangerstudios/1633637" class="button button-primary">View Gist</a>
24
+ <?php } ?>
25
+ </div>
26
+ </div> <!-- end info -->
27
+ <?php
28
+ }
adminpages/addons/pmpro-disable-emails.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Disable PMPro Emails
4
+ Slug: pmpro-disable-emails
5
+ */
6
+ pmpro_add_addon('gists', array(
7
+ 'title' => 'PMPro Disable Emails',
8
+ 'version' => '.1',
9
+ 'widget' => 'pmpro_addon_pmpro_disable_emails_widget',
10
+ 'enabled' => function_exists('dae_pmpro_email_recipient')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_disable_emails_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Disable all or specific emails sent by the PMPro plugin.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a target="_blank" href="https://gist.github.com/strangerstudios/3667545" class="button">Enabled</a>
22
+ <?php } else { ?>
23
+ <a target="_blank" href="https://gist.github.com/strangerstudios/3667545" class="button button-primary">View Gist</a>
24
+ <?php } ?>
25
+ </div>
26
+ </div> <!-- end info -->
27
+ <?php
28
+ }
adminpages/addons/pmpro-expiration.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Expiration Date
4
+ Slug: pmpro-expiration
5
+ */
6
+ pmpro_add_addon('gists', array(
7
+ 'title' => 'PMPro Expiration Date',
8
+ 'version' => '.1',
9
+ 'widget' => 'pmpro_addon_pmpro_expiration_widget',
10
+ 'enabled' => function_exists('my_pmpro_checkout_level_specific_expiration')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_expiration_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Set a specific expiration date for a Membership Level.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a target="_blank" href="https://gist.github.com/strangerstudios/5709300" class="button">Enabled</a>
22
+ <?php } else { ?>
23
+ <a target="_blank" href="https://gist.github.com/strangerstudios/5709300" class="button button-primary">View Gist</a>
24
+ <?php } ?>
25
+ </div>
26
+ </div> <!-- end info -->
27
+ <?php
28
+ }
adminpages/addons/pmpro-freeaddress.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Require Name and Address for Free Level
4
+ Slug: pmpro-freerequire
5
+ */
6
+ pmpro_add_addon('gists', array(
7
+ 'title' => 'PMPro Require Name/Address for Free Level',
8
+ 'version' => '.1',
9
+ 'widget' => 'pmpro_addon_pmpro_freerequire_widget',
10
+ 'enabled' => function_exists('my_pmpro_checkout_boxes_require_address')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_freerequire_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Require name/address for free Membership Level checkout.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a target="_blank" href="https://gist.github.com/strangerstudios/5716249" class="button">Enabled</a>
22
+ <?php } else { ?>
23
+ <a target="_blank" href="https://gist.github.com/strangerstudios/5716249" class="button button-primary">View Gist</a>
24
+ <?php } ?>
25
+ </div>
26
+ </div> <!-- end info -->
27
+ <?php
28
+ }
adminpages/addons/pmpro-infusionsoft.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Infusionsoft Integration
4
+ Slug: pmpro-infusionsoft
5
+ */
6
+ pmpro_add_addon('thirdparty', array(
7
+ 'title' => 'PMPro Infusionsoft Integration',
8
+ 'version' => '.2',
9
+ 'widget' => 'pmpro_addon_pmpro_infusionsoft_widget',
10
+ 'enabled' => function_exists('pmprois_init')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_infusionsoft_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-infusionsoft.jpg" />
18
+ <div class="info">
19
+ <p>Integrate with Infusionsoft. Add members to email lists (groups, tags) based on their membership level. (Note: works without PMPro as well.)</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-infusionsoft/" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-infusionsoft/pmpro-infusionsoft.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-infusionsoft/pmpro-infusionsoft.php'), 'activate-plugin_pmpro-infusionsoft/pmpro-infusionsoft.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-infusionsoft.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-international-addresses.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro International Addresses
4
+ Slug: pmpro-international-addresses
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro International Addresses',
8
+ 'version' => '.2.2',
9
+ 'widget' => 'pmpro_addon_pmpro_international_addresses_widget',
10
+ 'enabled' => function_exists('pmproia_pmpro_international_addresses')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_international_addresses_widget($addon)
15
+ {
16
+ ?>
17
+ <?php /* <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-international-addresses.jpg" /> */ ?>
18
+ <div class="info">
19
+ <p>Adds long form addresses to the PMPro checkout.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-international-addresses/" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-international-addresses/pmpro-international-addresses.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-international-addresses/pmpro-international-addresses.php'), 'activate-plugin_pmpro-international-addresses/pmpro-international-addresses.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-international-addresses.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-level-cost-text.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Custom Level Cost Text
4
+ Slug: pmpro-level-cost-text
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Level Cost Text',
8
+ 'version' => '.2',
9
+ 'widget' => 'pmpro_addon_pmpro_level_cost_text_widget',
10
+ 'enabled' => function_exists('pclct_pmpro_discount_code_after_level_settings')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_level_cost_text_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Adds a "level cost text" field to PMPro Membership Levels and Discount Codes to allow you to override the automatically generated level cost text PMPro provides.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a href="https://github.com/strangerstudios/pmpro-level-cost-text/blob/master/readme.txt" class="button">Enabled</a>
22
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-level-cost-text/pmpro-level-cost-text.php")) { ?>
23
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-level-cost-text/pmpro-level-cost-text.php'), 'activate-plugin_pmpro-level-cost-text/pmpro-level-cost-text.php')?>" class="button button-primary">Activate</a>
24
+ <?php } else { ?>
25
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-level-cost-text.zip" class="button button-primary">Download</a>
26
+ <?php } ?>
27
+ </div>
28
+ </div> <!-- end info -->
29
+ <?php
30
+ }
adminpages/addons/pmpro-mailchimp.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro MailChimp Integration
4
+ Slug: pmpro-mailchimp
5
+ */
6
+ pmpro_add_addon('thirdparty', array(
7
+ 'title' => 'PMPro MailChimp Integration',
8
+ 'version' => '.3.2',
9
+ 'widget' => 'pmpro_addon_pmpro_mailchimp_widget',
10
+ 'enabled' => function_exists('pmpromc_init')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_mailchimp_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-mailchimp.jpg" />
18
+ <div class="info">
19
+ <p>Integrate User Registrations with Mailchimp. Adds members to lists based on their membership level. (Note: works without PMPro as well.)</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-mailchimp/" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-mailchimp/pmpro-mailchimp.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-mailchimp/pmpro-mailchimp.php'), 'activate-plugin_pmpro-mailchimp/pmpro-mailchimp.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="<?php echo wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=pmpro-mailchimp'), 'install-plugin_pmpro-mailchimp'); ?>" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-network.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Network
4
+ Slug: pmpro-network
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Network',
8
+ 'version' => '.3.1',
9
+ 'widget' => 'pmpro_addon_pmpro_network_widget',
10
+ 'enabled' => function_exists('pmpron_new_blogs_settings')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_network_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-network.gif" />
18
+ <div class="info">
19
+ <p>Allow users to checkout for a membership to create a site on your WordPress multisite network.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-network/blob/master/readme.txt" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-network/pmpro-network.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-network/pmpro-network.php'), 'activate-plugin_pmpro-network/pmpro-network.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-network.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-post-affiliate-pro.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Post Affiliate Pro Integration
4
+ Slug: pmpro-post-affiliate-pro
5
+ */
6
+ pmpro_add_addon('thirdparty', array(
7
+ 'title' => 'PMPro Post Affiliate Pro Integration',
8
+ 'version' => '.3',
9
+ 'widget' => 'pmpro_addon_pmpro_post_affiliate_pro_widget',
10
+ 'enabled' => function_exists('pap_pmpro_track_sale')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_post_affiliate_pro_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-post-affiliate-pro.jpg" />
18
+ <div class="info">
19
+ <p>Integrate Paid Memberships Pro with the Post Affiliate Pro platform.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-post-affiliate-pro/blob/master/readme.txt" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-post-affiliate-pro/pmpro-post-affiliate-pro.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-post-affiliate-pro/pmpro-post-affiliate-pro.php'), 'activate-plugin_pmpro-post-affiliate-pro/pmpro-post-affiliate-pro.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-post-affiliate-pro.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-register-helper.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Register Helper
4
+ Slug: pmpro-register-helper
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Register Helper',
8
+ 'version' => '.5.2',
9
+ 'widget' => 'pmpro_addon_pmpro_register_helper_widget',
10
+ 'enabled' => class_exists('PMProRH_Field')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_register_helper_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-register-helper.gif" />
18
+ <div class="info">
19
+ <p>Add additional meta fields to your PMPro checkout page and/or "Your Profile" pages. Support for text, select, multi-select, textarea, hidden, and custom HTML. Loop into existing checkout/profile field sections or add new ones.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-register-helper/blob/master/readme.txt" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-register-helper/pmpro-register-helper.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-register-helper/pmpro-register-helper.php'), 'activate-plugin_pmpro-register-helper/pmpro-register-helper.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-register-helper.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-require-code-to-register.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Require Code to Register
4
+ Slug: pmpro-require-code-to-register
5
+ */
6
+ pmpro_add_addon('gists', array(
7
+ 'title' => 'PMPro Require a Code to Register',
8
+ 'version' => '.1',
9
+ 'widget' => 'pmpro_addon_pmpro_require_code_to_register_widget',
10
+ 'enabled' => function_exists('my_pmpro_registration_checks_require_code_to_register')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_require_code_to_register_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Require a discount code to checkout for a specific level.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a target="_blank" href="https://gist.github.com/strangerstudios/5573829" class="button">Enabled</a>
22
+ <?php } else { ?>
23
+ <a target="_blank" href="https://gist.github.com/strangerstudios/5573829" class="button button-primary">View Gist</a>
24
+ <?php } ?>
25
+ </div>
26
+ </div> <!-- end info -->
27
+ <?php
28
+ }
adminpages/addons/pmpro-series.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Series
4
+ Slug: pmpro-series
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Series',
8
+ 'version' => '.2',
9
+ 'widget' => 'pmpro_addon_pmpro_series_widget',
10
+ 'enabled' => class_exists("PMProSeries")
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_series_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-series.gif" />
18
+ <div class="info">
19
+ <p>"Drip feed" content to your members over the course of their membership. Serializes content by # of days post-registration.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="<?php echo admin_url("edit.php?post_type=pmpro_series");?>" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-series/pmpro-series.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-series/pmpro-series.php'), 'activate-plugin_pmpro-series/pmpro-series.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-series.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-shipping.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Shipping Add On
4
+ Slug: pmpro-shipping
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Shipping Add On',
8
+ 'version' => '.2.2.1',
9
+ 'widget' => 'pmpro_addon_pmpro_shipping_widget',
10
+ 'enabled' => function_exists('pmproship_pmpro_checkout_boxes')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_shipping_widget($addon)
15
+ {
16
+ ?>
17
+ <?php /* <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-shipping.jpg" /> */ ?>
18
+ <div class="info">
19
+ <p>Adds shipping fields to the checkout page, confirmation page, confirmation emails, member's list and edit user profile pages.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/pmpro-shipping/" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-shipping/pmpro-shipping.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-shipping/pmpro-shipping.php'), 'activate-plugin_pmpro-shipping/pmpro-shipping.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-shipping.zip" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/addons/pmpro-stripe-lite.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro Stripe Lite
4
+ Slug: pmpro-stripe-lite
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro Stripe Lite',
8
+ 'version' => '.1.1',
9
+ 'widget' => 'pmpro_addon_pmpro_stripe_lite_widget',
10
+ 'enabled' => function_exists('pmprosl_pmpro_pages_shortcode_checkout')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_stripe_lite_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Remove billing fields (not required by Stripe) from the checkout page when using the Stripe payment gateway with PMPro.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a href="https://github.com/strangerstudios/pmpro-stripe-lite/blob/master/readme.txt" class="button">Enabled</a>
22
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-stripe-lite/pmpro-stripe-lite.php")) { ?>
23
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-stripe-lite/pmpro-stripe-lite.php'), 'activate-plugin_pmpro-stripe-lite/pmpro-stripe-lite.php')?>" class="button button-primary">Activate</a>
24
+ <?php } else { ?>
25
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-stripe-lite.zip" class="button button-primary">Download</a>
26
+ <?php } ?>
27
+ </div>
28
+ </div> <!-- end info -->
29
+ <?php
30
+ }
adminpages/addons/pmpro-user-pages.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro User Pages
4
+ Slug: pmpro-user-pages
5
+ */
6
+ pmpro_add_addon('github', array(
7
+ 'title' => 'PMPro User Pages',
8
+ 'version' => '.3',
9
+ 'widget' => 'pmpro_addon_pmpro_user_pages_widget',
10
+ 'enabled' => function_exists('pmproup_pmpro_after_checkout')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_user_pages_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>Creates a unique page for each Member after checkout, giving the Admin access to write customized content for each specific member.</p>
19
+ <div class="actions">
20
+ <?php if($addon['enabled']) { ?>
21
+ <a href="https://github.com/strangerstudios/pmpro-user-pages/blob/master/readme.txt" class="button">Enabled</a>
22
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../pmpro-user-pages/pmpro-user-pages.php")) { ?>
23
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=pmpro-user-pages/pmpro-user-pages.php'), 'activate-plugin_pmpro-user-pages/pmpro-user-pages.php')?>" class="button button-primary">Activate</a>
24
+ <?php } else { ?>
25
+ <a href="http://www.paidmembershipspro.com/wp-content/uploads/plugins/pmpro-user-pages.zip" class="button button-primary">Download</a>
26
+ <?php } ?>
27
+ </div>
28
+ </div> <!-- end info -->
29
+ <?php
30
+ }
adminpages/addons/pmpro-wp-affiliate.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: PMPro WP Affiliate Platform Integration
4
+ Slug: pmpro-wp-affiliate
5
+ */
6
+ pmpro_add_addon('thirdparty', array(
7
+ 'title' => 'PMPro WP Affiliate Platform Integration',
8
+ 'version' => '.3',
9
+ 'widget' => 'pmpro_addon_pmpro_wp_affiliate_widget',
10
+ 'enabled' => function_exists('wpa_pmpro_after_checkout')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_pmpro_wp_affiliate_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/pmpro-wp-affiliate-platform.jpg" />
18
+ <div class="info">
19
+ <p>Process an affiliate via WP Affiliate Platform after a PMPro checkout.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a target="_blank" href="https://gist.github.com/strangerstudios/3137539" class="button">Enabled</a>
23
+ <?php } else { ?>
24
+ <a target="_blank" href="https://gist.github.com/strangerstudios/3137539" class="button button-primary">View Gist</a>
25
+ <?php } ?>
26
+ </div>
27
+ </div> <!-- end info -->
28
+ <?php
29
+ }
adminpages/addons/tml.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: Theme My Login
4
+ Slug: pmpro-tml
5
+ */
6
+ pmpro_add_addon('recommended', array(
7
+ 'title' => 'Theme My Login',
8
+ 'widget' => 'pmpro_addon_tml_widget',
9
+ 'enabled' => class_exists('Theme_My_Login'),
10
+ 'version' => '6.3.8'
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_tml_widget($addon)
15
+ {
16
+ ?>
17
+ <div class="info">
18
+ <p>This plugin themes the WordPress login, registration and forgot password pages according to your current theme. By <a href="http://www.jfarthing.com/" target="_blank">Jeff Farthing</a></p>
19
+ <div class="actions">
20
+ <form method="post" name="component-actions" action="">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="<?php echo admin_url("plugins.php");?>" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../theme-my-login/theme-my-login.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=theme-my-login/theme-my-login.php'), 'activate-plugin_theme-my-login/theme-my-login.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="<?php echo wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=theme-my-login'), 'install-plugin_theme-my-login'); ?>" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </form>
29
+ </div>
30
+ </div> <!-- end info -->
31
+ <?php
32
+ }
adminpages/addons/wp-bouncer.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Addon: WP Bouncer
4
+ Slug: wp-bouncer
5
+ */
6
+ pmpro_add_addon('repo', array(
7
+ 'title' => 'WP Bouncer',
8
+ 'version' => '1.0.1',
9
+ 'widget' => 'pmpro_addon_wp_bouncer_widget',
10
+ 'enabled' => class_exists('WP_Bouncer')
11
+ )
12
+ );
13
+
14
+ function pmpro_addon_wp_bouncer_widget($addon)
15
+ {
16
+ ?>
17
+ <img class="addon-thumb" src="<?php echo PMPRO_URL?>/adminpages/addons/images/wp-bouncer.gif" />
18
+ <div class="info">
19
+ <p>Make sure users are only logged in from one computer or device at a time.</p>
20
+ <div class="actions">
21
+ <?php if($addon['enabled']) { ?>
22
+ <a href="https://github.com/strangerstudios/wp-bouncer/blob/master/readme.txt" class="button">Enabled</a>
23
+ <?php } elseif(file_exists(dirname(__FILE__) . "/../../../wp-bouncer/wp-bouncer.php")) { ?>
24
+ <a href="<?php echo wp_nonce_url(self_admin_url('plugins.php?action=activate&plugin=wp-bouncer/wp-bouncer.php'), 'activate-plugin_wp-bouncer/wp-bouncer.php')?>" class="button button-primary">Activate</a>
25
+ <?php } else { ?>
26
+ <a href="<?php echo wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=wp-bouncer'), 'install-plugin_wp-bouncer'); ?>" class="button button-primary">Download</a>
27
+ <?php } ?>
28
+ </div>
29
+ </div> <!-- end info -->
30
+ <?php
31
+ }
adminpages/admin_footer.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <div class="clear"></div>
2
+ </div>
adminpages/admin_header.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/functions.php");
3
+
4
+ if(isset($_REQUEST['page']))
5
+ $view = $_REQUEST['page'];
6
+ else
7
+ $view = "";
8
+
9
+ global $pmpro_ready, $msg, $msgt;
10
+ $pmpro_ready = pmpro_is_ready();
11
+ if(!$pmpro_ready)
12
+ {
13
+ global $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready;
14
+ if(!isset($edit))
15
+ {
16
+ if(isset($_REQUEST['edit']))
17
+ $edit = $_REQUEST['edit'];
18
+ else
19
+ $edit = false;
20
+ }
21
+
22
+ if(empty($msg))
23
+ $msg = -1;
24
+ if(empty($pmpro_level_ready) && empty($edit))
25
+ $msgt .= " <a href=\"?page=pmpro-membershiplevels&edit=-1\">" . __("Add a membership level to get started.", "pmpro") . "</a>";
26
+ elseif($pmpro_level_ready && !$pmpro_pages_ready && $view != "pmpro-pagesettings")
27
+ $msgt .= " <a href=\"?page=pmpro-pagesettings\">" . __("Setup the membership pages", "pmpro") . "</a>.";
28
+ elseif($pmpro_level_ready && $pmpro_pages_ready && !$pmpro_gateway_ready && $view != "pmpro-paymentsettings")
29
+ $msgt .= " <a href=\"?page=pmpro-paymentsettings\">" . __("Setup your SSL certificate and payment gateway", "pmpro") . "</a>.";
30
+
31
+ if(empty($msgt))
32
+ $msg = false;
33
+ }
34
+
35
+ if(!pmpro_checkLevelForStripeCompatibility())
36
+ {
37
+ $msg = -1;
38
+ $msgt = __("The billing details for some of your membership levels is not supported by Stripe.", "pmpro");
39
+ if($view == "pmpro-membershiplevels" && !empty($_REQUEST['edit']) && $_REQUEST['edit'] > 0)
40
+ {
41
+ if(!pmpro_checkLevelForStripeCompatibility($_REQUEST['edit']))
42
+ {
43
+ global $pmpro_stripe_error;
44
+ $pmpro_stripe_error = true;
45
+ $msg = -1;
46
+ $msgt = __("The billing details for this level are not supported by Stripe. Please review the notes in the Billing Details section below.", "pmpro");
47
+ }
48
+ }
49
+ elseif($view == "pmpro-membershiplevels")
50
+ $msgt .= " " . __("The levels with issues are highlighted below.", "pmpro");
51
+ else
52
+ $msgt .= " <a href=\"?page=pmpro-membershiplevels\">" . __("Please edit your levels", "pmpro") . "</a>.";
53
+ }
54
+
55
+ if(!pmpro_checkLevelForPayflowCompatibility())
56
+ {
57
+ $msg = -1;
58
+ $msgt = __("The billing details for some of your membership levels is not supported by Payflow.", "pmpro");
59
+ if($view == "pmpro-membershiplevels" && !empty($_REQUEST['edit']) && $_REQUEST['edit'] > 0)
60
+ {
61
+ if(!pmpro_checkLevelForPayflowCompatibility($_REQUEST['edit']))
62
+ {
63
+ global $pmpro_payflow_error;
64
+ $pmpro_payflow_error = true;
65
+ $msg = -1;
66
+ $msgt = __("The billing details for this level are not supported by Payflow. Please review the notes in the Billing Details section below.", "pmpro");
67
+ }
68
+ }
69
+ elseif($view == "pmpro-membershiplevels")
70
+ $msgt .= " " . __("The levels with issues are highlighted below.", "pmpro");
71
+ else
72
+ $msgt .= " <a href=\"?page=pmpro-membershiplevels\">" . __("Please edit your levels", "pmpro") . "</a>.";
73
+ }
74
+
75
+ if(!pmpro_checkLevelForBraintreeCompatibility())
76
+ {
77
+ $msg = -1;
78
+ $msgt = __("The billing details for some of your membership levels is not supported by Braintree.", "pmpro");
79
+ if($view == "pmpro-membershiplevels" && !empty($_REQUEST['edit']) && $_REQUEST['edit'] > 0)
80
+ {
81
+ if(!pmpro_checkLevelForBraintreeCompatibility($_REQUEST['edit']))
82
+ {
83
+ global $pmpro_braintree_error;
84
+ $pmpro_braintree_error = true;
85
+ $msg = -1;
86
+ $msgt = __("The billing details for this level are not supported by Braintree. Please review the notes in the Billing Details section below.", "pmpro");
87
+ }
88
+ }
89
+ elseif($view == "pmpro-membershiplevels")
90
+ $msgt .= " " . __("The levels with issues are highlighted below.", "pmpro");
91
+ else
92
+ $msgt .= " <a href=\"?page=pmpro-membershiplevels\">" . __("Please edit your levels", "pmpro") . "</a>.";
93
+ }
94
+
95
+ if(!empty($msg))
96
+ {
97
+ ?>
98
+ <div id="message" class="<?php if($msg > 0) echo "updated fade"; else echo "error"; ?>"><p><?php echo $msgt?></p></div>
99
+ <?php
100
+ }
101
+
102
+ ?>
103
+ <div class="wrap pmpro_admin">
104
+ <div class="pmpro_banner">
105
+ <a class="pmpro_logo" title="Paid Memberships Pro - Membership Plugin for WordPress" target="_blank" href="<?php echo pmpro_https_filter("http://www.paidmembershipspro.com")?>"><img src="<?php echo PMPRO_URL?>/images/Paid-Memberships-Pro.png" width="350" height="75" border="0" alt="Paid Memberships Pro(c) - All Rights Reserved" /></a>
106
+ <div class="pmpro_meta"><span class="pmpro_tag-grey">v<?php echo PMPRO_VERSION?></span><a class="pmpro_tag-blue" href="<?php echo pmpro_https_filter("http://www.paidmembershipspro.com")?>"><?php _e('Plugin Support', 'pmpro');?></a><a class="pmpro_tag-blue" href="http://www.paidmembershipspro.com/forums/"><?php _e('User Forum', 'pmpro');?></a></div>
107
+
108
+ <br style="clear:both;" />
109
+ </div>
110
+
111
+ <div id="pmpro_notifications">
112
+ </div>
113
+ <script>
114
+ jQuery(document).ready(function() {
115
+ jQuery.get('<?php echo get_admin_url(NULL, "/admin-ajax.php?action=pmpro_notifications"); ?>', function(data) {
116
+ if(data && data != 'NULL')
117
+ jQuery('#pmpro_notifications').html(data);
118
+ });
119
+ });
120
+ </script>
121
+
122
+ <?php
123
+ $settings_tabs = array("pmpro-membershiplevels", "pmpro-pagesettings", "pmpro-paymentsettings", "pmpro-emailsettings", "pmpro-advancedsettings", "pmpro-addons");
124
+ if(in_array($view, $settings_tabs))
125
+ {
126
+ ?>
127
+ <h3 class="nav-tab-wrapper">
128
+ <a href="admin.php?page=pmpro-membershiplevels" class="nav-tab<?php if($view == 'pmpro-membershiplevels') { ?> nav-tab-active<?php } ?>"><?php _e('Membership Levels', 'pmpro');?></a>
129
+ <a href="admin.php?page=pmpro-pagesettings" class="nav-tab<?php if($view == 'pmpro-pagesettings') { ?> nav-tab-active<?php } ?>"><?php _e('Pages', 'pmpro');?></a>
130
+ <a href="admin.php?page=pmpro-paymentsettings" class="nav-tab<?php if($view == 'pmpro-paymentsettings') { ?> nav-tab-active<?php } ?>"><?php _e('Payment Gateway &amp; SSL', 'pmpro');?></a>
131
+ <a href="admin.php?page=pmpro-emailsettings" class="nav-tab<?php if($view == 'pmpro-emailsettings') { ?> nav-tab-active<?php } ?>"><?php _e('Email', 'pmpro');?></a>
132
+ <a href="admin.php?page=pmpro-advancedsettings" class="nav-tab<?php if($view == 'pmpro-advancedsettings') { ?> nav-tab-active<?php } ?>"><?php _e('Advanced', 'pmpro');?></a>
133
+ <a href="admin.php?page=pmpro-addons" class="nav-tab<?php if($view == 'pmpro-addons') { ?> nav-tab-active<?php } ?>"><?php _e('Add Ons', 'pmpro');?></a>
134
+ </h3>
135
+ <?php } ?>
adminpages/advancedsettings.php ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || !current_user_can("manage_options"))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb, $msg, $msgt;
9
+
10
+ //get/set settings
11
+ if(!empty($_REQUEST['savesettings']))
12
+ {
13
+ //other settings
14
+ pmpro_setOption("nonmembertext");
15
+ pmpro_setOption("notloggedintext");
16
+ pmpro_setOption("rsstext");
17
+ pmpro_setOption("showexcerpts");
18
+ pmpro_setOption("hideads");
19
+ pmpro_setOption("hideadslevels");
20
+ pmpro_setOption("redirecttosubscription");
21
+
22
+ //captcha
23
+ pmpro_setOption("recaptcha");
24
+ pmpro_setOption("recaptcha_publickey");
25
+ pmpro_setOption("recaptcha_privatekey");
26
+
27
+ //tos
28
+ pmpro_setOption("tospage");
29
+
30
+ //footer link
31
+ pmpro_setOption("hide_footer_link");
32
+
33
+ //assume success
34
+ $msg = true;
35
+ $msgt = __("Your advanced settings have been updated.", "pmpro");
36
+ }
37
+
38
+ $nonmembertext = pmpro_getOption("nonmembertext");
39
+ $notloggedintext = pmpro_getOption("notloggedintext");
40
+ $rsstext = pmpro_getOption("rsstext");
41
+ $hideads = pmpro_getOption("hideads");
42
+ $showexcerpts = pmpro_getOption("showexcerpts");
43
+ $hideadslevels = pmpro_getOption("hideadslevels");
44
+
45
+ if(is_multisite())
46
+ $redirecttosubscription = pmpro_getOption("redirecttosubscription");
47
+
48
+ $recaptcha = pmpro_getOption("recaptcha");
49
+ $recaptcha_publickey = pmpro_getOption("recaptcha_publickey");
50
+ $recaptcha_privatekey = pmpro_getOption("recaptcha_privatekey");
51
+
52
+ $tospage = pmpro_getOption("tospage");
53
+
54
+ $hide_footer_link = pmpro_getOption("hide_footer_link");
55
+
56
+ //default settings
57
+ if(!$nonmembertext)
58
+ {
59
+ $nonmembertext = "This content is for !!levels!! members only. <a href=\"" . wp_login_url() . "?action=register\">Register here</a>.";
60
+ pmpro_setOption("nonmembertext", $nonmembertext);
61
+ }
62
+ if(!$notloggedintext)
63
+ {
64
+ $notloggedintext = "Please <a href=\"" . wp_login_url( get_permalink() ) . "\">login</a> to view this content. (<a href=\"" . wp_login_url() . "?action=register\">Register here</a>.)";
65
+ pmpro_setOption("notloggedintext", $notloggedintext);
66
+ }
67
+ if(!$rsstext)
68
+ {
69
+ $rsstext = "This content is for members only. Visit the site and log in/register to read.";
70
+ pmpro_setOption("rsstext", $rsstext);
71
+ }
72
+
73
+ $levels = $wpdb->get_results( "SELECT * FROM {$wpdb->pmpro_membership_levels}", OBJECT );
74
+
75
+ require_once(dirname(__FILE__) . "/admin_header.php");
76
+ ?>
77
+
78
+ <form action="" method="post" enctype="multipart/form-data">
79
+ <h2><?php _e('Advanced Settings', 'pmpro');?></h2>
80
+
81
+ <table class="form-table">
82
+ <tbody>
83
+ <tr>
84
+ <th scope="row" valign="top">
85
+ <label for="nonmembertext"><?php _e('Message for Logged-in Non-members', 'pmpro');?>:</label>
86
+ </th>
87
+ <td>
88
+ <textarea name="nonmembertext" rows="3" cols="80"><?php echo stripslashes($nonmembertext)?></textarea><br />
89
+ <small class="litegray"><?php _e('This message replaces the post content for non-members. Available variables', 'pmpro');?>: !!levels!!, !!referrer!!</small>
90
+ </td>
91
+ </tr>
92
+ <tr>
93
+ <th scope="row" valign="top">
94
+ <label for="notloggedintext"><?php _e('Message for Logged-out Users', 'pmpro');?>:</label>
95
+ </th>
96
+ <td>
97
+ <textarea name="notloggedintext" rows="3" cols="80"><?php echo stripslashes($notloggedintext)?></textarea><br />
98
+ <small class="litegray"><?php _e('This message replaces the post content for logged-out visitors.', 'pmpro');?></small>
99
+ </td>
100
+ </tr>
101
+ <tr>
102
+ <th scope="row" valign="top">
103
+ <label for="rsstext"><?php _e('Message for RSS Feed', 'pmpro');?>:</label>
104
+ </th>
105
+ <td>
106
+ <textarea name="rsstext" rows="3" cols="80"><?php echo stripslashes($rsstext)?></textarea><br />
107
+ <small class="litegray"><?php _e('This message replaces the post content in RSS feeds.', 'pmpro');?></small>
108
+ </td>
109
+ </tr>
110
+
111
+ <tr>
112
+ <th scope="row" valign="top">
113
+ <label for="showexcerpts"><?php _e('Show Excerpts to Non-Members?', 'pmpro');?></label>
114
+ </th>
115
+ <td>
116
+ <select id="showexcerpts" name="showexcerpts">
117
+ <option value="0" <?php if(!$showexcerpts) { ?>selected="selected"<?php } ?>><?php _e('No - Hide excerpts.', 'pmpro');?></option>
118
+ <option value="1" <?php if($showexcerpts == 1) { ?>selected="selected"<?php } ?>><?php _e('Yes - Show excerpts.', 'pmpro');?></option>
119
+ </select>
120
+ </td>
121
+ </tr>
122
+ <tr>
123
+ <th scope="row" valign="top">
124
+ <label for="hideads">Hide Ads From Members?</label>
125
+ </th>
126
+ <td>
127
+ <select id="hideads" name="hideads" onchange="pmpro_updateHideAdsTRs();">
128
+ <option value="0" <?php if(!$hideads) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
129
+ <option value="1" <?php if($hideads == 1) { ?>selected="selected"<?php } ?>><?php _e('Hide Ads From All Members', 'pmpro');?></option>
130
+ <option value="2" <?php if($hideads == 2) { ?>selected="selected"<?php } ?>><?php _e('Hide Ads From Certain Members', 'pmpro');?></option>
131
+ </select>
132
+ </td>
133
+ </tr>
134
+ <tr id="hideads_explanation" <?php if($hideads < 2) { ?>style="display: none;"<?php } ?>>
135
+ <th scope="row" valign="top">&nbsp;</th>
136
+ <td>
137
+ <p class="top0em"><?php _e('Ads from the following plugins will be automatically turned off', 'pmpro');?>: <em>Easy Adsense</em>, ...</p>
138
+ <p><?php _e('To hide ads in your template code, use code like the following', 'pmpro');?>:</p>
139
+ <pre lang="PHP">
140
+ if(pmpro_displayAds())
141
+ {
142
+ //insert ad code here
143
+ }
144
+ </pre>
145
+ </td>
146
+ </tr>
147
+ <tr id="hideadslevels_tr" <?php if($hideads != 2) { ?>style="display: none;"<?php } ?>>
148
+ <th scope="row" valign="top">
149
+ <label for="hideadslevels"><?php _e('Choose Levels to Hide Ads From', 'pmpro');?>:</label>
150
+ </th>
151
+ <td>
152
+ <div class="checkbox_box" <?php if(count($levels) > 5) { ?>style="height: 100px; overflow: auto;"<?php } ?>>
153
+ <?php
154
+ $hideadslevels = pmpro_getOption("hideadslevels");
155
+ if(!is_array($hideadslevels))
156
+ $hideadslevels = explode(",", $hideadslevels);
157
+
158
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
159
+ $levels = $wpdb->get_results($sqlQuery, OBJECT);
160
+ foreach($levels as $level)
161
+ {
162
+ ?>
163
+ <div class="clickable"><input type="checkbox" id="hideadslevels_<?php echo $level->id?>" name="hideadslevels[]" value="<?php echo $level->id?>" <?php if(in_array($level->id, $hideadslevels)) { ?>checked="checked"<?php } ?>> <?php echo $level->name?></div>
164
+ <?php
165
+ }
166
+ ?>
167
+ </div>
168
+ <script>
169
+ jQuery('.checkbox_box input').click(function(event) {
170
+ event.stopPropagation()
171
+ });
172
+
173
+ jQuery('.checkbox_box div.clickable').click(function() {
174
+ var checkbox = jQuery(this).find(':checkbox');
175
+ checkbox.attr('checked', !checkbox.attr('checked'));
176
+ });
177
+ </script>
178
+ </td>
179
+ </tr>
180
+ <?php if(is_multisite()) { ?>
181
+ <tr>
182
+ <th scope="row" valign="top">
183
+ <label for="redirecttosubscription"><?php _e('Redirect all traffic from registration page to /susbcription/?', 'pmpro');?>: <em>(<?php _e('multisite only', 'pmpro');?>)</em></label>
184
+ </th>
185
+ <td>
186
+ <select id="redirecttosubscription" name="redirecttosubscription">
187
+ <option value="0" <?php if(!$redirecttosubscription) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
188
+ <option value="1" <?php if($redirecttosubscription == 1) { ?>selected="selected"<?php } ?>><?php _e('Yes', 'pmpro');?></option>
189
+ </select>
190
+ </td>
191
+ </tr>
192
+ <?php } ?>
193
+ <tr>
194
+ <th scope="row" valign="top">
195
+ <label for="recaptcha"><?php _e('Use reCAPTCHA?', 'pmpro');?>:</label>
196
+ </th>
197
+ <td>
198
+ <select id="recaptcha" name="recaptcha" onchange="pmpro_updateRecaptchaTRs();">
199
+ <option value="0" <?php if(!$recaptcha) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
200
+ <option value="1" <?php if($recaptcha == 1) { ?>selected="selected"<?php } ?>><?php _e('Yes - Free memberships only.', 'pmpro');?></option>
201
+ <option value="2" <?php if($recaptcha == 2) { ?>selected="selected"<?php } ?>><?php _e('Yes - All memberships.', 'pmpro');?></option>
202
+ </select><br />
203
+ <small><?php _e('A free reCAPTCHA key is required.', 'pmpro');?> <a href="https://www.google.com/recaptcha/admin/create"><?php _e('Click here to signup for reCAPTCHA', 'pmpro');?></a>.</small>
204
+ </td>
205
+ </tr>
206
+ <tr id="recaptcha_tr" <?php if(!$recaptcha) { ?>style="display: none;"<?php } ?>>
207
+ <th scope="row" valign="top">&nbsp;</th>
208
+ <td>
209
+ <label for="recaptcha_publickey"><?php _e('reCAPTCHA Public Key', 'pmpro');?>:</label>
210
+ <input type="text" name="recaptcha_publickey" size="60" value="<?php echo $recaptcha_publickey?>" />
211
+ <br /><br />
212
+ <label for="recaptcha_privatekey"><?php _e('reCAPTCHA Private Key', 'pmpro');?>:</label>
213
+ <input type="text" name="recaptcha_privatekey" size="60" value="<?php echo $recaptcha_privatekey?>" />
214
+ </td>
215
+ </tr>
216
+ <tr>
217
+ <th scope="row" valign="top">
218
+ <label for="tospage"><?php _e('Require Terms of Service on signups?', 'pmpro');?></label>
219
+ </th>
220
+ <td>
221
+ <?php
222
+ wp_dropdown_pages(array("name"=>"tospage", "show_option_none"=>"No", "selected"=>$tospage));
223
+ ?>
224
+ <br />
225
+ <small><?php _e('If yes, create a WordPress page containing your TOS agreement and assign it using the dropdown above.', 'pmpro');?></small>
226
+ </td>
227
+ </tr>
228
+
229
+ <?php /*
230
+ <tr>
231
+ <th scope="row" valign="top">
232
+ <label for="hide_footer_link">Hide the PMPro Link in the Footer?</label>
233
+ </th>
234
+ <td>
235
+ <select id="hide_footer_link" name="hide_footer_link">
236
+ <option value="0" <?php if(!$hide_footer_link) { ?>selected="selected"<?php } ?>>No - Leave the link. (Thanks!)</option>
237
+ <option value="1" <?php if($hide_footer_link == 1) { ?>selected="selected"<?php } ?>>Yes - Hide the link.</option>
238
+ </select>
239
+ </td>
240
+ </tr>
241
+ */ ?>
242
+ </tbody>
243
+ </table>
244
+ <script>
245
+ function pmpro_updateHideAdsTRs()
246
+ {
247
+ var hideads = jQuery('#hideads').val();
248
+ if(hideads == 2)
249
+ {
250
+ jQuery('#hideadslevels_tr').show();
251
+ }
252
+ else
253
+ {
254
+ jQuery('#hideadslevels_tr').hide();
255
+ }
256
+
257
+ if(hideads > 0)
258
+ {
259
+ jQuery('#hideads_explanation').show();
260
+ }
261
+ else
262
+ {
263
+ jQuery('#hideads_explanation').hide();
264
+ }
265
+ }
266
+ pmpro_updateHideAdsTRs();
267
+
268
+ function pmpro_updateRecaptchaTRs()
269
+ {
270
+ var recaptcha = jQuery('#recaptcha').val();
271
+ if(recaptcha > 0)
272
+ {
273
+ jQuery('#recaptcha_tr').show();
274
+ }
275
+ else
276
+ {
277
+ jQuery('#recaptcha_tr').hide();
278
+ }
279
+ }
280
+ pmpro_updateRecaptchaTRs();
281
+ </script>
282
+
283
+ <p class="submit">
284
+ <input name="savesettings" type="submit" class="button-primary" value="<?php _e('Save Settings', 'pmpro');?>" />
285
+ </p>
286
+ </form>
287
+
288
+ <?php
289
+ require_once(dirname(__FILE__) . "/admin_footer.php");
290
+ ?>
adminpages/dashboard.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Much of this code is borroed from yst_plugin_tools.php in the Yoast WordPress SEO plugin. Thanks, Yoast!
4
+ */
5
+
6
+ global $pmpro_feed;
7
+ $pmpro_feed = "http://feeds.feedburner.com/PaidMembershipsPro";
8
+
9
+ function pmpro_postbox($id, $title, $content)
10
+ {
11
+ ?>
12
+ <div id="<?php echo $id; ?>" class="postbox">
13
+ <div class="handlediv" title="Click to toggle"><br /></div>
14
+ <h3 class="hndle"><span><?php echo $title; ?></span></h3>
15
+ <div class="inside">
16
+ <?php echo $content; ?>
17
+ </div>
18
+ </div>
19
+ <?php
20
+ }
21
+
22
+ function pmpro_fetch_rss_items( $num )
23
+ {
24
+ global $pmpro_feed;
25
+
26
+ include_once(ABSPATH . WPINC . '/feed.php');
27
+ $rss = fetch_feed( $pmpro_feed );
28
+
29
+ // Bail if feed doesn't work
30
+ if ( is_wp_error($rss) )
31
+ return false;
32
+
33
+ $rss_items = $rss->get_items( 0, $rss->get_item_quantity( $num ) );
34
+
35
+ // If the feed was erroneously
36
+ if ( !$rss_items ) {
37
+ $md5 = md5( $pmpro_feed );
38
+ delete_transient( 'feed_' . $md5 );
39
+ delete_transient( 'feed_mod_' . $md5 );
40
+ $rss = fetch_feed( $pmpro_feed );
41
+ $rss_items = $rss->get_items( 0, $rss->get_item_quantity( $num ) );
42
+ }
43
+
44
+ return $rss_items;
45
+ }
46
+
47
+ /**
48
+ * Box with latest news from PaidMembershipsPro.com for sidebar
49
+ */
50
+ function pmpro_news()
51
+ {
52
+ $rss_items = pmpro_fetch_rss_items( 5 );
53
+
54
+ $content = '<ul>';
55
+ if ( !$rss_items ) {
56
+ $content .= '<li class="pmpro_news">no news items, feed might be broken...</li>';
57
+ } else {
58
+ foreach ( $rss_items as $item ) {
59
+ $content .= '<li class="pmpro_news">';
60
+ $content .= '<a class="rsswidget" href="'.esc_url( $item->get_permalink(), $protocolls=null, 'display' ).'">'. esc_html( $item->get_title() ) .'</a> ';
61
+ $content .= '</li>';
62
+ }
63
+ }
64
+ $content .= '</ul>';
65
+ $pmpro_postbox('pmprolatest', 'Recent Updates from PaidMembershipsPro.com', $content);
66
+ }
67
+
68
+ /**
69
+ * Widget with latest news from PaidMembershipsPro.com for dashbaord
70
+ */
71
+ function pmpro_db_widget()
72
+ {
73
+ global $pmpro_feed;
74
+
75
+ $options = get_option('pmpro_pmprodbwidget');
76
+
77
+ $network = '';
78
+ if ( function_exists('is_network_admin') && is_network_admin() )
79
+ $network = '_network';
80
+
81
+ if (isset($_POST['pmpro_removedbwidget'])) {
82
+ $options['removedbwidget'.$network] = true;
83
+ update_option('pmpro_pmprodbwidget',$options);
84
+ }
85
+ if ( isset($options['removedbwidget'.$network]) && $options['removedbwidget'.$network] ) {
86
+ echo "If you reload, this widget will be gone and never appear again, unless you decide to delete the database option 'pmpro_pmprodbwidget'.";
87
+ return;
88
+ }
89
+
90
+ $rss_items = pmpro_fetch_rss_items( 3 );
91
+
92
+ echo "<ul>";
93
+
94
+ if ( !$rss_items ) {
95
+ echo '<li class="pmpro_news">no news items, feed might be broken...</li>';
96
+ } else {
97
+ foreach ( $rss_items as $item ) {
98
+ echo '<li class="pmpro_news">';
99
+ echo '<a class="rsswidget" href="'.esc_url( $item->get_permalink(), $protocolls=null, 'display' ).'">'. esc_html( $item->get_title() ) .'</a>';
100
+ echo ' <span class="rss-date">'. $item->get_date(get_option('date_format')) .'</span>';
101
+ echo '<div class="rssSummary">'. esc_html( pmpro_text_limit( strip_tags( $item->get_description() ), 150 ) ).'</div>';
102
+ echo '</li>';
103
+ }
104
+ }
105
+
106
+ echo '</ul>';
107
+ echo '<br class="clear"/><div style="margin-top:10px;border-top: 1px solid #ddd; padding-top: 10px; text-align:center;">';
108
+ echo '<a href="'.$pmpro_feed.'"><img src="'.get_bloginfo('wpurl').'/wp-includes/images/rss.png" alt=""/> Subscribe with RSS</a>';
109
+ echo ' &nbsp; &nbsp; &nbsp; ';
110
+ echo '<a href="http://www.paidmembershipspro.com/"><img src="'.get_bloginfo('wpurl').'/wp-includes/images/wpmini-blue.png" alt=""/> View Online</a>';
111
+ echo '<form class="alignright" method="post"><input type="hidden" name="pmpro_removedbwidget" value="true"/><input title="Remove this widget from all users dashboards" class="button" type="submit" value="X"/></form>';
112
+ echo '</div>';
113
+ }
114
+
115
+ function pmpro_widget_setup()
116
+ {
117
+ $network = '';
118
+ if ( function_exists('is_network_admin') && is_network_admin() )
119
+ $network = '_network';
120
+
121
+ $options = get_option('pmpro_pmprodbwidget');
122
+ if ( !isset($options['removedbwidget'.$network]) || !$options['removedbwidget'.$network] )
123
+ wp_add_dashboard_widget( 'pmpro_db_widget' , 'The Latest From PaidMembershipsPro.com' , 'pmpro_db_widget');
124
+ }
125
+
126
+ add_action( 'wp_dashboard_setup', 'pmpro_widget_setup');
127
+ ?>
adminpages/discountcodes.php ADDED
@@ -0,0 +1,631 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_discountcodes")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ //vars
9
+ global $wpdb, $pmpro_currency_symbol;
10
+
11
+ if(isset($_REQUEST['edit']))
12
+ $edit = $_REQUEST['edit'];
13
+ else
14
+ $edit = false;
15
+
16
+ if(isset($_REQUEST['delete']))
17
+ $delete = $_REQUEST['delete'];
18
+ else
19
+ $delete = false;
20
+
21
+ if(isset($_REQUEST['saveid']))
22
+ $saveid = $_POST['saveid'];
23
+ else
24
+ $saveid = false;
25
+
26
+ if($saveid)
27
+ {
28
+ //get vars
29
+ $code = $_POST['code'];
30
+ $starts_month = $_POST['starts_month'];
31
+ $starts_day = $_POST['starts_day'];
32
+ $starts_year = $_POST['starts_year'];
33
+ $expires_month = $_POST['expires_month'];
34
+ $expires_day = $_POST['expires_day'];
35
+ $expires_year = $_POST['expires_year'];
36
+ $uses = $_POST['uses'];
37
+
38
+ //fix up dates
39
+ $starts = date("Y-m-d", strtotime($starts_month . "/" . $starts_day . "/" . $starts_year));
40
+ $expires = date("Y-m-d", strtotime($expires_month . "/" . $expires_day . "/" . $expires_year));
41
+
42
+ //updating or new?
43
+ if($saveid > 0)
44
+ {
45
+ $sqlQuery = "UPDATE $wpdb->pmpro_discount_codes SET code = '" . esc_sql($code) . "', starts = '" . $starts . "', expires = '" . $expires . "', uses = '" . intval($uses) . "' WHERE id = '" . $saveid . "' LIMIT 1";
46
+ if($wpdb->query($sqlQuery) !== false)
47
+ {
48
+ $pmpro_msg = __("Discount code updated successfully.", "pmpro");
49
+ $pmpro_msgt = "success";
50
+ $saved = true;
51
+ $edit = $saveid;
52
+ }
53
+ else
54
+ {
55
+ $pmpro_msg = __("Error updating discount code. That code may already be in use.", "pmpro");
56
+ $pmpro_msgt = "error";
57
+ }
58
+ }
59
+ else
60
+ {
61
+ $sqlQuery = "INSERT INTO $wpdb->pmpro_discount_codes (code, starts, expires, uses) VALUES('" . esc_sql($code) . "', '" . $starts . "', '" . $expires . "', '" . intval($uses) . "')";
62
+ if($wpdb->query($sqlQuery) !== false)
63
+ {
64
+ $pmpro_msg = __("Discount code added successfully.", "pmpro");
65
+ $pmpro_msgt = "success";
66
+ $saved = true;
67
+ $edit = $wpdb->insert_id;
68
+ }
69
+ else
70
+ {
71
+ $pmpro_msg = __("Error adding discount code. That code may already be in use.", "pmpro") . $wpdb->last_error;
72
+ $pmpro_msgt = "error";
73
+ }
74
+ }
75
+
76
+ //now add the membership level rows
77
+ if($saved && $edit > 0)
78
+ {
79
+ //get the submitted values
80
+ $all_levels_a = $_REQUEST['all_levels'];
81
+ if(!empty($_REQUEST['levels']))
82
+ $levels_a = $_REQUEST['levels'];
83
+ else
84
+ $levels_a = array();
85
+ $initial_payment_a = $_REQUEST['initial_payment'];
86
+ if(!empty($_REQUEST['recurring']))
87
+ $recurring_a = $_REQUEST['recurring'];
88
+ $billing_amount_a = $_REQUEST['billing_amount'];
89
+ $cycle_number_a = $_REQUEST['cycle_number'];
90
+ $cycle_period_a = $_REQUEST['cycle_period'];
91
+ $billing_limit_a = $_REQUEST['billing_limit'];
92
+ if(!empty($_REQUEST['custom_trial']))
93
+ $custom_trial_a = $_REQUEST['custom_trial'];
94
+ $trial_amount_a = $_REQUEST['trial_amount'];
95
+ $trial_limit_a = $_REQUEST['trial_limit'];
96
+ if(!empty($_REQUEST['expiration']))
97
+ $expiration_a = $_REQUEST['expiration'];
98
+ $expiration_number_a = $_REQUEST['expiration_number'];
99
+ $expiration_period_a = $_REQUEST['expiration_period'];
100
+
101
+ //clear the old rows
102
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_discount_codes_levels WHERE code_id = '" . $edit . "'";
103
+ $wpdb->query($sqlQuery);
104
+
105
+ //add a row for each checked level
106
+ if(!empty($levels_a))
107
+ {
108
+ foreach($levels_a as $level_id)
109
+ {
110
+ //get the values ready
111
+ $n = array_search($level_id, $all_levels_a); //this is the key location of this level's values
112
+ $initial_payment = $initial_payment_a[$n];
113
+
114
+ //is this recurring?
115
+ if(!empty($recurring_a))
116
+ {
117
+ if(in_array($level_id, $recurring_a))
118
+ $recurring = 1;
119
+ else
120
+ $recurring = 0;
121
+ }
122
+ else
123
+ $recurring = 0;
124
+
125
+ if(!empty($recurring))
126
+ {
127
+ $billing_amount = $billing_amount_a[$n];
128
+ $cycle_number = $cycle_number_a[$n];
129
+ $cycle_period = $cycle_period_a[$n];
130
+ $billing_limit = $billing_limit_a[$n];
131
+
132
+ //custom trial
133
+ if(!empty($custom_trial_a))
134
+ {
135
+ if(in_array($level_id, $custom_trial_a))
136
+ $custom_trial = 1;
137
+ else
138
+ $custom_trial = 0;
139
+ }
140
+ else
141
+ $custom_trial = 0;
142
+
143
+ if(!empty($custom_trial))
144
+ {
145
+ $trial_amount = $trial_amount_a[$n];
146
+ $trial_limit = $trial_limit_a[$n];
147
+ }
148
+ else
149
+ {
150
+ $trial_amount = '';
151
+ $trial_limit = '';
152
+ }
153
+ }
154
+ else
155
+ {
156
+ $billing_amount = '';
157
+ $cycle_number = '';
158
+ $cycle_period = 'Month';
159
+ $billing_limit = '';
160
+ $custom_trial = 0;
161
+ $trial_amount = '';
162
+ $trial_limit = '';
163
+ }
164
+
165
+ if(!empty($expiration_a))
166
+ {
167
+ if(in_array($level_id, $expiration_a))
168
+ $expiration = 1;
169
+ else
170
+ $expiration = 0;
171
+ }
172
+ else
173
+ $expiration = 0;
174
+
175
+ if(!empty($expiration))
176
+ {
177
+ $expiration_number = $expiration_number_a[$n];
178
+ $expiration_period = $expiration_period_a[$n];
179
+ }
180
+ else
181
+ {
182
+ $expiration_number = '';
183
+ $expiration_period = 'Month';
184
+ }
185
+
186
+ //okay, do the insert
187
+ $sqlQuery = "INSERT INTO $wpdb->pmpro_discount_codes_levels (code_id, level_id, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, expiration_number, expiration_period) VALUES('" . esc_sql($edit) . "', '" . esc_sql($level_id) . "', '" . (double)esc_sql($initial_payment) . "', '" . (double)esc_sql($billing_amount) . "', '" . intval(esc_sql($cycle_number)) . "', '" . esc_sql($cycle_period) . "', '" . intval(esc_sql($billing_limit)) . "', '" . (double)esc_sql($trial_amount) . "', '" . intval(esc_sql($trial_limit)) . "', '" . intval(esc_sql($expiration_number)) . "', '" . esc_sql($expiration_period) . "')";
188
+
189
+ if($wpdb->query($sqlQuery) !== false)
190
+ {
191
+ //okay
192
+ do_action("pmpro_save_discount_code_level", $saveid, $level_id);
193
+ }
194
+ else
195
+ {
196
+ $level_errors[] = sprintf(__("Error saving values for the %s level.", "pmpro"), $wpdb->get_var("SELECT name FROM $wpdb->pmpro_membership_levels WHERE id = '" . $level_id . "' LIMIT 1"));
197
+ }
198
+ }
199
+ }
200
+
201
+ //errors?
202
+ if(!empty($level_errors))
203
+ {
204
+ $pmpro_msg = __("There were errors updating the level values: ", "pmpro") . implode(" ", $level_errors);
205
+ $pmpro_msgt = "error";
206
+ }
207
+ else
208
+ {
209
+ //all good. set edit = NULL so we go back to the overview page
210
+ $edit = NULL;
211
+
212
+ do_action("pmpro_save_discount_code", $saveid);
213
+ }
214
+ }
215
+ }
216
+
217
+ //are we deleting?
218
+ if(!empty($delete))
219
+ {
220
+ //is this a code?
221
+ $code = $wpdb->get_var("SELECT code FROM $wpdb->pmpro_discount_codes WHERE id = '" . $delete . "' LIMIT 1");
222
+ if(!empty($code))
223
+ {
224
+ //delete the code levels
225
+ $r1 = $wpdb->query("DELETE FROM $wpdb->pmpro_discount_codes_levels WHERE code_id = '" . $delete . "'");
226
+
227
+ if($r1 !== false)
228
+ {
229
+ //delete the code
230
+ $r2 = $wpdb->query("DELETE FROM $wpdb->pmpro_discount_codes WHERE id = '" . $delete . "' LIMIT 1");
231
+
232
+ if($r2 !== false)
233
+ {
234
+ $pmpro_msg = sprintf(__("Code %s deleted successfully.", "pmpro"), $code);
235
+ $pmpro_msgt = "success";
236
+ }
237
+ else
238
+ {
239
+ $pmpro_msg = __("Error deleting discount code. The code was only partially deleted. Please try again.", "pmpro");
240
+ $pmpro_msgt = "error";
241
+ }
242
+ }
243
+ else
244
+ {
245
+ $pmpro_msg = __("Error deleting code. Please try again.", "pmpro");
246
+ $pmpro_msgt = "error";
247
+ }
248
+ }
249
+ else
250
+ {
251
+ $pmpro_msg = __("Code not found.", "pmpro");
252
+ $pmpro_msgt = "error";
253
+ }
254
+ }
255
+
256
+ require_once(dirname(__FILE__) . "/admin_header.php");
257
+ ?>
258
+
259
+ <?php if($edit) { ?>
260
+
261
+ <h2>
262
+ <?php
263
+ if($edit > 0)
264
+ echo __("Edit Discount Code", "pmpro");
265
+ else
266
+ echo __("Add New Discount Code", "pmpro");
267
+ ?>
268
+ </h2>
269
+
270
+ <?php if(!empty($pmpro_msg)) { ?>
271
+ <div id="message" class="<?php if($pmpro_msgt == "success") echo "updated fade"; else echo "error"; ?>"><p><?php echo $pmpro_msg?></p></div>
272
+ <?php } ?>
273
+
274
+ <div>
275
+ <?php
276
+ // get the code...
277
+ if($edit > 0)
278
+ {
279
+ $code = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(starts) as starts, UNIX_TIMESTAMP(expires) as expires FROM $wpdb->pmpro_discount_codes WHERE id = '" . $edit . "' LIMIT 1", OBJECT);
280
+ $uses = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . $code->id . "'");
281
+ $levels = $wpdb->get_results("SELECT l.id, l.name, cl.initial_payment, cl.billing_amount, cl.cycle_number, cl.cycle_period, cl.billing_limit, cl.trial_amount, cl.trial_limit FROM $wpdb->pmpro_membership_levels l LEFT JOIN $wpdb->pmpro_discount_codes_levels cl ON l.id = cl.level_id WHERE cl.code_id = '" . $code->code . "'");
282
+ $temp_id = $code->id;
283
+ }
284
+ elseif(!empty($copy) && $copy > 0)
285
+ {
286
+ $code = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(starts) as starts, UNIX_TIMESTAMP(expires) as expires FROM $wpdb->pmpro_discount_codes WHERE id = '" . $copy . "' LIMIT 1", OBJECT);
287
+ $temp_id = $level->id;
288
+ $level->id = NULL;
289
+ }
290
+
291
+ // didn't find a discount code, let's add a new one...
292
+ if(empty($code->id)) $edit = -1;
293
+
294
+ //defaults for new codes
295
+ if($edit == -1)
296
+ {
297
+ $code = new stdClass();
298
+ $code->code = pmpro_getDiscountCode();
299
+ }
300
+ ?>
301
+ <form action="" method="post">
302
+ <input name="saveid" type="hidden" value="<?php echo $edit?>" />
303
+ <table class="form-table">
304
+ <tbody>
305
+ <tr>
306
+ <th scope="row" valign="top"><label><?php _e('ID', 'pmpro');?>:</label></th>
307
+ <td class="pmpro_lite"><?php if(!empty($code->id)) echo $code->id; else echo __("This will be generated when you save.", "pmpro");?></td>
308
+ </tr>
309
+
310
+ <tr>
311
+ <th scope="row" valign="top"><label for="code"><?php _e('Code', 'pmpro');?>:</label></th>
312
+ <td><input name="code" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($code->code))?>" /></td>
313
+ </tr>
314
+
315
+ <?php
316
+ //some vars for the dates
317
+ $current_day = date("j");
318
+ if(!empty($code->starts))
319
+ $selected_starts_day = date("j", $code->starts);
320
+ else
321
+ $selected_starts_day = $current_day;
322
+ if(!empty($code->expires))
323
+ $selected_expires_day = date("j", $code->expires);
324
+ else
325
+ $selected_expires_day = $current_day;
326
+
327
+ $current_month = date("M");
328
+ if(!empty($code->starts))
329
+ $selected_starts_month = date("m", $code->starts);
330
+ else
331
+ $selected_starts_month = date("m");
332
+ if(!empty($code->expires))
333
+ $selected_expires_month = date("m", $code->expires);
334
+ else
335
+ $selected_expires_month = date("m");
336
+
337
+ $current_year = date("Y");
338
+ if(!empty($code->starts))
339
+ $selected_starts_year = date("Y", $code->starts);
340
+ else
341
+ $selected_starts_year = $current_year;
342
+ if(!empty($code->expires))
343
+ $selected_expires_year = date("Y", $code->expires);
344
+ else
345
+ $selected_expires_year = (int)$current_year + 1;
346
+ ?>
347
+
348
+ <tr>
349
+ <th scope="row" valign="top"><label for="starts"><?php _e('Start Date', 'pmpro');?>:</label></th>
350
+ <td>
351
+ <select name="starts_month">
352
+ <?php
353
+ for($i = 1; $i < 13; $i++)
354
+ {
355
+ ?>
356
+ <option value="<?php echo $i?>" <?php if($i == $selected_starts_month) { ?>selected="selected"<?php } ?>><?php echo date("M", strtotime($i . "/1/" . $current_year))?></option>
357
+ <?php
358
+ }
359
+ ?>
360
+ </select>
361
+ <input name="starts_day" type="text" size="2" value="<?php echo $selected_starts_day?>" />
362
+ <input name="starts_year" type="text" size="4" value="<?php echo $selected_starts_year?>" />
363
+ </td>
364
+ </tr>
365
+
366
+ <tr>
367
+ <th scope="row" valign="top"><label for="expires"><?php _e('Expiration Date', 'pmpro');?>:</label></th>
368
+ <td>
369
+ <select name="expires_month">
370
+ <?php
371
+ for($i = 1; $i < 13; $i++)
372
+ {
373
+ ?>
374
+ <option value="<?php echo $i?>" <?php if($i == $selected_expires_month) { ?>selected="selected"<?php } ?>><?php echo date("M", strtotime($i . "/1/" . $current_year))?></option>
375
+ <?php
376
+ }
377
+ ?>
378
+ </select>
379
+ <input name="expires_day" type="text" size="2" value="<?php echo $selected_expires_day?>" />
380
+ <input name="expires_year" type="text" size="4" value="<?php echo $selected_expires_year?>" />
381
+ </td>
382
+ </tr>
383
+
384
+ <tr>
385
+ <th scope="row" valign="top"><label for="uses"><?php _ex('Uses', 'Number of uses for a discount code', 'pmpro');?>:</label></th>
386
+ <td>
387
+ <input name="uses" type="text" size="10" value="<?php if(!empty($code->uses)) echo str_replace("\"", "&quot;", stripslashes($code->uses));?>" />
388
+ <small class="pmpro_lite"><?php _e('Leave blank for unlimited uses.', 'pmpro');?></small>
389
+ </td>
390
+ </tr>
391
+
392
+ </tbody>
393
+ </table>
394
+
395
+ <?php do_action("pmpro_discount_code_after_settings"); ?>
396
+
397
+ <h3>Which Levels Will This Code Apply To?</h3>
398
+
399
+ <div class="pmpro_discount_levels">
400
+ <?php
401
+ $levels = $wpdb->get_results("SELECT * FROM $wpdb->pmpro_membership_levels");
402
+ foreach($levels as $level)
403
+ {
404
+ //if this level is already managed for this discount code, use the code values
405
+ if($edit > 0)
406
+ {
407
+ $code_level = $wpdb->get_row("SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . $edit . "' AND cl.level_id = '" . $level->id . "' LIMIT 1");
408
+ if($code_level)
409
+ {
410
+ $level = $code_level;
411
+ $level->checked = true;
412
+ }
413
+ else
414
+ $level_checked = false;
415
+ }
416
+ else
417
+ $level_checked = false;
418
+ ?>
419
+ <div>
420
+ <input type="hidden" name="all_levels[]" value="<?php echo $level->id?>" />
421
+ <input type="checkbox" name="levels[]" value="<?php echo $level->id?>" <?php if(!empty($level->checked)) { ?>checked="checked"<?php } ?> onclick="if(jQuery(this).is(':checked')) jQuery(this).next().show(); else jQuery(this).next().hide();" />
422
+ <?php echo $level->name?>
423
+ <div class="pmpro_discount_levels_pricing level_<?php echo $level->id?>" <?php if(empty($level->checked)) { ?>style="display: none;"<?php } ?>>
424
+ <table class="form-table">
425
+ <tbody>
426
+ <tr>
427
+ <th scope="row" valign="top"><label for="initial_payment"><?php _e('Initial Payment', 'pmpro');?>:</label></th>
428
+ <td><?php echo $pmpro_currency_symbol?><input name="initial_payment[]" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->initial_payment))?>" /> <small><?php _e('The initial amount collected at registration.', 'pmpro');?></small></td>
429
+ </tr>
430
+
431
+ <tr>
432
+ <th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'pmpro');?>:</label></th>
433
+ <td><input class="recurring_checkbox" name="recurring[]" type="checkbox" value="<?php echo $level->id?>" <?php if(pmpro_isLevelRecurring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).attr('checked')) { jQuery(this).parent().parent().siblings('.recurring_info').show(); if(!jQuery('#custom_trial_<?php echo $level->id?>').is(':checked')) jQuery(this).parent().parent().siblings('.trial_info').hide();} else jQuery(this).parent().parent().siblings('.recurring_info').hide();" /> <small><?php _e('Check if this level has a recurring subscription payment.', 'pmpro');?></small></td>
434
+ </tr>
435
+
436
+ <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
437
+ <th scope="row" valign="top"><label for="billing_amount"><?php _e('Billing Ammount', 'pmpro');?>:</label></th>
438
+ <td>
439
+ <?php echo $pmpro_currency_symbol?><input name="billing_amount[]" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->billing_amount))?>" /> <small>per</small>
440
+ <input name="cycle_number[]" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->cycle_number))?>" />
441
+ <select name="cycle_period[]" onchange="updateCyclePeriod();">
442
+ <?php
443
+ $cycles = array( 'Day(s)' => 'Day', 'Week(s)' => 'Week', 'Month(s)' => 'Month', 'Year(s)' => 'Year' );
444
+ foreach ( $cycles as $name => $value ) {
445
+ echo "<option value='$value'";
446
+ if ( $level->cycle_period == $value ) echo " selected='selected'";
447
+ echo ">$name</option>";
448
+ }
449
+ ?>
450
+ </select>
451
+ <br /><small><?php _e('The amount to be billed one cycle after the initial payment.', 'pmpro');?></small>
452
+ </td>
453
+ </tr>
454
+
455
+ <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
456
+ <th scope="row" valign="top"><label for="billing_limit"><?php _e('Billing Cycle Limit', 'pmpro');?>:</label></th>
457
+ <td>
458
+ <input name="billing_limit[]" type="text" size="20" value="<?php echo $level->billing_limit?>" />
459
+ <br /><small><?php _e('The <strong>total</strong> number of recurring billing cycles for this level, including the trial period (if applicable) but not including the initial payment. Set to zero if membership is indefinite.', 'pmpro');?></small>
460
+ </td>
461
+ </tr>
462
+
463
+ <tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
464
+ <th scope="row" valign="top"><label><?php _e('Custom Trial', 'pmpro');?>:</label></th>
465
+ <td><input id="custom_trial_<?php echo $level->id?>" name="custom_trial[]" type="checkbox" value="<?php echo $level->id?>" <?php if ( pmpro_isLevelTrial($level) ) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).attr('checked')) jQuery(this).parent().parent().siblings('.trial_info').show(); else jQuery(this).parent().parent().siblings('.trial_info').hide();" /> <?php _e('Check to add a custom trial period.', 'pmpro');?></td>
466
+ </tr>
467
+
468
+ <tr class="trial_info recurring_info" <?php if (!pmpro_isLevelTrial($level)) echo "style='display:none;'";?>>
469
+ <th scope="row" valign="top"><label for="trial_amount"><?php _e('Trial Billing Amount', 'pmpro');?>:</label></th>
470
+ <td>
471
+ <?php echo $pmpro_currency_symbol?><input name="trial_amount[]" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->trial_amount))?>" />
472
+ <small><?php _e('for the first', 'pmpro');?></small>
473
+ <input name="trial_limit[]" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->trial_limit))?>" />
474
+ <small><?php _e('subscription payments', 'pmpro');?>.</small>
475
+ </td>
476
+ </tr>
477
+
478
+ <tr>
479
+ <th scope="row" valign="top"><label><?php _e('Membership Expiration', 'pmpro');?>:</label></th>
480
+ <td><input id="expiration" name="expiration[]" type="checkbox" value="<?php echo $level->id?>" <?php if(pmpro_isLevelExpiring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).is(':checked')) { jQuery(this).parent().parent().siblings('.expiration_info').show(); } else { jQuery(this).parent().parent().siblings('.expiration_info').hide();}" /> <small><?php _e('Check this to set an expiration date for new sign ups.', 'pmpro');?></small></td>
481
+ </tr>
482
+
483
+ <tr class="expiration_info" <?php if(!pmpro_isLevelExpiring($level)) {?>style="display: none;"<?php } ?>>
484
+ <th scope="row" valign="top"><label for="billing_amount"><?php _e('Expires In', 'pmpro');?>:</label></th>
485
+ <td>
486
+ <input id="expiration_number" name="expiration_number[]" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->expiration_number))?>" />
487
+ <select id="expiration_period" name="expiration_period[]">
488
+ <?php
489
+ $cycles = array( 'Day(s)' => 'Day', 'Week(s)' => 'Week', 'Month(s)' => 'Month', 'Year(s)' => 'Year' );
490
+ foreach ( $cycles as $name => $value ) {
491
+ echo "<option value='$value'";
492
+ if ( $level->expiration_period == $value ) echo " selected='selected'";
493
+ echo ">$name</option>";
494
+ }
495
+ ?>
496
+ </select>
497
+ <br /><small><?php _e('How long before the expiration expires. Note that any future payments will be cancelled when the membership expires.', 'pmpro');?></small>
498
+ </td>
499
+ </tr>
500
+ </tbody>
501
+ </table>
502
+
503
+ <?php do_action("pmpro_discount_code_after_level_settings", $edit, $level); ?>
504
+
505
+ </div>
506
+ </div>
507
+ <script>
508
+
509
+ </script>
510
+ <?php
511
+ }
512
+ ?>
513
+ </div>
514
+
515
+ <p class="submit topborder">
516
+ <input name="save" type="submit" class="button-primary" value="Save Code" />
517
+ <input name="cancel" type="button" value="Cancel" onclick="location.href='<?php echo get_admin_url(NULL, '/admin.php?page=pmpro-discountcodes')?>';" />
518
+ </p>
519
+ </form>
520
+ </div>
521
+
522
+ <?php } else { ?>
523
+
524
+ <h2>
525
+ <?php _e('Memberships Discount Codes', 'pmpro');?>
526
+ <a href="admin.php?page=pmpro-discountcodes&edit=-1" class="button add-new-h2"><?php _e('Add New Discount Code', 'pmpro');?></a>
527
+ </h2>
528
+
529
+ <?php if(!empty($pmpro_msg)) { ?>
530
+ <div id="message" class="<?php if($pmpro_msgt == "success") echo "updated fade"; else echo "error"; ?>"><p><?php echo $pmpro_msg?></p></div>
531
+ <?php } ?>
532
+
533
+ <form id="posts-filter" method="get" action="">
534
+ <p class="search-box">
535
+ <label class="screen-reader-text" for="post-search-input"><?php _e('Search Discount Codes', 'pmpro');?>:</label>
536
+ <input type="hidden" name="page" value="pmpro-discountcodes" />
537
+ <input id="post-search-input" type="text" value="<?php if(!empty($s)) echo $s;?>" name="s" size="30" />
538
+ <input class="button" type="submit" value="<?php _e('Search', 'pmpro');?>" id="search-submit "/>
539
+ </p>
540
+ </form>
541
+
542
+ <br class="clear" />
543
+
544
+ <table class="widefat">
545
+ <thead>
546
+ <tr>
547
+ <th><?php _e('ID', 'pmpro');?></th>
548
+ <th><?php _e('Code', 'pmpro');?></th>
549
+ <th><?php _e('Starts', 'pmpro');?></th>
550
+ <th><?php _e('Expires', 'pmpro');?></th>
551
+ <th><?php _e('Uses', 'pmpro');?></th>
552
+ <th><?php _e('Levels', 'pmpro');?></th>
553
+ <th></th>
554
+ <th></th>
555
+ </tr>
556
+ </thead>
557
+ <tbody>
558
+ <?php
559
+ $sqlQuery = "SELECT *, UNIX_TIMESTAMP(starts) as starts, UNIX_TIMESTAMP(expires) as expires FROM $wpdb->pmpro_discount_codes ";
560
+ if(!empty($s))
561
+ $sqlQuery .= "WHERE code LIKE '%$s%' ";
562
+ $sqlQuery .= "ORDER BY id ASC";
563
+
564
+ $codes = $wpdb->get_results($sqlQuery, OBJECT);
565
+
566
+ if(!$codes)
567
+ {
568
+ ?>
569
+ <tr><td colspan="7" class="pmpro_pad20">
570
+ <p><?php _e('Discount codes allow you to offer your memberships at discounted prices to select customers.', 'pmpro');?> <a href="admin.php?page=pmpro-discountcodes&edit=-1"><?php _e('Create your first discount code now', 'pmpro');?></a>.</p>
571
+ </td></tr>
572
+ <?php
573
+ }
574
+ else
575
+ {
576
+ foreach($codes as $code)
577
+ {
578
+ ?>
579
+ <tr>
580
+ <td><?php echo $code->id?></td>
581
+ <td>
582
+ <a href="?page=pmpro-discountcodes&edit=<?php echo $code->id?>"><?php echo $code->code?></a>
583
+ </td>
584
+ <td>
585
+ <?php echo date(get_option('date_format'), $code->starts)?>
586
+ </td>
587
+ <td>
588
+ <?php echo date(get_option('date_format'), $code->expires)?>
589
+ </td>
590
+ <td>
591
+ <?php
592
+ $uses = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . $code->id . "'");
593
+ if($code->uses > 0)
594
+ echo "<strong>" . (int)$uses . "</strong>/" . $code->uses;
595
+ else
596
+ echo "<strong>" . (int)$uses . "</strong>/unlimited";
597
+ ?>
598
+ </td>
599
+ <td>
600
+ <?php
601
+ $sqlQuery = "SELECT l.id, l.name FROM $wpdb->pmpro_membership_levels l LEFT JOIN $wpdb->pmpro_discount_codes_levels cl ON l.id = cl.level_id WHERE cl.code_id = '" . $code->id . "'";
602
+ $levels = $wpdb->get_results($sqlQuery);
603
+
604
+ $level_names = array();
605
+ foreach($levels as $level)
606
+ $level_names[] = "<a target=\"_blank\" href=\"" . pmpro_url("checkout", "?level=" . $level->id . "&discount_code=" . $code->code) . "\">" . $level->name . "</a>";
607
+ if($level_names)
608
+ echo implode(", ", $level_names);
609
+ else
610
+ echo "None";
611
+ ?>
612
+ </td>
613
+ <td>
614
+ <a href="?page=pmpro-discountcodes&edit=<?php echo $code->id?>"><?php _e('edit', 'pmpro');?></a>
615
+ </td>
616
+ <td>
617
+ <a href="javascript:askfirst('<?php printf(__('Are you sure you want to delete the %s discount code? The subscriptions for existing users will not change, but new users will not be able to use this code anymore.', 'pmpro'), $code->code);?>', '?page=pmpro-discountcodes&delete=<?php echo $code->id?>'); void(0);"><?php _e('delete', 'pmpro');?></a>
618
+ </td>
619
+ </tr>
620
+ <?php
621
+ }
622
+ }
623
+ ?>
624
+ </tbody>
625
+ </table>
626
+
627
+ <?php } ?>
628
+
629
+ <?php
630
+ require_once(dirname(__FILE__) . "/admin_footer.php");
631
+ ?>
adminpages/emailsettings.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_emailsettings")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb, $msg, $msgt;
9
+
10
+ //get/set settings
11
+ global $pmpro_pages;
12
+ if(!empty($_REQUEST['savesettings']))
13
+ {
14
+ //email options
15
+ pmpro_setOption("from_email");
16
+ pmpro_setOption("from_name");
17
+
18
+ pmpro_setOption("email_admin_checkout");
19
+ pmpro_setOption("email_admin_changes");
20
+ pmpro_setOption("email_admin_cancels");
21
+ pmpro_setOption("email_admin_billing");
22
+
23
+ pmpro_setOption("email_member_notification");
24
+
25
+ //assume success
26
+ $msg = true;
27
+ $msgt = "Your email settings have been updated.";
28
+ }
29
+
30
+ $from_email = pmpro_getOption("from_email");
31
+ $from_name = pmpro_getOption("from_name");
32
+
33
+ $email_admin_checkout = pmpro_getOption("email_admin_checkout");
34
+ $email_admin_changes = pmpro_getOption("email_admin_changes");
35
+ $email_admin_cancels = pmpro_getOption("email_admin_cancels");
36
+ $email_admin_billing = pmpro_getOption("email_admin_billing");
37
+
38
+ $email_member_notification = pmpro_getOption("email_member_notification");
39
+
40
+ if(empty($from_email))
41
+ {
42
+ $parsed = parse_url(home_url());
43
+ $hostname = $parsed[host];
44
+ $hostparts = split("\.", $hostname);
45
+ $email_domain = $hostparts[count($hostparts) - 2] . "." . $hostparts[count($hostparts) - 1];
46
+ $from_email = "wordpress@" . $email_domain;
47
+ pmpro_setOption("from_email", $from_email);
48
+ }
49
+
50
+ if(empty($from_name))
51
+ {
52
+ $from_name = "WordPress";
53
+ pmpro_setOption("from_name", $from_name);
54
+ }
55
+
56
+ require_once(dirname(__FILE__) . "/admin_header.php");
57
+ ?>
58
+
59
+ <form action="" method="post" enctype="multipart/form-data">
60
+ <h2><?php _e('Email Settings', 'pmpro');?></h2>
61
+ <p><?php _e('By default, system generated emails are sent from <em><strong>wordpress@yourdomain.com</strong></em>. You can update this from address using the fields below.', 'pmpro');?></p>
62
+
63
+ <p><?php _e('To modify the appearance of system generated emails, add the files <em>email_header.html</em> and <em>email_footer.html</em> to your theme\'s directory. This will modify both the WordPress default messages as well as messages generated by Paid Memberships Pro. <a title="Paid Memberships Pro - Member Communications" target="_blank" href="http://www.paidmembershipspro.com/documentation/member-communications/">Click here to learn more about Paid Memberships Pro emails</a>.', 'pmpro');?></p>
64
+
65
+ <table class="form-table">
66
+ <tbody>
67
+ <tr>
68
+ <th scope="row" valign="top">
69
+ <label for="from_email"><?php _e('From Email', 'pmpro');?>:</label>
70
+ </th>
71
+ <td>
72
+ <input type="text" name="from_email" size="60" value="<?php echo $from_email?>" />
73
+ </td>
74
+ </tr>
75
+ <tr>
76
+ <th scope="row" valign="top">
77
+ <label for="from_name"><?php _e('From Name', 'pmpro');?>:</label>
78
+ </th>
79
+ <td>
80
+ <input type="text" name="from_name" size="60" value="<?php echo $from_name?>" />
81
+ </td>
82
+ </tr>
83
+ </tbody>
84
+ </table>
85
+
86
+ <h3><?php _e('Send the site admin emails', 'pmpro');?>:</h3>
87
+
88
+ <table class="form-table">
89
+ <tbody>
90
+ <tr>
91
+ <th scope="row" valign="top">
92
+ <label for="email_admin_checkout"><?php _e('Checkout', 'pmpro');?>:</label>
93
+ </th>
94
+ <td>
95
+ <input type="checkbox" id="email_admin_checkout" name="email_admin_checkout" value="1" <?php if(!empty($email_admin_checkout)) { ?>checked="checked"<?php } ?> />
96
+ <?php _e('when a member checks out.', 'pmpro');?>
97
+ </td>
98
+ </tr>
99
+ <tr>
100
+ <th scope="row" valign="top">
101
+ <label for="email_admin_changes"><?php _e('Admin Changes', 'pmpro');?>:</label>
102
+ </th>
103
+ <td>
104
+ <input type="checkbox" id="email_admin_changes" name="email_admin_changes" value="1" <?php if(!empty($email_admin_changes)) { ?>checked="checked"<?php } ?> />
105
+ <?php _e('when an admin changes a user\'s membership level through the dashboard.', 'pmpro');?>
106
+ </td>
107
+ </tr>
108
+ <tr>
109
+ <th scope="row" valign="top">
110
+ <label for="email_admin_cancels"><?php _e('Cancellation', 'pmpro');?>:</label>
111
+ </th>
112
+ <td>
113
+ <input type="checkbox" id="email_admin_cancels" name="email_admin_cancels" value="1" <?php if(!empty($email_admin_cancels)) { ?>checked="checked"<?php } ?> />
114
+ <?php _e('when a user cancels his or her account.', 'pmpro');?>
115
+ </td>
116
+ </tr>
117
+ <tr>
118
+ <th scope="row" valign="top">
119
+ <label for="email_admin_billing"><?php _e('Bill Updates', 'pmpro');?>:</label>
120
+ </th>
121
+ <td>
122
+ <input type="checkbox" id="email_admin_billing" name="email_admin_billing" value="1" <?php if(!empty($email_admin_billing)) { ?>checked="checked"<?php } ?> />
123
+ <?php _e('when a user updates his or her billing information.', 'pmpro');?>
124
+ </td>
125
+ </tr>
126
+ </tbody>
127
+ </table>
128
+
129
+ <h3><?php _e('Send members emails', 'pmpro');?>:</h3>
130
+
131
+ <table class="form-table">
132
+ <tbody>
133
+ <tr>
134
+ <th scope="row" valign="top">
135
+ <label for="email_admin_checkout"><?php _e('New Users', 'pmpro');?>:</label>
136
+ </th>
137
+ <td>
138
+ <input type="checkbox" id="email_member_notification" name="email_member_notification" value="1" <?php if(!empty($email_member_notification)) { ?>checked="checked"<?php } ?> />
139
+ <?php _e('Default WP notification email. (Recommended: Leave unchecked. Members will still get an email confirmation from PMPro after checkout.)', 'pmpro');?>
140
+ </td>
141
+ </tr>
142
+ </tbody>
143
+ </table>
144
+
145
+ <p class="submit">
146
+ <input name="savesettings" type="submit" class="button-primary" value="Save Settings" />
147
+ </p>
148
+ </form>
149
+
150
+ <?php
151
+ require_once(dirname(__FILE__) . "/admin_footer.php");
152
+ ?>
adminpages/functions.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Checks if PMPro settings are complete or if there are any errors.
4
+ */
5
+ function pmpro_checkLevelForStripeCompatibility($level = NULL)
6
+ {
7
+ $gateway = pmpro_getOption("gateway");
8
+ if($gateway == "stripe")
9
+ {
10
+ global $wpdb;
11
+
12
+ //check ALL the levels
13
+ if(empty($level))
14
+ {
15
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ORDER BY id ASC";
16
+ $levels = $wpdb->get_results($sqlQuery, OBJECT);
17
+ if(!empty($levels))
18
+ {
19
+ foreach($levels as $level)
20
+ {
21
+ /*
22
+ Stripe currently does not support:
23
+ * Trial Amounts > 0.
24
+ * Daily or Weekly billing periods.
25
+ * Billing Limits.
26
+ */
27
+ if($level->trial_amount > 0 ||
28
+ ($level->cycle_number > 0 && ($level->cycle_period == "Day" || $level->cycle_period == "Week")) ||
29
+ $level->billing_limit > 0)
30
+ {
31
+ return false;
32
+ }
33
+ }
34
+ }
35
+ }
36
+ else
37
+ {
38
+ //need to look it up?
39
+ if(is_numeric($level))
40
+ $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($level) . "' LIMIT 1");
41
+
42
+ //check this level
43
+ if($level->trial_amount > 0 ||
44
+ ($level->cycle_number > 0 && ($level->cycle_period == "Day" || $level->cycle_period == "Week")) ||
45
+ $level->billing_limit > 0)
46
+ {
47
+ return false;
48
+ }
49
+ }
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ /*
56
+ Checks if PMPro settings are complete or if there are any errors.
57
+ */
58
+ function pmpro_checkLevelForPayflowCompatibility($level = NULL)
59
+ {
60
+ $gateway = pmpro_getOption("gateway");
61
+ if($gateway == "payflowpro")
62
+ {
63
+ global $wpdb;
64
+
65
+ //check ALL the levels
66
+ if(empty($level))
67
+ {
68
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ORDER BY id ASC";
69
+ $levels = $wpdb->get_results($sqlQuery, OBJECT);
70
+ if(!empty($levels))
71
+ {
72
+ foreach($levels as $level)
73
+ {
74
+ /*
75
+ Payflow currently does not support:
76
+ * Trial Amounts > 0.
77
+ * Daily billing periods.
78
+ */
79
+
80
+ if($level->trial_amount > 0 ||
81
+ $level->cycle_number > 1 ||
82
+ ($level->cycle_number == 1 && $level->cycle_period == "Day"))
83
+ {
84
+ return false;
85
+ }
86
+ }
87
+ }
88
+ }
89
+ else
90
+ {
91
+ //need to look it up?
92
+ if(is_numeric($level))
93
+ $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($level) . "' LIMIT 1");
94
+
95
+ //check this level
96
+ if($level->trial_amount > 0 ||
97
+ $level->cycle_number > 1 ||
98
+ ($level->cycle_number == 1 && $level->cycle_period == "Day"))
99
+ {
100
+ return false;
101
+ }
102
+ }
103
+ }
104
+
105
+ return true;
106
+ }
107
+
108
+ /*
109
+ Checks if PMPro settings are complete or if there are any errors.
110
+ */
111
+ function pmpro_checkLevelForBraintreeCompatibility($level = NULL)
112
+ {
113
+ $gateway = pmpro_getOption("gateway");
114
+ if($gateway == "braintree")
115
+ {
116
+ global $wpdb;
117
+
118
+ //check ALL the levels
119
+ if(empty($level))
120
+ {
121
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ORDER BY id ASC";
122
+ $levels = $wpdb->get_results($sqlQuery, OBJECT);
123
+ if(!empty($levels))
124
+ {
125
+ foreach($levels as $level)
126
+ {
127
+ /*
128
+ Braintree currently does not support:
129
+ * Trial Amounts > 0.
130
+ * Daily or Weekly billing periods.
131
+ */
132
+ if($level->trial_amount > 0 ||
133
+ ($level->cycle_number > 0 && ($level->cycle_period == "Day" || $level->cycle_period == "Week")))
134
+ {
135
+ return false;
136
+ }
137
+ }
138
+ }
139
+ }
140
+ else
141
+ {
142
+ //need to look it up?
143
+ if(is_numeric($level))
144
+ $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . esc_sql($level) . "' LIMIT 1");
145
+
146
+ //check this level
147
+ if($level->trial_amount > 0 ||
148
+ ($level->cycle_number > 0 && ($level->cycle_period == "Day" || $level->cycle_period == "Week")))
149
+ {
150
+ return false;
151
+ }
152
+ }
153
+ }
154
+
155
+ return true;
156
+ }
157
+
adminpages/membershiplevels.php ADDED
@@ -0,0 +1,580 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_membershiplevels")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb, $msg, $msgt, $pmpro_currency_symbol;
9
+
10
+ //some vars
11
+ $gateway = pmpro_getOption("gateway");
12
+ global $pmpro_stripe_error, $pmpro_braintree_error, $pmpro_payflow_error, $wp_version;
13
+
14
+ if(isset($_REQUEST['edit']))
15
+ $edit = $_REQUEST['edit'];
16
+ else
17
+ $edit = false;
18
+ if(isset($_REQUEST['copy']))
19
+ $copy = $_REQUEST['copy'];
20
+ if(isset($_REQUEST['s']))
21
+ $s = $_REQUEST['s'];
22
+ else
23
+ $s = "";
24
+
25
+ if(isset($_REQUEST['action']))
26
+ $action = $_REQUEST['action'];
27
+ else
28
+ $action = false;
29
+
30
+ if(isset($_REQUEST['saveandnext']))
31
+ $saveandnext = $_REQUEST['saveandnext'];
32
+
33
+ if(isset($_REQUEST['saveid']))
34
+ $saveid = $_REQUEST['saveid'];
35
+ if(isset($_REQUEST['deleteid']))
36
+ $deleteid = $_REQUEST['deleteid'];
37
+
38
+ if($action == "save_membershiplevel")
39
+ {
40
+ $ml_name = addslashes($_REQUEST['name']);
41
+ $ml_description = $_REQUEST['description'];
42
+ $ml_confirmation = $_REQUEST['confirmation'];
43
+ $ml_initial_payment = addslashes($_REQUEST['initial_payment']);
44
+ if(!empty($_REQUEST['recurring']))
45
+ $ml_recurring = 1;
46
+ else
47
+ $ml_recurring = 0;
48
+ $ml_billing_amount = addslashes($_REQUEST['billing_amount']);
49
+ $ml_cycle_number = addslashes($_REQUEST['cycle_number']);
50
+ $ml_cycle_period = addslashes($_REQUEST['cycle_period']);
51
+ $ml_billing_limit = addslashes($_REQUEST['billing_limit']);
52
+ if(!empty($_REQUEST['custom_trial']))
53
+ $ml_custom_trial = 1;
54
+ else
55
+ $ml_custom_trial = 0;
56
+ $ml_trial_amount = addslashes($_REQUEST['trial_amount']);
57
+ $ml_trial_limit = addslashes($_REQUEST['trial_limit']);
58
+ if(!empty($_REQUEST['expiration']))
59
+ $ml_expiration = 1;
60
+ else
61
+ $ml_expiration = 0;
62
+ $ml_expiration_number = addslashes($_REQUEST['expiration_number']);
63
+ $ml_expiration_period = addslashes($_REQUEST['expiration_period']);
64
+ $ml_categories = array();
65
+
66
+ //reversing disable to allow here
67
+ if(empty($_REQUEST['disable_signups']))
68
+ $ml_allow_signups = 1;
69
+ else
70
+ $ml_allow_signups = 0;
71
+
72
+ foreach ( $_REQUEST as $key => $value )
73
+ {
74
+ if ( $value == 'yes' && preg_match( '/^membershipcategory_(\d+)$/i', $key, $matches ) )
75
+ {
76
+ $ml_categories[] = $matches[1];
77
+ }
78
+ }
79
+
80
+ //clearing out values if checkboxes aren't checked
81
+ if(empty($ml_recurring))
82
+ {
83
+ $ml_billing_amount = $ml_cycle_number = $ml_cycle_period = $ml_billing_limit = $ml_trial_amount = $ml_trial_limit = 0;
84
+ }
85
+ elseif(empty($ml_custom_trial))
86
+ {
87
+ $ml_trial_amount = $ml_trial_limit = 0;
88
+ }
89
+ if(empty($ml_expiration))
90
+ {
91
+ $ml_expiration_number = $ml_expiration_period = 0;
92
+ }
93
+
94
+ if($saveid > 0)
95
+ {
96
+ $sqlQuery = " UPDATE {$wpdb->pmpro_membership_levels}
97
+ SET name = '" . esc_sql($ml_name) . "',
98
+ description = '" . esc_sql($ml_description) . "',
99
+ confirmation = '" . esc_sql($ml_confirmation) . "',
100
+ initial_payment = '" . esc_sql($ml_initial_payment) . "',
101
+ billing_amount = '" . esc_sql($ml_billing_amount) . "',
102
+ cycle_number = '" . esc_sql($ml_cycle_number) . "',
103
+ cycle_period = '" . esc_sql($ml_cycle_period) . "',
104
+ billing_limit = '" . esc_sql($ml_billing_limit) . "',
105
+ trial_amount = '" . esc_sql($ml_trial_amount) . "',
106
+ trial_limit = '" . esc_sql($ml_trial_limit) . "',
107
+ expiration_number = '" . esc_sql($ml_expiration_number) . "',
108
+ expiration_period = '" . esc_sql($ml_expiration_period) . "',
109
+ allow_signups = '" . esc_sql($ml_allow_signups) . "'
110
+ WHERE id = '$saveid' LIMIT 1;";
111
+ $wpdb->query($sqlQuery);
112
+
113
+ pmpro_updateMembershipCategories( $saveid, $ml_categories );
114
+ if(!mysql_errno())
115
+ {
116
+ $edit = false;
117
+ $msg = 2;
118
+ $msgt = __("Membership level updated successfully.", "pmpro");
119
+ }
120
+ else
121
+ {
122
+ $msg = -2;
123
+ $msg = true;
124
+ $msgt = __("Error updating membership level.", "pmpro");
125
+ }
126
+ }
127
+ else
128
+ {
129
+ $sqlQuery = " INSERT INTO {$wpdb->pmpro_membership_levels}
130
+ ( name, description, confirmation, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, expiration_number, expiration_period, allow_signups)
131
+ VALUES
132
+ ( '" . esc_sql($ml_name) . "', '" . esc_sql($ml_description) . "', '" . esc_sql($ml_confirmation) . "', '" . esc_sql($ml_initial_payment) . "', '" . esc_sql($ml_billing_amount) . "', '" . esc_sql($ml_cycle_number) . "', '" . esc_sql($ml_cycle_period) . "', '" . esc_sql($ml_billing_limit) . "', '" . esc_sql($ml_trial_amount) . "', '" . esc_sql($ml_trial_limit) . "', '" . esc_sql($ml_expiration_number) . "', '" . esc_sql($ml_expiration_period) . "', '" . esc_sql($ml_allow_signups) . "' )";
133
+ $wpdb->query($sqlQuery);
134
+ if(!mysql_errno())
135
+ {
136
+ $saveid = $wpdb->insert_id;
137
+ pmpro_updateMembershipCategories( $saveid, $ml_categories );
138
+
139
+ $edit = false;
140
+ $msg = 1;
141
+ $msgt = __("Membership level added successfully.", "pmpro");
142
+ }
143
+ else
144
+ {
145
+ $msg = -1;
146
+ $msgt = __("Error adding membership level.", "pmpro");
147
+ }
148
+ }
149
+
150
+ do_action("pmpro_save_membership_level", $saveid);
151
+ }
152
+ elseif($action == "delete_membership_level")
153
+ {
154
+ global $wpdb;
155
+
156
+ $ml_id = $_REQUEST['deleteid'];
157
+
158
+ if($ml_id > 0)
159
+ {
160
+ //remove any categories from the ml
161
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_categories WHERE membership_id = '$ml_id'";
162
+ $r1 = $wpdb->query($sqlQuery);
163
+
164
+ //cancel any subscriptions to the ml
165
+ $r2 = true;
166
+ $user_ids = $wpdb->get_col("SELECT user_id FROM $wpdb->pmpro_memberships_users WHERE membership_id = '$ml_id' AND status = 'active'");
167
+ foreach($user_ids as $user_id)
168
+ {
169
+ //change there membership level to none. that will handle the cancel
170
+ if(pmpro_changeMembershipLevel(0, $user_id))
171
+ {
172
+ //okay
173
+ }
174
+ else
175
+ {
176
+ //couldn't delete the subscription
177
+ //we should probably notify the admin
178
+ $pmproemail = new PMProEmail();
179
+ $pmproemail->data = array("body"=>"<p>" . sprintf(__("There was an error canceling the subscription for user with ID=%d. You will want to check your payment gateway to see if their subscription is still active.", "pmpro"), $user_id) . "</p>");
180
+ $last_order = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' ORDER BY timestamp DESC LIMIT 1");
181
+ if($last_order)
182
+ $pmproemail->data["body"] .= "<p>" . __("Last Invoice", "pmpro") . ":<br />" . nl2br(var_export($last_order, true)) . "</p>";
183
+ $pmproemail->sendEmail(get_bloginfo("admin_email"));
184
+
185
+ $r2 = false;
186
+ }
187
+ }
188
+
189
+ //delete the ml
190
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_membership_levels WHERE id = '$ml_id' LIMIT 1";
191
+ $r3 = $wpdb->query($sqlQuery);
192
+
193
+ if($r1 !== FALSE && $r2 !== FALSE && $r3 !== FALSE)
194
+ {
195
+ $msg = 3;
196
+ $msgt = __("Membership level deleted successfully.", "pmpro");
197
+ }
198
+ else
199
+ {
200
+ $msg = -3;
201
+ $msgt = __("Error deleting membership level.", "pmpro");
202
+ }
203
+ }
204
+ else
205
+ {
206
+ $msg = -3;
207
+ $msgt = __("Error deleting membership level.", "pmpro");
208
+ }
209
+ }
210
+
211
+ require_once(dirname(__FILE__) . "/admin_header.php");
212
+ ?>
213
+
214
+ <?php
215
+ if($edit)
216
+ {
217
+ ?>
218
+
219
+ <h2>
220
+ <?php
221
+ if($edit > 0)
222
+ echo __("Edit Membership Level", "pmpro");
223
+ else
224
+ echo __("Add New Membership Level", "pmpro");
225
+ ?>
226
+ </h2>
227
+
228
+ <div>
229
+ <?php
230
+ // get the level...
231
+ if(!empty($edit) && $edit > 0)
232
+ {
233
+ $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '$edit' LIMIT 1", OBJECT);
234
+ $temp_id = $level->id;
235
+ }
236
+ elseif(!empty($copy) && $copy > 0)
237
+ {
238
+ $level = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '$copy' LIMIT 1", OBJECT);
239
+ $temp_id = $level->id;
240
+ $level->id = NULL;
241
+ }
242
+ else
243
+
244
+ // didn't find a membership level, let's add a new one...
245
+ if(empty($level))
246
+ {
247
+ $level = new stdClass();
248
+ $level->id = NULL;
249
+ $level->name = NULL;
250
+ $level->description = NULL;
251
+ $level->confirmation = NULL;
252
+ $level->billing_amount = NULL;
253
+ $level->trial_amount = NULL;
254
+ $level->initial_payment = NULL;
255
+ $level->billing_limit = NULL;
256
+ $level->trial_limit = NULL;
257
+ $level->expiration_number = NULL;
258
+ $level->expiration_period = NULL;
259
+ $edit = -1;
260
+ }
261
+
262
+ //defaults for new levels
263
+ if(empty($copy) && $edit == -1)
264
+ {
265
+ $level->cycle_number = 1;
266
+ $level->cycle_period = "Month";
267
+ }
268
+
269
+ // grab the categories for the given level...
270
+ if(!empty($temp_id))
271
+ $level->categories = $wpdb->get_col("SELECT c.category_id
272
+ FROM $wpdb->pmpro_memberships_categories c
273
+ WHERE c.membership_id = '" . $temp_id . "'");
274
+ if(empty($level->categories))
275
+ $level->categories = array();
276
+
277
+ ?>
278
+ <form action="" method="post" enctype="multipart/form-data">
279
+ <input name="saveid" type="hidden" value="<?php echo $edit?>" />
280
+ <input type="hidden" name="action" value="save_membershiplevel" />
281
+ <table class="form-table">
282
+ <tbody>
283
+ <tr>
284
+ <th scope="row" valign="top"><label><?php _e('ID', 'pmpro');?>:</label></th>
285
+ <td>
286
+ <?php echo $level->id?>
287
+ </td>
288
+ </tr>
289
+
290
+ <tr>
291
+ <th scope="row" valign="top"><label for="name"><?php _e('Name', 'pmpro');?>:</label></th>
292
+ <td><input name="name" type="text" size="50" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->name))?>" /></td>
293
+ </tr>
294
+
295
+ <tr>
296
+ <th scope="row" valign="top"><label for="description"><?php _e('Description', 'pmpro');?>:</label></th>
297
+ <td>
298
+ <div id="poststuff" class="pmpro_description">
299
+ <?php
300
+ if(version_compare($wp_version, "3.3") >= 0)
301
+ wp_editor(stripslashes($level->description), "description", array("textarea_rows"=>5));
302
+ else
303
+ {
304
+ ?>
305
+ <textarea rows="10" cols="80" name="description" id="description"><?php echo stripslashes($level->description)?></textarea>
306
+ <?php
307
+ }
308
+ ?>
309
+ </div>
310
+ </td>
311
+ </tr>
312
+
313
+ <tr>
314
+ <th scope="row" valign="top"><label for="confirmation"><?php _e('Confirmation Message', 'pmpro');?>:</label></th>
315
+ <td>
316
+ <div class="pmpro_confirmation">
317
+ <?php
318
+ if(version_compare($wp_version, "3.3") >= 0)
319
+ wp_editor(stripslashes($level->confirmation), "confirmation", array("textarea_rows"=>5));
320
+ else
321
+ {
322
+ ?>
323
+ <textarea rows="10" cols="80" name="confirmation" id="confirmation"><?php echo stripslashes($level->confirmation)?></textarea>
324
+ <?php
325
+ }
326
+ ?>
327
+ </div>
328
+ </td>
329
+ </tr>
330
+ </tbody>
331
+ </table>
332
+
333
+ <h3 class="topborder"><?php _e('Billing Details', 'pmpro');?></h3>
334
+ <table class="form-table">
335
+ <tbody>
336
+ <tr>
337
+ <th scope="row" valign="top"><label for="initial_payment"><?php _e('Initial Payment', 'pmpro');?>:</label></th>
338
+ <td><?php echo $pmpro_currency_symbol?><input name="initial_payment" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->initial_payment))?>" /> <small><?php _e('The initial amount collected at registration.', 'pmpro');?></small></td>
339
+ </tr>
340
+
341
+ <tr>
342
+ <th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'pmpro');?>:</label></th>
343
+ <td><input id="recurring" name="recurring" type="checkbox" value="yes" <?php if(pmpro_isLevelRecurring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery('#recurring').is(':checked')) { jQuery('.recurring_info').show(); if(jQuery('#custom_trial').is(':checked')) {jQuery('.trial_info').show();} else {jQuery('.trial_info').hide();} } else { jQuery('.recurring_info').hide();}" /> <small><?php _e('Check if this level has a recurring subscription payment.', 'pmpro');?></small></td>
344
+ </tr>
345
+
346
+ <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
347
+ <th scope="row" valign="top"><label for="billing_amount"><?php _e('Billing Amount', 'pmpro');?>:</label></th>
348
+ <td>
349
+ <?php echo $pmpro_currency_symbol?><input name="billing_amount" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->billing_amount))?>" /> <small><?php _e('per', 'pmpro');?></small>
350
+ <input id="cycle_number" name="cycle_number" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->cycle_number))?>" />
351
+ <select id="cycle_period" name="cycle_period">
352
+ <?php
353
+ $cycles = array( __('Day(s)', 'pmpro') => 'Day', __('Week(s)', 'pmpro') => 'Week', __('Month(s)', 'pmpro') => 'Month', __('Year(s)', 'pmpro') => 'Year' );
354
+ foreach ( $cycles as $name => $value ) {
355
+ echo "<option value='$value'";
356
+ if ( $level->cycle_period == $value ) echo " selected='selected'";
357
+ echo ">$name</option>";
358
+ }
359
+ ?>
360
+ </select>
361
+ <br /><small>
362
+ <?php _e('The amount to be billed one cycle after the initial payment.', 'pmpro');?>
363
+ <?php if($gateway == "stripe") { ?>
364
+ <br /><strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration currently only supports billing periods of "Month" or "Year".', 'pmpro');?>
365
+ <?php } elseif($gateway == "braintree") { ?>
366
+ <br /><strong <?php if(!empty($pmpro_braintree_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Braintree integration currently only supports billing periods of "Month" or "Year".', 'pmpro');?>
367
+ <?php } elseif($gateway == "payflowpro") { ?>
368
+ <br /><strong <?php if(!empty($pmpro_payflow_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Payflow integration currently only supports billing frequencies of 1 and billing periods of "Week", "Month" or "Year".', 'pmpro');?>
369
+ <?php } ?>
370
+ </small>
371
+ <?php if($gateway == "braintree" && $edit < 0) { ?>
372
+ <p class="pmpro_message"><strong><?php _e('Note', 'pmpro');?>:</strong> <?php _e('After saving this level, make note of the ID and create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to <em>pmpro_#</em>, where # is the level ID.', 'pmpro');?></p>
373
+ <?php } elseif($gateway == "braintree") { ?>
374
+ <p class="pmpro_message"><strong><?php _e('Note', 'pmpro');?>:</strong> <?php _e('You will need to create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to', 'pmpro');?> <em>pmpro_<?php echo $level->id;?></em>.</p>
375
+ <?php } ?>
376
+ </td>
377
+ </tr>
378
+
379
+ <tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
380
+ <th scope="row" valign="top"><label for="billing_limit"><?php _e('Billing Cycle Limit', 'pmpro');?>:</label></th>
381
+ <td>
382
+ <input name="billing_limit" type="text" size="20" value="<?php echo $level->billing_limit?>" />
383
+ <br /><small>
384
+ <?php _e('The <strong>total</strong> number of recurring billing cycles for this level, including the trial period (if applicable) but not including the initial payment. Set to zero if membership is indefinite.', 'pmpro');?>
385
+ <?php if($gateway == "stripe") { ?>
386
+ <br /><strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration currently does not support billing limits. You can still set an expiration date below.', 'pmpro');?></strong>
387
+ <?php } ?>
388
+ </small>
389
+ </td>
390
+ </tr>
391
+
392
+ <tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
393
+ <th scope="row" valign="top"><label><?php _e('Custom Trial', 'pmpro');?>:</label></th>
394
+ <td><input id="custom_trial" name="custom_trial" type="checkbox" value="yes" <?php if ( pmpro_isLevelTrial($level) ) { echo "checked='checked'"; } ?> onclick="jQuery('.trial_info').toggle();" /> <?php _e('Check to add a custom trial period.', 'pmpro');?></td>
395
+ </tr>
396
+
397
+ <tr class="trial_info recurring_info" <?php if (!pmpro_isLevelTrial($level)) echo "style='display:none;'";?>>
398
+ <th scope="row" valign="top"><label for="trial_amount"><?php _e('Trial Billing Amount', 'pmpro');?>:</label></th>
399
+ <td>
400
+ <?php echo $pmpro_currency_symbol?><input name="trial_amount" type="text" size="20" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->trial_amount))?>" />
401
+ <small><?php _e('for the first', 'pmpro');?></small>
402
+ <input name="trial_limit" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->trial_limit))?>" />
403
+ <small><?php _e('subscription payments', 'pmpro');?>.</small>
404
+ <?php if($gateway == "stripe") { ?>
405
+ <br /><small>
406
+ <strong <?php if(!empty($pmpro_stripe_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Stripe integration currently does not support trial amounts greater than $0.', 'pmpro');?></strong>
407
+ </small>
408
+ <?php } elseif($gateway == "braintree") { ?>
409
+ <br /><small>
410
+ <strong <?php if(!empty($pmpro_braintree_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Braintree integration currently does not support trial amounts greater than $0.', 'pmpro');?></strong>
411
+ </small>
412
+ <?php } elseif($gateway == "payflowpro") { ?>
413
+ <br /><small>
414
+ <strong <?php if(!empty($pmpro_payflow_error)) { ?>class="pmpro_red"<?php } ?>><?php _e('Payflow integration currently does not support trial amounts greater than $0.', 'pmpro');?></strong>
415
+ </small>
416
+ <?php } ?>
417
+ </td>
418
+ </tr>
419
+
420
+ </tbody>
421
+ </table>
422
+ <h3 class="topborder"><?php _e('Other Settings', 'pmpro');?></h3>
423
+ <table class="form-table">
424
+ <tbody>
425
+ <tr>
426
+ <th scope="row" valign="top"><label><?php _e('Disable New Signups', 'pmpro');?>:</label></th>
427
+ <td><input name="disable_signups" type="checkbox" value="yes" <?php if($level->id && !$level->allow_signups) { ?>checked="checked"<?php } ?> /> <?php _e('Check to hide this level from the membership levels page and disable registration.', 'pmpro');?></td>
428
+ </tr>
429
+
430
+ <tr>
431
+ <th scope="row" valign="top"><label><?php _e('Membership Expiration', 'pmpro');?>:</label></th>
432
+ <td><input id="expiration" name="expiration" type="checkbox" value="yes" <?php if(pmpro_isLevelExpiring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery('#expiration').is(':checked')) { jQuery('.expiration_info').show(); } else { jQuery('.expiration_info').hide();}" /> <?php _e('Check this to set when membership access expires.', 'pmpro');?></td>
433
+ </tr>
434
+
435
+ <tr class="expiration_info" <?php if(!pmpro_isLevelExpiring($level)) {?>style="display: none;"<?php } ?>>
436
+ <th scope="row" valign="top"><label for="billing_amount"><?php _e('Expires In', 'pmpro');?>:</label></th>
437
+ <td>
438
+ <input id="expiration_number" name="expiration_number" type="text" size="10" value="<?php echo str_replace("\"", "&quot;", stripslashes($level->expiration_number))?>" />
439
+ <select id="expiration_period" name="expiration_period">
440
+ <?php
441
+ $cycles = array( 'Day(s)' => 'Day', 'Week(s)' => 'Week', 'Month(s)' => 'Month', 'Year(s)' => 'Year' );
442
+ foreach ( $cycles as $name => $value ) {
443
+ echo "<option value='$value'";
444
+ if ( $level->expiration_period == $value ) echo " selected='selected'";
445
+ echo ">$name</option>";
446
+ }
447
+ ?>
448
+ </select>
449
+ <br /><small><?php _e('Set the duration of membership access. Note that the any future payments (recurring subscription, if any) will be cancelled when the membership expires.', 'pmpro');?></small>
450
+ </td>
451
+ </tr>
452
+ </tbody>
453
+ </table>
454
+
455
+ <?php do_action("pmpro_membership_level_after_other_settings"); ?>
456
+
457
+ <h3 class="topborder"><?php _e('Content Settings', 'pmpro');?></h3>
458
+ <table class="form-table">
459
+ <tbody>
460
+ <tr>
461
+ <th scope="row" valign="top"><label><?php _e('Categories', 'pmpro');?>:</label></th>
462
+ <td>
463
+ <?php
464
+ $categories = get_categories( array( 'hide_empty' => 0 ) );
465
+ echo "<ul>";
466
+ foreach ( $categories as $cat )
467
+ {
468
+ $checked = in_array( $cat->term_id, $level->categories ) ? "checked='checked'" : '';
469
+ echo "<li><input name='membershipcategory_{$cat->term_id}' type='checkbox' value='yes' $checked /> {$cat->name}</li>\n";
470
+ }
471
+ echo "</ul>";
472
+ ?>
473
+ </td>
474
+ </tr>
475
+ </tbody>
476
+ </table>
477
+ <p class="submit topborder">
478
+ <input name="save" type="submit" class="button-primary" value="Save Level" />
479
+ <input name="cancel" type="button" value="Cancel" onclick="location.href='<?php echo get_admin_url(NULL, '/admin.php?page=pmpro-membershiplevels')?>';" />
480
+ </p>
481
+ </form>
482
+ </div>
483
+
484
+ <?php
485
+ }
486
+ else
487
+ {
488
+ ?>
489
+
490
+ <h2><?php _e('Membership Levels', 'pmpro');?> <a href="admin.php?page=pmpro-membershiplevels&edit=-1" class="button add-new-h2"><?php _e('Add New Level', 'pmpro');?></a></h2>
491
+ <form id="posts-filter" method="get" action="">
492
+ <p class="search-box">
493
+ <label class="screen-reader-text" for="post-search-input"><?php _e('Search Levels', 'pmpro');?>:</label>
494
+ <input type="hidden" name="page" value="pmpro-membershiplevels" />
495
+ <input id="post-search-input" type="text" value="<?php echo $s?>" name="s" size="30" />
496
+ <input class="button" type="submit" value="<?php _e('Search Levels', 'pmpro');?>" id="search-submit" />
497
+ </p>
498
+ </form>
499
+
500
+ <br class="clear" />
501
+
502
+ <table class="widefat">
503
+ <thead>
504
+ <tr>
505
+ <th><?php _e('ID', 'pmpro');?></th>
506
+ <th><?php _e('Name', 'pmpro');?></th>
507
+ <th><?php _e('Initial Payment', 'pmpro');?></th>
508
+ <th><?php _e('Billing Cycle', 'pmpro');?></th>
509
+ <th><?php _e('Trial Cycle', 'pmpro');?></th>
510
+ <th><?php _e('Expiration', 'pmpro');?></th>
511
+ <th><?php _e('Allow Signups', 'pmpro');?></th>
512
+ <th></th>
513
+ <th></th>
514
+ <th></th>
515
+ </tr>
516
+ </thead>
517
+ <tbody>
518
+ <?php
519
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
520
+ if($s)
521
+ $sqlQuery .= "WHERE name LIKE '%$s%' ";
522
+ $sqlQuery .= "ORDER BY id ASC";
523
+
524
+ $levels = $wpdb->get_results($sqlQuery, OBJECT);
525
+
526
+ foreach($levels as $level)
527
+ {
528
+ ?>
529
+ <tr class="<?php if(!$level->allow_signups) { ?>pmpro_gray<?php } ?> <?php if(!pmpro_checkLevelForStripeCompatibility($level) || !pmpro_checkLevelForBraintreeCompatibility($level) || !pmpro_checkLevelForPayflowCompatibility($level)) { ?>pmpro_error<?php } ?>">
530
+ <td><?php echo $level->id?></td>
531
+ <td><?php echo $level->name?></td>
532
+ <td>
533
+ <?php if(pmpro_isLevelFree($level)) { ?>
534
+ <?php _e('FREE', 'pmpro');?>
535
+ <?php } else { ?>
536
+ <?php echo $pmpro_currency_symbol?><?php echo $level->initial_payment?>
537
+ <?php } ?>
538
+ </td>
539
+ <td>
540
+ <?php if(!pmpro_isLevelRecurring($level)) { ?>
541
+ --
542
+ <?php } else { ?>
543
+ <?php echo $pmpro_currency_symbol?><?php echo $level->billing_amount?> <?php _e('every', 'pmpro');?> <?php echo $level->cycle_number.' '.sornot($level->cycle_period,$level->cycle_number)?>
544
+
545
+ <?php if($level->billing_limit) { ?>(<?php _e('for', 'pmpro');?> <?php echo $level->billing_limit?> <?php echo sornot($level->cycle_period,$level->billing_limit)?>)<?php } ?>
546
+
547
+ <?php } ?>
548
+ </td>
549
+ <td>
550
+ <?php if(!pmpro_isLevelTrial($level)) { ?>
551
+ --
552
+ <?php } else { ?>
553
+ <?php echo $pmpro_currency_symbol?><?php echo $level->trial_amount?> <?php _e('for', 'pmpro');?> <?php echo $level->trial_limit?> <?php echo sornot("payment",$level->trial_limit)?>
554
+ <?php } ?>
555
+ </td>
556
+ <td>
557
+ <?php if(!pmpro_isLevelExpiring($level)) { ?>
558
+ --
559
+ <?php } else { ?>
560
+ <?php _e('After', 'pmpro');?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
561
+ <?php } ?>
562
+ </td>
563
+ <td><?php if($level->allow_signups) { ?><?php _e('Yes', 'pmpro');?><?php } else { ?><?php _e('No', 'pmpro');?><?php } ?></td>
564
+ <td align="center"><a href="admin.php?page=pmpro-membershiplevels&edit=<?php echo $level->id?>" class="edit"><?php _e('edit', 'pmpro');?></a></td>
565
+ <td align="center"><a href="admin.php?page=pmpro-membershiplevels&copy=<?php echo $level->id?>&edit=-1" class="edit"><?php _e('copy', 'pmpro');?></a></td>
566
+ <td align="center"><a href="javascript: askfirst('<?php printf(__("Are you sure you want to delete membership level %s? All subscriptions will be cancelled.", "pmpro"), $level->name);?>','admin.php?page=pmpro-membershiplevels&action=delete_membership_level&deleteid=<?php echo $level->id?>'); void(0);" class="delete"><?php _e('delete', 'pmpro');?></a></td>
567
+ </tr>
568
+ <?php
569
+ }
570
+ ?>
571
+ </tbody>
572
+ </table>
573
+ <?php
574
+ }
575
+ ?>
576
+
577
+ <?php
578
+ require_once(dirname(__FILE__) . "/admin_footer.php");
579
+ ?>
580
+
adminpages/memberslist-csv.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_memberslist_csv")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb;
9
+
10
+ //get users
11
+ if(isset($_REQUEST['s']))
12
+ $s = $_REQUEST['s'];
13
+ else
14
+ $s = "";
15
+
16
+ if(isset($_REQUEST['l']))
17
+ $l = $_REQUEST['l'];
18
+ else
19
+ $l = false;
20
+
21
+ //some vars for the search
22
+ if(!empty($_REQUEST['pn']))
23
+ $pn = $_REQUEST['pn'];
24
+ else
25
+ $pn = 1;
26
+
27
+ if(!empty($_REQUEST['limit']))
28
+ $limit = $_REQUEST['limit'];
29
+ else
30
+ $limit = false;
31
+
32
+ if($limit)
33
+ {
34
+ $end = $pn * $limit;
35
+ $start = $end - $limit;
36
+ }
37
+ else
38
+ {
39
+ $end = NULL;
40
+ $start = NULL;
41
+ }
42
+
43
+ if($s)
44
+ {
45
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS u.ID, u.user_login, u.user_email, UNIX_TIMESTAMP(u.user_registered) as joindate, u.user_login, u.user_nicename, u.user_url, u.user_registered, u.user_status, u.display_name, mu.membership_id, mu.initial_payment, mu.billing_amount, mu.cycle_period, UNIX_TIMESTAMP(mu.enddate) as enddate, m.name as membership FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id LEFT JOIN $wpdb->pmpro_membership_levels m ON mu.membership_id = m.id WHERE mu.status = 'active' AND mu.membership_id > 0 AND (u.user_login LIKE '%" . esc_sql($s) . "%' OR u.user_email LIKE '%" . esc_sql($s) . "%' OR um.meta_value LIKE '%" . esc_sql($s) . "%') ";
46
+
47
+ if($l)
48
+ $sqlQuery .= " AND mu.membership_id = '" . esc_sql($l) . "' ";
49
+
50
+ $sqlQuery .= "GROUP BY u.ID ORDER BY user_registered DESC ";
51
+
52
+ if($limit)
53
+ $sqlQuery .= "LIMIT $start, $limit";
54
+ }
55
+ else
56
+ {
57
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS u.ID, u.user_login, u.user_email, UNIX_TIMESTAMP(u.user_registered) as joindate, u.user_login, u.user_nicename, u.user_url, u.user_registered, u.user_status, u.display_name, mu.membership_id, mu.initial_payment, mu.billing_amount, mu.cycle_period, UNIX_TIMESTAMP(mu.enddate) as enddate, m.name as membership FROM $wpdb->users u LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id LEFT JOIN $wpdb->pmpro_membership_levels m ON mu.membership_id = m.id";
58
+ $sqlQuery .= " WHERE mu.membership_id > 0 AND mu.status = 'active' ";
59
+ if($l)
60
+ $sqlQuery .= " AND mu.membership_id = '" . $l . "' ";
61
+ $sqlQuery .= "ORDER BY user_registered DESC ";
62
+ if($limit)
63
+ $sqlQuery .= "LIMIT $start, $limit";
64
+ }
65
+
66
+ $sqlQuery = apply_filters("pmpro_members_list_sql", $sqlQuery);
67
+
68
+ $theusers = $wpdb->get_results($sqlQuery);
69
+
70
+ $heading = "id,username,firstname,lastname,email,billing firstname,billing lastname,address1,address2,city,state,zipcode,country,phone,membership,initial payment,fee,term,discount_code_id,discount_code,joined,expires";
71
+ $heading = apply_filters("pmpro_members_list_csv_heading", $heading);
72
+ $csvoutput = $heading;
73
+
74
+ //these are the meta_keys for the fields (arrays are object, property. so e.g. $theuser->ID)
75
+ $default_columns = array(
76
+ array("theuser", "ID"),
77
+ array("theuser", "user_login"),
78
+ array("metavalues", "first_name"),
79
+ array("metavalues", "last_name"),
80
+ array("theuser", "user_email"),
81
+ array("metavalues", "pmpro_bfirstname"),
82
+ array("metavalues", "pmpro_blastname"),
83
+ array("metavalues", "pmpro_baddress1"),
84
+ array("metavalues", "pmpro_baddress2"),
85
+ array("metavalues", "pmpro_bcity"),
86
+ array("metavalues", "pmpro_bstate"),
87
+ array("metavalues", "pmpro_bzipcode"),
88
+ array("metavalues", "pmpro_bcountry"),
89
+ array("metavalues", "pmpro_bphone"),
90
+ array("theuser", "membership"),
91
+ array("theuser", "initial_payment"),
92
+ array("theuser", "billing_amount"),
93
+ array("theuser", "cycle_period"),
94
+ array("discount_code", "id"),
95
+ array("discount_code", "code")
96
+ //joindate and enddate are handled specifically below
97
+ );
98
+
99
+ //filter
100
+ $default_columns = apply_filters("pmpro_members_list_csv_default_columns", $default_columns);
101
+
102
+ //any extra columns
103
+ $extra_columns = apply_filters("pmpro_members_list_csv_extra_columns", array());
104
+ if(!empty($extra_columns))
105
+ {
106
+ foreach($extra_columns as $heading => $callback)
107
+ {
108
+ $csvoutput .= "," . $heading;
109
+ }
110
+ }
111
+
112
+ $csvoutput .= "\n";
113
+
114
+ if($theusers)
115
+ {
116
+ foreach($theusers as $theuser)
117
+ {
118
+ //get meta
119
+ $sqlQuery = "SELECT meta_key as `key`, meta_value as `value` FROM $wpdb->usermeta WHERE $wpdb->usermeta.user_id = '" . $theuser->ID . "'";
120
+ $metavalues = pmpro_getMetavalues($sqlQuery);
121
+ $theuser->metavalues = $metavalues;
122
+ $sqlQuery = "SELECT c.id, c.code FROM $wpdb->pmpro_discount_codes_uses cu LEFT JOIN $wpdb->pmpro_discount_codes c ON cu.code_id = c.id WHERE cu.user_id = '" . $theuser->ID . "' ORDER BY c.id DESC LIMIT 1";
123
+ $discount_code = $wpdb->get_row($sqlQuery);
124
+
125
+ //default columns
126
+ if(!empty($default_columns))
127
+ {
128
+ $count = 0;
129
+ foreach($default_columns as $col)
130
+ {
131
+ //add comma after the first item
132
+ $count++;
133
+ if($count > 1)
134
+ $csvoutput .= ",";
135
+
136
+ //checking $object->property. note the double $$
137
+ if(!empty($$col[0]->$col[1]))
138
+ $csvoutput .= pmpro_enclose($$col[0]->$col[1]); //output the value
139
+ }
140
+ }
141
+
142
+ //joindate and enddate
143
+ $csvoutput .= "," . pmpro_enclose(date("Y-m-d", $theuser->joindate)) . ",";
144
+ if($theuser->enddate)
145
+ $csvoutput .= pmpro_enclose(date("Y-m-d", $theuser->enddate));
146
+ else
147
+ $csvoutput .= pmpro_enclose("Never");
148
+
149
+ //any extra columns
150
+ if(!empty($extra_columns))
151
+ {
152
+ foreach($extra_columns as $heading => $callback)
153
+ {
154
+ $csvoutput .= "," . pmpro_enclose(call_user_func($callback, $theuser));
155
+ }
156
+ }
157
+
158
+ $csvoutput .= "\n";
159
+
160
+ }
161
+ }
162
+
163
+ $size_in_bytes = strlen($csvoutput);
164
+ header("Content-type: text/csv");
165
+ //header("Content-type: application/vnd.ms-excel");
166
+ if($s && $l)
167
+ header("Content-Disposition: attachment; filename=members_list_" . intval($l) . "_level" . sanitize_file_name($s) . ".csv; size=$size_in_bytes");
168
+ elseif($s)
169
+ header("Content-Disposition: attachment; filename=members_list_" . sanitize_file_name($s) . ".csv; size=$size_in_bytes");
170
+ elseif($l)
171
+ header("Content-Disposition: attachment; filename=members_list_level" . intval($l) . ".csv; size=$size_in_bytes");
172
+ else
173
+ header("Content-Disposition: attachment; filename=members_list.csv; size=$size_in_bytes");
174
+
175
+ print $csvoutput;
176
+
177
+ function pmpro_enclose($s)
178
+ {
179
+ return "\"" . str_replace("\"", "\\\"", $s) . "\"";
180
+ }
adminpages/memberslist.php ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_memberslist")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ //vars
9
+ global $wpdb, $pmpro_currency_symbol;
10
+ if(isset($_REQUEST['s']))
11
+ $s = $_REQUEST['s'];
12
+ else
13
+ $s = "";
14
+
15
+ if(isset($_REQUEST['l']))
16
+ $l = $_REQUEST['l'];
17
+ else
18
+ $l = false;
19
+
20
+ require_once(dirname(__FILE__) . "/admin_header.php");
21
+ ?>
22
+
23
+ <form id="posts-filter" method="get" action="">
24
+ <h2>
25
+ <?php _e('Members List', 'pmpro');?>
26
+ <a target="_blank" href="<?php echo admin_url('admin-ajax.php');?>?action=memberslist_csv&s=<?php echo $s?>&l=<?php echo $l?>" class="button add-new-h2"><?php _e('Export to CSV', 'pmpro');?></a>
27
+ </h2>
28
+ <ul class="subsubsub">
29
+ <li>
30
+ <?php _e('Show', 'pmpro');?>
31
+ <select name="l" onchange="jQuery('#posts-filter').submit();">
32
+ <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
33
+ <?php
34
+ $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
35
+ foreach($levels as $level)
36
+ {
37
+ ?>
38
+ <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
39
+ <?php
40
+ }
41
+ ?>
42
+ </select>
43
+ </li>
44
+ </ul>
45
+ <p class="search-box">
46
+ <label class="hidden" for="post-search-input"><?php _e('Search Members', 'pmpro');?>:</label>
47
+ <input type="hidden" name="page" value="pmpro-memberslist" />
48
+ <input id="post-search-input" type="text" value="<?php echo $s?>" name="s"/>
49
+ <input class="button" type="submit" value="<?php _e('Search Members', 'pmpro');?>"/>
50
+ </p>
51
+ <?php
52
+ //some vars for the search
53
+ if(isset($_REQUEST['pn']))
54
+ $pn = $_REQUEST['pn'];
55
+ else
56
+ $pn = 1;
57
+
58
+ if(isset($_REQUEST['limit']))
59
+ $limit = $_REQUEST['limit'];
60
+ else
61
+ $limit = 15;
62
+
63
+ $end = $pn * $limit;
64
+ $start = $end - $limit;
65
+
66
+ if($s)
67
+ {
68
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS u.ID, u.user_login, u.user_email, UNIX_TIMESTAMP(u.user_registered) as joindate, mu.membership_id, mu.initial_payment, mu.billing_amount, mu.cycle_period, mu.cycle_number, mu.billing_limit, mu.trial_amount, mu.trial_limit, UNIX_TIMESTAMP(mu.startdate) as startdate, UNIX_TIMESTAMP(mu.enddate) as enddate, m.name as membership FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id LEFT JOIN $wpdb->pmpro_membership_levels m ON mu.membership_id = m.id WHERE mu.status = 'active' AND mu.membership_id > 0 AND (u.user_login LIKE '%$s%' OR u.user_email LIKE '%$s%' OR um.meta_value LIKE '%$s%') ";
69
+
70
+ if($l)
71
+ $sqlQuery .= " AND mu.membership_id = '" . $l . "' ";
72
+
73
+ $sqlQuery .= "GROUP BY u.ID ORDER BY user_registered DESC LIMIT $start, $limit";
74
+ }
75
+ else
76
+ {
77
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS u.ID, u.user_login, u.user_email, UNIX_TIMESTAMP(u.user_registered) as joindate, mu.membership_id, mu.initial_payment, mu.billing_amount, mu.cycle_period, mu.cycle_number, mu.billing_limit, mu.trial_amount, mu.trial_limit, UNIX_TIMESTAMP(mu.startdate) as startdate, UNIX_TIMESTAMP(mu.enddate) as enddate, m.name as membership FROM $wpdb->users u LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id LEFT JOIN $wpdb->pmpro_membership_levels m ON mu.membership_id = m.id";
78
+ $sqlQuery .= " WHERE mu.membership_id > 0 AND mu.status = 'active' ";
79
+ if($l)
80
+ $sqlQuery .= " AND mu.membership_id = '" . $l . "' ";
81
+ $sqlQuery .= "GROUP BY u.ID ORDER BY user_registered DESC LIMIT $start, $limit";
82
+ }
83
+
84
+ $sqlQuery = apply_filters("pmpro_members_list_sql", $sqlQuery);
85
+
86
+ $theusers = $wpdb->get_results($sqlQuery);
87
+ $totalrows = $wpdb->get_var("SELECT FOUND_ROWS() as found_rows");
88
+
89
+ if($theusers)
90
+ {
91
+ $calculate_revenue = apply_filters("pmpro_memberslist_calculate_revenue", false);
92
+ if($calculate_revenue)
93
+ {
94
+ $initial_payments = pmpro_calculateInitialPaymentRevenue($s, $l);
95
+ $recurring_payments = pmpro_calculateRecurringRevenue($s, $l);
96
+ ?>
97
+ <p class="clear"><?php echo strval($totalrows)?> members found. These members have paid <strong>$<?php echo number_format($initial_payments)?> in initial payments</strong> and will generate an estimated <strong>$<?php echo number_format($recurring_payments)?> in revenue over the next year</strong>, or <strong>$<?php echo number_format($recurring_payments/12)?>/month</strong>. <span class="pmpro_lite">(This estimate does not take into account trial periods or billing limits.)</span></p>
98
+ <?php
99
+ }
100
+ else
101
+ {
102
+ ?>
103
+ <p class="clear"><?php printf(__("%d members found.", "pmpro"), $totalrows);?></span></p>
104
+ <?php
105
+ }
106
+ }
107
+ ?>
108
+ <table class="widefat">
109
+ <thead>
110
+ <tr class="thead">
111
+ <th><?php _e('ID', 'pmpro');?></th>
112
+ <th><?php _e('Username', 'pmpro');?></th>
113
+ <th><?php _e('First&nbsp;Name', 'pmpro');?></th>
114
+ <th><?php _e('Last&nbsp;Name', 'pmpro');?></th>
115
+ <th><?php _e('Email', 'pmpro');?></th>
116
+ <?php do_action("pmpro_memberslist_extra_cols_header", $theusers);?>
117
+ <th><?php _e('Billing Address', 'pmpro');?></th>
118
+ <th><?php _e('Membership', 'pmpro');?></th>
119
+ <th><?php _e('Fee', 'pmpro');?></th>
120
+ <th><?php _e('Joined', 'pmpro');?></th>
121
+ <th><?php _e('Expires', 'pmpro');?></th>
122
+ </tr>
123
+ </thead>
124
+ <tbody id="users" class="list:user user-list">
125
+ <?php
126
+ $count = 0;
127
+ foreach($theusers as $auser)
128
+ {
129
+ //get meta
130
+ $theuser = get_userdata($auser->ID);
131
+ ?>
132
+ <tr <?php if($count++ % 2 == 0) { ?>class="alternate"<?php } ?>>
133
+ <td><?php echo $theuser->ID?></td>
134
+ <td>
135
+ <?php echo get_avatar($theuser->ID, 32)?>
136
+ <strong>
137
+ <?php
138
+ $userlink = '<a href="user-edit.php?user_id=' . $theuser->ID . '">' . $theuser->user_login . '</a>';
139
+ $userlink = apply_filters("pmpro_members_list_user_link", $userlink, $theuser);
140
+ echo $userlink;
141
+ ?>
142
+ </strong>
143
+ </td>
144
+ <td><?php echo $theuser->first_name?></td>
145
+ <td><?php echo $theuser->last_name?></td>
146
+ <td><a href="mailto:<?php echo $theuser->user_email?>"><?php echo $theuser->user_email?></a></td>
147
+ <?php do_action("pmpro_memberslist_extra_cols_body", $theuser);?>
148
+ <td>
149
+ <?php
150
+ if(empty($theuser->pmpro_bfirstname))
151
+ $theuser->pmpro_bfirstname = "";
152
+ if(empty($theuser->pmpro_blastname))
153
+ $theuser->pmpro_blastname = "";
154
+ echo trim($theuser->pmpro_bfirstname . " " . $theuser->pmpro_blastname);
155
+ ?><br />
156
+ <?php if(!empty($theuser->pmpro_baddress1)) { ?>
157
+ <?php echo $theuser->pmpro_baddress1; ?><br />
158
+ <?php if(!empty($theuser->pmpro_baddress2)) echo $theuser->pmpro_baddress2 . "<br />"; ?>
159
+ <?php if($theuser->pmpro_bcity && $theuser->pmpro_bstate) { ?>
160
+ <?php echo $theuser->pmpro_bcity?>, <?php echo $theuser->pmpro_bstate?> <?php echo $theuser->pmpro_bzipcode?> <?php if(!empty($theuser->pmpro_bcountry)) echo $theuser->pmpro_bcountry?><br />
161
+ <?php } ?>
162
+ <?php } ?>
163
+ <?php if(!empty($theuser->pmpro_bphone)) echo formatPhone($theuser->pmpro_bphone);?>
164
+ </td>
165
+ <td><?php echo $auser->membership?></td>
166
+ <td>
167
+ <?php if((float)$auser->initial_payment > 0) { ?>
168
+ <?php echo $pmpro_currency_symbol; ?><?php echo $auser->initial_payment?>
169
+ <?php } ?>
170
+ <?php if((float)$auser->initial_payment > 0 && (float)$auser->billing_amount > 0) { ?>+<br /><?php } ?>
171
+ <?php if((float)$auser->billing_amount > 0) { ?>
172
+ <?php echo $pmpro_currency_symbol; ?><?php echo $auser->billing_amount?>/<?php echo $auser->cycle_period?>
173
+ <?php } ?>
174
+ <?php if((float)$auser->initial_payment <= 0 && (float)$auser->billing_amount <= 0) { ?>
175
+ -
176
+ <?php } ?>
177
+ </td>
178
+ <td><?php echo date("m/d/Y", strtotime($theuser->user_registered))?></td>
179
+ <td>
180
+ <?php
181
+ if($auser->enddate)
182
+ echo date(get_option('date_format'), $auser->enddate);
183
+ else
184
+ echo __("Never", "pmpro");
185
+ ?>
186
+ </td>
187
+ </tr>
188
+ <?php
189
+ }
190
+
191
+ if(!$theusers)
192
+ {
193
+ ?>
194
+ <tr>
195
+ <td colspan="9"><p><?php _e("No members found.", "pmpro");?> <?php if($l) { ?><a href="?page=pmpro-memberslist&s=<?php echo $s?>"><?php _e("Search all levels", "pmpro");?></a>.<?php } ?></p></td>
196
+ </tr>
197
+ <?php
198
+ }
199
+ ?>
200
+ </tbody>
201
+ </table>
202
+ </form>
203
+
204
+ <?php
205
+ echo pmpro_getPaginationString($pn, $totalrows, $limit, 1, get_admin_url(NULL, "/admin.php?page=pmpro-memberslist&s=" . urlencode($s)), "&l=$l&limit=$limit&pn=");
206
+ ?>
207
+
208
+ <?php
209
+ require_once(dirname(__FILE__) . "/admin_footer.php");
210
+ ?>
adminpages/orders-csv.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
7
+
8
+ global $wpdb;
9
+
10
+ //get users
11
+ if(isset($_REQUEST['s']))
12
+ $s = $_REQUEST['s'];
13
+ else
14
+ $s = "";
15
+
16
+ if(isset($_REQUEST['l']))
17
+ $l = $_REQUEST['l'];
18
+ else
19
+ $l = false;
20
+
21
+ //some vars for the search
22
+ if(!empty($_REQUEST['pn']))
23
+ $pn = $_REQUEST['pn'];
24
+ else
25
+ $pn = 1;
26
+
27
+ if(!empty($_REQUEST['limit']))
28
+ $limit = $_REQUEST['limit'];
29
+ else
30
+ $limit = false;
31
+
32
+ if($limit)
33
+ {
34
+ $end = $pn * $limit;
35
+ $start = $end - $limit;
36
+ }
37
+ else
38
+ {
39
+ $end = NULL;
40
+ $start = NULL;
41
+ }
42
+
43
+ if($s)
44
+ {
45
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS o.id FROM $wpdb->pmpro_membership_orders o LEFT JOIN $wpdb->users u ON o.user_id = u.ID LEFT JOIN $wpdb->pmpro_membership_levels l ON o.membership_id = l.id ";
46
+
47
+ $join_with_usermeta = apply_filters("pmpro_orders_search_usermeta", false);
48
+ if($join_with_usermeta)
49
+ $sqlQuery .= "LEFT JOIN $wpdb->usermeta um ON o.user_id = um.user_id ";
50
+
51
+ $sqlQuery .= "WHERE (1=2 ";
52
+
53
+ $fields = array("o.id", "o.code", "o.billing_name", "o.billing_street", "o.billing_city", "o.billing_state", "o.billing_zip", "o.billing_phone", "o.payment_type", "o.cardtype", "o.accountnumber", "o.status", "o.gateway", "o.gateway_environment", "o.payment_transaction_id", "o.subscription_transaction_id", "u.user_login", "u.user_email", "u.display_name", "l.name");
54
+
55
+ if($join_with_usermeta)
56
+ $fields[] = "um.meta_value";
57
+
58
+ $fields = apply_filters("pmpro_orders_search_fields", $fields);
59
+
60
+ foreach($fields as $field)
61
+ $sqlQuery .= " OR " . $field . " LIKE '%" . esc_sql($s) . "%' ";
62
+ $sqlQuery .= ") ";
63
+ $sqlQuery .= "ORDER BY o.timestamp DESC ";
64
+ }
65
+ else
66
+ {
67
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS id FROM $wpdb->pmpro_membership_orders ORDER BY timestamp DESC ";
68
+ }
69
+
70
+ if($limit)
71
+ $sqlQuery .= "LIMIT $start, $limit";
72
+
73
+ $order_ids = $wpdb->get_col($sqlQuery);
74
+
75
+ $csvoutput = "id,user_id,user_login,first_name,last_name,user_email,billing_name,billing_street,billing_city,billing_state,billing_zip,billing_country,billing_phone,membership_id,level_name,subtotal,tax,couponamount,total,payment_type,cardtype,accountnumber,expirationmonth,expirationyear,status,gateway,gateway_environment,payment_transaction_id,subscription_transaction_id,discount_code_id,discount_code,timestamp";
76
+
77
+ //these are the meta_keys for the fields (arrays are object, property. so e.g. $theuser->ID)
78
+ $default_columns = array(
79
+ array("order", "id"),
80
+ array("user", "ID"),
81
+ array("user", "user_login"),
82
+ array("user", "first_name"),
83
+ array("user", "last_name"),
84
+ array("user", "user_email"),
85
+ array("order", "billing", "name"),
86
+ array("order", "billing", "street"),
87
+ array("order", "billing", "city"),
88
+ array("order", "billing", "state"),
89
+ array("order", "billing", "zip"),
90
+ array("order", "billing", "country"),
91
+ array("order", "billing", "phone"),
92
+ array("order", "membership_id"),
93
+ array("level", "name"),
94
+ array("order", "subtotal"),
95
+ array("order", "tax"),
96
+ array("order", "couponamount"),
97
+ array("order", "total"),
98
+ array("order", "payment_type"),
99
+ array("order", "cardtype"),
100
+ array("order", "accountnumber"),
101
+ array("order", "expirationmonth"),
102
+ array("order", "expirationyear"),
103
+ array("order", "status"),
104
+ array("order", "gateway"),
105
+ array("order", "gateway_environment"),
106
+ array("order", "payment_transaction_id"),
107
+ array("order", "subscription_transactiond_id"),
108
+ array("discount_code", "id"),
109
+ array("discount_code", "code")
110
+ );
111
+
112
+ //any extra columns
113
+ $extra_columns = apply_filters("pmpro_orders_csv_extra_columns", array());
114
+ if(!empty($extra_columns))
115
+ {
116
+ foreach($extra_columns as $heading => $callback)
117
+ {
118
+ $csvoutput .= "," . $heading;
119
+ }
120
+ }
121
+
122
+ $csvoutput .= "\n";
123
+
124
+ if($order_ids)
125
+ {
126
+ foreach($order_ids as $order_id)
127
+ {
128
+ $order = new MemberOrder();
129
+ $order->nogateway = true;
130
+ $order->getMemberOrderByID($order_id);
131
+ $user = get_userdata($order->user_id);
132
+ $level = $order->getMembershipLevel();
133
+ $sqlQuery = "SELECT c.id, c.code FROM $wpdb->pmpro_discount_codes_uses cu LEFT JOIN $wpdb->pmpro_discount_codes c ON cu.code_id = c.id WHERE cu.order_id = '" . $order_id . "' LIMIT 1";
134
+ $discount_code = $wpdb->get_row($sqlQuery);
135
+
136
+ //default columns
137
+ if(!empty($default_columns))
138
+ {
139
+ $count = 0;
140
+ foreach($default_columns as $col)
141
+ {
142
+ //add comma after the first item
143
+ $count++;
144
+ if($count > 1)
145
+ $csvoutput .= ",";
146
+
147
+ //checking $object->property. note the double $$
148
+ if(!empty($col[2]) && isset($$col[0]->$col[1]->$col[2]))
149
+ $csvoutput .= pmpro_enclose($$col[0]->$col[1]->$col[2]); //output the value
150
+ elseif(!empty($$col[0]->$col[1]))
151
+ $csvoutput .= pmpro_enclose($$col[0]->$col[1]); //output the value
152
+ }
153
+ }
154
+
155
+ //timestamp
156
+ $csvoutput .= "," . pmpro_enclose(date(get_option("date_format"), $order->timestamp));
157
+
158
+ //any extra columns
159
+ if(!empty($extra_columns))
160
+ {
161
+ foreach($extra_columns as $heading => $callback)
162
+ {
163
+ $csvoutput .= "," . pmpro_enclose(call_user_func($callback, $order));
164
+ }
165
+ }
166
+
167
+ $csvoutput .= "\n";
168
+
169
+ }
170
+ }
171
+
172
+ $size_in_bytes = strlen($csvoutput);
173
+ header("Content-type: text/csv");
174
+ //header("Content-type: application/vnd.ms-excel");
175
+ if($s && $l)
176
+ header("Content-Disposition: attachment; filename=orders" . intval($l) . "_level" . sanitize_file_name($s) . ".csv; size=$size_in_bytes");
177
+ elseif($s)
178
+ header("Content-Disposition: attachment; filename=orders_" . sanitize_file_name($s) . ".csv; size=$size_in_bytes");
179
+ elseif($l)
180
+ header("Content-Disposition: attachment; filename=orders_level" . intval($l) . ".csv; size=$size_in_bytes");
181
+ else
182
+ header("Content-Disposition: attachment; filename=orders.csv; size=$size_in_bytes");
183
+
184
+ print $csvoutput;
185
+
186
+ function pmpro_enclose($s)
187
+ {
188
+ return "\"" . str_replace("\"", "\\\"", $s) . "\"";
189
+ }
adminpages/orders.php ADDED
@@ -0,0 +1,691 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ //vars
9
+ global $wpdb, $pmpro_currency_symbol;
10
+ if(isset($_REQUEST['s']))
11
+ $s = $_REQUEST['s'];
12
+ else
13
+ $s = "";
14
+
15
+ if(isset($_REQUEST['l']))
16
+ $l = $_REQUEST['l'];
17
+ else
18
+ $l = false;
19
+
20
+ //deleting?
21
+ if(!empty($_REQUEST['delete']))
22
+ {
23
+ $dorder = new MemberOrder(intval($_REQUEST['delete']));
24
+ if($dorder->deleteMe())
25
+ {
26
+ $pmpro_msg = __("Order deleted successfully.", "pmpro");
27
+ $pmpro_msgt = "success";
28
+ }
29
+ else
30
+ {
31
+ $pmpro_msg = __("Error deleting order.", "pmpro");
32
+ $pmpro_msgt = "error";
33
+ }
34
+ }
35
+
36
+ //this array stores fields that should be read only
37
+ $read_only_fields = apply_filters("pmpro_orders_read_only_fields", array("code", "payment_transaction_id", "subscription_transaction_id"));
38
+
39
+ //saving?
40
+ if(!empty($_REQUEST['save']))
41
+ {
42
+ //start with old order if applicable
43
+ $order_id = intval($_REQUEST['order']);
44
+ if($order_id > 0)
45
+ $order = new MemberOrder($order_id);
46
+ else
47
+ $order = new MemberOrder();
48
+
49
+ //update values
50
+ if(!in_array("code", $read_only_fields))
51
+ $order->code = $_POST['code'];
52
+ if(!in_array("user_id", $read_only_fields))
53
+ $order->user_id = intval($_POST['user_id']);
54
+ if(!in_array("membership_id", $read_only_fields))
55
+ $order->membership_id = intval($_POST['membership_id']);
56
+ if(!in_array("billing_name", $read_only_fields))
57
+ $order->billing->name = stripslashes($_POST['billing_name']);
58
+ if(!in_array("billing_street", $read_only_fields))
59
+ $order->billing->street = stripslashes($_POST['billing_street']);
60
+ if(!in_array("billing_city", $read_only_fields))
61
+ $order->billing->city = stripslashes($_POST['billing_city']);
62
+ if(!in_array("billing_state", $read_only_fields))
63
+ $order->billing->state = stripslashes($_POST['billing_state']);
64
+ if(!in_array("billing_zip", $read_only_fields))
65
+ $order->billing->zip = $_POST['billing_zip'];
66
+ if(!in_array("billing_country", $read_only_fields))
67
+ $order->billing->country = stripslashes($_POST['billing_country']);
68
+ if(!in_array("billing_phone", $read_only_fields))
69
+ $order->billing->phone = $_POST['billing_phone'];
70
+ if(!in_array("subtotal", $read_only_fields))
71
+ $order->subtotal = $_POST['subtotal'];
72
+ if(!in_array("tax", $read_only_fields))
73
+ $order->tax = $_POST['tax'];
74
+ if(!in_array("couponamount", $read_only_fields))
75
+ $order->couponamount = $_POST['couponamount'];
76
+ if(!in_array("total", $read_only_fields))
77
+ $order->total = $_POST['total'];
78
+ if(!in_array("payment_type", $read_only_fields))
79
+ $order->payment_type = $_POST['payment_type'];
80
+ if(!in_array("cardtype", $read_only_fields))
81
+ $order->cardtype = $_POST['cardtype'];
82
+ if(!in_array("accountnumber", $read_only_fields))
83
+ $order->accountnumber = $_POST['accountnumber'];
84
+ if(!in_array("expirationmonth", $read_only_fields))
85
+ $order->expirationmonth = $_POST['expirationmonth'];
86
+ if(!in_array("expirationyear", $read_only_fields))
87
+ $order->expirationyear = $_POST['expirationyear'];
88
+ if(!in_array("ExpirationDate", $read_only_fields))
89
+ $order->ExpirationDate = $order->expirationmonth . $order->expirationyear;
90
+ if(!in_array("status", $read_only_fields))
91
+ $order->status = stripslashes($_POST['status']);
92
+ if(!in_array("gateway", $read_only_fields))
93
+ $order->gateway = $_POST['gateway'];
94
+ if(!in_array("gateway_environment", $read_only_fields))
95
+ $order->gateway_environment = $_POST['gateway_environment'];
96
+ if(!in_array("payment_transaction_id", $read_only_fields))
97
+ $order->payment_transaction_id = $_POST['payment_transaction_id'];
98
+ if(!in_array("subscription_transaction_id", $read_only_fields))
99
+ $order->subscription_transaction_id = $_POST['subscription_transaction_id'];
100
+ if(!in_array("notes", $read_only_fields))
101
+ $order->notes = stripslashes($_POST['notes']);
102
+
103
+ //affiliate stuff
104
+ $affiliates = apply_filters("pmpro_orders_show_affiliate_ids", false);
105
+ if(!empty($affiliates))
106
+ {
107
+ if(!in_array("affiliate_id", $read_only_fields))
108
+ $order->affiliate_id = $_POST['affiliate_id'];
109
+ if(!in_array("affiliate_subid", $read_only_fields))
110
+ $order->affiliate_subid = $_POST['affiliate_subid'];
111
+ }
112
+
113
+ //save
114
+ if($order->saveOrder() !== false)
115
+ {
116
+ //handle timestamp
117
+ if($order->updateTimestamp($_POST['ts_year'], $_POST['ts_month'], $_POST['ts_day']) !== false)
118
+ {
119
+ $pmpro_msg = __("Order saved successfully.", "pmpro");
120
+ $pmpro_msgt = "success";
121
+ }
122
+ else
123
+ {
124
+ $pmpro_msg = __("Error updating order timestamp.", "pmpro");
125
+ $pmpro_msgt = "error";
126
+ }
127
+ }
128
+ else
129
+ {
130
+ $pmpro_msg = __("Error saving order.", "pmpro");
131
+ $pmpro_msgt = "error";
132
+ }
133
+ }
134
+ else
135
+ {
136
+ //order passed?
137
+ if(!empty($_REQUEST['order']))
138
+ {
139
+ $order_id = intval($_REQUEST['order']);
140
+ if($order_id > 0)
141
+ $order = new MemberOrder($order_id);
142
+ elseif(!empty($_REQUEST['copy']))
143
+ {
144
+ $order = new MemberOrder(intval($_REQUEST['copy']));
145
+
146
+ //new id
147
+ $order->id = NULL;
148
+
149
+ //new code
150
+ $order->code = $order->getRandomCode();
151
+ }
152
+ else
153
+ {
154
+ $order = new MemberOrder(); //new order
155
+
156
+ //defaults
157
+ $order->code = $order->getRandomCode();
158
+ $order->user_id = "";
159
+ $order->membership_id = "";
160
+ $order->billing->name = "";
161
+ $order->billing->street = "";
162
+ $order->billing->city = "";
163
+ $order->billing->state = "";
164
+ $order->billing->zip = "";
165
+ $order->billing->country = "";
166
+ $order->billing->phone = "";
167
+ $order->subtotal = "";
168
+ $order->tax = "";
169
+ $order->couponamount = "";
170
+ $order->total = "";
171
+ $order->payment_type = "";
172
+ $order->cardtype = "";
173
+ $order->accountnumber = "";
174
+ $order->expirationmonth = "";
175
+ $order->expirationyear = "";
176
+ $order->status = "success";
177
+ $order->gateway = pmpro_getOption("gateway");
178
+ $order->gateway_environment = pmpro_getOption("gateway_environment");
179
+ $order->payment_transaction_id = "";
180
+ $order->subscription_transaction_id = "";
181
+ $order->affiliate_id = "";
182
+ $order->affiliate_subid = "";
183
+ $order->notes = "";
184
+ }
185
+ }
186
+ }
187
+
188
+ require_once(dirname(__FILE__) . "/admin_header.php");
189
+ ?>
190
+
191
+ <?php if(!empty($order)) { ?>
192
+
193
+ <h2>
194
+ <?php if(!empty($order->id)) { ?>
195
+ <?php _e('Order', 'pmpro');?> #<?php echo $order->id?>: <?php echo $order->code?>
196
+ <?php } else { ?>
197
+ <?php _e('New Order', 'pmpro');?>
198
+ <?php } ?>
199
+ </h2>
200
+
201
+ <?php if(!empty($pmpro_msg)) { ?>
202
+ <div id="message" class="<?php if($pmpro_msgt == "success") echo "updated fade"; else echo "error"; ?>"><p><?php echo $pmpro_msg?></p></div>
203
+ <?php } ?>
204
+
205
+ <form method="post" action="">
206
+
207
+ <table class="form-table">
208
+ <tbody>
209
+ <tr>
210
+ <th scope="row" valign="top"><label>ID:</label></th>
211
+ <td><?php if(!empty($order->id)) echo $order->id; else echo __("This will be generated when you save.", "pmpro");?></td>
212
+ </tr>
213
+
214
+ <tr>
215
+ <th scope="row" valign="top"><label for="code"><?php _e('Code', 'pmpro');?>:</label></th>
216
+ <td>
217
+ <?php if(in_array("code", $read_only_fields)) { echo $order->code; } else { ?>
218
+ <input id="code" name="code" type="text" size="50" value="<?php echo esc_attr($order->code);?>" />
219
+ <?php } ?>
220
+ <?php if($order_id < 0) { ?><small class="pmpro_lite"><?php _e('Randomly generated for you.', 'pmpro');?></small><?php } ?>
221
+ </td>
222
+ </tr>
223
+
224
+ <tr>
225
+ <th scope="row" valign="top"><label for="user_id"><?php _e('User ID', 'pmpro');?>:</label></th>
226
+ <td>
227
+ <?php if(in_array("user_id", $read_only_fields) && $order_id > 0) { echo $order->user_id; } else { ?>
228
+ <input id="user_id" name="user_id" type="text" size="50" value="<?php echo esc_attr($order->user_id);?>" />
229
+ <?php } ?>
230
+ </td>
231
+ </tr>
232
+
233
+ <tr>
234
+ <th scope="row" valign="top"><label for="membership_id"><?php _e('Membership Level ID', 'pmpro');?>:</label></th>
235
+ <td>
236
+ <?php if(in_array("membership_id", $read_only_fields) && $order_id > 0) { echo $order->membership_id; } else { ?>
237
+ <input id="membership_id" name="membership_id" type="text" size="50" value="<?php echo esc_attr($order->membership_id);?>" />
238
+ <?php } ?>
239
+ </td>
240
+ </tr>
241
+
242
+ <tr>
243
+ <th scope="row" valign="top"><label for="billing_name"><?php _e('Billing Name', 'pmpro');?>:</label></th>
244
+ <td>
245
+ <?php if(in_array("billing_name", $read_only_fields) && $order_id > 0) { echo $order->billing_name; } else { ?>
246
+ <input id="billing_name" name="billing_name" type="text" size="50" value="<?php echo esc_attr($order->billing->name);?>" />
247
+ <?php } ?>
248
+ </td>
249
+ </tr>
250
+ <tr>
251
+ <th scope="row" valign="top"><label for="billing_street"><?php _e('Billing Street', 'pmpro');?>:</label></th>
252
+ <td>
253
+ <?php if(in_array("billing_street", $read_only_fields) && $order_id > 0) { echo $order->billing_street; } else { ?>
254
+ <input id="billing_street" name="billing_street" type="text" size="50" value="<?php echo esc_attr($order->billing->street);?>" /></td>
255
+ <?php } ?>
256
+ </tr>
257
+ <tr>
258
+ <th scope="row" valign="top"><label for="billing_city"><?php _e('Billing City', 'pmpro');?>:</label></th>
259
+ <td>
260
+ <?php if(in_array("billing_city", $read_only_fields) && $order_id > 0) { echo $order->billing_city; } else { ?>
261
+ <input id="billing_city" name="billing_city" type="text" size="50" value="<?php echo esc_attr($order->billing->city);?>" /></td>
262
+ <?php } ?>
263
+ </tr>
264
+ <tr>
265
+ <th scope="row" valign="top"><label for="billing_state"><?php _e('Billing State', 'pmpro');?>:</label></th>
266
+ <td>
267
+ <?php if(in_array("billing_state", $read_only_fields) && $order_id > 0) { echo $order->billing_state; } else { ?>
268
+ <input id="billing_state" name="billing_state" type="text" size="50" value="<?php echo esc_attr($order->billing->state);?>" /></td>
269
+ <?php } ?>
270
+ </tr>
271
+ <tr>
272
+ <th scope="row" valign="top"><label for="billing_zip"><?php _e('Billing Postal Code', 'pmpro');?>:</label></th>
273
+ <td>
274
+ <?php if(in_array("billing_zip", $read_only_fields) && $order_id > 0) { echo $order->billing_zip; } else { ?>
275
+ <input id="billing_zip" name="billing_zip" type="text" size="50" value="<?php echo esc_attr($order->billing->zip);?>" /></td>
276
+ <?php } ?>
277
+ </tr>
278
+ <tr>
279
+ <th scope="row" valign="top"><label for="billing_country"><?php _e('Billing Country', 'pmpro');?>:</label></th>
280
+ <td>
281
+ <?php if(in_array("billing_country", $read_only_fields) && $order_id > 0) { echo $order->billing_country; } else { ?>
282
+ <input id="billing_country" name="billing_country" type="text" size="50" value="<?php echo esc_attr($order->billing->country);?>" />
283
+ <?php } ?>
284
+ </td>
285
+ </tr>
286
+ <tr>
287
+ <th scope="row" valign="top"><label for="billing_phone"><?php _e('Billing Phone', 'pmpro');?>:</label></th>
288
+ <td>
289
+ <?php if(in_array("billing_phone", $read_only_fields) && $order_id > 0) { echo $order->billing_phone; } else { ?>
290
+ <input id="billing_phone" name="billing_phone" type="text" size="50" value="<?php echo esc_attr($order->billing->phone);?>" />
291
+ <?php } ?>
292
+ </td>
293
+ </tr>
294
+
295
+ <tr>
296
+ <th scope="row" valign="top"><label for="subtotal"><?php _e('Sub Total', 'pmpro');?>:</label></th>
297
+ <td>
298
+ <?php if(in_array("subtotal", $read_only_fields) && $order_id > 0) { echo $order->subtotal; } else { ?>
299
+ <input id="subtotal" name="subtotal" type="text" size="10" value="<?php echo esc_attr($order->subtotal);?>" />
300
+ <?php } ?>
301
+ </td>
302
+ </tr>
303
+ <tr>
304
+ <th scope="row" valign="top"><label for="tax"><?php _e('Tax', 'pmpro');?>:</label></th>
305
+ <td>
306
+ <?php if(in_array("tax", $read_only_fields) && $order_id > 0) { echo $order->tax; } else { ?>
307
+ <input id="tax" name="tax" type="text" size="10" value="<?php echo esc_attr($order->tax);?>" />
308
+ <?php } ?>
309
+ </td>
310
+ </tr>
311
+ <tr>
312
+ <th scope="row" valign="top"><label for="couponamount"><?php _e('Coupon Amount', 'pmpro');?>:</label></th>
313
+ <td>
314
+ <?php if(in_array("couponamount", $read_only_fields) && $order_id > 0) { echo $order->couponamount; } else { ?>
315
+ <input id="couponamount" name="couponamount" type="text" size="10" value="<?php echo esc_attr($order->couponamount);?>" />
316
+ <?php } ?>
317
+ </td>
318
+ </tr>
319
+ <tr>
320
+ <th scope="row" valign="top"><label for="total"><?php _e('Total', 'pmpro');?>:</label></th>
321
+ <td>
322
+ <?php if(in_array("total", $read_only_fields) && $order_id > 0) { echo $order->total; } else { ?>
323
+ <input id="total" name="total" type="text" size="10" value="<?php echo esc_attr($order->total);?>" />
324
+ <?php } ?>
325
+ <small class="pmpro_lite"><?php _e('Should be subtotal + tax - couponamount.', 'pmpro');?></small>
326
+ </td>
327
+ </tr>
328
+
329
+ <tr>
330
+ <th scope="row" valign="top"><label for="payment_type"><?php _e('Payment Type', 'pmpro');?>:</label></th>
331
+ <td>
332
+ <?php if(in_array("payment_type", $read_only_fields) && $order_id > 0) { echo $order->payment_type; } else { ?>
333
+ <input id="payment_type" name="payment_type" type="text" size="50" value="<?php echo esc_attr($order->payment_type);?>" />
334
+ <?php } ?>
335
+ <small class="pmpro_lite"><?php _e('e.g. PayPal Express, PayPal Standard, Credit Card.', 'pmpro');?></small>
336
+ </td>
337
+ </tr>
338
+ <tr>
339
+ <th scope="row" valign="top"><label for="cardtype"><?php _e('Card Type', 'pmpro');?></label></th>
340
+ <td>
341
+ <?php if(in_array("cardtype", $read_only_fields) && $order_id > 0) { echo $order->cardtype; } else { ?>
342
+ <input id="cardtype" name="cardtype" type="text" size="50" value="<?php echo esc_attr($order->cardtype);?>" />
343
+ <?php } ?>
344
+ <small class="pmpro_lite"><?php _e('e.g. Visa, MasterCard, AMEX, etc', 'pmpro');?></small>
345
+ </td>
346
+ </tr>
347
+ <tr>
348
+ <th scope="row" valign="top"><label for="accountnumber"><?php _e('Account Number', 'pmpro');?>:</label></th>
349
+ <td>
350
+ <?php if(in_array("accountnumber", $read_only_fields) && $order_id > 0) { echo $order->accountnumber; } else { ?>
351
+ <input id="accountnumber" name="accountnumber" type="text" size="50" value="<?php echo esc_attr($order->accountnumber);?>" />
352
+ <?php } ?>
353
+ <small class="pmpro_lite"><?php _e('Obscure all but last 4 digits.', 'pmpro');?></small>
354
+ </td>
355
+ </tr>
356
+ <?php if(in_array("ExpirationDate", $read_only_fields) && $order_id > 0) { echo $order->ExpirationDate; } else { ?>
357
+ <tr>
358
+ <th scope="row" valign="top"><label for="expirationmonth"><?php _e('Expiration Month', 'pmpro');?>:</label></th>
359
+ <td>
360
+ <input id="expirationmonth" name="expirationmonth" type="text" size="10" value="<?php echo esc_attr($order->expirationmonth);?>" />
361
+ <small class="pmpro_lite">MM</small>
362
+ </td>
363
+ </tr>
364
+ <tr>
365
+ <th scope="row" valign="top"><label for="expirationyear"><?php _e('Expiration Year', 'pmpro');?>:</label></th>
366
+ <td>
367
+ <input id="expirationyear" name="expirationyear" type="text" size="10" value="<?php echo esc_attr($order->expirationyear);?>" />
368
+ <small class="pmpro_lite">YYYY</small>
369
+ </td>
370
+ </tr>
371
+ <?php } ?>
372
+ <tr>
373
+ <th scope="row" valign="top"><label for="status"><?php _e('Status', 'pmpro');?>:</label></th>
374
+ <td>
375
+ <?php if(in_array("status", $read_only_fields) && $order_id > 0) { echo $order->status; } else { ?>
376
+ <?php
377
+ $statuses = array();
378
+ $default_statuses = array("", "success", "cancelled", "review", "token", "refunded");
379
+ $used_statuses = $wpdb->get_col("SELECT DISTINCT(status) FROM $wpdb->pmpro_membership_orders");
380
+ $statuses = array_unique(array_merge($default_statuses, $used_statuses));
381
+ asort($statuses);
382
+ $statuses = apply_filters("pmpro_order_statuses", $statuses);
383
+ ?>
384
+ <select id="status" name="status">
385
+ <?php foreach($statuses as $status) { ?>
386
+ <option value="<?php echo esc_attr($status);?>" <?php selected($order->status, $status);?>><?php echo $status;?></option>
387
+ <?php } ?>
388
+ </select>
389
+ <?php } ?>
390
+ </td>
391
+ </tr>
392
+
393
+ <tr>
394
+ <th scope="row" valign="top"><label for="gateway"><?php _e('Gateway', 'pmpro');?>:</label></th>
395
+ <td>
396
+ <?php if(in_array("gateway", $read_only_fields) && $order_id > 0) { echo $order->gateway; } else { ?>
397
+ <select id="gateway" name="gateway" onchange="pmpro_changeGateway(jQuery(this).val());">
398
+ <option value="" <?php if(empty($order->gateway)) { ?>selected="selected"<?php } ?>><?php _e('Testing Only', 'pmpro');?></option>
399
+ <option value="check" <?php if($order->gateway == "check") { ?>selected="selected"<?php } ?>><?php _e('Pay by Check', 'pmpro');?></option>
400
+ <option value="stripe" <?php if($order->gateway == "stripe") { ?>selected="selected"<?php } ?>>Stripe</option>
401
+ <option value="paypalstandard" <?php if($order->gateway == "paypalstandard") { ?>selected="selected"<?php } ?>>PayPal Standard</option>
402
+ <option value="paypalexpress" <?php if($order->gateway == "paypalexpress") { ?>selected="selected"<?php } ?>>PayPal Express</option>
403
+ <option value="paypal" <?php if($order->gateway == "paypal") { ?>selected="selected"<?php } ?>>PayPal Website Payments Pro</option>
404
+ <option value="payflowpro" <?php if($order->gateway == "payflowpro") { ?>selected="selected"<?php } ?>>PayPal Payflow Pro</option>
405
+ <option value="authorizenet" <?php if($order->gateway == "authorizenet") { ?>selected="selected"<?php } ?>>Authorize.net</option>
406
+ </select>
407
+ <?php } ?>
408
+ </td>
409
+ </tr>
410
+ <tr>
411
+ <th scope="row" valign="top"><label for="gateway_environment"><?php _e('Gateway Environment', 'pmpro');?>:</label></th>
412
+ <td>
413
+ <?php if(in_array("gateway_environment", $read_only_fields) && $order_id > 0) { echo $order->gateway_environment; } else { ?>
414
+ <select name="gateway_environment">
415
+ <option value="sandbox" <?php if($order->gateway_environment == "sandbox") { ?>selected="selected"<?php } ?>><?php _e('Sandbox/Testing', 'pmpro');?></option>
416
+ <option value="live" <?php if($order->gateway_environment == "live") { ?>selected="selected"<?php } ?>><?php _e('Live/Production', 'pmpro');?></option>
417
+ </select>
418
+ <?php } ?>
419
+ </td>
420
+ </tr>
421
+
422
+ <tr>
423
+ <th scope="row" valign="top"><label for="payment_transaction_id"><?php _e('Payment Transaction ID', 'pmpro');?>:</label></th>
424
+ <td>
425
+ <?php if(in_array("payment_transaction_id", $read_only_fields) && $order_id > 0) { echo $order->payment_transaction_id; } else { ?>
426
+ <input id="payment_transaction_id" name="payment_transaction_id" type="text" size="50" value="<?php echo esc_attr($order->payment_transaction_id);?>" />
427
+ <?php } ?>
428
+ <small class="pmpro_lite"><?php _e('Generated by the gateway. Useful to cross reference orders.', 'pmpro');?></small>
429
+ </td>
430
+ </tr>
431
+ <tr>
432
+ <th scope="row" valign="top"><label for="subscription_transaction_id"><?php _e('Subscription Transaction ID', 'pmpro');?>:</label></th>
433
+ <td>
434
+ <?php if(in_array("code", $read_only_fields) && $order_id > 0) { echo $order->subscription_transaction_id; } else { ?>
435
+ <input id="subscription_transaction_id" name="subscription_transaction_id" type="text" size="50" value="<?php echo esc_attr($order->subscription_transaction_id);?>" />
436
+ <?php } ?>
437
+ <small class="pmpro_lite"><?php _e('Generated by the gateway. Useful to cross reference subscriptions.', 'pmpro');?></small>
438
+ </td>
439
+ </tr>
440
+
441
+ <tr>
442
+ <th scope="row" valign="top"><label for="ts_month"><?php _e('Date', 'pmpro');?>:</label></th>
443
+ <td>
444
+ <?php if(in_array("timestamp", $read_only_fields) && $order_id > 0) { echo date(option("date_format"), $order->timestamp); } else { ?>
445
+ <?php
446
+ //setup date vars
447
+ if(!empty($order->timestamp))
448
+ $timestamp = $order->timestamp;
449
+ else
450
+ $timestamp = time();
451
+
452
+ $year = date("Y", $timestamp);
453
+ $month = date("n", $timestamp);
454
+ $day = date("j", $timestamp);
455
+ ?>
456
+ <select id="ts_month" name="ts_month">
457
+ <?php
458
+ for($i = 1; $i < 13; $i++)
459
+ {
460
+ ?>
461
+ <option value="<?php echo $i?>" <?php if($i == $month) { ?>selected="selected"<?php } ?>><?php echo date("M", strtotime($i . "/1/" . $year))?></option>
462
+ <?php
463
+ }
464
+ ?>
465
+ </select>
466
+ <input name="ts_day" type="text" size="2" value="<?php echo $day?>" />
467
+ <input name="ts_year" type="text" size="4" value="<?php echo $year?>" />
468
+ <?php } ?>
469
+ </td>
470
+ </tr>
471
+
472
+ <?php
473
+ $affiliates = apply_filters("pmpro_orders_show_affiliate_ids", false);
474
+ if(!empty($affiliates)) {
475
+ ?>
476
+ <tr>
477
+ <th scope="row" valign="top"><label for="affiliate_id"><?php _e('Affiliate ID', 'pmpro');?>Affiliate ID:</label></th>
478
+ <td>
479
+ <?php if(in_array("affiliate_id", $read_only_fields) && $order_id > 0) { echo $order->affiliate_id; } else { ?>
480
+ <input id="affiliate_id" name="affiliate_id" type="text" size="50" value="<?php echo esc_attr($order->affiliate_id);?>" />
481
+ <?php } ?>
482
+ </td>
483
+ </tr>
484
+ <tr>
485
+ <th scope="row" valign="top"><label for="affiliate_subid"><?php _e('Affiliate SubID', 'pmpro');?>Affiliate SubID:</label></th>
486
+ <td>
487
+ <?php if(in_array("affiliate_subid", $read_only_fields) && $order_id > 0) { echo $order->affiliate_subid; } else { ?>
488
+ <input id="affiliate_subid" name="affiliate_subid" type="text" size="50" value="<?php echo esc_attr($order->affiliate_subid);?>" />
489
+ <?php } ?>
490
+ </td>
491
+ </tr>
492
+ <?php } ?>
493
+
494
+ <tr>
495
+ <th scope="row" valign="top"><label for="notes"><?php _e('Notes', 'pmpro');?>:</label></th>
496
+ <td>
497
+ <?php if(in_array("notes", $read_only_fields) && $order_id > 0) { echo $order->notes; } else { ?>
498
+ <textarea id="notes" name="notes" rows="5" cols="80"><?php echo esc_textarea($order->notes);?></textarea>
499
+ <?php } ?>
500
+ </td>
501
+ </tr>
502
+
503
+ <?php do_action("pmpro_after_order_settings", $order); ?>
504
+
505
+ </tbody>
506
+ </table>
507
+
508
+ <p class="submit topborder">
509
+ <input name="order" type="hidden" value="<?php if(!empty($order->id)) echo $order->id; else echo $order_id;?>" />
510
+ <input name="save" type="submit" class="button-primary" value="<?php _e('Save Order', 'pmpro');?>" />
511
+ <input name="cancel" type="button" value="<?php _e('Cancel', 'pmpro');?>" onclick="location.href='<?php echo get_admin_url(NULL, '/admin.php?page=pmpro-orders')?>';" />
512
+ </p>
513
+
514
+ </form>
515
+
516
+ <?php } else { ?>
517
+
518
+ <form id="posts-filter" method="get" action="">
519
+ <h2>
520
+ <?php _e('Orders', 'pmpro');?>
521
+ <a href="admin.php?page=pmpro-orders&order=-1" class="button add-new-h2">+ <?php _e('Add New Order', 'pmpro');?></a>
522
+ <a target="_blank" href="<?php echo admin_url('admin-ajax.php');?>?action=orders_csv&s=<?php echo $s?>&l=<?php echo $l?>" class="button add-new-h2"><?php _e('Export to CSV', 'pmpro');?></a>
523
+ </h2>
524
+
525
+ <?php if(!empty($pmpro_msg)) { ?>
526
+ <div id="message" class="<?php if($pmpro_msgt == "success") echo "updated fade"; else echo "error"; ?>"><p><?php echo $pmpro_msg?></p></div>
527
+ <?php } ?>
528
+
529
+ <ul class="subsubsub">
530
+ <li>
531
+
532
+ </li>
533
+ </ul>
534
+ <p class="search-box">
535
+ <label class="hidden" for="post-search-input"><?php _e('Search Orders', 'pmpro');?>:</label>
536
+ <input type="hidden" name="page" value="pmpro-orders" />
537
+ <input id="post-search-input" type="text" value="<?php echo $s?>" name="s"/>
538
+ <input class="button" type="submit" value="<?php _e('Search Orders', 'pmpro');?>"/>
539
+ </p>
540
+ <?php
541
+ //some vars for the search
542
+ if(isset($_REQUEST['pn']))
543
+ $pn = $_REQUEST['pn'];
544
+ else
545
+ $pn = 1;
546
+
547
+ if(isset($_REQUEST['limit']))
548
+ $limit = $_REQUEST['limit'];
549
+ else
550
+ $limit = 15;
551
+
552
+ $end = $pn * $limit;
553
+ $start = $end - $limit;
554
+
555
+ if($s)
556
+ {
557
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS o.id FROM $wpdb->pmpro_membership_orders o LEFT JOIN $wpdb->users u ON o.user_id = u.ID LEFT JOIN $wpdb->pmpro_membership_levels l ON o.membership_id = l.id ";
558
+
559
+ $join_with_usermeta = apply_filters("pmpro_orders_search_usermeta", false);
560
+ if($join_with_usermeta)
561
+ $sqlQuery .= "LEFT JOIN $wpdb->usermeta um ON o.user_id = um.user_id ";
562
+
563
+ $sqlQuery .= "WHERE (1=2 ";
564
+
565
+ $fields = array("o.id", "o.code", "o.billing_name", "o.billing_street", "o.billing_city", "o.billing_state", "o.billing_zip", "o.billing_phone", "o.payment_type", "o.cardtype", "o.accountnumber", "o.status", "o.gateway", "o.gateway_environment", "o.payment_transaction_id", "o.subscription_transaction_id", "u.user_login", "u.user_email", "u.display_name", "l.name");
566
+
567
+ if($join_with_usermeta)
568
+ $fields[] = "um.meta_value";
569
+
570
+ $fields = apply_filters("pmpro_orders_search_fields", $fields);
571
+
572
+ foreach($fields as $field)
573
+ $sqlQuery .= " OR " . $field . " LIKE '%" . esc_sql($s) . "%' ";
574
+ $sqlQuery .= ") ";
575
+ $sqlQuery .= "GROUP BY o.id ORDER BY o.id DESC, o.timestamp DESC ";
576
+ }
577
+ else
578
+ {
579
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS id FROM $wpdb->pmpro_membership_orders ORDER BY id DESC, timestamp DESC ";
580
+ }
581
+
582
+ $sqlQuery .= "LIMIT $start, $limit";
583
+
584
+ $order_ids = $wpdb->get_col($sqlQuery);
585
+ $totalrows = $wpdb->get_var("SELECT FOUND_ROWS() as found_rows");
586
+
587
+ if($order_ids)
588
+ {
589
+ ?>
590
+ <p class="clear"><?php printf(__("%d orders found.", "pmpro"), $totalrows);?></span></p>
591
+ <?php
592
+ }
593
+ ?>
594
+ <table class="widefat">
595
+ <thead>
596
+ <tr class="thead">
597
+ <th><?php _e('ID', 'pmpro');?></th>
598
+ <th><?php _e('Code', 'pmpro');?></th>
599
+ <th><?php _e('User', 'pmpro');?></th>
600
+ <?php do_action("pmpro_orders_extra_cols_header", $order_ids);?>
601
+ <th><?php _e('Membership Level', 'pmpro');?></th>
602
+ <th><?php _e('Total', 'pmpro');?></th>
603
+ <th><?php _e('Payment', 'pmpro');?></th>
604
+ <th><?php _e('Gateway', 'pmpro');?></th>
605
+ <th><?php _e('Transaction IDs', 'pmpro');?></th>
606
+ <th><?php _e('Status', 'pmpro');?></th>
607
+ <th><?php _e('Date', 'pmpro');?></th>
608
+ <th></th>
609
+ <th></th>
610
+ <th></th>
611
+ </tr>
612
+ </thead>
613
+ <tbody id="orders" class="list:order orders-list">
614
+ <?php
615
+ $count = 0;
616
+ foreach($order_ids as $order_id)
617
+ {
618
+ $order = new MemberOrder();
619
+ $order->nogateway = true;
620
+ $order->getMemberOrderByID($order_id);
621
+ ?>
622
+ <tr <?php if($count++ % 2 == 0) { ?>class="alternate"<?php } ?>>
623
+ <td><a href="admin.php?page=pmpro-orders&order=<?php echo $order->id?>"><?php echo $order->id;?></a></td>
624
+ <td><a href="admin.php?page=pmpro-orders&order=<?php echo $order->id?>"><?php echo $order->code;?></a></td>
625
+ <td>
626
+ <?php $order->getUser(); ?>
627
+ <?php if(!empty($order->user)) { ?>
628
+ <a href="user-edit.php?user_id=<?php echo $order->user->ID?>"><?php echo $order->user->user_login?></a>
629
+ <?php } else { ?>
630
+ [<?php _e('deleted', 'pmpro');?>]
631
+ <?php } ?>
632
+ </td>
633
+ <?php do_action("pmpro_orders_extra_cols_body", $order);?>
634
+ <td><?php echo $order->membership_id;?></td>
635
+ <td><?php echo $pmpro_currency_symbol . $order->total;?></td>
636
+ <td>
637
+ <?php if(!empty($order->payment_type)) echo $order->payment_type . "<br />";?>
638
+ <?php if(!empty($order->accountnumber)) { ?>
639
+ <?php echo $order->cardtype;?>: x<?php echo last4($order->accountnumber);?><br />
640
+ <?php } ?>
641
+ <?php if(!empty($order->billing->street)) { ?>
642
+ <?php echo $order->billing->street; ?><br />
643
+ <?php if( $order->billing->city && $order->billing->state) { ?>
644
+ <?php echo $order->billing->city?>, <?php echo $order->billing->state?> <?php echo $order->billing->zip?> <?php if(!empty( $order->billing->country)) echo $order->billing->country?><br />
645
+ <?php } ?>
646
+ <?php } ?>
647
+ <?php if(!empty($order->billing->phone)) echo formatPhone($order->billing->phone);?>
648
+ </td>
649
+ <td><?php echo $order->gateway;?><?php if($order->gateway_environment == "test") echo "(test)";?></td>
650
+ <td>
651
+ <?php _e('Payment', 'pmpro');?>: <?php if(!empty($order->payment_transaction_id)) echo $order->payment_transaction_id; else echo "N/A";?>
652
+ <br />
653
+ <?php _e('Subscription', 'pmpro');?>: <?php if(!empty($order->subscription_transaction_id)) echo $order->subscription_transaction_id; else echo "N/A";?>
654
+ </td>
655
+ <td><?php echo $order->status;?></td>
656
+ <td><?php echo date(get_option('date_format'), $order->timestamp);?></td>
657
+ <td align="center">
658
+ <a href="admin.php?page=pmpro-orders&order=<?php echo $order->id;?>"><?php _e('edit', 'pmpro');?></a>
659
+ </td>
660
+ <td align="center">
661
+ <a href="admin.php?page=pmpro-orders&order=-1&copy=<?php echo $order->id;?>"><?php _e('copy', 'pmpro');?></a>
662
+ </td>
663
+ <td align="center">
664
+ <a href="javascript:askfirst('<?php printf(__("Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?", "pmpro"), str_replace("'", "", $order->code));?>', 'admin.php?page=pmpro-orders&delete=<?php echo $order->id;?>'); void(0);"><?php _e('delete', 'pmpro');?></a>
665
+ </td>
666
+ </tr>
667
+ <?php
668
+ }
669
+
670
+ if(!$order_ids)
671
+ {
672
+ ?>
673
+ <tr>
674
+ <td colspan="9"><p><?php _e('No orders found.', 'pmpro');?></p></td>
675
+ </tr>
676
+ <?php
677
+ }
678
+ ?>
679
+ </tbody>
680
+ </table>
681
+ </form>
682
+
683
+ <?php
684
+ echo pmpro_getPaginationString($pn, $totalrows, $limit, 1, get_admin_url(NULL, "/admin.php?page=pmpro-orders&s=" . urlencode($s)), "&l=$l&limit=$limit&pn=");
685
+ ?>
686
+
687
+ <?php } ?>
688
+
689
+ <?php
690
+ require_once(dirname(__FILE__) . "/admin_footer.php");
691
+ ?>
adminpages/pagesettings.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_pagesettings")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb, $msg, $msgt;
9
+
10
+ //get/set settings
11
+ global $pmpro_pages;
12
+ if(!empty($_REQUEST['savesettings']))
13
+ {
14
+ //page ids
15
+ pmpro_setOption("account_page_id");
16
+ pmpro_setOption("billing_page_id");
17
+ pmpro_setOption("cancel_page_id");
18
+ pmpro_setOption("checkout_page_id");
19
+ pmpro_setOption("confirmation_page_id");
20
+ pmpro_setOption("invoice_page_id");
21
+ pmpro_setOption("levels_page_id");
22
+
23
+ //update the pages array
24
+ $pmpro_pages["account"] = pmpro_getOption("account_page_id");
25
+ $pmpro_pages["billing"] = pmpro_getOption("billing_page_id");
26
+ $pmpro_pages["cancel"] = pmpro_getOption("cancel_page_id");
27
+ $pmpro_pages["checkout"] = pmpro_getOption("checkout_page_id");
28
+ $pmpro_pages["confirmation"] = pmpro_getOption("confirmation_page_id");
29
+ $pmpro_pages["invoice"] = pmpro_getOption("invoice_page_id");
30
+ $pmpro_pages["levels"] = pmpro_getOption("levels_page_id");
31
+
32
+ //assume success
33
+ $msg = true;
34
+ $msgt = "Your page settings have been updated.";
35
+ }
36
+
37
+ //are we generating pages?
38
+ if(!empty($_REQUEST['createpages']))
39
+ {
40
+ global $pmpro_pages;
41
+
42
+ $pages_created = array();
43
+
44
+ //check the pages array
45
+ foreach($pmpro_pages as $pmpro_page_name => $pmpro_page_id)
46
+ {
47
+ if(!$pmpro_page_id)
48
+ {
49
+ //no id set. create an array to store the page info
50
+ $insert = array(
51
+ 'post_title' => __('Membership', 'pmpro') . ' ' . ucwords($pmpro_page_name),
52
+ 'post_status' => 'publish',
53
+ 'post_type' => 'page',
54
+ 'post_content' => '[pmpro_' . $pmpro_page_name . ']',
55
+ 'comment_status' => 'closed',
56
+ 'ping_status' => 'closed'
57
+ );
58
+
59
+ //make non-account pages a subpage of account
60
+ if($pmpro_page_name != "account")
61
+ {
62
+ $insert['post_parent'] = $pmpro_pages['account'];
63
+ }
64
+
65
+ //create the page
66
+ $pmpro_pages[$pmpro_page_name] = wp_insert_post( $insert );
67
+
68
+ //add besecure post option to pages that need it
69
+ /* these pages are handling this themselves in the preheader
70
+ if(in_array($pmpro_page_name, array("billing", "checkout")))
71
+ update_post_meta($pmpro_pages[$pmpro_page_name], "besecure", 1);
72
+ */
73
+
74
+ //update the option too
75
+ pmpro_setOption($pmpro_page_name . "_page_id", $pmpro_pages[$pmpro_page_name]);
76
+ $pages_created[] = $pmpro_pages[$pmpro_page_name];
77
+ }
78
+ }
79
+
80
+ if(!empty($pages_created))
81
+ {
82
+ $msg = true;
83
+ $msgt = __("The following pages have been created for you", "pmpro") . ": " . implode(", ", $pages_created) . ".";
84
+ }
85
+ }
86
+
87
+ require_once(dirname(__FILE__) . "/admin_header.php");
88
+ ?>
89
+
90
+
91
+ <form action="" method="post" enctype="multipart/form-data">
92
+ <h2><?php _e('Pages', 'pmpro');?></h2>
93
+ <?php
94
+ global $pmpro_pages_ready;
95
+ if($pmpro_pages_ready)
96
+ {
97
+ ?>
98
+ <p><?php _e('Manage the WordPress pages assigned to each required Paid Memberships Pro page.', 'pmpro');?></p>
99
+ <?php
100
+ }
101
+ else
102
+ {
103
+ ?>
104
+ <p><?php _e('Assign the WordPress pages for each required Paid Memberships Pro page or', 'pmpro');?> <a href="?page=pmpro-pagesettings&createpages=1"><?php _e('click here to let us generate them for you', 'pmpro');?></a>.</p>
105
+ <?php
106
+ }
107
+ ?>
108
+ <table class="form-table">
109
+ <tbody>
110
+ <tr>
111
+ <th scope="row" valign="top">
112
+ <label for="account_page_id"><?php _e('Account Page', 'pmpro');?>:</label>
113
+ </th>
114
+ <td>
115
+ <?php
116
+ wp_dropdown_pages(array("name"=>"account_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['account']));
117
+ ?>
118
+ <?php if(!empty($pmpro_pages['account'])) { ?>
119
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['account']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
120
+ <?php } ?>
121
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_account].</small>
122
+ </td>
123
+ <tr>
124
+ <th scope="row" valign="top">
125
+ <label for="billing_page_id"><?php _e('Billing Information Page', 'pmpro');?>:</label>
126
+ </th>
127
+ <td>
128
+ <?php
129
+ wp_dropdown_pages(array("name"=>"billing_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['billing']));
130
+ ?>
131
+ <?php if(!empty($pmpro_pages['billing'])) { ?>
132
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['billing']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
133
+ <?php } ?>
134
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_billing].</small>
135
+ </td>
136
+ <tr>
137
+ <th scope="row" valign="top">
138
+ <label for="cancel_page_id"><?php _e('Cancel Page', 'pmpro');?>:</label>
139
+ </th>
140
+ <td>
141
+ <?php
142
+ wp_dropdown_pages(array("name"=>"cancel_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['cancel']));
143
+ ?>
144
+ <?php if(!empty($pmpro_pages['cancel'])) { ?>
145
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['cancel']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
146
+ <?php } ?>
147
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_cancel].</small>
148
+ </td>
149
+ </tr>
150
+ <tr>
151
+ <th scope="row" valign="top">
152
+ <label for="checkout_page_id"><?php _e('Checkout Page', 'pmpro');?>:</label>
153
+ </th>
154
+ <td>
155
+ <?php
156
+ wp_dropdown_pages(array("name"=>"checkout_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['checkout']));
157
+ ?>
158
+ <?php if(!empty($pmpro_pages['checkout'])) { ?>
159
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['checkout']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
160
+ <?php } ?>
161
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_checkout].</small>
162
+ </td>
163
+ </tr>
164
+ <tr>
165
+ <th scope="row" valign="top">
166
+ <label for="confirmation_page_id"><?php _e('Confirmation Page', 'pmpro');?>:</label>
167
+ </th>
168
+ <td>
169
+ <?php
170
+ wp_dropdown_pages(array("name"=>"confirmation_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['confirmation']));
171
+ ?>
172
+ <?php if(!empty($pmpro_pages['confirmation'])) { ?>
173
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['confirmation']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
174
+ <?php } ?>
175
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_confirmation].</small>
176
+ </td>
177
+ </tr>
178
+ <tr>
179
+ <th scope="row" valign="top">
180
+ <label for="invoice_page_id"><?php _e('Invoice Page', 'pmpro');?>:</label>
181
+ </th>
182
+ <td>
183
+ <?php
184
+ wp_dropdown_pages(array("name"=>"invoice_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['invoice']));
185
+ ?>
186
+ <?php if(!empty($pmpro_pages['invoice'])) { ?>
187
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['invoice']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
188
+ <?php } ?>
189
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_invoice].</small>
190
+ </td>
191
+ </tr>
192
+ <tr>
193
+ <th scope="row" valign="top">
194
+ <label for="levels_page_id"><?php _e('Levels Page', 'pmpro');?>:</label>
195
+ </th>
196
+ <td>
197
+ <?php
198
+ wp_dropdown_pages(array("name"=>"levels_page_id", "show_option_none"=>"-- Choose One --", "selected"=>$pmpro_pages['levels']));
199
+ ?>
200
+ <?php if(!empty($pmpro_pages['levels'])) { ?>
201
+ <a target="_blank" href="post.php?post=<?php echo $pmpro_pages['levels']?>&action=edit" class="pmpro_page_edit"><?php _e('edit page', 'pmpro');?></a>
202
+ <?php } ?>
203
+ <br /><small class="pmpro_lite"><?php _e('Include the shortcode', 'pmpro');?> [pmpro_levels].</small>
204
+ </td>
205
+ </tr>
206
+ </tbody>
207
+ </table>
208
+ <p class="submit">
209
+ <input name="savesettings" type="submit" class="button-primary" value="<?php _e('Save Settings', 'pmpro');?>" />
210
+ </p>
211
+ </form>
212
+
213
+ <?php
214
+ require_once(dirname(__FILE__) . "/admin_footer.php");
215
+ ?>
adminpages/paymentsettings.php ADDED
@@ -0,0 +1,579 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //only admins can get this
3
+ if(!function_exists("current_user_can") || (!current_user_can("manage_options") && !current_user_can("pmpro_paymentsettings")))
4
+ {
5
+ die(__("You do not have permissions to perform this action.", "pmpro"));
6
+ }
7
+
8
+ global $wpdb, $pmpro_currency_symbol, $msg, $msgt;
9
+
10
+ //get/set settings
11
+ if(!empty($_REQUEST['savesettings']))
12
+ {
13
+ pmpro_setOption("sslseal");
14
+ pmpro_setOption("nuclear_HTTPS");
15
+
16
+ //gateway options
17
+ pmpro_setOption("gateway");
18
+ pmpro_setOption("gateway_environment");
19
+ pmpro_setOption("gateway_email");
20
+ pmpro_setOption("payflow_partner");
21
+ pmpro_setOption("payflow_vendor");
22
+ pmpro_setOption("payflow_user");
23
+ pmpro_setOption("payflow_pwd");
24
+ pmpro_setOption("apiusername");
25
+ pmpro_setOption("apipassword");
26
+ pmpro_setOption("apisignature");
27
+ pmpro_setOption("loginname");
28
+ pmpro_setOption("transactionkey");
29
+ pmpro_setOption("stripe_secretkey");
30
+ pmpro_setOption("stripe_publishablekey");
31
+ pmpro_setOption("stripe_billingaddress");
32
+ pmpro_setOption("braintree_merchantid");
33
+ pmpro_setOption("braintree_publickey");
34
+ pmpro_setOption("braintree_privatekey");
35
+ pmpro_setOption("braintree_encryptionkey");
36
+ pmpro_setOption("twocheckout_apiusername");
37
+ pmpro_setOption("twocheckout_apipassword");
38
+ pmpro_setOption("twocheckout_accountnumber");
39
+ pmpro_setOption("twocheckout_secretword");
40
+ pmpro_setOption("cybersource_merchantid");
41
+ pmpro_setOption("cybersource_securitykey");
42
+
43
+ //currency
44
+ $currency_paypal = $_POST['currency_paypal'];
45
+ $currency_stripe = $_POST['currency_stripe'];
46
+ $currency_fixed = $_POST['currency_fixed'];
47
+
48
+ if($_POST['gateway'] == "payflowpro")
49
+ pmpro_setOption("currency", $currency_fixed);
50
+ elseif($_POST['gateway'] == "stripe" || $_POST['gateway'] == "authorizenet")
51
+ pmpro_setOption("currency", $currency_stripe);
52
+ else
53
+ pmpro_setOption("currency", $currency_paypal);
54
+
55
+ //credit cards
56
+ $pmpro_accepted_credit_cards = array();
57
+ if(!empty($_REQUEST['creditcards_visa']))
58
+ $pmpro_accepted_credit_cards[] = "Visa";
59
+ if(!empty($_REQUEST['creditcards_mastercard']))
60
+ $pmpro_accepted_credit_cards[] = "Mastercard";
61
+ if(!empty($_REQUEST['creditcards_amex']))
62
+ $pmpro_accepted_credit_cards[] = "American Express";
63
+ if(!empty($_REQUEST['creditcards_discover']))
64
+ $pmpro_accepted_credit_cards[] = "Discover";
65
+ if(!empty($_REQUEST['creditcards_dinersclub']))
66
+ $pmpro_accepted_credit_cards[] = "Diners Club";
67
+ if(!empty($_REQUEST['creditcards_enroute']))
68
+ $pmpro_accepted_credit_cards[] = "EnRoute";
69
+ if(!empty($_REQUEST['creditcards_jcb']))
70
+ $pmpro_accepted_credit_cards[] = "JCB";
71
+
72
+ //check instructions
73
+ pmpro_setOption("instructions");
74
+
75
+ //use_ssl is based on gateway
76
+ if($_REQUEST['gateway'] == "paypal" || $_REQUEST['gateway'] == "authorizenet" || $_REQUEST['gateway'] == "payflowpro")
77
+ pmpro_setOption("use_ssl", 1);
78
+ else
79
+ pmpro_setOption("use_ssl");
80
+
81
+ //tax
82
+ pmpro_setOption("tax_state");
83
+ pmpro_setOption("tax_rate");
84
+
85
+ pmpro_setOption("accepted_credit_cards", implode(",", $pmpro_accepted_credit_cards));
86
+
87
+ //assume success
88
+ $msg = true;
89
+ $msgt = __("Your payment settings have been updated.", "pmpro");
90
+ }
91
+
92
+ $sslseal = pmpro_getOption("sslseal");
93
+ $nuclear_HTTPS = pmpro_getOption("nuclear_HTTPS");
94
+
95
+ $gateway = pmpro_getOption("gateway");
96
+ $gateway_environment = pmpro_getOption("gateway_environment");
97
+ $gateway_email = pmpro_getOption("gateway_email");
98
+ $payflow_partner = pmpro_getOption("payflow_partner");
99
+ $payflow_vendor = pmpro_getOption("payflow_vendor");
100
+ $payflow_user = pmpro_getOption("payflow_user");
101
+ $payflow_pwd = pmpro_getOption("payflow_pwd");
102
+ $apiusername = pmpro_getOption("apiusername");
103
+ $apipassword = pmpro_getOption("apipassword");
104
+ $apisignature = pmpro_getOption("apisignature");
105
+ $loginname = pmpro_getOption("loginname");
106
+ $transactionkey = pmpro_getOption("transactionkey");
107
+ $stripe_secretkey = pmpro_getOption("stripe_secretkey");
108
+ $stripe_publishablekey = pmpro_getOption("stripe_publishablekey");
109
+ $stripe_billingaddress = pmpro_getOption("stripe_billingaddress");
110
+ $braintree_merchantid = pmpro_getOption("braintree_merchantid");
111
+ $braintree_publickey = pmpro_getOption("braintree_publickey");
112
+ $braintree_privatekey = pmpro_getOption("braintree_privatekey");
113
+ $braintree_encryptionkey = pmpro_getOption("braintree_encryptionkey");
114
+ $twocheckout_apiusername = pmpro_getOption("twocheckout_apiusername");
115
+ $twocheckout_apipassword = pmpro_getOption("twocheckout_apipassword");
116
+ $twocheckout_accountnumber = pmpro_getOption("twocheckout_accountnumber");
117
+ $twocheckout_secretword = pmpro_getOption("twocheckout_secretword");
118
+ $cybersource_merchantid = pmpro_getOption("cybersource_merchantid");
119
+ $cybersource_securitykey = pmpro_getOption("cybersource_securitykey");
120
+
121
+ $currency = pmpro_getOption("currency");
122
+
123
+ $pmpro_accepted_credit_cards = pmpro_getOption("accepted_credit_cards");
124
+
125
+ $instructions = pmpro_getOption("instructions");
126
+
127
+ $tax_state = pmpro_getOption("tax_state");
128
+ $tax_rate = pmpro_getOption("tax_rate");
129
+
130
+ //make sure the tax rate is not > 1
131
+ if((double)$tax_rate > 1)
132
+ {
133
+ //assume the entered X%
134
+ $tax_rate = $tax_rate / 100;
135
+ pmpro_setOption("tax_rate", $tax_rate);
136
+ }
137
+
138
+ $use_ssl = pmpro_getOption("use_ssl");
139
+
140
+ //default settings
141
+ if(empty($gateway_environment))
142
+ {
143
+ $gateway_environment = "sandbox";
144
+ pmpro_setOption("gateway_environment", $gateway_environment);
145
+ }
146
+ if(empty($pmpro_accepted_credit_cards))
147
+ {
148
+ $pmpro_accepted_credit_cards = "Visa,Mastercard,American Express,Discover";
149
+ pmpro_setOption("accepted_credit_cards", $pmpro_accepted_credit_cards);
150
+ }
151
+
152
+ $pmpro_accepted_credit_cards = explode(",", $pmpro_accepted_credit_cards);
153
+
154
+ require_once(dirname(__FILE__) . "/admin_header.php");
155
+ ?>
156
+
157
+ <form action="" method="post" enctype="multipart/form-data">
158
+ <h2><?php _e('Payment Gateway', 'pmpro');?> &amp; <?php _e('SSL Settings', 'pmpro');?></h2>
159
+
160
+ <p>Learn more about <a title="Paid Memberships Pro - SSL Settings" target="_blank" href="http://www.paidmembershipspro.com/support/initial-plugin-setup/ssl/">SSL</a> or <a title="Paid Memberships Pro - Payment Gateway Settings" target="_blank" href="http://www.paidmembershipspro.com/support/initial-plugin-setup/payment-gateway/">Payment Gateway Settings</a>.</p>
161
+
162
+ <table class="form-table">
163
+ <tbody>
164
+ <tr>
165
+ <th scope="row" valign="top">
166
+ <label for="gateway"><?php _e('Payment Gateway', 'pmpro');?>:</label>
167
+ </th>
168
+ <td>
169
+ <select id="gateway" name="gateway" onchange="pmpro_changeGateway(jQuery(this).val());">
170
+ <option value="">Testing Only</option>
171
+ <option value="check" <?php selected( $gateway, "check" ); ?>><?php _e('Pay by Check', 'pmpro');?></option>
172
+ <option value="stripe" <?php selected( $gateway, "stripe" ); ?>>Stripe</option>
173
+ <option value="paypalstandard" <?php selected( $gateway, "paypalstandard" ); ?>>PayPal Standard</option>
174
+ <option value="paypalexpress" <?php selected( $gateway, "paypalexpress" ); ?>>PayPal Express</option>
175
+ <option value="paypal" <?php selected( $gateway, "paypal" ); ?>>PayPal Website Payments Pro</option>
176
+ <option value="payflowpro" <?php selected( $gateway, "payflowpro" ); ?>>PayPal Payflow Pro/PayPal Advanced</option>
177
+ <option value="authorizenet" <?php selected( $gateway, "authorizenet" ); ?>>Authorize.net</option>
178
+ <option value="braintree" <?php selected( $gateway, "braintree" ); ?>>Braintree Payments</option>
179
+ <option value="twocheckout" <?php selected( $gateway, "twocheckout" ); ?>>Twocheckout</option>
180
+ <option value="cybersource" <?php selected( $gateway, "cybersource" ); ?>>CyberSource</option>
181
+ </select>
182
+ </td>
183
+ </tr>
184
+ <tr class="gateway gateway_cybersource gateway_twocheckout" <?php if($gateway != "cybersource" && $gateway != "twocheckout") { ?>style="display: none;"<?php } ?>>
185
+ <td colspan="2">
186
+ <strong><?php _e('Note', 'pmpro');?>:</strong> <?php _e('This gateway option is in beta. Some functionality may not be available. Please contact Paid Memberships Pro with any issues you run into. <strong>Please be sure to upgrade Paid Memberships Pro to the latest versions when available.</strong>', 'pmpro');?>
187
+ </td>
188
+ </tr>
189
+ <tr>
190
+ <th scope="row" valign="top">
191
+ <label for="gateway_environment"><?php _e('Gateway Environment', 'pmpro');?>:</label>
192
+ </th>
193
+ <td>
194
+ <select name="gateway_environment">
195
+ <option value="sandbox" <?php selected( $gateway_environment, "sandbox" ); ?>><?php _e('Sandbox/Testing', 'pmpro');?></option>
196
+ <option value="live" <?php selected( $gateway_environment, "live" ); ?>><?php _e('Live/Production', 'pmpro');?></option>
197
+ </select>
198
+ <script>
199
+ function pmpro_changeGateway(gateway)
200
+ {
201
+ //hide all gateway options
202
+ jQuery('tr.gateway').hide();
203
+ jQuery('tr.gateway_'+gateway).show();
204
+ }
205
+ pmpro_changeGateway(jQuery('#gateway').val());
206
+ </script>
207
+ </td>
208
+ </tr>
209
+ <tr class="gateway gateway_payflowpro" <?php if($gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
210
+ <th scope="row" valign="top">
211
+ <label for="payflow_partner"><?php _e('Partner', 'pmpro');?>:</label>
212
+ </th>
213
+ <td>
214
+ <input type="text" id="payflow_partner" name="payflow_partner" size="60" value="<?php echo esc_attr($payflow_partner)?>" />
215
+ </td>
216
+ </tr>
217
+ <tr class="gateway gateway_payflowpro" <?php if($gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
218
+ <th scope="row" valign="top">
219
+ <label for="payflow_vendor"><?php _e('Vendor', 'pmpro');?>:</label>
220
+ </th>
221
+ <td>
222
+ <input type="text" id="payflow_vendor" name="payflow_vendor" size="60" value="<?php echo esc_attr($payflow_vendor)?>" />
223
+ </td>
224
+ </tr>
225
+ <tr class="gateway gateway_payflowpro" <?php if($gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
226
+ <th scope="row" valign="top">
227
+ <label for="payflow_user"><?php _e('User', 'pmpro');?>:</label>
228
+ </th>
229
+ <td>
230
+ <input type="text" id="payflow_user" name="payflow_user" size="60" value="<?php echo esc_attr($payflow_user)?>" />
231
+ </td>
232
+ </tr>
233
+ <tr class="gateway gateway_payflowpro" <?php if($gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
234
+ <th scope="row" valign="top">
235
+ <label for="payflow_pwd"><?php _e('Password', 'pmpro');?>:</label>
236
+ </th>
237
+ <td>
238
+ <input type="password" id="payflow_pwd" name="payflow_pwd" size="60" value="<?php echo esc_attr($payflow_pwd)?>" />
239
+ </td>
240
+ </tr>
241
+ <tr class="gateway gateway_paypal gateway_paypalexpress gateway_paypalstandard" <?php if($gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "paypalstandard") { ?>style="display: none;"<?php } ?>>
242
+ <th scope="row" valign="top">
243
+ <label for="gateway_email"><?php _e('Gateway Account Email', 'pmpro');?>:</label>
244
+ </th>
245
+ <td>
246
+ <input type="text" id="gateway_email" name="gateway_email" size="60" value="<?php echo esc_attr($gateway_email)?>" />
247
+ </td>
248
+ </tr>
249
+ <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
250
+ <th scope="row" valign="top">
251
+ <label for="apiusername"><?php _e('API Username', 'pmpro');?>:</label>
252
+ </th>
253
+ <td>
254
+ <input type="text" id="apiusername" name="apiusername" size="60" value="<?php echo esc_attr($apiusername)?>" />
255
+ </td>
256
+ </tr>
257
+ <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
258
+ <th scope="row" valign="top">
259
+ <label for="apipassword"><?php _e('API Password', 'pmpro');?>:</label>
260
+ </th>
261
+ <td>
262
+ <input type="text" id="apipassword" name="apipassword" size="60" value="<?php echo esc_attr($apipassword)?>" />
263
+ </td>
264
+ </tr>
265
+ <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>>
266
+ <th scope="row" valign="top">
267
+ <label for="apisignature"><?php _e('API Signature', 'pmpro');?>:</label>
268
+ </th>
269
+ <td>
270
+ <input type="text" id="apisignature" name="apisignature" size="60" value="<?php echo esc_attr($apisignature)?>" />
271
+ </td>
272
+ </tr>
273
+
274
+ <tr class="gateway gateway_authorizenet" <?php if($gateway != "authorizenet") { ?>style="display: none;"<?php } ?>>
275
+ <th scope="row" valign="top">
276
+ <label for="loginname"><?php _e('Login Name', 'pmpro');?>:</label>
277
+ </th>
278
+ <td>
279
+ <input type="text" id="loginname" name="loginname" size="60" value="<?php echo esc_attr($loginname)?>" />
280
+ </td>
281
+ </tr>
282
+ <tr class="gateway gateway_authorizenet" <?php if($gateway != "authorizenet") { ?>style="display: none;"<?php } ?>>
283
+ <th scope="row" valign="top">
284
+ <label for="transactionkey"><?php _e('Transaction Key', 'pmpro');?>:</label>
285
+ </th>
286
+ <td>
287
+ <input type="text" id="transactionkey" name="transactionkey" size="60" value="<?php echo esc_attr($transactionkey)?>" />
288
+ </td>
289
+ </tr>
290
+
291
+ <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
292
+ <th scope="row" valign="top">
293
+ <label for="stripe_secretkey"><?php _e('Secret Key', 'pmpro');?>:</label>
294
+ </th>
295
+ <td>
296
+ <input type="text" id="stripe_secretkey" name="stripe_secretkey" size="60" value="<?php echo esc_attr($stripe_secretkey)?>" />
297
+ </td>
298
+ </tr>
299
+ <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
300
+ <th scope="row" valign="top">
301
+ <label for="stripe_publishablekey"><?php _e('Publishable Key', 'pmpro');?>:</label>
302
+ </th>
303
+ <td>
304
+ <input type="text" id="stripe_publishablekey" name="stripe_publishablekey" size="60" value="<?php echo esc_attr($stripe_publishablekey)?>" />
305
+ </td>
306
+ </tr>
307
+
308
+ <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
309
+ <th scope="row" valign="top">
310
+ <label for="braintree_merchantid"><?php _e('Merchant ID', 'pmpro');?>:</label>
311
+ </th>
312
+ <td>
313
+ <input type="text" id="braintree_merchantid" name="braintree_merchantid" size="60" value="<?php echo esc_attr($braintree_merchantid)?>" />
314
+ </td>
315
+ </tr>
316
+ <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
317
+ <th scope="row" valign="top">
318
+ <label for="braintree_publickey"><?php _e('Public Key', 'pmpro');?>:</label>
319
+ </th>
320
+ <td>
321
+ <input type="text" id="braintree_publickey" name="braintree_publickey" size="60" value="<?php echo esc_attr($braintree_publickey)?>" />
322
+ </td>
323
+ </tr>
324
+ <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
325
+ <th scope="row" valign="top">
326
+ <label for="braintree_privatekey"><?php _e('Private Key', 'pmpro');?>:</label>
327
+ </th>
328
+ <td>
329
+ <input type="text" id="braintree_privatekey" name="braintree_privatekey" size="60" value="<?php echo esc_attr($braintree_privatekey)?>" />
330
+ </td>
331
+ </tr>
332
+ <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
333
+ <th scope="row" valign="top">
334
+ <label for="braintree_encryptionkey"><?php _e('Client-Side Encryption Key', 'pmpro');?>:</label>
335
+ </th>
336
+ <td>
337
+ <textarea id="braintree_encryptionkey" name="braintree_encryptionkey" rows="3" cols="80"><?php echo esc_textarea($braintree_encryptionkey)?></textarea>
338
+ </td>
339
+ </tr>
340
+
341
+ <tr class="gateway gateway_twocheckout" <?php if($gateway != "twocheckout") { ?>style="display: none;"<?php } ?>>
342
+ <th scope="row" valign="top">
343
+ <label for="twocheckout_apiusername"><?php _e('API Username', 'pmpro');?>:</label>
344
+ </th>
345
+ <td>
346
+ <input type="text" id="twocheckout_apiusername" name="twocheckout_apiusername" size="60" value="<?php echo esc_attr($twocheckout_apiusername)?>" />
347
+ </td>
348
+ </tr>
349
+ <tr class="gateway gateway_twocheckout" <?php if($gateway != "twocheckout") { ?>style="display: none;"<?php } ?>>
350
+ <th scope="row" valign="top">
351
+ <label for="twocheckout_apipassword"><?php _e('API Password', 'pmpro');?>:</label>
352
+ </th>
353
+ <td>
354
+ <input type="text" id="twocheckout_apipassword" name="twocheckout_apipassword" size="60" value="<?php echo esc_attr($twocheckout_apipassword)?>" />
355
+ </td>
356
+ </tr>
357
+ <tr class="gateway gateway_twocheckout" <?php if($gateway != "twocheckout") { ?>style="display: none;"<?php } ?>>
358
+ <th scope="row" valign="top">
359
+ <label for="twocheckout_accountnumber"><?php _e('Account Number', 'pmpro');?>:</label>
360
+ </th>
361
+ <td>
362
+ <input type="text" name="twocheckout_accountnumber" size="60" value="<?php echo $twocheckout_accountnumber?>" />
363
+ </td>
364
+ </tr>
365
+ <tr class="gateway gateway_twocheckout" <?php if($gateway != "twocheckout") { ?>style="display: none;"<?php } ?>>
366
+ <th scope="row" valign="top">
367
+ <label for="twocheckout_secretword"><?php _e('Secret Word', 'pmpro');?>:</label>
368
+ </th>
369
+ <td>
370
+ <input type="text" name="twocheckout_secretword" size="60" value="<?php echo $twocheckout_secretword?>" />
371
+ </td>
372
+ </tr>
373
+
374
+ <tr class="gateway gateway_cybersource" <?php if($gateway != "cybersource") { ?>style="display: none;"<?php } ?>>
375
+ <th scope="row" valign="top">
376
+ <label for="cybersource_merchantid"><?php _e('Merchant ID', 'pmpro');?>:</label>
377
+ </th>
378
+ <td>
379
+ <input type="text" id="cybersource_merchantid" name="cybersource_merchantid" size="60" value="<?php echo esc_attr($cybersource_merchantid)?>" />
380
+ </td>
381
+ </tr>
382
+ <tr class="gateway gateway_cybersource" <?php if($gateway != "cybersource") { ?>style="display: none;"<?php } ?>>
383
+ <th scope="row" valign="top">
384
+ <label for="cybersource_securitykey"><?php _e('Transaction Security Key', 'pmpro');?>:</label>
385
+ </th>
386
+ <td>
387
+ <textarea id="cybersource_securitykey" name="cybersource_securitykey" rows="3" cols="80"><?php echo esc_textarea($cybersource_securitykey);?></textarea>
388
+ </td>
389
+ </tr>
390
+
391
+ <tr class="gateway gateway_payflowpro" <?php if($gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
392
+ <th scope="row" valign="top">
393
+ <label for="transactionkey"><?php _e('Currency', 'pmpro');?>:</label>
394
+ </th>
395
+ <td>
396
+ <input type="hidden" name="currency_fixed" size="60" value="USD" />
397
+ USD
398
+ </td>
399
+ </tr>
400
+
401
+ <tr class="gateway gateway_stripe gateway_authorizenet" <?php if($gateway != "stripe" && $gateway != "authorizenet") { ?>style="display: none;"<?php } ?>>
402
+ <th scope="row" valign="top">
403
+ <label for="transactionkey"><?php _e('Currency', 'pmpro');?>:</label>
404
+ </th>
405
+ <td>
406
+ <select name="currency_stripe">
407
+ <?php
408
+ global $pmpro_stripe_currencies;
409
+ foreach($pmpro_stripe_currencies as $ccode => $cdescription)
410
+ {
411
+ ?>
412
+ <option value="<?php echo $ccode?>" <?php if($currency == $ccode) { ?>selected="selected"<?php } ?>><?php echo $cdescription?></option>
413
+ <?php
414
+ }
415
+ ?>
416
+ </select>
417
+ </td>
418
+ </tr>
419
+
420
+ <tr class="gateway gateway_ gateway_paypal gateway_paypalexpress gateway_paypalstandard gateway_braintree gateway_twocheckout gateway_cybersource" <?php if(!empty($gateway) && $gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "paypalstandard" && $gateway != "braintree" && $gateway != "twocheckout" && $gateway != "cybersource") { ?>style="display: none;"<?php } ?>>
421
+ <th scope="row" valign="top">
422
+ <label for="currency_paypal"><?php _e('Currency', 'pmpro');?>:</label>
423
+ </th>
424
+ <td>
425
+ <select name="currency_paypal">
426
+ <?php
427
+ global $pmpro_currencies;
428
+ foreach($pmpro_currencies as $ccode => $cdescription)
429
+ {
430
+ ?>
431
+ <option value="<?php echo $ccode?>" <?php if($currency == $ccode) { ?>selected="selected"<?php } ?>><?php echo $cdescription?></option>
432
+ <?php
433
+ }
434
+ ?>
435
+ </select>
436
+ </td>
437
+ </tr>
438
+
439
+ <tr class="gateway gateway_ gateway_stripe gateway_authorizenet gateway_paypal gateway_payflowpro gateway_braintree gateway_twocheckout gateway_cybersource" <?php if(!empty($gateway) && $gateway != "authorizenet" && $gateway != "paypal" && $gateway != "stripe" && $gateway != "payflowpro" && $gateway != "braintree" && $gateway != "twocheckout" && $gateway != "cybersource") { ?>style="display: none;"<?php } ?>>
440
+ <th scope="row" valign="top">
441
+ <label for="creditcards"><?php _e('Accepted Credit Card Types', 'pmpro');?></label>
442
+ </th>
443
+ <td>
444
+ <input type="checkbox" name="creditcards_visa" value="1" <?php if(in_array("Visa", $pmpro_accepted_credit_cards)) { ?>checked="checked"<?php } ?> /> Visa<br />
445
+ <input type="checkbox" name="creditcards_mastercard" value="1" <?php if(in_array("Mastercard", $pmpro_accepted_credit_cards)) { ?>checked="checked"<?php } ?> /> Mastercard<br />
446
+ <input type="checkbox" name="creditcards_amex" value="1" <?php if(in_array("American Express", $pmpro_accepted_credit_cards)) { ?>checked="checked"<?php } ?> /> American Express<br />
447
+ <input type="checkbox" name="creditcards_discover" value="1" <?php if(in_array("Discover", $pmpro_accepted_credit_cards)) { ?>checked="checked"<?php } ?> /> Discover<br />
448
+ <input type="checkbox" name="creditcards_dinersclub" value="1" <?php if(in_array("Diners Club", $pmpro_accepted_credit_cards)) {?>checked="checked"<?php } ?> /> Diner's Club<br />
449
+ <input type="checkbox" name="creditcards_enroute" value="1" <?php if(in_array("EnRoute", $pmpro_accepted_credit_cards)) {?>checked="checked"<?php } ?> /> EnRoute<br />
450
+ <input type="checkbox" name="creditcards_jcb" value="1" <?php if(in_array("JCB", $pmpro_accepted_credit_cards)) {?>checked="checked"<?php } ?> /> JCB<br />
451
+ </td>
452
+ </tr>
453
+ <tr class="gateway gateway_check" <?php if($gateway != "check") { ?>style="display: none;"<?php } ?>>
454
+ <th scope="row" valign="top">
455
+ <label for="instructions"><?php _e('Instructions', 'pmpro');?></label>
456
+ </th>
457
+ <td>
458
+ <textarea id="instructions" name="instructions" rows="3" cols="80"><?php echo esc_textarea($instructions)?></textarea>
459
+ <p><small><?php _e('Who to write the check out to. Where to mail it. Shown on checkout, confirmation, and invoice pages.', 'pmpro');?></small></p>
460
+ </td>
461
+ </tr>
462
+
463
+ <tr class="gateway gateway_stripe" <?php if(!empty($gateway) && $gateway != "stripe") { ?>style="display: none;"<?php } ?>>
464
+ <th scope="row" valign="top">
465
+ <label for="stripe_billingaddress"><?php _e('Show Billing Address Fields', 'pmpro');?>:</label>
466
+ </th>
467
+ <td>
468
+ <select id="stripe_billingaddress" name="stripe_billingaddress">
469
+ <option value="0" <?php if(empty($stripe_billingaddress)) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
470
+ <option value="1" <?php if(!empty($stripe_billingaddress)) { ?>selected="selected"<?php } ?>><?php _e('Yes', 'pmpro');?></option>
471
+ </select>
472
+ <small><?php _e("Stripe doesn't require billing address fields. Choose 'No' to hide them on the checkout page.", 'pmpro');?></small>
473
+ </td>
474
+ </tr>
475
+
476
+ <tr class="gateway gateway_ gateway_stripe gateway_authorizenet gateway_paypal gateway_paypalexpress gateway_check gateway_paypalstandard gateway_payflowpro gateway_braintree gateway_twocheckout gateway_cybersource" <?php if(!empty($gateway) && $gateway != "stripe" && $gateway != "authorizenet" && $gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "check" && $gateway != "paypalstandard" && $gateway != "payflowpro" && $gateway != "braintree" && $gateway != "twocheckout" && $gateway != "cybersource") { ?>style="display: none;"<?php } ?>>
477
+ <th scope="row" valign="top">
478
+ <label for="tax"><?php _e('Sales Tax', 'pmpro');?> <small>(<?php _e('optional', 'pmpro');?>)</small></label>
479
+ </th>
480
+ <td>
481
+ <?php _e('Tax State', 'pmpro');?>:
482
+ <input type="text" id="tax_state" name="tax_state" size="4" value="<?php echo esc_attr($tax_state)?>" /> <small>(<?php _e('abbreviation, e.g. "PA"', 'pmpro');?>)</small>
483
+ &nbsp; Tax Rate:
484
+ <input type="text" id="tax_rate" name="tax_rate" size="10" value="<?php echo esc_attr($tax_rate)?>" /> <small>(<?php _e('decimal, e.g. "0.06"', 'pmpro');?>)</small>
485
+ <p><small><?php _e('If values are given, tax will be applied for any members ordering from the selected state. For more complex tax rules, use the "pmpro_tax" filter.', 'pmpro');?></small></p>
486
+ </td>
487
+ </tr>
488
+ <tr class="gateway gateway_ gateway_stripe gateway_paypalexpress gateway_check gateway_paypalstandard gateway_braintree gateway_twocheckout gateway_cybersource" <?php if(!empty($gateway) && $gateway != "stripe" && $gateway != "paypalexpress" && $gateway != "check" && $gateway != "paypalstandard" && $gateway != "braintree" && $gateway != "twocheckout" && $gateway != "cybersource") { ?>style="display: none;"<?php } ?>>
489
+ <th scope="row" valign="top">
490
+ <label for="use_ssl"><?php _e('Use SSL', 'pmpro');?>:</label>
491
+ </th>
492
+ <td>
493
+ <select id="use_ssl" name="use_ssl">
494
+ <option value="0" <?php if(empty($use_ssl)) { ?>selected="selected"<?php } ?>><?php _e('No', 'pmpro');?></option>
495
+ <option value="1" <?php if(!empty($use_ssl)) { ?>selected="selected"<?php } ?>><?php _e('Yes', 'pmpro');?></option>
496
+ </select>
497
+ </td>
498
+ </tr>
499
+ <tr class="gateway gateway_paypal gateway_authorizenet gateway_payflowpro" <?php if($gateway != "paypal" && $gateway != "authorizenet" && $gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
500
+ <th scope="row" valign="top">
501
+ <label for="use_ssl"><?php _e('Use SSL', 'pmpro');?>:</label>
502
+ </th>
503
+ <td>
504
+ <?php _e('Yes', 'pmpro');?>.
505
+ (<?php _e('Required by this Gateway Option', 'pmpro');?>)
506
+ </td>
507
+ </tr>
508
+ <tr>
509
+ <th scope="row" valign="top">
510
+ <label for="sslseal"><?php _e('SSL Seal Code', 'pmpro');?>:</label>
511
+ </th>
512
+ <td>
513
+ <textarea id="sslseal" name="sslseal" rows="3" cols="80"><?php echo esc_textarea($sslseal)?></textarea>
514
+ </td>
515
+ </tr>
516
+ <tr>
517
+ <th scope="row" valign="top">
518
+ <label for="nuclear_HTTPS"><?php _e('HTTPS Nuclear Option', 'pmpro');?>:</label>
519
+ </th>
520
+ <td>
521
+ <input type="checkbox" id="nuclear_HTTPS" name="nuclear_HTTPS" value="1" <?php if(!empty($nuclear_HTTPS)) { ?>checked="checked"<?php } ?> /> <?php _e('Use the "Nuclear Option" to use secure (HTTPS) URLs on your secure pages. Check this if you are using SSL and have warnings on your checkout pages.', 'pmpro');?>
522
+ </td>
523
+ </tr>
524
+ <tr class="gateway gateway_paypal gateway_paypalexpress gateway_paypalstandard gateway_payflowpro" <?php if($gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "paypalstandard" && $gateway != "payflowpro") { ?>style="display: none;"<?php } ?>>
525
+ <th scope="row" valign="top">
526
+ <label><?php _e('IPN Handler URL', 'pmpro');?>:</label>
527
+ </th>
528
+ <td>
529
+ <p><?php _e('To fully integrate with PayPal, be sure to set your IPN Handler URL to ', 'pmpro');?> <pre><?php echo admin_url("admin-ajax.php") . "?action=ipnhandler";?></pre>.</p>
530
+ </td>
531
+ </tr>
532
+ <tr class="gateway gateway_paypal gateway_twocheckout" <?php if($gateway != "twocheckout") { ?>style="display: none;"<?php } ?>>
533
+ <th scope="row" valign="top">
534
+ <label><?php _e('TwoCheckout INS URL', 'pmpro');?>:</label>
535
+ </th>
536
+ <td>
537
+ <p><?php _e('To fully integrate with TwoCheckout, be sure to set your TwoCheckout INS URL ', 'pmpro');?> <pre><?php echo admin_url("admin-ajax.php") . "?action=twocheckout-ins";?></pre>.</p>
538
+ </td>
539
+ </tr>
540
+ <tr class="gateway gateway_authorizenet" <?php if($gateway != "authorizenet") { ?>style="display: none;"<?php } ?>>
541
+ <th scope="row" valign="top">
542
+ <label><?php _e('Silent Post URL', 'pmpro');?>:</label>
543
+ </th>
544
+ <td>
545
+ <p><?php _e('To fully integrate with Authorize.net, be sure to set your Silent Post URL to', 'pmpro');?> <pre><?php echo admin_url("admin-ajax.php") . "?action=authnet_silent_post";?></pre>.</p>
546
+ </td>
547
+ </tr>
548
+ <tr class="gateway gateway_stripe" <?php if($gateway != "stripe") { ?>style="display: none;"<?php } ?>>
549
+ <th scope="row" valign="top">
550
+ <label><?php _e('Web Hook URL', 'pmpro');?>:</label>
551
+ </th>
552
+ <td>
553
+ <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>
554
+ </td>
555
+ </tr>
556
+ <tr class="gateway gateway_braintree" <?php if($gateway != "braintree") { ?>style="display: none;"<?php } ?>>
557
+ <th scope="row" valign="top">
558
+ <label><?php _e('Web Hook URL', 'pmpro');?>:</label>
559
+ </th>
560
+ <td>
561
+ <p>
562
+ <?php _e('To fully integrate with Braintree, be sure to set your Web Hook URL to', 'pmpro');?>
563
+ <pre><?php
564
+ //echo admin_url("admin-ajax.php") . "?action=braintree_webhook";
565
+ echo PMPRO_URL . "/services/braintree-webhook.php";
566
+ ?></pre>.
567
+ </p>
568
+ </td>
569
+ </tr>
570
+ </tbody>
571
+ </table>
572
+ <p class="submit">
573
+ <input name="savesettings" type="submit" class="button-primary" value="<?php _e('Save Settings', 'pmpro');?>" />
574
+ </p>
575
+ </form>
576
+
577
+ <?php
578
+ require_once(dirname(__FILE__) . "/admin_footer.php");
579
+ ?>
adminpages/reports.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ global $pmpro_reports;
3
+
4
+ require_once(dirname(__FILE__) . "/admin_header.php");
5
+
6
+ //default view, report widgets
7
+ if(empty($_REQUEST['report']))
8
+ {
9
+ //wrapper
10
+ ?>
11
+ <div id="dashboard-widgets-wrap">
12
+ <div id="dashboard-widgets" class="metabox-holder pmpro_reports-holder columns-2">
13
+ <div id="postbox-container-1" class="postbox-container">
14
+ <div id="normal-sortables" class="meta-box-sortables ui-sortable">
15
+ <?php
16
+
17
+ //report widgets
18
+ $count = 0;
19
+ $nreports = count($pmpro_reports);
20
+ $split = false;
21
+ foreach($pmpro_reports as $report => $title)
22
+ {
23
+ //put half of the report widgets in postbox-container-2
24
+ if(!$split && $count++ > $nreports/2)
25
+ {
26
+ $split = true;
27
+ ?>
28
+ </div></div><div id="postbox-container-2" class="postbox-container"><div id="side-sortables" class="meta-box-sortables ui-sortable">
29
+ <?php
30
+ }
31
+ ?>
32
+ <div id="pmpro_report_<?php echo $report; ?>" class="postbox pmpro_clickable" onclick="location.href='<?php echo admin_url("admin.php?page=pmpro-reports&report=" . $report);?>';">
33
+ <h3 class="hndle"><span><?php echo $title; ?></span></h3>
34
+ <div class="inside">
35
+ <?php call_user_func("pmpro_report_" . $report . "_widget"); ?>
36
+ <div style="margin-top:10px;border-top: 1px solid #ddd; padding-top: 10px; text-align:center;">
37
+ <a class="button button-primary" href="<?php echo admin_url("admin.php?page=pmpro-reports&report=" . $report);?>"><?php _e('Details', 'pmpro');?></a>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <?php
42
+ }
43
+
44
+ //end wrapper
45
+ ?>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ <?php
50
+ }
51
+ else
52
+ {
53
+ //view a single report
54
+ $report = $_REQUEST['report'];
55
+ call_user_func("pmpro_report_" . $report . "_page");
56
+ }
57
+
58
+ require_once(dirname(__FILE__) . "/admin_footer.php");
59
+ ?>
adminpages/reports/login.php ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ PMPro Report
4
+ Title: Logins
5
+ Slug: login
6
+
7
+ For each report, add a line like:
8
+ global $pmpro_reports;
9
+ $pmpro_reports['slug'] = 'Title';
10
+
11
+ For each report, also write two functions:
12
+ * pmpro_report_{slug}_widget() to show up on the report homepage.
13
+ * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
+ */
15
+ global $pmpro_reports;
16
+ $pmpro_reports['login'] = __('Visits, Views, and Logins', 'pmpro');
17
+
18
+ function pmpro_report_login_widget()
19
+ {
20
+ global $wpdb;
21
+ $visits = get_option("pmpro_visits", array("today"=>0, "thisday"=>date("Y-m-d"), "alltime"=>0, "month"=>0, "thismonth"=>date("n")));
22
+ $views = get_option("pmpro_views", array("today"=>0, "thisday"=>date("Y-m-d"), "alltime"=>0, "month"=>0, "thismonth"=>date("n")));
23
+ $logins = get_option("pmpro_logins", array("today"=>0, "thisday"=>date("Y-m-d"), "alltime"=>0, "month"=>0, "thismonth"=>date("n")));
24
+ ?>
25
+ <div style="width: 33%; float: left;">
26
+ <p><?php _e('Visits Today', 'pmpro')?>: <?php echo $visits['today'];?></p>
27
+ <p><?php _e('Visits This Month', 'pmpro')?>: <?php echo $visits['month'];?></p>
28
+ <p><?php _e('Visits All Time', 'pmpro')?>: <?php echo $visits['alltime'];?></p>
29
+ </div>
30
+ <div style="width: 33%; float: left;">
31
+ <p><?php _e('Views Today', 'pmpro')?>: <?php echo $views['today'];?></p>
32
+ <p><?php _e('Views This Month', 'pmpro')?>: <?php echo $views['month'];?></p>
33
+ <p><?php _e('Views All Time', 'pmpro')?>: <?php echo $views['alltime'];?></p>
34
+ </div>
35
+ <div style="width: 33%; float: left;">
36
+ <p><?php _e('Logins Today', 'pmpro')?>: <?php echo $logins['today'];?></p>
37
+ <p><?php _e('Logins This Month', 'pmpro')?>: <?php echo $logins['month'];?></p>
38
+ <p><?php _e('Logins All Time', 'pmpro')?>: <?php echo $logins['alltime'];?></p>
39
+ </div>
40
+ <div class="clear"></div>
41
+ <?php
42
+ }
43
+
44
+ function pmpro_report_login_page()
45
+ {
46
+ global $wpdb;
47
+
48
+ //vars
49
+ if(!empty($_REQUEST['s']))
50
+ $s = $_REQUEST['s'];
51
+ else
52
+ $s = "";
53
+
54
+ if(!empty($_REQUEST['l']))
55
+ $l = $_REQUEST['l'];
56
+ else
57
+ $l = "";
58
+ ?>
59
+ <form id="posts-filter" method="get" action="">
60
+ <h2>
61
+ <?php _e('Visits, Views, and Logins Report', 'pmpro');?>
62
+ </h2>
63
+ <ul class="subsubsub">
64
+ <li>
65
+ <?php _ex('Show', 'Dropdown label, e.g. Show All Users', 'pmpro')?> <select name="l" onchange="jQuery('#posts-filter').submit();">
66
+ <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Users', 'pmpro')?></option>
67
+ <option value="all" <?php if($l == "all") { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro')?></option>
68
+ <?php
69
+ $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
70
+ foreach($levels as $level)
71
+ {
72
+ ?>
73
+ <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
74
+ <?php
75
+ }
76
+ ?>
77
+ </select>
78
+ </li>
79
+ </ul>
80
+ <p class="search-box">
81
+ <label class="hidden" for="post-search-input"><?php _ex('Search', 'Search form label', 'pmpro')?> <?php if(empty($l)) echo "Users"; else echo "Members";?>:</label>
82
+ <input type="hidden" name="page" value="pmpro-reports" />
83
+ <input type="hidden" name="report" value="login" />
84
+ <input id="post-search-input" type="text" value="<?php echo $s?>" name="s"/>
85
+ <input class="button" type="submit" value="Search Members"/>
86
+ </p>
87
+ <?php
88
+ //some vars for the search
89
+ if(isset($_REQUEST['pn']))
90
+ $pn = $_REQUEST['pn'];
91
+ else
92
+ $pn = 1;
93
+
94
+ if(isset($_REQUEST['limit']))
95
+ $limit = $_REQUEST['limit'];
96
+ else
97
+ $limit = 15;
98
+
99
+ $end = $pn * $limit;
100
+ $start = $end - $limit;
101
+
102
+ if($s)
103
+ {
104
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS u.ID, u.user_login, u.user_email, UNIX_TIMESTAMP(u.user_registered) as joindate, mu.membership_id, mu.initial_payment, mu.billing_amount, mu.cycle_period, mu.cycle_number, mu.billing_limit, mu.trial_amount, mu.trial_limit, UNIX_TIMESTAMP(mu.startdate) as startdate, UNIX_TIMESTAMP(mu.enddate) as enddate, m.name as membership FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id AND mu.status = 'active' LEFT JOIN $wpdb->pmpro_membership_levels m ON mu.membership_id = m.id WHERE (u.user_login LIKE '%$s%' OR u.user_email LIKE '%$s%' OR um.meta_value LIKE '%$s%') ";
105
+
106
+ if($l == "all")
107
+ $sqlQuery .= " AND mu.status = 'active' AND mu.membership_id > 0 ";
108
+ elseif($l)
109
+ $sqlQuery .= " AND mu.membership_id = '" . $l . "' ";
110
+
111
+ $sqlQuery .= "GROUP BY u.ID ORDER BY user_registered DESC LIMIT $start, $limit";
112
+ }
113
+ else
114
+ {
115
+ $sqlQuery = "SELECT SQL_CALC_FOUND_ROWS u.ID, u.user_login, u.user_email, UNIX_TIMESTAMP(u.user_registered) as joindate, mu.membership_id, mu.initial_payment, mu.billing_amount, mu.cycle_period, mu.cycle_number, mu.billing_limit, mu.trial_amount, mu.trial_limit, UNIX_TIMESTAMP(mu.startdate) as startdate, UNIX_TIMESTAMP(mu.enddate) as enddate, m.name as membership FROM $wpdb->users u LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id AND mu.status = 'active' LEFT JOIN $wpdb->pmpro_membership_levels m ON mu.membership_id = m.id";
116
+ $sqlQuery .= " WHERE 1=1 ";
117
+
118
+ if($l == "all")
119
+ $sqlQuery .= " AND mu.membership_id > 0 AND mu.status = 'active' ";
120
+ elseif($l)
121
+ $sqlQuery .= " AND mu.membership_id = '" . $l . "' ";
122
+ $sqlQuery .= "GROUP BY u.ID ORDER BY user_registered DESC LIMIT $start, $limit";
123
+ }
124
+
125
+ $sqlQuery = apply_filters("pmpro_members_list_sql", $sqlQuery);
126
+
127
+ $theusers = $wpdb->get_results($sqlQuery);
128
+ $totalrows = $wpdb->get_var("SELECT FOUND_ROWS() as found_rows");
129
+
130
+ if($theusers)
131
+ {
132
+ ?>
133
+ <p class="clear"><?php echo strval($totalrows)?> <?php if(empty($l)) echo "users"; else echo "members";?> found.
134
+ <?php
135
+ }
136
+ ?>
137
+ <table class="widefat">
138
+ <thead>
139
+ <tr class="thead">
140
+ <th><?php _e('ID', 'pmpro')?></th>
141
+ <th><?php _e('User', 'pmpro')?></th>
142
+ <th><?php _e('Name', 'pmpro')?></th>
143
+ <th><?php _e('Membership', 'pmpro')?></th>
144
+ <th><?php _e('Joined', 'pmpro')?></th>
145
+ <th><?php _e('Expires', 'pmpro')?></th>
146
+ <th><?php _e('Last Visit', 'pmpro')?></th>
147
+ <th><?php _e('Visits This Month', 'pmpro')?></th>
148
+ <th><?php _e('Total Visits', 'pmpro')?></th>
149
+ <th><?php _e('Views This Month', 'pmpro')?></th>
150
+ <th><?php _e('Total Views', 'pmpro')?></th>
151
+ <th><?php _e('Last Login', 'pmpro')?></th>
152
+ <th><?php _e('Logins This Month', 'pmpro')?></th>
153
+ <th><?php _e('Total Logins', 'pmpro')?></th>
154
+ </tr>
155
+ </thead>
156
+ <tbody id="users" class="list:user user-list">
157
+ <?php
158
+ $count = 0;
159
+ foreach($theusers as $auser)
160
+ {
161
+ //get meta
162
+ $theuser = get_userdata($auser->ID);
163
+ $visits = get_user_meta($auser->ID, "pmpro_visits", true);
164
+ $views = get_user_meta($auser->ID, "pmpro_views", true);
165
+ $logins = get_user_meta($auser->ID, "pmpro_logins", true);
166
+ if(empty($logins))
167
+ $logins = array("last"=>"N/A", "month"=>"N/A", "alltime"=>"N/A");
168
+ ?>
169
+ <tr <?php if($count++ % 2 == 0) { ?>class="alternate"<?php } ?>>
170
+ <td><?php echo $theuser->ID?></td>
171
+ <td>
172
+ <?php echo get_avatar($theuser->ID, 32)?>
173
+ <strong>
174
+ <?php
175
+ $userlink = '<a href="user-edit.php?user_id=' . $theuser->ID . '">' . $theuser->user_login . '</a>';
176
+ $userlink = apply_filters("pmpro_members_list_user_link", $userlink, $theuser);
177
+ echo $userlink;
178
+ ?>
179
+ </strong>
180
+ </td>
181
+ <td>
182
+ <?php echo $theuser->display_name;?>
183
+ </td>
184
+ <td><?php echo $auser->membership?></td>
185
+ <td><?php echo date("m/d/Y", strtotime($theuser->user_registered))?></td>
186
+ <td>
187
+ <?php
188
+ if($auser->enddate)
189
+ echo date(get_option('date_format'), $auser->enddate);
190
+ else
191
+ echo "Never";
192
+ ?>
193
+ </td>
194
+ <td><?php if(!empty($visits['last'])) echo $visits['last'];?></td>
195
+ <td><?php if(!empty($visits['month'])) echo $visits['month'];?></td>
196
+ <td><?php if(!empty($visits['alltime'])) echo $visits['alltime'];?></td>
197
+ <td><?php if(!empty($visits['month'])) echo $views['month'];?></td>
198
+ <td><?php if(!empty($visits['alltime'])) echo $views['alltime'];?></td>
199
+ <td><?php if(!empty($visits['last'])) echo $logins['last'];?></td>
200
+ <td><?php if(!empty($visits['month'])) echo $logins['month'];?></td>
201
+ <td><?php if(!empty($visits['alltime'])) echo $logins['alltime'];?></td>
202
+ </tr>
203
+ <?php
204
+ }
205
+
206
+ if(!$theusers)
207
+ {
208
+ ?>
209
+ <tr>
210
+ <td colspan="9"><p><?php _e('No members found.', 'pmpro')?> <?php if($l) { ?><a href="?page=pmpro-memberslist&s=<?php echo $s?>"><?php _e('Search all levels', 'pmpro')?></a>.<?php } ?></p></td>
211
+ </tr>
212
+ <?php
213
+ }
214
+ ?>
215
+ </tbody>
216
+ </table>
217
+ </form>
218
+
219
+ <?php
220
+ echo pmpro_getPaginationString($pn, $totalrows, $limit, 1, get_admin_url(NULL, "/admin.php?page=pmpro-reports&report=login&s=" . urlencode($s)), "&l=$l&limit=$limit&pn=");
221
+ ?>
222
+ <?php
223
+ }
224
+
225
+ /*
226
+ Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
227
+ */
228
+
229
+ //track visits
230
+ function pmpro_report_login_wp_visits()
231
+ {
232
+ //don't track admin
233
+ if(is_admin())
234
+ return;
235
+
236
+ //only track logged in users
237
+ if(!is_user_logged_in())
238
+ return;
239
+
240
+ //check for cookie
241
+ if(!empty($_COOKIE['pmpro_visit']))
242
+ return;
243
+
244
+ //set cookie, then track
245
+ setcookie("pmpro_visit", "1", NULL, COOKIEPATH, COOKIE_DOMAIN, false);
246
+
247
+ global $current_user;
248
+ //track for user
249
+ if(!empty($current_user->ID))
250
+ {
251
+ $visits = $current_user->pmpro_visits;
252
+ if(empty($visits))
253
+ $visits = array("last"=>"N/A", "month"=>0, "alltime"=>0);
254
+
255
+ //track logins for user
256
+ $visits['last'] = date(get_option("date_format"));
257
+ $visits['alltime']++;
258
+ $thismonth = date("n");
259
+ if($thismonth == $visits['thismonth'])
260
+ $visits['month']++;
261
+ else
262
+ {
263
+ $visits['month'] = 1;
264
+ $visits['thismonth'] = $thismonth;
265
+ }
266
+
267
+ //update user data
268
+ update_user_meta($current_user->ID, "pmpro_visits", $visits);
269
+ }
270
+
271
+ //track for all
272
+ $visits = get_option("pmpro_visits");
273
+ if(empty($visits))
274
+ $visits = array("today"=>0, "month"=>0, "alltime"=>0);
275
+
276
+ $visits['alltime']++;
277
+ $thisdate = date("Y-d-m");
278
+ if($thisdate == $visits['thisdate'])
279
+ $visits['today']++;
280
+ else
281
+ {
282
+ $visits['today'] = 1;
283
+ $visits['thisdate'] = $thisdate;
284
+ }
285
+ if($thismonth == $visits['thismonth'])
286
+ $visits['month']++;
287
+ else
288
+ {
289
+ $visits['month'] = 1;
290
+ $visits['thismonth'] = $thismonth;
291
+ }
292
+
293
+ update_option("pmpro_visits", $visits);
294
+ }
295
+ add_action("wp", "pmpro_report_login_wp_visits");
296
+
297
+ //we want to clear the pmpro_visit cookie on login/logout
298
+ function pmpro_report_login_clear_visit_cookie()
299
+ {
300
+ if(isset($_COOKIE['pmpro_visit']))
301
+ unset($_COOKIE['pmpro_visit']);
302
+ }
303
+ add_action("wp_login", "pmpro_report_login_clear_visit_cookie");
304
+ add_action("wp_logout", "pmpro_report_login_clear_visit_cookie");
305
+
306
+ //track views
307
+ function pmpro_report_login_wp_views()
308
+ {
309
+ //don't track admin
310
+ if(is_admin())
311
+ return;
312
+
313
+ global $current_user;
314
+ //track for user
315
+ if(!empty($current_user->ID))
316
+ {
317
+ $views = $current_user->pmpro_views;
318
+ if(empty($views))
319
+ $views = array("last"=>"N/A", "month"=>0, "alltime"=>0);
320
+
321
+ //track logins for user
322
+ $views['last'] = date(get_option("date_format"));
323
+ $views['alltime']++;
324
+ $thismonth = date("n");
325
+ if(isset($views['thismonth']) && $thismonth == $views['thismonth'])
326
+ $views['month']++;
327
+ else
328
+ {
329
+ $views['month'] = 1;
330
+ $views['thismonth'] = $thismonth;
331
+ }
332
+
333
+ //update user data
334
+ update_user_meta($current_user->ID, "pmpro_views", $views);
335
+ }
336
+
337
+ //track for all
338
+ $views = get_option("pmpro_views");
339
+ if(empty($views))
340
+ $views = array("today"=>0, "month"=>0, "alltime"=>0);
341
+
342
+ $views['alltime']++;
343
+ $thisdate = date("Y-d-m");
344
+ if($thisdate == $views['thisdate'])
345
+ $views['today']++;
346
+ else
347
+ {
348
+ $views['today'] = 1;
349
+ $views['thisdate'] = $thisdate;
350
+ }
351
+ $thismonth = date("n");
352
+ if(isset($views['thismonth']) && $thismonth == $views['thismonth'])
353
+ $views['month']++;
354
+ else
355
+ {
356
+ $views['month'] = 1;
357
+ $views['thismonth'] = $thismonth;
358
+ }
359
+
360
+ update_option("pmpro_views", $views);
361
+ }
362
+ add_action("wp", "pmpro_report_login_wp_views");
363
+
364
+ //track logins
365
+ function pmpro_report_login_wp_login($user_login)
366
+ {
367
+ //get user data
368
+ $user = get_user_by("login", $user_login);
369
+ $logins = $user->pmpro_logins;
370
+ if(empty($logins))
371
+ $logins = array("last"=>"N/A", "month"=>0, "alltime"=>0);
372
+
373
+ //track logins for user
374
+ $logins['last'] = date(get_option("date_format"));
375
+ $logins['alltime']++;
376
+ $thismonth = date("n");
377
+ if($thismonth == $logins['thismonth'])
378
+ $logins['month']++;
379
+ else
380
+ {
381
+ $logins['month'] = 1;
382
+ $logins['thismonth'] = $thismonth;
383
+ }
384
+
385
+ //update user data
386
+ update_user_meta($user->ID, "pmpro_logins", $logins);
387
+
388
+ //track logins overall
389
+ $logins = get_option("pmpro_logins");
390
+ if(empty($logins))
391
+ $logins = array("today"=>0, "month"=>0, "alltime"=>0);
392
+
393
+ $logins['alltime']++;
394
+ $thisdate = date("Y-d-m");
395
+ if($thisdate == $logins['thisdate'])
396
+ $logins['today']++;
397
+ else
398
+ {
399
+ $logins['today'] = 1;
400
+ $logins['thisdate'] = $thisdate;
401
+ }
402
+ if($thismonth == $logins['thismonth'])
403
+ $logins['month']++;
404
+ else
405
+ {
406
+ $logins['month'] = 1;
407
+ $logins['thismonth'] = $thismonth;
408
+ }
409
+
410
+ update_option("pmpro_logins", $logins);
411
+ }
412
+ add_action("wp_login", "pmpro_report_login_wp_login");
adminpages/reports/memberships.php ADDED
@@ -0,0 +1,637 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ PMPro Report
4
+ Title: Membership Stats
5
+ Slug: memberships
6
+
7
+ For each report, add a line like:
8
+ global $pmpro_reports;
9
+ $pmpro_reports['slug'] = 'Title';
10
+
11
+ For each report, also write two functions:
12
+ * pmpro_report_{slug}_widget() to show up on the report homepage.
13
+ * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
+ */
15
+
16
+ global $pmpro_reports;
17
+
18
+ $pmpro_reports['memberships'] = __('Membership Stats', 'pmpro');
19
+
20
+ //queue Google Visualization JS on report page
21
+ function pmpro_report_memberships_init() {
22
+ if(is_admin() && isset($_REQUEST['report']) && $_REQUEST['report'] == "memberships" && isset($_REQUEST['page']) && $_REQUEST['page'] == "pmpro-reports")
23
+ wp_enqueue_script("jsapi", "https://www.google.com/jsapi");
24
+ }
25
+ add_action( 'init', 'pmpro_report_memberships_init' );
26
+
27
+
28
+ //widget
29
+ function pmpro_report_memberships_widget() {
30
+ global $wpdb, $pmpro_currency_symbol;
31
+ ?>
32
+ <style type="text/css">
33
+ #pmpro_report_memberships .section-label {
34
+ margin: 15px 0;
35
+ font-size: 18px;
36
+ text-align: left;
37
+ display: block;
38
+ }
39
+
40
+ #pmpro_report_memberships .section-label:first-child {
41
+ margin-top: 0;
42
+ }
43
+
44
+ #pmpro_report_memberships div {text-align: center;}
45
+ #pmpro_report_memberships em {display: block; font-style: normal; font-size: 2em; margin: 5px; line-height: 26px;}
46
+ </style>
47
+ <span id="pmpro_report_memberships">
48
+ <label class="section-label">Signups:</label>
49
+ <div style="width: 25%; float: left;">
50
+ <label>All Time</label>
51
+ <em><?php echo pmpro_getSignups( 'all time' ); ?></em>
52
+ </div>
53
+ <div style="width: 25%; float: left;">
54
+ <label>This Year</label>
55
+ <em><?php echo pmpro_getSignups( 'this year' ); ?></em>
56
+ </div>
57
+ <div style="width: 25%; float: left;">
58
+ <label>This Month</label>
59
+ <em><?php echo pmpro_getSignups( 'this month' ); ?></em>
60
+ </div>
61
+ <div style="width: 25%; float: left;">
62
+ <label>Today</label>
63
+ <em><?php echo pmpro_getSignups( 'today' ); ?></em>
64
+ </div>
65
+ <div class="clear"></div>
66
+
67
+ <label class="section-label">Cancellations:</label>
68
+ <div style="width: 25%; float: left;">
69
+ <label>All Time</label>
70
+ <em><?php echo pmpro_getCancellations( 'all time' ); ?></em>
71
+ </div>
72
+ <div style="width: 25%; float: left;">
73
+ <label>This Year</label>
74
+ <em><?php echo pmpro_getCancellations( 'this year' ); ?></em>
75
+ </div>
76
+ <div style="width: 25%; float: left;">
77
+ <label>This Month</label>
78
+ <em><?php echo pmpro_getCancellations( 'this month' ); ?></em>
79
+ </div>
80
+ <div style="width: 25%; float: left;">
81
+ <label>Today</label>
82
+ <em><?php echo pmpro_getCancellations( 'today' ); ?></em>
83
+ </div>
84
+ <div class="clear"></div>
85
+
86
+ <label class="section-label">Other Stats:</label>
87
+ <div style="width: 33%; float: left;">
88
+ <label>Monthly Recurring Revenue (MRR)</label>
89
+ <em><?php echo $pmpro_currency_symbol . $pmpro_mrr = number_format(pmpro_getMRR( 'all time' ), 2); ?></em>
90
+ </div>
91
+ <div style="width: 33%; float: left;">
92
+ <label>Cancellation Rate</label>
93
+ <em><?php echo pmpro_getCancellationRate('all time' ); ?>%</em>
94
+ </div>
95
+ <div style="width: 33%; float: left;">
96
+ <label>Lifetime Value (LTV)</label>
97
+ <em><?php echo $pmpro_currency_symbol . number_format(pmpro_getLTV('all time' ), 2); ?></em>
98
+ </div>
99
+ <div class="clear"></div>
100
+ </span>
101
+ <?php
102
+ }
103
+
104
+ function pmpro_report_memberships_page()
105
+ {
106
+ global $wpdb, $pmpro_currency_symbol;
107
+
108
+ //get values from form
109
+ if(isset($_REQUEST['type']))
110
+ $type = sanitize_text_field($_REQUEST['type']);
111
+ else
112
+ $type = "signup_v_cancel";
113
+
114
+ if(isset($_REQUEST['period']))
115
+ $period = sanitize_text_field($_REQUEST['period']);
116
+ else
117
+ $period = "monthly";
118
+
119
+ if(isset($_REQUEST['month']))
120
+ $month = intval($_REQUEST['month']);
121
+ else
122
+ $month = date("n");
123
+
124
+ $thisyear = date("Y");
125
+ if(isset($_REQUEST['year']))
126
+ $year = intval($_REQUEST['year']);
127
+ else
128
+ $year = date("Y");
129
+
130
+ if(isset($_REQUEST['level']))
131
+ $l = intval($_REQUEST['level']);
132
+ else
133
+ $l = "";
134
+
135
+ //calculate start date and how to group dates returned from DB
136
+ if($period == "daily")
137
+ {
138
+ $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
139
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-31';
140
+ $date_function = 'DAY';
141
+ }
142
+ elseif($period == "monthly")
143
+ {
144
+ $startdate = $year . '-01-01';
145
+ $enddate = strval(intval($year)+1) . '-01-01';
146
+ $date_function = 'MONTH';
147
+ }
148
+ elseif($period == "annual")
149
+ {
150
+ $startdate = '1960-01-01'; //all time
151
+ $date_function = 'YEAR';
152
+ }
153
+
154
+ //testing or live data
155
+ $gateway_environment = pmpro_getOption("gateway_environment");
156
+
157
+ //get data
158
+ if ( $type === "signup_v_cancel" ) {
159
+ $sqlQuery = "SELECT $date_function(startdate) as date, COUNT(DISTINCT user_id) as signups
160
+ FROM $wpdb->pmpro_memberships_users WHERE startdate >= '" . $startdate . "' ";
161
+
162
+ if(!empty($enddate))
163
+ $sqlQuery .= "AND startdate < '" . $enddate . "' ";
164
+ }
165
+ if ( $type === "mrr_ltv" ) {
166
+ // Get total revenue, number of months in system, and date
167
+ if ( $period == 'annual' )
168
+ $sqlQuery = "SELECT SUM(total) as total, COUNT(DISTINCT MONTH(timestamp)) as months, $date_function(timestamp) as date
169
+ FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token')
170
+ AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
171
+
172
+ if ( $period == 'monthly' )
173
+ $sqlQuery = "SELECT SUM(total) as total, $date_function(timestamp) as date
174
+ FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token')
175
+ AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
176
+
177
+ if(!empty($enddate))
178
+ $sqlQuery .= "AND timestamp < '" . $enddate . "' ";
179
+ }
180
+
181
+ if(!empty($l))
182
+ $sqlQuery .= "AND membership_id IN(" . $l . ") ";
183
+
184
+ $sqlQuery .= " GROUP BY date ORDER BY date ";
185
+
186
+ $dates = $wpdb->get_results($sqlQuery);
187
+
188
+ //fill in blanks in dates
189
+ $cols = array();
190
+ if($period == "daily")
191
+ {
192
+ $lastday = date("t", $startdate);
193
+
194
+ for($i = 1; $i <= $lastday; $i++)
195
+ {
196
+ // Signups vs. Cancellations
197
+ if ( $type === "signup_v_cancel" ) {
198
+ $cols[$i] = new stdClass();
199
+ $cols[$i]->signups = 0;
200
+ foreach($dates as $date)
201
+ {
202
+ if( $date->date == $i ) {
203
+ $cols[$i]->signups = $date->signups;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ elseif($period == "monthly")
210
+ {
211
+ for($i = 1; $i < 13; $i++)
212
+ {
213
+ // Signups vs. Cancellations
214
+ if ( $type === "signup_v_cancel" ) {
215
+ $cols[$i] = new stdClass();
216
+ $cols[$i]->date = $i;
217
+ $cols[$i]->signups = 0;
218
+ foreach($dates as $date)
219
+ {
220
+ if( $date->date == $i ) {
221
+ $cols[$i]->date = $date->date;
222
+ $cols[$i]->signups = $date->signups;
223
+ }
224
+ }
225
+ }
226
+
227
+ // MRR & LTV
228
+ if ( $type === "mrr_ltv" ) {
229
+ $cols[$i] = new stdClass();
230
+ $cols[$i]->date = $i;
231
+ $cols[$i]->months = 1;
232
+ foreach($dates as $date)
233
+ {
234
+ if( $date->date == $i ) {
235
+ $cols[$i]->total = $date->total;
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ elseif($period == "annual") //annual
242
+ {
243
+ }
244
+
245
+ $dates = ( ! empty( $cols ) ) ? $cols : $dates;
246
+
247
+ // Signups vs. cancellations
248
+ if ( $type === "signup_v_cancel" )
249
+ {
250
+ $sqlQuery = "SELECT $date_function(mu1.modified) as date, COUNT(DISTINCT mu1.user_id) as cancellations
251
+ FROM $wpdb->pmpro_memberships_users mu1
252
+ LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND
253
+ mu2.modified > mu1.enddate AND
254
+ DATE_ADD(mu1.modified, INTERVAL 1 DAY) > mu2.startdate
255
+ WHERE mu1.status = 'inactive'
256
+ AND mu2.id IS NULL
257
+ AND mu1.startdate >= '" . $startdate . "'
258
+ AND mu1.startdate < '" . $enddate . "' ";
259
+
260
+ //restrict by level
261
+ if(!empty($l))
262
+ $sqlQuery .= "AND membership_id IN(" . $l . ") ";
263
+
264
+ $sqlQuery .= " GROUP BY date ORDER BY date ";
265
+
266
+ $cdates = $wpdb->get_results($sqlQuery, OBJECT_K);
267
+
268
+ foreach( $dates as &$date )
269
+ {
270
+ if(!empty($cdates[$date->date]))
271
+ $date->cancellations = $cdates[$date->date]->cancellations;
272
+ else
273
+ $date->cancellations = 0;
274
+ }
275
+ }
276
+
277
+ // MRR & LTV
278
+ if ( $type === "mrr_ltv" && count( $dates ) === 1 ) {
279
+ $dummy_date = new stdClass();
280
+ $dummy_date->total = 0;
281
+ $dummy_date->months = 0;
282
+ $dummy_date->date = $dates[0]->date - 1;
283
+ array_unshift( $dates, $dummy_date ); // Add to beginning
284
+ }
285
+ ?>
286
+ <form id="posts-filter" method="get" action="">
287
+ <h2>
288
+ <?php _e('Membership Stats', 'pmpro');?>
289
+ </h2>
290
+ <ul class="subsubsub">
291
+ <li>
292
+ <?php _ex('Show', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?>
293
+ <select id="period" name="period">
294
+ <option value="daily" <?php selected($period, "daily");?>><?php _e('Daily', 'pmpro');?></option>
295
+ <option value="monthly" <?php selected($period, "monthly");?>><?php _e('Monthly', 'pmpro');?></option>
296
+ <option value="annual" <?php selected($period, "annual");?>><?php _e('Annual', 'pmpro');?></option>
297
+ </select>
298
+ <select id="type" name="type">
299
+ <option value="signup_v_cancel" <?php selected($type, "signup_v_cancel");?>><?php _e('Signups vs. Cancellations', 'pmpro');?></option>
300
+ <?php /*
301
+ <option value="mrr_ltv" <?php selected($type, "mrr_ltv");?>><?php _e('MRR & LTV', 'pmpro');?></option>
302
+ */ ?>
303
+ </select>
304
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
305
+ <select id="month" name="month">
306
+ <?php for($i = 1; $i < 13; $i++) { ?>
307
+ <option value="<?php echo $i;?>" <?php selected($month, $i);?>><?php echo date("F", mktime(0, 0, 0, $i));?></option>
308
+ <?php } ?>
309
+ </select>
310
+ <select id="year" name="year">
311
+ <?php for($i = $thisyear; $i > 2007; $i--) { ?>
312
+ <option value="<?php echo $i;?>" <?php selected($year, $i);?>><?php echo $i;?></option>
313
+ <?php } ?>
314
+ </select>
315
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
316
+ <select name="level">
317
+ <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
318
+ <?php
319
+ $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
320
+ foreach($levels as $level)
321
+ {
322
+ ?>
323
+ <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
324
+ <?php
325
+ }
326
+ ?>
327
+ </select>
328
+
329
+ <input type="hidden" name="page" value="pmpro-reports" />
330
+ <input type="hidden" name="report" value="memberships" />
331
+ <input type="submit" value="<?php _ex('Generate Report', 'Submit button value.', 'pmpro');?>" />
332
+ </li>
333
+ </ul>
334
+
335
+ <div id="chart_div" style="clear: both; width: 100%; height: 500px;"></div>
336
+
337
+ <script>
338
+ //update month/year when period dropdown is changed
339
+ jQuery(document).ready(function() {
340
+ jQuery('#period').change(function() {
341
+ pmpro_ShowMonthOrYear();
342
+ });
343
+ });
344
+
345
+ function pmpro_ShowMonthOrYear()
346
+ {
347
+ var period = jQuery('#period').val();
348
+ if(period == 'daily')
349
+ {
350
+ jQuery('#for').show();
351
+ jQuery('#month').show();
352
+ jQuery('#year').show();
353
+ }
354
+ else if(period == 'monthly')
355
+ {
356
+ jQuery('#for').show();
357
+ jQuery('#month').hide();
358
+ jQuery('#year').show();
359
+ }
360
+ else
361
+ {
362
+ jQuery('#for').hide();
363
+ jQuery('#month').hide();
364
+ jQuery('#year').hide();
365
+ }
366
+ }
367
+
368
+ pmpro_ShowMonthOrYear();
369
+
370
+ //draw the chart
371
+ google.load("visualization", "1", {packages:["corechart"]});
372
+ google.setOnLoadCallback(drawChart);
373
+ function drawChart() {
374
+
375
+ var data = google.visualization.arrayToDataTable([
376
+ <?php if ( $type === "signup_v_cancel" ) : // Signups vs. cancellations ?>
377
+ ['<?php echo $date_function;?>', 'Signups', 'Cancellations'],
378
+ <?php foreach($dates as $key => $value) { ?>
379
+ ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$value->date)); else if($period == "daily") echo $key; else echo $value->date;?>', <?php echo $value->signups; ?>, <?php echo $value->cancellations; ?>],
380
+ <?php } ?>
381
+ <?php endif; ?>
382
+
383
+ <?php if ( $type === "mrr_ltv" ) : // Signups vs. cancellations ?>
384
+ ['<?php echo $date_function;?>', 'MRR', 'LTV'],
385
+ <?php foreach($dates as $key => $value) { ?>
386
+ ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$value->date)); else if($period == "daily") echo $key; else echo $value->date;?>', <?php echo (($mrr = $value->total / $value->months) && $mrr != 0) ? $mrr : 0; ?>, <?php echo pmpro_getLTV($period, NULL, $mrr ); ?>],
387
+ <?php } ?>
388
+ <?php endif; ?>
389
+ ]);
390
+
391
+ var options = {
392
+ colors: ['#0099c6', '#dc3912'],
393
+ hAxis: {title: '<?php echo $date_function;?>', titleTextStyle: {color: 'black'}, maxAlternation: 1},
394
+ vAxis: {color: 'green', titleTextStyle: {color: '#51a351'}},
395
+ };
396
+
397
+ <?php if ( $type === "signup_v_cancel" ) : // Signups vs. cancellations ?>
398
+ var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
399
+ <?php elseif ( $type === "mrr_ltv" ) : // MRR & LTV ?>
400
+ var formatter = new google.visualization.NumberFormat({prefix: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
401
+ formatter.format(data, 2);
402
+ var formatter = new google.visualization.NumberFormat({prefix: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
403
+ formatter.format(data, 1);
404
+
405
+ var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
406
+ <?php endif; ?>
407
+ chart.draw(data, options);
408
+ }
409
+ </script>
410
+
411
+ </form>
412
+ <?php
413
+ }
414
+
415
+
416
+
417
+ /*
418
+ Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
419
+ */
420
+
421
+ //get signups
422
+ function pmpro_getSignups($period = false, $levels = 'all')
423
+ {
424
+ //check for a transient
425
+ $cache = get_transient( 'pmpro_report_memberships_signups' );
426
+ if( ! empty( $cache ) && ! empty( $cache[$period] ) && ! empty( $cache[$period][$levels] ) )
427
+ return $cache[$period][$levels];
428
+
429
+ //a sale is an order with status = success
430
+ if( $period == 'today' )
431
+ $startdate = date(' Y-m-d' );
432
+ elseif( $period == 'this month')
433
+ $startdate = date( 'Y-m' ) . '-01';
434
+ elseif( $period == 'this year')
435
+ $startdate = date( 'Y' ) . '-01-01';
436
+ else
437
+ $startdate = '';
438
+
439
+
440
+ //build query
441
+ global $wpdb;
442
+
443
+ $sqlQuery = "SELECT COUNT(DISTINCT user_id) FROM $wpdb->pmpro_memberships_users WHERE startdate >= '" . $startdate . "' ";
444
+
445
+ //restrict by level
446
+ if(!empty($levels) && $levels != 'all')
447
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
448
+
449
+ $signups = $wpdb->get_var($sqlQuery);
450
+
451
+ //save in cache
452
+ if(!empty($cache) && !empty($cache[$period]))
453
+ $cache[$period][$levels] = $signups;
454
+ elseif(!empty($cache))
455
+ $cache[$period] = array($levels => $signups);
456
+ else
457
+ $cache = array($period => array($levels => $signups));
458
+
459
+ set_transient("pmpro_report_memberships_signups", $cache, 3600*24);
460
+
461
+ return $signups;
462
+ }
463
+
464
+ //get cancellations
465
+ function pmpro_getCancellations($period = false, $levels = 'all')
466
+ {
467
+ //check for a transient
468
+ $cache = get_transient( 'pmpro_report_memberships_cancellations' );
469
+ if( ! empty( $cache ) && ! empty( $cache[$period] ) && ! empty( $cache[$period][$levels] ) )
470
+ return $cache[$period][$levels];
471
+
472
+ //figure out start date
473
+ if( $period == 'today' )
474
+ $startdate = date(' Y-m-d' );
475
+ elseif( $period == 'this month')
476
+ $startdate = date( 'Y-m' ) . '-01';
477
+ elseif( $period == 'this year')
478
+ $startdate = date( 'Y' ) . '-01-01';
479
+ else
480
+ $startdate = '';
481
+
482
+ $startdate_plus_one = strtotime( $startdate . + ' + 1 day' );
483
+
484
+ /*
485
+ build query.
486
+ cancellations are marked in the memberships users table with status = 'inactive'
487
+ we try to ignore cancellations when the user gets a new level with 24 hours (probably an upgrade or downgrade)
488
+ */
489
+ global $wpdb;
490
+
491
+ //$sqlQuery = "SELECT mu1.user_id, mu2.user_id FROM $wpdb->pmpro_memberships_users mu1 LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND mu2.status = 'inactive' AND mu2.startdate > mu1.startdate";
492
+ $sqlQuery = "SELECT COUNT(mu1.id)
493
+ FROM $wpdb->pmpro_memberships_users mu1
494
+ LEFT JOIN $wpdb->pmpro_memberships_users mu2 ON mu1.user_id = mu2.user_id AND
495
+ mu2.modified > mu1.enddate AND
496
+ DATE_ADD(mu1.modified, INTERVAL 1 DAY) > mu2.startdate
497
+ WHERE mu1.status = 'inactive'
498
+ AND mu2.id IS NULL
499
+ AND mu1.startdate >= '" . $startdate . "' ";
500
+
501
+ //restrict by level
502
+ if(!empty($levels) && $levels != 'all')
503
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
504
+
505
+ $cancellations = $wpdb->get_var($sqlQuery);
506
+
507
+ //save in cache
508
+ if(!empty($cache) && !empty($cache[$period]))
509
+ $cache[$period][$levels] = $cancellations;
510
+ elseif(!empty($cache))
511
+ $cache[$period] = array($levels => $sales);
512
+ else
513
+ $cache = array($period => array($levels => $cancellations));
514
+
515
+ set_transient("pmpro_report_memberships_cancellations", $cache, 3600*24);
516
+
517
+ return $cancellations;
518
+ }
519
+
520
+ //get MRR
521
+ function pmpro_getMRR($period, $levels = 'all')
522
+ {
523
+ //check for a transient
524
+ $cache = get_transient("pmpro_report_mrr");
525
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
526
+ return $cache[$period][$levels];
527
+
528
+ //a sale is an order with status NOT IN refunded, review, token, error
529
+ if($period == "this month")
530
+ $startdate = date("Y-m") . "-01";
531
+ elseif($period == "this year")
532
+ $startdate = date("Y") . "-01-01";
533
+ else
534
+ $startdate = "";
535
+
536
+ $gateway_environment = pmpro_getOption("gateway_environment");
537
+
538
+ //build query
539
+ global $wpdb;
540
+ // Get total revenue
541
+ $sqlQuery = "SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
542
+
543
+ //restrict by level
544
+ if(!empty($levels) && $levels != 'all') {
545
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
546
+ }
547
+
548
+ $revenue = $wpdb->get_var($sqlQuery);
549
+
550
+ //when was the first order
551
+ $first_order_timestamp = $wpdb->get_var("SELECT UNIX_TIMESTAMP(`timestamp`) FROM $wpdb->pmpro_membership_orders WHERE `timestamp` IS NOT NULL AND `timestamp` > '0000-00-00 00:00:00' ORDER BY `timestamp` LIMIT 1");
552
+
553
+ //if we don't have a timestamp, we can't do this
554
+ if(empty($first_order_timestamp))
555
+ return false;
556
+
557
+ //how many months ago was the first order
558
+ $date1 = new DateTime(date("Y-m-d", $first_order_timestamp));
559
+ $date2 = new DateTime(date("Y-m-d"));
560
+ $interval = $date1->diff($date2);
561
+ $years = intval($interval->format('%y'));
562
+ $months = $years*12 + intval($interval->format('%m'));
563
+
564
+ $mrr = $revenue / $months;
565
+
566
+ //save in cache
567
+ if(!empty($cache) && !empty($cache[$period]))
568
+ $cache[$period][$levels] = $mrr;
569
+ elseif(!empty($cache))
570
+ $cache[$period] = array($levels => $mrr);
571
+ else
572
+ $cache = array($period => array($levels => $mrr));
573
+
574
+ set_transient("pmpro_report_mrr", $cache, 3600*24);
575
+
576
+ return $mrr;
577
+ }
578
+
579
+ //get Cancellation Rate
580
+ function pmpro_getCancellationRate($period, $levels = 'all')
581
+ {
582
+ //check for a transient
583
+ $cache = get_transient("pmpro_report_cancellation_rate");
584
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
585
+ return $cache[$period][$levels];
586
+
587
+ $signups = pmpro_getSignups($period, $levels);
588
+ $cancellations = pmpro_getCancellations($period, $levels);
589
+
590
+ if(empty($signups))
591
+ return false;
592
+
593
+ $rate = number_format(($cancellations / $signups)*100, 2);
594
+
595
+ //save in cache
596
+ if(!empty($cache) && !empty($cache[$period]))
597
+ $cache[$period][$levels] = $rate;
598
+ elseif(!empty($cache))
599
+ $cache[$period] = array($levels => $rate);
600
+ else
601
+ $cache = array($period => array($levels => $rate));
602
+
603
+ set_transient("pmpro_report_cancellation_rate", $cache, 3600*24);
604
+
605
+ return $rate;
606
+ }
607
+
608
+ //get LTV
609
+ function pmpro_getLTV($period, $levels = 'all', $mrr = NULL, $signups = NULL, $cancellation_rate = NULL)
610
+ {
611
+ if(empty($mrr))
612
+ $mrr = pmpro_getMRR($period, $levels);
613
+ if(empty($signups))
614
+ $signups = pmpro_getSignups($period, $levels);
615
+ if(empty($cancellation_rate))
616
+ $cancellation_rate = pmpro_getCancellationRate($period, $levels);
617
+
618
+ //average monthly spend
619
+ if(empty($signups))
620
+ return false;
621
+ $ams = $mrr / $signups;
622
+
623
+ $ltv = $ams * (1/$cancellation_rate);
624
+
625
+ return $ltv;
626
+ }
627
+
628
+ //delete transients when an order goes through
629
+ function pmpro_report_memberships_delete_transients()
630
+ {
631
+ delete_transient("pmpro_report_mrr");
632
+ delete_transient("pmpro_report_cancellation_rate");
633
+ delete_transient("pmpro_report_memberships_cancellations");
634
+ delete_transient("pmpro_report_memberships_signups");
635
+ }
636
+ add_action("pmpro_after_checkout", "pmpro_report_memberships_delete_transients");
637
+ add_action("pmpro_updated_order", "pmpro_report_memberships_delete_transients");
adminpages/reports/sales.php ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ PMPro Report
4
+ Title: Sales
5
+ Slug: sales
6
+
7
+ For each report, add a line like:
8
+ global $pmpro_reports;
9
+ $pmpro_reports['slug'] = 'Title';
10
+
11
+ For each report, also write two functions:
12
+ * pmpro_report_{slug}_widget() to show up on the report homepage.
13
+ * pmpro_report_{slug}_page() to show up when users click on the report page widget.
14
+ */
15
+ global $pmpro_reports;
16
+ $gateway_environment = pmpro_getOption("gateway_environment");
17
+ if($gateway_environment == "sandbox")
18
+ $pmpro_reports['sales'] = __('Sales and Revenue (Testing/Sandbox)', 'pmpro');
19
+ else
20
+ $pmpro_reports['sales'] = __('Sales and Revenue', 'pmpro');
21
+
22
+ //queue Google Visualization JS on report page
23
+ function pmpro_report_sales_init()
24
+ {
25
+ if(is_admin() && isset($_REQUEST['report']) && $_REQUEST['report'] == "sales" && isset($_REQUEST['page']) && $_REQUEST['page'] == "pmpro-reports")
26
+ {
27
+ wp_enqueue_script("jsapi", "https://www.google.com/jsapi");
28
+ }
29
+ }
30
+ add_action("init", "pmpro_report_sales_init");
31
+
32
+ //widget
33
+ function pmpro_report_sales_widget()
34
+ {
35
+ global $wpdb, $pmpro_currency_symbol;
36
+ ?>
37
+ <style>
38
+ #pmpro_report_sales div {text-align: center;}
39
+ #pmpro_report_sales em {display: block; font-style: normal; font-size: 2em; margin: 5px;}
40
+ </style>
41
+ <span id="#pmpro_report_sales">
42
+ <div style="width: 25%; float: left;">
43
+ <em><?php echo pmpro_getSales("all time");?></em>
44
+ <label>All Time</label>
45
+ <em><?php echo $pmpro_currency_symbol . number_format(pmpro_getRevenue("all time"), 2);?></em>
46
+ </div>
47
+ <div style="width: 25%; float: left;">
48
+ <em><?php echo pmpro_getSales("this year");?></em>
49
+ <label>This Year</label>
50
+ <em><?php echo $pmpro_currency_symbol . number_format(pmpro_getRevenue("this year"), 2);?></em>
51
+ </div>
52
+ <div style="width: 25%; float: left;">
53
+ <em><?php echo pmpro_getSales("this month");?></em>
54
+ <label>This Month</label>
55
+ <em><?php echo $pmpro_currency_symbol . number_format(pmpro_getRevenue("this month"), 2);?></em>
56
+ </div>
57
+ <div style="width: 25%; float: left;">
58
+ <em><?php echo pmpro_getSales("today");?></em>
59
+ <label>Today</label>
60
+ <em><?php echo $pmpro_currency_symbol . number_format(pmpro_getRevenue("today"), 2);?></em>
61
+ </div>
62
+ <div class="clear"></div>
63
+ </span>
64
+ <?php
65
+ }
66
+
67
+ function pmpro_report_sales_page()
68
+ {
69
+ global $wpdb, $pmpro_currency_symbol;
70
+
71
+ //get values from form
72
+ if(isset($_REQUEST['type']))
73
+ $type = sanitize_text_field($_REQUEST['type']);
74
+ else
75
+ $type = "revenue";
76
+
77
+ if($type == "sales")
78
+ $type_function = "COUNT";
79
+ else
80
+ $type_function = "SUM";
81
+
82
+ if(isset($_REQUEST['period']))
83
+ $period = sanitize_text_field($_REQUEST['period']);
84
+ else
85
+ $period = "daily";
86
+
87
+ if(isset($_REQUEST['month']))
88
+ $month = intval($_REQUEST['month']);
89
+ else
90
+ $month = date("n");
91
+
92
+ $thisyear = date("Y");
93
+ if(isset($_REQUEST['year']))
94
+ $year = intval($_REQUEST['year']);
95
+ else
96
+ $year = $thisyear;
97
+
98
+ if(isset($_REQUEST['level']))
99
+ $l = intval($_REQUEST['level']);
100
+ else
101
+ $l = "";
102
+
103
+ //calculate start date and how to group dates returned from DB
104
+ if($period == "daily")
105
+ {
106
+ $startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
107
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-31';
108
+ $date_function = 'DAY';
109
+ }
110
+ elseif($period == "monthly")
111
+ {
112
+ $startdate = $year . '-01-01';
113
+ $enddate = strval(intval($year)+1) . '-01-01';
114
+ $date_function = 'MONTH';
115
+ }
116
+ else
117
+ {
118
+ $startdate = '1960-01-01'; //all time
119
+ $date_function = 'YEAR';
120
+ }
121
+
122
+ //testing or live data
123
+ $gateway_environment = pmpro_getOption("gateway_environment");
124
+
125
+ //get data
126
+ $sqlQuery = "SELECT $date_function(timestamp) as date, $type_function(total) as value FROM $wpdb->pmpro_membership_orders WHERE timestamp >= '" . $startdate . "' AND status NOT IN('refunded', 'review', 'token', 'error') AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
127
+
128
+ if(!empty($enddate))
129
+ $sqlQuery .= "AND timestamp < '" . $enddate . "' ";
130
+
131
+ if(!empty($l))
132
+ $sqlQuery .= "AND membership_id IN(" . $l . ") ";
133
+
134
+ $sqlQuery .= " GROUP BY date ORDER BY date ";
135
+
136
+ $dates = $wpdb->get_results($sqlQuery);
137
+
138
+ //fill in blanks in dates
139
+ $cols = array();
140
+ if($period == "daily")
141
+ {
142
+ $lastday = date("t", $startdate);
143
+
144
+ for($i = 1; $i <= $lastday; $i++)
145
+ {
146
+ $cols[$i] = 0;
147
+ foreach($dates as $date)
148
+ {
149
+ if($date->date == $i)
150
+ $cols[$i] = $date->value;
151
+ }
152
+ }
153
+ }
154
+ elseif($period == "monthly")
155
+ {
156
+ for($i = 1; $i < 13; $i++)
157
+ {
158
+ $cols[$i] = 0;
159
+ foreach($dates as $date)
160
+ {
161
+ if($date->date == $i)
162
+ $cols[$i] = $date->value;
163
+ }
164
+ }
165
+ }
166
+ else //annual
167
+ {
168
+ //get min and max years
169
+ $min = 9999;
170
+ $max = 0;
171
+ foreach($dates as $date)
172
+ {
173
+ $min = min($min, $date->date);
174
+ $max = max($max, $date->date);
175
+ }
176
+
177
+ for($i = $min; $i <= $max; $i++)
178
+ {
179
+ foreach($dates as $date)
180
+ {
181
+ if($date->date == $i)
182
+ $cols[$i] = $date->value;
183
+ }
184
+ }
185
+ }
186
+ ?>
187
+ <form id="posts-filter" method="get" action="">
188
+ <h2>
189
+ <?php _e('Sales and Revenue', 'pmpro');?>
190
+ </h2>
191
+
192
+ <ul class="subsubsub">
193
+ <li>
194
+ <?php _ex('Show', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?>
195
+ <select id="period" name="period">
196
+ <option value="daily" <?php selected($period, "daily");?>><?php _e('Daily', 'pmpro');?></option>
197
+ <option value="monthly" <?php selected($period, "monthly");?>><?php _e('Monthly', 'pmpro');?></option>
198
+ <option value="annual" <?php selected($period, "annual");?>><?php _e('Annual', 'pmpro');?></option>
199
+ </select>
200
+ <select name="type">
201
+ <option value="revenue" <?php selected($type, "revenue");?>><?php _e('Revenue', 'pmpro');?></option>
202
+ <option value="sales" <?php selected($type, "sales");?>><?php _e('Sales', 'pmpro');?></option>
203
+ </select>
204
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
205
+ <select id="month" name="month">
206
+ <?php for($i = 1; $i < 13; $i++) { ?>
207
+ <option value="<?php echo $i;?>" <?php selected($month, $i);?>><?php echo date("F", mktime(0, 0, 0, $i));?></option>
208
+ <?php } ?>
209
+ </select>
210
+ <select id="year" name="year">
211
+ <?php for($i = $thisyear; $i > 2007; $i--) { ?>
212
+ <option value="<?php echo $i;?>" <?php selected($year, $i);?>><?php echo $i;?></option>
213
+ <?php } ?>
214
+ </select>
215
+ <span id="for"><?php _ex('for', 'Dropdown label, e.g. Show Daily Revenue for January', 'pmpro')?></span>
216
+ <select name="level">
217
+ <option value="" <?php if(!$l) { ?>selected="selected"<?php } ?>><?php _e('All Levels', 'pmpro');?></option>
218
+ <?php
219
+ $levels = $wpdb->get_results("SELECT id, name FROM $wpdb->pmpro_membership_levels ORDER BY name");
220
+ foreach($levels as $level)
221
+ {
222
+ ?>
223
+ <option value="<?php echo $level->id?>" <?php if($l == $level->id) { ?>selected="selected"<?php } ?>><?php echo $level->name?></option>
224
+ <?php
225
+ }
226
+ ?>
227
+ </select>
228
+
229
+ <input type="hidden" name="page" value="pmpro-reports" />
230
+ <input type="hidden" name="report" value="sales" />
231
+ <input type="submit" value="<?php _ex('Generate Report', 'Submit button value.', 'pmpro');?>" />
232
+ </li>
233
+ </ul>
234
+
235
+ <div id="chart_div" style="clear: both; width: 100%; height: 500px;"></div>
236
+
237
+ <script>
238
+ //update month/year when period dropdown is changed
239
+ jQuery(document).ready(function() {
240
+ jQuery('#period').change(function() {
241
+ pmpro_ShowMonthOrYear();
242
+ });
243
+ });
244
+
245
+ function pmpro_ShowMonthOrYear()
246
+ {
247
+ var period = jQuery('#period').val();
248
+ if(period == 'daily')
249
+ {
250
+ jQuery('#for').show();
251
+ jQuery('#month').show();
252
+ jQuery('#year').show();
253
+ }
254
+ else if(period == 'monthly')
255
+ {
256
+ jQuery('#for').show();
257
+ jQuery('#month').hide();
258
+ jQuery('#year').show();
259
+ }
260
+ else
261
+ {
262
+ jQuery('#for').hide();
263
+ jQuery('#month').hide();
264
+ jQuery('#year').hide();
265
+ }
266
+ }
267
+
268
+ pmpro_ShowMonthOrYear();
269
+
270
+ //draw the chart
271
+ google.load("visualization", "1", {packages:["corechart"]});
272
+ google.setOnLoadCallback(drawChart);
273
+ function drawChart() {
274
+
275
+ var data = google.visualization.arrayToDataTable([
276
+ ['<?php echo $date_function;?>', '<?php echo ucwords($type);?>'],
277
+ <?php foreach($cols as $date => $value) { ?>
278
+ ['<?php if($period == "monthly") echo date("M", mktime(0,0,0,$date)); else echo $date;?>', <?php echo $value;?>],
279
+ <?php } ?>
280
+ ]);
281
+
282
+ var options = {
283
+ colors: ['#51a351', '#387038'],
284
+ hAxis: {title: '<?php echo $date_function;?>', titleTextStyle: {color: 'black'}, maxAlternation: 1},
285
+ vAxis: {color: 'green', titleTextStyle: {color: '#51a351'}},
286
+ };
287
+
288
+ <?php if($type != "sales") { ?>
289
+ var formatter = new google.visualization.NumberFormat({prefix: '<?php echo html_entity_decode($pmpro_currency_symbol);?>'});
290
+ formatter.format(data, 1);
291
+ <?php } ?>
292
+
293
+ var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
294
+ chart.draw(data, options);
295
+ }
296
+ </script>
297
+
298
+ </form>
299
+ <?php
300
+ }
301
+
302
+ /*
303
+ Other code required for your reports. This file is loaded every time WP loads with PMPro enabled.
304
+ */
305
+
306
+ //get sales
307
+ function pmpro_getSales($period, $levels = NULL)
308
+ {
309
+ //check for a transient
310
+ $cache = get_transient("pmpro_report_sales");
311
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
312
+ return $cache[$period][$levels];
313
+
314
+ //a sale is an order with status NOT IN('refunded', 'review', 'token', 'error')
315
+ if($period == "today")
316
+ $startdate = date("Y-m-d");
317
+ elseif($period == "this month")
318
+ $startdate = date("Y-m") . "-01";
319
+ elseif($period == "this year")
320
+ $startdate = date("Y") . "-01-01";
321
+ else
322
+ $startdate = "";
323
+
324
+ $gateway_environment = pmpro_getOption("gateway_environment");
325
+
326
+ //build query
327
+ global $wpdb;
328
+ $sqlQuery = "SELECT COUNT(*) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
329
+
330
+ //restrict by level
331
+ if(!empty($levels))
332
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
333
+
334
+ $sales = $wpdb->get_var($sqlQuery);
335
+
336
+ //save in cache
337
+ if(!empty($cache) && !empty($cache[$period]))
338
+ $cache[$period][$levels] = $sales;
339
+ elseif(!empty($cache))
340
+ $cache[$period] = array($levels => $sales);
341
+ else
342
+ $cache = array($period => array($levels => $sales));
343
+
344
+ set_transient("pmpro_report_sales", $cache, 3600*24);
345
+
346
+ return $sales;
347
+ }
348
+
349
+ //get revenue
350
+ function pmpro_getRevenue($period, $levels = NULL)
351
+ {
352
+ //check for a transient
353
+ $cache = get_transient("pmpro_report_revenue");
354
+ if(!empty($cache) && !empty($cache[$period]) && !empty($cache[$period][$levels]))
355
+ return $cache[$period][$levels];
356
+
357
+ //a sale is an order with status NOT IN('refunded', 'review', 'token', 'error')
358
+ if($period == "today")
359
+ $startdate = date("Y-m-d");
360
+ elseif($period == "this month")
361
+ $startdate = date("Y-m") . "-01";
362
+ elseif($period == "this year")
363
+ $startdate = date("Y") . "-01-01";
364
+ else
365
+ $startdate = "";
366
+
367
+ $gateway_environment = pmpro_getOption("gateway_environment");
368
+
369
+ //build query
370
+ global $wpdb;
371
+ $sqlQuery = "SELECT SUM(total) FROM $wpdb->pmpro_membership_orders WHERE status NOT IN('refunded', 'review', 'token', 'error') AND timestamp >= '" . $startdate . "' AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
372
+
373
+ //restrict by level
374
+ if(!empty($levels))
375
+ $sqlQuery .= "AND membership_id IN(" . $levels . ") ";
376
+
377
+ $revenue = $wpdb->get_var($sqlQuery);
378
+
379
+ //save in cache
380
+ if(!empty($cache) && !empty($cache[$period]))
381
+ $cache[$period][$levels] = $revenue;
382
+ elseif(!empty($cache))
383
+ $cache[$period] = array($levels => $revenue);
384
+ else
385
+ $cache = array($period => array($levels => $revenue));
386
+
387
+ set_transient("pmpro_report_revenue", $cache, 3600*24);
388
+
389
+ return $revenue;
390
+ }
391
+
392
+ //delete transients when an order goes through
393
+ function pmpro_report_sales_delete_transients()
394
+ {
395
+ delete_transient("pmpro_report_sales");
396
+ delete_transient("pmpro_report_revenue");
397
+ }
398
+ add_action("pmpro_after_checkout", "pmpro_report_sales_delete_transients");
399
+ add_action("pmpro_updated_order", "pmpro_report_sales_delete_transients");
classes/class.memberorder.php ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class MemberOrder
3
+ {
4
+ function MemberOrder($id = NULL)
5
+ {
6
+ //setup the gateway
7
+ $this->setGateway(pmpro_getOption("gateway"));
8
+
9
+ //get data if an id was passed
10
+ if($id)
11
+ {
12
+ if(is_numeric($id))
13
+ return $this->getMemberOrderByID($id);
14
+ else
15
+ return $this->getMemberOrderByCode($id);
16
+ }
17
+ else
18
+ return true; //blank constructor
19
+ }
20
+
21
+ function getMemberOrderByID($id)
22
+ {
23
+ global $wpdb;
24
+
25
+ if(!$id)
26
+ return false;
27
+
28
+ $gmt_offset = get_option('gmt_offset');
29
+ $dbobj = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(timestamp) + " . ($gmt_offset * 3600) . " as timestamp FROM $wpdb->pmpro_membership_orders WHERE id = '$id' LIMIT 1");
30
+
31
+ if($dbobj)
32
+ {
33
+ $this->id = $dbobj->id;
34
+ $this->code = $dbobj->code;
35
+ $this->session_id = $dbobj->session_id;
36
+ $this->user_id = $dbobj->user_id;
37
+ $this->membership_id = $dbobj->membership_id;
38
+ $this->paypal_token = $dbobj->paypal_token;
39
+ $this->billing = new stdClass();
40
+ $this->billing->name = $dbobj->billing_name;
41
+ $this->billing->street = $dbobj->billing_street;
42
+ $this->billing->city = $dbobj->billing_city;
43
+ $this->billing->state = $dbobj->billing_state;
44
+ $this->billing->zip = $dbobj->billing_zip;
45
+ $this->billing->country = $dbobj->billing_country;
46
+ $this->billing->phone = $dbobj->billing_phone;
47
+
48
+ //split up some values
49
+ $nameparts = pnp_split_full_name($this->billing->name);
50
+
51
+ if(!empty($nameparts['fname']))
52
+ $this->FirstName = $nameparts['fname'];
53
+ else
54
+ $this->FirstName = "";
55
+ if(!empty($nameparts['lname']))
56
+ $this->LastName = $nameparts['lname'];
57
+ else
58
+ $this->LastName = "";
59
+
60
+ $this->Address1 = $this->billing->street;
61
+
62
+ //get email from user_id
63
+ $this->Email = $wpdb->get_var("SELECT user_email FROM $wpdb->users WHERE ID = '" . $this->user_id . "' LIMIT 1");
64
+
65
+ $this->subtotal = $dbobj->subtotal;
66
+ $this->tax = $dbobj->tax;
67
+ $this->couponamount = $dbobj->couponamount;
68
+ $this->certificate_id = $dbobj->certificate_id;
69
+ $this->certificateamount = $dbobj->certificateamount;
70
+ $this->total = $dbobj->total;
71
+ $this->payment_type = $dbobj->payment_type;
72
+ $this->cardtype = $dbobj->cardtype;
73
+ $this->accountnumber = trim($dbobj->accountnumber);
74
+ $this->expirationmonth = $dbobj->expirationmonth;
75
+ $this->expirationyear = $dbobj->expirationyear;
76
+
77
+ //date formats sometimes useful
78
+ $this->ExpirationDate = $this->expirationmonth . $this->expirationyear;
79
+ $this->ExpirationDate_YdashM = $this->expirationyear . "-" . $this->expirationmonth;
80
+
81
+ $this->status = $dbobj->status;
82
+ $this->gateway = $dbobj->gateway;
83
+ $this->gateway_environment = $dbobj->gateway_environment;
84
+ $this->payment_transaction_id = $dbobj->payment_transaction_id;
85
+ $this->subscription_transaction_id = $dbobj->subscription_transaction_id;
86
+ $this->timestamp = $dbobj->timestamp;
87
+ $this->affiliate_id = $dbobj->affiliate_id;
88
+ $this->affiliate_subid = $dbobj->affiliate_subid;
89
+
90
+ $this->notes = $dbobj->notes;
91
+
92
+ //reset the gateway
93
+ if(empty($this->nogateway))
94
+ $this->setGateway();
95
+
96
+ return $this->id;
97
+ }
98
+ else
99
+ return false; //didn't find it in the DB
100
+ }
101
+
102
+ function setGateway($gateway = NULL)
103
+ {
104
+ //set the gateway property
105
+ if(isset($gateway))
106
+ {
107
+ $this->gateway = $gateway;
108
+ }
109
+
110
+ //which one to load?
111
+ $classname = "PMProGateway"; //default test gateway
112
+ if(!empty($this->gateway) && $this->gateway != "free")
113
+ $classname .= "_" . $this->gateway; //adding the gateway suffix
114
+
115
+ //try to load it
116
+ include_once(dirname(__FILE__) . "/gateways/class." . strtolower($classname) . ".php");
117
+ if(class_exists($classname))
118
+ $this->Gateway = new $classname($this->gateway);
119
+ else
120
+ {
121
+ $error = new WP_Error("PMPro1001", "Could not locate the gateway class file with class name = " . $classname . ".");
122
+ //die("Could not locate the gateway class file with class name = " . $classname . ".");
123
+ }
124
+
125
+ return $this->Gateway;
126
+ }
127
+
128
+ function getLastMemberOrder($user_id = NULL, $status = 'success')
129
+ {
130
+ global $current_user, $wpdb;
131
+ if(!$user_id)
132
+ $user_id = $current_user->ID;
133
+
134
+ if(!$user_id)
135
+ return false;
136
+
137
+ //build query
138
+ $this->sqlQuery = "SELECT id FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' ";
139
+ if(!empty($status) && is_array($status))
140
+ $this->sqlQuery .= "AND status IN('" . implode("','", $status) . "') ";
141
+ elseif(!empty($status))
142
+ $this->sqlQuery .= "AND status = '" . esc_sql($status) . "' ";
143
+ $this->sqlQuery .= "ORDER BY timestamp DESC LIMIT 1";
144
+
145
+ //get id
146
+ $id = $wpdb->get_var($this->sqlQuery);
147
+
148
+ return $this->getMemberOrderByID($id);
149
+ }
150
+
151
+ function getMemberOrderByCode($code)
152
+ {
153
+ global $wpdb;
154
+ $id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = '" . $code . "' LIMIT 1");
155
+ if($id)
156
+ return $this->getMemberOrderByID($id);
157
+ else
158
+ return false;
159
+ }
160
+
161
+ function getMemberOrderByPaymentTransactionID($payment_transaction_id)
162
+ {
163
+ global $wpdb;
164
+ $id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE payment_transaction_id = '" . esc_sql($payment_transaction_id) . "' LIMIT 1");
165
+ if($id)
166
+ return $this->getMemberOrderByID($id);
167
+ else
168
+ return false;
169
+ }
170
+
171
+ /*
172
+ Returns the last order using the given subscription_transaction_id.
173
+ */
174
+ function getLastMemberOrderBySubscriptionTransactionID($subscription_transaction_id)
175
+ {
176
+ //did they pass a sub id?
177
+ if(empty($subscription_transaction_id))
178
+ return false;
179
+
180
+ global $wpdb;
181
+ $id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE subscription_transaction_id = '" . esc_sql($subscription_transaction_id) . "' ORDER BY id DESC LIMIT 1");
182
+
183
+ if($id)
184
+ return $this->getMemberOrderByID($id);
185
+ else
186
+ return false;
187
+ }
188
+
189
+ function getMemberOrderByPayPalToken($token)
190
+ {
191
+ global $wpdb;
192
+ $id = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE paypal_token = '" . $token . "' LIMIT 1");
193
+ if($id)
194
+ return $this->getMemberOrderByID($id);
195
+ else
196
+ return false;
197
+ }
198
+
199
+ function getDiscountCode($force = false)
200
+ {
201
+ if(!empty($this->discount_code) && !$force)
202
+ return $this->discount_code;
203
+
204
+ global $wpdb;
205
+ $this->discount_code = $wpdb->get_row("SELECT dc.* FROM $wpdb->pmpro_discount_codes dc LEFT JOIN $wpdb->pmpro_discount_codes_uses dcu ON dc.id = dcu.code_id WHERE dcu.order_id = '" . $this->id . "' LIMIT 1");
206
+
207
+ return $this->discount_code;
208
+ }
209
+
210
+ function getUser()
211
+ {
212
+ global $wpdb;
213
+
214
+ if(!empty($this->user))
215
+ return $this->user;
216
+
217
+ $gmt_offset = get_option('gmt_offset');
218
+ $this->user = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(user_registered) + " . ($gmt_offset * 3600) . " as user_registered FROM $wpdb->users WHERE ID = '" . $this->user_id . "' LIMIT 1");
219
+ return $this->user;
220
+ }
221
+
222
+ function getMembershipLevel($force = false)
223
+ {
224
+ global $wpdb;
225
+
226
+ if(!empty($this->membership_level) && empty($force))
227
+ return $this->membership_level;
228
+
229
+ //check if there is an entry in memberships_users first
230
+ if(!empty($this->user_id))
231
+ {
232
+ $this->membership_level = $wpdb->get_row("SELECT l.id as level_id, l.name, l.description, l.allow_signups, l.expiration_number, l.expiration_period, mu.*, UNIX_TIMESTAMP(mu.startdate) as startdate, UNIX_TIMESTAMP(mu.enddate) as enddate, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_membership_levels l LEFT JOIN $wpdb->pmpro_memberships_users mu ON l.id = mu.membership_id WHERE mu.status = 'active' AND l.id = '" . $this->membership_id . "' AND mu.user_id = '" . $this->user_id . "' LIMIT 1");
233
+
234
+ //fix the membership level id
235
+ if(!empty($this->membership_level->level_id))
236
+ $this->membership_level->id = $this->membership_level->level_id;
237
+ }
238
+
239
+ //okay, do I have a discount code to check? (if there is no membership_level->membership_id value, that means there was no entry in memberships_users)
240
+ if(!empty($this->discount_code) && empty($this->membership_level->membership_id))
241
+ {
242
+ $sqlQuery = "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = '" . $this->discount_code . "' AND cl.level_id = '" . $this->membership_id . "' LIMIT 1";
243
+ $this->membership_level = $wpdb->get_row($sqlQuery);
244
+ }
245
+
246
+ //just get the info from the membership table (sigh, I really need to standardize the column names for membership_id/level_id) but we're checking if we got the information already or not
247
+ if(empty($this->membership_level->membership_id) && empty($this->membership_level->level_id))
248
+ {
249
+ $this->membership_level = $wpdb->get_row("SELECT l.* FROM $wpdb->pmpro_membership_levels l WHERE l.id = '" . $this->membership_id . "' LIMIT 1");
250
+ }
251
+
252
+ return $this->membership_level;
253
+ }
254
+
255
+ function getTaxForPrice($price)
256
+ {
257
+ //get options
258
+ $tax_state = pmpro_getOption("tax_state");
259
+ $tax_rate = pmpro_getOption("tax_rate");
260
+
261
+ //default
262
+ $tax = 0;
263
+
264
+ //calculate tax
265
+ if($tax_state && $tax_rate)
266
+ {
267
+ //we have values, is this order in the tax state?
268
+ if(trim(strtoupper($this->billing->state)) == trim(strtoupper($tax_state)))
269
+ {
270
+ //return value, pass through filter
271
+ $tax = round((float)$price * (float)$tax_rate, 2);
272
+ }
273
+ }
274
+
275
+ //set values array for filter
276
+ $values = array("price" => $price, "tax_state" => $tax_state, "tax_rate" => $tax_rate);
277
+ if(!empty($this->billing->state))
278
+ $values['billing_state'] = $this->billing->state;
279
+ if(!empty($this->billing->city))
280
+ $values['billing_city'] = $this->billing->city;
281
+ if(!empty($this->billing->zip))
282
+ $values['billing_zip'] = $this->billing->zip;
283
+ if(!empty($this->billing->country))
284
+ $values['billing_country'] = $this->billing->country;
285
+
286
+ //filter
287
+ $tax = apply_filters("pmpro_tax", $tax, $values, $this);
288
+ return $tax;
289
+ }
290
+
291
+ function getTax($force = false)
292
+ {
293
+ if(!empty($this->tax) && !$force)
294
+ return $this->tax;
295
+
296
+ //reset
297
+ $this->tax = $this->getTaxForPrice($this->subtotal);
298
+
299
+ return $this->tax;
300
+ }
301
+
302
+ function updateTimestamp($year, $month, $day, $time = NULL)
303
+ {
304
+ if(empty($this->id))
305
+ return false; //need a saved order
306
+
307
+ if(empty($time))
308
+ $time = "00:00:00";
309
+
310
+ $date = $year . "-" . $month . "-" . $day . " " . $time;
311
+
312
+ global $wpdb;
313
+ $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET timestamp = '" . $date . "' WHERE id = '" . $this->id . "' LIMIT 1";
314
+
315
+ if($wpdb->query($this->sqlQuery) !== "false")
316
+ return $this->getMemberOrderByID($this->id);
317
+ else
318
+ return false;
319
+ }
320
+
321
+ function saveOrder()
322
+ {
323
+ global $current_user, $wpdb;
324
+
325
+ //get a random code to use for the public ID
326
+ if(empty($this->code))
327
+ $this->code = $this->getRandomCode();
328
+
329
+ //figure out how much we charged
330
+ if(!empty($this->InitialPayment))
331
+ $amount = $this->InitialPayment;
332
+ elseif(!empty($this->subtotal))
333
+ $amount = $this->subtotal;
334
+ else
335
+ $amount = 0;
336
+
337
+ //Todo: Tax?!, Coupons, Certificates, affiliates
338
+ if(empty($this->subtotal))
339
+ $this->subtotal = $amount;
340
+ if(isset($this->tax))
341
+ $tax = $this->tax;
342
+ else
343
+ $tax = $this->getTax(true);
344
+ $this->certificate_id = "";
345
+ $this->certificateamount = "";
346
+
347
+ //calculate total
348
+ if(!empty($this->total))
349
+ $total = $this->total;
350
+ else
351
+ $total = (float)$amount + (float)$tax;
352
+
353
+ //these fix some warnings/notices
354
+ if(empty($this->billing))
355
+ {
356
+ $this->billing = new stdClass();
357
+ $this->billing->name = $this->billing->street = $this->billing->city = $this->billing->state = $this->billing->zip = $this->billing->country = $this->billing->phone = "";
358
+ }
359
+ if(empty($this->user_id))
360
+ $this->user_id = "";
361
+ if(empty($this->paypal_token))
362
+ $this->paypal_token = "";
363
+ if(empty($this->couponamount))
364
+ $this->couponamount = "";
365
+ if(empty($this->payment_type))
366
+ $this->payment_type = "";
367
+ if(empty($this->payment_transaction_id))
368
+ $this->payment_transaction_id = "";
369
+ if(empty($this->subscription_transaction_id))
370
+ $this->subscription_transaction_id = "";
371
+ if(empty($this->affiliate_id))
372
+ $this->affiliate_id = "";
373
+ if(empty($this->affiliate_subid))
374
+ $this->affiliate_subid = "";
375
+ if(empty($this->session_id))
376
+ $this->session_id = "";
377
+
378
+ if(empty($this->gateway))
379
+ $this->gateway = pmpro_getOption("gateway");
380
+ if(empty($this->gateway_environment))
381
+ $this->gateway_environment = pmpro_getOption("gateway_environment");
382
+
383
+ if(empty($this->notes))
384
+ $this->notes = "";
385
+
386
+ //build query
387
+ if(!empty($this->id))
388
+ {
389
+ //set up actions
390
+ $before_action = "pmpro_update_order";
391
+ $after_action = "pmpro_updated_order";
392
+ //update
393
+ $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders
394
+ SET `code` = '" . $this->code . "',
395
+ `session_id` = '" . $this->session_id . "',
396
+ `user_id` = '" . $this->user_id . "',
397
+ `membership_id` = '" . $this->membership_id . "',
398
+ `paypal_token` = '" . $this->paypal_token . "',
399
+ `billing_name` = '" . esc_sql($this->billing->name) . "',
400
+ `billing_street` = '" . esc_sql($this->billing->street) . "',
401
+ `billing_city` = '" . esc_sql($this->billing->city) . "',
402
+ `billing_state` = '" . esc_sql($this->billing->state) . "',
403
+ `billing_zip` = '" . esc_sql($this->billing->zip) . "',
404
+ `billing_country` = '" . esc_sql($this->billing->country) . "',
405
+ `billing_phone` = '" . esc_sql($this->billing->phone) . "',
406
+ `subtotal` = '" . $this->subtotal . "',
407
+ `tax` = '" . $this->tax . "',
408
+ `couponamount` = '" . $this->couponamount . "',
409
+ `certificate_id` = '" . $this->certificate_id . "',
410
+ `certificateamount` = '" . $this->certificateamount . "',
411
+ `total` = '" . $this->total . "',
412
+ `payment_type` = '" . $this->payment_type . "',
413
+ `cardtype` = '" . $this->cardtype . "',
414
+ `accountnumber` = '" . $this->accountnumber . "',
415
+ `expirationmonth` = '" . $this->expirationmonth . "',
416
+ `expirationyear` = '" . $this->expirationyear . "',
417
+ `status` = '" . esc_sql($this->status) . "',
418
+ `gateway` = '" . $this->gateway . "',
419
+ `gateway_environment` = '" . $this->gateway_environment . "',
420
+ `payment_transaction_id` = '" . esc_sql($this->payment_transaction_id) . "',
421
+ `subscription_transaction_id` = '" . esc_sql($this->subscription_transaction_id) . "',
422
+ `affiliate_id` = '" . esc_sql($this->affiliate_id) . "',
423
+ `affiliate_subid` = '" . esc_sql($this->affiliate_subid) . "',
424
+ `notes` = '" . esc_sql($this->notes) . "'
425
+ WHERE id = '" . $this->id . "'
426
+ LIMIT 1";
427
+ }
428
+ else
429
+ {
430
+ //set up actions
431
+ $before_action = "pmpro_add_order";
432
+ $after_action = "pmpro_added_order";
433
+ //insert
434
+ $this->sqlQuery = "INSERT INTO $wpdb->pmpro_membership_orders
435
+ (`code`, `session_id`, `user_id`, `membership_id`, `paypal_token`, `billing_name`, `billing_street`, `billing_city`, `billing_state`, `billing_zip`, `billing_country`, `billing_phone`, `subtotal`, `tax`, `couponamount`, `certificate_id`, `certificateamount`, `total`, `payment_type`, `cardtype`, `accountnumber`, `expirationmonth`, `expirationyear`, `status`, `gateway`, `gateway_environment`, `payment_transaction_id`, `subscription_transaction_id`, `timestamp`, `affiliate_id`, `affiliate_subid`, `notes`)
436
+ VALUES('" . $this->code . "',
437
+ '" . session_id() . "',
438
+ '" . $this->user_id . "',
439
+ '" . $this->membership_id . "',
440
+ '" . $this->paypal_token . "',
441
+ '" . esc_sql(trim($this->billing->name)) . "',
442
+ '" . esc_sql(trim($this->billing->street)) . "',
443
+ '" . esc_sql($this->billing->city) . "',
444
+ '" . esc_sql($this->billing->state) . "',
445
+ '" . esc_sql($this->billing->zip) . "',
446
+ '" . esc_sql($this->billing->country) . "',
447
+ '" . cleanPhone($this->billing->phone) . "',
448
+ '" . $amount . "',
449
+ '" . $tax . "',
450
+ '" . $this->couponamount. "',
451
+ '" . intval($this->certificate_id) . "',
452
+ '" . $this->certificateamount . "',
453
+ '" . $total . "',
454
+ '" . $this->payment_type . "',
455
+ '" . $this->cardtype . "',
456
+ '" . hideCardNumber($this->accountnumber, false) . "',
457
+ '" . substr($this->ExpirationDate, 0, 2) . "',
458
+ '" . substr($this->ExpirationDate, 2, 4) . "',
459
+ '" . esc_sql($this->status) . "',
460
+ '" . $this->gateway . "',
461
+ '" . $this->gateway_environment . "',
462
+ '" . esc_sql($this->payment_transaction_id) . "',
463
+ '" . esc_sql($this->subscription_transaction_id) . "',
464
+ now(),
465
+ '" . esc_sql($this->affiliate_id) . "',
466
+ '" . esc_sql($this->affiliate_subid) . "',
467
+ '" . esc_sql($this->notes) . "'
468
+ )";
469
+ }
470
+
471
+ do_action($before_action, $this);
472
+ if($wpdb->query($this->sqlQuery) !== false)
473
+ {
474
+ if(empty($this->id))
475
+ $this->id = $wpdb->insert_id;
476
+ do_action($after_action, $this);
477
+ return $this->getMemberOrderByID($this->id);
478
+ }
479
+ else
480
+ {
481
+ return false;
482
+ }
483
+ }
484
+
485
+ function getRandomCode()
486
+ {
487
+ global $wpdb;
488
+
489
+ while(empty($code))
490
+ {
491
+ $scramble = md5(AUTH_KEY . time() . SECURE_AUTH_KEY);
492
+ $code = substr($scramble, 0, 10);
493
+ $code = apply_filters("pmpro_random_code", $code, $this); //filter
494
+ $check = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = '$code' LIMIT 1");
495
+ if($check || is_numeric($code))
496
+ $code = NULL;
497
+ }
498
+
499
+ return strtoupper($code);
500
+ }
501
+
502
+ function updateStatus($newstatus)
503
+ {
504
+ global $wpdb;
505
+
506
+ if(empty($this->id))
507
+ return false;
508
+
509
+ $this->status = $newstatus;
510
+ $this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders SET status = '" . esc_sql($newstatus) . "' WHERE id = '" . $this->id . "' LIMIT 1";
511
+ if($wpdb->query($this->sqlQuery) !== false)
512
+ return true;
513
+ else
514
+ return false;
515
+ }
516
+
517
+ function process()
518
+ {
519
+ return $this->Gateway->process($this);
520
+ }
521
+
522
+ function cancel()
523
+ {
524
+ //only need to cancel on the gateway if there is a subscription id
525
+ if(empty($this->subscription_transaction_id))
526
+ {
527
+ //just mark as cancelled
528
+ $this->updateStatus("cancelled");
529
+ return true;
530
+ }
531
+ else
532
+ {
533
+ //cancel the gateway subscription first
534
+ return $this->Gateway->cancel($this);
535
+ }
536
+ }
537
+
538
+ function updateBilling()
539
+ {
540
+ return $this->Gateway->update($this);
541
+ }
542
+
543
+ function deleteMe()
544
+ {
545
+ if(empty($this->id))
546
+ return false;
547
+
548
+ global $wpdb;
549
+ $this->sqlQuery = "DELETE FROM $wpdb->pmpro_membership_orders WHERE id = '" . $this->id . "' LIMIT 1";
550
+ if($wpdb->query($this->sqlQuery) !== false)
551
+ {
552
+ do_action("pmpro_delete_order", $this->id, $this);
553
+ return true;
554
+ }
555
+ else
556
+ return false;
557
+ }
558
+ }
classes/class.mimetype.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ Copyright (C) 2002 Jason Sheets <jsheets@shadonet.com>.
4
+ All rights reserved.
5
+
6
+ THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions
9
+ are met:
10
+
11
+ 1. Redistributions of source code must retain the above copyright
12
+ notice, this list of conditions and the following disclaimer.
13
+
14
+ 2. Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
+
18
+ 3. Neither the name of the project nor the names of its contributors
19
+ may be used to endorse or promote products derived from this software
20
+ without specific prior written permission.
21
+
22
+ THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
+ SUCH DAMAGE.
33
+ **/
34
+
35
+ /**
36
+ Name: PHP MimeType Class
37
+
38
+ Version: 1.0
39
+ Date Released: 10/20/02
40
+
41
+ Description: This class allows a PHP script to determine the mime type
42
+ a file based on the file extension. This class is handy for PHP versions
43
+ without the benefit of other tools to determine the mime type. The class
44
+ defaults to octet-stream so if the file type is not recognized the user
45
+ is presented with a file download prompt.
46
+
47
+ NOTES: The mime types for this version are based on Apache 1.3.27
48
+ mime types may change or need to be added in the future, the mime types
49
+ are stored in an array so that an awk or other script can automatically
50
+ generate the code to make the array.
51
+
52
+ Usage:
53
+
54
+ First an instance of the mimetype class must be created, then the
55
+ getType method should be called with the filename. It will return
56
+ the mime type, an example follows.
57
+
58
+ Example:
59
+
60
+ $mimetype = new mimetype();
61
+ print $mimetype->getType('acrobat.pdf');
62
+
63
+ Author: Jason Sheets <jsheets@shadonet.com>
64
+
65
+ License: This script is distributed under the BSD License, you are free
66
+ to use, or modify it however you like. If you find this script useful please
67
+ e-mail me.
68
+ **/
69
+
70
+ class pmpro_mimetype {
71
+ function getType($filename) {
72
+ // get base name of the filename provided by user
73
+ $filename = basename($filename);
74
+
75
+ // break file into parts seperated by .
76
+ $filename = explode('.', $filename);
77
+
78
+ // take the last part of the file to get the file extension
79
+ $filename = $filename[count($filename)-1];
80
+
81
+ // find mime type
82
+ return $this->privFindType($filename);
83
+ }
84
+
85
+ function privFindType($ext) {
86
+ // create mimetypes array
87
+ $mimetypes = $this->privBuildMimeArray();
88
+
89
+ // return mime type for extension
90
+ if (isset($mimetypes[$ext])) {
91
+ return $mimetypes[$ext];
92
+ // if the extension wasn't found return octet-stream
93
+ } else {
94
+ return 'application/octet-stream';
95
+ }
96
+
97
+ }
98
+
99
+ function privBuildMimeArray() {
100
+ return array(
101
+ "ez" => "application/andrew-inset",
102
+ "hqx" => "application/mac-binhex40",
103
+ "cpt" => "application/mac-compactpro",
104
+ "doc" => "application/msword",
105
+ "bin" => "application/octet-stream",
106
+ "dms" => "application/octet-stream",
107
+ "lha" => "application/octet-stream",
108
+ "lzh" => "application/octet-stream",
109
+ "exe" => "application/octet-stream",
110
+ "class" => "application/octet-stream",
111
+ "so" => "application/octet-stream",
112
+ "dll" => "application/octet-stream",
113
+ "oda" => "application/oda",
114
+ "pdf" => "application/pdf",
115
+ "ai" => "application/postscript",
116
+ "eps" => "application/postscript",
117
+ "ps" => "application/postscript",
118
+ "smi" => "application/smil",
119
+ "smil" => "application/smil",
120
+ "wbxml" => "application/vnd.wap.wbxml",
121
+ "wmlc" => "application/vnd.wap.wmlc",
122
+ "wmlsc" => "application/vnd.wap.wmlscriptc",
123
+ "bcpio" => "application/x-bcpio",
124
+ "vcd" => "application/x-cdlink",
125
+ "pgn" => "application/x-chess-pgn",
126
+ "cpio" => "application/x-cpio",
127
+ "csh" => "application/x-csh",
128
+ "dcr" => "application/x-director",
129
+ "dir" => "application/x-director",
130
+ "dxr" => "application/x-director",
131
+ "dvi" => "application/x-dvi",
132
+ "spl" => "application/x-futuresplash",
133
+ "gtar" => "application/x-gtar",
134
+ "hdf" => "application/x-hdf",
135
+ "js" => "application/x-javascript",
136
+ "skp" => "application/x-koan",
137
+ "skd" => "application/x-koan",
138
+ "skt" => "application/x-koan",
139
+ "skm" => "application/x-koan",
140
+ "latex" => "application/x-latex",
141
+ "nc" => "application/x-netcdf",
142
+ "cdf" => "application/x-netcdf",
143
+ "sh" => "application/x-sh",
144
+ "shar" => "application/x-shar",
145
+ "swf" => "application/x-shockwave-flash",
146
+ "sit" => "application/x-stuffit",
147
+ "sv4cpio" => "application/x-sv4cpio",
148
+ "sv4crc" => "application/x-sv4crc",
149
+ "tar" => "application/x-tar",
150
+ "tcl" => "application/x-tcl",
151
+ "tex" => "application/x-tex",
152
+ "texinfo" => "application/x-texinfo",
153
+ "texi" => "application/x-texinfo",
154
+ "t" => "application/x-troff",
155
+ "tr" => "application/x-troff",
156
+ "roff" => "application/x-troff",
157
+ "man" => "application/x-troff-man",
158
+ "me" => "application/x-troff-me",
159
+ "ms" => "application/x-troff-ms",
160
+ "ustar" => "application/x-ustar",
161
+ "src" => "application/x-wais-source",
162
+ "xhtml" => "application/xhtml+xml",
163
+ "xht" => "application/xhtml+xml",
164
+ "zip" => "application/zip",
165
+ "au" => "audio/basic",
166
+ "snd" => "audio/basic",
167
+ "mid" => "audio/midi",
168
+ "midi" => "audio/midi",
169
+ "kar" => "audio/midi",
170
+ "mpga" => "audio/mpeg",
171
+ "mp2" => "audio/mpeg",
172
+ "mp3" => "audio/mpeg",
173
+ "aif" => "audio/x-aiff",
174
+ "aiff" => "audio/x-aiff",
175
+ "aifc" => "audio/x-aiff",
176
+ "m3u" => "audio/x-mpegurl",
177
+ "ram" => "audio/x-pn-realaudio",
178
+ "rm" => "audio/x-pn-realaudio",
179
+ "rpm" => "audio/x-pn-realaudio-plugin",
180
+ "ra" => "audio/x-realaudio",
181
+ "wav" => "audio/x-wav",
182
+ "pdb" => "chemical/x-pdb",
183
+ "xyz" => "chemical/x-xyz",
184
+ "bmp" => "image/bmp",
185
+ "gif" => "image/gif",
186
+ "ief" => "image/ief",
187
+ "jpeg" => "image/jpeg",
188
+ "jpg" => "image/jpeg",
189
+ "jpe" => "image/jpeg",
190
+ "png" => "image/png",
191
+ "tiff" => "image/tiff",
192
+ "tif" => "image/tif",
193
+ "djvu" => "image/vnd.djvu",
194
+ "djv" => "image/vnd.djvu",
195
+ "wbmp" => "image/vnd.wap.wbmp",
196
+ "ras" => "image/x-cmu-raster",
197
+ "pnm" => "image/x-portable-anymap",
198
+ "pbm" => "image/x-portable-bitmap",
199
+ "pgm" => "image/x-portable-graymap",
200
+ "ppm" => "image/x-portable-pixmap",
201
+ "rgb" => "image/x-rgb",
202
+ "xbm" => "image/x-xbitmap",
203
+ "xpm" => "image/x-xpixmap",
204
+ "xwd" => "image/x-windowdump",
205
+ "igs" => "model/iges",
206
+ "iges" => "model/iges",
207
+ "msh" => "model/mesh",
208
+ "mesh" => "model/mesh",
209
+ "silo" => "model/mesh",
210
+ "wrl" => "model/vrml",
211
+ "vrml" => "model/vrml",
212
+ "css" => "text/css",
213
+ "html" => "text/html",
214
+ "htm" => "text/html",
215
+ "asc" => "text/plain",
216
+ "txt" => "text/plain",
217
+ "rtx" => "text/richtext",
218
+ "rtf" => "text/rtf",
219
+ "sgml" => "text/sgml",
220
+ "sgm" => "text/sgml",
221
+ "tsv" => "text/tab-seperated-values",
222
+ "wml" => "text/vnd.wap.wml",
223
+ "wmls" => "text/vnd.wap.wmlscript",
224
+ "etx" => "text/x-setext",
225
+ "xml" => "text/xml",
226
+ "xsl" => "text/xml",
227
+ "mpeg" => "video/mpeg",
228
+ "mpg" => "video/mpeg",
229
+ "mpe" => "video/mpeg",
230
+ "qt" => "video/quicktime",
231
+ "mov" => "video/quicktime",
232
+ "mxu" => "video/vnd.mpegurl",
233
+ "avi" => "video/x-msvideo",
234
+ "movie" => "video/x-sgi-movie",
235
+ "ice" => "x-conference-xcooltalk"
236
+ );
237
+ }
238
+ }
239
+ ?>
classes/class.pmproemail.php ADDED
@@ -0,0 +1,747 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class PMProEmail
3
+ {
4
+ function PMProEmail()
5
+ {
6
+ $this->email = $this->from = $this->fromname = $this->subject = $this->template = $this->data = $this->body = NULL;
7
+ }
8
+
9
+ function sendEmail($email = NULL, $from = NULL, $fromname = NULL, $subject = NULL, $template = NULL, $data = NULL)
10
+ {
11
+ //if values were passed
12
+ if($email)
13
+ $this->email = $email;
14
+ if($from)
15
+ $this->from = $from;
16
+ if($fromname)
17
+ $this->fromname = $fromname;
18
+ if($subject)
19
+ $this->subject = $subject;
20
+ if($template)
21
+ $this->template = $template;
22
+ if($data)
23
+ $this->data = $data;
24
+
25
+ //default values
26
+ global $current_user;
27
+ if(!$this->email)
28
+ $this->email = $current_user->user_email;
29
+
30
+ if(!$this->from)
31
+ $this->from = pmpro_getOption("from_email");
32
+
33
+ if(!$this->fromname)
34
+ $this->fromname = pmpro_getOption("from_name");
35
+
36
+ if(!$this->subject)
37
+ $this->subject = sprintf(__("An Email From %s", "pmpro"), get_option("blogname"));
38
+
39
+ //decode the subject line in case there are apostrophes/etc in it
40
+ $this->subject = html_entity_decode($this->subject, ENT_QUOTES, 'UTF-8');
41
+
42
+ if(!$this->template)
43
+ $this->template = "default";
44
+
45
+ $this->headers = array("Content-Type: text/html");
46
+
47
+ //load the template
48
+ $locale = apply_filters("plugin_locale", get_locale(), "pmpro");
49
+ if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $this->template . ".html"))
50
+ $this->body = file_get_contents(get_stylesheet_directory() . "/paid-memberships-pro/email/" . $this->template . ".html"); //email folder in pmpro folder in theme
51
+ elseif(file_exists(get_stylesheet_directory() . "/membership-email-" . $this->template . ".html"))
52
+ $this->body = file_get_contents(get_stylesheet_directory() . "/membership-email-" . $this->template . ".html"); //membership- file in pmpro folder in theme
53
+ elseif(file_exists(TEMPLATEPATH . "/membership-email-" . $this->template . ".html"))
54
+ $this->body = file_get_contents(TEMPLATEPATH . "/membership-email-" . $this->template . ".html"); //membership- file in theme root
55
+ elseif(file_exists(WP_LANG_DIR . '/pmpro/email/' . $this->template . ".html"))
56
+ $this->body = file_get_contents(WP_LANG_DIR . '/pmpro/email/' . $this->template . ".html"); //email folder in WP language folder
57
+ elseif(file_exists(PMPRO_DIR . "/languages/" . $locale . "/" . $this->template . ".html"))
58
+ $this->body = file_get_contents(PMPRO_DIR . "/languages/" . $locale . "/" . $this->template . ".html"); //email folder in PMPro language folder
59
+ elseif(file_exists(PMPRO_DIR . "/email/" . $this->template . ".html"))
60
+ $this->body = file_get_contents(PMPRO_DIR . "/email/" . $this->template . ".html"); //default template in plugin
61
+
62
+ //header and footer
63
+ /* This is handled for all emails via the pmpro_send_html function in paid-memberships-pro now
64
+ if(file_exists(TEMPLATEPATH . "/email_header.html"))
65
+ {
66
+ $this->body = file_get_contents(TEMPLATEPATH . "/email_header.html") . "\n" . $this->body;
67
+ }
68
+ if(file_exists(TEMPLATEPATH . "/email_footer.html"))
69
+ {
70
+ $this->body = $this->body . "\n" . file_get_contents(TEMPLATEPATH . "/email_footer.html");
71
+ }
72
+ */
73
+
74
+ //if data is a string, assume we mean to replace !!body!! with it
75
+ if(is_string($this->data))
76
+ $this->data = array("body"=>$data);
77
+
78
+ //filter for data
79
+ $this->data = apply_filters("pmpro_email_data", $this->data, $this); //filter
80
+
81
+ //swap data into body
82
+ if(is_array($this->data))
83
+ {
84
+ foreach($this->data as $key => $value)
85
+ {
86
+ $this->body = str_replace("!!" . $key . "!!", $value, $this->body);
87
+ }
88
+ }
89
+
90
+ //filters
91
+ $temail = apply_filters("pmpro_email_filter", $this); //allows filtering entire email at once
92
+ $this->email = apply_filters("pmpro_email_recipient", $temail->email, $this);
93
+ $this->from = apply_filters("pmpro_email_sender", $temail->from, $this);
94
+ $this->fromname = apply_filters("pmpro_email_sender_name", $temail->fromname, $this);
95
+ $this->subject = apply_filters("pmpro_email_subject", $temail->subject, $this);
96
+ $this->template = apply_filters("pmpro_email_template", $temail->template, $this);
97
+ $this->body = apply_filters("pmpro_email_body", $temail->body, $this);
98
+ $this->headers = apply_filters("pmpro_email_headers", $temail->headers, $this);
99
+
100
+ if(wp_mail($this->email,$this->subject,$this->body,$this->headers))
101
+ {
102
+ return true;
103
+ }
104
+ else
105
+ {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ function sendCancelEmail($user = NULL)
111
+ {
112
+ global $current_user;
113
+ if(!$user)
114
+ $user = $current_user;
115
+
116
+ if(!$user)
117
+ return false;
118
+
119
+ $this->email = $user->user_email;
120
+ $this->subject = sprintf(__("Your membership at %s has been CANCELLED", "pmpro"), get_option("blogname"));
121
+ $this->template = "cancel";
122
+ $this->data = array("name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"));
123
+
124
+ return $this->sendEmail();
125
+ }
126
+
127
+ function sendCancelAdminEmail($user = NULL, $old_level_id)
128
+ {
129
+ global $wpdb, $current_user;
130
+ if(!$user)
131
+ $user = $current_user;
132
+
133
+ if(!$user)
134
+ return false;
135
+
136
+ //check settings
137
+ $send = pmpro_getOption("email_admin_cancels");
138
+ if(empty($send))
139
+ return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
140
+
141
+ $this->email = get_bloginfo("admin_email");
142
+ $this->subject = sprintf(__("Membership for %s at %s has been CANCELLED", "pmpro"), $user->user_login, get_option("blogname"));
143
+ $this->template = "cancel_admin";
144
+ $this->data = array("user_login" => $user->user_login, "user_email" => $user->user_email, "display_name" => $user->display_name, "sitename" => get_option("blogname"), "siteemail" => pmpro_getOption("from_email"), "login_link" => wp_login_url());
145
+ $this->data['membership_id'] = $old_level_id;
146
+ $this->data['membership_level_name'] = $wpdb->get_var("SELECT name FROM $wpdb->pmpro_membership_levels WHERE id = '" . $old_level_id . "' LIMIT 1");
147
+
148
+ //start and end date
149
+ $startdate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(startdate) as startdate FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND membership_id = '" . $old_level_id . "' AND status = 'inactive' ORDER BY id DESC");
150
+ if(!empty($startdate))
151
+ $this->data['startdate'] = date(get_option('date_format'), $startdate);
152
+ else
153
+ $this->data['startdate'] = "";
154
+ $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(enddate) as enddate FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND membership_id = '" . $old_level_id . "' AND status = 'inactive' ORDER BY id DESC");
155
+ if(!empty($enddate))
156
+ $this->data['enddate'] = date(get_option('date_format'), $enddate);
157
+ else
158
+ $this->data['enddate'] = "";
159
+
160
+ return $this->sendEmail();
161
+ }
162
+
163
+ function sendCheckoutEmail($user = NULL, $invoice = NULL)
164
+ {
165
+ global $wpdb, $current_user, $pmpro_currency_symbol;
166
+ if(!$user)
167
+ $user = $current_user;
168
+
169
+ if(!$user)
170
+ return false;
171
+
172
+ $this->email = $user->user_email;
173
+ $this->subject = sprintf(__("Your membership confirmation for %s", "pmpro"), get_option("blogname"));
174
+
175
+ $this->data = array(
176
+ "subject" => $this->subject,
177
+ "name" => $user->display_name,
178
+ "user_login" => $user->user_login,
179
+ "sitename" => get_option("blogname"),
180
+ "siteemail" => pmpro_getOption("from_email"),
181
+ "membership_id" => $user->membership_level->id,
182
+ "membership_level_name" => $user->membership_level->name,
183
+ "membership_cost" => pmpro_getLevelCost($user->membership_level),
184
+ "login_link" => pmpro_url("account"),
185
+ "display_name" => $user->display_name,
186
+ "user_email" => $user->user_email,0
187
+ );
188
+
189
+ if(!empty($invoice) && !pmpro_isLevelFree($user->membership_level))
190
+ {
191
+ if($invoice->gateway == "paypalexpress")
192
+ $this->template = "checkout_express";
193
+ elseif($invoice->gateway == "check")
194
+ {
195
+ $this->template = "checkout_check";
196
+ $this->data["instructions"] = wpautop(pmpro_getOption("instructions"));
197
+ }
198
+ elseif(pmpro_isLevelTrial($user->membership_level))
199
+ $this->template = "checkout_trial";
200
+ else
201
+ $this->template = "checkout_paid";
202
+ $this->data["invoice_id"] = $invoice->code;
203
+ $this->data["invoice_total"] = $pmpro_currency_symbol . number_format($invoice->total, 2);
204
+ $this->data["invoice_date"] = date(get_option('date_format'), $invoice->timestamp);
205
+ $this->data["billing_name"] = $invoice->billing->name;
206
+ $this->data["billing_street"] = $invoice->billing->street;
207
+ $this->data["billing_city"] = $invoice->billing->city;
208
+ $this->data["billing_state"] = $invoice->billing->state;
209
+ $this->data["billing_zip"] = $invoice->billing->zip;
210
+ $this->data["billing_country"] = $invoice->billing->country;
211
+ $this->data["billing_phone"] = $invoice->billing->phone;
212
+ $this->data["cardtype"] = $invoice->cardtype;
213
+ $this->data["accountnumber"] = hideCardNumber($invoice->accountnumber);
214
+ $this->data["expirationmonth"] = $invoice->expirationmonth;
215
+ $this->data["expirationyear"] = $invoice->expirationyear;
216
+
217
+ if($invoice->getDiscountCode())
218
+ $this->data["discount_code"] = "<p>" . __("Discount Code", "pmpro") . ": " . $invoice->discount_code->code . "</p>\n";
219
+ else
220
+ $this->data["discount_code"] = "";
221
+ }
222
+ elseif(pmpro_isLevelFree($user->membership_level))
223
+ {
224
+ $this->template = "checkout_free";
225
+ global $discount_code;
226
+ if(!empty($discount_code))
227
+ $this->data["discount_code"] = "<p>" . __("Discount Code", "pmpro") . ": " . $discount_code . "</p>\n";
228
+ else
229
+ $this->data["discount_code"] = "";
230
+ }
231
+ else
232
+ {
233
+ $this->template = "checkout_freetrial";
234
+ global $discount_code;
235
+ if(!empty($discount_code))
236
+ $this->data["discount_code"] = "<p>" . __("Discount Code", "pmpro") . ": " . $discount_code . "</p>\n";
237
+ else
238
+ $this->data["discount_code"] = "";
239
+ }
240
+
241
+ $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(enddate) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
242
+ if($enddate)
243
+ $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", "pmpro"), date(get_option('date_format'), $enddate)) . "</p>\n";
244
+ else
245
+ $this->data["membership_expiration"] = "";
246
+
247
+ return $this->sendEmail();
248
+ }
249
+
250
+ function sendCheckoutAdminEmail($user = NULL, $invoice = NULL)
251
+ {
252
+ global $wpdb, $current_user, $pmpro_currency_symbol;
253
+ if(!$user)
254
+ $user = $current_user;
255
+
256
+ if(!$user)
257
+ return false;
258
+
259
+ //check settings
260
+ $send = pmpro_getOption("email_admin_checkout");
261
+ if(empty($send))
262
+ return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
263
+
264
+ $this->email = get_bloginfo("admin_email");
265
+ $this->subject = sprintf(__("Member Checkout for %s at %s", "pmpro"), $user->membership_level->name, get_option("blogname"));
266
+
267
+ $this->data = array(
268
+ "subject" => $this->subject,
269
+ "name" => $user->display_name,
270
+ "user_login" => $user->user_login,
271
+ "sitename" => get_option("blogname"),
272
+ "siteemail" => pmpro_getOption("from_email"),
273
+ "membership_id" => $user->membership_level->id,
274
+ "membership_level_name" => $user->membership_level->name,
275
+ "membership_cost" => pmpro_getLevelCost($user->membership_level),
276
+ "login_link" => pmpro_url("account"),
277
+ "display_name" => $user->display_name,
278
+ "user_email" => $user->user_email,0
279
+ );
280
+
281
+ if($invoice)
282
+ {
283
+ if($invoice->gateway == "paypalexpress")
284
+ $this->template = "checkout_express_admin";
285
+ elseif($invoice->gateway == "check")
286
+ $this->template = "checkout_check_admin";
287
+ elseif(pmpro_isLevelTrial($user->membership_level))
288
+ $this->template = "checkout_trial_admin";
289
+ else
290
+ $this->template = "checkout_paid_admin";
291
+ $this->data["invoice_id"] = $invoice->code;
292
+ $this->data["invoice_total"] = $pmpro_currency_symbol . number_format($invoice->total, 2);
293
+ $this->data["invoice_date"] = date(get_option('date_format'), $invoice->timestamp);
294
+ $this->data["billing_name"] = $invoice->billing->name;
295
+ $this->data["billing_street"] = $invoice->billing->street;
296
+ $this->data["billing_city"] = $invoice->billing->city;
297
+ $this->data["billing_state"] = $invoice->billing->state;
298
+ $this->data["billing_zip"] = $invoice->billing->zip;
299
+ $this->data["billing_country"] = $invoice->billing->country;
300
+ $this->data["billing_phone"] = $invoice->billing->phone;
301
+ $this->data["cardtype"] = $invoice->cardtype;
302
+ $this->data["accountnumber"] = hideCardNumber($invoice->accountnumber);
303
+ $this->data["expirationmonth"] = $invoice->expirationmonth;
304
+ $this->data["expirationyear"] = $invoice->expirationyear;
305
+
306
+ if($invoice->getDiscountCode())
307
+ $this->data["discount_code"] = "<p>" . __("Discount Code", "pmpro") . ": " . $invoice->discount_code->code . "</p>\n";
308
+ else
309
+ $this->data["discount_code"] = "";
310
+ }
311
+ elseif(pmpro_isLevelFree($user->membership_level))
312
+ {
313
+ $this->template = "checkout_free_admin";
314
+ global $discount_code;
315
+ if(!empty($discount_code))
316
+ $this->data["discount_code"] = "<p>" . __("Discount Code", "pmpro") . ": " . $discount_code . "</p>\n";
317
+ else
318
+ $this->data["discount_code"] = "";
319
+ }
320
+ else
321
+ {
322
+ $this->template = "checkout_freetrial_admin";
323
+ $this->data["discount_code"] = "";
324
+ }
325
+
326
+ $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(enddate) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
327
+ if($enddate)
328
+ $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", "pmpro"), date(get_option('date_format'), $enddate)) . "</p>\n";
329
+ else
330
+ $this->data["membership_expiration"] = "";
331
+
332
+ return $this->sendEmail();
333
+ }
334
+
335
+ function sendBillingEmail($user = NULL, $invoice = NULL)
336
+ {
337
+ global $current_user;
338
+ if(!$user)
339
+ $user = $current_user;
340
+
341
+ if(!$user || !$invoice)
342
+ return false;
343
+
344
+ $this->email = $user->user_email;
345
+ $this->subject = sprintf(__("Your billing information has been udpated at %s", "pmpro"), get_option("blogname"));
346
+ $this->template = "billing";
347
+
348
+ $this->data = array(
349
+ "subject" => $this->subject,
350
+ "name" => $user->display_name,
351
+ "user_login" => $user->user_login,
352
+ "sitename" => get_option("blogname"),
353
+ "siteemail" => pmpro_getOption("from_email"),
354
+ "membership_id" => $user->membership_level->id,
355
+ "membership_level_name" => $user->membership_level->name,
356
+ "display_name" => $user->display_name,
357
+ "user_email" => $user->user_email,
358
+ "billing_name" => $invoice->billing->name,
359
+ "billing_street" => $invoice->billing->street,
360
+ "billing_city" => $invoice->billing->city,
361
+ "billing_state" => $invoice->billing->state,
362
+ "billing_zip" => $invoice->billing->zip,
363
+ "billing_country" => $invoice->billing->country,
364
+ "billing_phone" => $invoice->billing->phone,
365
+ "cardtype" => $invoice->cardtype,
366
+ "accountnumber" => hideCardNumber($invoice->accountnumber),
367
+ "expirationmonth" => $invoice->expirationmonth,
368
+ "expirationyear" => $invoice->expirationyear,
369
+ "login_link" => pmpro_url("account")
370
+ );
371
+
372
+ return $this->sendEmail();
373
+ }
374
+
375
+ function sendBillingAdminEmail($user = NULL, $invoice = NULL)
376
+ {
377
+ global $current_user;
378
+ if(!$user)
379
+ $user = $current_user;
380
+
381
+ if(!$user || !$invoice)
382
+ return false;
383
+
384
+ //check settings
385
+ $send = pmpro_getOption("email_admin_billing");
386
+ if(empty($send))
387
+ return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
388
+
389
+ $this->email = get_bloginfo("admin_email");
390
+ $this->subject = sprintf(__("Billing information has been udpated for %s at %s", "pmpro"), $user->user_login, get_option("blogname"));
391
+ $this->template = "billing_admin";
392
+
393
+ $this->data = array(
394
+ "subject" => $this->subject,
395
+ "name" => $user->display_name,
396
+ "user_login" => $user->user_login,
397
+ "sitename" => get_option("blogname"),
398
+ "siteemail" => pmpro_getOption("from_email"),
399
+ "membership_id" => $user->membership_level->id,
400
+ "membership_level_name" => $user->membership_level->name,
401
+ "display_name" => $user->display_name,
402
+ "user_email" => $user->user_email,
403
+ "billing_name" => $invoice->billing->name,
404
+ "billing_street" => $invoice->billing->street,
405
+ "billing_city" => $invoice->billing->city,
406
+ "billing_state" => $invoice->billing->state,
407
+ "billing_zip" => $invoice->billing->zip,
408
+ "billing_country" => $invoice->billing->country,
409
+ "billing_phone" => $invoice->billing->phone,
410
+ "cardtype" => $invoice->cardtype,
411
+ "accountnumber" => hideCardNumber($invoice->accountnumber),
412
+ "expirationmonth" => $invoice->expirationmonth,
413
+ "expirationyear" => $invoice->expirationyear,
414
+ "login_link" => wp_login_url()
415
+ );
416
+
417
+ return $this->sendEmail();
418
+ }
419
+
420
+ function sendBillingFailureEmail($user = NULL, $invoice = NULL)
421
+ {
422
+ global $current_user;
423
+ if(!$user)
424
+ $user = $current_user;
425
+
426
+ if(!$user || !$invoice)
427
+ return false;
428
+
429
+ $this->email = $user->user_email;
430
+ $this->subject = sprintf(__("Membership Payment Failed at %s", "pmpro"), get_option("blogname"));
431
+ $this->template = "billing_failure";
432
+
433
+ $this->data = array(
434
+ "subject" => $this->subject,
435
+ "name" => $user->display_name,
436
+ "user_login" => $user->user_login,
437
+ "sitename" => get_option("blogname"),
438
+ "siteemail" => pmpro_getOption("from_email"),
439
+ "membership_id" => $user->membership_level->id,
440
+ "membership_level_name" => $user->membership_level->name,
441
+ "display_name" => $user->display_name,
442
+ "user_email" => $user->user_email,
443
+ "billing_name" => $invoice->billing->name,
444
+ "billing_street" => $invoice->billing->street,
445
+ "billing_city" => $invoice->billing->city,
446
+ "billing_state" => $invoice->billing->state,
447
+ "billing_zip" => $invoice->billing->zip,
448
+ "billing_country" => $invoice->billing->country,
449
+ "billing_phone" => $invoice->billing->phone,
450
+ "cardtype" => $invoice->cardtype,
451
+ "accountnumber" => hideCardNumber($invoice->accountnumber),
452
+ "expirationmonth" => $invoice->expirationmonth,
453
+ "expirationyear" => $invoice->expirationyear,
454
+ "login_link" => pmpro_url("billing")
455
+ );
456
+
457
+ return $this->sendEmail();
458
+ }
459
+
460
+ function sendBillingFailureAdminEmail($email, $invoice = NULL)
461
+ {
462
+ if(!$invoice)
463
+ return false;
464
+
465
+ $user = get_userdata($invoice->user_id);
466
+
467
+ $this->email = $email;
468
+ $this->subject = sprintf(__("Membership Payment Failed For %s at %s", "pmpro"), $user->display_name, get_option("blogname"));
469
+ $this->template = "billing_failure_admin";
470
+
471
+ $this->data = array(
472
+ "subject" => $this->subject,
473
+ "name" => "Admin",
474
+ "user_login" => $user->user_login,
475
+ "sitename" => get_option("blogname"),
476
+ "siteemail" => pmpro_getOption("from_email"),
477
+ "membership_id" => $user->membership_level->id,
478
+ "membership_level_name" => $user->membership_level->name,
479
+ "display_name" => $user->display_name,
480
+ "user_email" => $user->user_email,
481
+ "billing_name" => $invoice->billing->name,
482
+ "billing_street" => $invoice->billing->street,
483
+ "billing_city" => $invoice->billing->city,
484
+ "billing_state" => $invoice->billing->state,
485
+ "billing_zip" => $invoice->billing->zip,
486
+ "billing_country" => $invoice->billing->country,
487
+ "billing_phone" => $invoice->billing->phone,
488
+ "cardtype" => $invoice->cardtype,
489
+ "accountnumber" => hideCardNumber($invoice->accountnumber),
490
+ "expirationmonth" => $invoice->expirationmonth,
491
+ "expirationyear" => $invoice->expirationyear,
492
+ "login_link" => pmpro_url("billing")
493
+ );
494
+
495
+ return $this->sendEmail();
496
+ }
497
+
498
+ function sendCreditCardExpiringEmail($user = NULL, $invoice = NULL)
499
+ {
500
+ global $current_user;
501
+ if(!$user)
502
+ $user = $current_user;
503
+
504
+ if(!$user || !$invoice)
505
+ return false;
506
+
507
+ $this->email = $user->user_email;
508
+ $this->subject = sprintf(__("Credit Card on File Expiring Soon at %s", "pmpro"), get_option("blogname"));
509
+ $this->template = "credit_card_expiring";
510
+
511
+ $this->data = array(
512
+ "subject" => $this->subject,
513
+ "name" => $user->display_name,
514
+ "user_login" => $user->user_login,
515
+ "sitename" => get_option("blogname"),
516
+ "siteemail" => pmpro_getOption("from_email"),
517
+ "membership_id" => $user->membership_level->id,
518
+ "membership_level_name" => $user->membership_level->name,
519
+ "display_name" => $user->display_name,
520
+ "user_email" => $user->user_email,
521
+ "billing_name" => $invoice->billing->name,
522
+ "billing_street" => $invoice->billing->street,
523
+ "billing_city" => $invoice->billing->city,
524
+ "billing_state" => $invoice->billing->state,
525
+ "billing_zip" => $invoice->billing->zip,
526
+ "billing_country" => $invoice->billing->country,
527
+ "billing_phone" => $invoice->billing->phone,
528
+ "cardtype" => $invoice->cardtype,
529
+ "accountnumber" => hideCardNumber($invoice->accountnumber),
530
+ "expirationmonth" => $invoice->expirationmonth,
531
+ "expirationyear" => $invoice->expirationyear,
532
+ "login_link" => pmpro_url("billing")
533
+ );
534
+
535
+ return $this->sendEmail();
536
+ }
537
+
538
+ function sendInvoiceEmail($user = NULL, $invoice = NULL)
539
+ {
540
+ global $wpdb, $current_user, $pmpro_currency_symbol;
541
+ if(!$user)
542
+ $user = $current_user;
543
+
544
+ if(!$user || !$invoice)
545
+ return false;
546
+
547
+ $this->email = $user->user_email;
548
+ $this->subject = sprintf(__("INVOICE for %s membership", "pmpro"), get_option("blogname"));
549
+ $this->template = "invoice";
550
+
551
+ $this->data = array(
552
+ "subject" => $this->subject,
553
+ "name" => $user->display_name,
554
+ "user_login" => $user->user_login,
555
+ "sitename" => get_option("blogname"),
556
+ "siteemail" => pmpro_getOption("from_email"),
557
+ "membership_id" => $user->membership_level->id,
558
+ "membership_level_name" => $user->membership_level->name,
559
+ "display_name" => $user->display_name,
560
+ "user_email" => $user->user_email,
561
+ "invoice_id" => $invoice->payment_transaction_id,
562
+ "invoice_total" => $pmpro_currency_symbol . number_format($invoice->total, 2),
563
+ "invoice_date" => date(get_option('date_format'), $invoice->timestamp),
564
+ "billing_name" => $invoice->billing->name,
565
+ "billing_street" => $invoice->billing->street,
566
+ "billing_city" => $invoice->billing->city,
567
+ "billing_state" => $invoice->billing->state,
568
+ "billing_zip" => $invoice->billing->zip,
569
+ "billing_country" => $invoice->billing->country,
570
+ "billing_phone" => $invoice->billing->phone,
571
+ "cardtype" => $invoice->cardtype,
572
+ "accountnumber" => hideCardNumber($invoice->accountnumber),
573
+ "expirationmonth" => $invoice->expirationmonth,
574
+ "expirationyear" => $invoice->expirationyear,
575
+ "login_link" => pmpro_url("account"),
576
+ "invoice_link" => pmpro_url("invoice", "?invoice=" . $invoice->code)
577
+ );
578
+
579
+ if($invoice->getDiscountCode())
580
+ $this->data["discount_code"] = "<p>" . __("Discount Code", "pmpro") . ": " . $invoice->discount_code . "</p>\n";
581
+ else
582
+ $this->data["discount_code"] = "";
583
+
584
+ $enddate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(enddate) FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . $user->ID . "' AND status = 'active' LIMIT 1");
585
+ if($enddate)
586
+ $this->data["membership_expiration"] = "<p>" . sprintf(__("This membership will expire on %s.", "pmpro"), date(get_option('date_format'), $enddate)) . "</p>\n";
587
+ else
588
+ $this->data["membership_expiration"] = "";
589
+
590
+ return $this->sendEmail();
591
+ }
592
+
593
+ function sendTrialEndingEmail($user = NULL)
594
+ {
595
+ global $current_user, $wpdb, $pmpro_currency_symbol;
596
+ if(!$user)
597
+ $user = $current_user;
598
+
599
+ if(!$user)
600
+ return false;
601
+
602
+ //make sure we have the current membership level data
603
+ /*$user->membership_level = $wpdb->get_row("SELECT l.id AS ID, l.name AS name, UNIX_TIMESTAMP(mu.startdate) as startdate, mu.billing_amount, mu.cycle_number, mu.cycle_period, mu.trial_amount, mu.trial_limit
604
+ FROM {$wpdb->pmpro_membership_levels} AS l
605
+ JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
606
+ WHERE mu.user_id = " . $user->ID . "
607
+ LIMIT 1");*/
608
+ $user->membership_level = pmpro_getMembershipLevelForUser($user->ID);
609
+
610
+ $this->email = $user->user_email;
611
+ $this->subject = sprintf(__("Your trial at %s is ending soon", "pmpro"), get_option("blogname"));
612
+ $this->template = "trial_ending";
613
+ $this->data = array(
614
+ "subject" => $this->subject,
615
+ "name" => $user->display_name,
616
+ "user_login" => $user->user_login,
617
+ "sitename" => get_option("blogname"),
618
+ "membership_id" => $user->membership_level->id,
619
+ "membership_level_name" => $user->membership_level->name,
620
+ "siteemail" => get_bloginfo("admin_email"),
621
+ "login_link" => wp_login_url(),
622
+ "display_name" => $user->display_name,
623
+ "user_email" => $user->user_email,
624
+ "billing_amount" => $pmpro_currency_symbol . $user->membership_level->billing_amount,
625
+ "cycle_number" => $user->membership_level->cycle_number,
626
+ "cycle_period" => $user->membership_level->cycle_period,
627
+ "trial_amount" => $pmpro_currency_symbol . $user->membership_level->trial_amount,
628
+ "trial_limit" => $user->membership_level->trial_limit,
629
+ "trial_end" => date(get_option('date_format'), strtotime(date("m/d/Y", $user->membership_level->startdate) . " + " . $user->membership_level->trial_limit . " " . $user->membership_level->cycle_period))
630
+ );
631
+
632
+ return $this->sendEmail();
633
+ }
634
+
635
+ function sendMembershipExpiredEmail($user = NULL)
636
+ {
637
+ global $current_user, $wpdb;
638
+ if(!$user)
639
+ $user = $current_user;
640
+
641
+ if(!$user)
642
+ return false;
643
+
644
+ $this->email = $user->user_email;
645
+ $this->subject = sprintf(__("Your membership at %s has ended", "pmpro"), get_option("blogname"));
646
+ $this->template = "membership_expired";
647
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "siteemail" => get_bloginfo("admin_email"), "login_link" => wp_login_url(), "display_name" => $user->display_name, "user_email" => $user->user_email, "levels_link" => pmpro_url("levels"));
648
+
649
+ return $this->sendEmail();
650
+ }
651
+
652
+ function sendMembershipExpiringEmail($user = NULL)
653
+ {
654
+ global $current_user, $wpdb;
655
+ if(!$user)
656
+ $user = $current_user;
657
+
658
+ if(!$user)
659
+ return false;
660
+
661
+ //make sure we have the current membership level data
662
+ /*$user->membership_level = $wpdb->get_row("SELECT l.id AS ID, l.name AS name, UNIX_TIMESTAMP(mu.enddate) as enddate
663
+ FROM {$wpdb->pmpro_membership_levels} AS l
664
+ JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
665
+ WHERE mu.user_id = " . $user->ID . "
666
+ LIMIT 1");*/
667
+ $user->membership_level = pmpro_getMembershipLevelForUser($user->ID);
668
+
669
+ $this->email = $user->user_email;
670
+ $this->subject = sprintf(__("Your membership at %s will end soon", "pmpro"), get_option("blogname"));
671
+ $this->template = "membership_expiring";
672
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_level_name" => $user->membership_level->name, "siteemail" => get_bloginfo("admin_email"), "login_link" => wp_login_url(), "enddate" => date(get_option('date_format'), $user->membership_level->enddate), "display_name" => $user->display_name, "user_email" => $user->user_email);
673
+
674
+ return $this->sendEmail();
675
+ }
676
+
677
+ function sendAdminChangeEmail($user = NULL)
678
+ {
679
+ global $current_user, $wpdb;
680
+ if(!$user)
681
+ $user = $current_user;
682
+
683
+ if(!$user)
684
+ return false;
685
+
686
+ //make sure we have the current membership level data
687
+ $user->membership_level = pmpro_getMembershipLevelForUser($user->ID);
688
+
689
+ $this->email = $user->user_email;
690
+ $this->subject = sprintf(__("Your membership at %s has been changed", "pmpro"), get_option("blogname"));
691
+ $this->template = "admin_change";
692
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_level_name" => $user->membership_level->name, "siteemail" => get_bloginfo("admin_email"), "login_link" => wp_login_url());
693
+ if($user->membership_level->ID)
694
+ $this->data["membership_change"] = sprintf(__("The new level is %s. This membership is free", "pmpro"), $user->membership_level->name);
695
+ else
696
+ $this->data["membership_change"] = __("Your membership has been cancelled", "pmpro");
697
+
698
+ if(!empty($user->membership_level->enddate))
699
+ {
700
+ $this->data["membership_change"] .= ". " . sprintf(__("This membership will expire on %s", "pmpro"), date(get_option('date_format'), $user->membership_level->enddate));
701
+ }
702
+ elseif(!empty($this->expiration_changed))
703
+ {
704
+ $this->data["membership_change"] .= ". " . __("This membership does not expire", "pmpro");
705
+ }
706
+
707
+ return $this->sendEmail();
708
+ }
709
+
710
+ function sendAdminChangeAdminEmail($user = NULL)
711
+ {
712
+ global $current_user, $wpdb;
713
+ if(!$user)
714
+ $user = $current_user;
715
+
716
+ if(!$user)
717
+ return false;
718
+
719
+ //check settings
720
+ $send = pmpro_getOption("email_admin_changes");
721
+ if(empty($send))
722
+ return true; //didn't send, but we also don't want to indicate failure because the settings say to not send
723
+
724
+ //make sure we have the current membership level data
725
+ $user->membership_level = pmpro_getMembershipLevelForUser($user->ID);
726
+
727
+ $this->email = get_bloginfo("admin_email");
728
+ $this->subject = sprintf(__("Membership for %s at %s has been changed", "pmpro"), $user->user_login, get_option("blogname"));
729
+ $this->template = "admin_change_admin";
730
+ $this->data = array("subject" => $this->subject, "name" => $user->display_name, "user_login" => $user->user_login, "sitename" => get_option("blogname"), "membership_level_name" => $user->membership_level->name, "siteemail" => get_bloginfo("admin_email"), "login_link" => wp_login_url());
731
+ if($user->membership_level->ID)
732
+ $this->data["membership_change"] = sprintf(__("The new level is %s. This membership is free", "pmpro"), $user->membership_level->name);
733
+ else
734
+ $this->data["membership_change"] = __("membership has been cancelled", "pmpro");
735
+
736
+ if(!empty($user->membership_level->enddate))
737
+ {
738
+ $this->data["membership_change"] .= ". " . sprintf(__("This membership will expire on %s", "pmpro"), date(get_option('date_format'), $user->membership_level->enddate));
739
+ }
740
+ elseif(!empty($this->expiration_changed))
741
+ {
742
+ $this->data["membership_change"] .= ". " . __("This membership does not expire", "pmpro");
743
+ }
744
+
745
+ return $this->sendEmail();
746
+ }
747
+ }
classes/gateways/class.pmprogateway.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway
4
+ {
5
+ function PMProGateway($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ //check for initial payment
14
+ if(floatval($order->InitialPayment) == 0)
15
+ {
16
+ //auth first, then process
17
+ if($this->authorize($order))
18
+ {
19
+ $this->void($order);
20
+ if(!pmpro_isLevelTrial($order->membership_level))
21
+ {
22
+ //subscription will start today with a 1 period trial
23
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
24
+ $order->TrialBillingPeriod = $order->BillingPeriod;
25
+ $order->TrialBillingFrequency = $order->BillingFrequency;
26
+ $order->TrialBillingCycles = 1;
27
+ $order->TrialAmount = 0;
28
+
29
+ //add a billing cycle to make up for the trial, if applicable
30
+ if(!empty($order->TotalBillingCycles))
31
+ $order->TotalBillingCycles++;
32
+ }
33
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
34
+ {
35
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
36
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
37
+ $order->TrialBillingCycles++;
38
+
39
+ //add a billing cycle to make up for the trial, if applicable
40
+ if($order->TotalBillingCycles)
41
+ $order->TotalBillingCycles++;
42
+ }
43
+ else
44
+ {
45
+ //add a period to the start date to account for the initial payment
46
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
47
+ }
48
+
49
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
50
+ return $this->subscribe($order);
51
+ }
52
+ else
53
+ {
54
+ if(empty($order->error))
55
+ $order->error = __("Unknown error: Authorization failed.", "pmpro");
56
+ return false;
57
+ }
58
+ }
59
+ else
60
+ {
61
+ //charge first payment
62
+ if($this->charge($order))
63
+ {
64
+ //setup recurring billing
65
+ if(pmpro_isLevelRecurring($order->membership_level))
66
+ {
67
+ if(!pmpro_isLevelTrial($order->membership_level))
68
+ {
69
+ //subscription will start today with a 1 period trial
70
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
71
+ $order->TrialBillingPeriod = $order->BillingPeriod;
72
+ $order->TrialBillingFrequency = $order->BillingFrequency;
73
+ $order->TrialBillingCycles = 1;
74
+ $order->TrialAmount = 0;
75
+
76
+ //add a billing cycle to make up for the trial, if applicable
77
+ if(!empty($order->TotalBillingCycles))
78
+ $order->TotalBillingCycles++;
79
+ }
80
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
81
+ {
82
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
83
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
84
+ $order->TrialBillingCycles++;
85
+
86
+ //add a billing cycle to make up for the trial, if applicable
87
+ if(!empty($order->TotalBillingCycles))
88
+ $order->TotalBillingCycles++;
89
+ }
90
+ else
91
+ {
92
+ //add a period to the start date to account for the initial payment
93
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $this->BillingFrequency . " " . $this->BillingPeriod)) . "T0:0:0";
94
+ }
95
+
96
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
97
+ if($this->subscribe($order))
98
+ {
99
+ return true;
100
+ }
101
+ else
102
+ {
103
+ if($this->void($order))
104
+ {
105
+ if(!$order->error)
106
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
107
+ }
108
+ else
109
+ {
110
+ if(!$order->error)
111
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
112
+
113
+ $order->error .= " " . __("A partial payment was made that we could not void. Please contact the site owner immediately to correct this.", "pmpro");
114
+ }
115
+
116
+ return false;
117
+ }
118
+ }
119
+ else
120
+ {
121
+ //only a one time charge
122
+ $order->status = "success"; //saved on checkout page
123
+ return true;
124
+ }
125
+ }
126
+ else
127
+ {
128
+ if(empty($order->error))
129
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
130
+
131
+ return false;
132
+ }
133
+ }
134
+ }
135
+
136
+ function authorize(&$order)
137
+ {
138
+ //create a code for the order
139
+ if(empty($order->code))
140
+ $order->code = $order->getRandomCode();
141
+
142
+ //simulate a successful authorization
143
+ $order->payment_transaction_id = "TEST" . $order->code;
144
+ $order->updateStatus("authorized");
145
+ return true;
146
+ }
147
+
148
+ function void(&$order)
149
+ {
150
+ //need a transaction id
151
+ if(empty($order->payment_transaction_id))
152
+ return false;
153
+
154
+ //simulate a successful void
155
+ $order->payment_transaction_id = "TEST" . $order->code;
156
+ $order->updateStatus("voided");
157
+ return true;
158
+ }
159
+
160
+ function charge(&$order)
161
+ {
162
+ //create a code for the order
163
+ if(empty($order->code))
164
+ $order->code = $order->getRandomCode();
165
+
166
+ //simulate a successful charge
167
+ $order->payment_transaction_id = "TEST" . $order->code;
168
+ $order->updateStatus("success");
169
+ return true;
170
+ }
171
+
172
+ function subscribe(&$order)
173
+ {
174
+ //create a code for the order
175
+ if(empty($order->code))
176
+ $order->code = $order->getRandomCode();
177
+
178
+ //filter order before subscription. use with care.
179
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
180
+
181
+ //simulate a successful subscription processing
182
+ $order->status = "success";
183
+ $order->subscription_transaction_id = "TEST" . $order->code;
184
+ return true;
185
+ }
186
+
187
+ function update(&$order)
188
+ {
189
+ //simulate a successful billing update
190
+ return true;
191
+ }
192
+
193
+ function cancel(&$order)
194
+ {
195
+ //require a subscription id
196
+ if(empty($order->subscription_transaction_id))
197
+ return false;
198
+
199
+ //simulate a successful cancel
200
+ $order->updateStatus("cancelled");
201
+ return true;
202
+ }
203
+ }
204
+ ?>
classes/gateways/class.pmprogateway_authorizenet.php ADDED
@@ -0,0 +1,872 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway_authorizenet extends PMProGateway
4
+ {
5
+ function PMProGateway_authorizenet($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ //check for initial payment
14
+ if(floatval($order->InitialPayment) == 0)
15
+ {
16
+ //auth first, then process
17
+ if($this->authorize($order))
18
+ {
19
+ $this->void($order);
20
+ if(!pmpro_isLevelTrial($order->membership_level))
21
+ {
22
+ //subscription will start today with a 1 period trial
23
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
24
+ $order->TrialBillingPeriod = $order->BillingPeriod;
25
+ $order->TrialBillingFrequency = $order->BillingFrequency;
26
+ $order->TrialBillingCycles = 1;
27
+ $order->TrialAmount = 0;
28
+
29
+ //add a billing cycle to make up for the trial, if applicable
30
+ if(!empty($order->TotalBillingCycles))
31
+ $order->TotalBillingCycles++;
32
+ }
33
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
34
+ {
35
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
36
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
37
+ $order->TrialBillingCycles++;
38
+
39
+ //add a billing cycle to make up for the trial, if applicable
40
+ if(!empty($order->TotalBillingCycles))
41
+ $order->TotalBillingCycles++;
42
+ }
43
+ else
44
+ {
45
+ //add a period to the start date to account for the initial payment
46
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
47
+ }
48
+
49
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
50
+ return $this->subscribe($order);
51
+ }
52
+ else
53
+ {
54
+ if(empty($order->error))
55
+ $order->error = __("Unknown error: Authorization failed.", "pmpro");
56
+ return false;
57
+ }
58
+ }
59
+ else
60
+ {
61
+ //charge first payment
62
+ if($this->charge($order))
63
+ {
64
+ //setup recurring billing
65
+ if(pmpro_isLevelRecurring($order->membership_level))
66
+ {
67
+ if(!pmpro_isLevelTrial($order->membership_level))
68
+ {
69
+ //subscription will start today with a 1 period trial
70
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
71
+ $order->TrialBillingPeriod = $order->BillingPeriod;
72
+ $order->TrialBillingFrequency = $order->BillingFrequency;
73
+ $order->TrialBillingCycles = 1;
74
+ $order->TrialAmount = 0;
75
+
76
+ //add a billing cycle to make up for the trial, if applicable
77
+ if(!empty($order->TotalBillingCycles))
78
+ $order->TotalBillingCycles++;
79
+ }
80
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
81
+ {
82
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
83
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
84
+ $order->TrialBillingCycles++;
85
+
86
+ //add a billing cycle to make up for the trial, if applicable
87
+ if(!empty($order->TotalBillingCycles))
88
+ $order->TotalBillingCycles++;
89
+ }
90
+ else
91
+ {
92
+ //add a period to the start date to account for the initial payment
93
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
94
+ }
95
+
96
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
97
+ if($this->subscribe($order))
98
+ {
99
+ return true;
100
+ }
101
+ else
102
+ {
103
+ if($this->void($order))
104
+ {
105
+ if(!$order->error)
106
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
107
+ }
108
+ else
109
+ {
110
+ if(!$order->error)
111
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
112
+ $order->error .= " " . __("A partial payment was made that we could not void. Please contact the site owner immediately to correct this.", "pmpro");
113
+ }
114
+
115
+ return false;
116
+ }
117
+ }
118
+ else
119
+ {
120
+ //only a one time charge
121
+ $order->status = "success"; //saved on checkout page
122
+ return true;
123
+ }
124
+ }
125
+ else
126
+ {
127
+ if(empty($order->error))
128
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
129
+
130
+ return false;
131
+ }
132
+ }
133
+ }
134
+
135
+ function authorize(&$order)
136
+ {
137
+ if(empty($order->code))
138
+ $order->code = $order->getRandomCode();
139
+
140
+ if(empty($order->gateway_environment))
141
+ $gateway_environment = pmpro_getOption("gateway_environment");
142
+ else
143
+ $gateway_environment = $order->gateway_environment;
144
+ if($gateway_environment == "live")
145
+ $host = "secure.authorize.net";
146
+ else
147
+ $host = "test.authorize.net";
148
+
149
+ $path = "/gateway/transact.dll";
150
+ $post_url = "https://" . $host . $path;
151
+
152
+ $post_url = apply_filters("pmpro_authorizenet_post_url", $post_url, $gateway_environment);
153
+
154
+ //what amount to authorize? just $1 to test
155
+ $amount = "1.00";
156
+
157
+ //combine address
158
+ $address = $order->Address1;
159
+ if(!empty($order->Address2))
160
+ $address .= "\n" . $order->Address2;
161
+
162
+ //customer stuff
163
+ $customer_email = $order->Email;
164
+ $customer_phone = $order->billing->phone;
165
+
166
+ if(!isset($order->membership_level->name))
167
+ $order->membership_level->name = "";
168
+
169
+ $post_values = array(
170
+
171
+ // the API Login ID and Transaction Key must be replaced with valid values
172
+ "x_login" => pmpro_getOption("loginname"),
173
+ "x_tran_key" => pmpro_getOption("transactionkey"),
174
+
175
+ "x_version" => "3.1",
176
+ "x_delim_data" => "TRUE",
177
+ "x_delim_char" => "|",
178
+ "x_relay_response" => "FALSE",
179
+
180
+ "x_type" => "AUTH_ONLY",
181
+ "x_method" => "CC",
182
+ "x_card_type" => $order->cardtype,
183
+ "x_card_num" => $order->accountnumber,
184
+ "x_exp_date" => $order->ExpirationDate,
185
+
186
+ "x_amount" => $amount,
187
+ "x_description" => $order->membership_level->name . " " . __("Membership", "pmpro"),
188
+
189
+ "x_first_name" => $order->FirstName,
190
+ "x_last_name" => $order->LastName,
191
+ "x_address" => $address,
192
+ "x_city" => $order->billing->city,
193
+ "x_state" => $order->billing->state,
194
+ "x_zip" => $order->billing->zip,
195
+ "x_country" => $order->billing->country,
196
+ "x_invoice_num" => $order->code,
197
+ "x_phone" => $customer_phone,
198
+ "x_email" => $order->Email
199
+ // Additional fields can be added here as outlined in the AIM integration
200
+ // guide at: http://developer.authorize.net
201
+ );
202
+
203
+ if(!empty($order->CVV2))
204
+ $post_values["x_card_code"] = $order->CVV2;
205
+
206
+ // This section takes the input fields and converts them to the proper format
207
+ // for an http post. For example: "x_login=username&x_tran_key=a1B2c3D4"
208
+ $post_string = "";
209
+ foreach( $post_values as $key => $value )
210
+ { $post_string .= "$key=" . urlencode( str_replace("#", "%23", $value) ) . "&"; }
211
+ $post_string = rtrim( $post_string, "& " );
212
+
213
+ //curl
214
+ $request = curl_init($post_url); // initiate curl object
215
+ curl_setopt($request, CURLOPT_HEADER, 0); // set to 0 to eliminate header info from response
216
+ curl_setopt($request, CURLOPT_RETURNTRANSFER, 1); // Returns response data instead of TRUE(1)
217
+ curl_setopt($request, CURLOPT_POSTFIELDS, $post_string); // use HTTP POST to send form data
218
+ curl_setopt($request, CURLOPT_SSL_VERIFYPEER, FALSE); // uncomment this line if you get no gateway response.
219
+ $post_response = curl_exec($request); // execute curl post and store results in $post_response
220
+ // additional options may be required depending upon your server configuration
221
+ // you can find documentation on curl options at http://www.php.net/curl_setopt
222
+ curl_close ($request); // close curl object
223
+
224
+ // This line takes the response and breaks it into an array using the specified delimiting character
225
+ $response_array = explode($post_values["x_delim_char"],$post_response);
226
+
227
+ if($response_array[0] == 1)
228
+ {
229
+ $order->payment_transaction_id = $response_array[6];
230
+ $order->updateStatus("authorized");
231
+
232
+ return true;
233
+ }
234
+ else
235
+ {
236
+ //$order->status = "error";
237
+ $order->errorcode = $response_array[2];
238
+ $order->error = $response_array[3];
239
+ $order->shorterror = $response_array[3];
240
+ return false;
241
+ }
242
+ }
243
+
244
+ function void(&$order)
245
+ {
246
+ if(empty($order->payment_transaction_id))
247
+ return false;
248
+
249
+ if(empty($order->gateway_environment))
250
+ $gateway_environment = pmpro_getOption("gateway_environment");
251
+ else
252
+ $gateway_environment = $order->gateway_environment;
253
+ if($gateway_environment == "live")
254
+ $host = "secure.authorize.net";
255
+ else
256
+ $host = "test.authorize.net";
257
+
258
+ $path = "/gateway/transact.dll";
259
+ $post_url = "https://" . $host . $path;
260
+
261
+ $post_url = apply_filters("pmpro_authorizenet_post_url", $post_url, $gateway_environment);
262
+
263
+ $post_values = array(
264
+
265
+ // the API Login ID and Transaction Key must be replaced with valid values
266
+ "x_login" => pmpro_getOption("loginname"),
267
+ "x_tran_key" => pmpro_getOption("transactionkey"),
268
+
269
+ "x_version" => "3.1",
270
+ "x_delim_data" => "TRUE",
271
+ "x_delim_char" => "|",
272
+ "x_relay_response" => "FALSE",
273
+
274
+ "x_type" => "VOID",
275
+ "x_trans_id" => $order->payment_transaction_id
276
+ // Additional fields can be added here as outlined in the AIM integration
277
+ // guide at: http://developer.authorize.net
278
+ );
279
+
280
+ // This section takes the input fields and converts them to the proper format
281
+ // for an http post. For example: "x_login=username&x_tran_key=a1B2c3D4"
282
+ $post_string = "";
283
+ foreach( $post_values as $key => $value )
284
+ { $post_string .= "$key=" . urlencode( str_replace("#", "%23", $value) ) . "&"; }
285
+ $post_string = rtrim( $post_string, "& " );
286
+
287
+ //curl
288
+ $request = curl_init($post_url); // initiate curl object
289
+ curl_setopt($request, CURLOPT_HEADER, 0); // set to 0 to eliminate header info from response
290
+ curl_setopt($request, CURLOPT_RETURNTRANSFER, 1); // Returns response data instead of TRUE(1)
291
+ curl_setopt($request, CURLOPT_POSTFIELDS, $post_string); // use HTTP POST to send form data
292
+ curl_setopt($request, CURLOPT_SSL_VERIFYPEER, FALSE); // uncomment this line if you get no gateway response.
293
+ $post_response = curl_exec($request); // execute curl post and store results in $post_response
294
+ // additional options may be required depending upon your server configuration
295
+ // you can find documentation on curl options at http://www.php.net/curl_setopt
296
+ curl_close ($request); // close curl object
297
+
298
+ // This line takes the response and breaks it into an array using the specified delimiting character
299
+ $response_array = explode($post_values["x_delim_char"],$post_response);
300
+ if($response_array[0] == 1)
301
+ {
302
+ $order->payment_transaction_id = $response_array[4];
303
+ $order->updateStatus("voided");
304
+ return true;
305
+ }
306
+ else
307
+ {
308
+ //$order->status = "error";
309
+ $order->errorcode = $response_array[2];
310
+ $order->error = $response_array[3];
311
+ $order->shorterror = $response_array[3];
312
+ return false;
313
+ }
314
+ }
315
+
316
+ function charge(&$order)
317
+ {
318
+ if(empty($order->code))
319
+ $order->code = $order->getRandomCode();
320
+
321
+ if(!empty($order->gateway_environment))
322
+ $gateway_environment = $order->gateway_environment;
323
+ if(empty($gateway_environment))
324
+ $gateway_environment = pmpro_getOption("gateway_environment");
325
+ if($gateway_environment == "live")
326
+ $host = "secure.authorize.net";
327
+ else
328
+ $host = "test.authorize.net";
329
+
330
+ $path = "/gateway/transact.dll";
331
+ $post_url = "https://" . $host . $path;
332
+
333
+ $post_url = apply_filters("pmpro_authorizenet_post_url", $post_url, $gateway_environment);
334
+
335
+ //what amount to charge?
336
+ $amount = $order->InitialPayment;
337
+
338
+ //tax
339
+ $order->subtotal = $amount;
340
+ $tax = $order->getTax(true);
341
+ $amount = round((float)$order->subtotal + (float)$tax, 2);
342
+
343
+ //combine address
344
+ $address = $order->Address1;
345
+ if(!empty($order->Address2))
346
+ $address .= "\n" . $order->Address2;
347
+
348
+ //customer stuff
349
+ $customer_email = $order->Email;
350
+ $customer_phone = $order->billing->phone;
351
+
352
+ if(!isset($order->membership_level->name))
353
+ $order->membership_level->name = "";
354
+
355
+ $post_values = array(
356
+
357
+ // the API Login ID and Transaction Key must be replaced with valid values
358
+ "x_login" => pmpro_getOption("loginname"),
359
+ "x_tran_key" => pmpro_getOption("transactionkey"),
360
+
361
+ "x_version" => "3.1",
362
+ "x_delim_data" => "TRUE",
363
+ "x_delim_char" => "|",
364
+ "x_relay_response" => "FALSE",
365
+
366
+ "x_type" => "AUTH_CAPTURE",
367
+ "x_method" => "CC",
368
+ "x_card_type" => $order->cardtype,
369
+ "x_card_num" => $order->accountnumber,
370
+ "x_exp_date" => $order->ExpirationDate,
371
+
372
+ "x_amount" => $amount,
373
+ "x_tax" => $tax,
374
+ "x_description" => $order->membership_level->name . " Membership",
375
+
376
+ "x_first_name" => $order->FirstName,
377
+ "x_last_name" => $order->LastName,
378
+ "x_address" => $address,
379
+ "x_city" => $order->billing->city,
380
+ "x_state" => $order->billing->state,
381
+ "x_zip" => $order->billing->zip,
382
+ "x_country" => $order->billing->country,
383
+ "x_invoice_num" => $order->code,
384
+ "x_phone" => $customer_phone,
385
+ "x_email" => $order->Email
386
+
387
+ // Additional fields can be added here as outlined in the AIM integration
388
+ // guide at: http://developer.authorize.net
389
+ );
390
+
391
+ if(!empty($order->CVV2))
392
+ $post_values["x_card_code"] = $order->CVV2;
393
+
394
+ // This section takes the input fields and converts them to the proper format
395
+ // for an http post. For example: "x_login=username&x_tran_key=a1B2c3D4"
396
+ $post_string = "";
397
+ foreach( $post_values as $key => $value )
398
+ { $post_string .= "$key=" . urlencode( str_replace("#", "%23", $value) ) . "&"; }
399
+ $post_string = rtrim( $post_string, "& " );
400
+
401
+ //curl
402
+ $request = curl_init($post_url); // initiate curl object
403
+ curl_setopt($request, CURLOPT_HEADER, 0); // set to 0 to eliminate header info from response
404
+ curl_setopt($request, CURLOPT_RETURNTRANSFER, 1); // Returns response data instead of TRUE(1)
405
+ curl_setopt($request, CURLOPT_POSTFIELDS, $post_string); // use HTTP POST to send form data
406
+ curl_setopt($request, CURLOPT_SSL_VERIFYPEER, FALSE); // uncomment this line if you get no gateway response.
407
+ $post_response = curl_exec($request); // execute curl post and store results in $post_response
408
+ // additional options may be required depending upon your server configuration
409
+ // you can find documentation on curl options at http://www.php.net/curl_setopt
410
+ curl_close ($request); // close curl object
411
+
412
+ // This line takes the response and breaks it into an array using the specified delimiting character
413
+ $response_array = explode($post_values["x_delim_char"],$post_response);
414
+ if($response_array[0] == 1)
415
+ {
416
+ $order->payment_transaction_id = $response_array[6];
417
+ $order->updateStatus("success");
418
+ return true;
419
+ }
420
+ else
421
+ {
422
+ //$order->status = "error";
423
+ $order->errorcode = $response_array[2];
424
+ $order->error = $response_array[3];
425
+ $order->shorterror = $response_array[3];
426
+ return false;
427
+ }
428
+ }
429
+
430
+ function subscribe(&$order)
431
+ {
432
+ //define variables to send
433
+
434
+ if(empty($order->code))
435
+ $order->code = $order->getRandomCode();
436
+
437
+ //filter order before subscription. use with care.
438
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
439
+
440
+ if(!empty($order->gateway_environment))
441
+ $gateway_environment = $order->gateway_environment;
442
+ if(empty($gateway_environment))
443
+ $gateway_environment = pmpro_getOption("gateway_environment");
444
+ if($gateway_environment == "live")
445
+ $host = "api.authorize.net";
446
+ else
447
+ $host = "apitest.authorize.net";
448
+
449
+ $path = "/xml/v1/request.api";
450
+
451
+ $loginname = pmpro_getOption("loginname");
452
+ $transactionkey = pmpro_getOption("transactionkey");
453
+
454
+ $amount = $order->PaymentAmount;
455
+ $refId = $order->code;
456
+ $name = $order->membership_name;
457
+ $length = (int)$order->BillingFrequency;
458
+
459
+ if($order->BillingPeriod == "Month")
460
+ $unit = "months";
461
+ elseif($order->BillingPeriod == "Day")
462
+ $unit = "days";
463
+ elseif($order->BillingPeriod == "Year" && $order->BillingFrequency == 1)
464
+ {
465
+ $unit = "months";
466
+ $length = 12;
467
+ }
468
+ elseif($order->BillingPeriod == "Week")
469
+ {
470
+ $unit = "days";
471
+ $length = $length * 7; //converting weeks to days
472
+ }
473
+ else
474
+ return false; //authorize.net only supports months and days
475
+
476
+ $startDate = substr($order->ProfileStartDate, 0, 10);
477
+ if(!empty($order->TotalBillingCycles))
478
+ $totalOccurrences = (int)$order->TotalBillingCycles;
479
+ if(empty($totalOccurrences))
480
+ $totalOccurrences = 9999;
481
+ if(isset($order->TrialBillingCycles))
482
+ $trialOccurrences = (int)$order->TrialBillingCycles;
483
+ else
484
+ $trialOccurrences = 0;
485
+ if(isset($order->TrialAmount))
486
+ $trialAmount = $order->TrialAmount;
487
+ else
488
+ $trialAmount = NULL;
489
+
490
+ //taxes
491
+ $amount_tax = $order->getTaxForPrice($amount);
492
+ $trial_tax = $order->getTaxForPrice($trialAmount);
493
+
494
+ $amount = round((float)$amount + (float)$amount_tax, 2);
495
+ $trialAmount = round((float)$trialAmount + (float)$trial_tax, 2);
496
+
497
+ //authorize.net doesn't support different periods between trial and actual
498
+
499
+ if(!empty($order->TrialBillingPeriod) && $order->TrialBillingPeriod != $order->BillingPeriod)
500
+ {
501
+ echo "F";
502
+ return false;
503
+ }
504
+
505
+ $cardNumber = $order->accountnumber;
506
+ $expirationDate = $order->ExpirationDate_YdashM;
507
+ $cardCode = $order->CVV2;
508
+
509
+ $firstName = $order->FirstName;
510
+ $lastName = $order->LastName;
511
+
512
+ //do address stuff then?
513
+ $address = $order->Address1;
514
+ if(!empty($order->Address2))
515
+ $address .= "\n" . $order->Address2;
516
+ $city = $order->billing->city;
517
+ $state = $order->billing->state;
518
+ $zip = $order->billing->zip;
519
+ $country = $order->billing->country;
520
+
521
+ //customer stuff
522
+ $customer_email = $order->Email;
523
+ if(strpos($order->billing->phone, "+") === false)
524
+ $customer_phone = $order->billing->phone;
525
+ else
526
+ $customer_phone = "";
527
+
528
+ //make sure the phone is in an okay format
529
+ $customer_phone = preg_replace("/[^0-9]/", "", $customer_phone);
530
+ if(strlen($customer_phone) > 10)
531
+ $customer_phone = "";
532
+
533
+ //build xml to post
534
+ $this->content =
535
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
536
+ "<ARBCreateSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">" .
537
+ "<merchantAuthentication>".
538
+ "<name>" . $loginname . "</name>".
539
+ "<transactionKey>" . $transactionkey . "</transactionKey>".
540
+ "</merchantAuthentication>".
541
+ "<refId><![CDATA[" . substr($refId, 0, 20) . "]]></refId>".
542
+ "<subscription>".
543
+ "<name><![CDATA[" . substr($name, 0, 50) . "]]></name>".
544
+ "<paymentSchedule>".
545
+ "<interval>".
546
+ "<length>". $length ."</length>".
547
+ "<unit>". $unit ."</unit>".
548
+ "</interval>".
549
+ "<startDate>" . $startDate . "</startDate>".
550
+ "<totalOccurrences>". $totalOccurrences . "</totalOccurrences>";
551
+ if(!empty($trialOccurrences))
552
+ $this->content .=
553
+ "<trialOccurrences>". $trialOccurrences . "</trialOccurrences>";
554
+ $this->content .=
555
+ "</paymentSchedule>".
556
+ "<amount>". $amount ."</amount>";
557
+ if(!empty($trialOccurrences))
558
+ $this->content .=
559
+ "<trialAmount>" . $trialAmount . "</trialAmount>";
560
+ $this->content .=
561
+ "<payment>".
562
+ "<creditCard>".
563
+ "<cardNumber>" . $cardNumber . "</cardNumber>".
564
+ "<expirationDate>" . $expirationDate . "</expirationDate>";
565
+ if(!empty($cardCode))
566
+ $this->content .= "<cardCode>" . $cardCode . "</cardCode>";
567
+ $this->content .=
568
+ "</creditCard>".
569
+ "</payment>".
570
+ "<order><invoiceNumber>" . substr($order->code, 0, 20) . "</invoiceNumber></order>".
571
+ "<customer>".
572
+ "<email>". substr($customer_email, 0, 255) . "</email>".
573
+ "<phoneNumber>". substr($customer_phone, 0, 25) . "</phoneNumber>".
574
+ "</customer>".
575
+ "<billTo>".
576
+ "<firstName><![CDATA[". substr($firstName, 0, 50) . "]]></firstName>".
577
+ "<lastName><![CDATA[" . substr($lastName, 0, 50) . "]]></lastName>".
578
+ "<address><![CDATA[". substr($address, 0, 60) . "]]></address>".
579
+ "<city><![CDATA[" . substr($city, 0, 40) . "]]></city>".
580
+ "<state>". substr($state, 0, 2) . "</state>".
581
+ "<zip>" . substr($zip, 0, 20) . "</zip>".
582
+ "<country>". substr($country, 0, 60) . "</country>".
583
+ "</billTo>".
584
+ "</subscription>".
585
+ "</ARBCreateSubscriptionRequest>";
586
+
587
+ //send the xml via curl
588
+ $this->response = $this->send_request_via_curl($host,$path,$this->content);
589
+ //if curl is unavilable you can try using fsockopen
590
+ /*
591
+ $response = send_request_via_fsockopen($host,$path,$content);
592
+ */
593
+
594
+ if(!empty($this->response)) {
595
+ list ($refId, $resultCode, $code, $text, $subscriptionId) = $this->parse_return($this->response);
596
+ if($resultCode == "Ok")
597
+ {
598
+ $order->status = "success"; //saved on checkout page
599
+ $order->subscription_transaction_id = $subscriptionId;
600
+ return true;
601
+ }
602
+ else
603
+ {
604
+ $order->status = "error";
605
+ $order->errorcode = $code;
606
+ $order->error = $text;
607
+ $order->shorterror = $text;
608
+ return false;
609
+ }
610
+ } else {
611
+ $order->status = "error";
612
+ $order->error = "Could not connect to Authorize.net";
613
+ $order->shorterror = "Could not connect to Authorize.net";
614
+ return false;
615
+ }
616
+ }
617
+
618
+ function update(&$order)
619
+ {
620
+ //define variables to send
621
+ $gateway_environment = $order->gateway_environment;
622
+ if(empty($gateway_environment))
623
+ $gateway_environment = pmpro_getOption("gateway_environment");
624
+ if($gateway_environment == "live")
625
+ $host = "api.authorize.net";
626
+ else
627
+ $host = "apitest.authorize.net";
628
+
629
+ $path = "/xml/v1/request.api";
630
+
631
+ $loginname = pmpro_getOption("loginname");
632
+ $transactionkey = pmpro_getOption("transactionkey");
633
+
634
+ //$amount = $order->PaymentAmount;
635
+ $refId = $order->code;
636
+ $subscriptionId = $order->subscription_transaction_id;
637
+
638
+ $cardNumber = $order->accountnumber;
639
+ $expirationDate = $order->ExpirationDate_YdashM;
640
+ $cardCode = $order->CVV2;
641
+
642
+ $firstName = $order->FirstName;
643
+ $lastName = $order->LastName;
644
+
645
+ //do address stuff then?
646
+ $address = $order->Address1;
647
+ if(!empty($order->Address2))
648
+ $address .= "\n" . $order->Address2;
649
+ $city = $order->billing->city;
650
+ $state = $order->billing->state;
651
+ $zip = $order->billing->zip;
652
+ $country = $order->billing->country;
653
+
654
+ //customer stuff
655
+ $customer_email = $order->Email;
656
+ if(strpos($order->billing->phone, "+") === false)
657
+ $customer_phone = $order->billing->phone;
658
+
659
+
660
+ //build xml to post
661
+ $this->content =
662
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
663
+ "<ARBUpdateSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">".
664
+ "<merchantAuthentication>".
665
+ "<name><![CDATA[" . $loginname . "]]></name>".
666
+ "<transactionKey>" . $transactionkey . "</transactionKey>".
667
+ "</merchantAuthentication>".
668
+ "<refId>" . substr($refId, 0, 20) . "</refId>".
669
+ "<subscriptionId>" . $subscriptionId . "</subscriptionId>".
670
+ "<subscription>".
671
+ "<payment>".
672
+ "<creditCard>".
673
+ "<cardNumber>" . $cardNumber . "</cardNumber>".
674
+ "<expirationDate>" . $expirationDate . "</expirationDate>";
675
+ if(!empty($cardCode))
676
+ $this->content .= "<cardCode>" . $cardCode . "</cardCode>";
677
+ $this->content .=
678
+ "</creditCard>".
679
+ "</payment>".
680
+ "<customer>".
681
+ "<email>". substr($customer_email, 0, 255) . "</email>".
682
+ "<phoneNumber>". substr(str_replace("1 (", "(", formatPhone($customer_phone)), 0, 25) . "</phoneNumber>".
683
+ "</customer>".
684
+ "<billTo>".
685
+ "<firstName><![CDATA[". substr($firstName, 0, 50) . "]]></firstName>".
686
+ "<lastName><![CDATA[" . substr($lastName, 0, 50) . "]]></lastName>".
687
+ "<address><![CDATA[". substr($address, 0, 60) . "]]></address>".
688
+ "<city><![CDATA[" . substr($city, 0, 40) . "]]></city>".
689
+ "<state><![CDATA[". substr($state, 0, 2) . "]]></state>".
690
+ "<zip>" . substr($zip, 0, 20) . "</zip>".
691
+ "<country>". substr($country, 0, 60) . "</country>".
692
+ "</billTo>".
693
+ "</subscription>".
694
+ "</ARBUpdateSubscriptionRequest>";
695
+
696
+ //send the xml via curl
697
+ $this->response = $this->send_request_via_curl($host,$path,$this->content);
698
+ //if curl is unavilable you can try using fsockopen
699
+ /*
700
+ $response = send_request_via_fsockopen($host,$path,$order->content);
701
+ */
702
+
703
+
704
+ if(!empty($this->response)) {
705
+ list ($resultCode, $code, $text, $subscriptionId) = $this->parse_return($this->response);
706
+
707
+ if($resultCode == "Ok" || $code == "Ok")
708
+ {
709
+ return true;
710
+ }
711
+ else
712
+ {
713
+ $order->status = "error";
714
+ $order->errorcode = $code;
715
+ $order->error = $text;
716
+ $order->shorterror = $text;
717
+ return false;
718
+ }
719
+ } else {
720
+ $order->status = "error";
721
+ $order->error = "Could not connect to Authorize.net";
722
+ $order->shorterror = "Could not connect to Authorize.net";
723
+ return false;
724
+ }
725
+ }
726
+
727
+ function cancel(&$order)
728
+ {
729
+ //define variables to send
730
+ $subscriptionId = $order->subscription_transaction_id;
731
+ $loginname = pmpro_getOption("loginname");
732
+ $transactionkey = pmpro_getOption("transactionkey");
733
+
734
+ $gateway_environment = $order->gateway_environment;
735
+ if(empty($gateway_environment))
736
+ $gateway_environment = pmpro_getOption("gateway_environment");
737
+ if($gateway_environment == "live")
738
+ $host = "api.authorize.net";
739
+ else
740
+ $host = "apitest.authorize.net";
741
+
742
+ $path = "/xml/v1/request.api";
743
+
744
+ if(!$subscriptionId || !$loginname || !$transactionkey)
745
+ return false;
746
+
747
+ //build xml to post
748
+ $content =
749
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>".
750
+ "<ARBCancelSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">".
751
+ "<merchantAuthentication>".
752
+ "<name>" . $loginname . "</name>".
753
+ "<transactionKey>" . $transactionkey . "</transactionKey>".
754
+ "</merchantAuthentication>" .
755
+ "<subscriptionId>" . $subscriptionId . "</subscriptionId>".
756
+ "</ARBCancelSubscriptionRequest>";
757
+
758
+ //send the xml via curl
759
+ $response = $this->send_request_via_curl($host,$path,$content);
760
+ //if curl is unavilable you can try using fsockopen
761
+ /*
762
+ $response = send_request_via_fsockopen($host,$path,$content);
763
+ */
764
+
765
+ //if the connection and send worked $response holds the return from Authorize.net
766
+ if ($response)
767
+ {
768
+ list ($resultCode, $code, $text, $subscriptionId) = $this->parse_return($response);
769
+
770
+ if($resultCode == "Ok" || $code == "Ok")
771
+ {
772
+ $order->updateStatus("cancelled");
773
+ return true;
774
+ }
775
+ else
776
+ {
777
+ //$order->status = "error";
778
+ $order->errorcode = $code;
779
+ $order->error = $text;
780
+ $order->shorterror = $text;
781
+ return false;
782
+ }
783
+ }
784
+ else
785
+ {
786
+ $order->status = "error";
787
+ $order->error = __("Could not connect to Authorize.net", "pmpro");
788
+ $order->shorterror = __("Could not connect to Authorize.net", "pmpro");
789
+ return false;
790
+ }
791
+ }
792
+
793
+ //Authorize.net Function
794
+ //function to send xml request via fsockopen
795
+ function send_request_via_fsockopen($host,$path,$content)
796
+ {
797
+ $posturl = "ssl://" . $host;
798
+ $header = "Host: $host\r\n";
799
+ $header .= "User-Agent: PHP Script\r\n";
800
+ $header .= "Content-Type: text/xml\r\n";
801
+ $header .= "Content-Length: ".strlen($content)."\r\n";
802
+ $header .= "Connection: close\r\n\r\n";
803
+ $fp = fsockopen($posturl, 443, $errno, $errstr, 30);
804
+ if (!$fp)
805
+ {
806
+ $response = false;
807
+ }
808
+ else
809
+ {
810
+ error_reporting(E_ERROR);
811
+ fputs($fp, "POST $path HTTP/1.1\r\n");
812
+ fputs($fp, $header.$content);
813
+ fwrite($fp, $out);
814
+ $response = "";
815
+ while (!feof($fp))
816
+ {
817
+ $response = $response . fgets($fp, 128);
818
+ }
819
+ fclose($fp);
820
+ error_reporting(E_ALL ^ E_NOTICE);
821
+ }
822
+ return $response;
823
+ }
824
+
825
+ //Authorize.net Function
826
+ //function to send xml request via curl
827
+ function send_request_via_curl($host,$path,$content)
828
+ {
829
+ $posturl = "https://" . $host . $path;
830
+ $posturl = apply_filters("pmpro_authorizenet_post_url", $posturl, pmpro_getOption("gateway_environment"));
831
+ $ch = curl_init();
832
+ curl_setopt($ch, CURLOPT_URL, $posturl);
833
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
834
+ curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml"));
835
+ curl_setopt($ch, CURLOPT_HEADER, 1);
836
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
837
+ curl_setopt($ch, CURLOPT_POST, 1);
838
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
839
+ $response = curl_exec($ch);
840
+ return $response;
841
+ }
842
+
843
+
844
+ //Authorize.net Function
845
+ //function to parse Authorize.net response
846
+ function parse_return($content)
847
+ {
848
+ $refId = $this->substring_between($content,'<refId>','</refId>');
849
+ $resultCode = $this->substring_between($content,'<resultCode>','</resultCode>');
850
+ $code = $this->substring_between($content,'<code>','</code>');
851
+ $text = $this->substring_between($content,'<text>','</text>');
852
+ $subscriptionId = $this->substring_between($content,'<subscriptionId>','</subscriptionId>');
853
+ return array ($refId, $resultCode, $code, $text, $subscriptionId);
854
+ }
855
+
856
+ //Authorize.net Function
857
+ //helper function for parsing response
858
+ function substring_between($haystack,$start,$end)
859
+ {
860
+ if (strpos($haystack,$start) === false || strpos($haystack,$end) === false)
861
+ {
862
+ return false;
863
+ }
864
+ else
865
+ {
866
+ $start_position = strpos($haystack,$start)+strlen($start);
867
+ $end_position = strpos($haystack,$end);
868
+ return substr($haystack,$start_position,$end_position-$start_position);
869
+ }
870
+ }
871
+ }
872
+ ?>
classes/gateways/class.pmprogateway_braintree.php ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ if(!class_exists("Braintree"))
4
+ require_once(dirname(__FILE__) . "/../../includes/lib/Braintree/Braintree.php");
5
+ class PMProGateway_braintree
6
+ {
7
+ function PMProGateway_braintree($gateway = NULL)
8
+ {
9
+ $this->gateway = $gateway;
10
+ $this->gateway_environment = pmpro_getOption("gateway_environment");
11
+
12
+ //convert to braintree nomenclature
13
+ $environment = $this->gateway_environment;
14
+ if($environment == "live")
15
+ $environment = "production";
16
+
17
+ Braintree_Configuration::environment($environment);
18
+ Braintree_Configuration::merchantId(pmpro_getOption("braintree_merchantid"));
19
+ Braintree_Configuration::publicKey(pmpro_getOption("braintree_publickey"));
20
+ Braintree_Configuration::privateKey(pmpro_getOption("braintree_privatekey"));
21
+
22
+ return $this->gateway;
23
+ }
24
+
25
+ function process(&$order)
26
+ {
27
+ //check for initial payment
28
+ if(floatval($order->InitialPayment) == 0)
29
+ {
30
+ //just subscribe
31
+ return $this->subscribe($order);
32
+ }
33
+ else
34
+ {
35
+ //charge then subscribe
36
+ if($this->charge($order))
37
+ {
38
+ if(pmpro_isLevelRecurring($order->membership_level))
39
+ {
40
+ if($this->subscribe($order))
41
+ {
42
+ //yay!
43
+ return true;
44
+ }
45
+ else
46
+ {
47
+ //try to refund initial charge
48
+ return false;
49
+ }
50
+ }
51
+ else
52
+ {
53
+ //only a one time charge
54
+ $order->status = "success"; //saved on checkout page
55
+ return true;
56
+ }
57
+ }
58
+ else
59
+ {
60
+ if(empty($order->error))
61
+ $order->error = __("Unknown error: Initial payment failed.", "pmpro");
62
+ return false;
63
+ }
64
+ }
65
+ }
66
+
67
+ function charge(&$order)
68
+ {
69
+ //create a code for the order
70
+ if(empty($order->code))
71
+ $order->code = $order->getRandomCode();
72
+
73
+ //what amount to charge?
74
+ $amount = $order->InitialPayment;
75
+
76
+ //tax
77
+ $order->subtotal = $amount;
78
+ $tax = $order->getTax(true);
79
+ $amount = round((float)$order->subtotal + (float)$tax, 2);
80
+
81
+ //create a customer
82
+ $this->getCustomer($order);
83
+ if(empty($this->customer))
84
+ {
85
+ //failed to create customer
86
+ return false;
87
+ }
88
+
89
+ //charge
90
+ try
91
+ {
92
+ $response = Braintree_Transaction::sale(array(
93
+ 'amount' => $amount,
94
+ 'customerId' => $this->customer->id
95
+ ));
96
+ }
97
+ catch (Exception $e)
98
+ {
99
+ //$order->status = "error";
100
+ $order->errorcode = true;
101
+ $order->error = "Error: " . $e->getMessage();
102
+ $order->shorterror = $order->error;
103
+ return false;
104
+ }
105
+
106
+ if($response->success)
107
+ {
108
+ //successful charge
109
+ $transaction_id = $response->transaction->id;
110
+ $response = Braintree_Transaction::submitForSettlement($transaction_id);
111
+ if($response->success)
112
+ {
113
+ $order->payment_transaction_id = $transaction_id;
114
+ $order->updateStatus("success");
115
+ return true;
116
+ }
117
+ else
118
+ {
119
+ $order->errorcode = true;
120
+ $order->error = __("Error during settlement:", "pmpro") . " " . $response->message;
121
+ $order->shorterror = $response->message;
122
+ return false;
123
+ }
124
+ }
125
+ else
126
+ {
127
+ //$order->status = "error";
128
+ $order->errorcode = true;
129
+ $order->error = __("Error during charge:", "pmpro") . " " . $response->message;
130
+ $order->shorterror = $response->message;
131
+ return false;
132
+ }
133
+ }
134
+
135
+ /*
136
+ This function will return a Braintree customer object.
137
+ If $this->customer is set, it returns it.
138
+ It first checks if the order has a subscription_transaction_id. If so, that's the customer id.
139
+ If not, it checks for a user_id on the order and searches for a customer id in the user meta.
140
+ If a customer id is found, it checks for a customer through the Braintree API.
141
+ If a customer is found and there is an AccountNumber on the order passed, it will update the customer.
142
+ If no customer is found and there is an AccountNumber on the order passed, it will create a customer.
143
+ */
144
+ function getCustomer(&$order, $force = false)
145
+ {
146
+ global $current_user;
147
+
148
+ //already have it?
149
+ if(!empty($this->customer) && !$force)
150
+ return $this->customer;
151
+
152
+ //try based on user id
153
+ if(!empty($order->user_id))
154
+ $user_id = $order->user_id;
155
+
156
+ //if no id passed, check the current user
157
+ if(empty($user_id) && !empty($current_user->ID))
158
+ $user_id = $current_user->ID;
159
+
160
+ //check for a braintree customer id
161
+ if(!empty($user_id))
162
+ {
163
+ $customer_id = get_user_meta($user_id, "pmpro_braintree_customerid", true);
164
+ }
165
+
166
+ //check for an existing stripe customer
167
+ if(!empty($customer_id))
168
+ {
169
+ try
170
+ {
171
+ $this->customer = Braintree_Customer::find($customer_id);
172
+
173
+ //update the customer description and card
174
+ if(!empty($order->accountnumber))
175
+ {
176
+ $response = Braintree_Customer::update(
177
+ $customer_id,
178
+ array(
179
+ 'firstName' => $order->FirstName,
180
+ 'lastName' => $order->LastName,
181
+ 'creditCard' => array(
182
+ 'number' => $order->braintree->number,
183
+ 'expirationDate' => $order->braintree->expiration_date,
184
+ 'cardholderName' => trim($order->FirstName . " " . $order->LastName),
185
+ 'options' => array(
186
+ 'updateExistingToken' => $customer_id
187
+ )
188
+ )
189
+ )
190
+ );
191
+
192
+ if($response->success)
193
+ {
194
+ $this->customer = $result->customer;
195
+ }
196
+ else
197
+ {
198
+ $order->error = __("Failed to update customer.", "pmpro");
199
+ $order->shorterror = $order->error;
200
+ return false;
201
+ }
202
+ }
203
+
204
+ return $this->customer;
205
+ }
206
+ catch (Exception $e)
207
+ {
208
+ //assume no customer found
209
+ }
210
+ }
211
+
212
+ //no customer id, create one
213
+ if(!empty($order->accountnumber))
214
+ {
215
+ try
216
+ {
217
+ $result = Braintree_Customer::create(array(
218
+ 'firstName' => $order->FirstName,
219
+ 'lastName' => $order->LastName,
220
+ 'email' => $order->Email,
221
+ 'phone' => $order->billing->phone,
222
+ 'creditCard' => array(
223
+ 'number' => $order->braintree->number,
224
+ 'expirationDate' => $order->braintree->expiration_date,
225
+ 'cvv' => $order->braintree->cvv,
226
+ 'cardholderName' => trim($order->FirstName . " " . $order->LastName),
227
+ 'billingAddress' => array(
228
+ 'firstName' => $order->FirstName,
229
+ 'lastName' => $order->LastName,
230
+ 'streetAddress' => $order->Address1,
231
+ 'extendedAddress' => $order->Address2,
232
+ 'locality' => $order->billing->city,
233
+ 'region' => $order->billing->state,
234
+ 'postalCode' => $order->billing->zip,
235
+ 'countryCodeAlpha2' => $order->billing->country
236
+ )
237
+ )
238
+ ));
239
+
240
+ if($result->success)
241
+ {
242
+ $this->customer = $result->customer;
243
+ }
244
+ else
245
+ {
246
+ $order->error = __("Failed to create customer.", "pmpro");
247
+ $order->shorterror = $order->error;
248
+ return false;
249
+ }
250
+ }
251
+ catch (Exception $e)
252
+ {
253
+ $order->error = __("Error creating customer record with Braintree:", "pmpro") . " " . $e->getMessage();
254
+ $order->shorterror = $order->error;
255
+ return false;
256
+ }
257
+
258
+ update_user_meta($user_id, "pmpro_braintree_customerid", $this->customer->id);
259
+ return $this->customer;
260
+ }
261
+
262
+ return false;
263
+ }
264
+
265
+ function subscribe(&$order)
266
+ {
267
+ //create a code for the order
268
+ if(empty($order->code))
269
+ $order->code = $order->getRandomCode();
270
+
271
+ //setup customer
272
+ $this->getCustomer($order);
273
+ if(empty($this->customer))
274
+ return false; //error retrieving customer
275
+
276
+ //figure out the amounts
277
+ $amount = $order->PaymentAmount;
278
+ $amount_tax = $order->getTaxForPrice($amount);
279
+ $amount = round((float)$amount + (float)$amount_tax, 2);
280
+
281
+ /*
282
+ There are two parts to the trial. Part 1 is simply the delay until the first payment
283
+ since we are doing the first payment as a separate transaction.
284
+ The second part is the actual "trial" set by the admin.
285
+
286
+ Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
287
+ */
288
+ //figure out the trial length (first payment handled by initial charge)
289
+ if($order->BillingPeriod == "Year")
290
+ $trial_period_days = $order->BillingFrequency * 365; //annual
291
+ elseif($order->BillingPeriod == "Day")
292
+ $trial_period_days = $order->BillingFrequency * 1; //daily
293
+ elseif($order->BillingPeriod == "Week")
294
+ $trial_period_days = $order->BillingFrequency * 7; //weekly
295
+ else
296
+ $trial_period_days = $order->BillingFrequency * 30; //assume monthly
297
+
298
+ //convert to a profile start date
299
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day")) . "T0:0:0";
300
+
301
+ //filter the start date
302
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
303
+
304
+ //convert back to days
305
+ $trial_period_days = ceil(abs(strtotime(date("Y-m-d")) - strtotime($order->ProfileStartDate)) / 86400);
306
+
307
+ //now add the actual trial set by the site
308
+ if(!empty($order->TrialBillingCycles))
309
+ {
310
+ $trialOccurrences = (int)$order->TrialBillingCycles;
311
+ if($order->BillingPeriod == "Year")
312
+ $trial_period_days = $trial_period_days + (365 * $order->BillingFrequency * $trialOccurrences); //annual
313
+ elseif($order->BillingPeriod == "Day")
314
+ $trial_period_days = $trial_period_days + (1 * $order->BillingFrequency * $trialOccurrences); //daily
315
+ elseif($order->BillingPeriod == "Week")
316
+ $trial_period_days = $trial_period_days + (7 * $order->BillingFrequency * $trialOccurrences); //weekly
317
+ else
318
+ $trial_period_days = $trial_period_days + (30 * $order->BillingFrequency * $trialOccurrences); //assume monthly
319
+ }
320
+
321
+ //subscribe to the plan
322
+ try
323
+ {
324
+ $details = array(
325
+ 'paymentMethodToken' => $this->customer->creditCards[0]->token,
326
+ 'planId' => 'pmpro_' . $order->membership_id,
327
+ 'price' => $amount
328
+ );
329
+
330
+ if(!empty($trial_period_days))
331
+ {
332
+ $details['trialPeriod'] = true;
333
+ $details['trialDuration'] = $trial_period_days;
334
+ $details['trialDurationUnit'] = "day";
335
+ }
336
+
337
+ if(!empty($order->TotalBillingCycles))
338
+ $details['numberOfBillingCycles'] = $order->TotalBillingCycles;
339
+
340
+ $result = Braintree_Subscription::create($details);
341
+ }
342
+ catch (Exception $e)
343
+ {
344
+ $order->error = __("Error subscribing customer to plan with Braintree:", "pmpro") . " " . $e->getMessage();
345
+ //return error
346
+ $order->shorterror = $order->error;
347
+ return false;
348
+ }
349
+
350
+ if($result->success)
351
+ {
352
+ //if we got this far, we're all good
353
+ $order->status = "success";
354
+ $order->subscription_transaction_id = $result->subscription->id;
355
+ return true;
356
+ }
357
+ else
358
+ {
359
+ $order->error = __("Failed to subscribe with Braintree:", "pmpro") . " " . $result->message;
360
+ $order->shorterror = $result->message;
361
+ return false;
362
+ }
363
+ }
364
+
365
+ function update(&$order)
366
+ {
367
+ //we just have to run getCustomer which will look for the customer and update it with the new token
368
+ $this->getCustomer($order);
369
+
370
+ if(!empty($this->customer))
371
+ {
372
+ return true;
373
+ }
374
+ else
375
+ {
376
+ return false; //couldn't find the customer
377
+ }
378
+ }
379
+
380
+ function cancel(&$order)
381
+ {
382
+ //require a subscription id
383
+ if(empty($order->subscription_transaction_id))
384
+ return false;
385
+
386
+ //find the customer
387
+ if(!empty($order->subscription_transaction_id))
388
+ {
389
+ //cancel
390
+ try
391
+ {
392
+ $result = Braintree_Subscription::cancel($order->subscription_transaction_id);
393
+ }
394
+ catch(Exception $e)
395
+ {
396
+ $order->updateStatus("cancelled"); //assume it's been cancelled already
397
+ $order->error = __("Could not find the subscription.", "pmpro");
398
+ $order->shorterror = $order->error;
399
+ return false; //no subscription found
400
+ }
401
+
402
+ if($result->success)
403
+ {
404
+ $order->updateStatus("cancelled");
405
+ return true;
406
+ }
407
+ else
408
+ {
409
+ $order->updateStatus("cancelled"); //assume it's been cancelled already
410
+ $order->error = __("Could not find the subscription.", "pmpro");
411
+ $order->shorterror = $order->error;
412
+ return false; //no subscription found
413
+ }
414
+ }
415
+ else
416
+ {
417
+ $order->error = __("Could not find the subscription.", "pmpro");
418
+ $order->shorterror = $order->error;
419
+ return false; //no customer found
420
+ }
421
+ }
422
+ }
classes/gateways/class.pmprogateway_check.php ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway_check
4
+ {
5
+ function PMProGateway_check($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ //clean up a couple values
14
+ $order->payment_type = "Check";
15
+ $order->CardType = "";
16
+ $order->cardtype = "";
17
+
18
+ //check for initial payment
19
+ if(floatval($order->InitialPayment) == 0)
20
+ {
21
+ //auth first, then process
22
+ if($this->authorize($order))
23
+ {
24
+ $this->void($order);
25
+ if(!pmpro_isLevelTrial($order->membership_level))
26
+ {
27
+ //subscription will start today with a 1 period trial
28
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
29
+ $order->TrialBillingPeriod = $order->BillingPeriod;
30
+ $order->TrialBillingFrequency = $order->BillingFrequency;
31
+ $order->TrialBillingCycles = 1;
32
+ $order->TrialAmount = 0;
33
+
34
+ //add a billing cycle to make up for the trial, if applicable
35
+ if(!empty($order->TotalBillingCycles))
36
+ $order->TotalBillingCycles++;
37
+ }
38
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
39
+ {
40
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
41
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
42
+ $order->TrialBillingCycles++;
43
+
44
+ //add a billing cycle to make up for the trial, if applicable
45
+ if($order->TotalBillingCycles)
46
+ $order->TotalBillingCycles++;
47
+ }
48
+ else
49
+ {
50
+ //add a period to the start date to account for the initial payment
51
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
52
+ }
53
+
54
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
55
+ return $this->subscribe($order);
56
+ }
57
+ else
58
+ {
59
+ if(empty($order->error))
60
+ $order->error = __("Unknown error: Authorization failed.", "pmpro");
61
+ return false;
62
+ }
63
+ }
64
+ else
65
+ {
66
+ //charge first payment
67
+ if($this->charge($order))
68
+ {
69
+ //setup recurring billing
70
+ if(pmpro_isLevelRecurring($order->membership_level))
71
+ {
72
+ if(!pmpro_isLevelTrial($order->membership_level))
73
+ {
74
+ //subscription will start today with a 1 period trial
75
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
76
+ $order->TrialBillingPeriod = $order->BillingPeriod;
77
+ $order->TrialBillingFrequency = $order->BillingFrequency;
78
+ $order->TrialBillingCycles = 1;
79
+ $order->TrialAmount = 0;
80
+
81
+ //add a billing cycle to make up for the trial, if applicable
82
+ if(!empty($order->TotalBillingCycles))
83
+ $order->TotalBillingCycles++;
84
+ }
85
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
86
+ {
87
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
88
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
89
+ $order->TrialBillingCycles++;
90
+
91
+ //add a billing cycle to make up for the trial, if applicable
92
+ if(!empty($order->TotalBillingCycles))
93
+ $order->TotalBillingCycles++;
94
+ }
95
+ else
96
+ {
97
+ //add a period to the start date to account for the initial payment
98
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $this->BillingFrequency . " " . $this->BillingPeriod)) . "T0:0:0";
99
+ }
100
+
101
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
102
+ if($this->subscribe($order))
103
+ {
104
+ return true;
105
+ }
106
+ else
107
+ {
108
+ if($this->void($order))
109
+ {
110
+ if(!$order->error)
111
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
112
+ }
113
+ else
114
+ {
115
+ if(!$order->error)
116
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
117
+
118
+ $order->error .= " " . __("A partial payment was made that we could not void. Please contact the site owner immediately to correct this.", "pmpro");
119
+ }
120
+
121
+ return false;
122
+ }
123
+ }
124
+ else
125
+ {
126
+ //only a one time charge
127
+ $order->status = apply_filters("pmpro_check_status_after_checkout", "success"); //saved on checkout page
128
+ return true;
129
+ }
130
+ }
131
+ else
132
+ {
133
+ if(empty($order->error))
134
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
135
+
136
+ return false;
137
+ }
138
+ }
139
+ }
140
+
141
+ function authorize(&$order)
142
+ {
143
+ //create a code for the order
144
+ if(empty($order->code))
145
+ $order->code = $order->getRandomCode();
146
+
147
+ //simulate a successful authorization
148
+ $order->payment_transaction_id = "CHECK" . $order->code;
149
+ $order->updateStatus("authorized");
150
+ return true;
151
+ }
152
+
153
+ function void(&$order)
154
+ {
155
+ //need a transaction id
156
+ if(empty($order->payment_transaction_id))
157
+ return false;
158
+
159
+ //simulate a successful void
160
+ $order->payment_transaction_id = "CHECK" . $order->code;
161
+ $order->updateStatus("voided");
162
+ return true;
163
+ }
164
+
165
+ function charge(&$order)
166
+ {
167
+ //create a code for the order
168
+ if(empty($order->code))
169
+ $order->code = $order->getRandomCode();
170
+
171
+ //simulate a successful charge
172
+ $order->payment_transaction_id = "CHECK" . $order->code;
173
+ $order->updateStatus("success");
174
+ return true;
175
+ }
176
+
177
+ function subscribe(&$order)
178
+ {
179
+ //create a code for the order
180
+ if(empty($order->code))
181
+ $order->code = $order->getRandomCode();
182
+
183
+ //filter order before subscription. use with care.
184
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
185
+
186
+ //simulate a successful subscription processing
187
+ $order->status = "success";
188
+ $order->subscription_transaction_id = "CHECK" . $order->code;
189
+ return true;
190
+ }
191
+
192
+ function update(&$order)
193
+ {
194
+ //simulate a successful billing update
195
+ return true;
196
+ }
197
+
198
+ function cancel(&$order)
199
+ {
200
+ //require a subscription id
201
+ if(empty($order->subscription_transaction_id))
202
+ return false;
203
+
204
+ //simulate a successful cancel
205
+ $order->updateStatus("cancelled");
206
+ return true;
207
+ }
208
+ }
classes/gateways/class.pmprogateway_cybersource.php ADDED
@@ -0,0 +1,765 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ if(!class_exists("CyberSourceSoapClient"))
4
+ require_once(dirname(__FILE__) . "/../../includes/lib/CyberSource/cyber_source_soap_client.php");
5
+ class PMProGateway_cybersource
6
+ {
7
+ function PMProGateway_cybersource($gateway = NULL)
8
+ {
9
+ $this->gateway = $gateway;
10
+ return $this->gateway;
11
+ }
12
+
13
+ function process(&$order)
14
+ {
15
+ //check for initial payment
16
+ if(floatval($order->InitialPayment) == 0)
17
+ {
18
+ //auth first, then process
19
+ if($this->authorize($order))
20
+ {
21
+ $this->void($order);
22
+ if(!pmpro_isLevelTrial($order->membership_level))
23
+ {
24
+ //subscription will start today with a 1 period trial
25
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
26
+ $order->TrialBillingPeriod = $order->BillingPeriod;
27
+ $order->TrialBillingFrequency = $order->BillingFrequency;
28
+ $order->TrialBillingCycles = 1;
29
+ $order->TrialAmount = 0;
30
+
31
+ //add a billing cycle to make up for the trial, if applicable
32
+ if(!empty($order->TotalBillingCycles))
33
+ $order->TotalBillingCycles++;
34
+ }
35
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
36
+ {
37
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
38
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
39
+ $order->TrialBillingCycles++;
40
+
41
+ //add a billing cycle to make up for the trial, if applicable
42
+ if($order->TotalBillingCycles)
43
+ $order->TotalBillingCycles++;
44
+ }
45
+ else
46
+ {
47
+ //add a period to the start date to account for the initial payment
48
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
49
+ }
50
+
51
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
52
+ return $this->subscribe($order);
53
+ }
54
+ else
55
+ {
56
+ if(empty($order->error))
57
+ $order->error = __("Unknown error: Authorization failed.", "pmpro");
58
+ return false;
59
+ }
60
+ }
61
+ else
62
+ {
63
+ //charge first payment
64
+ if($this->charge($order))
65
+ {
66
+ //setup recurring billing
67
+ if(pmpro_isLevelRecurring($order->membership_level))
68
+ {
69
+ if(!pmpro_isLevelTrial($order->membership_level))
70
+ {
71
+ //subscription will start today with a 1 period trial
72
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
73
+ $order->TrialBillingPeriod = $order->BillingPeriod;
74
+ $order->TrialBillingFrequency = $order->BillingFrequency;
75
+ $order->TrialBillingCycles = 1;
76
+ $order->TrialAmount = 0;
77
+
78
+ //add a billing cycle to make up for the trial, if applicable
79
+ if(!empty($order->TotalBillingCycles))
80
+ $order->TotalBillingCycles++;
81
+ }
82
+ elseif($order->InitialPayment == 0 && $order->TrialAmount == 0)
83
+ {
84
+ //it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
85
+ $order->ProfileStartDate = date("Y-m-d") . "T0:0:0";
86
+ $order->TrialBillingCycles++;
87
+
88
+ //add a billing cycle to make up for the trial, if applicable
89
+ if(!empty($order->TotalBillingCycles))
90
+ $order->TotalBillingCycles++;
91
+ }
92
+ else
93
+ {
94
+ //add a period to the start date to account for the initial payment
95
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $this->BillingFrequency . " " . $this->BillingPeriod)) . "T0:0:0";
96
+ }
97
+
98
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
99
+ if($this->subscribe($order))
100
+ {
101
+ return true;
102
+ }
103
+ else
104
+ {
105
+ if($this->void($order))
106
+ {
107
+ if(!$order->error)
108
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
109
+ }
110
+ else
111
+ {
112
+ if(!$order->error)
113
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
114
+
115
+ $order->error .= " " . __("A partial payment was made that we could not void. Please contact the site owner immediately to correct this.", "pmpro");
116
+ }
117
+
118
+ return false;
119
+ }
120
+ }
121
+ else
122
+ {
123
+ //only a one time charge
124
+ $order->status = "success"; //saved on checkout page
125
+ return true;
126
+ }
127
+ }
128
+ else
129
+ {
130
+ if(empty($order->error))
131
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
132
+
133
+ return false;
134
+ }
135
+ }
136
+ }
137
+
138
+ function getCardType($name)
139
+ {
140
+ $card_types = array(
141
+ 'Visa' => '001',
142
+ 'MasterCard' => '002',
143
+ 'Master Card' => '002',
144
+ 'AMEX' => '003',
145
+ 'American Express' => '003',
146
+ 'Discover' => '004',
147
+ 'Diners Club' => '005',
148
+ 'Carte Blanche' => '006',
149
+ 'JCB' => '007'
150
+ );
151
+
152
+ if(isset($card_types[$name]))
153
+ return $card_types[$name];
154
+ else
155
+ return false;
156
+ }
157
+
158
+ function getWSDL($order)
159
+ {
160
+ //which gateway environment?
161
+ if(empty($order->gateway_environment))
162
+ $gateway_environment = pmpro_getOption("gateway_environment");
163
+ else
164
+ $gateway_environment = $order->gateway_environment;
165
+
166
+ //which host?
167
+ if($gateway_environment == "live")
168
+ $host = "ics2ws.ic3.com";
169
+ else
170
+ $host = "ics2wstest.ic3.com";
171
+
172
+ //path
173
+ $path = "/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.90.wsdl";
174
+
175
+ //build url
176
+ $wsdl_url = "https://" . $host . $path;
177
+
178
+ //filter
179
+ $wsdl_url = apply_filters("pmpro_cybersource_wsdl_url", $wsdl_url, $gateway_environment);
180
+
181
+ return $wsdl_url;
182
+ }
183
+
184
+ function authorize(&$order)
185
+ {
186
+ if(empty($order->code))
187
+ $order->code = $order->getRandomCode();
188
+
189
+ $wsdl_url = $this->getWSDL($order);
190
+
191
+ //what amount to authorize? just $1 to test
192
+ $amount = "1.00";
193
+
194
+ //combine address
195
+ $address = $order->Address1;
196
+ if(!empty($order->Address2))
197
+ $address .= "\n" . $order->Address2;
198
+
199
+ //customer stuff
200
+ $customer_email = $order->Email;
201
+ $customer_phone = $order->billing->phone;
202
+
203
+ if(!isset($order->membership_level->name))
204
+ $order->membership_level->name = "";
205
+
206
+ //to store our request
207
+ $request = new stdClass();
208
+
209
+ //which service?
210
+ $ccAuthService = new stdClass();
211
+ $ccAuthService->run = "true";
212
+ $request->ccAuthService = $ccAuthService;
213
+
214
+ //merchant id and order code
215
+ $request->merchantID = pmpro_getOption("cybersource_merchantid");
216
+ $request->merchantReferenceCode = $order->code;
217
+
218
+ //bill to
219
+ $billTo = new stdClass();
220
+ $billTo->firstName = $order->FirstName;
221
+ $billTo->lastName = $order->LastName;
222
+ $billTo->street1 = $address;
223
+ $billTo->city = $order->billing->city;
224
+ $billTo->state = $order->billing->state;
225
+ $billTo->postalCode = $order->billing->zip;
226
+ $billTo->country = $order->billing->country;
227
+ $billTo->email = $order->Email;
228
+ $billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
229
+ $request->billTo = $billTo;
230
+
231
+ //card
232
+ $card = new stdClass();
233
+ $card->cardType = $this->getCardType($order->cardtype);
234
+ $card->accountNumber = $order->accountnumber;
235
+ $card->expirationMonth = $order->expirationmonth;
236
+ $card->expirationYear = $order->expirationyear;
237
+ $card->cvNumber = $order->CVV2;
238
+ $request->card = $card;
239
+
240
+ //currency
241
+ $purchaseTotals = new stdClass();
242
+ $purchaseTotals->currency = pmpro_getOption("currency");
243
+ $request->purchaseTotals = $purchaseTotals;
244
+
245
+ //item/price
246
+ $item0 = new stdClass();
247
+ $item0->unitPrice = $amount;
248
+ $item0->quantity = "1";
249
+ $item0->productName = $order->membership_level->name . " Membership";
250
+ $item0->productSKU = $order->membership_level->id;
251
+ $item0->id = $order->membership_id;
252
+ $request->item = array($item0);
253
+
254
+ $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
255
+ $reply = $soapClient->runTransaction($request);
256
+
257
+ if($reply->reasonCode == "100")
258
+ {
259
+ //success
260
+ $order->payment_transaction_id = $reply->requestID;
261
+ $order->updateStatus("authorized");
262
+ return true;
263
+ }
264
+ else
265
+ {
266
+ //error
267
+ $order->errorcode = $reply->reasonCode;
268
+ $order->error = $this->getErrorFromCode($reply->reasonCode);
269
+ $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
270
+ return false;
271
+ }
272
+ }
273
+
274
+ function void(&$order)
275
+ {
276
+ //need a transaction id
277
+ if(empty($order->payment_transaction_id))
278
+ return false;
279
+
280
+ //get wsdl
281
+ $wsdl_url = $this->getWSDL($order);
282
+
283
+ //to store our request
284
+ $request = new stdClass();
285
+
286
+ //which service?
287
+ $voidService = new stdClass();
288
+ $voidService->run = "true";
289
+ $voidService->voidRequestID = $order->payment_transaction_id;
290
+ $request->voidService = $voidService;
291
+
292
+ //merchant id and order code
293
+ $request->merchantID = pmpro_getOption("cybersource_merchantid");
294
+ $request->merchantReferenceCode = $order->code;
295
+
296
+ $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
297
+ $reply = $soapClient->runTransaction($request);
298
+
299
+ if($reply->reasonCode == "100")
300
+ {
301
+ //success
302
+ $order->payment_transaction_id = $reply->requestID;
303
+ $order->updateStatus("voided");
304
+ return true;
305
+ }
306
+ else
307
+ {
308
+ //error
309
+ $order->errorcode = $reply->reasonCode;
310
+ $order->error = $this->getErrorFromCode($reply->reasonCode);
311
+ $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
312
+ return false;
313
+ }
314
+ }
315
+
316
+ function charge(&$order)
317
+ {
318
+ //get a code
319
+ if(empty($order->code))
320
+ $order->code = $order->getRandomCode();
321
+
322
+ //get wsdl
323
+ $wsdl_url = $this->getWSDL($order);
324
+
325
+ //what amount to charge?
326
+ $amount = $order->InitialPayment;
327
+
328
+ //tax
329
+ $order->subtotal = $amount;
330
+ $tax = $order->getTax(true);
331
+ $amount = round((float)$order->subtotal + (float)$tax, 2);
332
+
333
+ //combine address
334
+ $address = $order->Address1;
335
+ if(!empty($order->Address2))
336
+ $address .= "\n" . $order->Address2;
337
+
338
+ //customer stuff
339
+ $customer_email = $order->Email;
340
+ $customer_phone = $order->billing->phone;
341
+
342
+ if(!isset($order->membership_level->name))
343
+ $order->membership_level->name = "";
344
+
345
+ //to store our request
346
+ $request = new stdClass();
347
+
348
+ //authorize and capture
349
+ $ccAuthService = new stdClass();
350
+ $ccAuthService->run = "true";
351
+ $request->ccAuthService = $ccAuthService;
352
+
353
+ $ccCaptureService = new stdClass();
354
+ $ccCaptureService->run = "true";
355
+ $request->ccCaptureService = $ccCaptureService;
356
+
357
+ //merchant id and order code
358
+ $request->merchantID = pmpro_getOption("cybersource_merchantid");
359
+ $request->merchantReferenceCode = $order->code;
360
+
361
+ //bill to
362
+ $billTo = new stdClass();
363
+ $billTo->firstName = $order->FirstName;
364
+ $billTo->lastName = $order->LastName;
365
+ $billTo->street1 = $address;
366
+ $billTo->city = $order->billing->city;
367
+ $billTo->state = $order->billing->state;
368
+ $billTo->postalCode = $order->billing->zip;
369
+ $billTo->country = $order->billing->country;
370
+ $billTo->email = $order->Email;
371
+ $billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
372
+ $request->billTo = $billTo;
373
+
374
+ //card
375
+ $card = new stdClass();
376
+ $card->cardType = $this->getCardType($order->cardtype);
377
+ $card->accountNumber = $order->accountnumber;
378
+ $card->expirationMonth = $order->expirationmonth;
379
+ $card->expirationYear = $order->expirationyear;
380
+ $card->cvNumber = $order->CVV2;
381
+ $request->card = $card;
382
+
383
+ //currency
384
+ $purchaseTotals = new stdClass();
385
+ $purchaseTotals->currency = pmpro_getOption("currency");
386
+ $request->purchaseTotals = $purchaseTotals;
387
+
388
+ //item/price
389
+ $item0 = new stdClass();
390
+ $item0->unitPrice = $amount;
391
+ $item0->quantity = "1";
392
+ $item0->productName = $order->membership_level->name . " Membership";
393
+ $item0->productSKU = $order->membership_level->id;
394
+ $item0->id = $order->membership_id;
395
+ $request->item = array($item0);
396
+
397
+ $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
398
+ $reply = $soapClient->runTransaction($request);
399
+
400
+ if($reply->reasonCode == "100")
401
+ {
402
+ //success
403
+ $order->payment_transaction_id = $reply->requestID;
404
+ $order->updateStatus("success");
405
+ return true;
406
+ }
407
+ else
408
+ {
409
+ //error
410
+ $order->errorcode = $reply->reasonCode;
411
+ $order->error = $this->getErrorFromCode($reply->reasonCode);
412
+ $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
413
+ return false;
414
+ }
415
+ }
416
+
417
+ function subscribe(&$order)
418
+ {
419
+ //create a code for the order
420
+ if(empty($order->code))
421
+ $order->code = $order->getRandomCode();
422
+
423
+ //filter order before subscription. use with care.
424
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
425
+
426
+ //get wsdl
427
+ $wsdl_url = $this->getWSDL($order);
428
+
429
+ //to store our request
430
+ $request = new stdClass();
431
+
432
+ //set service type
433
+ $paySubscriptionCreateService = new stdClass();
434
+ $paySubscriptionCreateService->run = 'true';
435
+ $paySubscriptionCreateService->disableAutoAuth = 'true'; //we do our own auth check
436
+ $request->paySubscriptionCreateService = $paySubscriptionCreateService;
437
+
438
+ //merchant id and order code
439
+ $request->merchantID = pmpro_getOption("cybersource_merchantid");
440
+ $request->merchantReferenceCode = $order->code;
441
+
442
+ /*
443
+ setup billing amount/etc
444
+ */
445
+ //figure out the amounts
446
+ $amount = $order->PaymentAmount;
447
+ $amount_tax = $order->getTaxForPrice($amount);
448
+ $order->subtotal = $amount;
449
+ $amount = round((float)$amount + (float)$amount_tax, 2);
450
+
451
+ /*
452
+ There are two parts to the trial. Part 1 is simply the delay until the first payment
453
+ since we are doing the first payment as a separate transaction.
454
+ The second part is the actual "trial" set by the admin.
455
+ */
456
+ //figure out the trial length (first payment handled by initial charge)
457
+ if($order->BillingPeriod == "Year")
458
+ $trial_period_days = $order->BillingFrequency * 365; //annual
459
+ elseif($order->BillingPeriod == "Day")
460
+ $trial_period_days = $order->BillingFrequency * 1; //daily
461
+ elseif($order->BillingPeriod == "Week")
462
+ $trial_period_days = $order->BillingFrequency * 7; //weekly
463
+ else
464
+ $trial_period_days = $order->BillingFrequency * 30; //assume monthly
465
+
466
+ //convert to a profile start date
467
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day")) . "T0:0:0";
468
+
469
+ //filter the start date
470
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
471
+
472
+ //convert back to days
473
+ $trial_period_days = ceil(abs(strtotime(date("Y-m-d")) - strtotime($order->ProfileStartDate)) / 86400);
474
+
475
+ //now add the actual trial set by the site
476
+ if(!empty($order->TrialBillingCycles))
477
+ {
478
+ $trialOccurrences = (int)$order->TrialBillingCycles;
479
+ if($order->BillingPeriod == "Year")
480
+ $trial_period_days = $trial_period_days + (365 * $order->BillingFrequency * $trialOccurrences); //annual
481
+ elseif($order->BillingPeriod == "Day")
482
+ $trial_period_days = $trial_period_days + (1 * $order->BillingFrequency * $trialOccurrences); //daily
483
+ elseif($order->BillingPeriod == "Week")
484
+ $trial_period_days = $trial_period_days + (7 * $order->BillingFrequency * $trialOccurrences); //weekly
485
+ else
486
+ $trial_period_days = $trial_period_days + (30 * $order->BillingFrequency * $trialOccurrences); //assume monthly
487
+ }
488
+
489
+ //convert back into a date
490
+ $profile_start_date = date("Ymd", strtotime("+ " . $trial_period_days . " Days"));
491
+
492
+ //figure out the frequency
493
+ if($order->BillingPeriod == "Year")
494
+ {
495
+ $frequency = "annually"; //ignoring BillingFrequency set on level.
496
+ }
497
+ elseif($order->BillingPeriod == "Month")
498
+ {
499
+ if($order->BillingFrequency == 6)
500
+ $frequency = "semi annually";
501
+ elseif($order->BillingFrequency == 3)
502
+ $frequency = "quarterly";
503
+ else
504
+ $frequency = "monthly";
505
+ }
506
+ elseif($order->BillingPeriod == "Week")
507
+ {
508
+ if($order->BillingFrequency == 4)
509
+ $frequency = "quad-weekly";
510
+ elseif($order->BillingFrequency == 2)
511
+ $frequency = "bi-weekly";
512
+ else
513
+ $frequency = "weekly";
514
+ }
515
+ elseif($order->BillingPeriod == "Day")
516
+ {
517
+ if($order->BillingFrequency == 365)
518
+ $frequency = "annually";
519
+ elseif($order->BillingFrequency == 182)
520
+ $frequency = "semi annually";
521
+ elseif($order->BillingFrequency == 183)
522
+ $frequency = "semi annually";
523
+ elseif($order->BillingFrequency == 90)
524
+ $frequency = "quaterly";
525
+ elseif($order->BillingFrequency == 30)
526
+ $frequency = "monthly";
527
+ elseif($order->BillingFrequency == 15)
528
+ $frequency = "semi-monthly";
529
+ elseif($order->BillingFrequency == 28)
530
+ $frequency = "quad-weekly";
531
+ elseif($order->BillingFrequency == 14)
532
+ $frequency = "bi-weekly";
533
+ elseif($order->BillingFrequency == 7)
534
+ $frequency = "weekly";
535
+ }
536
+
537
+ //set subscription info for API
538
+ $subscription = new stdClass();
539
+ $subscription->title = $order->membership_level->name;
540
+ $subscription->paymentMethod = "credit card";
541
+ $request->subscription = $subscription;
542
+
543
+ //recurring info
544
+ $recurringSubscriptionInfo = new stdClass();
545
+ $recurringSubscriptionInfo->amount = number_format($amount, 2);
546
+ $recurringSubscriptionInfo->startDate = $profile_start_date;
547
+ $recurringSubscriptionInfo->frequency = $frequency;
548
+ if(!empty($order->TotalBillingCycles))
549
+ $recurringSubscriptionInfo->numberOfPayments = $order->TotalBillingCycles;
550
+ $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
551
+
552
+ //combine address
553
+ $address = $order->Address1;
554
+ if(!empty($order->Address2))
555
+ $address .= "\n" . $order->Address2;
556
+
557
+ //bill to
558
+ $billTo = new stdClass();
559
+ $billTo->firstName = $order->FirstName;
560
+ $billTo->lastName = $order->LastName;
561
+ $billTo->street1 = $address;
562
+ $billTo->city = $order->billing->city;
563
+ $billTo->state = $order->billing->state;
564
+ $billTo->postalCode = $order->billing->zip;
565
+ $billTo->country = $order->billing->country;
566
+ $billTo->email = $order->Email;
567
+ $billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
568
+ $request->billTo = $billTo;
569
+
570
+ //card
571
+ $card = new stdClass();
572
+ $card->cardType = $this->getCardType($order->cardtype);
573
+ $card->accountNumber = $order->accountnumber;
574
+ $card->expirationMonth = $order->expirationmonth;
575
+ $card->expirationYear = $order->expirationyear;
576
+ $card->cvNumber = $order->CVV2;
577
+ $request->card = $card;
578
+
579
+ //currency
580
+ $purchaseTotals = new stdClass();
581
+ $purchaseTotals->currency = pmpro_getOption("currency");
582
+ $request->purchaseTotals = $purchaseTotals;
583
+
584
+ $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
585
+ $reply = $soapClient->runTransaction($request);
586
+
587
+ if($reply->reasonCode == "100")
588
+ {
589
+ //success
590
+ $order->subscription_transaction_id = $reply->requestID;
591
+ $order->status = "success";
592
+ return true;
593
+ }
594
+ else
595
+ {
596
+ //error
597
+ $order->status = "error";
598
+ $order->errorcode = $reply->reasonCode;
599
+ $order->error = $this->getErrorFromCode($reply->reasonCode);
600
+ $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
601
+ return false;
602
+ }
603
+ }
604
+
605
+ function update(&$order)
606
+ {
607
+ //get wsdl
608
+ $wsdl_url = $this->getWSDL($order);
609
+
610
+ //to store our request
611
+ $request = new stdClass();
612
+
613
+ //set service type
614
+ $paySubscriptionUpdateService = new stdClass();
615
+ $paySubscriptionUpdateService ->run = "true";
616
+ $request->paySubscriptionUpdateService = $paySubscriptionUpdateService ;
617
+
618
+ //merchant id and order code
619
+ $request->merchantID = pmpro_getOption("cybersource_merchantid");
620
+ $request->merchantReferenceCode = $order->code;
621
+
622
+ //set subscription info for API
623
+ $recurringSubscriptionInfo = new stdClass();
624
+ $recurringSubscriptionInfo->subscriptionID = $order->subscription_transaction_id;
625
+ $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
626
+
627
+ //combine address
628
+ $address = $order->Address1;
629
+ if(!empty($order->Address2))
630
+ $address .= "\n" . $order->Address2;
631
+
632
+ //bill to
633
+ $billTo = new stdClass();
634
+ $billTo->firstName = $order->FirstName;
635
+ $billTo->lastName = $order->LastName;
636
+ $billTo->street1 = $address;
637
+ $billTo->city = $order->billing->city;
638
+ $billTo->state = $order->billing->state;
639
+ $billTo->postalCode = $order->billing->zip;
640
+ $billTo->country = $order->billing->country;
641
+ $billTo->email = $order->Email;
642
+ $billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
643
+ $request->billTo = $billTo;
644
+
645
+ //card
646
+ $card = new stdClass();
647
+ $card->cardType = $this->getCardType($order->cardtype);
648
+ $card->accountNumber = $order->accountnumber;
649
+ $card->expirationMonth = $order->expirationmonth;
650
+ $card->expirationYear = $order->expirationyear;
651
+ $card->cvNumber = $order->CVV2;
652
+ $request->card = $card;
653
+
654
+ $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
655
+ $reply = $soapClient->runTransaction($request);
656
+
657
+ if($reply->reasonCode == "100")
658
+ {
659
+ //success
660
+ return true;
661
+ }
662
+ else
663
+ {
664
+ //error
665
+ $order->errorcode = $reply->reasonCode;
666
+ $order->error = $this->getErrorFromCode($reply->reasonCode);
667
+ $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
668
+ return false;
669
+ }
670
+ }
671
+
672
+ function cancel(&$order)
673
+ {
674
+ //require a subscription id
675
+ if(empty($order->subscription_transaction_id))
676
+ return false;
677
+
678
+ //get wsdl
679
+ $wsdl_url = $this->getWSDL($order);
680
+
681
+ //to store our request
682
+ $request = new stdClass();
683
+
684
+ //which service?
685
+ $paySubscriptionDeleteService = new stdClass();
686
+ $paySubscriptionDeleteService ->run = "true";
687
+ $request->paySubscriptionDeleteService = $paySubscriptionDeleteService ;
688
+
689
+ //which order
690
+ $recurringSubscriptionInfo = new stdClass();
691
+ $recurringSubscriptionInfo->subscriptionID = $order->subscription_transaction_id;
692
+ $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
693
+
694
+ //merchant id and order code
695
+ $request->merchantID = pmpro_getOption("cybersource_merchantid");
696
+ $request->merchantReferenceCode = $order->code;
697
+
698
+ $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
699
+ $reply = $soapClient->runTransaction($request);
700
+
701
+ if($reply->reasonCode == "100")
702
+ {
703
+ //success
704
+ $order->updateStatus("cancelled");
705
+ return true;
706
+ }
707
+ else
708
+ {
709
+ //error
710
+ $order->errorcode = $reply->reasonCode;
711
+ $order->error = $this->getErrorFromCode($reply->reasonCode);
712
+ $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
713
+ return false;
714
+ }
715
+ }
716
+
717
+ function getErrorFromCode($code)
718
+ {
719
+ $error_messages = array(
720
+ "100" => "Successful transaction.",
721
+ "101" => "The request is missing one or more required fields.",
722
+ "102" => "One or more fields in the request contains invalid data. Check that your billing address is valid.",
723
+ "104" => "Duplicate order detected.",
724
+ "110" => "Only partial amount was approved.",
725
+ "150" => "Error: General system failure.",
726
+ "151" => "Error: The request was received but there was a server timeout.",
727
+ "152" => "Error: The request was received, but a service did not finish running in time. ",
728
+ "200" => "Address Verification Service (AVS) failure.",
729
+ "201" => "Authorization failed.",
730
+ "202" => "Expired card or invalid expiration date.",
731
+ "203" => "The card was declined.",
732
+ "204" => "Insufficient funds in the account.",
733
+ "205" => "Stolen or lost card.",
734
+ "207" => "Issuing bank unavailable.",
735
+ "208" => "Inactive card or card not authorized for card-not-present transactions.",
736
+ "209" => "American Express Card Identification Digits (CID) did not match.",
737
+ "210" => "The card has reached the credit limit. ",
738
+ "211" => "Invalid card verification number.",
739
+ "221" => "The customer matched an entry on the processors negative file. ",
740
+ "230" => "Card verification (CV) check failed.",
741
+ "231" => "Invalid account number.",
742
+ "232" => "The card type is not accepted by the payment processor.",
743
+ "233" => "General decline by the processor.",
744
+ "234" => "There is a problem with your CyberSource merchant configuration.",
745
+ "235" => "The requested amount exceeds the originally authorized amount.",
746
+ "236" => "Processor failure.",
747
+ "237" => "The authorization has already been reversed.",
748
+ "238" => "The authorization has already been captured.",
749
+ "239" => "The requested transaction amount must match the previous transaction amount.",
750
+ "240" => "The card type sent is invalid or does not correlate with the credit card number.",
751
+ "241" => "The referenced request id is invalid for all follow-on transactions.",
752
+ "242" => "The request ID is invalid.",
753
+ "243" => "The transaction has already been settled or reversed.",
754
+ "246" => "The capture or credit is not voidable because the capture or credit information has already been submitted to your processor. Or, you requested a void for a type of transaction that cannot be voided.",
755
+ "247" => "You requested a credit for a capture that was previously voided.",
756
+ "250" => "Error: The request was received, but there was a timeout at the payment processor.",
757
+ "520" => "Smart Authorization failed."
758
+ );
759
+
760
+ if(isset($error_messages[$code]))
761
+ return $error_messages[$code];
762
+ else
763
+ return "Unknown error.";
764
+ }
765
+ }
classes/gateways/class.pmprogateway_payflowpro.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway_payflowpro
4
+ {
5
+ function PMProGateway_payflowpro($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ if(floatval($order->InitialPayment) == 0)
14
+ {
15
+ //auth first, then process
16
+ $authorization_id = $this->authorize($order);
17
+ if($authorization_id)
18
+ {
19
+ $this->void($order, $authorization_id);
20
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
21
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
22
+ return $this->subscribe($order);
23
+ }
24
+ else
25
+ {
26
+ if(empty($order->error))
27
+ $order->error = __("Unknown error: Authorization failed.", "pmpro");
28
+ return false;
29
+ }
30
+ }
31
+ else
32
+ {
33
+ //charge first payment
34
+ if($this->charge($order))
35
+ {
36
+ //setup recurring billing
37
+ if(pmpro_isLevelRecurring($order->membership_level))
38
+ {
39
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
40
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
41
+ if($this->subscribe($order))
42
+ {
43
+ return true;
44
+ }
45
+ else
46
+ {
47
+ if($this->void($order, $order->payment_transaction_id))
48
+ {
49
+ if(empty($order->error))
50
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
51
+ }
52
+ else
53
+ {
54
+ if(empty($order->error))
55
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
56
+
57
+ $order->error .= " " . __("A partial payment was made that we could not refund. Please contact the site owner immediately to correct this.", "pmpro");
58
+ }
59
+
60
+ return false;
61
+ }
62
+ }
63
+ else
64
+ {
65
+ //only a one time charge
66
+ $order->status = "success"; //saved on checkout page
67
+ $order->saveOrder();
68
+ return true;
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ function authorize(&$order)
75
+ {
76
+ if(empty($order->code))
77
+ $order->code = $order->getRandomCode();
78
+
79
+ //paypal profile stuff
80
+ $nvpStr = "";
81
+
82
+ $nvpStr .="&AMT=1.00";
83
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
84
+ //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
85
+
86
+ $nvpStr .= "&CUSTIP=" . $_SERVER['REMOTE_ADDR'] . "&INVNUM=" . $order->code;
87
+
88
+ //credit card fields
89
+ if($order->cardtype == "American Express")
90
+ $cardtype = "Amex";
91
+ else
92
+ $cardtype = $order->cardtype;
93
+
94
+ if(!empty($order->accountnumber))
95
+ $nvpStr .= "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->expirationmonth . substr($order->expirationyear, 2, 2) . "&CVV2=" . $order->CVV2;
96
+
97
+ //billing address, etc
98
+ if(!empty($order->Address1))
99
+ {
100
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
101
+
102
+ if($order->Address2)
103
+ $nvpStr .= " " . $order->Address2;
104
+
105
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
106
+ }
107
+
108
+ //for debugging, let's attach this to the class object
109
+ $this->nvpStr = $nvpStr;
110
+
111
+ $this->httpParsedResponseAr = $this->PPHttpPost('A', $nvpStr);
112
+
113
+ if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
114
+ $order->authorization_id = $this->httpParsedResponseAr['PNREF'];
115
+ $order->updateStatus("authorized");
116
+ return $order->authorization_id;
117
+ } else {
118
+ $order->status = "error";
119
+ $order->errorcode = $this->httpParsedResponseAr['RESULT'];
120
+ $order->error = urldecode($this->httpParsedResponseAr['RESPMSG']);
121
+ $order->shorterror = urldecode($this->httpParsedResponseAr['RESPMSG']);
122
+ return false;
123
+ }
124
+ }
125
+
126
+ function void(&$order, $authorization_id)
127
+ {
128
+ if(empty($authorization_id))
129
+ return false;
130
+
131
+ //paypal profile stuff
132
+ $nvpStr="&ORIGID=" . $authorization_id;
133
+
134
+ $this->httpParsedResponseAr = $this->PPHttpPost('V', $nvpStr);
135
+
136
+ if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
137
+ return true;
138
+ } else {
139
+ $order->status = "error";
140
+ $order->errorcode = $this->httpParsedResponseAr['RESULT'];
141
+ $order->error = urldecode($this->httpParsedResponseAr['RESPMSG']);
142
+ $order->shorterror = urldecode($this->httpParsedResponseAr['RESPMSG']);
143
+ return false;
144
+ }
145
+ }
146
+
147
+ function charge(&$order)
148
+ {
149
+ global $pmpro_currency;
150
+
151
+ if(empty($order->code))
152
+ $order->code = $order->getRandomCode();
153
+
154
+ //taxes on the amount
155
+ $amount = $order->InitialPayment;
156
+ $amount_tax = $order->getTaxForPrice($amount);
157
+ $order->subtotal = $amount;
158
+ $amount = round((float)$amount + (float)$amount_tax, 2);
159
+
160
+ //paypal profile stuff
161
+ $nvpStr = "";
162
+ $nvpStr .="&AMT=" . $amount . "&TAXAMT=" . $amount_tax;
163
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
164
+ //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
165
+
166
+ $nvpStr .= "&CUSTIP=" . $_SERVER['REMOTE_ADDR'] . "&INVNUM=" . $order->code;
167
+
168
+ if(!empty($order->accountnumber))
169
+ $nvpStr .= "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->expirationmonth . substr($order->expirationyear, 2, 2) . "&CVV2=" . $order->CVV2;
170
+
171
+ //billing address, etc
172
+ if($order->Address1)
173
+ {
174
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
175
+
176
+ if($order->Address2)
177
+ $nvpStr .= " " . $order->Address2;
178
+
179
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
180
+ }
181
+
182
+ $this->nvpStr = $nvpStr;
183
+ $this->httpParsedResponseAr = $this->PPHttpPost('S', $nvpStr);
184
+
185
+ if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
186
+ $order->payment_transaction_id = $this->httpParsedResponseAr['PNREF'];
187
+ $order->updateStatus("success");
188
+ return true;
189
+ } else {
190
+ $order->status = "error";
191
+ $order->errorcode = $this->httpParsedResponseAr['RESULT'];
192
+ $order->error = urldecode($this->httpParsedResponseAr['RESPMSG']);
193
+ $order->shorterror = urldecode($this->httpParsedResponseAr['RESPMSG']);
194
+ return false;
195
+ }
196
+ }
197
+
198
+ function subscribe(&$order)
199
+ {
200
+ global $pmpro_currency;
201
+
202
+ if(empty($order->code))
203
+ $order->code = $order->getRandomCode();
204
+
205
+ //filter order before subscription. use with care.
206
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
207
+
208
+ //taxes on the amount
209
+ $amount = $order->PaymentAmount;
210
+ $amount_tax = $order->getTaxForPrice($amount);
211
+ $amount = round((float)$amount + (float)$amount_tax, 2);
212
+
213
+ if($order->BillingPeriod == "Week")
214
+ $payperiod = "WEEK";
215
+ elseif($order->BillingPeriod == "Month")
216
+ $payperiod = "MONT";
217
+ elseif($order->BillingPeriod == "Year")
218
+ $payperiod = "YEAR";
219
+
220
+ //paypal profile stuff
221
+ $nvpStr = "&ACTION=A";
222
+ $nvpStr .="&AMT=" . $amount . "&TAXAMT=" . $amount_tax;
223
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
224
+ //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
225
+
226
+ $nvpStr .= "&PROFILENAME=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127));
227
+
228
+ $nvpStr .= "&PAYPERIOD=" . $payperiod;
229
+
230
+ $nvpStr .= "&CUSTIP=" . $_SERVER['REMOTE_ADDR'] . "&INVNUM=" . $order->code;
231
+
232
+ //if billing cycles are defined
233
+ if(!empty($order->TotalBillingCycles))
234
+ $nvpStr .= "&TERM=" . $order->TotalBillingCycles;
235
+ else
236
+ $nvpStr .= "&TERM=0";
237
+
238
+ if(!empty($order->accountnumber))
239
+ $nvpStr .= "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->expirationmonth . substr($order->expirationyear, 2, 2) . "&CVV2=" . $order->CVV2;
240
+
241
+ /*
242
+ Let's figure out the start date. There are two parts.
243
+ 1. We need to add the billing period to the start date to account for the initial payment.
244
+ 2. We can allow for free trials by further delaying the start date of the subscription.
245
+ */
246
+ if($order->BillingPeriod == "Year")
247
+ $trial_period_days = $order->BillingFrequency * 365; //annual
248
+ elseif($order->BillingPeriod == "Day")
249
+ $trial_period_days = $order->BillingFrequency * 1; //daily
250
+ elseif($order->BillingPeriod == "Week")
251
+ $trial_period_days = $order->BillingFrequency * 7; //weekly
252
+ else
253
+ $trial_period_days = $order->BillingFrequency * 30; //assume monthly
254
+
255
+ //convert to a profile start date
256
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day")) . "T0:0:0";
257
+
258
+ //filter the start date
259
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
260
+
261
+ //convert back to days
262
+ $trial_period_days = ceil(abs(strtotime(date("Y-m-d")) - strtotime($order->ProfileStartDate)) / 86400);
263
+
264
+ //now add the actual trial set by the site
265
+ if(!empty($order->TrialBillingCycles))
266
+ {
267
+ $trialOccurrences = (int)$order->TrialBillingCycles;
268
+ if($order->BillingPeriod == "Year")
269
+ $trial_period_days = $trial_period_days + (365 * $order->BillingFrequency * $trialOccurrences); //annual
270
+ elseif($order->BillingPeriod == "Day")
271
+ $trial_period_days = $trial_period_days + (1 * $order->BillingFrequency * $trialOccurrences); //daily
272
+ elseif($order->BillingPeriod == "Week")
273
+ $trial_period_days = $trial_period_days + (7 * $order->BillingFrequency * $trialOccurrences); //weekly
274
+ else
275
+ $trial_period_days = $trial_period_days + (30 * $order->BillingFrequency * $trialOccurrences); //assume monthly
276
+ }
277
+
278
+ //convert back into a date
279
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day")) . "T0:0:0";
280
+
281
+ //start date
282
+ $nvpStr .= "&START=" . date("mdY", strtotime($order->ProfileStartDate));
283
+
284
+ if(!empty($order->accountnumber))
285
+ $nvpStr .= "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->expirationmonth . substr($order->expirationyear, 2, 2) . "&CVV2=" . $order->CVV2;
286
+
287
+ //billing address, etc
288
+ if($order->Address1)
289
+ {
290
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
291
+
292
+ if($order->Address2)
293
+ $nvpStr .= " " . $order->Address2;
294
+
295
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
296
+ }
297
+
298
+ $this->nvpStr = $nvpStr;
299
+ $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
300
+
301
+ if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
302
+ $order->subscription_transaction_id = $this->httpParsedResponseAr['PROFILEID'];
303
+ $order->status = "success";
304
+ return true;
305
+ } else {
306
+ $order->status = "error";
307
+ $order->errorcode = $this->httpParsedResponseAr['RESULT'];
308
+ $order->error = urldecode($this->httpParsedResponseAr['RESPMSG']);
309
+ $order->shorterror = urldecode($this->httpParsedResponseAr['RESPMSG']);
310
+ return false;
311
+ }
312
+ }
313
+
314
+ function update(&$order)
315
+ {
316
+ $order->getMembershipLevel();
317
+
318
+ //paypal profile stuff
319
+ $nvpStr = "&ORIGPROFILEID=" . $order->subscription_transaction_id . "&ACTION=M";
320
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
321
+
322
+ $nvpStr .= "&PROFILENAME=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127));
323
+
324
+ $nvpStr .= "&CUSTIP=" . $_SERVER['REMOTE_ADDR'] . "&INVNUM=" . $order->code;
325
+
326
+ if(!empty($order->accountnumber))
327
+ $nvpStr .= "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->expirationmonth . substr($order->expirationyear, 2, 2) . "&CVV2=" . $order->CVV2;
328
+
329
+ //billing address, etc
330
+ if($order->Address1)
331
+ {
332
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
333
+
334
+ if($order->Address2)
335
+ $nvpStr .= " " . $order->Address2;
336
+
337
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&BILLTOCOUNTRY=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&PHONENUM=" . $order->billing->phone;
338
+ }
339
+
340
+ $this->nvpStr = $nvpStr;
341
+ $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
342
+
343
+ if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
344
+ $order->subscription_transaction_id = $this->httpParsedResponseAr['PROFILEID'];
345
+ $order->updateStatus("success");
346
+ return true;
347
+ } else {
348
+ $order->status = "error";
349
+ $order->errorcode = $this->httpParsedResponseAr['RESULT'];
350
+ $order->error = urldecode($this->httpParsedResponseAr['RESPMSG']);
351
+ $order->shorterror = urldecode($this->httpParsedResponseAr['RESPMSG']);
352
+ return false;
353
+ }
354
+ }
355
+
356
+ function cancel(&$order)
357
+ {
358
+ //require a subscription id
359
+ if(empty($order->subscription_transaction_id))
360
+ return false;
361
+
362
+ //paypal profile stuff
363
+ $nvpStr = "&ORIGPROFILEID=" . $order->subscription_transaction_id . "&ACTION=C";
364
+
365
+ $this->nvpStr = $nvpStr;
366
+ $this->httpParsedResponseAr = $this->PPHttpPost('R', $nvpStr);
367
+
368
+ if("0" == strtoupper($this->httpParsedResponseAr["RESULT"])) {
369
+ $order->updateStatus("cancelled");
370
+ return true;
371
+ } else {
372
+ $order->status = "error";
373
+ $order->errorcode = $this->httpParsedResponseAr['RESULT'];
374
+ $order->error = urldecode($this->httpParsedResponseAr['RESPMSG']);
375
+ $order->shorterror = urldecode($this->httpParsedResponseAr['RESPMSG']);
376
+ return false;
377
+ }
378
+ }
379
+
380
+ /**
381
+ * PAYPAL Function
382
+ * Send HTTP POST Request
383
+ *
384
+ * @param string The API method name
385
+ * @param string The POST Message fields in &name=value pair format
386
+ * @return array Parsed HTTP Response body
387
+ */
388
+ function PPHttpPost($methodName_, $nvpStr_) {
389
+ global $gateway_environment;
390
+ $environment = $gateway_environment;
391
+
392
+ $PARTNER = pmpro_getOption("payflow_partner");
393
+ $VENDOR = pmpro_getOption("payflow_vendor");
394
+ $USER = pmpro_getOption("payflow_user");
395
+ $PWD = pmpro_getOption("payflow_pwd");
396
+ $API_Endpoint = "https://payflowpro.paypal.com";
397
+ if("sandbox" === $environment || "beta-sandbox" === $environment) {
398
+ $API_Endpoint = "https://pilot-payflowpro.paypal.com";
399
+ }
400
+
401
+ $version = urlencode('4');
402
+
403
+ // setting the curl parameters.
404
+ $ch = curl_init();
405
+ curl_setopt($ch, CURLOPT_URL, $API_Endpoint);
406
+ curl_setopt($ch, CURLOPT_VERBOSE, 1);
407
+
408
+ // turning off the server and peer verification(TrustManager Concept).
409
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
410
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
411
+
412
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
413
+ curl_setopt($ch, CURLOPT_POST, 1);
414
+
415
+ // NVPRequest for submitting to server
416
+ $nvpreq = "TRXTYPE=" . $methodName_ . "&TENDER=C&PARTNER=" . $PARTNER . "&VENDOR=" . $VENDOR . "&USER=" . $USER . "&PWD=" . $PWD . "&VERBOSITY=medium" . $nvpStr_;
417
+
418
+ // setting the nvpreq as POST FIELD to curl
419
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq);
420
+
421
+ // getting response from server
422
+ $httpResponse = curl_exec($ch);
423
+
424
+ if(empty($httpResponse)) {
425
+ exit("$methodName_ failed: ".curl_error($ch).'('.curl_errno($ch).')');
426
+ }
427
+
428
+ // Extract the RefundTransaction response details
429
+ $httpResponseAr = explode("&", $httpResponse);
430
+
431
+ $httpParsedResponseAr = array();
432
+ foreach ($httpResponseAr as $i => $value) {
433
+ $tmpAr = explode("=", $value);
434
+ if(sizeof($tmpAr) > 1) {
435
+ $httpParsedResponseAr[$tmpAr[0]] = $tmpAr[1];
436
+ }
437
+ }
438
+
439
+ if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('RESULT', $httpParsedResponseAr)) {
440
+ exit("Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint.");
441
+ }
442
+
443
+ return $httpParsedResponseAr;
444
+ }
445
+ }
classes/gateways/class.pmprogateway_paypal.php ADDED
@@ -0,0 +1,454 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway_paypal
4
+ {
5
+ function PMProGateway_paypal($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ if(floatval($order->InitialPayment) == 0)
14
+ {
15
+ //auth first, then process
16
+ $authorization_id = $this->authorize($order);
17
+ if($authorization_id)
18
+ {
19
+ $this->void($order, $authorization_id);
20
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
21
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
22
+ return $this->subscribe($order);
23
+ }
24
+ else
25
+ {
26
+ if(empty($order->error))
27
+ $order->error = __("Unknown error: Authorization failed.", "pmpro");
28
+ return false;
29
+ }
30
+ }
31
+ else
32
+ {
33
+ //charge first payment
34
+ if($this->charge($order))
35
+ {
36
+ //setup recurring billing
37
+ if(pmpro_isLevelRecurring($order->membership_level))
38
+ {
39
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
40
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
41
+ if($this->subscribe($order))
42
+ {
43
+ return true;
44
+ }
45
+ else
46
+ {
47
+ if($this->refund($order, $order->payment_transaction_id))
48
+ {
49
+ if(empty($order->error))
50
+ $order->error = __("Unknown error: Payment failed.", "pmpro");
51
+ }
52
+ else
53
+ {
54
+ if(empty($order->error))
55
+ $order->error = "Unknown error: Payment failed.";
56
+
57
+ $order->error .= " " . __("A partial payment was made that we could not refund. Please contact the site owner immediately to correct this.", "pmpro");
58
+ }
59
+
60
+ return false;
61
+ }
62
+ }
63
+ else
64
+ {
65
+ //only a one time charge
66
+ $order->status = "success"; //saved on checkout page
67
+ $order->saveOrder();
68
+ return true;
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ function authorize(&$order)
75
+ {
76
+ if(empty($order->code))
77
+ $order->code = $order->getRandomCode();
78
+
79
+ //paypal profile stuff
80
+ $nvpStr = "";
81
+ if(!empty($order->Token))
82
+ $nvpStr .= "&TOKEN=" . $order->Token;
83
+ $nvpStr .="&AMT=1.00&CURRENCYCODE=" . pmpro_getOption("currency");
84
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
85
+ //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
86
+
87
+ $nvpStr .= "&PAYMENTACTION=Authorization&IPADDRESS=" . $_SERVER['REMOTE_ADDR'] . "&INVNUM=" . $order->code;
88
+
89
+ //credit card fields
90
+ if($order->cardtype == "American Express")
91
+ $cardtype = "Amex";
92
+ else
93
+ $cardtype = $order->cardtype;
94
+
95
+ if(!empty($cardtype))
96
+ $nvpStr .= "&CREDITCARDTYPE=" . $cardtype . "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->ExpirationDate . "&CVV2=" . $order->CVV2;
97
+
98
+ //Maestro/Solo card fields. (Who uses these?) :)
99
+ if(!empty($order->StartDate))
100
+ $nvpStr .= "&STARTDATE=" . $order->StartDate . "&ISSUENUMBER=" . $order->IssueNumber;
101
+
102
+ //billing address, etc
103
+ if(!empty($order->Address1))
104
+ {
105
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
106
+
107
+ if($order->Address2)
108
+ $nvpStr .= "&STREET2=" . $order->Address2;
109
+
110
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&SHIPTOPHONENUM=" . $order->billing->phone;
111
+ }
112
+
113
+ //for debugging, let's attach this to the class object
114
+ $this->nvpStr = $nvpStr;
115
+
116
+ $this->httpParsedResponseAr = $this->PPHttpPost('DoDirectPayment', $nvpStr);
117
+
118
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
119
+ $order->authorization_id = $this->httpParsedResponseAr['TRANSACTIONID'];
120
+ $order->updateStatus("authorized");
121
+ return $order->authorization_id;
122
+ } else {
123
+ $order->status = "error";
124
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
125
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
126
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
127
+ return false;
128
+ }
129
+ }
130
+
131
+ function void(&$order, $authorization_id)
132
+ {
133
+ if(empty($authorization_id))
134
+ return false;
135
+
136
+ //paypal profile stuff
137
+ $nvpStr="&AUTHORIZATIONID=" . $authorization_id . "&NOTE=Voiding an authorization for a recurring payment setup.";
138
+
139
+ $this->httpParsedResponseAr = $this->PPHttpPost('DoVoid', $nvpStr);
140
+
141
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
142
+ return true;
143
+ } else {
144
+ $order->status = "error";
145
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
146
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
147
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
148
+ return false;
149
+ }
150
+ }
151
+
152
+ function refund(&$order, $transaction_id)
153
+ {
154
+ if(empty($transaction_id))
155
+ return false;
156
+
157
+ //paypal profile stuff
158
+ $nvpStr="&TRANSACTIONID=" . $transaction_id . "&NOTE=Refunding a charge.";
159
+
160
+ $this->httpParsedResponseAr = $this->PPHttpPost('RefundTransaction', $nvpStr);
161
+
162
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
163
+ return true;
164
+ } else {
165
+ $order->status = "error";
166
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
167
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
168
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
169
+ return false;
170
+ }
171
+ }
172
+
173
+ function charge(&$order)
174
+ {
175
+ global $pmpro_currency;
176
+
177
+ if(empty($order->code))
178
+ $order->code = $order->getRandomCode();
179
+
180
+ //taxes on the amount
181
+ $amount = $order->InitialPayment;
182
+ $amount_tax = $order->getTaxForPrice($amount);
183
+ $order->subtotal = $amount;
184
+ $amount = round((float)$amount + (float)$amount_tax, 2);
185
+
186
+ //paypal profile stuff
187
+ $nvpStr = "";
188
+ if(!empty($order->Token))
189
+ $nvpStr .= "&TOKEN=" . $order->Token;
190
+ $nvpStr .="&AMT=" . $amount . "&ITEMAMT=" . $order->InitialPayment . "&TAXAMT=" . $amount_tax . "&CURRENCYCODE=" . $pmpro_currency;
191
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
192
+ //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
193
+
194
+ $nvpStr .= "&PAYMENTACTION=Sale&IPADDRESS=" . $_SERVER['REMOTE_ADDR'] . "&INVNUM=" . $order->code;
195
+
196
+ //credit card fields
197
+ if($order->cardtype == "American Express")
198
+ $cardtype = "Amex";
199
+ else
200
+ $cardtype = $order->cardtype;
201
+
202
+ if(!empty($cardtype))
203
+ $nvpStr .= "&CREDITCARDTYPE=" . $cardtype . "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->ExpirationDate . "&CVV2=" . $order->CVV2;
204
+
205
+ //Maestro/Solo card fields. (Who uses these?) :)
206
+ if(!empty($order->StartDate))
207
+ $nvpStr .= "&STARTDATE=" . $order->StartDate . "&ISSUENUMBER=" . $order->IssueNumber;
208
+
209
+ //billing address, etc
210
+ if($order->Address1)
211
+ {
212
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
213
+
214
+ if($order->Address2)
215
+ $nvpStr .= "&STREET2=" . $order->Address2;
216
+
217
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&SHIPTOPHONENUM=" . $order->billing->phone;
218
+ }
219
+
220
+ $this->httpParsedResponseAr = $this->PPHttpPost('DoDirectPayment', $nvpStr);
221
+
222
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
223
+ $order->payment_transaction_id = $this->httpParsedResponseAr['TRANSACTIONID'];
224
+ $order->updateStatus("success");
225
+ return true;
226
+ } else {
227
+ $order->status = "error";
228
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
229
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
230
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
231
+ return false;
232
+ }
233
+ }
234
+
235
+ function subscribe(&$order)
236
+ {
237
+ global $pmpro_currency;
238
+
239
+ if(empty($order->code))
240
+ $order->code = $order->getRandomCode();
241
+
242
+ //filter order before subscription. use with care.
243
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
244
+
245
+ //taxes on the amount
246
+ $amount = $order->PaymentAmount;
247
+ $amount_tax = $order->getTaxForPrice($amount);
248
+ $amount = round((float)$amount + (float)$amount_tax, 2);
249
+
250
+ //paypal profile stuff
251
+ $nvpStr = "";
252
+ if(!empty($order->Token))
253
+ $nvpStr .= "&TOKEN=" . $order->Token;
254
+ $nvpStr .="&AMT=" . $order->PaymentAmount . "&TAXAMT=" . $amount_tax . "&CURRENCYCODE=" . $pmpro_currency . "&PROFILESTARTDATE=" . $order->ProfileStartDate;
255
+ $nvpStr .= "&BILLINGPERIOD=" . $order->BillingPeriod . "&BILLINGFREQUENCY=" . $order->BillingFrequency . "&AUTOBILLAMT=AddToNextBilling";
256
+ $nvpStr .= "&DESC=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127));
257
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
258
+ //$nvpStr .= "&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . $order->PaymentAmount;
259
+
260
+ //if billing cycles are defined
261
+ if(!empty($order->TotalBillingCycles))
262
+ $nvpStr .= "&TOTALBILLINGCYCLES=" . $order->TotalBillingCycles;
263
+
264
+ //if a trial period is defined
265
+ if(!empty($order->TrialBillingPeriod))
266
+ {
267
+ $trial_amount = $order->TrialAmount;
268
+ $trial_tax = $order->getTaxForPrice($trial_amount);
269
+ $trial_amount = round((float)$trial_amount + (float)$trial_tax, 2);
270
+
271
+ $nvpStr .= "&TRIALBILLINGPERIOD=" . $order->TrialBillingPeriod . "&TRIALBILLINGFREQUENCY=" . $order->TrialBillingFrequency . "&TRIALAMT=" . $trial_amount;
272
+ }
273
+ if(!empty($order->TrialBillingCycles))
274
+ $nvpStr .= "&TRIALTOTALBILLINGCYCLES=" . $order->TrialBillingCycles;
275
+
276
+ //credit card fields
277
+ if($order->cardtype == "American Express")
278
+ $cardtype = "Amex";
279
+ else
280
+ $cardtype = $order->cardtype;
281
+
282
+ if($cardtype)
283
+ $nvpStr .= "&CREDITCARDTYPE=" . $cardtype . "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->ExpirationDate . "&CVV2=" . $order->CVV2;
284
+
285
+ //Maestro/Solo card fields. (Who uses these?) :)
286
+ if(!empty($order->StartDate))
287
+ $nvpStr .= "&STARTDATE=" . $order->StartDate . "&ISSUENUMBER=" . $order->IssueNumber;
288
+
289
+ //billing address, etc
290
+ if($order->Address1)
291
+ {
292
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
293
+
294
+ if($order->Address2)
295
+ $nvpStr .= "&STREET2=" . $order->Address2;
296
+
297
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip . "&SHIPTOPHONENUM=" . $order->billing->phone;
298
+ }
299
+
300
+ //for debugging let's add this to the class object
301
+ $this->nvpStr = $nvpStr;
302
+
303
+ $this->httpParsedResponseAr = $this->PPHttpPost('CreateRecurringPaymentsProfile', $nvpStr);
304
+
305
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
306
+ $order->status = "success";
307
+ $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
308
+ return true;
309
+ //exit('CreateRecurringPaymentsProfile Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
310
+ } else {
311
+ $order->status = "error";
312
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
313
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
314
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
315
+ return false;
316
+ //exit('CreateRecurringPaymentsProfile failed: ' . print_r($httpParsedResponseAr, true));
317
+ }
318
+ }
319
+
320
+ function update(&$order)
321
+ {
322
+ //paypal profile stuff
323
+ $nvpStr = "";
324
+ $nvpStr .= "&PROFILEID=" . $order->subscription_transaction_id;
325
+
326
+ //credit card fields
327
+ if($order->cardtype == "American Express")
328
+ $cardtype = "Amex";
329
+ else
330
+ $cardtype = $order->cardtype;
331
+
332
+ //credit card fields
333
+ if($cardtype)
334
+ $nvpStr .= "&CREDITCARDTYPE=" . $cardtype . "&ACCT=" . $order->accountnumber . "&EXPDATE=" . $order->ExpirationDate . "&CVV2=" . $order->CVV2;
335
+
336
+ //Maestro/Solo card fields. (Who uses these?) :)
337
+ if($order->StartDate)
338
+ $nvpStr .= "&STARTDATE=" . $order->StartDate . "&ISSUENUMBER=" . $order->IssueNumber;
339
+
340
+ //billing address, etc
341
+ if($order->Address1)
342
+ {
343
+ $nvpStr .= "&EMAIL=" . $order->Email . "&FIRSTNAME=" . $order->FirstName . "&LASTNAME=" . $order->LastName . "&STREET=" . $order->Address1;
344
+
345
+ if($order->Address2)
346
+ $nvpStr .= "&STREET2=" . $order->Address2;
347
+
348
+ $nvpStr .= "&CITY=" . $order->billing->city . "&STATE=" . $order->billing->state . "&COUNTRYCODE=" . $order->billing->country . "&ZIP=" . $order->billing->zip;
349
+ }
350
+
351
+ $this->httpParsedResponseAr = $this->PPHttpPost('UpdateRecurringPaymentsProfile', $nvpStr);
352
+
353
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
354
+ $order->status = "success";
355
+ $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
356
+ return true;
357
+ //exit('CreateRecurringPaymentsProfile Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
358
+ } else {
359
+ $order->status = "error";
360
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
361
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
362
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
363
+ return false;
364
+ //exit('CreateRecurringPaymentsProfile failed: ' . print_r($httpParsedResponseAr, true));
365
+ }
366
+ }
367
+
368
+ function cancel(&$order)
369
+ {
370
+ //paypal profile stuff
371
+ $nvpStr = "";
372
+ $nvpStr .= "&PROFILEID=" . $order->subscription_transaction_id . "&ACTION=Cancel&NOTE=User requested cancel.";
373
+
374
+ $this->httpParsedResponseAr = $this->PPHttpPost('ManageRecurringPaymentsProfileStatus', $nvpStr);
375
+
376
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"]) || $this->httpParsedResponseAr['L_ERRORCODE0'] == "11556") {
377
+ $order->updateStatus("cancelled");
378
+ return true;
379
+ //exit('CreateRecurringPaymentsProfile Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
380
+ } else {
381
+ $order->status = "error";
382
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
383
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
384
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
385
+ return false;
386
+ //exit('CreateRecurringPaymentsProfile failed: ' . print_r($httpParsedResponseAr, true));
387
+ }
388
+ }
389
+
390
+ /**
391
+ * PAYPAL Function
392
+ * Send HTTP POST Request
393
+ *
394
+ * @param string The API method name
395
+ * @param string The POST Message fields in &name=value pair format
396
+ * @return array Parsed HTTP Response body
397
+ */
398
+ function PPHttpPost($methodName_, $nvpStr_) {
399
+ global $gateway_environment;
400
+ $environment = $gateway_environment;
401
+
402
+ $API_UserName = pmpro_getOption("apiusername");
403
+ $API_Password = pmpro_getOption("apipassword");
404
+ $API_Signature = pmpro_getOption("apisignature");
405
+ $API_Endpoint = "https://api-3t.paypal.com/nvp";
406
+ if("sandbox" === $environment || "beta-sandbox" === $environment) {
407
+ $API_Endpoint = "https://api-3t.$environment.paypal.com/nvp";
408
+ }
409
+
410
+ $version = urlencode('72.0');
411
+
412
+ // setting the curl parameters.
413
+ $ch = curl_init();
414
+ curl_setopt($ch, CURLOPT_URL, $API_Endpoint);
415
+ curl_setopt($ch, CURLOPT_VERBOSE, 1);
416
+
417
+ // turning off the server and peer verification(TrustManager Concept).
418
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
419
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
420
+
421
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
422
+ curl_setopt($ch, CURLOPT_POST, 1);
423
+
424
+ // NVPRequest for submitting to server
425
+ $nvpreq = "METHOD=$methodName_&VERSION=$version&PWD=$API_Password&USER=$API_UserName&SIGNATURE=$API_Signature$nvpStr_";
426
+
427
+ // setting the nvpreq as POST FIELD to curl
428
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq);
429
+
430
+ // getting response from server
431
+ $httpResponse = curl_exec($ch);
432
+
433
+ if(empty($httpResponse)) {
434
+ exit("$methodName_ failed: ".curl_error($ch).'('.curl_errno($ch).')');
435
+ }
436
+
437
+ // Extract the RefundTransaction response details
438
+ $httpResponseAr = explode("&", $httpResponse);
439
+
440
+ $httpParsedResponseAr = array();
441
+ foreach ($httpResponseAr as $i => $value) {
442
+ $tmpAr = explode("=", $value);
443
+ if(sizeof($tmpAr) > 1) {
444
+ $httpParsedResponseAr[$tmpAr[0]] = $tmpAr[1];
445
+ }
446
+ }
447
+
448
+ if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) {
449
+ exit("Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint.");
450
+ }
451
+
452
+ return $httpParsedResponseAr;
453
+ }
454
+ }
classes/gateways/class.pmprogateway_paypalexpress.php ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway_paypalexpress
4
+ {
5
+ function PMProGateway_paypalexpress($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ if(pmpro_isLevelRecurring($order->membership_level))
14
+ {
15
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
16
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
17
+ return $this->subscribe($order);
18
+ }
19
+ else
20
+ return $this->charge($order);
21
+ }
22
+
23
+ //PayPal Express, this is run first to authorize from PayPal
24
+ function setExpressCheckout(&$order)
25
+ {
26
+ global $pmpro_currency;
27
+
28
+ if(empty($order->code))
29
+ $order->code = $order->getRandomCode();
30
+
31
+ //clean up a couple values
32
+ $order->payment_type = "PayPal Express";
33
+ $order->CardType = "";
34
+ $order->cardtype = "";
35
+
36
+ //taxes on initial amount
37
+ $initial_payment = $order->InitialPayment;
38
+ $initial_payment_tax = $order->getTaxForPrice($initial_payment);
39
+ $initial_payment = round((float)$initial_payment + (float)$initial_payment_tax, 2);
40
+
41
+ //taxes on the amount
42
+ $amount = $order->PaymentAmount;
43
+ $amount_tax = $order->getTaxForPrice($amount);
44
+ $order->subtotal = $amount;
45
+ $amount = round((float)$amount + (float)$amount_tax, 2);
46
+
47
+ //paypal profile stuff
48
+ $nvpStr = "";
49
+ $nvpStr .="&AMT=" . $initial_payment . "&CURRENCYCODE=" . $pmpro_currency . "&PROFILESTARTDATE=" . $order->ProfileStartDate;
50
+ $nvpStr .= "&BILLINGPERIOD=" . $order->BillingPeriod . "&BILLINGFREQUENCY=" . $order->BillingFrequency . "&AUTOBILLAMT=AddToNextBilling";
51
+ $nvpStr .= "&DESC=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127));
52
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
53
+ $nvpStr .= "&NOSHIPPING=1&L_BILLINGTYPE0=RecurringPayments&L_BILLINGAGREEMENTDESCRIPTION0=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127)) . "&L_PAYMENTTYPE0=Any";
54
+
55
+ //if billing cycles are defined
56
+ if(!empty($order->TotalBillingCycles))
57
+ $nvpStr .= "&TOTALBILLINGCYCLES=" . $order->TotalBillingCycles;
58
+
59
+ //if a trial period is defined
60
+ if(!empty($order->TrialBillingPeriod))
61
+ {
62
+ $trial_amount = $order->TrialAmount;
63
+ $trial_tax = $order->getTaxForPrice($trial_amount);
64
+ $trial_amount = round((float)$trial_amount + (float)$trial_tax, 2);
65
+
66
+ $nvpStr .= "&TRIALBILLINGPERIOD=" . $order->TrialBillingPeriod . "&TRIALBILLINGFREQUENCY=" . $order->TrialBillingFrequency . "&TRIALAMT=" . $trial_amount;
67
+ }
68
+ if(!empty($order->TrialBillingCycles))
69
+ $nvpStr .= "&TRIALTOTALBILLINGCYCLES=" . $order->TrialBillingCycles;
70
+
71
+ if(!empty($order->discount_code))
72
+ {
73
+ $nvpStr .= "&ReturnUrl=" . urlencode(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&discount_code=" . $order->discount_code . "&review=" . $order->code));
74
+ }
75
+ else
76
+ {
77
+ $nvpStr .= "&ReturnUrl=" . urlencode(pmpro_url("checkout", "?level=" . $order->membership_level->id . "&review=" . $order->code));
78
+ }
79
+
80
+ $additional_parameters = apply_filters("pmpro_paypal_express_return_url_parameters", array());
81
+ if(!empty($additional_parameters))
82
+ {
83
+ foreach($additional_parameters as $key => $value)
84
+ $nvpStr .= urlencode("&" . $key . "=" . $value);
85
+ }
86
+
87
+ $nvpStr .= "&CANCELURL=" . urlencode(pmpro_url("levels"));
88
+
89
+ $nvpStr = apply_filters("pmpro_set_express_checkout_nvpstr", $nvpStr, $order);
90
+
91
+ $this->httpParsedResponseAr = $this->PPHttpPost('SetExpressCheckout', $nvpStr);
92
+
93
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
94
+ $order->status = "token";
95
+ $order->paypal_token = urldecode($this->httpParsedResponseAr['TOKEN']);
96
+ $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
97
+
98
+ //update order
99
+ $order->saveOrder();
100
+
101
+ //redirect to paypal
102
+ $paypal_url = "https://www.paypal.com/webscr&cmd=_express-checkout&useraction=commit&token=" . $this->httpParsedResponseAr['TOKEN'];
103
+ $environment = pmpro_getOption("gateway_environment");
104
+ if("sandbox" === $environment || "beta-sandbox" === $environment)
105
+ {
106
+ $paypal_url = "https://www.sandbox.paypal.com/webscr&useraction=commit&cmd=_express-checkout&token=" . $this->httpParsedResponseAr['TOKEN'];
107
+ }
108
+
109
+ wp_redirect($paypal_url);
110
+ exit;
111
+
112
+ //exit('SetExpressCheckout Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
113
+ } else {
114
+ $order->status = "error";
115
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
116
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
117
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
118
+ return false;
119
+ //exit('SetExpressCheckout failed: ' . print_r($httpParsedResponseAr, true));
120
+ }
121
+
122
+ //write session?
123
+
124
+ //redirect to PayPal
125
+ }
126
+
127
+ function getExpressCheckoutDetails(&$order)
128
+ {
129
+ $nvpStr="&TOKEN=".$order->Token;
130
+
131
+ /* Make the API call and store the results in an array. If the
132
+ call was a success, show the authorization details, and provide
133
+ an action to complete the payment. If failed, show the error
134
+ */
135
+ $this->httpParsedResponseAr = $this->PPHttpPost('GetExpressCheckoutDetails', $nvpStr);
136
+
137
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
138
+ $order->status = "review";
139
+
140
+ //update order
141
+ $order->saveOrder();
142
+
143
+ return true;
144
+ } else {
145
+ $order->status = "error";
146
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
147
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
148
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
149
+ return false;
150
+ //exit('SetExpressCheckout failed: ' . print_r($httpParsedResponseAr, true));
151
+ }
152
+ }
153
+
154
+ function charge(&$order)
155
+ {
156
+ global $pmpro_currency;
157
+
158
+ if(empty($order->code))
159
+ $order->code = $order->getRandomCode();
160
+
161
+ //taxes on the amount
162
+ $amount = $order->InitialPayment;
163
+ $amount_tax = $order->getTaxForPrice($amount);
164
+ $order->subtotal = $amount;
165
+ $amount = round((float)$amount + (float)$amount_tax, 2);
166
+
167
+ //paypal profile stuff
168
+ $nvpStr = "";
169
+ if(!empty($order->Token))
170
+ $nvpStr .= "&TOKEN=" . $order->Token;
171
+ $nvpStr .="&AMT=" . $amount . "&CURRENCYCODE=" . $pmpro_currency . "&PROFILESTARTDATE=" . $order->ProfileStartDate;
172
+ if(!empty($amount_tax))
173
+ $nvpStr .= "&TAXAMT=" . $amount_tax;
174
+ $nvpStr .= "&BILLINGPERIOD=" . $order->BillingPeriod . "&BILLINGFREQUENCY=" . $order->BillingFrequency . "&AUTOBILLAMT=AddToNextBilling";
175
+ $nvpStr .= "&DESC=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127));
176
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
177
+ $nvpStr .= "&NOSHIPPING=1";
178
+
179
+ $nvpStr .= "&PAYERID=" . $_SESSION['payer_id'] . "&PAYMENTACTION=sale";
180
+ $order->nvpStr = $nvpStr;
181
+
182
+ $this->httpParsedResponseAr = $this->PPHttpPost('DoExpressCheckoutPayment', $nvpStr);
183
+
184
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
185
+ $order->payment_transaction_id = urldecode($this->httpParsedResponseAr['TRANSACTIONID']);
186
+ $order->status = "success";
187
+
188
+ //update order
189
+ $order->saveOrder();
190
+
191
+ return true;
192
+ } else {
193
+ $order->status = "error";
194
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
195
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
196
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
197
+ return false;
198
+ //exit('SetExpressCheckout failed: ' . print_r($httpParsedResponseAr, true));
199
+ }
200
+ }
201
+
202
+ function subscribe(&$order)
203
+ {
204
+ global $pmpro_currency;
205
+
206
+ if(empty($order->code))
207
+ $order->code = $order->getRandomCode();
208
+
209
+ //filter order before subscription. use with care.
210
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
211
+
212
+ //taxes on initial amount
213
+ $initial_payment = $order->InitialPayment;
214
+ $initial_payment_tax = $order->getTaxForPrice($initial_payment);
215
+ $initial_payment = round((float)$initial_payment + (float)$initial_payment_tax, 2);
216
+
217
+ //taxes on the amount
218
+ $amount = $order->PaymentAmount;
219
+ $amount_tax = $order->getTaxForPrice($amount);
220
+ $amount = round((float)$amount + (float)$amount_tax, 2);
221
+
222
+ //paypal profile stuff
223
+ $nvpStr = "";
224
+ if(!empty($order->Token))
225
+ $nvpStr .= "&TOKEN=" . $order->Token;
226
+ $nvpStr .="&INITAMT=" . $initial_payment . "&AMT=" . $amount . "&CURRENCYCODE=" . $pmpro_currency . "&PROFILESTARTDATE=" . $order->ProfileStartDate;
227
+ if(!empty($amount_tax))
228
+ $nvpStr .= "&TAXAMT=" . $amount_tax;
229
+ $nvpStr .= "&BILLINGPERIOD=" . $order->BillingPeriod . "&BILLINGFREQUENCY=" . $order->BillingFrequency . "&AUTOBILLAMT=AddToNextBilling";
230
+ $nvpStr .= "&NOTIFYURL=" . urlencode(admin_url('admin-ajax.php') . "?action=ipnhandler");
231
+ $nvpStr .= "&DESC=" . urlencode(substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127));
232
+
233
+ //if billing cycles are defined
234
+ if($order->TotalBillingCycles)
235
+ $nvpStr .= "&TOTALBILLINGCYCLES=" . $order->TotalBillingCycles;
236
+
237
+ //if a trial period is defined
238
+ if(!empty($order->TrialBillingPeriod))
239
+ {
240
+ $trial_amount = $order->TrialAmount;
241
+ $trial_tax = $order->getTaxForPrice($trial_amount);
242
+ $trial_amount = round((float)$trial_amount + (float)$trial_tax, 2);
243
+
244
+ $nvpStr .= "&TRIALBILLINGPERIOD=" . $order->TrialBillingPeriod . "&TRIALBILLINGFREQUENCY=" . $order->TrialBillingFrequency . "&TRIALAMT=" . $trial_amount;
245
+ }
246
+ if(!empty($order->TrialBillingCycles))
247
+ $nvpStr .= "&TRIALTOTALBILLINGCYCLES=" . $order->TrialBillingCycles;
248
+
249
+ $this->nvpStr = $nvpStr;
250
+
251
+ $this->httpParsedResponseAr = $this->PPHttpPost('CreateRecurringPaymentsProfile', $nvpStr);
252
+
253
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"])) {
254
+ $order->status = "success";
255
+ $order->payment_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
256
+ $order->subscription_transaction_id = urldecode($this->httpParsedResponseAr['PROFILEID']);
257
+
258
+ //update order
259
+ $order->saveOrder();
260
+
261
+ return true;
262
+ } else {
263
+ $order->status = "error";
264
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
265
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
266
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
267
+
268
+ return false;
269
+ }
270
+ }
271
+
272
+ function cancel(&$order)
273
+ {
274
+ //paypal profile stuff
275
+ $nvpStr = "";
276
+ $nvpStr .= "&PROFILEID=" . $order->subscription_transaction_id . "&ACTION=Cancel&NOTE=User requested cancel.";
277
+
278
+ $this->httpParsedResponseAr = $this->PPHttpPost('ManageRecurringPaymentsProfileStatus', $nvpStr);
279
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"]) || $this->httpParsedResponseAr['L_ERRORCODE0'] == "11556") {
280
+ $order->updateStatus("cancelled");
281
+ return true;
282
+ //exit('CreateRecurringPaymentsProfile Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
283
+ } else {
284
+ $order->status = "error";
285
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
286
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
287
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
288
+
289
+ return false;
290
+ //exit('CreateRecurringPaymentsProfile failed: ' . print_r($httpParsedResponseAr, true));
291
+ }
292
+ }
293
+
294
+ /**
295
+ * PAYPAL Function
296
+ * Send HTTP POST Request
297
+ *
298
+ * @param string The API method name
299
+ * @param string The POST Message fields in &name=value pair format
300
+ * @return array Parsed HTTP Response body
301
+ */
302
+ function PPHttpPost($methodName_, $nvpStr_) {
303
+ global $gateway_environment;
304
+ $environment = $gateway_environment;
305
+
306
+ $API_UserName = pmpro_getOption("apiusername");
307
+ $API_Password = pmpro_getOption("apipassword");
308
+ $API_Signature = pmpro_getOption("apisignature");
309
+ $API_Endpoint = "https://api-3t.paypal.com/nvp";
310
+ if("sandbox" === $environment || "beta-sandbox" === $environment) {
311
+ $API_Endpoint = "https://api-3t.$environment.paypal.com/nvp";
312
+ }
313
+
314
+ $version = urlencode('72.0');
315
+
316
+ // setting the curl parameters.
317
+ $ch = curl_init();
318
+ curl_setopt($ch, CURLOPT_URL, $API_Endpoint);
319
+ curl_setopt($ch, CURLOPT_VERBOSE, 1);
320
+
321
+ // turning off the server and peer verification(TrustManager Concept).
322
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
323
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
324
+
325
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
326
+ curl_setopt($ch, CURLOPT_POST, 1);
327
+
328
+ // NVPRequest for submitting to server
329
+ $nvpreq = "METHOD=$methodName_&VERSION=$version&PWD=$API_Password&USER=$API_UserName&SIGNATURE=$API_Signature$nvpStr_";
330
+
331
+ // setting the nvpreq as POST FIELD to curl
332
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq);
333
+
334
+ // getting response from server
335
+ $httpResponse = curl_exec($ch);
336
+
337
+ if(!$httpResponse) {
338
+ exit("$methodName_ failed: ".curl_error($ch).'('.curl_errno($ch).')');
339
+ }
340
+
341
+ // Extract the RefundTransaction response details
342
+ $httpResponseAr = explode("&", $httpResponse);
343
+
344
+ $httpParsedResponseAr = array();
345
+ foreach ($httpResponseAr as $i => $value) {
346
+ $tmpAr = explode("=", $value);
347
+ if(sizeof($tmpAr) > 1) {
348
+ $httpParsedResponseAr[$tmpAr[0]] = $tmpAr[1];
349
+ }
350
+ }
351
+
352
+ if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) {
353
+ exit("Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint.");
354
+ }
355
+
356
+ return $httpParsedResponseAr;
357
+ }
358
+ }
classes/gateways/class.pmprogateway_paypalstandard.php ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ class PMProGateway_paypalstandard
4
+ {
5
+ function PMProGateway_paypalstandard($gateway = NULL)
6
+ {
7
+ $this->gateway = $gateway;
8
+ return $this->gateway;
9
+ }
10
+
11
+ function process(&$order)
12
+ {
13
+ if(empty($order->code))
14
+ $order->code = $order->getRandomCode();
15
+
16
+ //clean up a couple values
17
+ $order->payment_type = "PayPal Standard";
18
+ $order->CardType = "";
19
+ $order->cardtype = "";
20
+
21
+ //just save, the user will go to PayPal to pay
22
+ $order->status = "review";
23
+ $order->saveOrder();
24
+
25
+ return true;
26
+ }
27
+
28
+ function sendToPayPal(&$order)
29
+ {
30
+ global $pmpro_currency;
31
+
32
+ //taxes on initial amount
33
+ $initial_payment = $order->InitialPayment;
34
+ $initial_payment_tax = $order->getTaxForPrice($initial_payment);
35
+ $initial_payment = round((float)$initial_payment + (float)$initial_payment_tax, 2);
36
+
37
+ //taxes on the amount
38
+ $amount = $order->PaymentAmount;
39
+ $amount_tax = $order->getTaxForPrice($amount);
40
+ $order->subtotal = $amount;
41
+ $amount = round((float)$amount + (float)$amount_tax, 2);
42
+
43
+ //build PayPal Redirect
44
+ $environment = pmpro_getOption("gateway_environment");
45
+ if("sandbox" === $environment || "beta-sandbox" === $environment)
46
+ $paypal_url ="https://www.sandbox.paypal.com/cgi-bin/webscr?business=" . urlencode(pmpro_getOption("gateway_email"));
47
+ else
48
+ $paypal_url = "https://www.paypal.com/cgi-bin/webscr?business=" . urlencode(pmpro_getOption("gateway_email"));
49
+
50
+ if(pmpro_isLevelRecurring($order->membership_level))
51
+ {
52
+ //convert billing period
53
+ if($order->BillingPeriod == "Day")
54
+ $period = "D";
55
+ elseif($order->BillingPeriod == "Week")
56
+ $period = "W";
57
+ elseif($order->BillingPeriod == "Month")
58
+ $period = "M";
59
+ elseif($order->BillingPeriod == "Year")
60
+ $period = "Y";
61
+ else
62
+ {
63
+ $order->error = "Invalid billing period: " . $order->BillingPeriod;
64
+ $order->shorterror = "Invalid billing period: " . $order->BillingPeriod;
65
+ return false;
66
+ }
67
+
68
+ //other args
69
+ $paypal_args = array(
70
+ 'cmd' => '_xclick-subscriptions',
71
+ 'a1' => number_format($initial_payment, 2),
72
+ 'p1' => $order->BillingFrequency,
73
+ 't1' => $period,
74
+ 'a3' => number_format($amount, 2),
75
+ 'p3' => $order->BillingFrequency,
76
+ 't3' => $period,
77
+ 'item_name' => substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127),
78
+ 'email' => $order->Email,
79
+ 'no_shipping' => '1',
80
+ 'shipping' => '0',
81
+ 'no_note' => '1',
82
+ 'currency_code' => $pmpro_currency,
83
+ 'item_number' => $order->code,
84
+ 'charset' => get_bloginfo( 'charset' ),
85
+ 'rm' => '2',
86
+ 'return' => pmpro_url("confirmation", "?level=" . $order->membership_level->id),
87
+ 'notify_url' => admin_url("admin-ajax.php") . "?action=ipnhandler",
88
+ 'src' => '1',
89
+ 'sra' => '1'
90
+ );
91
+
92
+ //trial?
93
+ /*
94
+ Note here that the TrialBillingCycles value is being ignored. PayPal Standard only offers 1 payment during each trial period.
95
+ */
96
+ if(!empty($order->TrialBillingPeriod))
97
+ {
98
+ //if a1 and a2 are 0, let's just combine them. PayPal doesn't like a2 = 0.
99
+ if($paypal_args['a1'] == 0 && $order->TrialAmount == 0)
100
+ {
101
+ $paypal_args['p1'] = $paypal_args['p1'] + $order->TrialBillingFrequency;
102
+ }
103
+ else
104
+ {
105
+ $trial_amount = $order->TrialAmount;
106
+ $trial_tax = $order->getTaxForPrice($trial_amount);
107
+ $trial_amount = round((float)$trial_amount + (float)$trial_tax, 2);
108
+
109
+ $paypal_args['a2'] = $trial_amount;
110
+ $paypal_args['p2'] = $order->TrialBillingFrequency;
111
+ $paypal_args['t2'] = $period;
112
+ }
113
+ }
114
+ else
115
+ {
116
+ //we can try to work in any change in ProfileStartDate
117
+ $psd = date("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod)) . "T0:0:0";
118
+ $adjusted_psd = apply_filters("pmpro_profile_start_date", $psd, $order);
119
+ if($psd != $adjusted_psd)
120
+ {
121
+ //someone is trying to push the start date back
122
+ $adjusted_psd_time = strtotime($adjusted_psd);
123
+ $seconds_til_psd = $adjusted_psd_time - time();
124
+ $days_til_psd = floor($seconds_til_psd/(60*60*24));
125
+
126
+ //push back trial one by days_til_psd
127
+ if($days_til_psd > 90)
128
+ {
129
+ //we need to convert to weeks, because PayPal limits t1 to 90 days
130
+ $weeks_til_psd = round($days_til_psd / 7);
131
+ $paypal_args['p1'] = $weeks_til_psd;
132
+ $paypal_args['t1'] = "W";
133
+ }
134
+ elseif($days_til_psd > 0)
135
+ {
136
+ //use days
137
+ $paypal_args['p1'] = $days_til_psd;
138
+ $paypal_args['t1'] = "D";
139
+ }
140
+ }
141
+ }
142
+
143
+ //billing limit?
144
+ if(!empty($order->TotalBillingCycles))
145
+ {
146
+ if(!empty($trial_amount))
147
+ $paypal_args['srt'] = intval($order->TotalBillingCycles) - 1; //subtract 1 for the trial period
148
+ else
149
+ $paypal_args['srt'] = intval($order->TotalBillingCycles);
150
+ }
151
+ else
152
+ $paypal_args['srt'] = '0'; //indefinite subscription
153
+ }
154
+ else
155
+ {
156
+ //other args
157
+ $paypal_args = array(
158
+ 'cmd' => '_xclick',
159
+ 'amount' => number_format($initial_payment, 2),
160
+ 'item_name' => substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127),
161
+ 'email' => $order->Email,
162
+ 'no_shipping' => '1',
163
+ 'shipping' => '0',
164
+ 'no_note' => '1',
165
+ 'currency_code' => $pmpro_currency,
166
+ 'item_number' => $order->code,
167
+ 'charset' => get_bloginfo( 'charset' ),
168
+ 'rm' => '2',
169
+ 'return' => pmpro_url("confirmation", "?level=" . $order->membership_level->id),
170
+ 'notify_url' => admin_url("admin-ajax.php") . "?action=ipnhandler"
171
+ );
172
+ }
173
+
174
+ $nvpStr = "";
175
+ foreach($paypal_args as $key => $value)
176
+ {
177
+ $nvpStr .= "&" . $key . "=" . urlencode($value);
178
+ }
179
+
180
+ //anything modders might add
181
+ $additional_parameters = apply_filters("pmpro_paypal_express_return_url_parameters", array());
182
+ if(!empty($additional_parameters))
183
+ {
184
+ foreach($additional_parameters as $key => $value)
185
+ $nvpStr .= urlencode("&" . $key . "=" . $value);
186
+ }
187
+
188
+ $nvpStr = apply_filters("pmpro_paypal_standard_nvpstr", $nvpStr, $order);
189
+
190
+ //redirect to paypal
191
+ $paypal_url .= $nvpStr;
192
+
193
+ //die($paypal_url);
194
+
195
+ wp_redirect($paypal_url);
196
+ exit;
197
+ }
198
+
199
+ function cancel(&$order)
200
+ {
201
+ //paypal profile stuff
202
+ $nvpStr = "";
203
+ $nvpStr .= "&PROFILEID=" . $order->subscription_transaction_id . "&ACTION=Cancel&NOTE=User requested cancel.";
204
+
205
+ $this->httpParsedResponseAr = $this->PPHttpPost('ManageRecurringPaymentsProfileStatus', $nvpStr);
206
+ if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"]) || $this->httpParsedResponseAr['L_ERRORCODE0'] == "11556") {
207
+ $order->updateStatus("cancelled");
208
+ return true;
209
+ //exit('CreateRecurringPaymentsProfile Completed Successfully: '.print_r($this->httpParsedResponseAr, true));
210
+ } else {
211
+ $order->status = "error";
212
+ $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0'];
213
+ $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']);
214
+ $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']);
215
+
216
+ return false;
217
+ //exit('CreateRecurringPaymentsProfile failed: ' . print_r($httpParsedResponseAr, true));
218
+ }
219
+ }
220
+
221
+ /**
222
+ * PAYPAL Function
223
+ * Send HTTP POST Request
224
+ *
225
+ * @param string The API method name
226
+ * @param string The POST Message fields in &name=value pair format
227
+ * @return array Parsed HTTP Response body
228
+ */
229
+ function PPHttpPost($methodName_, $nvpStr_) {
230
+ global $gateway_environment;
231
+ $environment = $gateway_environment;
232
+
233
+ $API_UserName = pmpro_getOption("apiusername");
234
+ $API_Password = pmpro_getOption("apipassword");
235
+ $API_Signature = pmpro_getOption("apisignature");
236
+ $API_Endpoint = "https://api-3t.paypal.com/nvp";
237
+ if("sandbox" === $environment || "beta-sandbox" === $environment) {
238
+ $API_Endpoint = "https://api-3t.$environment.paypal.com/nvp";
239
+ }
240
+
241
+ $version = urlencode('72.0');
242
+
243
+ // setting the curl parameters.
244
+ $ch = curl_init();
245
+ curl_setopt($ch, CURLOPT_URL, $API_Endpoint);
246
+ curl_setopt($ch, CURLOPT_VERBOSE, 1);
247
+
248
+ // turning off the server and peer verification(TrustManager Concept).
249
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
250
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
251
+
252
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
253
+ curl_setopt($ch, CURLOPT_POST, 1);
254
+
255
+ // NVPRequest for submitting to server
256
+ $nvpreq = "METHOD=$methodName_&VERSION=$version&PWD=$API_Password&USER=$API_UserName&SIGNATURE=$API_Signature$nvpStr_";
257
+
258
+ // setting the nvpreq as POST FIELD to curl
259
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $nvpreq);
260
+
261
+ // getting response from server
262
+ $httpResponse = curl_exec($ch);
263
+
264
+ if(!$httpResponse) {
265
+ exit("$methodName_ failed: ".curl_error($ch).'('.curl_errno($ch).')');
266
+ }
267
+
268
+ // Extract the RefundTransaction response details
269
+ $httpResponseAr = explode("&", $httpResponse);
270
+
271
+ $httpParsedResponseAr = array();
272
+ foreach ($httpResponseAr as $i => $value) {
273
+ $tmpAr = explode("=", $value);
274
+ if(sizeof($tmpAr) > 1) {
275
+ $httpParsedResponseAr[$tmpAr[0]] = $tmpAr[1];
276
+ }
277
+ }
278
+
279
+ if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) {
280
+ exit("Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint.");
281
+ }
282
+
283
+ return $httpParsedResponseAr;
284
+ }
285
+ }
classes/gateways/class.pmprogateway_stripe.php ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ if(!class_exists("Stripe"))
4
+ require_once(dirname(__FILE__) . "/../../includes/lib/Stripe/Stripe.php");
5
+ class PMProGateway_stripe
6
+ {
7
+ function PMProGateway_stripe($gateway = NULL)
8
+ {
9
+ $this->gateway = $gateway;
10
+ $this->gateway_environment = pmpro_getOption("gateway_environment");
11
+
12
+ Stripe::setApiKey(pmpro_getOption("stripe_secretkey"));
13
+
14
+ return $this->gateway;
15
+ }
16
+
17
+ function process(&$order)
18
+ {
19
+ //check for initial payment
20
+ if(floatval($order->InitialPayment) == 0)
21
+ {
22
+ //just subscribe
23
+ return $this->subscribe($order);
24
+ }
25
+ else
26
+ {
27
+ //charge then subscribe
28
+ if($this->charge($order))
29
+ {
30
+ if(pmpro_isLevelRecurring($order->membership_level))
31
+ {
32
+ if($this->subscribe($order))
33
+ {
34
+ //yay!
35
+ return true;
36
+ }
37
+ else
38
+ {
39
+ //try to refund initial charge
40
+ return false;
41
+ }
42
+ }
43
+ else
44
+ {
45
+ //only a one time charge
46
+ $order->status = "success"; //saved on checkout page
47
+ return true;
48
+ }
49
+ }
50
+ else
51
+ {
52
+ if(empty($order->error))
53
+ $order->error = __("Unknown error: Initial payment failed.", "pmpro");
54
+ return false;
55
+ }
56
+ }
57
+ }
58
+
59
+ function charge(&$order)
60
+ {
61
+ //create a code for the order
62
+ if(empty($order->code))
63
+ $order->code = $order->getRandomCode();
64
+
65
+ //what amount to charge?
66
+ $amount = $order->InitialPayment;
67
+
68
+ //tax
69
+ $order->subtotal = $amount;
70
+ $tax = $order->getTax(true);
71
+ $amount = round((float)$order->subtotal + (float)$tax, 2);
72
+
73
+ //create a customer
74
+ $this->getCustomer($order);
75
+ if(empty($this->customer))
76
+ {
77
+ //failed to create customer
78
+ return false;
79
+ }
80
+
81
+ //charge
82
+ try
83
+ {
84
+ $response = Stripe_Charge::create(array(
85
+ "amount" => $amount * 100, # amount in cents, again
86
+ "currency" => strtolower(pmpro_getOption("currency")),
87
+ "customer" => $this->customer->id,
88
+ "description" => "Order #" . $order->code . ", " . trim($order->FirstName . " " . $order->LastName) . " (" . $order->Email . ")"
89
+ )
90
+ );
91
+ }
92
+ catch (Exception $e)
93
+ {
94
+ //$order->status = "error";
95
+ $order->errorcode = true;
96
+ $order->error = "Error: " . $e->getMessage();
97
+ $order->shorterror = $order->error;
98
+ return false;
99
+ }
100
+
101
+ if(empty($response["failure_message"]))
102
+ {
103
+ //successful charge
104
+ $order->payment_transaction_id = $response["id"];
105
+ $order->updateStatus("success");
106
+ return true;
107
+ }
108
+ else
109
+ {
110
+ //$order->status = "error";
111
+ $order->errorcode = true;
112
+ $order->error = $response['failure_message'];
113
+ $order->shorterror = $response['failure_message'];
114
+ return false;
115
+ }
116
+ }
117
+
118
+ /*
119
+ This function will return a Stripe customer object.
120
+ If $this->customer is set, it returns it.
121
+ It first checks if the order has a subscription_transaction_id. If so, that's the customer id.
122
+ If not, it checks for a user_id on the order and searches for a customer id in the user meta.
123
+ If a customer id is found, it checks for a customer through the Stripe API.
124
+ If a customer is found and there is a stripeToken on the order passed, it will update the customer.
125
+ If no customer is found and there is a stripeToken on the order passed, it will create a customer.
126
+ */
127
+ function getCustomer(&$order, $force = false)
128
+ {
129
+ global $current_user;
130
+
131
+ //already have it?
132
+ if(!empty($this->customer) && !$force)
133
+ return $this->customer;
134
+
135
+ //transaction id?
136
+ if(!empty($order->subscription_transaction_id))
137
+ $customer_id = $order->subscription_transaction_id;
138
+ else
139
+ {
140
+ //try based on user id
141
+ if(!empty($order->user_id))
142
+ $user_id = $order->user_id;
143
+
144
+ //if no id passed, check the current user
145
+ if(empty($user_id) && !empty($current_user->ID))
146
+ $user_id = $current_user->ID;
147
+
148
+ //check for a stripe customer id
149
+ if(!empty($user_id))
150
+ {
151
+ $customer_id = get_user_meta($user_id, "pmpro_stripe_customerid", true);
152
+ }
153
+ }
154
+
155
+ //check for an existing stripe customer
156
+ if(!empty($customer_id))
157
+ {
158
+ try
159
+ {
160
+ $this->customer = Stripe_Customer::retrieve($customer_id);
161
+
162
+ //update the customer description and card
163
+ if(!empty($order->stripeToken))
164
+ {
165
+ $this->customer->description = trim($order->FirstName . " " . $order->LastName) . " (" . $order->Email . ")";
166
+ $this->customer->card = $order->stripeToken;
167
+ $this->customer->save();
168
+ }
169
+
170
+ return $this->customer;
171
+ }
172
+ catch (Exception $e)
173
+ {
174
+ //assume no customer found
175
+ }
176
+ }
177
+
178
+ //no customer id, create one
179
+ if(!empty($order->stripeToken))
180
+ {
181
+ try
182
+ {
183
+ $this->customer = Stripe_Customer::create(array(
184
+ "description" => trim($order->FirstName . " " . $order->LastName) . " (" . $order->Email . ")",
185
+ "card" => $order->stripeToken
186
+ ));
187
+ }
188
+ catch (Exception $e)
189
+ {
190
+ $order->error = __("Error creating customer record with Stripe:", "pmpro") . " " . $e->getMessage();
191
+ $order->shorterror = $order->error;
192
+ return false;
193
+ }
194
+
195
+ update_user_meta($user_id, "pmpro_stripe_customerid", $this->customer->id);
196
+
197
+ return $this->customer;
198
+ }
199
+
200
+ return false;
201
+ }
202
+
203
+ function subscribe(&$order)
204
+ {
205
+ //create a code for the order
206
+ if(empty($order->code))
207
+ $order->code = $order->getRandomCode();
208
+
209
+ //filter order before subscription. use with care.
210
+ $order = apply_filters("pmpro_subscribe_order", $order, $this);
211
+
212
+ //setup customer
213
+ $this->getCustomer($order);
214
+ if(empty($this->customer))
215
+ return false; //error retrieving customer
216
+
217
+ //figure out the amounts
218
+ $amount = $order->PaymentAmount;
219
+ $amount_tax = $order->getTaxForPrice($amount);
220
+ $amount = round((float)$amount + (float)$amount_tax, 2);
221
+
222
+ /*
223
+ There are two parts to the trial. Part 1 is simply the delay until the first payment
224
+ since we are doing the first payment as a separate transaction.
225
+ The second part is the actual "trial" set by the admin.
226
+
227
+ Stripe only supports Year or Month for billing periods, but we account for Days and Weeks just in case.
228
+ */
229
+ //figure out the trial length (first payment handled by initial charge)
230
+ if($order->BillingPeriod == "Year")
231
+ $trial_period_days = $order->BillingFrequency * 365; //annual
232
+ elseif($order->BillingPeriod == "Day")
233
+ $trial_period_days = $order->BillingFrequency * 1; //daily
234
+ elseif($order->BillingPeriod == "Week")
235
+ $trial_period_days = $order->BillingFrequency * 7; //weekly
236
+ else
237
+ $trial_period_days = $order->BillingFrequency * 30; //assume monthly
238
+
239
+ //convert to a profile start date
240
+ $order->ProfileStartDate = date("Y-m-d", strtotime("+ " . $trial_period_days . " Day")) . "T0:0:0";
241
+
242
+ //filter the start date
243
+ $order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
244
+
245
+ //convert back to days
246
+ $trial_period_days = ceil(abs(strtotime(date("Y-m-d")) - strtotime($order->ProfileStartDate)) / 86400);
247
+
248
+ //now add the actual trial set by the site
249
+ if(!empty($order->TrialBillingCycles))
250
+ {
251
+ $trialOccurrences = (int)$order->TrialBillingCycles;
252
+ if($order->BillingPeriod == "Year")
253
+ $trial_period_days = $trial_period_days + (365 * $order->BillingFrequency * $trialOccurrences); //annual
254
+ elseif($order->BillingPeriod == "Day")
255
+ $trial_period_days = $trial_period_days + (1 * $order->BillingFrequency * $trialOccurrences); //daily
256
+ elseif($order->BillingPeriod == "Week")
257
+ $trial_period_days = $trial_period_days + (7 * $order->BillingFrequency * $trialOccurrences); //weekly
258
+ else
259
+ $trial_period_days = $trial_period_days + (30 * $order->BillingFrequency * $trialOccurrences); //assume monthly
260
+ }
261
+
262
+ //create a plan
263
+ try
264
+ {
265
+ $plan = Stripe_Plan::create(array(
266
+ "amount" => $amount * 100,
267
+ "interval_count" => $order->BillingFrequency,
268
+ "interval" => strtolower($order->BillingPeriod),
269
+ "trial_period_days" => $trial_period_days,
270
+ "name" => $order->membership_name . " for order " . $order->code,
271
+ "currency" => strtolower(pmpro_getOption("currency")),
272
+ "id" => $order->code)
273
+ );
274
+ }
275
+ catch (Exception $e)
276
+ {
277
+ $order->error = __("Error creating plan with Stripe:", "pmpro") . $e->getMessage();
278
+ $order->shorterror = $order->error;
279
+ return false;
280
+ }
281
+
282
+ //subscribe to the plan
283
+ try
284
+ {
285
+ $this->customer->updateSubscription(array("prorate" => false, "plan" => $order->code));
286
+ }
287
+ catch (Exception $e)
288
+ {
289
+ //try to delete the plan
290
+ $plan->delete();
291
+
292
+ //return error
293
+ $order->error = __("Error subscribing customer to plan with Stripe:", "pmpro") . $e->getMessage();
294
+ $order->shorterror = $order->error;
295
+ return false;
296
+ }
297
+
298
+ //delete the plan
299
+ $plan = Stripe_Plan::retrieve($plan['id']);
300
+ $plan->delete();
301
+
302
+ //if we got this far, we're all good
303
+ $order->status = "success";
304
+ $order->subscription_transaction_id = $this->customer['id']; //transaction id is the customer id, we save it in user meta later too
305
+ return true;
306
+ }
307
+
308
+ function update(&$order)
309
+ {
310
+ //we just have to run getCustomer which will look for the customer and update it with the new token
311
+ $this->getCustomer($order);
312
+
313
+ if(!empty($this->customer))
314
+ {
315
+ return true;
316
+ }
317
+ else
318
+ {
319
+ return false; //couldn't find the customer
320
+ }
321
+ }
322
+
323
+ function cancel(&$order)
324
+ {
325
+ //require a subscription id
326
+ if(empty($order->subscription_transaction_id))
327
+ return false;
328
+
329
+ //find the customer
330
+ $this->getCustomer($order);
331
+
332
+ if(!empty($this->customer))
333
+ {
334
+ //cancel
335
+ try
336
+ {
337
+ $this->customer->cancelSubscription();
338
+ }
339
+ catch(Exception $e)
340
+ {
341
+ $order->updateStatus("cancelled"); //assume it's been cancelled already
342
+ $order->error = __("Could not find the subscription.", "pmpro");
343
+ $order->shorterror = $order->error;
344
+ return false; //no subscription found
345
+ }
346
+
347
+ $order->updateStatus("cancelled");
348
+ return true;
349
+ }
350
+ else
351
+ {
352
+ $order->error = __("Could not find the subscription.", "pmpro");
353
+ $order->shorterror = $order->error;
354
+ return false; //no customer found
355
+ }
356
+ }
357
+ }
classes/gateways/class.pmprogateway_twocheckout.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/class.pmprogateway.php");
3
+ if(!class_exists("Twocheckout"))
4
+ require_once(dirname(__FILE__) . "/../../includes/lib/Twocheckout/Twocheckout.php");
5
+ class PMProGateway_Twocheckout
6
+ {
7
+ function PMProGateway_Twocheckout($gateway = NULL)
8
+ {
9
+ $this->gateway = $gateway;
10
+ return $this->gateway;
11
+ }
12
+
13
+ function process(&$order)
14
+ {
15
+ if(empty($order->code))
16
+ $order->code = $order->getRandomCode();
17
+
18
+ //clean up a couple values
19
+ $order->payment_type = "2CheckOut";
20
+ $order->CardType = "";
21
+ $order->cardtype = "";
22
+
23
+ //just save, the user will go to PayPal to pay
24
+ $order->status = "review";
25
+ $order->saveOrder();
26
+
27
+ return true;
28
+ }
29
+
30
+ function sendToTwocheckout(&$order)
31
+ {
32
+ global $pmpro_currency;
33
+ // Set up credentials
34
+ Twocheckout::setCredentials( pmpro_getOption("twocheckout_apiusername"), pmpro_getOption("twocheckout_apipassword") );
35
+
36
+ $tco_args = array(
37
+ 'sid' => pmpro_getOption("twocheckout_accountnumber"),
38
+ 'mode' => '2CO', // will always be 2CO according to docs (@see https://www.2checkout.com/documentation/checkout/parameter-sets/pass-through-products/)
39
+ 'li_0_type' => 'product',
40
+ 'li_0_name' => substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127),
41
+ 'li_0_quantity' => 1,
42
+ 'li_0_tangible' => 'N',
43
+ 'li_0_product_id' => $order->code,
44
+ 'currency_code' => $pmpro_currency,
45
+ 'pay_method' => 'CC',
46
+ 'purchase_step' => 'billing-information',
47
+ 'x_receipt_link_url' => pmpro_url("confirmation", "?level=" . $order->membership_level->id)
48
+ );
49
+
50
+ //taxes on initial amount
51
+ $initial_payment = $order->InitialPayment;
52
+ $initial_payment_tax = $order->getTaxForPrice($initial_payment);
53
+ $initial_payment = round((float)$initial_payment + (float)$initial_payment_tax, 2);
54
+
55
+ // Recurring membership
56
+ if( pmpro_isLevelRecurring( $order->membership_level ) ) {
57
+ $tco_args['li_0_startup_fee'] = $initial_payment;
58
+
59
+ $recurring_payment = $order->membership_level->billing_amount;
60
+ $recurring_payment_tax = $order->getTaxForPrice($recurring_payment);
61
+ $recurring_payment = round((float)$recurring_payment + (float)$recurring_payment_tax, 2);
62
+ $tco_args['li_0_price'] = $recurring_payment;
63
+
64
+ $tco_args['li_0_recurrance'] = ( $order->BillingFrequency == 1 ) ? $order->BillingFrequency . ' ' . $order->BillingPeriod : $order->BillingFrequency . ' ' . $order->BillingPeriod . 's';
65
+
66
+ if( property_exists( $order, 'TotalBillingCycles' ) )
67
+ $tco_args['li_0_duration'] = ($order->BillingFrequency * $order->TotalBillingCycles ) . ' ' . $order->BillingPeriod;
68
+ }
69
+ // Non-recurring membership
70
+ else {
71
+ $tco_args['li_0_price'] = $initial_payment;
72
+ }
73
+
74
+ // Demo mode?
75
+ $environment = pmpro_getOption("gateway_environment");
76
+ if("sandbox" === $environment || "beta-sandbox" === $environment)
77
+ $tco_args['demo'] = 'Y';
78
+
79
+ //print_r( $tco_args );
80
+ //print_r( $order );
81
+
82
+ // Trial?
83
+ //li_#_startup_fee Any start up fees for the product or service. Can be negative to provide discounted first installment pricing, but cannot equal or surpass the product price.
84
+
85
+ // Coupon?
86
+ //coupon Specify a 2Checkout created coupon code. If applicable, the coupon will be automatically applied to the sale.
87
+
88
+ //taxes on the amount (NOT CURRENTLY USED)
89
+ $amount = $order->PaymentAmount;
90
+ $amount_tax = $order->getTaxForPrice($amount);
91
+ $order->subtotal = $amount;
92
+ $amount = round((float)$amount + (float)$amount_tax, 2);
93
+
94
+
95
+
96
+ $ptpStr = '';
97
+ foreach( $tco_args as $key => $value ) {
98
+ reset( $tco_args ); // Used to verify whether or not we're on the first argument
99
+ $ptpStr .= ( $key == key($tco_args) ) ? '?' . $key . '=' . urlencode( $value ) : '&' . $key . '=' . urlencode( $value );
100
+ }
101
+
102
+ //anything modders might add
103
+ $additional_parameters = apply_filters( 'pmpro_twocheckout_return_url_parameters', array() );
104
+ if( ! empty( $additional_parameters ) )
105
+ foreach( $additional_parameters as $key => $value )
106
+ $ptpStr .= urlencode( "&" . $key . "=" . $value );
107
+
108
+ $ptpStr = apply_filters( 'pmpro_twocheckout_ptpstr', $ptpStr, $order );
109
+ //echo "<br /><br />".$ptpStr. "<br /><br />";
110
+
111
+ //redirect to paypal
112
+ $tco_url = 'https://www.2checkout.com/checkout/purchase' . $ptpStr;
113
+
114
+ //echo $tco_url;
115
+ //die();
116
+ wp_redirect( $tco_url );
117
+ exit;
118
+ }
119
+
120
+ function cancel(&$order) {
121
+ }
122
+ }
123
+ ?>
css/admin.css ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .pmpro_admin {background: url(../images/Paid-Memberships-Pro_watermark.png) bottom right no-repeat !important; padding: 1em 0 70px 0; }
2
+
3
+ .pmpro_admin .pmpro_banner h2 {float: left; }
4
+ .pmpro_admin .pmpro_banner .pmpro_meta {float: left; margin: 26px 0 0 0; font-size: 12px; }
5
+ .pmpro_admin .pmpro_banner .pmpro_meta .pmpro_tag-blue {margin: 0 0 0 5px; }
6
+ .pmpro_admin .pmpro_banner .pmpro_logo {float: left; margin: 0 1em 0 0; width: 350px; height: 75px; }
7
+ .pmpro_admin .pmpro_banner ul.pmpro_menu {clear: both; border: 1px solid #CCC; border-radius: 5px; -moz-border-radius: 5px; background: #FFF; }
8
+ .pmpro_admin .pmpro_banner ul.pmpro_menu li {display: inline-block; margin: 10px 0; padding: 0px 10px; border-right: 1px solid #CCC; }
9
+ .pmpro_admin .pmpro_banner ul.pmpro_menu li a, .pmpro_admin .pmpro_banner ul.pmpro_menu li a:link {color: #1e0741; text-decoration: none; }
10
+ .pmpro_admin .pmpro_banner ul.pmpro_menu li a:hover {text-decoration: underline; color: #412f5b; }
11
+
12
+ .pmpro_admin .pmpro_tag-grey {display: inline-block; font-size: 11px; font-weight: bold; position: relative; padding: 2px 5px; border: 1px solid #CCC; background: whiteSmoke; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; }
13
+ .pmpro_admin .pmpro_tag-blue {display: inline-block; font-size: 11px; font-weight: bold; position: relative; padding: 2px 5px; border: 1px solid #2997c8; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; background: #2997c8; color: #FFF; text-decoration: none; }
14
+ .pmpro_admin a.pmpro_tag-blue:hover {background: #77a02e; border: 1px solid #77a02e; }
15
+
16
+ .pmpro_admin .topborder {border-top: 1px solid #CCC; margin-top: 1em; padding-top: 1em; }
17
+ .pmpro_admin #editorcontainer #description {width: 100%; height: 180px; }
18
+ .pmpro_admin .widefat {margin-top: 1em; }
19
+
20
+ .ssp_description #description {width: 100%;}
21
+ .checkbox_box {width: 300px; background: #FFFFFF; border: 1px solid #CCC;}
22
+ .checkbox_box div {border-bottom: 1px solid #CCC; padding: 3px;}
23
+ .checkbox_box .clickable {cursor: pointer;}
24
+ .checkbox_box .clickable:hover {background: #FFC;}
25
+ .top0em {margin-top: 0;}
26
+
27
+ tr.pmpro_gray td {color: #AAA;}
28
+
29
+ /* messages */
30
+ .pmpro_message {background-color: #D5E4F7; background-image: url(../images/icon_information.gif); background-position: 3px 5px; background-repeat: no-repeat; margin: .5em 0; padding: 6px 6px 6px 25px; color: #345395; font-size: 11px; font-weight: bold; line-height: 1.3em; }
31
+
32
+ .pmpro_success {background-color: #CFEECA; background-image: url(../images/icon_success.gif); color: #208A1B; }
33
+ .pmpro_error {background-color: #F9D6CB; background-image: url(../images/icon_error.gif); color: #E36154; }
34
+ .pmpro_alert {background-color: #FFF6CC; background-image: url(../images/icon_alert.gif); color: #CF8516; }
35
+
36
+ .pmpro_message a {color: #345395; }
37
+ .pmpro_success a {color: #208A1B; }
38
+ .pmpro_error a {color: #E36154; }
39
+ .pmpro_alert a {color: #CF8516; }
40
+
41
+ /* highlighted trs */
42
+ tr.pmpro_message {background-image: none;}
43
+ tr.pmpro_success {background-image: none;}
44
+ tr.pmpro_error {background-image: none;}
45
+ tr.pmpro_alert {background-image: none;}
46
+
47
+ /* discount levels */
48
+ .pmpro_discount_levels {border: 1px solid #CCC;}
49
+ .pmpro_discount_levels div {padding: 5px; border: 1px solid #CCC;}
50
+ .pmpro_discount_levels div div {margin-top: 5px; background: #F5F5F5;}
51
+
52
+ /* pagination */
53
+ div.pmpro_pagination {padding: 3px; margin: 5px 0px 5px 0px; font-size: 10px; float: right; }
54
+ div.pmpro_pagination a {padding: 2px 5px 2px 5px; margin: 1px; border: 1px solid #666; text-decoration: none; /* no underline */ color: #666; background: #EEE; }
55
+ div.pmpro_pagination a:hover, div.pmpro_pagination a:active {background: #FFF; }
56
+ div.pmpro_pagination span.current {border: 1px solid #FFF; color: #FFF; background: #666; padding: 2px 5px 2px 5px; margin: 1px; font-weight: bold; }
57
+ div.pmpro_pagination span.disabled {padding: 2px 5px 2px 5px; margin: 2px; border: 1px solid #BBB; color: #BBB; background: #EFEFEF;}
58
+
59
+ p.pmpro_meta_notice {font-size: .8em; padding-top: 5px; border-top: 1px solid #CCC;}
60
+
61
+ /* add ons */
62
+ .pmpro_admin .widgets-holder-wrap {clear: both; margin-top: 50px; }
63
+ .pmpro_admin .widgets-holder-wrap h3 {margin: 2em 0 0 0; }
64
+ .pmpro_admin .widgets-holder-wrap .widget {float: left; width: 32%; margin: 0 1% 10px 0; border: 1px solid #DFDFDF; position: relative; }
65
+ .pmpro_admin .widgets-holder-wrap .widget-top {height: auto; cursor: default; }
66
+ .pmpro_admin .widgets-holder-wrap .widget-inside {display: block; height: 130px; overflow: hidden; }
67
+ .pmpro_admin .widgets-holder-wrap .widget-inside p {height: 80px; overflow: hidden; }
68
+ .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside, .pmpro_admin #pmpro-gists.widgets-holder-wrap .widget-inside p {height: auto; }
69
+ .pmpro_admin .widgets-holder-wrap .widget-title { }
70
+ .pmpro_admin .widgets-holder-wrap .widget-title h4 { }
71
+ .pmpro_admin .widgets-holder-wrap .widget-title .status-label {display: block; float: left; margin: 0 5px 0 0; width: 10px;
72
+ height: 10px; overflow: hidden; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; border: 1px solid #DFDFDF; text-indent: -9999em; }
73
+ .pmpro_admin .widgets-holder-wrap .disabled .widget-title .status-label {background: #F00; }
74
+ .pmpro_admin .widgets-holder-wrap .enabled .widget-title .status-label {background: #0C0; }
75
+
76
+ .pmpro_admin .widgets-holder-wrap .widget-title .version {position: absolute; top: 5px; right: 5px; }
77
+ .pmpro_admin .widgets-holder-wrap .widget-inside .addon-thumb {width: 100px; height: 100px; float: right; margin: 0 0 10px 10px; border: 1px solid #DFDFDF; background: #FFF; padding: 2px;}
78
+
79
+ /*@media (min-width: 1200px) {
80
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
81
+ }
82
+ */
83
+ @media (max-width:900px) {
84
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget {float: none; width: 100%; }
85
+ .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside, .auto-fold .pmpro_admin .widgets-holder-wrap .widget-inside p {height: auto; }
86
+ }
87
+
88
+ /* misc */
89
+ .pmpro_lite {color: #AAA;}
90
+ .pmpro_pad20 {padding: 20px !important;}
91
+ .pmpro_red {color: #CC0000;}
92
+ .pmpro_green {color: #00AA00;}
93
+
94
+ /* reports */
95
+ .pmpro_reports-holder { }
96
+ .pmpro_clickable {cursor: pointer;}
97
+ .js .postbox.pmpro_clickable h3 {cursor: pointer;}
css/frontend.css ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*---------------------------------------
2
+ Buttons
3
+ ---------------------------------------*/
4
+ .pmpro_btn, .pmpro_content_message a, .pmpro_content_message a:link {
5
+ display: inline-block;
6
+ *display: inline;
7
+ padding: 4px 12px;
8
+ margin-bottom: 0;
9
+ *margin-left: .3em;
10
+ line-height: 20px;
11
+ color: #333333;
12
+ text-align: center;
13
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
14
+ text-decoration: none;
15
+ vertical-align: middle;
16
+ cursor: pointer;
17
+ background-color: #f5f5f5;
18
+ *background-color: #e6e6e6;
19
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
20
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
21
+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
22
+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
23
+ background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
24
+ background-repeat: repeat-x;
25
+ border: 1px solid #cccccc;
26
+ *border: 0;
27
+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
28
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
29
+ border-bottom-color: #b3b3b3;
30
+ -webkit-border-radius: 4px;
31
+ -moz-border-radius: 4px;
32
+ border-radius: 4px;
33
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
34
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
35
+ *zoom: 1;
36
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
37
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
38
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
39
+ }
40
+
41
+ .pmpro_btn:hover,
42
+ .pmpro_btn:focus,
43
+ .pmpro_btn:active,
44
+ .pmpro_btn.active,
45
+ .pmpro_btn.disabled,
46
+ .pmpro_btn[disabled],
47
+ .pmpro_content_message a:hover {
48
+ color: #333333;
49
+ background-color: #e6e6e6;
50
+ *background-color: #d9d9d9;
51
+ }
52
+
53
+ .pmpro_btn:active,
54
+ .pmpro_btn.active {
55
+ background-color: #cccccc \9;
56
+ }
57
+
58
+ .pmpro_btn:first-child {
59
+ *margin-left: 0;
60
+ }
61
+
62
+ .pmpro_btn:hover,
63
+ .pmpro_btn:focus,
64
+ .pmpro_content_message a:hover {
65
+ color: #333333;
66
+ text-decoration: none;
67
+ background-position: 0 -15px;
68
+ -webkit-transition: background-position 0.1s linear;
69
+ -moz-transition: background-position 0.1s linear;
70
+ -o-transition: background-position 0.1s linear;
71
+ transition: background-position 0.1s linear;
72
+ }
73
+
74
+ .pmpro_btn:focus {
75
+ outline: thin dotted #333;
76
+ outline: 5px auto -webkit-focus-ring-color;
77
+ outline-offset: -2px;
78
+ }
79
+
80
+ .pmpro_btn.active,
81
+ .pmpro_btn:active {
82
+ background-image: none;
83
+ outline: 0;
84
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
85
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
86
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
87
+ }
88
+
89
+ .pmpro_btn.disabled,
90
+ .pmpro_btn[disabled] {
91
+ cursor: default;
92
+ background-image: none;
93
+ opacity: 0.65;
94
+ filter: alpha(opacity=65);
95
+ -webkit-box-shadow: none;
96
+ -moz-box-shadow: none;
97
+ box-shadow: none;
98
+ }
99
+
100
+
101
+ /*---------------------------------------
102
+ Forms
103
+ ---------------------------------------*/
104
+ form.pmpro_form div {clear: left; margin: .5em 0 1em 0; }
105
+ form.pmpro_form label {float: left; margin: 3px 10px 0 0; width: 120px; font-weight: bold; text-align: right; }
106
+ form.pmpro_form .likelabel {font-weight: bold; }
107
+ form.pmpro_form .input, form.pmpro_form textarea, .input, form.pmpro_form select {padding: 3px; border: 1px solid #AAA; margin: 0 3px 0 0; }
108
+ form.pmpro_form textarea {font-family: Arial, Helvetica, sans-serif; font-size: 12px; }
109
+ form.pmpro_form select {margin: 2px 0 0 0 ; font-size: 12px;}
110
+ form.pmpro_form .lite {color: #666; }
111
+ form.pmpro_form .leftmar {margin: 8px 0 0 130px; }
112
+
113
+ form.pmpro_form .pmpro_captcha {margin: 0 0 0 130px !important; }
114
+ form.pmpro_form .pmpro_captcha div {clear: none; margin: 0; }
115
+ form.pmpro_form .pmpro_submit {margin-left: 130px; }
116
+ form.pmpro_form .pmpro_submit span {float: left; }
117
+ form.pmpro_form #pmpro_processing_message {margin: 5px 0 0 10px; font-style: italic; color: #999; }
118
+
119
+ /*--------------------------------------------------
120
+ Messages - Success, Error, Alert
121
+ ----------------------------------------------------*/
122
+ .pmpro_message {background-color: #D5E4F7; background-image: url(../images/icon_information.gif); background-position: 3px 5px; background-repeat: no-repeat; margin: .5em 0; padding: 6px 6px 6px 25px; color: #345395; font-size: 11px; font-weight: bold; line-height: 1.3em; }
123
+
124
+ .pmpro_success {background-color: #CFEECA; background-image: url(../images/icon_success.gif); color: #208A1B; }
125
+ .pmpro_error {background-color: #F9D6CB; background-image: url(../images/icon_error.gif); color: #E36154; }
126
+ .pmpro_alert {background-color: #FFF6CC; background-image: url(../images/icon_alert.gif); color: #CF8516; }
127
+
128
+ .pmpro_content_message a {margin: 5px 5px 0 0; }
129
+
130
+ .pmpro_message a {color: #345395; }
131
+ .pmpro_success a {color: #208A1B; }
132
+ .pmpro_error a {color: #E36154; }
133
+ .pmpro_alert a {color: #CF8516; }
134
+
135
+ input.pmpro_error {background-image: none;}
136
+
137
+ /*---------------------------------------
138
+ Membership Checkout
139
+ ---------------------------------------*/
140
+ .pmpro_checkout thead th {font-weight: bold; color: #444; padding: 10px; }
141
+ .pmpro_checkout tbody td {padding: 10px; }
142
+ .pmpro_checkout tr.odd td {background: rgba(125,125,125,.1); }
143
+ .pmpro_checkout tr.selected td {background: #FFC; }
144
+ .pmpro_checkout tr.active td {background: #FFC; }
145
+ .pmpro_checkout .name {font-weight: bold; }
146
+ .pmpro_checkout ul {margin: 5px 0 0 20px; padding: 0; font-size: .8em; color: #444; }
147
+
148
+ .pmpro_checkout tfoot td {padding: 10px; color: #444; }
149
+ .pmpro_checkout .topfoot td {border-top: 2px solid #CCC;}
150
+ .pmpro_checkout .total td {border-top: 1px solid #CCC; font-size: 1.2em; font-weight: bold; padding-bottom: 30px;}
151
+ .pmpro_checkout tfoot .entercode td {background: #EEE; }
152
+
153
+ .pmpro_checkout td.rtbdr {border-right: 1px solid #CCC; }
154
+
155
+ .pmpro_checkout select {font-size: 11px; }
156
+
157
+ .pmpro_thead-msg {display: block; float: right; width: auto; font-style: italic; font-weight: normal; text-align: right; }
158
+
159
+ .pmpro_ordersummary {float: right; }
160
+
161
+ #pmpro_license { background: #FFF; padding: 5px; border: 1px solid #CCC; height: 200px; margin: 3px; color: #666; overflow: auto; }
162
+
163
+ .pmpro_sslseal {float: right; clear: none !important; margin: 0 !important; }
164
+
165
+ a.pmpro_radio {text-decoration: none; color: #000;}
166
+
167
+ /*---------------------------------------
168
+ Membership Invoice
169
+ ---------------------------------------*/
170
+ .pmpro_invoice { }
171
+
172
+
173
+ /*---------------------------------------
174
+ Membership Account
175
+ ---------------------------------------*/
176
+ #pmpro_account .pmpro_box {border-top: 1px solid #CCC; padding: 1em 0; margin: 1em 0; }
177
+ #pmpro_account .pmpro_box h3 {margin: 0; padding: 0; border: none; background: none; }
178
+ #pmpro_account .pmpro_box p {margin: .5em 0 0 0; padding: 0; }
179
+ #pmpro_account .pmpro_box ul {margin-bottom: 0; }
180
+
181
+ #pmpro_account #pmpro_account-membership { }
182
+ #pmpro_account #pmpro_account-profile { }
183
+ #pmpro_account #pmpro_account-billing { }
184
+ #pmpro_account #pmpro_account-invoices { }
185
+ #pmpro_account #pmpro_account-links { }
186
+
187
+
188
+ .pmpro_hidden {display: none;}
189
+ li.pmpro_more {list-style-type: none; text-align: center; margin-left: -20px; padding-left: 0;}
190
+
191
+ /*---------------------------------------
192
+ Membership Levels
193
+ ---------------------------------------*/
194
+ #pmpro_levels_table {background: #FFF; }
195
+ #pmpro_levels_table .pmpro_btn {display: block; }
196
+
197
+ /*---------------------------------------
198
+ Misc
199
+ ---------------------------------------*/
200
+ .pmpro_a-right {float: right; width: auto; text-align: right; text-decoration: underline; font-size: 11px; }
201
+ .pmpro_a-print {float: right; width: auto; text-decoration: none; color: #345395; background: url(../images/printer.gif) top left no-repeat; padding: 0px 0px 2px 20px; font-size: 11px; line-height: 16px; cursor: pointer; }
202
+
203
+ .pmpro_red {color: #CC0000; }
204
+ .pmpro_grey {color: #999; }
205
+
206
+ .top1em {margin-top: 1em;}
207
+ .bot1em {margin-bottom: 1em;}
208
+ .bot0em {margin-bottom: 0em;}
209
+ .clear {clear: both; }
210
+
211
+ .pmpro_small {font-size: .8em;}
css/print.css ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ @media print {
2
+
3
+ .pmpro_a-print {display: none; position: absolute; left: -9999px; }
4
+
5
+ }
email/admin_change.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <p>An administrator at !!sitename!! has changed your membership level.</p>
2
+
3
+ <p>!!membership_change!!.</p>
4
+
5
+ <p>If you did not request this membership change and would like more information please contact us at !!siteemail!!</p>
6
+
7
+ <p>Log in to your membership account here: !!login_link!!</p>
email/admin_change_admin.html ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <p>An administrator at !!sitename!! has changed a membership level.</p>
2
+
3
+ <p>!!membership_change!!.</p>
4
+
5
+ <p>Log in to your WordPress admin here: !!login_link!!</p>
email/billing.html ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Your billing information at !!sitename!! has been changed.</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>
5
+ Billing Information:<br />
6
+ !!billing_name!!<br />
7
+ !!billing_street!!<br />
8
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
+ !!billing_phone!!
10
+ </p>
11
+
12
+ <p>
13
+ !!cardtype!!: !!accountnumber!!<br />
14
+ Expires: !!expirationmonth!!/!!expirationyear!!
15
+ </p>
16
+
17
+ <p>If you did not request a billing information change please contact us at !!siteemail!!</p>
18
+
19
+ <p>Log in to your membership account here: !!login_link!!</p>
email/billing_admin.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>The billing information for !!display_name!! at !!sitename!! has been changed.</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>
5
+ Billing Information:<br />
6
+ !!billing_name!!<br />
7
+ !!billing_street!!<br />
8
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
+ !!billing_phone!!
10
+ </p>
11
+
12
+ <p>
13
+ !!cardtype!!: !!accountnumber!!<br />
14
+ Expires: !!expirationmonth!!/!!expirationyear!!
15
+ </p>
16
+
17
+ <p>Log in to your WordPress dashboard here: !!login_link!!</p>
email/billing_failure.html ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>The current subscription payment for your !!sitename!! membership has failed. <strong>Please click the following link to log in and update your billing information to avoid account suspension. !!login_link!!</strong></p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>The most recent account information we have on file is:</p>
5
+
6
+ <p>!!billing_name!!</br />
7
+ !!billing_street!!<br />
8
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
+ !!billing_phone!!
10
+ </p>
11
+
12
+ <p>
13
+ !!cardtype!!: !!accountnumber!!<br />
14
+ Expires: !!expirationmonth!!/!!expirationyear!!
15
+ </p>
email/billing_failure_admin.html ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Payment Failure</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>The most recent account information we have on file is:</p>
5
+
6
+ <p>!!billing_name!!</br />
7
+ !!billing_street!!<br />
8
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
+ !!billing_phone!!
10
+ </p>
11
+
12
+ <p>
13
+ !!cardtype!!: !!accountnumber!!<br />
14
+ Expires: !!expirationmonth!!/!!expirationyear!!
15
+ </p>
email/cancel.html ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p>Your membership at !!sitename!! has been cancelled.</p>
2
+
3
+ <p>If you did not request this cancellation and would like more information please contact us at !!siteemail!!</p>
email/cancel_admin.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>The membership for !!user_login!! at !!sitename!! has been cancelled.</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>Membership Level: !!membership_level_name!!</p>
5
+ <p>Start Date: !!startdate!!</p>
6
+ <p>Cancellation Date: !!enddate!!</p>
7
+
8
+ <p>Log in to your WordPress admin here: !!login_link!!</p>
email/checkout_check.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
+
3
+ !!instructions!!
4
+
5
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
6
+
7
+ <p>Account: !!display_name!! (!!user_email!!)</p>
8
+ <p>Membership Level: !!membership_level_name!!</p>
9
+ <p>Membership Fee: !!membership_cost!!</p>
10
+ !!membership_expiration!! !!discount_code!!
11
+
12
+ <p>
13
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
14
+ Total Billed: $!!invoice_total!!
15
+ </p>
16
+
17
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_check_admin.html ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>There was a new member checkout at !!sitename!!.</p>
2
+
3
+ <p><strong>They have chosen to pay by check.</strong></p>
4
+
5
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
6
+
7
+ <p>Account: !!display_name!! (!!user_email!!)</p>
8
+ <p>Membership Level: !!membership_level_name!!</p>
9
+ <p>Membership Fee: !!membership_cost!!</p>
10
+ !!membership_expiration!! !!discount_code!!
11
+
12
+ <p>
13
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
14
+ Total Billed: $!!invoice_total!!
15
+ </p>
16
+
17
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_express.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
+ Total Billed: !!invoice_total!!
12
+ </p>
13
+
14
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_express_admin.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>There was a new member checkout at !!sitename!!.</p>
2
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
+ Total Billed: !!invoice_total!!
12
+ </p>
13
+
14
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_free.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
+ <p>Below are details about your membership account.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ !!membership_expiration!! !!discount_code!!
7
+
8
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_free_admin.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>There was a new member checkout at !!sitename!!.</p>
2
+ <p>Below are details about the new membership account.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ !!membership_expiration!! !!discount_code!!
7
+
8
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_freetrial.html ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
+ <p>Below are details about your membership account.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Billing Information on File:<br />
11
+ !!billing_name!!<br />
12
+ !!billing_street!!<br />
13
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
14
+ !!billing_phone!!
15
+ </p>
16
+
17
+ <p>
18
+ !!cardtype!!: !!accountnumber!!<br />
19
+ Expires: !!expirationmonth!!/!!expirationyear!!
20
+ </p>
21
+
22
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_freetrial_admin.html ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>There was a new member checkout at !!sitename!!.</p>
2
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Billing Information on File:<br />
11
+ !!billing_name!!<br />
12
+ !!billing_street!!<br />
13
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
14
+ !!billing_phone!!
15
+ </p>
16
+
17
+ <p>
18
+ !!cardtype!!: !!accountnumber!!<br />
19
+ Expires: !!expirationmonth!!/!!expirationyear!!
20
+ </p>
21
+
22
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_paid.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
+ Total Billed: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Billing Information:<br />
15
+ !!billing_name!!<br />
16
+ !!billing_street!!<br />
17
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
18
+ !!billing_phone!!
19
+ </p>
20
+
21
+ <p>
22
+ !!cardtype!!: !!accountnumber!!<br />
23
+ Expires: !!expirationmonth!!/!!expirationyear!!
24
+ </p>
25
+
26
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_paid_admin.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>There was a new member checkout at !!sitename!!.</p>
2
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
+ Total Billed: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Billing Information:<br />
15
+ !!billing_name!!<br />
16
+ !!billing_street!!<br />
17
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
18
+ !!billing_phone!!
19
+ </p>
20
+
21
+ <p>
22
+ !!cardtype!!: !!accountnumber!!<br />
23
+ Expires: !!expirationmonth!!/!!expirationyear!!
24
+ </p>
25
+
26
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_trial.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your membership account is now active.</p>
2
+ <p>Below are details about your membership account and a receipt for your initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
+ Total Billed: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Billing Information:<br />
15
+ !!billing_name!!<br />
16
+ !!billing_street!!<br />
17
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
18
+ !!billing_phone!!
19
+ </p>
20
+
21
+ <p>
22
+ !!cardtype!!: !!accountnumber!!<br />
23
+ Expires: !!expirationmonth!!/!!expirationyear!!
24
+ </p>
25
+
26
+ <p>Log in to your membership account here: !!login_link!!</p>
email/checkout_trial_admin.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>There was a new member checkout at !!sitename!!.</p>
2
+ <p>Below are details about the new membership account and a receipt for the initial membership invoice.</p>
3
+
4
+ <p>Account: !!display_name!! (!!user_email!!)</p>
5
+ <p>Membership Level: !!membership_level_name!!</p>
6
+ <p>Membership Fee: !!membership_cost!!</p>
7
+ !!membership_expiration!! !!discount_code!!
8
+
9
+ <p>
10
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
11
+ Total Billed: !!invoice_total!!
12
+ </p>
13
+ <p>
14
+ Billing Information:<br />
15
+ !!billing_name!!<br />
16
+ !!billing_street!!<br />
17
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
18
+ !!billing_phone!!
19
+ </p>
20
+
21
+ <p>
22
+ !!cardtype!!: !!accountnumber!!<br />
23
+ Expires: !!expirationmonth!!/!!expirationyear!!
24
+ </p>
25
+
26
+ <p>Log in to your membership account here: !!login_link!!</p>
email/credit_card_expiring.html ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>The payment method used for your membership at !!sitename!! will expire soon. <strong>Please click the following link to log in and update your billing information to avoid account suspension. !!login_link!!</strong></p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>The most recent account information we have on file is:</p>
5
+
6
+ <p>!!billing_name!!</br />
7
+ !!billing_street!!<br />
8
+ !!billing_city!!, !!billing_state!! !!billing_zip!! !!billing_country!!
9
+ !!billing_phone!!
10
+ </p>
11
+
12
+ <p>
13
+ !!cardtype!!: !!accountnumber!!<br />
14
+ Expires: !!expirationmonth!!/!!expirationyear!!
15
+ </p>
email/default.html ADDED
@@ -0,0 +1 @@
 
1
+ !!body!!
email/footer.html ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <p>
2
+ Respectfully,<br />
3
+ !!sitename!!
4
+ </p>
email/header.html ADDED
@@ -0,0 +1 @@
 
1
+ <p>Dear !!name!!,</p>
email/invoice.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Below is a receipt for your most recent membership invoice.</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>
5
+ Invoice #!!invoice_id!! on !!invoice_date!!<br />
6
+ Total Billed: $!!invoice_total!!
7
+ </p>
8
+ <p>
9
+ Billing Information:<br />
10
+ !!billing_name!!<br />
11
+ !!billing_street!!<br />
12
+ !!billing_city!!, !!billing_state!! !!billing_zip!!<br />
13
+ !!billing_country!!<br />
14
+ !!billing_phone!!
15
+ </p>
16
+
17
+ <p>
18
+ !!cardtype!!: !!accountnumber!!<br />
19
+ Expires: !!expirationmonth!!/!!expirationyear!!
20
+ </p>
21
+
22
+ <p>Log in to your membership account here: !!login_link!!</p>
23
+ <p>To view an online version of this invoice, click here: !!invoice_link!!</p>
email/membership_expired.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <p>Your membership at !!sitename!! has ended.</p>
2
+
3
+ <p>Thank you for your support.</p>
4
+
5
+ <p>View our current membership offerings here: !!levels_link!!</p>
6
+
7
+ <p>Log in to manage your account here: !!login_link!!</p>
email/membership_expiring.html ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. This is just a reminder that your membership will end on !!enddate!!.</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>Membership Level: !!membership_level_name!!</p>
5
+
6
+ <p>Log in to your membership account here: !!login_link!!</p>
email/trial_ending.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <p>Thank you for your membership to !!sitename!!. Your trial period is ending on !!trial_end!!.</p>
2
+
3
+ <p>Account: !!display_name!! (!!user_email!!)</p>
4
+ <p>Membership Level: !!membership_level_name!!</p>
5
+
6
+ <p>Your fee will be changing from !!trial_amount!! to !!billing_amount!! every !!cycle_number!! !!cycle_period!!(s).</p>
7
+
8
+ <p>Log in to your membership account here: !!login_link!!</p>
images/CCV-back.jpg ADDED
Binary file
images/CCV-front.jpg ADDED
Binary file
images/Paid-Memberships-Pro.png ADDED
Binary file
images/Paid-Memberships-Pro_watermark.png ADDED
Binary file
images/PaidMembershipsPro-grey.gif ADDED
Binary file
images/PaidMembershipsPro.gif ADDED
Binary file
images/bg_grad-chrome.gif ADDED
Binary file
images/bg_grad-grey.gif ADDED
Binary file
images/creditcards.gif ADDED
Binary file
images/delete.gif ADDED
Binary file
images/enlarge.gif ADDED
Binary file
images/icon-pmproadmin16-sprite.png ADDED
Binary file
images/icon-pmproadmin16-sprite_2x.png ADDED
Binary file
images/icon-pmproadmin32.png ADDED
Binary file
images/icon-pmproadmin32_2x.png ADDED
Binary file
images/icon_alert.gif ADDED
Binary file
images/icon_comment.gif ADDED
Binary file
images/icon_comments.gif ADDED
Binary file
images/icon_continue.gif ADDED
Binary file
images/icon_delete.gif ADDED
Binary file
images/icon_email.gif ADDED
Binary file
images/icon_error.gif ADDED
Binary file
images/icon_information.gif ADDED
Binary file
images/icon_phone.gif ADDED
Binary file
images/icon_search.gif ADDED
Binary file
images/icon_success.gif ADDED
Binary file
images/menu_users.png ADDED
Binary file
images/printer.gif ADDED
Binary file
images/spacer.gif ADDED
Binary file
images/tag_sale.png ADDED
Binary file
includes/adminpages.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Dashboard Menu
4
+ */
5
+ function pmpro_add_pages()
6
+ {
7
+ global $wpdb;
8
+
9
+ add_menu_page(__('Memberships', 'pmpro'), __('Memberships', 'pmpro'), 'manage_options', 'pmpro-membershiplevels', 'pmpro_membershiplevels', PMPRO_URL . '/images/menu_users.png');
10
+ add_submenu_page('pmpro-membershiplevels', __('Page Settings', 'pmpro'), __('Page Settings', 'pmpro'), 'manage_options', 'pmpro-pagesettings', 'pmpro_pagesettings');
11
+ add_submenu_page('pmpro-membershiplevels', __('Payment Settings', 'pmpro'), __('Payment Settings', 'pmpro'), 'manage_options', 'pmpro-paymentsettings', 'pmpro_paymentsettings');
12
+ add_submenu_page('pmpro-membershiplevels', __('Email Settings', 'pmpro'), __('Email Settings', 'pmpro'), 'manage_options', 'pmpro-emailsettings', 'pmpro_emailsettings');
13
+ add_submenu_page('pmpro-membershiplevels', __('Advanced Settings', 'pmpro'), __('Advanced Settings', 'pmpro'), 'manage_options', 'pmpro-advancedsettings', 'pmpro_advancedsettings');
14
+ add_submenu_page('pmpro-membershiplevels', __('Add Ons', 'pmpro'), __('Add Ons', 'pmpro'), 'manage_options', 'pmpro-addons', 'pmpro_addons');
15
+ add_submenu_page('pmpro-membershiplevels', __('Members List', 'pmpro'), __('Members List', 'pmpro'), 'manage_options', 'pmpro-memberslist', 'pmpro_memberslist');
16
+ add_submenu_page('pmpro-membershiplevels', __('Reports', 'pmpro'), __('Reports', 'pmpro'), 'manage_options', 'pmpro-reports', 'pmpro_reports');
17
+ add_submenu_page('pmpro-membershiplevels', __('Orders', 'pmpro'), __('Orders', 'pmpro'), 'manage_options', 'pmpro-orders', 'pmpro_orders');
18
+ add_submenu_page('pmpro-membershiplevels', __('Discount Codes', 'pmpro'), __('Discount Codes', 'pmpro'), 'manage_options', 'pmpro-discountcodes', 'pmpro_discountcodes');
19
+
20
+ //rename the automatically added Memberships submenu item
21
+ global $submenu;
22
+ if(!empty($submenu['pmpro-membershiplevels']))
23
+ {
24
+ $submenu['pmpro-membershiplevels'][0][0] = "Membership Levels";
25
+ $submenu['pmpro-membershiplevels'][0][3] = "Membership Levels";
26
+ }
27
+ }
28
+ add_action('admin_menu', 'pmpro_add_pages');
29
+
30
+ /*
31
+ Admin Bar
32
+ */
33
+ function pmpro_admin_bar_menu() {
34
+ global $wp_admin_bar;
35
+ if ( !is_super_admin() || !is_admin_bar_showing() )
36
+ return;
37
+ $wp_admin_bar->add_menu( array(
38
+ 'id' => 'paid-memberships-pro',
39
+ 'title' => __( 'Memberships', 'pmpro'),
40
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-membershiplevels') ) );
41
+ $wp_admin_bar->add_menu( array(
42
+ 'id' => 'pmpro-membership-levels',
43
+ 'parent' => 'paid-memberships-pro',
44
+ 'title' => __( 'Membership Levels', 'pmpro'),
45
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-membershiplevels') ) );
46
+ $wp_admin_bar->add_menu( array(
47
+ 'id' => 'pmpro-page-settings',
48
+ 'parent' => 'paid-memberships-pro',
49
+ 'title' => __( 'Page Settings', 'pmpro'),
50
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-pagesettings') ) );
51
+ $wp_admin_bar->add_menu( array(
52
+ 'id' => 'pmpro-payment-settings',
53
+ 'parent' => 'paid-memberships-pro',
54
+ 'title' => __( 'Payment Settings', 'pmpro'),
55
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-paymentsettings') ) );
56
+ $wp_admin_bar->add_menu( array(
57
+ 'id' => 'pmpro-email-settings',
58
+ 'parent' => 'paid-memberships-pro',
59
+ 'title' => __( 'Email Settings', 'pmpro'),
60
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-emailsettings') ) );
61
+ $wp_admin_bar->add_menu( array(
62
+ 'id' => 'pmpro-advanced-settings',
63
+ 'parent' => 'paid-memberships-pro',
64
+ 'title' => __( 'Advanced Settings', 'pmpro'),
65
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-advancedsettings') ) );
66
+ $wp_admin_bar->add_menu( array(
67
+ 'id' => 'pmpro-addons',
68
+ 'parent' => 'paid-memberships-pro',
69
+ 'title' => __( 'Add Ons', 'pmpro'),
70
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-addons') ) );
71
+ $wp_admin_bar->add_menu( array(
72
+ 'id' => 'pmpro-members-list',
73
+ 'parent' => 'paid-memberships-pro',
74
+ 'title' => __( 'Members List', 'pmpro'),
75
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-memberslist') ) );
76
+ $wp_admin_bar->add_menu( array(
77
+ 'id' => 'pmpro-reports',
78
+ 'parent' => 'paid-memberships-pro',
79
+ 'title' => __( 'Reports', 'pmpro'),
80
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-reports') ) );
81
+ $wp_admin_bar->add_menu( array(
82
+ 'id' => 'pmpro-orders',
83
+ 'parent' => 'paid-memberships-pro',
84
+ 'title' => __( 'Orders', 'pmpro'),
85
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-orders') ) );
86
+ $wp_admin_bar->add_menu( array(
87
+ 'id' => 'pmpro-discount-codes',
88
+ 'parent' => 'paid-memberships-pro',
89
+ 'title' => __( 'Discount Codes', 'pmpro'),
90
+ 'href' => get_admin_url(NULL, '/admin.php?page=pmpro-discountcodes') ) );
91
+ }
92
+ add_action('admin_bar_menu', 'pmpro_admin_bar_menu', 1000);
93
+
94
+ /*
95
+ Functions to load pages from adminpages directory
96
+ */
97
+ function pmpro_reports()
98
+ {
99
+ require_once(PMPRO_DIR . "/adminpages/reports.php");
100
+ }
101
+
102
+ function pmpro_memberslist()
103
+ {
104
+ require_once(PMPRO_DIR . "/adminpages/memberslist.php");
105
+ }
106
+
107
+ function pmpro_discountcodes()
108
+ {
109
+ require_once(PMPRO_DIR . "/adminpages/discountcodes.php");
110
+ }
111
+
112
+ function pmpro_membershiplevels()
113
+ {
114
+ require_once(PMPRO_DIR . "/adminpages/membershiplevels.php");
115
+ }
116
+
117
+ function pmpro_pagesettings()
118
+ {
119
+ require_once(PMPRO_DIR . "/adminpages/pagesettings.php");
120
+ }
121
+
122
+ function pmpro_paymentsettings()
123
+ {
124
+ require_once(PMPRO_DIR . "/adminpages/paymentsettings.php");
125
+ }
126
+
127
+ function pmpro_emailsettings()
128
+ {
129
+ require_once(PMPRO_DIR . "/adminpages/emailsettings.php");
130
+ }
131
+
132
+ function pmpro_advancedsettings()
133
+ {
134
+ require_once(PMPRO_DIR . "/adminpages/advancedsettings.php");
135
+ }
136
+
137
+ function pmpro_addons()
138
+ {
139
+ require_once(PMPRO_DIR . "/adminpages/addons.php");
140
+ }
141
+
142
+ function pmpro_orders()
143
+ {
144
+ require_once(PMPRO_DIR . "/adminpages/orders.php");
145
+ }
includes/cleanup.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Clean things up when deletes happen, etc. (This stuff needs a better home.)
4
+ */
5
+ //deleting a user? remove their account info.
6
+ function pmpro_delete_user($user_id = NULL)
7
+ {
8
+ global $wpdb;
9
+
10
+ //changing their membership level to 0 will cancel any subscription and remove their membership level entry
11
+ //we don't remove the orders because it would affect reporting
12
+ if(pmpro_changeMembershipLevel(0, $user_id))
13
+ {
14
+ //okay
15
+ }
16
+ else
17
+ {
18
+ //couldn't delete the subscription
19
+ //we should probably notify the admin
20
+ global $pmpro_error;
21
+ if(!empty($pmpro_error))
22
+ {
23
+ $pmproemail = new PMProEmail();
24
+ $pmproemail->data = array("body"=>"<p>" . sprintf(__("There was an error canceling the subscription for user with ID=%s. You will want to check your payment gateway to see if their subscription is still active.", "pmpro"), strval($user_id)) . "</p><p>Error: " . $pmpro_error . "</p>");
25
+ $last_order = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' ORDER BY timestamp DESC LIMIT 1");
26
+ if(!empty($last_order))
27
+ $pmproemail->data["body"] .= "<p>Last Invoice:<br />" . nl2br(var_export($last_order, true)) . "</p>";
28
+ $pmproemail->sendEmail(get_bloginfo("admin_email"));
29
+ }
30
+ }
31
+ }
32
+ add_action('delete_user', 'pmpro_delete_user');
33
+ add_action('wpmu_delete_user', 'pmpro_delete_user');
34
+
35
+ //deleting a category? remove any level associations
36
+ function pmpro_delete_category($cat_id = NULL)
37
+ {
38
+ global $wpdb;
39
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_categories WHERE category_id = '" . $cat_id . "'";
40
+ $wpdb->query($sqlQuery);
41
+ }
42
+ add_action('delete_category', 'pmpro_delete_category');
43
+
44
+ //deleting a post? remove any level associations
45
+ function pmpro_delete_post($post_id = NULL)
46
+ {
47
+ global $wpdb;
48
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_pages WHERE page_id = '" . $post_id . "'";
49
+ $wpdb->query($sqlQuery);
50
+ }
51
+ add_action('delete_post', 'pmpro_delete_post');
includes/content.php ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Functions to detect member content and protect it.
4
+ */
5
+ function pmpro_has_membership_access($post_id = NULL, $user_id = NULL, $return_membership_levels = false)
6
+ {
7
+ global $post, $wpdb, $current_user;
8
+ //use globals if no values supplied
9
+ if(!$post_id)
10
+ $post_id = $post->ID;
11
+ if(!$user_id)
12
+ $user_id = $current_user->ID;
13
+
14
+ //no post, return true (changed from false in version 1.7.2)
15
+ if(!$post_id)
16
+ return true;
17
+
18
+ //if no post or current_user object, set them up
19
+ if(!empty($post->ID) && $post_id == $post->ID)
20
+ $mypost = $post;
21
+ else
22
+ $mypost = get_post($post_id);
23
+
24
+ if($user_id == $current_user->ID)
25
+ $myuser = $current_user;
26
+ else
27
+ $myuser = get_user($user_id);
28
+
29
+ //for these post types, we want to check the parent
30
+ if($mypost->post_type == "attachment" || $mypost->post_type == "revision")
31
+ {
32
+ $mypost = get_post($mypost->post_parent);
33
+ }
34
+
35
+ if($mypost->post_type == "post")
36
+ {
37
+ $post_categories = wp_get_post_categories($mypost->ID);
38
+
39
+ if(!$post_categories)
40
+ {
41
+ //just check for entries in the memberships_pages table
42
+ $sqlQuery = "SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '" . $mypost->ID . "'";
43
+ }
44
+ else
45
+ {
46
+ //are any of the post categories associated with membership levels? also check the memberships_pages table
47
+ $sqlQuery = "(SELECT m.id, m.name FROM $wpdb->pmpro_memberships_categories mc LEFT JOIN $wpdb->pmpro_membership_levels m ON mc.membership_id = m.id WHERE mc.category_id IN(" . implode(",", $post_categories) . ") AND m.id IS NOT NULL) UNION (SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '" . $mypost->ID . "')";
48
+ }
49
+ }
50
+ else
51
+ {
52
+ //are any membership levels associated with this page?
53
+ $sqlQuery = "SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '" . $mypost->ID . "'";
54
+ }
55
+
56
+
57
+ $post_membership_levels = $wpdb->get_results($sqlQuery);
58
+
59
+ $post_membership_levels_ids = array();
60
+ $post_membership_levels_names = array();
61
+
62
+ if(!$post_membership_levels)
63
+ {
64
+ $hasaccess = true;
65
+ }
66
+ else
67
+ {
68
+ //we need to see if the user has access
69
+ foreach($post_membership_levels as $level)
70
+ {
71
+ $post_membership_levels_ids[] = $level->id;
72
+ $post_membership_levels_names[] = $level->name;
73
+ }
74
+
75
+ //levels found. check if this is in a feed or if the current user is in at least one of those membership levels
76
+ if(is_feed())
77
+ {
78
+ //always block restricted feeds
79
+ $hasaccess = false;
80
+ }
81
+ elseif(!empty($myuser->ID))
82
+ {
83
+ if(!empty($myuser->membership_level->ID) && in_array($myuser->membership_level->ID, $post_membership_levels_ids))
84
+ {
85
+ //the users membership id is one that will grant access
86
+ $hasaccess = true;
87
+ }
88
+ else
89
+ {
90
+ //user isn't a member of a level with access
91
+ $hasaccess = false;
92
+ }
93
+ }
94
+ else
95
+ {
96
+ //user is not logged in and this content requires membership
97
+ $hasaccess = false;
98
+ }
99
+ }
100
+
101
+ /*
102
+ Filters
103
+ The generic filter is run first. Then if there is a filter for this post type, that is run.
104
+ */
105
+ //general filter for all posts
106
+ $hasaccess = apply_filters("pmpro_has_membership_access_filter", $hasaccess, $mypost, $myuser, $post_membership_levels);
107
+ //filter for this post type
108
+ if(has_filter("pmpro_has_membership_access_action_" . $mypost->post_type))
109
+ $hasaccess = apply_filters("pmpro_has_membership_access_filter_" . $mypost->post_type, $hasaccess, $mypost, $myuser, $post_membership_levels);
110
+
111
+ //return
112
+ if($return_membership_levels)
113
+ return array($hasaccess, $post_membership_levels_ids, $post_membership_levels_names);
114
+ else
115
+ return $hasaccess;
116
+ }
117
+
118
+ function pmpro_search_filter($query)
119
+ {
120
+ global $current_user, $wpdb, $pmpro_pages;
121
+
122
+ //hide pmpro pages from search results
123
+ if(!$query->is_admin && $query->is_search)
124
+ {
125
+ $query->set('post__not_in', $pmpro_pages ); // id of page or post
126
+ }
127
+
128
+ //hide member pages from non-members (make sure they aren't hidden from members)
129
+ if(!$query->is_admin && $query->is_search)
130
+ {
131
+ //get pages that are in levels, but not in mine
132
+ $sqlQuery = "SELECT page_id FROM $wpdb->pmpro_memberships_pages ";
133
+ if(!empty($current_user->membership_level->ID))
134
+ $sqlQuery .= "WHERE membership_id <> '" . $current_user->membership_level->ID . "' ";
135
+ $hidden_page_ids = $wpdb->get_col($sqlQuery);
136
+ if($hidden_page_ids)
137
+ $query->set('post__not_in', $hidden_page_ids ); // id of page or post
138
+
139
+ //get categories that are filtered by level, but not my level
140
+ $sqlQuery = "SELECT category_id FROM $wpdb->pmpro_memberships_categories ";
141
+ if(!empty($current_user->membership_level->ID))
142
+ $sqlQuery .= "WHERE membership_id <> '" . $current_user->membership_level->ID . "' ";
143
+ $hidden_post_cats = $wpdb->get_col($sqlQuery);
144
+
145
+ //make this work
146
+ if($hidden_post_cats)
147
+ $query->set('category__not_in', $hidden_post_cats);
148
+ }
149
+
150
+ return $query;
151
+ }
152
+ $showexcerpts = pmpro_getOption("showexcerpts");
153
+ if(empty($showexcerpts))
154
+ add_filter( 'pre_get_posts', 'pmpro_search_filter' );
155
+
156
+ function pmpro_membership_content_filter($content, $skipcheck = false)
157
+ {
158
+ global $post, $current_user;
159
+
160
+ if(!$skipcheck)
161
+ {
162
+ $hasaccess = pmpro_has_membership_access(NULL, NULL, true);
163
+ if(is_array($hasaccess))
164
+ {
165
+ //returned an array to give us the membership level values
166
+ $post_membership_levels_ids = $hasaccess[1];
167
+ $post_membership_levels_names = $hasaccess[2];
168
+ $hasaccess = $hasaccess[0];
169
+ }
170
+ }
171
+
172
+ if($hasaccess)
173
+ {
174
+ //all good, return content
175
+ return $content;
176
+ }
177
+ else
178
+ {
179
+ //if show excerpts is set, return just the excerpt
180
+ if(pmpro_getOption("showexcerpts"))
181
+ {
182
+ //show excerpt
183
+ global $post;
184
+ if($post->post_excerpt)
185
+ {
186
+ //defined exerpt
187
+ $content = wpautop($post->post_excerpt);
188
+ }
189
+ elseif(strpos($content, "<span id=\"more-" . $post->ID . "\"></span>") !== false)
190
+ {
191
+ //more tag
192
+ $pos = strpos($content, "<span id=\"more-" . $post->ID . "\"></span>");
193
+ $content = wpautop(substr($content, 0, $pos));
194
+ }
195
+ elseif(strpos($content, 'class="more-link">') !== false)
196
+ {
197
+ //more link
198
+ $content = preg_replace("/\<a.*class\=\"more\-link\".*\>.*\<\/a\>/", "", $content);
199
+ }
200
+ else
201
+ {
202
+ //auto generated excerpt. pulled from wp_trim_excerpt
203
+ $content = strip_shortcodes( $content );
204
+ $content = str_replace(']]>', ']]&gt;', $content);
205
+ $content = strip_tags($content);
206
+ $excerpt_length = apply_filters('excerpt_length', 55);
207
+ $words = preg_split("/[\n\r\t ]+/", $content, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
208
+ if ( count($words) > $excerpt_length ) {
209
+ array_pop($words);
210
+ $content = implode(' ', $words);
211
+ $content = $content . "... ";
212
+ } else {
213
+ $content = implode(' ', $words) . "... ";
214
+ }
215
+
216
+ $content = wpautop($content);
217
+ }
218
+ }
219
+ else
220
+ {
221
+ //else hide everything
222
+ $content = "";
223
+ }
224
+
225
+ if(empty($post_membership_levels_ids))
226
+ $post_membership_levels_ids = array();
227
+
228
+ if(empty($post_membership_levels_names))
229
+ $post_membership_levels_names = array();
230
+
231
+ $pmpro_content_message_pre = '<div class="pmpro_content_message">';
232
+ $pmpro_content_message_post = '</div>';
233
+
234
+ $sr_search = array("!!levels!!", "!!referrer!!");
235
+ $sr_replace = array(pmpro_implodeToEnglish($post_membership_levels_names), $_SERVER['REQUEST_URI']);
236
+
237
+ //get the correct message to show at the bottom
238
+ if(is_feed())
239
+ {
240
+ $newcontent = apply_filters("pmpro_rss_text_filter", stripslashes(pmpro_getOption("rsstext")));
241
+ $content .= $pmpro_content_message_pre . str_replace($sr_search, $sr_replace, $newcontent) . $pmpro_content_message_post;
242
+ }
243
+ elseif($current_user->ID)
244
+ {
245
+ //not a member
246
+ $newcontent = apply_filters("pmpro_non_member_text_filter", stripslashes(pmpro_getOption("nonmembertext")));
247
+ $content .= $pmpro_content_message_pre . str_replace($sr_search, $sr_replace, $newcontent) . $pmpro_content_message_post;
248
+ }
249
+ else
250
+ {
251
+ //not logged in!
252
+ $newcontent = apply_filters("pmpro_not_logged_in_text_filter", stripslashes(pmpro_getOption("notloggedintext")));
253
+ $content .= $pmpro_content_message_pre . str_replace($sr_search, $sr_replace, $newcontent) . $pmpro_content_message_post;
254
+ }
255
+ }
256
+
257
+ return $content;
258
+ }
259
+ add_filter('the_content', 'pmpro_membership_content_filter', 5);
260
+ add_filter('the_content_rss', 'pmpro_membership_content_filter', 5);
261
+ add_filter('comment_text_rss', 'pmpro_membership_content_filter', 5);
262
+
263
+ /*
264
+ If the_excerpt is called, we want to disable the_content filters so the PMPro messages aren't added to the content before AND after the ecerpt.
265
+ */
266
+ function pmpro_membership_excerpt_filter($content, $skipcheck = false)
267
+ {
268
+ remove_filter('the_content', 'pmpro_membership_content_filter', 5);
269
+ $content = pmpro_membership_content_filter($content, $skipcheck);
270
+ add_filter('the_content', 'pmpro_membership_content_filter', 5);
271
+
272
+ return $content;
273
+ }
274
+ function pmpro_membership_get_excerpt_filter_start($content, $skipcheck = false)
275
+ {
276
+ remove_filter('the_content', 'pmpro_membership_content_filter', 5);
277
+ return $content;
278
+ }
279
+ function pmpro_membership_get_excerpt_filter_end($content, $skipcheck = false)
280
+ {
281
+ add_filter('the_content', 'pmpro_membership_content_filter', 5);
282
+ return $content;
283
+ }
284
+ add_filter('the_excerpt', 'pmpro_membership_excerpt_filter', 15);
285
+ add_filter('get_the_excerpt', 'pmpro_membership_get_excerpt_filter_start', 1);
286
+ add_filter('get_the_excerpt', 'pmpro_membership_get_excerpt_filter_end', 100);
287
+
288
+ function pmpro_comments_filter($comments, $post_id = NULL)
289
+ {
290
+ global $post, $wpdb, $current_user;
291
+ if(!$post_id)
292
+ $post_id = $post->ID;
293
+
294
+ if(!$comments)
295
+ return $comments; //if they are closed anyway, we don't need to check
296
+
297
+ global $post, $current_user;
298
+
299
+ $hasaccess = pmpro_has_membership_access(NULL, NULL, true);
300
+ if(is_array($hasaccess))
301
+ {
302
+ //returned an array to give us the membership level values
303
+ $post_membership_levels_ids = $hasaccess[1];
304
+ $post_membership_levels_names = $hasaccess[2];
305
+ $hasaccess = $hasaccess[0];
306
+ }
307
+
308
+ if($hasaccess)
309
+ {
310
+ //all good, return content
311
+ return $comments;
312
+ }
313
+ else
314
+ {
315
+ if(!$post_membership_levels_ids)
316
+ $post_membership_levels_ids = array();
317
+
318
+ if(!$post_membership_levels_names)
319
+ $post_membership_levels_names = array();
320
+
321
+ //get the correct message
322
+ if(is_feed())
323
+ {
324
+ if(is_array($comments))
325
+ return array();
326
+ else
327
+ return false;
328
+ }
329
+ elseif($current_user->ID)
330
+ {
331
+ //not a member
332
+ if(is_array($comments))
333
+ return array();
334
+ else
335
+ return false;
336
+ }
337
+ else
338
+ {
339
+ //not logged in!
340
+ if(is_array($comments))
341
+ return array();
342
+ else
343
+ return false;
344
+ }
345
+ }
346
+
347
+ return $comments;
348
+ }
349
+ add_filter("comments_array", "pmpro_comments_filter");
350
+ add_filter("comments_open", "pmpro_comments_filter");
351
+
352
+ //keep non-members from getting to certain pages (attachments, etc)
353
+ function pmpro_hide_pages_redirect()
354
+ {
355
+ global $post;
356
+
357
+ if(!is_admin() && !empty($post->ID))
358
+ {
359
+ if($post->post_type == "attachment")
360
+ {
361
+ //check if the user has access to the parent
362
+ if(!pmpro_has_membership_access($post->ID))
363
+ {
364
+ wp_redirect(pmpro_url("levels"));
365
+ exit;
366
+ }
367
+ }
368
+ }
369
+ }
370
+ add_action('wp', 'pmpro_hide_pages_redirect');
includes/countries.php ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //thanks JigoShop
3
+ global $pmpro_countries, $pmpro_default_country;
4
+ $pmpro_default_country = apply_filters("pmpro_default_country", "US");
5
+
6
+ $pmpro_countries = array(
7
+ 'AD' => 'Andorra',
8
+ 'AE' => 'United Arab Emirates',
9
+ 'AF' => 'Afghanistan',
10
+ 'AG' => 'Antigua and Barbuda',
11
+ 'AI' => 'Anguilla',
12
+ 'AL' => 'Albania',
13
+ 'AM' => 'Armenia',
14
+ 'AN' => 'Netherlands Antilles',
15
+ 'AO' => 'Angola',
16
+ 'AQ' => 'Antarctica',
17
+ 'AR' => 'Argentina',
18
+ 'AS' => 'American Samoa',
19
+ 'AT' => 'Austria',
20
+ 'AU' => 'Australia',
21
+ 'AW' => 'Aruba',
22
+ 'AX' => 'Aland Islands',
23
+ 'AZ' => 'Azerbaijan',
24
+ 'BA' => 'Bosnia and Herzegovina',
25
+ 'BB' => 'Barbados',
26
+ 'BD' => 'Bangladesh',
27
+ 'BE' => 'Belgium',
28
+ 'BF' => 'Burkina Faso',
29
+ 'BG' => 'Bulgaria',
30
+ 'BH' => 'Bahrain',
31
+ 'BI' => 'Burundi',
32
+ 'BJ' => 'Benin',
33
+ 'BL' => 'Saint Barthelemy',
34
+ 'BM' => 'Bermuda',
35
+ 'BN' => 'Brunei',
36
+ 'BO' => 'Bolivia',
37
+ 'BR' => 'Brazil',
38
+ 'BS' => 'Bahamas',
39
+ 'BT' => 'Bhutan',
40
+ 'BV' => 'Bouvet Island',
41
+ 'BW' => 'Botswana',
42
+ 'BY' => 'Belarus',
43
+ 'BZ' => 'Belize',
44
+ 'CA' => 'Canada',
45
+ 'CC' => 'Cocos (Keeling) Islands',
46
+ 'CD' => 'Congo (Kinshasa)',
47
+ 'CF' => 'Central African Republic',
48
+ 'CG' => 'Congo (Brazzaville)',
49
+ 'CH' => 'Switzerland',
50
+ 'CI' => 'Ivory Coast',
51
+ 'CK' => 'Cook Islands',
52
+ 'CL' => 'Chile',
53
+ 'CM' => 'Cameroon',
54
+ 'CN' => 'China',
55
+ 'CO' => 'Colombia',
56
+ 'CR' => 'Costa Rica',
57
+ 'CU' => 'Cuba',
58
+ 'CV' => 'Cape Verde',
59
+ 'CX' => 'Christmas Island',
60
+ 'CY' => 'Cyprus',
61
+ 'CZ' => 'Czech Republic',
62
+ 'DE' => 'Germany',
63
+ 'DJ' => 'Djibouti',
64
+ 'DK' => 'Denmark',
65
+ 'DM' => 'Dominica',
66
+ 'DO' => 'Dominican Republic',
67
+ 'DZ' => 'Algeria',
68
+ 'EC' => 'Ecuador',
69
+ 'EE' => 'Estonia',
70
+ 'EG' => 'Egypt',
71
+ 'EH' => 'Western Sahara',
72
+ 'ER' => 'Eritrea',
73
+ 'ES' => 'Spain',
74
+ 'ET' => 'Ethiopia',
75
+ 'FI' => 'Finland',
76
+ 'FJ' => 'Fiji',
77
+ 'FK' => 'Falkland Islands',
78
+ 'FM' => 'Micronesia',
79
+ 'FO' => 'Faroe Islands',
80
+ 'FR' => 'France',
81
+ 'GA' => 'Gabon',
82
+ 'GB' => 'United Kingdom',
83
+ 'GD' => 'Grenada',
84
+ 'GE' => 'Georgia',
85
+ 'GF' => 'French Guiana',
86
+ 'GG' => 'Guernsey',
87
+ 'GH' => 'Ghana',
88
+ 'GI' => 'Gibraltar',
89
+ 'GL' => 'Greenland',
90
+ 'GM' => 'Gambia',
91
+ 'GN' => 'Guinea',
92
+ 'GP' => 'Guadeloupe',
93
+ 'GQ' => 'Equatorial Guinea',
94
+ 'GR' => 'Greece',
95
+ 'GS' => 'South Georgia and the South Sandwich Islands',
96
+ 'GT' => 'Guatemala',
97
+ 'GU' => 'Guam',
98
+ 'GW' => 'Guinea-Bissau',
99
+ 'GY' => 'Guyana',
100
+ 'HK' => 'Hong Kong S.A.R., China',
101
+ 'HM' => 'Heard Island and McDonald Islands',
102
+ 'HN' => 'Honduras',
103
+ 'HR' => 'Croatia',
104
+ 'HT' => 'Haiti',
105
+ 'HU' => 'Hungary',
106
+ 'ID' => 'Indonesia',
107
+ 'IE' => 'Ireland',
108
+ 'IL' => 'Israel',
109
+ 'IM' => 'Isle of Man',
110
+ 'IN' => 'India',
111
+ 'IO' => 'British Indian Ocean Territory',
112
+ 'IQ' => 'Iraq',
113
+ 'IR' => 'Iran',
114
+ 'IS' => 'Iceland',
115
+ 'IT' => 'Italy',
116
+ 'JE' => 'Jersey',
117
+ 'JM' => 'Jamaica',
118
+ 'JO' => 'Jordan',
119
+ 'JP' => 'Japan',
120
+ 'KE' => 'Kenya',
121
+ 'KG' => 'Kyrgyzstan',
122
+ 'KH' => 'Cambodia',
123
+ 'KI' => 'Kiribati',
124
+ 'KM' => 'Comoros',
125
+ 'KN' => 'Saint Kitts and Nevis',
126
+ 'KP' => 'North Korea',
127
+ 'KR' => 'South Korea',
128
+ 'KW' => 'Kuwait',
129
+ 'KY' => 'Cayman Islands',
130
+ 'KZ' => 'Kazakhstan',
131
+ 'LA' => 'Laos',
132
+ 'LB' => 'Lebanon',
133
+ 'LC' => 'Saint Lucia',
134
+ 'LI' => 'Liechtenstein',
135
+ 'LK' => 'Sri Lanka',
136
+ 'LR' => 'Liberia',
137
+ 'LS' => 'Lesotho',
138
+ 'LT' => 'Lithuania',
139
+ 'LU' => 'Luxembourg',
140
+ 'LV' => 'Latvia',
141
+ 'LY' => 'Libya',
142
+ 'MA' => 'Morocco',
143
+ 'MC' => 'Monaco',
144
+ 'MD' => 'Moldova',
145
+ 'ME' => 'Montenegro',
146
+ 'MF' => 'Saint Martin (French part)',
147
+ 'MG' => 'Madagascar',
148
+ 'MH' => 'Marshall Islands',
149
+ 'MK' => 'Macedonia',
150
+ 'ML' => 'Mali',
151
+ 'MM' => 'Myanmar',
152
+ 'MN' => 'Mongolia',
153
+ 'MO' => 'Macao S.A.R., China',
154
+ 'MP' => 'Northern Mariana Islands',
155
+ 'MQ' => 'Martinique',
156
+ 'MR' => 'Mauritania',
157
+ 'MS' => 'Montserrat',
158
+ 'MT' => 'Malta',
159
+ 'MU' => 'Mauritius',
160
+ 'MV' => 'Maldives',
161
+ 'MW' => 'Malawi',
162
+ 'MX' => 'Mexico',
163
+ 'MY' => 'Malaysia',
164
+ 'MZ' => 'Mozambique',
165
+ 'NA' => 'Namibia',
166
+ 'NC' => 'New Caledonia',
167
+ 'NE' => 'Niger',
168
+ 'NF' => 'Norfolk Island',
169
+ 'NG' => 'Nigeria',
170
+ 'NI' => 'Nicaragua',
171
+ 'NL' => 'Netherlands',
172
+ 'NO' => 'Norway',
173
+ 'NP' => 'Nepal',
174
+ 'NR' => 'Nauru',
175
+ 'NU' => 'Niue',
176
+ 'NZ' => 'New Zealand',
177
+ 'OM' => 'Oman',
178
+ 'PA' => 'Panama',
179
+ 'PE' => 'Peru',
180
+ 'PF' => 'French Polynesia',
181
+ 'PG' => 'Papua New Guinea',
182
+ 'PH' => 'Philippines',
183
+ 'PK' => 'Pakistan',
184
+ 'PL' => 'Poland',
185
+ 'PM' => 'Saint Pierre and Miquelon',
186
+ 'PN' => 'Pitcairn',
187
+ 'PR' => 'Puerto Rico',
188
+ 'PS' => 'Palestinian Territory',
189
+ 'PT' => 'Portugal',
190
+ 'PW' => 'Palau',
191
+ 'PY' => 'Paraguay',
192
+ 'QA' => 'Qatar',
193
+ 'RE' => 'Reunion',
194
+ 'RO' => 'Romania',
195
+ 'RS' => 'Serbia',
196
+ 'RU' => 'Russia',
197
+ 'RW' => 'Rwanda',
198
+ 'SA' => 'Saudi Arabia',
199
+ 'SB' => 'Solomon Islands',
200
+ 'SC' => 'Seychelles',
201
+ 'SD' => 'Sudan',
202
+ 'SE' => 'Sweden',
203
+ 'SG' => 'Singapore',
204
+ 'SH' => 'Saint Helena',
205
+ 'SI' => 'Slovenia',
206
+ 'SJ' => 'Svalbard and Jan Mayen',
207
+ 'SK' => 'Slovakia',
208
+ 'SL' => 'Sierra Leone',
209
+ 'SM' => 'San Marino',
210
+ 'SN' => 'Senegal',
211
+ 'SO' => 'Somalia',
212
+ 'SR' => 'Suriname',
213
+ 'ST' => 'Sao Tome and Principe',
214
+ 'SV' => 'El Salvador',
215
+ 'SY' => 'Syria',
216
+ 'SZ' => 'Swaziland',
217
+ 'TC' => 'Turks and Caicos Islands',
218
+ 'TD' => 'Chad',
219
+ 'TF' => 'French Southern Territories',
220
+ 'TG' => 'Togo',
221
+ 'TH' => 'Thailand',
222
+ 'TJ' => 'Tajikistan',
223
+ 'TK' => 'Tokelau',
224
+ 'TL' => 'Timor-Leste',
225
+ 'TM' => 'Turkmenistan',
226
+ 'TN' => 'Tunisia',
227
+ 'TO' => 'Tonga',
228
+ 'TR' => 'Turkey',
229
+ 'TT' => 'Trinidad and Tobago',
230
+ 'TV' => 'Tuvalu',
231
+ 'TW' => 'Taiwan',
232
+ 'TZ' => 'Tanzania',
233
+ 'UA' => 'Ukraine',
234
+ 'UG' => 'Uganda',
235
+ 'UM' => 'United States Minor Outlying Islands',
236
+ 'US' => 'United States',
237
+ 'UY' => 'Uruguay',
238
+ 'UZ' => 'Uzbekistan',
239
+ 'VA' => 'Vatican',
240
+ 'VC' => 'Saint Vincent and the Grenadines',
241
+ 'VE' => 'Venezuela',
242
+ 'VG' => 'British Virgin Islands',
243
+ 'VI' => 'U.S. Virgin Islands',
244
+ 'VN' => 'Vietnam',
245
+ 'VU' => 'Vanuatu',
246
+ 'WF' => 'Wallis and Futuna',
247
+ 'WS' => 'Samoa',
248
+ 'YE' => 'Yemen',
249
+ 'YT' => 'Mayotte',
250
+ 'ZA' => 'South Africa',
251
+ 'ZM' => 'Zambia',
252
+ 'ZW' => 'Zimbabwe',
253
+ 'USAF' => 'US Armed Forces' ,
254
+ 'VE' => 'Venezuela',
255
+ );
256
+
257
+ asort($pmpro_countries);
258
+
259
+ $pmpro_countries = apply_filters("pmpro_countries", $pmpro_countries);
includes/currencies.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //thanks jigoshop
3
+ global $pmpro_currencies, $pmpro_default_currency;
4
+ $pmpro_default_currency = apply_filters("pmpro_default_currency", "USD");
5
+
6
+ $pmpro_currencies = array(
7
+ 'USD' => __('US Dollars (&#36;)', 'pmpro'),
8
+ 'EUR' => __('Euros (&euro;)', 'pmpro'),
9
+ 'GBP' => __('Pounds Sterling (&pound;)', 'pmpro'),
10
+ 'AUD' => __('Australian Dollars (&#36;)', 'pmpro'),
11
+ 'BRL' => __('Brazilian Real (&#36;)', 'pmpro'),
12
+ 'CAD' => __('Canadian Dollars (&#36;)', 'pmpro'),
13
+ 'CZK' => __('Czech Koruna', 'pmpro'),
14
+ 'DKK' => __('Danish Krone', 'pmpro'),
15
+ 'HKD' => __('Hong Kong Dollar (&#36;)', 'pmpro'),
16
+ 'HUF' => __('Hungarian Forint', 'pmpro'),
17
+ 'ILS' => __('Israeli Shekel', 'pmpro'),
18
+ 'JPY' => __('Japanese Yen (&yen;)', 'pmpro'),
19
+ 'MYR' => __('Malaysian Ringgits', 'pmpro'),
20
+ 'MXN' => __('Mexican Peso (&#36;)', 'pmpro'),
21
+ 'NZD' => __('New Zealand Dollar (&#36;)', 'pmpro'),
22
+ 'NOK' => __('Norwegian Krone', 'pmpro'),
23
+ 'PHP' => __('Philippine Pesos', 'pmpro'),
24
+ 'PLN' => __('Polish Zloty', 'pmpro'),
25
+ 'SGD' => __('Singapore Dollar (&#36;)', 'pmpro'),
26
+ 'SEK' => __('Swedish Krona', 'pmpro'),
27
+ 'CHF' => __('Swiss Franc', 'pmpro'),
28
+ 'TWD' => __('Taiwan New Dollars', 'pmpro'),
29
+ 'THB' => __('Thai Baht', 'pmpro')
30
+ );
31
+
32
+ $pmpro_currencies = apply_filters("pmpro_currencies", $pmpro_currencies);
33
+
34
+ //stripe only supports a few
35
+ global $pmpro_stripe_currencies;
36
+ $pmpro_stripe_currencies = array(
37
+ 'USD' => __('US Dollars (&#36;)', 'pmpro'),
38
+ 'CAD' => __('Canadian Dollars (&#36;)', 'pmpro'),
39
+ 'GBP' => __('Pounds Sterling (&pound;)', 'pmpro'),
40
+ 'EUR' => __('Euros (&euro;)', 'pmpro')
41
+ );
42
+ ?>
includes/email.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Nicer default emails
4
+ */
5
+ function pmpro_wp_mail_from_name($from_name)
6
+ {
7
+ $pmpro_from_name = pmpro_getOption("from_name");
8
+ if ($pmpro_from_name)
9
+ return $pmpro_from_name;
10
+ return $from_name;
11
+ }
12
+ function pmpro_wp_mail_from($from_email)
13
+ {
14
+ $pmpro_from_email = pmpro_getOption("from_email");
15
+ if ($pmpro_from_email && is_email( $pmpro_from_email ) )
16
+ return $pmpro_from_email;
17
+ return $from_email;
18
+ }
19
+ add_filter('wp_mail_from_name', 'pmpro_wp_mail_from_name');
20
+ add_filter('wp_mail_from', 'pmpro_wp_mail_from');
21
+
22
+ /*
23
+ If the $email_member_notification option is empty, disable the wp_new_user_notification email at checkout.
24
+ */
25
+ $email_member_notification = pmpro_getOption("email_member_notification");
26
+ if(empty($email_member_notification))
27
+ add_filter("pmpro_wp_new_user_notification", "__return_false", 0);
28
+
29
+ /*
30
+ Adds template files and changes content type to html if using PHPMailer directly.
31
+ */
32
+ function pmpro_send_html( $phpmailer ) {
33
+ // Set the original plain text message
34
+ $phpmailer->AltBody = wp_specialchars_decode($phpmailer->Body, ENT_QUOTES);
35
+ // Clean < and > around text links in WP 3.1
36
+ $phpmailer->Body = preg_replace('#<(http://[^*]+)>#', '$1', $phpmailer->Body);
37
+ // Convert line breaks & make links clickable
38
+ $phpmailer->Body = wpautop ( make_clickable ($phpmailer->Body) );
39
+
40
+ // Add template to message
41
+ if(file_exists(TEMPLATEPATH . "/email_header.html"))
42
+ {
43
+ $phpmailer->Body = file_get_contents(TEMPLATEPATH . "/email_header.html") . "\n" . $phpmailer->Body;
44
+ }
45
+ if(file_exists(TEMPLATEPATH . "/email_footer.html"))
46
+ {
47
+ $phpmailer->Body = $phpmailer->Body . "\n" . file_get_contents(TEMPLATEPATH . "/email_footer.html");
48
+ }
49
+
50
+ // Replace variables in email
51
+ global $current_user;
52
+ $data = array(
53
+ "name" => $current_user->display_name,
54
+ "sitename" => get_option("blogname"),
55
+ "login_link" => pmpro_url("account"),
56
+ "display_name" => $current_user->display_name,
57
+ "user_email" => $current_user->user_email,
58
+ "subject" => $phpmailer->Subject
59
+ );
60
+ foreach($data as $key => $value)
61
+ {
62
+ $phpmailer->Body = str_replace("!!" . $key . "!!", $value, $phpmailer->Body);
63
+ }
64
+
65
+ do_action("pmpro_after_phpmailer_init", $phpmailer);
66
+ do_action("pmpro_after_pmpmailer_init", $phpmailer); //typo left in for backwards compatibility
67
+ }
68
+
69
+ function pmpro_wp_mail_content_type( $content_type ) {
70
+ add_action('phpmailer_init', 'pmpro_send_html');
71
+
72
+ //change to html if not already
73
+ if( $content_type == 'text/plain')
74
+ {
75
+ $content_type = 'text/html';
76
+ }
77
+ return $content_type;
78
+ }
79
+ add_filter('wp_mail_content_type', 'pmpro_wp_mail_content_type');
includes/filters.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ This file was added in version 1.5.5 of the plugin. This file is meant to store various hacks, filters, and actions that were originally developed outside of the PMPro core and brought in later... or just things that are cleaner/easier to impement via hooks and filters.
4
+ */
5
+
6
+ /*
7
+ If checking out for the same level, add remaining days to the enddate.
8
+ Pulled in from: https://gist.github.com/3678054
9
+ */
10
+ function pmpro_checkout_level_extend_memberships($level)
11
+ {
12
+ global $pmpro_msg, $pmpro_msgt;
13
+
14
+ //does this level expire? are they an existing user of this level?
15
+ if(!empty($level) && !empty($level->expiration_number) && pmpro_hasMembershipLevel($level->id))
16
+ {
17
+ //get the current enddate of their membership
18
+ global $current_user;
19
+ $expiration_date = $current_user->membership_level->enddate;
20
+
21
+ //calculate days left
22
+ $todays_date = time();
23
+ $time_left = $expiration_date - $todays_date;
24
+
25
+ //time left?
26
+ if($time_left > 0)
27
+ {
28
+ //convert to days and add to the expiration date (assumes expiration was 1 year)
29
+ $days_left = floor($time_left/(60*60*24));
30
+
31
+ //figure out days based on period
32
+ if($level->expiration_period == "Day")
33
+ $total_days = $days_left + $level->expiration_number;
34
+ elseif($level->expiration_period == "Week")
35
+ $total_days = $days_left + $level->expiration_number * 7;
36
+ elseif($level->expiration_period == "Month")
37
+ $total_days = $days_left + $level->expiration_number * 30;
38
+ elseif($level->expiration_period == "Year")
39
+ $total_days = $days_left + $level->expiration_number * 365;
40
+
41
+ //update number and period
42
+ $level->expiration_number = $total_days;
43
+ $level->expiration_period = "Day";
44
+ }
45
+ }
46
+
47
+ return $level;
48
+ }
49
+ add_filter("pmpro_checkout_level", "pmpro_checkout_level_extend_memberships");
50
+ /*
51
+ Same thing as above but when processed by the ipnhandler for PayPal standard.
52
+ */
53
+ function pmpro_ipnhandler_level_extend_memberships($level, $user_id)
54
+ {
55
+ global $pmpro_msg, $pmpro_msgt;
56
+
57
+ //does this level expire? are they an existing user of this level?
58
+ if(!empty($level) && !empty($level->expiration_number) && pmpro_hasMembershipLevel($level->id, $user_id))
59
+ {
60
+ //get the current enddate of their membership
61
+ $user_level = pmpro_getMembershipLevelForUser($user_id);
62
+ $expiration_date = $user_level->enddate;
63
+
64
+ //calculate days left
65
+ $todays_date = time();
66
+ $time_left = $expiration_date - $todays_date;
67
+
68
+ //time left?
69
+ if($time_left > 0)
70
+ {
71
+ //convert to days and add to the expiration date (assumes expiration was 1 year)
72
+ $days_left = floor($time_left/(60*60*24));
73
+
74
+ //figure out days based on period
75
+ if($level->expiration_period == "Day")
76
+ $total_days = $days_left + $level->expiration_number;
77
+ elseif($level->expiration_period == "Week")
78
+ $total_days = $days_left + $level->expiration_number * 7;
79
+ elseif($level->expiration_period == "Month")
80
+ $total_days = $days_left + $level->expiration_number * 30;
81
+ elseif($level->expiration_period == "Year")
82
+ $total_days = $days_left + $level->expiration_number * 365;
83
+
84
+ //update number and period
85
+ $level->expiration_number = $total_days;
86
+ $level->expiration_period = "Day";
87
+ }
88
+ }
89
+
90
+ return $level;
91
+ }
92
+ add_filter("pmpro_ipnhandler_level", "pmpro_ipnhandler_level_extend_memberships", 10, 2);
93
+
94
+ /*
95
+ If checking out for the same level, keep your old startdate.
96
+ Added with 1.5.5
97
+ */
98
+ function pmpro_checkout_start_date_keep_startdate($startdate, $user_id, $level)
99
+ {
100
+ if(pmpro_hasMembershipLevel($level->id, $user_id))
101
+ {
102
+ global $wpdb;
103
+ $sqlQuery = "SELECT startdate FROM $wpdb->pmpro_memberships_users WHERE user_id = '" . esc_sql($user_id) . "' AND membership_id = '" . esc_sql($level->id) . "' AND status = 'active' ORDER BY id DESC LIMIT 1";
104
+ $old_startdate = $wpdb->get_var($sqlQuery);
105
+
106
+ if(!empty($old_startdate))
107
+ $startdate = "'" . $old_startdate . "'";
108
+ }
109
+
110
+ return $startdate;
111
+ }
112
+ add_filter("pmpro_checkout_start_date", "pmpro_checkout_start_date_keep_startdate", 10, 3);
113
+
114
+ /*
115
+ Stripe Lite Pulled into Core Plugin
116
+ */
117
+ //Stripe Lite, Set the Globals/etc
118
+ $stripe_billingaddress = pmpro_getOption("stripe_billingaddress");
119
+ if(empty($stripe_billingaddress))
120
+ {
121
+ global $pmpro_stripe_lite;
122
+ $pmpro_stripe_lite = true;
123
+ add_filter("pmpro_stripe_lite", "__return_true");
124
+ add_filter("pmpro_required_billing_fields", "pmpro_required_billing_fields_stripe_lite");
125
+ }
126
+
127
+ //Stripe Lite, Don't Require Billing Fields
128
+ function pmpro_required_billing_fields_stripe_lite($fields)
129
+ {
130
+ global $gateway;
131
+
132
+ //ignore if not using stripe
133
+ if($gateway != "stripe")
134
+ return $fields;
135
+
136
+ //some fields to remove
137
+ $remove = array('bfirstname', 'blastname', 'baddress1', 'bcity', 'bstate', 'bzipcode', 'bphone', 'bcountry', 'CardType');
138
+
139
+ //if a user is logged in, don't require bemail either
140
+ global $current_user;
141
+ if(!empty($current_user->user_email))
142
+ $remove[] = 'bemail';
143
+
144
+ //remove the fields
145
+ foreach($remove as $field)
146
+ unset($fields[$field]);
147
+
148
+ //ship it!
149
+ return $fields;
150
+ }
includes/functions.php ADDED
@@ -0,0 +1,1622 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if(!function_exists("sornot"))
3
+ {
4
+ function sornot($t, $n)
5
+ {
6
+ if($n == 1)
7
+ return $t;
8
+ else
9
+ return $t . "s";
10
+ }
11
+ }
12
+
13
+ //setup wpdb for the tables we need
14
+ function pmpro_setDBTables()
15
+ {
16
+ global $table_prefix, $wpdb;
17
+ $wpdb->hide_errors();
18
+ $wpdb->pmpro_membership_levels = $table_prefix . 'pmpro_membership_levels';
19
+ $wpdb->pmpro_memberships_users = $table_prefix . 'pmpro_memberships_users';
20
+ $wpdb->pmpro_memberships_categories = $table_prefix . 'pmpro_memberships_categories';
21
+ $wpdb->pmpro_memberships_pages = $table_prefix . 'pmpro_memberships_pages';
22
+ $wpdb->pmpro_membership_orders = $table_prefix . 'pmpro_membership_orders';
23
+ $wpdb->pmpro_discount_codes = $wpdb->prefix . 'pmpro_discount_codes';
24
+ $wpdb->pmpro_discount_codes_levels = $wpdb->prefix . 'pmpro_discount_codes_levels';
25
+ $wpdb->pmpro_discount_codes_uses = $wpdb->prefix . 'pmpro_discount_codes_uses';
26
+ }
27
+ pmpro_setDBTables();
28
+
29
+ //from: http://stackoverflow.com/questions/5266945/wordpress-how-detect-if-current-page-is-the-login-page/5892694#5892694
30
+ function pmpro_is_login_page() {
31
+ return (in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php')) || is_page("login"));
32
+ }
33
+
34
+ //thanks: http://wordpress.org/support/topic/is_plugin_active
35
+ function pmpro_is_plugin_active( $plugin ) {
36
+ return in_array( $plugin, (array) get_option( 'active_plugins', array() ) );
37
+ }
38
+
39
+ //scraping - override n if you have more than 1 group of matches and don't want the first group
40
+ function pmpro_getMatches($p, $s, $firstvalue = FALSE, $n = 1)
41
+ {
42
+ $ok = preg_match_all($p, $s, $matches);
43
+
44
+ if(!$ok)
45
+ return false;
46
+ else
47
+ {
48
+ if($firstvalue)
49
+ return $matches[$n][0];
50
+ else
51
+ return $matches[$n];
52
+ }
53
+ }
54
+
55
+ function pmpro_br2nl($text, $tags = "br")
56
+ {
57
+ if(!is_array($tags))
58
+ $tags = explode(" ", $tags);
59
+
60
+ foreach($tags as $tag)
61
+ {
62
+ $text = eregi_replace("<" . $tag . "[^>]*>", "\n", $text);
63
+ $text = eregi_replace("</" . $tag . "[^>]*>", "\n", $text);
64
+ }
65
+
66
+ return($text);
67
+ }
68
+
69
+ function pmpro_getOption($s, $force = false)
70
+ {
71
+ if(isset($_REQUEST[$s]) && !$force)
72
+ return trim($_REQUEST[$s]);
73
+ elseif(get_option("pmpro_" . $s))
74
+ return get_option("pmpro_" . $s);
75
+ else
76
+ return "";
77
+ }
78
+
79
+ function pmpro_setOption($s, $v = NULL)
80
+ {
81
+ //no value is given, set v to the request var
82
+ if($v === NULL && isset($_REQUEST[$s]))
83
+ $v = trim($_REQUEST[$s]);
84
+
85
+ if(is_array($v))
86
+ $v = implode(",", $v);
87
+
88
+ return update_option("pmpro_" . $s, $v);
89
+ }
90
+
91
+ function pmpro_get_slug($post_id)
92
+ {
93
+ global $pmpro_slugs, $wpdb;
94
+ if(!$pmpro_slugs[$post_id])
95
+ $pmpro_slugs[$post_id] = $wpdb->get_var("SELECT post_name FROM $wpdb->posts WHERE ID = '" . $post_id . "' LIMIT 1");
96
+
97
+ return $pmpro_slugs[$post_id];
98
+ }
99
+
100
+ function pmpro_url($page = NULL, $querystring = "", $scheme = NULL)
101
+ {
102
+ global $besecure;
103
+ $besecure = apply_filters("besecure", $besecure);
104
+
105
+ if(!$scheme && $besecure)
106
+ $scheme = "https";
107
+ elseif(!$scheme)
108
+ $scheme = "http";
109
+
110
+ if(!$page)
111
+ $page = "levels";
112
+
113
+ global $pmpro_pages;
114
+
115
+ //? vs &
116
+ if(strpos(get_permalink($pmpro_pages[$page]), "?"))
117
+ return home_url(str_replace(home_url(), "", get_permalink($pmpro_pages[$page])) . str_replace("?", "&", $querystring), $scheme);
118
+ else
119
+ return home_url(str_replace(home_url(), "", get_permalink($pmpro_pages[$page])) . $querystring, $scheme);
120
+ }
121
+
122
+ function pmpro_isLevelFree(&$level)
123
+ {
124
+ if(!empty($level) && $level->initial_payment <= 0 && $level->billing_amount <= 0 && $level->trial_amount <= 0)
125
+ return true;
126
+ else
127
+ return false;
128
+ }
129
+
130
+ function pmpro_isLevelRecurring(&$level)
131
+ {
132
+ if(!empty($level) && ($level->billing_amount > 0 || $level->trial_amount > 0))
133
+ return true;
134
+ else
135
+ return false;
136
+ }
137
+
138
+ function pmpro_isLevelTrial(&$level)
139
+ {
140
+ if($level->trial_limit > 0)
141
+ {
142
+ return true;
143
+ }
144
+ else
145
+ return false;
146
+ }
147
+
148
+ function pmpro_isLevelExpiring(&$level)
149
+ {
150
+ if($level->expiration_number > 0)
151
+ return true;
152
+ else
153
+ return false;
154
+ }
155
+
156
+ function pmpro_getLevelCost(&$level, $tags = true)
157
+ {
158
+ global $pmpro_currency_symbol;
159
+ //initial payment
160
+ $r = sprintf(_x('The price for membership is <strong>%s</strong> now', 'Initial payment in cost text generation.', 'pmpro'), $pmpro_currency_symbol . number_format($level->initial_payment, 2));
161
+
162
+ //recurring part
163
+ if($level->billing_amount != '0.00')
164
+ {
165
+ if($level->billing_limit > 1)
166
+ {
167
+ if($level->cycle_number == '1')
168
+ {
169
+ $r .= sprintf(__(' and then <strong>%s per %s for %d more %s</strong>.', 'Recurring payment in cost text generation. E.g. $5 every month for 2 more payments.', 'pmpro'), $pmpro_currency_symbol . $level->billing_amount, pmpro_translate_billing_period($level->cycle_period), $level->billing_limit, pmpro_translate_billing_period($level->cycle_period, $level->billing_limit));
170
+ }
171
+ else
172
+ {
173
+ $r .= sprintf(__(' and then <strong>%s every %d %s for %d more %s</strong>.', 'Recurring payment in cost text generation. E.g., $5 every 2 months for 2 more payments.', 'pmpro'), $pmpro_currency_symbol . $level->billing_amount, $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number), $level->billing_limit, pmpro_translate_billing_period($level->cycle_period, $level->billing_limit));
174
+ }
175
+ }
176
+ elseif($level->billing_limit == 1)
177
+ {
178
+ $r .= sprintf(__(' and then <strong>%s after %d %s</strong>.', 'Recurring payment in cost text generation. E.g. $5 after 2 months.', 'pmpro'), $pmpro_currency_symbol . $level->billing_amount, $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number));
179
+ }
180
+ else
181
+ {
182
+ if($level->cycle_number == '1')
183
+ {
184
+ $r .= sprintf(__(' and then <strong>%s per %s</strong>.', 'Recurring payment in cost text generation. E.g. $5 every month.', 'pmpro'), $pmpro_currency_symbol . $level->billing_amount, pmpro_translate_billing_period($level->cycle_period));
185
+ }
186
+ else
187
+ {
188
+ $r .= sprintf(__(' and then <strong>%s every %d %s</strong>.', 'Recurring payment in cost text generation. E.g., $5 every 2 months.', 'pmpro'), $pmpro_currency_symbol . $level->billing_amount, $level->cycle_number, pmpro_translate_billing_period($level->cycle_period, $level->cycle_number));
189
+ }
190
+ }
191
+ }
192
+ else
193
+ $r .= '.';
194
+
195
+ //trial part
196
+ if($level->trial_limit)
197
+ {
198
+ if($level->trial_amount == '0.00')
199
+ {
200
+ if($level->trial_limit == '1')
201
+ {
202
+ $r .= ' ' . _x('After your initial payment, your first payment is Free.', 'Trial payment in cost text generation.', 'pmpro');
203
+ }
204
+ else
205
+ {
206
+ $r .= ' ' . sprintf(_x('After your initial payment, your first %d payments are Free.', 'Trial payment in cost text generation.', 'pmpro'), $level->trial_limit);
207
+ }
208
+ }
209
+ else
210
+ {
211
+ if($level->trial_limit == '1')
212
+ {
213
+ $r .= ' ' . sprintf(_x('After your initial payment, your first payment will cost %s.', 'Trial payment in cost text generation.', 'pmpro'), $pmpro_currency_symbol . $level->trial_amount);
214
+ }
215
+ else
216
+ {
217
+ $r .= ' ' . sprintf(_x('After your initial payment, your first %d payments will cost %s.', 'Trial payment in cost text generation. E.g. ... first 2 payments will cost $5', 'pmpro'), $level->trial_limit, $pmpro_currency_symbol . $level->trial_amount);
218
+ }
219
+ }
220
+ }
221
+
222
+ //taxes part
223
+ $tax_state = pmpro_getOption("tax_state");
224
+ $tax_rate = pmpro_getOption("tax_rate");
225
+
226
+ if($tax_state && $tax_rate && !pmpro_isLevelFree($level))
227
+ {
228
+ $r .= sprintf(_x('Customers in %s will be charged %s%% tax.', 'Tax part in cost text generation', 'pmpro'), $tax_state, round($tax_rate * 100, 2));
229
+ }
230
+
231
+ if(!$tags)
232
+ $r = strip_tags($r);
233
+
234
+ $r = apply_filters("pmpro_level_cost_text", $r, $level);
235
+ return $r;
236
+ }
237
+
238
+ function pmpro_getLevelExpiration(&$level)
239
+ {
240
+ if($level->expiration_number)
241
+ {
242
+ $expiration_text = sprintf(_x("Membership expires after %d %s.", "Expiration text. E.g. Membership expires after 5 Months.", "pmpro"), $level->expiration_number, pmpro_translate_billing_period($level->expiration_period, $level->expiration_number));
243
+ }
244
+ else
245
+ $expiration_text = "";
246
+
247
+ $expiration_text = apply_filters("pmpro_level_expiration_text", $expiration_text, $level);
248
+ return $expiration_text;
249
+ }
250
+
251
+ function pmpro_hideAds()
252
+ {
253
+ global $pmpro_display_ads;
254
+ return !$pmpro_display_ads;
255
+ }
256
+
257
+ function pmpro_displayAds()
258
+ {
259
+ global $pmpro_display_ads;
260
+ return $pmpro_display_ads;
261
+ }
262
+
263
+ function pmpro_next_payment($user_id = NULL)
264
+ {
265
+ global $wpdb, $current_user;
266
+ if(!$user_id)
267
+ $user_id = $current_user->ID;
268
+
269
+ if(!$user_id)
270
+ return false;
271
+
272
+ //when were they last billed
273
+ $lastdate = $wpdb->get_var("SELECT UNIX_TIMESTAMP(timestamp) as timestamp FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' ORDER BY timestamp DESC LIMIT 1");
274
+
275
+ if($lastdate)
276
+ {
277
+ //next payment will be same day, following month
278
+ $lastmonth = date("n", $lastdate);
279
+ $lastday = date("j", $lastdate);
280
+ $lastyear = date("Y", $lastdate);
281
+
282
+ $nextmonth = ((int)$lastmonth) + 1;
283
+ if($nextmonth == 13)
284
+ {
285
+ $nextmonth = 1;
286
+ $nextyear = ((int)$lastyear) + 1;
287
+ }
288
+ else
289
+ $nextyear = $lastyear;
290
+
291
+ $daysinnextmonth = date("t", strtotime($nextyear . "-" . $nextmonth . "-1"));
292
+
293
+ if($daysinnextmonth < $lastday)
294
+ {
295
+ $nextday = $daysinnextmonth;
296
+ }
297
+ else
298
+ $nextday = $lastday;
299
+
300
+ return strtotime($nextyear . "-" . $nextmonth . "-" . $nextday);
301
+ }
302
+ else
303
+ {
304
+ return false;
305
+ }
306
+
307
+ }
308
+
309
+ if(!function_exists("last4"))
310
+ {
311
+ function last4($t)
312
+ {
313
+ return substr($t, strlen($t) - 4, 4);
314
+ }
315
+ }
316
+
317
+ if(!function_exists("hideCardNumber"))
318
+ {
319
+ function hideCardNumber($c, $dashes = true)
320
+ {
321
+ if($c)
322
+ {
323
+ if($dashes)
324
+ return "XXXX-XXXX-XXXX-" . substr($c, strlen($c) - 4, 4);
325
+ else
326
+ return "XXXXXXXXXXXX" . substr($c, strlen($c) - 4, 4);
327
+ }
328
+ else
329
+ {
330
+ return "";
331
+ }
332
+ }
333
+ }
334
+
335
+ if(!function_exists("cleanPhone"))
336
+ {
337
+ function cleanPhone($phone)
338
+ {
339
+ //if a + is passed, just pass it along
340
+ if(strpos($phone, "+") !== false)
341
+ return $phone;
342
+
343
+ //clean the phone
344
+ $phone = str_replace("-", "", $phone);
345
+ $phone = str_replace(".", "", $phone);
346
+ $phone = str_replace("(", "", $phone);
347
+ $phone = str_replace(")", "", $phone);
348
+ $phone = str_replace(" ", "", $phone);
349
+
350
+ return $phone;
351
+ }
352
+ }
353
+
354
+ if(!function_exists("formatPhone"))
355
+ {
356
+ function formatPhone($phone)
357
+ {
358
+ $phone = cleanPhone($phone);
359
+
360
+ if(strlen($phone) == 11)
361
+ return substr($phone, 0, 1) . " (" . substr($phone, 1, 3) . ") " . substr($phone, 4, 3) . "-" . substr($phone, 7, 4);
362
+ elseif(strlen($phone) == 10)
363
+ return "(" . substr($phone, 0, 3) . ") " . substr($phone, 3, 3) . "-" . substr($phone, 6, 4);
364
+ elseif(strlen($phone) == 7)
365
+ return substr($phone, 0, 3) . "-" . substr($phone, 3, 4);
366
+ else
367
+ return $phone;
368
+ }
369
+ }
370
+
371
+ function pmpro_showRequiresMembershipMessage()
372
+ {
373
+ //get the correct message
374
+ if(is_feed())
375
+ {
376
+ $content = pmpro_getOption("rsstext");
377
+ $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
378
+ }
379
+ elseif($current_user->ID)
380
+ {
381
+ //not a member
382
+ $content = pmpro_getOption("nonmembertext");
383
+ $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
384
+ }
385
+ else
386
+ {
387
+ //not logged in!
388
+ $content = pmpro_getOption("notloggedintext");
389
+ $content = str_replace("!!levels!!", implode(", ", $post_membership_levels_names), $content);
390
+ }
391
+ }
392
+
393
+ /* pmpro_hasMembershipLevel() checks if the passed user is a member of the passed level
394
+ *
395
+ * $level may either be the ID or name of the desired membership_level. (or an array of such)
396
+ * If $user_id is omitted, the value will be retrieved from $current_user.
397
+ *
398
+ * Return values:
399
+ * Success returns boolean true.
400
+ * Failure returns a string containing the error message.
401
+ */
402
+ function pmpro_hasMembershipLevel($levels = NULL, $user_id = NULL)
403
+ {
404
+ global $current_user, $all_membership_levels, $wpdb;
405
+
406
+ $return = false;
407
+
408
+ if(empty($user_id)) //no user_id passed, check the current user
409
+ {
410
+ $user_id = $current_user->ID;
411
+ $membership_levels = $current_user->membership_levels;
412
+ }
413
+ else //get membership levels for given user
414
+ {
415
+ $membership_levels = pmpro_getMembershipLevelsForUser($user_id);
416
+ }
417
+
418
+ if($levels === "0" || $levels === 0) //if 0 was passed, return true if they have no level and false if they have any
419
+ {
420
+ $return = empty($membership_levels);
421
+ }
422
+ elseif(empty($levels)) //if no level var was passed, we're just checking if they have any level
423
+ {
424
+ $return = !empty($membership_levels);
425
+ }
426
+ else
427
+ {
428
+ if(!is_array($levels)) //make an array out of a single element so we can use the same code
429
+ {
430
+ $levels = array($levels);
431
+ }
432
+
433
+ if(empty($membership_levels))
434
+ {
435
+ //user has no levels just check if 0 was sent in one of the levels
436
+ if(in_array(0, $levels) || in_array("0", $levels))
437
+ $return = true;
438
+ }
439
+ else
440
+ {
441
+ foreach($levels as $level)
442
+ {
443
+ $level_obj = pmpro_getLevel(is_numeric($level) ? abs(intval($level)) : $level); //make sure our level is in a proper format
444
+ if(empty($level_obj)){continue;} //invalid level
445
+ $found_level = false;
446
+ foreach($membership_levels as $membership_level)
447
+ {
448
+ if($membership_level->id == $level_obj->id) //found a match
449
+ {
450
+ $found_level = true;
451
+ }
452
+ }
453
+
454
+ if(is_numeric($level) and intval($level) < 0 and !$found_level) //checking for the absence of this level
455
+ {
456
+ $return = true;
457
+ }
458
+ else if($found_level) //checking for the presence of this level
459
+ {
460
+ $return = true;
461
+ }
462
+ }
463
+ }
464
+ }
465
+
466
+ $return = apply_filters("pmpro_has_membership_level", $return, $user_id, $levels);
467
+ return $return;
468
+ }
469
+
470
+ /* pmpro_changeMembershipLevel() creates or updates the membership level of the given user to the given level.
471
+ *
472
+ * $level may either be the ID or name of the desired membership_level.
473
+ * If $user_id is omitted, the value will be retrieved from $current_user.
474
+ *
475
+ * Return values:
476
+ * Success returns boolean true.
477
+ * Failure returns boolean false.
478
+ */
479
+ function pmpro_changeMembershipLevel($level, $user_id = NULL)
480
+ {
481
+ global $wpdb;
482
+ global $current_user, $pmpro_error;
483
+
484
+ if(empty($user_id))
485
+ {
486
+ $user_id = $current_user->ID;
487
+ }
488
+
489
+ if(empty($user_id))
490
+ {
491
+ $pmpro_error = __("User ID not found.", "pmpro");
492
+ return false;
493
+ }
494
+
495
+ if(empty($level)) //cancelling membership
496
+ {
497
+ $level = 0;
498
+ }
499
+ else if(is_array($level))
500
+ {
501
+ //custom level
502
+ }
503
+ else
504
+ {
505
+ $level_obj = pmpro_getLevel($level);
506
+ if(empty($level_obj))
507
+ {
508
+ $pmpro_error = __("Invalid level.", "pmpro");
509
+ return false;
510
+ }
511
+ $level = $level_obj->id;
512
+ }
513
+
514
+
515
+ //if it's a custom level, they're changing
516
+ if(!is_array($level))
517
+ {
518
+ //are they even changing?
519
+ if(pmpro_hasMembershipLevel($level, $user_id)) {
520
+ $pmpro_error = __("not changing?", "pmpro");
521
+ return false; //not changing
522
+ }
523
+ }
524
+
525
+ $old_levels = pmpro_getMembershipLevelsForUser($user_id);
526
+
527
+ $pmpro_cancel_previous_subscriptions = apply_filters("pmpro_cancel_previous_subscriptions", true);
528
+ if($pmpro_cancel_previous_subscriptions)
529
+ {
530
+ //deactivate old memberships (updates pmpro_memberships_users table)
531
+ if(!empty($old_levels))
532
+ {
533
+ foreach($old_levels as $old_level) {
534
+ $sql = "UPDATE $wpdb->pmpro_memberships_users SET `status`='inactive', `enddate`=NOW() WHERE `id`=".$old_level->subscription_id;
535
+ if(!$wpdb->query($sql))
536
+ {
537
+ $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
538
+ return false;
539
+ }
540
+ }
541
+ }
542
+
543
+ //cancel any other subscriptions they have (updates pmpro_membership_orders table)
544
+ $other_order_ids = $wpdb->get_col("SELECT id FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . $user_id . "' AND status = 'success' ORDER BY id DESC");
545
+ foreach($other_order_ids as $order_id)
546
+ {
547
+ $c_order = new MemberOrder($order_id);
548
+ $c_order->cancel();
549
+ }
550
+ }
551
+
552
+ //insert current membership
553
+ if(!empty($level)) //are we getting a new one or just cancelling the old ones
554
+ {
555
+ if(is_array($level))
556
+ {
557
+ //make sure the dates are in good formats
558
+ if($level['startdate'] != "NOW()" && $level['startdate'] != "NULL" && substr($level['startdate'], 0, 1) != "'")
559
+ $level['startdate'] = "'" . $level['startdate'] . "'";
560
+
561
+ if($level['enddate'] != "NOW()" && $level['enddate'] != "NULL" && substr($level['enddate'], 0, 1) != "'")
562
+ $level['enddate'] = "'" . $level['enddate'] . "'";
563
+
564
+ //Better support mySQL Strict Mode by passing a proper enum value for cycle_period
565
+ if ($level['cycle_period'] == '') $level['cycle_period'] = 0;
566
+
567
+ $sql = "INSERT INTO $wpdb->pmpro_memberships_users (user_id, membership_id, code_id, initial_payment, billing_amount, cycle_number, cycle_period, billing_limit, trial_amount, trial_limit, startdate, enddate)
568
+ VALUES('" . $level['user_id'] . "',
569
+ '" . $level['membership_id'] . "',
570
+ '" . intval($level['code_id']) . "',
571
+ '" . $level['initial_payment'] . "',
572
+ '" . $level['billing_amount'] . "',
573
+ '" . $level['cycle_number'] . "',
574
+ '" . $level['cycle_period'] . "',
575
+ '" . $level['billing_limit'] . "',
576
+ '" . $level['trial_amount'] . "',
577
+ '" . $level['trial_limit'] . "',
578
+ " . $level['startdate'] . ",
579
+ " . $level['enddate'] . ")";
580
+
581
+ if(!$wpdb->query($sql))
582
+ {
583
+ $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
584
+ return false;
585
+ }
586
+ }
587
+ else
588
+ {
589
+ $sql = "INSERT INTO $wpdb->pmpro_memberships_users (`membership_id`,`user_id`) VALUES ('" . $level . "','" . $user_id . "')";
590
+ if(!$wpdb->query($sql))
591
+ {
592
+ $pmpro_error = __("Error interacting with database", "pmpro") . ": ".(mysql_errno()?mysql_error():'unavailable');
593
+ return false;
594
+ }
595
+ }
596
+ }
597
+
598
+ //get level id
599
+ if(is_array($level))
600
+ $level_id = $level['membership_id']; //custom level
601
+ else
602
+ $level_id = $level; //just id
603
+
604
+ //update user data and call action
605
+ pmpro_set_current_user();
606
+ do_action("pmpro_after_change_membership_level", $level_id, $user_id); //$level is the $level_id here
607
+ return true;
608
+ }
609
+
610
+ /* pmpro_toggleMembershipCategory() creates or deletes a linking entry between the membership level and post category tables.
611
+ *
612
+ * $level may either be the ID or name of the desired membership_level.
613
+ * $category must be a valid post category ID.
614
+ *
615
+ * Return values:
616
+ * Success returns boolean true.
617
+ * Failure returns a string containing the error message.
618
+ */
619
+ function pmpro_toggleMembershipCategory( $level, $category, $value )
620
+ {
621
+ global $wpdb;
622
+ $category = intval($category);
623
+
624
+ if ( ($level = intval($level)) <= 0 )
625
+ {
626
+ $safe = addslashes($level);
627
+ if ( ($level = intval($wpdb->get_var("SELECT id FROM {$wpdb->pmpro_membership_levels} WHERE name = '$safe' LIMIT 1"))) <= 0 )
628
+ {
629
+ return __("Membership level not found.", "pmpro");
630
+ }
631
+ }
632
+
633
+ if ( $value )
634
+ {
635
+ $sql = "REPLACE INTO {$wpdb->pmpro_memberships_categories} (`membership_id`,`category_id`) VALUES ('$level','$category')";
636
+ $wpdb->query($sql);
637
+ if(mysql_errno()) return mysql_error();
638
+ }
639
+ else
640
+ {
641
+ $sql = "DELETE FROM {$wpdb->pmpro_memberships_categories} WHERE `membership_id` = '$level' AND `category_id` = '$category' LIMIT 1";
642
+ $wpdb->query($sql);
643
+ if(mysql_errno()) return mysql_error();
644
+ }
645
+
646
+ return true;
647
+ }
648
+
649
+ /* pmpro_updateMembershipCategories() ensures that all those and only those categories given
650
+ * are associated with the given membership level.
651
+ *
652
+ * $level is a valid membership level ID or name
653
+ * $categories is an array of post category IDs
654
+ *
655
+ * Return values:
656
+ * Success returns boolean true.
657
+ * Failure returns a string containing the error message.
658
+ */
659
+ function pmpro_updateMembershipCategories($level, $categories)
660
+ {
661
+ global $wpdb;
662
+
663
+ if(!is_numeric($level))
664
+ {
665
+ $level = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels WHERE name = '" . esc_sql($level) . "' LIMIT 1");
666
+ if(empty($level))
667
+ {
668
+ return __("Membership level not found.", "pmpro");
669
+ }
670
+ }
671
+
672
+ // remove all existing links...
673
+ $sqlQuery = "DELETE FROM $wpdb->pmpro_memberships_categories WHERE `membership_id` = '" . esc_sql($level) . "'";
674
+ $wpdb->query($sqlQuery);
675
+ if(mysql_errno()) return mysql_error();
676
+
677
+ // add the given links [back?] in...
678
+ foreach($categories as $cat)
679
+ {
680
+ if(is_string($r = pmpro_toggleMembershipCategory( $level, $cat, true)))
681
+ {
682
+ //uh oh, error
683
+ return $r;
684
+ }
685
+ }
686
+
687
+ //all good
688
+ return true;
689
+ }
690
+
691
+ /* pmpro_getMembershipCategories() returns the categories for a given level
692
+ *
693
+ * $level_id is a valid membership level ID
694
+ *
695
+ * Return values:
696
+ * Success returns boolean true.
697
+ * Failure returns boolean false.
698
+ */
699
+ function pmpro_getMembershipCategories($level_id)
700
+ {
701
+ global $wpdb;
702
+ $categories = $wpdb->get_results("SELECT c.category_id
703
+ FROM {$wpdb->pmpro_memberships_categories} AS c
704
+ WHERE c.membership_id = '" . $level_id . "'", ARRAY_N);
705
+
706
+ $returns = array();
707
+ if(is_array($categories))
708
+ {
709
+ foreach($categories as $cat)
710
+ {
711
+ $returns[] = $cat;
712
+ }
713
+ }
714
+ return $returns;
715
+ }
716
+
717
+ function pmpro_isAdmin($user_id = NULL)
718
+ {
719
+ global $current_user, $wpdb;
720
+ if(!$user_id)
721
+ $user_id = $current_user->ID;
722
+
723
+ if(!$user_id)
724
+ return false;
725
+
726
+ $admincap = user_can($user_id, "manage_options");
727
+ if($admincap)
728
+ return true;
729
+ else
730
+ return false;
731
+ }
732
+
733
+ function pmpro_replaceUserMeta($user_id, $meta_keys, $meta_values, $prev_values = NULL)
734
+ {
735
+ //expects all arrays for last 3 params or all strings
736
+ if(!is_array($meta_keys))
737
+ {
738
+ $meta_keys = array($meta_keys);
739
+ $meta_values = array($meta_values);
740
+ $prev_values = array($prev_values);
741
+ }
742
+
743
+ for($i = 0; $i < count($meta_values); $i++)
744
+ {
745
+ if($prev_values[$i])
746
+ {
747
+ update_user_meta($user_id, $meta_keys[$i], $meta_values[$i], $prev_values[$i]);
748
+ }
749
+ else
750
+ {
751
+ $old_value = get_user_meta($user_id, $meta_keys[$i], true);
752
+ if($old_value)
753
+ {
754
+ update_user_meta($user_id, $meta_keys[$i], $meta_values[$i], $old_value);
755
+ }
756
+ else
757
+ {
758
+ update_user_meta($user_id, $meta_keys[$i], $meta_values[$i]);
759
+ }
760
+ }
761
+ }
762
+
763
+ return $i;
764
+ }
765
+
766
+ function pmpro_getMetavalues($query)
767
+ {
768
+ global $wpdb;
769
+
770
+ $results = $wpdb->get_results($query);
771
+ foreach($results as $result)
772
+ {
773
+ $r->{$result->key} = $result->value;
774
+ }
775
+
776
+ return $r;
777
+ }
778
+
779
+ //function to return the pagination string
780
+ function pmpro_getPaginationString($page = 1, $totalitems, $limit = 15, $adjacents = 1, $targetpage = "/", $pagestring = "&pn=")
781
+ {
782
+ //defaults
783
+ if(!$adjacents) $adjacents = 1;
784
+ if(!$limit) $limit = 15;
785
+ if(!$page) $page = 1;
786
+ if(!$targetpage) $targetpage = "/";
787
+
788
+ //other vars
789
+ $prev = $page - 1; //previous page is page - 1
790
+ $next = $page + 1; //next page is page + 1
791
+ $lastpage = ceil($totalitems / $limit); //lastpage is = total items / items per page, rounded up.
792
+ $lpm1 = $lastpage - 1; //last page minus 1
793
+
794
+ /*
795
+ Now we apply our rules and draw the pagination object.
796
+ We're actually saving the code to a variable in case we want to draw it more than once.
797
+ */
798
+ $pagination = "";
799
+ if($lastpage > 1)
800
+ {
801
+ $pagination .= "<div class=\"pmpro_pagination\"";
802
+ if(!empty($margin) || !empty($padding))
803
+ {
804
+ $pagination .= " style=\"";
805
+ if($margin)
806
+ $pagination .= "margin: $margin;";
807
+ if($padding)
808
+ $pagination .= "padding: $padding;";
809
+ $pagination .= "\"";
810
+ }
811
+ $pagination .= ">";
812
+
813
+ //previous button
814
+ if ($page > 1)
815
+ $pagination .= "<a href=\"$targetpage$pagestring$prev\">&laquo; prev</a>";
816
+ else
817
+ $pagination .= "<span class=\"disabled\">&laquo; prev</span>";
818
+
819
+ //pages
820
+ if ($lastpage < 7 + ($adjacents * 2)) //not enough pages to bother breaking it up
821
+ {
822
+ for ($counter = 1; $counter <= $lastpage; $counter++)
823
+ {
824
+ if ($counter == $page)
825
+ $pagination .= "<span class=\"current\">$counter</span>";
826
+ else
827
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
828
+ }
829
+ }
830
+ elseif($lastpage >= 7 + ($adjacents * 2)) //enough pages to hide some
831
+ {
832
+ //close to beginning; only hide later pages
833
+ if($page < 1 + ($adjacents * 3))
834
+ {
835
+ for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
836
+ {
837
+ if ($counter == $page)
838
+ $pagination .= "<span class=\"current\">$counter</span>";
839
+ else
840
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
841
+ }
842
+ $pagination .= "...";
843
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lpm1 . "\">$lpm1</a>";
844
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lastpage . "\">$lastpage</a>";
845
+ }
846
+ //in middle; hide some front and some back
847
+ elseif($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2))
848
+ {
849
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "1\">1</a>";
850
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "2\">2</a>";
851
+ $pagination .= "...";
852
+ for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++)
853
+ {
854
+ if ($counter == $page)
855
+ $pagination .= "<span class=\"current\">$counter</span>";
856
+ else
857
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
858
+ }
859
+ $pagination .= "...";
860
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lpm1 . "\">$lpm1</a>";
861
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $lastpage . "\">$lastpage</a>";
862
+ }
863
+ //close to end; only hide early pages
864
+ else
865
+ {
866
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "1\">1</a>";
867
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . "2\">2</a>";
868
+ $pagination .= "...";
869
+ for ($counter = $lastpage - (1 + ($adjacents * 3)); $counter <= $lastpage; $counter++)
870
+ {
871
+ if ($counter == $page)
872
+ $pagination .= "<span class=\"current\">$counter</span>";
873
+ else
874
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $counter . "\">$counter</a>";
875
+ }
876
+ }
877
+ }
878
+
879
+ //next button
880
+ if ($page < $counter - 1)
881
+ $pagination .= "<a href=\"" . $targetpage . $pagestring . $next . "\">next &raquo;</a>";
882
+ else
883
+ $pagination .= "<span class=\"disabled\">next &raquo;</span>";
884
+ $pagination .= "</div>\n";
885
+ }
886
+
887
+ return $pagination;
888
+
889
+ }
890
+
891
+ function pmpro_calculateInitialPaymentRevenue($s = NULL, $l = NULL)
892
+ {
893
+ global $wpdb;
894
+
895
+ //if we're limiting users by search
896
+ if($s || $l)
897
+ {
898
+ $user_ids_query = "SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' ";
899
+ if($s)
900
+ $user_ids_query .= "AND (u.user_login LIKE '%$s%' OR u.user_email LIKE '%$s%' OR um.meta_value LIKE '%$s%') ";
901
+ if($l)
902
+ $user_ids_query .= "AND mu.membership_id = '$l' ";
903
+ }
904
+
905
+ //query to sum initial payments
906
+ $sqlQuery = "SELECT SUM(initial_payment) FROM $wpdb->pmpro_memberships_users WHERE `status` = 'active' ";
907
+ if(!empty($user_ids_query))
908
+ $sqlQuery .= "AND user_id IN(" . $user_ids_query . ") ";
909
+
910
+ $total = $wpdb->get_var($sqlQuery);
911
+
912
+ return (double)$total;
913
+ }
914
+
915
+ function pmpro_calculateRecurringRevenue($s, $l)
916
+ {
917
+ global $wpdb;
918
+
919
+ //if we're limiting users by search
920
+ if($s || $l)
921
+ {
922
+ $user_ids_query = "AND user_id IN(SELECT u.ID FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id LEFT JOIN $wpdb->pmpro_memberships_users mu ON u.ID = mu.user_id WHERE mu.status = 'active' ";
923
+ if($s)
924
+ $user_ids_query .= "AND (u.user_login LIKE '%$s%' OR u.user_email LIKE '%$s%' OR um.meta_value LIKE '%$s%') ";
925
+ if($l)
926
+ $user_ids_query .= "AND mu.membership_id = '$l' ";
927
+ $user_ids_query .= ")";
928
+ }
929
+ else
930
+ $user_ids_query = "";
931
+
932
+ //4 queries to get annual earnings for each cycle period. currently ignoring trial periods and billing limits.
933
+ $sqlQuery = "
934
+ SELECT SUM((12/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Month' AND cycle_number <> 12 $user_ids_query
935
+ UNION
936
+ SELECT SUM((365/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Day' AND cycle_number <> 365 $user_ids_query
937
+ UNION
938
+ SELECT SUM((52/cycle_number)*billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Week' AND cycle_number <> 52 $user_ids_query
939
+ UNION
940
+ SELECT SUM(billing_amount) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND cycle_period = 'Year' $user_ids_query
941
+ ";
942
+
943
+ $annual_revenues = $wpdb->get_col($sqlQuery);
944
+
945
+ $total = 0;
946
+ foreach($annual_revenues as $r)
947
+ {
948
+ $total += $r;
949
+ }
950
+
951
+ return $total;
952
+ }
953
+
954
+ function pmpro_generateUsername($firstname = "", $lastname = "", $email = "")
955
+ {
956
+ global $wpdb;
957
+
958
+ //try first initial + last name, firstname, lastname
959
+ $firstname = preg_replace("/[^A-Za-z]/", "", $firstname);
960
+ $lastname = preg_replace("/[^A-Za-z]/", "", $lastname);
961
+ if($firstname && $lastname)
962
+ {
963
+ $username = substr($firstname, 0, 1) . $lastname;
964
+ }
965
+ elseif($firstname)
966
+ {
967
+ $username = $firstname;
968
+ }
969
+ elseif($lastname)
970
+ {
971
+ $username = $lastname;
972
+ }
973
+
974
+ //is it taken?
975
+ $taken = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . $username . "' LIMIT 1");
976
+
977
+ if(!$taken)
978
+ return $username;
979
+
980
+ //try the beginning of the email address
981
+ $emailparts = explode("@", "email");
982
+ if(is_array($emailparts))
983
+ $email = preg_replace("/[^A-Za-z]/", "", $emailparts[0]);
984
+
985
+ if($email)
986
+ {
987
+ $username = $email;
988
+ }
989
+
990
+ //is this taken? if not, add numbers until it works
991
+ $taken = true;
992
+ $count = 0;
993
+ while($taken)
994
+ {
995
+ //add a # to the end
996
+ if($count)
997
+ {
998
+ $username = preg_replace("/[0-9]/", "", $username) . $count;
999
+ }
1000
+
1001
+ //taken?
1002
+ $taken = $wpdb->get_var("SELECT user_login FROM $wpdb->users WHERE user_login = '" . $username . "' LIMIT 1");
1003
+
1004
+ //increment the number
1005
+ $count++;
1006
+ }
1007
+
1008
+ //must have a good username now
1009
+ return $username;
1010
+ }
1011
+
1012
+ //get a new random code for discount codes
1013
+ function pmpro_getDiscountCode()
1014
+ {
1015
+ global $wpdb;
1016
+
1017
+ while(empty($code))
1018
+ {
1019
+ $scramble = md5(AUTH_KEY . time() . SECURE_AUTH_KEY);
1020
+ $code = substr($scramble, 0, 10);
1021
+ $check = $wpdb->get_var("SELECT code FROM $wpdb->pmpro_discount_codes WHERE code = '$code' LIMIT 1");
1022
+ if($check || is_numeric($code))
1023
+ $code = NULL;
1024
+ }
1025
+
1026
+ return strtoupper($code);
1027
+ }
1028
+
1029
+ //is a discount code valid
1030
+ function pmpro_checkDiscountCode($code, $level_id = NULL, $return_errors = false)
1031
+ {
1032
+ global $wpdb;
1033
+
1034
+ //no code, no code
1035
+ if(empty($code))
1036
+ {
1037
+ if($return_errors)
1038
+ return array(false, "No code was given to check.");
1039
+ else
1040
+ return false;
1041
+ }
1042
+
1043
+ //get code from db
1044
+ $dbcode = $wpdb->get_row("SELECT *, UNIX_TIMESTAMP(starts) as starts, UNIX_TIMESTAMP(expires) as expires FROM $wpdb->pmpro_discount_codes WHERE code ='" . $code . "' LIMIT 1");
1045
+
1046
+ //did we find it?
1047
+ if(empty($dbcode->id))
1048
+ {
1049
+ if($return_errors)
1050
+ return array(false, __("The discount code could not be found.", "pmpro"));
1051
+ else
1052
+ return false;
1053
+ }
1054
+
1055
+ //fix the date timestamps
1056
+ $dbcode->starts = strtotime(date("m/d/Y", $dbcode->starts));
1057
+ $dbcode->expires = strtotime(date("m/d/Y", $dbcode->expires));
1058
+
1059
+ //today
1060
+ $today = strtotime(date("m/d/Y 00:00:00"));
1061
+
1062
+ //has this code started yet?
1063
+ if(!empty($dbcode->starts) && $dbcode->starts > $today)
1064
+ {
1065
+ if($return_errors)
1066
+ return array(false, sprintf(__("This discount code goes into effect on %s.", "pmpro"), date(get_option('date_format'), $dbcode->starts)));
1067
+ else
1068
+ return false;
1069
+ }
1070
+
1071
+ //has this code expired?
1072
+ if(!empty($dbcode->expires) && $dbcode->expires < $today)
1073
+ {
1074
+ if($return_errors)
1075
+ return array(false, sprintf(__("This discount code expired on %s.", "pmpro"), date(get_option('date_format'), $dbcode->expires)));
1076
+ else
1077
+ return false;
1078
+ }
1079
+
1080
+ //have we run out of uses?
1081
+ if($dbcode->uses > 0)
1082
+ {
1083
+ $used = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->pmpro_discount_codes_uses WHERE code_id = '" . $dbcode->id . "'");
1084
+ if($used >= $dbcode->uses)
1085
+ {
1086
+ if($return_errors)
1087
+ return array(false, __("This discount code is no longer valid.", "pmpro"));
1088
+ else
1089
+ return false;
1090
+ }
1091
+ }
1092
+
1093
+ //if a level was passed check if this code applies
1094
+ $pmpro_check_discount_code_levels = apply_filters("pmpro_check_discount_code_levels", true, $dbcode->id);
1095
+ if(!empty($level_id) && $pmpro_check_discount_code_levels)
1096
+ {
1097
+ $code_level = $wpdb->get_row("SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id WHERE cl.code_id = '" . $dbcode->id . "' AND cl.level_id = '" . $level_id . "' LIMIT 1");
1098
+
1099
+ if(empty($code_level))
1100
+ {
1101
+ if(!empty($return_errors))
1102
+ return array(false, __("This discount code does not apply to this membership level.", "pmpro"));
1103
+ else
1104
+ return false;
1105
+ }
1106
+ }
1107
+
1108
+ //guess we're all good
1109
+ if(!empty($return_errors))
1110
+ return array(true, __("This discount code is okay.", "pmpro"));
1111
+ else
1112
+ return true;
1113
+ }
1114
+
1115
+ function pmpro_no_quotes($s, $quotes = array("'", '"'))
1116
+ {
1117
+ return str_replace($quotes, "", $s);
1118
+ }
1119
+
1120
+ //from: http://www.php.net/manual/en/function.implode.php#86845
1121
+ function pmpro_implodeToEnglish($array)
1122
+ {
1123
+ // sanity check
1124
+ if (!$array || !count ($array))
1125
+ return '';
1126
+
1127
+ // get last element
1128
+ $last = array_pop ($array);
1129
+
1130
+ // if it was the only element - return it
1131
+ if (!count ($array))
1132
+ return $last;
1133
+
1134
+ return implode (', ', $array).' ' . _x('and', 'Used in generation of a list. E.g. a, b, c (AND) d.', 'pmpro') . ' '.$last;
1135
+ }
1136
+
1137
+ //from yoast wordpress seo
1138
+ function pmpro_text_limit( $text, $limit, $finish = '&hellip;')
1139
+ {
1140
+ if( strlen( $text ) > $limit ) {
1141
+ $text = substr( $text, 0, $limit );
1142
+ $text = substr( $text, 0, - ( strlen( strrchr( $text,' ') ) ) );
1143
+ $text .= $finish;
1144
+ }
1145
+ return $text;
1146
+ }
1147
+
1148
+ /* pmpro_getMembershipLevelForUser() returns the first active membership level for a user
1149
+ *
1150
+ * If $user_id is omitted, the value will be retrieved from $current_user.
1151
+ *
1152
+ * Return values:
1153
+ * Success returns the level object.
1154
+ * Failure returns false.
1155
+ */
1156
+ function pmpro_getMembershipLevelForUser($user_id = NULL)
1157
+ {
1158
+ if(empty($user_id))
1159
+ {
1160
+ global $current_user;
1161
+ $user_id = $current_user->ID;
1162
+ }
1163
+
1164
+ if(empty($user_id))
1165
+ {
1166
+ return false;
1167
+ }
1168
+
1169
+ global $all_membership_levels;
1170
+
1171
+ if(isset($all_membership_levels[$user_id]))
1172
+ {
1173
+ return $all_membership_levels[$user_id];
1174
+ }
1175
+ else
1176
+ {
1177
+ global $wpdb;
1178
+ $all_membership_levels[$user_id] = $wpdb->get_row("SELECT
1179
+ l.id AS ID,
1180
+ l.id as id,
1181
+ mu.id as subscription_id,
1182
+ l.name AS name,
1183
+ l.description,
1184
+ l.expiration_number,
1185
+ l.expiration_period,
1186
+ mu.initial_payment,
1187
+ mu.billing_amount,
1188
+ mu.cycle_number,
1189
+ mu.cycle_period,
1190
+ mu.billing_limit,
1191
+ mu.trial_amount,
1192
+ mu.trial_limit,
1193
+ mu.code_id as code_id,
1194
+ UNIX_TIMESTAMP(startdate) as startdate,
1195
+ UNIX_TIMESTAMP(enddate) as enddate
1196
+ FROM {$wpdb->pmpro_membership_levels} AS l
1197
+ JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
1198
+ WHERE mu.user_id = $user_id AND mu.status = 'active'
1199
+ LIMIT 1");
1200
+ return $all_membership_levels[$user_id];
1201
+ }
1202
+ }
1203
+
1204
+ /* pmpro_getMembershipLevelsForUser() returns the membership levels for a user
1205
+ *
1206
+ * If $user_id is omitted, the value will be retrieved from $current_user.
1207
+ * By default it only includes actvie memberships.
1208
+ *
1209
+ * Return values:
1210
+ * Success returns an array of level objects.
1211
+ * Failure returns false.
1212
+ */
1213
+ function pmpro_getMembershipLevelsForUser($user_id = NULL, $include_inactive = false)
1214
+ {
1215
+ if(empty($user_id))
1216
+ {
1217
+ global $current_user;
1218
+ $user_id = $current_user->ID;
1219
+ }
1220
+
1221
+ if(empty($user_id))
1222
+ {
1223
+ return false;
1224
+ }
1225
+
1226
+ global $wpdb;
1227
+ return $wpdb->get_results("SELECT
1228
+ l.id AS ID,
1229
+ l.id as id,
1230
+ mu.id as subscription_id,
1231
+ l.name,
1232
+ l.description,
1233
+ l.expiration_number,
1234
+ l.expiration_period,
1235
+ mu.initial_payment,
1236
+ mu.billing_amount,
1237
+ mu.cycle_number,
1238
+ mu.cycle_period,
1239
+ mu.billing_limit,
1240
+ mu.trial_amount,
1241
+ mu.trial_limit,
1242
+ mu.code_id as code_id,
1243
+ UNIX_TIMESTAMP(startdate) as startdate,
1244
+ UNIX_TIMESTAMP(enddate) as enddate
1245
+ FROM {$wpdb->pmpro_membership_levels} AS l
1246
+ JOIN {$wpdb->pmpro_memberships_users} AS mu ON (l.id = mu.membership_id)
1247
+ WHERE mu.user_id = $user_id".($include_inactive?"":" AND mu.status = 'active'"));
1248
+ }
1249
+
1250
+ /* pmpro_getLevel() returns the level object for a level
1251
+ *
1252
+ * $level may be the level id or name
1253
+ *
1254
+ * Return values:
1255
+ * Success returns the level object.
1256
+ * Failure returns false.
1257
+ */
1258
+ function pmpro_getLevel($level)
1259
+ {
1260
+ global $pmpro_levels;
1261
+
1262
+ if(is_object($level) && !empty($level->id))
1263
+ $level = $level->id;
1264
+
1265
+ //was a name passed? (Todo: make sure level names have at least one non-numeric character.
1266
+ if(is_numeric($level))
1267
+ {
1268
+ $level_id = intval($level);
1269
+ if(isset($pmpro_levels[$level_id]))
1270
+ {
1271
+ return $pmpro_levels[$level_id];
1272
+ }
1273
+ else
1274
+ {
1275
+ global $wpdb;
1276
+ $pmpro_levels[$level_id] = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE id = '" . $level_id . "' LIMIT 1");
1277
+ return $pmpro_levels[$level_id];
1278
+ }
1279
+ }
1280
+ else
1281
+ {
1282
+ global $wpdb;
1283
+ $level_obj = $wpdb->get_row("SELECT * FROM $wpdb->pmpro_membership_levels WHERE name = '" . $level . "' LIMIT 1");
1284
+ $level_id = $level->ID;
1285
+ $pmpro_levels[$level_id] = $level_obj;
1286
+ return $pmpro_levels[$level_id];
1287
+ }
1288
+ }
1289
+
1290
+ /*
1291
+ Function to populate pmpro_levels with all levels. We query the DB every time just to be sure we have the latest.
1292
+ This should be called if you want to be sure you get all levels as $pmpro_levels may only have a subset of levels.
1293
+ */
1294
+ function pmpro_getAllLevels($include_hidden = false)
1295
+ {
1296
+ global $pmpro_levels, $wpdb;
1297
+
1298
+ //build query
1299
+ $sqlQuery = "SELECT * FROM $wpdb->pmpro_membership_levels ";
1300
+ if(!$include_hidden)
1301
+ $sqlQuery .= " WHERE allow_signups = 1 ORDER BY id";
1302
+
1303
+ //get levels from the DB
1304
+ $raw_levels = $wpdb->get_results($sqlQuery);
1305
+
1306
+ //lets put them into an array where the key is the id of the level
1307
+ $pmpro_levels = array();
1308
+ foreach($raw_levels as $raw_level)
1309
+ {
1310
+ $pmpro_levels[$raw_level->id] = $raw_level;
1311
+ }
1312
+
1313
+ return $pmpro_levels;
1314
+ }
1315
+
1316
+ function pmpro_getCheckoutButton($level_id, $button_text = NULL, $classes = NULL)
1317
+ {
1318
+ if(empty($button_text))
1319
+ $button_text = _x("Sign Up for !!name!! Now", "Do not translate !!name!!", "pmpro");
1320
+
1321
+ if(empty($classes))
1322
+ $classes = "btn btn-primary";
1323
+
1324
+ if(empty($level_id))
1325
+ $r = __("Please specify a level id.", "pmpro");
1326
+ else
1327
+ {
1328
+ //get level
1329
+ $level = pmpro_getLevel($level_id);
1330
+
1331
+ //replace vars
1332
+ $replacements = array(
1333
+ "!!id!!" => $level->id,
1334
+ "!!name!!" => $level->name,
1335
+ "!!description!!" => $level->description,
1336
+ "!!confirmation!!" => $level->confirmation,
1337
+ "!!initial_payment!!" => $level->initial_payment,
1338
+ "!!billing_amount!!" => $level->billing_amount,
1339
+ "!!cycle_number!!" => $level->cycle_number,
1340
+ "!!cycle_period!!" => $level->cycle_period,
1341
+ "!!billing_limit!!" => $level->billing_limit,
1342
+ "!!trial_amount!!" => $level->trial_amount,
1343
+ "!!trial_limit!!" => $level->trial_limit,
1344
+ "!!expiration_number!!" => $level->expiration_number,
1345
+ "!!expiration_period!!" => $level->expiration_period
1346
+ );
1347
+ $button_text = str_replace(array_keys($replacements), $replacements, $button_text);
1348
+
1349
+ //button text
1350
+ $r = "<a href=\"" . pmpro_url("checkout", "?level=" . $level_id) . "\" class=\"" . $classes . "\">" . $button_text . "</a>";
1351
+ }
1352
+ return $r;
1353
+ }
1354
+
1355
+ /**
1356
+ * Get the "domain" from a URL. By domain, we mean the host name, minus any subdomains. So just the domain and TLD.
1357
+ *
1358
+ * @param string $url The URL to parse. (generally pass site_url() in WP)
1359
+ * @return string The domain.
1360
+ */
1361
+ function pmpro_getDomainFromURL($url = NULL)
1362
+ {
1363
+ $domainparts = parse_url($url);
1364
+ $domainparts = explode(".", $domainparts['host']);
1365
+ if(count($domainparts) > 1)
1366
+ {
1367
+ //check for ips
1368
+ $isip = true;
1369
+ foreach($domainparts as $part)
1370
+ {
1371
+ if(!is_numeric($part))
1372
+ {
1373
+ $isip = false;
1374
+ break;
1375
+ }
1376
+ }
1377
+
1378
+ if($isip)
1379
+ {
1380
+ //ip, e.g. 127.1.1.1
1381
+ $domain = implode(".", $domainparts);
1382
+ }
1383
+ else
1384
+ {
1385
+ //www.something.com, etc.
1386
+ $domain = $domainparts[count($domainparts)-2] . "." . $domainparts[count($domainparts)-1];
1387
+ }
1388
+ }
1389
+ else
1390
+ {
1391
+ //localhost or another single word domain
1392
+ $domain = $domainparts[0];
1393
+ }
1394
+
1395
+ return $domain;
1396
+ }
1397
+
1398
+ /*
1399
+ Get a member's start date... either in general or for a specific level_id.
1400
+ */
1401
+ if(!function_exists("pmpro_getMemberStartdate"))
1402
+ {
1403
+ function pmpro_getMemberStartdate($user_id = NULL, $level_id = 0)
1404
+ {
1405
+ if(empty($user_id))
1406
+ {
1407
+ global $current_user;
1408
+ $user_id = $current_user->ID;
1409
+ }
1410
+
1411
+ global $pmpro_startdates; //for cache
1412
+ if(empty($pmpro_startdates[$user_id][$level_id]))
1413
+ {
1414
+ global $wpdb;
1415
+
1416
+ if(!empty($level_id))
1417
+ $sqlQuery = "SELECT UNIX_TIMESTAMP(startdate) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND membership_id IN(" . esc_sql($level_id) . ") AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
1418
+ else
1419
+ $sqlQuery = "SELECT UNIX_TIMESTAMP(startdate) FROM $wpdb->pmpro_memberships_users WHERE status = 'active' AND user_id = '" . $user_id . "' ORDER BY id LIMIT 1";
1420
+
1421
+ $startdate = $wpdb->get_var($sqlQuery);
1422
+
1423
+ $pmpro_startdates[$user_id][$level_id] = $startdate;
1424
+ }
1425
+
1426
+ return $pmpro_startdates[$user_id][$level_id];
1427
+ }
1428
+ }
1429
+
1430
+ /*
1431
+ How long has this member been a member
1432
+ */
1433
+ if(!function_exists("pmpro_getMemberDays"))
1434
+ {
1435
+ function pmpro_getMemberDays($user_id = NULL, $level_id = 0)
1436
+ {
1437
+ if(empty($user_id))
1438
+ {
1439
+ global $current_user;
1440
+ $user_id = $current_user->ID;
1441
+ }
1442
+
1443
+ global $pmpro_member_days;
1444
+ if(empty($pmpro_member_days[$user_id][$level_id]))
1445
+ {
1446
+ $startdate = pmpro_getMemberStartdate($user_id, $level_id);
1447
+
1448
+ $now = time();
1449
+ $days = ($now - $startdate)/3600/24;
1450
+
1451
+ $pmpro_member_days[$user_id][$level_id] = $days;
1452
+ }
1453
+
1454
+ return $pmpro_member_days[$user_id][$level_id];
1455
+ }
1456
+ }
1457
+
1458
+ //the start of a message handling script
1459
+ function pmpro_setMessage($message, $type, $force = false)
1460
+ {
1461
+ global $pmpro_msg, $pmpro_msgt;
1462
+
1463
+ //for now, we only show the first message generated
1464
+ if($force || empty($pmpro_msg))
1465
+ {
1466
+ $pmpro_msg = $message;
1467
+ $pmpro_msgt = $type;
1468
+ }
1469
+ }
1470
+
1471
+ //used in class definitions for input fields to see if there was an error
1472
+ function pmpro_getClassForField($field)
1473
+ {
1474
+ global $pmpro_error_fields, $pmpro_required_billing_fields, $pmpro_required_user_fields;
1475
+ $classes = array();
1476
+
1477
+ //error on this field?
1478
+ if(in_array($field, $pmpro_error_fields))
1479
+ {
1480
+ $classes[] = "pmpro_error";
1481
+ }
1482
+
1483
+ $required_fields = array_merge(array_keys($pmpro_required_billing_fields), array_keys($pmpro_required_user_fields));
1484
+
1485
+ //required?
1486
+ if(in_array($field, $required_fields))
1487
+ {
1488
+ $classes[] = "pmpro_required";
1489
+ }
1490
+
1491
+ $classes = apply_filters("pmpro_field_classes", $classes, $field);
1492
+
1493
+ if(!empty($classes))
1494
+ return implode(" ", $classes);
1495
+ else
1496
+ return "";
1497
+ }
1498
+
1499
+ //get a var from $_GET or $_POST
1500
+ function pmpro_getParam($index, $method = "REQUEST", $default = "")
1501
+ {
1502
+ if($method == "REQUEST")
1503
+ {
1504
+ if(!empty($_REQUEST[$index]))
1505
+ return $_REQUEST[$index];
1506
+ }
1507
+ elseif($method == "POST")
1508
+ {
1509
+ if(!empty($_POST[$index]))
1510
+ return $_POST[$index];
1511
+ }
1512
+ elseif($method == "GET")
1513
+ {
1514
+ if(!empty($_GET[$index]))
1515
+ return $_GET[$index];
1516
+ }
1517
+
1518
+ return $default;
1519
+ }
1520
+
1521
+ /*
1522
+ Checks if all required settings are set.
1523
+ */
1524
+ function pmpro_is_ready()
1525
+ {
1526
+ global $wpdb, $pmpro_pages, $pmpro_level_ready, $pmpro_gateway_ready, $pmpro_pages_ready;
1527
+
1528
+ //check if there is at least one level
1529
+ $pmpro_level_ready = (bool)$wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels LIMIT 1");
1530
+
1531
+ //check if the gateway settings are good. first check if it's needed (is there paid membership level)
1532
+ $paid_membership_level = $wpdb->get_var("SELECT id FROM $wpdb->pmpro_membership_levels WHERE allow_signups = 1 AND (initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0) LIMIT 1");
1533
+ $paid_user_subscription = $wpdb->get_var("SELECT user_id FROM $wpdb->pmpro_memberships_users WHERE initial_payment > 0 OR billing_amount > 0 OR trial_amount > 0 LIMIT 1");
1534
+
1535
+ if(empty($paid_membership_level) && empty($paid_user_subscription))
1536
+ {
1537
+ //no paid membership level now or attached to a user. we don't need the gateway setup
1538
+ $pmpro_gateway_ready = true;
1539
+ }
1540
+ else
1541
+ {
1542
+ $gateway = pmpro_getOption("gateway");
1543
+ if($gateway == "authorizenet")
1544
+ {
1545
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("loginname") && pmpro_getOption("transactionkey"))
1546
+ $pmpro_gateway_ready = true;
1547
+ else
1548
+ $pmpro_gateway_ready = false;
1549
+ }
1550
+ elseif($gateway == "paypal" || $gateway == "paypalexpress")
1551
+ {
1552
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("gateway_email") && pmpro_getOption("apiusername") && pmpro_getOption("apipassword") && pmpro_getOption("apisignature"))
1553
+ $pmpro_gateway_ready = true;
1554
+ else
1555
+ $pmpro_gateway_ready = false;
1556
+ }
1557
+ elseif($gateway == "paypalstandard")
1558
+ {
1559
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("gateway_email"))
1560
+ $pmpro_gateway_ready = true;
1561
+ else
1562
+ $pmpro_gateway_ready = false;
1563
+ }
1564
+ elseif($gateway == "payflowpro")
1565
+ {
1566
+ if(pmpro_getOption("payflow_partner") && pmpro_getOption("payflow_vendor") && pmpro_getOption("payflow_user") && pmpro_getOption("payflow_pwd"))
1567
+ $pmpro_gateway_ready = true;
1568
+ else
1569
+ $pmpro_gateway_ready = false;
1570
+ }
1571
+ elseif($gateway == "stripe")
1572
+ {
1573
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("stripe_secretkey") && pmpro_getOption("stripe_publishablekey"))
1574
+ $pmpro_gateway_ready = true;
1575
+ else
1576
+ $pmpro_gateway_ready = false;
1577
+ }
1578
+ elseif($gateway == "braintree")
1579
+ {
1580
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("braintree_merchantid") && pmpro_getOption("braintree_publickey") && pmpro_getOption("braintree_privatekey"))
1581
+ $pmpro_gateway_ready = true;
1582
+ else
1583
+ $pmpro_gateway_ready = false;
1584
+ }
1585
+ elseif($gateway == "twocheckout")
1586
+ {
1587
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("twocheckout_apiusername") && pmpro_getOption("twocheckout_apipassword"))
1588
+ $pmpro_gateway_ready = true;
1589
+ else
1590
+ $pmpro_gateway_ready = false;
1591
+ }
1592
+ elseif($gateway == "cybersource")
1593
+ {
1594
+ if(pmpro_getOption("gateway_environment") && pmpro_getOption("cybersource_merchantid") && pmpro_getOption("cybersource_securitykey"))
1595
+ $pmpro_gateway_ready = true;
1596
+ else
1597
+ $pmpro_gateway_ready = false;
1598
+ }
1599
+ else
1600
+ {
1601
+ $pmpro_gateway_ready = false;
1602
+ }
1603
+ }
1604
+
1605
+ //check if we have all pages
1606
+ if($pmpro_pages["account"] &&
1607
+ $pmpro_pages["billing"] &&
1608
+ $pmpro_pages["cancel"] &&
1609
+ $pmpro_pages["checkout"] &&
1610
+ $pmpro_pages["confirmation"] &&
1611
+ $pmpro_pages["invoice"] &&
1612
+ $pmpro_pages["levels"])
1613
+ $pmpro_pages_ready = true;
1614
+ else
1615
+ $pmpro_pages_ready = false;
1616
+
1617
+ //now check both
1618
+ if($pmpro_gateway_ready && $pmpro_pages_ready)
1619
+ return true;
1620
+ else
1621
+ return false;
1622
+ }
includes/https.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Code related to HTTPS/SSL
4
+ */
5
+
6
+ //this function checks if we have set the $isapage variable, and if so prevents WP from sending a 404
7
+ function pmpro_status_filter($s)
8
+ {
9
+ global $isapage;
10
+ if($isapage && strpos($s, "404"))
11
+ return false; //don't send the 404
12
+ else
13
+ return $s;
14
+ }
15
+
16
+ //filters links/etc to add HTTPS to URL if needed
17
+ function pmpro_https_filter($s)
18
+ {
19
+ global $besecure;
20
+ $besecure = apply_filters('pmpro_besecure', $besecure);
21
+
22
+ if($besecure || is_ssl())
23
+ return str_replace("http:", "https:", $s);
24
+ else
25
+ return str_replace("https:", "http:", $s);
26
+ }
27
+ add_filter('status_header', 'pmpro_status_filter');
28
+ add_filter('bloginfo_url', 'pmpro_https_filter');
29
+ add_filter('wp_list_pages', 'pmpro_https_filter');
30
+ add_filter('option_home', 'pmpro_https_filter');
31
+ add_filter('option_siteurl', 'pmpro_https_filter');
32
+ add_filter('logout_url', 'pmpro_https_filter');
33
+ add_filter('login_url', 'pmpro_https_filter');
34
+ add_filter('home_url', 'pmpro_https_filter');
35
+
36
+ //this function sets the besecure global which may be used in early code
37
+ /*
38
+ function pmpro_besecure_set()
39
+ {
40
+ global $besecure;
41
+ if(force_ssl_admin() || force_ssl_login() || is_ssl())
42
+ $besecure = true;
43
+
44
+ $besecure = apply_filters("pmpro_besecure", $besecure);
45
+ }
46
+ add_action('init', 'pmpro_besecure_set', 2);
47
+ */
48
+
49
+ //this function updates the besecure global with post data and redirects if needed
50
+ function pmpro_besecure()
51
+ {
52
+ global $besecure, $post;
53
+
54
+ //check the post option
55
+ if(!empty($post->ID) && !$besecure)
56
+ $besecure = get_post_meta($post->ID, "besecure", true);
57
+
58
+ //if forcing ssl on admin, be secure in admin and login page
59
+ if(!$besecure && force_ssl_admin() && (is_admin() || pmpro_is_login_page()))
60
+ $besecure = true;
61
+
62
+ //if forcing ssl on login, be secure on the login page
63
+ if(!$besecure && force_ssl_login() && pmpro_is_login_page())
64
+ $besecure = true;
65
+
66
+ $besecure = apply_filters("pmpro_besecure", $besecure);
67
+
68
+ if($besecure && (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == "off" || $_SERVER['HTTPS'] == "false"))
69
+ {
70
+ //need to be secure
71
+ wp_redirect("https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
72
+ exit;
73
+ }
74
+ elseif(!$besecure && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "false")
75
+ {
76
+ //don't need to be secure
77
+ wp_redirect("http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
78
+ exit;
79
+ }
80
+ }
81
+ add_action('wp', 'pmpro_besecure', 2);
82
+ add_action('login_init', 'pmpro_besecure', 2);
83
+
84
+ //If the site URL starts with https:, then force SSL/besecure to true. (Added 1.5.2)
85
+ function pmpro_check_site_url_for_https($besecure)
86
+ {
87
+ global $wpdb, $pmpro_siteurl;
88
+
89
+ //need to get this from the database because we filter get_option
90
+ if(empty($pmpro_siteurl))
91
+ $pmpro_siteurl = $wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'siteurl' LIMIT 1");
92
+
93
+ //entire site is over https?
94
+ if(strpos($pmpro_siteurl, "https:") !== false)
95
+ $besecure = true;
96
+
97
+ return $besecure;
98
+ }
99
+ add_filter("pmpro_besecure", "pmpro_check_site_url_for_https");
100
+
101
+ //capturing case where a user links to https admin without admin over https
102
+ function pmpro_admin_https_handler()
103
+ {
104
+ if(!empty($_SERVER['HTTPS']))
105
+ {
106
+ if($_SERVER['HTTPS'] && $_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "false" && is_admin())
107
+ {
108
+ if(substr(get_option("siteurl"), 0, 5) == "http:" && !force_ssl_admin())
109
+ {
110
+ //need to redirect to non https
111
+ wp_redirect("http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
112
+ exit;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ add_action('init', 'pmpro_admin_https_handler');
118
+
119
+ /*
120
+ This code is for the "nuke" option to make URLs secure on secure pages.
121
+ */
122
+ function pmpro_NuclearHTTPS()
123
+ {
124
+ //did they choose the option?
125
+ $nuking = pmpro_getOption("nuclear_HTTPS");
126
+ if(!empty($nuking))
127
+ {
128
+ ob_start("pmpro_replaceURLsInBuffer");
129
+ }
130
+ }
131
+ add_action("init", "pmpro_NuclearHTTPS");
132
+
133
+ function pmpro_replaceURLsInBuffer($buffer)
134
+ {
135
+ global $besecure;
136
+
137
+ //only swap URLs if this page is secure
138
+ if($besecure)
139
+ {
140
+ /*
141
+ okay swap out all links like these:
142
+ * http://domain.com
143
+ * http://anysubdomain.domain.com
144
+ * http://any.number.of.sub.domains.domain.com
145
+ */
146
+ $buffer = preg_replace("/http\:\/\/([a-zA-Z0-9\.\-]*" . str_replace(".", "\.", PMPRO_DOMAIN) . ")/i", "https://$1", $buffer);
147
+ }
148
+
149
+ return $buffer;
150
+ }
includes/init.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Code that runs on the init, set_current_user, or wp hooks to setup PMPro
4
+ */
5
+ //init code
6
+ function pmpro_init()
7
+ {
8
+ require_once(PMPRO_DIR . "/includes/countries.php");
9
+ require_once(PMPRO_DIR . "/includes/states.php");
10
+ require_once(PMPRO_DIR . "/includes/currencies.php");
11
+
12
+ wp_enqueue_script('ssmemberships_js', plugins_url('js/paid-memberships-pro.js',dirname(__FILE__) ), array('jquery'));
13
+
14
+ if(is_admin())
15
+ {
16
+ if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/admin.css"))
17
+ $admin_css = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/admin.css";
18
+ elseif(file_exists(get_template_directory() . "/paid-memberships-pro/admin.css"))
19
+ $admin_css = get_template_directory_uri() . "/paid-memberships-pro/admin.css";
20
+ else
21
+ $admin_css = plugins_url('css/admin.css',dirname(__FILE__) );
22
+ wp_enqueue_style('pmpro_admin', $admin_css, array(), PMPRO_VERSION, "screen");
23
+ }
24
+ else
25
+ {
26
+ if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/frontend.css"))
27
+ $frontend_css = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/frontend.css";
28
+ elseif(file_exists(get_template_directory() . "/paid-memberships-pro/frontend.css"))
29
+ $frontend_css = get_template_directory_uri() . "/paid-memberships-pro/frontend.css";
30
+ else
31
+ $frontend_css = plugins_url('css/frontend.css',dirname(__FILE__) );
32
+ wp_enqueue_style('pmpro_frontend', $frontend_css, array(), PMPRO_VERSION, "screen");
33
+
34
+ if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/css/print.css"))
35
+ $print_css = get_stylesheet_directory_uri() . "/paid-memberships-pro/css/print.css";
36
+ elseif(file_exists(get_template_directory() . "/paid-memberships-pro/print.css"))
37
+ $print_css = get_template_directory_uri() . "/paid-memberships-pro/print.css";
38
+ else
39
+ $print_css = plugins_url('css/print.css',dirname(__FILE__) );
40
+ wp_enqueue_style('pmpro_print', $print_css, array(), PMPRO_VERSION, "print");
41
+ }
42
+
43
+ global $pmpro_pages, $pmpro_ready, $pmpro_currency, $pmpro_currency_symbol;
44
+ $pmpro_pages = array();
45
+ $pmpro_pages["account"] = pmpro_getOption("account_page_id");
46
+ $pmpro_pages["billing"] = pmpro_getOption("billing_page_id");
47
+ $pmpro_pages["cancel"] = pmpro_getOption("cancel_page_id");
48
+ $pmpro_pages["checkout"] = pmpro_getOption("checkout_page_id");
49
+ $pmpro_pages["confirmation"] = pmpro_getOption("confirmation_page_id");
50
+ $pmpro_pages["invoice"] = pmpro_getOption("invoice_page_id");
51
+ $pmpro_pages["levels"] = pmpro_getOption("levels_page_id");
52
+
53
+ $pmpro_ready = pmpro_is_ready();
54
+
55
+ //set currency
56
+ $pmpro_currency = pmpro_getOption("currency");
57
+ if(!$pmpro_currency)
58
+ {
59
+ global $pmpro_default_currency;
60
+ $pmpro_currency = $pmpro_default_currency;
61
+ }
62
+
63
+ //figure out what symbol to show for currency
64
+ if(in_array($pmpro_currency, array("USD", "AUD", "BRL", "CAD", "HKD", "MXN", "NZD", "SGD")))
65
+ $pmpro_currency_symbol = "&#36;";
66
+ elseif($pmpro_currency == "EUR")
67
+ $pmpro_currency_symbol = "&euro;";
68
+ elseif($pmpro_currency == "GBP")
69
+ $pmpro_currency_symbol = "&pound;";
70
+ elseif($pmpro_currency == "JPY")
71
+ $pmpro_currency_symbol = "&yen;";
72
+ else
73
+ $pmpro_currency_symbol = $pmpro_currency . " "; //just use the code
74
+ }
75
+ add_action("init", "pmpro_init");
76
+
77
+ //this code runs after $post is set, but before template output
78
+ function pmpro_wp()
79
+ {
80
+ if(!is_admin())
81
+ {
82
+ global $post, $pmpro_pages, $pmpro_page_name, $pmpro_page_id, $pmpro_body_classes;
83
+
84
+ //run the appropriate preheader function
85
+ foreach($pmpro_pages as $pmpro_page_name => $pmpro_page_id)
86
+ {
87
+ if($pmpro_page_name == "checkout")
88
+ {
89
+ continue; //we do the checkout shortcode every time now
90
+ }
91
+
92
+ if(!empty($post->ID) && $pmpro_page_id == $post->ID)
93
+ {
94
+ //preheader
95
+ require_once(PMPRO_DIR . "/preheaders/" . $pmpro_page_name . ".php");
96
+
97
+ //add class to body
98
+ $pmpro_body_classes[] = "pmpro-" . str_replace("_", "-", $pmpro_page_name);
99
+
100
+ //shortcode
101
+ function pmpro_pages_shortcode($atts, $content=null, $code="")
102
+ {
103
+ global $pmpro_page_name;
104
+ ob_start();
105
+ if(file_exists(get_stylesheet_directory() . "/paid-memberships-pro/pages/" . $pmpro_page_name . ".php"))
106
+ include(get_stylesheet_directory() . "/paid-memberships-pro/pages/" . $pmpro_page_name . ".php");
107
+ else
108
+ include(PMPRO_DIR . "/pages/" . $pmpro_page_name . ".php");
109
+
110
+ $temp_content = ob_get_contents();
111
+ ob_end_clean();
112
+ return apply_filters("pmpro_pages_shortcode_" . $pmpro_page_name, $temp_content);
113
+ }
114
+ add_shortcode("pmpro_" . $pmpro_page_name, "pmpro_pages_shortcode");
115
+ break; //only the first page found gets a shortcode replacement
116
+ }
117
+ }
118
+
119
+ //make sure you load the preheader for the checkout page. the shortcode for checkout is loaded below
120
+ if(!empty($post->post_content) && strpos($post->post_content, "[pmpro_checkout]") !== false)
121
+ {
122
+ $pmpro_body_classes[] = "pmpro-checkout";
123
+ require_once(PMPRO_DIR . "/preheaders/checkout.php");
124
+ }
125
+ }
126
+ }
127
+ add_action("wp", "pmpro_wp", 1);
128
+
129
+ /*
130
+ Add PMPro page names to the BODY class.
131
+ */
132
+ function pmpro_body_class($classes)
133
+ {
134
+ global $pmpro_body_classes;
135
+
136
+ if(is_array($pmpro_body_classes))
137
+ $classes = array_merge($pmpro_body_classes, $classes);
138
+
139
+ return $classes;
140
+ }
141
+ add_filter("body_class", "pmpro_body_class");
142
+
143
+ //add membership level to current user object
144
+ function pmpro_set_current_user()
145
+ {
146
+ //this code runs at the beginning of the plugin
147
+ global $current_user, $wpdb;
148
+ get_currentuserinfo();
149
+ $id = intval($current_user->ID);
150
+ if($id)
151
+ {
152
+ $current_user->membership_level = pmpro_getMembershipLevelForUser($current_user->ID);
153
+ if(!empty($current_user->membership_level))
154
+ {
155
+ $current_user->membership_level->categories = pmpro_getMembershipCategories($current_user->membership_level->ID);
156
+ }
157
+ $current_user->membership_levels = pmpro_getMembershipLevelsForUser($current_user->ID);
158
+ }
159
+
160
+ //hiding ads?
161
+ $hideads = pmpro_getOption("hideads");
162
+ $hideadslevels = pmpro_getOption("hideadslevels");
163
+ if(!is_array($hideadslevels))
164
+ $hideadslevels = explode(",", $hideadslevels);
165
+ if($hideads == 1 && pmpro_hasMembershipLevel() || $hideads == 2 && pmpro_hasMembershipLevel($hideadslevels))
166
+ {
167
+ //disable ads in ezAdsense
168
+ if(class_exists("ezAdSense"))
169
+ {
170
+ global $ezCount, $urCount;
171
+ $ezCount = 100;
172
+ $urCount = 100;
173
+ }
174
+
175
+ //disable ads in Easy Adsense (newer versions)
176
+ if(class_exists("EzAdSense"))
177
+ {
178
+ global $ezAdSense;
179
+ $ezAdSense->ezCount = 100;
180
+ $ezAdSense->urCount = 100;
181
+ }
182
+
183
+ //set a global variable to hide ads
184
+ global $pmpro_display_ads;
185
+ $pmpro_display_ads = false;
186
+ }
187
+ else
188
+ {
189
+ global $pmpro_display_ads;
190
+ $pmpro_display_ads = true;
191
+ }
192
+
193
+ do_action("pmpro_after_set_current_user");
194
+ }
195
+ add_action('set_current_user', 'pmpro_set_current_user');
includes/lib/Braintree/Braintree.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree base class and initialization
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+
11
+ set_include_path(get_include_path() . PATH_SEPARATOR . realpath(dirname(__FILE__)));
12
+
13
+ /**
14
+ * Braintree PHP Library
15
+ *
16
+ * Provides methods to child classes. This class cannot be instantiated.
17
+ *
18
+ * @copyright 2010 Braintree Payment Solutions
19
+ */
20
+ abstract class Braintree
21
+ {
22
+ /**
23
+ * @ignore
24
+ * don't permit an explicit call of the constructor!
25
+ * (like $t = new Braintree_Transaction())
26
+ */
27
+ protected function __construct()
28
+ {
29
+ }
30
+ /**
31
+ * @ignore
32
+ * don't permit cloning the instances (like $x = clone $v)
33
+ */
34
+ protected function __clone()
35
+ {
36
+ }
37
+
38
+ /**
39
+ * returns private/nonexistent instance properties
40
+ * @ignore
41
+ * @access public
42
+ * @param string $name property name
43
+ * @return mixed contents of instance properties
44
+ */
45
+ public function __get($name)
46
+ {
47
+ if (array_key_exists($name, $this->_attributes)) {
48
+ return $this->_attributes[$name];
49
+ }
50
+ else {
51
+ trigger_error('Undefined property on ' . get_class($this) . ': ' . $name, E_USER_NOTICE);
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * used by isset() and empty()
58
+ * @access public
59
+ * @param string $name property name
60
+ * @return boolean
61
+ */
62
+ public function __isset($name)
63
+ {
64
+ return array_key_exists($name, $this->_attributes);
65
+ }
66
+
67
+ public function _set($key, $value)
68
+ {
69
+ $this->_attributes[$key] = $value;
70
+ }
71
+
72
+ /**
73
+ *
74
+ * @param string $className
75
+ * @param object $resultObj
76
+ * @return object returns the passed object if successful
77
+ * @throws Braintree_Exception_ValidationsFailed
78
+ */
79
+ public static function returnObjectOrThrowException($className, $resultObj)
80
+ {
81
+ $resultObjName = Braintree_Util::cleanClassName($className);
82
+ if ($resultObj->success) {
83
+ return $resultObj->$resultObjName;
84
+ } else {
85
+ throw new Braintree_Exception_ValidationsFailed();
86
+ }
87
+ }
88
+ }
89
+ require_once('Braintree/Modification.php');
90
+ require_once('Braintree/Instance.php');
91
+
92
+ require_once('Braintree/Address.php');
93
+ require_once('Braintree/AddOn.php');
94
+ require_once('Braintree/Collection.php');
95
+ require_once('Braintree/Configuration.php');
96
+ require_once('Braintree/CreditCard.php');
97
+ require_once('Braintree/Customer.php');
98
+ require_once('Braintree/CustomerSearch.php');
99
+ require_once('Braintree/Descriptor.php');
100
+ require_once('Braintree/Digest.php');
101
+ require_once('Braintree/Discount.php');
102
+ require_once('Braintree/IsNode.php');
103
+ require_once('Braintree/EqualityNode.php');
104
+ require_once('Braintree/Exception.php');
105
+ require_once('Braintree/Http.php');
106
+ require_once('Braintree/KeyValueNode.php');
107
+ require_once('Braintree/MultipleValueNode.php');
108
+ require_once('Braintree/MultipleValueOrTextNode.php');
109
+ require_once('Braintree/PartialMatchNode.php');
110
+ require_once('Braintree/Plan.php');
111
+ require_once('Braintree/RangeNode.php');
112
+ require_once('Braintree/ResourceCollection.php');
113
+ require_once('Braintree/SettlementBatchSummary.php');
114
+ require_once('Braintree/Subscription.php');
115
+ require_once('Braintree/SubscriptionSearch.php');
116
+ require_once('Braintree/SubscriptionStatus.php');
117
+ require_once('Braintree/TextNode.php');
118
+ require_once('Braintree/Transaction.php');
119
+ require_once('Braintree/TransactionSearch.php');
120
+ require_once('Braintree/TransparentRedirect.php');
121
+ require_once('Braintree/Util.php');
122
+ require_once('Braintree/Version.php');
123
+ require_once('Braintree/Xml.php');
124
+ require_once('Braintree/Error/Codes.php');
125
+ require_once('Braintree/Error/ErrorCollection.php');
126
+ require_once('Braintree/Error/Validation.php');
127
+ require_once('Braintree/Error/ValidationErrorCollection.php');
128
+ require_once('Braintree/Exception/Authentication.php');
129
+ require_once('Braintree/Exception/Authorization.php');
130
+ require_once('Braintree/Exception/Configuration.php');
131
+ require_once('Braintree/Exception/DownForMaintenance.php');
132
+ require_once('Braintree/Exception/ForgedQueryString.php');
133
+ require_once('Braintree/Exception/InvalidSignature.php');
134
+ require_once('Braintree/Exception/NotFound.php');
135
+ require_once('Braintree/Exception/ServerError.php');
136
+ require_once('Braintree/Exception/SSLCertificate.php');
137
+ require_once('Braintree/Exception/SSLCaFileNotFound.php');
138
+ require_once('Braintree/Exception/Unexpected.php');
139
+ require_once('Braintree/Exception/UpgradeRequired.php');
140
+ require_once('Braintree/Exception/ValidationsFailed.php');
141
+ require_once('Braintree/Result/CreditCardVerification.php');
142
+ require_once('Braintree/Result/Error.php');
143
+ require_once('Braintree/Result/Successful.php');
144
+ require_once('Braintree/Test/CreditCardNumbers.php');
145
+ require_once('Braintree/Test/TransactionAmounts.php');
146
+ require_once('Braintree/Transaction/AddressDetails.php');
147
+ require_once('Braintree/Transaction/CreditCardDetails.php');
148
+ require_once('Braintree/Transaction/CustomerDetails.php');
149
+ require_once('Braintree/Transaction/StatusDetails.php');
150
+ require_once('Braintree/Transaction/SubscriptionDetails.php');
151
+ require_once('Braintree/WebhookNotification.php');
152
+ require_once('Braintree/WebhookTesting.php');
153
+ require_once('Braintree/Xml/Generator.php');
154
+ require_once('Braintree/Xml/Parser.php');
155
+ require_once('Braintree/CreditCardVerification.php');
156
+ require_once('Braintree/CreditCardVerificationSearch.php');
157
+
158
+ if (version_compare(PHP_VERSION, '5.2.1', '<')) {
159
+ throw new Braintree_Exception('PHP version >= 5.2.1 required');
160
+ }
161
+
162
+
163
+ function requireDependencies() {
164
+ $requiredExtensions = array('xmlwriter', 'SimpleXML', 'openssl', 'dom', 'hash', 'curl');
165
+ foreach ($requiredExtensions AS $ext) {
166
+ if (!extension_loaded($ext)) {
167
+ throw new Braintree_Exception('The Braintree library requires the ' . $ext . ' extension.');
168
+ }
169
+ }
170
+ }
171
+
172
+ requireDependencies();
includes/lib/Braintree/Braintree/AddOn.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_AddOn extends Braintree_Modification
3
+ {
4
+ public static function all()
5
+ {
6
+ $response = Braintree_Http::get('/add_ons');
7
+
8
+ $addOns = array("addOn" => $response['addOns']);
9
+
10
+ return Braintree_Util::extractAttributeAsArray(
11
+ $addOns,
12
+ 'addOn'
13
+ );
14
+ }
15
+ }
includes/lib/Braintree/Braintree/Address.php ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Address module
4
+ *
5
+ * PHP Version 5
6
+ *
7
+ * @package Braintree
8
+ * @copyright 2010 Braintree Payment Solutions
9
+ */
10
+ /**
11
+ * Creates and manages Braintree Addresses
12
+ *
13
+ * An Address belongs to a Customer. It can be associated to a
14
+ * CreditCard as the billing address. It can also be used
15
+ * as the shipping address when creating a Transaction.
16
+ *
17
+ * @package Braintree
18
+ * @copyright 2010 Braintree Payment Solutions
19
+ *
20
+ * @property-read string $company
21
+ * @property-read string $countryName
22
+ * @property-read string $createdAt
23
+ * @property-read string $customerId
24
+ * @property-read string $extendedAddress
25
+ * @property-read string $firstName
26
+ * @property-read string $id
27
+ * @property-read string $lastName
28
+ * @property-read string $locality
29
+ * @property-read string $postalCode
30
+ * @property-read string $region
31
+ * @property-read string $streetAddress
32
+ * @property-read string $updatedAt
33
+ */
34
+ class Braintree_Address extends Braintree
35
+ {
36
+
37
+ /* public class methods */
38
+ /**
39
+ *
40
+ * @access public
41
+ * @param array $attribs
42
+ * @return object Result, either Successful or Error
43
+ */
44
+ public static function create($attribs)
45
+ {
46
+ Braintree_Util::verifyKeys(self::createSignature(), $attribs);
47
+ $customerId = isset($attribs['customerId']) ?
48
+ $attribs['customerId'] :
49
+ null;
50
+
51
+ self::_validateCustomerId($customerId);
52
+ unset($attribs['customerId']);
53
+ return self::_doCreate(
54
+ '/customers/' . $customerId . '/addresses',
55
+ array('address' => $attribs)
56
+ );
57
+ }
58
+
59
+ /**
60
+ * attempts the create operation assuming all data will validate
61
+ * returns a Braintree_Address object instead of a Result
62
+ *
63
+ * @access public
64
+ * @param array $attribs
65
+ * @return object
66
+ * @throws Braintree_Exception_ValidationError
67
+ */
68
+ public static function createNoValidate($attribs)
69
+ {
70
+ $result = self::create($attribs);
71
+ return self::returnObjectOrThrowException(__CLASS__, $result);
72
+
73
+ }
74
+
75
+ /**
76
+ * delete an address by id
77
+ *
78
+ * @param mixed $customerOrId
79
+ * @param string $addressId
80
+ */
81
+ public static function delete($customerOrId = null, $addressId = null)
82
+ {
83
+ self::_validateId($addressId);
84
+ $customerId = self::_determineCustomerId($customerOrId);
85
+ Braintree_Http::delete(
86
+ '/customers/' . $customerId . '/addresses/' . $addressId
87
+ );
88
+ return new Braintree_Result_Successful();
89
+ }
90
+
91
+ /**
92
+ * find an address by id
93
+ *
94
+ * Finds the address with the given <b>addressId</b> that is associated
95
+ * to the given <b>customerOrId</b>.
96
+ * If the address cannot be found, a NotFound exception will be thrown.
97
+ *
98
+ *
99
+ * @access public
100
+ * @param mixed $customerOrId
101
+ * @param string $addressId
102
+ * @return object Braintree_Address
103
+ * @throws Braintree_Exception_NotFound
104
+ */
105
+ public static function find($customerOrId, $addressId)
106
+ {
107
+
108
+ $customerId = self::_determineCustomerId($customerOrId);
109
+ self::_validateId($addressId);
110
+
111
+ try {
112
+ $response = Braintree_Http::get(
113
+ '/customers/' . $customerId . '/addresses/' . $addressId
114
+ );
115
+ return self::factory($response['address']);
116
+ } catch (Braintree_Exception_NotFound $e) {
117
+ throw new Braintree_Exception_NotFound(
118
+ 'address for customer ' . $customerId .
119
+ ' with id ' . $addressId . ' not found.'
120
+ );
121
+ }
122
+
123
+ }
124
+
125
+ /**
126
+ * returns false if comparing object is not a Braintree_Address,
127
+ * or is a Braintree_Address with a different id
128
+ *
129
+ * @param object $other address to compare against
130
+ * @return boolean
131
+ */
132
+ public function isEqual($other)
133
+ {
134
+ return !($other instanceof Braintree_Address) ?
135
+ false :
136
+ ($this->id === $other->id && $this->customerId === $other->customerId);
137
+ }
138
+
139
+ /**
140
+ * updates the address record
141
+ *
142
+ * if calling this method in static context,
143
+ * customerOrId is the 2nd attribute, addressId 3rd.
144
+ * customerOrId & addressId are not sent in object context.
145
+ *
146
+ *
147
+ * @access public
148
+ * @param array $attributes
149
+ * @param mixed $customerOrId (only used in static call)
150
+ * @param string $addressId (only used in static call)
151
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
152
+ */
153
+ public static function update($customerOrId, $addressId, $attributes)
154
+ {
155
+ self::_validateId($addressId);
156
+ $customerId = self::_determineCustomerId($customerOrId);
157
+ Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
158
+
159
+ $response = Braintree_Http::put(
160
+ '/customers/' . $customerId . '/addresses/' . $addressId,
161
+ array('address' => $attributes)
162
+ );
163
+
164
+ return self::_verifyGatewayResponse($response);
165
+
166
+ }
167
+
168
+ /**
169
+ * update an address record, assuming validations will pass
170
+ *
171
+ * if calling this method in static context,
172
+ * customerOrId is the 2nd attribute, addressId 3rd.
173
+ * customerOrId & addressId are not sent in object context.
174
+ *
175
+ * @access public
176
+ * @param array $transactionAttribs
177
+ * @param string $customerId
178
+ * @return object Braintree_Transaction
179
+ * @throws Braintree_Exception_ValidationsFailed
180
+ * @see Braintree_Address::update()
181
+ */
182
+ public static function updateNoValidate($customerOrId, $addressId, $attributes)
183
+ {
184
+ $result = self::update($customerOrId, $addressId, $attributes);
185
+ return self::returnObjectOrThrowException(__CLASS__, $result);
186
+ }
187
+
188
+ /**
189
+ * creates a full array signature of a valid create request
190
+ * @return array gateway create request format
191
+ */
192
+ public static function createSignature()
193
+ {
194
+ return array(
195
+ 'company', 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
196
+ 'countryName', 'customerId', 'extendedAddress', 'firstName',
197
+ 'lastName', 'locality', 'postalCode', 'region', 'streetAddress'
198
+ );
199
+ }
200
+
201
+ /**
202
+ * creates a full array signature of a valid update request
203
+ * @return array gateway update request format
204
+ */
205
+ public static function updateSignature()
206
+ {
207
+ // TODO: remove customerId from update signature
208
+ return self::createSignature();
209
+
210
+ }
211
+
212
+ /**
213
+ * create a printable representation of the object as:
214
+ * ClassName[property=value, property=value]
215
+ * @ignore
216
+ * @return var
217
+ */
218
+ public function __toString()
219
+ {
220
+ return __CLASS__ . '[' .
221
+ Braintree_Util::attributesToString($this->_attributes) .']';
222
+ }
223
+
224
+ /**
225
+ * sets instance properties from an array of values
226
+ *
227
+ * @ignore
228
+ * @access protected
229
+ * @param array $addressAttribs array of address data
230
+ * @return none
231
+ */
232
+ protected function _initialize($addressAttribs)
233
+ {
234
+ // set the attributes
235
+ $this->_attributes = $addressAttribs;
236
+ }
237
+
238
+ /**
239
+ * verifies that a valid address id is being used
240
+ * @ignore
241
+ * @param string $id address id
242
+ * @throws InvalidArgumentException
243
+ */
244
+ private static function _validateId($id = null)
245
+ {
246
+ if (empty($id) || trim($id) == "") {
247
+ throw new InvalidArgumentException(
248
+ 'expected address id to be set'
249
+ );
250
+ }
251
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
252
+ throw new InvalidArgumentException(
253
+ $id . ' is an invalid address id.'
254
+ );
255
+ }
256
+ }
257
+
258
+ /**
259
+ * verifies that a valid customer id is being used
260
+ * @ignore
261
+ * @param string $id customer id
262
+ * @throws InvalidArgumentException
263
+ */
264
+ private static function _validateCustomerId($id = null)
265
+ {
266
+ if (empty($id) || trim($id) == "") {
267
+ throw new InvalidArgumentException(
268
+ 'expected customer id to be set'
269
+ );
270
+ }
271
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
272
+ throw new InvalidArgumentException(
273
+ $id . ' is an invalid customer id.'
274
+ );
275
+ }
276
+
277
+ }
278
+
279
+ /**
280
+ * determines if a string id or Customer object was passed
281
+ * @ignore
282
+ * @param mixed $customerOrId
283
+ * @return string customerId
284
+ */
285
+ private static function _determineCustomerId($customerOrId)
286
+ {
287
+ $customerId = ($customerOrId instanceof Braintree_Customer) ? $customerOrId->id : $customerOrId;
288
+ self::_validateCustomerId($customerId);
289
+ return $customerId;
290
+
291
+ }
292
+
293
+ /* private class methods */
294
+ /**
295
+ * sends the create request to the gateway
296
+ * @ignore
297
+ * @param string $url
298
+ * @param array $params
299
+ * @return mixed
300
+ */
301
+ private static function _doCreate($url, $params)
302
+ {
303
+ $response = Braintree_Http::post($url, $params);
304
+
305
+ return self::_verifyGatewayResponse($response);
306
+
307
+ }
308
+
309
+ /**
310
+ * generic method for validating incoming gateway responses
311
+ *
312
+ * creates a new Braintree_Address object and encapsulates
313
+ * it inside a Braintree_Result_Successful object, or
314
+ * encapsulates a Braintree_Errors object inside a Result_Error
315
+ * alternatively, throws an Unexpected exception if the response is invalid.
316
+ *
317
+ * @ignore
318
+ * @param array $response gateway response values
319
+ * @return object Result_Successful or Result_Error
320
+ * @throws Braintree_Exception_Unexpected
321
+ */
322
+ private static function _verifyGatewayResponse($response)
323
+ {
324
+ if (isset($response['address'])) {
325
+ // return a populated instance of Braintree_Address
326
+ return new Braintree_Result_Successful(
327
+ self::factory($response['address'])
328
+ );
329
+ } else if (isset($response['apiErrorResponse'])) {
330
+ return new Braintree_Result_Error($response['apiErrorResponse']);
331
+ } else {
332
+ throw new Braintree_Exception_Unexpected(
333
+ "Expected address or apiErrorResponse"
334
+ );
335
+ }
336
+
337
+ }
338
+
339
+ /**
340
+ * factory method: returns an instance of Braintree_Address
341
+ * to the requesting method, with populated properties
342
+ * @ignore
343
+ * @return object instance of Braintree_Address
344
+ */
345
+ public static function factory($attributes)
346
+ {
347
+ $instance = new self();
348
+ $instance->_initialize($attributes);
349
+ return $instance;
350
+
351
+ }
352
+ }
includes/lib/Braintree/Braintree/Collection.php ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Generic collection
4
+ *
5
+ * PHP Version 5
6
+ *
7
+ * @package Braintree
8
+ * @subpackage Utility
9
+ * @copyright 2010 Braintree Payment Solutions
10
+ */
11
+
12
+ /**
13
+ * Generic Collection class
14
+ *
15
+ * Based on Generic Collection class from:
16
+ * {@link http://codeutopia.net/code/library/CU/Collection.php}
17
+ *
18
+ * @package Braintree
19
+ * @subpackage Utility
20
+ */
21
+ class Braintree_Collection implements Countable, IteratorAggregate, ArrayAccess
22
+ {
23
+ /**
24
+ *
25
+ * @var array $_collection collection storage
26
+ */
27
+ protected $_collection = array();
28
+
29
+ /**
30
+ * Add a value into the collection
31
+ * @param string $value
32
+ */
33
+ public function add($value)
34
+ {
35
+ $this->_collection[] = $value;
36
+ }
37
+
38
+ /**
39
+ * Set index's value
40
+ * @param integer $index
41
+ * @param mixed $value
42
+ * @throws OutOfRangeException
43
+ */
44
+ public function set($index, $value)
45
+ {
46
+ if($index >= $this->count())
47
+ throw new OutOfRangeException('Index out of range');
48
+
49
+ $this->_collection[$index] = $value;
50
+ }
51
+
52
+ /**
53
+ * Remove a value from the collection
54
+ * @param integer $index index to remove
55
+ * @throws OutOfRangeException if index is out of range
56
+ */
57
+ public function remove($index)
58
+ {
59
+ if($index >= $this->count())
60
+ throw new OutOfRangeException('Index out of range');
61
+
62
+ array_splice($this->_collection, $index, 1);
63
+ }
64
+
65
+ /**
66
+ * Return value at index
67
+ * @param integer $index
68
+ * @return mixed
69
+ * @throws OutOfRangeException
70
+ */
71
+ public function get($index)
72
+ {
73
+ if($index >= $this->count())
74
+ throw new OutOfRangeException('Index out of range');
75
+
76
+ return $this->_collection[$index];
77
+ }
78
+
79
+ /**
80
+ * Determine if index exists
81
+ * @param integer $index
82
+ * @return boolean
83
+ */
84
+ public function exists($index)
85
+ {
86
+ if($index >= $this->count())
87
+ return false;
88
+
89
+ return true;
90
+ }
91
+ /**
92
+ * Return count of items in collection
93
+ * Implements countable
94
+ * @return integer
95
+ */
96
+ public function count()
97
+ {
98
+ return count($this->_collection);
99
+ }
100
+
101
+
102
+ /**
103
+ * Return an iterator
104
+ * Implements IteratorAggregate
105
+ * @return ArrayIterator
106
+ */
107
+ public function getIterator()
108
+ {
109
+ return new ArrayIterator($this->_collection);
110
+ }
111
+
112
+ /**
113
+ * Set offset to value
114
+ * Implements ArrayAccess
115
+ * @see set
116
+ * @param integer $offset
117
+ * @param mixed $value
118
+ */
119
+ public function offsetSet($offset, $value)
120
+ {
121
+ $this->set($offset, $value);
122
+ }
123
+
124
+ /**
125
+ * Unset offset
126
+ * Implements ArrayAccess
127
+ * @see remove
128
+ * @param integer $offset
129
+ */
130
+ public function offsetUnset($offset)
131
+ {
132
+ $this->remove($offset);
133
+ }
134
+
135
+ /**
136
+ * get an offset's value
137
+ * Implements ArrayAccess
138
+ * @see get
139
+ * @param integer $offset
140
+ * @return mixed
141
+ */
142
+ public function offsetGet($offset)
143
+ {
144
+ return $this->get($offset);
145
+ }
146
+
147
+ /**
148
+ * Determine if offset exists
149
+ * Implements ArrayAccess
150
+ * @see exists
151
+ * @param integer $offset
152
+ * @return boolean
153
+ */
154
+ public function offsetExists($offset)
155
+ {
156
+ return $this->exists($offset);
157
+ }
158
+
159
+ }
includes/lib/Braintree/Braintree/Configuration.php ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ * Configuration registry
5
+ *
6
+ * @package Braintree
7
+ * @subpackage Utility
8
+ * @copyright 2010 Braintree Payment Solutions
9
+ */
10
+
11
+ /**
12
+ * acts as a registry for config data.
13
+ *
14
+ *
15
+ * @package Braintree
16
+ * @subpackage Utility
17
+ *
18
+ * */
19
+
20
+ class Braintree_Configuration extends Braintree
21
+ {
22
+ /**
23
+ * Braintree API version to use
24
+ * @access public
25
+ */
26
+ const API_VERSION = 3;
27
+
28
+ /**
29
+ * @var array array of config properties
30
+ * @access protected
31
+ * @static
32
+ */
33
+ private static $_cache = array(
34
+ 'environment' => '',
35
+ 'merchantId' => '',
36
+ 'publicKey' => '',
37
+ 'privateKey' => '',
38
+ );
39
+ /**
40
+ *
41
+ * @access protected
42
+ * @static
43
+ * @var array valid environments, used for validation
44
+ */
45
+ private static $_validEnvironments = array(
46
+ 'development',
47
+ 'sandbox',
48
+ 'production',
49
+ 'qa',
50
+ );
51
+
52
+ /**
53
+ * resets configuration to default
54
+ * @access public
55
+ * @static
56
+ */
57
+ public static function reset()
58
+ {
59
+ self::$_cache = array (
60
+ 'environment' => '',
61
+ 'merchantId' => '',
62
+ 'publicKey' => '',
63
+ 'privateKey' => '',
64
+ );
65
+ }
66
+
67
+ /**
68
+ * performs sanity checks when config settings are being set
69
+ *
70
+ * @ignore
71
+ * @access protected
72
+ * @param string $key name of config setting
73
+ * @param string $value value to set
74
+ * @throws InvalidArgumentException
75
+ * @throws Braintree_Exception_Configuration
76
+ * @static
77
+ * @return boolean
78
+ */
79
+ private static function validate($key=null, $value=null)
80
+ {
81
+ if (empty($key) && empty($value)) {
82
+ throw new InvalidArgumentException('nothing to validate');
83
+ }
84
+
85
+ if ($key === 'environment' &&
86
+ !in_array($value, self::$_validEnvironments) ) {
87
+ throw new Braintree_Exception_Configuration('"' .
88
+ $value . '" is not a valid environment.');
89
+ }
90
+
91
+ if (!isset(self::$_cache[$key])) {
92
+ throw new Braintree_Exception_Configuration($key .
93
+ ' is not a valid configuration setting.');
94
+ }
95
+
96
+ if (empty($value)) {
97
+ throw new InvalidArgumentException($key . ' cannot be empty.');
98
+ }
99
+
100
+ return true;
101
+ }
102
+
103
+ private static function set($key, $value)
104
+ {
105
+ // this method will raise an exception on invalid data
106
+ self::validate($key, $value);
107
+ // set the value in the cache
108
+ self::$_cache[$key] = $value;
109
+
110
+ }
111
+
112
+ private static function get($key)
113
+ {
114
+ // throw an exception if the value hasn't been set
115
+ if (isset(self::$_cache[$key]) &&
116
+ (empty(self::$_cache[$key]))) {
117
+ throw new Braintree_Exception_Configuration(
118
+ $key.' needs to be set'
119
+ );
120
+ }
121
+
122
+ if (array_key_exists($key, self::$_cache)) {
123
+ return self::$_cache[$key];
124
+ }
125
+
126
+ // return null by default to prevent __set from overloading
127
+ return null;
128
+ }
129
+
130
+
131
+ private static function setOrGet($name, $value = null)
132
+ {
133
+ if (!empty($value) && is_array($value)) {
134
+ $value = $value[0];
135
+ }
136
+ if (!empty($value)) {
137
+ self::set($name, $value);
138
+ } else {
139
+ return self::get($name);
140
+ }
141
+ return true;
142
+ }
143
+ /**#@+
144
+ * sets or returns the property after validation
145
+ * @access public
146
+ * @static
147
+ * @param string $value pass a string to set, empty to get
148
+ * @return mixed returns true on set
149
+ */
150
+ public static function environment($value = null)
151
+ {
152
+ return self::setOrGet(__FUNCTION__, $value);
153
+ }
154
+
155
+ public static function merchantId($value = null)
156
+ {
157
+ return self::setOrGet(__FUNCTION__, $value);
158
+ }
159
+
160
+ public static function publicKey($value = null)
161
+ {
162
+ return self::setOrGet(__FUNCTION__, $value);
163
+ }
164
+
165
+ public static function privateKey($value = null)
166
+ {
167
+ return self::setOrGet(__FUNCTION__, $value);
168
+ }
169
+ /**#@-*/
170
+
171
+ /**
172
+ * returns the full merchant URL based on config values
173
+ *
174
+ * @access public
175
+ * @static
176
+ * @param none
177
+ * @return string merchant URL
178
+ */
179
+ public static function merchantUrl()
180
+ {
181
+ return self::baseUrl() .
182
+ self::merchantPath();
183
+ }
184
+
185
+ /**
186
+ * returns the base braintree gateway URL based on config values
187
+ *
188
+ * @access public
189
+ * @static
190
+ * @param none
191
+ * @return string braintree gateway URL
192
+ */
193
+ public static function baseUrl()
194
+ {
195
+ return self::protocol() . '://' .
196
+ self::serverName() . ':' .
197
+ self::portNumber();
198
+ }
199
+
200
+ /**
201
+ * sets the merchant path based on merchant ID
202
+ *
203
+ * @access protected
204
+ * @static
205
+ * @param none
206
+ * @return string merchant path uri
207
+ */
208
+ public static function merchantPath()
209
+ {
210
+ return '/merchants/'.self::merchantId();
211
+ }
212
+
213
+ /**
214
+ * sets the physical path for the location of the CA certs
215
+ *
216
+ * @access public
217
+ * @static
218
+ * @param none
219
+ * @return string filepath
220
+ */
221
+ public static function caFile($sslPath = NULL)
222
+ {
223
+ $sslPath = $sslPath ? $sslPath : DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR .
224
+ 'ssl' . DIRECTORY_SEPARATOR;
225
+
226
+ switch(self::environment()) {
227
+ case 'production':
228
+ $caPath = realpath(
229
+ dirname(__FILE__) .
230
+ $sslPath . 'www_braintreegateway_com.ca.crt'
231
+ );
232
+ break;
233
+ case 'qa':
234
+ case 'sandbox':
235
+ default:
236
+ $caPath = realpath(
237
+ dirname(__FILE__) .
238
+ $sslPath . 'sandbox_braintreegateway_com.ca.crt'
239
+ );
240
+ break;
241
+ }
242
+
243
+ if (!file_exists($caPath))
244
+ {
245
+ throw new Braintree_Exception_SSLCaFileNotFound();
246
+ }
247
+
248
+ return $caPath;
249
+ }
250
+
251
+ /**
252
+ * returns the port number depending on environment
253
+ *
254
+ * @access public
255
+ * @static
256
+ * @param none
257
+ * @return int portnumber
258
+ */
259
+ public static function portNumber()
260
+ {
261
+ if (self::sslOn()) {
262
+ return 443;
263
+ }
264
+ return getenv("GATEWAY_PORT") ? getenv("GATEWAY_PORT") : 3000;
265
+ }
266
+
267
+ /**
268
+ * returns http protocol depending on environment
269
+ *
270
+ * @access public
271
+ * @static
272
+ * @param none
273
+ * @return string http || https
274
+ */
275
+ public static function protocol()
276
+ {
277
+ return self::sslOn() ? 'https' : 'http';
278
+ }
279
+
280
+ /**
281
+ * returns gateway server name depending on environment
282
+ *
283
+ * @access public
284
+ * @static
285
+ * @param none
286
+ * @return string server domain name
287
+ */
288
+ public static function serverName()
289
+ {
290
+ switch(self::environment()) {
291
+ case 'production':
292
+ $serverName = 'www.braintreegateway.com';
293
+ break;
294
+ case 'qa':
295
+ $serverName = 'qa.braintreegateway.com';
296
+ break;
297
+ case 'sandbox':
298
+ $serverName = 'sandbox.braintreegateway.com';
299
+ break;
300
+ case 'development':
301
+ default:
302
+ $serverName = 'localhost';
303
+ break;
304
+ }
305
+
306
+ return $serverName;
307
+ }
308
+
309
+ /**
310
+ * returns boolean indicating SSL is on or off for this session,
311
+ * depending on environment
312
+ *
313
+ * @access public
314
+ * @static
315
+ * @param none
316
+ * @return boolean
317
+ */
318
+ public static function sslOn()
319
+ {
320
+ switch(self::environment()) {
321
+ case 'development':
322
+ $ssl = false;
323
+ break;
324
+ case 'production':
325
+ case 'qa':
326
+ case 'sandbox':
327
+ default:
328
+ $ssl = true;
329
+ break;
330
+ }
331
+
332
+ return $ssl;
333
+ }
334
+
335
+ /**
336
+ * log message to default logger
337
+ *
338
+ * @param string $message
339
+ *
340
+ */
341
+ public static function logMessage($message)
342
+ {
343
+ error_log('[Braintree] ' . $message);
344
+ }
345
+
346
+ }
includes/lib/Braintree/Braintree/CreditCard.php ADDED
@@ -0,0 +1,591 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree CreditCard module
4
+ *
5
+ * @package Braintree
6
+ * @category Resources
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates and manages Braintree CreditCards
12
+ *
13
+ * <b>== More information ==</b>
14
+ *
15
+ * For more detailed information on CreditCards, see {@link http://www.braintreepayments.com/gateway/credit-card-api http://www.braintreepaymentsolutions.com/gateway/credit-card-api}<br />
16
+ * For more detailed information on CreditCard verifications, see {@link http://www.braintreepayments.com/gateway/credit-card-verification-api http://www.braintreepaymentsolutions.com/gateway/credit-card-verification-api}
17
+ *
18
+ * @package Braintree
19
+ * @category Resources
20
+ * @copyright 2010 Braintree Payment Solutions
21
+ *
22
+ * @property-read string $billingAddress
23
+ * @property-read string $bin
24
+ * @property-read string $cardType
25
+ * @property-read string $cardholderName
26
+ * @property-read string $createdAt
27
+ * @property-read string $customerId
28
+ * @property-read string $expirationDate
29
+ * @property-read string $expirationMonth
30
+ * @property-read string $expirationYear
31
+ * @property-read string $last4
32
+ * @property-read string $maskedNumber
33
+ * @property-read string $token
34
+ * @property-read string $updatedAt
35
+ */
36
+ class Braintree_CreditCard extends Braintree
37
+ {
38
+ // Card Type
39
+ const AMEX = 'American Express';
40
+ const CARTE_BLANCHE = 'Carte Blanche';
41
+ const CHINA_UNION_PAY = 'China UnionPay';
42
+ const DINERS_CLUB_INTERNATIONAL = 'Diners Club';
43
+ const DISCOVER = 'Discover';
44
+ const JCB = 'JCB';
45
+ const LASER = 'Laser';
46
+ const MAESTRO = 'Maestro';
47
+ const MASTER_CARD = 'MasterCard';
48
+ const SOLO = 'Solo';
49
+ const SWITCH_TYPE = 'Switch';
50
+ const VISA = 'Visa';
51
+ const UNKNOWN = 'Unknown';
52
+
53
+ // Credit card origination location
54
+ const INTERNATIONAL = "international";
55
+ const US = "us";
56
+
57
+ const PREPAID_YES = 'Yes';
58
+ const PREPAID_NO = 'No';
59
+ const PREPAID_UNKNOWN = 'Unknown';
60
+
61
+ const PAYROLL_YES = 'Yes';
62
+ const PAYROLL_NO = 'No';
63
+ const PAYROLL_UNKNOWN = 'Unknown';
64
+
65
+ const HEALTHCARE_YES = 'Yes';
66
+ const HEALTHCARE_NO = 'No';
67
+ const HEALTHCARE_UNKNOWN = 'Unknown';
68
+
69
+ const DURBIN_REGULATED_YES = 'Yes';
70
+ const DURBIN_REGULATED_NO = 'No';
71
+ const DURBIN_REGULATED_UNKNOWN = 'Unknown';
72
+
73
+ const DEBIT_YES = 'Yes';
74
+ const DEBIT_NO = 'No';
75
+ const DEBIT_UNKNOWN = 'Unknown';
76
+
77
+ const COMMERCIAL_YES = 'Yes';
78
+ const COMMERCIAL_NO = 'No';
79
+ const COMMERCIAL_UNKNOWN = 'Unknown';
80
+
81
+ const COUNTRY_OF_ISSUANCE_UNKNOWN = "Unknown";
82
+ const ISSUING_BANK_UNKNOWN = "Unknown";
83
+
84
+ public static function create($attribs)
85
+ {
86
+ Braintree_Util::verifyKeys(self::createSignature(), $attribs);
87
+ return self::_doCreate('/payment_methods', array('credit_card' => $attribs));
88
+ }
89
+
90
+ /**
91
+ * attempts the create operation assuming all data will validate
92
+ * returns a Braintree_CreditCard object instead of a Result
93
+ *
94
+ * @access public
95
+ * @param array $attribs
96
+ * @return object
97
+ * @throws Braintree_Exception_ValidationError
98
+ */
99
+ public static function createNoValidate($attribs)
100
+ {
101
+ $result = self::create($attribs);
102
+ return self::returnObjectOrThrowException(__CLASS__, $result);
103
+ }
104
+ /**
105
+ * create a customer from a TransparentRedirect operation
106
+ *
107
+ * @access public
108
+ * @param array $attribs
109
+ * @return object
110
+ */
111
+ public static function createFromTransparentRedirect($queryString)
112
+ {
113
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
114
+ $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
115
+ $queryString
116
+ );
117
+ return self::_doCreate(
118
+ '/payment_methods/all/confirm_transparent_redirect_request',
119
+ array('id' => $params['id'])
120
+ );
121
+ }
122
+
123
+ /**
124
+ *
125
+ * @access public
126
+ * @param none
127
+ * @return string
128
+ */
129
+ public static function createCreditCardUrl()
130
+ {
131
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
132
+ return Braintree_Configuration::merchantUrl() .
133
+ '/payment_methods/all/create_via_transparent_redirect_request';
134
+ }
135
+
136
+ /**
137
+ * returns a ResourceCollection of expired credit cards
138
+ * @return object ResourceCollection
139
+ */
140
+ public static function expired()
141
+ {
142
+ $response = Braintree_Http::post("/payment_methods/all/expired_ids");
143
+ $pager = array(
144
+ 'className' => __CLASS__,
145
+ 'classMethod' => 'fetchExpired',
146
+ 'methodArgs' => array()
147
+ );
148
+
149
+ return new Braintree_ResourceCollection($response, $pager);
150
+ }
151
+
152
+ public static function fetchExpired($ids)
153
+ {
154
+ $response = Braintree_Http::post("/payment_methods/all/expired", array('search' => array('ids' => $ids)));
155
+
156
+ return braintree_util::extractattributeasarray(
157
+ $response['paymentMethods'],
158
+ 'creditCard'
159
+ );
160
+ }
161
+ /**
162
+ * returns a ResourceCollection of credit cards expiring between start/end
163
+ *
164
+ * @return object ResourceCollection
165
+ */
166
+ public static function expiringBetween($startDate, $endDate)
167
+ {
168
+ $queryPath = '/payment_methods/all/expiring_ids?start=' . date('mY', $startDate) . '&end=' . date('mY', $endDate);
169
+ $response = Braintree_Http::post($queryPath);
170
+ $pager = array(
171
+ 'className' => __CLASS__,
172
+ 'classMethod' => 'fetchExpiring',
173
+ 'methodArgs' => array($startDate, $endDate)
174
+ );
175
+
176
+ return new Braintree_ResourceCollection($response, $pager);
177
+ }
178
+
179
+ public static function fetchExpiring($startDate, $endDate, $ids)
180
+ {
181
+ $queryPath = '/payment_methods/all/expiring?start=' . date('mY', $startDate) . '&end=' . date('mY', $endDate);
182
+ $response = Braintree_Http::post($queryPath, array('search' => array('ids' => $ids)));
183
+
184
+ return Braintree_Util::extractAttributeAsArray(
185
+ $response['paymentMethods'],
186
+ 'creditCard'
187
+ );
188
+ }
189
+
190
+ /**
191
+ * find a creditcard by token
192
+ *
193
+ * @access public
194
+ * @param string $token credit card unique id
195
+ * @return object Braintree_CreditCard
196
+ * @throws Braintree_Exception_NotFound
197
+ */
198
+ public static function find($token)
199
+ {
200
+ self::_validateId($token);
201
+ try {
202
+ $response = Braintree_Http::get('/payment_methods/'.$token);
203
+ return self::factory($response['creditCard']);
204
+ } catch (Braintree_Exception_NotFound $e) {
205
+ throw new Braintree_Exception_NotFound(
206
+ 'credit card with token ' . $token . ' not found'
207
+ );
208
+ }
209
+
210
+ }
211
+
212
+ /**
213
+ * create a credit on the card for the passed transaction
214
+ *
215
+ * @access public
216
+ * @param array $attribs
217
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
218
+ */
219
+ public static function credit($token, $transactionAttribs)
220
+ {
221
+ self::_validateId($token);
222
+ return Braintree_Transaction::credit(
223
+ array_merge(
224
+ $transactionAttribs,
225
+ array('paymentMethodToken' => $token)
226
+ )
227
+ );
228
+ }
229
+
230
+ /**
231
+ * create a credit on this card, assuming validations will pass
232
+ *
233
+ * returns a Braintree_Transaction object on success
234
+ *
235
+ * @access public
236
+ * @param array $attribs
237
+ * @return object Braintree_Transaction
238
+ * @throws Braintree_Exception_ValidationError
239
+ */
240
+ public static function creditNoValidate($token, $transactionAttribs)
241
+ {
242
+ $result = self::credit($token, $transactionAttribs);
243
+ return self::returnObjectOrThrowException('Transaction', $result);
244
+ }
245
+
246
+ /**
247
+ * create a new sale for the current card
248
+ *
249
+ * @param string $token
250
+ * @param array $transactionAttribs
251
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
252
+ * @see Braintree_Transaction::sale()
253
+ */
254
+ public static function sale($token, $transactionAttribs)
255
+ {
256
+ self::_validateId($token);
257
+ return Braintree_Transaction::sale(
258
+ array_merge(
259
+ $transactionAttribs,
260
+ array('paymentMethodToken' => $token)
261
+ )
262
+ );
263
+ }
264
+
265
+ /**
266
+ * create a new sale using this card, assuming validations will pass
267
+ *
268
+ * returns a Braintree_Transaction object on success
269
+ *
270
+ * @access public
271
+ * @param array $transactionAttribs
272
+ * @param string $token
273
+ * @return object Braintree_Transaction
274
+ * @throws Braintree_Exception_ValidationsFailed
275
+ * @see Braintree_Transaction::sale()
276
+ */
277
+ public static function saleNoValidate($token, $transactionAttribs)
278
+ {
279
+ $result = self::sale($token, $transactionAttribs);
280
+ return self::returnObjectOrThrowException('Transaction', $result);
281
+ }
282
+
283
+ /**
284
+ * updates the creditcard record
285
+ *
286
+ * if calling this method in static context, $token
287
+ * is the 2nd attribute. $token is not sent in object context.
288
+ *
289
+ * @access public
290
+ * @param array $attributes
291
+ * @param string $token (optional)
292
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
293
+ */
294
+ public static function update($token, $attributes)
295
+ {
296
+ Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
297
+ self::_validateId($token);
298
+ return self::_doUpdate('put', '/payment_methods/' . $token, array('creditCard' => $attributes));
299
+ }
300
+
301
+ /**
302
+ * update a creditcard record, assuming validations will pass
303
+ *
304
+ * if calling this method in static context, $token
305
+ * is the 2nd attribute. $token is not sent in object context.
306
+ * returns a Braintree_CreditCard object on success
307
+ *
308
+ * @access public
309
+ * @param array $attributes
310
+ * @param string $token
311
+ * @return object Braintree_CreditCard
312
+ * @throws Braintree_Exception_ValidationsFailed
313
+ */
314
+ public static function updateNoValidate($token, $attributes)
315
+ {
316
+ $result = self::update($token, $attributes);
317
+ return self::returnObjectOrThrowException(__CLASS__, $result);
318
+ }
319
+ /**
320
+ *
321
+ * @access public
322
+ * @param none
323
+ * @return string
324
+ */
325
+ public static function updateCreditCardUrl()
326
+ {
327
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
328
+ return Braintree_Configuration::merchantUrl() .
329
+ '/payment_methods/all/update_via_transparent_redirect_request';
330
+ }
331
+
332
+ /**
333
+ * update a customer from a TransparentRedirect operation
334
+ *
335
+ * @access public
336
+ * @param array $attribs
337
+ * @return object
338
+ */
339
+ public static function updateFromTransparentRedirect($queryString)
340
+ {
341
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
342
+ $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
343
+ $queryString
344
+ );
345
+ return self::_doUpdate(
346
+ 'post',
347
+ '/payment_methods/all/confirm_transparent_redirect_request',
348
+ array('id' => $params['id'])
349
+ );
350
+ }
351
+
352
+ /* instance methods */
353
+ /**
354
+ * returns false if default is null or false
355
+ *
356
+ * @return boolean
357
+ */
358
+ public function isDefault()
359
+ {
360
+ return $this->default;
361
+ }
362
+
363
+ /**
364
+ * checks whether the card is expired based on the current date
365
+ *
366
+ * @return boolean
367
+ */
368
+ public function isExpired()
369
+ {
370
+ return $this->expired;
371
+ }
372
+
373
+ public static function delete($token)
374
+ {
375
+ self::_validateId($token);
376
+ Braintree_Http::delete('/payment_methods/' . $token);
377
+ return new Braintree_Result_Successful();
378
+ }
379
+
380
+ /**
381
+ * sets instance properties from an array of values
382
+ *
383
+ * @access protected
384
+ * @param array $creditCardAttribs array of creditcard data
385
+ * @return none
386
+ */
387
+ protected function _initialize($creditCardAttribs)
388
+ {
389
+ // set the attributes
390
+ $this->_attributes = $creditCardAttribs;
391
+
392
+ // map each address into its own object
393
+ $billingAddress = isset($creditCardAttribs['billingAddress']) ?
394
+ Braintree_Address::factory($creditCardAttribs['billingAddress']) :
395
+ null;
396
+
397
+ $subscriptionArray = array();
398
+ if (isset($creditCardAttribs['subscriptions'])) {
399
+ foreach ($creditCardAttribs['subscriptions'] AS $subscription) {
400
+ $subscriptionArray[] = Braintree_Subscription::factory($subscription);
401
+ }
402
+ }
403
+
404
+ $this->_set('subscriptions', $subscriptionArray);
405
+ $this->_set('billingAddress', $billingAddress);
406
+ $this->_set('expirationDate', $this->expirationMonth . '/' . $this->expirationYear);
407
+ $this->_set('maskedNumber', $this->bin . '******' . $this->last4);
408
+ }
409
+
410
+ /**
411
+ * returns false if comparing object is not a Braintree_CreditCard,
412
+ * or is a Braintree_CreditCard with a different id
413
+ *
414
+ * @param object $otherCreditCard customer to compare against
415
+ * @return boolean
416
+ */
417
+ public function isEqual($otherCreditCard)
418
+ {
419
+ return !($otherCreditCard instanceof Braintree_CreditCard) ? false : $this->token === $otherCreditCard->token;
420
+ }
421
+
422
+ private static function baseOptions()
423
+ {
424
+ return array('makeDefault', 'verificationMerchantAccountId', 'verifyCard', 'venmoSdkSession');
425
+ }
426
+
427
+ private static function baseSignature($options)
428
+ {
429
+ return array(
430
+ 'billingAddressId', 'cardholderName', 'cvv', 'number',
431
+ 'expirationDate', 'expirationMonth', 'expirationYear', 'token', 'venmoSdkPaymentMethodCode',
432
+ array('options' => $options),
433
+ array(
434
+ 'billingAddress' => array(
435
+ 'firstName',
436
+ 'lastName',
437
+ 'company',
438
+ 'countryCodeAlpha2',
439
+ 'countryCodeAlpha3',
440
+ 'countryCodeNumeric',
441
+ 'countryName',
442
+ 'extendedAddress',
443
+ 'locality',
444
+ 'region',
445
+ 'postalCode',
446
+ 'streetAddress'
447
+ ),
448
+ ),
449
+ );
450
+ }
451
+
452
+ public static function createSignature()
453
+ {
454
+ $options = self::baseOptions();
455
+ $options[] = "failOnDuplicatePaymentMethod";
456
+ $signature = self::baseSignature($options);
457
+ $signature[] = 'customerId';
458
+ return $signature;
459
+ }
460
+
461
+ public static function updateSignature()
462
+ {
463
+ $signature = self::baseSignature(self::baseOptions());
464
+
465
+ $updateExistingBillingSignature = array(
466
+ array(
467
+ 'options' => array(
468
+ 'updateExisting'
469
+ )
470
+ )
471
+ );
472
+
473
+ foreach($signature AS $key => $value) {
474
+ if(is_array($value) and array_key_exists('billingAddress', $value)) {
475
+ $signature[$key]['billingAddress'] = array_merge_recursive($value['billingAddress'], $updateExistingBillingSignature);
476
+ }
477
+ }
478
+
479
+ return $signature;
480
+ }
481
+
482
+ /**
483
+ * sends the create request to the gateway
484
+ *
485
+ * @ignore
486
+ * @param string $url
487
+ * @param array $params
488
+ * @return mixed
489
+ */
490
+ public static function _doCreate($url, $params)
491
+ {
492
+ $response = Braintree_Http::post($url, $params);
493
+
494
+ return self::_verifyGatewayResponse($response);
495
+ }
496
+
497
+ /**
498
+ * create a printable representation of the object as:
499
+ * ClassName[property=value, property=value]
500
+ * @return string
501
+ */
502
+ public function __toString()
503
+ {
504
+ return __CLASS__ . '[' .
505
+ Braintree_Util::attributesToString($this->_attributes) .']';
506
+ }
507
+
508
+ /**
509
+ * verifies that a valid credit card token is being used
510
+ * @ignore
511
+ * @param string $token
512
+ * @throws InvalidArgumentException
513
+ */
514
+ private static function _validateId($token = null)
515
+ {
516
+ if (empty($token)) {
517
+ throw new InvalidArgumentException(
518
+ 'expected credit card id to be set'
519
+ );
520
+ }
521
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $token)) {
522
+ throw new InvalidArgumentException(
523
+ $token . ' is an invalid credit card id.'
524
+ );
525
+ }
526
+ }
527
+
528
+ /**
529
+ * sends the update request to the gateway
530
+ *
531
+ * @ignore
532
+ * @param string $url
533
+ * @param array $params
534
+ * @return mixed
535
+ */
536
+ private static function _doUpdate($httpVerb, $url, $params)
537
+ {
538
+ $response = Braintree_Http::$httpVerb($url, $params);
539
+ return self::_verifyGatewayResponse($response);
540
+ }
541
+
542
+ /**
543
+ * generic method for validating incoming gateway responses
544
+ *
545
+ * creates a new Braintree_CreditCard object and encapsulates
546
+ * it inside a Braintree_Result_Successful object, or
547
+ * encapsulates a Braintree_Errors object inside a Result_Error
548
+ * alternatively, throws an Unexpected exception if the response is invalid.
549
+ *
550
+ * @ignore
551
+ * @param array $response gateway response values
552
+ * @return object Result_Successful or Result_Error
553
+ * @throws Braintree_Exception_Unexpected
554
+ */
555
+ private static function _verifyGatewayResponse($response)
556
+ {
557
+ if (isset($response['creditCard'])) {
558
+ // return a populated instance of Braintree_Address
559
+ return new Braintree_Result_Successful(
560
+ self::factory($response['creditCard'])
561
+ );
562
+ } else if (isset($response['apiErrorResponse'])) {
563
+ return new Braintree_Result_Error($response['apiErrorResponse']);
564
+ } else {
565
+ throw new Braintree_Exception_Unexpected(
566
+ "Expected address or apiErrorResponse"
567
+ );
568
+ }
569
+ }
570
+
571
+ /**
572
+ * factory method: returns an instance of Braintree_CreditCard
573
+ * to the requesting method, with populated properties
574
+ *
575
+ * @ignore
576
+ * @return object instance of Braintree_CreditCard
577
+ */
578
+ public static function factory($attributes)
579
+ {
580
+ $defaultAttributes = array(
581
+ 'bin' => '',
582
+ 'expirationMonth' => '',
583
+ 'expirationYear' => '',
584
+ 'last4' => '',
585
+ );
586
+
587
+ $instance = new self();
588
+ $instance->_initialize(array_merge($defaultAttributes, $attributes));
589
+ return $instance;
590
+ }
591
+ }
includes/lib/Braintree/Braintree/CreditCardVerification.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_CreditCardVerification extends Braintree_Result_CreditCardVerification
3
+ {
4
+ public static function factory($attributes)
5
+ {
6
+ $instance = new self($attributes);
7
+ return $instance;
8
+ }
9
+
10
+ public static function fetch($query, $ids)
11
+ {
12
+ $criteria = array();
13
+ foreach ($query as $term) {
14
+ $criteria[$term->name] = $term->toparam();
15
+ }
16
+ $criteria["ids"] = Braintree_CreditCardVerificationSearch::ids()->in($ids)->toparam();
17
+ $response = braintree_http::post('/verifications/advanced_search', array('search' => $criteria));
18
+
19
+ return braintree_util::extractattributeasarray(
20
+ $response['creditCardVerifications'],
21
+ 'verification'
22
+ );
23
+ }
24
+
25
+ public static function search($query)
26
+ {
27
+ $criteria = array();
28
+ foreach ($query as $term) {
29
+ $criteria[$term->name] = $term->toparam();
30
+ }
31
+
32
+ $response = braintree_http::post('/verifications/advanced_search_ids', array('search' => $criteria));
33
+ $pager = array(
34
+ 'className' => __CLASS__,
35
+ 'classMethod' => 'fetch',
36
+ 'methodArgs' => array($query)
37
+ );
38
+
39
+ return new Braintree_ResourceCollection($response, $pager);
40
+ }
41
+ }
includes/lib/Braintree/Braintree/CreditCardVerificationSearch.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_CreditCardVerificationSearch
3
+ {
4
+ static function id() { return new Braintree_TextNode('id'); }
5
+ static function creditCardCardholderName() { return new Braintree_TextNode('credit_card_cardholder_name'); }
6
+
7
+ static function creditCardExpirationDate() { return new Braintree_EqualityNode('credit_card_expiration_date'); }
8
+ static function creditCardNumber() { return new Braintree_PartialMatchNode('credit_card_number'); }
9
+
10
+ static function ids() { return new Braintree_MultipleValueNode('ids'); }
11
+
12
+ static function creditCardCardType()
13
+ {
14
+ return new Braintree_MultipleValueNode("credit_card_card_type", array(
15
+ Braintree_CreditCard::AMEX,
16
+ Braintree_CreditCard::CARTE_BLANCHE,
17
+ Braintree_CreditCard::CHINA_UNION_PAY,
18
+ Braintree_CreditCard::DINERS_CLUB_INTERNATIONAL,
19
+ Braintree_CreditCard::DISCOVER,
20
+ Braintree_CreditCard::JCB,
21
+ Braintree_CreditCard::LASER,
22
+ Braintree_CreditCard::MAESTRO,
23
+ Braintree_CreditCard::MASTER_CARD,
24
+ Braintree_CreditCard::SOLO,
25
+ Braintree_CreditCard::SWITCH_TYPE,
26
+ Braintree_CreditCard::VISA,
27
+ Braintree_CreditCard::UNKNOWN
28
+ ));
29
+ }
30
+
31
+
32
+ static function createdAt() { return new Braintree_RangeNode("created_at"); }
33
+ }
34
+
includes/lib/Braintree/Braintree/Customer.php ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Customer module
4
+ *
5
+ * @package Braintree
6
+ * @category Resources
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates and manages Customers
12
+ *
13
+ * <b>== More information ==</b>
14
+ *
15
+ * For more detailed information on Customers, see {@link http://www.braintreepayments.com/gateway/customer-api http://www.braintreepaymentsolutions.com/gateway/customer-api}
16
+ *
17
+ * @package Braintree
18
+ * @category Resources
19
+ * @copyright 2010 Braintree Payment Solutions
20
+ *
21
+ * @property-read array $addresses
22
+ * @property-read string $company
23
+ * @property-read string $createdAt
24
+ * @property-read array $creditCards
25
+ * @property-read array $customFields custom fields passed with the request
26
+ * @property-read string $email
27
+ * @property-read string $fax
28
+ * @property-read string $firstName
29
+ * @property-read string $id
30
+ * @property-read string $lastName
31
+ * @property-read string $phone
32
+ * @property-read string $updatedAt
33
+ * @property-read string $website
34
+ */
35
+ class Braintree_Customer extends Braintree
36
+ {
37
+ public static function all()
38
+ {
39
+ $response = braintree_http::post('/customers/advanced_search_ids');
40
+ $pager = array(
41
+ 'className' => __CLASS__,
42
+ 'classMethod' => 'fetch',
43
+ 'methodArgs' => array(array())
44
+ );
45
+
46
+ return new Braintree_ResourceCollection($response, $pager);
47
+ }
48
+
49
+ public static function fetch($query, $ids)
50
+ {
51
+ $criteria = array();
52
+ foreach ($query as $term) {
53
+ $criteria[$term->name] = $term->toparam();
54
+ }
55
+ $criteria["ids"] = Braintree_CustomerSearch::ids()->in($ids)->toparam();
56
+ $response = braintree_http::post('/customers/advanced_search', array('search' => $criteria));
57
+
58
+ return braintree_util::extractattributeasarray(
59
+ $response['customers'],
60
+ 'customer'
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Creates a customer using the given +attributes+. If <tt>:id</tt> is not passed,
66
+ * the gateway will generate it.
67
+ *
68
+ * <code>
69
+ * $result = Braintree_Customer::create(array(
70
+ * 'first_name' => 'John',
71
+ * 'last_name' => 'Smith',
72
+ * 'company' => 'Smith Co.',
73
+ * 'email' => 'john@smith.com',
74
+ * 'website' => 'www.smithco.com',
75
+ * 'fax' => '419-555-1234',
76
+ * 'phone' => '614-555-1234'
77
+ * ));
78
+ * if($result->success) {
79
+ * echo 'Created customer ' . $result->customer->id;
80
+ * } else {
81
+ * echo 'Could not create customer, see result->errors';
82
+ * }
83
+ * </code>
84
+ *
85
+ * @access public
86
+ * @param array $attribs
87
+ * @return object Result, either Successful or Error
88
+ */
89
+ public static function create($attribs = array())
90
+ {
91
+ Braintree_Util::verifyKeys(self::createSignature(), $attribs);
92
+ return self::_doCreate('/customers', array('customer' => $attribs));
93
+ }
94
+
95
+ /**
96
+ * attempts the create operation assuming all data will validate
97
+ * returns a Braintree_Customer object instead of a Result
98
+ *
99
+ * @access public
100
+ * @param array $attribs
101
+ * @return object
102
+ * @throws Braintree_Exception_ValidationError
103
+ */
104
+ public static function createNoValidate($attribs = array())
105
+ {
106
+ $result = self::create($attribs);
107
+ return self::returnObjectOrThrowException(__CLASS__, $result);
108
+ }
109
+ /**
110
+ * create a customer from a TransparentRedirect operation
111
+ *
112
+ * @access public
113
+ * @param array $attribs
114
+ * @return object
115
+ */
116
+ public static function createFromTransparentRedirect($queryString)
117
+ {
118
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
119
+ $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
120
+ $queryString
121
+ );
122
+ return self::_doCreate(
123
+ '/customers/all/confirm_transparent_redirect_request',
124
+ array('id' => $params['id'])
125
+ );
126
+ }
127
+
128
+ /**
129
+ *
130
+ * @access public
131
+ * @param none
132
+ * @return string
133
+ */
134
+ public static function createCustomerUrl()
135
+ {
136
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
137
+ return Braintree_Configuration::merchantUrl() .
138
+ '/customers/all/create_via_transparent_redirect_request';
139
+ }
140
+
141
+
142
+ /**
143
+ * creates a full array signature of a valid create request
144
+ * @return array gateway create request format
145
+ */
146
+ public static function createSignature()
147
+ {
148
+
149
+ $creditCardSignature = Braintree_CreditCard::createSignature();
150
+ unset($creditCardSignature['customerId']);
151
+ $signature = array(
152
+ 'id', 'company', 'email', 'fax', 'firstName',
153
+ 'lastName', 'phone', 'website',
154
+ array('creditCard' => $creditCardSignature),
155
+ array('customFields' => array('_anyKey_')),
156
+ );
157
+ return $signature;
158
+ }
159
+
160
+ /**
161
+ * creates a full array signature of a valid update request
162
+ * @return array update request format
163
+ */
164
+ public static function updateSignature()
165
+ {
166
+ $creditCardSignature = Braintree_CreditCard::updateSignature();
167
+
168
+ foreach($creditCardSignature AS $key => $value) {
169
+ if(is_array($value) and array_key_exists('options', $value)) {
170
+ array_push($creditCardSignature[$key]['options'], 'updateExistingToken');
171
+ }
172
+ }
173
+
174
+ $signature = array(
175
+ 'id', 'company', 'email', 'fax', 'firstName',
176
+ 'lastName', 'phone', 'website',
177
+ array('creditCard' => $creditCardSignature),
178
+ array('customFields' => array('_anyKey_')),
179
+ );
180
+ return $signature;
181
+ }
182
+
183
+
184
+ /**
185
+ * find a customer by id
186
+ *
187
+ * @access public
188
+ * @param string id customer Id
189
+ * @return object Braintree_Customer
190
+ * @throws Braintree_Exception_NotFound
191
+ */
192
+ public static function find($id)
193
+ {
194
+ self::_validateId($id);
195
+ try {
196
+ $response = Braintree_Http::get('/customers/'.$id);
197
+ return self::factory($response['customer']);
198
+ } catch (Braintree_Exception_NotFound $e) {
199
+ throw new Braintree_Exception_NotFound(
200
+ 'customer with id ' . $id . ' not found'
201
+ );
202
+ }
203
+
204
+ }
205
+
206
+ /**
207
+ * credit a customer for the passed transaction
208
+ *
209
+ * @access public
210
+ * @param array $attribs
211
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
212
+ */
213
+ public static function credit($customerId, $transactionAttribs)
214
+ {
215
+ self::_validateId($customerId);
216
+ return Braintree_Transaction::credit(
217
+ array_merge($transactionAttribs,
218
+ array('customerId' => $customerId)
219
+ )
220
+ );
221
+ }
222
+
223
+ /**
224
+ * credit a customer, assuming validations will pass
225
+ *
226
+ * returns a Braintree_Transaction object on success
227
+ *
228
+ * @access public
229
+ * @param array $attribs
230
+ * @return object Braintree_Transaction
231
+ * @throws Braintree_Exception_ValidationError
232
+ */
233
+ public static function creditNoValidate($customerId, $transactionAttribs)
234
+ {
235
+ $result = self::credit($customerId, $transactionAttribs);
236
+ return self::returnObjectOrThrowException('Braintree_Transaction', $result);
237
+ }
238
+
239
+ /**
240
+ * delete a customer by id
241
+ *
242
+ * @param string $customerId
243
+ */
244
+ public static function delete($customerId)
245
+ {
246
+ self::_validateId($customerId);
247
+ Braintree_Http::delete('/customers/' . $customerId);
248
+ return new Braintree_Result_Successful();
249
+ }
250
+
251
+ /**
252
+ * create a new sale for a customer
253
+ *
254
+ * @param string $customerId
255
+ * @param array $transactionAttribs
256
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
257
+ * @see Braintree_Transaction::sale()
258
+ */
259
+ public static function sale($customerId, $transactionAttribs)
260
+ {
261
+ self::_validateId($customerId);
262
+ return Braintree_Transaction::sale(
263
+ array_merge($transactionAttribs,
264
+ array('customerId' => $customerId)
265
+ )
266
+ );
267
+ }
268
+
269
+ /**
270
+ * create a new sale for a customer, assuming validations will pass
271
+ *
272
+ * returns a Braintree_Transaction object on success
273
+ * @access public
274
+ * @param string $customerId
275
+ * @param array $transactionAttribs
276
+ * @return object Braintree_Transaction
277
+ * @throws Braintree_Exception_ValidationsFailed
278
+ * @see Braintree_Transaction::sale()
279
+ */
280
+ public static function saleNoValidate($customerId, $transactionAttribs)
281
+ {
282
+ $result = self::sale($customerId, $transactionAttribs);
283
+ return self::returnObjectOrThrowException('Braintree_Transaction', $result);
284
+ }
285
+
286
+ /**
287
+ * Returns a ResourceCollection of customers matching the search query.
288
+ *
289
+ * If <b>query</b> is a string, the search will be a basic search.
290
+ * If <b>query</b> is a hash, the search will be an advanced search.
291
+ * For more detailed information and examples, see {@link http://www.braintreepayments.com/gateway/customer-api#searching http://www.braintreepaymentsolutions.com/gateway/customer-api}
292
+ *
293
+ * @param mixed $query search query
294
+ * @param array $options options such as page number
295
+ * @return object Braintree_ResourceCollection
296
+ * @throws InvalidArgumentException
297
+ */
298
+ public static function search($query)
299
+ {
300
+ $criteria = array();
301
+ foreach ($query as $term) {
302
+ $criteria[$term->name] = $term->toparam();
303
+ }
304
+
305
+ $response = braintree_http::post('/customers/advanced_search_ids', array('search' => $criteria));
306
+ $pager = array(
307
+ 'className' => __CLASS__,
308
+ 'classMethod' => 'fetch',
309
+ 'methodArgs' => array($query)
310
+ );
311
+
312
+ return new Braintree_ResourceCollection($response, $pager);
313
+ }
314
+
315
+ /**
316
+ * updates the customer record
317
+ *
318
+ * if calling this method in static context, customerId
319
+ * is the 2nd attribute. customerId is not sent in object context.
320
+ *
321
+ * @access public
322
+ * @param array $attributes
323
+ * @param string $customerId (optional)
324
+ * @return object Braintree_Result_Successful or Braintree_Result_Error
325
+ */
326
+ public static function update($customerId, $attributes)
327
+ {
328
+ Braintree_Util::verifyKeys(self::updateSignature(), $attributes);
329
+ self::_validateId($customerId);
330
+ return self::_doUpdate(
331
+ 'put',
332
+ '/customers/' . $customerId,
333
+ array('customer' => $attributes)
334
+ );
335
+ }
336
+
337
+ /**
338
+ * update a customer record, assuming validations will pass
339
+ *
340
+ * if calling this method in static context, customerId
341
+ * is the 2nd attribute. customerId is not sent in object context.
342
+ * returns a Braintree_Customer object on success
343
+ *
344
+ * @access public
345
+ * @param array $attributes
346
+ * @param string $customerId
347
+ * @return object Braintree_Customer
348
+ * @throws Braintree_Exception_ValidationsFailed
349
+ */
350
+ public static function updateNoValidate($customerId, $attributes)
351
+ {
352
+ $result = self::update($customerId, $attributes);
353
+ return self::returnObjectOrThrowException(__CLASS__, $result);
354
+ }
355
+ /**
356
+ *
357
+ * @access public
358
+ * @param none
359
+ * @return string
360
+ */
361
+ public static function updateCustomerUrl()
362
+ {
363
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
364
+ return Braintree_Configuration::merchantUrl() .
365
+ '/customers/all/update_via_transparent_redirect_request';
366
+ }
367
+
368
+ /**
369
+ * update a customer from a TransparentRedirect operation
370
+ *
371
+ * @access public
372
+ * @param array $attribs
373
+ * @return object
374
+ */
375
+ public static function updateFromTransparentRedirect($queryString)
376
+ {
377
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
378
+ $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
379
+ $queryString
380
+ );
381
+ return self::_doUpdate(
382
+ 'post',
383
+ '/customers/all/confirm_transparent_redirect_request',
384
+ array('id' => $params['id'])
385
+ );
386
+ }
387
+
388
+ /* instance methods */
389
+
390
+ /**
391
+ * sets instance properties from an array of values
392
+ *
393
+ * @ignore
394
+ * @access protected
395
+ * @param array $customerAttribs array of customer data
396
+ * @return none
397
+ */
398
+ protected function _initialize($customerAttribs)
399
+ {
400
+ // set the attributes
401
+ $this->_attributes = $customerAttribs;
402
+
403
+ // map each address into its own object
404
+ $addressArray = array();
405
+ if (isset($customerAttribs['addresses'])) {
406
+
407
+ foreach ($customerAttribs['addresses'] AS $address) {
408
+ $addressArray[] = Braintree_Address::factory($address);
409
+ }
410
+ }
411
+ $this->_set('addresses', $addressArray);
412
+
413
+ // map each creditcard into its own object
414
+ $ccArray = array();
415
+ if (isset($customerAttribs['creditCards'])) {
416
+ foreach ($customerAttribs['creditCards'] AS $creditCard) {
417
+ $ccArray[] = Braintree_CreditCard::factory($creditCard);
418
+ }
419
+ }
420
+ $this->_set('creditCards', $ccArray);
421
+
422
+ }
423
+
424
+ /**
425
+ * returns a string representation of the customer
426
+ * @return string
427
+ */
428
+ public function __toString()
429
+ {
430
+ return __CLASS__ . '[' .
431
+ Braintree_Util::attributesToString($this->_attributes) .']';
432
+ }
433
+
434
+ /**
435
+ * returns false if comparing object is not a Braintree_Customer,
436
+ * or is a Braintree_Customer with a different id
437
+ *
438
+ * @param object $otherCust customer to compare against
439
+ * @return boolean
440
+ */
441
+ public function isEqual($otherCust)
442
+ {
443
+ return !($otherCust instanceof Braintree_Customer) ? false : $this->id === $otherCust->id;
444
+ }
445
+
446
+ /* private class properties */
447
+
448
+ /**
449
+ * @access protected
450
+ * @var array registry of customer data
451
+ */
452
+ protected $_attributes = array(
453
+ 'addresses' => '',
454
+ 'company' => '',
455
+ 'creditCards' => '',
456
+ 'email' => '',
457
+ 'fax' => '',
458
+ 'firstName' => '',
459
+ 'id' => '',
460
+ 'lastName' => '',
461
+ 'phone' => '',
462
+ 'createdAt' => '',
463
+ 'updatedAt' => '',
464
+ 'website' => '',
465
+ );
466
+
467
+ /**
468
+ * sends the create request to the gateway
469
+ *
470
+ * @ignore
471
+ * @param string $url
472
+ * @param array $params
473
+ * @return mixed
474
+ */
475
+ public static function _doCreate($url, $params)
476
+ {
477
+ $response = Braintree_Http::post($url, $params);
478
+
479
+ return self::_verifyGatewayResponse($response);
480
+ }
481
+
482
+ /**
483
+ * verifies that a valid customer id is being used
484
+ * @ignore
485
+ * @param string customer id
486
+ * @throws InvalidArgumentException
487
+ */
488
+ private static function _validateId($id = null) {
489
+ if (empty($id)) {
490
+ throw new InvalidArgumentException(
491
+ 'expected customer id to be set'
492
+ );
493
+ }
494
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
495
+ throw new InvalidArgumentException(
496
+ $id . ' is an invalid customer id.'
497
+ );
498
+ }
499
+ }
500
+
501
+
502
+ /* private class methods */
503
+
504
+ /**
505
+ * sends the update request to the gateway
506
+ *
507
+ * @ignore
508
+ * @param string $url
509
+ * @param array $params
510
+ * @return mixed
511
+ */
512
+ private static function _doUpdate($httpVerb, $url, $params)
513
+ {
514
+ $response = Braintree_Http::$httpVerb($url, $params);
515
+
516
+ return self::_verifyGatewayResponse($response);
517
+ }
518
+
519
+ /**
520
+ * generic method for validating incoming gateway responses
521
+ *
522
+ * creates a new Braintree_Customer object and encapsulates
523
+ * it inside a Braintree_Result_Successful object, or
524
+ * encapsulates a Braintree_Errors object inside a Result_Error
525
+ * alternatively, throws an Unexpected exception if the response is invalid.
526
+ *
527
+ * @ignore
528
+ * @param array $response gateway response values
529
+ * @return object Result_Successful or Result_Error
530
+ * @throws Braintree_Exception_Unexpected
531
+ */
532
+ private static function _verifyGatewayResponse($response)
533
+ {
534
+ if (isset($response['customer'])) {
535
+ // return a populated instance of Braintree_Customer
536
+ return new Braintree_Result_Successful(
537
+ self::factory($response['customer'])
538
+ );
539
+ } else if (isset($response['apiErrorResponse'])) {
540
+ return new Braintree_Result_Error($response['apiErrorResponse']);
541
+ } else {
542
+ throw new Braintree_Exception_Unexpected(
543
+ "Expected customer or apiErrorResponse"
544
+ );
545
+ }
546
+ }
547
+
548
+ /**
549
+ * factory method: returns an instance of Braintree_Customer
550
+ * to the requesting method, with populated properties
551
+ *
552
+ * @ignore
553
+ * @return object instance of Braintree_Customer
554
+ */
555
+ public static function factory($attributes)
556
+ {
557
+ $instance = new self();
558
+ $instance->_initialize($attributes);
559
+ return $instance;
560
+ }
561
+
562
+ }
includes/lib/Braintree/Braintree/CustomerSearch.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_CustomerSearch
3
+ {
4
+ static function addressCountryName() { return new Braintree_TextNode('address_country_name'); }
5
+ static function addressExtendedAddress() { return new Braintree_TextNode('address_extended_address'); }
6
+ static function addressFirstName() { return new Braintree_TextNode('address_first_name'); }
7
+ static function addressLastName() { return new Braintree_TextNode('address_last_name'); }
8
+ static function addressLocality() { return new Braintree_TextNode('address_locality'); }
9
+ static function addressPostalCode() { return new Braintree_TextNode('address_postal_code'); }
10
+ static function addressRegion() { return new Braintree_TextNode('address_region'); }
11
+ static function addressStreetAddress() { return new Braintree_TextNode('address_street_address'); }
12
+ static function cardholderName() { return new Braintree_TextNode('cardholder_name'); }
13
+ static function company() { return new Braintree_TextNode('company'); }
14
+ static function email() { return new Braintree_TextNode('email'); }
15
+ static function fax() { return new Braintree_TextNode('fax'); }
16
+ static function firstName() { return new Braintree_TextNode('first_name'); }
17
+ static function id() { return new Braintree_TextNode('id'); }
18
+ static function lastName() { return new Braintree_TextNode('last_name'); }
19
+ static function paymentMethodToken() { return new Braintree_TextNode('payment_method_token'); }
20
+ static function paymentMethodTokenWithDuplicates() { return new Braintree_IsNode('payment_method_token_with_duplicates'); }
21
+ static function phone() { return new Braintree_TextNode('phone'); }
22
+ static function website() { return new Braintree_TextNode('website'); }
23
+
24
+ static function creditCardExpirationDate() { return new Braintree_EqualityNode('credit_card_expiration_date'); }
25
+ static function creditCardNumber() { return new Braintree_PartialMatchNode('credit_card_number'); }
26
+
27
+ static function ids() { return new Braintree_MultipleValueNode('ids'); }
28
+
29
+ static function createdAt() { return new Braintree_RangeNode("created_at"); }
30
+ }
31
+
includes/lib/Braintree/Braintree/Descriptor.php ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <?php
2
+ class Braintree_Descriptor extends Braintree_Instance
3
+ {
4
+ }
includes/lib/Braintree/Braintree/Digest.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Digest encryption module
4
+ *
5
+ * @copyright 2010 Braintree Payment Solutions
6
+ */
7
+
8
+ /**
9
+ * Digest creates an HMAC-SHA1 hash for encrypting messages
10
+ *
11
+ * @copyright 2010 Braintree Payment Solutions
12
+ */
13
+ class Braintree_Digest
14
+ {
15
+ public static function hexDigest($string)
16
+ {
17
+ if(function_exists('hash_hmac')) {
18
+ return self::_builtInHmacSha1($string, Braintree_Configuration::privateKey());
19
+ } else {
20
+ return self::_hmacSha1($string, Braintree_Configuration::privateKey());
21
+ }
22
+ }
23
+
24
+ public static function secureCompare($left, $right)
25
+ {
26
+ if (strlen($left) != strlen($right)) {
27
+ return false;
28
+ }
29
+
30
+ $leftBytes = unpack("C*", $left);
31
+ $rightBytes = unpack("C*", $right);
32
+
33
+ $result = 0;
34
+ for ($i = 0; $i < strlen($left); $i++) {
35
+ $result = $result | ($left[$i] ^ $right[$i]);
36
+ }
37
+ return $result == 0;
38
+ }
39
+
40
+ public static function _builtInHmacSha1($message, $key)
41
+ {
42
+ return hash_hmac('sha1', $message, sha1(Braintree_Configuration::privateKey(), true));
43
+ }
44
+
45
+ public static function _hmacSha1($message, $key)
46
+ {
47
+ $pack = 'H40';
48
+ $keyDigest = sha1($key,true);
49
+ $innerPad = str_repeat(chr(0x36), 64);
50
+ $outerPad = str_repeat(chr(0x5C), 64);
51
+
52
+ for ($i = 0; $i < 20; $i++) {
53
+ $innerPad{$i} = $keyDigest{$i} ^ $innerPad{$i};
54
+ $outerPad{$i} = $keyDigest{$i} ^ $outerPad{$i};
55
+ }
56
+
57
+ return sha1($outerPad.pack($pack, sha1($innerPad.$message)));
58
+ }
59
+ }
includes/lib/Braintree/Braintree/Discount.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_Discount extends Braintree_Modification
3
+ {
4
+ public static function all()
5
+ {
6
+ $response = Braintree_Http::get('/discounts');
7
+
8
+ $discounts = array("discount" => $response['discounts']);
9
+
10
+ return Braintree_Util::extractAttributeAsArray(
11
+ $discounts,
12
+ 'discount'
13
+ );
14
+ }
15
+ }
includes/lib/Braintree/Braintree/EqualityNode.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_EqualityNode extends Braintree_IsNode
4
+ {
5
+ function isNot($value)
6
+ {
7
+ $this->searchTerms['is_not'] = strval($value);
8
+ return $this;
9
+ }
10
+ }
includes/lib/Braintree/Braintree/Error/Codes.php ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * validation Error codes and messages
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Errors
7
+ * @category Validation
8
+ * @copyright 2010 Braintree Payment Solutions
9
+ */
10
+
11
+ /**
12
+ *
13
+ * Validation Error codes and messages
14
+ *
15
+ * ErrorCodes class provides constants for validation errors.
16
+ * The constants should be used to check for a specific validation
17
+ * error in a ValidationErrorCollection.
18
+ * The error messages returned from the server may change,
19
+ * but the codes will remain the same.
20
+ *
21
+ * @package Braintree
22
+ * @subpackage Errors
23
+ * @category Validation
24
+ * @copyright 2010 Braintree Payment Solutions
25
+ */
26
+ class Braintree_Error_Codes
27
+ {
28
+ const ADDRESS_CANNOT_BE_BLANK = '81801';
29
+ const ADDRESS_COMPANY_IS_TOO_LONG = '81802';
30
+ const ADDRESS_COUNTRY_CODE_ALPHA2_IS_NOT_ACCEPTED = '91814';
31
+ const ADDRESS_COUNTRY_CODE_ALPHA3_IS_NOT_ACCEPTED = '91816';
32
+ const ADDRESS_COUNTRY_CODE_NUMERIC_IS_NOT_ACCEPTED = '91817';
33
+ const ADDRESS_COUNTRY_NAME_IS_NOT_ACCEPTED = '91803';
34
+ const ADDRESS_EXTENDED_ADDRESS_IS_TOO_LONG = '81804';
35
+ const ADDRESS_FIRST_NAME_IS_TOO_LONG = '81805';
36
+ const ADDRESS_INCONSISTENT_COUNTRY = '91815';
37
+ const ADDRESS_LAST_NAME_IS_TOO_LONG = '81806';
38
+ const ADDRESS_LOCALITY_IS_TOO_LONG = '81807';
39
+ const ADDRESS_POSTAL_CODE_INVALID_CHARACTERS = '81813';
40
+ const ADDRESS_POSTAL_CODE_IS_REQUIRED = '81808';
41
+ const ADDRESS_POSTAL_CODE_IS_TOO_LONG = '81809';
42
+ const ADDRESS_REGION_IS_TOO_LONG = '81810';
43
+ const ADDRESS_STREET_ADDRESS_IS_REQUIRED = '81811';
44
+ const ADDRESS_STREET_ADDRESS_IS_TOO_LONG = '81812';
45
+ const ADDRESS_TOO_MANY_ADDRESSES_PER_CUSTOMER = '91818';
46
+
47
+ const CREDIT_CARD_BILLING_ADDRESS_CONFLICT = '91701';
48
+ const CREDIT_CARD_BILLING_ADDRESS_ID_IS_INVALID = '91702';
49
+ const CREDIT_CARD_CARDHOLDER_NAME_IS_TOO_LONG = '81723';
50
+ const CREDIT_CARD_CREDIT_CARD_TYPE_IS_NOT_ACCEPTED = '81703';
51
+ const CREDIT_CARD_CREDIT_CARD_TYPE_IS_NOT_ACCEPTED_BY_SUBSCRIPTION_MERCHANT_ACCOUNT = '81718';
52
+ const CREDIT_CARD_CUSTOMER_ID_IS_INVALID = '91705';
53
+ const CREDIT_CARD_CUSTOMER_ID_IS_REQUIRED = '91704';
54
+ const CREDIT_CARD_CVV_IS_INVALID = '81707';
55
+ const CREDIT_CARD_CVV_IS_REQUIRED = '81706';
56
+ const CREDIT_CARD_DUPLICATE_CARD_EXISTS = '81724';
57
+ const CREDIT_CARD_EXPIRATION_DATE_CONFLICT = '91708';
58
+ const CREDIT_CARD_EXPIRATION_DATE_IS_INVALID = '81710';
59
+ const CREDIT_CARD_EXPIRATION_DATE_IS_REQUIRED = '81709';
60
+ const CREDIT_CARD_EXPIRATION_DATE_YEAR_IS_INVALID = '81711';
61
+ const CREDIT_CARD_EXPIRATION_MONTH_IS_INVALID = '81712';
62
+ const CREDIT_CARD_EXPIRATION_YEAR_IS_INVALID = '81713';
63
+ const CREDIT_CARD_NUMBER_INVALID_LENGTH = '81716';
64
+ const CREDIT_CARD_NUMBER_IS_INVALID = '81715';
65
+ const CREDIT_CARD_NUMBER_IS_REQUIRED = '81714';
66
+ const CREDIT_CARD_NUMBER_MUST_BE_TEST_NUMBER = '81717';
67
+ const CREDIT_CARD_OPTIONS_UPDATE_EXISTING_TOKEN_IS_INVALID = '91723';
68
+ const CREDIT_CARD_TOKEN_INVALID = '91718';
69
+ const CREDIT_CARD_TOKEN_IS_IN_USE = '91719';
70
+ const CREDIT_CARD_TOKEN_IS_NOT_ALLOWED = '91721';
71
+ const CREDIT_CARD_TOKEN_IS_REQUIRED = '91722';
72
+ const CREDIT_CARD_TOKEN_IS_TOO_LONG = '91720';
73
+
74
+ const CUSTOMER_COMPANY_IS_TOO_LONG = '81601';
75
+ const CUSTOMER_CUSTOM_FIELD_IS_INVALID = '91602';
76
+ const CUSTOMER_CUSTOM_FIELD_IS_TOO_LONG = '81603';
77
+ const CUSTOMER_EMAIL_IS_INVALID = '81604';
78
+ const CUSTOMER_EMAIL_IS_REQUIRED = '81606';
79
+ const CUSTOMER_EMAIL_IS_TOO_LONG = '81605';
80
+ const CUSTOMER_FAX_IS_TOO_LONG = '81607';
81
+ const CUSTOMER_FIRST_NAME_IS_TOO_LONG = '81608';
82
+ const CUSTOMER_ID_IS_INVAILD = '91610'; //Deprecated
83
+ const CUSTOMER_ID_IS_INVALID = '91610';
84
+ const CUSTOMER_ID_IS_IN_USE = '91609';
85
+ const CUSTOMER_ID_IS_NOT_ALLOWED = '91611';
86
+ const CUSTOMER_ID_IS_REQUIRED = '91613';
87
+ const CUSTOMER_ID_IS_TOO_LONG = '91612';
88
+ const CUSTOMER_LAST_NAME_IS_TOO_LONG = '81613';
89
+ const CUSTOMER_PHONE_IS_TOO_LONG = '81614';
90
+ const CUSTOMER_WEBSITE_IS_INVALID = '81616';
91
+ const CUSTOMER_WEBSITE_IS_TOO_LONG = '81615';
92
+
93
+ const DESCRIPTOR_NAME_FORMAT_IS_INVALID = '92201';
94
+ const DESCRIPTOR_PHONE_FORMAT_IS_INVALID = '92202';
95
+
96
+ const SETTLEMENT_BATCH_SUMMARY_SETTLEMENT_DATE_IS_INVALID = '82302';
97
+ const SETTLEMENT_BATCH_SUMMARY_SETTLEMENT_DATE_IS_REQUIRED = '82301';
98
+ const SETTLEMENT_BATCH_SUMMARY_CUSTOM_FIELD_IS_INVALID = '82303';
99
+
100
+ const SUBSCRIPTION_BILLING_DAY_OF_MONTH_CANNOT_BE_UPDATED = '91918';
101
+ const SUBSCRIPTION_BILLING_DAY_OF_MONTH_IS_INVALID = '91914';
102
+ const SUBSCRIPTION_BILLING_DAY_OF_MONTH_MUST_BE_NUMERIC = '91913';
103
+ const SUBSCRIPTION_CANNOT_ADD_DUPLICATE_ADDON_OR_DISCOUNT = '91911';
104
+ const SUBSCRIPTION_CANNOT_EDIT_CANCELED_SUBSCRIPTION = '81901';
105
+ const SUBSCRIPTION_CANNOT_EDIT_EXPIRED_SUBSCRIPTION = '81910';
106
+ const SUBSCRIPTION_CANNOT_EDIT_PRICE_CHANGING_FIELDS_ON_PAST_DUE_SUBSCRIPTION = '91920';
107
+ const SUBSCRIPTION_FIRST_BILLING_DATE_CANNOT_BE_IN_THE_PAST = '91916';
108
+ const SUBSCRIPTION_FIRST_BILLING_DATE_CANNOT_BE_UPDATED = '91919';
109
+ const SUBSCRIPTION_FIRST_BILLING_DATE_IS_INVALID = '91915';
110
+ const SUBSCRIPTION_ID_IS_IN_USE = '81902';
111
+ const SUBSCRIPTION_INCONSISTENT_NUMBER_OF_BILLING_CYCLES = '91908';
112
+ const SUBSCRIPTION_INCONSISTENT_START_DATE = '91917';
113
+ const SUBSCRIPTION_INVALID_REQUEST_FORMAT = '91921';
114
+ const SUBSCRIPTION_MERCHANT_ACCOUNT_ID_IS_INVALID = '91901';
115
+ const SUBSCRIPTION_MISMATCH_CURRENCY_ISO_CODE = '91923';
116
+ const SUBSCRIPTION_NUMBER_OF_BILLING_CYCLES_CANNOT_BE_BLANK = '91912';
117
+ const SUBSCRIPTION_NUMBER_OF_BILLING_CYCLES_IS_TOO_SMALL = '91909';
118
+ const SUBSCRIPTION_NUMBER_OF_BILLING_CYCLES_MUST_BE_GREATER_THAN_ZERO = '91907';
119
+ const SUBSCRIPTION_NUMBER_OF_BILLING_CYCLES_MUST_BE_NUMERIC = '91906';
120
+ const SUBSCRIPTION_PAYMENT_METHOD_TOKEN_CARD_TYPE_IS_NOT_ACCEPTED = '91902';
121
+ const SUBSCRIPTION_PAYMENT_METHOD_TOKEN_IS_INVALID = '91903';
122
+ const SUBSCRIPTION_PAYMENT_METHOD_TOKEN_NOT_ASSOCIATED_WITH_CUSTOMER = '91905';
123
+ const SUBSCRIPTION_PLAN_BILLING_FREQUENCY_CANNOT_BE_UPDATED = '91922';
124
+ const SUBSCRIPTION_PLAN_ID_IS_INVALID = '91904';
125
+ const SUBSCRIPTION_PRICE_CANNOT_BE_BLANK = '81903';
126
+ const SUBSCRIPTION_PRICE_FORMAT_IS_INVALID = '81904';
127
+ const SUBSCRIPTION_PRICE_IS_TOO_LARGE = '81923';
128
+ const SUBSCRIPTION_STATUS_IS_CANCELED = '81905';
129
+ const SUBSCRIPTION_TOKEN_FORMAT_IS_INVALID = '81906';
130
+ const SUBSCRIPTION_TRIAL_DURATION_FORMAT_IS_INVALID = '81907';
131
+ const SUBSCRIPTION_TRIAL_DURATION_IS_REQUIRED = '81908';
132
+ const SUBSCRIPTION_TRIAL_DURATION_UNIT_IS_INVALID = '81909';
133
+
134
+ const SUBSCRIPTION_MODIFICATION_AMOUNT_CANNOT_BE_BLANK = '92003';
135
+ const SUBSCRIPTION_MODIFICATION_AMOUNT_IS_INVALID = '92002';
136
+ const SUBSCRIPTION_MODIFICATION_AMOUNT_IS_TOO_LARGE = '92023';
137
+ const SUBSCRIPTION_MODIFICATION_CANNOT_EDIT_MODIFICATIONS_ON_PAST_DUE_SUBSCRIPTION = '92022';
138
+ const SUBSCRIPTION_MODIFICATION_CANNOT_UPDATE_AND_REMOVE = '92015';
139
+ const SUBSCRIPTION_MODIFICATION_EXISTING_ID_IS_INCORRECT_KIND = '92020';
140
+ const SUBSCRIPTION_MODIFICATION_EXISTING_ID_IS_INVALID = '92011';
141
+ const SUBSCRIPTION_MODIFICATION_EXISTING_ID_IS_REQUIRED = '92012';
142
+ const SUBSCRIPTION_MODIFICATION_ID_TO_REMOVE_IS_INCORRECT_KIND = '92021';
143
+ const SUBSCRIPTION_MODIFICATION_ID_TO_REMOVE_IS_NOT_PRESENT = '92016';
144
+ const SUBSCRIPTION_MODIFICATION_INCONSISTENT_NUMBER_OF_BILLING_CYCLES = '92018';
145
+ const SUBSCRIPTION_MODIFICATION_INHERITED_FROM_ID_IS_INVALID = '92013';
146
+ const SUBSCRIPTION_MODIFICATION_INHERITED_FROM_ID_IS_REQUIRED = '92014';
147
+ const SUBSCRIPTION_MODIFICATION_NUMBER_OF_BILLING_CYCLES_CANNOT_BE_BLANK = '92017';
148
+ const SUBSCRIPTION_MODIFICATION_NUMBER_OF_BILLING_CYCLES_IS_INVALID = '92005';
149
+ const SUBSCRIPTION_MODIFICATION_NUMBER_OF_BILLING_CYCLES_MUST_BE_GREATER_THAN_ZERO = '92019';
150
+ const SUBSCRIPTION_MODIFICATION_QUANTITY_CANNOT_BE_BLANK = '92004';
151
+ const SUBSCRIPTION_MODIFICATION_QUANTITY_IS_INVALID = '92001';
152
+ const SUBSCRIPTION_MODIFICATION_QUANTITY_MUST_BE_GREATER_THAN_ZERO = '92010';
153
+
154
+ const TRANSACTION_AMOUNT_CANNOT_BE_NEGATIVE = '81501';
155
+ const TRANSACTION_AMOUNT_IS_INVALID = '81503';
156
+ const TRANSACTION_AMOUNT_IS_REQUIRED = '81502';
157
+ const TRANSACTION_AMOUNT_IS_TOO_LARGE = '81528';
158
+ const TRANSACTION_AMOUNT_MUST_BE_GREATER_THAN_ZERO = '81531';
159
+ const TRANSACTION_BILLING_ADDRESS_CONFLICT = '91530';
160
+ const TRANSACTION_CANNOT_BE_VOIDED = '91504';
161
+ const TRANSACTION_CANNOT_CLONE_CREDIT = '91543';
162
+ const TRANSACTION_CANNOT_CLONE_TRANSACTION_WITH_VAULT_CREDIT_CARD = '91540';
163
+ const TRANSACTION_CANNOT_CLONE_UNSUCCESSFUL_TRANSACTION = '91542';
164
+ const TRANSACTION_CANNOT_CLONE_VOICE_AUTHORIZATIONS = '91541';
165
+ const TRANSACTION_CANNOT_REFUND_CREDIT = '91505';
166
+ const TRANSACTION_CANNOT_REFUND_UNLESS_SETTLED = '91506';
167
+ const TRANSACTION_CANNOT_REFUND_WITH_SUSPENDED_MERCHANT_ACCOUNT = '91538';
168
+ const TRANSACTION_CANNOT_SUBMIT_FOR_SETTLEMENT = '91507';
169
+ const TRANSACTION_CHANNEL_IS_TOO_LONG = '91550';
170
+ const TRANSACTION_CREDIT_CARD_IS_REQUIRED = '91508';
171
+ const TRANSACTION_CUSTOMER_DEFAULT_PAYMENT_METHOD_CARD_TYPE_IS_NOT_ACCEPTED = '81509';
172
+ const TRANSACTION_CUSTOMER_DOES_NOT_HAVE_CREDIT_CARD = '91511';
173
+ const TRANSACTION_CUSTOMER_ID_IS_INVALID = '91510';
174
+ const TRANSACTION_CUSTOM_FIELD_IS_INVALID = '91526';
175
+ const TRANSACTION_CUSTOM_FIELD_IS_TOO_LONG = '81527';
176
+ const TRANSACTION_HAS_ALREADY_BEEN_REFUNDED = '91512';
177
+ const TRANSACTION_MERCHANT_ACCOUNT_DOES_NOT_SUPPORT_REFUNDS = '91547';
178
+ const TRANSACTION_MERCHANT_ACCOUNT_ID_IS_INVALID = '91513';
179
+ const TRANSACTION_MERCHANT_ACCOUNT_IS_SUSPENDED = '91514';
180
+ const TRANSACTION_MERCHANT_ACCOUNT_NAME_IS_INVALID = '91513'; //Deprecated
181
+ const TRANSACTION_OPTIONS_SUBMIT_FOR_SETTLEMENT_IS_REQUIRED_FOR_CLONING = '91544';
182
+ const TRANSACTION_OPTIONS_VAULT_IS_DISABLED = '91525';
183
+ const TRANSACTION_ORDER_ID_IS_TOO_LONG = '91501';
184
+ const TRANSACTION_PAYMENT_METHOD_CONFLICT = '91515';
185
+ const TRANSACTION_PAYMENT_METHOD_DOES_NOT_BELONG_TO_CUSTOMER = '91516';
186
+ const TRANSACTION_PAYMENT_METHOD_DOES_NOT_BELONG_TO_SUBSCRIPTION = '91527';
187
+ const TRANSACTION_PAYMENT_METHOD_TOKEN_CARD_TYPE_IS_NOT_ACCEPTED = '91517';
188
+ const TRANSACTION_PAYMENT_METHOD_TOKEN_IS_INVALID = '91518';
189
+ const TRANSACTION_PROCESSOR_AUTHORIZATION_CODE_CANNOT_BE_SET = '91519';
190
+ const TRANSACTION_PROCESSOR_AUTHORIZATION_CODE_IS_INVALID = '81520';
191
+ const TRANSACTION_PROCESSOR_DOES_NOT_SUPPORT_CREDITS = '91546';
192
+ const TRANSACTION_PROCESSOR_DOES_NOT_SUPPORT_VOICE_AUTHORIZATIONS = '91545';
193
+ const TRANSACTION_PURCHASE_ORDER_NUMBER_IS_TOO_LONG = '91537';
194
+ const TRANSACTION_PURCHASE_ORDER_NUMBER_IS_INVALID = '91548';
195
+ const TRANSACTION_REFUND_AMOUNT_IS_TOO_LARGE = '91521';
196
+ const TRANSACTION_SETTLEMENT_AMOUNT_IS_TOO_LARGE = '91522';
197
+ const TRANSACTION_SUBSCRIPTION_DOES_NOT_BELONG_TO_CUSTOMER = '91529';
198
+ const TRANSACTION_SUBSCRIPTION_ID_IS_INVALID = '91528';
199
+ const TRANSACTION_SUBSCRIPTION_STATUS_MUST_BE_PAST_DUE = '91531';
200
+ const TRANSACTION_TAX_AMOUNT_CANNOT_BE_NEGATIVE = '81534';
201
+ const TRANSACTION_TAX_AMOUNT_FORMAT_IS_INVALID = '81535';
202
+ const TRANSACTION_TAX_AMOUNT_IS_TOO_LARGE = '81536';
203
+ const TRANSACTION_TYPE_IS_INVALID = '91523';
204
+ const TRANSACTION_TYPE_IS_REQUIRED = '91524';
205
+ const TRANSACTION_UNSUPPORTED_VOICE_AUTHORIZATION = '91539';
206
+ }
includes/lib/Braintree/Braintree/Error/ErrorCollection.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ *
4
+ * Error handler
5
+ *
6
+ * @package Braintree
7
+ * @subpackage Errors
8
+ * @category Errors
9
+ * @copyright 2010 Braintree Payment Solutions
10
+ */
11
+
12
+
13
+ /**
14
+ * Handles validation errors
15
+ *
16
+ * Contains a read-only property $error which is a ValidationErrorCollection
17
+ *
18
+ * @package Braintree
19
+ * @subpackage Errors
20
+ * @category Errors
21
+ * @copyright 2010 Braintree Payment Solutions
22
+ *
23
+ * @property-read object $errors
24
+ */
25
+ class Braintree_Error_ErrorCollection
26
+ {
27
+ private $_errors;
28
+
29
+ public function __construct($errorData)
30
+ {
31
+ $this->_errors =
32
+ new Braintree_Error_ValidationErrorCollection($errorData);
33
+ }
34
+
35
+
36
+ /**
37
+ * Returns all of the validation errors at all levels of nesting in a single, flat array.
38
+ */
39
+ public function deepAll()
40
+ {
41
+ return $this->_errors->deepAll();
42
+ }
43
+
44
+ /**
45
+ * Returns the total number of validation errors at all levels of nesting. For example,
46
+ *if creating a customer with a credit card and a billing address, and each of the customer,
47
+ * credit card, and billing address has 1 error, this method will return 3.
48
+ *
49
+ * @return int size
50
+ */
51
+ public function deepSize()
52
+ {
53
+ $size = $this->_errors->deepSize();
54
+ return $size;
55
+ }
56
+
57
+ /**
58
+ * return errors for the passed key name
59
+ *
60
+ * @param string $key
61
+ * @return mixed
62
+ */
63
+ public function forKey($key)
64
+ {
65
+ return $this->_errors->forKey($key);
66
+ }
67
+
68
+ /**
69
+ * return errors for the passed html field.
70
+ * For example, $result->errors->onHtmlField("transaction[customer][last_name]")
71
+ *
72
+ * @param string $field
73
+ * @return array
74
+ */
75
+ public function onHtmlField($field)
76
+ {
77
+ $pieces = preg_split("/[\[\]]+/", $field, 0, PREG_SPLIT_NO_EMPTY);
78
+ $errors = $this;
79
+ foreach(array_slice($pieces, 0, -1) as $key) {
80
+ $errors = $errors->forKey(Braintree_Util::delimiterToCamelCase($key));
81
+ if (!isset($errors)) { return array(); }
82
+ }
83
+ $finalKey = Braintree_Util::delimiterToCamelCase(end($pieces));
84
+ return $errors->onAttribute($finalKey);
85
+ }
86
+
87
+ /**
88
+ * Returns the errors at the given nesting level (see forKey) in a single, flat array:
89
+ *
90
+ * <code>
91
+ * $result = Braintree_Customer::create(...);
92
+ * $customerErrors = $result->errors->forKey('customer')->shallowAll();
93
+ * </code>
94
+ */
95
+ public function shallowAll()
96
+ {
97
+ return $this->_errors->shallowAll();
98
+ }
99
+
100
+ /**
101
+ *
102
+ * @ignore
103
+ */
104
+ public function __get($name)
105
+ {
106
+ $varName = "_$name";
107
+ return isset($this->$varName) ? $this->$varName : null;
108
+ }
109
+
110
+ /**
111
+ *
112
+ * @ignore
113
+ */
114
+ public function __toString()
115
+ {
116
+ return sprintf('%s', $this->_errors);
117
+ }
118
+ }
includes/lib/Braintree/Braintree/Error/Validation.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * error object returned as part of a validation error collection
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Error
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * error object returned as part of a validation error collection
12
+ * provides read-only access to $attribute, $code, and $message
13
+ *
14
+ * <b>== More information ==</b>
15
+ *
16
+ * For more detailed information on Validation errors, see {@link http://www.braintreepayments.com/gateway/validation-errors http://www.braintreepaymentsolutions.com/gateway/validation-errors}
17
+ *
18
+ * @package Braintree
19
+ * @subpackage Error
20
+ * @copyright 2010 Braintree Payment Solutions
21
+ *
22
+ * @property-read string $attribute
23
+ * @property-read string $code
24
+ * @property-read string $message
25
+ */
26
+ class Braintree_Error_Validation
27
+ {
28
+ private $_attribute;
29
+ private $_code;
30
+ private $_message;
31
+
32
+ /**
33
+ * @ignore
34
+ * @param array $attributes
35
+ */
36
+ public function __construct($attributes)
37
+ {
38
+ $this->_initializeFromArray($attributes);
39
+ }
40
+ /**
41
+ * initializes instance properties from the keys/values of an array
42
+ * @ignore
43
+ * @access protected
44
+ * @param array $attributes array of properties to set - single level
45
+ * @return none
46
+ */
47
+ private function _initializeFromArray($attributes)
48
+ {
49
+ foreach($attributes AS $name => $value) {
50
+ $varName = "_$name";
51
+ $this->$varName = Braintree_Util::delimiterToCamelCase($value, '_');
52
+ }
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @ignore
58
+ */
59
+ public function __get($name)
60
+ {
61
+ $varName = "_$name";
62
+ return isset($this->$varName) ? $this->$varName : null;
63
+ }
64
+ }
includes/lib/Braintree/Braintree/Error/ValidationErrorCollection.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * collection of errors enumerating all validation errors for a given request
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Error
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * collection of errors enumerating all validation errors for a given request
12
+ *
13
+ * <b>== More information ==</b>
14
+ *
15
+ * For more detailed information on Validation errors, see {@link http://www.braintreepayments.com/gateway/validation-errors http://www.braintreepaymentsolutions.com/gateway/validation-errors}
16
+ *
17
+ * @package Braintree
18
+ * @subpackage Error
19
+ * @copyright 2010 Braintree Payment Solutions
20
+ *
21
+ * @property-read array $errors
22
+ * @property-read array $nested
23
+ */
24
+ class Braintree_Error_ValidationErrorCollection extends Braintree_Collection
25
+ {
26
+ private $_errors = array();
27
+ private $_nested = array();
28
+
29
+ /**
30
+ * @ignore
31
+ */
32
+ public function __construct($data)
33
+ {
34
+ foreach($data AS $key => $errorData)
35
+ // map errors to new collections recursively
36
+ if ($key == 'errors') {
37
+ foreach ($errorData AS $error) {
38
+ $this->_errors[] = new Braintree_Error_Validation($error);
39
+ }
40
+ } else {
41
+ $this->_nested[$key] = new Braintree_Error_ValidationErrorCollection($errorData);
42
+ }
43
+
44
+ }
45
+
46
+ public function deepAll()
47
+ {
48
+ $validationErrors = array_merge(array(), $this->_errors);
49
+ foreach($this->_nested as $nestedErrors)
50
+ {
51
+ $validationErrors = array_merge($validationErrors, $nestedErrors->deepAll());
52
+ }
53
+ return $validationErrors;
54
+ }
55
+
56
+ public function deepSize()
57
+ {
58
+ $total = sizeof($this->_errors);
59
+ foreach($this->_nested as $_nestedErrors)
60
+ {
61
+ $total = $total + $_nestedErrors->deepSize();
62
+ }
63
+ return $total;
64
+ }
65
+
66
+ public function forIndex($index)
67
+ {
68
+ return $this->forKey("index" . $index);
69
+ }
70
+
71
+ public function forKey($key)
72
+ {
73
+ return isset($this->_nested[$key]) ? $this->_nested[$key] : null;
74
+ }
75
+
76
+ public function onAttribute($attribute)
77
+ {
78
+ $matches = array();
79
+ foreach ($this->_errors AS $key => $error) {
80
+ if($error->attribute == $attribute) {
81
+ $matches[] = $error;
82
+ }
83
+ }
84
+ return $matches;
85
+ }
86
+
87
+
88
+ public function shallowAll()
89
+ {
90
+ return $this->_errors;
91
+ }
92
+
93
+ /**
94
+ *
95
+ * @ignore
96
+ */
97
+ public function __get($name)
98
+ {
99
+ $varName = "_$name";
100
+ return isset($this->$varName) ? $this->$varName : null;
101
+ }
102
+
103
+ /**
104
+ * @ignore
105
+ */
106
+ public function __toString()
107
+ {
108
+ $output = array();
109
+
110
+ // TODO: implement scope
111
+ if (!empty($this->_errors)) {
112
+ $output[] = $this->_inspect($this->_errors);
113
+ }
114
+ if (!empty($this->_nested)) {
115
+ foreach ($this->_nested AS $key => $values) {
116
+ $output[] = $this->_inspect($this->_nested);
117
+ }
118
+ }
119
+ return join(', ', $output);
120
+ }
121
+
122
+ /**
123
+ * @ignore
124
+ */
125
+ private function _inspect($errors, $scope = null)
126
+ {
127
+ $eOutput = '[' . __CLASS__ . '/errors:[';
128
+ foreach($errors AS $error => $errorObj) {
129
+ $outputErrs[] = "({$errorObj->error['code']} {$errorObj->error['message']})";
130
+ }
131
+ $eOutput .= join(', ', $outputErrs) . ']]';
132
+
133
+ return $eOutput;
134
+ }
135
+ }
includes/lib/Braintree/Braintree/Exception.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * super class for all Braintree exceptions
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+
11
+ /**
12
+ * super class for all Braintree exceptions
13
+ *
14
+ * @package Braintree
15
+ * @subpackage Exception
16
+ * @copyright 2010 Braintree Payment Solutions
17
+ */
18
+ class Braintree_Exception extends Exception
19
+ {
20
+ }
includes/lib/Braintree/Braintree/Exception/Authentication.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when authentication fails
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when authentication fails.
12
+ * This may be caused by an incorrect Braintree_Configuration
13
+ *
14
+ * @package Braintree
15
+ * @subpackage Exception
16
+ * @copyright 2010 Braintree Payment Solutions
17
+ */
18
+ class Braintree_Exception_Authentication extends Braintree_Exception
19
+ {
20
+
21
+ }
includes/lib/Braintree/Braintree/Exception/Authorization.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when authorization fails
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+
11
+ /**
12
+ * Raised when the API key being used is not authorized to perform
13
+ * the attempted action according to the roles assigned to the user
14
+ * who owns the API key.
15
+ *
16
+ * @package Braintree
17
+ * @subpackage Exception
18
+ * @copyright 2010 Braintree Payment Solutions
19
+ */
20
+ class Braintree_Exception_Authorization extends Braintree_Exception
21
+ {
22
+
23
+ }
includes/lib/Braintree/Braintree/Exception/Configuration.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * raised when the Braintree library is not completely configured
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when the Braintree library is not completely configured.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @see Braintree_Configuration
16
+ */
17
+ class Braintree_Exception_Configuration extends Braintree_Exception
18
+ {
19
+
20
+ }
includes/lib/Braintree/Braintree/Exception/DownForMaintenance.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when the gateway is down for maintenance
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when the gateway is down for maintenance.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ */
17
+ class Braintree_Exception_DownForMaintenance extends Braintree_Exception
18
+ {
19
+
20
+ }
includes/lib/Braintree/Braintree/Exception/ForgedQueryString.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when a suspected forged query string is present
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised from methods that confirm transparent request requests
12
+ * when the given query string cannot be verified. This may indicate
13
+ * an attempted hack on the merchant's transparent redirect
14
+ * confirmation URL.
15
+ *
16
+ * @package Braintree
17
+ * @subpackage Exception
18
+ * @copyright 2010 Braintree Payment Solutions
19
+ */
20
+ class Braintree_Exception_ForgedQueryString extends Braintree_Exception
21
+ {
22
+
23
+ }
includes/lib/Braintree/Braintree/Exception/InvalidSignature.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+ class Braintree_Exception_InvalidSignature extends Braintree_Exception
3
+ {
4
+
5
+ }
includes/lib/Braintree/Braintree/Exception/NotFound.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when a record coult not be found
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when a record could not be found.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ */
17
+ class Braintree_Exception_NotFound extends Braintree_Exception
18
+ {
19
+
20
+ }
includes/lib/Braintree/Braintree/Exception/SSLCaFileNotFound.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when the SSL CaFile is not found.
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2011 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when the SSL CaFile is not found.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @copyright 2011 Braintree Payment Solutions
16
+ */
17
+ class Braintree_Exception_SSLCaFileNotFound extends Braintree_Exception
18
+ {
19
+
20
+ }
includes/lib/Braintree/Braintree/Exception/SSLCertificate.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when the SSL certificate fails verification.
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when the SSL certificate fails verification.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ */
17
+ class Braintree_Exception_SSLCertificate extends Braintree_Exception
18
+ {
19
+
20
+ }
includes/lib/Braintree/Braintree/Exception/ServerError.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when an unexpected server error occurs.
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when an unexpected server error occurs.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ */
17
+ class Braintree_Exception_ServerError extends Braintree_Exception
18
+ {
19
+
20
+ }
includes/lib/Braintree/Braintree/Exception/Unexpected.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when an unexpected error occurs
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised when an error occurs that the client library is not built to handle.
12
+ * This shouldn't happen.
13
+ *
14
+ * @package Braintree
15
+ * @subpackage Exception
16
+ * @copyright 2010 Braintree Payment Solutions
17
+ */
18
+ class Braintree_Exception_Unexpected extends Braintree_Exception
19
+ {
20
+
21
+ }
includes/lib/Braintree/Braintree/Exception/UpgradeRequired.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised when a client library must be upgraded.
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+ class Braintree_Exception_UpgradeRequired extends Braintree_Exception
10
+ {
11
+
12
+ }
includes/lib/Braintree/Braintree/Exception/ValidationsFailed.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Raised from non-validating exceptions when validations fail
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Exception
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Raised from non-validating methods when gateway validations fail.
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Exception
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ */
17
+ class Braintree_Exception_ValidationsFailed extends Braintree_Exception
18
+ {
19
+
20
+ }
21
+
includes/lib/Braintree/Braintree/Http.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree HTTP Client
4
+ *
5
+ * @copyright 2010 Braintree Payment Solutions
6
+ */
7
+
8
+ /**
9
+ * processes Http requests using curl
10
+ *
11
+ * @copyright 2010 Braintree Payment Solutions
12
+ */
13
+ class Braintree_Http
14
+ {
15
+ public static function delete($path)
16
+ {
17
+ $response = self::_doRequest('DELETE', $path);
18
+ if($response['status'] === 200) {
19
+ return true;
20
+ } else {
21
+ Braintree_Util::throwStatusCodeException($response['status']);
22
+ }
23
+ }
24
+
25
+ public static function get($path)
26
+ {
27
+ $response = self::_doRequest('GET', $path);
28
+ if($response['status'] === 200) {
29
+ return Braintree_Xml::buildArrayFromXml($response['body']);
30
+ } else {
31
+ Braintree_Util::throwStatusCodeException($response['status']);
32
+ }
33
+ }
34
+
35
+ public static function post($path, $params = null)
36
+ {
37
+ $response = self::_doRequest('POST', $path, self::_buildXml($params));
38
+ $responseCode = $response['status'];
39
+ if($responseCode === 200 || $responseCode === 201 || $responseCode === 422) {
40
+ return Braintree_Xml::buildArrayFromXml($response['body']);
41
+ } else {
42
+ Braintree_Util::throwStatusCodeException($responseCode);
43
+ }
44
+ }
45
+
46
+ public static function put($path, $params = null)
47
+ {
48
+ $response = self::_doRequest('PUT', $path, self::_buildXml($params));
49
+ $responseCode = $response['status'];
50
+ if($responseCode === 200 || $responseCode === 201 || $responseCode === 422) {
51
+ return Braintree_Xml::buildArrayFromXml($response['body']);
52
+ } else {
53
+ Braintree_Util::throwStatusCodeException($responseCode);
54
+ }
55
+ }
56
+
57
+ private static function _buildXml($params)
58
+ {
59
+ return empty($params) ? null : Braintree_Xml::buildXmlFromArray($params);
60
+ }
61
+
62
+ private static function _doRequest($httpVerb, $path, $requestBody = null)
63
+ {
64
+ $curl = curl_init();
65
+ curl_setopt($curl, CURLOPT_TIMEOUT, 60);
66
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $httpVerb);
67
+ curl_setopt($curl, CURLOPT_URL, Braintree_Configuration::merchantUrl() . $path);
68
+ curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
69
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array(
70
+ 'Accept: application/xml',
71
+ 'Content-Type: application/xml',
72
+ 'User-Agent: Braintree PHP Library ' . Braintree_Version::get(),
73
+ 'X-ApiVersion: ' . Braintree_Configuration::API_VERSION
74
+ ));
75
+ curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
76
+ curl_setopt($curl, CURLOPT_USERPWD, Braintree_Configuration::publicKey() . ':' . Braintree_Configuration::privateKey());
77
+ // curl_setopt($curl, CURLOPT_VERBOSE, true);
78
+ if (Braintree_Configuration::sslOn()) {
79
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
80
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
81
+ curl_setopt($curl, CURLOPT_CAINFO, Braintree_Configuration::caFile());
82
+ }
83
+
84
+ if(!empty($requestBody)) {
85
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $requestBody);
86
+ }
87
+
88
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
89
+ $response = curl_exec($curl);
90
+ $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
91
+ curl_close($curl);
92
+ if (Braintree_Configuration::sslOn()) {
93
+ if ($httpStatus == 0) {
94
+ throw new Braintree_Exception_SSLCertificate();
95
+ }
96
+ }
97
+ return array('status' => $httpStatus, 'body' => $response);
98
+ }
99
+ }
includes/lib/Braintree/Braintree/Instance.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Class Instance template
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Utility
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * abstract instance template for various objects
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Utility
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ * @abstract
17
+ */
18
+ abstract class Braintree_Instance
19
+ {
20
+ /**
21
+ *
22
+ * @param array $aAttribs
23
+ */
24
+ public function __construct($attributes)
25
+ {
26
+ if (!empty($attributes)) {
27
+ $this->_initializeFromArray($attributes);
28
+ }
29
+ }
30
+
31
+
32
+ /**
33
+ * returns private/nonexistent instance properties
34
+ * @access public
35
+ * @param var $name property name
36
+ * @return mixed contents of instance properties
37
+ */
38
+ public function __get($name)
39
+ {
40
+ if (array_key_exists($name, $this->_attributes)) {
41
+ return $this->_attributes[$name];
42
+ } else {
43
+ trigger_error('Undefined property on ' . get_class($this) . ': ' . $name, E_USER_NOTICE);
44
+ return null;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * create a printable representation of the object as:
50
+ * ClassName[property=value, property=value]
51
+ * @return var
52
+ */
53
+ public function __toString()
54
+ {
55
+ $objOutput = Braintree_Util::implodeAssociativeArray($this->_attributes);
56
+ return get_class($this) .'['.$objOutput.']';
57
+ }
58
+ /**
59
+ * initializes instance properties from the keys/values of an array
60
+ * @ignore
61
+ * @access protected
62
+ * @param <type> $aAttribs array of properties to set - single level
63
+ * @return none
64
+ */
65
+ private function _initializeFromArray($attributes)
66
+ {
67
+ $this->_attributes = $attributes;
68
+ }
69
+
70
+ }
includes/lib/Braintree/Braintree/IsNode.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_IsNode
4
+ {
5
+ function __construct($name)
6
+ {
7
+ $this->name = $name;
8
+ $this->searchTerms = array();
9
+ }
10
+
11
+ function is($value)
12
+ {
13
+ $this->searchTerms['is'] = strval($value);
14
+ return $this;
15
+ }
16
+
17
+ function toParam()
18
+ {
19
+ return $this->searchTerms;
20
+ }
21
+ }
22
+
includes/lib/Braintree/Braintree/KeyValueNode.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_KeyValueNode
4
+ {
5
+ function __construct($name)
6
+ {
7
+ $this->name = $name;
8
+ $this->searchTerm = True;
9
+
10
+ }
11
+
12
+ function is($value)
13
+ {
14
+ $this->searchTerm = $value;
15
+ return $this;
16
+ }
17
+
18
+ function toParam()
19
+ {
20
+ return $this->searchTerm;
21
+ }
22
+ }
includes/lib/Braintree/Braintree/Modification.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_Modification extends Braintree
3
+ {
4
+ protected function _initialize($attributes)
5
+ {
6
+ $this->_attributes = $attributes;
7
+
8
+ $addOnArray = array();
9
+ if (isset($attributes['addOns'])) {
10
+ foreach ($attributes['addOns'] AS $addOn) {
11
+ $addOnArray[] = Braintree_addOn::factory($addOn);
12
+ }
13
+ }
14
+ $this->_attributes['addOns'] = $addOnArray;
15
+ }
16
+
17
+ public static function factory($attributes)
18
+ {
19
+ $instance = new self();
20
+ $instance->_initialize($attributes);
21
+ return $instance;
22
+ }
23
+ }
includes/lib/Braintree/Braintree/MultipleValueNode.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_MultipleValueNode
4
+ {
5
+ function __construct($name, $allowedValues = array())
6
+ {
7
+ $this->name = $name;
8
+ $this->items = array();
9
+ $this->allowedValues = $allowedValues;
10
+ }
11
+
12
+ function in($values)
13
+ {
14
+ $bad_values = array_diff($values, $this->allowedValues);
15
+ if (count($this->allowedValues) > 0 && count($bad_values) > 0) {
16
+ $message = 'Invalid argument(s) for ' . $this->name . ':';
17
+ foreach ($bad_values AS $bad_value) {
18
+ $message .= ' ' . $bad_value;
19
+ }
20
+
21
+ throw new InvalidArgumentException($message);
22
+ }
23
+
24
+ $this->items = $values;
25
+ return $this;
26
+ }
27
+
28
+ function is($value)
29
+ {
30
+ return $this->in(array($value));
31
+ }
32
+
33
+ function toParam()
34
+ {
35
+ return $this->items;
36
+ }
37
+ }
includes/lib/Braintree/Braintree/MultipleValueOrTextNode.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_MultipleValueOrTextNode extends Braintree_MultipleValueNode
4
+ {
5
+ function __construct($name)
6
+ {
7
+ parent::__construct($name);
8
+ $this->textNode = new Braintree_TextNode($name);
9
+ }
10
+
11
+ function contains($value)
12
+ {
13
+ $this->textNode->contains($value);
14
+ return $this;
15
+ }
16
+
17
+ function endsWith($value)
18
+ {
19
+ $this->textNode->endsWith($value);
20
+ return $this;
21
+ }
22
+
23
+ function is($value)
24
+ {
25
+ $this->textNode->is($value);
26
+ return $this;
27
+ }
28
+
29
+ function isNot($value)
30
+ {
31
+ $this->textNode->isNot($value);
32
+ return $this;
33
+ }
34
+
35
+ function startsWith($value)
36
+ {
37
+ $this->textNode->startsWith($value);
38
+ return $this;
39
+ }
40
+
41
+ function toParam()
42
+ {
43
+ return array_merge(parent::toParam(), $this->textNode->toParam());
44
+ }
45
+ }
46
+
includes/lib/Braintree/Braintree/PartialMatchNode.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_PartialMatchNode extends Braintree_EqualityNode
4
+ {
5
+ function startsWith($value)
6
+ {
7
+ $this->searchTerms["starts_with"] = strval($value);
8
+ return $this;
9
+ }
10
+
11
+ function endsWith($value)
12
+ {
13
+ $this->searchTerms["ends_with"] = strval($value);
14
+ return $this;
15
+ }
16
+ }
includes/lib/Braintree/Braintree/Plan.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_Plan extends Braintree
3
+ {
4
+ public static function all()
5
+ {
6
+ $response = Braintree_Http::get('/plans');
7
+ if (key_exists('plans', $response)){
8
+ $plans = array("plan" => $response['plans']);
9
+ } else {
10
+ $plans = array("plan" => array());
11
+ }
12
+
13
+ return Braintree_Util::extractAttributeAsArray(
14
+ $plans,
15
+ 'plan'
16
+ );
17
+ }
18
+
19
+ public static function factory($attributes)
20
+ {
21
+ $instance = new self();
22
+ $instance->_initialize($attributes);
23
+
24
+ return $instance;
25
+ }
26
+
27
+ protected function _initialize($attributes)
28
+ {
29
+ $this->_attributes = $attributes;
30
+
31
+ $addOnArray = array();
32
+ if (isset($attributes['addOns'])) {
33
+ foreach ($attributes['addOns'] AS $addOn) {
34
+ $addOnArray[] = Braintree_AddOn::factory($addOn);
35
+ }
36
+ }
37
+ $this->_attributes['addOns'] = $addOnArray;
38
+
39
+ $discountArray = array();
40
+ if (isset($attributes['discounts'])) {
41
+ foreach ($attributes['discounts'] AS $discount) {
42
+ $discountArray[] = Braintree_Discount::factory($discount);
43
+ }
44
+ }
45
+ $this->_attributes['discounts'] = $discountArray;
46
+
47
+ $planArray = array();
48
+ if (isset($attributes['plans'])) {
49
+ foreach ($attributes['plans'] AS $plan) {
50
+ $planArray[] = Braintree_Plan::factory($plan);
51
+ }
52
+ }
53
+ $this->_attributes['plans'] = $planArray;
54
+ }
55
+ }
includes/lib/Braintree/Braintree/RangeNode.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_RangeNode
4
+ {
5
+ function __construct($name)
6
+ {
7
+ $this->name = $name;
8
+ $this->searchTerms = array();
9
+ }
10
+
11
+ function greaterThanOrEqualTo($value)
12
+ {
13
+ $this->searchTerms['min'] = $value;
14
+ return $this;
15
+ }
16
+
17
+ function lessThanOrEqualTo($value)
18
+ {
19
+ $this->searchTerms['max'] = $value;
20
+ return $this;
21
+ }
22
+
23
+ function is($value)
24
+ {
25
+ $this->searchTerms['is'] = $value;
26
+ return $this;
27
+ }
28
+
29
+ function between($min, $max)
30
+ {
31
+ return $this->greaterThanOrEqualTo($min)->lessThanOrEqualTo($max);
32
+ }
33
+
34
+ function toParam()
35
+ {
36
+ return $this->searchTerms;
37
+ }
38
+ }
includes/lib/Braintree/Braintree/ResourceCollection.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree ResourceCollection
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Utility
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * ResourceCollection is a container object for result data
12
+ *
13
+ * stores and retrieves search results and aggregate data
14
+ *
15
+ * example:
16
+ * <code>
17
+ * $result = Braintree_Customer::all();
18
+ *
19
+ * foreach($result as $transaction) {
20
+ * print_r($transaction->id);
21
+ * }
22
+ * </code>
23
+ *
24
+ * @package Braintree
25
+ * @subpackage Utility
26
+ * @copyright 2010 Braintree Payment Solutions
27
+ */
28
+ class Braintree_ResourceCollection implements Iterator
29
+ {
30
+ private $_index;
31
+ private $_batchIndex;
32
+ private $_items;
33
+ private $_pageSize;
34
+ private $_pager;
35
+
36
+ /**
37
+ * set up the resource collection
38
+ *
39
+ * expects an array of attributes with literal keys
40
+ *
41
+ * @param array $attributes
42
+ * @param array $pagerAttribs
43
+ */
44
+ public function __construct($response, $pager)
45
+ {
46
+ $this->_pageSize = $response["searchResults"]["pageSize"];
47
+ $this->_ids = $response["searchResults"]["ids"];
48
+ $this->_pager = $pager;
49
+ }
50
+
51
+ /**
52
+ * returns the current item when iterating with foreach
53
+ */
54
+ public function current()
55
+ {
56
+ return $this->_items[$this->_index];
57
+ }
58
+
59
+ /**
60
+ * returns the first item in the collection
61
+ *
62
+ * @return mixed
63
+ */
64
+ public function firstItem()
65
+ {
66
+ $ids = $this->_ids;
67
+ $page = $this->_getPage(array($ids[0]));
68
+ return $page[0];
69
+ }
70
+
71
+ public function key()
72
+ {
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * advances to the next item in the collection when iterating with foreach
78
+ */
79
+ public function next()
80
+ {
81
+ ++$this->_index;
82
+ }
83
+
84
+ /**
85
+ * rewinds thtestIterateOverResultse collection to the first item when iterating with foreach
86
+ */
87
+ public function rewind()
88
+ {
89
+ $this->_batchIndex = 0;
90
+ $this->_getNextPage();
91
+ }
92
+
93
+ /**
94
+ * returns whether the current item is valid when iterating with foreach
95
+ */
96
+ public function valid()
97
+ {
98
+ if ($this->_index == count($this->_items) && $this->_batchIndex < count($this->_ids)) {
99
+ $this->_getNextPage();
100
+ }
101
+
102
+ if ($this->_index < count($this->_items)) {
103
+ return true;
104
+ } else {
105
+ return false;
106
+ }
107
+ }
108
+
109
+ public function maximumCount()
110
+ {
111
+ return count($this->_ids);
112
+ }
113
+
114
+ private function _getNextPage()
115
+ {
116
+ if (empty($this->_ids))
117
+ {
118
+ $this->_items = array();
119
+ }
120
+ else
121
+ {
122
+ $this->_items = $this->_getPage(array_slice($this->_ids, $this->_batchIndex, $this->_pageSize));
123
+ $this->_batchIndex += $this->_pageSize;
124
+ $this->_index = 0;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * requests the next page of results for the collection
130
+ *
131
+ * @return none
132
+ */
133
+ private function _getPage($ids)
134
+ {
135
+ $className = $this->_pager['className'];
136
+ $classMethod = $this->_pager['classMethod'];
137
+ $methodArgs = array();
138
+ foreach ($this->_pager['methodArgs'] as $arg) {
139
+ array_push($methodArgs, $arg);
140
+ }
141
+ array_push($methodArgs, $ids);
142
+
143
+ return call_user_func_array(
144
+ array($className, $classMethod),
145
+ $methodArgs
146
+ );
147
+ }
148
+ }
includes/lib/Braintree/Braintree/Result/CreditCardVerification.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Credit Card Verification Result
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Result
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Braintree Credit Card Verification Result
12
+ *
13
+ * This object is returned as part of an Error Result; it provides
14
+ * access to the credit card verification data from the gateway
15
+ *
16
+ *
17
+ * @package Braintree
18
+ * @subpackage Result
19
+ * @copyright 2010 Braintree Payment Solutions
20
+ *
21
+ * @property-read string $avsErrorResponseCode
22
+ * @property-read string $avsPostalCodeResponseCode
23
+ * @property-read string $avsStreetAddressResponseCode
24
+ * @property-read string $cvvResponseCode
25
+ * @property-read string $status
26
+ *
27
+ */
28
+ class Braintree_Result_CreditCardVerification
29
+ {
30
+ // Status
31
+ const FAILED = 'failed';
32
+ const GATEWAY_REJECTED = 'gateway_rejected';
33
+ const PROCESSOR_DECLINED = 'processor_declined';
34
+ const VERIFIED = 'verified';
35
+
36
+ private $_attributes;
37
+ private $_avsErrorResponseCode;
38
+ private $_avsPostalCodeResponseCode;
39
+ private $_avsStreetAddressResponseCode;
40
+ private $_cvvResponseCode;
41
+ private $_gatewayRejectionReason;
42
+ private $_status;
43
+
44
+ /**
45
+ * @ignore
46
+ */
47
+ public function __construct($attributes)
48
+ {
49
+ $this->_initializeFromArray($attributes);
50
+ }
51
+ /**
52
+ * initializes instance properties from the keys/values of an array
53
+ * @ignore
54
+ * @access protected
55
+ * @param <type> $aAttribs array of properties to set - single level
56
+ * @return none
57
+ */
58
+ private function _initializeFromArray($attributes)
59
+ {
60
+ $this->_attributes = $attributes;
61
+ foreach($attributes AS $name => $value) {
62
+ $varName = "_$name";
63
+ $this->$varName = $value;
64
+ // $this->$varName = Braintree_Util::delimiterToCamelCase($value, '_');
65
+ }
66
+ }
67
+ /**
68
+ *
69
+ * @ignore
70
+ */
71
+ public function __get($name)
72
+ {
73
+ $varName = "_$name";
74
+ return isset($this->$varName) ? $this->$varName : null;
75
+ }
76
+
77
+ /**
78
+ * returns a string representation of the customer
79
+ * @return string
80
+ */
81
+ public function __toString()
82
+ {
83
+ return __CLASS__ . '[' .
84
+ Braintree_Util::attributesToString($this->_attributes) .']';
85
+ }
86
+ }
includes/lib/Braintree/Braintree/Result/Error.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Error Result
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Result
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Braintree Error Result
12
+ *
13
+ * An Error Result will be returned from gateway methods when
14
+ * the gateway responds with an error. It will provide access
15
+ * to the original request.
16
+ * For example, when voiding a transaction, Error Result will
17
+ * respond to the void request if it failed:
18
+ *
19
+ * <code>
20
+ * $result = Braintree_Transaction::void('abc123');
21
+ * if ($result->success) {
22
+ * // Successful Result
23
+ * } else {
24
+ * // Braintree_Result_Error
25
+ * }
26
+ * </code>
27
+ *
28
+ * @package Braintree
29
+ * @subpackage Result
30
+ * @copyright 2010 Braintree Payment Solutions
31
+ *
32
+ * @property-read array $params original passed params
33
+ * @property-read object $errors Braintree_Error_ErrorCollection
34
+ * @property-read object $creditCardVerification credit card verification data
35
+ */
36
+ class Braintree_Result_Error extends Braintree
37
+ {
38
+ /**
39
+ *
40
+ * @var boolean always false
41
+ */
42
+ public $success = false;
43
+
44
+ /**
45
+ * return original value for a field
46
+ * For example, if a user tried to submit 'invalid-email' in the html field transaction[customer][email],
47
+ * $result->valueForHtmlField("transaction[customer][email]") would yield "invalid-email"
48
+ *
49
+ * @param string $field
50
+ * @return string
51
+ */
52
+ public function valueForHtmlField($field)
53
+ {
54
+ $pieces = preg_split("/[\[\]]+/", $field, 0, PREG_SPLIT_NO_EMPTY);
55
+ $params = $this->params;
56
+ foreach(array_slice($pieces, 0, -1) as $key) {
57
+ $params = $params[Braintree_Util::delimiterToCamelCase($key)];
58
+ }
59
+ $finalKey = Braintree_Util::delimiterToCamelCase(end($pieces));
60
+ $fieldValue = isset($params[$finalKey]) ? $params[$finalKey] : null;
61
+ return $fieldValue;
62
+ }
63
+
64
+ /**
65
+ * overrides default constructor
66
+ * @ignore
67
+ * @param array $response gateway response array
68
+ */
69
+ public function __construct($response)
70
+ {
71
+ $this->_attributes = $response;
72
+ $this->_set('errors', new Braintree_Error_ErrorCollection($response['errors']));
73
+
74
+ if(isset($response['verification'])) {
75
+ $this->_set('creditCardVerification', new Braintree_Result_CreditCardVerification($response['verification']));
76
+ } else {
77
+ $this->_set('creditCardVerification', null);
78
+ }
79
+
80
+ if(isset($response['transaction'])) {
81
+ $this->_set('transaction', Braintree_Transaction::factory($response['transaction']));
82
+ } else {
83
+ $this->_set('transaction', null);
84
+ }
85
+
86
+ if(isset($response['subscription'])) {
87
+ $this->_set('subscription', Braintree_Subscription::factory($response['subscription']));
88
+ } else {
89
+ $this->_set('subscription', null);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * create a printable representation of the object as:
95
+ * ClassName[property=value, property=value]
96
+ * @ignore
97
+ * @return var
98
+ */
99
+ public function __toString()
100
+ {
101
+ $output = Braintree_Util::attributesToString($this->_attributes);
102
+ if (isset($this->_creditCardVerification)) {
103
+ $output .= sprintf('%s', $this->_creditCardVerification);
104
+ }
105
+ return __CLASS__ .'['.$output.']';
106
+ }
107
+ }
includes/lib/Braintree/Braintree/Result/Successful.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Successful Result
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Result
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Braintree Successful Result
12
+ *
13
+ * A Successful Result will be returned from gateway methods when
14
+ * validations pass. It will provide access to the created resource.
15
+ *
16
+ * For example, when creating a customer, Braintree_Result_Successful will
17
+ * respond to <b>customer</b> like so:
18
+ *
19
+ * <code>
20
+ * $result = Braintree_Customer::create(array('first_name' => "John"));
21
+ * if ($result->success) {
22
+ * // Braintree_Result_Successful
23
+ * echo "Created customer {$result->customer->id}";
24
+ * } else {
25
+ * // Braintree_Result_Error
26
+ * }
27
+ * </code>
28
+ *
29
+ *
30
+ * @package Braintree
31
+ * @subpackage Result
32
+ * @copyright 2010 Braintree Payment Solutions
33
+ */
34
+ class Braintree_Result_Successful extends Braintree_Instance
35
+ {
36
+ /**
37
+ *
38
+ * @var boolean always true
39
+ */
40
+ public $success = true;
41
+ /**
42
+ *
43
+ * @var string stores the internal name of the object providing access to
44
+ */
45
+ private $_returnObjectName;
46
+
47
+ /**
48
+ * @ignore
49
+ * @param string $classToReturn name of class to instantiate
50
+ */
51
+ public function __construct($objToReturn = null)
52
+ {
53
+ if(!empty($objToReturn)) {
54
+ // get a lowercase direct name for the property
55
+ $property = Braintree_Util::cleanClassName(
56
+ get_class($objToReturn)
57
+ );
58
+ // save the name for indirect access
59
+ $this->_returnObjectName = $property;
60
+
61
+ // create the property!
62
+ $this->$property = $objToReturn;
63
+ }
64
+ }
65
+
66
+
67
+ /**
68
+ *
69
+ * @ignore
70
+ * @return string string representation of the object's structure
71
+ */
72
+ public function __toString()
73
+ {
74
+ $returnObject = $this->_returnObjectName;
75
+ return __CLASS__ . '['.$this->$returnObject->__toString().']';
76
+ }
77
+
78
+ }
includes/lib/Braintree/Braintree/SettlementBatchSummary.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_SettlementBatchSummary extends Braintree
3
+ {
4
+ public static function generate($settlement_date, $groupByCustomField = NULL)
5
+ {
6
+ $criteria = array('settlement_date' => $settlement_date);
7
+ if (isset($groupByCustomField))
8
+ {
9
+ $criteria['group_by_custom_field'] = $groupByCustomField;
10
+ }
11
+ $params = array('settlement_batch_summary' => $criteria);
12
+ $response = Braintree_Http::post('/settlement_batch_summary', $params);
13
+
14
+ if (isset($groupByCustomField))
15
+ {
16
+ $response['settlementBatchSummary']['records'] = self::_underscoreCustomField(
17
+ $groupByCustomField,
18
+ $response['settlementBatchSummary']['records']
19
+ );
20
+ }
21
+
22
+ return self::_verifyGatewayResponse($response);
23
+ }
24
+
25
+ private static function _underscoreCustomField($groupByCustomField, $records)
26
+ {
27
+ $updatedRecords = array();
28
+
29
+ foreach ($records as $record)
30
+ {
31
+ $camelized = Braintree_Util::delimiterToCamelCase($groupByCustomField);
32
+ $record[$groupByCustomField] = $record[$camelized];
33
+ unset($record[$camelized]);
34
+ $updatedRecords[] = $record;
35
+ }
36
+
37
+ return $updatedRecords;
38
+ }
39
+
40
+ private static function _verifyGatewayResponse($response)
41
+ {
42
+ if (isset($response['settlementBatchSummary'])) {
43
+ return new Braintree_Result_Successful(
44
+ self::factory($response['settlementBatchSummary'])
45
+ );
46
+ } else if (isset($response['apiErrorResponse'])) {
47
+ return new Braintree_Result_Error($response['apiErrorResponse']);
48
+ } else {
49
+ throw new Braintree_Exception_Unexpected(
50
+ "Expected settlementBatchSummary or apiErrorResponse"
51
+ );
52
+ }
53
+ }
54
+
55
+ public static function factory($attributes)
56
+ {
57
+ $instance = new self();
58
+ $instance->_initialize($attributes);
59
+ return $instance;
60
+ }
61
+
62
+ /**
63
+ * @ignore
64
+ */
65
+ protected function _initialize($attributes)
66
+ {
67
+ $this->_attributes = $attributes;
68
+ }
69
+
70
+ public function records()
71
+ {
72
+ return $this->_attributes['records'];
73
+ }
74
+ }
includes/lib/Braintree/Braintree/Subscription.php ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Subscription module
4
+ *
5
+ * <b>== More information ==</b>
6
+ *
7
+ * For more detailed information on Subscriptions, see {@link http://www.braintreepayments.com/gateway/subscription-api http://www.braintreepaymentsolutions.com/gateway/subscription-api}
8
+ *
9
+ * PHP Version 5
10
+ *
11
+ * @package Braintree
12
+ * @copyright 2010 Braintree Payment Solutions
13
+ */
14
+ class Braintree_Subscription extends Braintree
15
+ {
16
+ const ACTIVE = 'Active';
17
+ const CANCELED = 'Canceled';
18
+ const EXPIRED = 'Expired';
19
+ const PAST_DUE = 'Past Due';
20
+ const PENDING = 'Pending';
21
+
22
+ public static function create($attributes)
23
+ {
24
+ Braintree_Util::verifyKeys(self::_createSignature(), $attributes);
25
+ $response = Braintree_Http::post('/subscriptions', array('subscription' => $attributes));
26
+ return self::_verifyGatewayResponse($response);
27
+ }
28
+
29
+ /**
30
+ * @ignore
31
+ */
32
+ public static function factory($attributes)
33
+ {
34
+ $instance = new self();
35
+ $instance->_initialize($attributes);
36
+
37
+ return $instance;
38
+ }
39
+
40
+ public static function find($id)
41
+ {
42
+ self::_validateId($id);
43
+
44
+ try {
45
+ $response = Braintree_Http::get('/subscriptions/' . $id);
46
+ return self::factory($response['subscription']);
47
+ } catch (Braintree_Exception_NotFound $e) {
48
+ throw new Braintree_Exception_NotFound('subscription with id ' . $id . ' not found');
49
+ }
50
+
51
+ }
52
+
53
+ public static function search($query)
54
+ {
55
+ $criteria = array();
56
+ foreach ($query as $term) {
57
+ $criteria[$term->name] = $term->toparam();
58
+ }
59
+
60
+
61
+ $response = braintree_http::post('/subscriptions/advanced_search_ids', array('search' => $criteria));
62
+ $pager = array(
63
+ 'className' => __CLASS__,
64
+ 'classMethod' => 'fetch',
65
+ 'methodArgs' => array($query)
66
+ );
67
+
68
+ return new Braintree_ResourceCollection($response, $pager);
69
+ }
70
+
71
+ public static function fetch($query, $ids)
72
+ {
73
+ $criteria = array();
74
+ foreach ($query as $term) {
75
+ $criteria[$term->name] = $term->toparam();
76
+ }
77
+ $criteria["ids"] = Braintree_SubscriptionSearch::ids()->in($ids)->toparam();
78
+ $response = Braintree_Http::post('/subscriptions/advanced_search', array('search' => $criteria));
79
+
80
+ return Braintree_Util::extractAttributeAsArray(
81
+ $response['subscriptions'],
82
+ 'subscription'
83
+ );
84
+ }
85
+
86
+ public static function update($subscriptionId, $attributes)
87
+ {
88
+ Braintree_Util::verifyKeys(self::_updateSignature(), $attributes);
89
+ $response = Braintree_Http::put(
90
+ '/subscriptions/' . $subscriptionId,
91
+ array('subscription' => $attributes)
92
+ );
93
+ return self::_verifyGatewayResponse($response);
94
+ }
95
+
96
+ public static function retryCharge($subscriptionId, $amount = null)
97
+ {
98
+ $transaction_params = array('type' => Braintree_Transaction::SALE,
99
+ 'subscriptionId' => $subscriptionId);
100
+ if (isset($amount)) {
101
+ $transaction_params['amount'] = $amount;
102
+ }
103
+
104
+ $response = Braintree_Http::post(
105
+ '/transactions',
106
+ array('transaction' => $transaction_params));
107
+ return self::_verifyGatewayResponse($response);
108
+ }
109
+
110
+ public static function cancel($subscriptionId)
111
+ {
112
+ $response = Braintree_Http::put('/subscriptions/' . $subscriptionId . '/cancel');
113
+ return self::_verifyGatewayResponse($response);
114
+ }
115
+
116
+ private static function _createSignature()
117
+ {
118
+ return array_merge(
119
+ array(
120
+ 'billingDayOfMonth',
121
+ 'firstBillingDate',
122
+ 'id',
123
+ 'merchantAccountId',
124
+ 'neverExpires',
125
+ 'numberOfBillingCycles',
126
+ 'paymentMethodToken',
127
+ 'planId',
128
+ 'price',
129
+ 'trialDuration',
130
+ 'trialDurationUnit',
131
+ 'trialPeriod',
132
+ array('descriptor' => array('name', 'phone')),
133
+ array('options' => array('doNotInheritAddOnsOrDiscounts', 'startImmediately')),
134
+ ),
135
+ self::_addOnDiscountSignature()
136
+ );
137
+ }
138
+
139
+ private static function _updateSignature()
140
+ {
141
+ return array_merge(
142
+ array(
143
+ 'merchantAccountId', 'numberOfBillingCycles', 'paymentMethodToken', 'planId',
144
+ 'id', 'neverExpires', 'price',
145
+ array('descriptor' => array('name', 'phone')),
146
+ array('options' => array('prorateCharges', 'replaceAllAddOnsAndDiscounts', 'revertSubscriptionOnProrationFailure')),
147
+ ),
148
+ self::_addOnDiscountSignature()
149
+ );
150
+ }
151
+
152
+ private static function _addOnDiscountSignature()
153
+ {
154
+ return array(
155
+ array(
156
+ 'addOns' => array(
157
+ array('add' => array('amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
158
+ array('update' => array('amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
159
+ array('remove' => array('_anyKey_')),
160
+ )
161
+ ),
162
+ array(
163
+ 'discounts' => array(
164
+ array('add' => array('amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
165
+ array('update' => array('amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity')),
166
+ array('remove' => array('_anyKey_')),
167
+ )
168
+ )
169
+ );
170
+ }
171
+
172
+ /**
173
+ * @ignore
174
+ */
175
+ protected function _initialize($attributes)
176
+ {
177
+ $this->_attributes = $attributes;
178
+
179
+ $addOnArray = array();
180
+ if (isset($attributes['addOns'])) {
181
+ foreach ($attributes['addOns'] AS $addOn) {
182
+ $addOnArray[] = Braintree_AddOn::factory($addOn);
183
+ }
184
+ }
185
+ $this->_attributes['addOns'] = $addOnArray;
186
+
187
+ $discountArray = array();
188
+ if (isset($attributes['discounts'])) {
189
+ foreach ($attributes['discounts'] AS $discount) {
190
+ $discountArray[] = Braintree_Discount::factory($discount);
191
+ }
192
+ }
193
+ $this->_attributes['discounts'] = $discountArray;
194
+
195
+ if (isset($attributes['descriptor'])) {
196
+ $this->_set('descriptor', new Braintree_Descriptor($attributes['descriptor']));
197
+ }
198
+
199
+ $transactionArray = array();
200
+ if (isset($attributes['transactions'])) {
201
+ foreach ($attributes['transactions'] AS $transaction) {
202
+ $transactionArray[] = Braintree_Transaction::factory($transaction);
203
+ }
204
+ }
205
+ $this->_attributes['transactions'] = $transactionArray;
206
+ }
207
+
208
+ /**
209
+ * @ignore
210
+ */
211
+ private static function _validateId($id = null) {
212
+ if (empty($id)) {
213
+ throw new InvalidArgumentException(
214
+ 'expected subscription id to be set'
215
+ );
216
+ }
217
+ if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
218
+ throw new InvalidArgumentException(
219
+ $id . ' is an invalid subscription id.'
220
+ );
221
+ }
222
+ }
223
+ /**
224
+ * @ignore
225
+ */
226
+ private static function _verifyGatewayResponse($response)
227
+ {
228
+ if (isset($response['subscription'])) {
229
+ return new Braintree_Result_Successful(
230
+ self::factory($response['subscription'])
231
+ );
232
+ } else if (isset($response['transaction'])) {
233
+ // return a populated instance of Braintree_Transaction, for subscription retryCharge
234
+ return new Braintree_Result_Successful(
235
+ Braintree_Transaction::factory($response['transaction'])
236
+ );
237
+ } else if (isset($response['apiErrorResponse'])) {
238
+ return new Braintree_Result_Error($response['apiErrorResponse']);
239
+ } else {
240
+ throw new Braintree_Exception_Unexpected(
241
+ "Expected subscription, transaction, or apiErrorResponse"
242
+ );
243
+ }
244
+ }
245
+
246
+ /**
247
+ * returns a string representation of the customer
248
+ * @return string
249
+ */
250
+ public function __toString()
251
+ {
252
+ return __CLASS__ . '[' .
253
+ Braintree_Util::attributesToString($this->_attributes) .']';
254
+ }
255
+
256
+ }
includes/lib/Braintree/Braintree/SubscriptionSearch.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_SubscriptionSearch
3
+ {
4
+ static function billingCyclesRemaining()
5
+ {
6
+ return new Braintree_RangeNode('billing_cycles_remaining');
7
+ }
8
+
9
+ static function daysPastDue()
10
+ {
11
+ return new Braintree_RangeNode('days_past_due');
12
+ }
13
+
14
+ static function id()
15
+ {
16
+ return new Braintree_TextNode('id');
17
+ }
18
+
19
+ static function inTrialPeriod()
20
+ {
21
+ return new Braintree_MultipleValueNode('in_trial_period', array(true, false));
22
+ }
23
+
24
+ static function merchantAccountId()
25
+ {
26
+ return new Braintree_MultipleValueNode('merchant_account_id');
27
+ }
28
+
29
+ static function nextBillingDate()
30
+ {
31
+ return new Braintree_RangeNode('next_billing_date');
32
+ }
33
+
34
+ static function planId()
35
+ {
36
+ return new Braintree_MultipleValueOrTextNode('plan_id');
37
+ }
38
+
39
+ static function price()
40
+ {
41
+ return new Braintree_RangeNode('price');
42
+ }
43
+
44
+ static function status()
45
+ {
46
+ return new Braintree_MultipleValueNode("status", array(
47
+ Braintree_Subscription::ACTIVE,
48
+ Braintree_Subscription::CANCELED,
49
+ Braintree_Subscription::EXPIRED,
50
+ Braintree_Subscription::PAST_DUE,
51
+ Braintree_Subscription::PENDING
52
+ ));
53
+ }
54
+
55
+ static function transactionId()
56
+ {
57
+ return new Braintree_TextNode('transaction_id');
58
+ }
59
+
60
+ static function ids()
61
+ {
62
+ return new Braintree_MultipleValueNode('ids');
63
+ }
64
+ }
includes/lib/Braintree/Braintree/SubscriptionStatus.php ADDED
File without changes
includes/lib/Braintree/Braintree/Test/CreditCardNumbers.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Credit card information used for testing purposes
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Test
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Credit card information used for testing purposes
12
+ *
13
+ * The constants contained in the Braintree_Test_CreditCardNumbers class provide
14
+ * credit card numbers that should be used when working in the sandbox environment.
15
+ * The sandbox will not accept any credit card numbers other than the ones listed below.
16
+ *
17
+ * @package Braintree
18
+ * @subpackage Test
19
+ * @copyright 2010 Braintree Payment Solutions
20
+ */
21
+ class Braintree_Test_CreditCardNumbers
22
+ {
23
+ public static $amExes = array(
24
+ '378282246310005',
25
+ '371449635398431',
26
+ '378734493671000',
27
+ );
28
+ public static $carteBlanches = array('30569309025904',);
29
+ public static $dinersClubs = array('38520000023237',);
30
+ public static $discoverCards = array(
31
+ '6011111111111117',
32
+ '6011000990139424',
33
+ );
34
+ public static $JCBs = array(
35
+ '3530111333300000',
36
+ '3566002020360505',
37
+ );
38
+
39
+ public static $masterCard = '5555555555554444';
40
+ public static $masterCardInternational = '5105105105105100';
41
+ public static $masterCards = array(
42
+ '5105105105105100',
43
+ '5555555555554444',
44
+ );
45
+
46
+ public static $visa = '4012888888881881';
47
+ public static $visaInternational = '4009348888881881';
48
+ public static $visas = array(
49
+ '4009348888881881',
50
+ '4012888888881881',
51
+ '4111111111111111',
52
+ '4000111111111115',
53
+ );
54
+
55
+ public static $unknowns = array(
56
+ '1000000000000008',
57
+ );
58
+
59
+ public static $failsSandboxVerification = array(
60
+ 'AmEx' => '378734493671000',
61
+ 'Discover' => '6011000990139424',
62
+ 'MasterCard' => '5105105105105100',
63
+ 'Visa' => '4000111111111115',
64
+ );
65
+
66
+
67
+ public static function getAll()
68
+ {
69
+ return array_merge(
70
+ self::$amExes,
71
+ self::$discoverCards,
72
+ self::$masterCards,
73
+ self::$visas
74
+ );
75
+ }
76
+ }
includes/lib/Braintree/Braintree/Test/TransactionAmounts.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Transaction amounts used for testing purposes
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Test
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Transaction amounts used for testing purposes
12
+ *
13
+ * The constants in this class can be used to create transactions with
14
+ * the desired status in the sandbox environment.
15
+ *
16
+ * @package Braintree
17
+ * @subpackage Test
18
+ * @copyright 2010 Braintree Payment Solutions
19
+ */
20
+ class Braintree_Test_TransactionAmounts
21
+ {
22
+ public static $authorize = '1000.00';
23
+ public static $decline = '2000.00';
24
+ }
includes/lib/Braintree/Braintree/TextNode.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Braintree_TextNode extends Braintree_PartialMatchNode
4
+ {
5
+ function contains($value)
6
+ {
7
+ $this->searchTerms["contains"] = strval($value);
8
+ return $this;
9
+ }
10
+ }
includes/lib/Braintree/Braintree/Transaction.php ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Transaction processor
4
+ *
5
+ * @package Braintree
6
+ * @category Resources
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates and manages transactions
12
+ *
13
+ * At minimum, an amount, credit card number, and
14
+ * credit card expiration date are required.
15
+ *
16
+ * <b>Minimalistic example:</b>
17
+ * <code>
18
+ * Braintree_Transaction::saleNoValidate(array(
19
+ * 'amount' => '100.00',
20
+ * 'creditCard' => array(
21
+ * 'number' => '5105105105105100',
22
+ * 'expirationDate' => '05/12',
23
+ * ),
24
+ * ));
25
+ * </code>
26
+ *
27
+ * <b>Full example:</b>
28
+ * <code>
29
+ * Braintree_Transaction::saleNoValidate(array(
30
+ * 'amount' => '100.00',
31
+ * 'orderId' => '123',
32
+ * 'channel' => 'MyShoppingCardProvider',
33
+ * 'creditCard' => array(
34
+ * // if token is omitted, the gateway will generate a token
35
+ * 'token' => 'credit_card_123',
36
+ * 'number' => '5105105105105100',
37
+ * 'expirationDate' => '05/2011',
38
+ * 'cvv' => '123',
39
+ * ),
40
+ * 'customer' => array(
41
+ * // if id is omitted, the gateway will generate an id
42
+ * 'id' => 'customer_123',
43
+ * 'firstName' => 'Dan',
44
+ * 'lastName' => 'Smith',
45
+ * 'company' => 'Braintree Payment Solutions',
46
+ * 'email' => 'dan@example.com',
47
+ * 'phone' => '419-555-1234',
48
+ * 'fax' => '419-555-1235',
49
+ * 'website' => 'http://braintreepayments.com'
50
+ * ),
51
+ * 'billing' => array(
52
+ * 'firstName' => 'Carl',
53
+ * 'lastName' => 'Jones',
54
+ * 'company' => 'Braintree',
55
+ * 'streetAddress' => '123 E Main St',
56
+ * 'extendedAddress' => 'Suite 403',
57
+ * 'locality' => 'Chicago',
58
+ * 'region' => 'IL',
59
+ * 'postalCode' => '60622',
60
+ * 'countryName' => 'United States of America'
61
+ * ),
62
+ * 'shipping' => array(
63
+ * 'firstName' => 'Andrew',
64
+ * 'lastName' => 'Mason',
65
+ * 'company' => 'Braintree',
66
+ * 'streetAddress' => '456 W Main St',
67
+ * 'extendedAddress' => 'Apt 2F',
68
+ * 'locality' => 'Bartlett',
69
+ * 'region' => 'IL',
70
+ * 'postalCode' => '60103',
71
+ * 'countryName' => 'United States of America'
72
+ * ),
73
+ * 'customFields' => array(
74
+ * 'birthdate' => '11/13/1954'
75
+ * )
76
+ * )
77
+ * </code>
78
+ *
79
+ * <b>== Storing in the Vault ==</b>
80
+ *
81
+ * The customer and credit card information used for
82
+ * a transaction can be stored in the vault by setting
83
+ * <i>transaction[options][storeInVault]</i> to true.
84
+ * <code>
85
+ * $transaction = Braintree_Transaction::saleNoValidate(array(
86
+ * 'customer' => array(
87
+ * 'firstName' => 'Adam',
88
+ * 'lastName' => 'Williams'
89
+ * ),
90
+ * 'creditCard' => array(
91
+ * 'number' => '5105105105105100',
92
+ * 'expirationDate' => '05/2012'
93
+ * ),
94
+ * 'options' => array(
95
+ * 'storeInVault' => true
96
+ * )
97
+ * ));
98
+ *
99
+ * echo $transaction->customerDetails->id
100
+ * // '865534'
101
+ * echo $transaction->creditCardDetails->token
102
+ * // '6b6m'
103
+ * </code>
104
+ *
105
+ * To also store the billing address in the vault, pass the
106
+ * <b>addBillingAddressToPaymentMethod</b> option.
107
+ * <code>
108
+ * Braintree_Transaction.saleNoValidate(array(
109
+ * ...
110
+ * 'options' => array(
111
+ * 'storeInVault' => true
112
+ * 'addBillingAddressToPaymentMethod' => true
113
+ * )
114
+ * ));
115
+ * </code>
116
+ *
117
+ * <b>== Submitting for Settlement==</b>
118
+ *
119
+ * This can only be done when the transction's
120
+ * status is <b>authorized</b>. If <b>amount</b> is not specified,
121
+ * the full authorized amount will be settled. If you would like to settle
122
+ * less than the full authorized amount, pass the desired amount.
123
+ * You cannot settle more than the authorized amount.
124
+ *
125
+ * A transaction can be submitted for settlement when created by setting
126
+ * $transaction[options][submitForSettlement] to true.
127
+ *
128
+ * <code>
129
+ * $transaction = Braintree_Transaction::saleNoValidate(array(
130
+ * 'amount' => '100.00',
131
+ * 'creditCard' => array(
132
+ * 'number' => '5105105105105100',
133
+ * 'expirationDate' => '05/2012'
134
+ * ),
135
+ * 'options' => array(
136
+ * 'submitForSettlement' => true
137
+ * )
138
+ * ));
139
+ * </code>
140
+ *
141
+ * <b>== More information ==</b>
142
+ *
143
+ * For more detailed information on Transactions, see {@link http://www.braintreepayments.com/gateway/transaction-api http://www.braintreepaymentsolutions.com/gateway/transaction-api}
144
+ *
145
+ * @package Braintree
146
+ * @category Resources
147
+ * @copyright 2010 Braintree Payment Solutions
148
+ *
149
+ *
150
+ * @property-read string $avsErrorResponseCode
151
+ * @property-read string $avsPostalCodeResponseCode
152
+ * @property-read string $avsStreetAddressResponseCode
153
+ * @property-read string $cvvResponseCode
154
+ * @property-read string $id transaction id
155
+ * @property-read string $amount transaction amount
156
+ * @property-read object $billingDetails transaction billing address
157
+ * @property-read string $createdAt transaction created timestamp
158
+ * @property-read object $creditCardDetails transaction credit card info
159
+ * @property-read object $customerDetails transaction customer info
160
+ * @property-read array $customFields custom fields passed with the request
161
+ * @property-read string $processorResponseCode gateway response code
162
+ * @property-read object $shippingDetails transaction shipping address
163
+ * @property-read string $status transaction status
164
+ * @property-read array $statusHistory array of StatusDetails objects
165
+ * @property-read string $type transaction type
166
+ * @property-read string $updatedAt transaction updated timestamp
167
+ *
168
+ */
169
+
170
+ final class Braintree_Transaction extends Braintree
171
+ {
172
+ // Transaction Status
173
+ const AUTHORIZATION_EXPIRED = 'authorization_expired';
174
+ const AUTHORIZING = 'authorizing';
175
+ const AUTHORIZED = 'authorized';
176
+ const GATEWAY_REJECTED = 'gateway_rejected';
177
+ const FAILED = 'failed';
178
+ const PROCESSOR_DECLINED = 'processor_declined';
179
+ const SETTLED = 'settled';
180
+ const SETTLING = 'settling';
181
+ const SUBMITTED_FOR_SETTLEMENT = 'submitted_for_settlement';
182
+ const VOIDED = 'voided';
183
+
184
+ // Transaction Types
185
+ const SALE = 'sale';
186
+ const CREDIT = 'credit';
187
+
188
+ // Transaction Created Using
189
+ const FULL_INFORMATION = 'full_information';
190
+ const TOKEN = 'token';
191
+
192
+ // Transaction Sources
193
+ const API = 'api';
194
+ const CONTROL_PANEL = 'control_panel';
195
+ const RECURRING = 'recurring';
196
+
197
+ // Gateway Rejection Reason
198
+ const AVS = 'avs';
199
+ const AVS_AND_CVV = 'avs_and_cvv';
200
+ const CVV = 'cvv';
201
+ const DUPLICATE = 'duplicate';
202
+
203
+ public static function cloneTransaction($transactionId, $attribs)
204
+ {
205
+ Braintree_Util::verifyKeys(self::cloneSignature(), $attribs);
206
+ return self::_doCreate('/transactions/' . $transactionId . '/clone', array('transactionClone' => $attribs));
207
+ }
208
+
209
+ /**
210
+ * @ignore
211
+ * @access public
212
+ * @param array $attribs
213
+ * @return object
214
+ */
215
+ private static function create($attribs)
216
+ {
217
+ Braintree_Util::verifyKeys(self::createSignature(), $attribs);
218
+ return self::_doCreate('/transactions', array('transaction' => $attribs));
219
+ }
220
+
221
+ /**
222
+ *
223
+ * @ignore
224
+ * @access public
225
+ * @param array $attribs
226
+ * @return object
227
+ * @throws Braintree_Exception_ValidationError
228
+ */
229
+ private static function createNoValidate($attribs)
230
+ {
231
+ $result = self::create($attribs);
232
+ return self::returnObjectOrThrowException(__CLASS__, $result);
233
+ }
234
+ /**
235
+ *
236
+ * @access public
237
+ * @param array $attribs
238
+ * @return object
239
+ */
240
+ public static function createFromTransparentRedirect($queryString)
241
+ {
242
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::confirm", E_USER_NOTICE);
243
+ $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
244
+ $queryString
245
+ );
246
+ return self::_doCreate(
247
+ '/transactions/all/confirm_transparent_redirect_request',
248
+ array('id' => $params['id'])
249
+ );
250
+ }
251
+ /**
252
+ *
253
+ * @access public
254
+ * @param none
255
+ * @return string
256
+ */
257
+ public static function createTransactionUrl()
258
+ {
259
+ trigger_error("DEPRECATED: Please use Braintree_TransparentRedirectRequest::url", E_USER_NOTICE);
260
+ return Braintree_Configuration::merchantUrl() .
261
+ '/transactions/all/create_via_transparent_redirect_request';
262
+ }
263
+
264
+ public static function cloneSignature()
265
+ {
266
+ return array('amount', 'channel', array('options' => array('submitForSettlement')));
267
+ }
268
+
269
+ /**
270
+ * creates a full array signature of a valid gateway request
271
+ * @return array gateway request signature format
272
+ */
273
+ public static function createSignature()
274
+ {
275
+ return array(
276
+ 'amount', 'customerId', 'merchantAccountId', 'orderId', 'channel', 'paymentMethodToken',
277
+ 'purchaseOrderNumber', 'recurring', 'shippingAddressId', 'taxAmount', 'taxExempt', 'type', 'venmoSdkPaymentMethodCode',
278
+ array('creditCard' =>
279
+ array('token', 'cardholderName', 'cvv', 'expirationDate', 'expirationMonth', 'expirationYear', 'number'),
280
+ ),
281
+ array('customer' =>
282
+ array(
283
+ 'id', 'company', 'email', 'fax', 'firstName',
284
+ 'lastName', 'phone', 'website'),
285
+ ),
286
+ array('billing' =>
287
+ array(
288
+ 'firstName', 'lastName', 'company', 'countryName',
289
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
290
+ 'extendedAddress', 'locality', 'postalCode', 'region',
291
+ 'streetAddress'),
292
+ ),
293
+ array('shipping' =>
294
+ array(
295
+ 'firstName', 'lastName', 'company', 'countryName',
296
+ 'countryCodeAlpha2', 'countryCodeAlpha3', 'countryCodeNumeric',
297
+ 'extendedAddress', 'locality', 'postalCode', 'region',
298
+ 'streetAddress'),
299
+ ),
300
+ array('options' =>
301
+ array(
302
+ 'storeInVault',
303
+ 'storeInVaultOnSuccess',
304
+ 'submitForSettlement',
305
+ 'addBillingAddressToPaymentMethod',
306
+ 'venmoSdkSession',
307
+ 'storeShippingAddressInVault'),
308
+ ),
309
+ array('customFields' => array('_anyKey_')
310
+ ),
311
+ array('descriptor' => array('name', 'phone')),
312
+ );
313
+ }
314
+
315
+ /**
316
+ *
317
+ * @access public
318
+ * @param array $attribs
319
+ * @return object
320
+ */
321
+ public static function credit($attribs)
322
+ {
323
+ return self::create(array_merge($attribs, array('type' => Braintree_Transaction::CREDIT)));
324
+ }
325
+
326
+ /**
327
+ *
328
+ * @access public
329
+ * @param array $attribs
330
+ * @return object
331
+ * @throws Braintree_Exception_ValidationError
332
+ */
333
+ public static function creditNoValidate($attribs)
334
+ {
335
+ $result = self::credit($attribs);
336
+ return self::returnObjectOrThrowException(__CLASS__, $result);
337
+ }
338
+
339
+
340
+ /**
341
+ * @access public
342
+ *
343
+ */
344
+ public static function find($id)
345
+ {
346
+ self::_validateId($id);
347
+ try {
348
+ $response = Braintree_Http::get('/transactions/'.$id);
349
+ return self::factory($response['transaction']);
350
+ } catch (Braintree_Exception_NotFound $e) {
351
+ throw new Braintree_Exception_NotFound(
352
+ 'transaction with id ' . $id . ' not found'
353
+ );
354
+ }
355
+
356
+ }
357
+ /**
358
+ * new sale
359
+ * @param array $attribs
360
+ * @return array
361
+ */
362
+ public static function sale($attribs)
363
+ {
364
+ return self::create(array_merge(array('type' => Braintree_Transaction::SALE), $attribs));
365
+ }
366
+
367
+ /**
368
+ * roughly equivalent to the ruby bang method
369
+ * @access public
370
+ * @param array $attribs
371
+ * @return array
372
+ * @throws Braintree_Exception_ValidationsFailed
373
+ */
374
+ public static function saleNoValidate($attribs)
375
+ {
376
+ $result = self::sale($attribs);
377
+ return self::returnObjectOrThrowException(__CLASS__, $result);
378
+ }
379
+
380
+ /**
381
+ * Returns a ResourceCollection of transactions matching the search query.
382
+ *
383
+ * If <b>query</b> is a string, the search will be a basic search.
384
+ * If <b>query</b> is a hash, the search will be an advanced search.
385
+ * For more detailed information and examples, see {@link http://www.braintreepayments.com/gateway/transaction-api#searching http://www.braintreepaymentsolutions.com/gateway/transaction-api}
386
+ *
387
+ * @param mixed $query search query
388
+ * @param array $options options such as page number
389
+ * @return object Braintree_ResourceCollection
390
+ * @throws InvalidArgumentException
391
+ */
392
+ public static function search($query)
393
+ {
394
+ $criteria = array();
395
+ foreach ($query as $term) {
396
+ $criteria[$term->name] = $term->toparam();
397
+ }
398
+
399
+ $response = braintree_http::post('/transactions/advanced_search_ids', array('search' => $criteria));
400
+ $pager = array(
401
+ 'className' => __CLASS__,
402
+ 'classMethod' => 'fetch',
403
+ 'methodArgs' => array($query)
404
+ );
405
+
406
+ return new Braintree_ResourceCollection($response, $pager);
407
+ }
408
+
409
+ public static function fetch($query, $ids)
410
+ {
411
+ $criteria = array();
412
+ foreach ($query as $term) {
413
+ $criteria[$term->name] = $term->toparam();
414
+ }
415
+ $criteria["ids"] = Braintree_TransactionSearch::ids()->in($ids)->toparam();
416
+ $response = braintree_http::post('/transactions/advanced_search', array('search' => $criteria));
417
+
418
+ return braintree_util::extractattributeasarray(
419
+ $response['creditCardTransactions'],
420
+ 'transaction'
421
+ );
422
+ }
423
+
424
+ /**
425
+ * void a transaction by id
426
+ *
427
+ * @param string $id transaction id
428
+ * @return object Braintree_Result_Successful|Braintree_Result_Error
429
+ */
430
+ public static function void($transactionId)
431
+ {
432
+ self::_validateId($transactionId);
433
+
434
+ $response = Braintree_Http::put('/transactions/'. $transactionId . '/void');
435
+ return self::_verifyGatewayResponse($response);
436
+ }
437
+ /**
438
+ *
439
+ */
440
+ public static function voidNoValidate($transactionId)
441
+ {
442
+ $result = self::void($transactionId);
443
+ return self::returnObjectOrThrowException(__CLASS__, $result);
444
+ }
445
+
446
+ public static function submitForSettlement($transactionId, $amount = null)
447
+ {
448
+ self::_validateId($transactionId);
449
+
450
+ $response = Braintree_Http::put(
451
+ '/transactions/'. $transactionId . '/submit_for_settlement',
452
+ array( 'transaction' => array( 'amount' => $amount))
453
+ );
454
+ return self::_verifyGatewayResponse($response);
455
+ }
456
+
457
+ public static function submitForSettlementNoValidate($transactionId, $amount = null)
458
+ {
459
+ $result = self::submitForSettlement($transactionId, $amount);
460
+ return self::returnObjectOrThrowException(__CLASS__, $result);
461
+ }
462
+
463
+
464
+ /**
465
+ * sets instance properties from an array of values
466
+ *
467
+ * @ignore
468
+ * @access protected
469
+ * @param array $transactionAttribs array of transaction data
470
+ * @return none
471
+ */
472
+ protected function _initialize($transactionAttribs)
473
+ {
474
+ $this->_attributes = $transactionAttribs;
475
+
476
+ $this->_set('creditCardDetails',
477
+ new Braintree_Transaction_CreditCardDetails(
478
+ $transactionAttribs['creditCard']
479
+ )
480
+ );
481
+ $this->_set('customerDetails',
482
+ new Braintree_Transaction_CustomerDetails(
483
+ $transactionAttribs['customer']
484
+ )
485
+ );
486
+ $this->_set('billingDetails',
487
+ new Braintree_Transaction_AddressDetails(
488
+ $transactionAttribs['billing']
489
+ )
490
+ );
491
+ $this->_set('shippingDetails',
492
+ new Braintree_Transaction_AddressDetails(
493
+ $transactionAttribs['shipping']
494
+ )
495
+ );
496
+ $this->_set('subscriptionDetails',
497
+ new Braintree_Transaction_SubscriptionDetails(
498
+ $transactionAttribs['subscription']
499
+ )
500
+ );
501
+ $this->_set('descriptor',
502
+ new Braintree_Descriptor(
503
+ $transactionAttribs['descriptor']
504
+ )
505
+ );
506
+
507
+ $statusHistory = array();
508
+ foreach ($transactionAttribs['statusHistory'] AS $history) {
509
+ $statusHistory[] = new Braintree_Transaction_StatusDetails($history);
510
+ }
511
+ $this->_set('statusHistory', $statusHistory);
512
+
513
+
514
+ $addOnArray = array();
515
+ if (isset($transactionAttribs['addOns'])) {
516
+ foreach ($transactionAttribs['addOns'] AS $addOn) {
517
+ $addOnArray[] = Braintree_AddOn::factory($addOn);
518
+ }
519
+ }
520
+ $this->_set('addOns', $addOnArray);
521
+
522
+ $discountArray = array();
523
+ if (isset($transactionAttribs['discounts'])) {
524
+ foreach ($transactionAttribs['discounts'] AS $discount) {
525
+ $discountArray[] = Braintree_Discount::factory($discount);
526
+ }
527
+ }
528
+ $this->_set('discounts', $discountArray);
529
+ }
530
+
531
+ /**
532
+ * returns a string representation of the transaction
533
+ * @return string
534
+ */
535
+ public function __toString()
536
+ {
537
+ // array of attributes to print
538
+ $display = array(
539
+ 'id', 'type', 'amount', 'status',
540
+ 'createdAt', 'creditCardDetails', 'customerDetails'
541
+ );
542
+
543
+ $displayAttributes = array();
544
+ foreach ($display AS $attrib) {
545
+ $displayAttributes[$attrib] = $this->$attrib;
546
+ }
547
+ return __CLASS__ . '[' .
548
+ Braintree_Util::attributesToString($displayAttributes) .']';
549
+ }
550
+
551
+ public static function refund($transactionId, $amount = null)
552
+ {
553
+ $params = array('transaction' => array('amount' => $amount));
554
+ $response = Braintree_Http::post('/transactions/' . $transactionId . '/refund', $params);
555
+ return self::_verifyGatewayResponse($response);
556
+ }
557
+
558
+ public function isEqual($otherTx)
559
+ {
560
+ return $this->id === $otherTx->id;
561
+ }
562
+
563
+ public function vaultCreditCard()
564
+ {
565
+ $token = $this->creditCardDetails->token;
566
+ if (empty($token)) {
567
+ return null;
568
+ }
569
+ else {
570
+ return Braintree_CreditCard::find($token);
571
+ }
572
+ }
573
+
574
+ public function vaultCustomer()
575
+ {
576
+ $customerId = $this->customerDetails->id;
577
+ if (empty($customerId)) {
578
+ return null;
579
+ }
580
+ else {
581
+ return Braintree_Customer::find($customerId);
582
+ }
583
+ }
584
+
585
+ /**
586
+ * sends the create request to the gateway
587
+ *
588
+ * @ignore
589
+ * @param var $url
590
+ * @param array $params
591
+ * @return mixed
592
+ */
593
+ public static function _doCreate($url, $params)
594
+ {
595
+ $response = Braintree_Http::post($url, $params);
596
+
597
+ return self::_verifyGatewayResponse($response);
598
+ }
599
+
600
+ /**
601
+ * verifies that a valid transaction id is being used
602
+ * @ignore
603
+ * @param string transaction id
604
+ * @throws InvalidArgumentException
605
+ */
606
+ private static function _validateId($id = null) {
607
+ if (empty($id)) {
608
+ throw new InvalidArgumentException(
609
+ 'expected transaction id to be set'
610
+ );
611
+ }
612
+ if (!preg_match('/^[0-9a-z]+$/', $id)) {
613
+ throw new InvalidArgumentException(
614
+ $id . ' is an invalid transaction id.'
615
+ );
616
+ }
617
+ }
618
+
619
+
620
+ /* private class methods */
621
+
622
+ /**
623
+ * generic method for validating incoming gateway responses
624
+ *
625
+ * creates a new Braintree_Transaction object and encapsulates
626
+ * it inside a Braintree_Result_Successful object, or
627
+ * encapsulates a Braintree_Errors object inside a Result_Error
628
+ * alternatively, throws an Unexpected exception if the response is invalid.
629
+ *
630
+ * @ignore
631
+ * @param array $response gateway response values
632
+ * @return object Result_Successful or Result_Error
633
+ * @throws Braintree_Exception_Unexpected
634
+ */
635
+ private static function _verifyGatewayResponse($response)
636
+ {
637
+ if (isset($response['transaction'])) {
638
+ // return a populated instance of Braintree_Transaction
639
+ return new Braintree_Result_Successful(
640
+ self::factory($response['transaction'])
641
+ );
642
+ } else if (isset($response['apiErrorResponse'])) {
643
+ return new Braintree_Result_Error($response['apiErrorResponse']);
644
+ } else {
645
+ throw new Braintree_Exception_Unexpected(
646
+ "Expected transaction or apiErrorResponse"
647
+ );
648
+ }
649
+ }
650
+
651
+ /**
652
+ * factory method: returns an instance of Braintree_Transaction
653
+ * to the requesting method, with populated properties
654
+ *
655
+ * @ignore
656
+ * @return object instance of Braintree_Transaction
657
+ */
658
+ public static function factory($attributes)
659
+ {
660
+ $instance = new self();
661
+ $instance->_initialize($attributes);
662
+ return $instance;
663
+ }
664
+ }
includes/lib/Braintree/Braintree/Transaction/AddressDetails.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Address details from a transaction
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Transaction
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates an instance of AddressDetails as returned from a transaction
12
+ *
13
+ *
14
+ * @package Braintree
15
+ * @subpackage Transaction
16
+ * @copyright 2010 Braintree Payment Solutions
17
+ *
18
+ * @property-read string $firstName
19
+ * @property-read string $lastName
20
+ * @property-read string $company
21
+ * @property-read string $streetAddress
22
+ * @property-read string $extendedAddress
23
+ * @property-read string $locality
24
+ * @property-read string $region
25
+ * @property-read string $postalCode
26
+ * @property-read string $countryName
27
+ * @uses Braintree_Instance inherits methods
28
+ */
29
+ class Braintree_Transaction_AddressDetails extends Braintree_Instance
30
+ {
31
+ protected $_attributes = array();
32
+ }
includes/lib/Braintree/Braintree/Transaction/CreditCardDetails.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CreditCard details from a transaction
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Transaction
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * creates an instance of CreditCardDetails
12
+ *
13
+ *
14
+ * @package Braintree
15
+ * @subpackage Transaction
16
+ * @copyright 2010 Braintree Payment Solutions
17
+ *
18
+ * @property-read string $bin
19
+ * @property-read string $cardType
20
+ * @property-read string $expirationDate
21
+ * @property-read string $expirationMonth
22
+ * @property-read string $expirationYear
23
+ * @property-read string $issuerLocation
24
+ * @property-read string $last4
25
+ * @property-read string $maskedNumber
26
+ * @property-read string $token
27
+ * @uses Braintree_Instance inherits methods
28
+ */
29
+ class Braintree_Transaction_CreditCardDetails extends Braintree_Instance
30
+ {
31
+ protected $_attributes = array();
32
+
33
+ /**
34
+ * @ignore
35
+ */
36
+ public function __construct($attributes)
37
+ {
38
+ parent::__construct($attributes);
39
+ $this->_attributes['expirationDate'] = $this->expirationMonth . '/' . $this->expirationYear;
40
+ $this->_attributes['maskedNumber'] = $this->bin . '******' . $this->last4;
41
+
42
+ }
43
+ }
includes/lib/Braintree/Braintree/Transaction/CustomerDetails.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Customer details from a transaction
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Transaction
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates an instance of customer details as returned from a transaction
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Transaction
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ *
17
+ * @property-read string $company
18
+ * @property-read string $email
19
+ * @property-read string $fax
20
+ * @property-read string $firstName
21
+ * @property-read string $id
22
+ * @property-read string $lastName
23
+ * @property-read string $phone
24
+ * @property-read string $website
25
+ * @uses Braintree_Instance inherits methods
26
+ */
27
+ class Braintree_Transaction_CustomerDetails extends Braintree_Instance
28
+ {
29
+ }
includes/lib/Braintree/Braintree/Transaction/StatusDetails.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Status details from a transaction
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Transaction
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates an instance of StatusDetails, as part of a transaction response
12
+ *
13
+ * @package Braintree
14
+ * @copyright 2010 Braintree Payment Solutions
15
+ *
16
+ * @property-read string $amount
17
+ * @property-read string $status
18
+ * @property-read string $timestamp
19
+ * @property-read string $transactionSource
20
+ * @property-read string $user
21
+ * @uses Braintree_Instance inherits methods
22
+ */
23
+ class Braintree_Transaction_StatusDetails extends Braintree_Instance
24
+ {
25
+ }
includes/lib/Braintree/Braintree/Transaction/SubscriptionDetails.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Customer details from a transaction
4
+ *
5
+ * @package Braintree
6
+ * @subpackage Transaction
7
+ * @copyright 2010 Braintree Payment Solutions
8
+ */
9
+
10
+ /**
11
+ * Creates an instance of customer details as returned from a transaction
12
+ *
13
+ * @package Braintree
14
+ * @subpackage Transaction
15
+ * @copyright 2010 Braintree Payment Solutions
16
+ *
17
+ * @property-read string $billing_period_start_date
18
+ * @property-read string $billing_period_end_date
19
+ */
20
+ class Braintree_Transaction_SubscriptionDetails extends Braintree_Instance
21
+ {
22
+ }
includes/lib/Braintree/Braintree/TransactionSearch.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_TransactionSearch
3
+ {
4
+ static function billingCompany() { return new Braintree_TextNode('billing_company'); }
5
+ static function billingCountryName() { return new Braintree_TextNode('billing_country_name'); }
6
+ static function billingExtendedAddress() { return new Braintree_TextNode('billing_extended_address'); }
7
+ static function billingFirstName() { return new Braintree_TextNode('billing_first_name'); }
8
+ static function billingLastName() { return new Braintree_TextNode('billing_last_name'); }
9
+ static function billingLocality() { return new Braintree_TextNode('billing_locality'); }
10
+ static function billingPostalCode() { return new Braintree_TextNode('billing_postal_code'); }
11
+ static function billingRegion() { return new Braintree_TextNode('billing_region'); }
12
+ static function billingStreetAddress() { return new Braintree_TextNode('billing_street_address'); }
13
+ static function creditCardCardholderName() { return new Braintree_TextNode('credit_card_cardholderName'); }
14
+ static function customerCompany() { return new Braintree_TextNode('customer_company'); }
15
+ static function customerEmail() { return new Braintree_TextNode('customer_email'); }
16
+ static function customerFax() { return new Braintree_TextNode('customer_fax'); }
17
+ static function customerFirstName() { return new Braintree_TextNode('customer_first_name'); }
18
+ static function customerId() { return new Braintree_TextNode('customer_id'); }
19
+ static function customerLastName() { return new Braintree_TextNode('customer_last_name'); }
20
+ static function customerPhone() { return new Braintree_TextNode('customer_phone'); }
21
+ static function customerWebsite() { return new Braintree_TextNode('customer_website'); }
22
+ static function id() { return new Braintree_TextNode('id'); }
23
+ static function ids() { return new Braintree_MultipleValueNode('ids'); }
24
+ static function orderId() { return new Braintree_TextNode('order_id'); }
25
+ static function paymentMethodToken() { return new Braintree_TextNode('payment_method_token'); }
26
+ static function processorAuthorizationCode() { return new Braintree_TextNode('processor_authorization_code'); }
27
+ static function settlementBatchId() { return new Braintree_TextNode('settlement_batch_id'); }
28
+ static function shippingCompany() { return new Braintree_TextNode('shipping_company'); }
29
+ static function shippingCountryName() { return new Braintree_TextNode('shipping_country_name'); }
30
+ static function shippingExtendedAddress() { return new Braintree_TextNode('shipping_extended_address'); }
31
+ static function shippingFirstName() { return new Braintree_TextNode('shipping_first_name'); }
32
+ static function shippingLastName() { return new Braintree_TextNode('shipping_last_name'); }
33
+ static function shippingLocality() { return new Braintree_TextNode('shipping_locality'); }
34
+ static function shippingPostalCode() { return new Braintree_TextNode('shipping_postal_code'); }
35
+ static function shippingRegion() { return new Braintree_TextNode('shipping_region'); }
36
+ static function shippingStreetAddress() { return new Braintree_TextNode('shipping_street_address'); }
37
+
38
+ static function creditCardExpirationDate() { return new Braintree_EqualityNode('credit_card_expiration_date'); }
39
+
40
+ static function creditCardNumber() { return new Braintree_PartialMatchNode('credit_card_number'); }
41
+
42
+ static function refund() { return new Braintree_KeyValueNode("refund"); }
43
+
44
+ static function amount() { return new Braintree_RangeNode("amount"); }
45
+ static function authorizedAt() { return new Braintree_RangeNode("authorizedAt"); }
46
+ static function authorizationExpiredAt() { return new Braintree_RangeNode("authorizationExpiredAt"); }
47
+ static function createdAt() { return new Braintree_RangeNode("createdAt"); }
48
+ static function failedAt() { return new Braintree_RangeNode("failedAt"); }
49
+ static function gatewayRejectedAt() { return new Braintree_RangeNode("gatewayRejectedAt"); }
50
+ static function processorDeclinedAt() { return new Braintree_RangeNode("processorDeclinedAt"); }
51
+ static function settledAt() { return new Braintree_RangeNode("settledAt"); }
52
+ static function submittedForSettlementAt() { return new Braintree_RangeNode("submittedForSettlementAt"); }
53
+ static function voidedAt() { return new Braintree_RangeNode("voidedAt"); }
54
+
55
+ static function merchantAccountId() { return new Braintree_MultipleValueNode("merchant_account_id"); }
56
+
57
+ static function createdUsing()
58
+ {
59
+ return new Braintree_MultipleValueNode("created_using", array(
60
+ Braintree_Transaction::FULL_INFORMATION,
61
+ Braintree_Transaction::TOKEN
62
+ ));
63
+ }
64
+
65
+ static function creditCardCardType()
66
+ {
67
+ return new Braintree_MultipleValueNode("credit_card_card_type", array(
68
+ Braintree_CreditCard::AMEX,
69
+ Braintree_CreditCard::CARTE_BLANCHE,
70
+ Braintree_CreditCard::CHINA_UNION_PAY,
71
+ Braintree_CreditCard::DINERS_CLUB_INTERNATIONAL,
72
+ Braintree_CreditCard::DISCOVER,
73
+ Braintree_CreditCard::JCB,
74
+ Braintree_CreditCard::LASER,
75
+ Braintree_CreditCard::MAESTRO,
76
+ Braintree_CreditCard::MASTER_CARD,
77
+ Braintree_CreditCard::SOLO,
78
+ Braintree_CreditCard::SWITCH_TYPE,
79
+ Braintree_CreditCard::VISA,
80
+ Braintree_CreditCard::UNKNOWN
81
+ ));
82
+ }
83
+
84
+ static function creditCardCustomerLocation()
85
+ {
86
+ return new Braintree_MultipleValueNode("credit_card_customer_location", array(
87
+ Braintree_CreditCard::INTERNATIONAL,
88
+ Braintree_CreditCard::US
89
+ ));
90
+ }
91
+
92
+ static function source()
93
+ {
94
+ return new Braintree_MultipleValueNode("source", array(
95
+ Braintree_Transaction::API,
96
+ Braintree_Transaction::CONTROL_PANEL,
97
+ Braintree_Transaction::RECURRING,
98
+ ));
99
+ }
100
+
101
+ static function status()
102
+ {
103
+ return new Braintree_MultipleValueNode("status", array(
104
+ Braintree_Transaction::AUTHORIZATION_EXPIRED,
105
+ Braintree_Transaction::AUTHORIZING,
106
+ Braintree_Transaction::AUTHORIZED,
107
+ Braintree_Transaction::GATEWAY_REJECTED,
108
+ Braintree_Transaction::FAILED,
109
+ Braintree_Transaction::PROCESSOR_DECLINED,
110
+ Braintree_Transaction::SETTLED,
111
+ Braintree_Transaction::SETTLING,
112
+ Braintree_Transaction::SUBMITTED_FOR_SETTLEMENT,
113
+ Braintree_Transaction::VOIDED
114
+ ));
115
+ }
116
+
117
+ static function type()
118
+ {
119
+ return new Braintree_MultipleValueNode("type", array(
120
+ Braintree_Transaction::SALE,
121
+ Braintree_Transaction::CREDIT
122
+ ));
123
+ }
124
+ }
includes/lib/Braintree/Braintree/TransparentRedirect.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ /**
5
+ * Braintree Transparent Redirect module
6
+ *
7
+ * @package Braintree
8
+ * @category Resources
9
+ * @copyright 2010 Braintree Payment Solutions
10
+ */
11
+
12
+ /**
13
+ * Static class providing methods to build Transparent Redirect urls
14
+ *
15
+ * The TransparentRedirect module provides methods to build the tr_data param
16
+ * that must be submitted when using the transparent redirect API.
17
+ * For more information
18
+ * about transparent redirect, see (TODO).
19
+ *
20
+ * You must provide a redirectUrl to which the gateway will redirect the
21
+ * user the action is complete.
22
+ *
23
+ * <code>
24
+ * $trData = Braintree_TransparentRedirect::createCustomerData(array(
25
+ * 'redirectUrl => 'http://example.com/redirect_back_to_merchant_site',
26
+ * ));
27
+ * </code>
28
+ *
29
+ * In addition to the redirectUrl, any data that needs to be protected
30
+ * from user tampering should be included in the trData.
31
+ * For example, to prevent the user from tampering with the transaction
32
+ * amount, include the amount in the trData.
33
+ *
34
+ * <code>
35
+ * $trData = Braintree_TransparentRedirect::transactionData(array(
36
+ * 'redirectUrl' => 'http://example.com/complete_transaction',
37
+ * 'transaction' => array('amount' => '100.00'),
38
+ * ));
39
+ *
40
+ * </code>
41
+ *
42
+ * @package Braintree
43
+ * @category Resources
44
+ * @copyright 2010 Braintree Payment Solutions
45
+ */
46
+ class Braintree_TransparentRedirect
47
+ {
48
+ // Request Kinds
49
+ const CREATE_TRANSACTION = 'create_transaction';
50
+ const CREATE_PAYMENT_METHOD = 'create_payment_method';
51
+ const UPDATE_PAYMENT_METHOD = 'update_payment_method';
52
+ const CREATE_CUSTOMER = 'create_customer';
53
+ const UPDATE_CUSTOMER = 'update_customer';
54
+
55
+ /**
56
+ *
57
+ * @ignore
58
+ */
59
+ private static $_transparentRedirectKeys = 'redirectUrl';
60
+ private static $_createCustomerSignature;
61
+ private static $_updateCustomerSignature;
62
+ private static $_transactionSignature;
63
+ private static $_createCreditCardSignature;
64
+ private static $_updateCreditCardSignature;
65
+
66
+
67
+ /**
68
+ * @ignore
69
+ * don't permit an explicit call of the constructor!
70
+ * (like $t = new Braintree_TransparentRedirect())
71
+ */
72
+ protected function __construct()
73
+ {
74
+
75
+ }
76
+
77
+ /**
78
+ * create signatures for different call types
79
+ * @ignore
80
+ */
81
+ public static function init()
82
+ {
83
+
84
+ self::$_createCustomerSignature = array(
85
+ self::$_transparentRedirectKeys,
86
+ array('customer' => Braintree_Customer::createSignature()),
87
+ );
88
+ self::$_updateCustomerSignature = array(
89
+ self::$_transparentRedirectKeys,
90
+ 'customerId',
91
+ array('customer' => Braintree_Customer::updateSignature()),
92
+ );
93
+ self::$_transactionSignature = array(
94
+ self::$_transparentRedirectKeys,
95
+ array('transaction' => Braintree_Transaction::createSignature()),
96
+ );
97
+ self::$_createCreditCardSignature = array(
98
+ self::$_transparentRedirectKeys,
99
+ array('creditCard' => Braintree_CreditCard::createSignature()),
100
+ );
101
+ self::$_updateCreditCardSignature = array(
102
+ self::$_transparentRedirectKeys,
103
+ 'paymentMethodToken',
104
+ array('creditCard' => Braintree_CreditCard::updateSignature()),
105
+ );
106
+ }
107
+
108
+ public static function confirm($queryString)
109
+ {
110
+ $params = Braintree_TransparentRedirect::parseAndValidateQueryString(
111
+ $queryString
112
+ );
113
+ $confirmationKlasses = array(
114
+ Braintree_TransparentRedirect::CREATE_TRANSACTION => 'Braintree_Transaction',
115
+ Braintree_TransparentRedirect::CREATE_CUSTOMER => 'Braintree_Customer',
116
+ Braintree_TransparentRedirect::UPDATE_CUSTOMER => 'Braintree_Customer',
117
+ Braintree_TransparentRedirect::CREATE_PAYMENT_METHOD => 'Braintree_CreditCard',
118
+ Braintree_TransparentRedirect::UPDATE_PAYMENT_METHOD => 'Braintree_CreditCard'
119
+ );
120
+ return call_user_func(array($confirmationKlasses[$params["kind"]], '_doCreate'),
121
+ '/transparent_redirect_requests/' . $params['id'] . '/confirm',
122
+ array()
123
+ );
124
+ }
125
+
126
+ /**
127
+ * returns the trData string for creating a credit card,
128
+ * @param array $params
129
+ * @return string
130
+ */
131
+ public static function createCreditCardData($params)
132
+ {
133
+ Braintree_Util::verifyKeys(
134
+ self::$_createCreditCardSignature,
135
+ $params
136
+ );
137
+ $params["kind"] = Braintree_TransparentRedirect::CREATE_PAYMENT_METHOD;
138
+ return self::_data($params);
139
+ }
140
+
141
+ /**
142
+ * returns the trData string for creating a customer.
143
+ * @param array $params
144
+ * @return string
145
+ */
146
+ public static function createCustomerData($params)
147
+ {
148
+ Braintree_Util::verifyKeys(
149
+ self::$_createCustomerSignature,
150
+ $params
151
+ );
152
+ $params["kind"] = Braintree_TransparentRedirect::CREATE_CUSTOMER;
153
+ return self::_data($params);
154
+
155
+ }
156
+
157
+ public static function url()
158
+ {
159
+ return Braintree_Configuration::merchantUrl() . "/transparent_redirect_requests";
160
+ }
161
+
162
+ /**
163
+ * returns the trData string for creating a transaction
164
+ * @param array $params
165
+ * @return string
166
+ */
167
+ public static function transactionData($params)
168
+ {
169
+ Braintree_Util::verifyKeys(
170
+ self::$_transactionSignature,
171
+ $params
172
+ );
173
+ $params["kind"] = Braintree_TransparentRedirect::CREATE_TRANSACTION;
174
+ $transactionType = isset($params['transaction']['type']) ?
175
+ $params['transaction']['type'] :
176
+ null;
177
+ if ($transactionType != Braintree_Transaction::SALE && $transactionType != Braintree_Transaction::CREDIT) {
178
+ throw new InvalidArgumentException(
179
+ 'expected transaction[type] of sale or credit, was: ' .
180
+ $transactionType
181
+ );
182
+ }
183
+
184
+ return self::_data($params);
185
+ }
186
+
187
+ /**
188
+ * Returns the trData string for updating a credit card.
189
+ *
190
+ * The paymentMethodToken of the credit card to update is required.
191
+ *
192
+ * <code>
193
+ * $trData = Braintree_TransparentRedirect::updateCreditCardData(array(
194
+ * 'redirectUrl' => 'http://example.com/redirect_here',
195
+ * 'paymentMethodToken' => 'token123',
196
+ * ));
197
+ * </code>
198
+ *
199
+ * @param array $params
200
+ * @return string
201
+ */
202
+ public static function updateCreditCardData($params)
203
+ {
204
+ Braintree_Util::verifyKeys(
205
+ self::$_updateCreditCardSignature,
206
+ $params
207
+ );
208
+ if (!isset($params['paymentMethodToken'])) {
209
+ throw new InvalidArgumentException(
210
+ 'expected params to contain paymentMethodToken.'
211
+ );
212
+ }
213
+ $params["kind"] = Braintree_TransparentRedirect::UPDATE_PAYMENT_METHOD;
214
+ return self::_data($params);
215
+ }
216
+
217
+ /**
218
+ * Returns the trData string for updating a customer.
219
+ *
220
+ * The customerId of the customer to update is required.
221
+ *
222
+ * <code>
223
+ * $trData = Braintree_TransparentRedirect::updateCustomerData(array(
224
+ * 'redirectUrl' => 'http://example.com/redirect_here',
225
+ * 'customerId' => 'customer123',
226
+ * ));
227
+ * </code>
228
+ *
229
+ * @param array $params
230
+ * @return string
231
+ */
232
+ public static function updateCustomerData($params)
233
+ {
234
+ Braintree_Util::verifyKeys(
235
+ self::$_updateCustomerSignature,
236
+ $params
237
+ );
238
+ if (!isset($params['customerId'])) {
239
+ throw new InvalidArgumentException(
240
+ 'expected params to contain customerId of customer to update'
241
+ );
242
+ }
243
+ $params["kind"] = Braintree_TransparentRedirect::UPDATE_CUSTOMER;
244
+ return self::_data($params);
245
+ }
246
+
247
+ public static function parseAndValidateQueryString($queryString)
248
+ {
249
+ // parse the params into an array
250
+ parse_str($queryString, $params);
251
+ // remove the hash
252
+ $queryStringWithoutHash = null;
253
+ if(preg_match('/^(.*)&hash=[a-f0-9]+$/', $queryString, $match)) {
254
+ $queryStringWithoutHash = $match[1];
255
+ }
256
+
257
+ if($params['http_status'] != '200') {
258
+ $message = null;
259
+ if(array_key_exists('bt_message', $params)) {
260
+ $message = $params['bt_message'];
261
+ }
262
+ Braintree_Util::throwStatusCodeException($params['http_status'], $message);
263
+ }
264
+
265
+ // recreate the hash and compare it
266
+ if(self::_hash($queryStringWithoutHash) == $params['hash']) {
267
+ return $params;
268
+ } else {
269
+ throw new Braintree_Exception_ForgedQueryString();
270
+ }
271
+ }
272
+
273
+
274
+ /**
275
+ *
276
+ * @ignore
277
+ */
278
+ private static function _data($params)
279
+ {
280
+ if (!isset($params['redirectUrl'])) {
281
+ throw new InvalidArgumentException(
282
+ 'expected params to contain redirectUrl'
283
+ );
284
+ }
285
+ $params = self::_underscoreKeys($params);
286
+ $now = new DateTime('now', new DateTimeZone('UTC'));
287
+ $trDataParams = array_merge($params,
288
+ array(
289
+ 'api_version' => Braintree_Configuration::API_VERSION,
290
+ 'public_key' => Braintree_Configuration::publicKey(),
291
+ 'time' => $now->format('YmdHis'),
292
+ )
293
+ );
294
+ ksort($trDataParams);
295
+ $trDataSegment = http_build_query($trDataParams, null, '&');
296
+ $trDataHash = self::_hash($trDataSegment);
297
+ return "$trDataHash|$trDataSegment";
298
+ }
299
+
300
+ private static function _underscoreKeys($array)
301
+ {
302
+ foreach($array as $key=>$value)
303
+ {
304
+ $newKey = Braintree_Util::camelCaseToDelimiter($key, '_');
305
+ unset($array[$key]);
306
+ if (is_array($value))
307
+ {
308
+ $array[$newKey] = self::_underscoreKeys($value);
309
+ }
310
+ else
311
+ {
312
+ $array[$newKey] = $value;
313
+ }
314
+ }
315
+ return $array;
316
+ }
317
+
318
+ /**
319
+ * @ignore
320
+ */
321
+ private static function _hash($string)
322
+ {
323
+ return Braintree_Digest::hexDigest($string);
324
+ }
325
+
326
+ }
327
+ Braintree_TransparentRedirect::init();
includes/lib/Braintree/Braintree/Util.php ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Utility methods
4
+ * PHP version 5
5
+ *
6
+ * @copyright 2010 Braintree Payment Solutions
7
+ */
8
+
9
+ /**
10
+ * Braintree Utility methods
11
+ *
12
+ *
13
+ * @copyright 2010 Braintree Payment Solutions
14
+ */
15
+ class Braintree_Util
16
+ {
17
+ /**
18
+ * extracts an attribute and returns an array of objects
19
+ *
20
+ * extracts the requested element from an array, and converts the contents
21
+ * of its child arrays to objects of type Braintree_$attributeName, or returns
22
+ * an array with a single element containing the value of that array element
23
+ *
24
+ * @param array $attribArray attributes from a search response
25
+ * @param string $attributeName indicates which element of the passed array to extract
26
+ *
27
+ * @return array array of Braintree_$attributeName objects, or a single element array
28
+ */
29
+ public static function extractAttributeAsArray(& $attribArray, $attributeName)
30
+ {
31
+ if(!isset($attribArray[$attributeName])):
32
+ return array();
33
+ endif;
34
+
35
+ // get what should be an array from the passed array
36
+ $data = $attribArray[$attributeName];
37
+ // set up the class that will be used to convert each array element
38
+ $classFactory = self::buildClassName($attributeName) . '::factory';
39
+ if(is_array($data)):
40
+ // create an object from the data in each element
41
+ $objectArray = array_map($classFactory, $data);
42
+ else:
43
+ return array($data);
44
+ endif;
45
+
46
+ unset($attribArray[$attributeName]);
47
+ return $objectArray;
48
+ }
49
+ /**
50
+ * throws an exception based on the type of error
51
+ * @param string $statusCode HTTP status code to throw exception from
52
+ * @throws Braintree_Exception multiple types depending on the error
53
+ *
54
+ */
55
+ public static function throwStatusCodeException($statusCode, $message=null)
56
+ {
57
+ switch($statusCode) {
58
+ case 401:
59
+ throw new Braintree_Exception_Authentication();
60
+ break;
61
+ case 403:
62
+ throw new Braintree_Exception_Authorization($message);
63
+ break;
64
+ case 404:
65
+ throw new Braintree_Exception_NotFound();
66
+ break;
67
+ case 426:
68
+ throw new Braintree_Exception_UpgradeRequired();
69
+ break;
70
+ case 500:
71
+ throw new Braintree_Exception_ServerError();
72
+ break;
73
+ case 503:
74
+ throw new Braintree_Exception_DownForMaintenance();
75
+ break;
76
+ default:
77
+ throw new Braintree_Exception_Unexpected('Unexpected HTTP_RESPONSE #'.$statusCode);
78
+ break;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * removes the Braintree_ header from a classname
84
+ *
85
+ * @param string $name Braintree_ClassName
86
+ * @return camelCased classname minus Braintree_ header
87
+ */
88
+ public static function cleanClassName($name)
89
+ {
90
+ $classNamesToResponseKeys = array(
91
+ 'CreditCard' => 'creditCard',
92
+ 'Customer' => 'customer',
93
+ 'Subscription' => 'subscription',
94
+ 'Transaction' => 'transaction',
95
+ 'CreditCardVerification' => 'verification',
96
+ 'AddOn' => 'addOn',
97
+ 'Discount' => 'discount',
98
+ 'Plan' => 'plan',
99
+ 'Address' => 'address',
100
+ 'SettlementBatchSummary' => 'settlementBatchSummary'
101
+ );
102
+
103
+ $name = str_replace('Braintree_', '', $name);
104
+ return $classNamesToResponseKeys[$name];
105
+ }
106
+
107
+ /**
108
+ *
109
+ * @param string $name className
110
+ * @return string Braintree_ClassName
111
+ */
112
+ public static function buildClassName($name)
113
+ {
114
+ $responseKeysToClassNames = array(
115
+ 'creditCard' => 'CreditCard',
116
+ 'customer' => 'Customer',
117
+ 'subscription' => 'Subscription',
118
+ 'transaction' => 'Transaction',
119
+ 'verification' => 'CreditCardVerification',
120
+ 'addOn' => 'AddOn',
121
+ 'discount' => 'Discount',
122
+ 'plan' => 'Plan',
123
+ 'address' => 'Address',
124
+ 'settlementBatchSummary' => 'SettlementBatchSummary'
125
+ );
126
+
127
+ return 'Braintree_' . $responseKeysToClassNames[$name];
128
+ }
129
+
130
+ /**
131
+ * convert alpha-beta-gamma to alphaBetaGamma
132
+ *
133
+ * @access public
134
+ * @param string $string
135
+ * @return string modified string
136
+ */
137
+ public static function delimiterToCamelCase($string, $delimiter = '[\-\_]')
138
+ {
139
+ return preg_replace('/' . $delimiter . '(\w)/e', 'strtoupper("$1")',$string);
140
+ }
141
+
142
+ /**
143
+ * convert alpha-beta-gamma to alpha_beta_gamma
144
+ *
145
+ * @access public
146
+ * @param string $string
147
+ * @return string modified string
148
+ */
149
+ public static function delimiterToUnderscore($string)
150
+ {
151
+ return preg_replace('/-/', '_', $string);
152
+ }
153
+
154
+
155
+ /**
156
+ * find capitals and convert to delimiter + lowercase
157
+ *
158
+ * @access public
159
+ * @param var $string
160
+ * @return var modified string
161
+ */
162
+ public static function camelCaseToDelimiter($string, $delimiter = '-')
163
+ {
164
+ return preg_replace('/([A-Z])/e', '"' . $delimiter . '" . strtolower("$1")', $string);
165
+ }
166
+
167
+ /**
168
+ *
169
+ * @param array $array associative array to implode
170
+ * @param string $separator (optional, defaults to =)
171
+ * @param string $glue (optional, defaults to ', ')
172
+ */
173
+ public static function implodeAssociativeArray($array, $separator = '=', $glue = ', ')
174
+ {
175
+ // build a new array with joined keys and values
176
+ $tmpArray = null;
177
+ foreach ($array AS $key => $value) {
178
+ $tmpArray[] = $key . $separator . $value;
179
+
180
+ }
181
+ // implode and return the new array
182
+ return (is_array($tmpArray)) ? implode($glue, $tmpArray) : false;
183
+ }
184
+
185
+ public static function attributesToString($attributes) {
186
+ $printableAttribs = array();
187
+ foreach ($attributes AS $key => $value) {
188
+ if (is_array($value)) {
189
+ $pAttrib = Braintree_Util::attributesToString($value);
190
+ } else if ($value instanceof DateTime) {
191
+ $pAttrib = $value->format(DateTime::RFC850);
192
+ } else {
193
+ $pAttrib = $value;
194
+ }
195
+ $printableAttribs[$key] = sprintf('%s', $pAttrib);
196
+ }
197
+ return Braintree_Util::implodeAssociativeArray($printableAttribs);
198
+ }
199
+
200
+ /**
201
+ * verify user request structure
202
+ *
203
+ * compares the expected signature of a gateway request
204
+ * against the actual structure sent by the user
205
+ *
206
+ * @param array $signature
207
+ * @param array $attributes
208
+ */
209
+ public static function verifyKeys($signature, $attributes)
210
+ {
211
+ $validKeys = self::_flattenArray($signature);
212
+ $userKeys = self::_flattenUserKeys($attributes);
213
+ $invalidKeys = array_diff($userKeys, $validKeys);
214
+ $invalidKeys = self::_removeWildcardKeys($validKeys, $invalidKeys);
215
+
216
+ if(!empty($invalidKeys)) {
217
+ asort($invalidKeys);
218
+ $sortedList = join(', ', $invalidKeys);
219
+ throw new InvalidArgumentException('invalid keys: '. $sortedList);
220
+ }
221
+ }
222
+ /**
223
+ * flattens a numerically indexed nested array to a single level
224
+ * @param array $keys
225
+ * @param string $namespace
226
+ * @return array
227
+ */
228
+ private static function _flattenArray($keys, $namespace = null)
229
+ {
230
+ $flattenedArray = array();
231
+ foreach($keys AS $key) {
232
+ if(is_array($key)) {
233
+ $theKeys = array_keys($key);
234
+ $theValues = array_values($key);
235
+ $scope = $theKeys[0];
236
+ $fullKey = empty($namespace) ? $scope : $namespace . '[' . $scope . ']';
237
+ $flattenedArray = array_merge($flattenedArray, self::_flattenArray($theValues[0], $fullKey));
238
+ } else {
239
+ $fullKey = empty($namespace) ? $key : $namespace . '[' . $key . ']';
240
+ $flattenedArray[] = $fullKey;
241
+ }
242
+ }
243
+ sort($flattenedArray);
244
+ return $flattenedArray;
245
+ }
246
+
247
+ private static function _flattenUserKeys($keys, $namespace = null)
248
+ {
249
+ $flattenedArray = array();
250
+
251
+ foreach($keys AS $key => $value) {
252
+ $fullKey = empty($namespace) ? $key : $namespace;
253
+ if (!is_numeric($key) && $namespace != null) {
254
+ $fullKey .= '[' . $key . ']';
255
+ }
256
+ if (is_numeric($key) && is_string($value)) {
257
+ $fullKey .= '[' . $value . ']';
258
+ }
259
+ if(is_array($value)) {
260
+ $more = self::_flattenUserKeys($value, $fullKey);
261
+ $flattenedArray = array_merge($flattenedArray, $more);
262
+ } else {
263
+ $flattenedArray[] = $fullKey;
264
+ }
265
+ }
266
+ sort($flattenedArray);
267
+ return $flattenedArray;
268
+ }
269
+
270
+ /**
271
+ * removes wildcard entries from the invalid keys array
272
+ * @param array $validKeys
273
+ * @param <array $invalidKeys
274
+ * @return array
275
+ */
276
+ private static function _removeWildcardKeys($validKeys, $invalidKeys)
277
+ {
278
+ foreach($validKeys AS $key) {
279
+ if (stristr($key, '[_anyKey_]')) {
280
+ $wildcardKey = str_replace('[_anyKey_]', '', $key);
281
+ foreach ($invalidKeys AS $index => $invalidKey) {
282
+ if (stristr($invalidKey, $wildcardKey)) {
283
+ unset($invalidKeys[$index]);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ return $invalidKeys;
289
+ }
290
+ }
includes/lib/Braintree/Braintree/Version.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Library Version
4
+ *
5
+ * @copyright 2010 Braintree Payment Solutions
6
+ */
7
+
8
+ /**
9
+ * stores version information about the Braintree library
10
+ *
11
+ *
12
+ * @copyright 2010 Braintree Payment Solutions
13
+ */
14
+ final class Braintree_Version
15
+ {
16
+ /**
17
+ * class constants
18
+ */
19
+ const MAJOR = 2;
20
+ const MINOR = 19;
21
+ const TINY = 0;
22
+
23
+ /**
24
+ * @ignore
25
+ * @access protected
26
+ */
27
+ protected function __construct()
28
+ {
29
+ }
30
+
31
+ /**
32
+ *
33
+ * @return string the current library version
34
+ */
35
+ public static function get()
36
+ {
37
+ return self::MAJOR.'.'.self::MINOR.'.'.self::TINY;
38
+ }
39
+ }
includes/lib/Braintree/Braintree/WebhookNotification.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_WebhookNotification extends Braintree
3
+ {
4
+ const SUBSCRIPTION_CANCELED = 'subscription_canceled';
5
+ const SUBSCRIPTION_CHARGED_SUCCESSFULLY = 'subscription_charged_successfully';
6
+ const SUBSCRIPTION_CHARGED_UNSUCCESSFULLY = 'subscription_charged_unsuccessfully';
7
+ const SUBSCRIPTION_EXPIRED = 'subscription_expired';
8
+ const SUBSCRIPTION_TRIAL_ENDED = 'subscription_trial_ended';
9
+ const SUBSCRIPTION_WENT_ACTIVE = 'subscription_went_active';
10
+ const SUBSCRIPTION_WENT_PAST_DUE = 'subscription_went_past_due';
11
+
12
+ public static function parse($signature, $payload)
13
+ {
14
+ self::_validateSignature($signature, $payload);
15
+
16
+ $xml = base64_decode($payload);
17
+ $attributes = Braintree_Xml::buildArrayFromXml($xml);
18
+ return self::factory($attributes['notification']);
19
+ }
20
+
21
+ public static function verify($challenge)
22
+ {
23
+ $publicKey = Braintree_Configuration::publicKey();
24
+ $digest = Braintree_Digest::hexDigest($challenge);
25
+ return "{$publicKey}|{$digest}";
26
+ }
27
+
28
+ public static function factory($attributes)
29
+ {
30
+ $instance = new self();
31
+ $instance->_initialize($attributes);
32
+ return $instance;
33
+ }
34
+
35
+ private static function _matchingSignature($signaturePairs)
36
+ {
37
+ foreach ($signaturePairs as $pair)
38
+ {
39
+ $components = preg_split("/\|/", $pair);
40
+ if ($components[0] == Braintree_Configuration::publicKey()) {
41
+ return $components[1];
42
+ }
43
+ }
44
+
45
+ return null;
46
+ }
47
+
48
+ private static function _validateSignature($signature, $payload)
49
+ {
50
+ $signaturePairs = preg_split("/&/", $signature);
51
+ $matchingSignature = self::_matchingSignature($signaturePairs);
52
+
53
+ $payloadSignature = Braintree_Digest::hexDigest($payload);
54
+ if (!Braintree_Digest::secureCompare($matchingSignature, $payloadSignature)) {
55
+ throw new Braintree_Exception_InvalidSignature("webhook notification signature invalid");
56
+ }
57
+ }
58
+
59
+ protected function _initialize($attributes)
60
+ {
61
+ $this->_attributes = $attributes;
62
+ if (isset($attributes['subject']) && isset($attributes['subject']['subscription'])) {
63
+ $this->_set('subscription', Braintree_Subscription::factory($attributes['subject']['subscription']));
64
+ }
65
+ }
66
+ }
includes/lib/Braintree/Braintree/WebhookTesting.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Braintree_WebhookTesting
3
+ {
4
+ public static function sampleNotification($kind, $id)
5
+ {
6
+ $payload = base64_encode(self::_sampleXml($kind, $id));
7
+ $signature = Braintree_Configuration::publicKey() . "|" . Braintree_Digest::hexDigest($payload);
8
+
9
+ return array(
10
+ 'signature' => $signature,
11
+ 'payload' => $payload
12
+ );
13
+ }
14
+
15
+ private static function _sampleXml($kind, $id)
16
+ {
17
+ $subjectXml = self::_subscriptionSampleXml($id);
18
+ $timestamp = self::_timestamp();
19
+ return "
20
+ <notification>
21
+ <timestamp type=\"datetime\">{$timestamp}</timestamp>
22
+ <kind>{$kind}</kind>
23
+ <subject>{$subjectXml}</subject>
24
+ </notification>
25
+ ";
26
+ }
27
+
28
+ private static function _subscriptionSampleXml($id)
29
+ {
30
+ return "
31
+ <subscription>
32
+ <id>{$id}</id>
33
+ <transactions type=\"array\">
34
+ </transactions>
35
+ <add_ons type=\"array\">
36
+ </add_ons>
37
+ <discounts type=\"array\">
38
+ </discounts>
39
+ </subscription>
40
+ ";
41
+ }
42
+
43
+ private static function _timestamp()
44
+ {
45
+ $originalZone = date_default_timezone_get();
46
+ date_default_timezone_set('UTC');
47
+ $timestamp = strftime('%Y-%m-%dT%TZ');
48
+ date_default_timezone_set($originalZone);
49
+
50
+ return $timestamp;
51
+ }
52
+ }
includes/lib/Braintree/Braintree/Xml.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Braintree Xml parser and generator
4
+ * PHP version 5
5
+ *
6
+ * @copyright 2010 Braintree Payment Solutions
7
+ */
8
+
9
+ /**
10
+ * superclass for Braintree XML parsing and generation
11
+ *
12
+ * @copyright 2010 Braintree Payment Solutions
13
+ */
14
+ final class Braintree_Xml
15
+ {
16
+ /**
17
+ * @ignore
18
+ */
19
+ protected function __construct()
20
+ {
21
+
22
+ }
23
+
24
+ /**
25
+ *
26
+ * @param string $xml
27
+ * @return array
28
+ */
29
+ public static function buildArrayFromXml($xml)
30
+ {
31
+ return Braintree_Xml_Parser::arrayFromXml($xml);
32
+ }
33
+
34
+ /**
35
+ *
36
+ * @param array $array
37
+ * @return string
38
+ */
39
+ public static function buildXmlFromArray($array)
40
+ {
41
+ return Braintree_Xml_Generator::arrayToXml($array);
42
+ }
43
+ }
includes/lib/Braintree/Braintree/Xml/Generator.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PHP version 5
4
+ *
5
+ * @copyright 2010 Braintree Payment Solutions
6
+ */
7
+
8
+ /**
9
+ * Generates XML output from arrays using PHP's
10
+ * built-in XMLWriter
11
+ *
12
+ * @copyright 2010 Braintree Payment Solutions
13
+ */
14
+ class Braintree_Xml_Generator
15
+ {
16
+ /**
17
+ * arrays passed to this method should have a single root element
18
+ * with an array as its value
19
+ * @param array $aData the array of data
20
+ * @return var XML string
21
+ */
22
+ public static function arrayToXml($aData)
23
+ {
24
+ // set up the XMLWriter
25
+ $writer = new XMLWriter();
26
+ $writer->openMemory();
27
+
28
+ $writer->setIndent(true);
29
+ $writer->setIndentString(' ');
30
+ $writer->startDocument('1.0', 'UTF-8');
31
+
32
+ // get the root element name
33
+ $aKeys = array_keys($aData);
34
+ $rootElementName = $aKeys[0];
35
+ // open the root element
36
+ $writer->startElement(Braintree_Util::camelCaseToDelimiter($rootElementName));
37
+ // create the body
38
+ self::_createElementsFromArray($writer, $aData[$rootElementName], $rootElementName);
39
+
40
+ // close the root element and document
41
+ $writer->endElement();
42
+ $writer->endDocument();
43
+
44
+ // send the output as string
45
+ return $writer->outputMemory();
46
+ }
47
+
48
+ /**
49
+ * Construct XML elements with attributes from an associative array.
50
+ *
51
+ * @access protected
52
+ * @static
53
+ * @param object $writer XMLWriter object
54
+ * @param array $aData contains attributes and values
55
+ * @return none
56
+ */
57
+ private static function _createElementsFromArray(&$writer, $aData)
58
+ {
59
+ if (!is_array($aData)) {
60
+ if (is_bool($aData)) {
61
+ $writer->text($aData ? 'true' : 'false');
62
+ } else {
63
+ $writer->text($aData);
64
+ }
65
+ return;
66
+ }
67
+ foreach ($aData AS $index => $element) {
68
+ // convert the style back to gateway format
69
+ $elementName = Braintree_Util::camelCaseToDelimiter($index, '-');
70
+ // handle child elements
71
+ $writer->startElement($elementName);
72
+ if (is_array($element)) {
73
+ if (array_key_exists(0, $element) || empty($element)) {
74
+ $writer->writeAttribute('type', 'array');
75
+ foreach ($element AS $ignored => $itemInArray) {
76
+ $writer->startElement('item');
77
+ self::_createElementsFromArray($writer, $itemInArray);
78
+ $writer->endElement();
79
+ }
80
+ }
81
+ else {
82
+ self::_createElementsFromArray($writer, $element);
83
+ }
84
+ } else {
85
+ // generate attributes as needed
86
+ $attribute = self::_generateXmlAttribute($element);
87
+ if (is_array($attribute)) {
88
+ $writer->writeAttribute($attribute[0], $attribute[1]);
89
+ $element = $attribute[2];
90
+ }
91
+ $writer->text($element);
92
+ }
93
+ $writer->endElement();
94
+ }
95
+ }
96
+
97
+ /**
98
+ * convert passed data into an array of attributeType, attributeName, and value
99
+ * dates sent as DateTime objects will be converted to strings
100
+ * @access protected
101
+ * @param mixed $value
102
+ * @return array attributes and element value
103
+ */
104
+ private static function _generateXmlAttribute($value)
105
+ {
106
+ if ($value instanceof DateTime) {
107
+ return array('type', 'datetime', self::_dateTimeToXmlTimestamp($value));
108
+ }
109
+ if (is_int($value)) {
110
+ return array('type', 'integer', $value);
111
+ }
112
+ if (is_bool($value)) {
113
+ return array('type', 'boolean', ($value ? 'true' : 'false'));
114
+ }
115
+ if ($value === NULL) {
116
+ return array('nil', 'true', $value);
117
+ }
118
+ }
119
+ /**
120
+ * converts datetime back to xml schema format
121
+ * @access protected
122
+ * @param object $dateTime
123
+ * @return var XML schema formatted timestamp
124
+ */
125
+ private static function _dateTimeToXmlTimestamp($dateTime)
126
+ {
127
+ $dateTime->setTimeZone(new DateTimeZone('UTC'));
128
+ return ($dateTime->format('Y-m-d\TH:i:s') . 'Z');
129
+ }
130
+
131
+ private static function _castDateTime($string)
132
+ {
133
+ try {
134
+ if (empty($string)) {
135
+ return false;
136
+ }
137
+ $dateTime = new DateTime($string);
138
+ return self::_dateTimeToXmlTimestamp($dateTime);
139
+ } catch (Exception $e) {
140
+ // not a datetime
141
+ return false;
142
+ }
143
+ }
144
+ }
includes/lib/Braintree/Braintree/Xml/Parser.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Braintree XML Parser
5
+ *
6
+ * @copyright 2010 Braintree Payment Solutions
7
+ */
8
+ /**
9
+ * Parses incoming Xml into arrays using PHP's
10
+ * built-in SimpleXML, and its extension via
11
+ * Iterator, SimpleXMLIterator
12
+ *
13
+ * @copyright 2010 Braintree Payment Solutions
14
+ */
15
+ class Braintree_Xml_Parser
16
+ {
17
+
18
+ private static $_xmlRoot;
19
+ private static $_responseType;
20
+
21
+ /**
22
+ * sets up the SimpleXMLIterator and starts the parsing
23
+ * @access public
24
+ * @param string $xml
25
+ * @return array array mapped to the passed xml
26
+ */
27
+ public static function arrayFromXml($xml)
28
+ {
29
+ // SimpleXML provides the root information on construct
30
+ $iterator = new SimpleXMLIterator($xml);
31
+ $xmlRoot = Braintree_Util::delimiterToCamelCase($iterator->getName());
32
+ $type = $iterator->attributes()->type;
33
+
34
+ self::$_xmlRoot = $iterator->getName();
35
+ self::$_responseType = $type;
36
+
37
+ // return the mapped array with the root element as the header
38
+ return array($xmlRoot => self::_iteratorToArray($iterator));
39
+
40
+ }
41
+
42
+ /**
43
+ * processes SimpleXMLIterator objects recursively
44
+ *
45
+ * @access protected
46
+ * @param object $iterator
47
+ * @return array xml converted to array
48
+ */
49
+ private static function _iteratorToArray($iterator)
50
+ {
51
+ $xmlArray = array();
52
+ $value = null;
53
+
54
+ // rewind the iterator and check if the position is valid
55
+ // if not, return the string it contains
56
+ $iterator->rewind();
57
+ if (!$iterator->valid()) {
58
+ return self::_typecastXmlValue($iterator);
59
+ }
60
+ for ($iterator->rewind(); $iterator->valid(); $iterator->next()) {
61
+
62
+ $tmpArray = null;
63
+ $value = null;
64
+
65
+ // get the attribute type string for use in conditions below
66
+ $attributeType = $iterator->attributes()->type;
67
+
68
+ // extract the parent element via xpath query
69
+ $parentElement = $iterator->xpath($iterator->key() . '/..');
70
+ if ($parentElement[0] instanceof SimpleXMLIterator) {
71
+ $parentElement = $parentElement[0];
72
+ $parentKey = Braintree_Util::delimiterToCamelCase($parentElement->getName());
73
+ } else {
74
+ $parentElement = null;
75
+ }
76
+
77
+
78
+ if ($parentKey == "customFields") {
79
+ $key = Braintree_Util::delimiterToUnderscore($iterator->key());
80
+ } else {
81
+ $key = Braintree_Util::delimiterToCamelCase($iterator->key());
82
+ }
83
+
84
+ // process children recursively
85
+ if ($iterator->hasChildren()) {
86
+ // return the child elements
87
+ $value = self::_iteratorToArray($iterator->current());
88
+
89
+ // if the element is an array type,
90
+ // use numeric keys to allow multiple values
91
+ if ($attributeType != 'array') {
92
+ $tmpArray[$key] = $value;
93
+ }
94
+ } else {
95
+ // cast values according to attributes
96
+ $tmpArray[$key] = self::_typecastXmlValue($iterator->current());
97
+ }
98
+
99
+ // set the output string
100
+ $output = isset($value) ? $value : $tmpArray[$key];
101
+
102
+ // determine if there are multiple tags of this name at the same level
103
+ if (isset($parentElement) &&
104
+ ($parentElement->attributes()->type == 'collection') &&
105
+ $iterator->hasChildren()) {
106
+ $xmlArray[$key][] = $output;
107
+ continue;
108
+ }
109
+
110
+ // if the element was an array type, output to a numbered key
111
+ // otherwise, use the element name
112
+ if ($attributeType == 'array') {
113
+ $xmlArray[] = $output;
114
+ } else {
115
+ $xmlArray[$key] = $output;
116
+ }
117
+ }
118
+
119
+ return $xmlArray;
120
+ }
121
+
122
+ /**
123
+ * typecast xml value based on attributes
124
+ * @param object $valueObj SimpleXMLElement
125
+ * @return mixed value for placing into array
126
+ */
127
+ private static function _typecastXmlValue($valueObj)
128
+ {
129
+ // get the element attributes
130
+ $attribs = $valueObj->attributes();
131
+ // the element is null, so jump out here
132
+ if (isset($attribs->nil) && $attribs->nil) {
133
+ return null;
134
+ }
135
+ // switch on the type attribute
136
+ // switch works even if $attribs->type isn't set
137
+ switch ($attribs->type) {
138
+ case 'datetime':
139
+ return self::_timestampToUTC((string) $valueObj);
140
+ break;
141
+ case 'date':
142
+ return new DateTime((string)$valueObj);
143
+ break;
144
+ case 'integer':
145
+ return (int) $valueObj;
146
+ break;
147
+ case 'boolean':
148
+ $value = (string) $valueObj;
149
+ // look for a number inside the string
150
+ if(is_numeric($value)) {
151
+ return (bool) $value;
152
+ } else {
153
+ // look for the string "true", return false in all other cases
154
+ return ($value != "true") ? FALSE : TRUE;
155
+ }
156
+ break;
157
+ case 'array':
158
+ return array();
159
+ default:
160
+ return (string) $valueObj;
161
+ }
162
+
163
+ }
164
+
165
+ /**
166
+ * convert xml timestamps into DateTime
167
+ * @param string $timestamp
168
+ * @return string UTC formatted datetime string
169
+ */
170
+ private static function _timestampToUTC($timestamp)
171
+ {
172
+ $tz = new DateTimeZone('UTC');
173
+ // strangely DateTime requires an explicit set below
174
+ // to show the proper time zone
175
+ $dateTime = new DateTime($timestamp, $tz);
176
+ $dateTime->setTimezone($tz);
177
+ return $dateTime;
178
+ }
179
+ }
includes/lib/Braintree/ssl/sandbox_braintreegateway_com.ca.crt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Subject: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com
2
+ -----BEGIN CERTIFICATE-----
3
+ MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
4
+ IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
5
+ BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
6
+ aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
7
+ 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
8
+ NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
9
+ azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
10
+ YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
11
+ Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
12
+ cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
13
+ dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
14
+ WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
15
+ v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
16
+ UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
17
+ IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
18
+ W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
19
+ -----END CERTIFICATE-----
includes/lib/Braintree/ssl/www_braintreegateway_com.ca.crt ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Subject: O=Entrust.net, OU=www.entrust.net/GCCA_CPS incorp. by ref. (limits liab.), OU=(c) 2000 Entrust.net Limited, CN=Entrust.net Client Certification Authority
2
+ -----BEGIN CERTIFICATE-----
3
+ MIIEgzCCA+ygAwIBAgIEOJ725DANBgkqhkiG9w0BAQQFADCBtDEUMBIGA1UE
4
+ ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9HQ0NB
5
+ X0NQUyBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
6
+ HChjKSAyMDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
7
+ c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAy
8
+ MDcxNjE2NDBaFw0yMDAyMDcxNjQ2NDBaMIG0MRQwEgYDVQQKEwtFbnRydXN0
9
+ Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0dDQ0FfQ1BTIGluY29y
10
+ cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDIwMDAg
11
+ RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xp
12
+ ZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
13
+ A4GNADCBiQKBgQCTdLS25MVL1qFof2LV7PdRV7NySpj10InJrWPNTTVRaoTU
14
+ rcloeW+46xHbh65cJFET8VQlhK8pK5/jgOLZy93GRUk0iJBeAZfv6lOm3fzB
15
+ 3ksqJeTpNfpVBQbliXrqpBFXO/x8PTbNZzVtpKklWb1m9fkn5JVn1j+SgF7y
16
+ NH0rhQIDAQABo4IBnjCCAZowEQYJYIZIAYb4QgEBBAQDAgAHMIHdBgNVHR8E
17
+ gdUwgdIwgc+ggcyggcmkgcYwgcMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUAw
18
+ PgYDVQQLFDd3d3cuZW50cnVzdC5uZXQvR0NDQV9DUFMgaW5jb3JwLiBieSBy
19
+ ZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0
20
+ Lm5ldCBMaW1pdGVkMTMwMQYDVQQDEypFbnRydXN0Lm5ldCBDbGllbnQgQ2Vy
21
+ dGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
22
+ IoAPMjAwMDAyMDcxNjE2NDBagQ8yMDIwMDIwNzE2NDY0MFowCwYDVR0PBAQD
23
+ AgEGMB8GA1UdIwQYMBaAFISLdP3FjcD/J20gN0V8/i3OutN9MB0GA1UdDgQW
24
+ BBSEi3T9xY3A/ydtIDdFfP4tzrrTfTAMBgNVHRMEBTADAQH/MB0GCSqGSIb2
25
+ fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQQFAAOBgQBObzWA
26
+ O9GK9Q6nIMstZVXQkvTnhLUGJoMShAusO7JE7r3PQNsgDrpuFOow4DtifH+L
27
+ a3xKp9U1PL6oXOpLu5OOgGarDyn9TS2/GpsKkMWr2tGzhtQvJFJcem3G8v7l
28
+ TRowjJDyutdKPkN+1MhQGof4T4HHdguEOnKdzmVml64mXg==
29
+ -----END CERTIFICATE-----
30
+
31
+ Subject: O=Entrust.net, OU=www.entrust.net/SSL_CPS incorp. by ref. (limits liab.), OU=(c) 2000 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
32
+ -----BEGIN CERTIFICATE-----
33
+ MIIElTCCA/6gAwIBAgIEOJsRPDANBgkqhkiG9w0BAQQFADCBujEUMBIGA1UE
34
+ ChMLRW50cnVzdC5uZXQxPzA9BgNVBAsUNnd3dy5lbnRydXN0Lm5ldC9TU0xf
35
+ Q1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
36
+ KGMpIDIwMDAgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVz
37
+ dC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
38
+ Fw0wMDAyMDQxNzIwMDBaFw0yMDAyMDQxNzUwMDBaMIG6MRQwEgYDVQQKEwtF
39
+ bnRydXN0Lm5ldDE/MD0GA1UECxQ2d3d3LmVudHJ1c3QubmV0L1NTTF9DUFMg
40
+ aW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykg
41
+ MjAwMCBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5l
42
+ dCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0G
43
+ CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwV9OcfHO8GCGD9JYf9Mzly0XonUw
44
+ tZZkJi9ow0SrqHXmAGc0V55lxyKbc+bT3QgON1WqJUaBbL3+qPZ1V1eMkGxK
45
+ wz6LS0MKyRFWmponIpnPVZ5h2QLifLZ8OAfc439PmrkDQYC2dWcTC5/oVzbI
46
+ XQA23mYU2m52H083jIITiQIDAQABo4IBpDCCAaAwEQYJYIZIAYb4QgEBBAQD
47
+ AgAHMIHjBgNVHR8EgdswgdgwgdWggdKggc+kgcwwgckxFDASBgNVBAoTC0Vu
48
+ dHJ1c3QubmV0MT8wPQYDVQQLFDZ3d3cuZW50cnVzdC5uZXQvU1NMX0NQUyBp
49
+ bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAy
50
+ MDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3QubmV0
51
+ IFNlY3VyZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNV
52
+ BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMDAyMDQxNzIwMDBagQ8yMDIwMDIw
53
+ NDE3NTAwMFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFMtswGvjuz7L/CKc
54
+ /vuLkpyw8m4iMB0GA1UdDgQWBBTLbMBr47s+y/winP77i5KcsPJuIjAMBgNV
55
+ HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkq
56
+ hkiG9w0BAQQFAAOBgQBi24GRzsiad0Iv7L0no1MPUBvqTpLwqa+poLpIYcvv
57
+ yQbvH9X07t9WLebKahlzqlO+krNQAraFJnJj2HVQYnUUt7NQGj/KEQALhUVp
58
+ bbalrlHhStyCP2yMNLJ3a9kC9n8O6mUE8c1UyrrJzOCE98g+EZfTYAkYvAX/
59
+ bIkz8OwVDw==
60
+ -----END CERTIFICATE-----
61
+
62
+ Subject: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048)
63
+ -----BEGIN CERTIFICATE-----
64
+ MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UE
65
+ ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNf
66
+ MjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
67
+ HChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
68
+ c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEy
69
+ MjQxNzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0
70
+ Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29y
71
+ cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkg
72
+ RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2Vy
73
+ dGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEF
74
+ AAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4
75
+ QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC
76
+ DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj
77
+ /L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLP
78
+ KQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZd
79
+ enoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
80
+ 4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB
81
+ 0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJ
82
+ FrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
83
+ AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFh
84
+ fGPjK50xA3B20qMooPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVU
85
+ KcgF7bISKo30Axv/55IQh7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaoho
86
+ wXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2
87
+ +z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof888
88
+ 6ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
89
+ -----END CERTIFICATE-----
90
+
91
+ Subject: C=US, O=Entrust.net, OU=www.entrust.net/Client_CA_Info/CPS incorp. by ref. limits liab., OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Client Certification Authority
92
+ -----BEGIN CERTIFICATE-----
93
+ MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE
94
+ BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50
95
+ cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs
96
+ aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp
97
+ bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0
98
+ aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa
99
+ MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV
100
+ BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw
101
+ LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50
102
+ cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50
103
+ IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL
104
+ ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv
105
+ x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV
106
+ iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173
107
+ iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw
108
+ ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50
109
+ cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff
110
+ SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE
111
+ CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50
112
+ cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD
113
+ VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D
114
+ bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx
115
+ MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW
116
+ /O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG
117
+ A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
118
+ hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ
119
+ OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU
120
+ ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE
121
+ PHayXOw=
122
+ -----END CERTIFICATE-----
123
+
124
+ Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
125
+ -----BEGIN CERTIFICATE-----
126
+ MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UE
127
+ BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50
128
+ cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
129
+ MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UE
130
+ AxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1
131
+ dGhvcml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQsw
132
+ CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3
133
+ dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlh
134
+ Yi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTow
135
+ OAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
136
+ b24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0
137
+ VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHIN
138
+ iC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3wkrYKZImZNHk
139
+ mGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcwggHT
140
+ MBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHY
141
+ pIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
142
+ BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChs
143
+ aW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBM
144
+ aW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
145
+ cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNo
146
+ dHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAi
147
+ gA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMC
148
+ AQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYE
149
+ FPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9
150
+ B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKn
151
+ CqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2Zcgx
152
+ xufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd2cNgQ4xYDiKWL2KjLB+6
153
+ rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
154
+ -----END CERTIFICATE-----
155
+
156
+ Subject: C=US, O=SecureTrust Corporation, CN=SecureTrust CA
157
+ -----BEGIN CERTIFICATE-----
158
+ MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
159
+ MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
160
+ FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
161
+ MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
162
+ cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
163
+ AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
164
+ Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
165
+ 0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
166
+ wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
167
+ 7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
168
+ 8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
169
+ BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
170
+ /zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
171
+ JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
172
+ NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
173
+ 6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
174
+ 3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
175
+ D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
176
+ CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
177
+ 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
178
+ -----END CERTIFICATE-----
179
+
180
+ Subject: C=US, O=SecureTrust Corporation, CN=Secure Global CA
181
+ -----BEGIN CERTIFICATE-----
182
+ MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
183
+ MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
184
+ GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
185
+ MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
186
+ Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
187
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
188
+ iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
189
+ /FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
190
+ jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
191
+ HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
192
+ sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
193
+ gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
194
+ MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
195
+ KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
196
+ AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
197
+ URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
198
+ H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
199
+ I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
200
+ iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
201
+ f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
202
+ -----END CERTIFICATE-----
includes/lib/CyberSource/cyber_source_soap_client.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * CyberSource version of SoapClient.
4
+ *
5
+ * An extension of PHP's own SoapClient library that includes the security
6
+ * header required by CyberSource. To enable SOAP support, PHP must be
7
+ * configured with --enable-soap.
8
+ *
9
+ * @author joe bartlett (xo@jdbartlett.com)
10
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
11
+ * @package CyberSource
12
+ * @subpackage CyberSource.libs
13
+ * @github https://github.com/jdbartlett/CyberSource/blob/master/libs/cyber_source_soap_client.php
14
+ */
15
+ class CyberSourceSoapClient extends SoapClient {
16
+
17
+ /**
18
+ * Instance-specific array to contain CyberSource-specific configuration
19
+ * settings.
20
+ *
21
+ * @var array
22
+ * @access protected
23
+ */
24
+ protected $_cyberSourceOptions = null;
25
+
26
+ /**
27
+ * Constructor.
28
+ *
29
+ * The WSDL is generated by CyberSourceSource::_buildWsdl, in accordance with
30
+ * the DataSource settings.
31
+ *
32
+ * The CyberSource Options array should contain a 'merchantID' key and a
33
+ * 'transactionKey' key, with appropriate string values.
34
+ *
35
+ * The SOAP options array (the standard options array for PHP's SoapClient
36
+ * class) can be empty for CyberSource transactions.
37
+ *
38
+ * @param mixed $wsdl
39
+ * @param array $cyberSourceOptions
40
+ * @param array $soapOptions
41
+ * @access private
42
+ */
43
+ public function __construct($wsdl, $cyberSourceOptions = null, $soapOptions = array()) {
44
+ parent::__construct($wsdl, $soapOptions);
45
+
46
+ $this->_cyberSourceOptions = array_merge(array(
47
+ 'merchantID' => '',
48
+ 'transactionKey' => '',
49
+ ), is_array($cyberSourceOptions) ? $cyberSourceOptions : array());
50
+ }
51
+
52
+ /**
53
+ * Transport layer for SOAP request.
54
+ *
55
+ * This is a straightforward wrapper for the standard SoapClient::__doRequest
56
+ * method; its parameters are identical.
57
+ *
58
+ * @param string $request
59
+ * @param string $location
60
+ * @param string $action
61
+ * @param string $version
62
+ * @param integer $one_way
63
+ * @return string XML SOAP response
64
+ * @access private
65
+ */
66
+ public function __doRequest($request, $location, $action, $version, $one_way = 0) {
67
+ $soapHeader = sprintf("<SOAP-ENV:Header xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"><wsse:Security SOAP-ENV:mustUnderstand=\"1\"><wsse:UsernameToken><wsse:Username>%s</wsse:Username><wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">%s</wsse:Password></wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>", $this->_cyberSourceOptions['merchantID'], $this->_cyberSourceOptions['transactionKey']);
68
+
69
+ $requestDOM = new DOMDocument('1.0');
70
+ $soapHeaderDOM = new DOMDocument('1.0');
71
+
72
+ try {
73
+ $requestDOM->loadXML($request);
74
+ $soapHeaderDOM->loadXML($soapHeader);
75
+ $node = $requestDOM->importNode($soapHeaderDOM->firstChild, true);
76
+ $requestDOM->firstChild->insertBefore(
77
+ $node, $requestDOM->firstChild->firstChild);
78
+
79
+ $request = $requestDOM->saveXML();
80
+ } catch (DOMException $e) {
81
+ trigger_error("CybserSource Error: Couldn't add token: " . $e->code, E_USER_WARNING);
82
+ }
83
+
84
+ return parent::__doRequest($request, $location, $action, $version);
85
+ }
86
+
87
+ }
includes/lib/Stripe/Stripe.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Tested on PHP 5.2, 5.3
4
+
5
+ // This snippet (and some of the curl code) due to the Facebook SDK.
6
+ if (!function_exists('curl_init')) {
7
+ throw new Exception('Stripe needs the CURL PHP extension.');
8
+ }
9
+ if (!function_exists('json_decode')) {
10
+ throw new Exception('Stripe needs the JSON PHP extension.');
11
+ }
12
+
13
+ // Stripe singleton
14
+ require(dirname(__FILE__) . '/Stripe/Stripe.php');
15
+
16
+ // Utilities
17
+ require(dirname(__FILE__) . '/Stripe/Util.php');
18
+ require(dirname(__FILE__) . '/Stripe/Util/Set.php');
19
+
20
+ // Errors
21
+ require(dirname(__FILE__) . '/Stripe/Error.php');
22
+ require(dirname(__FILE__) . '/Stripe/ApiError.php');
23
+ require(dirname(__FILE__) . '/Stripe/ApiConnectionError.php');
24
+ require(dirname(__FILE__) . '/Stripe/AuthenticationError.php');
25
+ require(dirname(__FILE__) . '/Stripe/CardError.php');
26
+ require(dirname(__FILE__) . '/Stripe/InvalidRequestError.php');
27
+
28
+ // Plumbing
29
+ require(dirname(__FILE__) . '/Stripe/Object.php');
30
+ require(dirname(__FILE__) . '/Stripe/ApiRequestor.php');
31
+ require(dirname(__FILE__) . '/Stripe/ApiResource.php');
32
+ require(dirname(__FILE__) . '/Stripe/SingletonApiResource.php');
33
+ require(dirname(__FILE__) . '/Stripe/List.php');
34
+
35
+ // Stripe API Resources
36
+ require(dirname(__FILE__) . '/Stripe/Account.php');
37
+ require(dirname(__FILE__) . '/Stripe/Charge.php');
38
+ require(dirname(__FILE__) . '/Stripe/Customer.php');
39
+ require(dirname(__FILE__) . '/Stripe/Invoice.php');
40
+ require(dirname(__FILE__) . '/Stripe/InvoiceItem.php');
41
+ require(dirname(__FILE__) . '/Stripe/Plan.php');
42
+ require(dirname(__FILE__) . '/Stripe/Token.php');
43
+ require(dirname(__FILE__) . '/Stripe/Coupon.php');
44
+ require(dirname(__FILE__) . '/Stripe/Event.php');
45
+ require(dirname(__FILE__) . '/Stripe/Transfer.php');
includes/lib/Stripe/Stripe/Account.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Account extends Stripe_SingletonApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedSingletonRetrieve($class, $apiKey);
15
+ }
16
+ }
includes/lib/Stripe/Stripe/ApiConnectionError.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_ApiConnectionError extends Stripe_Error
4
+ {
5
+ }
includes/lib/Stripe/Stripe/ApiError.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_ApiError extends Stripe_Error
4
+ {
5
+ }
includes/lib/Stripe/Stripe/ApiRequestor.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_ApiRequestor
4
+ {
5
+ public $apiKey;
6
+
7
+ public function __construct($apiKey=null)
8
+ {
9
+ $this->_apiKey = $apiKey;
10
+ }
11
+
12
+ public static function apiUrl($url='')
13
+ {
14
+ $apiBase = Stripe::$apiBase;
15
+ return "$apiBase$url";
16
+ }
17
+
18
+ public static function utf8($value)
19
+ {
20
+ if (is_string($value))
21
+ return utf8_encode($value);
22
+ else
23
+ return $value;
24
+ }
25
+
26
+ private static function _encodeObjects($d)
27
+ {
28
+ if ($d instanceof Stripe_ApiResource) {
29
+ return $d->id;
30
+ } else if ($d === true) {
31
+ return 'true';
32
+ } else if ($d === false) {
33
+ return 'false';
34
+ } else if (is_array($d)) {
35
+ $res = array();
36
+ foreach ($d as $k => $v)
37
+ $res[$k] = self::_encodeObjects($v);
38
+ return $res;
39
+ } else {
40
+ return $d;
41
+ }
42
+ }
43
+
44
+ public static function encode($d)
45
+ {
46
+ return http_build_query($d, null, '&');
47
+ }
48
+
49
+ public function request($meth, $url, $params=null)
50
+ {
51
+ if (!$params)
52
+ $params = array();
53
+ list($rbody, $rcode, $myApiKey) = $this->_requestRaw($meth, $url, $params);
54
+ $resp = $this->_interpretResponse($rbody, $rcode);
55
+ return array($resp, $myApiKey);
56
+ }
57
+
58
+ public function handleApiError($rbody, $rcode, $resp)
59
+ {
60
+ if (!is_array($resp) || !isset($resp['error']))
61
+ throw new Stripe_ApiError("Invalid response object from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody, $resp);
62
+ $error = $resp['error'];
63
+ switch ($rcode) {
64
+ case 400:
65
+ case 404:
66
+ throw new Stripe_InvalidRequestError(isset($error['message']) ? $error['message'] : null,
67
+ isset($error['param']) ? $error['param'] : null,
68
+ $rcode, $rbody, $resp);
69
+ case 401:
70
+ throw new Stripe_AuthenticationError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
71
+ case 402:
72
+ throw new Stripe_CardError(isset($error['message']) ? $error['message'] : null,
73
+ isset($error['param']) ? $error['param'] : null,
74
+ isset($error['code']) ? $error['code'] : null,
75
+ $rcode, $rbody, $resp);
76
+ default:
77
+ throw new Stripe_ApiError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
78
+ }
79
+ }
80
+
81
+ private function _requestRaw($meth, $url, $params)
82
+ {
83
+ $myApiKey = $this->_apiKey;
84
+ if (!$myApiKey)
85
+ $myApiKey = Stripe::$apiKey;
86
+ if (!$myApiKey)
87
+ throw new Stripe_AuthenticationError('No API key provided. (HINT: set your API key using "Stripe::setApiKey(<API-KEY>)". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.');
88
+
89
+ $absUrl = $this->apiUrl($url);
90
+ $params = self::_encodeObjects($params);
91
+ $langVersion = phpversion();
92
+ $uname = php_uname();
93
+ $ua = array('bindings_version' => Stripe::VERSION,
94
+ 'lang' => 'php',
95
+ 'lang_version' => $langVersion,
96
+ 'publisher' => 'stripe',
97
+ 'uname' => $uname);
98
+ $headers = array('X-Stripe-Client-User-Agent: ' . json_encode($ua),
99
+ 'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION,
100
+ 'Authorization: Bearer ' . $myApiKey);
101
+ list($rbody, $rcode) = $this->_curlRequest($meth, $absUrl, $headers, $params);
102
+ return array($rbody, $rcode, $myApiKey);
103
+ }
104
+
105
+ private function _interpretResponse($rbody, $rcode)
106
+ {
107
+ try {
108
+ $resp = json_decode($rbody, true);
109
+ } catch (Exception $e) {
110
+ throw new Stripe_ApiError("Invalid response body from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody);
111
+ }
112
+
113
+ if ($rcode < 200 || $rcode >= 300) {
114
+ $this->handleApiError($rbody, $rcode, $resp);
115
+ }
116
+ return $resp;
117
+ }
118
+
119
+ private function _curlRequest($meth, $absUrl, $headers, $params)
120
+ {
121
+ $curl = curl_init();
122
+ $meth = strtolower($meth);
123
+ $opts = array();
124
+ if ($meth == 'get') {
125
+ $opts[CURLOPT_HTTPGET] = 1;
126
+ if (count($params) > 0) {
127
+ $encoded = self::encode($params);
128
+ $absUrl = "$absUrl?$encoded";
129
+ }
130
+ } else if ($meth == 'post') {
131
+ $opts[CURLOPT_POST] = 1;
132
+ $opts[CURLOPT_POSTFIELDS] = self::encode($params);
133
+ } else if ($meth == 'delete') {
134
+ $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
135
+ if (count($params) > 0) {
136
+ $encoded = self::encode($params);
137
+ $absUrl = "$absUrl?$encoded";
138
+ }
139
+ } else {
140
+ throw new Stripe_ApiError("Unrecognized method $meth");
141
+ }
142
+
143
+ $absUrl = self::utf8($absUrl);
144
+ $opts[CURLOPT_URL] = $absUrl;
145
+ $opts[CURLOPT_RETURNTRANSFER] = true;
146
+ $opts[CURLOPT_CONNECTTIMEOUT] = 30;
147
+ $opts[CURLOPT_TIMEOUT] = 80;
148
+ $opts[CURLOPT_RETURNTRANSFER] = true;
149
+ $opts[CURLOPT_HTTPHEADER] = $headers;
150
+ if (!Stripe::$verifySslCerts)
151
+ $opts[CURLOPT_SSL_VERIFYPEER] = false;
152
+
153
+ curl_setopt_array($curl, $opts);
154
+ $rbody = curl_exec($curl);
155
+
156
+ $errno = curl_errno($curl);
157
+ if ($errno == CURLE_SSL_CACERT ||
158
+ $errno == CURLE_SSL_PEER_CERTIFICATE ||
159
+ $errno == 77 // CURLE_SSL_CACERT_BADFILE (constant not defined in PHP though)
160
+ ) {
161
+ array_push($headers, 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}');
162
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
163
+ curl_setopt($curl, CURLOPT_CAINFO,
164
+ dirname(__FILE__) . '/../data/ca-certificates.crt');
165
+ $rbody = curl_exec($curl);
166
+ }
167
+
168
+ if ($rbody === false) {
169
+ $errno = curl_errno($curl);
170
+ $message = curl_error($curl);
171
+ curl_close($curl);
172
+ $this->handleCurlError($errno, $message);
173
+ }
174
+
175
+ $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
176
+ curl_close($curl);
177
+ return array($rbody, $rcode);
178
+ }
179
+
180
+ public function handleCurlError($errno, $message)
181
+ {
182
+ $apiBase = Stripe::$apiBase;
183
+ switch ($errno) {
184
+ case CURLE_COULDNT_CONNECT:
185
+ case CURLE_COULDNT_RESOLVE_HOST:
186
+ case CURLE_OPERATION_TIMEOUTED:
187
+ $msg = "Could not connect to Stripe ($apiBase). Please check your internet connection and try again. If this problem persists, you should check Stripe's service status at https://twitter.com/stripestatus, or let us know at support@stripe.com.";
188
+ break;
189
+ case CURLE_SSL_CACERT:
190
+ case CURLE_SSL_PEER_CERTIFICATE:
191
+ $msg = "Could not verify Stripe's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to $apiBase in your browser.) If this problem persists, let us know at support@stripe.com.";
192
+ break;
193
+ default:
194
+ $msg = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com.";
195
+ }
196
+
197
+ $msg .= "\n\n(Network error [errno $errno]: $message)";
198
+ throw new Stripe_ApiConnectionError($msg);
199
+ }
200
+ }
includes/lib/Stripe/Stripe/ApiResource.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Stripe_ApiResource extends Stripe_Object
4
+ {
5
+ protected static function _scopedRetrieve($class, $id, $apiKey=null)
6
+ {
7
+ $instance = new $class($id, $apiKey);
8
+ $instance->refresh();
9
+ return $instance;
10
+ }
11
+
12
+ public function refresh()
13
+ {
14
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
15
+ $url = $this->instanceUrl();
16
+
17
+ list($response, $apiKey) = $requestor->request('get', $url);
18
+ $this->refreshFrom($response, $apiKey);
19
+ return $this;
20
+ }
21
+
22
+ public static function className($class)
23
+ {
24
+ // Useful for namespaces: Foo\Stripe_Charge
25
+ if ($postfix = strrchr($class, '\\'))
26
+ $class = substr($postfix, 1);
27
+ if (substr($class, 0, strlen('Stripe')) == 'Stripe')
28
+ $class = substr($class, strlen('Stripe'));
29
+ $class = str_replace('_', '', $class);
30
+ $name = urlencode($class);
31
+ $name = strtolower($name);
32
+ return $name;
33
+ }
34
+
35
+ public static function classUrl($class)
36
+ {
37
+ $base = self::className($class);
38
+ return "/v1/${base}s";
39
+ }
40
+
41
+ public function instanceUrl()
42
+ {
43
+ $id = $this['id'];
44
+ $class = get_class($this);
45
+ if (!$id) {
46
+ throw new Stripe_InvalidRequestError("Could not determine which URL to request: $class instance has invalid ID: $id", null);
47
+ }
48
+ $id = Stripe_ApiRequestor::utf8($id);
49
+ $base = self::classUrl($class);
50
+ $extn = urlencode($id);
51
+ return "$base/$extn";
52
+ }
53
+
54
+ private static function _validateCall($method, $params=null, $apiKey=null)
55
+ {
56
+ if ($params && !is_array($params))
57
+ throw new Stripe_Error("You must pass an array as the first argument to Stripe API method calls. (HINT: an example call to create a charge would be: \"StripeCharge::create(array('amount' => 100, 'currency' => 'usd', 'card' => array('number' => 4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")");
58
+ if ($apiKey && !is_string($apiKey))
59
+ throw new Stripe_Error('The second argument to Stripe API method calls is an optional per-request apiKey, which must be a string. (HINT: you can set a global apiKey by "Stripe::setApiKey(<apiKey>)")');
60
+ }
61
+
62
+ protected static function _scopedAll($class, $params=null, $apiKey=null)
63
+ {
64
+ self::_validateCall('all', $params, $apiKey);
65
+ $requestor = new Stripe_ApiRequestor($apiKey);
66
+ $url = self::classUrl($class);
67
+ list($response, $apiKey) = $requestor->request('get', $url, $params);
68
+ return Stripe_Util::convertToStripeObject($response, $apiKey);
69
+ }
70
+
71
+ protected static function _scopedCreate($class, $params=null, $apiKey=null)
72
+ {
73
+ self::_validateCall('create', $params, $apiKey);
74
+ $requestor = new Stripe_ApiRequestor($apiKey);
75
+ $url = self::classUrl($class);
76
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
77
+ return Stripe_Util::convertToStripeObject($response, $apiKey);
78
+ }
79
+
80
+ protected function _scopedSave($class)
81
+ {
82
+ self::_validateCall('save');
83
+ if ($this->_unsavedValues) {
84
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
85
+ $params = array();
86
+ foreach ($this->_unsavedValues->toArray() as $k)
87
+ $params[$k] = $this->$k;
88
+ $url = $this->instanceUrl();
89
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
90
+ $this->refreshFrom($response, $apiKey);
91
+ }
92
+ return $this;
93
+ }
94
+
95
+ protected function _scopedDelete($class, $params=null)
96
+ {
97
+ self::_validateCall('delete');
98
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
99
+ $url = $this->instanceUrl();
100
+ list($response, $apiKey) = $requestor->request('delete', $url, $params);
101
+ $this->refreshFrom($response, $apiKey);
102
+ return $this;
103
+ }
104
+ }
includes/lib/Stripe/Stripe/AuthenticationError.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_AuthenticationError extends Stripe_Error
4
+ {
5
+ }
includes/lib/Stripe/Stripe/CardError.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_CardError extends Stripe_Error
4
+ {
5
+ public function __construct($message, $param, $code, $http_status=null, $http_body=null, $json_body=null)
6
+ {
7
+ parent::__construct($message, $http_status, $http_body, $json_body);
8
+ $this->param = $param;
9
+ $this->code = $code;
10
+ }
11
+ }
includes/lib/Stripe/Stripe/Charge.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Charge extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function all($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedAll($class, $params, $apiKey);
21
+ }
22
+
23
+ public static function create($params=null, $apiKey=null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ public function refund($params=null)
30
+ {
31
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
32
+ $url = $this->instanceUrl() . '/refund';
33
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
34
+ $this->refreshFrom($response, $apiKey);
35
+ return $this;
36
+ }
37
+
38
+ public function capture($params=null)
39
+ {
40
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
41
+ $url = $this->instanceUrl() . '/capture';
42
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
43
+ $this->refreshFrom($response, $apiKey);
44
+ return $this;
45
+ }
46
+
47
+ public function updateDispute($params=null)
48
+ {
49
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
50
+ $url = $this->instanceUrl() . '/dispute';
51
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
52
+ $this->refreshFrom(array('dispute' => $response), $apiKey, true);
53
+ return $this->dispute;
54
+ }
55
+ }
includes/lib/Stripe/Stripe/Coupon.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Coupon extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function create($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedCreate($class, $params, $apiKey);
21
+ }
22
+
23
+ public function delete($params=null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedDelete($class, $params);
27
+ }
28
+
29
+ public static function all($params=null, $apiKey=null)
30
+ {
31
+ $class = get_class();
32
+ return self::_scopedAll($class, $params, $apiKey);
33
+ }
34
+ }
includes/lib/Stripe/Stripe/Customer.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Customer extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function all($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedAll($class, $params, $apiKey);
21
+ }
22
+
23
+ public static function create($params=null, $apiKey=null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ public function save()
30
+ {
31
+ $class = get_class();
32
+ return self::_scopedSave($class);
33
+ }
34
+
35
+ public function delete($params=null)
36
+ {
37
+ $class = get_class();
38
+ return self::_scopedDelete($class, $params);
39
+ }
40
+
41
+ public function addInvoiceItem($params=null)
42
+ {
43
+ if (!$params)
44
+ $params = array();
45
+ $params['customer'] = $this->id;
46
+ $ii = Stripe_InvoiceItem::create($params, $this->_apiKey);
47
+ return $ii;
48
+ }
49
+
50
+ public function invoices($params=null)
51
+ {
52
+ if (!$params)
53
+ $params = array();
54
+ $params['customer'] = $this->id;
55
+ $invoices = Stripe_Invoice::all($params, $this->_apiKey);
56
+ return $invoices;
57
+ }
58
+
59
+ public function invoiceItems($params=null)
60
+ {
61
+ if (!$params)
62
+ $params = array();
63
+ $params['customer'] = $this->id;
64
+ $iis = Stripe_InvoiceItem::all($params, $this->_apiKey);
65
+ return $iis;
66
+ }
67
+
68
+ public function charges($params=null)
69
+ {
70
+ if (!$params)
71
+ $params = array();
72
+ $params['customer'] = $this->id;
73
+ $charges = Stripe_Charge::all($params, $this->_apiKey);
74
+ return $charges;
75
+ }
76
+
77
+ public function updateSubscription($params=null)
78
+ {
79
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
80
+ $url = $this->instanceUrl() . '/subscription';
81
+ list($response, $apiKey) = $requestor->request('post', $url, $params);
82
+ $this->refreshFrom(array('subscription' => $response), $apiKey, true);
83
+ return $this->subscription;
84
+ }
85
+
86
+ public function cancelSubscription($params=null)
87
+ {
88
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
89
+ $url = $this->instanceUrl() . '/subscription';
90
+ list($response, $apiKey) = $requestor->request('delete', $url, $params);
91
+ $this->refreshFrom(array('subscription' => $response), $apiKey, true);
92
+ return $this->subscription;
93
+ }
94
+
95
+ public function deleteDiscount()
96
+ {
97
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
98
+ $url = $this->instanceUrl() . '/discount';
99
+ list($response, $apiKey) = $requestor->request('delete', $url);
100
+ $this->refreshFrom(array('discount' => null), $apiKey, true);
101
+ }
102
+ }
includes/lib/Stripe/Stripe/Error.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Error extends Exception
4
+ {
5
+ public function __construct($message=null, $http_status=null, $http_body=null, $json_body=null)
6
+ {
7
+ parent::__construct($message);
8
+ $this->http_status = $http_status;
9
+ $this->http_body = $http_body;
10
+ $this->json_body = $json_body;
11
+ }
12
+
13
+ public function getHttpStatus()
14
+ {
15
+ return $this->http_status;
16
+ }
17
+
18
+ public function getHttpBody()
19
+ {
20
+ return $this->http_body;
21
+ }
22
+
23
+ public function getJsonBody()
24
+ {
25
+ return $this->json_body;
26
+ }
27
+ }
includes/lib/Stripe/Stripe/Event.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Event extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function all($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedAll($class, $params, $apiKey);
21
+ }
22
+ }
includes/lib/Stripe/Stripe/InvalidRequestError.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_InvalidRequestError extends Stripe_Error
4
+ {
5
+ public function __construct($message, $param, $http_status=null, $http_body=null, $json_body=null)
6
+ {
7
+ parent::__construct($message, $http_status, $http_body, $json_body);
8
+ $this->param = $param;
9
+ }
10
+ }
includes/lib/Stripe/Stripe/Invoice.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Invoice extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function create($params=null, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedCreate($class, $params, $apiKey);
15
+ }
16
+
17
+ public static function retrieve($id, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedRetrieve($class, $id, $apiKey);
21
+ }
22
+
23
+ public static function all($params=null, $apiKey=null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedAll($class, $params, $apiKey);
27
+ }
28
+
29
+ public static function upcoming($params=null, $apiKey=null)
30
+ {
31
+ $requestor = new Stripe_ApiRequestor($apiKey);
32
+ $url = self::classUrl(get_class()) . '/upcoming';
33
+ list($response, $apiKey) = $requestor->request('get', $url, $params);
34
+ return Stripe_Util::convertToStripeObject($response, $apiKey);
35
+ }
36
+
37
+ public function save()
38
+ {
39
+ $class = get_class();
40
+ return self::_scopedSave($class);
41
+ }
42
+
43
+ public function pay()
44
+ {
45
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
46
+ $url = $this->instanceUrl() . '/pay';
47
+ list($response, $apiKey) = $requestor->request('post', $url);
48
+ $this->refreshFrom($response, $apiKey);
49
+ return $this;
50
+ }
51
+ }
includes/lib/Stripe/Stripe/InvoiceItem.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_InvoiceItem extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function all($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedAll($class, $params, $apiKey);
21
+ }
22
+
23
+ public static function create($params=null, $apiKey=null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedCreate($class, $params, $apiKey);
27
+ }
28
+
29
+ public function save()
30
+ {
31
+ $class = get_class();
32
+ return self::_scopedSave($class);
33
+ }
34
+
35
+ public function delete($params=null)
36
+ {
37
+ $class = get_class();
38
+ return self::_scopedDelete($class, $params);
39
+ }
40
+ }
includes/lib/Stripe/Stripe/List.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_List extends Stripe_Object
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public function all($params=null)
12
+ {
13
+ $requestor = new Stripe_ApiRequestor($this->_apiKey);
14
+ list($response, $apiKey) = $requestor->request('get', $this['url'], $params);
15
+ return Stripe_Util::convertToStripeObject($response, $apiKey);
16
+ }
17
+ }
includes/lib/Stripe/Stripe/Object.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Object implements ArrayAccess
4
+ {
5
+ public static $_permanentAttributes;
6
+
7
+ public static function init()
8
+ {
9
+ self::$_permanentAttributes = new Stripe_Util_Set(array('_apiKey'));
10
+ }
11
+
12
+ protected $_apiKey;
13
+ protected $_values;
14
+ protected $_unsavedValues;
15
+ protected $_transientValues;
16
+
17
+ public function __construct($id=null, $apiKey=null)
18
+ {
19
+ $this->_apiKey = $apiKey;
20
+ $this->_values = array();
21
+ $this->_unsavedValues = new Stripe_Util_Set();
22
+ $this->_transientValues = new Stripe_Util_Set();
23
+ if ($id)
24
+ $this->id = $id;
25
+ }
26
+
27
+ // Standard accessor magic methods
28
+ public function __set($k, $v)
29
+ {
30
+ // TODO: may want to clear from $_transientValues. (Won't be user-visible.)
31
+ $this->_values[$k] = $v;
32
+ if (!self::$_permanentAttributes->includes($k))
33
+ $this->_unsavedValues->add($k);
34
+ }
35
+ public function __isset($k)
36
+ {
37
+ return isset($this->_values[$k]);
38
+ }
39
+ public function __unset($k)
40
+ {
41
+ unset($this->_values[$k]);
42
+ $this->_transientValues->add($k);
43
+ $this->_unsavedValues->discard($k);
44
+ }
45
+ public function __get($k)
46
+ {
47
+ if (array_key_exists($k, $this->_values)) {
48
+ return $this->_values[$k];
49
+ } else if ($this->_transientValues->includes($k)) {
50
+ $class = get_class($this);
51
+ $attrs = join(', ', array_keys($this->_values));
52
+ error_log("Stripe Notice: Undefined property of $class instance: $k. HINT: The $k attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Stripe's API, probably as a result of a save(). The attributes currently available on this object are: $attrs");
53
+ return null;
54
+ } else {
55
+ $class = get_class($this);
56
+ error_log("Stripe Notice: Undefined property of $class instance: $k");
57
+ return null;
58
+ }
59
+ }
60
+
61
+ // ArrayAccess methods
62
+ public function offsetSet($k, $v)
63
+ {
64
+ $this->$k = $v;
65
+ }
66
+
67
+ public function offsetExists($k)
68
+ {
69
+ return array_key_exists($k, $this->_values);
70
+ }
71
+
72
+ public function offsetUnset($k)
73
+ {
74
+ unset($this->$k);
75
+ }
76
+ public function offsetGet($k)
77
+ {
78
+ return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
79
+ }
80
+
81
+ // This unfortunately needs to be public to be used in Util.php
82
+ public static function scopedConstructFrom($class, $values, $apiKey=null)
83
+ {
84
+ $obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey);
85
+ $obj->refreshFrom($values, $apiKey);
86
+ return $obj;
87
+ }
88
+
89
+ public static function constructFrom($values, $apiKey=null)
90
+ {
91
+ $class = get_class();
92
+ return self::scopedConstructFrom($class, $values, $apiKey);
93
+ }
94
+
95
+ public function refreshFrom($values, $apiKey, $partial=false)
96
+ {
97
+ $this->_apiKey = $apiKey;
98
+ // Wipe old state before setting new. This is useful for e.g. updating a
99
+ // customer, where there is no persistent card parameter. Mark those values
100
+ // which don't persist as transient
101
+ if ($partial)
102
+ $removed = new Stripe_Util_Set();
103
+ else
104
+ $removed = array_diff(array_keys($this->_values), array_keys($values));
105
+
106
+ foreach ($removed as $k) {
107
+ if (self::$_permanentAttributes->includes($k))
108
+ continue;
109
+ unset($this->$k);
110
+ }
111
+
112
+ foreach ($values as $k => $v) {
113
+ if (self::$_permanentAttributes->includes($k))
114
+ continue;
115
+ $this->_values[$k] = Stripe_Util::convertToStripeObject($v, $apiKey);
116
+ $this->_transientValues->discard($k);
117
+ $this->_unsavedValues->discard($k);
118
+ }
119
+ }
120
+
121
+ public function __toJSON()
122
+ {
123
+ if (defined('JSON_PRETTY_PRINT'))
124
+ return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
125
+ else
126
+ return json_encode($this->__toArray(true));
127
+ }
128
+
129
+ public function __toString()
130
+ {
131
+ return $this->__toJSON();
132
+ }
133
+
134
+ public function __toArray($recursive=false)
135
+ {
136
+ if ($recursive)
137
+ return Stripe_Util::convertStripeObjectToArray($this->_values);
138
+ else
139
+ return $this->_values;
140
+ }
141
+ }
142
+
143
+
144
+ Stripe_Object::init();
includes/lib/Stripe/Stripe/Plan.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Plan extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function create($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedCreate($class, $params, $apiKey);
21
+ }
22
+
23
+ public function delete($params=null)
24
+ {
25
+ $class = get_class();
26
+ return self::_scopedDelete($class, $params);
27
+ }
28
+
29
+ public function save()
30
+ {
31
+ $class = get_class();
32
+ return self::_scopedSave($class);
33
+ }
34
+
35
+ public static function all($params=null, $apiKey=null)
36
+ {
37
+ $class = get_class();
38
+ return self::_scopedAll($class, $params, $apiKey);
39
+ }
40
+ }
includes/lib/Stripe/Stripe/SingletonApiResource.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Stripe_SingletonApiResource extends Stripe_ApiResource
4
+ {
5
+ protected static function _scopedSingletonRetrieve($class, $apiKey=null)
6
+ {
7
+ $instance = new $class(null, $apiKey);
8
+ $instance->refresh();
9
+ return $instance;
10
+ }
11
+
12
+ public static function classUrl($class)
13
+ {
14
+ $base = self::className($class);
15
+ return "/v1/${base}";
16
+ }
17
+
18
+ public function instanceUrl()
19
+ {
20
+ $class = get_class($this);
21
+ $base = self::classUrl($class);
22
+ return "$base";
23
+ }
24
+ }
includes/lib/Stripe/Stripe/Stripe.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Stripe
4
+ {
5
+ public static $apiKey;
6
+ public static $apiBase = 'https://api.stripe.com';
7
+ public static $verifySslCerts = true;
8
+ const VERSION = '1.7.9';
9
+
10
+ public static function getApiKey()
11
+ {
12
+ return self::$apiKey;
13
+ }
14
+
15
+ public static function setApiKey($apiKey)
16
+ {
17
+ self::$apiKey = $apiKey;
18
+ }
19
+
20
+ public static function getVerifySslCerts() {
21
+ return self::$verifySslCerts;
22
+ }
23
+
24
+ public static function setVerifySslCerts($verify) {
25
+ self::$verifySslCerts = $verify;
26
+ }
27
+ }
includes/lib/Stripe/Stripe/Token.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Token extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function create($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedCreate($class, $params, $apiKey);
21
+ }
22
+ }
includes/lib/Stripe/Stripe/Transfer.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Transfer extends Stripe_ApiResource
4
+ {
5
+ public static function constructFrom($values, $apiKey=null)
6
+ {
7
+ $class = get_class();
8
+ return self::scopedConstructFrom($class, $values, $apiKey);
9
+ }
10
+
11
+ public static function retrieve($id, $apiKey=null)
12
+ {
13
+ $class = get_class();
14
+ return self::_scopedRetrieve($class, $id, $apiKey);
15
+ }
16
+
17
+ public static function all($params=null, $apiKey=null)
18
+ {
19
+ $class = get_class();
20
+ return self::_scopedAll($class, $params, $apiKey);
21
+ }
22
+ }
includes/lib/Stripe/Stripe/Util.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class Stripe_Util
4
+ {
5
+ public static function isList($array)
6
+ {
7
+ if (!is_array($array))
8
+ return false;
9
+ // TODO: this isn't actually correct in general, but it's correct given Stripe's responses
10
+ foreach (array_keys($array) as $k) {
11
+ if (!is_numeric($k))
12
+ return false;
13
+ }
14
+ return true;
15
+ }
16
+
17
+ public static function convertStripeObjectToArray($values)
18
+ {
19
+ $results = array();
20
+ foreach ($values as $k => $v) {
21
+ // FIXME: this is an encapsulation violation
22
+ if (Stripe_Object::$_permanentAttributes->includes($k)) {
23
+ continue;
24
+ }
25
+ if ($v instanceof Stripe_Object) {
26
+ $results[$k] = $v->__toArray(true);
27
+ }
28
+ else if (is_array($v)) {
29
+ $results[$k] = self::convertStripeObjectToArray($v);
30
+ }
31
+ else {
32
+ $results[$k] = $v;
33
+ }
34
+ }
35
+ return $results;
36
+ }
37
+
38
+ public static function convertToStripeObject($resp, $apiKey)
39
+ {
40
+ $types = array('charge' => 'Stripe_Charge',
41
+ 'customer' => 'Stripe_Customer',
42
+ 'list' => 'Stripe_List',
43
+ 'invoice' => 'Stripe_Invoice',
44
+ 'invoiceitem' => 'Stripe_InvoiceItem', 'event' => 'Stripe_Event',
45
+ 'transfer' => 'Stripe_Transfer');
46
+ if (self::isList($resp)) {
47
+ $mapped = array();
48
+ foreach ($resp as $i)
49
+ array_push($mapped, self::convertToStripeObject($i, $apiKey));
50
+ return $mapped;
51
+ } else if (is_array($resp)) {
52
+ if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']]))
53
+ $class = $types[$resp['object']];
54
+ else
55
+ $class = 'Stripe_Object';
56
+ return Stripe_Object::scopedConstructFrom($class, $resp, $apiKey);
57
+ } else {
58
+ return $resp;
59
+ }
60
+ }
61
+ }
includes/lib/Stripe/Stripe/Util/Set.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Stripe_Util_Set
4
+ {
5
+ private $_elts;
6
+
7
+ public function __construct($members=array())
8
+ {
9
+ $this->_elts = array();
10
+ foreach ($members as $item)
11
+ $this->_elts[$item] = true;
12
+ }
13
+
14
+ public function includes($elt)
15
+ {
16
+ return isset($this->_elts[$elt]);
17
+ }
18
+
19
+ public function add($elt)
20
+ {
21
+ $this->_elts[$elt] = true;
22
+ }
23
+
24
+ public function discard($elt)
25
+ {
26
+ unset($this->_elts[$elt]);
27
+ }
28
+
29
+ // TODO: make Set support foreach
30
+ public function toArray()
31
+ {
32
+ return array_keys($this->_elts);
33
+ }
34
+ }
includes/lib/Stripe/data/ca-certificates.crt ADDED
@@ -0,0 +1,3918 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated using the default CA bundle on Ubuntu Linux 11.10 on November 25, 2011
2
+
3
+ -----BEGIN CERTIFICATE-----
4
+ MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
5
+ EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
6
+ bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
7
+ YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
8
+ Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy
9
+ MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
10
+ A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
11
+ YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD
12
+ VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB
13
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA
14
+ isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj
15
+ Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50
16
+ QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt
17
+ bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR
18
+ yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID
19
+ AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0
20
+ cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f
21
+ BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj
22
+ cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB
23
+ /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1
24
+ U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl
25
+ YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos
26
+ SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/
27
+ t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u
28
+ mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb
29
+ K+9A46sd33oqK8n8
30
+ -----END CERTIFICATE-----
31
+ -----BEGIN CERTIFICATE-----
32
+ MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
33
+ IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
34
+ IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
35
+ Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
36
+ BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
37
+ MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
38
+ ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
39
+ CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
40
+ 8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
41
+ zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
42
+ fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
43
+ w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
44
+ G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
45
+ epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
46
+ laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
47
+ QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
48
+ fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
49
+ YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
50
+ ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
51
+ gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
52
+ MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
53
+ IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
54
+ dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
55
+ czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
56
+ dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
57
+ aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
58
+ AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
59
+ b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
60
+ ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
61
+ nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
62
+ 18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
63
+ gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
64
+ Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
65
+ sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
66
+ SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
67
+ CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
68
+ GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
69
+ zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
70
+ omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
71
+ -----END CERTIFICATE-----
72
+ -----BEGIN CERTIFICATE-----
73
+ MIIGCDCCA/CgAwIBAgIBATANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
74
+ IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
75
+ IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
76
+ Y2FjZXJ0Lm9yZzAeFw0wNTEwMTQwNzM2NTVaFw0zMzAzMjgwNzM2NTVaMFQxFDAS
77
+ BgNVBAoTC0NBY2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5v
78
+ cmcxHDAaBgNVBAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwggIiMA0GCSqGSIb3DQEB
79
+ AQUAA4ICDwAwggIKAoICAQCrSTURSHzSJn5TlM9Dqd0o10Iqi/OHeBlYfA+e2ol9
80
+ 4fvrcpANdKGWZKufoCSZc9riVXbHF3v1BKxGuMO+f2SNEGwk82GcwPKQ+lHm9WkB
81
+ Y8MPVuJKQs/iRIwlKKjFeQl9RrmK8+nzNCkIReQcn8uUBByBqBSzmGXEQ+xOgo0J
82
+ 0b2qW42S0OzekMV/CsLj6+YxWl50PpczWejDAz1gM7/30W9HxM3uYoNSbi4ImqTZ
83
+ FRiRpoWSR7CuSOtttyHshRpocjWr//AQXcD0lKdq1TuSfkyQBX6TwSyLpI5idBVx
84
+ bgtxA+qvFTia1NIFcm+M+SvrWnIl+TlG43IbPgTDZCciECqKT1inA62+tC4T7V2q
85
+ SNfVfdQqe1z6RgRQ5MwOQluM7dvyz/yWk+DbETZUYjQ4jwxgmzuXVjit89Jbi6Bb
86
+ 6k6WuHzX1aCGcEDTkSm3ojyt9Yy7zxqSiuQ0e8DYbF/pCsLDpyCaWt8sXVJcukfV
87
+ m+8kKHA4IC/VfynAskEDaJLM4JzMl0tF7zoQCqtwOpiVcK01seqFK6QcgCExqa5g
88
+ eoAmSAC4AcCTY1UikTxW56/bOiXzjzFU6iaLgVn5odFTEcV7nQP2dBHgbbEsPyyG
89
+ kZlxmqZ3izRg0RS0LKydr4wQ05/EavhvE/xzWfdmQnQeiuP43NJvmJzLR5iVQAX7
90
+ 6QIDAQABo4G/MIG8MA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUHAQEEUTBPMCMG
91
+ CCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggrBgEFBQcwAoYc
92
+ aHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBBMD8GCCsGAQQB
93
+ gZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9yZy9pbmRleC5w
94
+ aHA/aWQ9MTAwDQYJKoZIhvcNAQEEBQADggIBAH8IiKHaGlBJ2on7oQhy84r3HsQ6
95
+ tHlbIDCxRd7CXdNlafHCXVRUPIVfuXtCkcKZ/RtRm6tGpaEQU55tiKxzbiwzpvD0
96
+ nuB1wT6IRanhZkP+VlrRekF490DaSjrxC1uluxYG5sLnk7mFTZdPsR44Q4Dvmw2M
97
+ 77inYACHV30eRBzLI++bPJmdr7UpHEV5FpZNJ23xHGzDwlVks7wU4vOkHx4y/CcV
98
+ Bc/dLq4+gmF78CEQGPZE6lM5+dzQmiDgxrvgu1pPxJnIB721vaLbLmINQjRBvP+L
99
+ ivVRIqqIMADisNS8vmW61QNXeZvo3MhN+FDtkaVSKKKs+zZYPumUK5FQhxvWXtaM
100
+ zPcPEAxSTtAWYeXlCmy/F8dyRlecmPVsYGN6b165Ti/Iubm7aoW8mA3t+T6XhDSU
101
+ rgCvoeXnkm5OvfPi2RSLXNLrAWygF6UtEOucekq9ve7O/e0iQKtwOIj1CodqwqsF
102
+ YMlIBdpTwd5Ed2qz8zw87YC8pjhKKSRf/lk7myV6VmMAZLldpGJ9VzZPrYPvH5JT
103
+ oI53V93lYRE9IwCQTDz6o2CTBKOvNfYOao9PSmCnhQVsRqGP9Md246FZV/dxssRu
104
+ FFxtbUFm3xuTsdQAw+7Lzzw9IYCpX2Nl/N3gX6T0K/CFcUHUZyX7GrGXrtaZghNB
105
+ 0m6lG5kngOcLqagA
106
+ -----END CERTIFICATE-----
107
+ -----BEGIN CERTIFICATE-----
108
+ MIIESzCCAzOgAwIBAgIJAJigUTEEXRQpMA0GCSqGSIb3DQEBBQUAMHYxCzAJBgNV
109
+ BAYTAkRFMQ8wDQYDVQQIEwZIZXNzZW4xDjAMBgNVBAcTBUZ1bGRhMRAwDgYDVQQK
110
+ EwdEZWJjb25mMRMwEQYDVQQDEwpEZWJjb25mIENBMR8wHQYJKoZIhvcNAQkBFhBq
111
+ b2VyZ0BkZWJpYW4ub3JnMB4XDTA1MTEwNTE3NTUxNFoXDTE1MTEwMzE3NTUxNFow
112
+ djELMAkGA1UEBhMCREUxDzANBgNVBAgTBkhlc3NlbjEOMAwGA1UEBxMFRnVsZGEx
113
+ EDAOBgNVBAoTB0RlYmNvbmYxEzARBgNVBAMTCkRlYmNvbmYgQ0ExHzAdBgkqhkiG
114
+ 9w0BCQEWEGpvZXJnQGRlYmlhbi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
115
+ ggEKAoIBAQCvbOo0SrIwI5IMlsshH8WF3dHB9r9JlSKhMPaybawa1EyvZspMQ3wa
116
+ F5qxNf3Sj+NElEmjseEqvCZiIIzqwerHu0Qw62cDYCdCd2+Wb5m0bPYB5CGHiyU1
117
+ eNP0je42O0YeXG2BvUujN8AviocVo39X2YwNQ0ryy4OaqYgm2pRlbtT2ESbF+SfV
118
+ Y2iqQj/f8ymF+lHo/pz8tbAqxWcqaSiHFAVQJrdqtFhtoodoNiE3q76zJoUkZTXB
119
+ k60Yc3MJSnatZCpnsSBr/D7zpntl0THrUjjtdRWCjQVhqfhM1yZJV+ApbLdheFh0
120
+ ZWlSxdnp25p0q0XYw/7G92ELyFDfBUUNAgMBAAGjgdswgdgwHQYDVR0OBBYEFMuV
121
+ dFNb4mCWUFbcP5LOtxFLrEVTMIGoBgNVHSMEgaAwgZ2AFMuVdFNb4mCWUFbcP5LO
122
+ txFLrEVToXqkeDB2MQswCQYDVQQGEwJERTEPMA0GA1UECBMGSGVzc2VuMQ4wDAYD
123
+ VQQHEwVGdWxkYTEQMA4GA1UEChMHRGViY29uZjETMBEGA1UEAxMKRGViY29uZiBD
124
+ QTEfMB0GCSqGSIb3DQEJARYQam9lcmdAZGViaWFuLm9yZ4IJAJigUTEEXRQpMAwG
125
+ A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGZXxHg4mnkvilRIM1EQfGdY
126
+ S5b/WcyF2MYSTeTvK4aIB6VHwpZoZCnDGj2m2D3CkHT0upAD9o0zM1tdsfncLzV+
127
+ mDT/jNmBtYo4QXx5vEPwvEIcgrWjwk7SyaEUhZjtolTkHB7ACl0oD0r71St4iEPR
128
+ qTUCEXk2E47bg1Fz58wNt/yo2+4iqiRjg1XCH4evkQuhpW+dTZnDyFNqwSYZapOE
129
+ TBA+9zBb6xD1KM2DdY7r4GiyYItN0BKLfuWbh9LXGbl1C+f4P11g+m2MPiavIeCe
130
+ 1iazG5pcS3KoTLACsYlEX24TINtg4kcuS81XdllcnsV3Kdts0nIqPj6uhTTZD0k=
131
+ -----END CERTIFICATE-----
132
+ -----BEGIN CERTIFICATE-----
133
+ MIIDvjCCA3ygAwIBAgIFJQaThoEwCwYHKoZIzjgEAwUAMIGFMQswCQYDVQQGEwJG
134
+ UjEPMA0GA1UECBMGRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczEQMA4GA1UEChMHUE0v
135
+ U0dETjEOMAwGA1UECxMFRENTU0kxDjAMBgNVBAMTBUlHQy9BMSMwIQYJKoZIhvcN
136
+ AQkBFhRpZ2NhQHNnZG4ucG0uZ291di5mcjAeFw0wMjEyMTMxNDM5MTVaFw0yMDEw
137
+ MTcxNDM5MTRaMIGFMQswCQYDVQQGEwJGUjEPMA0GA1UECBMGRnJhbmNlMQ4wDAYD
138
+ VQQHEwVQYXJpczEQMA4GA1UEChMHUE0vU0dETjEOMAwGA1UECxMFRENTU0kxDjAM
139
+ BgNVBAMTBUlHQy9BMSMwIQYJKoZIhvcNAQkBFhRpZ2NhQHNnZG4ucG0uZ291di5m
140
+ cjCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCFkMImdk9zDzJfTO4XPdAAmLbAdWws
141
+ ZiEMZh19RyTo3CyhFqO77OIXrwY6vc1pcc3MgWJ0dgQpAgrDMtmFFxpUu4gmjVsx
142
+ 8GpxQC+4VOgLY8Cvmcd/UDzYg07EIRto8BwCpPJ/JfUxwzV2V3N713aAX+cEoKZ/
143
+ s+kgxC6nZCA7oQIVALME/JYjkdW2uKIGngsEPbXAjdhDAoGADh/uqWJx94UBm31c
144
+ 9d8ZTBfRGRnmSSRVFDgPWgA69JD4BR5da8tKz+1HjfMhDXljbMH86ixpD5Ka1Z0V
145
+ pRYUPbyAoB37tsmXMJY7kjyD19d5VdaZboUjVvhH6UJy5lpNNNGSvFl4fqkxyvw+
146
+ pq1QV0N5RcvK120hlXdfHUX+YKYDgYQAAoGAQGr7IuKJcYIvJRMjxwl43KxXY2xC
147
+ aoCiM/bv117MfI94aNf1UusGhp7CbYAY9CXuL60P0oPMAajbaTE5Z34AuITeHq3Y
148
+ CNMHwxalip8BHqSSGmGiQsXeK7T+r1rPXsccZ1c5ikGDZ4xn5gUaCyy2rCmb+fOJ
149
+ 6VAfCbAbAjmNKwejdzB1MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgFGMBUG
150
+ A1UdIAQOMAwwCgYIKoF6AXkBAQEwHQYDVR0OBBYEFPkeNRcUf8idzpKblYbLNxs0
151
+ MQhSMB8GA1UdIwQYMBaAFPkeNRcUf8idzpKblYbLNxs0MQhSMAsGByqGSM44BAMF
152
+ AAMvADAsAhRVh+CJA5eVyEYU5AO9Tm7GxX0rmQIUBCqsU5u1WxoZ5lEXicDX5/Ob
153
+ sRQ=
154
+ -----END CERTIFICATE-----
155
+ -----BEGIN CERTIFICATE-----
156
+ MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT
157
+ AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ
158
+ TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG
159
+ 9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw
160
+ MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM
161
+ BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO
162
+ MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2
163
+ LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI
164
+ s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2
165
+ xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4
166
+ u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b
167
+ F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx
168
+ Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd
169
+ PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV
170
+ HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx
171
+ NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF
172
+ AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ
173
+ L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY
174
+ YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
175
+ Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a
176
+ NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R
177
+ 0982gaEbeC9xs/FZTEYYKKuF0mBWWg==
178
+ -----END CERTIFICATE-----
179
+ -----BEGIN CERTIFICATE-----
180
+ MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
181
+ MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
182
+ IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
183
+ MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
184
+ FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
185
+ bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
186
+ dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
187
+ H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
188
+ uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
189
+ mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
190
+ a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
191
+ E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
192
+ WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
193
+ VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
194
+ Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
195
+ cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
196
+ IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
197
+ AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
198
+ YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
199
+ 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
200
+ Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
201
+ c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
202
+ mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
203
+ -----END CERTIFICATE-----
204
+ -----BEGIN CERTIFICATE-----
205
+ MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
206
+ MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
207
+ b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
208
+ MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
209
+ QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
210
+ VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
211
+ A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
212
+ CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
213
+ tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
214
+ dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
215
+ PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
216
+ +Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
217
+ BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
218
+ BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
219
+ MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
220
+ ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
221
+ IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
222
+ 7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
223
+ 43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
224
+ eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
225
+ pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
226
+ WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
227
+ -----END CERTIFICATE-----
228
+ -----BEGIN CERTIFICATE-----
229
+ MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
230
+ MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
231
+ b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
232
+ MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
233
+ ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
234
+ BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
235
+ AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
236
+ 6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
237
+ GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
238
+ dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
239
+ 1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
240
+ 62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
241
+ BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
242
+ AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
243
+ MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
244
+ cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
245
+ b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
246
+ IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
247
+ iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
248
+ GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
249
+ 4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
250
+ XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
251
+ -----END CERTIFICATE-----
252
+ -----BEGIN CERTIFICATE-----
253
+ MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
254
+ MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
255
+ b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
256
+ MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
257
+ EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
258
+ BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
259
+ AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
260
+ xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
261
+ 87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
262
+ 2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
263
+ WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
264
+ 0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
265
+ A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
266
+ AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
267
+ pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
268
+ ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
269
+ aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
270
+ hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
271
+ hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
272
+ dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
273
+ P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
274
+ iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
275
+ xqE=
276
+ -----END CERTIFICATE-----
277
+ -----BEGIN CERTIFICATE-----
278
+ MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
279
+ MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
280
+ bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2
281
+ MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
282
+ ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
283
+ Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
284
+ ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk
285
+ hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym
286
+ 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW
287
+ OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb
288
+ 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko
289
+ O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
290
+ AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU
291
+ AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
292
+ BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
293
+ Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb
294
+ LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir
295
+ oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C
296
+ MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds
297
+ sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
298
+ -----END CERTIFICATE-----
299
+ -----BEGIN CERTIFICATE-----
300
+ MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
301
+ MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
302
+ bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2
303
+ MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft
304
+ ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg
305
+ Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
306
+ ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC
307
+ 206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci
308
+ KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2
309
+ JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9
310
+ BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e
311
+ Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B
312
+ PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67
313
+ Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq
314
+ Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
315
+ o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3
316
+ +L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj
317
+ YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj
318
+ FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE
319
+ AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn
320
+ xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2
321
+ LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc
322
+ obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8
323
+ CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe
324
+ IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA
325
+ DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F
326
+ AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX
327
+ Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb
328
+ AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl
329
+ Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
330
+ RY8mkaKO/qk=
331
+ -----END CERTIFICATE-----
332
+ -----BEGIN CERTIFICATE-----
333
+ MIID5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMCVVMx
334
+ HTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNBbWVyaWNh
335
+ IE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIgUm9vdCBDZXJ0
336
+ aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyOTA2MDAwMFoXDTM3MTEyMDE1
337
+ MDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBT0wgVGltZSBXYXJuZXIg
338
+ SW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUgSW5jLjE3MDUGA1UEAxMuQU9M
339
+ IFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIw
340
+ DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnej8Mlo2k06AX3dLm/WpcZuS+U
341
+ 0pPlLYnKhHw/EEMbjIt8hFj4JHxIzyr9wBXZGH6EGhfT257XyuTZ16pYUYfw8ItI
342
+ TuLCxFlpMGK2MKKMCxGZYTVtfu/FsRkGIBKOQuHfD5YQUqjPnF+VFNivO3ULMSAf
343
+ RC+iYkGzuxgh28pxPIzstrkNn+9R7017EvILDOGsQI93f7DKeHEMXRZxcKLXwjqF
344
+ zQ6axOAAsNUl6twr5JQtOJyJQVdkKGUZHLZEtMgxa44Be3ZZJX8VHIQIfHNlIAqh
345
+ BC4aMqiaILGcLCFZ5/vP7nAtCMpjPiybkxlqpMKX/7eGV4iFbJ4VFitNLLMCAwEA
346
+ AaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoTYwFsuGkABFgFOxj8jY
347
+ PXy+XxIwHwYDVR0jBBgwFoAUoTYwFsuGkABFgFOxj8jYPXy+XxIwDgYDVR0PAQH/
348
+ BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQCKIBilvrMvtKaEAEAwKfq0FHNMeUWn
349
+ 9nDg6H5kHgqVfGphwu9OH77/yZkfB2FK4V1Mza3u0FIy2VkyvNp5ctZ7CegCgTXT
350
+ Ct8RHcl5oIBN/lrXVtbtDyqvpxh1MwzqwWEFT2qaifKNuZ8u77BfWgDrvq2g+EQF
351
+ Z7zLBO+eZMXpyD8Fv8YvBxzDNnGGyjhmSs3WuEvGbKeXO/oTLW4jYYehY0KswsuX
352
+ n2Fozy1MBJ3XJU8KDk2QixhWqJNIV9xvrr2eZ1d3iVCzvhGbRWeDhhmH05i9CBoW
353
+ H1iCC+GWaQVLjuyDUTEH1dSf/1l7qG6Fz9NLqUmwX7A5KGgOc90lmt4S
354
+ -----END CERTIFICATE-----
355
+ -----BEGIN CERTIFICATE-----
356
+ MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMCVVMx
357
+ HTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNBbWVyaWNh
358
+ IE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIgUm9vdCBDZXJ0
359
+ aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAwMFoXDTM3MDkyODIz
360
+ NDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRBT0wgVGltZSBXYXJuZXIg
361
+ SW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUgSW5jLjE3MDUGA1UEAxMuQU9M
362
+ IFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIw
363
+ DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ
364
+ 7ouZzU9AhqS2TcnZsdw8TQ2FTBVsRotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilb
365
+ m2BPJoPRYxJWSXakFsKlnUWsi4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOY
366
+ xFSMFkpBd4aVdQxHAWZg/BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZ
367
+ YYCLqJV+FNwSbKTQ2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbq
368
+ JS5Gr42whTg0ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fx
369
+ I2rSAG2X+Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETz
370
+ kxmlJ85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
371
+ EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNoKk/S
372
+ Btc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJKg71ZDIM
373
+ gtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1ExMVCgyhwn2RAu
374
+ rda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
375
+ FE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaAFE9pbQN+nZ8HGEO8txBO
376
+ 1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAO/Ouyugu
377
+ h4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0cnAxa8cZmIDJgt43d15Ui47y6mdP
378
+ yXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRFASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q
379
+ 7C+qPBR7V8F+GBRn7iTGvboVsNIYvbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKT
380
+ RuidDV29rs4prWPVVRaAMCf/drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/
381
+ ClTluUI8JPu3B5wwn3la5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyB
382
+ M5kYJRF3p+v9WAksmWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQ
383
+ my8YJPamTQr5O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xO
384
+ AU++CrYD062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT
385
+ 9Y41xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
386
+ hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOLZ8/5
387
+ fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
388
+ -----END CERTIFICATE-----
389
+ -----BEGIN CERTIFICATE-----
390
+ MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
391
+ RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
392
+ VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
393
+ DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
394
+ ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
395
+ VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
396
+ mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
397
+ IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
398
+ mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
399
+ XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
400
+ dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
401
+ jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
402
+ BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
403
+ DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
404
+ 9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
405
+ jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
406
+ Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
407
+ ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
408
+ R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
409
+ -----END CERTIFICATE-----
410
+ -----BEGIN CERTIFICATE-----
411
+ MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
412
+ MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
413
+ ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
414
+ b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
415
+ MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
416
+ ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
417
+ IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
418
+ AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
419
+ unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
420
+ BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
421
+ 7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
422
+ 0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
423
+ roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
424
+ A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
425
+ aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
426
+ 26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
427
+ BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
428
+ EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
429
+ BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
430
+ aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
431
+ AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
432
+ p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
433
+ 1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
434
+ XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
435
+ eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
436
+ tGWaIZDgqtCYvDi1czyL+Nw=
437
+ -----END CERTIFICATE-----
438
+ -----BEGIN CERTIFICATE-----
439
+ MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn
440
+ MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
441
+ ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo
442
+ YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9
443
+ MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy
444
+ NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G
445
+ A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA
446
+ A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0
447
+ Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s
448
+ QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV
449
+ eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795
450
+ B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh
451
+ z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T
452
+ AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i
453
+ ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w
454
+ TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH
455
+ MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD
456
+ VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE
457
+ VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
458
+ bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B
459
+ AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM
460
+ bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi
461
+ ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG
462
+ VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c
463
+ ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/
464
+ AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
465
+ -----END CERTIFICATE-----
466
+ -----BEGIN CERTIFICATE-----
467
+ MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
468
+ PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
469
+ cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
470
+ MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
471
+ IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
472
+ ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
473
+ VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
474
+ kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
475
+ EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
476
+ H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
477
+ HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
478
+ DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
479
+ QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
480
+ Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
481
+ AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
482
+ yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
483
+ FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
484
+ ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
485
+ kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
486
+ l7+ijrRU
487
+ -----END CERTIFICATE-----
488
+ -----BEGIN CERTIFICATE-----
489
+ MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
490
+ MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
491
+ QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
492
+ MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
493
+ QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
494
+ jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
495
+ ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
496
+ ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
497
+ Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
498
+ AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
499
+ HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
500
+ uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
501
+ TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
502
+ xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
503
+ CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
504
+ O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
505
+ 6GAqm4VKQPNriiTsBhYscw==
506
+ -----END CERTIFICATE-----
507
+ -----BEGIN CERTIFICATE-----
508
+ MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
509
+ MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
510
+ GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
511
+ YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
512
+ MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
513
+ BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
514
+ GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
515
+ ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
516
+ BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
517
+ 3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
518
+ YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
519
+ rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
520
+ ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
521
+ oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
522
+ MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
523
+ QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
524
+ b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
525
+ AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
526
+ GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
527
+ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
528
+ G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
529
+ l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
530
+ smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
531
+ -----END CERTIFICATE-----
532
+ -----BEGIN CERTIFICATE-----
533
+ MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
534
+ gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
535
+ A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
536
+ BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
537
+ MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
538
+ YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
539
+ RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
540
+ aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
541
+ UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
542
+ 2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
543
+ Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
544
+ +2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
545
+ DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
546
+ nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
547
+ /zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
548
+ PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
549
+ QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
550
+ SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
551
+ IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
552
+ RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
553
+ zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
554
+ BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
555
+ ZQ==
556
+ -----END CERTIFICATE-----
557
+ -----BEGIN CERTIFICATE-----
558
+ MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
559
+ MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
560
+ BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
561
+ IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
562
+ MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
563
+ ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
564
+ T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
565
+ biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
566
+ FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
567
+ cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
568
+ BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
569
+ BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
570
+ fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
571
+ GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
572
+ -----END CERTIFICATE-----
573
+ -----BEGIN CERTIFICATE-----
574
+ MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb
575
+ MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
576
+ GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp
577
+ ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow
578
+ fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
579
+ A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV
580
+ BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB
581
+ BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM
582
+ cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S
583
+ HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996
584
+ CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk
585
+ 3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz
586
+ 6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV
587
+ HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
588
+ EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
589
+ Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw
590
+ Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww
591
+ DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0
592
+ 5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
593
+ Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI
594
+ gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ
595
+ aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl
596
+ izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk=
597
+ -----END CERTIFICATE-----
598
+ -----BEGIN CERTIFICATE-----
599
+ MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb
600
+ MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
601
+ GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0
602
+ aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla
603
+ MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
604
+ BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD
605
+ VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B
606
+ AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW
607
+ fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt
608
+ TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL
609
+ fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW
610
+ 1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7
611
+ kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G
612
+ A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD
613
+ VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
614
+ ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo
615
+ dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu
616
+ Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/
617
+ HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
618
+ pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS
619
+ jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+
620
+ xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn
621
+ dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi
622
+ -----END CERTIFICATE-----
623
+ -----BEGIN CERTIFICATE-----
624
+ MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
625
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
626
+ d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
627
+ b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
628
+ EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
629
+ cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
630
+ MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
631
+ JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
632
+ mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
633
+ wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
634
+ VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
635
+ AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
636
+ AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
637
+ BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
638
+ pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
639
+ dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
640
+ fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
641
+ NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
642
+ H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
643
+ +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
644
+ -----END CERTIFICATE-----
645
+ -----BEGIN CERTIFICATE-----
646
+ MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
647
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
648
+ d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
649
+ QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
650
+ MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
651
+ b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
652
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
653
+ CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
654
+ nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
655
+ 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
656
+ T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
657
+ gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
658
+ BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
659
+ TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
660
+ DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
661
+ hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
662
+ 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
663
+ PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
664
+ YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
665
+ CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
666
+ -----END CERTIFICATE-----
667
+ -----BEGIN CERTIFICATE-----
668
+ MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
669
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
670
+ d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
671
+ ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
672
+ MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
673
+ LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
674
+ RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
675
+ +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
676
+ PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
677
+ xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
678
+ Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
679
+ hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
680
+ EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
681
+ MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
682
+ FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
683
+ nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
684
+ eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
685
+ hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
686
+ Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
687
+ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
688
+ +OkuE6N36B9K
689
+ -----END CERTIFICATE-----
690
+ -----BEGIN CERTIFICATE-----
691
+ MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV
692
+ UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL
693
+ EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ
694
+ BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x
695
+ ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg
696
+ bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ
697
+ j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV
698
+ Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG
699
+ SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx
700
+ JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI
701
+ RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw
702
+ MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5
703
+ fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i
704
+ +DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG
705
+ SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN
706
+ QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+
707
+ gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl
708
+ -----END CERTIFICATE-----
709
+ -----BEGIN CERTIFICATE-----
710
+ MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV
711
+ UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL
712
+ EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ
713
+ BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x
714
+ ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/
715
+ k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso
716
+ LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o
717
+ TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG
718
+ SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx
719
+ JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI
720
+ RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3
721
+ MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C
722
+ TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5
723
+ WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG
724
+ SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR
725
+ xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL
726
+ B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID
727
+ -----END CERTIFICATE-----
728
+ -----BEGIN CERTIFICATE-----
729
+ MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb
730
+ MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx
731
+ ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w
732
+ MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD
733
+ VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx
734
+ FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
735
+ MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu
736
+ ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7
737
+ gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH
738
+ fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a
739
+ ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT
740
+ ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF
741
+ MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk
742
+ c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto
743
+ dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt
744
+ aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI
745
+ hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk
746
+ QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/
747
+ h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
748
+ nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR
749
+ rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2
750
+ 9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis=
751
+ -----END CERTIFICATE-----
752
+ -----BEGIN CERTIFICATE-----
753
+ MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
754
+ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
755
+ DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
756
+ PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
757
+ Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
758
+ AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
759
+ rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
760
+ OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
761
+ xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
762
+ 7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
763
+ aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
764
+ HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
765
+ SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
766
+ ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
767
+ AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
768
+ R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
769
+ JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
770
+ Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
771
+ -----END CERTIFICATE-----
772
+ -----BEGIN CERTIFICATE-----
773
+ MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
774
+ RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
775
+ bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
776
+ IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
777
+ ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy
778
+ MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
779
+ LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
780
+ YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
781
+ A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
782
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
783
+ K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
784
+ sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
785
+ MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
786
+ XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
787
+ HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
788
+ 4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA
789
+ vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G
790
+ CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA
791
+ WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo
792
+ oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ
793
+ h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18
794
+ f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN
795
+ B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy
796
+ vUxFnmG6v4SBkgPR0ml8xQ==
797
+ -----END CERTIFICATE-----
798
+ -----BEGIN CERTIFICATE-----
799
+ MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
800
+ VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
801
+ ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
802
+ KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
803
+ ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
804
+ MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
805
+ ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
806
+ b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
807
+ bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
808
+ U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
809
+ A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
810
+ I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
811
+ wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
812
+ AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
813
+ oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
814
+ BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
815
+ dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
816
+ MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
817
+ b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
818
+ dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
819
+ MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
820
+ E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
821
+ MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
822
+ hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
823
+ 95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
824
+ 2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
825
+ -----END CERTIFICATE-----
826
+ -----BEGIN CERTIFICATE-----
827
+ MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
828
+ VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
829
+ Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
830
+ KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
831
+ cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
832
+ NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
833
+ NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
834
+ ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
835
+ BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
836
+ KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
837
+ Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
838
+ 4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
839
+ KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
840
+ rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
841
+ 94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
842
+ sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
843
+ gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
844
+ kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
845
+ vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
846
+ A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
847
+ O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
848
+ AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
849
+ 9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
850
+ eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
851
+ 0vdXcDazv/wor3ElhVsT/h5/WrQ8
852
+ -----END CERTIFICATE-----
853
+ -----BEGIN CERTIFICATE-----
854
+ MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
855
+ UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
856
+ dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
857
+ MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
858
+ dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
859
+ AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
860
+ BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
861
+ cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
862
+ AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
863
+ MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
864
+ aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
865
+ ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
866
+ IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
867
+ MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
868
+ A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
869
+ 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
870
+ 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
871
+ -----END CERTIFICATE-----
872
+ -----BEGIN CERTIFICATE-----
873
+ MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
874
+ MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
875
+ ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
876
+ MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
877
+ LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
878
+ KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
879
+ RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
880
+ WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
881
+ Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
882
+ AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
883
+ eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
884
+ zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
885
+ WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
886
+ /Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
887
+ -----END CERTIFICATE-----
888
+ -----BEGIN CERTIFICATE-----
889
+ MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
890
+ UzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2Vj
891
+ dXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0
892
+ NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYD
893
+ VQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0B
894
+ AQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0G
895
+ vxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/
896
+ BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0C
897
+ AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEX
898
+ MBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJl
899
+ IGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkw
900
+ NjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBq
901
+ y/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQF
902
+ MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
903
+ A4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
904
+ 0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1
905
+ E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
906
+ -----END CERTIFICATE-----
907
+ -----BEGIN CERTIFICATE-----
908
+ MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc
909
+ MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT
910
+ ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw
911
+ MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj
912
+ dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l
913
+ c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC
914
+ UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc
915
+ 58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/
916
+ o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH
917
+ MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr
918
+ aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA
919
+ A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA
920
+ Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv
921
+ 8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
922
+ -----END CERTIFICATE-----
923
+ -----BEGIN CERTIFICATE-----
924
+ MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMx
925
+ IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1
926
+ dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
927
+ MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w
928
+ HhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTELMAkGA1UEBhMCRVMx
929
+ IjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1
930
+ dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2
931
+ MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20w
932
+ ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5u
933
+ Cp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5Vj1H5WuretXDE7aTt/6MNbg9kUDGvASdY
934
+ rv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJHlShbz++AbOCQl4oBPB3z
935
+ hxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf3H5idPay
936
+ BQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcL
937
+ iam8NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcb
938
+ AgMBAAGjgZ8wgZwwKgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lv
939
+ bmFsLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0
940
+ MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
941
+ FgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQADggEBAEdz/o0n
942
+ VPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq
943
+ u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36m
944
+ hoEyIwOdyPdfwUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzfl
945
+ ZKG+TQyTmAyX9odtsz/ny4Cm7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBp
946
+ QWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YGVM+h4k0460tQtcsm9MracEpqoeJ5
947
+ quGnM/b9Sh/22WA=
948
+ -----END CERTIFICATE-----
949
+ -----BEGIN CERTIFICATE-----
950
+ MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
951
+ MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
952
+ IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
953
+ EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
954
+ R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
955
+ PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
956
+ Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
957
+ TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
958
+ 5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
959
+ S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
960
+ 2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
961
+ FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
962
+ EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
963
+ EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
964
+ /NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
965
+ A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
966
+ abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
967
+ I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
968
+ 4iIprn2DQKi6bA==
969
+ -----END CERTIFICATE-----
970
+ -----BEGIN CERTIFICATE-----
971
+ MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
972
+ MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
973
+ YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
974
+ EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
975
+ R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
976
+ 9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
977
+ fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
978
+ iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
979
+ 1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
980
+ bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
981
+ MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
982
+ ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
983
+ uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
984
+ Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
985
+ tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
986
+ PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
987
+ hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
988
+ 5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
989
+ -----END CERTIFICATE-----
990
+ -----BEGIN CERTIFICATE-----
991
+ MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
992
+ MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
993
+ R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
994
+ MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
995
+ Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
996
+ ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
997
+ AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
998
+ AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
999
+ ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
1000
+ 7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
1001
+ kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
1002
+ mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
1003
+ A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
1004
+ KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
1005
+ 6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
1006
+ 4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
1007
+ oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
1008
+ UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
1009
+ AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
1010
+ -----END CERTIFICATE-----
1011
+ -----BEGIN CERTIFICATE-----
1012
+ MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
1013
+ MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
1014
+ c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
1015
+ VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
1016
+ c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
1017
+ AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
1018
+ WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
1019
+ FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
1020
+ XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
1021
+ se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
1022
+ KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
1023
+ IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
1024
+ y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
1025
+ hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
1026
+ QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
1027
+ Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
1028
+ HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
1029
+ HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
1030
+ KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
1031
+ dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
1032
+ L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
1033
+ Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
1034
+ ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
1035
+ T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
1036
+ GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
1037
+ 1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
1038
+ OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
1039
+ 6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
1040
+ QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
1041
+ -----END CERTIFICATE-----
1042
+ -----BEGIN CERTIFICATE-----
1043
+ MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
1044
+ MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
1045
+ c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
1046
+ BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
1047
+ IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
1048
+ VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
1049
+ cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
1050
+ QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
1051
+ F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
1052
+ c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
1053
+ mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
1054
+ VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
1055
+ teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
1056
+ f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
1057
+ Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
1058
+ nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
1059
+ /wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
1060
+ MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
1061
+ 9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
1062
+ aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
1063
+ IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
1064
+ ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
1065
+ uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
1066
+ Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
1067
+ QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
1068
+ koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
1069
+ ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
1070
+ DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
1071
+ bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
1072
+ -----END CERTIFICATE-----
1073
+ -----BEGIN CERTIFICATE-----
1074
+ MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
1075
+ A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
1076
+ b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
1077
+ MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
1078
+ YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
1079
+ aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
1080
+ jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
1081
+ xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1082
+ 1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
1083
+ snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
1084
+ U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
1085
+ 9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
1086
+ BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
1087
+ AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
1088
+ yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
1089
+ 38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
1090
+ AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
1091
+ DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
1092
+ HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
1093
+ -----END CERTIFICATE-----
1094
+ -----BEGIN CERTIFICATE-----
1095
+ MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
1096
+ A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
1097
+ Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
1098
+ MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
1099
+ A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
1100
+ hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
1101
+ v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
1102
+ eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
1103
+ tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
1104
+ C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
1105
+ zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
1106
+ mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
1107
+ V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
1108
+ bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
1109
+ 3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
1110
+ J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
1111
+ 291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
1112
+ ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
1113
+ AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
1114
+ TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
1115
+ -----END CERTIFICATE-----
1116
+ -----BEGIN CERTIFICATE-----
1117
+ MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
1118
+ MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
1119
+ YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
1120
+ MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
1121
+ ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
1122
+ MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
1123
+ ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
1124
+ PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
1125
+ wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
1126
+ EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
1127
+ avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
1128
+ YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
1129
+ sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
1130
+ /t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
1131
+ IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
1132
+ YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
1133
+ ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
1134
+ OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
1135
+ TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
1136
+ HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
1137
+ dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
1138
+ ReYNnyicsbkqWletNw+vHX/bvZ8=
1139
+ -----END CERTIFICATE-----
1140
+ -----BEGIN CERTIFICATE-----
1141
+ MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
1142
+ VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
1143
+ bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
1144
+ b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
1145
+ UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
1146
+ cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
1147
+ b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
1148
+ iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
1149
+ r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
1150
+ 04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
1151
+ GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
1152
+ 3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
1153
+ lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
1154
+ -----END CERTIFICATE-----
1155
+ -----BEGIN CERTIFICATE-----
1156
+ MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx
1157
+ ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
1158
+ b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD
1159
+ EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05
1160
+ OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G
1161
+ A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
1162
+ Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l
1163
+ dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG
1164
+ SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK
1165
+ gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX
1166
+ iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc
1167
+ Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E
1168
+ BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G
1169
+ SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu
1170
+ b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh
1171
+ bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv
1172
+ Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln
1173
+ aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0
1174
+ IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
1175
+ c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph
1176
+ biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo
1177
+ ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP
1178
+ UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj
1179
+ YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo
1180
+ dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA
1181
+ bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06
1182
+ sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa
1183
+ n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS
1184
+ NitjrFgBazMpUIaD8QFI
1185
+ -----END CERTIFICATE-----
1186
+ -----BEGIN CERTIFICATE-----
1187
+ MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx
1188
+ ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
1189
+ b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD
1190
+ EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X
1191
+ DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw
1192
+ DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u
1193
+ c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr
1194
+ TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN
1195
+ BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA
1196
+ OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC
1197
+ 2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW
1198
+ RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P
1199
+ AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW
1200
+ ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0
1201
+ YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz
1202
+ b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO
1203
+ ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB
1204
+ IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs
1205
+ b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
1206
+ ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s
1207
+ YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg
1208
+ a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g
1209
+ SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0
1210
+ aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg
1211
+ YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg
1212
+ Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY
1213
+ ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g
1214
+ pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4
1215
+ Fp1hBWeAyNDYpQcCNJgEjTME1A==
1216
+ -----END CERTIFICATE-----
1217
+ -----BEGIN CERTIFICATE-----
1218
+ MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV
1219
+ MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe
1220
+ TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0
1221
+ dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB
1222
+ KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0
1223
+ N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC
1224
+ dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu
1225
+ MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL
1226
+ b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG
1227
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD
1228
+ zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi
1229
+ 3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8
1230
+ WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY
1231
+ Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi
1232
+ NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC
1233
+ ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4
1234
+ QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0
1235
+ YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz
1236
+ aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
1237
+ IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm
1238
+ ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg
1239
+ ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs
1240
+ amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv
1241
+ IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3
1242
+ Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6
1243
+ ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1
1244
+ YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg
1245
+ dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs
1246
+ b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G
1247
+ CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO
1248
+ xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP
1249
+ 0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ
1250
+ QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk
1251
+ f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK
1252
+ 8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI
1253
+ -----END CERTIFICATE-----
1254
+ -----BEGIN CERTIFICATE-----
1255
+ MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx
1256
+ ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
1257
+ b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD
1258
+ EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz
1259
+ aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w
1260
+ MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G
1261
+ A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
1262
+ Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l
1263
+ dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh
1264
+ bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq
1265
+ hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq
1266
+ eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe
1267
+ r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5
1268
+ 3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd
1269
+ vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l
1270
+ mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC
1271
+ wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg
1272
+ hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0
1273
+ TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh
1274
+ biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg
1275
+ ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg
1276
+ dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6
1277
+ b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl
1278
+ c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0
1279
+ ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3
1280
+ dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu
1281
+ ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh
1282
+ bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo
1283
+ ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3
1284
+ Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u
1285
+ ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA
1286
+ A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ
1287
+ MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+
1288
+ NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR
1289
+ VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY
1290
+ 83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3
1291
+ macqaJVmlaut74nLYKkGEsaUR+ko
1292
+ -----END CERTIFICATE-----
1293
+ -----BEGIN CERTIFICATE-----
1294
+ MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
1295
+ MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
1296
+ MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
1297
+ dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
1298
+ UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
1299
+ ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
1300
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
1301
+ c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
1302
+ OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
1303
+ mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
1304
+ BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
1305
+ qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
1306
+ gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
1307
+ BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
1308
+ bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
1309
+ dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
1310
+ 6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
1311
+ h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
1312
+ /nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
1313
+ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
1314
+ pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
1315
+ -----END CERTIFICATE-----
1316
+ -----BEGIN CERTIFICATE-----
1317
+ MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
1318
+ GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
1319
+ b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
1320
+ BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
1321
+ YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
1322
+ GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
1323
+ Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
1324
+ WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
1325
+ rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
1326
+ +ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
1327
+ ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
1328
+ Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
1329
+ PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
1330
+ /zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
1331
+ oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
1332
+ yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
1333
+ EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
1334
+ A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
1335
+ MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
1336
+ ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
1337
+ BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
1338
+ g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
1339
+ fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
1340
+ WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
1341
+ B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
1342
+ hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
1343
+ TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
1344
+ mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
1345
+ ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
1346
+ 4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
1347
+ 8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
1348
+ -----END CERTIFICATE-----
1349
+ -----BEGIN CERTIFICATE-----
1350
+ MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
1351
+ GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
1352
+ b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
1353
+ BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
1354
+ YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
1355
+ V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
1356
+ 4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
1357
+ H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
1358
+ 8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
1359
+ vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
1360
+ mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
1361
+ btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
1362
+ T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
1363
+ WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
1364
+ c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
1365
+ 4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
1366
+ VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
1367
+ CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
1368
+ aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
1369
+ aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
1370
+ dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
1371
+ czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
1372
+ A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
1373
+ TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
1374
+ Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
1375
+ 7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
1376
+ d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
1377
+ +LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
1378
+ 4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
1379
+ t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
1380
+ DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
1381
+ k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
1382
+ zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
1383
+ Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
1384
+ mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
1385
+ 4SVhM7JZG+Ju1zdXtg2pEto=
1386
+ -----END CERTIFICATE-----
1387
+ -----BEGIN CERTIFICATE-----
1388
+ MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
1389
+ TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
1390
+ aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
1391
+ aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
1392
+ MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
1393
+ IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
1394
+ dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
1395
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
1396
+ li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
1397
+ rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
1398
+ WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
1399
+ F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
1400
+ xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
1401
+ Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
1402
+ dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
1403
+ ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
1404
+ IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
1405
+ c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
1406
+ ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
1407
+ Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
1408
+ KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
1409
+ KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
1410
+ y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
1411
+ dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
1412
+ VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
1413
+ MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
1414
+ fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
1415
+ 7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
1416
+ cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
1417
+ mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
1418
+ xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
1419
+ SnQ2+Q==
1420
+ -----END CERTIFICATE-----
1421
+ -----BEGIN CERTIFICATE-----
1422
+ MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
1423
+ IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
1424
+ BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
1425
+ aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
1426
+ 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy
1427
+ NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
1428
+ azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
1429
+ YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
1430
+ Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
1431
+ cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD
1432
+ cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs
1433
+ 2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY
1434
+ JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE
1435
+ Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ
1436
+ n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
1437
+ PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
1438
+ -----END CERTIFICATE-----
1439
+ -----BEGIN CERTIFICATE-----
1440
+ MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6
1441
+ MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp
1442
+ dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX
1443
+ BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy
1444
+ MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp
1445
+ eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg
1446
+ /9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl
1447
+ wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh
1448
+ AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2
1449
+ PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu
1450
+ AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
1451
+ BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR
1452
+ MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc
1453
+ HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/
1454
+ Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
1455
+ f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO
1456
+ rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch
1457
+ 6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3
1458
+ 7CAFYd4=
1459
+ -----END CERTIFICATE-----
1460
+ -----BEGIN CERTIFICATE-----
1461
+ MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
1462
+ MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
1463
+ GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
1464
+ MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
1465
+ Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
1466
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
1467
+ iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
1468
+ /FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
1469
+ jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
1470
+ HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
1471
+ sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
1472
+ gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
1473
+ MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
1474
+ KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
1475
+ AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
1476
+ URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
1477
+ H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
1478
+ I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
1479
+ iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
1480
+ f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
1481
+ -----END CERTIFICATE-----
1482
+ -----BEGIN CERTIFICATE-----
1483
+ MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
1484
+ MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
1485
+ FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
1486
+ MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
1487
+ cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
1488
+ AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
1489
+ Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
1490
+ 0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
1491
+ wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
1492
+ 7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
1493
+ 8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
1494
+ BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
1495
+ /zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
1496
+ JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
1497
+ NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
1498
+ 6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
1499
+ 3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
1500
+ D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
1501
+ CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
1502
+ 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
1503
+ -----END CERTIFICATE-----
1504
+ -----BEGIN CERTIFICATE-----
1505
+ MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
1506
+ MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
1507
+ dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
1508
+ WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
1509
+ VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
1510
+ DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
1511
+ 9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
1512
+ DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
1513
+ Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
1514
+ QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
1515
+ xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
1516
+ A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
1517
+ AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
1518
+ kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
1519
+ Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
1520
+ Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
1521
+ JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
1522
+ RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
1523
+ -----END CERTIFICATE-----
1524
+ -----BEGIN CERTIFICATE-----
1525
+ MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
1526
+ MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx
1527
+ MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV
1528
+ BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI
1529
+ hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG
1530
+ 29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk
1531
+ oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk
1532
+ 3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL
1533
+ qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN
1534
+ nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw
1535
+ DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG
1536
+ MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX
1537
+ ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H
1538
+ DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO
1539
+ TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv
1540
+ kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w
1541
+ zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa
1542
+ -----END CERTIFICATE-----
1543
+ -----BEGIN CERTIFICATE-----
1544
+ MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
1545
+ MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
1546
+ MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
1547
+ BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
1548
+ hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
1549
+ Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
1550
+ 5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
1551
+ 3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
1552
+ vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
1553
+ 8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
1554
+ DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
1555
+ MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
1556
+ zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
1557
+ 3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
1558
+ FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
1559
+ Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
1560
+ ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
1561
+ -----END CERTIFICATE-----
1562
+ -----BEGIN CERTIFICATE-----
1563
+ MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO
1564
+ TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh
1565
+ dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy
1566
+ MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk
1567
+ ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB
1568
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn
1569
+ ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71
1570
+ 9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO
1571
+ hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U
1572
+ tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o
1573
+ BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh
1574
+ SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww
1575
+ OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv
1576
+ cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA
1577
+ 7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k
1578
+ /rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm
1579
+ eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6
1580
+ u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy
1581
+ 7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
1582
+ iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
1583
+ -----END CERTIFICATE-----
1584
+ -----BEGIN CERTIFICATE-----
1585
+ MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
1586
+ MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
1587
+ U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
1588
+ NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
1589
+ ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
1590
+ ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
1591
+ DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
1592
+ 8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
1593
+ +lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
1594
+ X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
1595
+ K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
1596
+ 1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
1597
+ A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
1598
+ zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
1599
+ YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
1600
+ bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
1601
+ DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
1602
+ L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
1603
+ eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
1604
+ xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
1605
+ VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
1606
+ WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
1607
+ -----END CERTIFICATE-----
1608
+ -----BEGIN CERTIFICATE-----
1609
+ MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
1610
+ MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
1611
+ Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
1612
+ dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
1613
+ MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
1614
+ U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
1615
+ cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
1616
+ A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
1617
+ pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
1618
+ OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
1619
+ Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
1620
+ Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
1621
+ HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
1622
+ Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
1623
+ +2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
1624
+ Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
1625
+ Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
1626
+ 26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
1627
+ AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
1628
+ FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
1629
+ ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
1630
+ LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
1631
+ BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
1632
+ Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
1633
+ dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
1634
+ cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
1635
+ YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
1636
+ dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
1637
+ bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
1638
+ YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
1639
+ TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
1640
+ 9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
1641
+ jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
1642
+ FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
1643
+ ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
1644
+ ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
1645
+ EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
1646
+ L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
1647
+ yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
1648
+ O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
1649
+ um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
1650
+ NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
1651
+ -----END CERTIFICATE-----
1652
+ -----BEGIN CERTIFICATE-----
1653
+ MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk
1654
+ MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
1655
+ YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
1656
+ Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT
1657
+ AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
1658
+ Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN
1659
+ BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9
1660
+ m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih
1661
+ FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/
1662
+ TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F
1663
+ EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco
1664
+ kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu
1665
+ HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF
1666
+ vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo
1667
+ 19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC
1668
+ L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW
1669
+ bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX
1670
+ JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
1671
+ FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
1672
+ BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc
1673
+ K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf
1674
+ ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik
1675
+ Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB
1676
+ sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e
1677
+ 3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR
1678
+ ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip
1679
+ mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH
1680
+ b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf
1681
+ rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms
1682
+ hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y
1683
+ zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6
1684
+ MBr1mmz0DlP5OlvRHA==
1685
+ -----END CERTIFICATE-----
1686
+ -----BEGIN CERTIFICATE-----
1687
+ MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
1688
+ BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
1689
+ biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
1690
+ MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
1691
+ d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
1692
+ CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
1693
+ 76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
1694
+ bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
1695
+ 6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
1696
+ emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
1697
+ MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
1698
+ MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
1699
+ MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
1700
+ FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
1701
+ aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
1702
+ gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
1703
+ qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
1704
+ lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
1705
+ 8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
1706
+ L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
1707
+ 45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
1708
+ UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
1709
+ O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
1710
+ bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
1711
+ GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
1712
+ 77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
1713
+ hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
1714
+ 92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
1715
+ Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
1716
+ ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
1717
+ Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
1718
+ -----END CERTIFICATE-----
1719
+ -----BEGIN CERTIFICATE-----
1720
+ MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
1721
+ BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu
1722
+ IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw
1723
+ WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD
1724
+ ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD
1725
+ ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y
1726
+ IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn
1727
+ IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+
1728
+ 6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob
1729
+ jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw
1730
+ izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl
1731
+ +zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY
1732
+ zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP
1733
+ pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF
1734
+ KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW
1735
+ ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB
1736
+ AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
1737
+ BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0
1738
+ ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW
1739
+ IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA
1740
+ A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0
1741
+ uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+
1742
+ FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7
1743
+ jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/
1744
+ u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D
1745
+ YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1
1746
+ puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa
1747
+ icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG
1748
+ DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x
1749
+ kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z
1750
+ Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g==
1751
+ -----END CERTIFICATE-----
1752
+ -----BEGIN CERTIFICATE-----
1753
+ MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
1754
+ BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
1755
+ IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
1756
+ RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
1757
+ U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
1758
+ MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
1759
+ Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
1760
+ YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
1761
+ nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
1762
+ 6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
1763
+ eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
1764
+ c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
1765
+ MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
1766
+ HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
1767
+ jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
1768
+ 5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
1769
+ rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
1770
+ F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
1771
+ wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
1772
+ cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
1773
+ AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
1774
+ WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
1775
+ xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
1776
+ 2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
1777
+ IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
1778
+ aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
1779
+ em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
1780
+ dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
1781
+ OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
1782
+ hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
1783
+ tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
1784
+ -----END CERTIFICATE-----
1785
+ -----BEGIN CERTIFICATE-----
1786
+ MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
1787
+ MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
1788
+ YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
1789
+ PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
1790
+ Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
1791
+ AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
1792
+ IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
1793
+ gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
1794
+ yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
1795
+ F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
1796
+ jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
1797
+ ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
1798
+ VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
1799
+ YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
1800
+ EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
1801
+ Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
1802
+ DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
1803
+ MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
1804
+ UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
1805
+ TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
1806
+ qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
1807
+ ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
1808
+ JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
1809
+ hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
1810
+ EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
1811
+ nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
1812
+ udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
1813
+ ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
1814
+ LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
1815
+ pYYsfPQS
1816
+ -----END CERTIFICATE-----
1817
+ -----BEGIN CERTIFICATE-----
1818
+ MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF
1819
+ MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU
1820
+ QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI
1821
+ MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENBMSkwJwYJKoZIhvcN
1822
+ AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla
1823
+ Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy
1824
+ ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y
1825
+ IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1
1826
+ c3RDZW50ZXIgQ2xhc3MgMiBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA
1827
+ dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0y
1828
+ AClxgwENv4wB3NrGrTmkqYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDw
1829
+ TFXlay3HhQswHJJOgtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8
1830
+ /vhYnvgpjbB7zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQF
1831
+ MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3
1832
+ LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
1833
+ CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+WLDO/
1834
+ jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xRT3h2oNms
1835
+ Gb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/AcASZ4smZHcFFk
1836
+ -----END CERTIFICATE-----
1837
+ -----BEGIN CERTIFICATE-----
1838
+ MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF
1839
+ MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU
1840
+ QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI
1841
+ MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENBMSkwJwYJKoZIhvcN
1842
+ AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla
1843
+ Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy
1844
+ ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y
1845
+ IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1
1846
+ c3RDZW50ZXIgQ2xhc3MgMyBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA
1847
+ dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUF
1848
+ Lg2N7KBAahwOJ6ZQkmtQGwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGw
1849
+ Dtf7pBc9r6tpepYnv68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDW
1850
+ w1Krj10nnGvAo+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQF
1851
+ MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3
1852
+ LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
1853
+ CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4iJIE
1854
+ Tb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yCGdHHsbHD
1855
+ 2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQSCdS7kjXvD9s0
1856
+ -----END CERTIFICATE-----
1857
+ -----BEGIN CERTIFICATE-----
1858
+ MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE
1859
+ SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg
1860
+ Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV
1861
+ BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl
1862
+ cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA
1863
+ vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu
1864
+ Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a
1865
+ 0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1
1866
+ 4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN
1867
+ eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD
1868
+ R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG
1869
+ A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu
1870
+ dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME
1871
+ Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3
1872
+ WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw
1873
+ HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ
1874
+ KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO
1875
+ Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX
1876
+ wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+
1877
+ 2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89
1878
+ 9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0
1879
+ jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38
1880
+ aQNiuJkFBT1reBK9sG9l
1881
+ -----END CERTIFICATE-----
1882
+ -----BEGIN CERTIFICATE-----
1883
+ MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJE
1884
+ SzEMMAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEw
1885
+ ODM5MzBaFw0zNzAyMTEwOTA5MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNU
1886
+ REMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
1887
+ MIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuHnEz9pPPEXyG9VhDr
1888
+ 2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0zY0s
1889
+ 2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItU
1890
+ GBxIYXvViGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKj
1891
+ dGqPqcNiKXEx5TukYBdedObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+r
1892
+ TpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/
1893
+ BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB5DCB4TCB3gYIKoFQgSkB
1894
+ AQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5kay9yZXBv
1895
+ c2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRl
1896
+ ciBmcmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEu
1897
+ MS4xLiBDZXJ0aWZpY2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIg
1898
+ T0lEIDEuMi4yMDguMTY5LjEuMS4xLjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1Ud
1899
+ HwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEMMAoGA1UEChMDVERDMRQwEgYD
1900
+ VQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYmaHR0cDovL2Ny
1901
+ bC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy
1902
+ MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZ
1903
+ J2cdUBVLc647+RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqG
1904
+ SIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACrom
1905
+ JkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4A9G28kNBKWKnctj7fAXmMXAnVBhO
1906
+ inxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYscA+UYyAFMP8uXBV2Y
1907
+ caaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9AOoB
1908
+ mbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQ
1909
+ YqbsFbS1AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9
1910
+ BKNDLdr8C2LqL19iUw==
1911
+ -----END CERTIFICATE-----
1912
+ -----BEGIN CERTIFICATE-----
1913
+ MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMCWkEx
1914
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
1915
+ VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
1916
+ ZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVt
1917
+ YWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUu
1918
+ Y29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgdExCzAJBgNVBAYT
1919
+ AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEa
1920
+ MBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRp
1921
+ b24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBG
1922
+ cmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhh
1923
+ d3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfY
1924
+ DFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5E
1925
+ rHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVq
1926
+ uzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
1927
+ BgkqhkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
1928
+ MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgCneSa
1929
+ /RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr5PjRznei
1930
+ gQ==
1931
+ -----END CERTIFICATE-----
1932
+ -----BEGIN CERTIFICATE-----
1933
+ MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
1934
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
1935
+ VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
1936
+ biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
1937
+ dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
1938
+ MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
1939
+ MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
1940
+ A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
1941
+ b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
1942
+ cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
1943
+ bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
1944
+ VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
1945
+ ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
1946
+ uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
1947
+ 9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
1948
+ hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
1949
+ pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
1950
+ -----END CERTIFICATE-----
1951
+ -----BEGIN CERTIFICATE-----
1952
+ MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
1953
+ qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
1954
+ Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
1955
+ MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
1956
+ BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
1957
+ NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
1958
+ LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
1959
+ A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
1960
+ IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
1961
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
1962
+ W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
1963
+ 3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
1964
+ 6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
1965
+ Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
1966
+ NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
1967
+ MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
1968
+ r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
1969
+ DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
1970
+ YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
1971
+ xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
1972
+ /qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
1973
+ LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
1974
+ jVaMaA==
1975
+ -----END CERTIFICATE-----
1976
+ -----BEGIN CERTIFICATE-----
1977
+ MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
1978
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
1979
+ VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
1980
+ biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
1981
+ MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
1982
+ MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
1983
+ DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
1984
+ dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
1985
+ cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
1986
+ DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
1987
+ gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
1988
+ yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
1989
+ L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
1990
+ EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
1991
+ 7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
1992
+ QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
1993
+ qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
1994
+ -----END CERTIFICATE-----
1995
+ -----BEGIN CERTIFICATE-----
1996
+ MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCWkEx
1997
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
1998
+ BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAd
1999
+ BgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcwMTAxMDAwMDAwWhcN
2000
+ MjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4g
2001
+ Q2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsG
2002
+ A1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1l
2003
+ c3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYrWHhhRYZT
2004
+ 6jR7UZztsOYuGA7+4F+oJ9O0yeB8WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQa
2005
+ Wt9MevPZQx08EHp5JduQ/vBR5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL
2006
+ 8vg7ij5FrHGSALSQQZj7X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMB
2007
+ Af8wDQYJKoZIhvcNAQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC
2008
+ 9RAIDb/LogWK0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQ
2009
+ pgCed/r8zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZ
2010
+ CayJSdM=
2011
+ -----END CERTIFICATE-----
2012
+ -----BEGIN CERTIFICATE-----
2013
+ MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc
2014
+ UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
2015
+ c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg
2016
+ MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8
2017
+ dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz
2018
+ MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy
2019
+ dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD
2020
+ VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg
2021
+ xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu
2022
+ xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7
2023
+ XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k
2024
+ heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J
2025
+ YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C
2026
+ urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1
2027
+ JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51
2028
+ b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV
2029
+ 9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7
2030
+ kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh
2031
+ fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
2032
+ B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA
2033
+ aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS
2034
+ RGQDJereW26fyfJOrN3H
2035
+ -----END CERTIFICATE-----
2036
+ -----BEGIN CERTIFICATE-----
2037
+ MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc
2038
+ UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
2039
+ c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS
2040
+ S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
2041
+ SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3
2042
+ WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv
2043
+ bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU
2044
+ UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw
2045
+ bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe
2046
+ LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
2047
+ AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef
2048
+ J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh
2049
+ R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ
2050
+ Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX
2051
+ JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p
2052
+ zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S
2053
+ Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
2054
+ KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq
2055
+ ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
2056
+ Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz
2057
+ gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH
2058
+ uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS
2059
+ y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI=
2060
+ -----END CERTIFICATE-----
2061
+ -----BEGIN CERTIFICATE-----
2062
+ MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
2063
+ kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
2064
+ Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
2065
+ dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
2066
+ IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
2067
+ EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
2068
+ VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
2069
+ dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
2070
+ BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
2071
+ E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
2072
+ D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
2073
+ 4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
2074
+ lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
2075
+ bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
2076
+ o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
2077
+ MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
2078
+ LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
2079
+ BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
2080
+ AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
2081
+ Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
2082
+ j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
2083
+ KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
2084
+ 2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
2085
+ mfnGV/TJVTl4uix5yaaIK/QI
2086
+ -----END CERTIFICATE-----
2087
+ -----BEGIN CERTIFICATE-----
2088
+ MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB
2089
+ rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
2090
+ Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
2091
+ dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt
2092
+ Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa
2093
+ Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV
2094
+ BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l
2095
+ dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE
2096
+ AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls
2097
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B
2098
+ YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9
2099
+ hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l
2100
+ L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm
2101
+ SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM
2102
+ 1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws
2103
+ 6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
2104
+ DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw
2105
+ Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50
2106
+ aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
2107
+ AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u
2108
+ 7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0
2109
+ xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ
2110
+ rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim
2111
+ eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk
2112
+ USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ=
2113
+ -----END CERTIFICATE-----
2114
+ -----BEGIN CERTIFICATE-----
2115
+ MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
2116
+ lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
2117
+ Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
2118
+ dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
2119
+ SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
2120
+ A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
2121
+ MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
2122
+ d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
2123
+ cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
2124
+ 0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
2125
+ M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
2126
+ MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb