Compare commits

..

29 Commits

Author SHA1 Message Date
a97d52bc59 update user agent
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 47s
2025-01-10 09:31:04 +05:00
80eb74c65c add jq to keep alive
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 1m20s
2025-01-10 08:24:12 +05:00
470df69c56 auto build
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 1m50s
2025-01-10 08:19:47 +05:00
c2ab2ea996 add the cookie server script to docker.... 2025-01-10 08:18:24 +05:00
37dfe7e261 update docs 2025-01-10 08:10:59 +05:00
a72f22b9a4 build cookie server 2025-01-10 07:59:41 +05:00
0e60f139ef added support for cookie server 2025-01-10 07:52:09 +05:00
618fc9371d no more cookie hunting, totp generated automatically 2025-01-10 07:38:28 +05:00
9decf2ed2b no more cookie hunting 2025-01-10 07:37:41 +05:00
ee57150807 Add support for profile selection 2025-01-10 07:36:06 +05:00
1264d92a04 cookie api works, but need to select profile 2025-01-10 06:35:58 +05:00
03ee1dec32 print json, nicely ask mib ismath to give me real api 2025-01-10 05:48:44 +05:00
e0684cb11e get cookie automatically 2025-01-10 05:26:58 +05:00
ab64ce071e dont know how jq got removed 2024-12-20 16:56:03 +05:00
af0a122d2b ignore negative transacations 2024-12-20 16:51:33 +05:00
244f63e80d noooooooo how did this happen 2024-12-09 10:05:41 +05:00
95235e39e8 f 2024-12-09 09:13:02 +05:00
f96f787861 til that there is a tab for cookies 2024-12-09 09:12:10 +05:00
bfe7238877 docs update - better errors 2024-12-09 08:59:48 +05:00
61384f39bf update docs 2024-12-09 08:58:32 +05:00
5fa2bf780d stop giving false json 2024-12-09 08:45:34 +05:00
867b32e45e update docs for cleaner transaction output 2024-12-09 08:25:51 +05:00
c91a4c06fd clean up transaction output 2024-12-09 08:09:22 +05:00
155e56d7b6 added date time to keepalive 2024-12-09 00:37:19 +05:00
203319aba9 add detailed tx info to output 2024-12-09 00:19:00 +05:00
098e268001 custom sleep timer for keep alinve 2024-12-08 23:52:18 +05:00
3dd6680a9f docs update 2024-10-22 02:03:51 +05:00
bc897f90bd docs update 2024-10-22 01:53:50 +05:00
2b05b9100c docs update 2024-10-22 01:52:46 +05:00
16 changed files with 407 additions and 75 deletions

View File

@ -1,7 +1,7 @@
FROM python:3.9.20-slim-bookworm FROM python:3.9.20-slim-bookworm
RUN pip install python-dotenv flask gunicorn RUN pip install python-dotenv flask gunicorn
RUN apt-get update && apt-get install -y curl && apt-get clean && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y curl jq && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
COPY api.py tx.sh fetchname.sh . COPY api.py tx.sh fetchname.sh .

View File

@ -9,3 +9,8 @@ services:
context: ../../ context: ../../
dockerfile: .build/prod/api.Dockerfile dockerfile: .build/prod/api.Dockerfile
image: git.shihaam.dev/shihaam/mib-payment-verify/api image: git.shihaam.dev/shihaam/mib-payment-verify/api
cookieserver:
build:
context: ../../
dockerfile: .build/prod/cookieserver.Dockerfile
image: git.shihaam.dev/shihaam/mib-payment-verify/cookieserver

View File

@ -0,0 +1,10 @@
FROM python:3.9.20-slim-bookworm
RUN pip install beautifulsoup4 pyotp Flask python-dotenv requests
RUN apt-get update && apt-get install -y curl jq && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY getcookie.py .
CMD python getcookie.py

View File

@ -1,6 +1,6 @@
FROM debian:bullseye-slim FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y curl && apt-get clean && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y curl jq && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
COPY keepalive.sh . COPY keepalive.sh .

View File

