Skip to main content

Overview

Keep customers on your site when they set up a recurring payment method. Instead of redirecting them to a HitPay-hosted page, pass a method-specific parameter (generate_direct_link, generate_qr, or generate_instructions) to receive a link, QR code, or setup instructions directly in the API response — ready to render in your own UI.
Embedded Recurring APMs

Supported Payment Methods

Payment MethodCodeParameterResponse typeCurrencies
ZaloPayzalopaygenerate_qrqr_code_dataVND
Shopee Payshopee_paygenerate_direct_linkdirect_link (web URL)SGD, MYR, PHP
GrabPaygrabpay_directgenerate_direct_linkdirect_link (web URL)SGD, MYR
Touch ‘N Gotouch_n_gogenerate_direct_linkdirect_link (web URL)MYR
GIROgirogenerate_instructionsinstructionsSGD
Send exactly one generate parameter per request (generate_direct_link, generate_qr, or generate_instructions). The parameter must match the payment method — sending generate_qr for shopee_pay, for example, returns a 400 error. Omitting all three generate parameters defaults to the hosted-page flow.

How It Works

1

Create a Recurring Billing with the method-specific parameter

Call POST /v1/recurring-billing with exactly one generate parameter (generate_direct_link, generate_qr, or generate_instructions) and exactly one payment_methods[] entry. The API initiates the APM setup immediately and returns qr_code_data, direct_link, or instructions inline.
2

Present the Link or Instructions

Render the returned data in your UI: show the link as a button or display the setup steps for GIRO.
3

Customer Completes Setup

The customer taps the link or follows the GIRO instructions.
4

Receive Webhooks

HitPay fires recurring_billing.method_attached and recurring_billing.subscription_updated when the payment method is linked and the subscription activates. If a charge is made, charge.created is fired — listen to this event to confirm a successful payment.

Step 1: Create a Recurring Billing

Endpoint

POST /v1/recurring-billing

Request Parameters

Send exactly one generate parameter per request. For redirect-based methods (shopee_pay, grabpay_direct, touch_n_go), redirect_url is also required when using generate_direct_link. Sending more than one generate parameter returns a 400.
ParameterDescriptionExample
nameRequired. Name for this billing session.Spotify Premium
customer_emailRequired. Customer’s email address.paul@hitpayapp.com
customer_nameRequired. Customer’s name.Paul
amountRequired. Amount to display (minimum 1.00).9.90
save_payment_methodSet to true to save the payment method.true
currencyCurrency code.sgd
payment_methods[]Required. Exactly one method per request.grabpay_direct
generate_direct_linkSet to true to receive a direct_link inline. Use with shopee_pay, grabpay_direct, or touch_n_go.true
generate_qrSet to true to receive qr_code_data inline. Use with zalopay.true
generate_instructionsSet to true to receive instructions inline. Use with giro.true
redirect_urlRequired when using generate_direct_link. Not required for generate_qr or generate_instructions.https://example.com/success
descriptionDescription shown to the customer.Spotify Membership
referenceYour internal reference ID for this customer or session.cust_123
send_emailSend email receipt to customer. Default: false.true
customer_phone_numberRequired for shopee_pay. Customer’s phone number.91234567
customer_phone_number_country_codeRequired for shopee_pay. Country code of the phone number.65

Example Requests

curl --location --request POST 'https://api.sandbox.hit-pay.com/v1/recurring-billing' \
--header 'X-BUSINESS-API-KEY: your_api_key' \
--header 'X-Requested-With: XMLHttpRequest' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'name=Spotify Premium' \
--data-urlencode 'customer_email=tan@example.com' \
--data-urlencode 'customer_name=Tan Ah Kow' \
--data-urlencode 'amount=9.90' \
--data-urlencode 'currency=sgd' \
--data-urlencode 'save_payment_method=true' \
--data-urlencode 'payment_methods[]=shopee_pay' \
--data-urlencode 'generate_direct_link=true' \
--data-urlencode 'customer_phone_number=91234567' \
--data-urlencode 'customer_phone_number_country_code=65' \
--data-urlencode 'redirect_url=https://merchant.com/billing/complete'

Step 2: Present the Response in Your UI

The response includes all the standard recurring billing fields plus one of the following objects depending on the payment method.

QR code (qr_code_data) — ZaloPay

