Quick Start

This guide shows you how to integrate Bank Hub into your app in a few simple steps – from creating tokens and linking them to embedding webview iframes and receiving balance change notifications.


  • Obtain an access_token from the /v1/token API
  • Use the access_token to create a link token
  • Embed the hosted_link_url into an iframe on your website
Before You Start

Make sure you have:

  • client_id and client_secret provided by SePay
  • company_xid (UUID of the company created in the Bank Hub system) – Create Company API
  • A backend server to call APIs (do not call APIs from the client-side for security reasons)

Step 1: Get Access Token

  • First, you need to obtain an access_token to authenticate subsequent API calls. This API uses Basic Authentication with client_id and client_secret.
Security

DO NOT call this API from the client-side (browser or mobile app). This API requires client_secret, which must be strictly protected on the server. Only call this API from your backend server.

  • API Endpoint
POST
https://bankhub-api-sandbox.sepay.vn/v1/token
Authorization: Basic {base64(client_id:client_secret)}
Content-Type: application/x-www-form-urlencoded
  • Code examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
// File: app/Services/BankHubService.php
 
namespace App\Services;
 
use Illuminate\Support\Facades\Http;
 
class BankHubService
{
private $clientId;
private $clientSecret;
private $baseUrl;
 
public function __construct()
{
$this->clientId = config('bankhub.client_id');
$this->clientSecret = config('bankhub.client_secret');
$this->baseUrl = config('bankhub.base_url', 'https://bankhub-api-sandbox.sepay.vn');
}
 
/**
* Get access token from Bank Hub
*/
public function getAccessToken(): ?string
{
$response = Http::withBasicAuth($this->clientId, $this->clientSecret)
->post($this->baseUrl . '/v1/token');
 
if ($response->successful()) {
$data = $response->json();
return $data['access_token'] ?? null;
}
 
throw new \Exception('Failed to get access token: ' . $response->body());
}
}
 
  • Response
RESPONSE 201 - Success
{
  "code": 201,
  "access_token": "36483db493b10304eb3abc143b3593fa1473eb9b",
  "ttl": 60000
}
Token Storage
  • Store the access_token in cache (Redis, Memcached) or session
  • The token has a limited lifetime (ttl), so implement automatic refresh logic
  • When receiving a 401 Unauthorized error, automatically request a new token

After obtaining the access_token, use it to create a link token. The link token provides a hosted_link_url, which will be embedded into an iframe.

  • API Endpoint
POST
https://bankhub-api-sandbox.sepay.vn/v1/link-token/create
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
  • Code examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
// File: app/Services/BankHubService.php (continued)
 
namespace App\Services;
 
use Illuminate\Support\Facades\Http;
 
class BankHubService
{
// ... (getAccessToken code above)
 
/**
* Create link token to link bank account
*/
public function createLinkToken(
string $companyXid,
string $purpose = 'LINK_BANK_ACCOUNT',
?string $redirectUri = null
): array {
$accessToken = $this->getAccessToken();
 
$payload = [
'company_xid' => $companyXid,
'purpose' => $purpose,
];
 
if ($redirectUri) {
$payload['completion_redirect_uri'] = $redirectUri;
}
 
$response = Http::withToken($accessToken)
->post($this->baseUrl . '/v1/link-token/create', $payload);
 
if ($response->successful()) {
return $response->json();
}
 
throw new \Exception('Failed to create link token: ' . $response->body());
}
}
 
// Usage in Controller:
// File: app/Http/Controllers/BankHubController.php
 
namespace App\Http\Controllers;
 
use App\Services\BankHubService;
use Illuminate\Http\Request;
 
class BankHubController extends Controller
{
private $bankHub;
 
public function __construct(BankHubService $bankHub)
{
$this->bankHub = $bankHub;
}
 
public function getLinkUrl(Request $request)
{
$companyXid = $request->user()->company_xid; // From logged in user
$redirectUri = route('bankhub.callback'); // Your callback URL
 
$result = $this->bankHub->createLinkToken(
$companyXid,
'LINK_BANK_ACCOUNT',
$redirectUri
);
 
return response()->json([
'hosted_link_url' => $result['hosted_link_url'],
'expires_at' => $result['expires_at']
]);
}
}
 
