How to Build a Hosted Checkout Page and Embed a Checkout Toolkit

by Allan MacGregor

Building and maintaining an e-commerce website can be challenging; in particular, building a checkout requires a high level of technical knowledge and understanding of payment security, compliance, regulations, and fraud prevention.

Traditional e-commerce solutions can be complex and challenging to maintain and integrate, or otherwise limited in functionality and customization. Fortunately for developers that want to build a custom checkout, while still providing the best possible security and user experience, Rapyd Checkout provides an ideal solution.

This article will walk you through the steps of building a hosted checkout page with Rapyd Checkout and then embedding it in your website using Rapyd’s Checkout Toolkit.

Rapyd Checkout is a checkout solution that allows you to accept hundreds of payment methods worldwide without worrying about PCI DSS compliance. Rapyd Checkout can be implemented using either of the following methods:

  • A Rapyd Hosted Checkout page: This allows you to leverage all the Rapyd Collect capabilities to accept payments on a single page. As with any other hosted checkout solution, it’s already PCI DSS certified and allows you to customize the look and feel to match your branding.

  • The Rapyd Checkout Toolkit: For more complex integrations that require deeper integration with the e-commerce platform or website, developers can use the Rapyd Checkout Toolkit to embed the checkout page into the website. The Checkout Toolkit allows deeper customization of various aspects of the checkout experience.

Why Is Rapyd Checkout Better than Traditional Solutions?

There are several advantages to using Rapyd Checkout over traditional checkout solutions:

  • It’s PCI DSS certified out of the box.
  • It accepts payments worldwide.
  • It has multiple payment method options:
    • Card payments (local and international)
    • Bank payments
    • Cash payments online; you can pay with cash from two million+ stores worldwide.
    • eWallet payments allows customers to directly use solutions like Paytm, GrabPay, etc.

In addition, Rapyd facilitates subscriptions, recurring billing, or recurring payments, making it an excellent option for merchants looking to sell digital services and products.

What Is Rapyd Checkout?

Rapyd Checkout is a checkout solution that provides the quickest and easiest way to start accepting hundreds of payment methods from a website or mobile application.

Rapyd Checkout is an excellent option for developers and merchants looking to integrate existing websites and catalogs without worrying about PCI DSS compliance or dealing with the complexities of implementing a dedicated checkout solution, which would require periodic security audits, advanced technical knowledge, and process changes within the organization.

Rapyd Checkout also offers more payment flexibility than current SaaS solutions like Shopify; for example, recurring payments, subscriptions, and breadth of payment methods are areas where existing SaaS are somewhat limited.

Benefits of Rapyd Checkout

Out of the box, Rapyd Checkout offers a few key benefits that help developers build a better checkout experience:

  • Mobile-ready: Rapyd Checkout can be used on mobile devices.
  • International support: Merchants can immediately start accepting payments from anywhere in the world. Rapyd Collect supports nine hundred plus payment methods and more than sixty-five currencies in over a hundred countries.
  • Reduced risk: Hosted Checkout has built-in fraud protection with Rapyd Protect and pre-built security with PCI DSS compliance.
  • Multi-lingual support: Hosted Checkout supports multiple languages.

Implementing Rapyd Checkout

The next sections walk you through the steps of building a Hosted Checkout page with Rapyd Checkout and how to use Rapyd’s Checkout Toolkit to embed the checkout page in your website.

We’ll be using a sample web application built in Elixir/Phoenix for this article. Keep in mind the steps to integrate Rapyd Checkout are the same as for any other web application.

Prerequisites

To follow along with this article, you’ll need to have a Rapyd account and a local copy of the project repository. You can check out the starter branch with the following command:

bash
git clone https://github.com/amacgregor/rapyd_checkout_example
git checkout starter

In order to run this application you’ll need Elixir 1.11 or later installed locally.

The sample application provides a few pre-built pages:

  • A Hosted Checkout product: This example uses the Hosted Checkout page to check out a single product.
  • A Checkout Toolkit product: This example uses the Checkout Toolkit to allow for inline checkout without leaving your sample application.
  • A Testing API page: You can use this page to confirm that your API credentials and signature algorithm are set up correctly.

Implementing Hosted Checkout

With these prerequisites in place, the following steps will walk you through setting up a Hosted Checkout page:

Step 1. Get an API Key and Set Up Request Authorization

Before you can start building the page, you need to get an API key from Rapyd. This API key will be used to build your auth headers added to every API request.

CLIENT PORTAL

