Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/manoj-intellect/paypal-additional-charge/archive/refs/heads/main.zipCreate additional PayPal payment requests (invoices) for existing WooCommerce orders — without any direct PayPal API integration.
Overview
This plugin adds an Additional PayPal Charge meta box to the WooCommerce order edit screen. From there, an administrator can create a brand-new WooCommerce order that acts as an invoice for an additional amount and is paid through WooCommerce PayPal Payments using WooCommerce's standard Pay for Order flow.
It relies entirely on existing WooCommerce infrastructure:
- WooCommerce
- WooCommerce PayPal Payments
- The built-in Customer Invoice email
- The built-in Pay for Order page
There is no PayPal REST SDK and no direct PayPal API call. The payment is processed 100% through WooCommerce PayPal Payments.
Features
- 🧾 Additional charge meta box on the order edit page — amount, reason, and a Create Payment Order button.
- 🆕 Creates a real WooCommerce order for the same customer, carrying over billing, shipping, email, phone, currency, and tax context (but not the original products).
- 💵 Single Fee line item (
Additional charge for Order #111657) — the order total equals the fee amount. - 💳 PayPal Payments + Pending Payment — the customer pays via the standard Pay for Order page.
- ✉️ Automatic Customer Invoice email with a Pay for this order link, reusing WooCommerce's own email template.
- 🔗 Bidirectional linking between the original order and the payment order.
- 🚫 Duplicate protection — won't create a second request while one is pending, processing, on-hold, or paid.
- 🔄 Payment sync — when the payment order is paid, the original order is updated with the status, date, amount, transaction ID, and an order note.
- ⚡ Full HPOS compatibility — uses WooCommerce CRUD APIs only; no direct
wp_posts/wp_postmetaqueries. - 🧩 Fully isolated from the Authorize.Net additional charge plugin — both can run side by side.
How it works
flowchart TD
A[Admin opens order #111657] --> B[Enters amount + reason<br/>in meta box]
B --> C[Clicks Create Payment Order]
C --> D[New order #111658 created<br/>same customer, Fee item, PayPal, Pending]
D --> E[Customer Invoice email sent<br/>with Pay for this order link]
E --> F[Customer pays via<br/>WooCommerce PayPal Payments]
F --> G[Original order #111657 updated:<br/>status Paid, date, amount, txn ID, note]
- Open an order and use the Additional PayPal Charge meta box.
- Enter an amount and a reason, then click Create Payment Order.
- The plugin creates a new order for the same customer with a single Fee item, set to PayPal Payments + Pending Payment.
- WooCommerce's built-in Customer Invoice email is sent with a Pay for this order link.
- When the customer pays, the original order is updated automatically with the payment result and an order note.
Installation
- Copy the
paypal-additional-chargefolder intowp-content/plugins/. - In WP Admin → Plugins, activate PayPal Additional Charge for WooCommerce.
- Ensure WooCommerce and WooCommerce PayPal Payments are installed and active.
Requirements: WordPress 5.8+, WooCommerce, WooCommerce PayPal Payments, PHP 7.4+.
Usage
- Go to WooCommerce → Orders and open any order.
- In the Additional PayPal Charge meta box (right sidebar):
- Enter the Charge amount (required, must be greater than zero).
- Enter a Reason / description (required).
- Click Create Payment Order.
- A confirmation prompt appears; confirm to create the order and email the customer.
- The meta box then shows the linked payment order, its status, and dates.
Once paid, the original order's meta box shows:
Additional Payment
Amount: $10.00
Reason: Additional charge for Order #111657
Status: Paid
Payment Order: #111658 ← clickable
Created: July 3 2026
Paid: July 4 2026
Architecture
The plugin follows a clean, modular, object-oriented structure with one responsibility per file.
paypal-additional-charge/
│
├── paypal-additional-charge.php # Bootstrap, constants, HPOS declaration
│
├── includes/
│ ├── class-admin.php # Meta box UI, validation, processing, notices
│ ├── class-order-manager.php # Payment order creation, Fee item, linking, dup guard
│ ├── class-payment-sync.php # Syncs paid status back to the original order
│ ├── class-email.php # Customer Invoice email integration
│ ├── class-meta.php # Meta keys + typed getters (single source of truth)
│ ├── class-hpos.php # HPOS detection helpers
│ └── class-helper.php # Gateway detection, admin URLs, formatting
│
├── assets/
│ ├── admin.css
│ └── admin.js
│
├── languages/
│ └── paypal-additional-charge.pot
│
├── readme.txt # WordPress.org-style readme
└── README.md # This file
| Module | Responsibility |
|---|---|
PAC_Plugin |
Bootstrap, constants, text domain, HPOS + blocks compatibility declaration |
PAC_Admin |
Meta box rendering, form validation, submission handling, admin notices |
PAC_Order_Manager |
Creates the payment order, copies context, adds the Fee item, links both orders, enforces duplicate prevention |
PAC_Payment_Sync |
Mirrors the paid result (status, date, amount, transaction ID, note) back to the original order |
PAC_Email |
Triggers WooCommerce's Customer Invoice email and injects the requested intro copy |
PAC_Meta |
Central registry of all meta keys, status constants, and typed getters |
PAC_HPOS |
HPOS detection and order-screen ID resolution |
PAC_Helper |
PayPal gateway detection, HPOS-aware admin URLs, price/date formatting |
Order linking (metadata)
Both orders reference each other so the relationship survives independently.
Original order
| Meta key | Description |
|---|---|
_paypal_additional_payment_order_id |
Linked payment order ID |
_paypal_additional_payment_status |
pending | paid |
_paypal_additional_amount |
Charge amount |
_paypal_additional_reason |
Reason / description |
_paypal_additional_created_date |
When the request was created |
_paypal_additional_paid_date |
When it was paid (set on completion) |
_paypal_additional_transaction_id |
PayPal transaction ID (if available) |
Payment order
| Meta key | Description |
|---|---|
_original_order_id |
The source order ID |
_is_additional_payment_order |
yes flag identifying our orders |
_additional_payment_reason |
Reason / description |
_additional_payment_amount |
Fee amount |
Duplicate protection
While a linked payment order is pending, processing, on-hold, or completed/paid, the meta box disables creating another request and explains why, with a link to the existing payment order. This is enforced both in the UI and inside PAC_Order_Manager::create_payment_order().
No new email template is created. The plugin triggers WooCommerce's built-in Customer Invoice email (WC_Email_Customer_Invoice) and — only for orders it created — injects the requested intro copy via the woocommerce_email_before_order_table hook:
Hi John,
An order has been created for you on Tactical Scorpion Gear.
Your order details are below, with a link to make payment when you're ready.
[ Pay for this order ]
The email retains WooCommerce's standard layout: company logo, order title, order summary, fee line item, total, customer note, billing address, and footer.
HPOS compatibility
- Declares compatibility with High Performance Order Storage (
custom_order_tables) and Cart/Checkout Blocks. - All order access goes through WooCommerce CRUD (
wc_get_order,$order->get_meta(),$order->update_meta_data(),$order->save()). - No direct
wp_posts/wp_postmetaqueries and no deprecated WooCommerce functions.
Compatibility
| Requirement | Status |
|---|---|
| WooCommerce | ✅ Required |
| WooCommerce PayPal Payments | ✅ Required |
| HPOS | ✅ Compatible |
| Customer Invoice email | ✅ Reused |
| Pay for Order page | ✅ Reused |
| Authorize.Net dependency | ❌ None |
| PayPal REST SDK / API calls | ❌ None |
Development notes
- Follows WordPress Coding Standards and object-oriented PHP.
- All input is sanitized; all output is escaped.
- Nonce verification and capability checks (
edit_shop_order) on every action. - Isolated namespacing (
PAC_classes,_paypal_additional_*meta) so it never collides with the Authorize.Net plugin.
Lint all PHP files:
for f in paypal-additional-charge.php includes/*.php; do php -l "$f"; done
Regenerate the translation template:
wp i18n make-pot . languages/paypal-additional-charge.pot
Changelog
1.0.0
- Initial release.
License
Released under the GPLv2 (or later) license, consistent with WordPress and WooCommerce.