hitpay
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHitPay Integration
HitPay 集成
Payment gateway integration for APAC businesses using Next.js and JavaScript/TypeScript. Supports card payments via redirect and QR-based payments (PayNow, GrabPay, etc.) via embedded QR codes.
为使用Next.js和JavaScript/TypeScript的亚太地区企业提供支付网关集成方案。支持通过跳转方式实现银行卡支付,以及通过嵌入式二维码实现基于QR码的支付(如PayNow、GrabPay等)。
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
- Processing refunds
在以下场景中参考本技能:
- 集成HitPay支付网关
- 创建支付结账流程
- 实现二维码支付(PayNow、GrabPay、ShopeePay)
- 处理支付Webhook
- 处理退款
Step 1: Determine Payment Methods
步骤1:确定支付方式
Ask which payment methods the customer wants to support:
询问客户想要支持的支付方式:
Card-Only Methods
仅支持银行卡的方式
- (Visa, Mastercard, Amex)
card
- (Visa、Mastercard、Amex)
card
QR-Only Methods
仅支持二维码的方式
- (Singapore)
paynow_online grabpay_directshopee_paywechatalipay- (Malaysia)
fpx - (Thailand)
promptpay - (Thailand)
truemoney - (Vietnam)
vietqr - (Indonesia)
qris - (India)
upi - (Philippines)
gcash
- (新加坡)
paynow_online grabpay_directshopee_paywechatalipay- (马来西亚)
fpx - (泰国)
promptpay - (泰国)
truemoney - (越南)
vietqr - (印度尼西亚)
qris - (印度)
upi - (菲律宾)
gcash
Step 2: Choose Frontend Approach
步骤2:选择前端实现方案
| Payment Methods | Recommended Frontend |
|---|---|
| Card only | Redirect to HitPay checkout |
| QR only | Embedded QR on your site |
| Both card + QR | Payment method selector then appropriate flow |
| 支付方式 | 推荐的前端方案 |
|---|---|
| 仅银行卡 | 跳转至HitPay结账页面 |
| 仅二维码 | 在你的网站中嵌入二维码 |
| 银行卡+二维码 | 先显示支付方式选择器,再进入对应流程 |
API Basics
API基础信息
| Environment | Base URL |
|---|---|
| Sandbox | |
| Production | |
| 环境 | 基础URL |
|---|---|
| 沙箱环境 | |
| 生产环境 | |
Authentication
身份验证
typescript
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.
typescript
const headers = {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY,
'Content-Type': 'application/json',
};从HitPay控制台的「设置」→「支付网关」→「API密钥」获取API密钥。
Frontend Option A: Redirect (Cards)
前端方案A:跳转方式(银行卡)
Best for card payments or when you want HitPay to handle the full checkout UI.
最适合银行卡支付场景,或希望由HitPay处理完整结账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
- 后端创建支付请求
- 前端跳转至
response.url - 客户在HitPay托管页面完成支付
- 客户返回至你的
redirect_url - 后端通过Webhook确认支付状态
Next.js API Route
Next.js API路由
typescript
// 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 });
}typescript
// 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
前端组件
typescript
// 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>;
}typescript
// 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
前端方案B:嵌入式二维码
Best for QR-based payments where customer stays on your site.
最适合客户无需离开你的网站即可完成的基于QR码的支付场景。
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
- 后端创建支付请求并设置
generate_qr: true - 前端通过渲染二维码
response.qr_code_data - 客户使用支付应用扫描二维码
- 前端轮询状态或监听Webhook
- 后端通过Webhook确认支付状态
Next.js API Route
Next.js API路由
typescript
// 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,
});
}typescript
// 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
前端组件
typescript
// 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>
);
}typescript
// 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
前端方案C:支付方式选择器
Best for supporting both cards and QR methods.
最适合同时支持银行卡和QR码支付的场景。
Frontend Component
前端组件
typescript
// 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>
);
}typescript
// 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
Webhook处理
Always verify webhook signatures before processing. See for details.
references/webhook-events.mdtypescript
// 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');
// Verify 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);
// Process payment confirmation
if (payload.status === 'completed') {
// Mark order as paid
await markOrderAsPaid(payload.reference_number);
}
return NextResponse.json({ received: true });
}处理前务必验证Webhook签名。详情请参考。
references/webhook-events.mdtypescript
// 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');
// Verify 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);
// Process payment confirmation
if (payload.status === 'completed') {
// Mark order as paid
await markOrderAsPaid(payload.reference_number);
}
return NextResponse.json({ received: true });
}Checking Payment Status
检查支付状态
See for the full API reference.
references/payment-request-api.mdtypescript
// 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);
}完整API参考请见。
references/payment-request-api.mdtypescript
// 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);
}Refunds
退款
See for full details.
references/refunds.mdtypescript
// app/api/payments/[id]/refund/route.ts
export async function POST(
request: Request,
{ params }: { params: { id: string } }
) {
const { amount } = await request.json();
const response = await fetch(
`https://api.sandbox.hit-pay.com/v1/payment-requests/${params.id}/refund`,
{
method: 'POST',
headers: {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount }),
}
);
const data = await response.json();
return NextResponse.json(data);
}详情请见。
references/refunds.mdtypescript
// app/api/payments/[id]/refund/route.ts
export async function POST(
request: Request,
{ params }: { params: { id: string } }
) {
const { amount } = await request.json();
const response = await fetch(
`https://api.sandbox.hit-pay.com/v1/payment-requests/${params.id}/refund`,
{
method: 'POST',
headers: {
'X-BUSINESS-API-KEY': process.env.HITPAY_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount }),
}
);
const data = await response.json();
return NextResponse.json(data);
}Environment Variables
环境变量
bash
undefinedbash
undefined.env.local
.env.local
HITPAY_API_KEY=your_api_key
HITPAY_SALT=your_webhook_salt
NEXT_PUBLIC_APP_URL=http://localhost:3000
undefinedHITPAY_API_KEY=your_api_key
HITPAY_SALT=your_webhook_salt
NEXT_PUBLIC_APP_URL=http://localhost:3000
undefinedTest 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 |
| 结果 | 卡号 | 有效期 | CVC |
|---|---|---|---|
| 支付成功 | 4242 4242 4242 4242 | 任意未来日期 | 任意3位数字 |
| 支付失败 | 4000 0000 0000 0002 | 任意未来日期 | 任意3位数字 |
Resources
相关资源
- Sandbox Dashboard: https://dashboard.sandbox.hit-pay.com
- Production Dashboard: https://dashboard.hit-pay.com
- API Docs: https://docs.hitpayapp.com
- 沙箱环境控制台:https://dashboard.sandbox.hit-pay.com
- 生产环境控制台:https://dashboard.hit-pay.com
- API文档:https://docs.hitpayapp.com