Framer
Option 1: HTML embed
Section titled “Option 1: HTML embed”The simplest approach — add an Embed element to your Framer page:
<button class="sanwo-button" data-sanwo-provider="paystack" data-sanwo-key="pk_test_xxxxx" data-sanwo-amount="500000" data-sanwo-currency="NGN" data-sanwo-email="customer@example.com" data-sanwo-callback="onSanwoPaid"> Pay ₦5,000</button>
<style> .sanwo-button { display: inline-flex; align-items: center; padding: 12px 24px; background: #0066ff; color: #fff; border: none; border-radius: 6px; font-size: 16px; font-weight: 600; cursor: pointer; font-family: inherit; } .sanwo-button:hover { background: #0052cc; }</style>
<script src="https://cdn.jsdelivr.net/npm/@sanwohq/embed/dist/sanwo.global.js"></script><script> function onSanwoPaid(result) { if (result.status === 'successful') { window.top.location.href = '/thank-you'; } }</script>Option 2: Code component
Section titled “Option 2: Code component”Create a Framer code component for a reusable, configurable payment button:
import { addPropertyControls, ControlType } from "framer"import { useEffect, useRef, useState } from "react"
export default function SanwoButton(props) { const { provider = "paystack", publicKey = "", amount = 500000, currency = "NGN", email = "", buttonText = "Pay Now", redirectUrl = "", } = props
const [loading, setLoading] = useState(false) const scriptLoaded = useRef(false)
useEffect(() => { if (scriptLoaded.current) return const script = document.createElement("script") script.src = "https://cdn.jsdelivr.net/npm/@sanwohq/embed/dist/sanwo.global.js" script.onload = () => { scriptLoaded.current = true } document.head.appendChild(script) }, [])
const handleClick = async () => { if (!window.Sanwo || !publicKey) return setLoading(true)
try { const sanwo = window.Sanwo.create({ provider, publicKey }) const result = await sanwo.checkout({ amount, currency, customer: { email }, })
if (result.status === "successful" && redirectUrl) { window.location.href = redirectUrl } } catch (err) { console.error("Sanwo error:", err) } finally { setLoading(false) } }
return ( <button onClick={handleClick} disabled={loading} style={{ display: "inline-flex", alignItems: "center", padding: "12px 24px", background: loading ? "#7c8db5" : "#0066ff", color: "#fff", border: "none", borderRadius: "6px", fontSize: "16px", fontWeight: 600, cursor: loading ? "not-allowed" : "pointer", width: "100%", justifyContent: "center", }} > {loading ? "Processing..." : buttonText} </button> )}
addPropertyControls(SanwoButton, { provider: { type: ControlType.Enum, title: "Provider", options: ["paystack", "flutterwave", "razorpay", "monnify", "interswitch"], defaultValue: "paystack", }, publicKey: { type: ControlType.String, title: "Public Key", defaultValue: "" }, amount: { type: ControlType.Number, title: "Amount (minor units)", defaultValue: 500000 }, currency: { type: ControlType.String, title: "Currency", defaultValue: "NGN" }, email: { type: ControlType.String, title: "Customer Email", defaultValue: "" }, buttonText: { type: ControlType.String, title: "Button Text", defaultValue: "Pay Now" }, redirectUrl: { type: ControlType.String, title: "Redirect URL", defaultValue: "" },})Add this as a Code Component in Framer. All settings are configurable from the properties panel.
- The code component approach gives you property controls in the Framer editor
- HTML embeds run in iframes — use
window.top.location.hreffor redirects - The code component runs in the page context, so
window.location.hrefworks directly - Test on the published site for the full payment flow