Build a subscriptions integration
Use this guide to learn how to sell fixed-price subscriptions. You'll use the Frame.js Element to create a custom payment form that you embed in your application.
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.
Collect payment details
You're ready to collect payment details on the client with the Frame.js Payment Element. The Payment Element is a prebuilt UI component that simplifies collecting payment details.
Set up Frame.js
The Payment Element is automatically available as a feature 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>
Create an instance of Frame with the following JavaScript on your checkout page:
// Set your publishable key: remember to change this to your live publishable key in production
// See your keys here: https://app.framepayments.com/developer/apikeys
const frame = await Frame.init("<PUBLISHABLE_KEY>");
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 });
// listen on complete event and assign the card information to cardPayload variable
card.on("complete", (payload) => {
cardPayload = payload.card;
});
// 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.
Add the Payment Element
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="pay" type="submit">Subscribe</button>
<div id="messages">
<!-- Display message to your customers here -->
</div>
</form>
Create the product
Frame needs a product for each subscription. You can create recurring product via Frame Dashboard or API.
Create the customer
Frame needs a customer for each subscription. In your application frontend, collect any necessary information from your users and pass it to the backend.
Create the subscription
When the customer submits your payment form, use a Subscription to facilitate the confirmation and payment process. To create a subscription, you need a Customer and a Product.
Included on a Subscription is a client secret. Return this value to your client for Frame.js to use to securely complete the payment process.
post "/create-subscription" do
content_type "application/json"
data = JSON.parse(request.body.read)
customer_id = data["customerId"]
product_id = data["productId"]
# create payment method
payment_method_params = {
"type": "card",
"card_number": data["payment_method"]["number"],
"exp_month": data["payment_method"]["expiry"]["month"],
"exp_year": data["payment_method"]["expiry"]["year"],
"cvc": data["payment_method"]["cvc"],
"customer": customer_id,
# change with the billing address
"billing": {
"line_1": "45 Winding Hill Rd",
"city": "Halifax",
"country": "US",
"state": "PA",
"postal_code": 17032
}
}
payment_method_response = post_request("/payment_methods", payment_method_params)
payment_method = JSON.parse(payment_method_response.read_body)
# create a subscription
subscription_params = {
customer: customer_id,
product: product_id,
currency: "USD",
default_payment_method: payment_method["id"]
}
subscription_response = post_request("/subscriptions", subscription_params)
subscription = JSON.parse(subscription_response.read_body)
# retrieve the charge intent of the subscription
charge_intent_response = get_request("/charge_intents/#{subscription["latest_charge_intent"]}")
charge_intent = JSON.parse(charge_intent_response.read_body)
{ clientSecret: charge_intent["client_secret"] }.to_json
end
Confirm the subscription
Use frame.confirmCardPayment
to confirm the subscription using details from the Payment Element. Indicate where Frame should redirect the customer after confirmation. You can freely determine where the user will be redirected after the payment is complete by checking hasError
and listening to webhook events.
const payBtn = document.querySelector("#pay");
const form = document.getElementById("payment-form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
payBtn.disabled = true;
const body = {
payment_method: cardPayload,
// pass your customer ID
customerId: "47a10b46-151b-4cc6-99d7-4994ef2081f1",
// pass your product ID
productId: "049a5468-d131-4a65-a848-1e424beacfc9",
};
// Create the subscription
const { clientSecret } = await fetch("/create-subscription", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then((r) => r.json());
const { hasError, error } = await frame.confirmCardPayment(clientSecret);
if (hasError) {
// handle error
} else {
// You should redirect customer to success page
}
});
Provision access to your service
Now that the subscription is active, give your user access to your service. To do this, listen to the customer.subscription.created
and customer.subscription.updated
events. These events pass a subscription object which contains a status field indicating whether the subscription is active, past due, or canceled. See the subscription lifecycle for a complete list of statuses.
Example Implementation for Subscription
For a complete working example of Frame payment integration, explore our sample application:
We maintain a comprehensive example application that demonstrates best practices for Frame payment integration.