Initial commit: Complete Event Tickets Mail-In Payment Gateway

- Complete mail-in payment gateway for Event Tickets Commerce
- Support for check payments with collapsible checkout interface
- Admin interface for managing check payments and order status
- Professional asset organization with proper WordPress enqueuing
- Order management with status tracking (pending, received, bounced)
- Template system with responsive design and accessibility features
- Integration with Event Tickets Commerce order system
- Settings page for configuring payment instructions and addresses
This commit is contained in:
Ruben Ramirez 2025-06-30 21:56:49 -04:00
commit 8fd8d9708c
18 changed files with 4124 additions and 0 deletions

221
README.md Normal file
View file

@ -0,0 +1,221 @@
# Event Tickets - Mail-In Payment Gateway
A complementary WordPress plugin that adds a mail-in payment gateway for Event Tickets Commerce, allowing customers to pay by mailing checks.
## Description
This plugin extends the Event Tickets plugin by adding a "Mail-In Payment (Check)" gateway that allows customers to reserve tickets and pay by mailing a check. It provides comprehensive order management features for administrators to track and process check payments.
## Features
- **Check Payment Gateway**: Seamlessly integrates with Event Tickets Commerce
- **Order Management**: Admin interface to track pending check payments
- **Email Notifications**: Automated emails for payment instructions, confirmations, and cancellations
- **Customizable Settings**: Configure mailing address, payment instructions, and reservation periods
- **Order Tracking**: Track check received status, bounce handling, and order notes
- **Auto-Cancellation**: Optional automatic cancellation of expired orders
- **Template System**: Customizable checkout and email templates
## Requirements
- WordPress 6.6 or higher
- PHP 7.4 or higher
- Event Tickets plugin (with Commerce module)
- The Events Calendar plugin (recommended)
## Installation
1. Download the plugin files
2. Upload the `event-tickets-mail-in-payment` folder to `/wp-content/plugins/`
3. Activate the plugin through the 'Plugins' menu in WordPress
4. Configure the gateway settings under **Tickets > Settings > Payments**
## Configuration
### Basic Setup
1. Navigate to **Tickets > Settings > Payments**
2. Find the "Mail-In Payment (Check) Settings" section
3. Configure the required settings:
- **Enable Mail-In Payments**: Check to activate the gateway
- **Make Check Payable To**: Organization or person name for checks
- **Mailing Address**: Complete address where checks should be sent
- **Payment Instructions**: Custom instructions for customers
### Advanced Settings
- **Payment Method Title**: Customize the gateway name shown to customers
- **Payment Method Description**: Brief description shown during checkout
- **Reservation Period**: Number of days to hold tickets while awaiting payment
- **Auto-Cancel Expired Orders**: Automatically cancel orders after reservation period
## Order Management
### Admin Interface
Access mail-in payment management through:
- **Tickets > Orders > Mail-In Payments** - Dedicated management page
- Individual order edit screens include mail-in payment details
### Order Processing
1. **Pending Orders**: Orders awaiting check payment appear in the management interface
2. **Mark as Received**: When a check arrives, mark it as received with optional notes
3. **Mark as Bounced**: Handle returned/bounced checks with reason tracking
4. **Automatic Emails**: System sends confirmation emails when status changes
### Order Statuses
- **Pending/Approved**: Order created, awaiting check payment
- **Completed**: Check received and processed
- **Denied**: Check bounced or order manually cancelled
## Templates
### Customization
Templates can be overridden by copying them to your theme:
```
wp-content/themes/[your-theme]/event-tickets-mail-in/
├── checkout/
│ └── mail-in-form.php
├── emails/
│ ├── payment-instructions.php
│ ├── payment-confirmation.php
│ └── order-cancelled.php
└── admin/
├── mail-in-orders.php
└── order-meta-box.php
```
### Available Templates
- **Checkout Form**: `checkout/mail-in-form.php` - Payment method display during checkout
- **Email Templates**: Complete HTML email templates for all notifications
- **Admin Templates**: Management interface templates
## Hooks and Filters
### Actions
```php
// Triggered when a check is marked as received
do_action( 'et_mail_in_check_received', $order_id, $notes );
// Triggered when a check is marked as bounced
do_action( 'et_mail_in_check_bounced', $order_id, $reason );
// Daily cleanup of expired orders
do_action( 'et_mail_in_cleanup_expired_orders' );
```
### Filters
```php
// Customize payment instructions
add_filter( 'et_mail_in_payment_instructions', function( $instructions, $order ) {
return $instructions;
}, 10, 2 );
// Modify email content
add_filter( 'et_mail_in_email_content', function( $content, $template, $order ) {
return $content;
}, 10, 3 );
// Customize reservation period per order
add_filter( 'et_mail_in_reservation_days', function( $days, $order ) {
return $days;
}, 10, 2 );
```
## Email System
### Automated Emails
1. **Payment Instructions**: Sent when order is created
2. **Payment Confirmation**: Sent when check is marked as received
3. **Order Cancellation**: Sent when order is cancelled or expires
### Email Customization
- HTML templates with responsive design
- Includes order details, payment instructions, and mailing address
- Uses WordPress mail system with customizable headers
## Security Features
- **Nonce Verification**: All admin actions are protected with WordPress nonces
- **Capability Checks**: Admin functions require appropriate user permissions
- **Data Sanitization**: All user inputs are properly sanitized
- **Order Validation**: Prevents unauthorized access to order data
## Development
### File Structure
```
event-tickets-mail-in-payment/
├── event-tickets-mail-in-payment.php # Main plugin file
├── src/ # Core classes
│ ├── Gateway.php # Payment gateway implementation
│ ├── Order.php # Order management
│ ├── Settings.php # Configuration handling
│ ├── Provider.php # Service provider
│ └── Hooks.php # WordPress hooks
├── templates/ # Template files
│ ├── checkout/ # Frontend templates
│ ├── emails/ # Email templates
│ └── admin/ # Admin interface templates
└── README.md # Documentation
```
### Extending the Plugin
The plugin is built with extensibility in mind:
- PSR-4 autoloading with namespace `ET_Mail_In_Payment`
- Service provider pattern for modular functionality
- Hook system for customization
- Template override system
## Troubleshooting
### Common Issues
1. **Gateway Not Appearing**: Ensure Event Tickets plugin is active and Commerce module is enabled
2. **Settings Not Saving**: Check file permissions and WordPress error logs
3. **Emails Not Sending**: Verify WordPress mail configuration
4. **Template Issues**: Clear any caching plugins after template changes
### Debug Mode
Enable WordPress debug mode to see detailed error messages:
```php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
```
## Support
For support and questions:
1. Check the plugin settings and this documentation
2. Review WordPress error logs for any issues
3. Ensure all requirements are met
4. Contact the plugin developer for technical support
## License
This plugin is licensed under the GPL v2 or later.
## Changelog
### 1.0.0
- Initial release
- Mail-in payment gateway implementation
- Order management interface
- Email notification system
- Template system
- Settings configuration

122
assets/css/admin.css Normal file
View file

