JSPM

@blaze-money/react

0.1.2
    • ESM via JSPM
    • ES Module Entrypoint
    • Export Map
    • Keywords
    • License
    • Repository URL
    • TypeScript Types
    • README
    • Created
    • Published
    • Downloads 27
    • Score
      100M100P100Q78581F
    • License MIT

    React components for accepting payments with Blaze

    Package Exports

    • @blaze-money/react
    • @blaze-money/react/package.json

    Readme

    @blaze-money/react

    Drop-in React components for accepting payments with Blaze.

    npm version bundle size PCI DSS SAQ A-EP


    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.)

    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/appId from GET /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 paymentLinkCode is the bare short code (pl_ImsNk4rl), not the share URL. Passing the full https://blze.at/pl_ImsNk4rl produces a request to GET /v1/checkout/pay/https://blze.at/pl_ImsNk4rl, which returns a 404. Pass only the pl_... 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 themesdefault, 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 onEvent callback
    • onReady callback — 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


    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