drop-in-ui
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
| Approach | Best For | PCI Scope | Customization |
|---|---|---|---|
| Drop-In UI | QR-based methods (PayNow, GrabPay, ShopeePay, etc.) | None | Limited (colors, logo) |
| Redirect | Card payments, Apple Pay, Google Pay | None | None (HitPay page) |
| Embedded QR | QR-only payments, custom UI | None | Full 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
| Option | Type | Required | Description |
|---|---|---|---|
domain |
string | Yes | 'hit-pay.com' (prod) or 'sandbox.hit-pay.com' (sandbox) |
apiDomain |
string | Yes | 'api.hit-pay.com' (prod) or 'api.sandbox.hit-pay.com' (sandbox) |
onSuccess |
function | No | Called when payment completes successfully |
onError |
function | No | Called when payment fails |
onClose |
function | No | Called 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
- Always verify via webhook — The
onSuccesscallback is client-side and can be spoofed. Never fulfill orders based solely on the callback. See the webhook-handler skill. - HitPay.js is loaded globally — It attaches to
window.HitPay. Only callinit()after the script has loaded. - One modal at a time — Calling
init()again replaces the previous payment session. - Mobile behavior — The Drop-In opens as a full-screen overlay on mobile devices.
- CSP headers — If using Content Security Policy, allow
frame-srcforhit-pay.comorsandbox.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.
More from hit-pay/claude-code-plugin
qr-checkout
Generate a local QR payment checkout page using HitPay MCP tools. Use when user says "create a QR checkout page", "QR payment page", "show QR code for PayNow", "QR checkout for QRIS", "build a QR payment page", "QR page for [country] customer", or "embedded QR checkout page".
1webhook-handler
Handle HitPay payment webhooks with signature verification. Use when user says "HitPay webhook", "verify webhook signature", "payment notification", "webhook handler", "Hitpay-Signature", or "payment confirmation".
1payment-methods
Look up HitPay payment methods by country, currency, and provider. Use when user says "which payment methods in Malaysia", "what methods for Singapore", "HitPay payment methods", "payment method lookup", "which QR methods", "supported methods", or "available payment methods".
1payment-integration
Integrate HitPay payment gateway for online payments in Next.js and JS/TS applications. Use when user says "Add HitPay", "HitPay checkout", "HitPay payments", "PayNow integration", "HitPay integration", "payment gateway", or "accept payments".
1