Over 05 years we help companies reach their financial and branding goals. webtechguru is a values-driven technology agency dedicated.

Gallery

Contacts

support@webtechguru.in

How to Create 3D Secure Payments Using Recurly.js

Introduction

In this guide, we’ll walk through setting up 3D Secure payments using Recurly.js, covering both client-side and server-side configuration. We will do this using PHP.

Requirements

Before you begin, make sure you have the following:

  • A Recurly account
  • A Recurly Public API Key
  • A Recurly Private API Key
  • A Subscription Plan with a plan code

You can create a Recurly account here.

A Recurly account

A Recurly Public API Key

A Recurly Private API Key

A Subscription Plan with a plan code

Step 1: Install and Configure Recurly.js

Add Recurly.js to Your HTML

<script src="https://js.recurly.com/v4/recurly.js"></script>

Initialize Recurly

recurly.configure({
  publicKey: 'your-public-key-here'
});

Step 2: Setup the Payment Form with Hosted Fields

Recurly Hosted Fields securely collect card data while ensuring PCI compliance.

<form id="payment-form">
         <!-- Billing Details -->
         <input type="hidden" data-recurly="first_name" value="Ben" name="first_name">
         <input type="hidden" data-recurly="last_name" value="du Monde" name="last_name">
         <input type="hidden" data-recurly="address1" value="1313 Main St." name="address1">
         <input type="hidden" data-recurly="city" value="Hope" name="city">
         <input type="hidden" data-recurly="country" value="US" name="country">
         <input type="hidden" data-recurly="state" value="WA" name="state">
         <input type="hidden" data-recurly="postal_code" value="98552" name="postal_code">

         <!-- Recurly Card Element -->
         <div id="recurly-element-card"></div>

         <div id="payment-message" class="mt-2"></div>

         <div class="d-flex justify-content-center align-items-center">
           <button type="submit" id="subscribe" class="btn mt-2 btn-success">Subscribe</button>
         </div>

         <input type="hidden" id="recurly_token" name="recurly_token">
       </form>

Style Hosted Fields (Optional)

const elements = recurly.Elements();
    const cardElement = elements.CardElement({
      style: {
        fontFamily: 'Open Sans',
        fontSize: '1rem',
        fontWeight: 'bold',
        fontColor: '#2c0730'
      }
    });

Step 3: Tokenize the Card & Initiate 3D Secure

document.getElementById('payment-form').addEventListener('submit', function (event) {
  event.preventDefault();

  recurly.token(document.querySelector('#payment-form'), function (err, token) {
    if (err) {
      console.error('Tokenization error:', err);
    } else {
      initiate3DS(token);
    }
  });
});

Step 4: Handle 3D Secure Challenge

We need the following files for the payment process:

  • index.php
  • payment.php (AJAX file)
  • The Recurly PHP SDK

index.php

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Recurly.js Example: 3-D Secure</title>
  <script src="https://js.recurly.com/v4/recurly.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
  <link href="https://js.recurly.com/v4/recurly.css" rel="stylesheet" />
  <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" />
  <link href="/style.css" rel="stylesheet" />

  <!-- Bootstrap -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>

  <div class="container p-5">
    <div class="row">
      <div class="col-sm-12 col-lg-6 offset-lg-3 col-md-6 offset-md-3">
        <form id="payment-form">
          <!-- Billing Details -->
          <input type="hidden" data-recurly="first_name" value="Ben" name="first_name">
          <input type="hidden" data-recurly="last_name" value="du Monde" name="last_name">
          <input type="hidden" data-recurly="address1" value="1313 Main St." name="address1">
          <input type="hidden" data-recurly="city" value="Hope" name="city">
          <input type="hidden" data-recurly="country" value="US" name="country">
          <input type="hidden" data-recurly="state" value="WA" name="state">
          <input type="hidden" data-recurly="postal_code" value="98552" name="postal_code">

          <!-- Recurly Card Element -->
          <div id="recurly-element-card"></div>

          <div id="payment-message" class="mt-2"></div>

          <div class="d-flex justify-content-center align-items-center">
            <button type="submit" id="subscribe" class="btn mt-2 btn-success">Subscribe</button>
          </div>

          <input type="hidden" id="recurly_token" name="recurly_token">
        </form>
      </div>
    </div>
  </div>

  <!-- 3D Secure Modal -->
  <div class="modal fade" id="recurlyBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="recurlyBackdropLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-body">
          <div id="my-auth-container" style="width: 450px;height: 450px;"></div>
        </div>
      </div>
    </div>
  </div>

  <script>
    recurly.configure({
      publicKey: 'your-public-key' // Replace with your actual public key
    });

    const elements = recurly.Elements();
    const cardElement = elements.CardElement({
      style: {
        fontFamily: 'Open Sans',
        fontSize: '1rem',
        fontWeight: 'bold',
        fontColor: '#2c0730'
      }
    });
    cardElement.attach('#recurly-element-card');

    $('#payment-form').on('submit', function(event) {
      event.preventDefault();

      $('#subscribe').prop('disabled', true).text('Processing...');
      $('#payment-message').html('');

      const form = this;
      const formData = new FormData(form);

      recurly.token(elements, form, function(err, token) {
        if (err) {
          $('#payment-message').html('<div class="alert alert-danger">' + err.message + '</div>');
          $('#subscribe').prop('disabled', false).text('Subscribe');
        } else {

           formData.append('recurly_token', token.id);

          $.ajax({
            url: 'payment.php',
            method: 'POST',
            data: formData,
            dataType: 'json',
            processData: false,
            contentType: false,
            success: function(response) {
              if (response.status === true) {
                $('#payment-message').html('<div class="alert alert-success">Payment successful!</div>');
                $('#subscribe').prop('disabled', false).text('Subscribe');
              } else if (response.status === false && response.threeDSToken) {
                $('#recurlyBackdrop').modal('show');
                handle3DSecureAuthentication(response.threeDSToken);
              } else {
                $('#payment-message').html('<div class="alert alert-danger">Payment failed.</div>');
                $('#subscribe').prop('disabled', false).text('Subscribe');
              }
            },
            error: function() {
              $('#payment-message').html('<div class="alert alert-danger">Server error during payment.</div>');
              $('#subscribe').prop('disabled', false).text('Subscribe');
            }
          });
        }
      });
    });

    function handle3DSecureAuthentication(threeDSToken) {
      const risk = recurly.Risk();
      const threeDSecure = risk.ThreeDSecure({
        actionTokenId: threeDSToken
      });

      threeDSecure.on('token', function(token) {
        $('#recurlyBackdrop').modal('hide');

        // Send 3DS result token to backend
        $('#payment-message').html('<div class="alert alert-success">3D Secure authentication complete.</div>');

           $('#subscribe').prop('disabled', false).text('Subscribe');
           $('#payment-message').html('');

           // You can add here success payment code
      });

      threeDSecure.on('error', function(error) {
        $('#payment-message').html('<div class="alert alert-danger">3DS Error: ' + error.message + '</div>');
        $('#subscribe').prop('disabled', false).text('Subscribe');
      });

      $('#my-auth-container').html('');
      threeDSecure.attach(document.querySelector('#my-auth-container'));
    }
  </script>
