Get Started Card Payments Check Payments Recurring Payments Gift & Loyalty Payments Fraud Plugins & Integrations Reporting Demo Heartland Pay App Other Solutions Fully Integrated POS Middleware Connectors Portico Gateway Documentation API Overview Gift & Loyalty Bill Pay Payroll Introduction Developer Support Authentication Credit Card Gift Card eCheck & ACH Recurring Reporting Error Handling Semi-Integrated Devices Heartland Profac Global Payments eCommerce Tokenization Demo Testing Knowledge Center GitHub Partnerships
Get sandbox account
Get sandbox account

Recurring Payments

Heartland’s recurring product, PayPlan, allows you to easily setup reccuring payments on a tokenized credit card.

Create a Recurring Customer

Create a Customer

using GlobalPayments.Api.Entities;

var customer = new Customer {
            Id = GenerateCustomerId(),
            Status = "Active",
            FirstName = "Bill",
            LastName = "Johnson",
            Company = "Heartland Payment Systems",
            Address = new Address {
            StreetAddress1 = "987 Elm St",
            City = "Princeton",
            Province = "NJ",
            PostalCode = "12345",
            Country = "USA"
            HomePhone = "9876543210",
            WorkPhone = "9876543210",
            Fax = "9876543210",
            MobilePhone = "9876543210",
            Email = ""

Address address = new Address();
address.setStreetAddress1("987 Elm St");

Customer customer = new Customer();
customer.setCompany("Heartland Payment Systems");
customer = customer.create();
use GlobalPayments\Api\Entities\Address;
use GlobalPayments\Api\Entities\Customer;

$customer = new Customer();
$customer->id = generateCustomerId();
$customer->firstName = 'John';
$customer->lastName = 'Doe';
$customer->status = 'Active';
$customer->email = '';
$customer->address = new Address();
$customer->address->streetAddress1 = '123 Main St.';
$customer->address->city = 'Dallas';
$customer->address->province = 'TX';
$customer->address->postalCode = '75024';
$customer->address->country = 'USA';
$customer->workPhone = '5551112222';
$customer = $customer->create();
# coming soon
# coming soon
import { Address, Customer } from "globalpayments-api";

let customer = new Customer(); = generateCustomerId();
customer.firstName = "John";
customer.lastName = "Doe";
customer.status = "Active"; = "";
customer.address = new Address();
customer.address.streetAddress1 = "123 Main St."; = "Dallas";
customer.address.state = "TX";
customer.address.postalCode = "98765"; = "USA";
customer.workPhone = "5551112222";
customer = await customer.create();

Create a Payment Method for a Customer

Follow the example below to create a new Payment Method for a customer.The object payPlanService was defined in the customer creation example code above.

Create a Payment Method

using GlobalPayments.Api.PaymentMethods;

var paymentMethod = customer.AddPaymentMethod(
            new CreditCardData {
            Number = "4111111111111111",
            ExpMonth = 12,
            ExpYear = 2025

CreditCardData card = new CreditCardData();

RecurringPaymentMethod paymentMethod = customer.addPaymentMethod(
use GlobalPayments\Api\PaymentMethods\CreditCardData;
use GlobalPayments\Api\PaymentMethods\RecurringPaymentMethod;

$card = new CreditCardData();
$card->number = '4111111111111111';
$card->expMonth = '12';
$card->expYear = '2025';

$paymentMethod = $customer->addPaymentMethod(
# coming soon
# coming soon
import { CreditCardData } from "globalpayments-api";

const card = new CreditCardData();
card.number = "4111111111111111";
card.expMonth = "12";
card.expYear = "2025";

const paymentMethod = await customerPerson
            .addPaymentMethod(generatePaymentMethodId(), card)

Create a Recurring Plan for a Payment Method

Use the following example to create a new subscription plan for a given Payment Method. The objects payPlanService and paymentMethod were defined in earlier example code.

Create a Schedule

using GlobalPayments.Api.Entities;

var schedule = paymentMethod.AddSchedule(GenerateScheduleId())
import java.math.BigDecimal;

Schedule schedule = paymentMethod.addSchedule(generateScheduleId())
            .withAmount(new BigDecimal("30.02"))
use GlobalPayments\Api\Entities\Enums\ScheduleFrequency;

$schedule = $paymentMethod->addSchedule(
            ->withStartDate(\DateTime::createFromFormat('Y-m-d', '2027-02-01'))
            ->withEndDate(\DateTime::createFromFormat('Y-m-d', '2027-04-01'))
# coming soon
# coming soon
import {
} from "globalpayments-api";

const schedule = await paymentMethodVisa
            .withStartDate(new Date(2027, 1, 1))

Failed Scheduled Transactions

A schedule with a Failed status is an indication that the merchant must reach out to the customer to obtain new payment information. PayPlan has an email notification that a merchant can opt in to receive a list of all schedules that failed during the nightly processing. If a card exceeds retries with non-fatal decline codes, then the schedule status changes to Failed but the payment status remains Active. This email notification is based on emailReceipt and emailAdvanceNotice.


There is logic in place for recurring billing; it allows us to drop the expiration date on a card if it is less than current MMYYYY and it’s a recurring billing transaction. Eventually these will decline and/or trigger a fatal error. -below-


If a card is declined when processing a schedule, the “Failure Count” field is incremented. If the Failure Count exceeds the reprocessing count, then the schedule status is also updated to ‘Failed’. Each subsequent try is aproximately 24 hours after.

Communication Failures

If there is a communication failure, the schedule will fall into an error queue and no updates will be made. Any schedule in the error queue is manually reviewed the next business day. This is exceptionally rare and we do not typically provide any information to a merchant when an instance occurs.

Other fatal errors

  • Invalid
  • Expired
  • Lost/Stolen
  • Revoked

A payment status can be set to something other than Active/Inactive only by the “fatal” response codes from an issuer during schedule processing; a one-time transaction does not change a payment method status. For example, if we process a schedule with a card and the issuer returns lost/stolen, then the schedule status changes to failed and the payment status changes to Lost/Stolen. If a card exceeds retries with non-fatal decline codes, then the schedule status changes to Failed but the payment status remains Active.

Once the payment method is updated an edit can be performed to restart the schedule.

Find Customers

You can find customers associated using this method call.

Find Customers

using GlobalPayments.Api.Entities;

var customers = Customer.FindAll();
import java.util.List;

List<Customer> customers = Customer.findAll();
use GlobalPayments\Api\Entities\Customer;

$customers = Customer::findAll();
# coming soon
# coming soon
import { Customer } from "globalpayments-api";

const customers = await Customer.findAll();

Find Payment Methods

You can find payment methods associated using this method call.

Find Payment Methods

using GlobalPayments.Api.PaymentMethods;

var paymentMethods = RecurringPaymentMethod.FindAll();
import java.util.List;

List<RecurringPaymentMethod> paymentMethods = RecurringPaymentMethod.findAll();
use GlobalPayments\Api\PaymentMethods\RecurringPaymentMethod;

$paymentMethods = RecurringPaymentMethod::findAll();
# coming soon
# coming soon
import { RecurringPaymentMethod } from "globalpayments-api";

const paymentMethods = await RecurringPaymentMethod.findAll();

Find Schedules

You can find schedules associated using this method call.

Find Schedules

using GlobalPayments.Api.Entities;

var schedules = Schedule.FindAll();
import java.util.List;

List<Schedule> schedules = Schedule.findAll();
use GlobalPayments\Api\Entities\Schedule;

$schedules = Schedule::findAll();
# coming soon
# coming soon
import { Schedule } from "globalpayments-api";

const schedules = await Schedule.findAll();

Edit Schedule

Conditional Parameters

scheduleStarted returns true if at least one transaction in the schedule has processed even if it has declined. The PHP-SDK will drop illegally passed fields during the edit call. More complete code examples can be found in the SDK

scheduleStarted = False

The following fields may only be edited when the schedule has not started processing

Parameter Description
Schedule identifier String 50 character max
Start date date in DDMMYYYY format
Frequency Weekly, Bi-Weekly, Semi-Monthly, Monthly, Bi-Monthly, Quarterly, Semi-Annually, Annually
Duration Ongoing, Limited Number, End Date

scheduleStarted = true

Once the schedule has started processing then the following field becomes editable

Parameter Description
Next processing date date in DDMMYYYY format
Cancellation date date in DDMMYYYY format

Edit Schedule

using GlobalPayments.Api.Entities;

var schedule = Schedule.Find(GetScheduleId());
schedule.Status = "Inactive";

Schedule schedule = Schedule.find(getScheduleId());
use GlobalPayments\Api\Entities\Schedule;

$schedule = Schedule::find(getScheduleId());
$schedule->status = 'Inactive';
# coming soon
# coming soon
import { Schedule } from "globalpayments-api";

const schedule = await Schedule.find(getScheduleId());
schedule.status = "Inactive";
await schedule.saveChanges();