hitpay

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

HitPay 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

仅支持银行卡的方式

  • card
    (Visa, Mastercard, Amex)
  • card
    (Visa、Mastercard、Amex)

QR-Only Methods

仅支持二维码的方式

  • paynow_online
    (Singapore)
  • grabpay_direct
  • shopee_pay
  • wechat
  • alipay
  • fpx
    (Malaysia)
  • promptpay
    (Thailand)
  • truemoney
    (Thailand)
  • vietqr
    (Vietnam)
  • qris
    (Indonesia)
  • upi
    (India)
  • gcash
    (Philippines)
  • paynow_online
    (新加坡)
  • grabpay_direct
  • shopee_pay
  • wechat
  • alipay
  • fpx
    (马来西亚)
  • promptpay
    (泰国)
  • truemoney
    (泰国)
  • vietqr
    (越南)
  • qris
    (印度尼西亚)
  • upi
    (印度)
  • gcash
    (菲律宾)

Step 2: Choose Frontend Approach

步骤2:选择前端实现方案

Payment MethodsRecommended Frontend
Card onlyRedirect to HitPay checkout
QR onlyEmbedded QR on your site
Both card + QRPayment method selector then appropriate flow
支付方式推荐的前端方案
仅银行卡跳转至HitPay结账页面
仅二维码在你的网站中嵌入二维码
银行卡+二维码先显示支付方式选择器,再进入对应流程

API Basics

API基础信息

EnvironmentBase URL
Sandbox
https://api.sandbox.hit-pay.com
Production
https://api.hit-pay.com
环境基础URL
沙箱环境
https://api.sandbox.hit-pay.com
生产环境
https://api.hit-pay.com

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

流程

  1. Backend creates payment request
  2. Frontend redirects to
    response.url
  3. Customer pays on HitPay hosted page
  4. Customer returns to your
    redirect_url
  5. Backend confirms via webhook
  1. 后端创建支付请求
  2. 前端跳转至
    response.url
  3. 客户在HitPay托管页面完成支付
  4. 客户返回至你的
    redirect_url
  5. 后端通过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

流程

  1. Backend creates payment request with
    generate_qr: true
  2. Frontend renders QR code from
    response.qr_code_data
  3. Customer scans with payment app
  4. Frontend polls for status or listens for webhook
  5. Backend confirms via webhook
  1. 后端创建支付请求并设置
    generate_qr: true
  2. 前端通过
    response.qr_code_data
    渲染二维码
  3. 客户使用支付应用扫描二维码
  4. 前端轮询状态或监听Webhook
  5. 后端通过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
references/webhook-events.md
for details.
typescript
// 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.md
typescript
// 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
references/payment-request-api.md
for the full API reference.
typescript
// 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.md
typescript
// 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
references/refunds.md
for full details.
typescript
// 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.md
typescript
// 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
undefined
bash
undefined

.env.local

.env.local

HITPAY_API_KEY=your_api_key HITPAY_SALT=your_webhook_salt NEXT_PUBLIC_APP_URL=http://localhost:3000
undefined
HITPAY_API_KEY=your_api_key HITPAY_SALT=your_webhook_salt NEXT_PUBLIC_APP_URL=http://localhost:3000
undefined

Test Cards (Sandbox)

测试银行卡(沙箱环境)

ResultNumberExpiryCVC
Success4242 4242 4242 4242Any futureAny 3 digits
Declined4000 0000 0000 0002Any futureAny 3 digits
结果卡号有效期CVC
支付成功4242 4242 4242 4242任意未来日期任意3位数字
支付失败4000 0000 0000 0002任意未来日期任意3位数字

Resources

相关资源