Recurring payments are simply resales performed in specific time periods. You can perform them based on any conditions defined by your business model.
Resales do not require all the credit/debit card and customer information. That is why they have to use either a single transaction (usually the first transaction which initiated the whole recurring procedure) or a card authorization.
This means we can define two types of recurring payments:
Important: Your first sale/authorization should always check 3D – Secure.
For recuring payment based on transaction use 3DSecure-authSale and for based on card authorization use 3DSecure-auth
.
Recurring payments based on transactions
Start with preparing the necessary information to perform a resale. In order to do that you must first retrieve the ID number of a previous transaction. This ID number identifies the transaction in PayLane’s systems. You can easily retrieve this number while performing the transaction, for example:
1 2 | $status = $client->cardSale($card_params); $id_first_sale = $status['id_sale']; |
1 2 | status = client.card_sale(card_params) id_first_sale = status['id_sale'] |
1 2 | status = client.card_sale(card_params) id_first_sale = status['id_sale'] |
1 2 3 4 5 6 | api.cardSale(sale, customer, card, new Callback<CardSaleResult>() { @Override public void onFinish(CardSaleResult result) { long idFirstSale = result.getIdSale(); } }); |
Although you can refer to any previous transaction ID to perform a resale, we highly recommend to refer to the most recent transaction. This approach has several advantages, for example it allows to easily track the transaction flow.
Usually merchants store such ID numbers in their database. This way they do not have to store any sensitive data, yet they are still able to refer to a specific transaction.
Now, having retrieved the ID number (i.e. from your database), prepare the required information and call the resaleBySale function.
You can also check whether the transaction was performed successfully by calling the isSuccess method. Retrieving the transaction ID number (or error details, if anything goes wrong) is also very simple and can be done as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $resale_params = array( 'id_sale' => $id_first_sale, 'amount' => 99.99, 'currency' => 'EUR', 'description' => 'Recurring billing product #1', ); // perform the resale: try { $status = $client->resaleBySale($resale_params); } catch (Exception $e) { // handle exceptions here } // checking transaction status example (optional): if ($client->isSuccess()) { echo "Success, second id_sale: {$status['id_sale']} \n"; } else { die("Error ID: {$status['error']['id_error']}, \n". "Error number: {$status['error']['error_number']}, \n". "Error description: {$status['error']['error_description']}"); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | resale_params = { 'id_sale' => id_first_sale, 'amount' => 99.99, 'currency' => 'EUR', 'description' => 'Recurring billing product #1' } # perform the resale: begin status = client.resale_by_sale(resale_params) rescue PayLane::ClientError => e # handle exceptions here end # checking transaction status example (optional): if client.success? puts "Success, id_second_sale: #{status["id_sale"]}" else puts "Error ID: #{status["error"]["id_error"]}, \n"\ "Error number: #{status["error"]["error_number"]}, \n"\ "Error description: #{status["error"]["error_description"]}" exit end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | resale_params = { 'id_sale' : id_first_sale, 'amount' : 99.99, 'currency' : 'EUR', 'description' : 'Recurring billing product #1' } # perform the resale: try: status = client.resale_by_sale(resale_params) except Exception, e: # handle exceptions here # checking transaction status example (optional): if client.is_success(): print 'Success, second id_sale: %s' % status['id_sale'] else: sys.exit('Error ID: ' + str(status["error"]["id_error"]) + '\n' \ 'Error number: ' + str(status["error"]["error_number"]) + '\n' \ 'Error description: ' + str(status["error"]["error_description"])) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | api.resaleSale( idFirstSale, 99.99, "EUR", "Recurring billing product #1", new Callback<FraudSaleResult>() { @Override public void onFinish(FraudSaleResult result) { // success } @HandleException public void onProtocolError(ProtocolException e) { // invoke if not success // e.getCode() - error code // e.getMessage() - error message } @Override public void onError(Exception e) { // connection error etc. } } ); |
Recurring payments are just resales performed periodically. It’s your choice whether you want to do this weekly, monthly or annually; it is also possible to perform a resale e.g. when a customer reaches a certain amount that should be paid.
Recurring payments based on card authorization
If a customer did not buy anything yet (no transaction was performed), you can still begin with recurring payments. The customer just has to provide you the necessary card data.
You can collect such data in various situations, for example:
- when the customer signs up for an account in your e-store,
- when a customer signs up for a free trial.
When you collect the data, perform a card authorization and save its ID number.
Now let’s get back to recurring payments. Assuming that you have already retrieved the customer’s card authorization ID number, you can prepare the required data and perform a resale (call the resaleByAuthorization method).
Just like in the case of the resale based on the first sale, you can check whether the transaction was performed successfully by calling the isSuccess method; you can also retrieve the transaction ID number (or error details, if anything goes wrong).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $resale_params = array( 'id_authorization' => $id_authorization, 'amount' => 99.99, 'currency' => 'EUR', 'description' => 'Recurring billing for product #1', ); // perform the resale: try { $status = $client->resaleByAuthorization($resale_params); } catch (Exception $e) { // handle exceptions here } // checking transaction status example (optional): if ($client->isSuccess()) { echo "Success, id_sale: {$status['id_sale']} \n"; } else { die("Error ID: {$status['error']['id_error']}, \n". "Error number: {$status['error']['error_number']}, \n". "Error description: {$status['error']['error_description']}"); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | resale_params = { 'id_authorization' => id_authorization, 'amount' => 99.99, 'currency' => 'EUR', 'description' => 'Recurring billing for product #1' } # perform the resale: begin status = client.resale_by_authorization(resale_params) rescue PayLane::ClientError => e # handle exceptions here end # checking transaction status example (optional): if client.success? puts "Success, id_sale: #{status["id_sale"]}" else puts "Error ID: #{status["error"]["id_error"]}, \n"\ "Error number: #{status["error"]["error_number"]}, \n"\ "Error description: #{status["error"]["error_description"]}" exit end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | resale_params = { 'id_authorization' : id_authorization, 'amount' : 99.99, 'currency' : 'EUR', 'description' : 'Recurring billing product #1' } # perform the resale: try: status = client.resale_by_authorization(resale_params) except Exception, e: # handle exceptions here # checking transaction status example (optional): if client.is_success(): print 'Success, id_sale: %s' % status['id_sale'] else: sys.exit('Error ID: ' + str(status["error"]["id_error"]) + '\n' \ 'Error number: ' + str(status["error"]["error_number"]) + '\n' \ 'Error description: ' + str(status["error"]["error_description"])) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | api.resaleAuthorization( idFirstSale, 99.99, "EUR", "Recurring billing product #1", new Callback<FraudSaleResult>() { @Override public void onFinish(FraudSaleResult result) { // success } @HandleException public void onProtocolError(ProtocolException e) { // invoke if not success // e.getCode() - error code // e.getMessage() - error message } @Override public void onError(Exception e) { // connection error etc. } } ); |