Lesson 78: Processing Administrator Payment Status Updates and Beginning Payment Verification

Overview

In the previous lesson, a dedicated Administrator Payments Dashboard was introduced, allowing administrators to view submitted buyer payments, inspect transaction details, and access a payment management interface.

However, the interface was still informational. Although administrators could select a payment status from a dropdown, those changes were not yet saved to the database.

In this lesson, we will connect the user interface to the backend by implementing secure form processing and updating payment records.


Objectives

By the end of this lesson, we will:

  • Register a secure administrator POST action.
  • Process payment status update requests.
  • Verify administrator permissions.
  • Validate WordPress nonces.
  • Update the payment_status field in the database.
  • Redirect administrators with success messages.
  • Prepare the payment verification workflow for future approval actions.

Why This Lesson Is Important

Until now, administrators could only view payment information.

This lesson transforms the payment dashboard into a working management system by allowing administrators to update payment progress after reviewing submitted payment proofs.


Current Workflow

Current administrator workflow:

Buyer Uploads Payment Proof
            │
            ▼
Payment Appears in Dashboard
            │
            ▼
Administrator Opens Details
            │
            ▼
Select Payment Status
            │
            ▼
Nothing Happens ❌

Desired Workflow

After this lesson:

Buyer Uploads Payment Proof
            │
            ▼
Payment Appears in Dashboard
            │
            ▼
Administrator Opens Details
            │
            ▼
Select Payment Status
            │
            ▼
Click Update
            │
            ▼
Database Updated
            │
            ▼
Success Message Displayed

Planned Implementation

1. Register the Admin POST Action

The payment management form already submits to WordPress using admin-post.php.

This lesson will register a dedicated action handler for processing payment updates.


2. Verify Administrator Permissions

Before processing any request, the plugin will confirm that the current user has sufficient privileges.

Only administrators should be allowed to modify payment records.


3. Verify the Nonce

Every request will validate the security nonce before updating the database.

This protects against Cross-Site Request Forgery (CSRF) attacks.


4. Validate Submitted Data

Incoming data will be sanitized and validated before use.

Examples include:

  • Transaction ID
  • Payment Status

This ensures only expected values are processed.


5. Update the Database

The selected payment status will be written to the payment_status column of the transaction table.

Typical status transitions include:

  • Pending
  • Processing
  • Paid
  • Completed
  • Cancelled
  • Refunded

6. Redirect Back to the Transaction

After processing, administrators will be redirected back to the Transaction Details page instead of the generic transactions list.

This provides a smoother workflow.


7. Display Success Notices

Administrators should immediately know whether the update succeeded.

Examples include:

Payment status updated successfully.

or

Unable to update payment status.

8. Prepare for Payment Approval

Although this lesson focuses on updating payment statuses, the implementation prepares the foundation for future verification actions.

Upcoming lessons will introduce dedicated buttons such as:

  • Approve Payment
  • Reject Payment
  • Request New Payment Proof

Database Changes

This lesson will primarily update the following transaction field:

payment_status

Possible values include:

  • pending
  • submitted
  • processing
  • paid
  • completed
  • cancelled
  • refunded

Future lessons may introduce additional verification-specific statuses if needed.


Security Considerations

The payment verification process will follow standard WordPress security practices:

  • Capability checks
  • Nonce verification
  • Data sanitization
  • Safe database updates
  • Secure redirects

Expected Outcome

After completing this lesson:

  • Administrators can update payment status.
  • Database records are updated securely.
  • Transaction details immediately reflect the latest payment state.
  • Payment management becomes fully functional.
  • The administrator workflow becomes suitable for production use.

What You Will Learn

During this lesson, you will learn how to:

  • Process administrator forms using admin-post.php.
  • Secure backend form submissions.
  • Update custom database tables.
  • Redirect users after successful processing.
  • Separate payment management from transaction management.

Looking Ahead

Once payment status updates are working, the Flipnzee Auctions plugin will be ready for the next stage of payment verification.


Next Lesson Preview

Lesson 79: Reviewing Uploaded Payment Proofs and Approving Buyer Payments

In the next lesson, we will enhance the administrator experience by allowing payment proofs to be viewed directly from the Transaction Details page. Administrators will be able to inspect uploaded receipts, preview images or PDFs, and approve or reject payments before initiating the website ownership transfer process.

This will bring Flipnzee one step closer to a complete end-to-end marketplace workflow and lay the groundwork for integrating Escrow.com as the preferred payment gateway for live auctions.

Lesson 77 Implementation: Building the Administrator Payment Review Dashboard for Flipnzee Auctions

In the previous lesson, buyers were able to upload payment proof securely through the payment page, with uploaded receipts stored in the WordPress Media Library and linked to the corresponding transaction.

This lesson shifted focus from the buyer to the administrator by introducing a dedicated payment review dashboard. Administrators can now view submitted payments, inspect transaction details, and prepare payments for verification.


Objective

The primary goal of this lesson was to create an administrator interface that allows the Flipnzee team to review buyer payment submissions before approving website ownership transfers.

By the end of this implementation, administrators could:

  • View all submitted payments.
  • Open detailed transaction information.
  • Review payment metadata.
  • Prepare payment status management.
  • Lay the foundation for future payment verification.

Step 1 – Creating the Admin Payments Page

A new administrator page was created.

File created

admin/class-admin-payments.php

The page was implemented as a dedicated admin class.

class Flipnzee_Admin_Payments {

    /**
     * Render Payments page.
     *
     * @return void
     */
    public static function render_page() {

        ?>

        <div class="wrap">

            <h1>Buyer Payments</h1>

            <p>

                Review buyer payment submissions before approving
                the transfer of ownership.

            </p>

        </div>

        <?php
    }
}

This provided a clean starting point for the administrator payment workflow.


Step 2 – Registering the Payments Menu

A new submenu was added beneath the Flipnzee Auctions admin menu.

