Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions docs/capture-reminders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Capture Reminder Email Feature

This feature adds automated reminder emails for Stripe payments that are approaching their capture deadline.

## Overview

When payments are authorized but not yet captured, merchants have a limited time window (typically 7 days) to capture the funds before they expire. This feature automatically sends reminder emails 24 hours before the capture deadline to help merchants avoid losing access to authorized funds.

## Setup

### For New Installations

The feature is automatically enabled for new installations. The database table will include the necessary `reminder_sent_at` column.

### For Existing Installations

If upgrading from a previous version, you need to update the database schema:

1. Call the schema update endpoint once:
```
GET https://yourstore.com/rth_stripe.php?action=updateSchema
```

This will add the `reminder_sent_at` column to the `rth_stripe_payment` table.

## Automated Checking

Set up a cron job to regularly check for payments nearing their capture deadline:

```bash
# Check every hour for payments requiring reminders
0 * * * * curl -s "https://yourstore.com/rth_stripe.php?action=checkCaptureReminders"
```

### Cron Job Response

The endpoint returns JSON with the results:

```json
{
"success": true,
"reminders_sent": 2,
"message": "Checked capture reminders. Sent 2 reminder(s)."
}
```

## How It Works

1. **Payment Scanning**: The system checks all payments that haven't had reminder emails sent
2. **Deadline Calculation**: Uses the same logic as the admin interface to calculate capture deadlines
3. **Timing Check**: Sends reminders when between 1-24 hours remain until deadline
4. **Email Delivery**: Sends HTML email to the merchant with order and deadline details
5. **Tracking**: Marks payments as having received reminders to prevent duplicates

## Email Content

Reminder emails include:
- Order ID and customer email
- Payment amount and currency
- Stripe Payment Intent ID
- Capture deadline date/time
- Remaining time until deadline
- Action instructions

## Email Configuration

The system attempts to use the store's configured email address in this order:
1. `STORE_OWNER_EMAIL_ADDRESS` from store configuration
2. Fallback to `admin@yourdomain.com`

## Technical Details

### Database Changes

Adds `reminder_sent_at` column to `rth_stripe_payment` table:
```sql
ALTER TABLE `rth_stripe_payment`
ADD COLUMN `reminder_sent_at` datetime DEFAULT NULL
```

### New Classes

- `CaptureReminderService` - Core reminder logic
- Extended `PaymentRepository` - Database operations for reminders
- New controller actions in `Controller.php`

### Requirements

- Payments must have `capture_method` set to `manual`
- Payments must be in `requires_capture` status
- No previous reminder must have been sent for the payment

## Troubleshooting

### No Emails Received

1. Check cron job is running: `curl "https://yourstore.com/rth_stripe.php?action=checkCaptureReminders"`
2. Verify email configuration in store settings
3. Check server mail logs for delivery issues
4. Ensure payments are in `requires_capture` status

### Schema Update Issues

If the schema update fails:
1. Manually add the column:
```sql
ALTER TABLE `rth_stripe_payment` ADD COLUMN `reminder_sent_at` datetime DEFAULT NULL;
```
2. Verify the column was added:
```sql
SHOW COLUMNS FROM `rth_stripe_payment` LIKE 'reminder_sent_at';
```
61 changes: 61 additions & 0 deletions src-mmlc/Classes/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,23 @@
use RobinTheHood\Stripe\Classes\Service\PaymentCaptureService;
use RobinTheHood\Stripe\Classes\Service\SessionService;
use RobinTheHood\Stripe\Classes\Service\WebhookService;
use RobinTheHood\Stripe\Classes\Service\CaptureReminderService;