@ -0,0 +1,122 @@
/**
* Mail-In Payment Gateway Admin Styles
* @since 1.0.0
*/
.et-mail-in-admin-page {
max-width: 1200px;
margin: 20px 0;
}
.et-mail-in-orders-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.et-mail-in-orders-table th,
.et-mail-in-orders-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.et-mail-in-orders-table th {
background-color: #f1f1f1;
font-weight: bold;
}
.et-mail-in-status {
padding: 4px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
.et-mail-in-status--pending {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.et-mail-in-status--received {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.et-mail-in-status--bounced {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.et-mail-in-action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.et-mail-in-action-button {
padding: 6px 12px;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
transition: background-color 0.2s;
}
.et-mail-in-action-button--primary {
background-color: #0073aa;
color: white;
}
.et-mail-in-action-button--primary:hover {
background-color: #005a87;
}
.et-mail-in-action-button--danger {
background-color: #dc3545;
color: white;
}
.et-mail-in-action-button--danger:hover {
background-color: #c82333;
}
.et-mail-in-meta-box {
background: #fff;
border: 1px solid #ccd0d4;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.et-mail-in-meta-row {
display: flex;
margin-bottom: 12px;
align-items: center;
}
.et-mail-in-meta-label {
font-weight: bold;
min-width: 150px;
color: #23282d;
}
.et-mail-in-meta-value {
flex: 1;
color: #32373c;
}
.et-mail-in-address-block {
background: #f9f9f9;
padding: 10px;
border-left: 3px solid #0073aa;
font-family: monospace;
white-space: pre-line;
border-radius: 0 3px 3px 0;
}

192
assets/css/checkout.css Normal file
View file

@ -0,0 +1,192 @@
/**
* Mail-In Payment Gateway Checkout Styles
* @since 1.0.0
*/
.tribe-tickets-commerce-gateway-mail-in {
margin: 30px 0 15px 0 !important; /* More space above */
border-radius: 4px;
border: 1px solid #ddd !important;
overflow: hidden;
}
/* Toggle Button Styling */
.tribe-tickets-commerce-gateway-mail-in__toggle {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.tribe-tickets-commerce-gateway-mail-in__toggle-button {
width: 100%;
padding: 18px 20px; /* Slightly more padding for larger appearance */
background: none;
border: none;
text-align: center; /* Center the text */
cursor: pointer;
font-size: 18px; /* Larger font size */
font-weight: 500; /* Slightly bolder */
color: #0073aa;
display: flex;
align-items: center;
justify-content: center; /* Center the content */
gap: 12px; /* Slightly more gap */
transition: background-color 0.2s;
}
.tribe-tickets-commerce-gateway-mail-in__toggle-button:hover {
background: #e9ecef;
}
.tribe-tickets-commerce-gateway-mail-in__toggle-button:focus {
outline: 2px solid #0073aa;
outline-offset: -2px;
}
.toggle-icon {
font-size: 12px;
transition: transform 0.2s;
width: 12px;
display: inline-block;
}
/* Content Styling */
.tribe-tickets-commerce-gateway-mail-in__content {
padding: 20px;
background: #fff;
}
.tribe-tickets-commerce-gateway-mail-in__header h4 {
margin: 0 0 10px 0;
color: #333;
}
.tribe-tickets-commerce-gateway-mail-in__description {
margin: 0 0 20px 0;
font-style: italic;
color: #666;
}
.tribe-tickets-commerce-gateway-mail-in__instructions {
background: #fff;
padding: 15px;
border-radius: 4px;
border: 1px solid #e5e5e5;
}
.tribe-tickets-commerce-gateway-mail-in__instructions strong {
color: #333;
font-size: 14px;
}
.tribe-tickets-commerce-gateway-mail-in__details {
margin: 15px 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 768px) {
.tribe-tickets-commerce-gateway-mail-in__details {
grid-template-columns: 1fr;
}
}
.tribe-tickets-commerce-gateway-mail-in__payable-to-name {
font-size: 16px;
font-weight: bold;
margin: 5px 0;
color: #0073aa;
}
.tribe-tickets-commerce-gateway-mail-in__address-block {
background: #f5f5f5;
padding: 10px;
border-radius: 4px;
border-left: 3px solid #0073aa;
line-height: 1.6;
margin-top: 5px;
}
.tribe-tickets-commerce-gateway-mail-in__important-note {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 15px;
border-radius: 4px;
margin-top: 15px;
}
.tribe-tickets-commerce-gateway-mail-in__important-note strong {
color: #856404;
}
.tribe-tickets-commerce-gateway-mail-in__important-note ul {
margin: 10px 0 0 0;
padding-left: 20px;
}
.tribe-tickets-commerce-gateway-mail-in__important-note li {
margin-bottom: 5px;
color: #856404;
}
/* Form styling */
.tribe-tickets__commerce-checkout-purchaser-info {
background: #f8f9fa;
padding: 20px;
border-radius: 4px;
border: 1px solid #dee2e6;
margin: 20px 0;
}
.tribe-tickets__commerce-checkout-purchaser-info h4 {
margin: 0 0 15px 0;
color: #333;
}
.tribe-tickets__commerce-checkout-form-field {
margin-bottom: 15px;
}
.tribe-tickets__commerce-checkout-form-field label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.tribe-tickets__commerce-checkout-form-field .required {
color: #dc3545;
}
.tribe-tickets__commerce-checkout-form-field input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
}
.tribe-tickets__commerce-checkout-form-field input:focus {
outline: none;
border-color: #0073aa;
box-shadow: 0 0 0 2px rgba(0, 115, 170, 0.2);
}
.tribe-tickets__commerce-checkout-form-submit {
margin-top: 20px;
}
.tribe-tickets__commerce-checkout-form-submit-button {
background: #0073aa;
color: white;
border: none;
padding: 12px 24px;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.tribe-tickets__commerce-checkout-form-submit-button:hover {
background: #005a87;
}

63
assets/js/admin.js Normal file
View file

@ -0,0 +1,63 @@
/**
* Mail-In Payment Gateway Admin JavaScript
* @since 1.0.0
*/
(function($) {
'use strict';
$(document).ready(function() {
// Confirmation dialogs for admin actions
$('.et-mail-in-mark-received').on('click', function(e) {
const confirmMessage = etMailInAdmin.strings.confirm_received || 'Are you sure you want to mark this check as received?';
if (!confirm(confirmMessage)) {
e.preventDefault();
return false;
}
});
$('.et-mail-in-mark-bounced').on('click', function(e) {
const confirmMessage = etMailInAdmin.strings.confirm_bounced || 'Are you sure you want to mark this check as bounced?';
if (!confirm(confirmMessage)) {
e.preventDefault();
return false;
}
});
// Auto-hide admin notices after 5 seconds
setTimeout(function() {
$('.notice.is-dismissible').fadeOut();
}, 5000);
// Enhanced form validation
$('form[data-et-mail-in-form]').on('submit', function(e) {
const form = $(this);
const requiredFields = form.find('[required]');
let hasErrors = false;
requiredFields.each(function() {
const field = $(this);
const value = field.val().trim();
if (!value) {
field.addClass('error');
hasErrors = true;
} else {
field.removeClass('error');
}
});
if (hasErrors) {
e.preventDefault();
alert('Please fill in all required fields.');
return false;
}
});
// Remove error styling on input
$('input, textarea, select').on('input change', function() {
$(this).removeClass('error');
});
});
})(jQuery);

46
assets/js/checkout.js Normal file
View file

@ -0,0 +1,46 @@
/**
* Mail-In Payment Gateway Checkout JavaScript
* @since 1.0.0
*/
document.addEventListener('DOMContentLoaded', function() {
const toggleButton = document.getElementById('mail-in-toggle-button');
const content = document.getElementById('mail-in-content');
const toggleIcon = toggleButton ? toggleButton.querySelector('.toggle-icon') : null;
// Only initialize if elements exist
if (!toggleButton || !content || !toggleIcon) {
return;
}
// Initialize accessibility attributes
toggleButton.setAttribute('aria-expanded', 'false');
toggleButton.setAttribute('aria-controls', 'mail-in-content');
content.setAttribute('aria-hidden', 'true');
toggleButton.addEventListener('click', function() {
const isExpanded = content.style.display !== 'none';
if (isExpanded) {
// Hide content
content.style.display = 'none';
toggleIcon.textContent = '▶';
toggleButton.setAttribute('aria-expanded', 'false');
content.setAttribute('aria-hidden', 'true');
} else {
// Show content
content.style.display = 'block';
toggleIcon.textContent = '▼';
toggleButton.setAttribute('aria-expanded', 'true');
content.setAttribute('aria-hidden', 'false');
}
});
// Handle keyboard navigation
toggleButton.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleButton.click();
}
});
});

View file

@ -0,0 +1,185 @@
<?php
/**
* Plugin Name: Event Tickets - Mail-In Payment Gateway
* Plugin URI: https://github.com/yourusername/event-tickets-mail-in-payment
* Description: Adds a mail-in payment gateway for Event Tickets Commerce to accept check payments.
* Version: 1.0.0
* Requires at least: 6.6
* Requires PHP: 7.4
* Author: Your Name
* License: GPLv2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: event-tickets-mail-in
* Domain Path: /languages/
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Define plugin constants
define( 'ET_MAIL_IN_VERSION', '1.0.0' );
define( 'ET_MAIL_IN_FILE', __FILE__ );
define( 'ET_MAIL_IN_PATH', plugin_dir_path( __FILE__ ) );
define( 'ET_MAIL_IN_URL', plugin_dir_url( __FILE__ ) );
/**
* Main plugin class
*/
class ET_Mail_In_Payment {
/**
* Single instance of the class
*
* @var ET_Mail_In_Payment
*/
private static $instance;
/**
* Get single instance
*
* @return ET_Mail_In_Payment
*/
public static function instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
add_action( 'plugins_loaded', [ $this, 'init' ] );
register_activation_hook( __FILE__, [ $this, 'activate' ] );
register_deactivation_hook( __FILE__, [ $this, 'deactivate' ] );
}
/**
* Initialize the plugin
*/
public function init() {
// Check if Event Tickets is active
if ( ! $this->is_event_tickets_active() ) {
add_action( 'admin_notices', [ $this, 'event_tickets_missing_notice' ] );
return;
}
// Load text domain
load_plugin_textdomain( 'event-tickets-mail-in', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
// Include required files
$this->includes();
// Initialize components
$this->init_components();
}
/**
* Check if Event Tickets is active
*
* @return bool
*/
private function is_event_tickets_active() {
return class_exists( 'Tribe__Tickets__Main' ) &&
class_exists( 'TEC\Tickets\Commerce\Module' );
}
/**
* Include required files
*/
private function includes() {
require_once ET_MAIL_IN_PATH . 'src/Gateway.php';
require_once ET_MAIL_IN_PATH . 'src/Order.php';
require_once ET_MAIL_IN_PATH . 'src/Settings.php';
require_once ET_MAIL_IN_PATH . 'src/Provider.php';
require_once ET_MAIL_IN_PATH . 'src/Hooks.php';
}
/**
* Initialize components
*/
private function init_components() {
// Register our provider with dependency injection container
tribe_register_provider( ET_Mail_In_Payment\Provider::class );
}
/**
* Plugin activation
*/
public function activate() {
// Check dependencies
if ( ! $this->is_event_tickets_active() ) {
deactivate_plugins( plugin_basename( __FILE__ ) );
wp_die(
__( 'Event Tickets - Mail-In Payment Gateway requires Event Tickets to be installed and activated.', 'event-tickets-mail-in' ),
__( 'Plugin Activation Error', 'event-tickets-mail-in' ),
[ 'back_link' => true ]
);
}
// Set default options if they don't exist
$this->set_default_options();
}
/**
* Plugin deactivation
*/
public function deactivate() {
// Clean up if needed
}
/**
* Get default payment instructions
*
* @return string
*/
private function get_default_instructions() {
return __( 'Please mail your check to the address provided below. Your tickets will be reserved for 7 days while we wait for your payment to arrive. Include your order number on the check memo line.', 'event-tickets-mail-in' );
}
/**
* Set default options on activation
*/
private function set_default_options() {
// Set default options if they don't exist
if ( false === tribe_get_option( 'tickets-commerce-mail-in-enabled' ) ) {
tribe_update_option( 'tickets-commerce-mail-in-enabled', true );
}
if ( empty( tribe_get_option( 'tickets-commerce-mail-in-title' ) ) ) {
tribe_update_option( 'tickets-commerce-mail-in-title', __( 'Mail-In Payment (Check)', 'event-tickets-mail-in' ) );
}
if ( empty( tribe_get_option( 'tickets-commerce-mail-in-description' ) ) ) {
tribe_update_option( 'tickets-commerce-mail-in-description', __( 'Pay by mailing a check to our address.', 'event-tickets-mail-in' ) );
}
if ( empty( tribe_get_option( 'tickets-commerce-mail-in-instructions' ) ) ) {
tribe_update_option( 'tickets-commerce-mail-in-instructions', $this->get_default_instructions() );
}
// For testing purposes, set some default values
if ( empty( tribe_get_option( 'tickets-commerce-mail-in-address' ) ) ) {
tribe_update_option( 'tickets-commerce-mail-in-address', "123 Main Street\nAnytown, ST 12345\nUSA" );
}
if ( empty( tribe_get_option( 'tickets-commerce-mail-in-payable-to' ) ) ) {
tribe_update_option( 'tickets-commerce-mail-in-payable-to', "Your Organization Name" );
}
}
/**
* Display admin notice when Event Tickets is missing
*/
public function event_tickets_missing_notice() {
echo '<div class="notice notice-error"><p>';
echo esc_html__( 'Event Tickets - Mail-In Payment Gateway requires Event Tickets to be installed and activated.', 'event-tickets-mail-in' );
echo '</p></div>';
}
}
// Initialize the plugin
ET_Mail_In_Payment::instance();

546
src/Gateway.php Normal file
View file

@ -0,0 +1,546 @@
<?php
namespace ET_Mail_In_Payment;
use TEC\Tickets\Commerce\Gateways\Contracts\Abstract_Gateway;
use TEC\Tickets\Commerce\Status;
use TEC\Tickets\Commerce\Utils\Value;
/**
* Mail-In Payment Gateway class
*
* @since 1.0.0
*/
class Gateway extends Abstract_Gateway {
/**
* Gateway key identifier
*
* @since 1.0.0
* @var string
*/
protected static string $key = 'mail-in';
/**
* Gateway settings class
*
* @since 1.0.0
* @var string
*/
protected static string $settings = Settings::class;
/**
* No merchant class needed for mail-in payments
*
* @since 1.0.0
* @var string
*/
protected static string $merchant = '';
/**
* Supported currencies (all currencies since it's manual)
*
* @since 1.0.0
* @var array
*/
protected static array $supported_currencies = [
'USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY', 'CHF', 'CNY', 'SEK', 'NZD',
'MXN', 'SGD', 'HKD', 'NOK', 'PHP', 'DKK', 'PLN', 'CZK', 'HUF', 'ILS',
'CLP', 'TRY', 'RON', 'HRK', 'BGN', 'BRL', 'ARS', 'INR', 'KRW', 'THB',
'MYR', 'ZAR', 'RUB', 'UAH', 'EGP', 'MAD', 'TND', 'DZD', 'NGN', 'KES'
];
/**
* Get the gateway key
*
* @since 1.0.0
* @return string
*/
public static function get_key(): string {
return static::$key;
}
/**
* Get the gateway label
*
* @since 1.0.0
* @return string
*/
public static function get_label() {
return tribe_get_option( 'tickets-commerce-mail-in-title', __( 'Mail-In Payment (Check)', 'event-tickets-mail-in' ) );
}
/**
* Check if the gateway is connected/configured
* Override Abstract_Gateway's merchant-dependent implementation
*
* @since 1.0.0
* @return bool
*/
public static function is_connected(): bool {
if ( ! static::should_show() ) {
return false;
}
// Gateway is connected if mailing address is configured
$address = tribe_get_option( 'tickets-commerce-mail-in-address', '' );
$payable_to = tribe_get_option( 'tickets-commerce-mail-in-payable-to', '' );
return ! empty( $address ) && ! empty( $payable_to );
}
/**
* Check if the gateway is active
* Override Abstract_Gateway's merchant-dependent implementation
*
* @since 1.0.0
* @return bool
*/
public static function is_active(): bool {
if ( ! static::should_show() ) {
return false;
}
return static::is_enabled() && static::is_connected();
}
/**
* Check if the gateway should be shown
*
* @since 1.0.0
* @return bool
*/
public static function should_show() {
return true;
}
/**
* Check if the gateway is enabled
*
* @since 1.0.0
* @return bool
*/
public static function is_enabled(): bool {
$should_show = static::should_show();
$enabled_option = tribe_get_option( 'tickets-commerce-mail-in-enabled', false );
if ( ! $should_show ) {
return false;
}
return tribe_is_truthy( $enabled_option );
}
/**
* Check if this gateway renders solo (prevents other gateways from showing)
* Mail-in payments should coexist with other gateways
* Override Abstract_Gateway's default true
*
* @since 1.0.0
* @return bool
*/
public function renders_solo(): bool {
return false;
}
/**
* Get gateway description
*
* @since 1.0.0
* @return string
*/
public static function get_description() {
return tribe_get_option(
'tickets-commerce-mail-in-description',
__( 'Pay by mailing a check to our address.', 'event-tickets-mail-in' )
);
}
/**
* Render the checkout form
*
* @since 1.0.0
* @param array $args Template arguments
* @return void
*/
public function checkout_form( $args = [] ) {
error_log( 'Mail-In Gateway: checkout_form called' );
$instructions = tribe_get_option( 'tickets-commerce-mail-in-instructions', '' );
$mailing_address = tribe_get_option( 'tickets-commerce-mail-in-address', '' );
$payable_to = tribe_get_option( 'tickets-commerce-mail-in-payable-to', '' );
// Direct output for debugging
echo '<div style="background: blue; border: 5px solid orange; padding: 20px; margin: 20px; color: white; font-size: 16px;">';
echo '<h3>🔵 MAIL-IN CHECKOUT FORM 🔵</h3>';
echo '<p><strong>Label:</strong> ' . esc_html( static::get_label() ) . '</p>';
echo '<p><strong>Description:</strong> ' . esc_html( static::get_description() ) . '</p>';
echo '<p><strong>Payable To:</strong> ' . esc_html( $payable_to ) . '</p>';
echo '<p><strong>Address:</strong> ' . esc_html( $mailing_address ) . '</p>';
echo '</div>';
}
/**
* Process the payment
*
* @since 1.0.0
* @param array $order_data Order data
* @return array Processing result
*/
public function process_payment( $order_data ) {
// For mail-in payments, we create the order in "pending" status
// and provide payment instructions
try {
// Get cart instance
$cart = tribe( \TEC\Tickets\Commerce\Cart::class );
if ( ! $cart->has_items() ) {
return [
'success' => false,
'message' => __( 'Your cart is empty.', 'event-tickets-mail-in' ),
];
}
// Extract purchaser data from order_data
$purchaser = null;
if ( isset( $order_data['purchaser_name'] ) || isset( $order_data['purchaser_email'] ) ) {
$purchaser = [
'purchaser_full_name' => $order_data['purchaser_name'] ?? '',
'purchaser_first_name' => $order_data['purchaser_first_name'] ?? '',
'purchaser_last_name' => $order_data['purchaser_last_name'] ?? '',
'purchaser_email' => $order_data['purchaser_email'] ?? '',
'purchaser_user_id' => $order_data['purchaser_user_id'] ?? null,
];
}
// Create order from current cart using the main Order class (like other gateways)
$order = tribe( \TEC\Tickets\Commerce\Order::class )->create_from_cart( $this, $purchaser );
if ( ! $order ) {
return [
'success' => false,
'message' => __( 'Failed to create order. Please try again.', 'event-tickets-mail-in' ),
];
}
// Set gateway order ID so success page can find the order
update_post_meta( $order->ID, '_tec_tc_order_gateway_order_id', $order->ID );
// Add mail-in specific metadata
$this->add_mail_in_meta( $order );
// Set order to pending status (waiting for check to arrive)
tribe( \TEC\Tickets\Commerce\Order::class )->modify_status( $order->ID, Status\Pending::SLUG );
// Clear the cart
$cart->clear_cart();
return [
'success' => true,
'order_id' => $order->ID,
'redirect_url' => $this->get_success_url( $order ),
'message' => __( 'Order created successfully. Please mail your check as instructed.', 'event-tickets-mail-in' ),
];
} catch ( \Exception $e ) {
return [
'success' => false,
'message' => sprintf(
__( 'Payment processing failed: %s', 'event-tickets-mail-in' ),
$e->getMessage()
),
];
}
}
/**
* Create order for mail-in payment
*
* @since 1.0.0
* @param array $order_data Order data (contains purchaser info)
* @return \WP_Post|false
*/
protected function create_order( $order_data ) {
// Extract purchaser data from order_data
$purchaser = null;
if ( isset( $order_data['purchaser_name'] ) || isset( $order_data['purchaser_email'] ) ) {
$purchaser = [
'purchaser_full_name' => $order_data['purchaser_name'] ?? '',
'purchaser_first_name' => $order_data['purchaser_first_name'] ?? '',
'purchaser_last_name' => $order_data['purchaser_last_name'] ?? '',
'purchaser_email' => $order_data['purchaser_email'] ?? '',
'purchaser_user_id' => $order_data['purchaser_user_id'] ?? null,
];
}
// Use the main Order class like other gateways
$order = tribe( \TEC\Tickets\Commerce\Order::class )->create_from_cart( $this, $purchaser );
if ( $order ) {
$this->add_mail_in_meta( $order );
}
return $order;
}
/**
* Add mail-in payment specific metadata
*
* @since 1.0.0
* @param \WP_Post $order
* @return void
*/
protected function add_mail_in_meta( $order ) {
$order_id = $order->ID;
// Store gateway identifier
update_post_meta( $order_id, '_tec_tc_gateway', 'mail-in' );
// Store payment method
update_post_meta( $order_id, '_tec_tc_payment_method', 'mail-in-check' );
// Store mailing instructions
$instructions = tribe_get_option( 'tickets-commerce-mail-in-instructions', '' );
if ( $instructions ) {
update_post_meta( $order_id, '_mail_in_instructions', $instructions );
}
// Store mailing address
$address = tribe_get_option( 'tickets-commerce-mail-in-address', '' );
if ( $address ) {
update_post_meta( $order_id, '_mail_in_mailing_address', $address );
}
// Store payable to
$payable_to = tribe_get_option( 'tickets-commerce-mail-in-payable-to', '' );
if ( $payable_to ) {
update_post_meta( $order_id, '_mail_in_payable_to', $payable_to );
}
// Set check received flag to false initially
update_post_meta( $order_id, '_mail_in_check_received', false );
// Store creation date for tracking purposes
update_post_meta( $order_id, '_mail_in_created_date', current_time( 'mysql' ) );
// Add order note with payment instructions
$mailing_address = tribe_get_option( 'tickets-commerce-mail-in-address', '' );
$payable_to_name = tribe_get_option( 'tickets-commerce-mail-in-payable-to', '' );
$note = __( 'Order created for mail-in payment. Customer should mail check to:', 'event-tickets-mail-in' ) . "\n";
$note .= $mailing_address . "\n";
$note .= __( 'Make payable to:', 'event-tickets-mail-in' ) . ' ' . $payable_to_name;
$this->add_order_note( $order_id, $note );
}
/**
* Add order note
*
* @since 1.0.0
* @param int $order_id Order ID
* @param string $note Note content
* @return void
*/
protected function add_order_note( $order_id, $note ) {
$existing_notes = get_post_meta( $order_id, '_tec_tc_order_notes', true );
if ( ! is_array( $existing_notes ) ) {
$existing_notes = [];
}
$existing_notes[] = [
'date' => current_time( 'mysql' ),
'note' => $note,
'type' => 'system',
];
update_post_meta( $order_id, '_tec_tc_order_notes', $existing_notes );
}
/**
* Generate unique order ID for mail-in payments
*
* @since 1.0.0
* @return string
*/
protected function generate_order_id() {
return 'mail-in-' . uniqid() . '-' . time();
}
/**
* Get success page URL
*
* @since 1.0.0
* @param \WP_Post $order
* @return string
*/
protected function get_success_url( $order ) {
return add_query_arg( [ 'tc-order-id' => $order->ID ], tribe( \TEC\Tickets\Commerce\Success::class )->get_url() );
}
/**
* Handle refunds (not applicable for mail-in payments)
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order
* @param Value $amount
* @return array
*/
public function process_refund( $order, Value $amount ) {
return [
'success' => false,
'message' => __( 'Refunds for mail-in payments must be processed manually.', 'event-tickets-mail-in' ),
];
}
/**
* Get checkout template object
*
* @since 1.0.0
* @return \Tribe__Template
*/
protected function get_template() {
return new \Tribe__Template();
}
/**
* Validate checkout data
*
* @since 1.0.0
* @param array $data Checkout data
* @return bool|\WP_Error
*/
public function validate_checkout_data( $data ) {
// Basic validation - ensure required fields are present
if ( empty( $data['purchaser_email'] ) ) {
return new \WP_Error( 'missing_email', __( 'Email address is required.', 'event-tickets-mail-in' ) );
}
if ( empty( $data['purchaser_name'] ) ) {
return new \WP_Error( 'missing_name', __( 'Name is required.', 'event-tickets-mail-in' ) );
}
return true;
}
/**
* Get gateway-specific order notes
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order
* @return array
*/
public function get_order_notes( $order ) {
$notes = [];
$notes[] = sprintf(
__( 'Mail-in payment order created. Order ID: %s', 'event-tickets-mail-in' ),
$order->get_gateway_order_id()
);
$mailing_address = get_option( 'et_mail_in_mailing_address', '' );
$payable_to = get_option( 'et_mail_in_check_payable_to', '' );
if ( $mailing_address ) {
$notes[] = sprintf(
__( 'Check should be mailed to: %s', 'event-tickets-mail-in' ),
$mailing_address
);
}
if ( $payable_to ) {
$notes[] = sprintf(
__( 'Check should be made payable to: %s', 'event-tickets-mail-in' ),
$payable_to
);
}
return $notes;
}
/**
* Get admin notices for the gateway
*
* @since 1.0.0
* @return array
*/
public function get_admin_notices() {
$notices = [];
// Check if gateway is properly configured
if ( ! self::is_connected() ) {
$notices[] = [
'type' => 'warning',
'message' => sprintf(
__( 'Mail-In Payment gateway is not properly configured. Please <a href="%s">configure the required settings</a>.', 'event-tickets-mail-in' ),
self::get_settings_url()
),
];
}
return $notices;
}
/**
* Get order details link by order
*
* @since 1.0.0
* @param \WP_Post $order Order post object
* @return string
*/
public function get_order_details_link_by_order( $order ): string {
// For mail-in payments, return the order ID as a simple string
return (string) $order->ID;
}
/**
* Render checkout template
*
* @since 1.0.0
* @param \Tribe__Template $template Template instance
* @return string
*/
public function render_checkout_template( \Tribe__Template $template ): string {
$instructions = tribe_get_option( 'tickets-commerce-mail-in-instructions', '' );
$mailing_address = tribe_get_option( 'tickets-commerce-mail-in-address', '' );
$payable_to = tribe_get_option( 'tickets-commerce-mail-in-payable-to', '' );
$context = [
'gateway' => $this,
'instructions' => $instructions,
'mailing_address' => $mailing_address,
'payable_to' => $payable_to,
];
// Use our template file
ob_start();
$template_file = ET_MAIL_IN_PATH . 'templates/v2/commerce/gateway/mail-in/container.php';
if ( file_exists( $template_file ) ) {
// Extract context variables for the template
extract( $context );
include $template_file;
} else {
// Fallback display if template file is missing
echo '<div class="tribe-tickets-commerce-gateway-mail-in">';
echo '<h4>' . esc_html( static::get_label() ) . '</h4>';
echo '<p>' . esc_html( static::get_description() ) . '</p>';
if ( $mailing_address ) {
echo '<p><strong>' . esc_html__( 'Mail your check to:', 'event-tickets-mail-in' ) . '</strong><br>';
echo esc_html( $mailing_address ) . '</p>';
}
if ( $payable_to ) {
echo '<p><strong>' . esc_html__( 'Make check payable to:', 'event-tickets-mail-in' ) . '</strong><br>';
echo esc_html( $payable_to ) . '</p>';
}
echo '</div>';
}
$output = ob_get_clean();
// Output directly to handle any buffering issues with the shortcode
echo $output;
return $output;
}
}

405
src/Hooks.php Normal file
View file

@ -0,0 +1,405 @@
<?php
namespace ET_Mail_In_Payment;
/**
* Mail-In Payment Hooks class
*
* @since 1.0.0
*/
class Hooks {
/**
* Register hooks
*
* @since 1.0.0
* @return void
*/
public function register() {
// Template hooks
add_filter( 'tec_tickets_commerce_checkout_template_paths', [ $this, 'add_template_paths' ] );
// Email hooks
add_action( 'tec_tickets_commerce_order_created', [ $this, 'send_payment_instructions' ], 10, 2 );
// Order status hooks
add_action( 'tec_tickets_commerce_order_status_changed', [ $this, 'handle_status_change' ], 10, 3 );
// Cleanup hooks for expired orders
add_action( 'init', [ $this, 'schedule_cleanup' ] );
add_action( 'et_mail_in_cleanup_expired_orders', [ $this, 'cleanup_expired_orders' ] );
// Admin hooks
add_filter( 'tec_tickets_commerce_order_actions', [ $this, 'add_order_actions' ], 10, 2 );
// Gateway display hooks
add_filter( 'tec_tickets_commerce_gateway_checkout_template', [ $this, 'get_checkout_template' ], 10, 2 );
}
/**
* Add template paths for mail-in payment templates
*
* @since 1.0.0
* @param array $paths Template paths
* @return array
*/
public function add_template_paths( $paths ) {
$paths[] = ET_MAIL_IN_PATH . 'templates/';
return $paths;
}
/**
* Send payment instructions email when order is created
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order Order object
* @param array $cart_data Cart data
* @return void
*/
public function send_payment_instructions( $order, $cart_data ) {
// Only for mail-in payment orders
if ( $order->get_gateway() !== 'mail-in' ) {
return;
}
$this->send_instructions_email( $order );
}
/**
* Handle order status changes
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order Order object
* @param string $old_status Previous status
* @param string $new_status New status
* @return void
*/
public function handle_status_change( $order, $old_status, $new_status ) {
// Only for mail-in payment orders
if ( $order->get_gateway() !== 'mail-in' ) {
return;
}
// Send confirmation email when payment is completed
if ( $new_status === 'completed' ) {
$this->send_confirmation_email( $order );
}
// Send cancellation email if order is cancelled
if ( in_array( $new_status, [ 'denied', 'cancelled', 'refunded' ] ) ) {
$this->send_cancellation_email( $order );
}
}
/**
* Schedule cleanup of expired orders
*
* @since 1.0.0
* @return void
*/
public function schedule_cleanup() {
if ( ! wp_next_scheduled( 'et_mail_in_cleanup_expired_orders' ) ) {
wp_schedule_event( time(), 'daily', 'et_mail_in_cleanup_expired_orders' );
}
}
/**
* Cleanup expired mail-in orders
*
* @since 1.0.0
* @return void
*/
public function cleanup_expired_orders() {
// Only run if auto-cancel is enabled
if ( ! tribe_get_option( 'tickets-commerce-mail-in-auto-cancel', false ) ) {
return;
}
$reservation_days = absint( tribe_get_option( 'tickets-commerce-mail-in-reservation-days', 7 ) );
$cutoff_date = date( 'Y-m-d H:i:s', strtotime( "-{$reservation_days} days" ) );
$args = [
'post_type' => 'tec_tc_order',
'meta_query' => [
[
'key' => '_tec_tc_gateway',
'value' => 'mail-in',
'compare' => '='
],
[
'key' => '_mail_in_check_received',
'value' => false,
'compare' => '='
],
[
'key' => '_mail_in_created_date',
'value' => $cutoff_date,
'compare' => '<',
'type' => 'DATETIME'
]
],
'post_status' => [ 'tec-tc-pending', 'tec-tc-approved' ],
'posts_per_page' => 50, // Process in batches
];
$expired_orders = new \WP_Query( $args );
$order_controller = new Order();
foreach ( $expired_orders->posts as $order_post ) {
$order = tribe( 'tickets.commerce.order' )->get_by_id( $order_post->ID );
if ( $order ) {
// Cancel the order
$order->update_status( 'denied' );
// Add order note
$order_controller->add_order_note(
$order_post->ID,
__( 'Order automatically cancelled due to expired payment deadline.', 'event-tickets-mail-in' )
);
}
}
}
/**
* Add custom actions to order admin page
*
* @since 1.0.0
* @param array $actions Existing actions
* @param \TEC\Tickets\Commerce\Order $order Order object
* @return array
*/
public function add_order_actions( $actions, $order ) {
// Only for mail-in payment orders
if ( $order->get_gateway() !== 'mail-in' ) {
return $actions;
}
$order_controller = new Order();
$details = $order_controller->get_mail_in_details( $order->get_id() );
// Add "Mark Check Received" action if check hasn't been received
if ( ! $details['check_received'] && ! $details['check_bounced'] ) {
$actions['mark_check_received'] = [
'label' => __( 'Mark Check Received', 'event-tickets-mail-in' ),
'url' => wp_nonce_url(
admin_url( 'admin-post.php?action=mark_check_received&order_id=' . $order->get_id() ),
'et_mail_in_admin'
),
'class' => 'button-secondary',
];
$actions['mark_check_bounced'] = [
'label' => __( 'Mark Check Bounced', 'event-tickets-mail-in' ),
'url' => wp_nonce_url(
admin_url( 'admin-post.php?action=mark_check_bounced&order_id=' . $order->get_id() ),
'et_mail_in_admin'
),
'class' => 'button-secondary',
];
}
return $actions;
}
/**
* Get checkout template for mail-in payment
*
* @since 1.0.0
* @param string $template Template name
* @param string $gateway Gateway key
* @return string
*/
public function get_checkout_template( $template, $gateway ) {
if ( $gateway === 'mail-in' ) {
return 'checkout/mail-in-form';
}
return $template;
}
/**
* Send payment instructions email
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order Order object
* @return void
*/
protected function send_instructions_email( $order ) {
$purchaser_email = $order->get_purchaser_email();
if ( ! $purchaser_email ) {
return;
}
$subject = sprintf(
__( 'Payment Instructions for Order #%s', 'event-tickets-mail-in' ),
$order->get_order_number()
);
$order_controller = new Order();
$details = $order_controller->get_mail_in_details( $order->get_id() );
$message = $this->get_email_template( 'payment-instructions', [
'order' => $order,
'details' => $details,
] );
wp_mail( $purchaser_email, $subject, $message, $this->get_email_headers() );
}
/**
* Send payment confirmation email
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order Order object
* @return void
*/
protected function send_confirmation_email( $order ) {
$purchaser_email = $order->get_purchaser_email();
if ( ! $purchaser_email ) {
return;
}
$subject = sprintf(
__( 'Payment Confirmed for Order #%s', 'event-tickets-mail-in' ),
$order->get_order_number()
);
$message = $this->get_email_template( 'payment-confirmation', [
'order' => $order,
] );
wp_mail( $purchaser_email, $subject, $message, $this->get_email_headers() );
}
/**
* Send order cancellation email
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order Order object
* @return void
*/
protected function send_cancellation_email( $order ) {
$purchaser_email = $order->get_purchaser_email();
if ( ! $purchaser_email ) {
return;
}
$subject = sprintf(
__( 'Order Cancelled: #%s', 'event-tickets-mail-in' ),
$order->get_order_number()
);
$message = $this->get_email_template( 'order-cancelled', [
'order' => $order,
] );
wp_mail( $purchaser_email, $subject, $message, $this->get_email_headers() );
}
/**
* Get email template content
*
* @since 1.0.0
* @param string $template Template name
* @param array $args Template arguments
* @return string
*/
protected function get_email_template( $template, $args = [] ) {
$template_file = ET_MAIL_IN_PATH . "templates/emails/{$template}.php";
if ( ! file_exists( $template_file ) ) {
return $this->get_fallback_email_content( $template, $args );
}
ob_start();
extract( $args );
include $template_file;
return ob_get_clean();
}
/**
* Get fallback email content
*
* @since 1.0.0
* @param string $template Template name
* @param array $args Template arguments
* @return string
*/
protected function get_fallback_email_content( $template, $args ) {
switch ( $template ) {
case 'payment-instructions':
return $this->get_instructions_fallback( $args );
case 'payment-confirmation':
return $this->get_confirmation_fallback( $args );
case 'order-cancelled':
return $this->get_cancellation_fallback( $args );
default:
return '';
}
}
/**
* Get payment instructions fallback content
*
* @since 1.0.0
* @param array $args Template arguments
* @return string
*/
protected function get_instructions_fallback( $args ) {
$order = $args['order'];
$details = $args['details'];
$content = sprintf( __( 'Thank you for your order #%s!', 'event-tickets-mail-in' ), $order->get_order_number() ) . "\n\n";
$content .= $details['instructions'] . "\n\n";
$content .= sprintf( __( 'Mailing Address:\n%s', 'event-tickets-mail-in' ), $details['mailing_address'] ) . "\n\n";
$content .= sprintf( __( 'Make Check Payable To: %s', 'event-tickets-mail-in' ), $details['payable_to'] ) . "\n\n";
$content .= sprintf( __( 'Order Total: %s', 'event-tickets-mail-in' ), $order->get_total_value()->get_formatted() ) . "\n";
return $content;
}
/**
* Get confirmation fallback content
*
* @since 1.0.0
* @param array $args Template arguments
* @return string
*/
protected function get_confirmation_fallback( $args ) {
$order = $args['order'];
return sprintf(
__( 'Your payment for order #%s has been received and processed. Your tickets are now confirmed!', 'event-tickets-mail-in' ),
$order->get_order_number()
);
}
/**
* Get cancellation fallback content
*
* @since 1.0.0
* @param array $args Template arguments
* @return string
*/
protected function get_cancellation_fallback( $args ) {
$order = $args['order'];
return sprintf(
__( 'Your order #%s has been cancelled. If you believe this is an error, please contact us.', 'event-tickets-mail-in' ),
$order->get_order_number()
);
}
/**
* Get email headers
*
* @since 1.0.0
* @return array
*/
protected function get_email_headers() {
return [
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_option( 'blogname' ) . ' <' . get_option( 'admin_email' ) . '>',
];
}
}

251
src/Order.php Normal file
View file

@ -0,0 +1,251 @@
<?php
namespace ET_Mail_In_Payment;
use TEC\Tickets\Commerce\Abstract_Order;
use TEC\Tickets\Commerce\Order as Commerce_Order;
use TEC\Tickets\Commerce\Status;
/**
* Mail-In Payment Order class
*
* @since 1.0.0
*/
class Order extends Abstract_Order {
/**
* Create order with mail-in payment gateway
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Gateways\Contracts\Gateway_Interface $gateway Gateway instance
* @param array|null $purchaser Purchaser data
* @return \TEC\Tickets\Commerce\Order|false
*/
public function create_from_gateway( \TEC\Tickets\Commerce\Gateways\Contracts\Gateway_Interface $gateway, $purchaser = null ) {
// Use the main Commerce Order class to create the order
$order = tribe( \TEC\Tickets\Commerce\Order::class )->create_from_cart( $gateway, $purchaser );
if ( ! $order ) {
return false;
}
// Add mail-in specific meta data
$this->add_mail_in_meta( $order );
return $order;
}
/**
* Add mail-in payment specific metadata
*
* @since 1.0.0
* @param \TEC\Tickets\Commerce\Order $order
* @return void
*/
protected function add_mail_in_meta( $order ) {
$order_id = $order->get_id();
// Store payment method
update_post_meta( $order_id, '_tec_tc_payment_method', 'mail-in-check' );
// Store mailing instructions
$instructions = get_option( 'et_mail_in_instructions', '' );
if ( $instructions ) {
update_post_meta( $order_id, '_mail_in_instructions', $instructions );
}
// Store mailing address
$address = get_option( 'et_mail_in_mailing_address', '' );
if ( $address ) {
update_post_meta( $order_id, '_mail_in_mailing_address', $address );
}
// Store payable to
$payable_to = get_option( 'et_mail_in_check_payable_to', '' );
if ( $payable_to ) {
update_post_meta( $order_id, '_mail_in_payable_to', $payable_to );
}
// Set check received flag to false initially
update_post_meta( $order_id, '_mail_in_check_received', false );
// Store creation date for tracking purposes
update_post_meta( $order_id, '_mail_in_created_date', current_time( 'mysql' ) );
// Add order note
$this->add_order_note(
$order_id,
__( 'Mail-in payment order created. Awaiting check payment.', 'event-tickets-mail-in' )
);
}
/**
* Mark check as received
*
* @since 1.0.0
* @param int $order_id Order ID
* @param string $notes Optional notes about the received check
* @return bool
*/
public function mark_check_received( $order_id, $notes = '' ) {
$order = $this->get_order( $order_id );
if ( ! $order ) {
return false;
}
// Update check received status
update_post_meta( $order_id, '_mail_in_check_received', true );
update_post_meta( $order_id, '_mail_in_check_received_date', current_time( 'mysql' ) );
// Add notes if provided
if ( $notes ) {
update_post_meta( $order_id, '_mail_in_check_notes', $notes );
}
// Update order status to completed
tribe( \TEC\Tickets\Commerce\Order::class )->modify_status( $order_id, Status\Completed::SLUG );
// Add order note
$note = __( 'Check payment received and processed.', 'event-tickets-mail-in' );
if ( $notes ) {
$note .= ' ' . sprintf( __( 'Notes: %s', 'event-tickets-mail-in' ), $notes );
}
$this->add_order_note( $order_id, $note );
return true;
}
/**
* Mark check as bounced/failed
*
* @since 1.0.0
* @param int $order_id Order ID
* @param string $reason Reason for bounce/failure
* @return bool
*/
public function mark_check_bounced( $order_id, $reason = '' ) {
$order = $this->get_order( $order_id );
if ( ! $order ) {
return false;
}
// Update order status to denied
tribe( \TEC\Tickets\Commerce\Order::class )->modify_status( $order_id, Status\Denied::SLUG );
// Add bounce information
update_post_meta( $order_id, '_mail_in_check_bounced', true );
update_post_meta( $order_id, '_mail_in_check_bounce_date', current_time( 'mysql' ) );
if ( $reason ) {
update_post_meta( $order_id, '_mail_in_bounce_reason', $reason );
}
// Add order note
$note = __( 'Check payment bounced/failed.', 'event-tickets-mail-in' );
if ( $reason ) {
$note .= ' ' . sprintf( __( 'Reason: %s', 'event-tickets-mail-in' ), $reason );
}
$this->add_order_note( $order_id, $note );
return true;
}
/**
* Get mail-in payment details for an order
*
* @since 1.0.0
* @param int $order_id Order ID
* @return array
*/
public function get_mail_in_details( $order_id ) {
return [
'instructions' => get_post_meta( $order_id, '_mail_in_instructions', true ),
'mailing_address' => get_post_meta( $order_id, '_mail_in_mailing_address', true ),
'payable_to' => get_post_meta( $order_id, '_mail_in_payable_to', true ),
'check_received' => get_post_meta( $order_id, '_mail_in_check_received', true ),
'check_received_date' => get_post_meta( $order_id, '_mail_in_check_received_date', true ),
'check_notes' => get_post_meta( $order_id, '_mail_in_check_notes', true ),
'check_bounced' => get_post_meta( $order_id, '_mail_in_check_bounced', true ),
'bounce_reason' => get_post_meta( $order_id, '_mail_in_bounce_reason', true ),
'bounce_date' => get_post_meta( $order_id, '_mail_in_check_bounce_date', true ),
'created_date' => get_post_meta( $order_id, '_mail_in_created_date', true ),
];
}
/**
* Add order note
*
* @since 1.0.0
* @param int $order_id Order ID
* @param string $note Note content
* @return void
*/
public function add_order_note( $order_id, $note ) {
$existing_notes = get_post_meta( $order_id, '_tec_tc_order_notes', true );
if ( ! is_array( $existing_notes ) ) {
$existing_notes = [];
}
$existing_notes[] = [
'date' => current_time( 'mysql' ),
'note' => $note,
'type' => 'system',
];
update_post_meta( $order_id, '_tec_tc_order_notes', $existing_notes );
}
/**
* Get order by ID
*
* @since 1.0.0
* @param int $order_id Order ID
* @return \WP_Post|false
*/
protected function get_order( $order_id ) {
$order = get_post( $order_id );
// Verify this is actually an order post
if ( ! $order || $order->post_type !== 'tec_tc_order' ) {
return false;
}
return $order;
}
/**
* Get pending mail-in orders (for admin management)
*
* @since 1.0.0
* @param array $args Query arguments
* @return \WP_Query
*/
public function get_pending_orders( $args = [] ) {
$default_args = [
'post_type' => 'tec_tc_order',
'meta_query' => [
[
'key' => '_tec_tc_gateway',
'value' => 'mail-in',
'compare' => '='
],
[
'key' => '_mail_in_check_received',
'value' => false,
'compare' => '='
]
],
'post_status' => [ 'tec-tc-pending', 'tec-tc-approved' ],
'orderby' => 'date',
'order' => 'DESC',
];
$args = wp_parse_args( $args, $default_args );
return new \WP_Query( $args );
}
}

442
src/Provider.php Normal file
View file

@ -0,0 +1,442 @@
<?php
namespace ET_Mail_In_Payment;
use TEC\Common\Contracts\Service_Provider;
/**
* Mail-In Payment Service Provider
*
* @since 1.0.0
*/
class Provider extends Service_Provider {
/**
* Register services
*
* @since 1.0.0
* @return void
*/
public function register() {
// Register gateway with Commerce module - match PayPal's implementation
add_filter( 'tec_tickets_commerce_gateways', [ $this, 'filter_add_gateway' ], 10, 2 );
// Register hooks
$this->register_hooks();
// Register admin features
$this->register_admin_features();
// Register form processing
$this->register_form_processing();
// Register frontend assets
$this->register_frontend_assets();
}
/**
* Add this gateway to the list of available gateways
*
* @since 1.0.0
* @param array $gateways Existing gateways
* @return array
*/
public function filter_add_gateway( array $gateways = [] ) {
// Create our gateway instance and register it
return $this->container->make( Gateway::class )->register_gateway( $gateways );
}
/**
* Register hooks and filters
*
* @since 1.0.0
* @return void
*/
protected function register_hooks() {
$hooks = new Hooks();
$hooks->register();
}
/**
* Register admin features
*
* @since 1.0.0
* @return void
*/
protected function register_admin_features() {
if ( ! is_admin() ) {
return;
}
// Add admin menu for managing mail-in orders
add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
// Add admin scripts and styles
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
// Add order meta box for mail-in payments
add_action( 'add_meta_boxes', [ $this, 'add_order_meta_box' ] );
// Handle admin actions
add_action( 'admin_post_mark_check_received', [ $this, 'handle_mark_check_received' ] );
add_action( 'admin_post_mark_check_bounced', [ $this, 'handle_mark_check_bounced' ] );
}
/**
* Add admin menu for mail-in order management
*
* @since 1.0.0
* @return void
*/
public function add_admin_menu() {
add_submenu_page(
'edit.php?post_type=tec_tc_order',
__( 'Mail-In Payments', 'event-tickets-mail-in' ),
__( 'Mail-In Payments', 'event-tickets-mail-in' ),
'manage_options',
'mail-in-payments',
[ $this, 'admin_page' ]
);
}
/**
* Display admin page for managing mail-in payments
*
* @since 1.0.0
* @return void
*/
public function admin_page() {
$order_controller = new Order();
$pending_orders = $order_controller->get_pending_orders();
include ET_MAIL_IN_PATH . 'templates/admin/mail-in-orders.php';
}
/**
* Enqueue admin scripts and styles
*
* @since 1.0.0
* @param string $hook Current admin page hook
* @return void
*/
public function admin_scripts( $hook ) {
// Only load on relevant admin pages
if ( ! in_array( $hook, [ 'edit.php', 'post.php', 'tickets_page_mail-in-payments' ] ) ) {
return;
}
wp_enqueue_style(
'et-mail-in-admin',
ET_MAIL_IN_URL . 'assets/css/admin.css',
[],
ET_MAIL_IN_VERSION
);
wp_enqueue_script(
'et-mail-in-admin',
ET_MAIL_IN_URL . 'assets/js/admin.js',
[ 'jquery' ],
ET_MAIL_IN_VERSION,
true
);
wp_localize_script( 'et-mail-in-admin', 'etMailInAdmin', [
'nonce' => wp_create_nonce( 'et_mail_in_admin' ),
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'strings' => [
'confirm_received' => __( 'Are you sure you want to mark this check as received?', 'event-tickets-mail-in' ),
'confirm_bounced' => __( 'Are you sure you want to mark this check as bounced?', 'event-tickets-mail-in' ),
],
] );
}
/**
* Add meta box to order edit screen
*
* @since 1.0.0
* @return void
*/
public function add_order_meta_box() {
global $post;
// Only add to mail-in orders
if ( get_post_meta( $post->ID, '_tec_tc_gateway', true ) !== 'mail-in' ) {
return;
}
add_meta_box(
'mail-in-payment-details',
__( 'Mail-In Payment Details', 'event-tickets-mail-in' ),
[ $this, 'order_meta_box_callback' ],
'tec_tc_order',
'normal',
'high'
);
}
/**
* Render order meta box
*
* @since 1.0.0
* @param \WP_Post $post Order post object
* @return void
*/
public function order_meta_box_callback( $post ) {
$order_controller = new Order();
$details = $order_controller->get_mail_in_details( $post->ID );
include ET_MAIL_IN_PATH . 'templates/admin/order-meta-box.php';
}
/**
* Handle marking check as received
*
* @since 1.0.0
* @return void
*/
public function handle_mark_check_received() {
// Verify nonce
if ( ! wp_verify_nonce( $_POST['nonce'] ?? '', 'et_mail_in_admin' ) ) {
wp_die( __( 'Security check failed.', 'event-tickets-mail-in' ) );
}
// Check permissions
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( __( 'You do not have permission to perform this action.', 'event-tickets-mail-in' ) );
}
$order_id = absint( $_POST['order_id'] ?? 0 );
$notes = sanitize_textarea_field( $_POST['notes'] ?? '' );
if ( ! $order_id ) {
wp_die( __( 'Invalid order ID.', 'event-tickets-mail-in' ) );
}
$order_controller = new Order();
$result = $order_controller->mark_check_received( $order_id, $notes );
if ( $result ) {
add_action( 'admin_notices', function() {
echo '<div class="notice notice-success is-dismissible"><p>' .
esc_html__( 'Check marked as received successfully.', 'event-tickets-mail-in' ) .
'</p></div>';
} );
} else {
add_action( 'admin_notices', function() {
echo '<div class="notice notice-error is-dismissible"><p>' .
esc_html__( 'Failed to mark check as received.', 'event-tickets-mail-in' ) .
'</p></div>';
} );
}
// Redirect back
$redirect_url = $_POST['redirect_url'] ?? admin_url( 'edit.php?post_type=tec_tc_order&page=mail-in-payments' );
wp_redirect( $redirect_url );
exit;
}
/**
* Handle marking check as bounced
*
* @since 1.0.0
* @return void
*/
public function handle_mark_check_bounced() {
// Verify nonce
if ( ! wp_verify_nonce( $_POST['nonce'] ?? '', 'et_mail_in_admin' ) ) {
wp_die( __( 'Security check failed.', 'event-tickets-mail-in' ) );
}
// Check permissions
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( __( 'You do not have permission to perform this action.', 'event-tickets-mail-in' ) );
}
$order_id = absint( $_POST['order_id'] ?? 0 );
$reason = sanitize_textarea_field( $_POST['reason'] ?? '' );
if ( ! $order_id ) {
wp_die( __( 'Invalid order ID.', 'event-tickets-mail-in' ) );
}
$order_controller = new Order();
$result = $order_controller->mark_check_bounced( $order_id, $reason );
if ( $result ) {
add_action( 'admin_notices', function() {
echo '<div class="notice notice-success is-dismissible"><p>' .
esc_html__( 'Check marked as bounced successfully.', 'event-tickets-mail-in' ) .
'</p></div>';
} );
} else {
add_action( 'admin_notices', function() {
echo '<div class="notice notice-error is-dismissible"><p>' .
esc_html__( 'Failed to mark check as bounced.', 'event-tickets-mail-in' ) .
'</p></div>';
} );
}
// Redirect back
$redirect_url = $_POST['redirect_url'] ?? admin_url( 'edit.php?post_type=tec_tc_order&page=mail-in-payments' );
wp_redirect( $redirect_url );
exit;
}
/**
* Register form processing
*
* @since 1.0.0
* @return void
*/
protected function register_form_processing() {
// Handle checkout form submission
add_action( 'template_redirect', [ $this, 'handle_checkout_form' ] );
}
/**
* Handle checkout form submission
*
* @since 1.0.0
* @return void
*/
public function handle_checkout_form() {
// Check if this is our gateway form submission
if ( empty( $_POST['tec_tc_gateway'] ) || $_POST['tec_tc_gateway'] !== 'mail-in' ) {
return;
}
// Verify nonce
if ( ! wp_verify_nonce( $_POST['tec_tickets_commerce_checkout_nonce'] ?? '', 'tec_tickets_commerce_checkout' ) ) {
wp_die( __( 'Security check failed.', 'event-tickets-mail-in' ) );
}
try {
// Get cart and process order directly
if ( ! class_exists( 'TEC\Tickets\Commerce\Module' ) ) {
wp_die( __( 'Commerce module not available.', 'event-tickets-mail-in' ) );
}
// Get cart instance
$cart = tribe( \TEC\Tickets\Commerce\Cart::class );
// Check if cart has items
if ( ! $cart->has_items() ) {
wp_redirect( add_query_arg( 'tec_tc_error', urlencode( __( 'Your cart is empty.', 'event-tickets-mail-in' ) ), wp_get_referer() ) );
exit;
}
// Get purchaser data from form
$purchaser_data = [
'purchaser_name' => sanitize_text_field( $_POST['purchaser_name'] ?? '' ),
'purchaser_email' => sanitize_email( $_POST['purchaser_email'] ?? '' ),
];
// Validate required fields
if ( empty( $purchaser_data['purchaser_name'] ) ) {
wp_redirect( add_query_arg( 'tec_tc_error', urlencode( __( 'Full name is required.', 'event-tickets-mail-in' ) ), wp_get_referer() ) );
exit;
}
if ( empty( $purchaser_data['purchaser_email'] ) ) {
wp_redirect( add_query_arg( 'tec_tc_error', urlencode( __( 'Email address is required.', 'event-tickets-mail-in' ) ), wp_get_referer() ) );
exit;
}
// Get the Mail-In gateway
$gateway = $this->container->make( Gateway::class );
// Process payment through gateway
$result = $gateway->process_payment( $purchaser_data );
if ( ! $result['success'] ) {
// Handle error
wp_redirect( add_query_arg( 'tec_tc_error', urlencode( $result['message'] ), wp_get_referer() ) );
exit;
}
// Success - redirect to success page
if ( isset( $result['redirect_url'] ) ) {
wp_redirect( $result['redirect_url'] );
exit;
}
} catch ( \Exception $e ) {
wp_redirect( add_query_arg( 'tec_tc_error', urlencode( $e->getMessage() ), wp_get_referer() ) );
exit;
}
}
/**
* Register frontend assets
*
* @since 1.0.0
* @return void
*/
protected function register_frontend_assets() {
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_frontend_assets' ] );
}
/**
* Enqueue frontend assets
*
* @since 1.0.0
* @return void
*/
public function enqueue_frontend_assets() {
// Only load on pages that might have the checkout shortcode
if ( ! $this->should_load_frontend_assets() ) {
return;
}
wp_enqueue_style(
'et-mail-in-checkout',
ET_MAIL_IN_URL . 'assets/css/checkout.css',
[],
ET_MAIL_IN_VERSION
);
wp_enqueue_script(
'et-mail-in-checkout',
ET_MAIL_IN_URL . 'assets/js/checkout.js',
[],
ET_MAIL_IN_VERSION,
true
);
}
/**
* Check if we should load frontend assets
*
* @since 1.0.0
* @return bool
*/
protected function should_load_frontend_assets() {
global $post;
// Load on posts/pages with checkout shortcode
if ( $post && has_shortcode( $post->post_content, 'tec_tickets_checkout' ) ) {
return true;
}
// Load on checkout pages (if using different URL structure)
if ( is_page() && (
strpos( get_the_title(), 'checkout' ) !== false ||
strpos( get_the_title(), 'Checkout' ) !== false
) ) {
return true;
}
// Load on pages that might contain Event Tickets Commerce elements
if ( is_page() && class_exists( 'TEC\Tickets\Commerce\Module' ) ) {
// Check if page content contains commerce-related content
if ( $post && (
strpos( $post->post_content, 'tec_tickets' ) !== false ||
strpos( $post->post_content, 'tribe_events' ) !== false
) ) {
return true;
}
}
return false;
}
}

