JSPM

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

Lightweight JavaScript SDK for Paypercut Checkout

Package Exports

  • @paypercut/checkout-js
  • @paypercut/checkout-js/cdn
  • @paypercut/checkout-js/package.json

Readme

Paypercut Checkout JavaScript SDK

A lightweight, framework-agnostic JavaScript SDK for embedding Paypercut Checkout into your web application. Works seamlessly with vanilla JavaScript, TypeScript, React, Vue, Angular, and other modern frameworks.

npm version License: MIT Bundle Size TypeScript


Table of Contents


Installation

Via NPM

npm install @paypercut/checkout-js

Via Yarn

yarn add @paypercut/checkout-js

Via PNPM

pnpm add @paypercut/checkout-js

Via CDN

<!-- jsDelivr (recommended with SRI) -->
<script src="https://cdn.jsdelivr.net/npm/@paypercut/checkout-js@1.0.4/dist/paypercut-checkout.iife.min.js"
        integrity="sha384-..."
        crossorigin="anonymous"></script>

<!-- or UNPKG -->
<script src="https://unpkg.com/@paypercut/checkout-js@1.0.4/dist/paypercut-checkout.iife.min.js"></script>

Quick Start

This returns a checkout session ID like CHK_12345.

1. Embed the Checkout

Use the checkout ID to initialize the checkout on your frontend:

import { PaypercutCheckout } from '@paypercut/checkout-js';

// Initialize the checkout
const checkout = PaypercutCheckout({
  id: 'CHK_12345',              // Your checkout session ID from step 1
  containerId: '#checkout'       // CSS selector or HTMLElement
});

// Listen for payment events
checkout.on('success', () => {
  console.log('Payment successful!');
});

checkout.on('error', () => {
  console.error('Payment failed');
});

// Optional: Listen for when iframe finishes loading (useful for modal implementations)
checkout.on('loaded', () => {
  console.log('Checkout loaded and ready');
});

// Render the checkout
checkout.render();

That's it! The checkout is now embedded in your page.

2. Display Mode

The SDK supports one display mode:

Mode When to Use Configuration
Embedded Checkout is part of your page layout Provide containerId

API Reference

PaypercutCheckout(options)

Creates a new checkout instance.

Options

Option Type Required Description
id string Yes Checkout session identifier
containerId string | HTMLElement Yes CSS selector or element where iframe mounts.

Example

const checkout = PaypercutCheckout({
  id: 'CHK_12345',
  containerId: '#checkout-container'
});

Instance Methods

render()

Mounts and displays the checkout iframe.

checkout.render();

destroy()

Destroys the instance and cleans up all event listeners. Call this when you're done with the checkout instance.

checkout.destroy();

on(event, handler)

Subscribes to checkout events. Returns an unsubscribe function.

const unsubscribe = checkout.on('success', () => {
  console.log('Payment successful!');
});

// Later, to unsubscribe
unsubscribe();

once(event, handler)

Subscribes to a checkout event that automatically unsubscribes after the first emission. Returns an unsubscribe function.

checkout.once('loaded', () => {
  console.log('Checkout loaded - this will only fire once');
});

off(event, handler)

Unsubscribes from checkout events.

const handler = () => console.log('Payment successful!');
checkout.on('success', handler);
checkout.off('success', handler);

isMounted()

Returns whether the checkout is currently mounted.

if (checkout.isMounted()) {
  console.log('Checkout is visible');
}

Events

Subscribe to events using the on() method:

Event Description Payload
loaded Checkout iframe has finished loading void
success Payment completed successfully void
error Payment failed or an error occurred void

Usage Examples

Vanilla JavaScript (CDN)

<!DOCTYPE html>
<html>
<head>
  <title>Paypercut Checkout</title>
</head>
<body>
  <div id="checkout"></div>

  <script src="https://cdn.jsdelivr.net/npm/@paypercut/checkout-js@1.0.4/dist/paypercut-checkout.iife.min.js"></script>
  <script>
    const checkout = PaypercutCheckout({
      id: 'CHK_12345',
      containerId: '#checkout'
    });

    checkout.on('success', function(data) {
      alert('Payment successful! Transaction ID: ' + data.transactionId);
    });

    checkout.on('error', function(error) {
      alert('Payment failed: ' + error.message);
    });

    checkout.render();
  </script>
</body>
</html>

TypeScript / ESM

import { PaypercutCheckout } from '@paypercut/checkout-js';

const checkout = PaypercutCheckout({
  id: 'CHK_12345',
  containerId: '#checkout'
});

checkout.on('success', (data) => {
  console.log('Payment successful:', data);
  // Redirect to success page
  window.location.href = '/success';
});

checkout.on('error', (error) => {
  console.error('Payment error:', error);
  // Show error message to user
  alert(`Payment failed: ${error.message}`);
});

checkout.render();

React

import { useEffect, useRef, useState } from 'react';
import { PaypercutCheckout, CheckoutInstance } from '@paypercut/checkout-js';

export function CheckoutComponent({ checkoutId }: { checkoutId: string }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const checkoutRef = useRef<CheckoutInstance | null>(null);
  const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');

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

    const checkout = PaypercutCheckout({
      id: checkoutId,
      containerId: containerRef.current
    });

    checkout.on('loaded', () => {
      setStatus('idle');
      console.log('Checkout loaded');
    });

    checkout.on('success', (data) => {
      setStatus('success');
      console.log('Payment successful:', data);
    });

    checkout.on('error', (error) => {
      setStatus('error');
      console.error('Payment error:', error);
    });

    checkout.render();
    checkoutRef.current = checkout;

    return () => {
      checkout.destroy();
    };
  }, [checkoutId]);

  return (
    <div>
      <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
      {status === 'loading' && <p>Processing payment...</p>}
      {status === 'success' && <p>✅ Payment successful!</p>}
      {status === 'error' && <p>❌ Payment failed. Please try again.</p>}
    </div>
  );
}

