mirror of
https://github.com/standardebooks/web.git
synced 2025-07-20 13:24:48 -04:00
Allow adjustment of Patrons Circle cost
This commit is contained in:
parent
a4d1e9d724
commit
9a2b095b70
8 changed files with 153 additions and 123 deletions
|
@ -37,12 +37,12 @@ $faUsername = get_cfg_var('se.secrets.fractured_atlas.username');
|
|||
$faPassword = get_cfg_var('se.secrets.fractured_atlas.password');
|
||||
|
||||
// Test donations
|
||||
// fa000cbf-af6f-4c14-8919-da6cf81a27ea Regular donation, patrons, public, recurring
|
||||
// a010dcaf-d2ab-49da-878c-cb447b12152e Regular donation, non-patrons, private, one time
|
||||
// 5a544447-708d-43da-a7b8-7bd8d9804652 AOGF donation, patrons, public, one time
|
||||
// e097c777-e2d8-4b21-b99c-e83da8696af8 AOGF donation, non-patrons, anonymous, one time
|
||||
// 946554ca-ffc0-4259-bcc6-be6c844fbbdc Regular donation, patrons, private, recurring
|
||||
// 416608c6-cbf5-4153-8956-cb9051bb849e Regular donation, patrons, public, one time, in memory of
|
||||
// `fa000cbf-af6f-4c14-8919-da6cf81a27ea` Regular donation, patrons, public, recurring.
|
||||
// `a010dcaf-d2ab-49da-878c-cb447b12152e` Regular donation, non-patrons, private, one time.
|
||||
// `5a544447-708d-43da-a7b8-7bd8d9804652` AOGF donation, patrons, public, one time.
|
||||
// `e097c777-e2d8-4b21-b99c-e83da8696af8` AOGF donation, non-patrons, anonymous, one time.
|
||||
// `946554ca-ffc0-4259-bcc6-be6c844fbbdc` Regular donation, patrons, private, recurring.
|
||||
// `416608c6-cbf5-4153-8956-cb9051bb849e` Regular donation, patrons, public, one time, in memory of.
|
||||
|
||||
Db::Query('start transaction');
|
||||
|
||||
|
@ -108,7 +108,7 @@ try{
|
|||
}
|
||||
|
||||
// Wait until the page finishes loading.
|
||||
// We have to expand the row before we can select its contents, so click the 'expand' button once it's visible.
|
||||
// We have to expand the row before we can select its contents, so click the "expand" button once it's visible.
|
||||
try{
|
||||
/** @var WebDriverElement $toggleButton */
|
||||
$toggleButton = $driver->wait(20, 250)->until(WebDriverExpectedCondition::visibilityOfElementLocated(WebDriverBy::xpath('//button[contains(@class, "button-toggle")]')));
|
||||
|
@ -132,7 +132,7 @@ try{
|
|||
$payment->Processor = $pendingPayment->Processor;
|
||||
$hasSoftCredit = false;
|
||||
try{
|
||||
// If the donation is via a foundation (like American Online Giving Foundation) then there will be a 'soft credit' <th> element.
|
||||
// If the donation is via a foundation (like American Online Giving Foundation) then there will be a "soft credit" `<th>` element.
|
||||
if(sizeof($detailsRow->findElements(WebDriverBy::xpath('//th[normalize-space(.) = "Soft Credit Donor Info"]'))) > 0){
|
||||
// We're a foundation donation
|
||||
$payment->User->Name = trim($detailsRow->findElement(WebDriverBy::xpath('//td[preceding-sibling::th[normalize-space(.) = "Name"] and (ancestor::tbody[1])[(./preceding-sibling::thead[1])//th[normalize-space(.) = "Soft Credit Donor Info"]]]'))->getText());
|
||||
|
@ -147,7 +147,7 @@ try{
|
|||
|
||||
// These donations are typically (always?) employer matches.
|
||||
// FA does not provide a way to connect the original donation with the employer match.
|
||||
// Example bbf87b83-d341-426f-b6c9-9091e3222e57
|
||||
// See donation `bbf87b83-d341-426f-b6c9-9091e3222e57`.
|
||||
if($payment->User->Name == 'American Online Giving Foundation'){
|
||||
$payment->IsMatchingDonation = true;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ try{
|
|||
$payment->TransactionId = trim($transactionId);
|
||||
|
||||
// We might also get a case where the donation is on behalf of a company match, but there's not really a way to distinguish that. Do a rough check.
|
||||
// See donation 00b60a22-eafa-44cb-9850-54bef9763e8d
|
||||
// See donation `00b60a22-eafa-44cb-9850-54bef9763e8d`.
|
||||
if($payment->User !== null && !$hasSoftCredit && preg_match('/\b(L\.?L\.?C\.?|Foundation|President|Fund|Charitable)\b/ius', $payment->User->Name ?? '')){
|
||||
$payment->User = null;
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ try{
|
|||
(
|
||||
$payment->IsRecurring
|
||||
&&
|
||||
$payment->Amount >= 10
|
||||
$payment->Amount >= PATRONS_CIRCLE_MONTHLY_COST
|
||||
&&
|
||||
$payment->Created >= $lastMonth
|
||||
)
|
||||
|
@ -202,22 +202,17 @@ try{
|
|||
(
|
||||
!$payment->IsRecurring
|
||||
&&
|
||||
$payment->Amount >= 100
|
||||
$payment->Amount >= PATRONS_CIRCLE_YEARLY_COST
|
||||
&&
|
||||
$payment->Created >= $lastYear
|
||||
)
|
||||
){
|
||||
// This payment is eligible for the Patrons Circle!
|
||||
if($payment->UserId !== null && $payment->User !== null){
|
||||
$patron = Db::Query('SELECT * from Patrons where UserId = ? and Ended is null', [$payment->UserId], Patron::class)[0] ?? null;
|
||||
|
||||
// Are we already a patron?
|
||||
if(!Db::QueryBool('
|
||||
SELECT exists(
|
||||
select *
|
||||
from Patrons
|
||||
where UserId = ?
|
||||
and Ended is null
|
||||
)
|
||||
', [$payment->UserId])){
|
||||
if($patron === null){
|
||||
// Not a patron yet, add them to the Patrons Circle.
|
||||
|
||||
$patron = new Patron();
|
||||
|
@ -231,25 +226,39 @@ try{
|
|||
$patron->AlternateName = trim($detailsRow->findElement(WebDriverBy::xpath('//td[preceding-sibling::th[normalize-space(.) = "Attribution Text"]]'))->getText());
|
||||
}
|
||||
catch(Exception){
|
||||
// Pass.
|
||||
}
|
||||
|
||||
if($payment->IsRecurring){
|
||||
$patron->BaseCost = PATRONS_CIRCLE_MONTHLY_COST;
|
||||
$patron->CycleType == Enums\CycleType::Monthly;
|
||||
}
|
||||
else{
|
||||
$patron->BaseCost = PATRONS_CIRCLE_YEARLY_COST;
|
||||
$patron->CycleType == Enums\CycleType::Yearly;
|
||||
}
|
||||
|
||||
$log->Write('Adding donor as patron ...');
|
||||
$patron->Create();
|
||||
}
|
||||
elseif(!$payment->IsRecurring && !$payment->IsMatchingDonation){
|
||||
// User is already a patron, but they made another non-recurring, non-matching donation.
|
||||
// Send a thank-you email.
|
||||
else{
|
||||
// User is already a patron.
|
||||
// We may get a case where an existing Patron makes another donation that
|
||||
if(!$payment->IsRecurring && !$payment->IsMatchingDonation){
|
||||
// User is already a Patron, but they made another non-recurring, non-matching donation.
|
||||
// Send a thank-you email.
|
||||
|
||||
$log->Write('Sending thank you email to patron donor donating extra.');
|
||||
$em = new Email();
|
||||
$em->To = $payment->User->Email ?? '';
|
||||
$em->ToName = $payment->User->Name ?? '';
|
||||
$em->From = EDITOR_IN_CHIEF_EMAIL_ADDRESS;
|
||||
$em->FromName = EDITOR_IN_CHIEF_NAME;
|
||||
$em->Subject = 'Thank you for supporting Standard Ebooks!';
|
||||
$em->Body = Template::EmailDonationThankYou();
|
||||
$em->TextBody = Template::EmailDonationThankYouText();
|
||||
$em->Send();
|
||||
$log->Write('Sending thank you email to patron donor donating extra.');
|
||||
$em = new Email();
|
||||
$em->To = $payment->User->Email ?? '';
|
||||
$em->ToName = $payment->User->Name ?? '';
|
||||
$em->From = EDITOR_IN_CHIEF_EMAIL_ADDRESS;
|
||||
$em->FromName = EDITOR_IN_CHIEF_NAME;
|
||||
$em->Subject = 'Thank you for supporting Standard Ebooks!';
|
||||
$em->Body = Template::EmailDonationThankYou();
|
||||
$em->TextBody = Template::EmailDonationThankYouText();
|
||||
$em->Send();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif(!$payment->IsRecurring && !$payment->IsMatchingDonation){
|
||||
|
|
|
@ -1,98 +1,35 @@
|
|||
#!/usr/bin/php
|
||||
<?
|
||||
/**
|
||||
* Get a list of payments that are within 1 year / 45 days of today, and deactivate Patrons Circle members who aren't in that list.
|
||||
* We give a 15 day grace period to Patrons Circle members because sometimes FA can be delayed in charging.
|
||||
*/
|
||||
|
||||
require_once('/standardebooks.org/web/lib/Core.php');
|
||||
|
||||
use Safe\DateTimeImmutable;
|
||||
|
||||
use function Safe\file_get_contents;
|
||||
use function Safe\preg_match_all;
|
||||
use function Safe\shell_exec;
|
||||
|
||||
// Get a list of payments that are within 1 year / 45 days of today, and deactivate Patrons Circle members who aren't in that list.
|
||||
// We give a 15 day grace period to Patrons Circle members because sometimes FA can be delayed in charging.
|
||||
|
||||
$lastYear = new DateTimeImmutable('-1 year');
|
||||
|
||||
$expiredPatrons = Db::Query('
|
||||
SELECT * from Patrons
|
||||
where
|
||||
Ended is null and
|
||||
SELECT * from Patrons where
|
||||
Ended is null
|
||||
and
|
||||
UserId not in
|
||||
(
|
||||
select distinct UserId from Payments where
|
||||
UserId is not null
|
||||
and
|
||||
(
|
||||
(IsRecurring = true and Amount >= 10 and Created > ? - interval 45 day)
|
||||
or
|
||||
(IsRecurring = false and Amount >= 100 and Created > ? - interval 1 year)
|
||||
)
|
||||
select distinct p.UserId from Patrons p
|
||||
inner join Payments py
|
||||
using (UserId)
|
||||
where
|
||||
p.Ended is null and
|
||||
(
|
||||
(IsRecurring = true and CycleType = ? and Amount >= p.BaseCost and py.Created > ? - interval 45 day)
|
||||
or
|
||||
(IsRecurring = false and CycleType = ? and Amount >= p.BaseCost and py.Created > ? - interval 1 year)
|
||||
)
|
||||
)
|
||||
', [NOW, NOW], Patron::class);
|
||||
', [Enums\CycleType::Monthly, NOW, Enums\CycleType::Yearly, NOW], Patron::class);
|
||||
|
||||
if(sizeof($expiredPatrons) > 0){
|
||||
$ebooksThisYear = 0;
|
||||
|
||||
// We can't use the Library class to get ebooks because this script is typically run via cron or CLI, which doesn't have access PHP-FMP's APCu cache.
|
||||
foreach(explode("\n", trim(shell_exec('find ' . EBOOKS_DIST_PATH . ' -name "content.opf"'))) as $filename){
|
||||
$metadata = file_get_contents($filename);
|
||||
|
||||
// Don't create a new Ebook object because that's very slow. Just do a regex match for speed.
|
||||
preg_match_all('/<dc:date>(.+?)<\/dc:date>/iu', $metadata, $matches);
|
||||
|
||||
if(sizeof($matches) > 0){
|
||||
$created = new DateTimeImmutable($matches[1][0]);
|
||||
if($created >= $lastYear){
|
||||
$ebooksThisYear++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$ebooksThisYear = Db::QueryInt('SELECT count(*) from Ebooks where EbookCreated >= ? - interval 1 year', [NOW]);
|
||||
|
||||
foreach($expiredPatrons as $patron){
|
||||
Db::Query('
|
||||
UPDATE Patrons
|
||||
set Ended = ?
|
||||
where UserId = ?
|
||||
', [NOW, $patron->UserId]);
|
||||
|
||||
Db::Query('
|
||||
UPDATE Benefits
|
||||
set CanAccessFeeds = false,
|
||||
CanVote = false,
|
||||
CanBulkDownload = false
|
||||
where UserId = ?
|
||||
', [$patron->UserId]);
|
||||
|
||||
// Email the patron to notify them their term has ended.
|
||||
// Is the patron a recurring subscriber?
|
||||
$lastPayment = Db::Query('
|
||||
SELECT *
|
||||
from Payments
|
||||
where UserId = ?
|
||||
order by Created desc
|
||||
limit 1
|
||||
', [$patron->UserId], Payment::class);
|
||||
|
||||
if(sizeof($lastPayment) > 0 && $patron->User->Email !== null){
|
||||
$em = new Email();
|
||||
$em->From = EDITOR_IN_CHIEF_EMAIL_ADDRESS;
|
||||
$em->FromName = EDITOR_IN_CHIEF_NAME;
|
||||
$em->To = $patron->User->Email;
|
||||
$em->ToName = $patron->User->Name ?? '';
|
||||
$em->Subject = 'Will you still help us make free, beautiful digital literature?';
|
||||
|
||||
if($lastPayment[0]->IsRecurring){
|
||||
// Email recurring donors who have lapsed.
|
||||
$em->Body = Template::EmailPatronsCircleRecurringCompleted();
|
||||
$em->TextBody = Template::EmailPatronsCircleRecurringCompletedText();
|
||||
}
|
||||
else{
|
||||
// Email one time donors who have expired after one year.
|
||||
$em->Body = Template::EmailPatronsCircleCompleted(['ebooksThisYear' => $ebooksThisYear]);
|
||||
$em->TextBody = Template::EmailPatronsCircleCompletedText(['ebooksThisYear' => $ebooksThisYear]);
|
||||
}
|
||||
|
||||
$em->Send();
|
||||
}
|
||||
$patron->End($ebooksThisYear);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue