Package Exports
- @blaze-money/react
- @blaze-money/react/package.json
Readme
@blaze-money/react
Drop-in React components for accepting payments with Blaze.
Quick Start
There is no client-side key. Authentication comes from a server-created payment link (or checkout session) — the widget only needs the resulting short code. In-browser card encryption is initialized automatically: the widget fetches Blaze's public Evervault identifiers from the backend (GET /v1/checkout/config), so you don't pass them. (You can optionally override them via evervaultTeamId / evervaultAppId — see the advanced note below.)
1. Create a payment link (server-side)
Call this from your backend with your secret API key. The secret key never reaches the browser.
// e.g. Next.js API route, Express handler, etc.
const res = await fetch("https://api.blaze.money/v1/payment-links", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BLAZE_SECRET_KEY}`, // requires scope PAYMENT_LINKS_WRITE
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 2500, // cents
currency: "USD",
description: "Pro Plan — Annual",
metadata: { user_id: "usr_abc123" },
}),
})
const { short_code } = await res.json()
// e.g. short_code = "pl_ImsNk4rl" (the share URL is https://blze.at/<short_code>)
// Return ONLY the short_code to your frontend — never the full share URL.2. Embed the checkout (client-side)
import { BlazeCheckout } from "@blaze-money/react"
export function CheckoutPage({ paymentLinkCode }: { paymentLinkCode: string }) {
return (
<BlazeCheckout
paymentLinkCode={paymentLinkCode} // short code ONLY, e.g. "pl_ImsNk4rl"
onComplete={(event) => {
console.log("Payment succeeded:", event.sessionId)
window.location.href = "/success"
}}
onError={(err) => console.error("Payment failed:", err)}
appearance={{
theme: "night",
variables: { colorPrimary: "#6366f1", borderRadius: "12px" },
}}
/>
)
}That is a fully working integration — the widget fetches its Evervault encryption IDs from Blaze automatically. Everything else below is optional.
Advanced — overriding the Evervault IDs. By default the widget loads Blaze's public Evervault
teamId/appIdfromGET /v1/checkout/config. To pin specific values (e.g. while testing against a custom backend), pass them explicitly:<BlazeCheckout paymentLinkCode={paymentLinkCode} evervaultTeamId={process.env.NEXT_PUBLIC_EVERVAULT_TEAM_ID!} evervaultAppId={process.env.NEXT_PUBLIC_EVERVAULT_APP_ID!} onComplete={(event) => window.location.href = "/success"} />
The
paymentLinkCodeis the bare short code (pl_ImsNk4rl), not the share URL. Passing the fullhttps://blze.at/pl_ImsNk4rlproduces a request toGET /v1/checkout/pay/https://blze.at/pl_ImsNk4rl, which returns a 404. Pass only thepl_...code.
How auth works (no publishable key)
| Side | Endpoint | Auth |
|---|---|---|
| Server (your backend) | POST /v1/payment-links |
Your secret API key in Authorization (scope PAYMENT_LINKS_WRITE) |
| Client (the widget) | GET /v1/checkout/config |
None — fetches Blaze's public Evervault IDs to initialize card encryption |
| Client (the widget) | GET /v1/checkout/pay/:code |
None — fetches payment link data by short code |
| Client (the widget) | POST /v1/checkout/pay/:code/submit |
None — submits the Evervault-encrypted card payment |
The widget never sends a client-side key of any kind. apiUrl defaults to https://api.blaze.money (production); point it at staging to test.
Features
- Stripe-like Appearance API — theme, variables, rules, label modes, input modes, animation toggle
- 3 built-in themes —
default,night,flat - 30 appearance variables — colors, typography, spacing, border radii, shadows
- 16 rule selectors with state variants (
:focus,:hover,:disabled,:invalid,::placeholder) - Floating labels mode — single toggle, no extra markup needed
- Condensed input mode — compact layout for space-constrained UIs
- Express checkout — Apple Pay / Google Pay with automatic device detection
- Promo code input — apply, remove, success, and error states with async validation
- Internationalization — 4 locales (en, es, fr, pt), 58 translated strings
- Loading skeleton — graceful placeholder while the session loads
- Animated success screen — confirms payment with configurable auto-redirect
- 3DS authentication — modal overlay handled automatically
- Card brand detection — real-time BIN matching for Visa, Mastercard, Amex, Discover
- Analytics events — 7 event types via
onEventcallback onReadycallback — know exactly when the form is interactive- Terms / consent text — configurable legal copy below the pay button
- Full accessibility — ARIA attributes, keyboard navigation, focus trapping
- CSS class hooks — every element has a
blaze-*class for external styling - CDN / IIFE build — embed on Shopify, WordPress, or vanilla HTML without a bundler
- PCI DSS SAQ A-EP compliant — card data encrypted by Evervault before touching any server
- Output formats — CJS, ESM, IIFE, full TypeScript declarations
Appearance API
The appearance prop gives you full control over styling without writing CSS.
<BlazeCheckout
// ... required props
appearance={{
theme: "flat", // start from a built-in base
variables: {
colorPrimary: "#0066ff",
colorBackground: "#fafafa",
colorText: "#1a1a1a",
fontFamily: "'Inter', sans-serif",
borderRadius: "8px",
spacingUnit: "14px",
},
rules: {
".Input": {
border: "1px solid #e2e8f0",
":focus": { borderColor: "#0066ff", boxShadow: "0 0 0 2px rgba(0,102,255,0.15)" },
},
".Button": {
borderRadius: "8px",
fontWeight: "600",
textTransform: "uppercase",
},
".Label": { fontSize: "13px", fontWeight: "500" },
".Error": { color: "#dc2626" },
},
labels: "floating", // "above" (default) | "floating"
inputs: "condensed", // "default" | "condensed"
disableAnimations: false,
}}
/>See docs/themes.md for the full variable reference, all 16 rule selectors, state variants, and visual examples.
Built-in Themes
| Theme | Description |
|---|---|
default |
Clean white background, subtle borders, system font stack |
night |
Dark surface with light text, ideal for dark-mode apps |
flat |
Minimal borders, filled input backgrounds, modern flat aesthetic |
Apply a theme and override individual variables:
appearance={{ theme: "night", variables: { colorPrimary: "#10b981" } }}Express Checkout
Show Apple Pay and Google Pay buttons when available on the device:
<BlazeCheckout
// ... required props
wallets={{ applePay: true, googlePay: true }}
onWalletClick={(wallet) => {
console.log(`User chose: ${wallet}`) // "applePay" | "googlePay"
}}
/>Wallet buttons render only when the device/browser supports the payment method. No buttons appear on unsupported environments — no broken UI.
Promo Codes
Enable promo code entry with server-side validation:
<BlazeCheckout
// ... required props
onApplyPromo={async (code) => {
const res = await fetch(`/api/promo/validate?code=${code}`)
const data = await res.json()
return {
valid: data.valid,
discount: data.valid ? data.discount : undefined, // e.g. "20% off"
error: data.valid ? undefined : "Invalid code",
}
}}
onRemovePromo={() => {
// User cleared the promo code
}}
appliedPromoCode="SUMMER20"
appliedPromoDiscount="20% off"
/>Internationalization
Pass a locale prop to render all UI strings in the target language:
<BlazeCheckout
// ... required props
locale="es" // "en" | "es" | "fr" | "pt"
/>All 58 user-facing strings (labels, placeholders, errors, buttons, success messages) are translated. The default is "en".
Events
The onEvent callback fires for granular lifecycle and interaction events:
| Event Type | Payload | Description |
|---|---|---|
focus |
{ field } |
User focused a form field |
blur |
{ field } |
User left a form field |
validation_error |
{ field, message } |
Client-side validation failed |
card_brand_detected |
{ brand } |
Card brand identified from BIN |
submit_attempt |
— | User clicked the pay button |
form_ready |
— | Form is fully interactive |
wallet_click |
{ wallet } |
User clicked a wallet button |
<BlazeCheckout
// ... required props
onEvent={(event) => {
analytics.track("blaze_checkout", event)
}}
onReady={() => {
console.log("Checkout form is interactive")
}}
/>CDN / Non-React Usage
For Shopify, WordPress, or any site without a React build step, use the IIFE bundle:
<div id="blaze-checkout-root"></div>
<script src="https://cdn.blaze.money/react/latest/index.iife.js"></script>
<script>
BlazeCheckout.render(document.getElementById("blaze-checkout-root"), {
paymentLinkCode: "pl_ImsNk4rl", // short code ONLY, not the share URL
// evervaultTeamId / evervaultAppId are fetched from Blaze automatically;
// only pass them to override the backend-provided values.
onComplete: function (event) {
window.location.href = "/thank-you?session=" + event.sessionId
},
appearance: {
theme: "flat",
variables: { colorPrimary: "#e11d48" },
},
})
</script>The IIFE build bundles React internally — no additional script tags needed.
Advanced: Custom UI with Hooks
For full control over markup, use the exported hooks and primitives:
import {
BlazeCheckoutProvider,
useCheckoutSession,
useCardPayment,
CardSection,
BillingAddressForm,
SubmitButton,
} from "@blaze-money/react"
import { EvervaultProvider } from "@evervault/react"
function CustomCheckout({ sessionId }: { sessionId: string }) {
const { session, loading } = useCheckoutSession(sessionId, "https://api.blaze.money")
const { submit, status, errorMessage } = useCardPayment({
sessionId,
apiUrl: "https://api.blaze.money",
onComplete: (event) => console.log("Paid!", event.sessionId),
})
if (loading) return <p>Loading...</p>
return (
<EvervaultProvider teamId="team_xxx" appId="app_xxx">
<form onSubmit={(e) => { e.preventDefault(); submit() }}>
<CardSection />
<BillingAddressForm />
<SubmitButton status={status}>
Pay {session?.formattedAmount}
</SubmitButton>
{errorMessage && <p className="error">{errorMessage}</p>}
</form>
</EvervaultProvider>
)
}Props Reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
paymentLinkCode |
string |
Yes¹ | — | Payment link short code only (e.g. pl_ImsNk4rl) — never the full https://blze.at/... share URL |
sessionId |
string |
Yes¹ | — | Checkout session ID from your server (checkout-session mode) |
evervaultTeamId |
string |
No | fetched from backend | Optional override. Blaze-provided public Evervault team ID. By default the widget fetches it from GET /v1/checkout/config; only pass this to override |
evervaultAppId |
string |
No | fetched from backend | Optional override. Blaze-provided public Evervault app ID. By default the widget fetches it from GET /v1/checkout/config; only pass this to override |
publishableKey |
string |
No | — | Deprecated and ignored. No client-side key exists; pass nothing. Kept only for backward compatibility |
onComplete |
(event) => void |
Yes | — | Called when payment succeeds |
onCancel |
() => void |
No | — | Called when user cancels |
onError |
(error) => void |
No | — | Called on unrecoverable error |
onEvent |
(event) => void |
No | — | Granular lifecycle/interaction events |
onReady |
() => void |
No | — | Called when form is fully interactive |
appearance |
Appearance |
No | default theme | Visual customization object |
locale |
"en" | "es" | "fr" | "pt" |
No | "en" |
UI language |
collectAddress |
boolean |
No | true |
Show billing address fields |
wallets |
{ applePay?: boolean; googlePay?: boolean } |
No | — | Enable express checkout buttons |
onWalletClick |
(wallet) => void |
No | — | Called when a wallet button is clicked |
onApplyPromo |
(code) => Promise<{valid, discount?, error?}> |
No | — | Server-side promo validation |
onRemovePromo |
() => void |
No | — | Called when promo is cleared |
appliedPromoCode |
string | null |
No | — | Currently applied promo code |
appliedPromoDiscount |
string | null |
No | — | Discount description to display |
terms |
string | ReactNode |
No | — | Legal/consent text below pay button |
apiUrl |
string |
No | https://api.blaze.money |
Override API base (point at staging for testing) |
¹ Provide exactly one of paymentLinkCode (recommended, for real payments) or sessionId (checkout-session mode).
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
404 on GET /v1/checkout/pay/... |
You passed the full share URL (https://blze.at/pl_...) as paymentLinkCode instead of the bare short code |
Pass only the pl_... short code |
CORS error or 404 on GET /v1/checkout/config |
apiUrl points at a dead or wrong URL — there is no Blaze backend there to answer, so the config (and encryption IDs) can't load |
Ensure apiUrl points at a live Blaze backend. It defaults to https://api.blaze.money; only override it with a real, reachable backend (e.g. staging). A dead/wrong URL yields a 404 or CORS error because nothing is serving the endpoint |
CORS error (No 'Access-Control-Allow-Origin' header) |
The checkout API does not allow the origin you're calling from | Confirm your origin is permitted. If testing from localhost, the origin must be allowed by the Blaze checkout API; if your production origin is being CORS-blocked, contact Blaze |
| Evervault 404 / "An error occurred while retrieving the apps key" | The Evervault teamId / appId pair is wrong (only possible if you passed explicit overrides), or the app isn't published under that team |
Remove any evervaultTeamId / evervaultAppId overrides to use Blaze's backend-provided values. A 404 from keys.evervault.com means the team/app pair is wrong or the app is not published under that team |
Documentation
- docs/themes.md — Full Appearance API reference (all 30 variables, 16 rule selectors, state variants)
- docs/features.md — Detailed feature documentation (express checkout, promo codes, i18n, 3DS, success screen)
- docs/integration-checklist.md — Step-by-step go-live checklist
- docs/pci-compliance.md — PCI DSS SAQ A-EP compliance details
- examples/gallery.html — Interactive visual playground with all themes and features
Security
Card numbers and CVCs are encrypted by Evervault inside the browser before any network request leaves the page. Neither your server nor Blaze's API ever sees raw card data — only Evervault-encrypted ciphertext. This architecture qualifies you for PCI DSS SAQ A-EP, the lightest compliance level for merchants that embed payment forms.
See docs/pci-compliance.md for the full compliance model and your responsibilities.
License
MIT