From the account you’ve created in the Rapyd Client Portal, retrieve the Secret key and the Access key. This section can be accessed by clicking Developers > Credentials Details from the left side navigation. Make sure to set the account to sandbox mode by toggling the sandbox switch on the bottom left corner before retrieving the credentials.

Every request to the Rapyd API requires the following headers:

  • access_key: The access key generated when you created your Rapyd account.
  • content_type: The content type of the request. This is always “application/json.”
  • salt: A random string used to generate the signature.
  • signature: A generated value per request.
  • timestamp: The current time in seconds.

An essential part of every request made to Rapyd’s API is the signature; without this header, there is no way to authenticate the request, and it will undoubtedly be rejected. As part of your implementation, you’ll have to calculate the signature and add it to the request headers.

The signature is calculated with the following formula:

signature = BASE64 ( HASH ( http_method + url_path + salt + timestamp + access_key + secret_key + body_string ) )

The sample application provides a sample endpoint to help you test the API key and signature. First, you should be aware of what will happen if you don’t have a key or signature setup.

Make sure the application is running by running mix phx.server on your console, and visit localhost:4000/testing; you should see the following response.

The lib/rapyd_checkout_example_web/controllers/testing_controller controller code in this case looks like this:

  def index(conn, _params) do
    response = HTTPoison.get! "https://sandboxapi.rapyd.net/v1/data/countries"
    response = response.body |> Jason.decode!
    render(conn, "index.html", response: response)
  end

Your request failed because you’re missing the authentication headers completely. You can refactor the application code to handle the request signature. Start by updating config/dev.exs and adding the following code block:

config :rapyd_checkout_example,
  rapyd_access_key: "YOUR_ACCESS_KEY",
  rapyd_secret_key: "YOUR_SECRET_KEY",

Replace the values with your API key and secret key from the Rapyd Client Portal. Next, refactor the testing_controller.ex to account for the headers:

  def index(conn, _params) do
    # Define the base url and target path
    base_url = "https://sandboxapi.rapyd.net"
    url_path = "/v1/data/countries"

    # Generate the values needed for the headers
    access_key = Application.fetch_env!(:rapyd_checkout_example, :rapyd_access_key)
    salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
    timestamp = System.os_time(:second)
    signature = sign_request("get", url_path, salt, timestamp, access_key, "")

    # Build the headers
    headers = [
      {"access_key", access_key},
      {"salt", salt},
      {"timestamp", timestamp},
      {"url_path", url_path},
      {"signature", signature}
    ]

    response = HTTPoison.get!(base_url <> url_path, headers)
    response = response.body |> Jason.decode!()
    render(conn, "index.html", response: response)
  end

As you can tell, all you’re doing is adding the headers to the request. More importantly, the following code is used to generate the signature:

  def sign_request(http_method, url_path, salt, timestamp, access_key, body) do
    secret_key = Application.fetch_env!(:rapyd_checkout_example, :rapyd_secret_key)

    cond do
      body == "" ->
        body = ""
      true ->
        {:ok, body} = body |> Jason.encode()
    end

    signature_string =
      [http_method, url_path, salt, timestamp, access_key, secret_key, body] |> Enum.join("")

    :crypto.mac(:hmac, :sha256, secret_key, signature_string)
    |> Base.encode16(case: :lower)
    |> Base.encode64()
  end

This function will take the following parameters:

  • http_method: The HTTP method used in the request.
  • url_path: The path of the request.
  • salt: A random string used to generate the signature.
  • timestamp: The current time in seconds.
  • access_key: The access key generated when you created your Rapyd account.
  • secret_key: The secret key generated when you created your Rapyd account.
  • body: The body of the request.

Please note that the body encoding is distinctly important for signature generation, as it might cause mismatches between the signature generated and the validation on the Rapyd API end. You should now be able to make a request to the API and see the response. In this case, a list of all the countries supported by Rapyd.

Step 2. Create Your Checkout Experience on the Rapyd Client Portal

Next, you can create the checkout experience on the Rapyd Client Portal. Log in to your Rapyd account and navigate to Settings > Branding. From here, you can customize the look and feel of the Hosted Checkout page, as well tweak the different payment methods available.

For this example, you’re going to update the color of the checkout page:

  • Click on the second icon in the toolbar.
  • Using the color picker, change the color of the buttons.
  • Finally, click on the Save button to apply the changes.

Step 3. Generate Your Checkout Page

Now that you’ve validated the API key and signature and customized the look and feel of your checkout page, you can generate the checkout page where your customers will be redirected.

