Accept a payment

Build a custom payment integration by embedding UI components on your site using Frame.js. This guide covers both client-side and server-side code to create a checkout form that accepts card payments.

Note: Currently, Frame only accepts card payment methods. We'll update you when more options become available.

1. Set up Frame

Currently, we don't have official libraries. Use any programming language you're comfortable with. In this guide, we'll use Ruby for the server-side code.

2. Collect payment details

Use Frame.js Elements to collect payment details on the client side. Frame.js Elements is a prebuilt UI component that simplifies the collection of payment information for various payment methods.

Frame.js Elements uses an iframe to securely send payment information to Frame over an HTTPS connection. To ensure compatibility with all payment methods, avoid placing Frame.js Elements within another iframe, as some methods require redirecting to a separate page for payment confirmation.

Set up Frame.js

The Payment Element is automatically available as part of Frame.js. Include the Frame.js script on your checkout page by adding it to the head of your HTML file. Always load Frame.js directly from js.framepayments.com to remain PCI compliant. Don't include the script in a bundle or host a copy of it yourself.

<head>
  <title>Checkout</title>
  <script src="https://js.framepayments.com/v1/index.js"></script>
</head>

Add the following JavaScript to your checkout page to create an instance of Frame:

const frame = await Frame.init("<PUBLISHABLE_KEY>");

Add the Payment Element to your checkout page

The Payment Element needs a place to live on your checkout page. Create an empty DOM node (container) with a unique ID in your payment form:

<form id="payment-form">
  <div id="payment-card-element">
    <!-- UI Card will create form elements here -->
  </div>
  <button id="submit">Submit</button>
  <div id="message">
    <!-- Display message to your customers here -->
  </div>
</form>

When the payment element has loaded, create an Elements instance with the type card. Then, create an instance of the Payment Element and mount it to the container DOM node.

// pick up frame theme
const theme = frame.themes("clean");
// create card element and use theme
const card = await frame.createElement("card", { theme: theme  });
// mount it to the container DOM node
card.mount("#payment-card-element");

The Payment Element renders a Card UI form for customers to enter their card details. You can customize its appearance to match your site's design by passing theme options. For more information, see the Styling section.

3. Submit the payment to Frame

When the customer submit your payment form, use a ChargeIntent to facilitate the confirmation and payment process. Create a ChargeIntent on your server with an amount and currency enabled.

# main.rb
require "sinatra"
require "uri"
require "net/http"
require "openssl"

set :static, true
set :port, 4242

get "/" do
  "Accept a Payment with Frame Payments"
end

post "/create-intent" do
  data = JSON.parse(request.body.read)

  url = URI("https://api.framepayments.com/v1/charge_intents")
  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  request = Net::HTTP::Post.new(url)
  request["Authorization"] = "Bearer #{api_key}"
  request["Content-Type"] = 'application/json'

  charge_intent_body = {
    amount: data["amount"],
    currency: "usd",
    confirm: true,
    customer_data: {
      name: "Customer name",
      email: "yourcustomer@example.com"
    },
    payment_method_data: {
      card_number: data["payment_method"]["number"],
      cvc: data["payment_method"]["cvc"],
      exp_month: data["payment_method"]["expiry"]["month"],
      exp_year: data["payment_method"]["expiry"]["year"],
      type: "card",
    }
  }
  request.body = charge_intent_body.to_json
  response = http.request(request)
  response_body = response.read_body
  return { success: true }.to_json
end

Add JavaScript code to send the card information to your server. Here's a complete example of a checkout.js file:

// checkout.js
let cardPayload = {};
const submitBtn = document.querySelector("#submit");
const messageContainer = document.querySelector("#message");

document
  .querySelector("#payment-form")
  .addEventListener("submit", handleSubmit);

initialize();

async function initialize() {
  submitBtn.disabled = true;

  const frame = await Frame.init("PUBLISHABLE_KEY");
  const theme = frame.themes("clean");
  const card = await frame.createElement("card", { theme: theme });
  card.on("complete", (payload) => {
    cardPayload = payload.card;
    submitBtn.disabled = false;
  });
  card.mount("#payment-card-element");
}

async function handleSubmit(e) {
  e.preventDefault();
  submitBtn.disabled = true;
  messageContainer.textContent = "";

  const body = {
    amount: 100, // amount in cents
    payment_method: cardPayload,
  };

  const response = await fetch("/create-intent", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const responseBody = await response.json();
  if (responseBody.success) {
    messageContainer.textContent = "Payment success!";
  } else {
    messageContainer.textContent = `Errors: ${responseBody.message}`
  }
  submitBtn.disabled = false;
}

Handle post-payment events

Frame sends a charge_intent.succeeded event when the payment is completed. Use the Dashboard or a custom webhook to receive these events and perform actions such as:

  • Sending an invoice to your customer
  • Logging the transaction in your database
  • Updating order status

For more information on setting up and handling webhooks, refer to our Webhook documentation.