Payments API Reference

The Payments API is currently in Early Access - please contact us for more information.

Lean’s Payments API supports bank-to-bank transfers from your customer’s account to your business account. There are three steps required to use the Payments API with your customers:

  1. Set up your payment details.
  2. Enable customer payments from a nominated account.
  3. Initiate payment from a customer account to your account.

Set Up Payment Details

To enable payments for your customers, you first need to provide Lean with your business‘s bank details for your customers to transfer funds to.

To do this, speak to your product representative at Lean.

View required information bank details

Contact Details

Nick Name

First Name

Last Name

Email Address

Telephone Number

SMS


Account Details

Bank Name

Bank Address

Swift Code

Account Number

IBAN

Was this section helpful? 

Add additional Payment Destinations

A Destination is an account that your users can transfer money to. Additional Destination accounts only need to be added for applications that require routing of funds based on business logic. For example USD payments to USD accounts or Peer to Peer (P2P) transfers. In later stages, Lean will default to your nominated account in the section above where a Payment Destination is not specified.

Request

bank_identifier String

The Lean value for the bank_identifier - you can get a list of accepted banks with a GET request to the /banks/v1 endpoint.


name String

The legal name on the account.


iban String

The IBAN for the account


display_name String

A friendly name for the account to be displayed to users in the Link SDK.


address String

The address of the bank that holds the account


city String

The city the holding bank is in.


country String

The 3 letter ISO code for the country of the holding bank.


swift_code String

The swift code for the holding bank.


account_number String

The bank account number


customer_id String: optional

The customer_id for the account. This is field is optional, and indicates to Lean that this is one of your customer's accounts.


Response

id String

The id for the Payment Destination, this is used for future calls to direct funds.


bank_identifier String

The Lean value for the bank_identifier - you can get a list of accepted banks with a GET request to the /banks/v1 endpoint.


name String

The legal name on the account.


iban String

The IBAN for the account


display_name String

A friendly name for the account to be displayed to users in the Link SDK.


address String

The address of the bank that holds the account


city String

The city the holding bank is in.


country String

The 3 letter ISO code for the country of the holding bank.


swift_code String

The swift code for the holding bank.


account_number String

The bank account number


default bool

Whether this is your account's default destination or not. If true this destination will be used when a payment_destination_id is not specified in a Payment Intent.


customer_id String: optional

The customer_id for the account. This is field is optional, and indicates to Lean that this is one of your customer's accounts.


owner_type ENUM

The type of owner. If no customer_id is present, this will be APPLICATION if a customer_id is present the value will be CUSTOMER.


Was this section helpful? 

bash

curl -X POST 'https://sandbox.leantech.me/payments/v1/destinations' \
--header 'lean-app-token: YOUR_APP_TOKEN' \
--data-raw '{
"bank_identifier": "ENBD_UAE",
"name": "Second Account",
"iban": "AE870200000030798508001",
"display_name": "Second account",
"address": "1234 1234",
"city": "Abu Dhabi",
"country": "ARE",
"swift_code": "EBILAEADXXX",
"account_number": "030798508001",
"customer_id": "f08fb010-878f-407a-9ac2-a7840fb56185"
}'

json

{
"id": "517f416f-6632-407f-9110-18e11a4fd4d2",
"bank_identifier": "ENBD_UAE",
"name": "Second Account",
"iban": "AE870200000030798508001",
"display_name": "Second account",
"account_number": "030798508001",
"swift_code": "EBILAEADXXX",
"status": "CONFIRMED",
"address": "1234 1234",
"country": "ARE",
"city": "Abu Dhabi",
"default": false,
"customer_id": "f08fb010-878f-407a-9ac2-a7840fb56185",
"owner_type": "CUSTOMER"
}

Payment Flow Overview

Lean's Payment API is split into two parts. Creating a payment source and making a payment. You can see an overview of the flow you will be implementing below:

Was this section helpful? 

Errors

Lean uses conventional HTTP response codes with requests to help you identify errors and how to rectify them. We also provide a status and message within the body of an error response to indicate how to resolve the error that has occurred.

In general: codes in the 2XX range indicate success, codes in the 4XX range indicate a bad request and codes in the 5XX range indicate something went wrong on our end.

View List of Error Statuses

MISSING_PARAMETERS 400

The request does not have all the required parameters to complete the call.


INVALID_PARAMETERS 400

The request provided invalid parameters.


