Improve error message for people trying to log in to the Patrons Circle

This commit is contained in:
Alex Cabal 2024-12-13 10:10:07 -06:00
parent 95d1b9e02e
commit cf5f488cae
9 changed files with 104 additions and 11 deletions

View file

@ -0,0 +1,7 @@
<?
namespace Enums;
enum ExceptionMessageType{
case Html;
case Text;
}

View file

@ -2,4 +2,5 @@
namespace Exceptions;
class AppException extends \Exception{
public \Enums\ExceptionMessageType $MessageType = \Enums\ExceptionMessageType::Text;
}

View file

@ -4,6 +4,7 @@ use function Safe\preg_replace;
class Formatter{
private static Transliterator $_Transliterator;
private static Parsedown $_MarkdownParser;
private static NumberFormatter $_NumberFormatter;
/**
* Remove diacritics from a string, leaving the now-unaccented characters in place.
@ -122,4 +123,32 @@ class Formatter{
return $output;
}
/**
* Format a float into a USD currency string. The result is prepended with `$`.
*
* @param ?float $amount The amount to format.
* @param bool $trimZeroCents If `$amount` has zero cents, don't include the cents value.
*/
public static function FormatCurrency(?float $amount, bool $trimZeroCents = false): string{
if($amount === null){
$amount = 0;
}
if(!isset(Formatter::$_NumberFormatter)){
Formatter::$_NumberFormatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
}
$output = Formatter::$_NumberFormatter->formatCurrency($amount, 'USD');
if($output === false){
$output = '$0.00';
}
if($trimZeroCents){
$output = preg_replace('/\.00$/u', '', $output);
}
return $output;
}
}

View file

@ -1,4 +1,6 @@
<?
use Exceptions\InvalidLoginException;
use Ramsey\Uuid\Uuid;
use Safe\DateTimeImmutable;
@ -72,7 +74,21 @@ class Session{
self::SetSessionCookie($this->SessionId);
}
catch(Exceptions\UserNotFoundException){
throw new Exceptions\InvalidLoginException();
// We couldn't find a *registered * `User`. But, often people make a small donation assuming it automatically adds them to the Patrons Circle. So, check if they made a donation less than 7 days ago, and if so, notify them about the requirements to join the Patrons Circle.
$ex = new Exceptions\InvalidLoginException();
try{
$user = User::GetByIdentifier($identifier);
/** @throws void */
if($user->LastPayment !== null && $user->LastPayment->Created > new DateTimeImmutable('7 days ago')){
$ex = new InvalidLoginException('<p>We couldnt find you in the Patrons Circle, but you recently ' . ($user->LastPayment->IsRecurring ? 'started a recurring' : 'made a one-time') . ' donation of ' . Formatter::FormatCurrency($user->LastPayment->Amount) . '.</p><p>To join the Patrons Circle, supporters must <a href="/donate#patrons-circle">start a recurring donation</a> of ' . Formatter::FormatCurrency(PATRONS_CIRCLE_MONTHLY_COST, true) . '/month or more, or <a href="/donate#patrons-circle">make a one-time donation</a> of ' . Formatter::FormatCurrency(PATRONS_CIRCLE_YEARLY_COST, true) . ' or more to join for one year.</p><p>Once you join the Patrons Circle, youll be able to log in and access member benefits.</p>');
$ex->MessageType = Enums\ExceptionMessageType::Html;
}
}
catch(Exceptions\UserNotFoundException){
// Pass.
}
throw $ex;
}
}

View file

