Paid Memberships Pro - Version 2.1

Version Description

  • 2019-09-13 =
  • FEATURE: Updated Stripe integration to support Stripe v3, Stripe Elements, and their Secure Customer Authorization process.
  • FEATURE: Updated how we store prices to support up to 8 decimals (e.g. for Bitcoin gateway implementations).
  • ENHANCEMENT: Improved error messaging on the update billing page when a gateway doesn't support it or the user's current membership doesn't have a subscription.
  • ENHANCEMENT: Added a pmpro_is_checkout() function that will return true if on the PMPro checkout page or a page with the PMPro checkout shortcode or block.
  • ENHANCEMENT: Showing a warning message when a user about to be deleted has a membership so admins know that existing subscriptions will be deleted at the gateway.
  • ENHANCEMENT: Added a pmpro_braintree_plan_id filter in case you need to adjust plan IDs. This is useful if you have several sites running on the same Braintree account.
  • ENHANCEMENT: Added a pmpro_num_expiration_years filter to adjust the number of years to include in the dropdown to set the year membership will expire.
  • ENHANCEMENT: Tweaked the UI of the orders list and members list in the dashboard.
  • ENHANCEMENT: Added pmpro_membership_levels_table_extra_cols_header and pmpro_membership_levels_table_extra_cols_body hooks to add columns to the members list.
  • ENHANCEMENT: Showing notices to admins when categories are hidden from them on the frontend of the site.
  • ENHANCEMENT: Added a pmpro_url filter to filter URLs returned from that function.
  • ENHANCEMENT: Adding a pmpro_checkout_gateway-stripe or pmpro_checkout_gateway-paypal/etc CSS class to the wrapping div for payment fields to aid in styling.
  • ENHANCEMENT: Using the site's date format option when printing orders.
  • BUG FIX/ENHANCEMENT: If a site has no paying levels, the test gateway will show as the "Default" gateway and we will no longer show a message about requiring gateway setup on the checkout page.
  • BUG FIX/ENHANCEMENT: Updated Russian Ruble definition to have 0 decimals and use   as the thousands separator. (Thanks, Airat Halitov)
  • BUG FIX/ENHANCEMENT: Using add_query_arg when generating IPN URLs to avoid issues on sites that aren't using pretty permalinks or have moved their admin directory.
  • BUG FIX/ENHANCEMENT: Fixed issue on advanced settings page where clicking on labels didn't check the corresponding check boxes.
  • BUG FIX/ENHANCEMENT: Updated our pmpro_generateUsername() function to be a bit smarter.
  • BUG FIX/ENHANCEMENT: Now using wp_generate_password() when choosing a random password for a user (e.g. when using the Sign Up Shortcode add on or the $skip_account_fields global).
  • BUG FIX/ENHANCEMENT: Setting autocomplete to false on the "fullname" honeypot field. This will prevent user's with certain autocomplete tools from accidentally filling it out.
  • BUG FIX/EHNANCEMENT: Now sending name and email fields to PayPay (using Website Payments Pro) even if no address was captured.
  • BUG FIX/ENHANCEMENT: More specific CSS selectors for checkout form elements to make sure errors are highlighted/etc with different themes.
  • BUG FIX: Fixed issue where the first 2000 or so orders might be skipped when exporting orders on large sites.
  • BUG FIX: Fixed issue with setting custom trials on discount codes.
  • BUG FIX: Fixed issue in the SQL query in the pmpro_calculateInitialPaymentRevenue() function. This function is deprecated, but still used by some custom code.
  • BUG FIX: Fixed issue where default templates would fail to load if a custom template was specified.
  • BUG FIX: Fixed fatal errors that could happen when using the PMPro REST API endpoints.
  • BUG FIX: Fixed bug where the invoices page would sometimes show data for the current (admin) users instead of the user the invoice was for.
  • BUG FIX: Fixed bug where the membership stats graphs would sometimes show up blank.
  • BUG FIX: Now falling back to using readfile() if fpassthru() doesn't existing.
  • BUG FIX: Fixed issue where the from name and email were not set properly if the Only Filter PMPro Emails setting was checked. (Thanks, mjulian87 on GitHub)
  • BUG FIX: Fixed several error handling issues with the Cybersource gateway integration.
  • REFACTOR: Moved JavaScript out of pages/checkout.php and other places into files in the /js/ folder. This will avoid issues where other JS at checkout breaks PMPro checkout and will improve compatibility with tools that optimize JS.
  • REFACTOR: Added unit testing and a started on coverage of some functions in includes/functions.php. (Thanks, Mike Auteri)
  • REFACTOR: The JS function askfirst is now prefixed as pmpro_askfirst.
Download this release

Release Info

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

Code changes from version 2.0.7 to 2.1

Files changed (173) hide show
  1. CHANGELOG.txt +65 -2
  2. adminpages/admin_header.php +1 -1
  3. adminpages/discountcodes.php +28 -21
  4. adminpages/functions.php +66 -0
  5. adminpages/membershiplevels.php +38 -6
  6. adminpages/memberslist-csv.php +13 -8
  7. adminpages/orders-csv.php +14 -9
  8. adminpages/orders.php +43 -93
  9. adminpages/paymentsettings.php +17 -0
  10. adminpages/reports/memberships.php +2 -2
  11. adminpages/templates/orders-print.php +1 -1
  12. bin/install-wp-tests.sh +152 -0
  13. classes/class.pmproemail.php +104 -0
  14. classes/gateways/class.pmprogateway_authorizenet.php +1 -1
  15. classes/gateways/class.pmprogateway_braintree.php +50 -37
  16. classes/gateways/class.pmprogateway_cybersource.php +215 -165
  17. classes/gateways/class.pmprogateway_payflowpro.php +5 -5
  18. classes/gateways/class.pmprogateway_paypal.php +105 -55
  19. classes/gateways/class.pmprogateway_paypalexpress.php +28 -45
  20. classes/gateways/class.pmprogateway_paypalstandard.php +2 -2
  21. classes/gateways/class.pmprogateway_stripe.php +1422 -890
  22. css/admin.css +1 -1
  23. css/frontend.css +82 -8
  24. email/payment_action.html +4 -0
  25. email/payment_action_admin.html +7 -0
  26. includes/cleanup.php +31 -1
  27. includes/currencies.php +3 -0
  28. includes/filters.php +1 -1
  29. includes/functions.php +299 -36
  30. includes/init.php +0 -71
  31. includes/lib/Stripe/LICENSE +1 -1
  32. includes/lib/Stripe/README.md +103 -10
  33. includes/lib/Stripe/VERSION +1 -1
  34. includes/lib/Stripe/data/ca-certificates.crt +421 -694
  35. includes/lib/Stripe/init.php +54 -3
  36. includes/lib/Stripe/lib/Account.php +297 -46
  37. includes/lib/Stripe/lib/AccountLink.php +21 -0
  38. includes/lib/Stripe/lib/AlipayAccount.php +60 -1
  39. includes/lib/Stripe/lib/ApiOperations/All.php +34 -0
  40. includes/lib/Stripe/lib/ApiOperations/Create.php +28 -0
  41. includes/lib/Stripe/lib/ApiOperations/Delete.php +27 -0
  42. includes/lib/Stripe/lib/ApiOperations/NestedResource.php +118 -0
  43. includes/lib/Stripe/lib/ApiOperations/Request.php +61 -0
  44. includes/lib/Stripe/lib/ApiOperations/Retrieve.php +27 -0
  45. includes/lib/Stripe/lib/ApiOperations/Update.php +46 -0
  46. includes/lib/Stripe/lib/ApiRequestor.php +198 -16
  47. includes/lib/Stripe/lib/ApiResource.php +40 -130
  48. includes/lib/Stripe/lib/ApplePayDomain.php +8 -46
  49. includes/lib/Stripe/lib/ApplicationFee.php +51 -23
  50. includes/lib/Stripe/lib/ApplicationFeeRefund.php +16 -0
  51. includes/lib/Stripe/lib/Balance.php +7 -3
  52. includes/lib/Stripe/lib/BalanceTransaction.php +41 -24
  53. includes/lib/Stripe/lib/BankAccount.php +85 -1
  54. includes/lib/Stripe/lib/BitcoinReceiver.php +20 -69
  55. includes/lib/Stripe/lib/BitcoinTransaction.php +1 -0
  56. includes/lib/Stripe/lib/Capability.php +83 -0
  57. includes/lib/Stripe/lib/Card.php +118 -1
  58. includes/lib/Stripe/lib/Charge.php +86 -64
  59. includes/lib/Stripe/lib/Checkout/Session.php +40 -0
  60. includes/lib/Stripe/lib/Collection.php +27 -6
  61. includes/lib/Stripe/lib/CountrySpec.php +12 -32
  62. includes/lib/Stripe/lib/Coupon.php +22 -64
  63. includes/lib/Stripe/lib/CreditNote.php +74 -0
  64. includes/lib/Stripe/lib/Customer.php +204 -75
  65. includes/lib/Stripe/lib/CustomerBalanceTransaction.php +88 -0
  66. includes/lib/Stripe/lib/Discount.php +21 -0
  67. includes/lib/Stripe/lib/Dispute.php +33 -39
  68. includes/lib/Stripe/lib/EphemeralKey.php +6 -20
  69. includes/lib/Stripe/lib/Error/Base.php +9 -0
  70. includes/lib/Stripe/lib/Error/Card.php +6 -7
  71. includes/lib/Stripe/lib/Error/Idempotency.php +7 -0
  72. includes/lib/Stripe/lib/Error/OAuth/OAuthBase.php +2 -2
  73. includes/lib/Stripe/lib/Event.php +148 -24
  74. includes/lib/Stripe/lib/ExchangeRate.php +17 -0
  75. includes/lib/Stripe/lib/File.php +62 -0
  76. includes/lib/Stripe/lib/FileLink.php +29 -0
  77. includes/lib/Stripe/lib/FileUpload.php +2 -58
  78. includes/lib/Stripe/lib/HttpClient/ClientInterface.php +5 -2
  79. includes/lib/Stripe/lib/HttpClient/CurlClient.php +241 -39
  80. includes/lib/Stripe/lib/Invoice.php +137 -27
  81. includes/lib/Stripe/lib/InvoiceItem.php +26 -64
  82. includes/lib/Stripe/lib/InvoiceLineItem.php +32 -0
  83. includes/lib/Stripe/lib/IssuerFraudRecord.php +25 -0
  84. includes/lib/Stripe/lib/Issuing/Authorization.php +68 -0
  85. includes/lib/Stripe/lib/Issuing/Card.php +51 -0
  86. includes/lib/Stripe/lib/Issuing/CardDetails.php +21 -0
  87. includes/lib/Stripe/lib/Issuing/Cardholder.php +30 -0
  88. includes/lib/Stripe/lib/Issuing/Dispute.php +30 -0
  89. includes/lib/Stripe/lib/Issuing/Transaction.php +35 -0
  90. includes/lib/Stripe/lib/LoginLink.php +5 -0
  91. includes/lib/Stripe/lib/OAuth.php +3 -8
  92. includes/lib/Stripe/lib/Order.php +29 -53
  93. includes/lib/Stripe/lib/OrderItem.php +22 -0
  94. includes/lib/Stripe/lib/OrderReturn.php +13 -31
  95. includes/lib/Stripe/lib/PaymentIntent.php +107 -0
  96. includes/lib/Stripe/lib/PaymentMethod.php +60 -0
  97. includes/lib/Stripe/lib/Payout.php +43 -48
  98. includes/lib/Stripe/lib/Person.php +108 -0
  99. includes/lib/Stripe/lib/Plan.php +25 -76
  100. includes/lib/Stripe/lib/Product.php +30 -61
  101. includes/lib/Stripe/lib/Radar/EarlyFraudWarning.php +36 -0
  102. includes/lib/Stripe/lib/Radar/ValueList.php +32 -0
  103. includes/lib/Stripe/lib/Radar/ValueListItem.php +26 -0
  104. includes/lib/Stripe/lib/Recipient.php +22 -67
  105. includes/lib/Stripe/lib/RecipientTransfer.php +2 -1
  106. includes/lib/Stripe/lib/Refund.php +30 -49
  107. includes/lib/Stripe/lib/Reporting/ReportRun.php +28 -0
  108. includes/lib/Stripe/lib/Reporting/ReportType.php +24 -0
  109. includes/lib/Stripe/lib/RequestTelemetry.php +27 -0
  110. includes/lib/Stripe/lib/Review.php +57 -0
  111. includes/lib/Stripe/lib/SKU.php +21 -64
  112. includes/lib/Stripe/lib/SetupIntent.php +75 -0
  113. includes/lib/Stripe/lib/Sigma/ScheduledQueryRun.php +33 -0
  114. includes/lib/Stripe/lib/SingletonApiResource.php +3 -1
  115. includes/lib/Stripe/lib/Source.php +90 -52
  116. includes/lib/Stripe/lib/SourceTransaction.php +23 -0
  117. includes/lib/Stripe/lib/Stripe.php +97 -7
  118. includes/lib/Stripe/lib/StripeObject.php +344 -115
  119. includes/lib/Stripe/lib/Subscription.php +58 -57
  120. includes/lib/Stripe/lib/SubscriptionItem.php +23 -67
  121. includes/lib/Stripe/lib/SubscriptionSchedule.php +111 -0
  122. includes/lib/Stripe/lib/SubscriptionScheduleRevision.php +77 -0
  123. includes/lib/Stripe/lib/TaxId.php +81 -0
  124. includes/lib/Stripe/lib/TaxRate.php +31 -0
  125. includes/lib/Stripe/lib/Terminal/ConnectionToken.php +17 -0
  126. includes/lib/Stripe/lib/Terminal/Location.php +25 -0
  127. includes/lib/Stripe/lib/Terminal/Reader.php +30 -0
  128. includes/lib/Stripe/lib/ThreeDSecure.php +6 -23
  129. includes/lib/Stripe/lib/Token.php +14 -22
  130. includes/lib/Stripe/lib/Topup.php +60 -0
  131. includes/lib/Stripe/lib/Transfer.php +63 -47
  132. includes/lib/Stripe/lib/TransferReversal.php +10 -1
  133. includes/lib/Stripe/lib/UsageRecord.php +44 -0
  134. includes/lib/Stripe/lib/UsageRecordSummary.php +22 -0
  135. includes/lib/Stripe/lib/Util/AutoPagingIterator.php +3 -3
  136. includes/lib/Stripe/lib/Util/CaseInsensitiveArray.php +62 -0
  137. includes/lib/Stripe/lib/Util/DefaultLogger.php +2 -2
  138. includes/lib/Stripe/lib/Util/LoggerInterface.php +1 -1
  139. includes/lib/Stripe/lib/Util/RandomGenerator.php +34 -0
  140. includes/lib/Stripe/lib/Util/RequestOptions.php +34 -5
  141. includes/lib/Stripe/lib/Util/Set.php +2 -2
  142. includes/lib/Stripe/lib/Util/Util.php +216 -72
  143. includes/lib/Stripe/lib/Webhook.php +4 -4
  144. includes/lib/Stripe/lib/WebhookEndpoint.php +29 -0
  145. includes/lib/Stripe/lib/WebhookSignature.php +4 -3
  146. includes/lib/php-jwt/BeforeValidException.php +7 -0
  147. includes/lib/php-jwt/ExpiredException.php +7 -0
  148. includes/lib/php-jwt/JWT.php +379 -0
  149. includes/lib/php-jwt/SignatureInvalidException.php +7 -0
  150. includes/profile.php +39 -39
  151. includes/recaptcha.php +14 -2
  152. includes/rest-api.php +49 -44
  153. includes/scripts.php +99 -0
  154. includes/sessions.php +81 -36
  155. includes/upgradecheck.php +16 -9
  156. js/pmpro-admin.js +49 -0
  157. js/pmpro-braintree.js +25 -0
  158. js/pmpro-checkout.js +203 -0
  159. js/pmpro-paypal.js +24 -0
  160. js/pmpro-stripe.js +159 -0
  161. languages/paid-memberships-pro.mo +0 -0
  162. languages/paid-memberships-pro.po +1468 -918
  163. languages/paid-memberships-pro.pot +1468 -918
  164. license.txt +1 -1
  165. pages/billing.php +47 -9
  166. pages/checkout.php +34 -208
  167. pages/invoice.php +2 -2
  168. paid-memberships-pro.php +8 -2
  169. preheaders/billing.php +6 -0
  170. preheaders/checkout.php +41 -94
  171. readme.txt +41 -4
  172. services/ipnhandler.php +1 -1
  173. services/stripe-webhook.php +53 -4
