> ## Documentation Index
> Fetch the complete documentation index at: https://doc.haipay.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscription (Legacy navigation V20260623)

> Historical subscription guide comparing PayUrl Redirect with JS Embed, including Custom Combined Mode (mode: payment or subscription), launch requirements, dispute fees, and test card numbers.

HaiPay subscription payments are designed for memberships, recurring services, and auto-renew billing. This page now acts as a cleaner landing page: it helps you choose an integration mode, understand launch constraints, and jump into the right API Reference pages instead of scanning a long wall of duplicated parameter tables.

> **[Quick Start](#quick-start)**
>
> Review the integration path, required product capabilities, and reading order.

> **[Integration Modes](#integration-modes)**
>
> Compare Apple Pay / Google Pay, card component, and combined mode.

> **[Core APIs](#core-apis)**
>
> Jump directly to create, query, cancel, and refund endpoints.

> [!WARNING]
>
> * Provide a clear subscription entry, billing-cycle explanation, and cancellation entry.
> * Notify users outside the platform after subscription activation and before or after each recurring charge.
> * Show a subscription service agreement and obtain explicit user consent before starting recurring billing.
> * Keep customer support available, at minimum during the operating hours of your main markets.

> [!TIP]
>
> * Consumers may dispute card charges within 180 days after the transaction date.
> * Each dispute incurs a 20 USD dispute handling fee.
> * If you choose to defend the dispute, an additional 20 USD rebuttal fee applies.
> * If the rebuttal succeeds, HaiPay refunds the rebuttal fee; the original dispute fee is not refunded by default.

> [!CAUTION]
>
> PCI DSS strictly prohibits embedding card input pages inside `WebView` or `iframe`. Always collect card details and complete authorization in a trusted browser environment to avoid compliance and data-security issues.

## Quick Start

**1. Confirm the business model and prerequisites**

Start with the [API Description Guide](https://doc.haipay.net/en/history/V20260623/get-started/api-conventions.md) and the [Subscription Process](https://doc.haipay.net/en/history/V20260623/api/version2/subscription-process.md) to confirm that your scenario is recurring billing rather than a one-time payment flow.

**2. Choose the right integration mode**

Subscription payments offer two integration modes: **PayUrl Redirect** (server obtains an authorization URL and redirects the user to HaiPay's hosted payment page) and **JS CDN Embed** (uses a client token to embed payment components directly in your own page). Choose based on your checkout design and frontend capabilities.

**3. Connect APIs, callbacks, and cancellation**

After creating a subscription, you still need subscription query, recurring callback handling, and a user-facing cancellation flow. The detailed fields and debug entry points are linked in the API cards below.

## Integration Modes

Subscription payments offer two integration modes. The key difference is who hosts the payment page:

| Comparison | PayUrl Redirect | JS Embed |
| :- | :- | :- |
| Payment page | Redirects to HaiPay's hosted payment page | Embeds payment components in your own page |
| Frontend effort | Almost zero, just handle the redirect | Requires importing JS scripts and listening to events |
| UI control | Low, page style controlled by HaiPay | High, customizable theme, layout, and interactions |
| Key response field | Uses `payUrl` from the response | Uses `clientToken` from the response |
| Best for | Quick integration, no custom payment page needed | Custom checkout, seamless in-page payment experience |

**PayUrl Redirect**

The simplest integration path. After your server calls the subscription create API, use the returned `payUrl` to redirect the user to HaiPay's hosted payment page where they complete authorization and the first payment.

**Integration Flow**

**1. Server calls the subscription create API**

Your server sends a [Subscription Create](https://doc.haipay.net/en/history/V20260623/api-reference/subscription/api/subscriptionApply.md) request to HaiPay with the required parameters such as amount, cycle, and callback URLs.

**2. Get payUrl and redirect**

The API response includes a `payUrl` field. Redirect the user's browser to that URL.

**3. User completes payment**

The user fills in card details and authorizes the subscription on HaiPay's hosted page. After payment, HaiPay redirects the user back to your `callBackUrl` and sends an asynchronous notification to your `notifyUrl`.

**Example code (frontend redirect after server obtains payUrl)**

```javascript theme={null}
// Assuming your server has called the subscription create API
// and returned the payUrl to the frontend
const payUrl = response.data.payUrl;

// Redirect in the current window
window.location.href = payUrl;

// Or open in a new window
// window.open(payUrl, '_blank');
```

**When to choose it**

* You want the fastest possible subscription integration with minimal frontend changes.
* You have no specific customization requirements for the payment page UI.
* For mobile H5 scenarios, the redirect experience is usually sufficient.

**JS Embed**

Embed payment components directly in your own page using HaiPay JS scripts. After your server calls the subscription create API, use the returned `clientToken` to initialize the frontend component. Users complete payment without leaving your page.

The JS offers the following component types. Choose based on your checkout design:

**Custom Combined Mode**

Similar to Combined Mode, but additionally supports switching between one-time payment and subscription payment via the `mode` parameter. Ideal for merchants who need to manage multiple payment scenarios with one integration.

**Script**

`https://cashier.haipay.top/js/composite-component_1.0.0.min.js`

**Parameters**

| Parameter | Required | Description |
| :- | :- | :- |
| `sandbox` | Yes | `true` for test, `false` for production |
| `clientToken` | Yes | Client token returned by the subscription create API |
| `mode` | Yes | Payment scenario: `payment` (one-time) or `subscription` (recurring) |
| `showPayButton` | No | Whether to display the credit card submit button. Default: `false` (hidden) |
| `themeMode` | No | Theme mode, `light` or `dark`, default `light` |
| `paymentMethodConfig` | No | Payment-method list, default `["GOOGLE_PAY","APPLE_PAY","CREDIT_CARD"]` |
| `applePayButtonType` | No | Apple Pay button text, e.g., `buy`, `subscribe` |
| `googlePayButtonType` | No | Google Pay button text, e.g., `buy`, `subscribe` |
| `cardPosition` | No | Credit card placement position, default `bottom`, options: `bottom`, `top` |

**Events**

* `ready` — Component rendered, ready for interaction
* `error` — Initialization failed or payment error occurred
* `confirm` — Form submission result (success/failure)
* `paymentStatus` — Payment result notification, failure will return error message (if not listened, the component will handle result display automatically)
* `paymentMethodType` — Currently selected payment method

**When to choose it**

* When you need to support both one-time payment and subscription payment in the same codebase.
* If you only enable certain payment methods, just pass the required combination.

```javascript theme={null}
<script src="https://cashier.haipay.top/js/composite-component_1.0.0.min.js"></script>

<div>
  <div id="paymentId"></div>
</div>

const elements = haiPay.create({
  mode: 'payment',
  sandbox: true,
  clientToken: 'your-client-token',
  themeMode: 'light',
  paymentMethodConfig: ['GOOGLE_PAY', 'APPLE_PAY', 'CREDIT_CARD']
});

elements.mount('#paymentId');

elements.on('ready', (event) => {
  console.log('rendered successfully:', event);
});

elements.on('error', (event) => {
  console.error('Payment error:', event);
});

elements.on('paymentMethodType', (event) => {
  console.log('payment method type', event);
});

elements.submitCardPayment();

elements.on('confirm', (event) => {
  console.log('confirm:', event);
});

elements.on('paymentStatus', (event, message) => {
  console.log('payment status:', event, message);
});

```

**Apple Pay / Google Pay (Deprecated Soon)**

> [!WARNING]
>
> This component will be deprecated soon. Please use "Custom Combined Mode" instead.

Best for teams that already support wallet-based checkout and want a fast subscription authorization experience.

**Recommended script**

`https://cashier.haipay.top/js/applePayGooglePaySubscribe_1.0.0.min.js`

**Key parameters**

| Parameter | Required | Description |
| :- | :- | :- |
| `sandbox` | Yes | `true` for test, `false` for production |
| `clientToken` | Yes | Client token |
| `buttonType` | No | Button text such as `buy` or `subscribe` |
| `buttonTheme` | No | Button theme such as `black` or `white` |
| `buttonHeight` | No | Button height, default `50` |

**Typical events**

* `ready`: button rendered
* `error`: initialization or payment error
* `paymentStatus`: payment result callback (If you do not listen to this event, the component will process the payment result by default)
* `paymentMethodType` — Currently selected payment method

```javascript theme={null}
<script src="https://cashier.haipay.top/js/applePayGooglePaySubscribe_1.0.0.min.js"></script>

<div>
  <div id="applePayGooglePayId"></div>
</div>

const elements = applePayGooglePaySubscribe.create('applePayGooglePaySubscribe', {
  sandbox: true,
  clientToken: 'your-client-token',
  buttonType: 'subscribe',
  buttonTheme: 'black',
  buttonHeight: 50
});

elements.mount('#applePayGooglePayId');

elements.on('ready', (data) => {
  console.log('Button rendered successfully:', data);
});

elements.on('error', (data) => {
  console.error('Payment error:', data);
});

elements.on('paymentMethodType', (event) => {
  console.log('payment method type', event);
});

elements.on('paymentStatus', (data) => {
  console.log('payment status:', data);
});
```

**Credit Card (Deprecated Soon)**

> [!WARNING]
>
> This component will be deprecated soon. Please use "Custom Combined Mode" instead.

Best when you want to render the subscription card form directly and control the submit action yourself.

**Recommended script**

`https://cashier.haipay.top/js/creditCardSubscription_1.0.0.min.js`

**Key parameters**

| Parameter | Required | Description |
| :- | :- | :- |
| `sandbox` | Yes | `true` for test, `false` for production |
| `clientToken` | Yes | Client token |
| `cardTheme` | No | Form theme, default `white` |

**Typical events**

* `ready`: card component rendered
* `confirm`: form submission result
* `paymentStatus`: payment result callback (If you do not listen to this event, the component will process the payment result by default)

```javascript theme={null}
<script src="https://cashier.haipay.top/js/creditCardSubscription_1.0.0.min.js"></script>

<div>
  <div id="creditCardId"></div>
</div>

const elements = creditCardSubscription.create('card', {
  sandbox: true,
  clientToken: 'your-client-token',
  cardTheme: 'white'
});

elements.mount('#creditCardId');

elements.on('ready', (data) => {
  console.log('Card rendered successfully:', data);
});

elements.on('error', (data) => {
  console.error('Payment error:', data);
});

elements.submit();

elements.on('confirm', (data) => {
  console.log('confirm:', data);
});

elements.on('paymentStatus', (data) => {
  console.log('payment status:', data);
});
```

## Core APIs

For full request fields, response schemas, and live debugging, go directly to the API Reference pages:

> **[Subscription Create](https://doc.haipay.net/en/history/V20260623/api-reference/subscription/api/subscriptionApply.md)**
>
> Create a subscription order, launch user authorization, and complete the first subscription payment.

> **[Subscription Query](https://doc.haipay.net/en/history/V20260623/api-reference/subscription/subscription-query.md)**
>
> Check subscription status, platform subscription number, and deduction records.

> **[Subscription Cancel](https://doc.haipay.net/en/history/V20260623/api-reference/subscription/subscription-cancel.md)**
>
> Cancel an active subscription and stop future recurring charges.

## Asynchronous Notifications

When the subscription status changes or a recurring deduction completes, HaiPay pushes an asynchronous notification to the `notifyUrl` provided in the subscription create request.

> [!TIP]
>
> To ensure extensibility and compatibility, follow these callback parameter reception guidelines:
>
> 1. **Do not** declare a specific POJO object to receive callback parameters.
> 2. **Must** use a generic data structure (like JSONObject, Map, etc.) to parse parameters.
> 3. This design ensures that if callback fields are extended later, it will not affect your signature verification logic.

After receiving a notification, return **SUCCESS** (uppercase) in the response body.

**Subscription Status Notification**

Triggered when the subscription status changes (e.g., subscription activated, canceled, failed, etc.).

| Parameter | Type | Description |
| :- | :- | :- |
| `type` | String | Notification type, fixed as `SUBSCRIPTION` |
| `appId` | Long | Business ID |
| `currency` | String | Currency |
| `subscriptionOrderId` | String | Merchant subscription order ID |
| `subscriptionNo` | String | Platform subscription number |
| `amount` | String | Subscription amount |
| `status` | Integer | Subscription status (1: Pending, 2: Active, 3: Failed, 4: Canceled, 5: Completed) |
| `recurringInterval` | String | Recurring cycle type (D/W/M/Y) |
| `recurringIntervalCount` | Integer | Recurring cycle interval |
| `subject` | String | Subscription title |
| `sign` | String | Signature (verify using the business secret key corresponding to the currency) |

**Deduction Notification**

Triggered when each recurring deduction succeeds or fails.

| Parameter | Type | Description |
| :- | :- | :- |
| `type` | String | Notification type, fixed as `SUBSCRIPTIONS_DEDUCT` |
| `appId` | Long | Business ID |
| `currency` | String | Currency |
| `subscriptionOrderId` | String | Merchant subscription order ID |
| `subscriptionNo` | String | Platform subscription number |
| `deductNo` | String | Deduction number |
| `orderNo` | String | Platform order number |
| `amount` | String | Deduction amount for this cycle |
| `status` | Integer | Deduction status (2: Success, 3: Failed) |
| `startTime` | String | Deduction cycle start time, format: yyyy-MM-dd HH:mm:ss |
| `endTime` | String | Deduction cycle end time, format: yyyy-MM-dd HH:mm:ss |
| `sign` | String | Signature (verify using the business secret key corresponding to the currency) |

## Key Business Rules

| Item | Details |
| :- | :- |
| Amount limit | Subscription amount range is `0.99 - 1000 USD` |
| Max duration | Total subscription duration cannot exceed 3 years regardless of cycle unit |
| Cycle unit | Apple Pay does not support `W`; use `D` + `7` for weekly billing |
| Payment type | Subscription flows use `SUBSCRIPTION` |
| User notifications | Notify users after activation and before or after recurring deductions |
| Cancellation | Production environments must provide a clear and accessible cancellation entry |
| Payment method parameters | In the subscription create API, `inBankCode` and `paymentMethods` are mutually exclusive: use `inBankCode` for a single payment method code, or `paymentMethods` for a comma-separated list of payment methods. `paymentMethods` currently only supports the front-end component combination mode |

## Test Card Numbers

> [!WARNING]
>
> For use in test environment only

**Simulate Successful Payment**

Use the following test card number. Enter any 3-digit CVC and any future expiration date:

* `4242424242424242`

**Simulate Payment Failure**

Use the following test card number with invalid data:

* Card Number: `4000000000009995`
* Invalid Month: `13`
* Invalid CVV: `99`

## Common Questions

> **When should I use this page versus the API Reference?**
>
> Use this page to understand the product model, select an integration mode, and review the launch rules. When you are ready to build or debug, move to the [Subscription API Reference](https://doc.haipay.net/en/history/V20260623/api-reference/subscription/api/subscriptionApply.md) for full field definitions, example payloads, and testing tools.

> **Why are cancellation and off-platform notifications mandatory for subscriptions?**
>
> Subscription payments create a higher dispute risk than one-time payments. Clear authorization, notification, and cancellation controls reduce chargebacks, complaints, and risk-review issues, and they are usually required for a stable production rollout.

> **How should I interpret Unsupported region: CN in production?**
>
> It usually means the current production configuration does not support that region, or the payment-method setup does not match the target market. Confirm your merchant configuration, target market, and payment-method mix first, then contact HaiPay support if needed.

> **How should I choose between PayUrl Redirect and JS Embed?**
>
> If you want the fastest launch with minimal frontend changes, use PayUrl Redirect. If you need a custom checkout UI where users stay on your page, use JS CDN Embed. Both modes use the same subscription create API; the only difference is whether you use the returned `payUrl` or `clientToken`.

> **Should I use inBankCode or paymentMethods?**
>
> These two parameters are mutually exclusive — you cannot pass both, and you must pass at least one. Use `inBankCode` when only a single payment method is needed. Use `paymentMethods` when multiple payment methods are required (e.g., Combined Mode). Note that `paymentMethods` currently only supports the front-end component combination mode; for other modes, continue using `inBankCode`.

> **Which mode should I choose for a modern subscription page with the least frontend work?**
>
> If you go with JS Embed, start with Combined Mode. It gives you a more complete checkout container, lowers the amount of frontend composition work, and is usually easier for users to understand.

## Related Topics

* [Common API](https://doc.haipay.net/en/history/V20260623/api/version2/CommonApi.md)
* [HaiPay API Description and Common Rules](https://doc.haipay.net/en/history/V20260623/get-started/api-conventions.md)
* [Subscription Payment Flowchart](https://doc.haipay.net/en/history/V20260623/api/version2/subscription-process.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.