If you want to create a modal-like experience, you can use the loaded event to show/hide a loading overlay:

import { useEffect, useRef, useState } from 'react';
import { PaypercutCheckout, CheckoutInstance } from '@paypercut/checkout-js';

export function ModalCheckout({ checkoutId, onClose }: { checkoutId: string; onClose: () => void }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isLoading, setIsLoading] = useState(true);

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

    const checkout = PaypercutCheckout({
      id: checkoutId,
      containerId: containerRef.current
    });

    checkout.on('loaded', () => {
      setIsLoading(false);
    });

    checkout.on('success', (data) => {
      console.log('Payment successful:', data);
      onClose();
    });

    checkout.on('error', (error) => {
      console.error('Payment error:', error);
    });

    checkout.render();

    return () => {
      checkout.destroy();
    };
  }, [checkoutId, onClose]);

  return (
    <div style={{
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      zIndex: 9999
    }}>
      <div style={{
        position: 'relative',
        width: '90%',
        maxWidth: '500px',
        height: '90%',
        maxHeight: '700px',
        backgroundColor: '#fff',
        borderRadius: '12px',
        overflow: 'hidden'
      }}>
        {isLoading && (
          <div style={{
            position: 'absolute',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: '#fff',
            zIndex: 1
          }}>
            <p>Loading checkout...</p>
          </div>
        )}
        <div ref={containerRef} style={{ width: '100%', height: '100%' }} />
        <button
          onClick={onClose}
          style={{
            position: 'absolute',
            top: '10px',
            right: '10px',
            zIndex: 2
          }}
        ></button>
      </div>
    </div>
  );
}

Security

Origin Validation

The SDK automatically validates all postMessage communications against the configured checkoutHost origin. This prevents unauthorized messages from being processed.

Content Security Policy

For enhanced security, configure your Content Security Policy headers:

Content-Security-Policy:
  frame-src https://buy.paypercut.io OR something else;
  script-src 'self' https://cdn.jsdelivr.net https://unpkg.com;
  connect-src https://buy.paypercut.io  OR something else;

HTTPS Only

Always use HTTPS in production. The SDK is designed for secure communication over HTTPS.

Troubleshooting

Checkout not displaying

Problem: The iframe doesn't appear after calling render().

Solutions:

  • Ensure the container element exists in the DOM before calling render()
  • Check that the id is a valid checkout session ID
  • Verify there are no CSP errors in the browser console
  • Enable debug mode: debug: true to see detailed logs

Events not firing

Problem: Event handlers are not being called.

Solutions:

  • Ensure you subscribe to events before calling render()
  • Check that the checkout session is active and not expired
  • Enable debug mode to see message logs

TypeScript errors

Problem: TypeScript shows type errors when using the SDK.

Solutions:

  • Ensure you're importing types: import { PaypercutCheckout, CheckoutInstance } from '@paypercut/checkout-js'
  • Update to the latest version of the SDK
  • Check that your tsconfig.json includes the SDK's type definitions

Performance Optimization

Preconnect to Checkout Host

Add DNS prefetch and preconnect hints to your HTML for faster loading:

<link rel="preconnect" href="https://buy.paypercut.io ">
<link rel="dns-prefetch" href="https://buy.paypercut.io ">

Lazy Loading

Load the SDK only when needed to reduce initial bundle size:

async function loadCheckout() {
  const { PaypercutCheckout } = await import('@paypercut/checkout-js');
  return PaypercutCheckout;
}

// Load when user clicks pay button
payButton.addEventListener('click', async () => {
  const PaypercutCheckout = await loadCheckout();
  const checkout = PaypercutCheckout({ id: 'CHK_12345' });
  checkout.render();
});

Best Practices

1. Always Verify Payments on Your Backend

Never rely solely on frontend events for payment confirmation. Always verify payments using webhooks on your backend:

// ✅ Good: Use frontend events for UI updates only
checkout.on('success', () => {
  // Show success message
  showSuccessMessage();
  // Redirect to order confirmation page
  window.location.href = '/orders/confirmation';
});

// ❌ Bad: Don't grant access based on frontend events alone
checkout.on('success', () => {
  // This can be manipulated by users!
  grantPremiumAccess(userId); // Don't do this!
});

2. Handle All Event Types

Always handle both success and error events:

checkout.on('success', () => {
  // Handle success
  showSuccessMessage();
});

checkout.on('error', () => {
  // Show user-friendly error message
  alert('Payment failed. Please try again.');
});

3. Clean Up on Component Unmount

Always call destroy() when your component unmounts to prevent memory leaks:

// React example
useEffect(() => {
  const checkout = PaypercutCheckout({ id: checkoutId });
  checkout.render();

  return () => {
    checkout.destroy(); // Clean up!
  };
}, []);

FAQ For Internal Validation

Q: How do I handle successful payments? Listen to the success event and redirect users or update your UI accordingly. Always verify the payment on your backend using webhooks.

Q: Can I use this with server-side rendering (SSR)? Yes, but ensure the SDK is only initialized on the client side. For Next.js, use dynamic imports with ssr: false.

Q: What happens if the user closes the browser during payment?

Q: Can I have multiple checkouts on one page? Maybe Yes, but only one should be active at a time to avoid user confusion. If no, we destroy the previous one and create a new one.

Q: How do I handle errors? Listen to the error event and display user-friendly error messages. Log errors for debugging.