Skip to content

Framer

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>

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.href for redirects
  • The code component runs in the page context, so window.location.href works directly
  • Test on the published site for the full payment flow