Payment Request Button
The Payment Request Button gives your customers a fast, single-click checkout experience. It's a single integration that dynamically shows wallet options like Apple Pay, allowing customers to pay using payment details securely stored on their device.
Note: Currently, the Payment Request Button only supports Apple Pay. We'll update you when more options become available.
Prerequisites
Before you can display the Payment Request Button, you must meet the following requirements:
- Your application must be served over HTTPS. This is a browser security requirement for both development and production. Services like ngrok can be used to create a secure tunnel to your local server for testing.
- You must register your domain with us for both test (sandbox) and live modes. Please contact Frame support to complete this step.
- Apple Pay Requirements:
- Your customer must be using a supported device and browser, such as Safari on macOS 10.13+ or iOS 11.3+.
- They must have a card added to their Wallet.
- Note that Apple Pay may not display for users in certain regions, such as India.
1. Set up Frame Elements
First, add the Frame.js script to your checkout page. Always load it directly from js.framepayments.com
to remain PCI compliant.
<script src="https://js.framepayments.com/v1/index.js"></script>
Next, create a container DOM node for the button on your page and initialize Frame with your publishable key.
<div id="payment-request-button">
<!-- A Frame Element will be inserted here. -->
</div>
const frame = await Frame.init("<PUBLISHABLE_KEY>");
2. Create a paymentRequest instance
Create a paymentRequest
instance, providing the payment details such as currency and the total amount.
const paymentRequest = frame.paymentRequest({
country: "US",
currency: "USD",
total: {
label: "Demo (Card is not charged)"
amount: 990,
},
requestPayerName: true,
requestPayerEmail: true
});
Use the requestPayerName
parameter to collect the payer's billing address for Apple Pay. You can use the billing address to perform address verification and block fraudulent payments. All other payment methods automatically collect the billing address when one is available.
3. Create and mount the paymentRequestButton
Check if the customer's device and browser support the Payment Request Button by calling paymentRequest.canMakePayment()
. If it returns wallet is supported, create and mount the paymentRequestButton
Element to the container you created earlier. If not, hide the container and consider showing your standard card input form instead.
(async () => {
const prButton = await frame.createElement("paymentRequestButton", { paymentRequest });
// Check the availability of the Payment Request API first.
const result = await paymentRequest.canMakePayment();
if (result) {
prButton.mount('#payment-request-button');
} else {
document.getElementById('payment-request-button').style.display = 'none';
}
})();
4. Create a ChargeIntent
Frame uses a ChargeIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.
Create a ChargeIntent
on your server with an amount and currency. Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.
curl --request POST \
--url https://api.framepayments.com/v1/charge_intents \
--header 'Authorization: Bearer API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"amount": 990,
"currency": "usd"
}'
The API response for the ChargeIntent includes a client_secret
. Return this client_secret
to the client-side to securely confirm the payment.
5. Complete the payment
Listen for the paymentmethod
event on your paymentRequest object. This event fires after the customer authenticates the payment with their device (e.g., using Face ID or Touch ID). The event handler provides a PaymentMethod object.
Use the client_secret
from the ChargeIntent and the id
from the new PaymentMethod
to confirm the payment with frame.confirmCardPayment
. Finally, call ev.complete()
with the status ("success"
or "fail"
) to close the browser's payment interface and provide feedback to the customer.
paymentRequest.on("paymentmethod", async (ev) => {
const { hasError } = await frame.confirmCardPayment(clientSecret, { payment_method: ev.paymentMethod.id });
if (hasError) {
// Report to the browser that the payment failed, prompting it to
// re-show the payment interface, or show an error message and close
// the payment interface.
ev.complete("fail");
} else {
// Report to the browser that the confirmation was successful, prompting
// it to close the browser payment method collection interface.
ev.complete("success");
}
});
Caution: The customer can dismiss the payment interface in some browsers even after they authorize the payment. This means that you might receive a cancel
event on your PaymentRequest object after receiving a paymentmethod
event. If you use the cancel event as a hook for canceling the customer's order, make sure you also refund the payment that you just created.
6. Test your integration
To test your integration, you must use HTTPS and a supported browser. In addition, payment method and browser has specific requirements:
Safari
- Safari on Mac running macOS Sierra or later
- A compatible device with a card in its Wallet paired to your Mac with Handoff, or a Mac with TouchID. You can find instructions on the Apple Support site.
- A registered domain with Apple Pay.
Mobile Safari
- Mobile Safari on iOS 11.3 or later
- A card in your Wallet (go to Settings > Wallet & Apple Pay).
- A registered domain with Apple Pay.
As of iOS 16, Apple Pay might work in some non-Safari mobile browsers with a card saved in your Wallet.