This guide will help you to get set up making payments between a customer and your default bank account. For advanced usage such as routing funds to different accounts, please see the Multi-Beneficiary and P2P guides respectively.
The Payment flow can be roughly broken down into four stages:
A Destination is a bank account that you want your customer to be able transfer money into.
Call the Destinations endpoint in the Payments API to create a new Destination resource:
curl -X POST 'https://sandbox.leantech.me/payments/v1/destinations' \--header 'Content-Type: application/json' \--header 'lean-app-token: YOUR_APP_TOKEN' \--data-raw '{"display_name": "Acme USD Account","name": "Acme Inc.","bank_identifier": "ENBD_UAE","address": "","city": "","country": "","account_number": "","iban": ""}'
{"id": "266733cc-6f46-4e87-af7e-9bb46b99601d""display_name": "Acme USD Account","name": "Acme Inc.","bank_identifier": "ENBD_UAE","address": "","city": "","country": "","account_number": "","iban": ""}
You should store the ID of this Destination resource - as it will be used to determine where you route customer funds to in later stages.
If you would like to retrieve a list of all created Destinations in your application, you can make a GET
request to the Destinations endpoint.
GET https://sandbox.leantech.me/payments/v1/destinations
In order to start fetching data, you first need to create a Customer resource. This creates a relationship between your application, user and all the services they consume within Lean into a single object.
To create a Customer, call the /customers/v1
endpoint with a reference to your app_user_id
. You should save the returned customer_id
against your user table for future reference.
Please note:
app_user_id
has a unique constraint, but does not need to map directly to the id of the user in your database, for example you could passprod_usr_1246
as theapp_user_id
, so that you can identify that the Customer is a user of your production database. In this case, you should save both thecustomer_id
andapp_user_id
against your user table for later retrieval and mapping.
curl -X POST 'https://sandbox.leantech.me/customers/v1/' \--header 'Content-Type: application/json' \--header 'lean-app-token: YOUR_APP_TOKEN' \--data-raw '{"app_user_id": "IDENTIFIER_FOR_CUSTOMER"}'
{app_user_id: "IDENTIFIER_FOR_CUSTOMER",customer_id: "f08fb010-878f-407a-9ac2-a7840fb56185"}
In order for your customer to be able to make a payment, first they need to authorise their bank to transfer funds from their account to your account. This is known in their bank account as a 'Beneficiary'.
For some banks, creating a beneficiary can have a cool-down period of up to 24 hours, which is why we have separated the flow into two distinct parts so that users can complete future initiations without having to enter passwords and wait for cool-down periods.
Please note: Setting up payments does not require actually making a payment from your end-user, in order to make functionality available as soon as possible to your customer, you can create a Payment Source during initial onboarding.
On your Front-end, make a .createPaymentSource()
call to the LinkSDK to guide your user through the process of creating a beneficiary. For further information on integrating the LinkSDK into your platform please see the LinkSDK integration documentation at https://docs.leantech.me/link-sdk. For the purposes of this guide we will be using Javascript as the language being used by the front-end.
// page.html<button onClick={() => cps(...params)}>Set up payments</button>// script.jsfunction cps(customer_id) {Lean.createPaymentSource({app_token: "YOUR_APP_TOKEN",customer_id: customer_id,payment_destination_id: "266733cc-6f46-4e87-af7e-9bb46b99601d"})}
When a customer completes the LinkSDK - please note, no details are shared directly from the SDK to your front-end. A Success, Error or Cancelled message can be returned from the SDK via a callback (depending on the platform you're integrating with, this is handled differently) in the following format:
{"status": "SUCCESS","method": "CREATE_PAYMENT_SOURCE","message": "Customer created Payment Source successfully"}
The details of your newly created entity will instead be sent via a webhook to your backend services. Your front-end should therefore handle the success by making a call to your backend to refresh the data.
When a customer creates a Payment Source using the LinkSDK, your backend will receive two webhooks, one payment_source.created
and one payment_source.beneficiary.created
webhook.
For the purpose of this guide, please respond to the payment_source.beneficiary.created
webhook with a 200 response and ignore it.
The payment_source.created
webhook will look like this:
{"type": "payment_source.created","payload": {"id": "d8d7a9b3-c401-49f6-81a4-386d8398aa2c","customer_id": "6db94cb7-3a02-4a57-a03a-e2a92ce86406","status": "AWAITING_BENEFICIARY_COOL_OFF","bank_identifier": "ENBD_UAE","bank_name" : "Emirates NBD"},"message": "A payment source was preauthorized by your customer.","timestamp": "2020-07-31T07:11:39.862804Z"}
The key component here is the AWAITING_BENEFICIARY_COOL_OFF
status. This status indicates that there is a cool down period before the account is chargeable. If the status is ACTIVE
your customer can make a payment immediately.
Using the id
in the payload from the webhook, fetch the newly created Payment Source and save it to your database:
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'
Returns
{"id": "d8d7a9b3-c401-49f6-81a4-386d8398aa2c","customer_id": "6db94cb7-3a02-4a57-a03a-e2a92ce86406","app_id": "2c9280887230f322017231b408cf0007","bank_identifier": "ENBD_UAE","beneficiaries": [{"id": "26891A75-9406-4E06-97BA-F4D6DA664F3C","payment_destination_id": "266733cc-6f46-4e87-af7e-9bb46b99601d""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": "1015528734001","iban": "AE340260001015528734001","balance": 35681.15,"currency": "AED","balance_last_updated": "2021-02-12T13:20:23.83922Z"}, {"id": "1457d23a-c1f8-4aee-bc6f-2a253b2e617f","account_id": "69f22112-e95d-427e-8bd1-eb58b4db60ba","account_name": "CURRENCY PASSPORT SAVINGS AC","account_number": "0315528734002","iban": "AE790260000315528734002","balance": 80871.00,"currency": "USD","balance_last_updated": "2021-02-12T13:20:57.370659Z"}]}
When a Payment Source switches from AWAITING_BENEFICIARY_COOL_OFF
to ACTIVE
another webhook is sent to your server, notifying you that an update has been made.
You can use this webhook to drive Push Notifications to bring your customer back into your application to complete a purchase or top up an account.
{"type": "payment_source.updated","payload": {"id": "d8d7a9b3-c401-49f6-81a4-386d8398aa2c","customer_id": "6db94cb7-3a02-4a57-a03a-e2a92ce86406","status": "ACTIVE","bank_identifier": "ENBD_UAE","bank_name" : "Emirates NBD"},"message": "A payment source has been updated.","timestamp": "2020-07-31T07:11:39.862804Z"}
Now that your user has a Payment Source - the next step is to create a Payment Intent.
When your user clicks transfer a call to your backend should be initiated expecting a payment_intent_id
as the response.
onClick={() => callMyApi(amount, currency, customer_id, payment_destination_id)}
Call
curl -X POST 'https://sandbox.leantech.me/payments/v1/intents' \--header 'Content-Type: application/json' \--header 'lean-app-token: YOUR_APP_TOKEN' \--data-raw '{"amount": 1000,"currency": "USD","payment_destination_id": "266733cc-6f46-4e87-af7e-9bb46b99601d","customer_id": "CUSTOMER_ID"}'
Response
{"payment_intent_id": "9dafc6f5-37e8-4f72-aa0b-5323c0935c5f","customer_id": "6bc2336a-6d74-4e59-a492-65313423a8f8","amount": 1000.00,"currency": "AED","payments": [],"payment_destination": {"id": "XXXXXX-c568-40d7-8710-3e330dXXXXXX","bank_identifier": "HSBC_UAE","name": "XXXXXX","iban": "AEXXXXXXXXXXXXXXXX","display_name": "XXXXXXX","account_number": "XXXXXXXX","swift_code": "BBMEAEAD","status": "CONFIRMED","address": "1234 1234","country": "ARE","city": "Dubai","default": true}}
You should take the payment_intent_id
received in the response and return back to your front-end to initiate the payment.
Once an initiation has been completed, you will receive a payment.created webhook to your Webhook URL:
{"payload": {"id": "81f686b5-0cf3-4ee1-9250-9f8dae030a1d","customer_id": "e52d8b7e-7c4d-40af-b851-90ab5c061028","intent_id": "88f73a6d-f017-4fc6-85bf-2cc2804c2a4a","status": "ACCEPTED_BY_BANK","amount": 10.12,"currency": "AED"},"type": "payment.created","message": "A payment object has been created.","timestamp": "2021-03-24T12:50:47.521324Z"}
Payment Initiations can have three final statuses on the Lean platform.
REJECTED_BY_BANK / FAILED: The bank rejected the payment initiation. This could be for a number of reasons such as insufficient balance to complete the transaction, or a connectivity failure.
PENDING_WITH_BANK: The bank accepted the initiation, but didn’t provide a transaction reference number. Pending payments may be processed by the bank at a future date and require manual reconciliation.
ACCEPTED_BY_BANK: The bank accepted the initiation and provided a transaction reference number. Transactions that are ACCEPTED_BY_BANK should not be treated as completed payments until the funds have been reconciled in your account. There are a rare number of circumstances when a delay may occur between a bank accepting a payment and the funds reaching your account and/or circumstances which may cause the payment to fail. For example, a bank may request additional KYC checks or there may be insufficient funds available for the transaction after the bank transfer fees are applied. We recommend that funds are reconciled in your account before making them available to the customer.