Save card for billing
If your business model requires the amount to be dynamic and does not have a fixed billing cycle, you can use the “save card” feature of the recurring billing.
This API integration includes 4 Steps

Step 1 - Save card for billing
NOTE: Similar API as creating a recurring billing with save_card set to true, plan_id should be null and start_date is not required.
HTTP Request
POST 'https://api.sandbox.hit-pay.com/v1/recurring-billing'
Query Parameters
Mandatory fields are name
, customer_email
and amount
, . Remember to include header Content-Type: application/x-www-form-urlencoded
Parameter | Description | Example |
---|---|---|
name | Plan name | Spotify Premium |
description | description of the plan | Spotify Membership |
save_card | Save the card to charge the customer later | true |
customer_email | Customer email | [email protected] |
customer_name | Customer name | Paul |
amount | Amount related to the recurring billing | 9.90 |
currency | Currency related to the recurring billing | SGD |
payment_methods[] | Choice of payment methods you want to offer the customer. Default value is card | giro, card |
redirect_url | URL where hitpay redirects the user after the users enters the card details and the subscription is active. Query arguments reference (subscription id) and status are sent along | https://spotify.com/subscription-completed |
reference | Arbitrary reference number that you can map to your internal reference number. This value cannot be edited by the customer | XXXX123 |
webhook | Optional URL value to which hitpay will send a POST request when there is a new charge or if there is an error charging the card | https://webhoo.site/test |
send_email | Hitpay to send email receipts to the customer. Default value is false | true |
curl --location --request POST 'https://api.staging.hit-pay.com/v1/recurring-billing' \
--header 'X-BUSINESS-API-KEY: meowmeowmeow' \
--header 'X-Requested-With: XMLHttpRequest' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'name=Spotify Premium' \
--data-urlencode 'description=Spotify Membership' \
--data-urlencode 'save_card=true' \
--data-urlencode '[email protected]' \
--data-urlencode 'customer_name=Paul' \
--data-urlencode 'amount=9.90' \
--data-urlencode 'currency=SGD' \
--data-urlencode 'payment_methods[]=giro' \
--data-urlencode 'payment_methods[]=card' \
--data-urlencode 'redirect_url=https://spotify.com/subscription-completed' \
--data-urlencode 'reference=cust_id_123' \
--data-urlencode 'webhook=https://webhoo.site/test' \
--data-urlencode 'send_email=true'
Response
{
"id": "9741164c-06a1-4dd7-a649-72cca8f9603a",
"business_recurring_plans_id": "",
"customer_name": "Paul",
"customer_email": "[email protected]",
"name": "Spotify Premium",
"description": "Spotify Membership",
"reference": "cust_id_123",
"cycle": "save_card",
"cycle_repeat": null,
"cycle_frequency": null,
"currency": "sgd",
"amount": 9.90,
"times_to_be_charged": 3,
"times_charged": 0,
"status": "scheduled",
"send_email": false,
"save_card": 1,
"redirect_url": "https://github.com/",
"payment_methods": [
"giro",
"card"
],
"created_at": "2022-09-13T16:33:47",
"updated_at": "2022-09-13T16:33:47",
"expires_at": null,
"url": "https://securecheckout.staging.hit-pay.com/9673bdea-058c-44b5-a957-845a7c487bc2/recurring-plan/9741164c-06a1-4dd7-a649-72cca8f9603a",
"webhook": "https://webhoo.site/test"
}
Step 2 - Redirect customer to recurring billing page (One time set up)
Redirect the customer to the “url” value.