add_submenu_page(
    'flipnzee-auctions',
    'Payments',
    'Payments',
    'manage_options',
    'flipnzee-payments',
    array(
        'Flipnzee_Admin_Payments',
        'render_page',
    )
);

This created a dedicated Payments section for administrators.


Step 3 – Loading Submitted Payments

The Payments page was connected to the transaction table.

global $wpdb;

$table = $wpdb->prefix . 'flipnzee_transactions';

$payments = $wpdb->get_results(
    "
    SELECT *
    FROM {$table}
    WHERE payment_status = 'submitted'
    ORDER BY updated_at DESC
    "
);

Only transactions that had submitted payment proofs were displayed.


Step 4 – Handling Empty Results

Before rendering the table, the plugin checks whether submitted payments exist.

if ( empty( $payments ) ) {

    echo '<p>No payment submissions found.</p>';

} else {

    // Display payment table.

}

This prevents empty tables and provides useful feedback to administrators.


Step 5 – Building the Payments Table

A professional WordPress admin table was introduced.

<table class="widefat striped">

    <thead>

        <tr>

            <th>ID</th>
            <th>Listing</th>
            <th>Buyer</th>
            <th>Amount</th>
            <th>Gateway</th>
            <th>Status</th>
            <th>Submitted</th>
            <th>Actions</th>

        </tr>

    </thead>

The table closely follows the standard WordPress administration interface.


Step 6 – Displaying Submitted Payments

Each submitted payment is displayed using a loop.

<?php foreach ( $payments as $payment ) : ?>

<tr>

    <td><?php echo esc_html( $payment->id ); ?></td>

    <td><?php echo esc_html( $payment->listing_id ); ?></td>

    <td><?php echo esc_html( $payment->buyer_id ); ?></td>

    <td><?php echo esc_html(
        number_format_i18n(
            $payment->winning_bid,
            2
        )
    ); ?></td>

    <td><?php echo esc_html(
        $payment->payment_gateway
    ); ?></td>

    <td><?php echo esc_html(
        ucfirst( $payment->payment_status )
    ); ?></td>

    <td><?php echo esc_html(
        $payment->updated_at
    ); ?></td>

</tr>

<?php endforeach; ?>

The administrator can immediately identify submitted payments requiring review.


Step 7 – Adding the View Details Button

Instead of displaying placeholder text, each payment now links to a detailed transaction page.

<td>

    <a
        class="button button-primary"
        href="<?php echo esc_url(
            admin_url(
                'admin.php?page=flipnzee-transaction-details&transaction_id=' .
                absint( $payment->id )
            )
        ); ?>">

        View Details

    </a>

</td>

This significantly improves navigation between the payment dashboard and transaction details.


Step 8 – Enhancing the Transaction Details Page

The existing transaction details page was expanded with payment information.

Additional rows were added to display:

<tr>
    <th>Payment Status</th>
    <td><?php echo esc_html(
        ucfirst( $transaction['payment_status'] )
    ); ?></td>
</tr>

<tr>
    <th>Payment Gateway</th>
    <td><?php echo esc_html(
        $transaction['payment_gateway']
    ); ?></td>
</tr>

<tr>
    <th>Payment Submitted</th>
    <td><?php echo esc_html(
        $transaction['payment_submitted_at']
    ); ?></td>
</tr>

Administrators can now review payment-specific information alongside the transaction details.


Step 9 – Creating the Payment Management Section

A dedicated Payment Management panel was introduced.

<h2>Payment Management</h2>

<form
    method="post"
    action="<?php echo esc_url(
        admin_url( 'admin-post.php' )
    ); ?>">

This prepares the interface for future payment verification actions.


Step 10 – Securing the Form

The management form was protected using a WordPress nonce.

wp_nonce_field(
    'flipnzee_update_payment_status',
    'flipnzee_payment_nonce'
);

This ensures only legitimate administrators can submit payment updates.


Step 11 – Payment Status Dropdown

Administrators can now select a payment status.

<select
    name="payment_status"
    id="payment_status">

    <option value="pending">Pending</option>

    <option value="processing">Processing</option>

    <option value="paid">Paid</option>

    <option value="completed">Completed</option>

    <option value="cancelled">Cancelled</option>

    <option value="refunded">Refunded</option>

</select>

Although the update handler will be implemented in the next lesson, the interface is now fully prepared.


Challenges Encountered

Several issues arose during development.

Method Name Mismatch

Initially, the Payments submenu referenced render_page(), while the class still used render().

Standardizing on render_page() resolved the fatal error.


PHP and HTML Mixing

While building the Payment Management form, HTML was accidentally placed inside an open PHP block.

Example:

<?php

wp_nonce_field(...);

<input ...>

Closing PHP before the HTML resolved the syntax error.


Duplicate Status Rows

During iterative development, duplicate Payment Status rows were unintentionally introduced.

Cleaning up duplicate markup produced a clearer transaction details page.


Payment vs Transaction Status

One important architectural decision emerged during development.

The plugin now distinguishes between:

  • status (overall transaction lifecycle)
  • payment_status (buyer payment lifecycle)

This separation prepares the plugin for multiple payment gateways, including Escrow.com, without affecting the broader transaction workflow.


Testing Performed

The implementation was tested by:

  • Opening the new Payments admin menu.
  • Confirming submitted transactions appear in the table.
  • Verifying payment amounts and gateways display correctly.
  • Opening transaction details using the View Details button.
  • Confirming payment metadata is displayed.
  • Checking the Payment Management form renders correctly.
  • Validating PHP syntax after each modification.

Lessons Learned

This implementation reinforced several WordPress development practices:

  • Separate administrator workflows from buyer workflows.
  • Keep transaction management and payment management independent.
  • Use dedicated admin pages instead of overloading existing screens.
  • Secure administrator forms using nonces.
  • Build reusable interfaces that can support additional payment gateways in future.

Download Source Code

Download the starting version of the plugin before the lesson:

Download the completed version after this lesson:


Current Progress

At the end of Lesson 77, the Flipnzee Auctions plugin now includes:

  • ✅ Administrator Payments menu
  • ✅ Submitted Payments dashboard
  • ✅ Payment listing table
  • ✅ View Details navigation
  • ✅ Enhanced transaction details page
  • ✅ Payment metadata display
  • ✅ Payment Management interface
  • ✅ Secure administrator form ready for processing

The actual processing of payment status updates will be completed in the next lesson.


Next Lesson

Lesson 78: Processing Administrator Payment Status Updates

In the next lesson, we will connect the Payment Management form to the backend by:

  • Registering the administrator POST handler.
  • Verifying administrator permissions and nonces.
  • Updating the payment_status field in the database.
  • Redirecting administrators with success messages.
  • Preparing the workflow for payment approval, rejection, and future Escrow.com integration.

This will complete the first functional administrator payment verification workflow in the Flipnzee Auctions plugin.

Lesson 72: Building the Payment Gateway Selection Interface

Objective

With the payment architecture now prepared, the next logical step is to give buyers the ability to choose how they would like to pay.

In this lesson, we will introduce a Payment Method Selection section on the Payment page. Although only a placeholder gateway exists today, the interface will be built so future gateways (Stripe, PayPal, Razorpay, Bank Transfer, Crypto, etc.) can be added with almost no changes to the frontend.

This lesson focuses entirely on UI architecture, not actual payment processing.


What We’ll Build

Instead of only showing:

Payment Gateway
Manual Payment (Coming Soon)

the payment page will display something like:

Select Payment Method

(•) Manual Payment (Coming Soon)
( ) Stripe
( ) PayPal
( ) Razorpay
( ) Cryptocurrency (USDT)

[Continue]

Only Manual Payment will be enabled.

The remaining gateways will appear disabled with a “Coming Soon” label.


Why This Lesson Matters

This is an important architectural step because:

  • separates payment UI from payment logic
  • allows new gateways without redesigning pages
  • provides a familiar checkout experience
  • keeps the plugin scalable
  • prepares for future gateway plugins

Files We’ll Modify

Existing

includes/class-payment-page.php

Existing

includes/class-payment-manager.php

(add helper function for available gateways)


New Features

1. Payment Gateway List

Create a helper such as:

Flipnzee_Payment_Manager::get_available_gateways()

which returns an array like

array(
    'manual' => array(
        'label' => 'Manual Payment',
        'enabled' => true,
    ),
    'stripe' => array(
        'label' => 'Stripe',
        'enabled' => false,
    ),
    'paypal' => array(
        'label' => 'PayPal',
        'enabled' => false,
    ),
    'razorpay' => array(
        'label' => 'Razorpay',
        'enabled' => false,
    ),
    'crypto' => array(
        'label' => 'USDT Cryptocurrency',
        'enabled' => false,
    ),
);

2. Display Gateway Choices

Show all gateways as radio buttons.

Only enabled gateways are selectable.

Disabled gateways display:

Coming Soon

3. Continue Button

Display

Continue to Payment

No payment processing yet.


4. Clean HTML Structure

Wrap the section in

<div class="flipnzee-payment-gateways">

for future styling.


User Experience

Current page:

Transaction Details

Gateway:
Manual Payment

New page:

Transaction Details

Select Payment Method

○ Stripe
○ PayPal
● Manual Payment
○ Razorpay
○ Crypto

Continue

Benefits

After this lesson the plugin will have:

  • scalable payment architecture
  • configurable gateway list
  • reusable gateway rendering
  • future-ready checkout interface
  • no dependency on a specific payment provider

What We Won’t Build Yet

To keep the project stable, we are not implementing:

  • Stripe API
  • PayPal API
  • Razorpay API
  • Crypto payments
  • Order confirmation
  • Payment verification

Those will come in later lessons.


Expected Outcome

By the end of Lesson 72, buyers will see a professional payment method selection interface with a working placeholder for Manual Payment and clearly marked future payment options, laying the foundation for integrating real payment gateways in the upcoming lessons.

Lesson 73: Capturing and Validating the Buyer’s Selected Payment Method

Objective

In the previous lesson, we introduced a dynamic payment gateway selection interface. Buyers can now see the available payment methods, but their selection is not yet processed.

In this lesson, we’ll begin building the actual checkout workflow by wrapping the gateway list inside a form, capturing the selected payment method, validating it on submission, and preparing the plugin for gateway-specific payment processing.

Although real payment gateways are still not connected, this lesson establishes the core workflow that every future payment provider will use.


Why This Lesson Matters

A payment page is only useful if it can process the buyer’s choice.

Instead of immediately integrating Stripe, PayPal, or Escrow.com APIs, we first need a common checkout workflow that:

  • accepts the selected gateway
  • validates user input
  • prevents invalid gateway selections
  • prepares the transaction for payment
  • redirects to the appropriate payment handler

Once this workflow exists, every new payment provider can plug into it.


What We’ll Build

The payment page will evolve from:

○ Escrow.com
● Manual Payment
○ Stripe
○ PayPal

[Continue (Disabled)]

into:

○ Escrow.com
● Manual Payment
○ Stripe
○ PayPal

[Continue to Payment]

When the buyer clicks the button:

  1. The selected gateway is submitted.
  2. The selection is validated.
  3. Disabled gateways cannot be submitted.
  4. Manual Payment continues to the next step.
  5. Future gateways display an informative placeholder message.

Files We’ll Modify

Existing

includes/class-payment-page.php

Existing

includes/class-payment-manager.php

Features to Implement

1. Wrap Gateway Selection Inside a Form

Convert the payment gateway section into a proper HTML form.

The form will submit the selected gateway using the POST method.


2. Enable the Continue Button

Replace the disabled placeholder button with an active submit button.

Example:

Continue to Payment

3. Capture Buyer Selection

Read the submitted gateway using:

$_POST['payment_gateway']

Sanitize the value before processing.


4. Validate the Selected Gateway

Verify that:

  • the gateway exists
  • the gateway is currently enabled

If validation fails, display a user-friendly error message.


5. Prepare Gateway Routing

Rather than processing payments directly, create routing logic similar to:

if Manual Payment
    continue to manual payment workflow

if Escrow
    placeholder

if Stripe
    placeholder

if PayPal
    placeholder

This architecture allows future lessons to implement each gateway independently.


User Experience

Current:

Choose Gateway

Manual Payment

Continue (disabled)

After Lesson 73:

Choose Gateway

Manual Payment

Continue to Payment

Upon submission:

Selected Gateway:
Manual Payment

or

Escrow.com integration is coming soon.

depending on the selected gateway.


Architecture Improvement

Before Lesson 73:

Payment Page

↓

Display Gateways

After Lesson 73:

Payment Page

↓

Capture Form

↓

Validate Gateway

↓

Route to Selected Payment Method

↓

Future Gateway Handler

This creates a reusable payment flow that every payment provider will follow.


Benefits

By the end of this lesson, Flipnzee Auctions will have:

  • Functional payment selection form
  • Gateway validation
  • Secure handling of buyer input
  • Centralized routing logic
  • Foundation for integrating Escrow.com, Stripe, PayPal, Razorpay, and cryptocurrency payments

What We Won’t Build Yet

To keep the implementation stable, we are not implementing:

  • Escrow.com API
  • Stripe Checkout
  • PayPal Checkout
  • Razorpay API
  • Cryptocurrency payments
  • Payment confirmation
  • Webhooks
  • Automatic transaction updates

Those will be introduced in future lessons after the payment workflow has been completed.


Expected Outcome

By the end of Lesson 73, the Payment page will evolve from a static gateway selection interface into the first stage of a real checkout process. Buyers will be able to submit their chosen payment method, the plugin will validate the selection securely, and the architecture will be ready to hand control to the appropriate payment gateway implementation in future lessons.

Lesson 74: Manual Payment Instructions and Buyer Confirmation Workflow

Introduction

With the payment gateway routing architecture completed in the previous lessons, buyers can now securely select their preferred payment method. However, selecting Manual Payment currently only displays a placeholder message.

In this lesson, we’ll implement the first real payment workflow in Flipnzee Auctions by displaying manual payment instructions after the buyer selects the Manual Payment gateway.

Rather than integrating a live payment processor immediately, we’ll build a professional workflow that guides buyers through the payment process while preparing the plugin for future automation.


What We’ll Build

After selecting Manual Payment and clicking Continue to Payment, the buyer will see:

  • A payment confirmation notice
  • Transaction reference number
  • Amount to be paid
  • Payment instructions
  • Placeholder bank/account details
  • Buyer checklist
  • “I’ve Completed Payment” button
  • Architecture ready for payment verification in future lessons

Why This Matters

Many marketplace platforms begin with manual payments before integrating payment gateways.

This approach allows:

  • Faster marketplace launch
  • Manual verification by administrators
  • Easy transition to automated gateways later
  • Reusable payment workflow

The same workflow will later support:

  • Escrow.com
  • Stripe
  • PayPal
  • Razorpay
  • USDT Cryptocurrency

Learning Objectives

By the end of this lesson you will:

  • Display professional payment instructions
  • Generate a transaction reference for buyers
  • Show payment amount clearly
  • Build a buyer payment confirmation interface
  • Prepare the plugin for payment verification
  • Create a reusable payment workflow

Planned User Experience

Instead of seeing only:

Manual Payment selected.

The buyer will see something similar to:

Manual Payment

Transaction Reference:
FLIP-000001

Amount:
₹55,555,609.00

Payment Instructions

✓ Transfer the exact amount.

✓ Use the reference number.

✓ Keep your payment receipt.

✓ Click "I've Completed Payment" after payment.

