CI / No Debug Code (pull_request) Successful in 5s
CI / Tests (PHP 8.2) (pull_request) Successful in 54s
CI / Tests (PHP 8.1) (pull_request) Successful in 56s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / PHPStan (pull_request) Successful in 3m48s
CI / Tests (PHP 8.3) (pull_request) Successful in 5m31s
CI / Build Plugin Zip (pull_request) Skipped
Stripe configuration was a one-way door. Keys could be entered but never removed, and entering them moved every student onto card billing at once, so there was no way to have Stripe live and satisfy yourself that card payments worked before committing the studio to them. Two settings-page changes open both directions: Default payment method (`us_default_payment_method`) is now an explicit choice between card and e-transfer for students with no per-student override, rather than something inferred from whether keys exist. Card remains the default, so a site that adds keys and changes nothing else behaves as before. BillingMethodResolver still degrades a card default to e-transfer while Stripe is unconfigured — there is nothing to charge a card with — and `comp` is deliberately not offerable studio-wide, since it would silently stop billing everybody; an unrecognised stored value reads back as card. Holding the default on e-transfer with Stripe live is the staged-rollout path: move individual students to card on their detail page, watch real charges land, then flip the studio over. Clear Stripe configuration deletes the publishable key, secret key and webhook signing secret and returns the mode to test, so a re-configuration later cannot inherit live. Currency, HST, e-transfer and registration settings are untouched, as are recorded payments. The button only appears when some Stripe value is stored, and reuses the page's existing nonce and `manage_billing` check. `composer test` (915), `composer lint` and `composer cs` all pass. Options only — no schema change, so no version bump. Closes #173 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WyktWmwNRgMYuwe5eBuPZm
176 lines
11 KiB
PHP
176 lines
11 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
if (! defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* @var string $publishableKey
|
|
* @var bool $secretKeySet
|
|
* @var bool $webhookSecretSet
|
|
* @var string $webhookUrl
|
|
* @var string $mode
|
|
* @var string $currency
|
|
* @var string $etransferEmail
|
|
* @var float $hstRate
|
|
* @var bool $stripeConfigured
|
|
* @var string $defaultMethod
|
|
* @var bool $stripeAnySet
|
|
* @var string $notice
|
|
* @var bool $openRegistration
|
|
* @var int $cancellationCutoffDays
|
|
*/
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php esc_html_e('Studio Settings', 'unsupervised-schedular'); ?></h1>
|
|
|
|
<?php if ('' !== $notice) : ?>
|
|
<div class="notice notice-success inline">
|
|
<p><?php echo esc_html($notice); ?></p>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="notice notice-info inline">
|
|
<p>
|
|
<?php if ($stripeConfigured && 'card' === $defaultMethod) : ?>
|
|
<?php esc_html_e('Stripe is configured — new registrations default to credit-card billing.', 'unsupervised-schedular'); ?>
|
|
<?php elseif ($stripeConfigured) : ?>
|
|
<?php esc_html_e('Stripe is configured, but the studio default is e-transfer — only students you switch to credit card individually are billed by card.', 'unsupervised-schedular'); ?>
|
|
<?php else : ?>
|
|
<?php esc_html_e('Stripe is not configured — new registrations default to e-transfer, which a studio admin marks paid on receipt. Add your Stripe keys below to enable card billing.', 'unsupervised-schedular'); ?>
|
|
<?php endif; ?>
|
|
</p>
|
|
</div>
|
|
|
|
<h2><?php esc_html_e('Stripe', 'unsupervised-schedular'); ?></h2>
|
|
<form method="post">
|
|
<?php wp_nonce_field('usc_settings_action'); ?>
|
|
<input type="hidden" name="usc_action" value="save">
|
|
<table class="form-table">
|
|
<tr>
|
|
<th><label for="publishable_key"><?php esc_html_e('Publishable key', 'unsupervised-schedular'); ?></label></th>
|
|
<td><input type="text" name="publishable_key" id="publishable_key" class="regular-text" value="<?php echo esc_attr($publishableKey); ?>" autocomplete="off"></td>
|
|
</tr>
|
|
<tr>
|
|
<th><label for="secret_key"><?php esc_html_e('Secret key', 'unsupervised-schedular'); ?></label></th>
|
|
<td>
|
|
<input type="password" name="secret_key" id="secret_key" class="regular-text" value="" autocomplete="off" placeholder="<?php echo esc_attr($secretKeySet ? esc_html__('Saved — leave blank to keep', 'unsupervised-schedular') : ''); ?>">
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><label for="webhook_secret"><?php esc_html_e('Webhook signing secret', 'unsupervised-schedular'); ?></label></th>
|
|
<td>
|
|
<input type="password" name="webhook_secret" id="webhook_secret" class="regular-text" value="" autocomplete="off" placeholder="<?php echo esc_attr($webhookSecretSet ? esc_html__('Saved — leave blank to keep', 'unsupervised-schedular') : ''); ?>">
|
|
<p class="description">
|
|
<?php esc_html_e('In the Stripe Dashboard, add a webhook endpoint for the payment_intent.succeeded and payment_intent.payment_failed events pointing at:', 'unsupervised-schedular'); ?><br>
|
|
<code><?php echo esc_html($webhookUrl); ?></code><br>
|
|
<?php esc_html_e('then paste its signing secret (whsec_…) here. Card payments are only marked paid once Stripe confirms them via this webhook.', 'unsupervised-schedular'); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><label for="mode"><?php esc_html_e('Mode', 'unsupervised-schedular'); ?></label></th>
|
|
<td>
|
|
<select name="mode" id="mode">
|
|
<option value="test" <?php selected($mode, 'test'); ?>><?php esc_html_e('Test', 'unsupervised-schedular'); ?></option>
|
|
<option value="live" <?php selected($mode, 'live'); ?>><?php esc_html_e('Live', 'unsupervised-schedular'); ?></option>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><label for="currency"><?php esc_html_e('Default currency', 'unsupervised-schedular'); ?></label></th>
|
|
<td><input type="text" name="currency" id="currency" class="small-text" maxlength="3" value="<?php echo esc_attr($currency); ?>"></td>
|
|
</tr>
|
|
<tr>
|
|
<th><label for="hst_rate"><?php esc_html_e('Default HST rate (%)', 'unsupervised-schedular'); ?></label></th>
|
|
<td>
|
|
<input type="number" name="hst_rate" id="hst_rate" class="small-text" min="0" max="100" step="0.01" value="<?php echo esc_attr(number_format($hstRate, 2)); ?>">
|
|
<p class="description"><?php esc_html_e('Added to the subtotal when billing. 0 means no tax. Overridable per booking on My Lessons.', 'unsupervised-schedular'); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2><?php esc_html_e('Billing', 'unsupervised-schedular'); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e('Default payment method', 'unsupervised-schedular'); ?></th>
|
|
<td>
|
|
<fieldset>
|
|
<label>
|
|
<input type="radio" name="default_payment_method" value="card" <?php checked($defaultMethod, 'card'); ?>>
|
|
<?php esc_html_e('Credit card (requires Stripe)', 'unsupervised-schedular'); ?>
|
|
</label><br>
|
|
<label>
|
|
<input type="radio" name="default_payment_method" value="etransfer" <?php checked($defaultMethod, 'etransfer'); ?>>
|
|
<?php esc_html_e('E-transfer', 'unsupervised-schedular'); ?>
|
|
</label>
|
|
<p class="description">
|
|
<?php esc_html_e('Applies to every student without their own billing method. Keep this on e-transfer while you trial card payments: switch individual students to Credit card under Students → student detail → Billing method, confirm their bookings charge correctly, then move the whole studio over by changing this setting.', 'unsupervised-schedular'); ?>
|
|
</p>
|
|
<?php if (! $stripeConfigured) : ?>
|
|
<p class="description"><?php esc_html_e('Credit card has no effect until Stripe keys are saved above — until then every student is billed by e-transfer.', 'unsupervised-schedular'); ?></p>
|
|
<?php endif; ?>
|
|
</fieldset>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2><?php esc_html_e('E-transfer', 'unsupervised-schedular'); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th><label for="etransfer_email"><?php esc_html_e('Default e-transfer email', 'unsupervised-schedular'); ?></label></th>
|
|
<td>
|
|
<input type="email" name="etransfer_email" id="etransfer_email" class="regular-text" value="<?php echo esc_attr($etransferEmail); ?>">
|
|
<p class="description"><?php esc_html_e('Where students send e-transfers, unless an offering overrides it. Instructors can also override per offering or per booking.', 'unsupervised-schedular'); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2><?php esc_html_e('Cancellations', 'unsupervised-schedular'); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th><label for="cancellation_cutoff_days"><?php esc_html_e('Cancellation cutoff (days)', 'unsupervised-schedular'); ?></label></th>
|
|
<td>
|
|
<input type="number" name="cancellation_cutoff_days" id="cancellation_cutoff_days" class="small-text" min="0" step="1" value="<?php echo esc_attr((string) $cancellationCutoffDays); ?>">
|
|
<p class="description"><?php esc_html_e('How far ahead of a lesson a student may still cancel it. Within this window only an instructor can cancel. 0 lets students cancel any time. An offering can override this with its own value.', 'unsupervised-schedular'); ?></p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2><?php esc_html_e('Registration', 'unsupervised-schedular'); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php esc_html_e('Student sign-up', 'unsupervised-schedular'); ?></th>
|
|
<td>
|
|
<fieldset>
|
|
<label>
|
|
<input type="checkbox" name="open_registration" value="1" <?php checked($openRegistration); ?>>
|
|
<?php esc_html_e('Allow anyone to sign up (email confirmation + admin approval)', 'unsupervised-schedular'); ?>
|
|
</label>
|
|
<p class="description">
|
|
<?php esc_html_e('Off by default — students join by invite only. When on, anyone can register on the student registration page; each new account must confirm its email and then be approved by a studio admin under Students → Pending Students before it can be used.', 'unsupervised-schedular'); ?>
|
|
</p>
|
|
<p class="description">
|
|
<?php esc_html_e('Turning this on also switches on Settings → General → “Anyone can register” and sets the default new-user role to Student. Turning it off restores those two settings to what they were before.', 'unsupervised-schedular'); ?>
|
|
</p>
|
|
</fieldset>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<?php submit_button(esc_html__('Save Settings', 'unsupervised-schedular')); ?>
|
|
</form>
|
|
|
|
<?php if ($stripeAnySet) : ?>
|
|
<h2><?php esc_html_e('Clear Stripe configuration', 'unsupervised-schedular'); ?></h2>
|
|
<p class="description">
|
|
<?php esc_html_e('Forgets the publishable key, secret key and webhook signing secret, and returns the mode to Test. Billing falls back to e-transfer until Stripe is set up again; payments already recorded are untouched.', 'unsupervised-schedular'); ?>
|
|
</p>
|
|
<form method="post" onsubmit="return confirm('<?php echo esc_js(esc_html__('Clear the stored Stripe keys? Card billing stops until you enter them again.', 'unsupervised-schedular')); ?>');">
|
|
<?php wp_nonce_field('usc_settings_action'); ?>
|
|
<input type="hidden" name="usc_action" value="clear_stripe">
|
|
<?php submit_button(esc_html__('Clear Stripe configuration', 'unsupervised-schedular'), 'delete', 'submit', true); ?>
|
|
</form>
|
|
<?php endif; ?>
|
|
</div>
|