PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260814
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260814
260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 All 189 releases
s2member / src / includes / menu-pages / paypal-ops.inc.php

paypal-ops.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions 260814, at src/includes/menu-pages/paypal-ops.inc.php

1,861 lines 145.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreFile
3 /**
4 * Menu page for the s2Member plugin (PayPal Options page).
5 *
6 * Copyright: © 2009-2011
7 * {@link http://websharks-inc.com/ WebSharks, Inc.}
8 * (coded in the USA)
9 *
10 * Released under the terms of the GNU General Public License.
11 * You should have received a copy of the GNU General Public License,
12 * along with this software. In the main directory, see: /licensing/
13 * If not, see: {@link http://www.gnu.org/licenses/}.
14 *
15 * @package s2Member\Menu_Pages
16 * @since 3.0
17 */
18 if(!defined('WPINC')) // MUST have WordPress.
19 exit("Do not access this file directly.");
20
21 if(!class_exists("c_ws_plugin__s2member_menu_page_paypal_ops"))
22 {
23 /**
24 * Menu page for the s2Member plugin (PayPal Options page).
25 *
26 * @package s2Member\Menu_Pages
27 * @since 110531
28 */
29 class c_ws_plugin__s2member_menu_page_paypal_ops
30 {
31 public function __construct()
32 {
33 $ppco_webhook_notice = '';
34 $ppco_creds_notice = '';
35 $ppco_cache_notice = '';
36
37 $ppco_r = !empty($_POST) ? $_POST : $_GET;
38 if(!empty($ppco_r['s2member_ppco_webhook']) && empty($ppco_r['s2member_ppco_creds_test']) && empty($ppco_r['s2member_ppco_clear_cache']) && current_user_can('manage_options'))
39 {
40 if(!empty($ppco_r['_wpnonce']) && wp_verify_nonce((string)$ppco_r['_wpnonce'], 's2member_ppco_webhook'))
41 {
42 $env = (!empty($ppco_r['ppco_webhook_env']) && $ppco_r['ppco_webhook_env'] === 'sandbox') ? 'sandbox' : 'live';
43 $env_label = ($env === 'sandbox') ? esc_html__('Sandbox', 's2member') : esc_html__('Live', 's2member');
44
45 $client_id = '';
46 $secret = '';
47
48 if($env === 'sandbox')
49 {
50 $client_id = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"];
51 $secret = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"];
52 }
53 else // live
54 {
55 $client_id = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"];
56 $secret = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"];
57 }
58
59 if(!empty($client_id) && !empty($secret))
60 {
61 $scheme = strtolower((string)wp_parse_url(home_url('/'), PHP_URL_SCHEME));
62
63 //260224 Reverse-proxy/Cloudflare fallback for admin-side HTTPS detection.
64 // This only affects the admin gating check here. It does NOT change the webhook URL itself.
65 $proxy_https = false;
66 if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && stripos(strtolower((string)$_SERVER['HTTP_X_FORWARDED_PROTO']), 'https') !== false)
67 $proxy_https = true;
68 else if(!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower((string)$_SERVER['HTTP_X_FORWARDED_SSL']) === 'on')
69 $proxy_https = true;
70 else if(!empty($_SERVER['HTTPS']) && in_array(strtolower((string)$_SERVER['HTTPS']), array('on', '1'), true))
71 $proxy_https = true;
72 else if(!empty($_SERVER['HTTP_CF_VISITOR']) && stripos((string)$_SERVER['HTTP_CF_VISITOR'], '"scheme":"https"') !== false)
73 $proxy_https = true;
74
75 if($scheme !== 'https' && !$proxy_https)
76 $ppco_webhook_notice = '<div class="error"><p>'.esc_html__('Unable to create/update PayPal Checkout webhook. PayPal requires an HTTPS (SSL) webhook URL, but your site URL appears to be configured without HTTPS. Please enable SSL and update your WordPress Site URL/Home URL to use https://, then try again.', 's2member').'</p></div>'."\n";
77 else
78 {
79 //260216 IMPORTANT:
80 // Do not block webhook creation/update based on a server-side "loopback" probe.
81 // Many shared hosts/WAFs return false negatives for loopback requests (e.g., HEAD blocked,
82 // ModSecurity rules, disabled loopback/outbound HTTP). PayPal can still deliver webhooks
83 // successfully from the outside.
84
85 $r = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_webhook_upsert($env);
86
87 if(!empty($r['id']))
88 $ppco_webhook_notice = '<div class="updated"><p>'.sprintf(esc_html__('PayPal Checkout %1$s webhook created/updated successfully.', 's2member'), $env_label).'</p></div>'."\n";
89 else
90 $ppco_webhook_notice = '<div class="error"><p>'.sprintf(esc_html__('Unable to create/update PayPal Checkout %1$s webhook. See the s2Member paypal-checkout.log for details.', 's2member'), $env_label).' <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">'.esc_html__('Log Viewer', 's2member').'</a></p></div>'."\n";
91 }
92 }
93 else
94 $ppco_webhook_notice = '<div class="error"><p>'.sprintf(esc_html__('PayPal Checkout %1$s credentials are not configured.', 's2member'), $env_label).'</p></div>'."\n";
95 }
96 else
97 $ppco_webhook_notice = '<div class="error"><p>'.esc_html__('Invalid request. Please try again.', 's2member').'</p></div>'."\n";
98 }
99
100 // No redirect; display notice inline.
101
102 if(!empty($ppco_r['s2member_ppco_creds_test']) && empty($ppco_r['s2member_ppco_webhook']) && empty($ppco_r['s2member_ppco_clear_cache']) && current_user_can('manage_options'))
103 {
104 if(!empty($ppco_r['_wpnonce']) && wp_verify_nonce((string)$ppco_r['_wpnonce'], 's2member_ppco_creds_test'))
105 {
106 $env = (!empty($ppco_r['ppco_creds_env']) && $ppco_r['ppco_creds_env'] === 'sandbox') ? 'sandbox' : 'live';
107 $env_label = ($env === 'sandbox') ? esc_html__('Sandbox', 's2member') : esc_html__('Live', 's2member');
108
109 $client_id = '';
110 $secret = '';
111
112 if($env === 'sandbox')
113 {
114 $client_id = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"];
115 $secret = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"];
116 }
117 else // live
118 {
119 $client_id = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"];
120 $secret = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"];
121 }
122
123 if(!empty($client_id) && !empty($secret))
124 {
125 $ok = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_creds_test($env);
126
127 if($ok)
128 $ppco_creds_notice = '<div class="updated"><p>'.sprintf(esc_html__('PayPal Checkout %1$s credentials test OK.', 's2member'), $env_label).'</p></div>'."\n";
129 else
130 $ppco_creds_notice = '<div class="error"><p>'.sprintf(esc_html__('PayPal Checkout %1$s credentials test failed. Please verify that the Client ID and Secret were copied from the same PayPal %1$s REST app, paste them again, save options, and test again. See the s2Member paypal-checkout.log for details.', 's2member'), $env_label).' <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">'.esc_html__('Log Viewer', 's2member').'</a></p></div>'."\n";
131 }
132 else
133 $ppco_creds_notice = '<div class="error"><p>'.sprintf(esc_html__('PayPal Checkout %1$s credentials are not configured.', 's2member'), $env_label).'</p></div>'."\n";
134 }
135 else
136 $ppco_creds_notice = '<div class="error"><p>'.esc_html__('Invalid request. Please try again.', 's2member').'</p></div>'."\n";
137 }
138
139 // No redirect; display notice inline.
140
141 if(!empty($ppco_r['s2member_ppco_clear_cache']) && empty($ppco_r['s2member_ppco_webhook']) && empty($ppco_r['s2member_ppco_creds_test']) && current_user_can('manage_options'))
142 {
143 if(!empty($ppco_r['_wpnonce']) && wp_verify_nonce((string)$ppco_r['_wpnonce'], 's2member_ppco_clear_cache'))
144 {
145 $env = (!empty($ppco_r['ppco_clear_env']) && $ppco_r['ppco_clear_env'] === 'sandbox') ? 'sandbox' : 'live';
146 $env_label = ($env === 'sandbox') ? esc_html__('Sandbox', 's2member') : esc_html__('Live', 's2member');
147
148 c_ws_plugin__s2member_paypal_utilities::paypal_checkout_clear_cache($env);
149
150 $ppco_cache_notice = '<div class="updated"><p>'.sprintf(esc_html__('PayPal Checkout cache cleared for %1$s.', 's2member'), $env_label).'</p></div>'."\n";
151 }
152 else
153 $ppco_cache_notice = '<div class="error"><p>'.esc_html__('Invalid request. Please try again.', 's2member').'</p></div>'."\n";
154 }
155
156 // No redirect; display notice inline.
157
158 echo '<div class="wrap ws-menu-page">'."\n";
159 echo $ppco_webhook_notice;
160 echo $ppco_creds_notice;
161 echo $ppco_cache_notice;
162
163 echo '<div class="wp-header-end"></div>'."\n";
164
165 echo '<div class="ws-menu-page-toolbox">'."\n";
166 c_ws_plugin__s2member_menu_pages_tb::display();
167 echo '</div>'."\n";
168
169 echo '<h2>PayPal Options</h2>'."\n";
170
171 echo '<table class="ws-menu-page-table">'."\n";
172 echo '<tbody class="ws-menu-page-table-tbody">'."\n";
173 echo '<tr class="ws-menu-page-table-tr">'."\n";
174 echo '<td class="ws-menu-page-table-l">'."\n";
175
176 do_action("ws_plugin__s2member_during_paypal_ops_page_before_left_sections_form", get_defined_vars());
177
178 echo '<form method="post" name="ws_plugin__s2member_options_form" id="ws-plugin--s2member-options-form" autocomplete="off">'."\n";
179 echo '<input type="hidden" name="ws_plugin__s2member_options_save" id="ws-plugin--s2member-options-save" value="'.esc_attr(wp_create_nonce("ws-plugin--s2member-options-save")).'" />'."\n";
180 echo '<input type="hidden" name="ws_plugin__s2member_configured" id="ws-plugin--s2member-configured" value="1" />'."\n";
181
182 do_action("ws_plugin__s2member_during_paypal_ops_page_before_left_sections", get_defined_vars());
183
184 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_account_details", TRUE, get_defined_vars()))
185 {
186 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_account_details", get_defined_vars());
187
188 //260106
189 echo '<div class="ws-menu-page-group" title="PayPal Checkout (Beta)">'."\n";
190
191 echo '<div class="ws-menu-page-section ws-plugin--s2member-paypal-checkout-account-details-section">'."\n";
192 echo '<a href="https://s2member.com/r/paypal/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/src/images/paypal-pp-logo-200px.png" class="ws-menu-page-right s2m-ppco-paypal-logo" style="width:250px; max-width:25%; height:auto; margin:0 20px 20px 0;" alt="PayPal" /></a>'."\n";
193
194 echo '<div class="ws-menu-page-notice ws-menu-page-notice-info">'."\n";
195 echo '<p>'.esc_html__('When enabled, existing s2Member PayPal Button shortcodes are powered by PayPal Checkout without requiring shortcode edits. If disabled, s2Member continues using PayPal Standard.', 's2member').'</p>'."\n";
196
197 echo '<p style="margin:0 0 6px 0;"><strong>'.esc_html__('Quick Setup (Live or Sandbox)', 's2member').'</strong></p>'."\n";
198 echo '<ol style="margin:0 0 0 20px;">'."\n";
199 echo '<li>'.esc_html__('Enable PayPal Checkout below, choose the environment (Live or Sandbox).', 's2member').'</li>'."\n";
200 echo '<li>'.esc_html__('Go to PayPal\'s “Apps & Credentials”, and click “Create App” for your site (s2Member).', 's2member').' <a href="https://developer.paypal.com/dashboard/applications/live" target="_blank" rel="external">'.esc_html__('Live', 's2member').'</a> / <a href="https://developer.paypal.com/dashboard/applications/sandbox" target="_blank" rel="external">'.esc_html__('Sandbox', 's2member').'</a></li>'."\n";
201 echo '<li>'.esc_html__('Copy the app\'s Client ID and Secret (REST API, not NVP) and paste them below.', 's2member').'</li>'."\n";
202 echo '<li>'.esc_html__('Click “Save All Changes”.', 's2member').'</li>'."\n";
203 echo '<li>'.esc_html__('Click “Test Credentials” to verify the saved Client ID and Secret are correct.', 's2member').'</li>'."\n";
204 echo '<li>'.esc_html__('Click “Create/Update Webhook” to register your webhook URL with PayPal and populate the Webhook ID field.', 's2member').'</li>'."\n";
205 echo '<li>'.esc_html__('Click “Test Webhook Endpoint” to check it.', 's2member').'</li>'."\n";
206 echo '</ol>'."\n";
207
208 echo '<p style="margin:12px 0 0 0;"><strong>'.esc_html__('Useful PayPal links', 's2member').'</strong></p>'."\n";
209 echo '<ul style="margin:0 0 0 20px; list-style:disc;">'."\n";
210 echo '<li><a href="https://developer.paypal.com/api/rest/webhooks/" target="_blank" rel="external">'.esc_html__('Webhooks overview (PayPal docs)', 's2member').'</a></li>'."\n";
211 echo '<li><a href="https://developer.paypal.com/api/rest/webhooks/simulator/" target="_blank" rel="external">'.esc_html__('Webhooks simulator (connectivity testing)', 's2member').'</a></li>'."\n";
212 echo '<li><a href="https://developer.paypal.com/api/rest/webhooks/events-dashboard/" target="_blank" rel="external">'.esc_html__('Webhooks Events dashboard (view/re-deliver events)', 's2member').'</a></li>'."\n";
213 echo '</ul>'."\n";
214
215 echo '</div>'."\n";
216
217 echo '<p><em>'.esc_html__('Leave these fields blank to continue using PayPal Standard.', 's2member').'</em></p>'."\n";
218
219 $ppco_https_ready = true;
220 $ppco_https_reason = '';
221
222 $ppco_https_scheme = strtolower((string)wp_parse_url(home_url('/'), PHP_URL_SCHEME));
223
224 //260224 Reverse-proxy/Cloudflare fallback for admin-side HTTPS detection.
225 // This keeps PayPal Checkout configuration available when WordPress is behind a proxy
226 // and the current admin request is clearly HTTPS at the edge.
227 $ppco_proxy_https = false;
228 if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && stripos(strtolower((string)$_SERVER['HTTP_X_FORWARDED_PROTO']), 'https') !== false)
229 $ppco_proxy_https = true;
230 else if(!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower((string)$_SERVER['HTTP_X_FORWARDED_SSL']) === 'on')
231 $ppco_proxy_https = true;
232 else if(!empty($_SERVER['HTTPS']) && in_array(strtolower((string)$_SERVER['HTTPS']), array('on', '1'), true))
233 $ppco_proxy_https = true;
234 else if(!empty($_SERVER['HTTP_CF_VISITOR']) && stripos((string)$_SERVER['HTTP_CF_VISITOR'], '"scheme":"https"') !== false)
235 $ppco_proxy_https = true;
236
237 if($ppco_https_scheme !== 'https' && !$ppco_proxy_https)
238 {
239 $ppco_https_ready = false;
240 $ppco_https_reason = __('Your WordPress Site URL/Home URL is currently not configured for HTTPS.', 's2member');
241 }
242 else
243 {
244 //260216 Note: Avoid gating configuration on a server-side HTTPS "loopback" probe.
245 // Many shared hosts/WAFs return false negatives for loopback requests (e.g., HEAD blocked,
246 // ModSecurity rules, disabled loopback/outbound HTTP). Reachability is verified separately
247 // via the "Test Webhook Endpoint" link and PayPal's webhook simulator.
248 }
249
250 if(!$ppco_https_ready)
251 {
252 echo '<div class="ws-menu-page-notice ws-menu-page-notice-info">'."\n";
253 echo '<p><span class="ws-menu-page-error"><strong>'.esc_html__('PayPal Checkout requires HTTPS (SSL).', 's2member').'</strong></span></p>'."\n";
254 echo '<p><span class="ws-menu-page-error">'.esc_html__('This site does not currently have a working HTTPS endpoint for this domain. PayPal Checkout is disabled here because webhooks cannot be delivered without HTTPS.', 's2member').'</span></p>'."\n";
255 echo '<p><code>'.esc_html($ppco_https_reason).'</code></p>'."\n";
256 echo '</div>'."\n";
257 }
258
259 echo '<table class="form-table" style="margin:0;">'."\n";
260 echo '<tbody>'."\n";
261 echo '<tr>'."\n";
262
263 echo '<th style="padding-top:0;">'."\n";
264 echo '<label for="ws-plugin--s2member-paypal-checkout-enable-0">'."\n";
265 echo 'Enable PayPal Checkout (Beta)?'."\n";
266 echo '</label>'."\n";
267 echo '</th>'."\n";
268
269 echo '</tr>'."\n";
270 echo '<tr>'."\n";
271
272 echo '<td>'."\n";
273 echo '<input type="radio" name="ws_plugin__s2member_paypal_checkout_enable" id="ws-plugin--s2member-paypal-checkout-enable-0" value="0" onclick="window.s2mPpcoApplyEnvCues&&window.s2mPpcoApplyEnvCues();"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_enable"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-checkout-enable-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_paypal_checkout_enable" id="ws-plugin--s2member-paypal-checkout-enable-1" value="1" onclick="window.s2mPpcoApplyEnvCues&&window.s2mPpcoApplyEnvCues();"'.((!$ppco_https_ready) ? ' disabled="disabled"' : '').(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_enable"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-checkout-enable-1">Yes, enable PayPal Checkout.</label><br />'."\n";
274 echo '<em>Only enable this after you have configured PayPal Checkout credentials below. If disabled, s2Member continues using PayPal Standard.</em>'."\n";
275
276 if(!$ppco_https_ready)
277 echo '<br /><span class="ws-menu-page-error">'.esc_html__('Disabled: PayPal Checkout requires HTTPS (SSL) for webhooks. Fix HTTPS for this domain, then enable PayPal Checkout.', 's2member').'</span>'."\n";
278 echo '</td>'."\n";
279
280 echo '</tr>'."\n";
281 echo '<tr>'."\n";
282
283 echo '<th>'."\n";
284 echo '<label for="ws-plugin--s2member-paypal-checkout-sandbox-0">'."\n";
285 echo 'PayPal Checkout Environment'."\n";
286 echo '</label>'."\n";
287 echo '</th>'."\n";
288
289 echo '</tr>'."\n";
290 echo '<tr>'."\n";
291
292 echo '<td>'."\n";
293 echo '<input type="radio" name="ws_plugin__s2member_paypal_checkout_sandbox" id="ws-plugin--s2member-paypal-checkout-sandbox-0" value="0" onclick="window.s2mPpcoApplyEnvCues && window.s2mPpcoApplyEnvCues(\'click-live\');"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-checkout-sandbox-0">Live</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_paypal_checkout_sandbox" id="ws-plugin--s2member-paypal-checkout-sandbox-1" value="1" onclick="window.s2mPpcoApplyEnvCues && window.s2mPpcoApplyEnvCues(\'click-sandbox\');"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-checkout-sandbox-1">Sandbox</label><br />'."\n";
294 echo '<em>This controls which credentials and webhook ID are used at runtime.</em>'."\n";
295 echo '</td>'."\n";
296
297 echo '</tr>'."\n";
298 echo '</tbody>'."\n";
299 echo '</table>'."\n";
300
301 echo '<div class="ws-menu-page-hr"></div>'."\n";
302
303 echo '<style type="text/css">'."\n";
304 echo '/* PayPal Checkout (Beta): environment cues (Live vs Sandbox) */'."\n";
305 echo '.ws-plugin--s2member-paypal-checkout-account-details-section .s2m-ppco-env-wrap{border-radius:8px;}'."\n";
306 echo '.ws-plugin--s2member-paypal-checkout-account-details-section .s2m-ppco-env-live-wrap{margin:0 0 18px 0;}'."\n";
307 echo '.ws-plugin--s2member-paypal-checkout-account-details-section .s2m-ppco-env-sandbox-wrap{background:#fcfeff; margin:0 -12px 18px -12px; padding:12px;}'."\n";
308 echo '.ws-plugin--s2member-paypal-checkout-account-details-section .s2m-ppco-env-inactive{opacity:0.50;}'."\n";
309 echo '/* Prevent long <code> strings from forcing horizontal overflow (e.g., webhook URL). */'."\n";
310 echo '.ws-plugin--s2member-paypal-checkout-account-details-section code{display:inline-block; max-width:100%; margin:0 !important; white-space:normal; overflow-wrap:anywhere; word-break:break-word;}'."\n";
311 echo '/* Keep inputs inside the section from overflowing the container when zoomed. */'."\n";
312 echo '.ws-plugin--s2member-paypal-checkout-account-details-section input[type="text"],'."\n";
313 echo '.ws-plugin--s2member-paypal-checkout-account-details-section input[type="password"]{max-width:100%; box-sizing:border-box;}'."\n";
314 echo '</style>'."\n";
315
316 echo '<script type="text/javascript">'."\n";
317 echo 'window.s2mPpcoApplyEnvCues=function(){'."\n";
318 echo ' var en=document.getElementById(\'ws-plugin--s2member-paypal-checkout-enable-1\');'."\n";
319 echo ' var sb=document.getElementById(\'ws-plugin--s2member-paypal-checkout-sandbox-1\');'."\n";
320 echo ' var live=document.querySelector(\'.ws-plugin--s2member-paypal-checkout-account-details-section .s2m-ppco-env-live-wrap\');'."\n";
321 echo ' var sandbox=document.querySelector(\'.ws-plugin--s2member-paypal-checkout-account-details-section .s2m-ppco-env-sandbox-wrap\');'."\n";
322 echo ' if(!live||!sandbox){return;}'."\n";
323 echo ' var enabled=!!(en&&en.checked);'."\n";
324 echo ' var isSandbox=!!(sb&&sb.checked);'."\n";
325 echo ' live.classList.toggle(\'s2m-ppco-env-inactive\', !enabled||isSandbox);'."\n";
326 echo ' sandbox.classList.toggle(\'s2m-ppco-env-inactive\', !enabled||!isSandbox);'."\n";
327 echo '};'."\n";
328 echo 'window.s2mPpcoApplyEnvCues();'."\n";
329 echo '</script>'."\n";
330
331 $ppco_post_back_url = remove_query_arg(array('s2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_creds_test', 'ppco_creds_env', 's2member_ppco_clear_cache', 'ppco_clear_env', '_wpnonce'));
332
333 $ppco_webhook_nonce = wp_create_nonce('s2member_ppco_webhook');
334 $ppco_creds_test_nonce = wp_create_nonce('s2member_ppco_creds_test');
335 $ppco_clear_cache_nonce = wp_create_nonce('s2member_ppco_clear_cache');
336
337 echo '<script type="text/javascript">'."\n";
338 echo 'window.s2mPpcoPostBackUrl='.wp_json_encode($ppco_post_back_url).';'."\n";
339 echo 'window.s2mPpcoPostAction=function(action,env,nonce){'."\n";
340 echo ' try{'."\n";
341 echo ' var f=document.createElement("form");'."\n";
342 echo ' f.method="post";'."\n";
343 echo ' f.action=window.s2mPpcoPostBackUrl||window.location.href;'."\n";
344 echo ' var add=function(n,v){var i=document.createElement("input");i.type="hidden";i.name=n;i.value=v;f.appendChild(i);};'."\n";
345 echo ' if(action==="webhook"){add("s2member_ppco_webhook","1");add("ppco_webhook_env",env);}'."\n";
346 echo ' else if(action==="creds_test"){add("s2member_ppco_creds_test","1");add("ppco_creds_env",env);}'."\n";
347 echo ' else if(action==="clear_cache"){add("s2member_ppco_clear_cache","1");add("ppco_clear_env",env);}'."\n";
348 echo ' add("_wpnonce",nonce);'."\n";
349 echo ' document.body.appendChild(f);'."\n";
350 echo ' f.submit();'."\n";
351 echo ' }catch(e){}'."\n";
352 echo ' return false;'."\n";
353 echo '};'."\n";
354 echo '</script>'."\n";
355
356 $ppco_webhook_live_url = add_query_arg(array(
357 's2member_ppco_webhook' => '1',
358 'ppco_webhook_env' => 'live',
359 '_wpnonce' => wp_create_nonce('s2member_ppco_webhook'),
360 ), remove_query_arg(array('s2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_creds_test', 'ppco_creds_env', 's2member_ppco_clear_cache', 'ppco_clear_env', '_wpnonce')));
361
362 $ppco_creds_test_live_url = add_query_arg(array(
363 's2member_ppco_creds_test' => '1',
364 'ppco_creds_env' => 'live',
365 '_wpnonce' => wp_create_nonce('s2member_ppco_creds_test'),
366 ), remove_query_arg(array('s2member_ppco_creds_test', 'ppco_creds_env', 's2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_clear_cache', 'ppco_clear_env', '_wpnonce')));
367
368 $ppco_clear_cache_live_url = add_query_arg(array(
369 's2member_ppco_clear_cache' => '1',
370 'ppco_clear_env' => 'live',
371 '_wpnonce' => wp_create_nonce('s2member_ppco_clear_cache'),
372 ), remove_query_arg(array('s2member_ppco_clear_cache', 'ppco_clear_env', 's2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_creds_test', 'ppco_creds_env', '_wpnonce')));
373
374 $ppco_webhook_test_live_url = add_query_arg(array(
375 's2member_paypal_webhook' => '1',
376 's2member_paypal_webhook_test' => '1',
377 'ppco_webhook_env' => 'live',
378 '_wpnonce' => wp_create_nonce('s2member_ppco_webhook_test'),
379 ), home_url("/", 'https'));
380
381 $ppco_webhook_sandbox_url = add_query_arg(array(
382 's2member_ppco_webhook' => '1',
383 'ppco_webhook_env' => 'sandbox',
384 '_wpnonce' => wp_create_nonce('s2member_ppco_webhook'),
385 ), remove_query_arg(array('s2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_creds_test', 'ppco_creds_env', 's2member_ppco_clear_cache', 'ppco_clear_env', '_wpnonce')));
386
387 $ppco_creds_test_sandbox_url = add_query_arg(array(
388 's2member_ppco_creds_test' => '1',
389 'ppco_creds_env' => 'sandbox',
390 '_wpnonce' => wp_create_nonce('s2member_ppco_creds_test'),
391 ), remove_query_arg(array('s2member_ppco_creds_test', 'ppco_creds_env', 's2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_clear_cache', 'ppco_clear_env', '_wpnonce')));
392
393 $ppco_clear_cache_sandbox_url = add_query_arg(array(
394 's2member_ppco_clear_cache' => '1',
395 'ppco_clear_env' => 'sandbox',
396 '_wpnonce' => wp_create_nonce('s2member_ppco_clear_cache'),
397 ), remove_query_arg(array('s2member_ppco_clear_cache', 'ppco_clear_env', 's2member_ppco_webhook', 'ppco_webhook_env', 's2member_ppco_creds_test', 'ppco_creds_env', '_wpnonce')));
398
399 $ppco_webhook_test_sandbox_url = add_query_arg(array(
400 's2member_paypal_webhook' => '1',
401 's2member_paypal_webhook_test' => '1',
402 'ppco_webhook_env' => 'sandbox',
403 '_wpnonce' => wp_create_nonce('s2member_ppco_webhook_test'),
404 ), home_url("/", 'https'));
405
406 $ppco_cache = !empty($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_cache"]) && is_array($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_cache"]) ? $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_cache"] : array();
407
408 $ppco_live_cred_id = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_cred_id('live');
409 $ppco_sandbox_cred_id = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_cred_id('sandbox');
410
411 $ppco_plan_count_live = (!empty($ppco_cache[$ppco_live_cred_id]['live']['plan_ids']) && is_array($ppco_cache[$ppco_live_cred_id]['live']['plan_ids'])) ? count($ppco_cache[$ppco_live_cred_id]['live']['plan_ids']) : 0;
412 $ppco_prod_count_live = (!empty($ppco_cache[$ppco_live_cred_id]['live']['product_ids']) && is_array($ppco_cache[$ppco_live_cred_id]['live']['product_ids'])) ? count($ppco_cache[$ppco_live_cred_id]['live']['product_ids']) : 0;
413
414 $ppco_plan_count_sandbox = (!empty($ppco_cache[$ppco_sandbox_cred_id]['sandbox']['plan_ids']) && is_array($ppco_cache[$ppco_sandbox_cred_id]['sandbox']['plan_ids'])) ? count($ppco_cache[$ppco_sandbox_cred_id]['sandbox']['plan_ids']) : 0;
415 $ppco_prod_count_sandbox = (!empty($ppco_cache[$ppco_sandbox_cred_id]['sandbox']['product_ids']) && is_array($ppco_cache[$ppco_sandbox_cred_id]['sandbox']['product_ids'])) ? count($ppco_cache[$ppco_sandbox_cred_id]['sandbox']['product_ids']) : 0;
416
417 echo '<div class="s2m-ppco-env-wrap s2m-ppco-env-live-wrap'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox"]) ? ' s2m-ppco-env-inactive' : '').'">'."\n";
418 echo '<h3 style="margin:0 0 6px 0;">Live</h3>'."\n";
419
420 echo '<table class="form-table">'."\n";
421 echo '<tbody>'."\n";
422
423 echo '<tr>'."\n";
424 echo '<th>'."\n";
425 echo '<label for="ws-plugin--s2member-paypal-checkout-client-id">'."\n";
426 echo 'PayPal Checkout Client ID (Live — REST API):'."\n";
427 echo '</label>'."\n";
428 echo '</th>'."\n";
429 echo '</tr>'."\n";
430
431 echo '<tr>'."\n";
432 echo '<td>'."\n";
433 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_checkout_client_id" id="ws-plugin--s2member-paypal-checkout-client-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"]).'" /><br />'."\n";
434 echo 'At PayPal, see: <strong><a href="https://developer.paypal.com/dashboard/applications/live" target="_blank" rel="external">My Apps &amp; Credentials (Live)</a></strong>'."\n";
435 echo '</td>'."\n";
436 echo '</tr>'."\n";
437
438 echo '<tr>'."\n";
439 echo '<th>'."\n";
440 echo '<label for="ws-plugin--s2member-paypal-checkout-client-secret">'."\n";
441 echo 'PayPal Checkout Client Secret (Live — REST API):'."\n";
442 echo '</label>'."\n";
443 echo '</th>'."\n";
444 echo '</tr>'."\n";
445
446 echo '<tr>'."\n";
447 echo '<td>'."\n";
448 echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_paypal_checkout_client_secret" id="ws-plugin--s2member-paypal-checkout-client-secret" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"]).'" /><br />'."\n";
449 echo 'At PayPal, see: <strong><a href="https://developer.paypal.com/dashboard/applications/live" target="_blank" rel="external">My Apps &amp; Credentials (Live)</a></strong>'."\n";
450
451 echo '<table class="form-table" style="margin:10px 0 0 0;">'."\n";
452 echo '<tbody>'."\n";
453 echo '<tr>'."\n";
454 echo '<th style="width:260px; padding:0 10px 0 0;">'."\n";
455 if($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"])
456 echo '<a class="button" style="min-width:240px; text-align:center;" href="'.esc_attr($ppco_creds_test_live_url).'" onclick="return window.s2mPpcoPostAction(\'creds_test\',\'live\',\''.esc_attr($ppco_creds_test_nonce).'\');">'.esc_html__('Test Credentials', 's2member').'</a>'."\n";
457 else echo '<span class="button" style="min-width:240px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Save Live credentials first.', 's2member').'">'.esc_html__('Test Credentials', 's2member').'</span>'."\n";
458 echo '</th>'."\n";
459 echo '<td style="padding:0;">'."\n";
460 echo '<em>'.esc_html__('Verifies your Live Client ID/Secret by requesting an access token.', 's2member').'</em>'."\n";
461 echo '</td>'."\n";
462 echo '</tr>'."\n";
463 echo '</tbody>'."\n";
464 echo '</table>'."\n";
465
466 echo '</td>'."\n";
467 echo '</tr>'."\n";
468
469 echo '<tr>'."\n";
470 echo '<th>'."\n";
471 echo '<label for="ws-plugin--s2member-paypal-checkout-webhook-id">'."\n";
472 echo 'PayPal Checkout Webhook ID (Live):'."\n";
473 echo '</label>'."\n";
474 echo '</th>'."\n";
475 echo '</tr>'."\n";
476
477 echo '<tr>'."\n";
478 echo '<td>'."\n";
479 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_checkout_webhook_id" id="ws-plugin--s2member-paypal-checkout-webhook-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_webhook_id"]).'" />'."\n";
480 echo '<br /><em>'.esc_html__('From your PayPal app webhook settings (Live).', 's2member').'</em><br />'."\n";
481
482 echo '<table class="form-table" style="margin:12px 0 0 0;">'."\n";
483 echo '<tbody>'."\n";
484
485 echo '<tr>'."\n";
486 echo '<th style="width:260px; padding:0 10px 0 0;">'."\n";
487 if($ppco_https_ready && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"])
488 echo '<a class="button button-primary" style="min-width:250px; text-align:center; color:#fff !important;" href="'.esc_attr($ppco_webhook_live_url).'" onclick="return window.s2mPpcoPostAction(\'webhook\',\'live\',\''.esc_attr($ppco_webhook_nonce).'\');">'.esc_html__('Create/Update Webhook', 's2member').'</a>'."\n";
489 else if(!$ppco_https_ready)
490 echo '<span class="button button-primary" style="min-width:250px; text-align:center; color:#fff !important; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('HTTPS required.', 's2member').'">'.esc_html__('Create/Update Webhook', 's2member').'</span>'."\n";
491 else
492 echo '<span class="button button-primary" style="min-width:250px; text-align:center; color:#fff !important; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Save Live credentials first.', 's2member').'">'.esc_html__('Create/Update Webhook', 's2member').'</span>'."\n";
493 echo '</th>'."\n";
494 echo '<td style="padding:0;">'."\n";
495 echo '<em>'.esc_html__('Registers your webhook URL with PayPal (HTTPS required).', 's2member').' '.esc_html__('If your site URL changes, re-run Create/Update Webhook to update the callback URL at PayPal.', 's2member').'</em>'."\n";
496 echo '</td>'."\n";
497 echo '</tr>'."\n";
498
499 echo '<tr>'."\n";
500 echo '<th style="width:260px; padding:8px 10px 0 0;">'."\n";
501 if($ppco_https_ready && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_webhook_id"])
502 echo '<a class="button" style="min-width:250px; text-align:center;" target="_blank" href="'.esc_attr($ppco_webhook_test_live_url).'">'.esc_html__('Test Webhook Endpoint', 's2member').'</a>'."\n";
503 else if(!$ppco_https_ready)
504 echo '<span class="button disabled" style="min-width:250px; text-align:center;" title="'.esc_attr__('HTTPS required.', 's2member').'">'.esc_html__('Test Webhook Endpoint', 's2member').'</span>'."\n";
505 else if(!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"] || !$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"])
506 echo '<span class="button disabled" style="min-width:250px; text-align:center;" title="'.esc_attr__('Save Live credentials first.', 's2member').'">'.esc_html__('Test Webhook Endpoint', 's2member').'</span>'."\n";
507 else
508 echo '<span class="button" style="min-width:250px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Set a Live Webhook ID first (or use Create/Update Webhook).', 's2member').'">'.esc_html__('Test Webhook Endpoint', 's2member').'</span>'."\n";
509 echo '</th>'."\n";
510 echo '<td style="padding:8px 0 0 0;">'."\n";
511 echo '<em>'.esc_html__('Reachability-only check. Use real webhooks for end-to-end verification.', 's2member').'</em>'."\n";
512 echo '<br /><em>'.esc_html__('Test URL: ', 's2member').'<code>'.esc_html($ppco_webhook_test_live_url).'</code></em>'."\n";
513 echo '</td>'."\n";
514 echo '</tr>'."\n";
515
516 echo '</tbody>'."\n";
517 echo '</table>'."\n";
518
519 echo '<br /><br />'."\n";
520 echo '<em>'.esc_html__('Webhook URL:', 's2member').'</em><br /><code>'.esc_html(add_query_arg("s2member_paypal_webhook", "1", home_url("/", 'https'))).'</code>'."\n";
521 echo '<br /><em class="ws-menu-page-hilite">'.esc_html__('Note: PayPal requires an HTTPS (SSL) webhook URL.', 's2member').'</em>'."\n";
522
523 $ppco_https_scheme = strtolower((string)wp_parse_url(home_url('/'), PHP_URL_SCHEME));
524
525 //260224 Reverse-proxy/Cloudflare fallback for admin-side HTTPS warning display.
526 $ppco_proxy_https = false;
527 if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && stripos(strtolower((string)$_SERVER['HTTP_X_FORWARDED_PROTO']), 'https') !== false)
528 $ppco_proxy_https = true;
529 else if(!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower((string)$_SERVER['HTTP_X_FORWARDED_SSL']) === 'on')
530 $ppco_proxy_https = true;
531 else if(!empty($_SERVER['HTTPS']) && in_array(strtolower((string)$_SERVER['HTTPS']), array('on', '1'), true))
532 $ppco_proxy_https = true;
533 else if(!empty($_SERVER['HTTP_CF_VISITOR']) && stripos((string)$_SERVER['HTTP_CF_VISITOR'], '"scheme":"https"') !== false)
534 $ppco_proxy_https = true;
535
536 if($ppco_https_scheme !== 'https' && !$ppco_proxy_https)
537 echo '<br /><span class="ws-menu-page-error">'.esc_html__('Warning: Your WordPress Site URL/Home URL is currently not configured for HTTPS. PayPal may reject webhook creation and delivery until your site supports SSL and your Site URL uses https://.', 's2member').'</span>'."\n";
538 else
539 {
540 $ppco_https_probe = wp_remote_head(home_url("/", 'https'), array('timeout' => 8, 'redirection' => 0));
541 $ppco_https_code = !is_wp_error($ppco_https_probe) ? (int)wp_remote_retrieve_response_code($ppco_https_probe) : 0;
542 $ppco_https_loc = !is_wp_error($ppco_https_probe) ? (string)wp_remote_retrieve_header($ppco_https_probe, 'location') : '';
543
544 if(is_wp_error($ppco_https_probe))
545 echo '<br /><span class="ws-menu-page-error">'.esc_html__('Warning: Your HTTPS endpoint could not be reached from this server. This often indicates SSL/TLS or virtual-host configuration issues; PayPal webhooks may not be delivered successfully.', 's2member').'<br /><code>'.esc_html($ppco_https_probe->get_error_message()).'</code></span>'."\n";
546 else if($ppco_https_loc && stripos($ppco_https_loc, 'cgi-sys/defaultwebpage.cgi') !== false)
547 echo '<br /><span class="ws-menu-page-error">'.esc_html__('Warning: Your HTTPS endpoint appears to redirect to a hosting default page (cgi-sys/defaultwebpage.cgi). This suggests SSL/virtual-host configuration issues; PayPal webhooks may not be delivered successfully.', 's2member').'</span>'."\n";
548 else if($ppco_https_code && $ppco_https_code >= 400)
549 echo '<br /><span class="ws-menu-page-error">'.sprintf(esc_html__('Warning: Server-side probe to your HTTPS endpoint returned HTTP %1$s. This can be caused by SSL/virtual-host configuration issues, but it can also be a false negative due to security rules that block loopback requests. Use the “Test Webhook Endpoint” link and PayPal\'s webhook simulator to confirm external reachability.', 's2member'), (int)$ppco_https_code).'</span>'."\n";
550 }
551
552 //260216 Include additional events required for refunds/reversals and subscription lifecycle.
553 echo '<br /><br /><em>'.esc_html__('Required Events:', 's2member').'</em><br /><code>PAYMENT.SALE.COMPLETED</code> <code>PAYMENT.CAPTURE.COMPLETED</code> <code>PAYMENT.SALE.REFUNDED</code> <code>PAYMENT.CAPTURE.REFUNDED</code> <code>PAYMENT.SALE.REVERSED</code> <code>PAYMENT.CAPTURE.REVERSED</code> <code>BILLING.SUBSCRIPTION.CREATED</code> <code>BILLING.SUBSCRIPTION.ACTIVATED</code> <code>BILLING.SUBSCRIPTION.RE-ACTIVATED</code> <code>BILLING.SUBSCRIPTION.UPDATED</code> <code>BILLING.SUBSCRIPTION.CANCELLED</code> <code>BILLING.SUBSCRIPTION.SUSPENDED</code> <code>BILLING.SUBSCRIPTION.EXPIRED</code> <code>BILLING.SUBSCRIPTION.PAYMENT.FAILED</code>'."\n";
554
555 echo '<p style="margin:10px 0 0 0;"><em>'.esc_html__('Tip: Use PayPal\'s Webhooks Events dashboard to view and re-deliver webhook events while testing.', 's2member').' <a href="https://developer.paypal.com/api/rest/webhooks/events-dashboard/" target="_blank" rel="external">'.esc_html__('Open Events Dashboard', 's2member').'</a></em></p>'."\n";
556
557 if(!$ppco_https_ready)
558 echo '<p style="margin:10px 0 0 0;"><span class="ws-menu-page-error">'.esc_html__('Disabled: PayPal webhooks require a working HTTPS (SSL) endpoint on this domain.', 's2member').'</span></p>'."\n";
559
560 echo '<p style="margin:16px 0 10px 0;"><strong>'.esc_html__('Plan/Product Cache (Live)', 's2member').'</strong></p>'."\n";
561 echo '<table class="form-table" style="margin:0;">'."\n";
562 echo '<tbody>'."\n";
563
564 echo '<tr>'."\n";
565 echo '<th style="width:260px; padding:0 10px 0 0;">'."\n";
566 if((int)$ppco_plan_count_live || (int)$ppco_prod_count_live)
567 echo '<a class="button" style="min-width:250px; text-align:center;" href="'.esc_attr($ppco_clear_cache_live_url).'" onclick="return window.s2mPpcoPostAction(\'clear_cache\',\'live\',\''.esc_attr($ppco_clear_cache_nonce).'\');">'.esc_html__('Clear Plan/Product Cache', 's2member').'</a>'."\n";
568 else echo '<span class="button disabled" style="min-width:250px; text-align:center;" title="'.esc_attr__('Cache is already empty.', 's2member').'">'.esc_html__('Clear Plan/Product Cache', 's2member').'</span>'."\n";
569 echo '</th>'."\n";
570 echo '<td style="padding:0;">'."\n";
571 echo '<em>'.sprintf(esc_html__('Clears the local PayPal Checkout Product/Plan ID cache (used to avoid creating duplicates). Existing subscriptions are unaffected. Next checkout will create new Products/Plans as needed. Current cache: %1$s plan(s), %2$s product(s).', 's2member'), (int)$ppco_plan_count_live, (int)$ppco_prod_count_live).'</em>'."\n";
572 echo '</td>'."\n";
573 echo '</tr>'."\n";
574
575 echo '</tbody>'."\n";
576 echo '</table>'."\n";
577
578 echo '</td>'."\n";
579 echo '</tr>'."\n";
580
581 echo '</tbody>'."\n";
582 echo '</table>'."\n";
583 echo '</div>'."\n";
584
585 echo '<div class="ws-menu-page-hr"></div>'."\n";
586
587 echo '<div class="s2m-ppco-env-wrap s2m-ppco-env-sandbox-wrap'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox"]) ? ' s2m-ppco-env-inactive' : '').'">'."\n";
588 echo '<h3 style="margin:0 0 6px 0;">Sandbox</h3>'."\n";
589
590 echo '<table class="form-table">'."\n";
591 echo '<tbody>'."\n";
592
593 echo '<tr>'."\n";
594 echo '<th>'."\n";
595 echo '<label for="ws-plugin--s2member-paypal-checkout-sandbox-client-id">'."\n";
596 echo 'PayPal Checkout Client ID (Sandbox — REST API):'."\n";
597 echo '</label>'."\n";
598 echo '</th>'."\n";
599 echo '</tr>'."\n";
600
601 echo '<tr>'."\n";
602 echo '<td>'."\n";
603 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_checkout_sandbox_client_id" id="ws-plugin--s2member-paypal-checkout-sandbox-client-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"]).'" /><br />'."\n";
604 echo 'At PayPal, see: <strong><a href="https://developer.paypal.com/dashboard/applications/sandbox" target="_blank" rel="external">My Apps &amp; Credentials (Sandbox)</a></strong>'."\n";
605 echo '</td>'."\n";
606 echo '</tr>'."\n";
607
608 echo '<tr>'."\n";
609 echo '<th>'."\n";
610 echo '<label for="ws-plugin--s2member-paypal-checkout-sandbox-client-secret">'."\n";
611 echo 'PayPal Checkout Client Secret (Sandbox — REST API):'."\n";
612 echo '</label>'."\n";
613 echo '</th>'."\n";
614 echo '</tr>'."\n";
615
616 echo '<tr>'."\n";
617 echo '<td>'."\n";
618 echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_paypal_checkout_sandbox_client_secret" id="ws-plugin--s2member-paypal-checkout-sandbox-client-secret" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"]).'" /><br />'."\n";
619 echo 'At PayPal, see: <strong><a href="https://developer.paypal.com/dashboard/applications/sandbox" target="_blank" rel="external">My Apps &amp; Credentials (Sandbox)</a></strong>'."\n";
620
621 echo '<table class="form-table" style="margin:10px 0 0 0;">'."\n";
622 echo '<tbody>'."\n";
623 echo '<tr>'."\n";
624 echo '<th style="width:260px; padding:0 10px 0 0;">'."\n";
625 if($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"])
626 echo '<a class="button" style="min-width:240px; text-align:center;" href="'.esc_attr($ppco_creds_test_sandbox_url).'" onclick="return window.s2mPpcoPostAction(\'creds_test\',\'sandbox\',\''.esc_attr($ppco_creds_test_nonce).'\');">'.esc_html__('Test Credentials', 's2member').'</a>'."\n";
627 else echo '<span class="button" style="min-width:240px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Save Sandbox credentials first.', 's2member').'">'.esc_html__('Test Credentials', 's2member').'</span>'."\n";
628 echo '</th>'."\n";
629 echo '<td style="padding:0;">'."\n";
630 echo '<em>'.esc_html__('Verifies your Sandbox Client ID/Secret by requesting an access token.', 's2member').'</em>'."\n";
631 echo '</td>'."\n";
632 echo '</tr>'."\n";
633 echo '</tbody>'."\n";
634 echo '</table>'."\n";
635
636 echo '</td>'."\n";
637 echo '</tr>'."\n";
638
639 echo '<tr>'."\n";
640 echo '<th>'."\n";
641 echo '<label for="ws-plugin--s2member-paypal-checkout-sandbox-webhook-id">'."\n";
642 echo 'PayPal Checkout Webhook ID (Sandbox):'."\n";
643 echo '</label>'."\n";
644 echo '</th>'."\n";
645 echo '</tr>'."\n";
646
647 echo '<tr>'."\n";
648 echo '<td>'."\n";
649 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_checkout_sandbox_webhook_id" id="ws-plugin--s2member-paypal-checkout-sandbox-webhook-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_webhook_id"]).'" />'."\n";
650 echo '<br /><em>'.esc_html__('From your PayPal app webhook settings (Sandbox).', 's2member').'</em><br />'."\n";
651
652 echo '<table class="form-table" style="margin:12px 0 0 0;">'."\n";
653 echo '<tbody>'."\n";
654
655 echo '<tr>'."\n";
656 echo '<th style="width:260px; padding:0 10px 0 0;">'."\n";
657 if($ppco_https_ready && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"])
658 echo '<a class="button button-primary" style="min-width:250px; text-align:center; color:#fff !important;" href="'.esc_attr($ppco_webhook_sandbox_url).'" onclick="return window.s2mPpcoPostAction(\'webhook\',\'sandbox\',\''.esc_attr($ppco_webhook_nonce).'\');">'.esc_html__('Create/Update Webhook', 's2member').'</a>'."\n";
659 else if(!$ppco_https_ready)
660 echo '<span class="button button-primary" style="min-width:250px; text-align:center; color:#fff !important; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('HTTPS required.', 's2member').'">'.esc_html__('Create/Update Webhook', 's2member').'</span>'."\n";
661 else
662 echo '<span class="button button-primary" style="min-width:250px; text-align:center; color:#fff !important; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Save Sandbox credentials first.', 's2member').'">'.esc_html__('Create/Update Webhook', 's2member').'</span>'."\n";
663 echo '</th>'."\n";
664 echo '<td style="padding:0;">'."\n";
665 echo '<em>'.esc_html__('Registers your webhook URL with PayPal (HTTPS required).', 's2member').' '.esc_html__('If your site URL changes, re-run Create/Update Webhook to update the callback URL at PayPal.', 's2member').'</em>'."\n";
666 echo '</td>'."\n";
667 echo '</tr>'."\n";
668
669 echo '<tr>'."\n";
670 echo '<th style="width:260px; padding:8px 10px 0 0;">'."\n";
671 if($ppco_https_ready && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_webhook_id"])
672 echo '<a class="button" style="min-width:250px; text-align:center;" target="_blank" href="'.esc_attr($ppco_webhook_test_sandbox_url).'">'.esc_html__('Test Webhook Endpoint', 's2member').'</a>'."\n";
673 else if(!$ppco_https_ready)
674 echo '<span class="button" style="min-width:250px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('HTTPS required.', 's2member').'">'.esc_html__('Test Webhook Endpoint', 's2member').'</span>'."\n";
675 else if(!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"] || !$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"])
676 echo '<span class="button" style="min-width:250px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Save Sandbox credentials first.', 's2member').'">'.esc_html__('Test Webhook Endpoint', 's2member').'</span>'."\n";
677 else
678 echo '<span class="button" style="min-width:250px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Set a Sandbox Webhook ID first (or use Create/Update Webhook).', 's2member').'">'.esc_html__('Test Webhook Endpoint', 's2member').'</span>'."\n";
679 echo '</th>'."\n";
680 echo '<td style="padding:8px 0 0 0;">'."\n";
681 echo '<em>'.esc_html__('Reachability-only check. Use real webhooks for end-to-end verification.', 's2member').'</em>'."\n";
682 echo '<br /><em>'.esc_html__('Test URL: ', 's2member').'<code>'.esc_html($ppco_webhook_test_sandbox_url).'</code></em>'."\n";
683 echo '</td>'."\n";
684 echo '</tr>'."\n";
685
686 echo '</tbody>'."\n";
687 echo '</table>'."\n";
688
689 echo '<br /><br />'."\n";
690 echo '<em>'.esc_html__('Webhook URL:', 's2member').'</em><br /><code>'.esc_html(add_query_arg("s2member_paypal_webhook", "1", home_url("/", 'https'))).'</code>'."\n";
691 echo '<br /><em class="ws-menu-page-hilite">'.esc_html__('Note: PayPal requires an HTTPS (SSL) webhook URL.', 's2member').'</em>'."\n";
692
693 //260216 Include additional events required for refunds/reversals and subscription lifecycle.
694 echo '<br /><br /><em>'.esc_html__('Required Events:', 's2member').'</em><br /><code>PAYMENT.SALE.COMPLETED</code> <code>PAYMENT.CAPTURE.COMPLETED</code> <code>PAYMENT.SALE.REFUNDED</code> <code>PAYMENT.CAPTURE.REFUNDED</code> <code>PAYMENT.SALE.REVERSED</code> <code>PAYMENT.CAPTURE.REVERSED</code> <code>BILLING.SUBSCRIPTION.CREATED</code> <code>BILLING.SUBSCRIPTION.ACTIVATED</code> <code>BILLING.SUBSCRIPTION.RE-ACTIVATED</code> <code>BILLING.SUBSCRIPTION.UPDATED</code> <code>BILLING.SUBSCRIPTION.CANCELLED</code> <code>BILLING.SUBSCRIPTION.SUSPENDED</code> <code>BILLING.SUBSCRIPTION.EXPIRED</code> <code>BILLING.SUBSCRIPTION.PAYMENT.FAILED</code>'."\n";
695
696 echo '<p style="margin:10px 0 0 0;"><em>'.esc_html__('Tip: Use PayPal\'s Webhooks Events dashboard to view and re-deliver webhook events while testing.', 's2member').' <a href="https://developer.paypal.com/api/rest/webhooks/events-dashboard/" target="_blank" rel="external">'.esc_html__('Open Events Dashboard', 's2member').'</a></em></p>'."\n";
697
698 if(!$ppco_https_ready)
699 echo '<p style="margin:10px 0 0 0;"><span class="ws-menu-page-error">'.esc_html__('Disabled: PayPal webhooks require a working HTTPS (SSL) endpoint on this domain.', 's2member').'</span></p>'."\n";
700
701 echo '<p style="margin:16px 0 10px 0;"><strong>'.esc_html__('Plan/Product Cache (Sandbox)', 's2member').'</strong></p>'."\n";
702 echo '<table class="form-table" style="margin:0;">'."\n";
703 echo '<tbody>'."\n";
704
705 echo '<tr>'."\n";
706 echo '<th style="width:260px; padding:0 10px 0 0;">'."\n";
707 if((int)$ppco_plan_count_sandbox || (int)$ppco_prod_count_sandbox)
708 echo '<a class="button" style="min-width:250px; text-align:center;" href="'.esc_attr($ppco_clear_cache_sandbox_url).'" onclick="return window.s2mPpcoPostAction(\'clear_cache\',\'sandbox\',\''.esc_attr($ppco_clear_cache_nonce).'\');">'.esc_html__('Clear Plan/Product Cache', 's2member').'</a>'."\n";
709 else echo '<span class="button" style="min-width:250px; text-align:center; opacity:0.50; cursor:default; pointer-events:none;" title="'.esc_attr__('Cache is already empty.', 's2member').'">'.esc_html__('Clear Plan/Product Cache', 's2member').'</span>'."\n";
710 echo '</th>'."\n";
711 echo '<td style="padding:0;">'."\n";
712 echo '<em>'.sprintf(esc_html__('Clears the local PayPal Checkout Product/Plan ID cache (used to avoid creating duplicates). Existing subscriptions are unaffected. Next checkout will create new Products/Plans as needed. Current cache: %1$s plan(s), %2$s product(s).', 's2member'), (int)$ppco_plan_count_sandbox, (int)$ppco_prod_count_sandbox).'</em>'."\n";
713 echo '</td>'."\n";
714 echo '</tr>'."\n";
715
716 echo '</tbody>'."\n";
717 echo '</table>'."\n";
718
719 echo '</td>'."\n";
720 echo '</tr>'."\n";
721
722 echo '</tbody>'."\n";
723 echo '</table>'."\n";
724 echo '</div>'."\n";
725
726 echo '</div>'."\n"; // .ws-menu-page-section
727
728 echo '</div>'."\n"; // .ws-menu-page-group
729
730 echo '<div class="ws-menu-page-group" title="PayPal Account Details">'."\n";
731
732 echo '<div class="ws-menu-page-section ws-plugin--s2member-paypal-account-details-section">'."\n";
733 echo '<a href="https://s2member.com/r/paypal/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/src/images/paypal-logo.png" class="ws-menu-page-right" style="width:125px; height:125px; border:0;" alt="." /></a>'."\n";
734 echo '<h3>PayPal Account Details (required, if using PayPal)</h3>'."\n";
735 echo '<p>s2Member integrates with <a href="https://s2member.com/r/paypal/" target="_blank" rel="external">PayPal Payments Standard</a>—for businesses. You do not need a PayPal Pro account. You just need to upgrade your Personal PayPal account to a Business status, which is free. A PayPal account can be <a href="http://s2member.com/r/paypal-business-upgrade/" target="_blank" rel="external">upgraded</a> from a Personal account to a Business account by clicking the "Profile" link under your "My Account" tab, selecting "Personal Business Information", and then clicking the "Upgrade Your Account" button. <strong>See also:</strong> This KB article: <a href="http://s2member.com/kb-article/supported-paypal-account-types/" target="_blank" rel="external">PayPal Compatibility (Account Types)</a>.</p>'."\n";
736 echo '<p><em><strong>PayPal API Credentials:</strong> Once you have a PayPal Business account, you\'ll need access to your <a href="http://s2member.com/r/paypal-profile-api-access/" target="_blank" rel="external">PayPal API Credentials</a>. Log into your PayPal account, and navigate to <strong>Profile → API Access (or → Request API Credentials)</strong>. From the available options, please choose "Request API Signature".</em></p>'."\n";
737 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_account_details", get_defined_vars());
738
739 echo '<table class="form-table">'."\n";
740 echo '<tbody>'."\n";
741 echo '<tr>'."\n";
742
743 echo '<th>'."\n";
744 echo '<label for="ws-plugin--s2member-paypal-merchant-id">'."\n";
745 echo 'Your PayPal Merchant ID:'."\n";
746 echo '</label>'."\n";
747 echo '</th>'."\n";
748
749 echo '</tr>'."\n";
750 echo '<tr>'."\n";
751
752 echo '<td>'."\n";
753 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_merchant_id" id="ws-plugin--s2member-paypal-merchant-id" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_merchant_id"]).'" /><br />'."\n";
754 echo 'At PayPal, see: <strong><a href="https://www.paypal.com/businessmanage/account/aboutBusiness" target="_blank" rel="external">Account Settings → Business Information → PayPal Merchant ID</a></strong>'."\n";
755 echo '</td>'."\n";
756
757 echo '</tr>'."\n";
758 echo '<tr>'."\n";
759
760 echo '<th>'."\n";
761 echo '<label for="ws-plugin--s2member-paypal-business">'."\n";
762 echo 'Your PayPal EMail Address:'."\n";
763 echo '</label>'."\n";
764 echo '</th>'."\n";
765
766 echo '</tr>'."\n";
767 echo '<tr>'."\n";
768
769 echo '<td>'."\n";
770 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_business" id="ws-plugin--s2member-paypal-business" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_business"]).'" /><br />'."\n";
771 echo 'At PayPal, see: <strong><a href="https://www.paypal.com/businessmanage/account/accountOwner" target="_blank" rel="external">Account Settings → Account Owner Information → Email Address</a></strong>'."\n";
772 echo '</td>'."\n";
773
774 echo '</tr>'."\n";
775 echo '</tbody>'."\n";
776 echo '</table>'."\n";
777
778 echo '<div class="ws-menu-page-hr"></div>'."\n";
779
780 echo '<table class="form-table">'."\n";
781 echo '<tbody>'."\n";
782 echo '<tr>'."\n";
783
784 echo '<th>'."\n";
785 echo '<label for="ws-plugin--s2member-paypal-api-username">'."\n";
786 echo 'Your PayPal API Username:'."\n";
787 echo '</label>'."\n";
788 echo '</th>'."\n";
789
790 echo '</tr>'."\n";
791 echo '<tr>'."\n";
792
793 echo '<td>'."\n";
794 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_api_username" id="ws-plugin--s2member-paypal-api-username" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_api_username"]).'" /><br />'."\n";
795 echo 'At PayPal, see: <strong><a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty/signature" target="_blank" rel="external">Account Settings → Website Payments → API Access → NVP/SOAP API integration (Classic) → Manage API Credentials</a></strong>'."\n";
796 echo '</td>'."\n";
797
798 echo '</tr>'."\n";
799 echo '<tr>'."\n";
800
801 echo '<th>'."\n";
802 echo '<label for="ws-plugin--s2member-paypal-api-password">'."\n";
803 echo 'Your PayPal API Password:'."\n";
804 echo '</label>'."\n";
805 echo '</th>'."\n";
806
807 echo '</tr>'."\n";
808 echo '<tr>'."\n";
809
810 echo '<td>'."\n";
811 echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_paypal_api_password" id="ws-plugin--s2member-paypal-api-password" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_api_password"]).'" /><br />'."\n";
812 echo 'At PayPal, see: <strong><a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty/signature" target="_blank" rel="external">Account Settings → Website Payments → API Access → NVP/SOAP API integration (Classic) → Manage API Credentials</a></strong>'."\n";
813 echo '</td>'."\n";
814
815 echo '</tr>'."\n";
816 echo '<tr>'."\n";
817
818 echo '<th>'."\n";
819 echo '<label for="ws-plugin--s2member-paypal-api-signature">'."\n";
820 echo 'Your PayPal API Signature:'."\n";
821 echo '</label>'."\n";
822 echo '</th>'."\n";
823
824 echo '</tr>'."\n";
825 echo '<tr>'."\n";
826
827 echo '<td>'."\n";
828 echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_paypal_api_signature" id="ws-plugin--s2member-paypal-api-signature" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_api_signature"]).'" /><br />'."\n";
829 echo 'At PayPal, see: <strong><a href="https://www.paypal.com/businessprofile/mytools/apiaccess/firstparty/signature" target="_blank" rel="external">Account Settings → Website Payments → API Access → NVP/SOAP API integration (Classic) → Manage API Credentials</a></strong>'."\n";
830 echo '</td>'."\n";
831
832 echo '</tr>'."\n";
833 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_account_detail_rows", get_defined_vars());
834
835 echo '</tbody>'."\n";
836 echo '</table>'."\n";
837
838 echo '<div class="ws-menu-page-hr"></div>'."\n";
839
840 echo '<table class="form-table" style="margin:0;">'."\n";
841 echo '<tbody>'."\n";
842 echo '<tr>'."\n";
843
844 echo '<th style="padding-top:0;">'."\n";
845 echo '<label for="ws-plugin--s2member-paypal-sandbox">'."\n";
846 echo 'Developer/Sandbox Testing?'."\n";
847 echo '</label>'."\n";
848 echo '</th>'."\n";
849
850 echo '</tr>'."\n";
851 echo '<tr>'."\n";
852
853 echo '<td>'."\n";
854 echo '<input type="radio" name="ws_plugin__s2member_paypal_sandbox" id="ws-plugin--s2member-paypal-sandbox-0" value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-sandbox-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_paypal_sandbox" id="ws-plugin--s2member-paypal-sandbox-1" value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_sandbox"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-sandbox-1">Yes, enable support for Sandbox testing.</label><br />'."\n";
855 echo '<em>Only enable this if you\'ve provided Sandbox credentials above.<br />This puts the API, IPN, PDT and Form/Button Generators all into Sandbox mode. See: <a href="http://s2member.com/r/paypal-developers/" target="_blank" rel="external">PayPal Developers</a></em><br />'."\n";
856 echo '<em><strong>Warning:</strong> The PayPal Sandbox doesn\'t always give you an accurate view of what will happen once you go live, and in fact it is sometimes buggy at best. For this reason, our strong recommendation is that instead of using Sandbox Mode to run tests, that you go live and run tests with low-dollar amounts; i.e., $0.01 transactions are possible with PayPal in live mode, and that is a better way to test your installation of s2Member.</em>'."\n";
857 echo '</td>'."\n";
858
859 echo '</tr>'."\n";
860 echo '<tr>'."\n";
861
862 echo '<th>'."\n";
863 echo '<label for="ws-plugin--s2member-paypal-btn-encryption">'."\n";
864 echo 'Button Encryption'."\n";
865 echo '</label>'."\n";
866 echo '</th>'."\n";
867
868 echo '</tr>'."\n";
869 echo '<tr>'."\n";
870
871 echo '<td>'."\n";
872 echo '<p style="margin-bottom: 0.5em;"><span class="ws-menu-page-error">⚠️ PayPal has given some trouble recently with encrypted buttons, so for the time being it\'s recommended to leave it disabled and allow non-encrypted payments. <a href="https://www.paypal.com/businessmanage/preferences/website" target="_blank" rel="external" style="font-style: italic;">PayPal Account Settings → Website Payments → Website Preferences → Encrypted Website Payments</a></span></p>'."\n"; //230807
873 echo '<input type="radio" name="ws_plugin__s2member_paypal_btn_encryption" id="ws-plugin--s2member-paypal-btn-encryption-0" value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_btn_encryption"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-btn-encryption-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_paypal_btn_encryption" id="ws-plugin--s2member-paypal-btn-encryption-1" value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_btn_encryption"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-paypal-btn-encryption-1">Yes, enable PayPal Button encryption.</label><br />'."\n";
874 echo '<em>If enabled, all of your PayPal Button Shortcodes will produce <em>encrypted</em> PayPal Buttons. This improves security against fraudulent transactions. For extra security, you should update your PayPal account too, under: <strong><a href="https://www.paypal.com/businessmanage/preferences/website" target="_blank" rel="external">Account Settings → Website Payments → Website Preferences → Encrypted Website Payments</a></strong>. You\'ll want to block all non-encrypted payments. <strong>Note:</strong> this will NOT work until you\'ve supplied s2Member with your PayPal Email Address, and also with your API Username/Password/Signature.</em>'."\n";
875 echo '</td>'."\n";
876
877 echo '</tr>'."\n";
878
879 if(!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site())
880 {
881 echo '<tr>'."\n";
882
883 echo '<th>'."\n";
884 echo '<label for="ws-plugin--s2member-gateway-debug-logs">'."\n";
885 echo 'Enable Logging Routines?'."\n";
886 echo '</label>'."\n";
887 echo '</th>'."\n";
888
889 echo '</tr>'."\n";
890 echo '<tr>'."\n";
891
892 echo '<td>'."\n";
893 echo '<input type="radio" name="ws_plugin__s2member_gateway_debug_logs" id="ws-plugin--s2member-gateway-debug-logs-0" value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["gateway_debug_logs"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-gateway-debug-logs-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_gateway_debug_logs" id="ws-plugin--s2member-gateway-debug-logs-1" value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["gateway_debug_logs"]) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-gateway-debug-logs-1">Yes, enable debugging, with API, IPN &amp; Return Page logging.</label><br />'."\n";
894 echo '<em>This enables API, IPN and Return Page logging. The log files are stored here: <code>'.esc_html(c_ws_plugin__s2member_utils_dirs::doc_root_path($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["logs_dir"])).'</code></em><br />'."\n";
895 echo '<em class="ws-menu-page-hilite">If you have any trouble, please review your s2Member log files for problems. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Viewer</a></em>'."\n";
896 echo '</td>'."\n";
897
898 echo '</tr>'."\n";
899 echo '<tr>'."\n";
900
901 echo '<td>'."\n";
902 echo '<div class="info" style="margin-bottom:0;">'."\n";
903 echo '<p style="margin-top:0;"><span>We highly recommend that you enable logging during your initial testing phase. Logs produce lots of useful details that can help in debugging. Logs can help you find issues in your configuration and/or problems during payment processing. See: <a href="'.esc_attr(admin_url("/admin.php?page=ws-plugin--s2member-logs")).'">Log Files (Debug)</a>.</span></p>'."\n";
904 echo '<p style="margin-bottom:0;"><span class="ws-menu-page-error">However, it is very important to disable logging once you go live. Log files may contain personally identifiable information, credit card numbers, secret API credentials, passwords and/or other sensitive information. We strongly suggest that logging be disabled on a live site (for security reasons).</span></p>'."\n";
905 echo '</div>'."\n";
906 echo '</td>'."\n";
907
908 echo '</tr>'."\n";
909 }
910 echo '</tbody>'."\n";
911 echo '</table>'."\n";
912
913 echo '<div class="ws-menu-page-hr"></div>'."\n";
914
915 echo '<p><em><strong>Sandbox Tip:</strong> If you\'re testing your site through a PayPal Sandbox account, please remember that Email Confirmations from s2Member will not be received after a test purchase. s2Member sends its Confirmation Emails to the PayPal Email Address of the Customer. Since PayPal Sandbox addresses are usually bogus (for testing), you will have to run live transactions before Email Confirmations from s2Member are received. That being said, all other s2Member functionality can be tested through a PayPal Sandbox account. Email Confirmations are the only hang-up.</em></p>'."\n";
916 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_account_details_after_sandbox_tip", get_defined_vars());
917 echo '</div>'."\n";
918
919 echo '</div>'."\n";
920
921 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_account_details", get_defined_vars());
922 }
923
924 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_payflow_account_details", c_ws_plugin__s2member_utils_conds::pro_is_installed(), get_defined_vars()))
925 {
926 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_payflow_account_details", get_defined_vars());
927
928 echo '<div class="ws-menu-page-group" title="PayPal Pro Payflow Edition API Credentials">'."\n";
929
930 echo '<div class="ws-menu-page-section ws-plugin--s2member-paypal-payflow-account-details-section">'."\n";
931 echo '<a href="https://s2member.com/r/paypal/" target="_blank"><img src="'.esc_attr($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]).'/src/images/paypal-logo.png" class="ws-menu-page-right" style="width:125px; height:125px; border:0;" alt="." /></a>'."\n";
932 echo '<h3>Payflow Account Details (required, if using Payflow)</h3>'."\n";
933 echo '<p>Newer PayPal Pro accounts (i.e., PayPal Pro w/ Payflow Edition), come with the Payflow API for Recurring Billing service. If you have a newer PayPal Pro account, and you wish to integrate PayPal\'s Recurring Billing service with s2Member Pro-Forms, you will need to fill in the details here. Providing Payflow API Credentials below, will automatically put s2Member\'s Recurring Billing integration through Pro-Forms into Payflow mode. Just fill in the details below, and you\'re ready to generate Pro-Forms that charge customers on a recurring basis. s2Member will use the Payflow Edition API instead of the older PayPal Pro DPRP service; which is being slowly phased out in favor of Payflow Edition APIs.</p>'."\n";
934 echo '<p><em><strong>Payflow API Credentials:</strong> Once you have a PayPal Pro account, you\'ll need access to your <a href="http://s2member.com/r/paypal-profile-api-access/" target="_blank" rel="external">Payflow API Credentials</a>. Log into your PayPal account, and navigate to <strong>Profile → API Access (or → Request API Credentials)</strong>. From the available options, please choose "Payflow / API Access". You will need the following credentials: Username, Password, Partner, and Vendor.</em></p>'."\n";
935 echo '<p><em><strong>Important Note:</strong> Supplying Payflow API Credentials here does not mean you can bypass other areas of s2Member\'s configuration; i.e., please supply s2Member with all of your PayPal account details. Your PayPal Pro (Payflow Edition) account is configured here, but up above you will need to configure other Account Details also.</em></p>'."\n";
936 echo '<p><strong>See also:</strong> This KB article: <a href="http://s2member.com/kb-article/supported-paypal-account-types/" target="_blank" rel="external">PayPal Account Types</a>.</p>'."\n";
937 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_payflow_account_details", get_defined_vars());
938
939 echo '<table class="form-table">'."\n";
940 echo '<tbody>'."\n";
941 echo '<tr>'."\n";
942
943 echo '<th>'."\n";
944 echo '<label for="ws-plugin--s2member-paypal-payflow-api-username">'."\n";
945 echo 'Your Payflow API Username:'."\n";
946 echo '</label>'."\n";
947 echo '</th>'."\n";
948
949 echo '</tr>'."\n";
950 echo '<tr>'."\n";
951
952 echo '<td>'."\n";
953 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_paypal_payflow_api_username" id="ws-plugin--s2member-paypal-payflow-api-username" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_payflow_api_username"]).'" /><br />'."\n";
954 echo 'At PayPal, see: <strong>Profile → API Access (or → Request API Credentials) → Payflow API Access</strong>'."\n";
955 echo '</td>'."\n";
956
957 echo '</tr>'."\n";
958 echo '<tr>'."\n";
959
960 echo '<th>'."\n";
961 echo '<label for="ws-plugin--s2member-paypal-payflow-api-password">'."\n";
962 echo 'Your Payflow API Password:'."\n";
963 echo '</label>'."\n";
964 echo '</th>'."\n";
965
966 echo '</tr>'."\n";
967 echo '<tr>'."\n";
968
969 echo '<td>'."\n";
970 echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_paypal_payflow_api_password" id="ws-plugin--s2member-paypal-payflow-api-password" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_payflow_api_password"]).'" /><br />'."\n";
971 echo 'At PayPal, see: <strong>Profile → API Access (or → Request API Credentials) → Payflow API Access</strong>'."\n";
972 echo '</td>'."\n";
973
974 echo '</tr>'."\n";
975 echo '<tr>'."\n";
976
977 echo '<th>'."\n";
978 echo '<label for="ws-plugin--s2member-paypal-payflow-api-partner">'."\n";
979 echo 'Your Payflow API Partner:'."\n";
980 echo '</label>'."\n";
981 echo '</th>'."\n";
982
983 echo '</tr>'."\n";
984 echo '<tr>'."\n";
985
986 echo '<td>'."\n";
987 echo '<input type="text" name="ws_plugin__s2member_paypal_payflow_api_partner" id="ws-plugin--s2member-paypal-payflow-api-partner" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_payflow_api_partner"]).'" /><br />'."\n";
988 echo 'At PayPal, see: <strong>Profile → API Access (or → Request API Credentials) → Payflow API Access</strong>'."\n";
989 echo '</td>'."\n";
990
991 echo '</tr>'."\n";
992 echo '<tr>'."\n";
993
994 echo '<th>'."\n";
995 echo '<label for="ws-plugin--s2member-paypal-payflow-api-vendor">'."\n";
996 echo 'Your Payflow API Vendor:'."\n";
997 echo '</label>'."\n";
998 echo '</th>'."\n";
999
1000 echo '</tr>'."\n";
1001 echo '<tr>'."\n";
1002
1003 echo '<td>'."\n";
1004 echo '<input type="text" name="ws_plugin__s2member_paypal_payflow_api_vendor" id="ws-plugin--s2member-paypal-payflow-api-vendor" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_payflow_api_vendor"]).'" /><br />'."\n";
1005 echo 'At PayPal, see: <strong>Profile → API Access (or → Request API Credentials) → Payflow API Access</strong>'."\n";
1006 echo '</td>'."\n";
1007
1008 echo '</tr>'."\n";
1009 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_payflow_account_detail_rows", get_defined_vars());
1010 echo '</tbody>'."\n";
1011 echo '</table>'."\n";
1012 echo '</div>'."\n";
1013
1014 echo '</div>'."\n";
1015
1016 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_payflow_account_details", get_defined_vars());
1017 }
1018
1019 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_ipn", TRUE, get_defined_vars()))
1020 {
1021 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_ipn", get_defined_vars());
1022
1023 echo '<div class="ws-menu-page-group" title="PayPal IPN Integration">'."\n";
1024
1025 echo '<div class="ws-menu-page-section ws-plugin--s2member-paypal-ipn-section">'."\n";
1026 echo '<h3>PayPal IPN / Instant Payment Notifications (required, please enable)</h3>'."\n";
1027 echo '<p>Log into your PayPal account and navigate to this section:<br /><strong><a href="http://s2member.com/r/paypal-com-ipn-configuration-page/" target="_blank" rel="external">Account Settings → Website Payments → Instant Payment Notifications</a></strong></p>'."\n";
1028 echo '<p>Edit your IPN settings &amp; turn IPN Notifications: <strong><code>On</code></strong></p>'."\n";
1029 echo '<p>You\'ll need your IPN URL, which is:<br /><code>'.esc_html(home_url("/?s2member_paypal_notify=1", "https")).'</code></p>'."\n";
1030 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_ipn", get_defined_vars());
1031
1032 echo '<h4 style="margin-bottom:0;"><strong class="ws-menu-page-hilite">Note: SSL is required by PayPal</strong></h4>'."\n";
1033 echo '<p style="margin-top:0;">If you configure your PayPal.com account using the URL above, your site <strong><em>must</em> support SSL</strong> (i.e., the <code>https://</code> protocol). In other words, PayPal\'s system will refuse to accept any URL that does not begin with <code>https://</code>. The IPN URL that s2Member provides (see above) <em>does</em> start with <code>https://</code>. However, that doesn\'t necessarily mean that the URL actually works. Please be sure that your hosting account is configured with a valid SSL certificate before giving this URL to PayPal.</p>'."\n";
1034
1035 //250426 Fallback for IPN Signup Vars.
1036 echo '<div class="ws-menu-page-hr"></div>'."\n";
1037
1038 echo '<table class="form-table">'."\n";
1039 echo '<tbody>'."\n";
1040 echo '<tr>'."\n";
1041
1042 echo '<th>'."\n";
1043 echo '<label for="ws-plugin--s2member-ipn-signup-vars-fallback">'."\n";
1044 echo 'Enable Fallback for Missing User "IPN Signup Vars"? (beta)<br />'."\n";
1045 echo '<small><em class="ws-menu-page-hilite">* This setting applies to all gateways. [ <a href="#" onclick="alert(\'This configuration option may also appear under the other gateways. You can change it here, but remember that this setting is shared among all Payment Gateways integrated with s2Member.\'); return false;">?</a> ]</em></small>'."\n";
1046 echo '</label>'."\n";
1047 echo '</th>'."\n";
1048
1049 echo '</tr>'."\n";
1050 echo '<tr>'."\n";
1051
1052 echo '<td>'."\n";
1053 echo '<input type="radio" name="ws_plugin__s2member_ipn_signup_vars_fallback" id="ws-plugin--s2member-ipn-signup-vars-fallback-0" value="0"'.((!$GLOBALS['WS_PLUGIN__']['s2member']['o']['ipn_signup_vars_fallback']) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-ipn-signup-vars-fallback-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_ipn_signup_vars_fallback" id="ws-plugin--s2member-ipn-signup-vars-fallback-1" value="1"'.(($GLOBALS['WS_PLUGIN__']['s2member']['o']['ipn_signup_vars_fallback']) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-ipn-signup-vars-fallback-1">Yes, enable user IPN Signup Vars fallback.</label><br />'."\n";
1054 echo '<em>Enable fallback behavior when the user\'s IPN Signup Variables are missing during the IPN event processing (useful for migrated or imported subscriptions, for example).</em><br />'."\n";
1055 echo '</td>'."\n";
1056
1057 echo '</tr>'."\n";
1058 echo '</tbody>'."\n";
1059 echo '</table>'."\n";
1060
1061 //250606 Skip Custom Value Domain Validation.
1062 echo '<div class="ws-menu-page-hr"></div>'."\n";
1063
1064 echo '<table class="form-table">'."\n";
1065 echo '<tbody>'."\n";
1066 echo '<tr>'."\n";
1067
1068 echo '<th>'."\n";
1069 echo '<label for="ws-plugin--s2member-skip-ipn-domain-validation">'."\n";
1070 echo 'Skip Domain Validation of the `custom` Value? (beta)<br />'."\n";
1071 echo '<small><em class="ws-menu-page-hilite">* This setting applies to all gateways. [ <a href="#" onclick="alert(\'This configuration option may also appear under the other gateways. You can change it here, but remember that this setting is shared among all Payment Gateways integrated with s2Member.\'); return false;">?</a> ]</em></small>'."\n";
1072 echo '</label>'."\n";
1073 echo '</th>'."\n";
1074
1075 echo '</tr>'."\n";
1076 echo '<tr>'."\n";
1077
1078 echo '<td>'."\n";
1079 echo '<input type="radio" name="ws_plugin__s2member_skip_ipn_domain_validation" id="ws-plugin--s2member-skip-ipn-domain-validation-0" value="0"'.((!$GLOBALS['WS_PLUGIN__']['s2member']['o']['skip_ipn_domain_validation']) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-skip-ipn-domain-validation-0">No</label> &nbsp;&nbsp;&nbsp; <input type="radio" name="ws_plugin__s2member_skip_ipn_domain_validation" id="ws-plugin--s2member-skip-ipn-domain-validation-1" value="1"'.(($GLOBALS['WS_PLUGIN__']['s2member']['o']['skip_ipn_domain_validation']) ? ' checked="checked"' : '').' /> <label for="ws-plugin--s2member-skip-ipn-domain-validation-1">Yes, skip domain checks in the `custom` value.</label><br />'."\n";
1080 echo '<em>This allows continued processing of notifications even if the domain in the `custom` value doesn\'t match the current <code>'.esc_html($_SERVER["HTTP_HOST"]).'</code>. Useful for subscriptions originated outside of s2Member, or under a different domain.</em><br />'."\n";
1081 echo '</td>'."\n";
1082
1083 echo '</tr>'."\n";
1084 echo '</tbody>'."\n";
1085 echo '</table>'."\n";
1086
1087 echo '<div class="ws-menu-page-hr"></div>'."\n";
1088
1089 echo '<h3 style="margin:0;">More Information (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-paypal-ipn-details\').toggle(); return false;" class="ws-dotted-link">click here</a>)</h3>'."\n";
1090 echo '<div id="ws-plugin--s2member-paypal-ipn-details" style="margin-top:10px; display:none;">'."\n";
1091 echo '<p><em><strong>IPN History:</strong> Here\'s PayPal\'s <a href="https://s2member.com/r/paypal-ipn-history/" target="_blank" rel="external">Instant Payment Notification (IPN) History page</a>.</em></p>';
1092 echo '<p><em><strong>Quick Tip:</strong> In addition to the <a href="http://s2member.com/r/paypal-com-ipn-configuration-page/" target="_blank" rel="external">default IPN settings inside your PayPal account</a>, the IPN URL is also set on a per-transaction basis by the special PayPal Button Code that s2Member provides you with. In other words, if you have multiple sites operating on one PayPal account, that\'s OK. s2Member dynamically sets the IPN URL for each transaction. The result is that the IPN URL configured from within your PayPal account, becomes the default, which is then overwritten on a per-transaction basis. In fact, PayPal recently updated their system to support IPN URL preservation. One PayPal account can handle multiple sites, all using different IPN URLs.</em></p>'."\n";
1093 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_ipn_after_quick_tip", get_defined_vars());
1094 echo '<p><em><strong>IPN Communications:</strong> You\'ll be happy to know that s2Member handles cancellations, expirations, failed payments, terminations (e.g., refunds &amp; chargebacks) for you automatically. If you log into your PayPal account and cancel a Member\'s Subscription, or, if the Member logs into their PayPal account and cancels their own Subscription, s2Member will be notified of these important changes and react accordingly through the PayPal IPN service that runs silently behind-the-scene. The PayPal IPN service will notify s2Member whenever a Member\'s payments have been failing, and/or whenever a Member\'s Subscription has expired for any reason. Even refunds &amp; chargeback reversals are supported through the IPN service. If you issue a refund to an unhappy Customer through PayPal, s2Member will be notified, and the account for that Customer will either be demoted to a Free Subscriber, or deleted automatically (based on your configuration). The communication from PayPal → s2Member is seamless.</em></p>'."\n";
1095 echo '</div>'."\n";
1096
1097 echo '<div class="ws-menu-page-hr"></div>'."\n";
1098
1099 echo '<h3 style="margin:0;">IPN w/ Proxy Key (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-paypal-ipn-proxy-details\').toggle(); return false;" class="ws-dotted-link">optional, for 3rd-party integrations</a>)</h3>'."\n";
1100 echo '<div id="ws-plugin--s2member-paypal-ipn-proxy-details" style="margin-top:10px; display:none;">'."\n";
1101 echo '<p>If you\'re using a 3rd-party application that needs to POST simulated IPN transactions to your s2Member installation, you can use this alternate IPN URL, which includes a Proxy Key. This encrypted Proxy Key verifies incoming data being received by s2Member\'s IPN processor. You can change <em>[proxy-gateway]</em> to whatever you like. The <em>[proxy-gateway]</em> value is required. It will be stored by s2Member as the Customer\'s Paid Subscr. Gateway. Your [proxy-gateway] value will also be reflected in s2Member\'s IPN log.</p>'."\n";
1102 echo '<input type="text" autocomplete="off" value="'.format_to_edit(home_url("/?s2member_paypal_notify=1&s2member_paypal_proxy=[proxy-gateway]&s2member_paypal_proxy_verification=".urlencode(c_ws_plugin__s2member_paypal_utilities::paypal_proxy_key_gen()), "https")).'" style="width:99%;" />'."\n";
1103 echo '<p><em>Any 3rd-party application that is sending IPN transactions to your s2Member installation must include the <code>custom</code> POST variable, and that variable must always start with your installation domain (i.e., custom=<code>'.esc_html($_SERVER["HTTP_HOST"]).'</code>). In addition, the <code>item_number</code> variable must always match a format that s2Member looks for. Generally speaking, the <code>item_number</code> should be <code>1, 2, 3, or 4</code>, indicating a specific s2Member Level #. However, s2Member also uses some advanced formats in this field. Just to be sure, we suggest creating a PayPal Button with the s2Member Button Generator, and then taking a look at the Full Button Code to see how s2Member expects <code>item_number</code> to be formatted. Other than the aforementioned exceptions, all other POST variables should follow PayPal standards. Please see: <a href="https://s2member.com/r/paypal/-ipn-pdt-vars" target="_blank" rel="external">PayPal\'s IPN/PDT reference guide</a> for full documentation.</em></p>'."\n";
1104 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_ipn_after_proxy", get_defined_vars());
1105 echo '</div>'."\n";
1106 echo '</div>'."\n";
1107
1108 echo '</div>'."\n";
1109
1110 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_ipn", get_defined_vars());
1111 }
1112
1113 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_pdt", TRUE, get_defined_vars()))
1114 {
1115 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_pdt", get_defined_vars());
1116
1117 echo '<div class="ws-menu-page-group" title="PayPal PDT/Auto-Return Integration">'."\n";
1118
1119 echo '<div class="ws-menu-page-section ws-plugin--s2member-paypal-pdt-section">'."\n";
1120 echo '<h3>PayPal PDT Identity Token (required, please enable)</h3>'."\n";
1121 echo '<p>Log into your PayPal account and navigate to this section:<br /><strong><a href="https://www.paypal.com/businessmanage/preferences/website" target="_blank" rel="external">Account Settings → Website Payments → Website Preferences</a></strong></p>'."\n";
1122 echo '<p>Turn the Auto-Return feature: <strong><code>On</code></strong></p>'."\n";
1123 echo '<p>You\'ll need your <a href="'.esc_attr(home_url("/?s2member_paypal_return=1&s2member_paypal_proxy=paypal&s2member_paypal_proxy_use=x-preview")).'" target="_blank" rel="external">Auto-Return URL</a>, which is:<br /><code>'.esc_html(home_url("/?s2member_paypal_return=1")).'</code></p>'."\n";
1124 echo '<p>You must also enable PDT (Payment Data Transfer): <strong><code>On</code></strong><br /><em>You\'ll be issued an Identity Token that you can enter below.</em></p>'."\n";
1125 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_pdt", get_defined_vars());
1126
1127 echo '<table class="form-table">'."\n";
1128 echo '<tbody>'."\n";
1129 echo '<tr>'."\n";
1130
1131 echo '<th>'."\n";
1132 echo '<label for="ws-plugin--s2member-paypal-identity-token">'."\n";
1133 echo 'PayPal PDT Identity Token:'."\n";
1134 echo '</label>'."\n";
1135 echo '</th>'."\n";
1136
1137 echo '</tr>'."\n";
1138 echo '<tr>'."\n";
1139
1140 echo '<td>'."\n";
1141 echo '<input type="password" autocomplete="off" name="ws_plugin__s2member_paypal_identity_token" id="ws-plugin--s2member-paypal-identity-token" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_identity_token"]).'" /><br />'."\n";
1142 echo 'Your PDT Identity Token will appear under <strong>Account Settings → Website Payments → Website Preferences</strong> in your PayPal account.'."\n";
1143 echo '</td>'."\n";
1144
1145 echo '</tr>'."\n";
1146 echo '</tbody>'."\n";
1147 echo '</table>'."\n";
1148
1149 echo '<div class="ws-menu-page-hr"></div>'."\n";
1150
1151 echo '<h3 style="margin:0;">More Information (<a href="#" onclick="jQuery(\'div#ws-plugin--s2member-paypal-pdt-details\').toggle(); return false;" class="ws-dotted-link">click here</a>)</h3>'."\n";
1152 echo '<div id="ws-plugin--s2member-paypal-pdt-details" style="margin-top:10px; display:none;">'."\n";
1153 echo '<p><em><strong>Quick Tip:</strong> In addition to the <a href="http://s2member.com/r/paypal-pdt-setup/" target="_blank" rel="external">default Auto-Return/PDT configuration inside your PayPal account</a>, the Auto-Return URL is also set on a per-transaction basis from within the special PayPal Button Code that s2Member provides you with. In other words, if you have multiple sites operating on one PayPal account, that\'s OK. s2Member sets the Auto-Return URL (dynamically) for each transaction. The result is that the Auto-Return URL configured from within your PayPal account becomes the default, which is then overwritten on a per-transaction basis by the s2Member software.</em></p>'."\n";
1154 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_pdt_after_quick_tip", get_defined_vars());
1155 echo '</div>'."\n";
1156
1157 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_pdt_after_more_info", get_defined_vars());
1158
1159 echo '</div>'."\n";
1160
1161 echo '</div>'."\n";
1162
1163 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_pdt", get_defined_vars());
1164 }
1165
1166 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_signup_confirmation_email", TRUE, get_defined_vars()))
1167 {
1168 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_signup_confirmation_email", get_defined_vars());
1169
1170 echo '<div class="ws-menu-page-group" title="Signup Confirmation Email (Standard)">'."\n";
1171
1172 echo '<div class="ws-menu-page-section ws-plugin--s2member-signup-confirmation-email-section">'."\n";
1173 echo '<h3>Signup Confirmation Email (required, but the default works fine)</h3>'."\n";
1174 echo '<p>This email is sent to new Customers after they return from a successful signup at PayPal. The <strong>primary</strong> purpose of this email is to provide the Customer with instructions, along with a link to register a Username for their Membership. You may also customize this further by providing details that are specifically geared to your site.</p>'."\n";
1175 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_signup_confirmation_email", get_defined_vars());
1176
1177 echo '<table class="form-table">'."\n";
1178 echo '<tbody>'."\n";
1179 echo '<tr>'."\n";
1180
1181 echo '<th>'."\n";
1182 echo '<label for="ws-plugin--s2member-signup-email-recipients">'."\n";
1183 echo 'Signup Confirmation Recipients:'."\n";
1184 echo '</label>'."\n";
1185 echo '</th>'."\n";
1186
1187 echo '</tr>'."\n";
1188 echo '<tr>'."\n";
1189
1190 echo '<td>'."\n";
1191 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_signup_email_recipients" id="ws-plugin--s2member-signup-email-recipients" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["signup_email_recipients"]).'" /><br />'."\n";
1192 echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:<br />'."\n";
1193 echo '<code>"%%full_name%%" &lt;%%payer_email%%&gt;; admin@example.com; "Webmaster" &lt;webmaster@example.com&gt;</code>'."\n";
1194 echo '</td>'."\n";
1195
1196 echo '</tr>'."\n";
1197 echo '<tr>'."\n";
1198
1199 echo '<th>'."\n";
1200 echo '<label for="ws-plugin--s2member-signup-email-subject">'."\n";
1201 echo 'Signup Confirmation Email Subject:'."\n";
1202 echo '</label>'."\n";
1203 echo '</th>'."\n";
1204
1205 echo '</tr>'."\n";
1206 echo '<tr>'."\n";
1207
1208 echo '<td>'."\n";
1209 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_signup_email_subject" id="ws-plugin--s2member-signup-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["signup_email_subject"]).'" /><br />'."\n";
1210 echo 'Subject Line used in the email sent to a Customer after a successful signup has occurred through PayPal.'."\n";
1211 echo '</td>'."\n";
1212
1213 echo '</tr>'."\n";
1214 echo '<tr>'."\n";
1215
1216 echo '<th>'."\n";
1217 echo '<label for="ws-plugin--s2member-signup-email-message">'."\n";
1218 echo 'Signup Confirmation Email Message:'."\n";
1219 echo '</label>'."\n";
1220 echo '</th>'."\n";
1221
1222 echo '</tr>'."\n";
1223 echo '<tr>'."\n";
1224
1225 echo '<td>'."\n";
1226 //250615 Visual editor for HTML emails.
1227 if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) {
1228 echo '<textarea name="ws_plugin__s2member_signup_email_message" id="ws-plugin--s2member-signup-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["signup_email_message"]).'</textarea><br />'."\n";
1229 } else {
1230 c_ws_plugin__s2member_utilities::editor('signup_email_message');
1231 }
1232
1233 echo 'Message Body used in the email sent to a Customer after a successful signup has occurred through PayPal.<br /><br />'."\n";
1234 echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
1235 echo '<ul class="ws-menu-page-li-margins">'."\n";
1236 echo '<li><code>%%registration_url%%</code> = The full URL (generated by s2Member) where the Customer can get registered.</li>'."\n";
1237 echo '<li><code>%%subscr_id%%</code> = The PayPal Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the purchase. PayPal does not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there is only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
1238 echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
1239 echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
1240 echo '<li><code>%%initial%%</code> = The Initial Fee charged during signup. If you offered a 100% Free Trial, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This will always represent the amount of money the Customer spent, whenever they initially signed up, no matter what. If a Customer signs up, under the terms of a 100% Free Trial Period, this will be 0.\'); return false;">?</a> ]</li>'."\n";
1241 echo '<li><code>%%regular%%</code> = The Regular Amount of the Subscription. If you offer something 100% free, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This is how much the Subscription costs after an Initial Period expires. If you did NOT offer an Initial Period at a different price, %%initial%% and %%regular%% will be equal to the same thing.\'); return false;">?</a> ]</li>'."\n";
1242 echo '<li><code>%%recurring%%</code> = This is the amount that will be charged on a recurring basis, or <code>0</code> if non-recurring. [ <a href="#" onclick="alert(\'If Recurring Payments have not been required, this will be equal to 0. That being said, %%regular%% &amp; %%recurring%% are usually the same value. This variable can be used in two different ways. You can use it to determine what the Regular Recurring Rate is, or to determine whether the Subscription will recur or not. If it is going to recur, %%recurring%% will be > 0.\'); return false;">?</a> ]</li>'."\n";
1243 echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased the Membership Subscription.</li>'."\n";
1244 echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased the Membership Subscription.</li>'."\n";
1245 echo '<li><code>%%full_name%%</code> = The Full Name (First &amp; Last) of the Customer who purchased the Membership Subscription.</li>'."\n";
1246 echo '<li><code>%%payer_email%%</code> = The Email Address of the Customer who purchased the Membership Subscription.</li>'."\n";
1247 echo '<li><code>%%user_ip%%</code> = The Customer\'s IP Address, detected during checkout via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
1248 echo '<li><code>%%item_number%%</code> = The Item Number (colon separated <code><em>level:custom_capabilities:fixed term</em></code>) that the Subscription is for.</li>'."\n";
1249 echo '<li><code>%%item_name%%</code> = The Item Name (as provided by the <code>desc=""</code> attribute in your Shortcode, which briefly describes the Item Number).</li>'."\n";
1250 echo '<li><code>%%initial_term%%</code> = This is the term length of the Initial Period. This will be a numeric value, followed by a space, then a single letter. [ <a href="#" onclick="alert(\'Here are some examples:\\n\\n%%initial_term%% = 1 D (this means 1 Day)\\n%%initial_term%% = 1 W (this means 1 Week)\\n%%initial_term%% = 1 M (this means 1 Month)\\n%%initial_term%% = 1 Y (this means 1 Year)\\n\\nThe Initial Period never recurs, so this only lasts for the term length specified, then it is over.\'); return false;">?</a> ]</li>'."\n";
1251 echo '<li><code>%%initial_cycle%%</code> = This is the <code>%%initial_term%%</code> from above, converted to a cycle representation of: <code><em>X days/weeks/months/years</em></code>.</li>'."\n";
1252 echo '<li><code>%%regular_term%%</code> = This is the term length of the Regular Period. This will be a numeric value, followed by a space, then a single letter. [ <a href="#" onclick="alert(\'Here are some examples:\\n\\n%%regular_term%% = 1 D (this means 1 Day)\\n%%regular_term%% = 1 W (this means 1 Week)\\n%%regular_term%% = 1 M (this means 1 Month)\\n%%regular_term%% = 1 Y (this means 1 Year)\\n%%regular_term%% = 1 L (this means 1 Lifetime)\\n\\nThe Regular Term is usually recurring. So the Regular Term value represents the period (or duration) of each recurring period. If %%recurring%% = 0, then the Regular Term only applies once, because it is not recurring. So if it is not recurring, the value of %%regular_term%% simply represents how long their Membership privileges are going to last after the %%initial_term%% has expired, if there was an Initial Term. The value of this variable ( %%regular_term%% ) will never be empty, it will always be at least: 1 D, meaning 1 day. No exceptions.\'); return false;">?</a> ]</li>'."\n";
1253 echo '<li><code>%%regular_cycle%%</code> = This is the <code>%%regular_term%%</code> from above, converted to a cycle representation of: <code><em>[every] X days/weeks/months/years—OR daily, weekly, bi-weekly, monthly, bi-monthly, quarterly, yearly, or lifetime</em></code>. This is a very useful Replacment Code. Its value is dynamic; depending on term length, recurring status, and period/term lengths configured.</li>'."\n";
1254 echo '<li><code>%%recurring/regular_cycle%%</code> = Example (<code>14.95 / Monthly</code>), or ... (<code>0 / non-recurring</code>); depending on the value of <code>%%recurring%%</code>.</li>'."\n";
1255 echo '</ul>'."\n";
1256
1257 echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
1258 echo '<ul class="ws-menu-page-li-margins">'."\n";
1259 echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
1260 echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
1261 echo '</ul>'."\n";
1262 echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
1263 echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
1264 echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
1265
1266 echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
1267 '<div class="ws-menu-page-hr"></div>'."\n".
1268 '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags—optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$paypal</code> variable is the most important one. It contains all of the <code>$_POST</code> variables received from PayPal\'s IPN service—or from an s2Member Pro-Form integration (e.g., <code>$paypal["item_number"]</code>, <code>$paypal["item_name"]</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
1269 : '';
1270 echo '</td>'."\n";
1271
1272 echo '</tr>'."\n";
1273 echo '</tbody>'."\n";
1274 echo '</table>'."\n";
1275 echo '</div>'."\n";
1276
1277 echo '</div>'."\n";
1278
1279 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_signup_confirmation_email", get_defined_vars());
1280 }
1281
1282 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_modification_confirmation_email", c_ws_plugin__s2member_utils_conds::pro_is_installed(), get_defined_vars()))
1283 {
1284 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_modification_confirmation_email", get_defined_vars());
1285
1286 echo '<div class="ws-menu-page-group" title="Modification Confirmation Email '.((c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '(Standard/Pro-Form)' : '(Standard)').'">'."\n";
1287
1288 echo '<div class="ws-menu-page-section ws-plugin--s2member-modification-confirmation-email-section">'."\n";
1289 echo '<h3>Modification Confirmation Email (required, but the default works fine)</h3>'."\n";
1290 echo '<p>This email is sent to existing Users after they complete an upgrade/downgrade (if and when you make this possible). For instance, if a Free Subscriber upgrades to a paid Membership Level, s2Member considers this a Modification (NOT a Signup; a Signup is associated only with someone completely new). The <strong>primary</strong> purpose of this email is to provide the Customer with a confirmation that their account was updated. You may also customize this further by providing details that are specifically geared to your site.</p>'."\n";
1291 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_modification_confirmation_email", get_defined_vars());
1292
1293 echo '<table class="form-table">'."\n";
1294 echo '<tbody>'."\n";
1295 echo '<tr>'."\n";
1296
1297 echo '<th>'."\n";
1298 echo '<label for="ws-plugin--s2member-modification-email-recipients">'."\n";
1299 echo 'Modification Confirmation Recipients:'."\n";
1300 echo '</label>'."\n";
1301 echo '</th>'."\n";
1302
1303 echo '</tr>'."\n";
1304 echo '<tr>'."\n";
1305
1306 echo '<td>'."\n";
1307 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_modification_email_recipients" id="ws-plugin--s2member-modification-email-recipients" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["modification_email_recipients"]).'" /><br />'."\n";
1308 echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:<br />'."\n";
1309 echo '<code>"%%full_name%%" &lt;%%payer_email%%&gt;; admin@example.com; "Webmaster" &lt;webmaster@example.com&gt;</code>'."\n";
1310 echo '</td>'."\n";
1311
1312 echo '</tr>'."\n";
1313 echo '<tr>'."\n";
1314
1315 echo '<th>'."\n";
1316 echo '<label for="ws-plugin--s2member-modification-email-subject">'."\n";
1317 echo 'Modification Confirmation Email Subject:'."\n";
1318 echo '</label>'."\n";
1319 echo '</th>'."\n";
1320
1321 echo '</tr>'."\n";
1322 echo '<tr>'."\n";
1323
1324 echo '<td>'."\n";
1325 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_modification_email_subject" id="ws-plugin--s2member-modification-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["modification_email_subject"]).'" /><br />'."\n";
1326 echo 'Subject Line used in the email sent to a Customer after a successful modification has occurred through PayPal.'."\n";
1327 echo '</td>'."\n";
1328
1329 echo '</tr>'."\n";
1330 echo '<tr>'."\n";
1331
1332 echo '<th>'."\n";
1333 echo '<label for="ws-plugin--s2member-modification-email-message">'."\n";
1334 echo 'Modification Confirmation Email Message:'."\n";
1335 echo '</label>'."\n";
1336 echo '</th>'."\n";
1337
1338 echo '</tr>'."\n";
1339 echo '<tr>'."\n";
1340
1341 echo '<td>'."\n";
1342 //250615 Visual editor for HTML emails.
1343 if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) {
1344 echo '<textarea name="ws_plugin__s2member_modification_email_message" id="ws-plugin--s2member-modification-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["modification_email_message"]).'</textarea><br />'."\n";
1345 } else {
1346 c_ws_plugin__s2member_utilities::editor('modification_email_message');
1347 }
1348
1349 echo 'Message Body used in the email sent to a Customer after a successful modification has occurred through PayPal.<br /><br />'."\n";
1350 echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
1351 echo '<ul class="ws-menu-page-li-margins">'."\n";
1352 echo '<li><code>%%subscr_id%%</code> = The PayPal Subscription ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'There is one exception. If you are selling Lifetime or Fixed-Term (non-recurring) access, using Buy Now functionality; the %%subscr_id%% is actually set to the Transaction ID for the purchase. PayPal does not provide a specific Subscription ID for Buy Now purchases. Since Lifetime &amp; Fixed-Term Subscriptions are NOT recurring (i.e., there is only ONE payment), using the Transaction ID as the Subscription ID is a graceful way to deal with this minor conflict.\'); return false;">?</a> ]</li>'."\n";
1353 echo '<li><code>%%subscr_baid%%</code> = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any &amp; all future payments. [ <a href="#" onclick="alert(\'Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. In all other cases, the %%subscr_baid%% is simply set to the %%subscr_id%% value; i.e., it is a duplicate of %%subscr_id%% in most cases.\'); return false;">?</a> ]</li>'."\n";
1354 echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
1355 echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
1356 echo '<li><code>%%initial%%</code> = The Initial Fee. If you offered a 100% Free Trial, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This will always represent the amount of money the Customer spent when they completed checkout, no matter what. Even if that amount is 0. If a Customer upgrades/downgrades under the terms of a 100% Free Trial Period, this will be 0.\'); return false;">?</a> ]</li>'."\n";
1357 echo '<li><code>%%regular%%</code> = The Regular Amount of the Subscription. If you offer something 100% free, this will be <code>0</code>. [ <a href="#" onclick="alert(\'This is how much the Subscription costs after an Initial Period expires. If you did NOT offer an Initial Period at a different price, %%initial%% and %%regular%% will be equal to the same thing.\'); return false;">?</a> ]</li>'."\n";
1358 echo '<li><code>%%recurring%%</code> = This is the amount that will be charged on a recurring basis, or <code>0</code> if non-recurring. [ <a href="#" onclick="alert(\'If Recurring Payments have not been required, this will be equal to 0. That being said, %%regular%% &amp; %%recurring%% are usually the same value. This variable can be used in two different ways. You can use it to determine what the Regular Recurring Rate is, or to determine whether the Subscription will recur or not. If it is going to recur, %%recurring%% will be > 0.\'); return false;">?</a> ]</li>'."\n";
1359 echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased the Membership Subscription.</li>'."\n";
1360 echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased the Membership Subscription.</li>'."\n";
1361 echo '<li><code>%%full_name%%</code> = The Full Name (First &amp; Last) of the Customer who purchased the Membership Subscription.</li>'."\n";
1362 echo '<li><code>%%payer_email%%</code> = The Email Address of the Customer who purchased the Membership Subscription.</li>'."\n";
1363 echo '<li><code>%%item_number%%</code> = The Item Number (colon separated <code><em>level:custom_capabilities:fixed term</em></code>) that the Subscription is for.</li>'."\n";
1364 echo '<li><code>%%item_name%%</code> = The Item Name (as provided by the <code>desc=""</code> attribute in your Shortcode, which briefly describes the Item Number).</li>'."\n";
1365 echo '<li><code>%%initial_term%%</code> = This is the term length of the Initial Period. This will be a numeric value, followed by a space, then a single letter. [ <a href="#" onclick="alert(\'Here are some examples:\\n\\n%%initial_term%% = 1 D (this means 1 Day)\\n%%initial_term%% = 1 W (this means 1 Week)\\n%%initial_term%% = 1 M (this means 1 Month)\\n%%initial_term%% = 1 Y (this means 1 Year)\\n\\nThe Initial Period never recurs, so this only lasts for the term length specified, then it is over.\'); return false;">?</a> ]</li>'."\n";
1366 echo '<li><code>%%initial_cycle%%</code> = This is the <code>%%initial_term%%</code> from above, converted to a cycle representation of: <code><em>X days/weeks/months/years</em></code>.</li>'."\n";
1367 echo '<li><code>%%regular_term%%</code> = This is the term length of the Regular Period. This will be a numeric value, followed by a space, then a single letter. [ <a href="#" onclick="alert(\'Here are some examples:\\n\\n%%regular_term%% = 1 D (this means 1 Day)\\n%%regular_term%% = 1 W (this means 1 Week)\\n%%regular_term%% = 1 M (this means 1 Month)\\n%%regular_term%% = 1 Y (this means 1 Year)\\n%%regular_term%% = 1 L (this means 1 Lifetime)\\n\\nThe Regular Term is usually recurring. So the Regular Term value represents the period (or duration) of each recurring period. If %%recurring%% = 0, then the Regular Term only applies once, because it is not recurring. So if it is not recurring, the value of %%regular_term%% simply represents how long their Membership privileges are going to last after the %%initial_term%% has expired, if there was an Initial Term. The value of this variable ( %%regular_term%% ) will never be empty, it will always be at least: 1 D, meaning 1 day. No exceptions.\'); return false;">?</a> ]</li>'."\n";
1368 echo '<li><code>%%regular_cycle%%</code> = This is the <code>%%regular_term%%</code> from above, converted to a cycle representation of: <code><em>[every] X days/weeks/months/years—OR daily, weekly, bi-weekly, monthly, bi-monthly, quarterly, yearly, or lifetime</em></code>. This is a very useful Replacment Code. Its value is dynamic; depending on term length, recurring status, and period/term lengths configured.</li>'."\n";
1369 echo '<li><code>%%recurring/regular_cycle%%</code> = Example (<code>14.95 / Monthly</code>), or ... (<code>0 / non-recurring</code>); depending on the value of <code>%%recurring%%</code>.</li>'."\n";
1370 echo '<li><code>%%user_first_name%%</code> = The First Name listed on their User account. This might be different than what is on file with PayPal.</li>'."\n";
1371 echo '<li><code>%%user_last_name%%</code> = The Last Name listed on their User account. This might be different than what is on file with PayPal.</li>'."\n";
1372 echo '<li><code>%%user_full_name%%</code> = The Full Name listed on their User account. This might be different than what is on file with PayPal.</li>'."\n";
1373 echo '<li><code>%%user_email%%</code> = The Email Address associated with their User account. This might be different than what is on file with PayPal.</li>'."\n";
1374 echo '<li><code>%%user_login%%</code> = The Username associated with their account. The Customer created this during registration.</li>'."\n";
1375 echo '<li><code>%%user_ip%%</code> = The Customer\'s original IP Address, during checkout/registration via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
1376 echo '<li><code>%%user_id%%</code> = A unique WordPress User ID that references this account in the WordPress database.</li>'."\n";
1377 echo '</ul>'."\n";
1378
1379 if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
1380 {
1381 echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):</strong>'."\n";
1382 echo '<ul class="ws-menu-page-li-margins">'."\n";
1383 echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).</li>'."\n";
1384 echo '<li><code>%%coupon_code%%</code> = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
1385 echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e., the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
1386 echo '</ul>'."\n";
1387 }
1388 echo '<strong>Custom Registration/Profile Fields are also supported in this email:</strong>'."\n";
1389 echo '<ul class="ws-menu-page-li-margins">'."\n";
1390 echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
1391 echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
1392 echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
1393 echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member → General Options → Registration/Profile Fields</em>.</li>'."\n";
1394 echo '</ul>'."\n";
1395
1396 echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
1397 echo '<ul class="ws-menu-page-li-margins">'."\n";
1398 echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
1399 echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
1400 echo '</ul>'."\n";
1401 echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
1402 echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
1403 echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
1404
1405 echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
1406 '<div class="ws-menu-page-hr"></div>'."\n".
1407 '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags—optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$paypal</code> variable is the most important one. It contains all of the <code>$_POST</code> variables received from PayPal\'s IPN service—or from an s2Member Pro-Form integration (e.g., <code>$paypal["item_number"]</code>, <code>$paypal["item_name"]</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
1408 : '';
1409 echo '</td>'."\n";
1410
1411 echo '</tr>'."\n";
1412 echo '</tbody>'."\n";
1413 echo '</table>'."\n";
1414 echo '</div>'."\n";
1415
1416 echo '</div>'."\n";
1417
1418 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_modification_confirmation_email", get_defined_vars());
1419 }
1420
1421 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_ccap_confirmation_email", c_ws_plugin__s2member_utils_conds::pro_is_installed(), get_defined_vars()))
1422 {
1423 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_ccap_confirmation_email", get_defined_vars());
1424
1425 echo '<div class="ws-menu-page-group" title="Capability Confirmation Email '.((c_ws_plugin__s2member_utils_conds::pro_is_installed()) ? '(Standard/Pro-Form)' : '(Standard)').'">'."\n";
1426
1427 echo '<div class="ws-menu-page-section ws-plugin--s2member-ccap-confirmation-email-section">'."\n";
1428 echo '<h3>Capability Confirmation Email (required, but the default works fine)</h3>'."\n";
1429 echo '<p>This email is sent to existing Users after they complete a Buy Now purchase for one or more Custom Capabilities (if and when you make this possible); see: <strong>Dashboard → s2Member → PayPal Buttons/Forms → Capability (Buy Now)</strong>. The <strong>primary</strong> purpose of this email is to provide the Customer with a confirmation that their account was updated. You may also customize this further by providing details that are specifically geared to your site.</p>'."\n";
1430 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_ccap_confirmation_email", get_defined_vars());
1431
1432 echo '<table class="form-table">'."\n";
1433 echo '<tbody>'."\n";
1434 echo '<tr>'."\n";
1435
1436 echo '<th>'."\n";
1437 echo '<label for="ws-plugin--s2member-ccap-email-recipients">'."\n";
1438 echo 'Capability Confirmation Recipients:'."\n";
1439 echo '</label>'."\n";
1440 echo '</th>'."\n";
1441
1442 echo '</tr>'."\n";
1443 echo '<tr>'."\n";
1444
1445 echo '<td>'."\n";
1446 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_ccap_email_recipients" id="ws-plugin--s2member-ccap-email-recipients" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["ccap_email_recipients"]).'" /><br />'."\n";
1447 echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:<br />'."\n";
1448 echo '<code>"%%full_name%%" &lt;%%payer_email%%&gt;; admin@example.com; "Webmaster" &lt;webmaster@example.com&gt;</code>'."\n";
1449 echo '</td>'."\n";
1450
1451 echo '</tr>'."\n";
1452 echo '<tr>'."\n";
1453
1454 echo '<th>'."\n";
1455 echo '<label for="ws-plugin--s2member-ccap-email-subject">'."\n";
1456 echo 'Capability Confirmation Email Subject:'."\n";
1457 echo '</label>'."\n";
1458 echo '</th>'."\n";
1459
1460 echo '</tr>'."\n";
1461 echo '<tr>'."\n";
1462
1463 echo '<td>'."\n";
1464 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_ccap_email_subject" id="ws-plugin--s2member-ccap-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["ccap_email_subject"]).'" /><br />'."\n";
1465 echo 'Subject Line used in the email sent to a Customer after a purchase is completed through PayPal.'."\n";
1466 echo '</td>'."\n";
1467
1468 echo '</tr>'."\n";
1469 echo '<tr>'."\n";
1470
1471 echo '<th>'."\n";
1472 echo '<label for="ws-plugin--s2member-ccap-email-message">'."\n";
1473 echo 'Capability Confirmation Email Message:'."\n";
1474 echo '</label>'."\n";
1475 echo '</th>'."\n";
1476
1477 echo '</tr>'."\n";
1478 echo '<tr>'."\n";
1479
1480 echo '<td>'."\n";
1481 //250615 Visual editor for HTML emails.
1482 if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) {
1483 echo '<textarea name="ws_plugin__s2member_ccap_email_message" id="ws-plugin--s2member-ccap-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["ccap_email_message"]).'</textarea><br />'."\n";
1484 } else {
1485 c_ws_plugin__s2member_utilities::editor('ccap_email_message');
1486 }
1487
1488 echo 'Message Body used in the email sent to a Customer after a purchase is completed through PayPal.<br /><br />'."\n";
1489 echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
1490 echo '<ul class="ws-menu-page-li-margins">'."\n";
1491 echo '<li><code>%%txn_id%%</code> = The PayPal Transaction ID. PayPal assigns a unique identifier for every purchase.</li>'."\n";
1492 echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
1493 echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
1494 echo '<li><code>%%amount%%</code> = The full Amount that you charged for Custom Capability access.</li>'."\n";
1495 echo '<li><code>%%first_name%%</code> = The First Name of the Customer who completed the purchase.</li>'."\n";
1496 echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who completed the purchase.</li>'."\n";
1497 echo '<li><code>%%full_name%%</code> = The Full Name (First &amp; Last) of the Customer who completed the purchase.</li>'."\n";
1498 echo '<li><code>%%payer_email%%</code> = The Email Address of the Customer who completed the purchase.</li>'."\n";
1499 echo '<li><code>%%item_number%%</code> = The Item Number (colon separated <code><em>*:custom_capabilities</em></code>); where <code>custom_capabilities</code> is a comma-delimited list of the Custom Capabilities they purchased.</li>'."\n";
1500 echo '<li><code>%%item_name%%</code> = The Item Name (as provided by the <code>desc=""</code> attribute in your Shortcode, which briefly describes the Item Number).</li>'."\n";
1501 echo '<li><code>%%user_first_name%%</code> = The First Name listed on their User account. This might be different than what is on file with PayPal.</li>'."\n";
1502 echo '<li><code>%%user_last_name%%</code> = The Last Name listed on their User account. This might be different than what is on file with PayPal.</li>'."\n";
1503 echo '<li><code>%%user_full_name%%</code> = The Full Name listed on their User account. This might be different than what is on file with PayPal.</li>'."\n";
1504 echo '<li><code>%%user_email%%</code> = The Email Address associated with their User account. This might be different than what is on file with PayPal.</li>'."\n";
1505 echo '<li><code>%%user_login%%</code> = The Username associated with their account. The Customer created this during registration.</li>'."\n";
1506 echo '<li><code>%%user_ip%%</code> = The Customer\'s original IP Address, during checkout/registration via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
1507 echo '<li><code>%%user_id%%</code> = A unique WordPress User ID that references this account in the WordPress database.</li>'."\n";
1508 echo '</ul>'."\n";
1509
1510 if(c_ws_plugin__s2member_utils_conds::pro_is_installed())
1511 {
1512 echo '<strong>Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):</strong>'."\n";
1513 echo '<ul class="ws-menu-page-li-margins">'."\n";
1514 echo '<li><code>%%full_coupon_code%%</code> = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).</li>'."\n";
1515 echo '<li><code>%%coupon_code%%</code> = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).</li>'."\n";
1516 echo '<li><code>%%coupon_affiliate_id%%</code> = This is the end of an Affiliate Coupon Code <em>(i.e., the referring affiliate\'s ID)</em>. This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.</li>'."\n";
1517 echo '</ul>'."\n";
1518 }
1519 echo '<strong>Custom Registration/Profile Fields are also supported in this email:</strong>'."\n";
1520 echo '<ul class="ws-menu-page-li-margins">'."\n";
1521 echo '<li><code>%%date_of_birth%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>date_of_birth</code>.</li>'."\n";
1522 echo '<li><code>%%street_address%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>street_address</code>.</li>'."\n";
1523 echo '<li><code>%%country%%</code> would be valid; if you have a Custom Registration/Profile Field with the ID <code>country</code>.</li>'."\n";
1524 echo '<li><em><code>%%etc, etc...%%</code> <strong>see:</strong> s2Member → General Options → Registration/Profile Fields</em>.</li>'."\n";
1525 echo '</ul>'."\n";
1526
1527 echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
1528 echo '<ul class="ws-menu-page-li-margins">'."\n";
1529 echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
1530 echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
1531 echo '</ul>'."\n";
1532 echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
1533 echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
1534 echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
1535
1536 echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
1537 '<div class="ws-menu-page-hr"></div>'."\n".
1538 '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags—optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$paypal</code> variable is the most important one. It contains all of the <code>$_POST</code> variables received from PayPal\'s IPN service—or from an s2Member Pro-Form integration (e.g., <code>$paypal["item_number"]</code>, <code>$paypal["item_name"]</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
1539 : '';
1540 echo '</td>'."\n";
1541
1542 echo '</tr>'."\n";
1543 echo '</tbody>'."\n";
1544 echo '</table>'."\n";
1545 echo '</div>'."\n";
1546
1547 echo '</div>'."\n";
1548
1549 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_ccap_confirmation_email", get_defined_vars());
1550 }
1551
1552 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_sp_confirmation_email", TRUE, get_defined_vars()))
1553 {
1554 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_sp_confirmation_email", get_defined_vars());
1555
1556 echo '<div class="ws-menu-page-group" title="Specific Post/Page Confirmation Email (Standard)">'."\n";
1557
1558 echo '<div class="ws-menu-page-section ws-plugin--s2member-sp-confirmation-email-section">'."\n";
1559 echo '<h3>Specific Post/Page Confirmation Email (required, but the default works fine)</h3>'."\n";
1560 echo '<p>This email is sent to new Customers after they return from a successful purchase at PayPal, for Specific Post/Page Access. (see: <strong>s2Member → Restriction Options → Specific Post/Page Access</strong>). This is NOT used for Membership sales, only for Specific Post/Page Access. The <strong>primary</strong> purpose of this email is to provide the Customer with instructions, along with a link to access the Specific Post/Page they\'ve purchased access to. If you\'ve created a Specific Post/Page Package (with multiple Posts/Pages bundled together into one transaction), this ONE link (<code>%%sp_access_url%%</code>) will automatically authenticate them for access to ALL of the Posts/Pages included in their transaction. You may customize this email further by providing details that are specifically geared to your site.</p>'."\n";
1561 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_sp_confirmation_email", get_defined_vars());
1562
1563 echo '<table class="form-table">'."\n";
1564 echo '<tbody>'."\n";
1565 echo '<tr>'."\n";
1566
1567 echo '<th>'."\n";
1568 echo '<label for="ws-plugin--s2member-sp-email-recipients">'."\n";
1569 echo 'Specific Post/Page Confirmation Recipients:'."\n";
1570 echo '</label>'."\n";
1571 echo '</th>'."\n";
1572
1573 echo '</tr>'."\n";
1574 echo '<tr>'."\n";
1575
1576 echo '<td>'."\n";
1577 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_sp_email_recipients" id="ws-plugin--s2member-sp-email-recipients" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_email_recipients"]).'" /><br />'."\n";
1578 echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:<br />'."\n";
1579 echo '<code>"%%full_name%%" &lt;%%payer_email%%&gt;; admin@example.com; "Webmaster" &lt;webmaster@example.com&gt;</code>'."\n";
1580 echo '</td>'."\n";
1581
1582 echo '</tr>'."\n";
1583 echo '<tr>'."\n";
1584
1585 echo '<th>'."\n";
1586 echo '<label for="ws-plugin--s2member-sp-email-subject">'."\n";
1587 echo 'Specific Post/Page Confirmation Email Subject:'."\n";
1588 echo '</label>'."\n";
1589 echo '</th>'."\n";
1590
1591 echo '</tr>'."\n";
1592 echo '<tr>'."\n";
1593
1594 echo '<td>'."\n";
1595 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_sp_email_subject" id="ws-plugin--s2member-sp-email-subject" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_email_subject"]).'" /><br />'."\n";
1596 echo 'Subject Line used in the email sent to a Customer after a successful purchase has occurred through PayPal, for Specific Post/Page Access.'."\n";
1597 echo '</td>'."\n";
1598
1599 echo '</tr>'."\n";
1600 echo '<tr>'."\n";
1601
1602 echo '<th>'."\n";
1603 echo '<label for="ws-plugin--s2member-sp-email-message">'."\n";
1604 echo 'Specific Post/Page Confirmation Email Message:'."\n";
1605 echo '</label>'."\n";
1606 echo '</th>'."\n";
1607
1608 echo '</tr>'."\n";
1609 echo '<tr>'."\n";
1610
1611 echo '<td>'."\n";
1612 //250615 Visual editor for HTML emails.
1613 if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) {
1614 echo '<textarea name="ws_plugin__s2member_sp_email_message" id="ws-plugin--s2member-sp-email-message" rows="10">'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["sp_email_message"]).'</textarea><br />'."\n";
1615 } else {
1616 c_ws_plugin__s2member_utilities::editor('sp_email_message');
1617 }
1618
1619 echo 'Message Body used in the email sent to a Customer after a successful purchase has occurred through PayPal, for Specific Post/Page Access.<br /><br />'."\n";
1620 echo '<strong>You can also use these special Replacement Codes if you need them:</strong>'."\n";
1621 echo '<ul class="ws-menu-page-li-margins">'."\n";
1622 echo '<li><code>%%sp_access_url%%</code> = The full URL (generated by s2Member) where the Customer can gain access.</li>'."\n";
1623 echo '<li><code>%%sp_access_exp%%</code> = Human readable expiration for <code>%%sp_access_url%%</code>. Ex: <em>(link expires in <code>%%sp_access_exp%%</code>)</em>.</li>'."\n";
1624 echo '<li><code>%%txn_id%%</code> = The PayPal Transaction ID. PayPal assigns a unique identifier for every purchase.</li>'."\n";
1625 echo '<li><code>%%currency%%</code> = Three-character currency code (uppercase); e.g., <code>USD</code></li>'."\n";
1626 echo '<li><code>%%currency_symbol%%</code> = Currency code symbol; e.g., <code>$</code></li>'."\n";
1627 echo '<li><code>%%amount%%</code> = The full Amount that you charged for Specific Post/Page Access.</li>'."\n";
1628 echo '<li><code>%%first_name%%</code> = The First Name of the Customer who purchased Specific Post/Page Access.</li>'."\n";
1629 echo '<li><code>%%last_name%%</code> = The Last Name of the Customer who purchased Specific Post/Page Access.</li>'."\n";
1630 echo '<li><code>%%full_name%%</code> = The Full Name (First &amp; Last) of the Customer who purchased Specific Post/Page Access.</li>'."\n";
1631 echo '<li><code>%%payer_email%%</code> = The Email Address of the Customer who purchased Specific Post/Page Access.</li>'."\n";
1632 echo '<li><code>%%user_ip%%</code> = The Customer\'s IP Address, detected during checkout via <code>$_SERVER["REMOTE_ADDR"]</code>.</li>'."\n";
1633 echo '<li><code>%%item_number%%</code> = The Item Number. Ex: <code><em>sp:13,24,36:72</em></code> (translates to: <code><em>sp:comma-delimited IDs:expiration hours</em></code>).</li>'."\n";
1634 echo '<li><code>%%item_name%%</code> = The Item Name (as provided by the <code>desc=""</code> attribute in your Shortcode, which briefly describes the Item Number).</li>'."\n";
1635 echo '</ul>'."\n";
1636
1637 echo '<strong>Custom Replacement Codes can also be inserted using these instructions:</strong>'."\n";
1638 echo '<ul class="ws-menu-page-li-margins">'."\n";
1639 echo '<li><code>%%cv0%%</code> = The domain of your site, which is passed through the `custom` attribute in your Shortcode.</li>'."\n";
1640 echo '<li><code>%%cv1%%</code> = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: <code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3"</code>. You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.</li>'."\n";
1641 echo '</ul>'."\n";
1642 echo '<strong>This example uses cv1 to record a special marketing campaign:</strong><br />'."\n";
1643 echo '<em>(The campaign (i.e., christmas-promo) could be referenced using <code>%%cv1%%</code>)</em><br />'."\n";
1644 echo '<code>custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"</code>'."\n";
1645
1646 echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ?
1647 '<div class="ws-menu-page-hr"></div>'."\n".
1648 '<p style="margin:0;"><strong>PHP Code:</strong> It is also possible to use PHP tags—optional (for developers). If you use PHP tags, please run a test email with <code>&lt;?php print_r(get_defined_vars()); ?&gt;</code>. This will give you a full list of all PHP variables available to you in this email. The <code>$paypal</code> variable is the most important one. It contains all of the <code>$_POST</code> variables received from PayPal\'s IPN service—or from an s2Member Pro-Form integration (e.g., <code>$paypal["item_number"]</code>, <code>$paypal["item_name"]</code>, etc). Please note that all Replacement Codes will be parsed first, and then any PHP tags that you\'ve included. Also, please remember that emails are sent in plain text format.</p>'."\n"
1649 : '';
1650 echo '</td>'."\n";
1651
1652 echo '</tr>'."\n";
1653 echo '</tbody>'."\n";
1654 echo '</table>'."\n";
1655 echo '</div>'."\n";
1656
1657 echo '</div>'."\n";
1658
1659 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_sp_confirmation_email", get_defined_vars());
1660 }
1661
1662 if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_eot_behavior", TRUE, get_defined_vars()))
1663 {
1664 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_eot_behavior", get_defined_vars());
1665
1666 echo '<div class="ws-menu-page-group" title="Automatic EOT Behavior">'."\n";
1667
1668 echo '<div class="ws-menu-page-section ws-plugin--s2member-eot-behavior-section">'."\n";
1669 echo '<h3>PayPal EOT Behavior (required, please choose)</h3>'."\n";
1670 echo '<p>EOT = End Of Term. By default, s2Member will demote a paid Member to a Free Subscriber whenever their Subscription term has ended (i.e., expired), been cancelled, refunded, charged back to you, etc. s2Member demotes them to a Free Subscriber, so they will no longer have Member Level Access to your site. However, in some cases, you may prefer to have Customer accounts deleted completely, instead of just being demoted. This is where you choose which method works best for your site. If you don\'t want s2Member to take ANY action at all, you can disable s2Member\'s EOT System temporarily, or even completely. There are also a few other configurable options here, so please read carefully. These options are all very important.</p>'."\n";
1671 echo '<p><strong>PayPal IPNs:</strong> The PayPal IPN service will notify s2Member whenever a Member\'s payments have been failing, and/or whenever a Member\'s Subscription has expired for any reason. Even refunds &amp; chargeback reversals are supported through the IPN service. For example, if you issue a refund to an unhappy Customer through PayPal, s2Member will eventually be notified, and the account for that Customer will either be demoted to a Free Subscriber, or deleted automatically (based on your configuration). The communication from PayPal → s2Member is seamless.</p>'."\n";
1672 echo '<p><em><strong>Some Hairy Details:</strong> There might be times whenever you notice that a Member\'s Subscription has been cancelled through PayPal... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only remove the User\'s Membership privileges when an EOT (End Of Term) is processed, a refund occurs, a chargeback occurs, or when a cancellation occurs - which would later result in a delayed Auto-EOT by s2Member.</em></p>'."\n";
1673 echo '<p><em>s2Member will not process an EOT until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will remove their Membership privileges; by either demoting them to a Free Subscriber, or deleting their account from the system (based on your configuration). s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.</em></p>'."\n";
1674 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_eot_behavior", get_defined_vars());
1675
1676 echo '<p id="ws-plugin--s2member-auto-eot-system-enabled-via-cron"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] == 2 && (!function_exists("wp_cron") || !wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule"))) ? '' : ' style="display:none;"').'>If you\'d like to run s2Member\'s Auto-EOT System through a more traditional Cron Job; instead of through <code>WP-Cron</code>, you will need to configure a Cron Job through your server control panel; provided by your hosting company. Set the Cron Job to run <code>once about every 10 minutes to an hour</code>. You\'ll want to configure an HTTP Cron Job that loads this URL:<br /><code>'.esc_html(home_url("/?s2member_auto_eot_system_via_cron=1")).'</code></p>'."\n";
1677
1678 echo '<table class="form-table">'."\n";
1679 echo '<tbody>'."\n";
1680 echo '<tr>'."\n";
1681
1682 echo '<th>'."\n";
1683 echo '<label for="ws-plugin--s2member-auto-eot-system-enabled">'."\n";
1684 echo 'Enable s2Member\'s Auto-EOT System?'."\n";
1685 echo '</label>'."\n";
1686 echo '</th>'."\n";
1687
1688 echo '</tr>'."\n";
1689 echo '<tr>'."\n";
1690
1691 echo '<td>'."\n";
1692 echo '<select name="ws_plugin__s2member_auto_eot_system_enabled" id="ws-plugin--s2member-auto-eot-system-enabled">'."\n";
1693 // Very advanced conditionals here. If the Auto-EOT System is NOT running, or NOT fully configured, this will indicate that no option is set - as sort of a built-in acknowledgment/warning in the UI panel.
1694 echo (($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] == 1 && (!function_exists("wp_cron") || !wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule"))) || ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] == 2 && (function_exists("wp_cron") && wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule"))) || (!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] && (function_exists("wp_cron") && wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule")))) ? '<option value=""></option>'."\n" : '';
1695 echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] == 1 && function_exists("wp_cron") && wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule")) ? ' selected="selected"' : '').'>Yes (enable the Auto-EOT System through WP-Cron)</option>'."\n";
1696 echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '<option value="2"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] == 2 && (!function_exists("wp_cron") || !wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule"))) ? ' selected="selected"' : '').'>Yes (but, I\'ll run it with my own Cron Job)</option>'."\n" : '';
1697 echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"] && (!function_exists("wp_cron") || !wp_get_schedule("ws_plugin__s2member_auto_eot_system__schedule"))) ? ' selected="selected"' : '').'>No (disable the Auto-EOT System)</option>'."\n";
1698 echo '</select><br />'."\n";
1699 echo 'Recommended setting: (<code>Yes / enable via WP-Cron</code>)'."\n";
1700 echo '</td>'."\n";
1701
1702 echo '</tr>'."\n";
1703 echo '</tbody>'."\n";
1704 echo '</table>'."\n";
1705
1706 echo '<div class="ws-menu-page-hr"></div>'."\n";
1707
1708 echo '<table class="form-table">'."\n";
1709 echo '<tbody>'."\n";
1710 echo '<tr>'."\n";
1711
1712 echo '<th>'."\n";
1713 echo '<label for="ws-plugin--s2member-membership-eot-behavior">'."\n";
1714 echo 'Membership EOT Behavior (Demote or Delete)?'."\n";
1715 echo '</label>'."\n";
1716 echo '</th>'."\n";
1717
1718 echo '</tr>'."\n";
1719 echo '<tr>'."\n";
1720
1721 echo '<td>'."\n";
1722 echo '<select name="ws_plugin__s2member_membership_eot_behavior" id="ws-plugin--s2member-membership-eot-behavior">'."\n";
1723 echo '<option value="demote"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_eot_behavior"] === "demote") ? ' selected="selected"' : '').'>Demote (convert them to a Free Subscriber)</option>'."\n";
1724 echo '<option value="delete"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_eot_behavior"] === "delete") ? ' selected="selected"' : '').'>Delete (erase their account completely)</option>'."\n";
1725 echo '</select>'."\n";
1726 echo '</td>'."\n";
1727
1728 echo '</tr>'."\n";
1729 echo '<tr>'."\n";
1730
1731 echo '<th>'."\n";
1732 echo '<label for="ws-plugin--s2member-eots-remove-ccaps">'."\n";
1733 echo 'Membership EOTs also Remove all Custom Capabilities?'."\n";
1734 echo '</label>'."\n";
1735 echo '</th>'."\n";
1736
1737 echo '</tr>'."\n";
1738 echo '<tr>'."\n";
1739
1740 echo '<td>'."\n";
1741 echo '<select name="ws_plugin__s2member_eots_remove_ccaps" id="ws-plugin--s2member-eots-remove-ccaps">'."\n";
1742 echo '<option value="1"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eots_remove_ccaps"]) ? ' selected="selected"' : '').'>Yes (an EOT also results in the loss of any Custom Capabilities a User/Member may have)</option>'."\n";
1743 echo '<option value="0"'.((!$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eots_remove_ccaps"]) ? ' selected="selected"' : '').'>No (an EOT has no impact on any Custom Capabilities a User/Member may have)</option>'."\n";
1744 echo '</select><br />'."\n";
1745 echo '<em>NOTE: If Refunds/Reversals trigger an Immediate EOT (see setting below); Custom Capabilities will always be removed when/if a Refund or Reversal occurs. In other words, this setting is ignored for Refunds/Reversals (IF they trigger an Immediate EOT—based on your configuration below). If you prefer to review all Refunds/Reversals for yourself, please choose that option below.</em>'."\n";
1746 echo '</td>'."\n";
1747
1748 echo '</tr>'."\n";
1749 echo '</tbody>'."\n";
1750 echo '</table>'."\n";
1751
1752 echo '<div class="ws-menu-page-hr"></div>'."\n";
1753
1754 echo '<table class="form-table">'."\n";
1755 echo '<tbody>'."\n";
1756 echo '<tr>'."\n";
1757
1758 echo '<th>'."\n";
1759 echo '<label for="ws-plugin--s2member-eot-grace-time">'."\n";
1760 echo 'EOT Grace Time (in seconds):'."\n";
1761 echo '</label>'."\n";
1762 echo '</th>'."\n";
1763
1764 echo '</tr>'."\n";
1765 echo '<tr>'."\n";
1766
1767 echo '<td>'."\n";
1768 echo '<input type="text" autocomplete="off" name="ws_plugin__s2member_eot_grace_time" id="ws-plugin--s2member-eot-grace-time" value="'.format_to_edit($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eot_grace_time"]).'" /><br />'."\n";
1769 echo '<em>This is represented in seconds. For example, a value of: <code>86400</code> = 1 day. Your EOT Grace Time; is the amount of time you will offer as a grace period (if any). Most site owners will give customers an additional 24 hours of access; just to help avoid any negativity that may result from a customer losing access sooner than they might expect. You can disable EOT Grace Time by setting this to: <code>0</code>. Note: there is NO Grace Time applied when/if a Refund or Reversal occurs. If Refunds/Reversals trigger an Immediate EOT (see setting below); there is never any Grace Time applied in that scenario.</em>'."\n";
1770 echo '</td>'."\n";
1771
1772 echo '</tr>'."\n";
1773 echo '</tbody>'."\n";
1774 echo '</table>'."\n";
1775
1776 echo '<div class="ws-menu-page-hr"></div>'."\n";
1777
1778 echo '<table class="form-table">'."\n";
1779 echo '<tbody>'."\n";
1780 echo '<tr>'."\n";
1781
1782 echo '<th>'."\n";
1783 echo '<label for="ws-plugin--s2member-triggers-immediate-eot">'."\n";
1784 echo 'Refunds/Partial Refunds/Reversals (trigger Immediate EOT)?'."\n";
1785 echo '</label>'."\n";
1786 echo '</th>'."\n";
1787
1788 echo '</tr>'."\n";
1789 echo '<tr>'."\n";
1790
1791 echo '<td>'."\n";
1792 echo '<select name="ws_plugin__s2member_triggers_immediate_eot" id="ws-plugin--s2member-triggers-immediate-eot">'."\n";
1793 echo '<option value="none"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["triggers_immediate_eot"] === "none") ? ' selected="selected"' : '').'>None (I\'ll review these events manually)</option>'."\n";
1794 echo '<option value="refunds"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["triggers_immediate_eot"] === "refunds") ? ' selected="selected"' : '').'>Full Refunds (full refunds only; ALWAYS trigger an Immediate EOT action)</option>'."\n";
1795 echo '<option value="reversals"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["triggers_immediate_eot"] === "reversals") ? ' selected="selected"' : '').'>Reversals (chargebacks only; ALWAYS trigger an Immediate EOT action)</option>'."\n";
1796 echo '<option value="refunds,reversals"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["triggers_immediate_eot"] === "refunds,reversals") ? ' selected="selected"' : '').'>Full Refunds, Reversals (these ALWAYS trigger an Immediate EOT action)</option>'."\n";
1797 echo '<option value="refunds,partial_refunds,reversals"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["triggers_immediate_eot"] === "refunds,partial_refunds,reversals") ? ' selected="selected"' : '').'>Full Refunds, Partial Refunds, Reversals (these ALWAYS trigger an Immediate EOT action)</option>'."\n";
1798 echo '</select><br />'."\n";
1799 echo '<em><strong>Note:</strong> s2Member is not equipped to detect partial refunds against multi-payment Subscriptions reliably. Therefore, all refunds processed against Subscriptions (of any kind) are considered <strong>Partial</strong> Refunds. Full refunds (in the eyes of s2Member) occur only against Buy Now transactions where it is easy for s2Member to see that the refund amount is &gt;= the original Buy Now purchase price (i.e., a Full Refund). <strong>Also Note:</strong> This setting (no matter what you choose) will NOT impact s2Member\'s internal API Notifications for Refund/Reversal events. <a href="#" onclick="alert(\'A Full or Partial Refund; and/or a Reversal Notification will ALWAYS be processed internally by s2Member, even if no action is taken by s2Member in accordance with your configuration here.\\n\\nIn this way, you\\\'ll have the full ability to listen for these events on your own (via API Notifications); if you prefer (optional). For more information, check your Dashboard under: `s2Member → API Notifications → Refunds/Reversals`.\'); return false;">Click here for details</a>.</em>'."\n";
1800 echo '</td>'."\n";
1801
1802 echo '</tr>'."\n";
1803 echo '</tbody>'."\n";
1804 echo '</table>'."\n";
1805
1806 echo '<div class="ws-menu-page-hr"></div>'."\n";
1807
1808 echo '<table class="form-table">'."\n";
1809 echo '<tbody>'."\n";
1810 echo '<tr>'."\n";
1811
1812 echo '<th>'."\n";
1813 echo '<label for="ws-plugin--s2member-eot-time-ext-behavior">'."\n";
1814 echo 'Fixed-Term Extensions (Auto-Extend)?'."\n";
1815 echo '</label>'."\n";
1816 echo '</th>'."\n";
1817
1818 echo '</tr>'."\n";
1819 echo '<tr>'."\n";
1820
1821 echo '<td>'."\n";
1822 echo '<select name="ws_plugin__s2member_eot_time_ext_behavior" id="ws-plugin--s2member-eot-time-ext-behavior">'."\n";
1823 echo '<option value="extend"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eot_time_ext_behavior"] === "extend") ? ' selected="selected"' : '').'>Yes (default, automatically extend any existing EOT Time)</option>'."\n";
1824 echo '<option value="reset"'.(($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["eot_time_ext_behavior"] === "reset") ? ' selected="selected"' : '').'>No (do NOT extend; s2Member should reset EOT Time completely)</option>'."\n";
1825 echo '</select><br />'."\n";
1826 echo '<em>This setting will only affect Buy Now transactions for fixed-term lengths. By default, s2Member will automatically extend any existing EOT Time that a Customer may have. For example, if I buy one year of access, and then I buy another year of access (before my first year is totally used up); I end up with everything I paid you for (now over 1 year of access) if this is set to <code>Yes</code>. If this was set to <code>No</code>, the EOT Time would be reset when I make the second purchase; leaving me with only 1 year of access, starting the date of my second purchase.</em>'."\n";
1827 echo '</td>'."\n";
1828
1829 echo '</tr>'."\n";
1830 echo '</tbody>'."\n";
1831 echo '</table>'."\n";
1832 echo '</div>'."\n";
1833
1834 echo '</div>'."\n";
1835
1836 do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_eot_behavior", get_defined_vars());
1837 }
1838
1839 do_action("ws_plugin__s2member_during_paypal_ops_page_after_left_sections", get_defined_vars());
1840
1841 echo '<p class="submit"><input type="submit" value="Save All Changes" /></p>'."\n";
1842
1843 echo '</form>'."\n";
1844
1845 echo '</td>'."\n";
1846
1847 echo '<td class="ws-menu-page-table-r">'."\n";
1848 c_ws_plugin__s2member_menu_pages_rs::display();
1849 echo '</td>'."\n";
1850
1851 echo '</tr>'."\n";
1852 echo '</tbody>'."\n";
1853 echo '</table>'."\n";
1854
1855 echo '</div>'."\n";
1856 }
1857 }
1858 }
1859
1860 new c_ws_plugin__s2member_menu_page_paypal_ops();
1861