payment-integration
HitPay Payment Integration
Payment gateway integration for APAC businesses using Next.js and JavaScript/TypeScript. Supports card payments via redirect, QR-based payments (PayNow, GrabPay, etc.) via embedded QR codes, and HitPay Drop-In UI for an all-in-one checkout.
When to Apply
Reference this skill when:
- Integrating HitPay payment gateway
- Creating payment checkout flows
- Implementing QR code payments (PayNow, GrabPay, ShopeePay)
- Handling payment webhooks
- Building checkout pages
Need the Drop-In UI? For HitPay.js embedded checkout modal, see the drop-in-ui skill. Operating payments via MCP tools? For provider-currency mapping and QR operations, use the payment-methods skill.
Step 1: Determine Payment Methods
Ask which payment methods the developer wants to support:
Card-Only Methods
card(Visa, Mastercard, Amex)
QR-Only Methods
paynow_online(Singapore)grabpay_directshopee_paywechatalipayfpx(Malaysia)promptpay(Thailand)truemoney(Thailand)vietqr(Vietnam)qris(Indonesia)upi(India)gcash(Philippines)
Step 2: Choose Frontend Approach
| Payment Methods | Recommended Frontend |
|---|---|
| Card only | Option A: Redirect to HitPay checkout |
| QR only | Option B: Embedded QR on your site |
| Both card + QR | Option C: Payment method selector |
| Full checkout experience | Option D: HitPay Drop-In UI (see drop-in-ui skill) |
API Basics
| Environment | Base URL |
|---|---|
| Sandbox | https://api.sandbox.hit-pay.com |
| Production | https://api.hit-pay.com |
Authentication
const headers = {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY,
'Content-Type': 'application/json',
};
Get API keys from Settings > Payment Gateway > API Keys in your HitPay dashboard.
Frontend Option A: Redirect (Cards)
Best for card payments or when you want HitPay to handle the full checkout UI.
Flow
- Backend creates payment request
- Frontend redirects to
response.url - Customer pays on HitPay hosted page
- Customer returns to your
redirect_url - Backend confirms via webhook
Next.js API Route
// app/api/payments/create/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const { amount, currency, orderId } = await request.json();
const response = await fetch('https://api.sandbox.hit-pay.com/v1/payment-requests', {
method: 'POST',
headers: {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
currency,
payment_methods: ['card'],
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({ url: data.url, paymentRequestId: data.id });
}
Frontend Component
// components/CheckoutButton.tsx
'use client';
export function CheckoutButton({ amount, currency, orderId }: Props) {
const handleCheckout = async () => {
const response = await fetch('/api/payments/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, currency, orderId }),
});
const { url } = await response.json();
window.location.href = url; // Redirect to HitPay
};
return <button onClick={handleCheckout}>Pay with Card</button>;
}
Frontend Option B: Embedded QR Code
Best for QR-based payments where customer stays on your site.
Flow
- Backend creates payment request with
generate_qr: true - Frontend renders QR code from
response.qr_code_data - Customer scans with payment app
- Frontend polls for status or listens for webhook
- Backend confirms via webhook
Next.js API Route
// app/api/payments/create-qr/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const { amount, currency, orderId, paymentMethod } = await request.json();
const response = await fetch('https://api.sandbox.hit-pay.com/v1/payment-requests', {
method: 'POST',
headers: {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
currency,
payment_methods: [paymentMethod],
generate_qr: true,
reference_number: orderId,
webhook: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/hitpay`,
}),
});
const data = await response.json();
return NextResponse.json({
paymentRequestId: data.id,
qrCodeData: data.qr_code_data,
});
}
Frontend Component
// components/QRPayment.tsx
'use client';
import { useEffect, useRef } from 'react';
import QRCode from 'qrcode';
export function QRPayment({ amount, currency, orderId, paymentMethod }: Props) {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const createQR = async () => {
const response = await fetch('/api/payments/create-qr', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, currency, orderId, paymentMethod }),
});
const { qrCodeData } = await response.json();
if (canvasRef.current && qrCodeData) {
await QRCode.toCanvas(canvasRef.current, qrCodeData, { width: 256 });
}
};
createQR();
}, [amount, currency, orderId, paymentMethod]);
return (
<div>
<canvas ref={canvasRef} />
<p>Scan with your payment app</p>
</div>
);
}
Frontend Option C: Payment Method Selector
Best for supporting both cards and QR methods.
// components/PaymentMethodSelector.tsx
'use client';
import { useState } from 'react';
import { QRPayment } from './QRPayment';
const PAYMENT_METHODS = [
{ id: 'card', label: 'Credit/Debit Card', type: 'redirect' },
{ id: 'paynow_online', label: 'PayNow', type: 'qr' },
{ id: 'grabpay_direct', label: 'GrabPay', type: 'qr' },
{ id: 'shopee_pay', label: 'ShopeePay', type: 'qr' },
];
export function PaymentMethodSelector({ amount, currency, orderId }: Props) {
const [selected, setSelected] = useState<string | null>(null);
const handlePayment = async (method: typeof PAYMENT_METHODS[0]) => {
if (method.type === 'redirect') {
const response = await fetch('/api/payments/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, currency, orderId }),
});
const { url } = await response.json();
window.location.href = url;
} else {
setSelected(method.id);
}
};
if (selected) {
return (
<QRPayment
amount={amount}
currency={currency}
orderId={orderId}
paymentMethod={selected}
/>
);
}
return (
<div>
<h3>Select Payment Method</h3>
{PAYMENT_METHODS.map((method) => (
<button key={method.id} onClick={() => handlePayment(method)}>
{method.label}
</button>
))}
</div>
);
}
Webhook Handling
Always verify webhook signatures before processing. See the webhook-handler skill for full details.
// app/api/webhooks/hitpay/route.ts
import { NextResponse } from 'next/server';
import crypto from 'crypto';
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('Hitpay-Signature');
const expectedSignature = crypto
.createHmac('sha256', process.env.HITPAY_SALT!)
.update(body)
.digest('hex');
if (signature !== expectedSignature) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const payload = JSON.parse(body);
if (payload.status === 'completed') {
await markOrderAsPaid(payload.reference_number);
}
return NextResponse.json({ received: true });
}
Checking Payment Status
See references/payment-request-api.md for the full API reference.
// app/api/payments/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const response = await fetch(
`https://api.sandbox.hit-pay.com/v1/payment-requests/${params.id}`,
{
headers: {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY!,
},
}
);
const data = await response.json();
return NextResponse.json(data);
}
Environment Variables
# .env.local
HITPAY_API_KEY=your_api_key
HITPAY_SALT=your_webhook_salt
NEXT_PUBLIC_APP_URL=http://localhost:3000
Test Cards (Sandbox)
| Result | Number | Expiry | CVC |
|---|---|---|---|
| Success | 4242 4242 4242 4242 | Any future | Any 3 digits |
| Declined | 4000 0000 0000 0002 | Any future | Any 3 digits |
Resources
- Sandbox Dashboard: https://dashboard.sandbox.hit-pay.com
- Production Dashboard: https://dashboard.hit-pay.com
- API Docs: https://docs.hitpayapp.com
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".
1drop-in-ui
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".
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".
1