Overpayments with WHMCS and PayPal

Back   Posted on 21 april 2022 / Updated on 26 april 2022
Reading time 5 minutes

Duplicate Subscription and Payment

With PayPal Subscriptions you can bill customers for services at regular intervals. Services and domains are automatically renewed by PayPal before expiration.

You'll never need to chase up forgetful customers again however, there are some significant downsides to using PayPal recurring payments.

The screenshot below comes from what looks like a normal proforma.

In reality this a proforma with an active subscription that PayPay is set to automatically pay as soon as the due date arrives.

There are no details about the existing subscription. Even worse WHMCS allows customers to send payments manually before PayPal performs the auto-renew. This results into overpayments.

On WHMCS 7.8 and older, the issue is even bigger since WHMCS not only allows manual payments but also the creation of new subscriptions on top of an existing ones.

As result, you are going to have duplicate subscriptions causing overpayments on every cycle. This is a cause of frustration for clients and a time drain for your staff.


Starting from WHMCS v7.9, there have been improvements. The new PayPal Checkout module adds a notice to inform customers about existing subscriptions.

You can find this new module in Features Payment Gateways section.

Sadly this implementation has some shortcomings:

  • Customers can still press Make a Payment causing overpayments
  • You must upgrade to v7.9 and comply to new pricing model
  • Not everyone likes or can use the new PayPal module

The good news is that Billing Extension can help you solve this issue.

Preventing Overpayments

Similarly to WHMCS PayPal Checkout module, Billing Extension validates subscription status via API however similarities end here.

The first difference is that, unlike WHMCS implementation, our module avoids being overpaid. As you can see from the screenshot below, proformas with active subscriptions can't be (over)paid with PayPal.

Another difference is that on a daily basis we perform scheduled checks to validate all subscription. This way customers can quickly find domains and services with active subscriptions from the following pages:

  • My Services
  • My Domains
  • Service Details
  • Domain Details

In Client Area Integration we describe how to customize the view of such pages but first let us explain you how to configure and use this feature.

PayPal API

Generate or recover your API credentials from developer.paypal.com. You can login with your PayPal credentials. Once logged in, enabe Live mode.

Now scroll down and press Create App button that you can name as you like (eg. your company name). This will provide you Client ID and Secret parameters that needs to be copy/pasted in Billing Extension.

Client Area Integration

We decided not to inject hardcoded HTML blocks to your template and let you freely customzie the look of your client area.

Before we start, keep in mind we store every detail about PayPal Subscriptions in bx_paypalsubscriptions table. You can find things like full business details, remaining cycles, end date, last payment and much more.

When it comes to dates, they are automatically formatted according to your General Settings > Localisation > Client Date Format.

You can easily review such variables in {$kata_ppsubscriptions} array by using {debug} in the following Smarty templates:

  • clientareadomains.tpl - My Domains
  • clientareaproducts.tpl - My Products/Services
  • clientareadomaindetails.tpl - Domain Details
  • clientareaproductdetails.tpl - Product/Service Details

Below you find some examples you can adapt to your needs.

We included subscription details in clientareadomaindetails.tpl by simply adding a new column next to "Status" like follows.

<div class="row">
<div class="col-sm-offset-1 col-sm-5">
<h4><strong>{$LANG.clientareastatus}:</strong></h4> {$status}
{if $kata_ppsubscriptions}
<div class="col-sm-6">
<h4><strong>Next Automatic Payment:</strong></h4> {$kata_ppsubscriptions.billing_info.next_billing_time} <span data-toggle="tooltip" data-placement="bottom" title="Subscription ID: {$kata_ppsubscriptions.id}"><i class="fa fa-info-circle"></i></span>

The if statement ensures subscription block only shows up on domains with an active subscription. There's also an icon that shows more details on mouse hover.

The same approach can be used in clientareaproductdetails.tpl. Here we added subscription details right below "Payment Method".

Here is the code.


{if $kata_ppsubscriptions}
<h4>Next Automatic Payment:</h4>
{$kata_ppsubscriptions.billing_info.next_billing_time} <span data-toggle="tooltip" data-placement="bottom" title="Subscription ID: {$kata_ppsubscriptions.id}"><i class="fa fa-info-circle"></i></span>

As for tables in My Domains (clientareadomains.tpl) and My Services (clientareaservices.tpl), you must be familiar with arrays. Let's begin with My Domains.

We made a small change to "Next Due" colum to include PayPal icon on domains with an active subscription. The icon shows additional information on mouse hover. Here is the code.

<span class="hidden">{$domain.normalisedNextDueDate}</span>
{if $kata_ppsubscriptions[$domain.id]}
<span data-toggle="tooltip" data-placement="bottom" data-html="true" title="Next Payment: {$kata_ppsubscriptions[$domain.id].billing_info.next_billing_time}<br>ID: {$kata_ppsubscriptions[$domain.id].id}"><i class="fab fa-paypal text-info" aria-hidden="true"></i></span>

The magic is inside the if statement. First thing first, the if ensures the icon doesn't show up on domains with no existing subscription.

As for the rest, we are including subscriptions details in the tooltip. The same change can be replicated in My Services page.

The code is almost the same.

<td class="text-center">
<span class="hidden">{$service.normalisedNextDueDate}</span>
{if $kata_ppsubscriptions[$service.id]}
<span data-toggle="tooltip" data-placement="bottom" data-html="true" title="Next Payment: {$kata_ppsubscriptions[$service.id].billing_info.next_billing_time}<br>ID: {$kata_ppsubscriptions[$service.id].id}"><i class="fab fa-paypal text-info" aria-hidden="true"></i></span>

Comments (0)

Speak Your Mind Cancel Reply