For every instance of a customer checkout, you’ll need to generate a unique checkout page. The initial checkout request requires the following three parameters:

  • Amount: The amount of the transaction.
  • Country: The country of the customer.
  • Currencies: The currency of the transaction.

Next, you’ll create a new route that will generate the checkout page and redirect the customer to it. Open lib/rapyd_checkout_example_web/controllers/product_controller.ex and add the following code:

  def hosted_checkout(conn, _params) do
    # Define the base url and target path
    base_url = "https://sandboxapi.rapyd.net"
    url_path = "/v1/checkout"

    # Generate the values needed for the headers
    access_key = Application.fetch_env!(:rapyd_checkout_example, :rapyd_access_key)
    salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
    timestamp = System.os_time(:second)

    # Create the body
    {:ok, body} = %{
      amount: "140.00",
      currency: "USD",
      country: "US"
    } |> Jason.encode()

    signature = sign_request("post", url_path, salt, timestamp, access_key, to_string(body))

    # Build the headers
    headers = [
      {"access_key", access_key},
      {"salt", salt},
      {"timestamp", timestamp},
      {"url_path", url_path},
      {"signature", signature}
    ]

    response = HTTPoison.post!(base_url <> url_path, body, headers)
    response = response.body |> Jason.decode!()

    # Redirect to the checkout page
    redirect(conn, external: response["data"]["redirect_url"])

  end

  def sign_request(http_method, url_path, salt, timestamp, access_key, body) do
    secret_key = Application.fetch_env!(:rapyd_checkout_example, :rapyd_secret_key)

    signature_string =
      [http_method, url_path, salt, timestamp, access_key, secret_key, body] |> Enum.join("")

    :crypto.mac(:hmac, :sha256, secret_key, signature_string)
    |> Base.encode16(case: :lower)
    |> Base.encode64()
  end

The code is very similar to your test controller with a few changes; the URL path is now /v1/checkout, and the body is now a JSON string containing the amount, currency, and country.

Make sure the route is mapped by updating lib/rapyd_checkout_example_web/routes.ex and adding the following code:

  # Routes
  post "/hosted-checkout", ProductController, :checkout

Step 4. Redirect Your Customer

Next, you’re going to wire your product button to the checkout page. You need to modify the lib/rapyd_checkout_example_web/templates/product/hosted_checkout.html.ex file. Replace:

            <button type="submit" class="max-w-xs flex-1 bg-indigo-600 border border-transparent rounded-md py-3 px-8 flex items-center justify-center text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500 sm:w-full">Buy Now!</button>

with:

<%= button("Buy Now!", to: "/hosted-checkout", method: "post", class: "max-w-xs flex-1 bg-indigo-600 border border-transparent rounded-md py-3 px-8 flex items-center justify-center text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500 sm:w-full", custom: "property") %>

That’s it! You are now ready to test your checkout page. Load http://localhost:4000/products/hosted-checkout and click the Buy Now! button. You should see a checkout page similar to the one below.

This is the full checkout page, unique to this transaction; you can use a fake credit card number to attempt checkout.

Implementing Checkout Toolkit

While the Hosted Checkout page is a great way to get started with the checkout experience, it has one major drawback: it’s not a fully integrated experience, as customers get redirected to a third-party website.

Fortunately, it’s not the only type of checkout offered by Rapyd, and you can also use the Checkout Toolkit to implement a fully integrated checkout experience.

Step 1. Add Rapyd Checkout Toolkit scripts

First you need to add the Rapyd Checkout Toolkit scripts to your lib/rapyd_checkout_example_web/templates/layout/root.html.heex file.

<!DOCTYPE html>
<html lang="en" data-theme="light" class="h-full bg-base-100">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <%= csrf_meta_tag() %>
    <%= live_title_tag assigns[:page_title] || "RapydCheckoutExample", suffix: " · Phoenix Framework" %>
    <link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")} />
    <script defer phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
    <script src="https://sandboxcheckouttoolkit.rapyd.net"></script>
  </head>
...
</html>

Step 2. Add a Rapyd Checkout Toolkit Div

Next, you’ll create a new route and template to load the Rapyd checkout. Add the following code to lib/rapyd_checkout_example_web/routes.ex:

  # Routes
  get "/checkout", ProductController, :checkout

Update the controller under lib/rapyd_checkout_example_web/controllers/product_controller.ex:

  def checkout(conn_params) do
    render(conn, "checkout.html")
  end

Create the lib/rapyd_checkout_example_web/templates/product/checkout.html.ex file with the following code:

<div class="bg-gray-100">
  <div id="rapyd-checkout"></div>
</div>

