# Create subscription payment Recurring **bank card** payments allow you to charge a **token** of a saved card without the payer re-entering card details. With an active binding, charges are processed through GATE (`init_payment`). Contact your 1Payment account manager to enable this feature for your project. Request format and signatures — see [API request format](../init-request.md). ## How it works The card subscription process consists of four steps: 1. **First payment (card binding)** — complete a **successful** payment with `subscription=1` via the [payment form](./init-form.md) or [GATE (host2host)](./host2host.md). The payer enters card details and completes 3-D Secure if required. 2. **Receive token** — after successful payment, 1Payment sends a unique `token` in the callback to `notify_url` or in the [status request](./status.md) response. Save it for subsequent charges. 3. **Automatic charge (recurring)** — for repeat payments, your server calls GATE (`init_payment`), passing the saved `token` **instead of** `account`, `card_holder`, `year`, `month`, and `cvc`. 4. **Notification** — after each charge attempt, a callback arrives with the final transaction result. ## Technical information | Stage | Endpoint | Methods | | --- | --- | --- | | Subscription registration (form) | `https://api.1payment.com/init_form` | `GET`, `POST` | | Subscription registration (GATE) | `https://api.1payment.com/init_payment` | `GET`, `POST` | | Recurring charge (GATE) | `https://api.1payment.com/init_payment` | `POST` (recommended), `GET` | Response format: **JSON**. For cards, specify `payment_type=card` in GATE requests. ## 1. Obtain token (first payment) To bind a card, complete a **successful** payment with `subscription=1`. For form and GATE parameters, see [Create payment via form](./init-form.md) and [Create host2host (GATE) payment](./host2host.md). Below is the required set for binding. ### Required parameters (form initialization) | Parameter | Type | Description | | --- | --- | --- | | `partner_id` | Integer | Your unique ID in the 1Payment system. | | `project_id` | Integer | Your project identifier. | | `amount` | Number | Payment amount for binding (for example, `50.00`). | | `subscription` | Integer | Subscription creation flag. Always `1`. Without it, no token is returned. | | `user_data` | String | Your unique order ID for matching. | | `shop_url` | String | URL of the website where the purchase is made. | | `sign` | String | Request signature (see below). | ### Required parameters (GATE initialization) | Parameter | Type | Description | | --- | --- | --- | | `partner_id` | Integer | Your unique ID in the 1Payment system. | | `payment_type` | String | Always `card`. | | `project_id` | Integer | Your project identifier. | | `account` | String | Bank card number. | | `card_holder` | String | Cardholder name (as printed on the card). | | `year` | String | Card expiry year, last two digits (for example, `22`). | | `month` | String | Card expiry month (for example, `01`). | | `cvc` | String | Card CVV/CVC code. | | `amount` | Number | Payment amount for binding (for example, `50.00`). | | `subscription` | Integer | Subscription creation flag. Always `1`. Without it, no token is returned. | | `user_data` | String | Your unique order ID for matching. | | `shop_url` | String | URL of the website where the payment originates. | | `sign` | String | Request signature (see below). | ### Optional parameters (initialization) | Parameter | Type | Description | | --- | --- | --- | | `description` | String | Payment description. | | `success_url` | String | Customer redirect URL on success (form). | | `failure_url` | String | Customer redirect URL on error (form). | | `return_url` | String | Payer redirect URL after payment (GATE). | | `ip` | String | Payer IP address (GATE). | | `user_id` | String | Payer ID in your system. | **Signature for form registration** — prefix `init_form`. **Via GATE** — prefix `init_payment`. ```text MD5(init_form + <параметры_без_sign_в_алфавитном_порядке_через_&> + ) ``` ```text MD5(init_payment + <параметры_без_sign_в_алфавитном_порядке_через_&> + ) ``` After the final `SUCCESS` status, save the `token` value from the callback or from the [Payment statuses](./status.md) response. ## 2. Recurring charge (repeat payments) For automatic charging, send a GATE request with the saved `token`. **Do not pass** `account`, `card_holder`, `year`, `month`, and `cvc` — use only `token` instead. ### Required parameters (recurring) | Parameter | Type | Description | | --- | --- | --- | | `partner_id` | Integer | Your unique ID in the 1Payment system. | | `payment_type` | String | Always `card`. | | `project_id` | Integer | Your project identifier. | | `amount` | Number | Amount of the recurring charge (for example, `500.00`). | | `token` | String | Token received from the first successful payment (from callback or status request). | | `user_data` | String | New unique transaction ID on your side. | | `shop_url` | String | URL of the website where the payment originates. | | `sign` | String | Request signature (prefix `init_payment`). | ### Optional parameters (recurring) | Parameter | Type | Description | | --- | --- | --- | | `description` | String | Payment description. | | `ip` | String | Payer IP address. | | `return_url` | String | Payer redirect URL after payment. | | `user_id` | String | Payer ID in your system. | ### Signature generation (`sign`) Signature: MD5, lowercase (hex). General rules — [API request format](../init-request.md#подпись-запроса-sign). **Formula:** ```text MD5(init_payment + <параметры_без_sign_в_алфавитном_порядке_через_&> + ) ``` **Example string before hashing:** ```text init_paymentamount=500.00&partner_id=1234&payment_type=card&project_id=5678&shop_url=https://test.com&token=card_t_1a2b3c&user_data=card_order_888secret_key ``` ### Signature verification (recurring charge) {% signatureVerifier method="init_payment" preset="card_recurring_payment" /%} ## Request examples {% codeTabs %} {% tab label="Node.js" %} ```javascript const crypto = require('crypto'); const axios = require('axios'); // 1. Инициация подписки через форму (первичная привязка) async function setupCardSubscriptionForm(apiKey, params) { const sortedKeys = Object.keys(params).sort(); const queryString = sortedKeys.map((k) => `${k}=${params[k]}`).join('&'); const baseString = `init_form${queryString}${apiKey}`; params.sign = crypto.createHash('md5').update(baseString).digest('hex'); const response = await axios.post('https://api.1payment.com/init_form', params); return response.data; // поле url — ссылка на форму } // 2. Инициация подписки через GATE async function setupCardSubscriptionGate(apiKey, params) { const sortedKeys = Object.keys(params).sort(); const queryString = sortedKeys.map((k) => `${k}=${params[k]}`).join('&'); const baseString = `init_payment${queryString}${apiKey}`; params.sign = crypto.createHash('md5').update(baseString).digest('hex'); const response = await axios.post('https://api.1payment.com/init_payment', params); return response.data; // redirect_url при необходимости 3-D Secure } // 3. Рекуррентное списание (по токену) async function chargeCardByToken(apiKey, params) { const sortedKeys = Object.keys(params).sort(); const queryString = sortedKeys.map((k) => `${k}=${params[k]}`).join('&'); const baseString = `init_payment${queryString}${apiKey}`; params.sign = crypto.createHash('md5').update(baseString).digest('hex'); const response = await axios.post('https://api.1payment.com/init_payment', params); return response.data; } const apiKey = process.env.ONEPAYMENT_API_KEY; // Пример: привязка через форму const setupFormData = { partner_id: 1234, project_id: 5678, amount: '50.00', subscription: 1, user_data: 'card_setup_777', shop_url: 'https://test.com', }; // setupCardSubscriptionForm(apiKey, setupFormData).then(console.log); // Пример: привязка через GATE const setupGateData = { partner_id: 1234, payment_type: 'card', project_id: 5678, account: '4111111111111111', card_holder: 'TEST', year: '22', month: '01', cvc: '111', amount: '50.00', subscription: 1, user_data: 'card_setup_777', shop_url: 'https://test.com', }; // setupCardSubscriptionGate(apiKey, setupGateData).then(console.log); // Пример: списание const recurringData = { partner_id: 1234, payment_type: 'card', project_id: 5678, token: 'card_t_1a2b3c', amount: '500.00', user_data: 'card_order_888', shop_url: 'https://test.com', }; chargeCardByToken(apiKey, recurringData).then(console.log); ``` {% /tab %} {% tab label="Python" %} ```python import hashlib import os import requests def setup_card_subscription_form(api_key: str, params: dict) -> dict: sorted_keys = sorted(params.keys()) query_string = "&".join(f"{k}={params[k]}" for k in sorted_keys) base_string = f"init_form{query_string}{api_key}" params["sign"] = hashlib.md5(base_string.encode("utf-8")).hexdigest() response = requests.post("https://api.1payment.com/init_form", data=params) response.raise_for_status() return response.json() def setup_card_subscription_gate(api_key: str, params: dict) -> dict: sorted_keys = sorted(params.keys()) query_string = "&".join(f"{k}={params[k]}" for k in sorted_keys) base_string = f"init_payment{query_string}{api_key}" params["sign"] = hashlib.md5(base_string.encode("utf-8")).hexdigest() response = requests.post("https://api.1payment.com/init_payment", data=params) response.raise_for_status() return response.json() def charge_card_by_token(api_key: str, params: dict) -> dict: sorted_keys = sorted(params.keys()) query_string = "&".join(f"{k}={params[k]}" for k in sorted_keys) base_string = f"init_payment{query_string}{api_key}" params["sign"] = hashlib.md5(base_string.encode("utf-8")).hexdigest() response = requests.post("https://api.1payment.com/init_payment", data=params) response.raise_for_status() return response.json() api_key = os.environ["ONEPAYMENT_API_KEY"] setup_form_data = { "partner_id": 1234, "project_id": 5678, "amount": "50.00", "subscription": 1, "user_data": "card_setup_777", "shop_url": "https://test.com", } # print(setup_card_subscription_form(api_key, setup_form_data)) setup_gate_data = { "partner_id": 1234, "payment_type": "card", "project_id": 5678, "account": "4111111111111111", "card_holder": "TEST", "year": "22", "month": "01", "cvc": "111", "amount": "50.00", "subscription": 1, "user_data": "card_setup_777", "shop_url": "https://test.com", } # print(setup_card_subscription_gate(api_key, setup_gate_data)) recurring_data = { "partner_id": 1234, "payment_type": "card", "project_id": 5678, "token": "card_t_1a2b3c", "amount": "500.00", "user_data": "card_order_888", "shop_url": "https://test.com", } print(charge_card_by_token(api_key, recurring_data)) ``` {% /tab %} {% tab label="PHP" %} ```php $v) { if ($k === 'sign') { continue; } $tail .= "{$k}={$v}&"; } $tail = substr($tail, 0, -1); $params['sign'] = md5($prefix . $tail . $apiKey); return $params; } function setupCardSubscriptionForm(string $apiKey, array $params): string { $params = signParams('init_form', $apiKey, $params); $ch = curl_init('https://api.1payment.com/init_form'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); return $response; } function setupCardSubscriptionGate(string $apiKey, array $params): string { $params = signParams('init_payment', $apiKey, $params); $ch = curl_init('https://api.1payment.com/init_payment'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); return $response; } function chargeCardByToken(string $apiKey, array $params): string { $params = signParams('init_payment', $apiKey, $params); $ch = curl_init('https://api.1payment.com/init_payment'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); return $response; } $apiKey = getenv('ONEPAYMENT_API_KEY'); $setupFormData = [ 'partner_id' => 1234, 'project_id' => 5678, 'amount' => '50.00', 'subscription' => 1, 'user_data' => 'card_setup_777', 'shop_url' => 'https://test.com', ]; // echo setupCardSubscriptionForm($apiKey, $setupFormData); $setupGateData = [ 'partner_id' => 1234, 'payment_type' => 'card', 'project_id' => 5678, 'account' => '4111111111111111', 'card_holder' => 'TEST', 'year' => '22', 'month' => '01', 'cvc' => '111', 'amount' => '50.00', 'subscription' => 1, 'user_data' => 'card_setup_777', 'shop_url' => 'https://test.com', ]; // echo setupCardSubscriptionGate($apiKey, $setupGateData); $recurringData = [ 'partner_id' => 1234, 'payment_type' => 'card', 'project_id' => 5678, 'token' => 'card_t_1a2b3c', 'amount' => '500.00', 'user_data' => 'card_order_888', 'shop_url' => 'https://test.com', ]; echo chargeCardByToken($apiKey, $recurringData); ``` {% /tab %} {% tab label="cURL" %} ```bash # Привязка через форму: sign = md5("init_form" + amount=50.00&partner_id=1234&project_id=5678&shop_url=...&subscription=1&user_data=... + API_KEY) curl -sS -X POST "https://api.1payment.com/init_form" \ -d "partner_id=1234&project_id=5678&amount=50.00&subscription=1&user_data=card_setup_777&shop_url=https%3A%2F%2Ftest.com&sign=PASTE_MD5_HEX" # Рекуррент: sign = md5("init_payment" + amount=500.00&partner_id=1234&payment_type=card&project_id=5678&shop_url=...&token=...&user_data=... + API_KEY) curl -sS -X POST "https://api.1payment.com/init_payment" \ -d "partner_id=1234&payment_type=card&project_id=5678&token=card_t_1a2b3c&amount=500.00&user_data=card_order_888&shop_url=https%3A%2F%2Ftest.com&sign=PASTE_MD5_HEX" ``` {% /tab %} {% /codeTabs %} ## API response On a successful **recurring** request (`200`): ```json { "order_id": "8p3brmb19gfg0sg8gcwhws8kgc748s87", "status": 2, "status_description": "PENDING", "status_code": 0 } ``` | Field | Description | | --- | --- | | `order_id` | Payment ID in the 1Payment system; use for [status requests](./status.md). | | `status` | Numeric status code (`2` — pending). | | `status_description` | Text status description (`PENDING`, etc.). | | `status_code` | Decline error code (see [decline codes](../decline-error-codes.md)). | | `redirect_url` | URL for 3-D Secure if additional authentication is required (may be absent). | For the **first payment with `subscription=1` via form**, the response contains the `url` field (see [Create payment via form](./init-form.md)); via GATE — `redirect_url` if 3-D Secure is required (see [Create host2host (GATE) payment](./host2host.md)). After successful payment, the `token` field arrives in the callback to `notify_url` or in the [status request](./status.md) response — without `subscription=1`, no token is issued. Request error (`400`): ```json { "error_code": 2 } ``` ## Status notifications (callbacks) After successful card binding or a recurring charge, the system sends a **POST** request in JSON format to your `notify_url`. | Parameter | Type | Req. | Description | | --- | --- | --- | --- | | `payment_type` | String | Yes | Payment type (`card`). | | `order_id` | String | Yes | Payment ID in the 1Payment system. | | `project_id` | Integer | Yes | Your project ID. | | `status` | Integer | Yes | State: `2` (pending), `3` (success), `4` (declined). | | `status_description` | String | Yes | `PENDING`, `SUCCESS`, or `FAILURE`. | | `init_time` | String | Yes | Payment creation time. | | `status_time` | String | Yes | Time the final status was received. | | `merchant_price` | Number | Yes | Amount charged to the payer. | | `init_price` | Number | No | Amount at initialization. | | `user_price` | Number | Yes | Partner payout amount. | | `currency` | String | Yes | Payment currency (ISO 4217). | | `account` | String | Yes | Masked card number. | | `user_data` | String | Yes | Transaction ID passed at creation. | | `sign` | String | Yes | Callback signature. | | `token` | String | No | Token for subsequent charges — **save** after the first successful payment. | | `test` | Integer | No | `1` for test payments. | | `status_code` | String | No | Decline reason code. | **Important:** your server must return **HTTP 200 OK**. Otherwise, the system retries the callback **once per minute for 10 minutes**. **Callback signature verification:** MD5 of all parameters in alphabetical order joined with `&` + `API_KEY` (**without** the `init_payment` prefix). For details, see [API request format](../init-request.md#колбеки-уведомления). You can also obtain the token via [payment status request](./status.md) — the `token` field in the API response. ## Related sections - [Create payment via form](./init-form.md) - [Create host2host (GATE) payment](./host2host.md) - [Payment statuses](./status.md) - [Payment refunds](./refund.md) - [Transaction statuses](../transaction-statuses.md) - [Error codes (API)](../error-codes-api.md) - [Error codes (declines)](../decline-error-codes.md)