Once the customer enters the payment information, the card will be saved and you can charge the card anytime using the charge the saved card API.
Step 3 - Charge the saved card
Once the card is saved you can charged the card anytime using the following endpoint
HTTP Request
POST 'https://api.sandbox.hit-pay.com/v1/charge/recurring-billing/{recurring-billing-id}'
recurring-billing-id is the id value from step 1 response.
Query Parameters
Mandatory fields are amount
and currency
. Remember to include header Content-Type: application/x-www-form-urlencoded
Parameter | Description | Example |
---|---|---|
currency | Currency related to the recurring billing | SGD |
amount | Amount related to the recurring billing | 9.90 |
curl --location --request POST 'https://api.sandbox.hit-pay.com/v1/charge/recurring-billing/{recurring_billing_id}' \
--header 'X-BUSINESS-API-KEY: meowmeowmeow' \
--header 'X-Requested-With: XMLHttpRequest' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI4YjdiYzdhYS1iZGUxLTQzODAtYjQ5ZS03ZjFiYWViOGMzY2UiLCJqdGkiOiJjYTA4NjVmMjMxYzA2Yjg2NTY5ODc1ZTFkNjVlMmUwM2EwMmI1ZDcxZTBiMTEwMWUwYzE4Y2Y3NWU2ZjYwZmM0MTIxYmVkMTExZWI4YjRlZSIsImlhdCI6MTY2MzExNzUxOCwibmJmIjoxNjYzMTE3NTE4LCJleHAiOjE2OTQ2NTM1MTcsInN1YiI6IjkwMjdjYTRkLTBhYmItNDk2NC04MzIxLTQ0NWQ0YjMyNzY5NCIsInNjb3BlcyI6W119.VfQoR_luqIZKSBTEXMv_srTVRApk9OfimbX_ghmRkCjrnvZUm-gCSHUbVnBUSzUcjRbVs-rCFs5wKZX4V0bL_76_WqBwxmNzsxRFf-QXFHSt1dDG7TnH6OSduHFeI-6akfQX0DGqal2pStz-UQY07lUiJ_aRe6QnvYqKZaA_eKsAn5XnEo0vn92mk8_i9KTxhvPH85qinfpg23-j3RJNlTDXeRWPn7CmufsrFfdRGtDL2h2thyqEQvju47XAM_Nyar2IjHw_ZcT9ZWnS7sskSwrsrBmOvjSuHA-ANr55ufc11GwjdRyzBPLu3SOUJ8kHJnprdep70VIpYLtO_nKG1xMzJRJzSno-Hvhn7RzjT-xpSudfUzRKb6M9z_BVmSQ8eUfuigwcmadH-pAFP67noNQAL5zeOjlr4RXGRKoMdeOOM4hxciojZRqoiBT-i74aAHg0AAlJHx4NQnM4LcDkN_Sh0kK4Ip4BHZHuxE4t9CZ24erizjXcdwvzv0UG0QCYRSfWN41PcHTbljDXQWmV719PDtPVwoAb1Ht2EKzuAQ4umuLx6NzOpBFuXpElZfwVT9XoDr22Dwts-7cW2fqj_C6igptqoAeRuCGEejDRgq2dA-UJTpRxfi6J02XXKpeDv-hGyFCYE8TUHBqTg5HMRQeHtga3-Hq05IPFhp9fmyk' \
--data-urlencode 'amount=9.90' \
--data-urlencode 'currency=SGD
Response
{
"payment_id": "9746f906-bdbb-4064-8372-642cf5877e0c",
"recurring_billing_id": "9746f8c2-2b7c-4c78-8832-012f203ae687",
"amount": 9.90,
"currency": "sgd",
"status": "succeeded"
}
Step 4: Handle Successful Payment
Webhooks
HitPay will send a Webhook POST request when there is a new charge or if there is an error charging the card.
If you are using HitPay APIs to integrate into your app you must mark your order as paid ONLY after the webhook is received and validated.
- Create an endpoint (E.g. /payment-confirmation/webhook) in your server that accepts POST requests. This request is
application/x-www-form-urlencoded
. - Validate the webhook data using your salt value
- Return HTTP status code 200 to Hitpay
- Mark your order as paid
payment_id=9746f906-bdbb-4064-8372-642cf5877e0c&recurring_billing_id=9746f8c2-2b7c-4c78-8832-012f203ae687&amount=9.90¤cy=sgd&status=succeeded&hmac=7690ff7ab7d88480a480b8be722f6dd12cc58f489da25c504dbe29c04b297418
Webhook fields
Following fields are sent with the webhook request:
Parameter | Description |
---|---|
payment_id | Payment ID |
recurring_billing_id | Recurring billing request ID |
amount | Amount related to the recurring billing |
currency | Currency related to the recurring billing |
status | Payment status (succeeded / failed) |
hmac | Message Authentication code of this webhook request |
Validate Webhook
Hitpay creates a list of all values from the key-value pairs that we send in the POST request and sort them in the order of their keys alphabetically. We then concatenate all these values together. We then use the HMAC-SHA256 algorithm to generate the signature. The HMAC key for the signature generation is the secret salt
from your dashboard under Settings > Payment Gateway > API Keys
.
public function generateSignatureArray($secret, array $args)
{
$hmacSource = [];
foreach ($args as $key => $val) {
$hmacSource[$key] = "{$key}{$val}";
}
ksort($hmacSource);
$sig = implode("", array_values($hmacSource));
$calculatedHmac = hash_hmac('sha256', $sig, $secret);
return $calculatedHmac;
}
Signature Mismatch?
Possible reasons for wrong hmac value generated
- Ensure that you are using the correct salt value from the correct environment (Sandbox or Production)
- Make sure NOT to include the hamc value when calculating the hmac
- Make sure all the values stated above are included in the payload including reference. Use an empty string if the value does not exist
Next Step
Congrats! You have now successfully completed the recurring billing integration.
Refund API: This is an optional API call, you may use this to refund a specific payment.
Production Checklist: Checkout before you go live.
Getting Started: Reference to the API Details
Updated 8 months ago