agentleFS
hit-payhit-pay/claude-code-plugin/skills/drop-in-ui/SKILL.md

drop-in-ui

Skill1 starsChanged 8 months agoMITView on GitHub

Embed HitPay's Drop-In checkout UI (HitPay.js) into web applications. Use when user says "HitPay Drop-In", "embed payment form", "HitPay.js", "payment modal", "checkout popup", "inline checkout", or "embedded checkout".

  • Reads credentials
  • Sends data out

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 3 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Apple Pay / Google Pay: Not supported — use redirect flow instead

    line 35
  2. Distinctive:

    Add the script tag to your HTML or layout:

    line 41
  3. Distinctive:

    Create a payment request on your server, then pass the URL to the client:

    line 76

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

HitPay Drop-In UI

Embed HitPay's hosted checkout as a modal overlay on your site using HitPay.js. The Drop-In is best suited for QR-based payment methods (PayNow, GrabPay, ShopeePay, etc.) — no PCI scope required. For card payments, Apple Pay, or Google Pay, use the redirect checkout flow instead.

When to Apply

  • Developer wants an embedded checkout without building custom UI
  • Developer asks about HitPay.js, payment modal, or checkout popup
  • Need a simpler alternative to building redirect or QR flows from scratch

Building custom flows? See the payment-integration skill for redirect and embedded QR patterns. Payment method lookup? See the payment-methods skill.

When to Use Drop-In vs Other Approaches

ApproachBest ForPCI ScopeCustomization
Drop-In UIQR-based methods (PayNow, GrabPay, ShopeePay, etc.)NoneLimited (colors, logo)
RedirectCard payments, Apple Pay, Google PayNoneNone (HitPay page)
Embedded QRQR-only payments, custom UINoneFull control

Important: Drop-In UI is recommended for QR-based payment methods only. For card payments, use the redirect flow — 3DS authentication pop-ups do not work reliably in the Drop-In modal.

Drop-In Limitations

  • Cards not recommended: 3DS authentication pop-ups cause issues inside the Drop-In modal — use redirect flow for card payments
  • Apple Pay / Google Pay: Not supported — use redirect flow instead
  • Custom styling: Limited to color and logo customization
  • Mobile: Opens as full-screen overlay on mobile devices

Step 1: Include HitPay.js

Add the script tag to your HTML or layout:

<!-- Production -->
<script src="https://hit-pay.com/hitpay.js"></script>

<!-- Sandbox -->
<script src="https://sandbox.hit-pay.com/hitpay.js"></script>

For Next.js, add via next/script:

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script
          src={process.env.NEXT_PUBLIC_HITPAY_ENV === 'production'
            ? 'https://hit-pay.com/hitpay.js'
            : 'https://sandbox.hit-pay.com/hitpay.js'}
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Step 2: Create Payment Request (Server)

Create a payment request on your server, then pass the URL to the client:

// app/api/payments/create/route.ts
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const { amount, currency, orderId, email } = await request.json();

  const baseUrl = process.env.HITPAY_ENV === 'production'
    ? 'https://api.hit-pay.com'
    : 'https://api.sandbox.hit-pay.com';

  const response = await fetch(`${baseUrl}/v1/payment-requests`, {
    method: 'POST',
    headers: {
      'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount,
      currency,
      email,
      reference_number: orderId,
      redirect_url: `${process.env.NEXT_PUBLIC_APP_URL}/payment/complete`,
      webhook: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/hitpay`,
    }),
  });

  const data = await response.json();
  return NextResponse.json({
    paymentRequestId: data.id,
    paymentUrl: data.url,
  });
}

Step 3: Initialize Drop-In (Client)

// components/DropInCheckout.tsx
'use client';

import { useCallback } from 'react';

declare global {
  interface Window {
    HitPay: {
      init(url: string, options: HitPayInitOptions): void;
      toggle(options?: { open: boolean }): void;
    };
  }
}

interface HitPayInitOptions {
  domain?: string;
  apiDomain?: string;
  onSuccess?: (data: { payment_request_id: string; status: string; reference_number?: string }) => void;
  onError?: (error: { message: string }) => void;
  onClose?: () => void;
}

interface Props {
  amount: number;
  currency: string;
  orderId: string;
  email?: string;
}

export function DropInCheckout({ amount, currency, orderId, email }: Props) {
  const handleCheckout = useCallback(async () => {
    // 1. Create payment request on server
    const response = await fetch('/api/payments/create', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ amount, currency, orderId, email }),
    });

    const { paymentUrl } = await response.json();

    // 2. Initialize Drop-In with the payment URL
    window.HitPay.init(paymentUrl, {
      domain: process.env.NEXT_PUBLIC_HITPAY_ENV === 'production'
        ? 'hit-pay.com'
        : 'sandbox.hit-pay.com',
      apiDomain: process.env.NEXT_PUBLIC_HITPAY_ENV === 'production'
        ? 'api.hit-pay.com'
        : 'api.sandbox.hit-pay.com',
      onSuccess: (data) => {
        console.log('Payment successful:', data.payment_request_id);
        // Redirect to success page or update UI
        // IMPORTANT: Always verify via webhook before fulfilling
        window.location.href = `/payment/complete?ref=${data.reference_number}`;
      },
      onError: (error) => {
        console.error('Payment error:', error.message);
        // Show error to user
      },
      onClose: () => {
        console.log('Payment modal closed');
        // User closed without completing — no action needed
      },
    });

    // 3. Open the Drop-In modal
    window.HitPay.toggle({ open: true });
  }, [amount, currency, orderId, email]);

  return (
    <button onClick={handleCheckout}>
      Pay {currency} {amount.toFixed(2)}
    </button>
  );
}

HitPay.init() Options

OptionTypeRequiredDescription
domainstringYes'hit-pay.com' (prod) or 'sandbox.hit-pay.com' (sandbox)
apiDomainstringYes'api.hit-pay.com' (prod) or 'api.sandbox.hit-pay.com' (sandbox)
onSuccessfunctionNoCalled when payment completes successfully
onErrorfunctionNoCalled when payment fails
onClosefunctionNoCalled when user closes the modal

onSuccess Callback Data

{
  payment_request_id: string;  // HitPay payment request ID
  status: string;              // "completed"
  reference_number?: string;   // Your order reference
}

onError Callback Data

{
  message: string;  // Human-readable error message
}

Full Next.js Example

Server Component (Page)

// app/checkout/page.tsx
import { DropInCheckout } from '@/components/DropInCheckout';

export default function CheckoutPage({
  searchParams,
}: {
  searchParams: { orderId: string; amount: string };
}) {
  return (
    <main>
      <h1>Complete Your Purchase</h1>
      <DropInCheckout
        amount={parseFloat(searchParams.amount)}
        currency="SGD"
        orderId={searchParams.orderId}
      />
    </main>
  );
}

Payment Complete Page

// app/payment/complete/page.tsx
export default function PaymentCompletePage({
  searchParams,
}: {
  searchParams: { ref?: string };
}) {
  return (
    <main>
      <h1>Payment Received</h1>
      <p>Thank you! Your order {searchParams.ref} is being processed.</p>
      <p>You will receive a confirmation email shortly.</p>
    </main>
  );
}

Important Notes

  1. Always verify via webhook — The onSuccess callback is client-side and can be spoofed. Never fulfill orders based solely on the callback. See the webhook-handler skill.
  2. HitPay.js is loaded globally — It attaches to window.HitPay. Only call init() after the script has loaded.
  3. One modal at a time — Calling init() again replaces the previous payment session.
  4. Mobile behavior — The Drop-In opens as a full-screen overlay on mobile devices.
  5. CSP headers — If using Content Security Policy, allow frame-src for hit-pay.com or sandbox.hit-pay.com.

Environment Variables

# .env.local
HITPAY_API_KEY=your_api_key
HITPAY_SALT=your_webhook_salt
HITPAY_ENV=sandbox  # or "production"
NEXT_PUBLIC_HITPAY_ENV=sandbox  # client-side env detection
NEXT_PUBLIC_APP_URL=http://localhost:3000

See references/dropin-ui-api.md for the full HitPay.js API reference including CSS customization.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.