CHANGELOG.txt CHANGED
@@ -1,6 +1,70 @@
1
== Changelog ==
2
3
- = 2.0.4 - 2019-01-11 =
4
* BUG FIX: Fixed warning in code added in 2.0.3 that could cause issues at checkout.
5
* BUG FIX: Setting priority of pmpro_check_admin_capabilities to 5 to ensure it runs before dashboard redirect.
6
* BUG FIX: Removed duplicate id attribute on the Membership Account page "cancel" action link.
@@ -103,7 +167,6 @@
103
* ENHANCEMENT: Showing the Stripe version we use on the Payment Settings page.
104
* ENHANCEMENT: Updated Copyright date and GPLv2 link in license.txt.
105
106
-
107
= 1.9.5.3 - 2018-06-26 =
108
* BUG FIX: The pmpro_ipnhandler_extend_memberships function actually needed use $user_id instead of $current_user.
109
1
== Changelog ==
2
3
+ = 2.1 - 2019-09-13 =
4
+ * FEATURE: Updated Stripe integration to support Stripe v3, Stripe Elements, and their Secure Customer Authorization process.
5
+ * FEATURE: Updated how we store prices to support up to 8 decimals (e.g. for Bitcoin gateway implementations).
6
+ * ENHANCEMENT: Improved error messaging on the update billing page when a gateway doesn't support it or the user's current membership doesn't have a subscription.
7
+ * ENHANCEMENT: Added a pmpro_is_checkout() function that will return true if on the PMPro checkout page or a page with the PMPro checkout shortcode or block.
8
+ * ENHANCEMENT: Showing a warning message when a user about to be deleted has a membership so admins know that existing subscriptions will be deleted at the gateway.
9
+ * ENHANCEMENT: Added a pmpro_braintree_plan_id filter in case you need to adjust plan IDs. This is useful if you have several sites running on the same Braintree account.
10
+ * ENHANCEMENT: Added a pmpro_num_expiration_years filter to adjust the number of years to include in the dropdown to set the year membership will expire.
11
+ * ENHANCEMENT: Tweaked the UI of the orders list and members list in the dashboard.
12
+ * ENHANCEMENT: Added pmpro_membership_levels_table_extra_cols_header and pmpro_membership_levels_table_extra_cols_body hooks to add columns to the members list.
13
+ * ENHANCEMENT: Showing notices to admins when categories are hidden from them on the frontend of the site.
14
+ * ENHANCEMENT: Added a pmpro_url filter to filter URLs returned from that function.
15
+ * ENHANCEMENT: Adding a pmpro_checkout_gateway-stripe or pmpro_checkout_gateway-paypal/etc CSS class to the wrapping div for payment fields to aid in styling.
16
+ * ENHANCEMENT: Using the site's date format option when printing orders.
17
+ * BUG FIX/ENHANCEMENT: If a site has no paying levels, the test gateway will show as the "Default" gateway and we will no longer show a message about requiring gateway setup on the checkout page.
18
+ * BUG FIX/ENHANCEMENT: Updated Russian Ruble definition to have 0 decimals and use   as the thousands separator. (Thanks, Airat Halitov)
19
+ * BUG FIX/ENHANCEMENT: Using add_query_arg when generating IPN URLs to avoid issues on sites that aren't using pretty permalinks or have moved their admin directory.
20
+ * BUG FIX/ENHANCEMENT: Fixed issue on advanced settings page where clicking on labels didn't check the corresponding check boxes.
21
+ * BUG FIX/ENHANCEMENT: Updated our pmpro_generateUsername() function to be a bit smarter.
22
+ * BUG FIX/ENHANCEMENT: Now using wp_generate_password() when choosing a random password for a user (e.g. when using the Sign Up Shortcode add on or the $skip_account_fields global).
23
+ * BUG FIX/ENHANCEMENT: Setting autocomplete to false on the "fullname" honeypot field. This will prevent user's with certain autocomplete tools from accidentally filling it out.
24
+ * BUG FIX/EHNANCEMENT: Now sending name and email fields to PayPay (using Website Payments Pro) even if no address was captured.
25
+ * BUG FIX/ENHANCEMENT: More specific CSS selectors for checkout form elements to make sure errors are highlighted/etc with different themes.
26
+ * BUG FIX: Fixed issue where the first 2000 or so orders might be skipped when exporting orders on large sites.
27
+ * BUG FIX: Fixed issue with setting custom trials on discount codes.
28
+ * BUG FIX: Fixed issue in the SQL query in the pmpro_calculateInitialPaymentRevenue() function. This function is deprecated, but still used by some custom code.
29
+ * BUG FIX: Fixed issue where default templates would fail to load if a custom template was specified.
30
+ * BUG FIX: Fixed fatal errors that could happen when using the PMPro REST API endpoints.
31
+ * BUG FIX: Fixed bug where the invoices page would sometimes show data for the current (admin) users instead of the user the invoice was for.
32
+ * BUG FIX: Fixed bug where the membership stats graphs would sometimes show up blank.
33
+ * BUG FIX: Now falling back to using readfile() if fpassthru() doesn't existing.
34
+ * BUG FIX: Fixed issue where the from name and email were not set properly if the Only Filter PMPro Emails setting was checked. (Thanks, mjulian87 on GitHub)
35
+ * BUG FIX: Fixed several error handling issues with the Cybersource gateway integration.
36
+ * REFACTOR: Moved JavaScript out of pages/checkout.php and other places into files in the /js/ folder. This will avoid issues where other JS at checkout breaks PMPro checkout and will improve compatibility with tools that optimize JS.
37
+ * REFACTOR: Added unit testing and a started on coverage of some functions in includes/functions.php. (Thanks, Mike Auteri)
38
+ * REFACTOR: The JS function askfirst is now prefixed as pmpro_askfirst.
39
+
40
+ = 2.0.7 - 2019-05-30 =
41
+ * BUG FIX: Fixed issue where the profile start date would sometimes be set incorrectly on the Stripe subscription.
42
+ * BUG FIX: Fixed issue where the membership shortcode would not work properly if more than one level name was given.
43
+ * BUG FIX: Fixed issue where an incorrect email address was sometimes set in the confirm email field on the update billing page. (Thanks, Jessica Thomas)
44
+ * BUG FIX/ENHANCEMENT: Fixed placement of the hr tag above the user fields at checkout for consistency.
45
+ * ENHANCEMENT: Set the priority on the Require Membership meta box to "high" so it appears higher in the right sidebar.
46
+
47
+ = 2.0.6 - 2019-05-30 =
48
+ * SECURITY: Now using wp_safe_redirect when possible, especially in includes/login.php where the user-provided redirect_to URL parameter is used. (Thanks PluginVulnerabilities.com)
49
+
50
+ = 2.0.5 - 2019-04-25 =
51
+ * BUG FIX: Fixed fatal error on return from 2Checkout.
52
+ * BUG FIX: Removed error when installing PMPro via WP-CLI.
53
+ * BUG FIX: Fix database upgrade error on localhost environment. (Thanks, codezz on GitHub)
54
+ * BUG FIX: Fixed issue where the credit card expiring email didn't include user info because the user ID wasn't passed in properly. (Thanks, David Cervantes Caballero)
55
+ * BUG FIX: Fixed typo on edit level page. (Thanks, Theuns Coetzee)
56
+ * BUG FIX: Fixed bug with daily revenue reports not showing up in some cases.
57
+ * BUG FIX: Now checking before cancelling a Stripe subscription at the gateway to see if it has already been cancelled.
58
+ * BUG FIX/ENHANCEMENT: Now caching the query results in pmpro_getMembershipLevelsForUser(). This improves performance, especially when there are many posts on one page to check membership for. (Thanks, Seagyn Davis)
59
+ * BUG FIX/ENHANCEMENT: Now sending display_name to the $data array passed to PMPro email filters. (Thanks, David Cervantes Caballero)
60
+ * BUG FIX/ENHANCEMENT: Now searching for the last order with "success" or "pending" status on the Billing page.
61
+ * BUG FIX/ENHANCEMENT: Added pmpro_checkout_preheader_before_get_level_at_checkout and pmpro_checkout_preheader_after_get_level_at_checkout action hooks. Using pmpro_checkout_preheader_before_get_level_at_checkout to start the session earlier now.
62
+ * BUG FIX/ENHANCEMENT: Removed the "membership_code_id" and "membership_code" as field options for the member shortcode. These weren't working and it's unclear what would be meant to ask for a user's discount code since a user could have several orders with or without discount codes. Added "membership_description" and "membership_confirmation" instead.
63
+ * BUG FIX/ENHANCEMENT: Filtering the password reset message to make sure the link still works in all cases when we convert emails to HTML.
64
+ * BUG FIX/ENHANCEMENT: Added reCAPTCHA v3 and invisible reCAPTCHA support. It is recommended sites using Stripe or Braintree update to the reCAPTCHA v3 option. Read more here: https://www.paidmembershipspro.com/pmpro-update-2-0-5/
65
+ * REFACTOR: Now running the pmpro_billing_preheader hook after the jquery.creditCardValidator script is enqueued in preheader/billing.php to match how we do it in preheader/checkout.php. (Thanks, Rafe Colton)
66
+
67
+ = 2.0.4 - 2019-01-14 =
68
* BUG FIX: Fixed warning in code added in 2.0.3 that could cause issues at checkout.
69
* BUG FIX: Setting priority of pmpro_check_admin_capabilities to 5 to ensure it runs before dashboard redirect.
70
* BUG FIX: Removed duplicate id attribute on the Membership Account page "cancel" action link.
167
* ENHANCEMENT: Showing the Stripe version we use on the Payment Settings page.
168
* ENHANCEMENT: Updated Copyright date and GPLv2 link in license.txt.
169
170
= 1.9.5.3 - 2018-06-26 =
171
* BUG FIX: The pmpro_ipnhandler_extend_memberships function actually needed use $user_id instead of $current_user.
172
adminpages/admin_header.php CHANGED
@@ -25,7 +25,7 @@
25
$msgt .= " <a href=\"" . admin_url('admin.php?page=pmpro-membershiplevels&edit=-1') . "\">" . __("Add a membership level to get started.", 'paid-memberships-pro' ) . "</a>";
26
elseif($pmpro_level_ready && !$pmpro_pages_ready && $view != "pmpro-pagesettings")
27
$msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-pagesettings') . "\">" . __("Set up the membership pages", 'paid-memberships-pro' ) . "</a>.";
28
- elseif($pmpro_level_ready && $pmpro_pages_ready && !$pmpro_gateway_ready && $view != "pmpro-paymentsettings")
29
$msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-paymentsettings') . "\">" . __("Set up your SSL certificate and payment gateway", 'paid-memberships-pro' ) . "</a>.";
30
31
if(empty($msgt))
25
$msgt .= " <a href=\"" . admin_url('admin.php?page=pmpro-membershiplevels&edit=-1') . "\">" . __("Add a membership level to get started.", 'paid-memberships-pro' ) . "</a>";
26
elseif($pmpro_level_ready && !$pmpro_pages_ready && $view != "pmpro-pagesettings")
27
$msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-pagesettings') . "\">" . __("Set up the membership pages", 'paid-memberships-pro' ) . "</a>.";
28
+ elseif($pmpro_level_ready && $pmpro_pages_ready && !$pmpro_gateway_ready && $view != "pmpro-paymentsettings" && ! pmpro_onlyFreeLevels())
29
$msgt .= " <strong>" . __( 'Next step:', 'paid-memberships-pro' ) . "</strong> <a href=\"" . admin_url('admin.php?page=pmpro-paymentsettings') . "\">" . __("Set up your SSL certificate and payment gateway", 'paid-memberships-pro' ) . "</a>.";
30
31
if(empty($msgt))
adminpages/discountcodes.php CHANGED
@@ -585,7 +585,7 @@
585
586
<tr>
587
<th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'paid-memberships-pro' );?>:</label></th>
588
- <td><input class="recurring_checkbox" id="recurring_<?php echo $level->id;?>" 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();" /> <label for="recurring_<?php echo $level->id;?>"><?php _e('Check if this level has a recurring subscription payment.', 'paid-memberships-pro' );?></label></td>
589
</tr>
590
591
<tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
@@ -626,7 +626,7 @@
626
627
<tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
628
<th scope="row" valign="top"><label><?php _e('Custom Trial', 'paid-memberships-pro' );?>:</label></th>
629
- <td><input id="custom_trial_<?php echo $level->id?>" 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();" /> <label for="custom_trial_<?php echo $level->id;?>"><?php _e('Check to add a custom trial period.', 'paid-memberships-pro' );?></label></td>
630
</tr>
631
632
<tr class="trial_info recurring_info" <?php if (!pmpro_isLevelTrial($level)) echo "style='display:none;'";?>>
@@ -750,7 +750,6 @@
750
<th><?php _e('Uses', 'paid-memberships-pro' );?></th>
751
<th><?php _e('Levels', 'paid-memberships-pro' );?></th>
752
<?php do_action("pmpro_discountcodes_extra_cols_header", $codes);?>
753
- <th></th>
754
</tr>
755
</thead>
756
<tbody>
@@ -759,8 +758,24 @@
759
foreach($codes as $code) { ?>
760
<tr<?php if($count++ % 2 == 1) { ?> class="alternate"<?php } ?>>
761
<td><?php echo $code->id?></td>
762
- <td>
763
- <a href="?page=pmpro-discountcodes&edit=<?php echo $code->id?>"><?php echo $code->code?></a>
764
</td>
765
<td>
766
<?php echo date_i18n(get_option('date_format'), $code->starts)?>
@@ -790,25 +805,17 @@
790
$levels = $wpdb->get_results($sqlQuery);
791
792
$level_names = array();
793
- foreach($levels as $level)
794
- $level_names[] = "<a target=\"_blank\" href=\"" . pmpro_url("checkout", "?level=" . $level->id . "&discount_code=" . $code->code) . "\">" . $level->name . "</a>";
795
- if($level_names)
796
- echo implode(", ", $level_names);
797
- else
798
- echo "None";
799
?>
800
</td>
801
<?php do_action("pmpro_discountcodes_extra_cols_body", $code);?>
802
- <td>
803
- <a title="<?php _e('edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $code->id ), admin_url('admin.php' ) ); ?>" class="button-primary"><?php _e( 'edit', 'paid-memberships-pro' ); ?></a>
804
- <a title="<?php _e('copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => -1, 'copy' => $code->id ), admin_url('admin.php' ) ); ?>" class="button-secondary"><?php _e( 'copy', 'paid-memberships-pro' ); ?></a>
805
- <a title="<?php _e('delete', 'paid-memberships-pro' ); ?>" href="javascript:askfirst('<?php echo str_replace("'", "\'", sprintf(__('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.', 'paid-memberships-pro' ), $code->code));?>', '<?php echo wp_nonce_url(add_query_arg( array( 'page' => 'pmpro-discountcodes', 'delete' => $code->id), admin_url( 'admin.php' ) ), 'delete', 'pmpro_discountcodes_nonce'); ?>'); void(0);" class="button-secondary"><?php _e('delete', 'paid-memberships-pro' ); ?></a>
806
- <?php if ( (int)$uses > 0 ) { ?>
807
- <a title="<?php _e('view orders', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'discount_code' => $code->id, 'filter' => 'with-discount-code' ), admin_url('admin.php' ) ); ?>" class="button-secondary"><?php _e( 'orders', 'paid-memberships-pro' ); ?></a>
808
- <?php } else { ?>
809
- <a title="<?php _e('no orders', 'paid-memberships-pro' ); ?>" href="#" class="button-secondary button-disabled"><?php _e( 'orders', 'paid-memberships-pro' ); ?></a>
810
- <?php } ?>
811
- </td>
812
</tr>
813
<?php
814
}
585
586
<tr>
587
<th scope="row" valign="top"><label><?php _e('Recurring Subscription', 'paid-memberships-pro' );?>:</label></th>
588
+ <td><input class="recurring_checkbox" id="recurring_<?php echo $level->id;?>" name="recurring[]" type="checkbox" value="<?php echo $level->id?>" <?php if(pmpro_isLevelRecurring($level)) { echo "checked='checked'"; } ?> onclick="if(jQuery(this).prop('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();" /> <label for="recurring_<?php echo $level->id;?>"><?php _e('Check if this level has a recurring subscription payment.', 'paid-memberships-pro' );?></label></td>
589
</tr>
590
591
<tr class="recurring_info" <?php if(!pmpro_isLevelRecurring($level)) {?>style="display: none;"<?php } ?>>
626
627
<tr class="recurring_info" <?php if (!pmpro_isLevelRecurring($level)) echo "style='display:none;'";?>>
628
<th scope="row" valign="top"><label><?php _e('Custom Trial', 'paid-memberships-pro' );?>:</label></th>
629
+ <td><input id="custom_trial_<?php echo $level->id?>" 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).prop('checked')) jQuery(this).parent().parent().siblings('.trial_info').show(); else jQuery(this).parent().parent().siblings('.trial_info').hide();" /> <label for="custom_trial_<?php echo $level->id;?>"><?php _e('Check to add a custom trial period.', 'paid-memberships-pro' );?></label></td>
630
</tr>
631
632
<tr class="trial_info recurring_info" <?php if (!pmpro_isLevelTrial($level)) echo "style='display:none;'";?>>
750
<th><?php _e('Uses', 'paid-memberships-pro' );?></th>
751
<th><?php _e('Levels', 'paid-memberships-pro' );?></th>
752
<?php do_action("pmpro_discountcodes_extra_cols_header", $codes);?>
753
</tr>
754
</thead>
755
<tbody>
758
foreach($codes as $code) { ?>
759
<tr<?php if($count++ % 2 == 1) { ?> class="alternate"<?php } ?>>
760
<td><?php echo $code->id?></td>
761
+ <td class="has-row-actions">
762
+ <a title="<?php echo sprintf( 'Edit Code: %s', $code->code ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $code->id ), admin_url('admin.php' ) ); ?>"><?php echo $code->code?></a>
763
+ <div class="row-actions">
764
+ <span class="edit">
765
+ <a title="<?php _e( 'Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => $code->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Edit', 'paid-memberships-pro' ); ?></a>
766
+ </span> |
767
+ <span class="copy">
768
+ <a title="<?php _e( 'Copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-discountcodes', 'edit' => -1, 'copy' => $code->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Copy', 'paid-memberships-pro' ); ?></a>
769
+ </span> |
770
+ <span class="delete">
771
+ <a title="<?php _e( 'Delete', 'paid-memberships-pro' ); ?>" href="javascript:pmpro_askfirst('<?php echo str_replace("'", "\'", sprintf(__('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.', 'paid-memberships-pro' ), $code->code));?>', '<?php echo wp_nonce_url(add_query_arg( array( 'page' => 'pmpro-discountcodes', 'delete' => $code->id), admin_url( 'admin.php' ) ), 'delete', 'pmpro_discountcodes_nonce'); ?>'); void(0);"><?php _e('Delete', 'paid-memberships-pro' ); ?></a>
772
+ </span>
773
+ <?php if ( (int)$uses > 0 ) { ?>
774
+ | <span class="orders">
775
+ <a title="<?php _e(' View Orders', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'discount_code' => $code->id, 'filter' => 'with-discount-code' ), admin_url('admin.php' ) ); ?>"><?php _e( 'Orders', 'paid-memberships-pro' ); ?></a>
776
+ </span>
777
+ <?php } ?>
778
+ </div>
779
</td>
780
<td>
781
<?php echo date_i18n(get_option('date_format'), $code->starts)?>
805
$levels = $wpdb->get_results($sqlQuery);
806
807
$level_names = array();
808
+ foreach( $levels as $level ) {
809
+ $level_names[] = '<a title="' . pmpro_url( 'checkout', '?level=' . $level->id . '&discount_code=' . $code->code) . '" target="_blank" href="' . pmpro_url( 'checkout', '?level=' . $level->id . '&discount_code=' . $code->code) . '">' . $level->name . '</a>';
810
+ }
811
+ if( $level_names ) {
812
+ echo implode( ', ', $level_names );
813
+ } else {
814
+ echo 'None';
815
+ }
816
?>
817
</td>
818
<?php do_action("pmpro_discountcodes_extra_cols_body", $code);?>
819
</tr>
820
<?php
821
}
adminpages/functions.php CHANGED
@@ -242,3 +242,69 @@ function pmpro_getClassesForPaymentSettingsField($field, $force = false)
242
//return space separated string
243
return implode(" ", $rgateways);
244
}
242
//return space separated string
243
return implode(" ", $rgateways);
244
}
245
+
246
+
247
+ /**
248
+ * Code to handle emailing billable invoices.
249
+ *
250
+ * @since 1.8.6
251
+ */
252
+
253
+ /**
254
+ * Get the gateway-related classes for fields on the payment settings page.
255
+ *
256
+ * @param string $field The name of the field to check.
257
+ * @param bool $force If true, it will rebuild the cached results.
258
+ *
259
+ * @since 1.8
260
+ */
261
+ function pmpro_add_email_order_modal() {
262
+
263
+ // emailing?
264
+ if ( ! empty( $_REQUEST['email'] ) && ! empty( $_REQUEST['order'] ) ) {
265
+ $email = new PMProEmail();
266
+ $user = get_user_by( 'email', sanitize_email( $_REQUEST['email'] ) );
267
+ $order = new MemberOrder( $_REQUEST['order'] );
268
+ if ( $email->sendBillableInvoiceEmail( $user, $order ) ) { ?>
269
+ <div class="notice notice-success is-dismissible">
270
+ <p><?php _e( 'Invoice emailed successfully.', 'paid-memberships-pro' ); ?></p>
271
+ </div>
272
+ <?php } else { ?>
273
+ <div class="notice notice-error is-dismissible">
274
+ <p><?php _e( 'Error emailing invoice.', 'paid-memberships-pro' ); ?></p>
275
+ </div>
276
+ <?php }
277
+ }
278
+
279
+ ?>
280
+ <script>
281
+ // Update fields in email modal.
282
+ jQuery(document).ready(function ($) {
283
+ var order, order_id;
284
+ $('.email_link').click(function () {
285
+ order_id = $(this).data('order');
286
+ $('input[name=order]').val(order_id);
287
+ // Get email address from order ID
288
+ data = {
289
+ action: 'pmpro_get_order_json',
290
+ order_id: order_id
291
+ };
292
+ $.post(ajaxurl, data, function (response) {
293
+ order = JSON.parse(response);
294
+ $('input[name=email]').val(order.Email);
295
+ });
296
+ });
297
+ });
298
+ </script>
299
+ <?php add_thickbox(); ?>
300
+ <div id="email_invoice" style="display:none;">
301
+ <h3><?php _e( 'Email Invoice', 'paid-memberships-pro' ); ?></h3>
302
+ <form method="post" action="">
303
+ <input type="hidden" name="order" value=""/>
304
+ <?php _e( 'Send an invoice for this order to: ', 'paid-memberships-pro' ); ?>
305
+ <input type="text" value="" name="email"/>
306
+ <button class="button button-primary alignright"><?php _e( 'Send Email', 'paid-memberships-pro' ); ?></button>
307
+ </form>
308
+ </div>
309
+ <?php
310
+ }
adminpages/membershiplevels.php CHANGED
@@ -452,7 +452,7 @@
452
$has_bt_plan = PMProGateway_braintree::checkLevelForPlan( $level->id );
453
?>
454
<p class="pmpro_message <?php if ( ! $has_bt_plan ) {?>pmpro_error<?php } ?>">
455
- <strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php printf( __('You will need to create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to %s.', 'paid-memberships-pro' ), 'pmpro_' . $level->id ); ?></p>
456
<?php } ?>
457
</td>
458
</tr>
@@ -570,6 +570,32 @@
570
<?php do_action("pmpro_membership_level_after_other_settings"); ?>
571
572
<h3 class="topborder"><?php _e('Content Settings', 'paid-memberships-pro' );?></h3>
573
<table class="form-table">
574
<tbody>
575
<tr class="membership_categories">
@@ -717,7 +743,7 @@
717
<th><?php _e('Billing Details', 'paid-memberships-pro' );?></th>
718
<th><?php _e('Expiration', 'paid-memberships-pro' );?></th>
719
<th><?php _e('Allow Signups', 'paid-memberships-pro' );?></th>
720
- <th></th>
721
</tr>
722
</thead>
723
<tbody>
@@ -728,7 +754,14 @@
728
?>
729
<tr class="<?php if($count++ % 2 == 1) { ?>alternate<?php } ?> <?php if(!$level->allow_signups) { ?>pmpro_gray<?php } ?> <?php if(!pmpro_checkLevelForStripeCompatibility($level) || !pmpro_checkLevelForBraintreeCompatibility($level) || !pmpro_checkLevelForPayflowCompatibility($level) || !pmpro_checkLevelForTwoCheckoutCompatibility($level)) { ?>pmpro_error<?php } ?>">
730
<td><?php echo $level->id?></td>
731
- <td class="level_name"><a href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url( 'admin.php' ) ); ?>"><?php esc_attr_e( $level->name ); ?></a></td>
732
<td>
733
<?php if(pmpro_isLevelFree($level)) { ?>
734
<?php _e('FREE', 'paid-memberships-pro' );?>
@@ -743,9 +776,8 @@
743
<?php _e('After', 'paid-memberships-pro' );?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
744
<?php } ?>
745
</td>
746
- <td><?php if($level->allow_signups) { ?><a href="<?php echo add_query_arg( 'level', $level->id, pmpro_url("checkout") );?>"><?php _e('Yes', 'paid-memberships-pro' );?></a><?php } else { ?><?php _e('No', 'paid-memberships-pro' );?><?php } ?></td>
747
-
748
- <td><a title="<?php _e('edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url('admin.php' ) ); ?>" class="button-primary"><?php _e('edit', 'paid-memberships-pro' ); ?></a>&nbsp;<a title="<?php _e('copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => -1, 'copy' => $level->id ), admin_url( 'admin.php' ) ); ?>" class="button-secondary"><?php _e('copy', 'paid-memberships-pro' ); ?></a>&nbsp;<a title="<?php _e('delete', 'paid-memberships-pro' ); ?>" href="javascript:askfirst('<?php echo str_replace("'", "\'", sprintf(__("Are you sure you want to delete membership level %s? All subscriptions will be cancelled.", 'paid-memberships-pro' ), $level->name));?>', '<?php echo wp_nonce_url(add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'action' => 'delete_membership_level', 'deleteid' => $level->id ), admin_url( 'admin.php' ) ), 'delete_membership_level', 'pmpro_membershiplevels_nonce'); ?>'); void(0);" class="button-secondary"><?php _e('delete', 'paid-memberships-pro' ); ?></a></td>
749
</tr>
750
<?php
751
}
452
$has_bt_plan = PMProGateway_braintree::checkLevelForPlan( $level->id );
453
?>
454
<p class="pmpro_message <?php if ( ! $has_bt_plan ) {?>pmpro_error<?php } ?>">
455
+ <strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php printf( __('You will need to create a "Plan" in your Braintree dashboard with the same settings and the "Plan ID" set to %s.', 'paid-memberships-pro' ), PMProGateway_braintree::get_plan_id( $level->id ) ); ?></p>
456
<?php } ?>
457
</td>
458
</tr>
570
<?php do_action("pmpro_membership_level_after_other_settings"); ?>
571
572
<h3 class="topborder"><?php _e('Content Settings', 'paid-memberships-pro' );?></h3>
573
+ <?php
574
+ // Get the Advanced Settings for filtering queries and showing excerpts.
575
+ $filterqueries = pmpro_getOption('filterqueries');
576
+ $showexcerpts = pmpro_getOption("showexcerpts");
577
+
578
+ $allowed_html = array (
579
+ 'a' => array (
580
+ 'href' => array(),
581
+ 'target' => array(),
582
+ 'title' => array(),
583
+ ),
584
+ );
585
+
586
+ if ( $filterqueries == 1 ) {
587
+ // Show a message that posts in these categories are hidden.
588
+ echo '<p>' . sprintf( wp_kses( __( 'Non-members will not see posts in these categories. You can <a href="%s" title="Advanced Settings" target="_blank">update this setting here</a>.', 'paid-memberships-pro' ), $allowed_html ), admin_url( 'admin.php?page=pmpro-advancedsettings' ) ) . '</p>';
589
+ } else {
590
+ if ( $showexcerpts == 1 ) {
591
+ // Show a message that posts in these categories will show title and excerpt.
592
+ echo '<p>' . sprintf( wp_kses( __( 'Non-members will see the title and excerpt for posts in these categories. You can <a href="%s" title="Advanced Settings" target="_blank">update this setting here</a>.', 'paid-memberships-pro' ), $allowed_html ), admin_url( 'admin.php?page=pmpro-advancedsettings' ) ) . '</p>';
593
+ } else {
594
+ // Show a message that posts in these categories will show only the title.
595
+ echo '<p>' . sprintf( wp_kses( __( 'Non-members will see the title only for posts in these categories. You can <a href="%s" title="Advanced Settings" target="_blank">update this setting here</a>.', 'paid-memberships-pro' ), $allowed_html ), admin_url( 'admin.php?page=pmpro-advancedsettings' ) ) . '</p>';
596
+ }
597
+ }
598
+ ?>
599
<table class="form-table">
600
<tbody>
601
<tr class="membership_categories">
743
<th><?php _e('Billing Details', 'paid-memberships-pro' );?></th>
744
<th><?php _e('Expiration', 'paid-memberships-pro' );?></th>
745
<th><?php _e('Allow Signups', 'paid-memberships-pro' );?></th>
746
+ <?php do_action( 'pmpro_membership_levels_table_extra_cols_header', $reordered_levels ); ?>
747
</tr>
748
</thead>
749
<tbody>
754
?>
755
<tr class="<?php if($count++ % 2 == 1) { ?>alternate<?php } ?> <?php if(!$level->allow_signups) { ?>pmpro_gray<?php } ?> <?php if(!pmpro_checkLevelForStripeCompatibility($level) || !pmpro_checkLevelForBraintreeCompatibility($level) || !pmpro_checkLevelForPayflowCompatibility($level) || !pmpro_checkLevelForTwoCheckoutCompatibility($level)) { ?>pmpro_error<?php } ?>">
756
<td><?php echo $level->id?></td>
757
+ <td class="level_name has-row-actions">
758
+ <span class="level-name"><a href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url( 'admin.php' ) ); ?>"><?php esc_attr_e( $level->name ); ?></a></span>
759
+ <div class="row-actions">
760
+ <span class="edit"><a title="<?php _e('Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => $level->id ), admin_url('admin.php' ) ); ?>"><?php _e('Edit', 'paid-memberships-pro' ); ?></a></span> |
761
+ <span class="copy"><a title="<?php _e('Copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'edit' => -1, 'copy' => $level->id ), admin_url( 'admin.php' ) ); ?>"><?php _e('Copy', 'paid-memberships-pro' ); ?></a></span> |
762
+ <span class="delete"><a title="<?php _e('Delete', 'paid-memberships-pro' ); ?>" href="javascript:pmpro_askfirst('<?php echo str_replace("'", "\'", sprintf(__("Are you sure you want to delete membership level %s? All subscriptions will be cancelled.", 'paid-memberships-pro' ), $level->name));?>', '<?php echo wp_nonce_url(add_query_arg( array( 'page' => 'pmpro-membershiplevels', 'action' => 'delete_membership_level', 'deleteid' => $level->id ), admin_url( 'admin.php' ) ), 'delete_membership_level', 'pmpro_membershiplevels_nonce'); ?>'); void(0);"><?php _e('Delete', 'paid-memberships-pro' ); ?></a></span>
763
+ </div>
764
+ </td>
765
<td>
766
<?php if(pmpro_isLevelFree($level)) { ?>
767
<?php _e('FREE', 'paid-memberships-pro' );?>
776
<?php _e('After', 'paid-memberships-pro' );?> <?php echo $level->expiration_number?> <?php echo sornot($level->expiration_period,$level->expiration_number)?>
777
<?php } ?>
778
</td>
779
+ <td><?php if($level->allow_signups) { ?><a target="_blank" href="<?php echo add_query_arg( 'level', $level->id, pmpro_url("checkout") );?>"><?php _e('Yes', 'paid-memberships-pro' );?></a><?php } else { ?><?php _e('No', 'paid-memberships-pro' );?><?php } ?></td>
780
+ <?php do_action( 'pmpro_membership_levels_table_extra_cols_body', $level ); ?>
781
</tr>
782
<?php
783
}
adminpages/memberslist-csv.php CHANGED
@@ -296,10 +296,7 @@
296
$last_uid = $theusers[($users_found - 1)];
297
298
//increment starting position
299
- if(0 < $iterations)
300
- {
301
- $i_start += $max_users_per_loop;
302
- }
303
304
//escape the % for LIKE comparison with $wpdb
305
if(!empty($search))
@@ -553,10 +550,18 @@
553
ini_set('zlib.output_compression', 'Off');
554
}
555
556
- // open and send the file contents to the remote location
557
- $fh = fopen( $filename, 'rb' );
558
- fpassthru($fh);
559
- fclose($fh);
560
561
// remove the temp file
562
unlink($filename);
296
$last_uid = $theusers[($users_found - 1)];
297
298
//increment starting position
299
+ $i_start += $max_users_per_loop;
300
301
//escape the % for LIKE comparison with $wpdb
302
if(!empty($search))
550
ini_set('zlib.output_compression', 'Off');
551
}
552
553
+ if( function_exists('fpassthru') )
554
+ {
555
+ // open and send the file contents to the remote location
556
+ $fh = fopen( $filename, 'rb' );
557
+ fpassthru($fh);
558
+ fclose($fh);
559
+ }
560
+ else
561
+ {
562
+ // use readfile() if fpassthru() is disabled (like on Flywheel Hosted)
563
+ readfile($fh);
564
+ }
565
566
// remove the temp file
567
unlink($filename);
adminpages/orders-csv.php CHANGED
@@ -428,11 +428,11 @@ for ( $ic = 1; $ic <= $iterations; $ic ++ ) {
428
}
429
430
//increment starting position
431
- if ( $iterations > 1 ) {
432
$i_start += $max_orders_per_loop;
433
}
434
// get the order list we should process
435
- $order_list = array_slice( $order_ids, $i_start, ( $i_start + ( $max_orders_per_loop - 1 ) ) );
436
437
if (PMPRO_BENCHMARK)
438
{
@@ -441,7 +441,6 @@ for ( $ic = 1; $ic <= $iterations; $ic ++ ) {
441
}
442
443
foreach ( $order_list as $order_id ) {
444
-
445
$csvoutput = array();
446
447
$order = new MemberOrder();
@@ -537,11 +536,9 @@ for ( $ic = 1; $ic <= $iterations; $ic ++ ) {
537
error_log("PMPRO_BENCHMARK - Time processing data: {$sec}.{$usec} seconds");
538
error_log("PMPRO_BENCHMARK - Peak memory usage: " . number_format($memory_processing_data, false, '.', ',') . " bytes");
539
}
540
-
541
$order_list = null;
542
wp_cache_flush();
543
}
544
-
545
pmpro_transmit_order_content( $csv_fh, $filename, $headers );
546
547
function pmpro_enclose( $s ) {
@@ -588,10 +585,18 @@ function pmpro_transmit_order_content( $csv_fh, $filename, $headers = array() )
588
ini_set( 'zlib.output_compression', 'Off' );
589
}
590
591
- // open and send the file contents to the remote location
592
- $fh = fopen( $filename, 'rb' );
593
- fpassthru( $fh );
594
- fclose( $fh );
595
596
// remove the temp file
597
unlink( $filename );
428
}
429
430
//increment starting position
431
+ if ( $ic > 1 ) {
432
$i_start += $max_orders_per_loop;
433
}
434
// get the order list we should process
435
+ $order_list = array_slice( $order_ids, $i_start, $max_orders_per_loop );
436
437
if (PMPRO_BENCHMARK)
438
{
441
}
442
443
foreach ( $order_list as $order_id ) {
444
$csvoutput = array();
445
446
$order = new MemberOrder();
536
error_log("PMPRO_BENCHMARK - Time processing data: {$sec}.{$usec} seconds");
537
error_log("PMPRO_BENCHMARK - Peak memory usage: " . number_format($memory_processing_data, false, '.', ',') . " bytes");
538
}
539
$order_list = null;
540
wp_cache_flush();
541
}
542
pmpro_transmit_order_content( $csv_fh, $filename, $headers );
543
544
function pmpro_enclose( $s ) {
585
ini_set( 'zlib.output_compression', 'Off' );
586
}
587
588
+ if( function_exists('fpassthru') )
589
+ {
590
+ // open and send the file contents to the remote location
591
+ $fh = fopen( $filename, 'rb' );
592
+ fpassthru($fh);
593
+ fclose($fh);
594
+ }
595
+ else
596
+ {
597
+ // use readfile() if fpassthru() is disabled (like on Flywheel Hosted)
598
+ readfile($fh);
599
+ }
600
601
// remove the temp file
602
unlink( $filename );
adminpages/orders.php CHANGED
@@ -149,24 +149,6 @@ if ( empty( $filter ) || $filter === 'all' ) {
149
$condition = "o.total = 0";
150
}
151
152
- // emailing?
153
- if ( ! empty( $_REQUEST['email'] ) && ! empty( $_REQUEST['order'] ) ) {
154
- $email = new PMProEmail();
155
- $user = get_user_by( 'email', sanitize_email( $_REQUEST['email'] ) );
156
- $order = new MemberOrder( $_REQUEST['order'] );
157
- if ( $email->sendBillableInvoiceEmail( $user, $order ) ) {
158
- $pmpro_msg = __( 'Invoice emailed successfully.', 'paid-memberships-pro' );
159
- $pmpro_msgt = 'success';
160
- } else {
161
- $pmpro_msg = __( 'Error emailing invoice.', 'paid-memberships-pro' );
162
- $pmpro_msgt = 'error';
163
- }
164
-
165
- // clean up so we stay on the orders list view
166
- unset( $_REQUEST['order'] );
167
- $order = null;
168
- }
169
-
170
// deleting?
171
if ( ! empty( $_REQUEST['delete'] ) ) {
172
$dorder = new MemberOrder( intval( $_REQUEST['delete'] ) );
@@ -373,6 +355,12 @@ if ( ! empty( $_REQUEST['save'] ) ) {
373
}
374
375
require_once( dirname( __FILE__ ) . '/admin_header.php' );
376
?>
377
378
<?php if ( ! empty( $order ) ) { ?>
@@ -380,6 +368,8 @@ require_once( dirname( __FILE__ ) . '/admin_header.php' );
380
<h2>
381
<?php if ( ! empty( $order->id ) ) { ?>
382
<?php _e( 'Order', 'paid-memberships-pro' ); ?> #<?php echo $order->id; ?>: <?php echo $order->code; ?>
383
<?php } else { ?>
384
<?php _e( 'New Order', 'paid-memberships-pro' ); ?>
385
<?php } ?>
@@ -953,42 +943,7 @@ selected="selected"<?php } ?>><?php echo date_i18n( 'M', strtotime( $i . '/1/' .
953
</form>
954
955
<?php } else { ?>
956
- <?php
957
- /**
958
- * Code to handle emailing billable invoices.
959
- *
960
- * @since 1.8.6
961
- */
962
- ?>
963
- <script>
964
- // Update fields in email modal.
965
- jQuery(document).ready(function ($) {
966
- var order, order_id;
967
- $('.email_link').click(function () {
968
- order_id = $(this).data('order');
969
- $('input[name=order]').val(order_id);
970
- // Get email address from order ID
971
- data = {
972
- action: 'pmpro_get_order_json',
973
- order_id: order_id
974
- };
975
- $.post(ajaxurl, data, function (response) {
976
- order = JSON.parse(response);
977
- $('input[name=email]').val(order.Email);
978
- });
979
- });
980
- });
981
- </script>
982
- <?php add_thickbox(); ?>
983
- <div id="email_invoice" style="display:none;">
984
- <h3><?php _e( 'Email Invoice', 'paid-memberships-pro' ); ?></h3>
985
- <form method="post" action="">
986
- <input type="hidden" name="order" value=""/>
987
- <?php _e( 'Send an invoice for this order to: ', 'paid-memberships-pro' ); ?>
988
- <input type="text" value="" name="email"/>
989
- <button class="button button-primary alignright"><?php _e( 'Send Email', 'paid-memberships-pro' ); ?></button>
990
- </form>
991
- </div>
992
<form id="posts-filter" method="get" action="">
993
<h2>
994
<?php _e( 'Orders', 'paid-memberships-pro' ); ?>
@@ -1361,11 +1316,6 @@ selected="selected"<?php } ?>><?php echo date_i18n( 'M', strtotime( $i . '/1/' .
1361
<th><?php _e( 'Status', 'paid-memberships-pro' ); ?></th>
1362
<th><?php _e( 'Date', 'paid-memberships-pro' ); ?></th>
1363
<th><?php _e( 'Discount Code', 'paid-memberships-pro' );?></th>
1364
- <th></th>
1365
- <th></th>
1366
- <th></th>
1367
- <th></th>
1368
- <th></th>
1369
</tr>
1370
</thead>
1371
<tbody id="orders" class="list:order orders-list">
@@ -1387,7 +1337,7 @@ class="alternate"<?php } ?>>
1387
<td>
1388
<a href="admin.php?page=pmpro-orders&order=<?php echo $order->id; ?>"><?php echo $order->code; ?></a>
1389
</td>
1390
- <td class="username column-username">
1391
<?php $order->getUser(); ?>
1392
<?php if ( ! empty( $order->user ) ) { ?>
1393
<a href="user-edit.php?user_id=<?php echo $order->user->ID; ?>"><?php echo $order->user->user_login; ?></a>
@@ -1396,23 +1346,40 @@ class="alternate"<?php } ?>>
1396
<?php } else { ?>
1397
[<?php _e( 'none', 'paid-memberships-pro' ); ?>]
1398
<?php } ?>
1399
- <br/>
1400
- <?php
1401
- // Set up the hover actions for this user
1402
- $actions = apply_filters( 'pmpro_orders_user_row_actions', array(), $order->user, $order );
1403
- $action_count = count( $actions );
1404
- $i = 0;
1405
- if ( $action_count ) {
1406
- $out = '<div class="row-actions">';
1407
- foreach ( $actions as $action => $link ) {
1408
- ++ $i;
1409
- ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
1410
- $out .= "<span class='$action'>$link$sep</span>";
1411
}
1412
- $out .= '</div>';
1413
- echo $out;
1414
- }
1415
- ?>
1416
</td>
1417
<?php do_action( 'pmpro_orders_extra_cols_body', $order ); ?>
1418
<td><?php echo $order->membership_id; ?></td>
@@ -1485,23 +1452,6 @@ class="alternate"<?php } ?>>
1485
</a>
1486
<?php } ?>
1487
</td>
1488
- <td align="center">
1489
- <a href="admin.php?page=pmpro-orders&order=<?php echo $order->id; ?>"><?php _e( 'edit', 'paid-memberships-pro' ); ?></a>
1490
- </td>
1491
- <td align="center">
1492
- <a href="admin.php?page=pmpro-orders&order=-1&copy=<?php echo $order->id; ?>"><?php _e( 'copy', 'paid-memberships-pro' ); ?></a>
1493
- </td>
1494
- <td align="center">
1495
- <a href="javascript:askfirst('<?php echo str_replace( "'", "\'", sprintf( __( 'Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?', 'paid-memberships-pro' ), str_replace( "'", '', $order->code ) ) ); ?>', 'admin.php?page=pmpro-orders&delete=<?php echo $order->id; ?>'); void(0);"><?php _e( 'delete', 'paid-memberships-pro' ); ?></a>
1496
- </td>
1497
- <td align="center">
1498
- <a href="admin-ajax.php?action=pmpro_orders_print_view&order=<?php echo $order->id; ?>"
1499
- target="_blank"><?php _e( 'print', 'paid-memberships-pro' ); ?></a>
1500
- </td>
1501
- <td align="center">
1502
- <a href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link"
1503
- data-order="<?php echo $order->id; ?>"><?php _e( 'email', 'paid-memberships-pro' ); ?></a>
1504
- </td>
1505
</tr>
1506
<?php
1507
}
149
$condition = "o.total = 0";
150
}
151
152
// deleting?
153
if ( ! empty( $_REQUEST['delete'] ) ) {
154
$dorder = new MemberOrder( intval( $_REQUEST['delete'] ) );
355
}
356
357
require_once( dirname( __FILE__ ) . '/admin_header.php' );
358
+
359
+ if ( function_exists( 'pmpro_add_email_order_modal' ) ) {
360
+ // Load the email order modal.
361
+ pmpro_add_email_order_modal();
362
+ }
363
+
364
?>
365
366
<?php if ( ! empty( $order ) ) { ?>
368
<h2>
369
<?php if ( ! empty( $order->id ) ) { ?>
370
<?php _e( 'Order', 'paid-memberships-pro' ); ?> #<?php echo $order->id; ?>: <?php echo $order->code; ?>
371
+ <a title="<?php _e( 'Print', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'action' => 'pmpro_orders_print_view', 'order' => $order->id ), admin_url('admin-ajax.php' ) ); ?>" class="add-new-h2" target="_blank" ><?php _e( 'Print', 'paid-memberships-pro' ); ?></a>
372
+ <a title="<?php _e( 'Email', 'paid-memberships-pro' ); ?>" href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link add-new-h2" data-order="<?php echo $order->id; ?>"><?php _e( 'Email', 'paid-memberships-pro' ); ?></a>
373
<?php } else { ?>
374
<?php _e( 'New Order', 'paid-memberships-pro' ); ?>
375
<?php } ?>
943
</form>
944
945
<?php } else { ?>
946
+
947
<form id="posts-filter" method="get" action="">
948
<h2>
949
<?php _e( 'Orders', 'paid-memberships-pro' ); ?>
1316
<th><?php _e( 'Status', 'paid-memberships-pro' ); ?></th>
1317
<th><?php _e( 'Date', 'paid-memberships-pro' ); ?></th>
1318
<th><?php _e( 'Discount Code', 'paid-memberships-pro' );?></th>
1319
</tr>
1320
</thead>
1321
<tbody id="orders" class="list:order orders-list">
1337
<td>
1338
<a href="admin.php?page=pmpro-orders&order=<?php echo $order->id; ?>"><?php echo $order->code; ?></a>
1339
</td>
1340
+ <td class="username column-username has-row-actions">
1341
<?php $order->getUser(); ?>
1342
<?php if ( ! empty( $order->user ) ) { ?>
1343
<a href="user-edit.php?user_id=<?php echo $order->user->ID; ?>"><?php echo $order->user->user_login; ?></a>
1346
<?php } else { ?>
1347
[<?php _e( 'none', 'paid-memberships-pro' ); ?>]
1348
<?php } ?>
1349
+ <br />
1350
+ <div class="row-actions">
1351
+ <span class="edit">
1352
+ <a title="<?php _e( 'Edit', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'order' => $order->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Edit', 'paid-memberships-pro' ); ?></a>
1353
+ </span> |
1354
+ <span class="copy">
1355
+ <a title="<?php _e( 'Copy', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'page' => 'pmpro-orders', 'order' => '-1', 'copy' => $order->id ), admin_url('admin.php' ) ); ?>"><?php _e( 'Copy', 'paid-memberships-pro' ); ?></a>
1356
+ </span> |
1357
+ <span class="delete">
1358
+ <a href="javascript:pmpro_askfirst('<?php echo str_replace( "'", "\'", sprintf( __( 'Deleting orders is permanent and can affect active users. Are you sure you want to delete order %s?', 'paid-memberships-pro' ), str_replace( "'", '', $order->code ) ) ); ?>', 'admin.php?page=pmpro-orders&delete=<?php echo $order->id; ?>'); void(0);"><?php _e( 'Delete', 'paid-memberships-pro' ); ?></a>
1359
+ </span> |
1360
+ <span class="print">
1361
+ <a target="_blank" title="<?php _e( 'Print', 'paid-memberships-pro' ); ?>" href="<?php echo add_query_arg( array( 'action' => 'pmpro_orders_print_view', 'order' => $order->id ), admin_url('admin-ajax.php' ) ); ?>"><?php _e( 'Print', 'paid-memberships-pro' ); ?></a>
1362
+ </span> |
1363
+ <span class="email">
1364
+ <a href="#TB_inline?width=600&height=200&inlineId=email_invoice" class="thickbox email_link"
1365
+ data-order="<?php echo $order->id; ?>"><?php _e( 'Email', 'paid-memberships-pro' ); ?></a>
1366
+ </span>
1367
+ <?php
1368
+ // Set up the hover actions for this user
1369
+ $actions = apply_filters( 'pmpro_orders_user_row_actions', array(), $order->user, $order );
1370
+ $action_count = count( $actions );
1371
+ $i = 0;
1372
+ if ( $action_count ) {
1373
+ $out = ' | ';
1374
+ foreach ( $actions as $action => $link ) {
1375
+ ++ $i;
1376
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
1377
+ $out .= "<span class='$action'>$link$sep</span>";
1378
+ }
1379
+ echo $out;
1380
}
1381
+ ?>
1382
+ </div>
1383
</td>
1384
<?php do_action( 'pmpro_orders_extra_cols_body', $order ); ?>
1385
<td><?php echo $order->membership_id; ?></td>
1452
</a>
1453
<?php } ?>
1454
</td>
1455
</tr>
1456
<?php
1457
}
adminpages/paymentsettings.php CHANGED
@@ -134,6 +134,9 @@
134
}
135
?>
136
</select>
137
</td>
138
</tr>
139
<tr>
@@ -151,6 +154,20 @@
151
//hide all gateway options
152
jQuery('tr.gateway').hide();
153
jQuery('tr.gateway_'+gateway).show();
154
}
155
pmpro_changeGateway(jQuery('#gateway').val());
156
</script>
134
}
135
?>
136
</select>
137
+ <?php if( pmpro_onlyFreeLevels() ) { ?>
138
+ <div id='pmpro-default-gateway-message' style="display:none;"><small><?php echo __( 'This gateway is for membership sites with Free levels or for sites that accept payment offline.', 'paid-memberships-pro' ) . '<br/>' . __( 'It is not connected to a live gateway environment and cannot accept payments.', 'paid-memberships-pro' ); ?></small></div>
139
+ <?php } ?>
140
</td>
141
</tr>
142
<tr>
154
//hide all gateway options
155
jQuery('tr.gateway').hide();
156
jQuery('tr.gateway_'+gateway).show();
157
+
158
+ //hide sub settings and toggle them on based on triggers
159
+ jQuery('tr.pmpro_toggle_target').hide();
160
+ jQuery( 'input[pmpro_toggle_trigger_for]' ).each( function() {
161
+ if ( jQuery( this ).is( ':visible' ) ) {
162
+ pmpro_toggle_elements_by_selector( jQuery( this ).attr( 'pmpro_toggle_trigger_for' ), jQuery( this ).prop( 'checked' ) );
163
+ }
164
+ });
165
+
166
+ if ( jQuery('#gateway').val() === '' ) {
167
+ jQuery('#pmpro-default-gateway-message').show();
168
+ } else {
169
+ jQuery('#pmpro-default-gateway-message').hide();
170
+ }
171
}
172
pmpro_changeGateway(jQuery('#gateway').val());
173
</script>
adminpages/reports/memberships.php CHANGED
@@ -186,7 +186,7 @@ function pmpro_report_memberships_page()
186
if($period == "daily")
187
{
188
$startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
189
- $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-32';
190
$date_function = 'DAY';
191
}
192
elseif($period == "monthly")
@@ -221,7 +221,7 @@ function pmpro_report_memberships_page()
221
$sqlQuery .= "WHERE mu.startdate >= '" . esc_sql( $startdate ) . "' ";
222
223
if ( ! empty( $enddate ) ) {
224
- $sqlQuery .= "AND mu.startdate < '" . esc_sql( $enddate ) . "' ";
225
}
226
}
227
186
if($period == "daily")
187
{
188
$startdate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-01';
189
+ $enddate = $year . '-' . substr("0" . $month, strlen($month) - 1, 2) . '-31';
190
$date_function = 'DAY';
191
}
192
elseif($period == "monthly")
221
$sqlQuery .= "WHERE mu.startdate >= '" . esc_sql( $startdate ) . "' ";
222
223
if ( ! empty( $enddate ) ) {
224
+ $sqlQuery .= "AND mu.startdate <= '" . esc_sql( $enddate ) . "' ";
225
}
226
}
227
adminpages/templates/orders-print.php CHANGED
@@ -51,7 +51,7 @@
51
</tr>
52
<tr>
53
<td>
54
- <?php echo __( 'Date:', 'paid-memberships-pro' ) . '&nbsp;' . date_i18n( 'Y-m-d', $order->timestamp ) ?>
55
</td>
56
</tr>
57
</table>
51
</tr>
52
<tr>
53
<td>
54
+ <?php echo __( 'Date:', 'paid-memberships-pro' ) . '&nbsp;' . date_i18n( get_option( 'date_format' ), $order->timestamp ); ?>
55
</td>
56
</tr>
57
</table>
bin/install-wp-tests.sh ADDED
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if [ $# -lt 3 ]; then
4
+ echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]"
5
+ exit 1
6
+ fi
7
+
8
+ DB_NAME=$1
9
+ DB_USER=$2
10
+ DB_PASS=$3
11
+ DB_HOST=${4-localhost}
12
+ WP_VERSION=${5-latest}
13
+ SKIP_DB_CREATE=${6-false}
14
+
15
+ TMPDIR=${TMPDIR-/tmp}
16
+ TMPDIR=$(echo $TMPDIR | sed -e "s/\/#x2F;/")
17
+ WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
18
+ WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
19
+
20
+ download() {
21
+ if [ `which curl` ]; then
22
+ curl -s "$1" > "$2";
23
+ elif [ `which wget` ]; then
24
+ wget -nv -O "$2" "$1"
25
+ fi
26
+ }
27
+
28
+ if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
29
+ WP_TESTS_TAG="branches/$WP_VERSION"
30
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
31
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
32
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
33
+ WP_TESTS_TAG="tags/${WP_VERSION%??}"
34
+ else
35
+ WP_TESTS_TAG="tags/$WP_VERSION"
36
+ fi
37
+ elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
38
+ WP_TESTS_TAG="trunk"
39
+ else
40
+ # http serves a single offer, whereas https serves multiple. we only want one
41
+ download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
42
+ grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
43
+ LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
44
+ if [[ -z "$LATEST_VERSION" ]]; then
45
+ echo "Latest WordPress version could not be found"
46
+ exit 1
47
+ fi
48
+ WP_TESTS_TAG="tags/$LATEST_VERSION"
49
+ fi
50
+
51
+ set -ex
52
+
53
+ install_wp() {
54
+
55
+ if [ -d $WP_CORE_DIR ]; then
56
+ return;
57
+ fi
58
+
59
+ mkdir -p $WP_CORE_DIR
60
+
61
+ if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
62
+ mkdir -p $TMPDIR/wordpress-nightly
63
+ download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
64
+ unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
65
+ mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
66
+ else
67
+ if [ $WP_VERSION == 'latest' ]; then
68
+ local ARCHIVE_NAME='latest'
69
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
70
+ # https serves multiple offers, whereas http serves single.
71
+ download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
72
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
73
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
74
+ LATEST_VERSION=${WP_VERSION%??}
75
+ else
76
+ # otherwise, scan the releases and get the most up to date minor version of the major release
77
+ local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
78
+ LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
79
+ fi
80
+ if [[ -z "$LATEST_VERSION" ]]; then
81
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
82
+ else
83
+ local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
84
+ fi
85
+ else
86
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
87
+ fi
88
+ download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
89
+ tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
90
+ fi
91
+
92
+ download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
93
+ }
94
+
95
+ install_test_suite() {
96
+ # portable in-place argument for both GNU sed and Mac OSX sed
97
+ if [[ $(uname -s) == 'Darwin' ]]; then
98
+ local ioption='-i .bak'
99
+ else
100
+ local ioption='-i'
101
+ fi
102
+
103
+ # set up testing suite if it doesn't yet exist
104
+ if [ ! -d $WP_TESTS_DIR ]; then
105
+ # set up testing suite
106
+ mkdir -p $WP_TESTS_DIR
107
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
108
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
109
+ fi
110
+
111
+ if [ ! -f wp-tests-config.php ]; then
112
+ download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
113
+ # remove all forward slashes in the end
114
+ WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
115
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
116
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
117
+ sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
118
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
119
+ sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
120
+ fi
121
+
122
+ }
123
+
124
+ install_db() {
125
+
126
+ if [ ${SKIP_DB_CREATE} = "true" ]; then
127
+ return 0
128
+ fi
129
+
130
+ # parse DB_HOST for port or socket references
131
+ local PARTS=(${DB_HOST//\:/ })
132
+ local DB_HOSTNAME=${PARTS[0]};
133
+ local DB_SOCK_OR_PORT=${PARTS[1]};
134
+ local EXTRA=""
135
+
136
+ if ! [ -z $DB_HOSTNAME ] ; then
137
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}#x27;) ]; then
138
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
139
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
140
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
141
+ elif ! [ -z $DB_HOSTNAME ] ; then
142
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
143
+ fi
144
+ fi
145
+
146
+ # create database
147
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
148
+ }
149
+
150
+ install_wp
151
+ install_test_suite
152
+ install_db
classes/class.pmproemail.php CHANGED
@@ -114,6 +114,7 @@
114
$this->email = apply_filters("pmpro_email_recipient", $temail->email, $this);
115
$this->from = apply_filters("pmpro_email_sender", $temail->from, $this);
116
$this->fromname = apply_filters("pmpro_email_sender_name", $temail->fromname, $this);
117
$this->subject = apply_filters("pmpro_email_subject", $temail->subject, $this);
118
$this->template = apply_filters("pmpro_email_template", $temail->template, $this);
119
$this->body = apply_filters("pmpro_email_body", $temail->body, $this);
@@ -130,6 +131,33 @@
130
}
131
}
132
133
function sendCancelEmail($user = NULL, $old_level_id = NULL)
134
{
135
global $wpdb, $current_user;
@@ -953,6 +981,82 @@
953
954
return $this->sendEmail();
955
}
956
957
/**
958
* Load the text for each default email template.
114
$this->email = apply_filters("pmpro_email_recipient", $temail->email, $this);
115
$this->from = apply_filters("pmpro_email_sender", $temail->from, $this);
116
$this->fromname = apply_filters("pmpro_email_sender_name", $temail->fromname, $this);
117
+ $this->add_from_to_headers();
118
$this->subject = apply_filters("pmpro_email_subject", $temail->subject, $this);
119
$this->template = apply_filters("pmpro_email_template", $temail->template, $this);
120
$this->body = apply_filters("pmpro_email_body", $temail->body, $this);
131
}
132
}
133
134
+ /**
135
+ * Add the From Name and Email to the headers.
136
+ * @since 2.1
137
+ */
138
+ function add_from_to_headers() {
139
+ // Make sure we have a headers array
140
+ if ( empty( $this->headers ) ) {
141
+ $this->headers = array();
142
+ } elseif ( ! is_array( $this->headers ) ) {
143
+ $this->headers = array( $this->headers );
144
+ }
145
+
146
+ // Remove any previous from header
147
+ foreach( $this->headers as $key => $header ) {
148
+ if( strtolower( substr( $header, 0, 5 ) ) == 'from:' ) {
149
+ unset( $this->headers[$key] );
150
+ }
151
+ }
152
+
153
+ // Add From Email and Name or Just Email
154
+ if( !empty( $this->from ) && !empty( $this->fromname ) ) {
155
+ $this->headers[] = 'From:' . $this->fromname . ' <' . $this->from . '>';
156
+ } elseif( !empty( $this->from ) ) {
157
+ $this->headers[] = 'From:' . $this->from;
158
+ }
159
+ }
160
+
161
function sendCancelEmail($user = NULL, $old_level_id = NULL)
162
{
163
global $wpdb, $current_user;
981
982
return $this->sendEmail();
983
}
984
+
985
+ function sendPaymentActionRequiredEmail($user = NULL, $order = NULL, $invoice_url = NULL)
986
+ {
987
+ global $wpdb, $current_user;
988
+ if(!$user)
989
+ $user = $current_user;
990
+
991
+ if(!$user || !$order)
992
+ return false;
993
+
994
+ // if an invoice URL wasn't passed in, grab it from the order
995
+ if(empty($invoice_url) && isset($order->invoice_url))
996
+ $invoice_url = $order->invoice_url;
997
+
998
+ // still no invoice URL? bail
999
+ if(empty($invoice_url))
1000
+ return false;
1001
+
1002
+ $this->email = $user->user_email;
1003
+ $this->subject = sprintf(__("Payment action required for your %s membership", 'paid-memberships-pro' ), get_option("blogname"));
1004
+
1005
+ $this->template = "payment_action";
1006
+
1007
+ $this->template = apply_filters("pmpro_email_template", $this->template, $this);
1008
+
1009
+ $this->data = array(
1010
+ "subject" => $this->subject,
1011
+ "name" => $user->display_name,
1012
+ "display_name" => $user->display_name,
1013
+ "user_login" => $user->user_login,
1014
+ "sitename" => get_option("blogname"),
1015
+ "siteemail" => pmpro_getOption("from_email"),
1016
+ "invoice_url" => $invoice_url,
1017
+ );
1018
+
1019
+ return $this->sendEmail();
1020
+ }
1021
+
1022
+ function sendPaymentActionRequiredAdminEmail($user = NULL, $order = NULL, $invoice_url = NULL)
1023
+ {
1024
+ global $wpdb, $current_user;
1025
+ if(!$user)
1026
+ $user = $current_user;
1027
+
1028
+ if(!$user || !$order)
1029
+ return false;
1030
+
1031
+ // if an invoice URL wasn't passed in, grab it from the order
1032
+ if(empty($invoice_url) && isset($order->invoice_url))
1033
+ $invoice_url = $order->invoice_url;
1034
+
1035
+ // still no invoice URL? bail
1036
+ if(empty($invoice_url))
1037
+ return false;
1038
+
1039
+ $this->email = get_bloginfo("admin_email");
1040
+ $this->subject = sprintf(__("Payment action required: membership for %s at %s", 'paid-memberships-pro' ), $user->user_login, get_option("blogname"));
1041
+
1042
+ $this->template = "payment_action_admin";
1043
+
1044
+ $this->template = apply_filters("pmpro_email_template", $this->template, $this);
1045
+
1046
+ $this->data = array(
1047
+ "subject" => $this->subject,
1048
+ "name" => $user->display_name,
1049
+ "display_name" => $user->display_name,
1050
+ "user_login" => $user->user_login,
1051
+ "sitename" => get_option("blogname"),
1052
+ "siteemail" => pmpro_getOption("from_email"),
1053
+ "user_email" => $user->user_email,
1054
+ "invoice_url" => $invoice_url,
1055
+ );
1056
+
1057
+ return $this->sendEmail();
1058
+ }
1059
+
1060
1061
/**
1062
* Load the text for each default email template.
classes/gateways/class.pmprogateway_authorizenet.php CHANGED
@@ -58,7 +58,7 @@
58
'use_ssl',
59
'tax_state',
60
'tax_rate',
61
- 'accepted_credit_cards'
62
);
63
64
return $options;
58
'use_ssl',
59
'tax_state',
60
'tax_rate',
61
+ 'accepted_credit_cards',
62
);
63
64
return $options;
classes/gateways/class.pmprogateway_braintree.php CHANGED
@@ -200,7 +200,9 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
200
*/
201
static function checkLevelForPlan($level_id) {
202
$Gateway = new PMProGateway_braintree();
203
- $plan = $Gateway->getPlanByID('pmpro_' . $level_id);
204
if(!empty($plan))
205
return true;
206
else
@@ -226,7 +228,8 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
226
$current_gateway = pmpro_getGateway();
227
if( ( $default_gateway == "braintree" || $current_gateway == "braintree" && empty($_REQUEST['review']))) //$_REQUEST['review'] means the PayPal Express review page
228
{
229
- add_action( 'pmpro_save_membership_level', array( 'PMProGateway_braintree', 'pmpro_save_level_action') );
230
add_action('pmpro_checkout_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
231
add_action('pmpro_billing_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
232
add_filter('pmpro_checkout_order', array('PMProGateway_braintree', 'pmpro_checkout_order'));
@@ -268,7 +271,7 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
268
'use_ssl',
269
'tax_state',
270
'tax_rate',
271
- 'accepted_credit_cards'
272
);
273
274
return $options;
@@ -351,6 +354,29 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
351
</tr>
352
<?php
353
}
354
355
/**
356
* Filtering orders at checkout.
@@ -403,39 +429,6 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
403
?>
404
<input type='hidden' data-encrypted-name='expiration_date' id='credit_card_exp' />
405
<input type='hidden' name='AccountNumber' id='BraintreeAccountNumber' />
406
- <script type="text/javascript" src="https://js.braintreegateway.com/v1/braintree.js"></script>
407
- <script type="text/javascript">
408
- <!--
409
- /**
410
- * @since 1.9.5 - BUG FIX: substr() on undefined error
411
- */
412
- jQuery(document).ready(function() {
413
- //set up braintree encryption
414
- var braintree = Braintree.create('<?php echo pmpro_getOption("braintree_encryptionkey"); ?>');
415
- braintree.onSubmitEncryptForm('pmpro_form');
416
-
417
- //pass expiration dates in original format
418
- function pmpro_updateBraintreeCardExp()
419
- {
420
- jQuery('#credit_card_exp').val(jQuery('#ExpirationMonth').val() + "/" + jQuery('#ExpirationYear').val());
421
- }
422
- jQuery('#ExpirationMonth, #ExpirationYear').change(function() {
423
- pmpro_updateBraintreeCardExp();
424
- });
425
- pmpro_updateBraintreeCardExp();
426
-
427
- //pass last 4 of credit card
428
- function pmpro_updateBraintreeAccountNumber()
429
- {
430
- jQuery('#BraintreeAccountNumber').val('XXXXXXXXXXXXX' + jQuery('#AccountNumber').val().substr(jQuery('#AccountNumber').val().length - 4));
431
- }
432
- jQuery('#AccountNumber').change(function() {
433
- pmpro_updateBraintreeAccountNumber();
434
- });
435
- pmpro_updateBraintreeAccountNumber();
436
- });
437
- -->
438
- </script>
439
<?php
440
}
441
@@ -917,9 +910,10 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
917
//subscribe to the plan
918
try
919
{
920
$details = array(
921
'paymentMethodToken' => $this->customer->creditCards[0]->token,
922
- 'planId' => 'pmpro_' . $order->membership_id,
923
'price' => $amount
924
);
925
@@ -1058,4 +1052,23 @@ use Braintree\WebhookNotification as Braintree_WebhookNotification;
1058
update_user_meta($user_id, 'pmpro_braintree_customerid', $pmpro_braintree_customerid);
1059
}
1060
}
1061
}
200
*/
201
static function checkLevelForPlan($level_id) {
202
$Gateway = new PMProGateway_braintree();
203
+
204
+ $plan = $Gateway->getPlanByID( $Gateway->get_plan_id( $level_id ) );
205
+
206
if(!empty($plan))
207
return true;
208
else
228
$current_gateway = pmpro_getGateway();
229
if( ( $default_gateway == "braintree" || $current_gateway == "braintree" && empty($_REQUEST['review']))) //$_REQUEST['review'] means the PayPal Express review page
230
{
231
+ add_action('pmpro_checkout_preheader', array('PMProGateway_braintree', 'pmpro_checkout_preheader'));
232
+ add_action( 'pmpro_save_membership_level', array( 'PMProGateway_braintree', 'pmpro_save_level_action') );
233
add_action('pmpro_checkout_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
234
add_action('pmpro_billing_before_submit_button', array('PMProGateway_braintree', 'pmpro_checkout_before_submit_button'));
235
add_filter('pmpro_checkout_order', array('PMProGateway_braintree', 'pmpro_checkout_order'));
271
'use_ssl',
272
'tax_state',
273
'tax_rate',
274
+ 'accepted_credit_cards',
275
);
276
277
return $options;
354
</tr>
355
<?php
356
}
357
+
358
+ /**
359
+ * Code added to checkout preheader.
360
+ *
361
+ * @since 2.1
362
+ */
363
+ static function pmpro_checkout_preheader() {
364
+ global $gateway, $pmpro_level;
365
+
366
+ $default_gateway = pmpro_getOption("gateway");
367
+
368
+ if(($gateway == "braintree" || $default_gateway == "braintree") && !pmpro_isLevelFree($pmpro_level)) {
369
+ wp_enqueue_script("stripe", "https://js.braintreegateway.com/v1/braintree.js", array(), NULL);
370
+ wp_register_script( 'pmpro_braintree',
371
+ plugins_url( 'js/pmpro-braintree.js', PMPRO_BASE_FILE ),
372
+ array( 'jquery' ),
373
+ PMPRO_VERSION );
374
+ wp_localize_script( 'pmpro_braintree', 'pmpro_braintree', array(
375
+ 'encryptionkey' => pmpro_getOption( 'braintree_encryptionkey' )
376
+ ));
377
+ wp_enqueue_script( 'pmpro_braintree' );
378
+ }
379
+ }
380
381
/**
382
* Filtering orders at checkout.
429
?>
430
<input type='hidden' data-encrypted-name='expiration_date' id='credit_card_exp' />
431
<input type='hidden' name='AccountNumber' id='BraintreeAccountNumber' />
432
<?php
433
}
434
910
//subscribe to the plan
911
try
912
{
913
+
914
$details = array(
915
'paymentMethodToken' => $this->customer->creditCards[0]->token,
916
+ 'planId' => $this->get_plan_id( $order->membership_id ),
917
'price' => $amount
918
);
919
1052
update_user_meta($user_id, 'pmpro_braintree_customerid', $pmpro_braintree_customerid);
1053
}
1054
}
1055
+
1056
+ /**
1057
+ * Gets the Braintree plan ID for a given level ID
1058
+ * @param int $level_id level to get plan ID for
1059
+ * @return string Braintree plan ID
1060
+ */
1061
+ static function get_plan_id( $level_id ) {
1062
+ /**
1063
+ * Filter pmpro_braintree_plan_id
1064
+ *
1065
+ * Used to change the Braintree plan ID for a given level
1066
+ *
1067
+ * @since 2.1.0
1068
+ *
1069
+ * @param string $plan_id for the given level
1070
+ * @param int $level_id the level id to make a plan id for
1071
+ */
1072
+ return apply_filters( 'pmpro_braintree_plan_id', 'pmpro_' . $level_id, $level_id );
1073
}
1074
+ }
classes/gateways/class.pmprogateway_cybersource.php CHANGED
@@ -1,21 +1,17 @@
1
<?php
2
//include pmprogateway
3
require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
-
5
//load classes init method
6
add_action('init', array('PMProGateway_cybersource', 'init'));
7
-
8
class PMProGateway_cybersource extends PMProGateway
9
{
10
function __construct($gateway = NULL)
11
{
12
if(!class_exists("CyberSourceSoapClient"))
13
require_once(dirname(__FILE__) . "/../../includes/lib/CyberSource/cyber_source_soap_client.php");
14
-
15
$this->gateway = $gateway;
16
return $this->gateway;
17
}
18
-
19
/**
20
* Run on WP init
21
*
@@ -25,12 +21,10 @@
25
{
26
//make sure CyberSource is a gateway option
27
add_filter('pmpro_gateways', array('PMProGateway_cybersource', 'pmpro_gateways'));
28
-
29
//add fields to payment settings
30
add_filter('pmpro_payment_options', array('PMProGateway_cybersource', 'pmpro_payment_options'));
31
add_filter('pmpro_payment_option_fields', array('PMProGateway_cybersource', 'pmpro_payment_option_fields'), 10, 2);
32
}
33
-
34
/**
35
* Make sure this gateway is in the gateways list
36
*
@@ -40,10 +34,8 @@
40
{
41
if(empty($gateways['cybersource']))
42
$gateways['cybersource'] = __('CyberSource', 'paid-memberships-pro' );
43
-
44
return $gateways;
45
}
46
-
47
/**
48
* Get a list of payment options that the this gateway needs/supports.
49
*
@@ -61,12 +53,10 @@
61
'use_ssl',
62
'tax_state',
63
'tax_rate',
64
- 'accepted_credit_cards'
65
);
66
-
67
return $options;
68
}
69
-
70
/**
71
* Set payment options for payment settings page.
72
*
@@ -76,13 +66,10 @@
76
{
77
//get stripe options
78
$cybersource_options = PMProGateway_cybersource::getGatewayOptions();
79
-
80
//merge with others.
81
$options = array_merge($cybersource_options, $options);
82
-
83
return $options;
84
}
85
-
86
/**
87
* Display fields for this gateway's options.
88
*
@@ -119,7 +106,6 @@
119
</tr>
120
<?php
121
}
122
-
123
/**
124
* Process checkout.
125
*
@@ -141,7 +127,6 @@
141
$order->TrialBillingFrequency = $order->BillingFrequency;
142
$order->TrialBillingCycles = 1;
143
$order->TrialAmount = 0;
144
-
145
//add a billing cycle to make up for the trial, if applicable
146
if(!empty($order->TotalBillingCycles))
147
$order->TotalBillingCycles++;
@@ -151,7 +136,6 @@
151
//it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
152
$order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
153
$order->TrialBillingCycles++;
154
-
155
//add a billing cycle to make up for the trial, if applicable
156
if($order->TotalBillingCycles)
157
$order->TotalBillingCycles++;
@@ -161,7 +145,6 @@
161
//add a period to the start date to account for the initial payment
162
$order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
163
}
164
-
165
$order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
166
return $this->subscribe($order);
167
}
@@ -188,7 +171,6 @@
188
$order->TrialBillingFrequency = $order->BillingFrequency;
189
$order->TrialBillingCycles = 1;
190
$order->TrialAmount = 0;
191
-
192
//add a billing cycle to make up for the trial, if applicable
193
if(!empty($order->TotalBillingCycles))
194
$order->TotalBillingCycles++;
@@ -198,7 +180,6 @@
198
//it has a trial, but the amount is the same as the initial payment, so we can squeeze it in there
199
$order->ProfileStartDate = date_i18n("Y-m-d") . "T0:0:0";
200
$order->TrialBillingCycles++;
201
-
202
//add a billing cycle to make up for the trial, if applicable
203
if(!empty($order->TotalBillingCycles))
204
$order->TotalBillingCycles++;
@@ -208,7 +189,6 @@
208
//add a period to the start date to account for the initial payment
209
$order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0";
210
}
211
-
212
$order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
213
if($this->subscribe($order))
214
{
@@ -225,10 +205,8 @@
225
{
226
if(!$order->error)
227
$order->error = __("Unknown error: Payment failed.", 'paid-memberships-pro' );
228
-
229
$order->error .= " " . __("A partial payment was made that we could not void. Please contact the site owner immediately to correct this.", 'paid-memberships-pro' );
230
}
231
-
232
return false;
233
}
234
}
@@ -243,12 +221,10 @@
243
{
244
if(empty($order->error))
245
$order->error = __("Unknown error: Payment failed.", 'paid-memberships-pro' );
246
-
247
return false;
248
}
249
}
250
}
251
-
252
function getCardType($name)
253
{
254
$card_types = array(
@@ -263,13 +239,11 @@
263
'Carte Blanche' => '006',
264
'JCB' => '007'
265
);
266
-
267
if(isset($card_types[$name]))
268
return $card_types[$name];
269
else
270
return false;
271
}
272
-
273
function getWSDL($order)
274
{
275
//which gateway environment?
@@ -277,61 +251,45 @@
277
$gateway_environment = pmpro_getOption("gateway_environment");
278
else
279
$gateway_environment = $order->gateway_environment;
280
-
281
//which host?
282
if($gateway_environment == "live")
283
$host = "ics2ws.ic3.com";
284
else
285
$host = "ics2wstest.ic3.com";
286
-
287
//path
288
- $path = "/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.90.wsdl";
289
-
290
//build url
291
$wsdl_url = "https://" . $host . $path;
292
-
293
//filter
294
$wsdl_url = apply_filters("pmpro_cybersource_wsdl_url", $wsdl_url, $gateway_environment);
295
-
296
return $wsdl_url;
297
}
298
-
299
function authorize(&$order)
300
{
301
global $pmpro_currency;
302
-
303
if(empty($order->code))
304
$order->code = $order->getRandomCode();
305
-
306
$wsdl_url = $this->getWSDL($order);
307
-
308
//what amount to authorize? just $1 to test
309
$amount = "1.00";
310
-
311
//combine address
312
$address = $order->Address1;
313
if(!empty($order->Address2))
314
$address .= "\n" . $order->Address2;
315
-
316
//customer stuff
317
$customer_email = $order->Email;
318
$customer_phone = $order->billing->phone;
319
-
320
if(!isset($order->membership_level->name))
321
$order->membership_level->name = "";
322
-
323
//to store our request
324
$request = new stdClass();
325
-
326
//which service?
327
$ccAuthService = new stdClass();
328
$ccAuthService->run = "true";
329
$request->ccAuthService = $ccAuthService;
330
-
331
//merchant id and order code
332
$request->merchantID = pmpro_getOption("cybersource_merchantid");
333
$request->merchantReferenceCode = $order->code;
334
-
335
//bill to
336
$billTo = new stdClass();
337
$billTo->firstName = $order->FirstName;
@@ -344,7 +302,6 @@
344
$billTo->email = $order->Email;
345
$billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
346
$request->billTo = $billTo;
347
-
348
//card
349
$card = new stdClass();
350
$card->cardType = $this->getCardType($order->cardtype);
@@ -354,6 +311,13 @@
354
$card->cvNumber = $order->CVV2;
355
$request->card = $card;
356
357
//currency
358
$purchaseTotals = new stdClass();
359
$purchaseTotals->currency = $pmpro_currency;
@@ -368,8 +332,24 @@
368
$item0->id = $order->membership_id;
369
$request->item = array($item0);
370
371
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
372
- $reply = $soapClient->runTransaction($request);
373
374
if($reply->reasonCode == "100")
375
{
@@ -382,36 +362,46 @@
382
{
383
//error
384
$order->errorcode = $reply->reasonCode;
385
- $order->error = $this->getErrorFromCode($reply->reasonCode);
386
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
387
return false;
388
}
389
}
390
-
391
function void(&$order)
392
{
393
//need a transaction id
394
if(empty($order->payment_transaction_id))
395
return false;
396
-
397
//get wsdl
398
$wsdl_url = $this->getWSDL($order);
399
-
400
//to store our request
401
$request = new stdClass();
402
-
403
//which service?
404
$voidService = new stdClass();
405
$voidService->run = "true";
406
$voidService->voidRequestID = $order->payment_transaction_id;
407
$request->voidService = $voidService;
408
-
409
//merchant id and order code
410
$request->merchantID = pmpro_getOption("cybersource_merchantid");
411
$request->merchantReferenceCode = $order->code;
412
413
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
414
- $reply = $soapClient->runTransaction($request);
415
416
if($reply->reasonCode == "100")
417
{
@@ -424,59 +414,46 @@
424
{
425
//error
426
$order->errorcode = $reply->reasonCode;
427
- $order->error = $this->getErrorFromCode($reply->reasonCode);
428
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
429
return false;
430
}
431
}
432
-
433
function charge(&$order)
434
{
435
global $pmpro_currency;
436
-
437
//get a code
438
if(empty($order->code))
439
$order->code = $order->getRandomCode();
440
-
441
//get wsdl
442
$wsdl_url = $this->getWSDL($order);
443
-
444
//what amount to charge?
445
$amount = $order->InitialPayment;
446
-
447
//tax
448
$order->subtotal = $amount;
449
$tax = $order->getTax(true);
450
$amount = pmpro_round_price((float)$order->subtotal + (float)$tax);
451
-
452
//combine address
453
$address = $order->Address1;
454
if(!empty($order->Address2))
455
$address .= "\n" . $order->Address2;
456
-
457
//customer stuff
458
$customer_email = $order->Email;
459
$customer_phone = $order->billing->phone;
460
-
461
if(!isset($order->membership_level->name))
462
$order->membership_level->name = "";
463
-
464
//to store our request
465
$request = new stdClass();
466
-
467
//authorize and capture
468
$ccAuthService = new stdClass();
469
$ccAuthService->run = "true";
470
$request->ccAuthService = $ccAuthService;
471
-
472
$ccCaptureService = new stdClass();
473
$ccCaptureService->run = "true";
474
$request->ccCaptureService = $ccCaptureService;
475
-
476
//merchant id and order code
477
$request->merchantID = pmpro_getOption("cybersource_merchantid");
478
$request->merchantReferenceCode = $order->code;
479
-
480
//bill to
481
$billTo = new stdClass();
482
$billTo->firstName = $order->FirstName;
@@ -489,7 +466,6 @@
489
$billTo->email = $order->Email;
490
$billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
491
$request->billTo = $billTo;
492
-
493
//card
494
$card = new stdClass();
495
$card->cardType = $this->getCardType($order->cardtype);
@@ -499,11 +475,17 @@
499
$card->cvNumber = $order->CVV2;
500
$request->card = $card;
501
502
//currency
503
$purchaseTotals = new stdClass();
504
$purchaseTotals->currency = $pmpro_currency;
505
$request->purchaseTotals = $purchaseTotals;
506
-
507
//item/price
508
$item0 = new stdClass();
509
$item0->unitPrice = $amount;
@@ -513,8 +495,23 @@
513
$item0->id = $order->membership_id;
514
$request->item = array($item0);
515
516
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
517
- $reply = $soapClient->runTransaction($request);
518
519
if($reply->reasonCode == "100")
520
{
@@ -527,39 +524,31 @@
527
{
528
//error
529
$order->errorcode = $reply->reasonCode;
530
- $order->error = $this->getErrorFromCode($reply->reasonCode);
531
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
532
return false;
533
}
534
}
535
-
536
function subscribe(&$order)
537
{
538
global $pmpro_currency;
539
-
540
//create a code for the order
541
if(empty($order->code))
542
$order->code = $order->getRandomCode();
543
-
544
//filter order before subscription. use with care.
545
$order = apply_filters("pmpro_subscribe_order", $order, $this);
546
-
547
//get wsdl
548
$wsdl_url = $this->getWSDL($order);
549
-
550
//to store our request
551
$request = new stdClass();
552
-
553
//set service type
554
$paySubscriptionCreateService = new stdClass();
555
$paySubscriptionCreateService->run = 'true';
556
$paySubscriptionCreateService->disableAutoAuth = 'true'; //we do our own auth check
557
$request->paySubscriptionCreateService = $paySubscriptionCreateService;
558
-
559
//merchant id and order code
560
$request->merchantID = pmpro_getOption("cybersource_merchantid");
561
$request->merchantReferenceCode = $order->code;
562
-
563
/*
564
set up billing amount/etc
565
*/
@@ -567,7 +556,6 @@
567
$amount = $order->PaymentAmount;
568
$amount_tax = $order->getTaxForPrice($amount);
569
$amount = pmpro_round_price((float)$amount + (float)$amount_tax);
570
-
571
/*
572
There are two parts to the trial. Part 1 is simply the delay until the first payment
573
since we are doing the first payment as a separate transaction.
@@ -582,16 +570,12 @@
582
$trial_period_days = $order->BillingFrequency * 7; //weekly
583
else
584
$trial_period_days = $order->BillingFrequency * 30; //assume monthly
585
-
586
//convert to a profile start date
587
$order->ProfileStartDate = date_i18n("Y-m-d", strtotime("+ " . $trial_period_days . " Day", current_time("timestamp"))) . "T0:0:0";
588
-
589
//filter the start date
590
$order->ProfileStartDate = apply_filters("pmpro_profile_start_date", $order->ProfileStartDate, $order);
591
-
592
//convert back to days
593
$trial_period_days = ceil(abs(strtotime(date_i18n("Y-m-d"), current_time('timestamp')) - strtotime($order->ProfileStartDate, current_time("timestamp"))) / 86400);
594
-
595
//now add the actual trial set by the site
596
if(!empty($order->TrialBillingCycles))
597
{
@@ -605,10 +589,8 @@
605
else
606
$trial_period_days = $trial_period_days + (30 * $order->BillingFrequency * $trialOccurrences); //assume monthly
607
}
608
-
609
//convert back into a date
610
$profile_start_date = date_i18n("Ymd", strtotime("+ " . $trial_period_days . " Days"));
611
-
612
//figure out the frequency
613
if($order->BillingPeriod == "Year")
614
{
@@ -641,7 +623,7 @@
641
elseif($order->BillingFrequency == 183)
642
$frequency = "semi annually";
643
elseif($order->BillingFrequency == 90)
644
- $frequency = "quaterly";
645
elseif($order->BillingFrequency == 30)
646
$frequency = "monthly";
647
elseif($order->BillingFrequency == 15)
@@ -653,13 +635,11 @@
653
elseif($order->BillingFrequency == 7)
654
$frequency = "weekly";
655
}
656
-
657
//set subscription info for API
658
$subscription = new stdClass();
659
$subscription->title = $order->membership_level->name;
660
$subscription->paymentMethod = "credit card";
661
$request->subscription = $subscription;
662
-
663
//recurring info
664
$recurringSubscriptionInfo = new stdClass();
665
$recurringSubscriptionInfo->amount = number_format($amount, 2);
@@ -668,12 +648,10 @@
668
if(!empty($order->TotalBillingCycles))
669
$recurringSubscriptionInfo->numberOfPayments = $order->TotalBillingCycles;
670
$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
671
-
672
//combine address
673
$address = $order->Address1;
674
if(!empty($order->Address2))
675
$address .= "\n" . $order->Address2;
676
-
677
//bill to
678
$billTo = new stdClass();
679
$billTo->firstName = $order->FirstName;
@@ -686,7 +664,6 @@
686
$billTo->email = $order->Email;
687
$billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
688
$request->billTo = $billTo;
689
-
690
//card
691
$card = new stdClass();
692
$card->cardType = $this->getCardType($order->cardtype);
@@ -696,13 +673,35 @@
696
$card->cvNumber = $order->CVV2;
697
$request->card = $card;
698
699
//currency
700
$purchaseTotals = new stdClass();
701
$purchaseTotals->currency = $pmpro_currency;
702
$request->purchaseTotals = $purchaseTotals;
703
704
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
705
- $reply = $soapClient->runTransaction($request);
706
707
if($reply->reasonCode == "100")
708
{
@@ -716,39 +715,32 @@
716
//error
717
$order->status = "error";
718
$order->errorcode = $reply->reasonCode;
719
- $order->error = $this->getErrorFromCode($reply->reasonCode);
720
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
721
return false;
722
}
723
}
724
-
725
function update(&$order)
726
{
727
//get wsdl
728
$wsdl_url = $this->getWSDL($order);
729
-
730
//to store our request
731
$request = new stdClass();
732
-
733
//set service type
734
$paySubscriptionUpdateService = new stdClass();
735
$paySubscriptionUpdateService ->run = "true";
736
$request->paySubscriptionUpdateService = $paySubscriptionUpdateService ;
737
-
738
//merchant id and order code
739
$request->merchantID = pmpro_getOption("cybersource_merchantid");
740
$request->merchantReferenceCode = $order->code;
741
-
742
//set subscription info for API
743
$recurringSubscriptionInfo = new stdClass();
744
$recurringSubscriptionInfo->subscriptionID = $order->subscription_transaction_id;
745
$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
746
-
747
//combine address
748
$address = $order->Address1;
749
if(!empty($order->Address2))
750
$address .= "\n" . $order->Address2;
751
-
752
//bill to
753
$billTo = new stdClass();
754
$billTo->firstName = $order->FirstName;
@@ -761,7 +753,6 @@
761
$billTo->email = $order->Email;
762
$billTo->ipAddress = $_SERVER['REMOTE_ADDR'];
763
$request->billTo = $billTo;
764
-
765
//card
766
$card = new stdClass();
767
$card->cardType = $this->getCardType($order->cardtype);
@@ -771,8 +762,30 @@
771
$card->cvNumber = $order->CVV2;
772
$request->card = $card;
773
774
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
775
- $reply = $soapClient->runTransaction($request);
776
777
if($reply->reasonCode == "100")
778
{
@@ -783,8 +796,8 @@
783
{
784
//error
785
$order->errorcode = $reply->reasonCode;
786
- $order->error = $this->getErrorFromCode($reply->reasonCode);
787
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
788
return false;
789
}
790
}
@@ -794,29 +807,39 @@
794
//require a subscription id
795
if(empty($order->subscription_transaction_id))
796
return false;
797
-
798
//get wsdl
799
$wsdl_url = $this->getWSDL($order);
800
-
801
//to store our request
802
$request = new stdClass();
803
-
804
//which service?
805
$paySubscriptionDeleteService = new stdClass();
806
$paySubscriptionDeleteService ->run = "true";
807
$request->paySubscriptionDeleteService = $paySubscriptionDeleteService ;
808
-
809
//which order
810
$recurringSubscriptionInfo = new stdClass();
811
$recurringSubscriptionInfo->subscriptionID = $order->subscription_transaction_id;
812
$request->recurringSubscriptionInfo = $recurringSubscriptionInfo;
813
-
814
//merchant id and order code
815
$request->merchantID = pmpro_getOption("cybersource_merchantid");
816
$request->merchantReferenceCode = $order->code;
817
818
- $soapClient = new CyberSourceSoapClient($wsdl_url, array("merchantID"=>pmpro_getOption("cybersource_merchantid"), "transactionKey"=>pmpro_getOption("cybersource_securitykey")));
819
- $reply = $soapClient->runTransaction($request);
820
821
if($reply->reasonCode == "100")
822
{
@@ -828,58 +851,85 @@
828
{
829
//error
830
$order->errorcode = $reply->reasonCode;
831
- $order->error = $this->getErrorFromCode($reply->reasonCode);
832
- $order->shorterror = $this->getErrorFromCode($reply->reasonCode);
833
return false;
834
}
835
}
836
837
- function getErrorFromCode($code)
838
{
839
$error_messages = array(
840
- "100" => "Successful transaction.",
841
- "101" => "The request is missing one or more required fields.",
842
- "102" => "One or more fields in the request contains invalid data. Check that your billing address is valid.",
843
- "104" => "Duplicate order detected.",
844
- "110" => "Only partial amount was approved.",
845
- "150" => "Error: General system failure.",
846
- "151" => "Error: The request was received but there was a server timeout.",
847
- "152" => "Error: The request was received, but a service did not finish running in time. ",
848
- "200" => "Address Verification Service (AVS) failure.",
849
- "201" => "Authorization failed.",
850
- "202" => "Expired card or invalid expiration date.",
851
- "203" => "The card was declined.",
852
- "204" => "Insufficient funds in the account.",
853
- "205" => "Stolen or lost card.",
854
- "207" => "Issuing bank unavailable.",
855
- "208" => "Inactive card or card not authorized for card-not-present transactions.",
856
- "209" => "American Express Card Identification Digits (CID) did not match.",
857
- "210" => "The card has reached the credit limit. ",
858
- "211" => "Invalid card verification number.",
859
- "221" => "The customer matched an entry on the processors negative file. ",
860
- "230" => "Card verification (CV) check failed.",
861
- "231" => "Invalid account number.",
862
- "232" => "The card type is not accepted by the payment processor.",
863
- "233" => "General decline by the processor.",
864
- "234" => "There is a problem with your CyberSource merchant configuration.",
865
- "235" => "The requested amount exceeds the originally authorized amount.",
866
- "236" => "Processor failure.",
867
- "237" => "The authorization has already been reversed.",
868
- "238" => "The authorization has already been captured.",
869
- "239" => "The requested transaction amount must match the previous transaction amount.",
870
- "240" => "The card type sent is invalid or does not correlate with the credit card number.",
871
- "241" => "The referenced request id is invalid for all follow-on transactions.",
872
- "242" => "The request ID is invalid.",
873
- "243" => "The transaction has already been settled or reversed.",
874
- "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.",
875
- "247" => "You requested a credit for a capture that was previously voided.",
876
- "250" => "Error: The request was received, but there was a timeout at the payment processor.",
877
- "520" => "Smart Authorization failed."
878
);
879
880
- if(isset($error_messages[$code]))
881
- return $error_messages[$code];
882
else
883
- return "Unknown error.";
884
}
885
}
1
<?php
2
//include pmprogateway
3
require_once(dirname(__FILE__) . "/class.pmprogateway.php");
4
//load classes init method
5
add_action('init', array('PMProGateway_cybersource', 'init'));
6
class PMProGateway_cybersource extends PMProGateway
7
{
8
function __construct($gateway = NULL)
9
{
10
if(!class_exists("CyberSourceSoapClient"))
11
require_once(dirname(__FILE__) . "/../../includes/lib/CyberSource/cyber_source_soap_client.php");
12
$this->gateway = $gateway;
13
return $this->gateway;
14
}
15
/**
16
* Run on WP init
17
*
21
{
22
//make sure CyberSource is a gateway option
23
add_filter('pmpro_gateways', array('PMProGateway_cybersource', 'pmpro_gateways'));
24
//add fields to payment settings
25
add_filter('pmpro_payment_options', array('PMProGateway_cybersource', 'pmpro_payment_options'));
26
add_filter('pmpro_payment_option_fields', array('PMProGateway_cybersource', 'pmpro_payment_option_fields'), 10, 2);
27
}
28
/**
29
* Make sure this gateway is in the gateways list
30
*
34
{
35
if(empty($gateways['cybersource']))
36
$gateways['cybersource'] = __('CyberSource', 'paid-memberships-pro' );
37
return $gateways;
38
}
39
/**
40
* Get a list of payment options that the this gateway needs/supports.
41
*
53
'use_ssl',
54
'tax_state',
55
'tax_rate',
56
+ 'accepted_credit_cards',
57
);
58
return $options;
59
}
60
/**
61
* Set payment options for payment settings page.
62
*
66
{
67
//get stripe options
68
$cybersource_options = PMProGateway_cybersource::getGatewayOptions();
69
//merge with others.
70
$options = array_merge($cybersource_options, $options);
71
return $options;
72
}
73
/**
74
* Display fields for this gateway's options.
75
*
106
</tr>
107
<?php
108
}
109
/**
110
* Process checkout.
111
*
127