This allows you to embed an iframe with Rapyd Checkout directly into your page.

Step 3. Initialize the Checkout Page Object

Just like in the Hosted Checkout example, you need to initialize a checkout page and pass that unique ID to your embedded page. In your lib/rapyd_checkout_example_web/controllers/product_controller.ex file, add the following code:

  def checkout(conn, _params) do
    # Create the body
    {:ok, body} = %{
      amount: "220.00",
      currency: "USD",
      country: "US"
    } |> Jason.encode()

    response = generate_checkout(body)

    render(conn, "checkout.html", checkout_id: response["data"]["id"])
  end

  def checkout_redirect(conn, _params) do
    # Create the body
    {:ok, body} = %{
      amount: "140.00",
      currency: "USD",
      country: "US"
    } |> Jason.encode()

    response = generate_checkout(body)

    # Redirect to the checkout page
    redirect(conn, external: response["data"]["redirect_url"])
  end

  defp generate_checkout(body) do
      # Define the base url and target path
      base_url = "https://sandboxapi.rapyd.net"
      url_path = "/v1/checkout"

      # Generate the values needed for the headers
      access_key = Application.fetch_env!(:rapyd_checkout_example, :rapyd_access_key)
      salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
      timestamp = System.os_time(:second)

      signature = sign_request("post", url_path, salt, timestamp, access_key, to_string(body))

      # Build the headers
      headers = [
        {"access_key", access_key},
        {"salt", salt},
        {"timestamp", timestamp},
        {"url_path", url_path},
        {"signature", signature}
      ]

      response = HTTPoison.post!(base_url <> url_path, body, headers)
      response = response.body |> Jason.decode!()
  end

The most significant change in this code is the addition of the generate_checkout method. This method will generate the checkout object and be reused by either your hosted checkout or your embedded checkout.

Next, you need to wire the Checkout button in lib/rapyd_checkout_example_web/templates/product/toolkit-checkout.html.ex. Replace:

            <button type="submit" class="w-full bg-indigo-600 border border-transparent rounded-md py-3 px-8 flex items-center justify-center text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500">Add to bag</button>

with:

<%= button("Purchase Now!", to: "/checkout", method: "get", class: "max-w-xs flex-1 bg-indigo-600 border border-transparent rounded-md py-3 px-8 flex items-center justify-center text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500 sm:w-full", custom: "property") %>

This will now render a button that will redirect to your checkout page.

Step 4. Display the Checkout Page

Finally, you need to display the checkout page. For this, you’ll need to add the following code to lib/rapyd_checkout_example_web/templates/product/checkout.html.heex:

<div class="bg-gray-100">
  <div id="rapyd-checkout"></div>
      <script>
        window.onload = function () {
            let checkout = new RapydCheckoutToolkit({
                pay_button_text: "Click to pay",
                pay_button_color: "blue",
                id: "<%= @checkout_id %>",
            });
            checkout.displayCheckout();
        }
    </script>
</div>

If everything was successful, you should see a checkout page that looks like this.

Step 5. Add Event Listeners for Checkout Success and Failure

One last thing to note is that you need to handle both the checkout success and failure events for the embedded checkout; you can do this by adding event listeners to the checkout page script.

Log the following events to the browser console, for illustration purposes:

<div class="bg-gray-100">
  <div id="rapyd-checkout"></div>
      <script>
        window.onload = function () {
            let checkout = new RapydCheckoutToolkit({
                pay_button_text: "Click to pay",
                pay_button_color: "blue",
                id: "<%= @checkout_id %>",
            });
            checkout.displayCheckout();
        }
        window.addEventListener('onCheckoutPaymentSuccess', function (event) {
            console.log(event.detail)
        });
        window.addEventListener('onCheckoutFailure', function (event) {
            console.log(event.detail.error)
        });
    </script>
</div>

However, developers can do much more with these events, from redirecting to confirmation pages to triggering more complex integrations with other systems.

With the event handles in place, first check out, and then you can verify the successful response on the console.

Conclusion

This article covered the basics of Rapyd Checkout and how to enable checkout functionality on your website. You also learned about the two types of integrations: hosted and embedded.

On the one hand, Hosted Checkout provides a quick, secure, and easy-to-use checkout experience for your customers with support for hundreds of payment methods. On the other hand, the Checkout Toolkit elevates that checkout experience with significant customization options and seamless functionality, without the need to redirect to a third-party website.

Rapyd Checkout is a powerful toolkit that lets you easily implement e-commerce experiences with support for hundreds of payment methods worldwide, native localization support, and built-in security.

5 Likes