Custom Checkout Flow
The hosted checkout page is a simple, easy-to-use page that allows you to collect payment information from your customers. However, if you want to customize the checkout page, you can use your own checkout page instead of the hosted checkout page.
In the following example, we will use our Fynn.SDK and Fynn.JS library to create a custom checkout page.
Used components
- Fynn.SDK: CartProductService
- Fynn.JS: Elements > Payment Methods
- Fynn.JS: Elements > Pricing table (optional)
Display pricing table (optional)
To display available plans as a pricing table, you can use your own HTML table or use the Fynn.JS library to create a pricing table.
<html>
<body style="height: 100%;">
<main style="background-color: ghostwhite; height: 100%;">
<div style="margin: auto auto;">
<div id="pricing-table">
<!-- The pricing table will be inserted here -->
</div>
</div>
</main>
</body>
<script src="https://js.fynn.eu/v1/fynn.js" type="text/javascript"></script>
<script type="text/javascript">
const Fynn = FynnJS()
Fynn.setup('pk_live_xxxxxx');
const pricingTableElements = Fynn.elements.pricingTables({
brandColor: 'c7f639'
})
const element = pricingTableElements.create('plan_group_xxxxx', {
useHostedCheckout: false,
})
element.mount('#pricing-table')
element.onPlanSelected((plan) => {
window.location.href = `cart.html?plan=${plan.id}`
})
</script>
</html>
For further details on how to use the pricing table, please refer to the pricing table documentation.
Create custom checkout page
Backend example
This example is based on php but can be adapted to any other language.
<?php
require_once 'vendor/autoload.php';
use Fynn\Sdk\V1\Cart\CartProductService;
function fetchDebtorIdFromCurrentUser(): string {
// do your magic to return the debtor id of the current user, maybe you need to create a new debtor
// for creation see: https://docs.fynn.eu/docs/developer/debitor
}
// Retrieve your api key from the fynn configuration page.
$client = ClientFactory::create('api_live_xxxxxx', 'api-username');
$cartService = new CartProductService($client);
$planId = $_POST['plan_id'];
$debtorId = fetchDebtorIdFromCurrentUser();
$cart = $cartService->createPlanCart($planId, $debtorId);
header('Content-Type: application/json');
echo json_encode([
'cart_id' => $cart->getCartId(),
'cart_authentication_token' => $cart->getCartAuthenticationToken(),
'debtor_id' => $debtorId,
]);
Frontend example
<html>
<head>
<script src="https://js.fynn.eu/v1/fynn.js" type="text/javascript"></script>
<script lang="text/javascript">
let plan_id = new URLSearchParams(window.location.search).get('plan')
let cart_id = null;
let cart_authentication_token = null;
let debtor_id = null
const fetchCart = async (planId) => {
fetch('/create_cart.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
plan_id: plan_id,
}),
}).then((response) => {
{ cart_id, cart_authentication_token, debtor_id } = response.json();
})
}
const initializePaymentElements = async (type) => {
const paymentMethodsElements = await Fynn.elements.paymentMethods({
secret: cart_authentication_token,
// you can customize the allowed payment methods in your tenant configuration, or override them here
paymentMethods: ['creditcard', 'paypal', 'sepa', 'bank_transfer'],
// if you want the user to use their already created payment methods, you can set this to true
showExistingPaymentMethods: false,
})
paymentElement = paymentMethodsElements.create(type, {
debtorId: debtor_id,
});
paymentElement.mount('#payment-element-container');
paymentElement.onPaymentMethodValid((paymentMethod) => {
// display some hint if you want
console.log(paymentMethod);
});
paymentElement.onPaymentMethodInvalid((error) => {
// display some hint if you want
console.log(error);
});
}
const payCart = async () => {
const { error } = await paymentElement.confirmCart({
cart_id,
cart_authentication_token,
// optional: you can add a redirect URL here, which will be used after the checkout is completed, when we need to redirect offsite
{
returnUrl: 'https://example.com/return-url',
}
})
// do some error handling
return error
}
await fetchCart()
await initializePaymentElements('creditcard')
document
.querySelector("#checkout-form")
.addEventListener("submit", payCart)
;
</script>
</head>
<body style="background-color: ghostwhite;">
<main>
<div style="display: flex; justify-content: center; align-items: center; height: 100vh;">
<form id="checkout-form" style="width: 100%; max-width: 500px; padding: 20px; ">
<h1>Checkout</h1>
<div>
<div id="productName">
<h2>Product Name</h2>
</div>
<div id="price" style="margin-top: 10px;">
<h3>Price</h3>
</div>
<div id="priceFuture" style="margin-top: 10px;">
<h3>Recurring price</h3>
</div>
</div>
<div style="width: 100%; max-width: 500px; padding: 20px; background-color: white; border-radius: 10px; margin-top: 10px;">
<select onchange="initializePaymentElements(this.value)" style="width: 100%; padding: 10px;">
<option value="sepa">SEPA</option>
<option value="stripe-card">Kreditkarte</option>
<option value="stripe-iban">Stripe SEPA</option>
<option value="paypal">PayPal</option>
</select>
<div id="payment-element-container" style="height: 500px; margin-top: 10px;">
<!-- The payment elements will be inserted here -->
</div>
</div>
<button type="submit" style="flex: 1; width: 100%; border:none; padding: 3px; margin-top: 10px; margin-left: 20px;">Order now</button>
</form>
</div>
</main>
</body>
</html>
Usage of custom payment methods element
For further details on how to use the payment elements, please refer to the payment elements documentation.