Render the qr_code value as a scannable QR image in your UI.
{
  "id": "9741164c-06a1-4dd7-a649-72cca8f9603c",
  "customer_name": "Nguyen Van A",
  "customer_email": "nguyen@example.com",
  "name": "Spotify Premium",
  "cycle": "save_card",
  "cycle_repeat": null,
  "cycle_frequency": null,
  "currency": "vnd",
  "amount": 99000,
  "status": "scheduled",
  "save_payment_method": 1,
  "payment_methods": ["zalopay"],
  "qr_code_data": {
    "qr_code": "00020101021238570010A000000727...",
    "qr_code_expiry": "2026-04-10T15:30:00"
  },
  "created_at": "2026-04-10T15:00:00",
  "updated_at": null
}
FieldTypeDescription
qr_codestringQR code string to render as a scannable image.
qr_code_expirystringISO 8601 timestamp when the QR code expires.
Render direct_link_url as a button (e.g. “Authorise with Shopee Pay”). On mobile, use direct_link_app_url to open the payment app directly if available.
{
  "id": "9741164c-06a1-4dd7-a649-72cca8f9603a",
  "customer_name": "Tan Ah Kow",
  "customer_email": "tan@example.com",
  "name": "Spotify Premium",
  "cycle": "save_card",
  "cycle_repeat": null,
  "cycle_frequency": null,
  "currency": "sgd",
  "amount": 9.9,
  "status": "scheduled",
  "save_payment_method": 1,
  "payment_methods": ["shopee_pay"],
  "redirect_url": "https://merchant.com/billing/complete",
  "direct_link": {
    "direct_link_url": "https://shopeepay.sg/u/account-linking-agreement_sg?client_id=abc123...",
    "direct_link_app_url": "shopeesg://main?apprl=%2Frn%2FTRANSFER_PAGE%3F...",
    "expiry_time": null
  },
  "created_at": "2026-04-01T15:00:00",
  "updated_at": "2026-04-01T15:00:00"
}
FieldTypeDescription
direct_link_urlstringWeb redirect URL. Always present. Use this on desktop or as a fallback on mobile.
direct_link_app_urlstring | nullApp deep link for mobile app-to-app flows. Present for Shopee Pay; null for GrabPay and Touch ‘N Go.

Instructions (instructions) — GIRO

Display the steps as a numbered list. Make the reference value copyable so the customer can paste it into their banking portal.
{
  "id": "9741164c-06a1-4dd7-a649-72cca8f9603b",
  "customer_name": "Lim Wei",
  "customer_email": "lim@example.com",
  "name": "Spotify Premium",
  "cycle": "save_card",
  "cycle_repeat": null,
  "cycle_frequency": null,
  "currency": "sgd",
  "amount": 9.9,
  "status": "scheduled",
  "save_payment_method": 1,
  "payment_methods": ["giro"],
  "instructions": {
    "reference": "RPSHISXMB1E",
    "steps": [
      "Log in to your DBS/POSB Internet Banking Account on the Web",
      "Click on Pay > Add GIRO Arrangement",
      "Under Billing Organisation, select HitPay",
      "Add RPSHISXMB1E as Bill Reference",
      "Enter 0 under Payment Limit",
      "Click on Next and hit Submit"
    ]
  },
  "created_at": "2026-04-01T15:00:00",
  "updated_at": "2026-04-01T15:00:00"
}
FieldTypeDescription
referencestringThe bill reference the customer enters in their banking portal.
stepsstring[]Ordered setup instructions. Display as a numbered list.

Step 3: Customer Completes Setup

Shopee Pay / GrabPay / TNG

Customer taps the link or is redirected to the provider’s authorisation page, then returns to your redirect_url.

GIRO

Customer logs in to DBS/POSB internet banking and follows the displayed steps. This may take 1–3 business days to activate.

Step 4: Handle Webhooks

Register Your Webhook

  1. Navigate to Developers > Webhook Endpoints in your dashboard
  2. Click New Webhook
  3. Enter a name and your webhook URL
  4. Select the events you want to receive:
    • recurring_billing.method_attached — Payment method successfully linked
    • recurring_billing.subscription_updated — Subscription status changes (e.g., scheduledactive)
    • charge.created — A charge was successfully processed.
  5. Save your webhook configuration

Webhook Payload

When a payment is completed, HitPay sends a JSON payload to your registered webhook URL with the following headers:
HTTP HeaderDescription
Hitpay-SignatureHMAC-SHA256 signature of the JSON payload, using your salt value
Hitpay-Event-Typecompleted
Hitpay-Event-Objectpayment_request
User-AgentHitPay v2.0

Validating the Webhook

To ensure the webhook is authentic, validate the Hitpay-Signature header:
  1. Receive the JSON payload and Hitpay-Signature from the request
  2. Use your salt value (from the dashboard) as the secret key
  3. Compute HMAC-SHA256 of the JSON payload using your salt
  4. Compare the computed signature with Hitpay-Signature - they must match
function validateWebhook($payload, $signature, $salt) {
    $computedSignature = hash_hmac('sha256', $payload, $salt);
    return hash_equals($computedSignature, $signature);
}

// Usage
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_HITPAY_SIGNATURE'];
$salt = 'your_salt_from_dashboard';

if (validateWebhook($payload, $signature, $salt)) {
    $data = json_decode($payload, true);
    // Process the payment confirmation
    // Mark order as paid
} else {
    http_response_code(401);
    exit('Invalid signature');
}

FAQs

No. If you omit all three generate parameters, the API behaves exactly as before — returning the HitPay hosted page url. No migration required.
No. The method-specific generate parameters only work for supported APMs.
The API returns a 400 error. Send exactly one generate parameter per request.
The API returns a 400 error with a message indicating the correct parameter to use (e.g., "shopee_pay does not support QR-based setup. Use generate_direct_link instead.").
Existing webhook retry logic applies. You can also check the recurring billing status via GET /v1/recurring-billing/{id} — if the method is linked, status will be active.
  • Change the base URL to https://api.hit-pay.com/v1/
  • Update API keys and salt values from the production dashboard
  • Ensure the APM provider is onboarded in your production account
  • Register your webhook URL in production and subscribe to recurring_billing.method_attached and recurring_billing.subscription_updated