AMOUNT_OVER_MAX_VALUE 400

The amount specified in a payment_intent request is over the maximum allowed value for a single transfer.

Maximum transfer amounts
CurrencyAmount
AED40,000.00

AMOUNT_UNDER_MIN_VALUE 400

The amount specified in a payment_intent request is under the minimum allowed value for a single transfer (AED 10).

Minimum transfer amounts
CurrencyAmount
AED10.00

INVALID_CERTIFICATE 403

The certificate on your server has not been recognized.


UNKNOWN_ENTITY 404

The entity_id provided did not match an account in our records.


UNKNOWN_APP_ID 404

The app_id provided has not been recognized by Lean.


UNKNOWN_RESULTS_ID 404

The results_id provided has not been recognized by Lean


RATE_LIMIT_EXCEEDED 429

Rate limit exceeded. Please try again later.


INTERNAL_SERVER_ERROR 500

Something has gone wrong. If you continue to see this error, please get in touch with Lean.

Was this section helpful? 

javascript

HTTP CODE: 400
{
results_id: "139820938109823",
status: "MISSING_PARAMETERS",
message: "The request does not have all the required parameters to complete the call.",
payload: null
meta: {
...meta_data
}
timestamp: "2020-08-08T00:00:00Z"
}

Create a new Customer

A Customer object is a container for all the billing details for your customer and should map on a one-to-one basis with users in your platform. To create a Customer object, make a call to the Customer API endpoint with your application token in the header and the related user ID in your own database.

A new Customer object will be returned, and the customer_id should be saved in your own database.

Request

app_user_id String

The user ID in your own user table. This is to allow you to easily reconcile users and customers in the future.

Please note, this value has a unique constraint, no two Customers can share the same app_user_id

Response

app_user_id String

The user ID you assigned the customer during creation.


customer_id String

A UUID identifying the customer for future calls.


Full Customer API Reference

Was this section helpful? 

bash

curl -X POST 'https://sandbox.leantech.me/customers/v1/' \
--header 'lean-app-token: YOUR_APP_TOKEN' \
--data-raw '{
"app_user_id": "001"
}'

json

{
app_user_id: "001",
customer_id: "f08fb010-878f-407a-9ac2-a7840fb56185"
}

Create a Payment Source

Next, you need to enable a payment source for your customer. You can enable multiple payment sources for a customer with different banks.

Please note: dependent on the bank, a cooling-off period up to 24 hours will come into effect before you can make transfers between the customer account and your own account.

To create a payment_source you will need to have the Link SDK available in your application.

Test Users & OTPs

Request

customer_id String

The Customer object you want to create a payment source for.


bank_identifier String (optional)

The bank identifier you want your customer to add a payment source for - you can read more about skipping bank selection here.


Returns

status ENUM

Indicates success or failure of the connection process.


Webhooks

Once a payment_source is successfully created, two webhooks will be sent to your server notifying you of the creation. a payment_source.created and a payment_source.beneficiary.created.

If you are only directing funds to a single company account - you should use the payment_source webhooks to drive your logic.

If you are managing multiple Payment Destinations for a single user - you should use the payment_source.beneficiary webhooks - please see the next section for details on processing these webhooks.

Was this section helpful? 

javascript

Lean.createPaymentSource({
app_token: "2c9280887230f322017231b408cf0007",
customer_id: "f08fb010-878f-407a-9ac2-a7840fb56185",
bank_identifier: "HSBC_UAE"
})

json

{
status: "200 OK"
}

json

