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.