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.

Download full app