// Webhook response
{
"payload": {
"id": "54525057-75d6-4a3a-984b-dac2f7af527c",
"customer_id": "6dc68854-e3fc-46e2-880a-9e3afbf50e5e",
"status": "AWAITING_BENEFICIARY_COOL_OFF",
"bank_identifier": "ADCB_UAE",
"bank_name": "Abu Dhabi Commercial Bank"
},
"type": "payment_source.created",
"message": "A payment source was pre-authorized by your customer.",
"timestamp": "2021-05-18T13:51:17.30604Z"
}
{
"payload": {
"id": "1db93804-6eb9-4e6b-a35b-ffd9a2c13427",
"customer_id": "6dc68854-e3fc-46e2-880a-9e3afbf50e5e",
"payment_source_id": "54525057-75d6-4a3a-984b-dac2f7af527c",
"payment_destination_id": "95c29647-c568-40d7-8710-3e330de1b5f2",
"status": "AWAITING_BENEFICIARY_COOL_OFF"
},
"type": "payment_source.beneficiary.created",
"message": "A beneficiary was added for a payment source.",
"timestamp": "2021-05-18T13:51:17.121812Z"
}
// Payment source ACTIVE
{
"payload": {
"id": "0f2e5b78-4077-48e9-a9e5-d42777389fd8",
"customer_id": "1c12b6c9-4379-4264-bab0-6ff6798479e2",
"status": "ACTIVE",
"bank_identifier": "ENBD_UAE",
"bank_name": "Emirates NBD"
},
"type": "payment_source.updated",
"message": "A payment source has been updated.",
"timestamp": "2021-05-13T16:02:48.814838Z"
}
{
"payload": {
"id": "f4b69b1e-ddb8-4210-a854-ed32cfbbd1e7",
"customer_id": "1c12b6c9-4379-4264-bab0-6ff6798479e2",
"payment_source_id": "0f2e5b78-4077-48e9-a9e5-d42777389fd8",
"payment_destination_id": "95c29647-c568-40d7-8710-3e330de1b5f2",
"status": "ACTIVE"
},
"type": "payment_source.beneficiary.updated",
"message": "A beneficiary was updated for a payment source.",
"timestamp": "2021-05-13T16:02:48.800026Z"
}

Update a Payment Source

To add a second beneficiary to an existing Payment Source, to enable P2P or routing of funds depending on account type, you can specify an additional Destination to add to the account as a beneficiary for future payments.

Request

customer_id String

The Customer object you want to create a payment source for.


payment_source_id String

The Payment Source you want your user to update.


payment_destination_id String

The Payment Destination you want your user to be able to transfer funds to.


Returns

status ENUM

Indicates success or failure of the connection process.


Processing the Webhooks

Once a payment_source is updated, a payment_source.beneficiary.created webhook will be sent.

If you are only directing funds to a single company account - you should use the payment_source webhooks to drive your logic. If you are managing multiple Payment Destinations for a single user - you should use the payment_source.beneficiary webhooks. You should check your database for the existence of the payment_source_id.

If the payment source does not exist in your database

Fetch the newly created Payment Source and save it to your database:

bash

curl -X GET 'sandbox.leantech.me/customers/v1/<customer_id>/payment_sources/<payment_source_id>' \
--header 'Content-Type: application/json' \
--header 'lean-app-token: YOUR_APP_TOKEN'

json

{
"id": "d8d7a9b3-c401-49f6-81a4-386d8398vg4b",
"customer_id": "6db94cb7-3a02-4a57-a03a-e2a92ce86406",
"app_id": "2c9280887230f322017231b408cf0007",
"bank_identifier": "ENBD_UAE",
"beneficiaries": [
{
"id": "26891A75-9406-4E06-97BA-F4D6DA664F3C",
"payment_destination_id": "f044c18b-3a51-49a2-bd3f-a585679d8885"
"status": "AWAITING_BENEFICIARY_COOL_OFF",
"beneficiary_cool_off_expiry": "2021-02-13T13:20:57.674299Z"
}
]
"accounts": [
{
"id": "9acde98e-9d19-495d-934c-b2ff966262c8",
"account_id": "aaadbc4e-96a4-420f-ba01-597fa63502ee",
"account_name": "CURRENT ACCOUNT",
"account_number": "1015528XXXXXX",
"iban": "AE340260001015528XXXXXX",
"balance": 35681.15,
"currency": "AED",
"balance_last_updated": "2021-02-12T13:20:23.83922Z"
}
]
}

If the payment source exists in your database

Append the new beneficiary from the webhook's payload to the beneficiaries array within the payment_source

Was this section helpful? 

javascript

Lean.updatePaymentSource({
app_token: "2c9280887230f322017231b408cf0007",
customer_id: "f08fb010-878f-407a-9ac2-a7840fb56185",
payment_source_id: "0f2e5b78-4077-48e9-a9e5-d42777389fd8",
payment_destination_id: "517f416f-6632-407f-9110-18e11a4fd4d2"
})

json

{
status: "SUCCESS"
}

json