@ -11,6 +11,7 @@ use function Safe\preg_match;
* @property string $Url
* @property ?Patron $Patron
* @property ?NewsletterSubscription $NewsletterSubscription
* @property ?Payment $LastPayment
*/
class User{
use Traits\Accessor;
@ -27,6 +28,7 @@ class User{
protected bool $_IsRegistered;
/** @var array<Payment> $_Payments */
protected array $_Payments;
protected ?Payment $_LastPayment;
protected Benefits $_Benefits;
protected string $_Url;
protected ?Patron $_Patron;
@ -87,6 +89,20 @@ class User{
return $this->_Payments;
}
protected function GetLastPayment(): ?Payment{
if(!isset($this->_LastPayment)){
$this->_LastPayment = Db::Query('
SELECT *
from Payments
where UserId = ?
order by Created desc
limit 1
', [$this->UserId], Payment::class)[0] ?? null;
}
return $this->_LastPayment;
}
protected function GetBenefits(): Benefits{
if(!isset($this->_Benefits)){
if(isset($this->UserId)){

View file

@ -56,7 +56,7 @@ $digits = str_split(str_pad((string)$current, 3, "0", STR_PAD_LEFT))
</div>
<p>Our fiscal sponsor, <a href="https://www.fracturedatlas.org">Fractured Atlas</a>, is celebrating the twenty-year anniversary of their fiscal sponsorship program by <a href="https://media.fracturedatlas.org/what-would-you-do-with-an-extra-1000">distributing $1,000 to twenty different projects</a>.</p>
<p><strong>Each one-time donation of any amount to Standard Ebooks through <?= $deadline ?> gives us one entry in this $1,000 giveaway.</strong> The more donations we receive through <?= $deadline ?>, the more chances we have to win!</p>
<p><strong>This is a great time to <a href="/donate#patrons-circle">join our Patrons Circle</a> with a donation of $<?= number_format(PATRONS_CIRCLE_YEARLY_COST) ?>.</strong> Not only will your donation support us directly, but itll give us one more entry in this big giveaway.</p>
<p><strong>This is a great time to <a href="/donate#patrons-circle">join our Patrons Circle</a> with a donation of <?= Formatter::FormatCurrency(PATRONS_CIRCLE_YEARLY_COST, true) ?>.</strong> Not only will your donation support us directly, but itll give us one more entry in this big giveaway.</p>
<p>Will you show your support for free, beautiful digital literature?</p>
<? if($showDonateButton){ ?>
<p class="donate-button">

View file

@ -3,6 +3,8 @@
* @var ?Exception $exception
*/
use Exceptions\AppException;
if($exception === null){
return;
}
@ -18,8 +20,18 @@ else{
?>
<ul class="message error">
<? foreach($exceptions as $ex){ ?>
<?
$message = $ex->getMessage();
if($message == ''){
$message = 'An error occurred.';
}
if(!($ex instanceof AppException) || $ex->MessageType == Enums\ExceptionMessageType::Text){
$message = '<p>' . str_replace('CAPTCHA', '<abbr class="acronym">CAPTCHA</abbr>', Formatter::EscapeHtml($message)) . '</p>';
}
?>
<li>
<p><? $message = $ex->getMessage(); if($message == ''){ $message = 'An error occurred.'; } ?><?= str_replace('CAPTCHA', '<abbr class="acronym">CAPTCHA</abbr>', Formatter::EscapeHtml($message)) ?></p>
<?= $message ?>
</li>
<? } ?>
</ul>

View file

@ -34,10 +34,10 @@ $newsletterSubscriberCount = floor(Db::QueryInt('
<p>Membership in the Patrons Circle is limited to individuals only. Organizations, please see <a href="#corporate-sponsors">corporate sponsorship</a> instead.</p>
<div class="join-patrons-circle-callout">
<h3>Join now</h3>
<p><i>Join the Patrons Circle by starting a recurring donation of $<?= number_format(PATRONS_CIRCLE_MONTHLY_COST) ?>/month or more, or join for one year with a one-time donation of $<?= number_format(PATRONS_CIRCLE_YEARLY_COST) ?> or more.</i></p>
<p><i>Join the Patrons Circle by starting a recurring donation of <?= Formatter::FormatCurrency(PATRONS_CIRCLE_MONTHLY_COST, true) ?>/month or more, or join for one year with a one-time donation of <?= Formatter::FormatCurrency(PATRONS_CIRCLE_YEARLY_COST, true) ?> or more.</i></p>
<p class="button-row">
<a href="https://fundraising.fracturedatlas.org/standard-ebooks/monthly_support" class="button">Donate $<?= number_format(PATRONS_CIRCLE_MONTHLY_COST) ?>/month or more</a>
<a href="https://fundraising.fracturedatlas.org/standard-ebooks/general_support" class="button">Donate $<?= number_format(PATRONS_CIRCLE_YEARLY_COST) ?> or more</a>
<a href="https://fundraising.fracturedatlas.org/standard-ebooks/monthly_support" class="button">Donate <?= Formatter::FormatCurrency(PATRONS_CIRCLE_MONTHLY_COST, true) ?>/month or more</a>
<a href="https://fundraising.fracturedatlas.org/standard-ebooks/general_support" class="button">Donate <?= Formatter::FormatCurrency(PATRONS_CIRCLE_YEARLY_COST, true) ?> or more</a>
</p>
<p><strong>Important:</strong> We need to know your email address to be able to log you in to the Patrons Circle. Make sure to select either “List my name publicly” or “Dont list publicly, but reveal to project” during checkout to be able to log in to the Patrons Circle.</p>
</div>

View file

@ -87,11 +87,23 @@ catch(Exceptions\SeeOtherException $ex){
</tr>
<tr>
<td>Cycle type:</td>
<td><?= ucfirst($user->Patron->CycleType->value) ?></td>
<td>
<? if($user->Patron->CycleType !== null){ ?>
<?= ucfirst($user->Patron->CycleType->value) ?>
<? }else{ ?>
<i>Not set</i>
<? } ?>
</td>
</tr>
<tr>
<td>Base cost:</td>
<td>$<?= number_format($user->Patron->BaseCost) ?></td>
<td>
<? if($user->Patron->BaseCost !== null){ ?>
<?= Formatter::FormatCurrency($user->Patron->BaseCost) ?>
<? }else{ ?>
<i>Not set</i>
<? } ?>
</td>
</tr>
<tr>
<td>Is anonymous:</td>
@ -193,13 +205,13 @@ catch(Exceptions\SeeOtherException $ex){
<? } ?>
</td>
<td>
$<?= number_format($payment->Amount, 2) ?>
<?= Formatter::FormatCurrency($payment->Amount) ?>
</td>
<td>
$<?= number_format($payment->Fee, 2) ?>
<?= Formatter::FormatCurrency($payment->Fee) ?>
</td>
<td>
$<?= number_format($payment->Amount - $payment->Fee, 2) ?>
<?= Formatter::FormatCurrency($payment->Amount - $payment->Fee) ?>
</td>
<td>
<a href="<?= $payment->ProcessorUrl ?>"><?= Formatter::EscapeHtml($payment->TransactionId) ?></a>