class Controller extends AbstractController
{
private CheckoutService $checkoutService;
private SessionService $sessionService;
private WebhookService $webhookService;
private PaymentCaptureService $captureService;
private CaptureReminderService $reminderService;
private UrlBuilder $urlBuilder;

public function __construct(
CheckoutService $checkoutService,
SessionService $sessionService,
WebhookService $webhookService,
PaymentCaptureService $captureService,
CaptureReminderService $reminderService,
UrlBuilder $urlBuilder
) {
parent::__construct();
Expand All @@ -48,6 +51,7 @@ public function __construct(
$this->sessionService = $sessionService;
$this->webhookService = $webhookService;
$this->captureService = $captureService;
$this->reminderService = $reminderService;
$this->urlBuilder = $urlBuilder;
}

Expand Down Expand Up @@ -125,4 +129,61 @@ protected function invokeCapture(Request $request): Response
return new RedirectResponse($this->urlBuilder->getAdminOrders() . '?oID=' . $orderId . '&action=edit');
}
}

/**
* Check for payments nearing capture deadline and send reminder emails
* This action can be called via cron job: /rth_stripe.php?action=checkCaptureReminders
*/
protected function invokeCheckCaptureReminders(Request $request): Response
{
try {
$remindersSent = $this->reminderService->checkAndSendReminders();

return new Response(
json_encode([
'success' => true,
'reminders_sent' => $remindersSent,
'message' => "Checked capture reminders. Sent {$remindersSent} reminder(s)."
]),
200
);
} catch (Exception $e) {
return new Response(
json_encode([
'success' => false,
'error' => $e->getMessage()
]),
500
);
}
}

/**
* Update database schema for reminder functionality
* This action can be called once after module update: /rth_stripe.php?action=updateSchema
*/
protected function invokeUpdateSchema(Request $request): Response
{
try {
// Update the payment table schema
$paymentRepo = $this->reminderService->getPaymentRepository();
$paymentRepo->updateTableSchema();

return new Response(
json_encode([
'success' => true,
'message' => 'Database schema updated successfully.'
]),
200
);
} catch (Exception $e) {
return new Response(
json_encode([
'success' => false,
'error' => $e->getMessage()
]),
500
);
}
}
}
58 changes: 58 additions & 0 deletions src-mmlc/Classes/Repository/PaymentRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,30 @@ public function createTable(): void
`created` datetime DEFAULT NULL,
`order_id` int(11) DEFAULT NULL,
`stripe_payment_intent_id` varchar(255) DEFAULT NULL,
`reminder_sent_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
);"
);
}

/**
* Update table schema to add reminder_sent_at column for existing installations
*/
public function updateTableSchema(): void
{
// Check if the column already exists
$result = $this->db->query("SHOW COLUMNS FROM `rth_stripe_payment` LIKE 'reminder_sent_at'");
$row = $this->db->fetch($result);

if (!$row) {
// Column doesn't exist, add it
$this->db->query(
"ALTER TABLE `rth_stripe_payment`
ADD COLUMN `reminder_sent_at` datetime DEFAULT NULL"
);
}
}

public function add(int $orderId, string $stripePaymentIntentId): int
{
$dateTime = new \DateTime();
Expand Down Expand Up @@ -82,4 +101,43 @@ public function findByStripePaymentIntentId(string $paymentIntentId): array|fals

return $row;
}

/**
* Find all payments that have not had reminder emails sent yet
*
* @return array
*/
public function findPaymentsWithoutReminders(): array
{
$query = $this->db->query(
"SELECT * FROM rth_stripe_payment
WHERE reminder_sent_at IS NULL
ORDER BY created ASC"
);

$results = [];
while ($row = $this->db->fetch($query)) {
$results[] = $row;
}

return $results;
}

/**
* Mark a payment as having had its reminder email sent
*
* @param int $paymentId
* @return void
*/
public function markReminderSent(int $paymentId): void
{
$dateTime = new \DateTime();
$formattedDateTime = $dateTime->format('Y-m-d H:i:s');

$this->db->query(
"UPDATE rth_stripe_payment
SET reminder_sent_at = '$formattedDateTime'
WHERE id = $paymentId"
);
}
}
Loading