Authorization Requests
Unit allows you to inject yourself into the decision making process, whenever a card gets used to move funds. Unit will make a call to a dedicated endpoint you create, letting you know about every attempt to use the card, and the details of the attempt. You will respond with an approval or a decline, based on your internal business logic. We refer to this process as "programmatic authorization of card use".
This capability can be used to:
- Restrict payments to certain types of institutions / services
- Create elaborate spend controls
- Run your own fraud logic
- Move funds between accounts of the customer in real time to prevent a card decline due to insufficient funds.
This is a new version of the programmatic authorization flow, that supports multiple concurrent authorization requests (unlike the serial, webhook based version). The webhook version will be available through 2023. We highly recommend migrating to the new version. The documentation for the deprecated version can be found here.
Programmatic authorization of card use
The programmatic authorization feature is not enabled by default. To set it up, please reach out to your Unit contact.
You would have to create a dedicated API endpoint (details below) that Unit will call with every authorization request. You can configure that URL in the Unit Dashboard, in the org settings screen.
There are 3 types of card use that you can choose to receive authorization requests on: Purchases, ATM transactions, and Card Transactions (representing mostly peer-to-peer transfers). For each of those, you can choose whether to enable the programmatic authorization flow, as well as the default behavior in cases of timeout.
The Programmatic authorization of card use includes the following steps:
- An authorization pending http request is sent by Unit to the url you defined in your org settings.
- You can then approve or decline the authorization request by responding to the API call Unit sent to you.
- If you do not approve or decline the authorization request within 2 seconds, Unit will approve or decline it according to the default that is defined for you on Unit's settings (which can be changed any time by contacting Unit).
- The response from the
approve
operation will include the final authorization request status. Approving an authorization request may still result in aDeclined
status for different reasons. For example, at this stage Unit will check the balance on the account, and may decline the authorization due to insufficient funds. - Unit will send an
authorizationRequest.approved
orauthorizationRequest.declined
webhook event based on the final approval status. - If an authorization's amount changes the whole process will restart from stage (1).
In order to test the programmatic authorization of card use in Sandbox, you may take advantage of the ability to simulate a Card Purchase Authorization Request, Card Transaction Authorization Request or ATM Authorization Request.
Securing your http servers
Ensure your server is only receiving the expected Unit requests.
Once your server is configured to receive payloads, it'll listen for any payloads sent to the endpoint you configured.
For security reasons, you probably want to verify that the payloads are coming from Unit.
To verify the payloads when defining a url you can set up a secret token which Unit will use to sign the payloads.
Setting up your secret token
You'll need to set up your secret token in two places: Unit dashboard and your server. To set your token in Unit Dashboard:
- Navigate to OrgSettings on the top menu under Developer section.
- Navigate to GeneralSettings under OrgSettings
- Enable API based authorization requests and set your url
- Enable Add optional secret and set your secret token
Verifying payloads from Unit
If your secret token is set, Unit will use it to create a hash signature with the entire body of the webhook request.
This hash signature, encoded with base64 is passed along with each request in the headers as X-Unit-Signature.
Unit uses an HMAC SHA1 to compute the hash.
const express = require('express')
var crypto = require('crypto')
const app = express()
const port = 4000
app.post('/my-webhook', (request, response) => {
response.send('request passed signature validation...')
})
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.use((request, response, next) => {
var signature = request.header("x-unit-signature")
var hmac = crypto.createHmac('sha1', <your secret>)
hmac.update(JSON.stringify(request.body))
if(hmac.digest('base64') == signature) {
const responseBody = {
data: {
type: 'declineAuthorizationRequest',
attributes: {
reason: "InsufficientFunds"
},
},
};
response.status(200).json(responseBody);
}
else {
response.status(500).send("Signatures didn't match!")
}
})
app.listen(port, (err) => {
console.log(`server is listening on ${port}`)
})
Authorization Request
The below covers the structure of the request Unit makes to your API, as well as the two response options - approve or decline.
Field | Type | Description |
---|---|---|
data | PurchaseAuthorizationRequest / CardTransactionAuthorizationRequest / AtmAuthorizationRequest / IIASAuthorizationRequest | The authorization request possible resources. |
{
"data": [
{
"id": "412",
"type": "pendingAuthorizationRequest",
"attributes": {
"createdAt": "2021-06-24T08:10:08.081Z",
"amount": 2000,
"available": 150000,
"status": "Pending",
"partialApprovalAllowed": false,
"merchant": {
"name": "Merchant name",
"type": "6012",
"id": "311204598883"
},
"recurring": false,
"ecommerce": true,
"cardPresent": false,
"direction": "Debit",
"mustBeApproved": false,
"tags": {
"tag": "value"
},
"currencyConversion": {
"originalCurrency": "EUR",
"amountInOriginalCurrency": 1000,
"fxRate": "1.164"
},
"isInternational": true,
"cardNetwork": "Visa"
},
"relationships": {
"authorizationRequest": {
"data": {
"id": "6",
"type": "purchaseAuthorizationRequest"
}
},
"account": {
"data": {
"id": "10001",
"type": "account"
}
},
"customer": {
"data": {
"id": "10000",
"type": "customer"
}
},
"card": {
"data": {
"id": "7",
"type": "card"
}
}
}
}
]
}
Approving an Authorization Request
Response is a JSON:API document.
200 OK
Attributes
Name | Type | Description |
---|---|---|
amount | integer | Optional. The approved amount (in cents). Can only be specified if the authorization request's partialApprovalAllowed is set to true . |
fundingAccount | string | Optional. The id of an alternate account (either the customer's or another's) that should be used for funding the transaction. Please contact Unit to enable this feature. |
tags | object | Optional, See Tags. The specified tags will be inherited by the resulting authorization and transaction resources. |
{
"data": {
"type": "approveAuthorizationRequest",
"attributes": {
"amount": 5000
}
}
}
Declining an Authorization Request
Response is a JSON:API document.
200 OK
Attributes
Name | Type | Description |
---|---|---|
reason | string | The reason for declining the authorization request. One of AccountClosed , CardExceedsAmountLimit , DoNotHonor , InsufficientFunds , InvalidMerchant , ReferToCardIssuer , RestrictedCard , TransactionNotPermittedToCardholder . |
{
"data": {
"type": "declineAuthorizationRequest",
"attributes": {
"reason": "InsufficientFunds"
}
}
}
Get by Id
Get an authorization request resource by id.
Verb | GET |
Url | https://api.s.unit.sh/authorization-requests/{id} |
Required Scope | authorization-requests |
Timeout (Seconds) | 5 |
Response
Response is a JSON:API document.
200 OK
Field | Type | Description |
---|---|---|
data | PurchaseAuthorizationRequest / CardTransactionAuthorizationRequest / AtmAuthorizationRequest | Authorization Request resource. |
curl -X GET 'https://api.s.unit.sh/authorization-requests/1' \
-H "Authorization: Bearer ${TOKEN}"
List
List authorization requests. Filtering and paging can be applied.
Verb | GET |
Url | https://api.s.unit.sh/authorization-requests |
Required Scope | authorization-requests |
Timeout (Seconds) | 5 |
Query Parameters
Name | Type | Default | Description |
---|---|---|---|
page[limit] | integer | 100 | Optional. Maximum number of resources that will be returned. Maximum is 1000 resources. |
page[offset] | integer | 0 | Optional. Number of resources to skip. |
filter[accountId] | string | (empty) | Optional. Filters the results by the specified account id. |
filter[customerId] | string | (empty) | Optional. Filters the results by the specified customer id. |
filter[merchantCategoryCode][] | Integer | (empty) | Optional. Filter result by their 4-digit ISO 18245 merchant category code (MCC). |
filter[fromAmount] | Integer | (empty) | Optional. Filters the result that have an amount that is higher or equal to the specified amount (in cents). e.g. 5000 |
filter[toAmount] | Integer | (empty) | Optional. Filters the result that have an amount that is lower or equal to the specified amount (in cents). e.g. 7000 |
curl -X GET 'https://api.s.unit.sh/authorization-requests?page[limit]=20&page[offset]=0' \
-H "Authorization: Bearer ${TOKEN}"
Response
Response is a JSON:API document.
200 OK
Field | Type | Description |
---|---|---|
data | Array of PurchaseAuthorizationRequest / CardTransactionAuthorizationRequest / AtmAuthorizationRequest | Array of authorization request resources. |
{
"data": [
{
"type": "purchaseAuthorizationRequest",
"id": "1",
"attributes": {
"createdAt": "2021-06-22T13:39:17.018Z",
"amount": 2500,
"status": "Approved",
"partialApprovalAllowed": false,
"approvedAmount": 2500,
"merchant": {
"name": "Apple Inc.",
"type": 1000,
"category": "",
"location": "Cupertino, CA",
"id": "311204598883"
},
"recurring": false
},
"relationships": {
"customer": {
"data": {
"type": "customer",
"id": "10000"
}
},
"account": {
"data": {
"type": "account",
"id": "10001"
}
},
"card": {
"data": {
"type": "card",
"id": "7"
}
}
}
},
{
"type": "cardTransactionAuthorizationRequest",
"id": "2",
"attributes": {
"createdAt": "2021-06-22T13:41:01.379Z",
"amount": 2500,
"status": "Pending",
"partialApprovalAllowed": false,
"merchant": {
"name": "Apple Inc.",
"type": 1000,
"category": "",
"location": "Cupertino, CA",
"id": "311204598883"
},
"recurring": false
},
"relationships": {
"customer": {
"data": {
"type": "customer",
"id": "10000"
}
},
"account": {
"data": {
"type": "account",
"id": "10001"
}
},
"card": {
"data": {
"type": "card",
"id": "7"
}
}
}
}
]
}