@ -2,11 +2,16 @@ USERNAME=
PASSWORD= PASSWORD=
TOTP_SEED= TOTP_SEED=
PROFILE_ID=
PROFILE_TYPE=
ACCOUNT_NUMBER= ACCOUNT_NUMBER=
#Cookie COOKIE_SERVER=http://cookieserver:5000
QL_0=
IBSID=
APP_DEBUG=true APP_DEBUG=true
TIME_DIFF_LIMIT=1
## This valu is for time difference between request json and actual time of the transacation (+ or -) in mins, 5 or 10 is probably a good value
TIME_DIFF_LIMIT=7
## Sleep is the delay between keepalive request for extending sesssion, somewhere between 30 and 150 should be ok
SLEEP=45

View File

@ -0,0 +1,37 @@
name: Build and Push Docker Images
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
docker:
name: Build and Push Docker Images
runs-on: builder
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Build Docker images
working-directory: .build/prod
run: docker compose build
- name: Login to Docker Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ vars.DOCKER_REGISTRY_URL }}
username: ${{ vars.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
- name: Push Docker images
if: github.event_name != 'pull_request'
working-directory: .build/prod
run: docker compose push

View File

@ -1,23 +1,27 @@
### Maldives Islamic Bank Payment Verification API ### Maldives Islamic Bank Payment Verification API
This api will allow you to match customers payment and your MIB account history, it will match with transaction time, account owner name and amount. \ - This api will allow you to match customers payment and your MIB account history, it will match with transaction time, account owner name and amount.
but slight issue is that, MIB to MIB transaction sometimes have MIBs made-up name for the account owner name, This is challenging, so work around that, \ - Issue with using account number name is that, MIB to MIB transaction sometimes have MIBs made-up name for the account owner name, This is challenging, so work around that, I added option to send customers account number to fetch the made-up name and match with it.
I added option to send customers account number to fetch the made-up name and match with it. \ - BML (Farava) transations works perfectly with just the name!
(BML) Farava transations works perfectly with just the name! \ - It uses cookie from webssion to fetch everything, it also has a keepalive service so websession does not expire after 5mins.
It uses cookie from webssion to fetch everything, it also has a keepalive service so websession does not expire after 5mins.
## How to deploy/get started (with docker/podman) ## How to deploy/get started (with docker/podman)
1. Login to MIB web [https://faisanet.mib.com.mv/](https://faisanet.mib.com.mv/) and get the cookie: 1. Login to MIB web [https://faisanet.mib.com.mv/](https://faisanet.mib.com.mv/) and get the your profile id and type:
![get_cookie_env.png](get_cookie_env.png) ![select_profile.jpg](select_profile.jpg)
2. copy .env.example to .env and fill `QL_0` and `IBSID` with contents from step 1, Make sure to fill `ACCOUNT_NUMBER` with your account number. 2. copy .env.example to .env and fill your MIB Username, Password and TOTP Seed, `PROFILE_ID` and `PROFILE_TYPE` needs to be filled with contents from step 1, Make sure to fill `ACCOUNT_NUMBER` with your account number.
3. Create `compose.yml` with the follwing contents: 3. Create `compose.yml` with the follwing contents:
```yaml ```yaml
services: services:
keepalive: keepalive:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive
cookieserver:
env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/cookieserver
api: api:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/api image: git.shihaam.dev/shihaam/mib-payment-verify/api
@ -25,7 +29,7 @@ services:
- 5000:5000 - 5000:5000
``` ```
3. Run `docker compose up -d` and that is all. 4. Run `docker compose up -d` and that is all.
## Using the API with curl examples: ## Using the API with curl examples:
@ -69,17 +73,27 @@ curl -X POST http://localhost:5000/verify-payment \
```json ```json
{ {
"message": "Payment verified using beneficiary name", "message": "Payment verified using beneficiary name",
"success": true "success": true,
"transaction": {
"ref": "MALBIPS20241208190231 HBTLGAKCNUS",
"sourceBank": "BML",
"trxDate": "2024-12-09 00:02:36"
}
} }
``` ```
2. Both benefName and accountNo or only accountNo provided, but failed with benefName 2. Both benefName and accountNo or only accountNo provided, but failed with benefName
```json ```json
{ {
"message": "Payment verified using account number", "message": "Payment verified using account number",
"success": true "success": true,
"transaction": {
"ref": "1-77722758-34140519-1",
"sourceBank": "MIB",
"trxDate": "2024-12-05 12:57:32"
}
} }
``` ```
## Example Failed response: ## Example Failed response:
1. If transaction is not found with the provided details. 1. If transaction is not found with the provided details.
```json ```json
@ -88,3 +102,17 @@ curl -X POST http://localhost:5000/verify-payment \
"success": false "success": false
} }
``` ```
2. If session times out
```json
{
"message": "Error from tx.sh: Session Expired",
"success": false
}
```
3. If 500 from server
```json
{
"message": "Internal server error"
"success": false,
}
```

133
api.py
View File

@ -23,6 +23,28 @@ def fetch_account_name(account_no):
app.logger.error(f"Error fetching account name: {str(e)}") app.logger.error(f"Error fetching account name: {str(e)}")
return None return None
def get_transaction_details(tx_data):
"""Extract reference and sourceBank based on transaction type"""
descr1 = tx_data.get('descr1', '')
if descr1 == "Favara Credit":
reference = tx_data.get('descr2', '')
# Extract sourceBank from descr3 (part before the first '-')
descr3 = tx_data.get('descr3', '')
sourceBank = descr3.split(' - ')[0] if ' - ' in descr3 else ''
elif descr1 == "IB Acc to Acc":
reference = tx_data.get('trxNumber', '')
sourceBank = "MIB"
else:
reference = tx_data.get('trxNumber', '')
sourceBank = ""
return {
"ref": reference,
"trxDate": tx_data.get('trxDate', ''),
"sourceBank": sourceBank
}
def verify_transaction(benef_name, abs_amount, request_time, tx_data_list): def verify_transaction(benef_name, abs_amount, request_time, tx_data_list):
for tx_data in tx_data_list: for tx_data in tx_data_list:
required_keys = ['trxDate', 'benefName', 'absAmount'] required_keys = ['trxDate', 'benefName', 'absAmount']
@ -38,64 +60,95 @@ def verify_transaction(benef_name, abs_amount, request_time, tx_data_list):
if (tx_benef_name == benef_name.strip().lower() and if (tx_benef_name == benef_name.strip().lower() and
compare_amounts(tx_data['absAmount'], abs_amount) and compare_amounts(tx_data['absAmount'], abs_amount) and
time_diff <= timedelta(minutes=TIME_DIFF_LIMIT)): time_diff <= timedelta(minutes=TIME_DIFF_LIMIT)):
return True return True, tx_data
except ValueError as e: except ValueError as e:
app.logger.error(f"Error processing transaction: {str(e)}") app.logger.error(f"Error processing transaction: {str(e)}")
return False return False, None
def compare_amounts(amount1, amount2): def compare_amounts(amount1, amount2):
"""Compare two amount strings as Decimal objects.""" """Compare two amount strings as Decimal objects."""
return Decimal(amount1) == Decimal(amount2) return Decimal(amount1) == Decimal(amount2)
@app.errorhandler(400)
def bad_request(error):
return jsonify({"success": False, "message": "Invalid request: Malformed JSON"}), 400
@app.errorhandler(500)
def internal_error(error):
return jsonify({"success": False, "message": "Internal server error"}), 500
@app.route('/verify-payment', methods=['POST']) @app.route('/verify-payment', methods=['POST'])
def verify_payment(): def verify_payment():
data = request.json
benef_name = data.get('benefName', '').strip()
account_no = data.get('accountNo')
abs_amount = data.get('absAmount')
time_str = data.get('time')
if not all([abs_amount, time_str]):
return jsonify({"success": False, "message": "Missing required parameters"}), 400
if not benef_name and not account_no:
return jsonify({"success": False, "message": "Either benefName or accountNo must be provided"}), 400
try: try:
request_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M") if not request.is_json:
except ValueError: return jsonify({"success": False, "message": "Request must be JSON"}), 400
return jsonify({"success": False, "message": "Invalid time format"}), 400
try: data = request.get_json()
result = subprocess.run(['./tx.sh'], capture_output=True, text=True, check=True) if data is None:
tx_response = json.loads(result.stdout) return jsonify({"success": False, "message": "Invalid JSON format"}), 400
app.logger.debug(f"tx_response: {json.dumps(tx_response, indent=2)}") benef_name = data.get('benefName', '').strip() if data.get('benefName') else ''
account_no = data.get('accountNo')
abs_amount = data.get('absAmount')
time_str = data.get('time')
if not tx_response.get('success'): if not all([abs_amount, time_str]):
return jsonify({"success": False, "message": f"Error from tx.sh: {tx_response.get('reasonText', 'Unknown error')}"}), 500 return jsonify({"success": False, "message": "Missing required parameters"}), 400
tx_data_list = tx_response.get('data', []) if not benef_name and not account_no:
if not tx_data_list: return jsonify({"success": False, "message": "Either benefName or accountNo must be provided"}), 400
return jsonify({"success": False, "message": "No transaction data found"}), 404
except subprocess.CalledProcessError as e: try:
return jsonify({"success": False, "message": f"Error executing tx.sh: {str(e)}", "stderr": e.stderr}), 500 request_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M")
except json.JSONDecodeError as e: except ValueError:
return jsonify({"success": False, "message": f"Error parsing tx.sh output: {str(e)}", "output": result.stdout}), 500 return jsonify({"success": False, "message": "Invalid time format"}), 400
# First, try to verify with benefName if provided try:
if benef_name and verify_transaction(benef_name, abs_amount, request_time, tx_data_list): result = subprocess.run(['./tx.sh'], capture_output=True, text=True, check=True)
return jsonify({"success": True, "message": "Payment verified using beneficiary name"}) tx_response = json.loads(result.stdout)
# If benefName verification failed or wasn't provided, try with accountNo app.logger.debug(f"tx_response: {json.dumps(tx_response, indent=2)}")
if account_no:
fetched_name = fetch_account_name(account_no)
if fetched_name and verify_transaction(fetched_name, abs_amount, request_time, tx_data_list):
return jsonify({"success": True, "message": "Payment verified using account number"})
# If both verifications fail if not tx_response.get('success'):
return jsonify({"success": False, "message": "Transaction not found, contact support"}) return jsonify({"success": False, "message": f"Error from tx.sh: {tx_response.get('reasonText', 'Unknown error')}"}), 500
tx_data_list = tx_response.get('data', [])
if not tx_data_list:
return jsonify({"success": False, "message": "No transaction data found"}), 404
except subprocess.CalledProcessError as e:
return jsonify({"success": False, "message": f"Error executing tx.sh: {str(e)}"}), 500
except json.JSONDecodeError as e:
return jsonify({"success": False, "message": f"Error parsing tx.sh output: {str(e)}"}), 500
# First, try to verify with benefName if provided
if benef_name:
verified, tx_data = verify_transaction(benef_name, abs_amount, request_time, tx_data_list)
if verified:
return jsonify({
"success": True,
"message": "Payment verified using beneficiary name",
"transaction": get_transaction_details(tx_data)
})
# If benefName verification failed or wasn't provided, try with accountNo
if account_no:
fetched_name = fetch_account_name(account_no)
if fetched_name:
verified, tx_data = verify_transaction(fetched_name, abs_amount, request_time, tx_data_list)
if verified:
return jsonify({
"success": True,
"message": "Payment verified using account number",
"transaction": get_transaction_details(tx_data)
})
# If both verifications fail
return jsonify({"success": False, "message": "Transaction not found, contact support"})
except Exception as e:
app.logger.error(f"Unexpected error: {str(e)}")
return jsonify({"success": False, "message": "Internal server error"}), 500
if __name__ == '__main__': if __name__ == '__main__':
debug_mode = os.getenv('APP_DEBUG', 'False').lower() in ('true', '1', 't') debug_mode = os.getenv('APP_DEBUG', 'False').lower() in ('true', '1', 't')

View File

@ -2,6 +2,9 @@ services:
keepalive: keepalive:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive
cookieserver:
env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/cookieserver
api: api:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/api image: git.shihaam.dev/shihaam/mib-payment-verify/api

View File

@ -4,6 +4,10 @@ source .env 2> /dev/null
accountNo=$1 accountNo=$1
COOKIE=$(curl -s $COOKIE_SERVER/getcookie)
IBSID=$(echo $COOKIE | jq -r .IBSID)
QL_0=$(echo $COOKIE | jq -r .ql_0)
curl -s 'https://faisanet.mib.com.mv/ajaxBeneficiary/getAccountName' \ curl -s 'https://faisanet.mib.com.mv/ajaxBeneficiary/getAccountName' \
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \ -H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \
--data-raw "accountNo=${accountNo}" --data-raw "accountNo=${accountNo}"

View File

@ -1,6 +0,0 @@
import pyotp
import sys
seed = sys.argv[1]
totp = pyotp.TOTP(seed)
print(totp.now())

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

158
getcookie.py Normal file
View File

@ -0,0 +1,158 @@
from flask import Flask, jsonify, request
import os
import pyotp
import time
from urllib.parse import urlencode
from bs4 import BeautifulSoup
import requests
from dotenv import load_dotenv
class MIBLogin:
def __init__(self):
self.base_url = "https://faisanet.mib.com.mv"
self.headers = {
"User-Agent": "Mozilla/5.0 (ismath-said-will) give-real-api/so-i-wont-have-to-do-this AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36",
"Accept": "*/*"
}
self.last_cookie = None
self.last_renewal_time = 0
self.create_new_session()
def create_new_session(self):
self.session = requests.Session()
def get_login_page(self):
response = self.session.get(f"{self.base_url}/auth", headers=self.headers)
soup = BeautifulSoup(response.text, 'html.parser')
rtag_input = soup.find('input', {'name': 'rTag'})
if not rtag_input:
raise ValueError("Failed to find rTag input field in login page.")
return rtag_input['value']
def get_auth_type(self, rtag, username, retain=1):
data = {'rTag': rtag, 'pgf01': username, 'retain': retain}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
response = self.session.post(
f"{self.base_url}/aAuth/getAuthType",
headers=headers,
data=urlencode(data)
)
return response.json()
def login(self, username, password, retain=1):
rtag = self.get_login_page()
self.get_auth_type(rtag, username, retain)
data = {'rTag': rtag, 'pgf01': username, 'pgf02': password, 'retain': retain}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
self.session.post(
f"{self.base_url}/aAuth",
headers=headers,
data=urlencode(data)
)
def auth_2fa(self, totp_seed):
self.session.get(f"{self.base_url}/auth2FA", headers=self.headers)
totp = pyotp.TOTP(totp_seed)
data = {'otpType': 3, 'otp': totp.now()}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
self.session.post(
f"{self.base_url}/aAuth2FA/verifyOTP",
headers=headers,
data=urlencode(data)
)
cookies = {
cookie.name: cookie.value
for cookie in self.session.cookies
if cookie.name in ['IBSID', 'ql_0']
}
self.last_cookie = cookies
self.last_renewal_time = time.time()
return cookies
def get_profile_rtag(self):
url = f"{self.base_url}/profiles"
response = self.session.get(url, headers=self.headers)
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
profile_card = soup.find('div', {'class': 'card profile-card smooth smooth-shadow'})
if profile_card:
return profile_card.get('data-rt')
else:
raise ValueError("Failed to fetch profile rTag.")
else:
raise ValueError(f"Failed to retrieve profiles page. Status code: {response.status_code}")
def switch_profile(self, profile_id, profile_type):
rtag = self.get_profile_rtag()
url = f"{self.base_url}/aProfileHandler/switchProfile"
data = {'rTag': rtag, 'profileId': profile_id, 'profileType': profile_type}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
response = self.session.post(url, headers=headers, data=urlencode(data))
if response.status_code == 200:
result = response.json()
if result.get("success"):
print("Profile switched successfully.")
else:
raise ValueError("Failed to switch profile: " + result.get("reasonText", "Unknown error"))
else:
raise ValueError(f"Profile switch failed with status code: {response.status_code}")
def clear_cookie(self):
self.last_cookie = None
self.create_new_session()
# Initialize Flask app
app = Flask(__name__)
mib = MIBLogin()
load_dotenv()
def renew_cookie_internal():
try:
print(f"Starting cookie renewal process...")
mib.clear_cookie()
mib.login(os.getenv('USERNAME'), os.getenv('PASSWORD'))
cookies = mib.auth_2fa(os.getenv('TOTP_SEED'))
# Profile selection after successful login and 2FA
mib.switch_profile(os.getenv('PROFILE_ID'), os.getenv('PROFILE_TYPE'))
print(f"Cookie renewal and profile switch completed successfully")
return cookies
except Exception as e:
print(f"Error during cookie renewal: {str(e)}")
return {"error": str(e)}
@app.route('/getcookie', methods=['GET'])
def get_cookie():
client_ip = request.remote_addr
print(f"Providing cookie to: {client_ip}")
if mib.last_cookie:
return jsonify(mib.last_cookie)
else:
cookies = renew_cookie_internal()
if "error" in cookies:
return jsonify(cookies), 500
return jsonify(cookies)
@app.route('/newcookie', methods=['GET'])
def new_cookie():
client_ip = request.remote_addr
current_time = time.time()
# Check if less than 60 seconds since last renewal
if current_time - mib.last_renewal_time < 60:
print(f"Providing existing cookie to {client_ip} (last renewal was {int(current_time - mib.last_renewal_time)} seconds ago)")
return jsonify(mib.last_cookie)
print(f"Renewing cookie as requested by: {client_ip}")
cookies = renew_cookie_internal()
if "error" in cookies:
return jsonify(cookies), 500
return jsonify(cookies)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@ -1,10 +1,40 @@
#!/bin/bash #!/bin/bash
request_new_cookie(){
curl -s $COOKIE_SERVER/newcookie > /dev/null
}
while true; do while true; do
source .env 2> /dev/null source .env 2> /dev/null
KEEP_ALIVE=$(curl -s 'https://faisanet.mib.com.mv/aProfile/keepAlive' \
-X 'POST' \ # Get current date and time
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}") CURRENT_TIME=$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')
echo $KEEP_ALIVE
sleep 300 COOKIE=$(curl -s $COOKIE_SERVER/getcookie)
IBSID=$(echo $COOKIE | jq -r .IBSID)
QL_0=$(echo $COOKIE | jq -r .ql_0)
# Make the keep-alive request
KEEP_ALIVE=$(curl -s 'https://faisanet.mib.com.mv/aProfile/keepAlive' \
-X 'POST' \
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}")
# Check for session expiration
SUCCESS=$(echo "$KEEP_ALIVE" | jq -r .success)
RESPONSE_CODE=$(echo "$KEEP_ALIVE" | jq -r .responseCode)
if [[ "$SUCCESS" == "false" && "$RESPONSE_CODE" == "3" ]]; then
echo "Session expired. Requesting new cookie..."
request_new_cookie
continue # Restart the loop
fi
# Remove the last closing brace from KEEP_ALIVE
KEEP_ALIVE_MODIFIED=${KEEP_ALIVE%?}
# Combine timestamp with the keep-alive response
echo "$KEEP_ALIVE_MODIFIED, \"timestamp\":\"$CURRENT_TIME\"}"
# Sleep for the specified duration
sleep $SLEEP
done done

BIN
select_profile.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

7
tx.sh
View File

@ -2,7 +2,12 @@
source .env 2> /dev/null source .env 2> /dev/null
COOKIE=$(curl -s $COOKIE_SERVER/getcookie)
IBSID=$(echo $COOKIE | jq -r .IBSID)
QL_0=$(echo $COOKIE | jq -r .ql_0)
curl 'https://faisanet.mib.com.mv/ajaxAccounts/trxHistory' \ curl 'https://faisanet.mib.com.mv/ajaxAccounts/trxHistory' \
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \ -H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \
--data-raw "accountNo=${ACCOUNT_NUMBER}&trxNo=&trxType=0&sortTrx=date&sortDir=desc&fromDate=&toDate=&start=1&end=20&includeCount=1" -s \ --data-raw "accountNo=${ACCOUNT_NUMBER}&trxNo=&trxType=0&sortTrx=date&sortDir=desc&fromDate=&toDate=&start=1&end=20&includeCount=1" -s \
| sed 's/"benefName":"BML - /"benefName":"/g' | sed 's/"benefName":"BML - /"benefName":"/g' \
| jq '.data |= map(select(.baseAmount | tonumber > 0))'