[ I've Completed Payment ]

What We’ll Implement

Step 1

Replace the temporary success message with a real payment instruction section.


Step 2

Generate a payment reference number using the transaction ID.

Example:

FLIP-000001

Step 3

Display the winning bid amount prominently.


Step 4

Display manual payment instructions.


Step 5

Add a buyer checklist before payment.


Step 6

Add an I’ve Completed Payment button.

Initially this button will not update the database.

It simply prepares the workflow for the next lesson.


Files We’ll Modify

Primary file:

includes/class-payment-page.php

Possible future updates:

includes/class-payment-manager.php

Skills You’ll Learn

  • Building multi-step payment workflows
  • Creating reusable payment interfaces
  • Improving user experience
  • Structuring payment pages
  • Preparing for payment verification
  • Designing scalable payment architecture

Expected Result

By the end of Lesson 74, buyers will experience a much more realistic payment process instead of a placeholder message. They’ll receive clear payment instructions, a unique transaction reference, the payment amount, and a confirmation button that prepares the marketplace for the payment verification workflow introduced in the next lesson.


Coming Next

Lesson 75: Recording Buyer Payment Confirmation and Updating Transaction Status

In the next lesson, clicking I’ve Completed Payment will begin updating the transaction status (for example, to Awaiting Verification) and lay the groundwork for seller/admin payment verification.

Lesson 75 Implementation: Refactoring the Buyer Payment Page into Modular Components

As the Flipnzee payment system evolved, the class-payment-page.php file gradually accumulated multiple responsibilities. It was responsible for retrieving transactions, validating payment requests, rendering payment instructions, displaying transaction information, and generating the payment gateway interface.

Rather than continuing to add more features to an increasingly large method, this lesson focused on improving the internal architecture of the payment page through refactoring.

Although no new user-facing functionality was introduced, this refactoring significantly improves code readability, maintainability, and prepares the payment system for future features such as payment proof uploads, administrator verification, and live payment gateway integrations.


Why Refactor?

One of the most common problems in software development is allowing a single function to become too large.

Our original render() method was responsible for:

  • Loading transactions
  • Validating requests
  • Processing payment gateway selection
  • Rendering manual payment instructions
  • Displaying transaction information
  • Displaying the payment gateway selector

Instead of adding even more functionality to this method, we separated the interface into reusable helper methods.


Step 1 – Extract the Transaction Summary

The payment summary table was moved into its own private method.

Instead of embedding the HTML directly inside render(), we created:

private static function render_transaction_summary( $transaction ) {
?>

<h2>Payment</h2>

<table class="widefat striped">

<tr>
    <th>Transaction ID</th>
    <td><?php echo esc_html( $transaction->id ); ?></td>
</tr>

<tr>
    <th>Winning Bid</th>
    <td>
        <?php
        echo esc_html(
            number_format_i18n(
                $transaction->winning_bid,
                2
            )
        );
        ?>
    </td>
</tr>

<tr>
    <th>Status</th>
    <td><?php echo esc_html( ucfirst( $transaction->status ) ); ?></td>
</tr>

<tr>
    <th>Payment Status</th>
    <td><?php echo esc_html( ucfirst( $transaction->payment_status ) ); ?></td>
</tr>

<tr>
    <th>Payment Gateway</th>
    <td>
        <?php
        echo esc_html(
            Flipnzee_Payment_Manager::get_gateway_name( $transaction )
        );
        ?>
    </td>
</tr>

</table>

<?php
}

The main render method now simply calls:

self::render_transaction_summary( $transaction );

Step 2 – Extract the Gateway Selector

Next, the payment gateway selection form was moved into a dedicated helper method.

private static function render_gateway_selector( $gateways ) {
?>

<form method="post">

<?php
wp_nonce_field(
    'flipnzee_payment_action',
    'flipnzee_payment_nonce'
);
?>

<h3>Select Payment Method</h3>

<div class="flipnzee-payment-gateways">

<?php foreach ( $gateways as $gateway_id => $gateway ) : ?>

<p>

<label>

<input
    type="radio"
    name="payment_gateway"
    value="<?php echo esc_attr( $gateway_id ); ?>"
    <?php checked( $gateway['enabled'] ); ?>
    <?php disabled( ! $gateway['enabled'] ); ?>
>

<?php echo esc_html( $gateway['label'] ); ?>

<?php if ( ! $gateway['enabled'] ) : ?>

<em>(Coming Soon)</em>

<?php endif; ?>

</label>

</p>

<?php endforeach; ?>

</div>

<p class="flipnzee-payment-actions">

<button
    type="submit"
    name="flipnzee_payment_completed"
    class="button"
>
I've Completed Payment
</button>

<button
    type="submit"
    name="flipnzee_continue_payment"
    class="button button-primary"
>
Continue to Payment
</button>

</p>

</form>

<?php
}

This method now encapsulates the entire payment gateway interface.


Step 3 – Extract Manual Payment Instructions

The manual payment interface was also separated into its own method.

private static function render_manual_payment( $transaction ) {
?>

<div class="notice notice-success">

    <p><strong>Manual Payment Selected</strong></p>

    <p>Please complete your payment using the instructions below.</p>

</div>

<div class="flipnzee-manual-payment">

<h3>Payment Instructions</h3>

<p>Thank you for choosing Manual Payment.</p>

<p>Please use the transaction reference below when sending your payment.</p>

<table class="widefat striped">

<tr>
    <th>Reference Number</th>
    <td>
        <?php
        echo esc_html(
            'FLIP-' . str_pad(
                $transaction->id,
                6,
                '0',
                STR_PAD_LEFT
            )
        );
        ?>
    </td>
</tr>

<tr>
    <th>Amount</th>
    <td>
        <?php
        echo esc_html(
            number_format_i18n(
                $transaction->winning_bid,
                2
            )
        );
        ?>
    </td>
</tr>

<tr>
    <th>Status</th>
    <td>Awaiting Payment</td>
</tr>

</table>

<h4>Important</h4>

<ul>

<li>Include the reference number with your payment.</li>

<li>Keep proof of payment for verification.</li>

<li>Your transaction will be reviewed before ownership transfer.</li>

</ul>

</div>

<?php
}

Step 4 – Simplify the Gateway Router

Previously, the switch statement contained a large block of HTML for the Manual Payment gateway.

After refactoring, it became much cleaner:

switch ( $selected_gateway ) {

    case 'manual':

        self::render_manual_payment( $transaction );

        break;

    case 'escrow':
        // Escrow placeholder.
        break;

    case 'stripe':
    case 'paypal':
    case 'razorpay':
    case 'crypto':
        // Future gateways.
        break;

    default:
        ?>
        <div class="notice notice-error">
            <p>Unknown payment gateway.</p>
        </div>
        <?php
        break;
}

The routing logic now clearly expresses intent without mixing presentation and control flow.


Benefits of the Refactoring

This refactoring provides several long-term advantages:

  • Cleaner render() method
  • Smaller, focused helper methods
  • Easier debugging
  • Improved readability
  • Better separation of concerns
  • Simpler extension for future payment gateways
  • Reduced code duplication
  • Easier maintenance as the payment workflow grows

Testing

After completing the refactoring:

  • The Buyer Payment Page displayed exactly the same information as before.
  • Manual Payment instructions continued to work.
  • The transaction summary rendered correctly.
  • Payment gateway selection remained functional.
  • No user-facing functionality changed.
  • PHP syntax validation completed successfully.

This confirmed that the refactoring preserved existing behavior while improving the internal architecture.


Lessons Learned

As features accumulate, it is often beneficial to pause and improve the code structure before introducing additional functionality. Separating presentation into dedicated helper methods makes the codebase easier to navigate, simplifies future enhancements, and reduces the risk of introducing bugs when extending existing features.

This lesson also reinforced the importance of keeping rendering logic modular so that future components—such as payment proof uploads, administrator verification, and live gateway integrations—can be added with minimal impact on existing code.

Download Source Code

Download the starting version of the plugin before the lesson:

Download the completed version after this lesson:


Conclusion

Lesson 75 focused on improving the maintainability of the Buyer Payment Page through thoughtful refactoring. By extracting the transaction summary, payment gateway selector, and manual payment instructions into reusable helper methods, the payment page is now significantly cleaner and better organized. This modular architecture provides a solid foundation for the next stage of development, where buyers will be able to upload payment proof and administrators will verify completed payments before ownership transfer.

Lesson 75: Refactoring the Buyer Payment Page for Better Maintainability

Introduction

As new payment features were added in previous lessons, the class-payment-page.php file began taking on multiple responsibilities. It was loading transactions, processing form submissions, rendering payment instructions, displaying transaction summaries, and generating the payment gateway interface.

Before adding more complex features such as payment proof uploads, transaction status updates, and administrator verification, it became important to reorganize the code into smaller, reusable methods.

In this lesson, we refactor the Buyer Payment Page without changing its functionality. The goal is to improve readability, maintainability, and prepare the payment architecture for future enhancements.


Why Refactor?

Rather than allowing one method to grow indefinitely, we separate different responsibilities into dedicated helper methods.

Benefits include:

  • Cleaner code
  • Easier debugging
  • Better readability
  • Improved reusability
  • Simpler future development
  • Better alignment with object-oriented programming principles

What We Refactored

1. Transaction Summary

The payment summary table was moved into its own method:

private static function render_transaction_summary( $transaction )

This isolates all transaction display logic from the main rendering workflow.


2. Gateway Selection

The payment gateway form was extracted into:

private static function render_gateway_selector( $gateways )

This method now handles:

  • Gateway radio buttons
  • WordPress nonce
  • Payment action buttons

3. Manual Payment Instructions

The manual payment instructions were extracted into:

private static function render_manual_payment( $transaction )

This keeps all manual payment presentation in one place and makes future enhancements (payment proof upload, bank details, etc.) much easier.


4. Cleaner Main Render Method

Instead of containing hundreds of lines of mixed HTML and PHP, the main render() method now delegates responsibilities to helper methods, making the overall flow much easier to understand.


Architecture Before Refactoring

render()

├── Transaction Summary
├── Manual Payment HTML
├── Gateway Form
├── Payment Instructions
├── Form Processing
└── Validation

Architecture After Refactoring

render()
│
├── Process Request
│
├── render_manual_payment()
│
├── render_transaction_summary()
│
└── render_gateway_selector()

This modular approach makes each method easier to read, test, and extend.


Files Modified

includes/class-payment-page.php

Skills Learned

During this lesson, we practiced:

  • Refactoring legacy code
  • Separating responsibilities
  • Creating reusable helper methods
  • Improving object-oriented design
  • Preparing a codebase for future expansion

Outcome

Although this lesson did not introduce new user-facing functionality, it significantly improved the internal architecture of the payment system. The Buyer Payment Page is now modular, easier to maintain, and ready for upcoming features such as payment proof uploads, transaction status updates, administrator verification, and future payment gateway integrations.


Lesson 76: Uploading Payment Proof and Recording Buyer Payment Submission

Introduction

With the Buyer Payment Page now refactored into modular components, the payment system is ready for its next major milestone.

In previous lessons, buyers could:

  • View their transaction details
  • Select a payment gateway
  • Read manual payment instructions
  • Receive a unique payment reference number

However, after making a payment, there is still no mechanism for buyers to notify the marketplace or provide proof that the payment has been completed.

In this lesson, we will implement the Payment Proof Upload feature. Buyers will be able to upload a payment receipt or screenshot directly from the payment page, and Flipnzee will securely store the uploaded file while associating it with the transaction for later administrator review.

This marks the beginning of the complete manual payment verification workflow.


Why This Feature Is Important

Manual payments require evidence before ownership of a digital asset can be transferred.

Instead of asking buyers to send screenshots through email or messaging apps, Flipnzee will manage everything inside the marketplace.

Benefits include:

  • Centralized payment records
  • Better buyer experience
  • Easier administrator verification
  • Improved transaction tracking
  • Foundation for dispute resolution
  • Scalable payment workflow

What We Will Build

After completing this lesson, buyers will be able to:

  • Upload payment proof directly from the payment page.
  • Submit JPG, PNG, or PDF payment receipts.
  • Securely upload files using WordPress.
  • Associate uploaded proof with the transaction.
  • Receive confirmation that the proof has been submitted.
  • Prepare the transaction for administrator verification.

Learning Objectives

By the end of this lesson, you will learn how to:

  • Create secure file upload forms.
  • Handle multipart form submissions.
  • Validate uploaded files.
  • Upload files to the WordPress Media Library.
  • Store attachment IDs against marketplace transactions.
  • Use WordPress upload APIs safely.
  • Prepare transactions for manual verification.

Implementation Roadmap

Step 1

Extend the payment form to support file uploads.


Step 2

Add a Payment Proof upload section.


Step 3

Allow supported file formats:

  • JPG
  • JPEG
  • PNG
  • PDF

Step 4

Secure the upload using the existing WordPress nonce.


Step 5

Upload the file into the WordPress Media Library.


Step 6

Store the uploaded attachment ID against the payment transaction.


Step 7

Display a success confirmation to the buyer.


Step 8

Prepare the transaction for administrator verification.


Files We’ll Modify

Primary files:

includes/class-payment-page.php
includes/class-payment-manager.php

Depending on your current database schema, we may also update the transaction table to include a field for the uploaded payment proof (for example, an attachment ID or file reference).


Expected Buyer Workflow

Buyer Wins Auction
        │
        ▼
Open Payment Page
        │
        ▼
Choose Manual Payment
        │
        ▼
View Payment Instructions
        │
        ▼
Complete Bank Transfer
        │
        ▼
Upload Payment Proof
        │
        ▼
Payment Proof Stored
        │
        ▼
Awaiting Verification

Skills You’ll Learn

During this lesson, you’ll gain experience with:

  • WordPress file upload handling
  • Media Library integration
  • Secure file validation
  • Transaction file associations
  • Payment workflow design
  • Preparing data for administrator approval

Expected Outcome

By the end of Lesson 76, Flipnzee will support one of the most important features of a marketplace payment system: allowing buyers to submit proof of payment directly within the platform. This removes the need for external communication channels and creates a streamlined, auditable workflow for manual payment verification.


Coming Next

Lesson 77: Administrator Payment Verification Dashboard

In the next lesson, administrators will be able to:

  • Review uploaded payment proofs.
  • View associated transaction details.
  • Approve or reject submitted payments.
  • Update payment and transaction statuses.
  • Notify buyers of verification results.
  • Continue the website ownership transfer process.

This will complete the first end-to-end manual payment workflow in the Flipnzee Auction plugin.

Lesson 77: Improving the Buyer Payment Experience After Payment Proof Submission

Overview

In the previous lesson, buyers gained the ability to upload payment proof securely using WordPress’ Media Library. Uploaded receipts were successfully stored, linked to transactions, and duplicate uploads were prevented.

Although the functionality worked correctly, the user experience could still be improved. After submitting payment proof, buyers continued to see payment options and generic transaction statuses, making it unclear whether their submission had been received successfully.

In this lesson, the focus shifts from functionality to user experience by redesigning the payment page after proof submission.


Objectives

By the end of this lesson, we will:

  • Replace generic Pending messages with clearer payment statuses.
  • Hide payment options after payment proof has been uploaded.
  • Display a professional confirmation message.
  • Add a “View Uploaded Payment Proof” link.
  • Prevent buyers from attempting another payment.
  • Prepare the plugin for administrator verification.

Why This Improvement Matters

Once a buyer uploads payment proof, their next question is usually:

  • Did my upload succeed?
  • Will someone review it?
  • What happens next?

Showing payment options again creates uncertainty.

Instead, the page should reassure buyers that everything has been received and explain the next step.


Current Workflow

Current payment flow:

Auction Won
      │
      ▼
Choose Manual Payment
      │
      ▼
Complete Bank Transfer
      │
      ▼
Upload Payment Proof
      │
      ▼
Success Message
      │
      ▼
Payment Options Still Visible ❌

Desired Workflow

After this lesson:

Auction Won
      │
      ▼
Choose Manual Payment
      │
      ▼
Complete Bank Transfer
      │
      ▼
Upload Payment Proof
      │
      ▼
Payment Submitted
      │
      ▼
Waiting for Admin Verification
      │
      ▼
Payment Options Hidden

Planned Improvements

1. Improve Payment Status

Instead of displaying:

Pending

buyers should see something more meaningful, such as:

Submitted for Verification

or

Awaiting Verification

2. Hide Payment Method Selection

Once payment proof exists, buyers should no longer see:

  • Manual Payment
  • Escrow
  • Stripe
  • PayPal
  • Razorpay
  • USDT

These options are no longer relevant after submission.


3. Hide Action Buttons

Buttons such as:

  • Continue to Payment
  • I’ve Completed Payment

should disappear after payment proof submission.


4. Display a Confirmation Card

Instead of payment controls, buyers should see a professional confirmation message.

Example:

✓ Payment Proof Submitted

Reference:
FLIP-000001

Payment Status:
Submitted for Verification

Thank you.

Our team will review your payment shortly.

Ownership transfer will begin once payment has been verified.

5. Add “View Uploaded Proof”

Since the uploaded receipt already exists in the WordPress Media Library, buyers should be able to confirm exactly what they uploaded.

Example:

View Uploaded Receipt

This will help buyers verify that the correct document was submitted.


6. Improve Transaction Summary

The transaction table should become more informative.

Example:

FieldValue
Transaction1
Winning Bid₹55,555,609
Payment StatusSubmitted for Verification
Payment MethodManual Payment
Payment ProofUploaded ✓

7. Introduce a Timeline

A simple progress indicator makes the payment journey much easier to understand.

✓ Auction Won

✓ Manual Payment Selected

✓ Payment Proof Uploaded

⏳ Verification Pending

□ Ownership Transfer

8. Prepare for Admin Approval

This lesson also prepares the foundation for administrator workflows.

Future lessons will allow administrators to:

  • Review uploaded receipts.
  • Approve payments.
  • Reject invalid payment proofs.
  • Request new payment proof.
  • Notify buyers automatically.

Expected Outcome

After completing this lesson:

  • Buyers clearly understand their payment has been received.
  • Duplicate payment attempts are eliminated.
  • The payment page becomes cleaner and more professional.
  • The plugin is ready for administrator verification features.
  • The overall user experience aligns with real-world marketplace payment workflows.

What You Will Learn

Throughout this lesson, you will learn how to:

  • Render different interfaces based on transaction state.
  • Improve user experience using conditional rendering.
  • Present payment progress more clearly.
  • Separate buyer actions from administrator actions.
  • Design workflows that scale as new payment gateways are added.

Next Lesson Preview

Lesson 78 – Building the Administrator Payment Verification Dashboard

In the next lesson, we will begin the administrator side of the payment system by creating a dashboard where site administrators can:

  • View submitted payment proofs.
  • Open uploaded receipts directly from the Media Library.
  • Approve or reject payments.
  • Update transaction statuses.
  • Trigger the next stage of the website ownership transfer workflow.

This will complete the first full end-to-end manual payment verification process in the Flipnzee Auctions plugin.

Lesson 76: Building Secure Payment Proof Uploads for Manual Payments in the Flipnzee Auctions Plugin

In the previous lesson, the Flipnzee Auctions plugin was refactored to separate the payment page into reusable methods. With the foundation now in place, the next logical step was to allow buyers to securely upload proof of payment after completing a manual bank transfer or other offline payment.

This lesson focused on implementing a complete payment proof upload workflow using WordPress’ built-in Media Library functions while ensuring security through nonce verification and preventing duplicate uploads.


Objective

Implement a secure payment proof upload system that:

  • Allows buyers to upload payment receipts.
  • Stores uploaded files in the WordPress Media Library.
  • Saves the attachment ID in the transaction table.
  • Prevents duplicate uploads.
  • Displays appropriate confirmation messages.
  • Prepares the plugin for future admin verification.

Step 1 – Creating the Upload Section

A new upload section was added beneath the manual payment instructions.

<h3>Upload Payment Proof</h3>

<p>
After completing your payment, upload your receipt or screenshot below.
</p>

<form
    method="post"
    enctype="multipart/form-data"
>

Using multipart/form-data is essential whenever files are uploaded.


Step 2 – Protecting the Form with a WordPress Nonce

Every upload request should be protected against CSRF attacks.

wp_nonce_field(
    'flipnzee_upload_proof',
    'flipnzee_upload_nonce'
);

The nonce is later verified before processing the upload.


Step 3 – Creating the File Input

The upload field accepts common payment proof formats.

<input
    type="file"
    name="flipnzee_payment_proof"
    accept=".jpg,.jpeg,.png,.pdf"
    required
>

Supported file types include:

  • JPG
  • JPEG
  • PNG
  • PDF

Step 4 – Detecting Upload Requests

Inside the main render method, the plugin detects whether the buyer submitted a payment proof.

if (
    isset( $_POST['flipnzee_upload_payment_proof'] ) &&
    isset( $_FILES['flipnzee_payment_proof'] ) &&
    ! empty( $_FILES['flipnzee_payment_proof']['name'] )
) {

}

This ensures uploads are only processed when the upload button is pressed.


Step 5 – Verifying the Nonce

Before accepting any uploaded file, the nonce is validated.

if (
    ! isset( $_POST['flipnzee_upload_nonce'] ) ||
    ! wp_verify_nonce(
        sanitize_text_field(
            wp_unslash( $_POST['flipnzee_upload_nonce'] )
        ),
        'flipnzee_upload_proof'
    )
) {
    return '<p>Security check failed.</p>';
}

This protects the upload endpoint from forged requests.


Step 6 – Loading WordPress Upload Libraries

Instead of manually moving files, WordPress provides built-in upload helpers.

require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';

These libraries automatically handle:

  • Uploads
  • File validation
  • Image metadata
  • Media Library integration

Step 7 – Uploading the File

The upload is performed using WordPress’ native API.

$attachment_id = media_handle_upload(
    'flipnzee_payment_proof',
    0
);

Successful uploads immediately become Media Library attachments.


Step 8 – Saving the Attachment ID

After a successful upload, the attachment ID is saved against the transaction.

Flipnzee_Payment_Manager::save_payment_proof(
    $transaction->id,
    $attachment_id
);

This updates the transaction record with:

  • payment_proof_id
  • payment_status

Step 9 – Refreshing the Transaction

One subtle issue appeared during testing.

Although the database updated successfully, the current $transaction object still contained the old values because it had been loaded before the upload.

Refreshing the transaction solved the issue.

$transaction = Flipnzee_Payment_Manager::get_transaction(
    $transaction->id
);

This immediately reflects the latest payment information.


Step 10 – Preventing Duplicate Uploads

Instead of always displaying the upload form, the page now checks whether a payment proof already exists.

<?php if ( empty( $transaction->payment_proof_id ) ) : ?>

<!-- Upload Form -->

<?php else : ?>

<div class="notice notice-success">

    <p>

        <strong>Payment Proof Already Submitted.</strong>

        Your payment proof has already been uploaded and is awaiting verification by the Flipnzee team.

    </p>

</div>

<?php endif; ?>

This prevents accidental duplicate submissions.


Step 11 – Testing

The upload workflow was tested thoroughly.

Successful tests included:

  • Uploading PNG payment receipts.
  • Verifying files appear in the WordPress Media Library.
  • Confirming payment_proof_id is stored in the database.
  • Confirming payment_status changes to submitted.
  • Confirming duplicate uploads are prevented.
  • Confirming success messages appear after upload.

Challenges Encountered

Several issues were encountered during implementation.

Transaction Not Refreshing

Although the database updated correctly, the upload form continued appearing because the transaction object had not been refreshed after saving the payment proof.

Reloading the transaction solved this issue.


Media Library Confusion

Initially it appeared that uploads were failing because the uploaded image was not immediately visible.

The issue turned out to be Media Library filtering and caching rather than the upload itself.


Conditional Rendering

Wrapping the upload form inside a conditional block required careful placement of the opening and closing PHP tags to avoid syntax errors.

Once corrected, the page behaved exactly as expected.


What Was Achieved

By the end of this lesson, the Flipnzee Auctions plugin could:

  • Accept secure payment proof uploads.
  • Store uploaded receipts in the Media Library.
  • Save attachment IDs with transactions.
  • Prevent duplicate uploads.
  • Display confirmation messages.
  • Prepare transactions for future verification.

Lessons Learned

Several important WordPress development concepts were reinforced:

  • Always use nonces when processing forms.
  • Prefer WordPress Media APIs over custom upload code.
  • Refresh database objects after updates.
  • Use conditional rendering to improve user experience.
  • Store Media Library attachment IDs instead of file paths whenever possible.

Download Source Code

Download the starting version of the plugin before the lesson:

Download the completed version after this lesson:


Next Lesson

In Lesson 77, the payment experience will be polished further by:

  • Displaying Submitted for Verification instead of Pending.
  • Hiding payment options once proof has been uploaded.
  • Showing a cleaner buyer confirmation page.
  • Adding links to view uploaded payment proof.
  • Beginning the admin verification workflow.

This will complete the buyer-side manual payment journey and prepare the plugin for administrator approval of submitted payments.