// Webhook response
{
"payload": {
"id": "1db93804-6eb9-4e6b-a35b-ffd9a2c13427",
"customer_id": "6dc68854-e3fc-46e2-880a-9e3afbf50e5e",
"payment_source_id": "54525057-75d6-4a3a-984b-dac2f7af527c",
"payment_destination_id": "95c29647-c568-40d7-8710-3e330de1b5f2",
"status": "AWAITING_BENEFICIARY_COOL_OFF"
},
"type": "payment_source.beneficiary.created",
"message": "A beneficiary was added for a payment source.",
"timestamp": "2021-05-18T13:51:17.121812Z"
}
// Payment source ACTIVE
{
"payload": {
"id": "f4b69b1e-ddb8-4210-a854-ed32cfbbd1e7",
"customer_id": "1c12b6c9-4379-4264-bab0-6ff6798479e2",
"payment_source_id": "0f2e5b78-4077-48e9-a9e5-d42777389fd8",
"payment_destination_id": "95c29647-c568-40d7-8710-3e330de1b5f2",
"status": "ACTIVE"
},
"type": "payment_source.beneficiary.updated",
"message": "A beneficiary was updated for a payment source.",
"timestamp": "2021-05-13T16:02:48.800026Z"
}

Payment Intents

Before you can initiate a payment for a customer, you will need to create a payment intent for the value of the transaction you wish to make from your backend to Lean.

Request

amount Int

The value of the transaction.


currency String

The three-letter ISO code for the currency you’re trying to process the payment in.


payment_destination_id String (optional)

The destination you want to route the Payment to. If no value is provided, the transfer will be routed to your default account.


customer_id String

The customer you want to bill for this transaction.


Response

payment_intent_id String

This ID will need to be passed to your application to input to the Link SDK and to retrieve updates on the payment in the future.


Was this section helpful? 

bash

curl -X POST sandbox.leantech.me/payments/v1/intents \
--header 'lean-app-token: YOUR_APP_TOKEN' \
--data-raw '{
"amount": 400,
"currency": "AED",
"payment_destination_id": "95c29647-c568-40d7-8710-3e330de1b5f2",
"customer_id": "6dd9c7d2-1c8c-4862-bb1f-fcf52f5033d4"
}'

json

{
payment_intent_id: "452bcde4-5e48-44bb-8f6d-40c5c286466b"
}

Initiate a Payment

Once you have a payment intent you can now initiate the Link SDK to complete the payment.

To use the Link SDK, it must be available in your application: How to install the Link SDK in your application.

To initiate a payment, call the .pay() method in the LinkSDK from your front-end. This will guide your user through the final steps to complete the payment.

Test Users & OTPs

Request

payment_intent String

The payment intent created by your backend.


account_id String (optional)

The account id to initiate the payment from - you can read more about skipping account selection here.


Response

status String

The status of the payment once the LinkSDK has completed.

View statuses

success

Payment was successful.


insufficient_funds

Payment failed due to insufficient funds.


user_unconfirmed

User did not confirm the payment details.


payment_unauthorized

User did not enter correct authorization details.

Webhook Structure

type String

The type of the webhook, this will be payment.created.

payload Payment Object

The payment attempt created by the LinkSDK flow.

View Object Attributes

id String

The ID of the payment.


customer_id String

The id of the customer who has initiated this payment


intent_id String

The payment intent id that initiated this payment


bank_transaction_reference String

The confirmation reference returned by banks upon acceptance of the payment inititaion, this value can help with reconciliation.


status Enum

View statuses

ACCEPTED_BY_BANK

The payment request has been accepted by the bank.


FAILED

The payment request was rejected by the bank.


PENDING_WITH_BANK

The payment request has been accepted by the bank, but money hasn't been transferred immediately. Requires manual reconcilliation.


amount Int

The amount transferred from the customer’s payment source.


currency String

The currency the transfer was made in.

Was this section helpful? 

javascript

Lean.pay({
app_token: "YOUR_APP_TOKEN",
payment_intent_id: "452bcde4-5e48-44bb-8f6d-40c5c286466b",
account_id: "53bf3538-d5fa-4230-9a36-8622da90de99"
})

json