</body>
</html>

Install the Recurly PHP SDK

composer require recurly/recurly-client

payment.php (AJAX file)

<?php
// Autoload the Recurly SDK
require 'vendor/autoload.php';

use Recurly\Client;
use Recurly\RecurlyError;

$apiKey = 'recurly-private-api-key';  // Replace with your actual Recurly API key
$client = new Client($apiKey);

// Check if the Recurly token is present
if (!isset($_POST['recurly_token']) || empty($_POST['recurly_token'])) {
    die("Error: Missing Recurly token.");
}

// Retrieve the token and generate a dynamic account code
$paymentToken = $_POST['recurly_token'];  // Get the token from the form
$accountCode = date('YmdHis') . '-test';  // Use the dynamic account code

// Prepare the purchase creation array
$purchase_create = [
    "currency" => 'INR',
    "account" => [
        "code" => $accountCode,
        "first_name" => $_POST['first_name'],
        "last_name" => $_POST['last_name'],
        "email" => 'example@gmail.com',  // Replace with dynamic email
        "billing_info" => [
            "token_id" => $paymentToken,  // Pass the Recurly token
        ],
    ],
    "subscriptions" => [
        [
            "plan_code" => "110586",  // Replace with your plan code
            "unit_amount" => 500,  // Price in cents (500 INR)
            "quantity" => "1",  // Number of items
        ]
    ]
];

try {
    // Create the purchase
    $invoice_collection = $client->createPurchase($purchase_create);

    // Get the transaction error
    $transaction_error = $invoice_collection->getChargeInvoice()->getTransactions()[0]->getTransactionError();
    
} catch (\Recurly\Errors\Validation $e) {
    // Handle validation errors
    $response = array(
        'status' => false, 
        'message' => $e->getMessage(),
    );
    echo json_encode($response);

} catch (\Recurly\RecurlyError $e) {
    // Handle Recurly API errors (e.g., 3D Secure)
    $three_d_secure_action_token_id = $e->getApiError()->getTransactionError()->getThreeDSecureActionTokenId();

    $response = array(
        'status' => false, 
        'message' => $e->getMessage(),
        'threeDSToken' => $three_d_secure_action_token_id,  // Send 3DS token if needed
    );
    echo json_encode($response);
}
?>



Final Steps

When you follow the steps above, you will see a screen like the one shown.

Enter a test payment card number and use a future expiration date in the form.

Card Number Behavior
4000000000003220 Challenge flow
4000000000003063 Device fingerprint flow
4222222222222220 Approved fraud review (frictionless flow)
4000008400001629 3DS2 challenge for any recurring transaction (e.g., dunning)

Once submitted, a 3D Secure popup will appear, allowing you to complete the authentication.

This completes the Recurly 3D Secure payment integration process.

Conclusion

You’ve now set up a fully functional 3D Secure payment flow using Recurly.js! This ensures your payment processing is secure, compliant, and optimized for user trust. By leveraging Recurly’s built-in tools, you’ve simplified what would otherwise be a very complex integration.

If you have any questions or need assistance, feel free to contact me.
I’ll be available to help you as soon as possible.

Thank you!

 

Author

Admin

Leave a comment

Your email address will not be published. Required fields are marked *