Menu

Recurring Payments

This guide explains how to create a recurring payment (also known as a subscription payment).


First payment

The first time your customer makes a recurring payment, you must take their card details using one of the methods above. If using tokens, remember to set Worldpay.reusable = true so we can store the card details on your behalf. You will need to securely store the token or card details in your database in order to take subsequent payments from this customer.

The first payment should otherwise be completed in the same way as a non-recurring payment (please refer to the relevant guide section for instructions on this, depending on the payment type you are making).

Card Scheme rules mean we have to process recurring payments slightly differently from other types of payments, so you need to set orderType to RECURRING (see the code sample below).

Note: Both recurring payments andcard-on-file paymentsuse reusable tokens, but only recurring payments have orderType set as RECURRING. For card-on-file payments, orderType is "ECOM" (or unspecified because the default is ECOM).

Subsequent payments

For subsequent payments, you already store your customer's card details. You canmake the payments directly from your serverwithout any customer interaction. As before, you will need to set orderType to RECURRING.

Below are code examples of how to make a recurring payment from your server:

Copied!
curl https://api.worldpay.com/v1/orders 
-H "Authorization: your-test-service-key" 
-H "Content-type: application/json" 
-X POST 
-d '{ 
    "token": "your-order-token", 
    "orderType": "RECURRING", 
    "orderDescription": "your-order-description", 
    "amount": 500, 
    "currencyCode": "GBP" }' 

The response to this call will include the following JSON object:

{ 
    "orderCode": "worldpay-generated-order-code", 
    "token": "your-order-token", 
    "orderDescription": "your-order-description", 
    "amount": 500, 
    "currencyCode": "GBP", 
    "paymentStatus": "SUCCESS", 
    "paymentResponse":
    { 
        "type": "ObfuscatedCard", 
        "name": "name-of-shopper", 
        "expiryMonth": "7", 
        "expiryYear": "2018",
        "cardType": "VISA", 
        "maskedCardNumber": "**** **** **** 1111" }, 
    "environment": "TEST" 
}
$worldpay = new Worldpay('your-test-service-key');

try {
    $response = $worldpay->createOrder(array(
        'token' => 'your-reusable-token',
        'amount-in-cents' => 500,
        'currencyCode' => 'GBP',
        'name' => 'test name',
        'billingAddress' => array(
            'address1'=>'123 House Road',
            'address2' => 'A village',
            'address3'=> '',
            'postalCode' => 'EC1 1AA',
            'city' => 'London',
            'state' => '',
            'countryCode'=> 'GB',
        ),
        'orderType' => 'RECURRING',
        'orderDescription' => 'your-order-description',
        'customerOrderCode' => 'your-order-code'
    ));
    if ($response['paymentStatus'] === 'SUCCESS') {
        $worldpayOrderCode = $response['orderCode'];
    } else {
        throw new WorldpayException(print_r($response, true));
    }
} catch (WorldpayException $e) {
    echo 'Error code: ' .$e->getCustomCode() .'

    HTTP status code:' . $e->getHttpStatusCode() . '

    Error description: ' . $e->getDescription()  . '

    Error message: ' . $e->getMessage();
} catch (Exception $e) {
    echo 'Error message: '. $e->getMessage();
}
worldpay = Worldpay.new('your-test-service-key')

begin

    response = worldpay.createOrder({
        'token' => 'your-reusable-token',
        'amount' => 500,
        'currencyCode' => 'GBP',
        'name' => 'test name',
        'billingAddress' => {
            "address1"=>'123 House Road',
            "address2"=> 'A village',
            "address3"=> '',
            "postalCode"=> 'EC1 1AA',
            "city"=> 'London',
            "state"=> '',
            "countryCode"=> 'GB'
        },
        'orderType' => 'RECURRING',
        'orderDescription' => 'Order description',
        'customerOrderCode' => 'Order code'
    })

    if (response['body']['paymentStatus'] === 'SUCCESS')
        @_worldpayOrderCode = response['body']['orderCode']
    else
        raise response.to_s
    end
rescue Exception => e
    print e.to_s
end
WorldpayRestClient restClient = new WorldpayRestClient("https://api.worldpay.com/v1", "your-test-service-key");

var address = new Address()
{
    address1 = "123 House Road",
    address2 = "A village",
    city = "London",
    countryCode = CountryCode.GB,
    postalCode = "EC1 1AA"
};

var orderRequest = new OrderRequest()
{
    token = "your-reusable-token",
    amount = 500,
    currencyCode = CurrencyCode.GBP,
    name = "test name",
    billingAddress = address,
    orderType = OrderType.RECURRING,
    orderDescription = "Order description",
    customerOrderCode = "Order code"
};

try
{
    OrderResponse orderResponse = restClient.GetOrderService().Create(orderRequest);

    string orderCode;
    if (orderResponse.paymentStatus == OrderStatus.SUCCESS)
    {
        orderCode = orderResponse.orderCode;
    }
    else
    {
        throw new WorldpayException("Expected order status SUCCESS");
    }
}
catch (WorldpayException e)
{
    Console.WriteLine("Error code:" + e.apiError.customCode);
    Console.WriteLine("Error description: " + e.apiError.description);
    Console.WriteLine("Error message: " + e.apiError.message);
}
WorldpayRestClient restClient = new WorldpayRestClient("your-test-service-key");

OrderRequest orderRequest = new OrderRequest();
orderRequest.setToken("your-order-token");
orderRequest.setAmount(500);
orderRequest.setCurrencyCode(CurrencyCode.GBP);
orderRequest.setName("test name");
orderRequest.setOrderDescription("Order description");
orderRequest.setCustomerOrderCode("Order code");
orderRequest.setOrderType("RECURRING");

Address address = new Address();
address.setAddress1("123 House Road");
address.setAddress2("A village");
address.setCity("London");
address.setCountryCode(CountryCode.GB);
address.setPostalCode("EC1 1AA");
orderRequest.setBillingAddress(address);

try {
    OrderResponse orderResponse = restClient.getOrderService().create(orderRequest);
    System.out.println("Order code: " + orderResponse.getOrderCode());
} catch (WorldpayException e) {
    System.out.println("Error code: " + e.getApiError().getCustomCode());
    System.out.println("Error description: " + e.getApiError().getDescription());
    System.out.println("Error message: " + e.getApiError().getMessage());
}

The ability to schedule regular subscription payments is on ourfeatures roadmap. This means that after you have obtained a reusable token, your server will need to trigger every subsequent payment.

Recurring decline codes

If you use recurring payments, Visa have 'stop payment' decline codes that aim to reduce cardholder complaints caused by recurring transactions appearing on shopper bank statements after they have cancelled a recurring transaction. These codes are:

  • R1 - Revocation of authorization order
  • R3 - Revocation of all authorizations order

In the response, these look like:

Copied!
"iso8583status": "973 - Revocation of Authorization Order"
"iso8583status": "975 - Revocation of All Authorizations Order"

What should I do if I receive one of these decline codes?

You must ensure that your systems can process these decline codes. This may require you to make changes to your systems.

If you receive a R1 or R3 decline code, you must stop any further authorization attempts against the card used and notify the cardholder.

For more information about these decline codes, seeWhat is ISO8583?.

Suggested next steps: