Checkout SDK

Framework integrations

Use the Checkout SDK in React, Vue, Angular, and vanilla JavaScript.

The Checkout SDK is framework-neutral. Use ZippyPay.create() for imperative control, or the <zippy-pay-checkout> web component for declarative integrations. Always set clientToken via JavaScript — never as an HTML attribute.

Vanilla JavaScript

main.js

javascript
import { ZippyPay } from "@zippypay/checkout";

const checkout = ZippyPay.create({
  environment: "production",
  sessionId: "550e8400-e29b-41d4-a716-446655440000",
  clientToken: "zps_REDACTED",
  mode: "inline",
  container: "#zippy-checkout",
  onComplete: (session) => {
    console.log("Paid:", session.transactionId);
  },
  onError: (error) => {
    console.error(error.code, error.detail);
  },
});

window.addEventListener("beforeunload", () => checkout.destroy());

React

Use the web component with a ref, or call ZippyPay.create() imperatively inside useEffect.

Web component (Next.js App Router)

tsx
"use client";

import { useEffect, useRef } from "react";
import "@zippypay/checkout";
import type { PaymentSession } from "@zippypay/checkout";

type Props = {
  environment: "production" | "staging" | "development";
  sessionId: string;
  clientToken: string;
};

export function ZippyCheckout({ environment, sessionId, clientToken }: Props) {
  const ref = useRef<HTMLElement & { clientToken: string }>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;

    el.clientToken = clientToken;

    const onComplete = (e: Event) => {
      const session = (e as CustomEvent<PaymentSession>).detail;
      console.log("Paid", session.transactionId);
    };

    el.addEventListener("zippy-complete", onComplete);
    return () => el.removeEventListener("zippy-complete", onComplete);
  }, [clientToken]);

  return (
    <zippy-pay-checkout
      ref={ref}
      environment={environment}
      session-id={sessionId}
      mode="inline"
      theme-primary="#2563eb"
    />
  );
}

Imperative

tsx
"use client";

import { useEffect, useRef } from "react";
import { ZippyPay } from "@zippypay/checkout";

type Props = {
  sessionId: string;
  clientToken: string;
};

export function ZippyCheckout({ sessionId, clientToken }: Props) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const checkout = ZippyPay.create({
      environment: "production",
      sessionId,
      clientToken,
      mode: "inline",
      container: containerRef.current,
      onComplete: (s) => console.log(s.transactionId),
    });

    return () => checkout.destroy();
  }, [sessionId, clientToken]);

  return <div ref={containerRef} />;
}

Vue 3

ZippyCheckout.vue

vue
<template>
  <zippy-pay-checkout
    ref="checkoutEl"
    environment="production"
    :session-id="sessionId"
    mode="inline"
    theme-primary="#2563eb"
    @zippy-complete="onComplete"
    @zippy-error="onError"
  />
</template>

<script setup lang="ts">
import "@zippypay/checkout";
import { onMounted, ref, watch } from "vue";
import type { PaymentSession, ZippyPayError } from "@zippypay/checkout";

const props = defineProps<{
  sessionId: string;
  clientToken: string;
}>();

const checkoutEl = ref<{ clientToken: string } | null>(null);

onMounted(() => {
  if (checkoutEl.value) {
    checkoutEl.value.clientToken = props.clientToken;
  }
});

watch(
  () => props.clientToken,
  (token) => {
    if (checkoutEl.value) checkoutEl.value.clientToken = token;
  },
);

function onComplete(e: CustomEvent<PaymentSession>) {
  console.log("Paid", e.detail.transactionId);
}

function onError(e: CustomEvent<ZippyPayError>) {
  console.error(e.detail.code, e.detail.detail);
}
</script>

Angular

zippy-checkout.component.ts

typescript
import "@zippypay/checkout";
import {
  Component,
  CUSTOM_ELEMENTS_SCHEMA,
  ElementRef,
  ViewChild,
  AfterViewInit,
  Input,
} from "@angular/core";

@Component({
  selector: "app-zippy-checkout",
  template: `
    <zippy-pay-checkout
      #checkout
      [attr.environment]="environment"
      [attr.session-id]="sessionId"
      mode="inline"
      (zippy-complete)="onComplete($event)">
    </zippy-pay-checkout>
  `,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class ZippyCheckoutComponent implements AfterViewInit {
  @Input() environment = "production";
  @Input() sessionId = "";
  @Input() clientToken = "";

  @ViewChild("checkout", { read: ElementRef })
  checkoutRef!: ElementRef<{ clientToken: string }>;

  ngAfterViewInit() {
    this.checkoutRef.nativeElement.clientToken = this.clientToken;
  }

  onComplete(event: Event) {
    const session = (event as CustomEvent).detail;
    console.log(session.transactionId);
  }
}

Web component

Auto-registered on import '@zippypay/checkout'. Mounts on connectedCallback and unmounts on disconnectedCallback. Does not mount until both session-id and clientToken are set.

HTML + module script

html
<script type="module">
  import "@zippypay/checkout";
</script>

<zippy-pay-checkout
  environment="production"
  session-id="550e8400-e29b-41d4-a716-446655440000"
  mode="inline"
  theme-primary="#2563eb"
  theme-mode="auto"
></zippy-pay-checkout>

<script type="module">
  const el = document.querySelector("zippy-pay-checkout");
  el.clientToken = "zps_REDACTED";
  el.addEventListener("zippy-complete", (e) => console.log(e.detail));
</script>