Recurring API

PayHere Recurring API lets you accept recurring payments from your customers so that the customer has to enter the card credentials only once & then his/her card will be automatically charged as per a time period you set & authorized by the customer.

It's a simple HTML Form based POST API (similar to PayHere Checkout API) which initiate a recurring payment request and redirect your customers to PayHere Payment Gateway to securely process & authorize the recurring payment. Once the first payment is processed & recurring payment is authorized, it notifies your given URL (notify_url) about the payment status with a checksum to verify, everytime your customer's card is charged. You can use those response parameters & checksum to verify and update your system, based on the payment status.


Prerequisites

You need the following things ready to integrate your website with PayHere Recurring API.

  • Merchant ID
    • You can find your Merchant ID from Side Menu > Integrations of your PayHere Account.
  • Merchant Secret
    • You can generate a Merchant Secret for your domain/app by following below steps.
      1. Go to Side Menu > Integrations section of your PayHere Account
      2. Click 'Add Domain/App' > Enter your top level domain or App package name > Click 'Request to Allow'
      3. Wait for the approval for your domain/app (This will take up to 24 hours)
      4. Copy the Merchant Secret shown in front of your domain/app

Attention: Please note that Merchant ID is unique to your PayHere account, but Merchant Secret is specific to your integrating domain/app. Therefore, you need to add your new domains/apps & get a new Merchant Secret every time you're integrating PayHere on a new domain/app.

Integration

You need to complete the following 3 steps in order to fully integrate your website with PayHere Recurring API.

1. Redirecting Customer to PayHere Payment Gateway

Regardless of your scripting language, you can simply use an HTML Form to submit the below POST params to PayHere Payment Gateway. When the form is submitted, your customer will be securely redirected to the PayHere Payment Gateway & the customer can then enter the credentials (Card No) & securely process the payment there.


Action URL
Live    - https://www.payhere.lk/pay/checkout
Sandbox - https://sandbox.payhere.lk/pay/checkout

Required POST Parameters
  • merchant_id - PayHere Merchant ID
  • return_url - URL to redirect users when success
  • cancel_url - URL to redirect users when cancelled
  • notify_url - URL to callback the status of the payment
  • first_name - Customer’s First Name
  • last_name - Customer’s Last Name
  • email - Customer’s Email (Must be a valid email format)
  • phone - Customer’s Phone No (Must be a valid phone number)
  • address - Customer’s Address Line1 + Line2
  • city - Customer’s City
  • country - Customer’s Country
  • order_id - Order ID generated by the merchant
  • items - Item title or Order/Invoice number
  • currency - Currency Code (LKR/USD)
  • recurrence - Recurring Period (A number & a word separated by a space such as 2 Week, 1 Month, 6 Month, 1 Year, etc. Word can be ‘Week’, ‘Month’ or ‘Year’ in singular. Number can be any to define recurrent period with word.)
  • duration - Duration to charge ('Forever' if there's no time limitation. Otherwise a Number & a word separated by a space as 1 Month, 1 Year, 3 Year, ect. Word can be ‘Week’, ‘Month’, ‘Year’. Number should be compatible with the word in recurrence.)
  • amount - Total Payment Amount (eg. 1000.00)
  • hash - Generated hash value as mentioned below (*Required from 2023-01-16)

Generating 'hash' Value

You can generate the hash value using the merchant_id, order_id, amount, currency and the merchant_secret.

hash = to_upper_case(md5(merchant_id + order_id + amount + currency + to_upper_case(md5(merchant_secret))))

PHP Code sample for generating hash value:

$hash = strtoupper(
    md5(
        $merchant_id . 
        $order_id . 
        number_format($amount, 2, '.', '') . 
        $currency .  
        strtoupper(md5($merchant_secret)) 
    ) 
);

Attention: Please note that the hash parameter should not be generated in client-side since it will expose your merchant_secret.


Hash Generation for a Future Recurring Date

When recurring_start_date is specified with a future date, the amount used for generating the hash must be overridden.

For LKR transactions, use 10.00 and other currencies use 0.50 as the amount when generating the hash.

The amount parameter submitted in the payment request must still contain the actual subscription amount. Only the amount used during hash generation should be overridden.


Optional POST Parameters

  • delivery_address - Delivery Address Line1 + Line2
  • delivery_city - Delivery City
  • delivery_country - Delivery Country
  • item_name_1 - Name of Item 1
  • item_number_1 - Model number of Item 1
  • amount_1 - Unit amount of Item 1
  • quantity_1 - Quantity of Item 1
  • item_name_2 - Name of Item 2
  • item_number_2 - Model number of Item 2
  • amount_2 - Unit amount of Item 2
  • quantity_2 - Quantity of Item 2
    (You can list rest of the items also like this)
  • platform - Referring Platform
  • custom_1 - Custom param 1 set by merchant
  • custom_2 - Custom param 2 set by merchant
  • startup_fee - Additional fee or discount in 1st payment occurance
  • auto_cancel - Whether the subscription should automatically be cancelled after the maximum recurring payment retry count is exceeded. (true / false, Default: true)
  • max_retries - Maximum number of retry attempts to perform when a recurring installment payment fails. Each retry is attempted once per day. (Default: PayHere system default)
  • recurring_start_date - Date on which the recurring payment cycle should begin. Format: YYYY-MM-DD. Can be used for delayed billing, free trials, or starting the subscription on a specific date.
  • is_recovery_due - Whether missed recurring installments should be recovered during the following billing cycles. (true / false, Default: true)

Attention:

Please note that the recurring_start_date, auto_cancel, max_retries, and is_recovery_due features are currently available only in the Live environment and are not supported in the Sandbox environment.


Code Sample
<html>
<body>
<form method="post" action="https://sandbox.payhere.lk/pay/checkout">   
    <input type="hidden" name="merchant_id" value="121XXXX">    <!-- Replace your Merchant ID -->
    <input type="hidden" name="return_url" value="http://sample.com/return">
    <input type="hidden" name="cancel_url" value="http://sample.com/cancel">
    <input type="hidden" name="notify_url" value="http://sample.com/notify">  
    <br><br>Item Details<br>
    <input type="text" name="order_id" value="ItemNo12345">
    <input type="text" name="items" value="Door bell wireless"><br>
    <input type="text" name="currency" value="LKR">
    <input type="text" name="recurrence" value="1 Month">
    <input type="text" name="duration" value="Forever">
    <input type="text" name="amount" value="1000">  
    <br><br>Customer Details<br>
    <input type="text" name="first_name" value="Saman">
    <input type="text" name="last_name" value="Perera"><br>
    <input type="text" name="email" value="[email protected]">
    <input type="text" name="phone" value="0771234567"><br>
    <input type="text" name="address" value="No.1, Galle Road">
    <input type="text" name="city" value="Colombo">
    <input type="hidden" name="country" value="Sri Lanka">
    <input type="hidden" name="hash" value="098F6BCD4621D373CADE4E832627B4F6">    <!-- Replace with generated hash -->
    <input type="submit" value="Buy Now">   
</form> 
</body>
</html>

2. Listening to Payment Notification

As soon as the payment is processed, PayHere notifies the payment status to the notify_url you posted to the Checkout API as a server callback & redirects the customer back to your website to the return_url. Payment notification will contain the following data as POST params, so you need to host a script on your notify_url to fetch the following POST params & update your database accordingly.

POST params
  • merchant_id - PayHere Merchant ID of the merchant
  • order_id - Order ID sent by Merchant to Checkout page
  • payment_id - Unique Payment ID generated by PayHere for the processed payment
  • subscription_id - Unique Subscription ID generated by PayHere for the authorized subscription
  • payhere_amount - Total Amount of the payment
  • payhere_currency - Currency code of the payment (LKR/USD)
  • status_code - Payment status code (2, 0, -1, -2, -3)
  • md5sig - Encrypted signature to verify the payment
  • custom_1 - Custom param 1 sent by merchant to Checkout page
  • custom_2 - Custom param 2 sent by merchant to Checkout page
  • method - Payment method selected by the customer. (VISA, MASTER)
  • status_message - Message received from payment gateway which the customer tried to pay
  • recurring - Whether payment is a recurring payment (1:recurring, 0: not-recurring)
  • message_type - Status message (AUTHORIZATION_SUCCESS, AUTHORIZATION_FAILED, RECURRING_INSTALLMENT_SUCCESS, RECURRING_INSTALLMENT_FAILED, RECURRING_COMPLETE, RECURRING_STOPPED)
  • item_recurrence - How often it charges. eg. 1 Month
  • item_duration - How long it charges. eg. 1 Year
  • item_rec_status - Status of recurring subscription. (0: active, -1: cancelled, 1: completed)
  • item_rec_date_next - Date of next recurring installment (YYYY-MM-DD)
  • item_rec_install_paid - Number of successful recurring installments charged

    If the customer made the payment by VISA or MASTER credit/debit card, following parameters will also be available.
  • card_holder_name - Card Holder Name
  • card_no - Masked card number (Ex: ************4564)
  • card_expiry - Card expiry in format MMYY (Ex: 0122)

Payment Status Codes

  • 2 - success
  • 0 - pending
  • -1 - canceled
  • -2 - failed
  • -3 - chargedback

Payment Status Messages

  • AUTHORIZATION_SUCCESS – The initial card authorization was successful.
  • AUTHORIZATION_FAILED – The initial card authorization failed.
  • RECURRING_INSTALLMENT_SUCCESS – A scheduled recurring installment was successfully charged.
  • RECURRING_INSTALLMENT_FAILED – A scheduled recurring installment failed.
  • RECURRING_COMPLETE – The recurring plan completed after all scheduled installments were processed.
  • RECURRING_STOPPED – The recurring plan was stopped or cancelled before completion.

Attention:

  • You cannot test the payment notification by print/echo methods since notify_url never loads to the browser as it's a server callback. You can only test it by updating your database upon fetching the notification.
  • You cannot test the payment notification on localhost. You need to submit a publically accessible IP or domain based URL as your notify_url for PayHere to directly notify your server.
  • No payment status parameters are passed to the return_url when redirecting the customer back to your website. You need to update your database upon fetching payment status by your script on notify_url & then show the payment status to your customer in the page on return_url by fetching the status from your database.
  • The request parameters are encoded in the 'application/x-www-form-urlencoded' format, not 'application/json'.

How the Subscription Flow Works by Default (Live Environment)
  • By default, PayHere processes recurring installments according to the configured recurrence period until the specified duration is completed.

  • If a recurring installment is successful, PayHere captures the payment and sends a notification to the configured notify_url with:

    message_type = RECURRING_INSTALLMENT_SUCCESS
    status_code = 2
    
  • If a recurring installment fails, PayHere automatically retries the payment once per day for up to 7 consecutive days. A notification is sent to the notify_url for each failed attempt:

    message_type = RECURRING_INSTALLMENT_FAILED
    status_code = -2
    
  • During the retry period, the subscription remains active. If a retry succeeds, PayHere sends:

    message_type = RECURRING_INSTALLMENT_SUCCESS
    status_code = 2
    
  • A successful retry does not change the original recurring billing schedule. Future installments continue according to the originally configured recurrence date.

  • By default, when payments from previous billing cycles remain outstanding, PayHere attempts to recover the missed installments together with the current billing cycle. Each outstanding installment is attempted separately on consecutive days.

  • If all retry attempts fail, the subscription is automatically stopped. PayHere sends:

    message_type = RECURRING_STOPPED
    status_code = -2
    item_rec_status = -1
    
  • No further recurring installments will be attempted for that subscription.

  • This is the default PayHere recurring payment behaviour. You can customize the retry and recovery behaviour using the following parameters:

    • auto_cancel
    • max_retries
    • is_recovery_due

Testing Recurring Payments in the Sandbox Environment

When testing recurring payments in the PayHere Sandbox environment, the recurring payment cycle is accelerated so that installment notifications can be tested without waiting for the actual recurrence period.

After the initial subscription payment is successfully authorized, PayHere will automatically trigger the next recurring installment approximately one minute later.

This allows you to verify both the initial subscription authorization notification and the recurring installment notification through your notify_url.

You should receive the following notifications:

  1. Initial Subscription Authorization

    The first notification is sent after the initial subscription payment is successfully processed and the recurring payment authorization is created.

    The notification will contain:

    message_type = AUTHORIZATION_SUCCESS
    status_code = 2
    
  2. Recurring Installment

    Approximately one minute after the initial authorization, PayHere will trigger a recurring installment payment and send another notification to your notify_url.

    The notification will contain:

    message_type = RECURRING_INSTALLMENT_SUCCESS
    status_code = 2
    

This Sandbox behaviour allows you to confirm that your application correctly receives and processes recurring installment notifications without having to wait for the actual subscription interval, such as 1 Month, 3 Month, or 1 Year.

Attention:

  • The accelerated recurring installment timing applies only to the Sandbox environment for testing purposes.
  • In the Live environment, recurring installments are processed according to the recurrence value specified when the subscription is created.
  • Always identify the type of recurring notification using the message_type parameter rather than relying on the time at which the notification is received.
  • Your notify_url should independently verify every notification using the md5sig verification process described below before updating payment or subscription records.

3. Verifying the Payment Status

It is critical to verify the Payment Notification before taking any actions on the payment response. You can do the verification using the md5sig checksum parameter that is generated & sent by PayHere along with the payment status params according to following logic.

md5sig = strtoupper(
    md5 (
        merchant_id + 
        order_id + 
        payhere_amount + 
        payhere_currency + 
        status_code + 
        strtoupper(md5(merchant_secret)) 
    ) 
)

Once you receive the payment status parameters from PayHere, you can locally generate this checksum using the merchant_id, order_id, payhere_amount, payhere_currency & status_code sent by the payment notification and the merchant_secret you have locally. Your locally generated checksum should be equal to the md5sig sent by PayHere if the payment notification is valid.


Code Sample (PHP)

You can host this script at your notify_url.

<?php

$merchant_id        = $_POST['merchant_id'];
$order_id           = $_POST['order_id'];
$payhere_amount     = $_POST['payhere_amount'];
$payhere_currency   = $_POST['payhere_currency'];
$status_code        = $_POST['status_code'];
$md5sig             = $_POST['md5sig'];

$merchant_secret = 'XXXXXXXXXXXXX'; // Replace with your Merchant Secret

$local_md5sig = strtoupper(
    md5(
        $merchant_id . 
        $order_id . 
        $payhere_amount . 
        $payhere_currency . 
        $status_code . 
        strtoupper(md5($merchant_secret)) 
    ) 
);
       
if (($local_md5sig === $md5sig) AND ($status_code == 2) ){
        //TODO: Update your database as payment success
}

?>

Attention:

  • Please make sure to consider the payment as successful only after the verfification, as it ensures that the payment notification received to your notify_url was genuinely initiated by PayHere, not by any other party.
  • If you do not implement this payment verification properly, there's a security risk of a third party sending a manipulated payment notification to your notify_url, falsly notifing that the payment is successful.

4. Error Handling

Error messages may returned for above APIs end points due to the following reasons. You need to identify them & handle them properly in your application.

Unauthorized Payment Request

An Unauthorized payment request error may occur due to an invalid hash value or an unrecognized domain.

Please verify the following.

  • Ensure the values used to generate the hash exactly match the values submitted in the payment request.
  • Initiate the payment from the same domain registered in the Integration section of the PayHere Merchant Portal.
  • Use the correct Merchant Secret associated with that registered domain.
  • Ensure the browser request includes the Referer header. Avoid configurations such as no-referrer that suppress it.
  • Ensure only one Integration record exists per domain in the PayHere Merchant Portal. Duplicate domain records are not supported and may cause validation issues.

Unexpected Error Occurred (PH-0022)

An Unexpected Error (PH-0022) may occur if an incorrect PayHere Action URL is used.

For the Live environment, ensure that the Action URL includes www and is set exactly as follows:

https://www.payhere.lk/pay/checkout

Using the Live checkout URL without www may result in the PH-0022 error.


Unexpected Error for HelaPay Payments

An Unexpected Error may occur when initiating a HelaPay payment if the required customer contact details are missing or invalid.

Please ensure that a valid email address and valid phone number are included when initiating the payment request.


Something Went Wrong (Error Code)

A Something Went Wrong error may occur when the payment request contains invalid or incomplete data.

Please verify the following:

  • Ensure that all required POST parameters are included in the request payload.
  • Ensure that parameter values contain only valid and supported data.
  • Avoid unsupported content such as emojis, script tags, or executable/script content within parameter values.

If the error persists, review the submitted payload and confirm that all required parameters and values comply with the API requirements.

Recurring API - PayHere Knowledge Base
Still need help? Get in touch!
Last updated on 4th Sep 2026