// Webhook response - Payment accepted by bank
{
"type": "payment.created",
"payload" : {
"id": "452bcde4-5e48-44bb-8f6d-40c5c286466b"
"customer_id": "6dd9c7d2-1c8c-4862-bb1f-fcf52f5033d4",
"intent_id": "8c339261-a62f-4251-bb58-f005bd0c6cd2",
"status": "ACCEPTED_BY_BANK",
"amount": 123.45,
"currency": "AED"
"bank_transaction_reference": "101231785",
},
"message": "A payment object has been created.",
"timestamp": "2020-07-31T07:11:39.862804Z"
}
// Webhook response - Payment rejected by bank
{
"type": "payment.created",
"payload": {
"id": "99829d37-47f7-4cda-b2d7-ece1aa90860b",
"customer_id": "6dd9c7d2-1c8c-4862-bb1f-fcf52f5033d4",
"intent_id": "7cfd19ae-dd0a-4c60-9389-9121097c784d",
"status": "FAILED",
"amount": 1234.56,
"currency": "AED"
"bank_transaction_reference": null,
},
"message": "A payment object has been created.",
"timestamp": "2020-07-31T07:11:39.779636Z"
}

Fetching Payment Details

Once a payment has been completed - you can fetch the details from the original Payment Intent object.

Request

payment_intent String

The payment intent you want to view the payment details for.


Response

payment_intent_id String

The ID of the Payment Intent requested


customer_id String

The ID of the Customer assigned to the Payment Intent


amount Float

The amount requested in the Payment Intent


currency String

The 3 letter ISO currency code for the Payment Intent.


Payments Array

Payments executed for this Payment Intent, the Payments array will be empty if no payment attempts have been made.

View Attributes

id String

The id of the Payment


payment_intent_id String

The ID of the Payment Intent that generated this Payment


sender_details Object

Account details of the account that made the payment.

View Attributes

account_number String

The account number of the sending bank account.


iban String

The iban of the sending bank account.


recipient_details Object

Account details of the account that made the payment.

View Attributes

account_number String

The account number of the payment recipient.


iban String

The iban of the payment recipient.


name String

The name of the payment recipient or account.


amount Float

The amount transferred with the payment.


currency String

The 3 letter ISO currency code that the Payment was executed with.


description String

The reference number Lean processed the transaction with, may display in your bank statement for reconcilliation.


bank_transaction_reference String

The reference number the bank processed the transaction with, may display in your bank statement for reconcilliation.


submission_timestamp Datetime

The date and time the payment was submitted.


initiation_timestamp Datetime

The date and time the payment was initiated with the bank.


status ENUM

The current status of the payment.

View Status'

ACCEPTED_BY_BANK

The transaction was accepted by the bank.


PENDING_WITH_BANK

The transaction is pending with the bank, pending transactions will need to be manually reconciled.


FAILED

The transaction was rejected by the bank or the user entered incorrect details.


UNKNOWN

The bank did not provide a response to our initiation. Unknown transactions will need to be manually reconciled.

Was this section helpful? 

bash

curl -X GET 'https://sandbox.leantech.me/payments/v1/intents/<payment_intent_id>' \
--header 'lean-app-token: YOUR_APP_TOKEN'

json

{
"payment_intent_id": "65181c8c-16ce-4c9e-8472-03a92bb94eee",
"customer_id": "fd8e0e89-3bb1-466e-954a-9afe894f4639",
"amount": 10.12,
"currency": "AED",
"payments": [
{
"id": "5f152fe5-e16d-4ee6-b138-46c7e0514ccb",
"payment_intent_id": "65181c8c-16ce-4c9e-8472-03a92bb94eee",
"sender_details": {
"account_number": "1015528713201",
"iban": "AE930260001015528713201"
},
"recipient_details": {
"account_number": "020386403001",
"iban": "AE870200000020386403001",
"name": "Fintech App Ltd"
},
"amount": 10.12,
"currency": "AED",
"description": "65181c8c16ce4c9e847203a92bb94eee",
"bank_transaction_reference": "107261785",
"submission_timestamp": "2021-03-24T12:49:31.914459Z",
"initiation_timestamp": "2021-03-24T12:50:47.502080Z",
"status": "ACCEPTED_BY_BANK"
}
]
}

Delete a Payment Source

You can delete a payment source via the API with a simple DELETE request. This will remove the user's login information and the payment source from Lean systems, please note: this will not remove your accounts as a beneficiary of the users account.

Request

Reason enum

Please always pass in the value USER_REQUESTED.

Response

A 200 OK response will be sent back.

bash

curl -X DELETE 'sandbox.leantech.me/customers/v1/{customer_id}/payment-sources/{payment_source_id}' \
--header 'Content-Type: application/json' \
--header 'lean-app-token: YOUR_APP_TOKEN' \
--data-raw '{
"reason": "USER_REQUESTED"
}'