157 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
from decimal import Decimal
import os
from dotenv import load_dotenv
from flask import Flask, request, jsonify
import subprocess
import json
from datetime import datetime, timedelta
load_dotenv() # This will load environment variables from a .env file if it exists
TIME_DIFF_LIMIT = int(os.getenv('TIME_DIFF_LIMIT', 2)) # Default to 2mins if not set
app = Flask(__name__)
def fetch_account_name(account_no):
try:
result = subprocess.run(['./fetchname.sh', account_no], capture_output=True, text=True, check=True)
response = json.loads(result.stdout)
if response.get('success'):
return response.get('accountName')
except (subprocess.CalledProcessError, json.JSONDecodeError) as e:
app.logger.error(f"Error fetching account name: {str(e)}")
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):
for tx_data in tx_data_list:
required_keys = ['trxDate', 'benefName', 'absAmount']
if not all(key in tx_data for key in required_keys):
continue # Skip this transaction if it's missing required keys
try:
tx_time = datetime.strptime(tx_data['trxDate'], "%Y-%m-%d %H:%M:%S")
time_diff = abs(tx_time - request_time)
tx_benef_name = tx_data['benefName'].strip().lower()
if (tx_benef_name == benef_name.strip().lower() and
compare_amounts(tx_data['absAmount'], abs_amount) and
time_diff <= timedelta(minutes=TIME_DIFF_LIMIT)):
return True, tx_data
except ValueError as e:
app.logger.error(f"Error processing transaction: {str(e)}")
return False, None
def compare_amounts(amount1, amount2):
"""Compare two amount strings as Decimal objects."""
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'])
def verify_payment():
try:
if not request.is_json:
return jsonify({"success": False, "message": "Request must be JSON"}), 400
data = request.get_json()
if data is None:
return jsonify({"success": False, "message": "Invalid JSON format"}), 400
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 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:
request_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M")
except ValueError:
return jsonify({"success": False, "message": "Invalid time format"}), 400
try:
result = subprocess.run(['./tx.sh'], capture_output=True, text=True, check=True)
tx_response = json.loads(result.stdout)
app.logger.debug(f"tx_response: {json.dumps(tx_response, indent=2)}")
if not tx_response.get('success'):
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__':
debug_mode = os.getenv('APP_DEBUG', 'False').lower() in ('true', '1', 't')
port = int(os.getenv('PORT', 5000))
app.run(host='0.0.0.0', port=port, debug=debug_mode)