410
src/Settings.php Normal file
View file

@ -0,0 +1,410 @@
<?php
namespace ET_Mail_In_Payment;
use TEC\Tickets\Commerce\Gateways\Contracts\Abstract_Settings;
use Tribe__Tickets__Main;
/**
* Mail-In Payment Settings class
*
* @since 1.0.0
*/
class Settings extends Abstract_Settings {
/**
* @inheritDoc
*/
public static $option_sandbox = 'et-mail-in-sandbox';
/**
* Settings option prefix
*
* @since 1.0.0
* @var string
*/
protected static string $option_prefix = 'et_mail_in_';
/**
* Get the list of settings for the gateway
*
* @since 1.0.0
* @return array The list of settings for the gateway
*/
public function get_settings() {
// Return the actual settings fields that integrate with Tribe's settings system
return $this->get_settings_fields();
}
/**
* Get the HTML for the connection box in the admin
*
* @since 1.0.0
* @return string
*/
public function get_connection_settings_html() {
$status = $this->get_connection_status();
$html = '<div class="tec-settings-form__element">';
$html .= '<h3>' . esc_html__( 'Mail-In Payment (Check)', 'event-tickets-mail-in' ) . '</h3>';
$html .= '<p class="description">' . esc_html( $status['message'] ) . '</p>';
if ( ! $status['is_active'] ) {
$html .= '<p class="description">' . esc_html__( 'Configure the settings below to enable this payment method.', 'event-tickets-mail-in' ) . '</p>';
}
$html .= '</div>';
return $html;
}
/**
* Get settings fields for internal use
*
* @since 1.0.0
* @return array
*/
public function get_settings_fields() {
$fields = [
'mail_in_section' => [
'type' => 'html',
'html' => '<h3>' . __( 'Mail-In Payment (Check) Settings', 'event-tickets-mail-in' ) . '</h3>',
],
'tickets-commerce-mail-in-enabled' => [
'type' => 'checkbox_bool',
'label' => __( 'Enable Mail-In Payments', 'event-tickets-mail-in' ),
'tooltip' => __( 'Enable this payment method to allow customers to pay by mailing checks.', 'event-tickets-mail-in' ),
'default' => false,
'validation_type' => 'boolean',
],
'tickets-commerce-mail-in-title' => [
'type' => 'text',
'label' => __( 'Payment Method Title', 'event-tickets-mail-in' ),
'tooltip' => __( 'This is the title customers will see during checkout.', 'event-tickets-mail-in' ),
'default' => __( 'Mail-In Payment (Check)', 'event-tickets-mail-in' ),
'validation_type' => 'html',
'size' => 'large',
],
'tickets-commerce-mail-in-description' => [
'type' => 'textarea',
'label' => __( 'Payment Method Description', 'event-tickets-mail-in' ),
'tooltip' => __( 'Brief description shown to customers during checkout.', 'event-tickets-mail-in' ),
'default' => __( 'Pay by mailing a check to our address.', 'event-tickets-mail-in' ),
'validation_type' => 'html',
'size' => 'large',
],
'tickets-commerce-mail-in-payable-to' => [
'type' => 'text',
'label' => __( 'Make Check Payable To', 'event-tickets-mail-in' ),
'tooltip' => __( 'The name or organization that checks should be made payable to.', 'event-tickets-mail-in' ),
'default' => '',
'validation_type' => 'html',
'size' => 'large',
'required' => true,
],
'tickets-commerce-mail-in-address' => [
'type' => 'textarea',
'label' => __( 'Mailing Address', 'event-tickets-mail-in' ),
'tooltip' => __( 'The complete mailing address where checks should be sent.', 'event-tickets-mail-in' ),
'default' => '',
'validation_type' => 'html',
'size' => 'large',
'required' => true,
],
'tickets-commerce-mail-in-instructions' => [
'type' => 'textarea',
'label' => __( 'Payment Instructions', 'event-tickets-mail-in' ),
'tooltip' => __( 'Detailed instructions for customers on how to complete their payment.', 'event-tickets-mail-in' ),
'default' => __( 'Please mail your check to the address provided below. Your tickets will be reserved for 7 days while we wait for your payment to arrive. Include your order number on the check memo line.', 'event-tickets-mail-in' ),
'validation_type' => 'html',
'size' => 'large',
],
'tickets-commerce-mail-in-reservation-days' => [
'type' => 'text',
'label' => __( 'Reservation Period (Days)', 'event-tickets-mail-in' ),
'tooltip' => __( 'Number of days to hold tickets while waiting for check payment.', 'event-tickets-mail-in' ),
'default' => '7',
'validation_type' => 'int',
'size' => 'small',
],
'tickets-commerce-mail-in-auto-cancel' => [
'type' => 'checkbox_bool',
'label' => __( 'Auto-Cancel Expired Orders', 'event-tickets-mail-in' ),
'tooltip' => __( 'Automatically cancel orders if payment is not received within the reservation period.', 'event-tickets-mail-in' ),
'default' => false,
'validation_type' => 'boolean',
],
];
return $fields;
}
/**
* Get the settings section for the Payments tab
*
* @since 1.0.0
* @return array
*/
public function get_settings_section() {
return [
'mail_in_payment_settings' => [
'priority' => 30,
'fields' => $this->get_settings_fields(),
],
];
}
/**
* Validate settings before saving
*
* @since 1.0.0
* @param array $input Settings input
* @param array $field Field configuration
* @return mixed
*/
public function validate_settings_field( $input, $field ) {
switch ( $field['validation_type'] ?? 'html' ) {
case 'boolean':
return (bool) $input;
case 'int':
return max( 1, absint( $input ) );
case 'html':
default:
return wp_kses_post( $input );
}
}
/**
* Add custom validation for required fields
*
* @since 1.0.0
* @param array $input All settings input
* @return array
*/
public function validate_settings( $input ) {
$validated = [];
$errors = [];
foreach ( $this->get_settings_fields() as $key => $field ) {
$value = $input[ $key ] ?? $field['default'] ?? '';
// Check required fields
if ( ! empty( $field['required'] ) && empty( $value ) ) {
$errors[] = sprintf(
__( '%s is required.', 'event-tickets-mail-in' ),
$field['label']
);
continue;
}
$validated[ $key ] = $this->validate_settings_field( $value, $field );
}
// Display errors if any
if ( ! empty( $errors ) ) {
add_settings_error(
'et_mail_in_settings',
'validation_error',
implode( ' ', $errors ),
'error'
);
}
return $validated;
}
/**
* Get connection status for admin display
*
* @since 1.0.0
* @return array
*/
public function get_connection_status() {
$address = tribe_get_option( 'tickets-commerce-mail-in-address', '' );
$payable_to = tribe_get_option( 'tickets-commerce-mail-in-payable-to', '' );
$enabled = tribe_get_option( 'tickets-commerce-mail-in-enabled', false );
$is_connected = ! empty( $address ) && ! empty( $payable_to );
$status = [
'is_connected' => $is_connected,
'is_enabled' => $enabled,
'is_active' => $enabled && $is_connected,
];
if ( $is_connected ) {
$status['message'] = __( 'Mail-in payment gateway is properly configured.', 'event-tickets-mail-in' );
$status['status'] = 'success';
} else {
$missing = [];
if ( empty( $address ) ) {
$missing[] = __( 'Mailing Address', 'event-tickets-mail-in' );
}
if ( empty( $payable_to ) ) {
$missing[] = __( 'Check Payable To', 'event-tickets-mail-in' );
}
$status['message'] = sprintf(
__( 'Please configure the following required fields: %s', 'event-tickets-mail-in' ),
implode( ', ', $missing )
);
$status['status'] = 'error';
}
return $status;
}
/**
* Register settings with WordPress
*
* @since 1.0.0
* @return void
*/
public function register_settings() {
foreach ( $this->get_settings_fields() as $key => $field ) {
if ( $field['type'] === 'html' ) {
continue; // Skip HTML fields
}
register_setting(
'et_mail_in_settings',
$key,
[
'type' => $this->get_wp_setting_type( $field['validation_type'] ?? 'string' ),
'sanitize_callback' => [ $this, 'validate_settings_field' ],
'default' => $field['default'] ?? '',
]
);
}
}
/**
* Convert validation type to WordPress setting type
*
* @since 1.0.0
* @param string $validation_type
* @return string
*/
protected function get_wp_setting_type( $validation_type ) {
switch ( $validation_type ) {
case 'boolean':
return 'boolean';
case 'int':
return 'integer';
default:
return 'string';
}
}
/**
* Render settings template
*
* @since 1.0.0
* @param array $context Template context
* @return string
*/
protected function render_settings_template( $context ) {
$gateway = $context['gateway'];
$status = $context['connection_status'];
$fields = $this->get_settings_fields();
ob_start();
?>
<div class="tec-settings-form__element tec-settings-form__element--full-width">
<div class="tec-tickets__admin-settings-tickets-commerce-gateway-wrapper">
<!-- Gateway Status -->
<div class="tec-tickets__admin-settings-tickets-commerce-gateway-status">
<div class="tec-tickets__admin-settings-tickets-commerce-gateway-status-icon">
<?php if ( $status['is_active'] ) : ?>
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
<?php else : ?>
<span class="dashicons dashicons-warning" style="color: #ffb900;"></span>
<?php endif; ?>
</div>
<div class="tec-tickets__admin-settings-tickets-commerce-gateway-status-text">
<h3><?php echo esc_html( $gateway::get_label() ); ?></h3>
<p class="description">
<?php echo esc_html( $status['message'] ); ?>
</p>
<?php if ( ! $status['is_active'] ) : ?>
<p class="description">
<?php esc_html_e( 'Configure the settings below to enable this payment method.', 'event-tickets-mail-in' ); ?>
</p>
<?php endif; ?>
</div>
</div>
<!-- Settings Form -->
<div class="tec-tickets__admin-settings-tickets-commerce-gateway-settings">
<table class="form-table">
<tbody>
<?php foreach ( $fields as $field_key => $field ) : ?>
<?php if ( $field['type'] === 'html' ) : ?>
<tr>
<td colspan="2"><?php echo wp_kses_post( $field['html'] ); ?></td>
</tr>
<?php else : ?>
<tr>
<th scope="row">
<label for="<?php echo esc_attr( $field_key ); ?>">
<?php echo esc_html( $field['label'] ); ?>
<?php if ( ! empty( $field['required'] ) ) : ?>
<span class="required" style="color: #d63638;">*</span>
<?php endif; ?>
</label>
</th>
<td>
<?php $this->render_field( $field_key, $field ); ?>
<?php if ( ! empty( $field['tooltip'] ) ) : ?>
<p class="description"><?php echo esc_html( $field['tooltip'] ); ?></p>
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Render individual field
*
* @since 1.0.0
* @param string $field_key Field key
* @param array $field Field configuration
* @return void
*/
protected function render_field( $field_key, $field ) {
$value = tribe_get_option( $field_key, $field['default'] ?? '' );
$size_class = ! empty( $field['size'] ) ? 'class="' . esc_attr( $field['size'] ) . '-text"' : '';
switch ( $field['type'] ) {
case 'checkbox_bool':
echo '<label>';
echo '<input type="checkbox" name="' . esc_attr( $field_key ) . '" value="1" ' . checked( $value, true, false ) . ' />';
echo ' ' . esc_html( $field['label'] );
echo '</label>';
break;
case 'textarea':
echo '<textarea name="' . esc_attr( $field_key ) . '" rows="4" cols="50" ' . $size_class . '>';
echo esc_textarea( $value );
echo '</textarea>';
break;
case 'text':
default:
echo '<input type="text" name="' . esc_attr( $field_key ) . '" value="' . esc_attr( $value ) . '" ' . $size_class . ' />';
break;
}
}
}

View file

@ -0,0 +1,315 @@
<?php
/**
* Admin page for managing mail-in payment orders
*
* @since 1.0.0
* @var \WP_Query $pending_orders Query object with pending orders
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<div class="wrap">
<h1><?php esc_html_e( 'Mail-In Payment Orders', 'event-tickets-mail-in' ); ?></h1>
<div class="et-mail-in-admin-header">
<p><?php esc_html_e( 'Manage orders that are waiting for check payments to arrive.', 'event-tickets-mail-in' ); ?></p>
</div>
<?php if ( $pending_orders->have_posts() ) : ?>
<div class="et-mail-in-orders-table">
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th scope="col"><?php esc_html_e( 'Order', 'event-tickets-mail-in' ); ?></th>
<th scope="col"><?php esc_html_e( 'Customer', 'event-tickets-mail-in' ); ?></th>
<th scope="col"><?php esc_html_e( 'Event', 'event-tickets-mail-in' ); ?></th>
<th scope="col"><?php esc_html_e( 'Amount', 'event-tickets-mail-in' ); ?></th>
<th scope="col"><?php esc_html_e( 'Date', 'event-tickets-mail-in' ); ?></th>
<th scope="col"><?php esc_html_e( 'Status', 'event-tickets-mail-in' ); ?></th>
<th scope="col"><?php esc_html_e( 'Actions', 'event-tickets-mail-in' ); ?></th>
</tr>
</thead>
<tbody>
<?php
$order_controller = new \ET_Mail_In_Payment\Order();
while ( $pending_orders->have_posts() ) :
$pending_orders->the_post();
$order_id = get_the_ID();
$order = tribe( 'tickets.commerce.order' )->get_by_id( $order_id );
$details = $order_controller->get_mail_in_details( $order_id );
$created_date = get_post_meta( $order_id, '_mail_in_created_date', true );
$event_id = get_post_meta( $order_id, '_tec_tc_order_event_id', true );
$event_title = $event_id ? get_the_title( $event_id ) : __( 'N/A', 'event-tickets-mail-in' );
?>
<tr>
<td>
<strong>
<a href="<?php echo esc_url( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) ); ?>">
#<?php echo esc_html( $order->get_order_number() ); ?>
</a>
</strong>
</td>
<td>
<?php echo esc_html( $order->get_purchaser_name() ); ?><br>
<small><?php echo esc_html( $order->get_purchaser_email() ); ?></small>
</td>
<td>
<?php if ( $event_id ) : ?>
<a href="<?php echo esc_url( admin_url( 'post.php?post=' . $event_id . '&action=edit' ) ); ?>">
<?php echo esc_html( $event_title ); ?>
</a>
<?php else : ?>
<?php echo esc_html( $event_title ); ?>
<?php endif; ?>
</td>
<td>
<strong><?php echo esc_html( $order->get_total_value()->get_formatted() ); ?></strong>
</td>
<td>
<?php echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $created_date ) ) ); ?>
<br><small><?php echo esc_html( human_time_diff( strtotime( $created_date ), current_time( 'timestamp' ) ) . ' ago' ); ?></small>
</td>
<td>
<span class="et-mail-in-status et-mail-in-status--pending">
<?php esc_html_e( 'Awaiting Payment', 'event-tickets-mail-in' ); ?>
</span>
</td>
<td>
<div class="et-mail-in-actions">
<button type="button" class="button button-secondary et-mail-in-mark-received"
data-order-id="<?php echo esc_attr( $order_id ); ?>"
data-order-number="<?php echo esc_attr( $order->get_order_number() ); ?>">
<?php esc_html_e( 'Mark Received', 'event-tickets-mail-in' ); ?>
</button>
<button type="button" class="button button-secondary et-mail-in-mark-bounced"
data-order-id="<?php echo esc_attr( $order_id ); ?>"
data-order-number="<?php echo esc_attr( $order->get_order_number() ); ?>">
<?php esc_html_e( 'Mark Bounced', 'event-tickets-mail-in' ); ?>
</button>
</div>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
<?php
// Pagination
$pagination = paginate_links( [
'total' => $pending_orders->max_num_pages,
'current' => max( 1, get_query_var( 'paged' ) ),
'format' => '?paged=%#%',
'prev_text' => __( '&laquo; Previous', 'event-tickets-mail-in' ),
'next_text' => __( 'Next &raquo;', 'event-tickets-mail-in' ),
] );
if ( $pagination ) {
echo '<div class="tablenav bottom">';
echo '<div class="tablenav-pages">' . $pagination . '</div>';
echo '</div>';
}
?>
<?php else : ?>
<div class="et-mail-in-no-orders">
<p><?php esc_html_e( 'No pending mail-in payment orders found.', 'event-tickets-mail-in' ); ?></p>
</div>
<?php endif; ?>
<?php wp_reset_postdata(); ?>
</div>
<!-- Mark Received Modal -->
<div id="et-mail-in-received-modal" class="et-mail-in-modal" style="display: none;">
<div class="et-mail-in-modal-content">
<span class="et-mail-in-modal-close">&times;</span>
<h2><?php esc_html_e( 'Mark Check as Received', 'event-tickets-mail-in' ); ?></h2>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="mark_check_received">
<input type="hidden" name="order_id" id="received-order-id">
<input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce( 'et_mail_in_admin' ) ); ?>">
<input type="hidden" name="redirect_url" value="<?php echo esc_url( admin_url( 'edit.php?post_type=tec_tc_order&page=mail-in-payments' ) ); ?>">
<p id="received-order-info"></p>
<label for="received-notes"><?php esc_html_e( 'Notes (optional):', 'event-tickets-mail-in' ); ?></label>
<textarea name="notes" id="received-notes" rows="3" style="width: 100%;"></textarea>
<div class="et-mail-in-modal-actions">
<button type="submit" class="button button-primary">
<?php esc_html_e( 'Mark as Received', 'event-tickets-mail-in' ); ?>
</button>
<button type="button" class="button et-mail-in-modal-cancel">
<?php esc_html_e( 'Cancel', 'event-tickets-mail-in' ); ?>
</button>
</div>
</form>
</div>
</div>
<!-- Mark Bounced Modal -->
<div id="et-mail-in-bounced-modal" class="et-mail-in-modal" style="display: none;">
<div class="et-mail-in-modal-content">
<span class="et-mail-in-modal-close">&times;</span>
<h2><?php esc_html_e( 'Mark Check as Bounced', 'event-tickets-mail-in' ); ?></h2>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<input type="hidden" name="action" value="mark_check_bounced">
<input type="hidden" name="order_id" id="bounced-order-id">
<input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce( 'et_mail_in_admin' ) ); ?>">
<input type="hidden" name="redirect_url" value="<?php echo esc_url( admin_url( 'edit.php?post_type=tec_tc_order&page=mail-in-payments' ) ); ?>">
<p id="bounced-order-info"></p>
<label for="bounced-reason"><?php esc_html_e( 'Reason for bounce:', 'event-tickets-mail-in' ); ?></label>
<textarea name="reason" id="bounced-reason" rows="3" style="width: 100%;" required></textarea>
<div class="et-mail-in-modal-actions">
<button type="submit" class="button button-primary">
<?php esc_html_e( 'Mark as Bounced', 'event-tickets-mail-in' ); ?>
</button>
<button type="button" class="button et-mail-in-modal-cancel">
<?php esc_html_e( 'Cancel', 'event-tickets-mail-in' ); ?>
</button>
</div>
</form>
</div>
</div>
<style>
.et-mail-in-admin-header {
background: #f1f1f1;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
.et-mail-in-orders-table {
margin-top: 20px;
}
.et-mail-in-status {
padding: 4px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
.et-mail-in-status--pending {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.et-mail-in-actions {
display: flex;
gap: 5px;
flex-wrap: wrap;
}
.et-mail-in-actions .button {
font-size: 11px;
padding: 2px 8px;
height: auto;
}
.et-mail-in-no-orders {
text-align: center;
padding: 40px;
background: #f9f9f9;
border-radius: 4px;
margin: 20px 0;
}
.et-mail-in-modal {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.et-mail-in-modal-content {
background-color: #fefefe;
margin: 10% auto;
padding: 20px;
border-radius: 4px;
width: 80%;
max-width: 500px;
position: relative;
}
.et-mail-in-modal-close {
position: absolute;
right: 15px;
top: 15px;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.et-mail-in-modal-close:hover {
color: #999;
}
.et-mail-in-modal-actions {
margin-top: 20px;
display: flex;
gap: 10px;
justify-content: flex-end;
}
.et-mail-in-modal h2 {
margin-top: 0;
}
.et-mail-in-modal label {
display: block;
margin: 15px 0 5px 0;
font-weight: bold;
}
</style>
<script>
jQuery(document).ready(function($) {
// Handle mark received button
$('.et-mail-in-mark-received').on('click', function() {
var orderId = $(this).data('order-id');
var orderNumber = $(this).data('order-number');
$('#received-order-id').val(orderId);
$('#received-order-info').text('Mark check as received for Order #' + orderNumber + '?');
$('#et-mail-in-received-modal').show();
});
// Handle mark bounced button
$('.et-mail-in-mark-bounced').on('click', function() {
var orderId = $(this).data('order-id');
var orderNumber = $(this).data('order-number');
$('#bounced-order-id').val(orderId);
$('#bounced-order-info').text('Mark check as bounced for Order #' + orderNumber + '?');
$('#et-mail-in-bounced-modal').show();
});
// Handle modal close
$('.et-mail-in-modal-close, .et-mail-in-modal-cancel').on('click', function() {
$('.et-mail-in-modal').hide();
$('textarea').val('');
});
// Close modal when clicking outside
$(window).on('click', function(event) {
if ($(event.target).hasClass('et-mail-in-modal')) {
$('.et-mail-in-modal').hide();
$('textarea').val('');
}
});
});
</script>

View file

@ -0,0 +1,218 @@
<?php
/**
* Order meta box template for mail-in payments
*
* @since 1.0.0
* @var \WP_Post $post Order post object
* @var array $details Mail-in payment details
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<div class="et-mail-in-order-details">
<table class="form-table">
<tbody>
<tr>
<th scope="row"><?php esc_html_e( 'Payment Method', 'event-tickets-mail-in' ); ?></th>
<td><?php esc_html_e( 'Mail-In Payment (Check)', 'event-tickets-mail-in' ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Check Status', 'event-tickets-mail-in' ); ?></th>
<td>
<?php if ( $details['check_received'] ) : ?>
<span class="et-mail-in-status et-mail-in-status--received">
<?php esc_html_e( 'Received', 'event-tickets-mail-in' ); ?>
</span>
<?php if ( $details['check_received_date'] ) : ?>
<br><small><?php echo esc_html( sprintf( __( 'Received on: %s', 'event-tickets-mail-in' ), date_i18n( get_option( 'date_format' ), strtotime( $details['check_received_date'] ) ) ) ); ?></small>
<?php endif; ?>
<?php elseif ( $details['check_bounced'] ) : ?>
<span class="et-mail-in-status et-mail-in-status--bounced">
<?php esc_html_e( 'Bounced', 'event-tickets-mail-in' ); ?>
</span>
<?php if ( $details['bounce_date'] ) : ?>
<br><small><?php echo esc_html( sprintf( __( 'Bounced on: %s', 'event-tickets-mail-in' ), date_i18n( get_option( 'date_format' ), strtotime( $details['bounce_date'] ) ) ) ); ?></small>
<?php endif; ?>
<?php else : ?>
<span class="et-mail-in-status et-mail-in-status--pending">
<?php esc_html_e( 'Awaiting Payment', 'event-tickets-mail-in' ); ?>
</span>
<?php endif; ?>
</td>
</tr>
<?php if ( $details['created_date'] ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Order Created', 'event-tickets-mail-in' ); ?></th>
<td>
<?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $details['created_date'] ) ) ); ?>
<br><small><?php echo esc_html( human_time_diff( strtotime( $details['created_date'] ), current_time( 'timestamp' ) ) . ' ago' ); ?></small>
</td>
</tr>
<?php endif; ?>
<?php if ( $details['payable_to'] ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Check Payable To', 'event-tickets-mail-in' ); ?></th>
<td><strong><?php echo esc_html( $details['payable_to'] ); ?></strong></td>
</tr>
<?php endif; ?>
<?php if ( $details['mailing_address'] ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Mailing Address', 'event-tickets-mail-in' ); ?></th>
<td>
<div class="et-mail-in-address">
<?php echo wp_kses_post( nl2br( $details['mailing_address'] ) ); ?>
</div>
</td>
</tr>
<?php endif; ?>
<?php if ( $details['check_notes'] ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Payment Notes', 'event-tickets-mail-in' ); ?></th>
<td><?php echo wp_kses_post( nl2br( $details['check_notes'] ) ); ?></td>
</tr>
<?php endif; ?>
<?php if ( $details['bounce_reason'] ) : ?>
<tr>
<th scope="row"><?php esc_html_e( 'Bounce Reason', 'event-tickets-mail-in' ); ?></th>
<td><?php echo wp_kses_post( nl2br( $details['bounce_reason'] ) ); ?></td>
</tr>
<?php endif; ?>
</tbody>
</table>
<?php if ( ! $details['check_received'] && ! $details['check_bounced'] ) : ?>
<div class="et-mail-in-actions">
<h4><?php esc_html_e( 'Payment Actions', 'event-tickets-mail-in' ); ?></h4>
<p><?php esc_html_e( 'Use these actions to update the payment status when you receive or process the check.', 'event-tickets-mail-in' ); ?></p>
<div class="et-mail-in-action-buttons">
<button type="button" class="button button-primary et-mail-in-mark-received-single"
data-order-id="<?php echo esc_attr( $post->ID ); ?>">
<?php esc_html_e( 'Mark Check as Received', 'event-tickets-mail-in' ); ?>
</button>
<button type="button" class="button button-secondary et-mail-in-mark-bounced-single"
data-order-id="<?php echo esc_attr( $post->ID ); ?>">
<?php esc_html_e( 'Mark Check as Bounced', 'event-tickets-mail-in' ); ?>
</button>
</div>
</div>
<?php endif; ?>
</div>
<style>
.et-mail-in-order-details .form-table th {
width: 150px;
font-weight: 600;
}
.et-mail-in-status {
display: inline-block;
padding: 4px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
.et-mail-in-status--pending {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.et-mail-in-status--received {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.et-mail-in-status--bounced {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.et-mail-in-address {
background: #f5f5f5;
padding: 10px;
border-radius: 4px;
border-left: 4px solid #0073aa;
line-height: 1.6;
}
.et-mail-in-actions {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ddd;
}
.et-mail-in-actions h4 {
margin: 0 0 10px 0;
}
.et-mail-in-action-buttons {
margin-top: 15px;
}
.et-mail-in-action-buttons .button {
margin-right: 10px;
}
</style>
<script>
jQuery(document).ready(function($) {
$('.et-mail-in-mark-received-single').on('click', function() {
var orderId = $(this).data('order-id');
var notes = prompt('<?php echo esc_js( __( 'Optional notes about the received check:', 'event-tickets-mail-in' ) ); ?>');
if (notes !== null) { // User didn't cancel
var form = $('<form>', {
'method': 'POST',
'action': '<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>'
});
form.append($('<input>', {'type': 'hidden', 'name': 'action', 'value': 'mark_check_received'}));
form.append($('<input>', {'type': 'hidden', 'name': 'order_id', 'value': orderId}));
form.append($('<input>', {'type': 'hidden', 'name': 'notes', 'value': notes}));
form.append($('<input>', {'type': 'hidden', 'name': 'nonce', 'value': '<?php echo esc_attr( wp_create_nonce( 'et_mail_in_admin' ) ); ?>'}));
form.append($('<input>', {'type': 'hidden', 'name': 'redirect_url', 'value': window.location.href}));
$('body').append(form);
form.submit();
}
});
$('.et-mail-in-mark-bounced-single').on('click', function() {
var orderId = $(this).data('order-id');
var reason = prompt('<?php echo esc_js( __( 'Reason for check bounce (required):', 'event-tickets-mail-in' ) ); ?>');
if (reason && reason.trim() !== '') {
var form = $('<form>', {
'method': 'POST',
'action': '<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>'
});
form.append($('<input>', {'type': 'hidden', 'name': 'action', 'value': 'mark_check_bounced'}));
form.append($('<input>', {'type': 'hidden', 'name': 'order_id', 'value': orderId}));
form.append($('<input>', {'type': 'hidden', 'name': 'reason', 'value': reason}));
form.append($('<input>', {'type': 'hidden', 'name': 'nonce', 'value': '<?php echo esc_attr( wp_create_nonce( 'et_mail_in_admin' ) ); ?>'}));
form.append($('<input>', {'type': 'hidden', 'name': 'redirect_url', 'value': window.location.href}));
$('body').append(form);
form.submit();
} else if (reason !== null) {
alert('<?php echo esc_js( __( 'Please provide a reason for the bounce.', 'event-tickets-mail-in' ) ); ?>');
}
});
});
</script>

View file

@ -0,0 +1,151 @@
<?php
/**
* Mail-In Payment checkout form template
*
* @since 1.0.0
* @var array $args Template arguments
* @var \ET_Mail_In_Payment\Gateway $gateway Gateway instance
* @var string $instructions Payment instructions
* @var string $mailing_address Mailing address
* @var string $payable_to Check payable to
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$gateway = $args['gateway'] ?? null;
$instructions = $args['instructions'] ?? '';
$mailing_address = $args['mailing_address'] ?? '';
$payable_to = $args['payable_to'] ?? '';
?>
<div class="tribe-tickets-commerce-gateway-mail-in">
<div class="tribe-tickets-commerce-gateway-mail-in__header">
<h3><?php echo esc_html( $gateway::get_label() ); ?></h3>
<p class="tribe-tickets-commerce-gateway-mail-in__description">
<?php echo esc_html( $gateway::get_description() ); ?>
</p>
</div>
<div class="tribe-tickets-commerce-gateway-mail-in__instructions">
<?php if ( $instructions ) : ?>
<div class="tribe-tickets-commerce-gateway-mail-in__instructions-text">
<h4><?php esc_html_e( 'Payment Instructions:', 'event-tickets-mail-in' ); ?></h4>
<p><?php echo wp_kses_post( nl2br( $instructions ) ); ?></p>
</div>
<?php endif; ?>
<div class="tribe-tickets-commerce-gateway-mail-in__details">
<?php if ( $payable_to ) : ?>
<div class="tribe-tickets-commerce-gateway-mail-in__payable-to">
<h4><?php esc_html_e( 'Make Check Payable To:', 'event-tickets-mail-in' ); ?></h4>
<p class="tribe-tickets-commerce-gateway-mail-in__payable-to-name">
<strong><?php echo esc_html( $payable_to ); ?></strong>
</p>
</div>
<?php endif; ?>
<?php if ( $mailing_address ) : ?>
<div class="tribe-tickets-commerce-gateway-mail-in__address">
<h4><?php esc_html_e( 'Mail Your Check To:', 'event-tickets-mail-in' ); ?></h4>
<div class="tribe-tickets-commerce-gateway-mail-in__address-block">
<?php echo wp_kses_post( nl2br( $mailing_address ) ); ?>
</div>
</div>
<?php endif; ?>
</div>
<div class="tribe-tickets-commerce-gateway-mail-in__important-note">
<h4><?php esc_html_e( 'Important Notes:', 'event-tickets-mail-in' ); ?></h4>
<ul>
<li><?php esc_html_e( 'Please include your order number in the check memo line.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Your tickets will be reserved while we wait for your payment to arrive.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'You will receive a confirmation email once your payment is processed.', 'event-tickets-mail-in' ); ?></li>
</ul>
</div>
</div>
<style>
.tribe-tickets-commerce-gateway-mail-in {
border: 1px solid #ddd;
padding: 20px;
margin: 15px 0 !important;
background: #f9f9f9;
border-radius: 4px;
}
.tribe-tickets-commerce-gateway-mail-in__header h3 {
margin: 0 0 10px 0;
color: #333;
}
.tribe-tickets-commerce-gateway-mail-in__description {
margin: 0 0 20px 0;
font-style: italic;
color: #666;
}
.tribe-tickets-commerce-gateway-mail-in__instructions {
background: #fff;
padding: 20px;
border-radius: 4px;
border: 1px solid #e5e5e5;
}
.tribe-tickets-commerce-gateway-mail-in__instructions h4 {
margin: 0 0 10px 0;
color: #333;
font-size: 16px;
}
.tribe-tickets-commerce-gateway-mail-in__details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin: 20px 0;
}
@media (max-width: 768px) {
.tribe-tickets-commerce-gateway-mail-in__details {
grid-template-columns: 1fr;
}
}
.tribe-tickets-commerce-gateway-mail-in__payable-to-name {
font-size: 18px;
margin: 5px 0;
}
.tribe-tickets-commerce-gateway-mail-in__address-block {
background: #f5f5f5;
padding: 15px;
border-radius: 4px;
border-left: 4px solid #0073aa;
line-height: 1.6;
}
.tribe-tickets-commerce-gateway-mail-in__important-note {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 15px;
border-radius: 4px;
margin-top: 20px;
}
.tribe-tickets-commerce-gateway-mail-in__important-note h4 {
color: #856404;
margin-top: 0;
}
.tribe-tickets-commerce-gateway-mail-in__important-note ul {
margin: 10px 0 0 0;
padding-left: 20px;
}
.tribe-tickets-commerce-gateway-mail-in__important-note li {
margin-bottom: 5px;
color: #856404;
}
</style>
</div>

View file

@ -0,0 +1,141 @@
<?php
/**
* Order cancelled email template
*
* @since 1.0.0
* @var \TEC\Tickets\Commerce\Order $order Order object
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php printf( __( 'Order Cancelled: #%s', 'event-tickets-mail-in' ), $order->get_order_number() ); ?></title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background: #f8d7da;
padding: 20px;
text-align: center;
border-radius: 8px 8px 0 0;
border-bottom: 3px solid #dc3545;
}
.content {
background: #fff;
padding: 30px;
border: 1px solid #ddd;
}
.order-summary {
background: #f8f9fa;
padding: 20px;
border-radius: 4px;
margin: 20px 0;
}
.cancellation-notice {
background: #f8d7da;
padding: 20px;
border-radius: 4px;
border-left: 4px solid #dc3545;
margin: 20px 0;
}
.contact-info {
background: #e7f3ff;
padding: 15px;
border-radius: 4px;
border-left: 4px solid #0073aa;
margin: 20px 0;
}
.footer {
background: #f8f9fa;
padding: 20px;
text-align: center;
border-radius: 0 0 8px 8px;
border-top: 1px solid #ddd;
font-size: 14px;
color: #666;
}
h1, h2, h3 {
color: #dc3545;
}
.order-number {
font-size: 24px;
font-weight: bold;
color: #dc3545;
}
.cancel-icon {
font-size: 48px;
color: #dc3545;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<div class="cancel-icon"></div>
<h1><?php esc_html_e( 'Order Cancelled', 'event-tickets-mail-in' ); ?></h1>
<p><?php printf( __( 'Order #%s', 'event-tickets-mail-in' ), '<span class="order-number">' . esc_html( $order->get_order_number() ) . '</span>' ); ?></p>
</div>
<div class="content">
<h2><?php esc_html_e( 'Your order has been cancelled', 'event-tickets-mail-in' ); ?></h2>
<p><?php printf(
__( 'Hello %s,', 'event-tickets-mail-in' ),
esc_html( $order->get_purchaser_name() )
); ?></p>
<div class="cancellation-notice">
<h3><?php esc_html_e( 'Order Cancellation Notice', 'event-tickets-mail-in' ); ?></h3>
<p><?php esc_html_e( 'We regret to inform you that your order has been cancelled. This may have occurred for one of the following reasons:', 'event-tickets-mail-in' ); ?></p>
<ul>
<li><?php esc_html_e( 'Payment was not received within the specified timeframe', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Check payment was returned/bounced', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Manual cancellation by administrator', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Event was cancelled or postponed', 'event-tickets-mail-in' ); ?></li>
</ul>
</div>
<div class="order-summary">
<h3><?php esc_html_e( 'Cancelled Order Details', 'event-tickets-mail-in' ); ?></h3>
<p><strong><?php esc_html_e( 'Order Number:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_order_number() ); ?></p>
<p><strong><?php esc_html_e( 'Order Total:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_total_value()->get_formatted() ); ?></p>
<p><strong><?php esc_html_e( 'Cancellation Date:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ); ?></p>
<p><strong><?php esc_html_e( 'Status:', 'event-tickets-mail-in' ); ?></strong> <span style="color: #dc3545; font-weight: bold;"><?php esc_html_e( 'Cancelled', 'event-tickets-mail-in' ); ?></span></p>
</div>
<h3><?php esc_html_e( 'What this means:', 'event-tickets-mail-in' ); ?></h3>
<ul>
<li><?php esc_html_e( 'Your ticket reservation has been released', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'No payment is required for this cancelled order', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'If you already mailed a check, please contact us for a refund', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'You can place a new order if tickets are still available', 'event-tickets-mail-in' ); ?></li>
</ul>
<div class="contact-info">
<h3><?php esc_html_e( 'Questions or Concerns?', 'event-tickets-mail-in' ); ?></h3>
<p><?php esc_html_e( 'If you believe this cancellation was made in error, or if you have any questions about this cancellation, please contact us immediately.', 'event-tickets-mail-in' ); ?></p>
<p><?php esc_html_e( 'We apologize for any inconvenience this may cause.', 'event-tickets-mail-in' ); ?></p>
</div>
<p><?php esc_html_e( 'Thank you for your understanding.', 'event-tickets-mail-in' ); ?></p>
</div>
<div class="footer">
<p><?php echo esc_html( get_option( 'blogname' ) ); ?></p>
<p><?php echo esc_html( get_option( 'admin_email' ) ); ?></p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,122 @@
<?php
/**
* Payment confirmation email template
*
* @since 1.0.0
* @var \TEC\Tickets\Commerce\Order $order Order object
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php printf( __( 'Payment Confirmed for Order #%s', 'event-tickets-mail-in' ), $order->get_order_number() ); ?></title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background: #d4edda;
padding: 20px;
text-align: center;
border-radius: 8px 8px 0 0;
border-bottom: 3px solid #28a745;
}
.content {
background: #fff;
padding: 30px;
border: 1px solid #ddd;
}
.order-summary {
background: #f8f9fa;
padding: 20px;
border-radius: 4px;
margin: 20px 0;
}
.success-message {
background: #d4edda;
padding: 20px;
border-radius: 4px;
border-left: 4px solid #28a745;
margin: 20px 0;
}
.footer {
background: #f8f9fa;
padding: 20px;
text-align: center;
border-radius: 0 0 8px 8px;
border-top: 1px solid #ddd;
font-size: 14px;
color: #666;
}
h1, h2, h3 {
color: #28a745;
}
.order-number {
font-size: 24px;
font-weight: bold;
color: #28a745;
}
.success-icon {
font-size: 48px;
color: #28a745;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<div class="success-icon"></div>
<h1><?php esc_html_e( 'Payment Confirmed!', 'event-tickets-mail-in' ); ?></h1>
<p><?php printf( __( 'Order #%s', 'event-tickets-mail-in' ), '<span class="order-number">' . esc_html( $order->get_order_number() ) . '</span>' ); ?></p>
</div>
<div class="content">
<h2><?php esc_html_e( 'Your payment has been received!', 'event-tickets-mail-in' ); ?></h2>
<p><?php printf(
__( 'Hello %s,', 'event-tickets-mail-in' ),
esc_html( $order->get_purchaser_name() )
); ?></p>
<div class="success-message">
<h3><?php esc_html_e( 'Great news!', 'event-tickets-mail-in' ); ?></h3>
<p><?php esc_html_e( 'We have received and processed your check payment. Your order is now complete and your tickets are confirmed!', 'event-tickets-mail-in' ); ?></p>
</div>
<div class="order-summary">
<h3><?php esc_html_e( 'Order Summary', 'event-tickets-mail-in' ); ?></h3>
<p><strong><?php esc_html_e( 'Order Number:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_order_number() ); ?></p>
<p><strong><?php esc_html_e( 'Payment Method:', 'event-tickets-mail-in' ); ?></strong> <?php esc_html_e( 'Check Payment', 'event-tickets-mail-in' ); ?></p>
<p><strong><?php esc_html_e( 'Total Amount:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_total_value()->get_formatted() ); ?></p>
<p><strong><?php esc_html_e( 'Payment Status:', 'event-tickets-mail-in' ); ?></strong> <span style="color: #28a745; font-weight: bold;"><?php esc_html_e( 'Completed', 'event-tickets-mail-in' ); ?></span></p>
</div>
<h3><?php esc_html_e( 'What happens next?', 'event-tickets-mail-in' ); ?></h3>
<ul>
<li><?php esc_html_e( 'Your tickets are now confirmed and active.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'You should receive your tickets in a separate email shortly.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Please keep this confirmation for your records.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'If you have any questions, please contact us.', 'event-tickets-mail-in' ); ?></li>
</ul>
<p><?php esc_html_e( 'Thank you for your payment and we look forward to seeing you at the event!', 'event-tickets-mail-in' ); ?></p>
</div>
<div class="footer">
<p><?php echo esc_html( get_option( 'blogname' ) ); ?></p>
<p><?php echo esc_html( get_option( 'admin_email' ) ); ?></p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,170 @@
<?php
/**
* Payment instructions email template
*
* @since 1.0.0
* @var \TEC\Tickets\Commerce\Order $order Order object
* @var array $details Mail-in payment details
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php printf( __( 'Payment Instructions for Order #%s', 'event-tickets-mail-in' ), $order->get_order_number() ); ?></title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.header {
background: #f8f9fa;
padding: 20px;
text-align: center;
border-radius: 8px 8px 0 0;
border-bottom: 3px solid #0073aa;
}
.content {
background: #fff;
padding: 30px;
border: 1px solid #ddd;
}
.order-summary {
background: #f8f9fa;
padding: 20px;
border-radius: 4px;
margin: 20px 0;
}
.payment-details {
background: #e7f3ff;
padding: 20px;
border-radius: 4px;
border-left: 4px solid #0073aa;
margin: 20px 0;
}
.important-notes {
background: #fff3cd;
padding: 15px;
border-radius: 4px;
border-left: 4px solid #ffc107;
margin: 20px 0;
}
.footer {
background: #f8f9fa;
padding: 20px;
text-align: center;
border-radius: 0 0 8px 8px;
border-top: 1px solid #ddd;
font-size: 14px;
color: #666;
}
.mailing-address {
background: #f5f5f5;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
font-weight: bold;
}
h1, h2, h3 {
color: #0073aa;
}
.order-number {
font-size: 24px;
font-weight: bold;
color: #0073aa;
}
ul {
padding-left: 20px;
}
li {
margin-bottom: 5px;
}
</style>
</head>
<body>
<div class="email-container">
<div class="header">
<h1><?php esc_html_e( 'Payment Instructions', 'event-tickets-mail-in' ); ?></h1>
<p><?php printf( __( 'Order #%s', 'event-tickets-mail-in' ), '<span class="order-number">' . esc_html( $order->get_order_number() ) . '</span>' ); ?></p>
</div>
<div class="content">
<h2><?php esc_html_e( 'Thank you for your order!', 'event-tickets-mail-in' ); ?></h2>
<p><?php printf(
__( 'Hello %s,', 'event-tickets-mail-in' ),
esc_html( $order->get_purchaser_name() )
); ?></p>
<p><?php esc_html_e( 'Your order has been received and your tickets have been reserved. To complete your purchase, please mail your check payment using the details below.', 'event-tickets-mail-in' ); ?></p>
<div class="order-summary">
<h3><?php esc_html_e( 'Order Summary', 'event-tickets-mail-in' ); ?></h3>
<p><strong><?php esc_html_e( 'Order Number:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_order_number() ); ?></p>
<p><strong><?php esc_html_e( 'Order Date:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $details['created_date'] ) ) ); ?></p>
<p><strong><?php esc_html_e( 'Total Amount:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_total_value()->get_formatted() ); ?></p>
</div>
<div class="payment-details">
<h3><?php esc_html_e( 'Payment Instructions', 'event-tickets-mail-in' ); ?></h3>
<?php if ( $details['instructions'] ) : ?>
<p><?php echo wp_kses_post( nl2br( $details['instructions'] ) ); ?></p>
<?php endif; ?>
<?php if ( $details['payable_to'] ) : ?>
<p><strong><?php esc_html_e( 'Make Check Payable To:', 'event-tickets-mail-in' ); ?></strong></p>
<p style="font-size: 18px; font-weight: bold; color: #0073aa;"><?php echo esc_html( $details['payable_to'] ); ?></p>
<?php endif; ?>
<?php if ( $details['mailing_address'] ) : ?>
<p><strong><?php esc_html_e( 'Mail Your Check To:', 'event-tickets-mail-in' ); ?></strong></p>
<div class="mailing-address">
<?php echo wp_kses_post( nl2br( $details['mailing_address'] ) ); ?>
</div>
<?php endif; ?>
<p><strong><?php esc_html_e( 'Check Amount:', 'event-tickets-mail-in' ); ?></strong> <?php echo esc_html( $order->get_total_value()->get_formatted() ); ?></p>
</div>
<div class="important-notes">
<h3><?php esc_html_e( 'Important Notes', 'event-tickets-mail-in' ); ?></h3>
<ul>
<li><?php printf( __( 'Please write your order number (%s) in the check memo line.', 'event-tickets-mail-in' ), esc_html( $order->get_order_number() ) ); ?></li>
<li><?php
$reservation_days = absint( get_option( 'et_mail_in_reservation_days', 7 ) );
printf(
_n(
'Your tickets will be reserved for %d day while we wait for your payment.',
'Your tickets will be reserved for %d days while we wait for your payment.',
$reservation_days,
'event-tickets-mail-in'
),
$reservation_days
);
?></li>
<li><?php esc_html_e( 'You will receive a confirmation email once your payment is received and processed.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Please keep this email for your records.', 'event-tickets-mail-in' ); ?></li>
</ul>
</div>
<p><?php esc_html_e( 'If you have any questions about your order or payment, please contact us.', 'event-tickets-mail-in' ); ?></p>
<p><?php esc_html_e( 'Thank you for your business!', 'event-tickets-mail-in' ); ?></p>
</div>
<div class="footer">
<p><?php echo esc_html( get_option( 'blogname' ) ); ?></p>
<p><?php echo esc_html( get_option( 'admin_email' ) ); ?></p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,124 @@
<?php
/**
* Mail-In Payment checkout container template
*
* @since 1.0.0
* @var array $args Template arguments
* @var \ET_Mail_In_Payment\Gateway $gateway Gateway instance
* @var string $instructions Payment instructions
* @var string $mailing_address Mailing address
* @var string $payable_to Check payable to
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Variables are extracted from $context in the Gateway class
// If not available directly, try to get from $args (fallback)
if ( ! isset( $gateway ) ) {
$gateway = $args['gateway'] ?? null;
}
if ( ! isset( $instructions ) ) {
$instructions = $args['instructions'] ?? '';
}
if ( ! isset( $mailing_address ) ) {
$mailing_address = $args['mailing_address'] ?? '';
}
if ( ! isset( $payable_to ) ) {
$payable_to = $args['payable_to'] ?? '';
}
// Get class name for static method calls
$gateway_class = $gateway ? get_class( $gateway ) : '';
?>
<div class="tribe-tickets-commerce-gateway-mail-in">
<!-- Toggle Button -->
<div class="tribe-tickets-commerce-gateway-mail-in__toggle">
<button type="button" id="mail-in-toggle-button" class="tribe-tickets-commerce-gateway-mail-in__toggle-button">
<span class="toggle-icon"></span>
<?php esc_html_e( 'Click here to pay by mail (check)', 'event-tickets-mail-in' ); ?>
</button>
</div>
<!-- Hidden Content (Payment Instructions and Form) -->
<div class="tribe-tickets-commerce-gateway-mail-in__content" id="mail-in-content" style="display: none;">
<div class="tribe-tickets-commerce-gateway-mail-in__header">
<h4><?php echo esc_html( $gateway_class ? $gateway_class::get_label() : __( 'Mail-In Payment', 'event-tickets-mail-in' ) ); ?></h4>
<p class="tribe-tickets-commerce-gateway-mail-in__description">
<?php echo esc_html( $gateway_class ? $gateway_class::get_description() : __( 'Pay by mailing a check', 'event-tickets-mail-in' ) ); ?>
</p>
</div>
<div class="tribe-tickets-commerce-gateway-mail-in__instructions">
<?php if ( $instructions ) : ?>
<div class="tribe-tickets-commerce-gateway-mail-in__instructions-text">
<strong><?php esc_html_e( 'Payment Instructions:', 'event-tickets-mail-in' ); ?></strong>
<p><?php echo wp_kses_post( nl2br( $instructions ) ); ?></p>
</div>
<?php endif; ?>
<div class="tribe-tickets-commerce-gateway-mail-in__details">
<?php if ( $payable_to ) : ?>
<div class="tribe-tickets-commerce-gateway-mail-in__payable-to">
<strong><?php esc_html_e( 'Make Check Payable To:', 'event-tickets-mail-in' ); ?></strong>
<p class="tribe-tickets-commerce-gateway-mail-in__payable-to-name">
<?php echo esc_html( $payable_to ); ?>
</p>
</div>
<?php endif; ?>
<?php if ( $mailing_address ) : ?>
<div class="tribe-tickets-commerce-gateway-mail-in__address">
<strong><?php esc_html_e( 'Mail Your Check To:', 'event-tickets-mail-in' ); ?></strong>
<div class="tribe-tickets-commerce-gateway-mail-in__address-block">
<?php echo wp_kses_post( nl2br( $mailing_address ) ); ?>
</div>
</div>
<?php endif; ?>
</div>
<div class="tribe-tickets-commerce-gateway-mail-in__important-note">
<strong><?php esc_html_e( 'Important Notes:', 'event-tickets-mail-in' ); ?></strong>
<ul>
<li><?php esc_html_e( 'Please include your order number in the check memo line.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'Your tickets will be reserved while we wait for your payment to arrive.', 'event-tickets-mail-in' ); ?></li>
<li><?php esc_html_e( 'You will receive a confirmation email once your payment is processed.', 'event-tickets-mail-in' ); ?></li>
</ul>
</div>
</div>
<!-- Gateway Selection Form -->
<form class="tribe-tickets__commerce-checkout-form tribe-tickets__commerce-checkout-form--mail-in" method="post">
<?php wp_nonce_field( 'tec_tickets_commerce_checkout', 'tec_tickets_commerce_checkout_nonce' ); ?>
<input type="hidden" name="tec_tc_gateway" value="mail-in" />
<!-- Purchaser Information -->
<div class="tribe-tickets__commerce-checkout-purchaser-info">
<h4><?php esc_html_e( 'Contact Information', 'event-tickets-mail-in' ); ?></h4>
<div class="tribe-tickets__commerce-checkout-form-field">
<label for="purchaser_name"><?php esc_html_e( 'Full Name', 'event-tickets-mail-in' ); ?> <span class="required">*</span></label>
<input type="text" id="purchaser_name" name="purchaser_name" required class="tribe-common-form-control-text__input" />
</div>
<div class="tribe-tickets__commerce-checkout-form-field">
<label for="purchaser_email"><?php esc_html_e( 'Email Address', 'event-tickets-mail-in' ); ?> <span class="required">*</span></label>
<input type="email" id="purchaser_email" name="purchaser_email" required class="tribe-common-form-control-text__input" />
</div>
</div>
<div class="tribe-tickets__commerce-checkout-form-submit">
<button
id="tec-tc-gateway-mail-in-checkout-button"
type="submit"
class="tribe-common-c-btn tribe-tickets__commerce-checkout-form-submit-button"
data-gateway="mail-in"
>
<?php esc_html_e( 'Place Order (Pay by Check)', 'event-tickets-mail-in' ); ?>
</button>
</div>
</form>
</div> <!-- End .tribe-tickets-commerce-gateway-mail-in__content -->
</div>