Note

The code examples above demonstrate creating a link token for the bank account linking flow. If you need to create an unlink flow, set purpose to UNLINK_BANK_ACCOUNT and provide bank_account_xid.

  • Response
RESPONSE 201 - Created
{
  "xid": "850e8400-e29b-41d4-a716-446655440000",
  "hosted_link_url": "https://bankhub.sepay.vn/link/850e8400-e29b-41d4-a716-446655440000",
  "link_token": "950e8400-e29b-41d4-a716-446655440000",
  "expires_at": "2024-01-17 10:30:00"
}

Step 3: Embed Iframe into Website

After receiving the hosted_link_url from Step 2, you can embed it into your website using an iframe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<!DOCTYPE html>
<html>
<head>
<title>Link Bank Account</title>
<style>
.bankhub-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
 
.bankhub-iframe {
width: 100%;
height: 600px;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="bankhub-container">
<h1>Link Bank Account</h1>
<iframe
id="bankhub-iframe"
class="bankhub-iframe"
src=""
frameborder="0"
allow="clipboard-write"
></iframe>
</div>
 
<script>
// Call backend API to get hosted_link_url
async function loadBankHubLink() {
try {
const response = await fetch('/api/bankhub/get-link-url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userToken}`
}
});
 
const data = await response.json();
document.getElementById('bankhub-iframe').src = data.hosted_link_url;
} catch (error) {
console.error('Error loading Bank Hub link:', error);
alert('Unable to load bank linking interface. Please try again.');
}
}
 
loadBankHubLink();
 
// Listen to events from iframe (postMessage)
window.addEventListener('message', function(event) {
if (event.origin !== 'https://bankhub.sepay.vn') return;
 
const { event: eventType, metadata, timestamp } = event.data;
 
switch(eventType) {
case 'FINISHED_BANK_ACCOUNT_LINK':
console.log('Account linked:', metadata);
alert(`Linked successfully: ${metadata.account_number}`);
window.location.href = '/dashboard';
break;
case 'FINISHED_BANK_ACCOUNT_UNLINK':
console.log('Account unlinked');
alert('Unlinked successfully!');
window.location.href = '/dashboard';
break;
case 'BANKHUB_CLOSE_LINK':
console.log('User closed linking flow');
break;
case 'BANKHUB_TOKEN_EXPIRED':
console.warn('Token expired');
alert('Session expired. Please try again.');
break;
case 'BANKHUB_SESSION_EXPIRED':
console.warn('Session expired');
alert('Session expired. Please try again.');
break;
}
});
</script>
</body>
</html>
Information

If you provided completion_redirect_uri when creating the link token, users will be redirected to that URL after completion.


PostMessage Events

The iframe sends events via window.postMessage in the following format:

event format
{
  "event": "FINISHED_BANK_ACCOUNT_LINK | FINISHED_BANK_ACCOUNT_UNLINK | BANKHUB_CLOSE_LINK | BANKHUB_TOKEN_EXPIRED | BANKHUB_SESSION_EXPIRED",
  "metadata": {
    "account_number": "string",
    "account_type": "individual | enterprise"
  },
  "timestamp": "string"
}

Event types:

FINISHED_BANK_ACCOUNT_LINK

The bank account has been successfully linked. Metadata contains account information.

FINISHED_BANK_ACCOUNT_UNLINK

The bank account has been successfully unlinked.

BANKHUB_CLOSE_LINK

The user closes or cancels the linking flow.

BANKHUB_TOKEN_EXPIRED

The link token has expired and a new token must be created.

BANKHUB_SESSION_EXPIRED

The session has expired and must be re-initialized.


Configure Balance Change Notifications (IPN)

➤ You can view the details here