Commit f4d06669 by aa.gusti

new version

1 parent 4c2697ca
......@@ -253,7 +253,7 @@ Buat tabel terkait web service::
Untuk mengarahkan web service path ``/rpc`` ke section ``[host_kota_bogor]``
maka::
$ ~/env/bin/iso8583_set_conf test-bjb-h2h.ini --nama="rpc to iso8583" --value="kota_bogor"
$ ~/env/bin/iso8583_set_conf test-bjb-h2h.ini --nama="rpc to iso8583" --nilai="kota_bogor"
Sekarang jalankan daemon::
......
......@@ -54,6 +54,7 @@ def main(global_config, **settings):
config.add_notfound_view(RemoveSlashNotFoundViewFactory())
config.add_translation_dirs('locale')
config.registry['mailer'] = mailer_factory_from_settings(settings)
config.add_jsonrpc_endpoint('rpc', '/rpc')
config.scan()
start_iso8583(global_config['__file__'])
return config.make_wsgi_app()
......@@ -94,6 +94,7 @@ class BaseIsoView(BaseView):
# Get ISO8583 connection
def get_connection(self):
conf = self.get_iso_conf()
found_conn = None
for ip_port, conn in conn_mgr:
ip, port = ip_port.split(':')
......
import json
from iso8583_web.models.meta import Base
from sqlalchemy import (
Column,
Integer,
String,
Text,
)
from sqlalchemy.ext.declarative import declarative_base
# Base = declarative_base()
class Conf(Base):
__tablename__ = 'conf'
id = Column(Integer, primary_key=True)
nama = Column(String(100), unique=True)
nilai = Column(Text)
keterangan = Column(Text)
def as_boolean(self):
return self.nilai == 'true'
def as_int(self):
return int(self.nilai)
def as_float(self):
return float(self.nilai)
def as_list(self):
return self.nilai.split()
def as_dict(self):
return json.loads(self.nilai)
class QConf:
def __init__(self, db_session):
self.db_session = db_session
def get(self, key):
q = self.db_session.query(Conf).filter_by(nama=key)
return q.first()
def get_value(self, key):
row = self.get(key)
return row and row.nilai
from iso8583_web.models.meta import Base
from sqlalchemy import (
Column,
Integer,
String,
Text, DateTime, ForeignKey, func, JSON,
)
class Log(Base):
__tablename__ = 'log_iso'
id = Column(Integer, primary_key=True)
created = Column(
DateTime(timezone=True), nullable=False, server_default=func.now())
rpc_id = Column(Integer)
ip = Column(String(15))
conf_name = Column(String(16), nullable=False)
mti = Column(String(4), nullable=False)
bit_002 = Column(String(99))
bit_003 = Column(String(6))
bit_004 = Column(String(12))
bit_007 = Column(String(10))
bit_011 = Column(String(6))
bit_012 = Column(String(6))
bit_013 = Column(String(4))
bit_015 = Column(String(4))
bit_018 = Column(String(4))
bit_022 = Column(String(3))
bit_032 = Column(String(4))
bit_033 = Column(String(10))
bit_035 = Column(String(99))
bit_037 = Column(String(12))
bit_039 = Column(String(2))
bit_041 = Column(String(8))
bit_042 = Column(String(15))
bit_043 = Column(String(40))
bit_047 = Column(String(99))
bit_048 = Column(String(99))
bit_049 = Column(String(3))
bit_059 = Column(String(16))
bit_060 = Column(String(3))
bit_061 = Column(String(22))
bit_062 = Column(String(512))
bit_063 = Column(String(255))
bit_102 = Column(String(32))
bit_107 = Column(String(8))
bit_062_data = Column(JSON)
error = Column(Text)
nama,nilai,keterangan
linkaja allowed ip,127.0.0.1 10.8.50.22 10.14.0.203 10.11.0.118 10.11.0.35 202.43.164.162,list lebih dari satu pisahkan dengan spasi. 0.0.0.0 berarti boleh semua.
linkaja pbb prefix id 3271,PBB Kota Bogor
linkaja pbb prefix id 3275,PBB Kota Bekasi
linkaja pbb prefix id 3278,PBB Kota Depok
linkaja pbb prefix id 3619,PBB Kabupaten Tangerang
linkaja pbb prefix id 3675,PBB Kota Tangerang
linkaja pbb to iso8583,bjb,Route ke section [host_bjb] di file konfigurasi
linkaja sambat aggregator code,W0227
linkaja sambat bank base url,http://localhost:8080/sam-core-app2/rest/SAMWebService
linkaja sambat bank name,bjb,Untuk field linkaja_bank.conf_name
linkaja sambat ca code,CA1864
rpc allowed ip,0.0.0.0
rpc to iso8583,kabupaten_cirebon_payment_point,Route ke section [host_kabupaten_cirebon_payment_point] di file konfigurasi
......@@ -26,6 +26,9 @@ from ..models.ziggurat import (
UserGroup,
User,
)
from ..models.conf import Conf
from ..models.log_iso import Log
from ..models.meta import (
metadata,
Base,
......@@ -143,6 +146,7 @@ def setup_models(dbsession):
UserService.set_password(user, password)
append_csv(Group, 'groups.csv', ['group_name'])
restore_csv(UserGroup, 'users_groups.csv')
append_csv(Conf, 'conf.csv', ['nama'])
def parse_args(argv):
......
import sys
from argparse import ArgumentParser
import transaction
from pyramid.paster import get_appsettings
from iso8583_web.models import (
get_engine,
get_session_factory,
get_tm_session,
)
from ..models.conf import Conf
def parse_args(argv):
parser = ArgumentParser()
parser.add_argument('conf', help='File konfigurasi')
parser.add_argument('--nama')
parser.add_argument('--nilai')
return parser.parse_args(argv)
def out(s):
print(s)
sys.exit()
def main(argv=sys.argv[1:]):
args = parse_args(argv)
settings = get_appsettings(args.conf)
engine = get_engine(settings)
session_factory = get_session_factory(engine)
with transaction.manager:
db_session = get_tm_session(session_factory, transaction.manager)
if not args.nama:
q = db_session.query(Conf).order_by(Conf.nama)
for row in q:
print(f'"{row.nama}" = "{row.nilai}"')
sys.exit()
q = db_session.query(Conf).filter_by(nama=args.nama)
row = q.first()
if not row:
out(f'Tidak ditemukan')
if not args.nilai:
out(f'"{args.nama}" = "{row.nilai}"')
if row.nilai == args.nilai:
out(f'Masih sama')
old = row.nilai
row.nilai = args.nilai
db_session.add(row)
print(f'Konfigurasi telah diubah')
print(f'dari "{old}"')
print(f'menjadi "{args.nilai}"')
import sys
import os
import requests
import json
from datetime import datetime
from random import randrange
from time import (
sleep,
time,
)
from threading import Thread
from argparse import ArgumentParser
headers = {'content-type': 'application/json'}
threads = dict()
end_threads = list()
durations = dict()
json_responses = dict()
server_info = dict()
default_url = 'http://localhost:7000/rpc'
default_count = 1
default_delay = 0.1
help_url = 'default ' + default_url
help_count = 'default {}'.format(default_count)
help_delay = 'default {} detik saat --count'.format(default_delay)
help_invoice_id = 'wajib saat --payment dan --reversal'
help_amount = 'wajib saat --payment dan --reversal'
help_ntb = 'opsional saat --payment, wajib saat --reversal'
help_stan = 'opsional saat --payment, wajib saat --reversal'
help_bit = 'bit tambahan, contoh: --bit=42:TOKOPEDIA'
help_conf = 'konfigurasi tambahan, contoh untuk multi: --conf=pajak:bphtb'
def error(s):
print('ERROR: {}'.format(s))
sys.exit()
def log_info(s):
t = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
t = t[:-3]
msg = '{} {}'.format(t, s)
print(msg)
def get_option(argv):
parser = ArgumentParser()
parser.add_argument('--url', default=default_url, help=help_url)
parser.add_argument(
'--count', type=int, default=default_count, help=help_count)
parser.add_argument(
'--delay', type=float, default=default_delay, help=help_delay)
parser.add_argument('--invoice-id', help=help_invoice_id)
parser.add_argument('--payment', action='store_true')
parser.add_argument('--reversal', action='store_true')
parser.add_argument('--amount', type=int, help=help_amount)
parser.add_argument('--ntb', help=help_ntb)
parser.add_argument('--stan', type=int, help=help_stan)
parser.add_argument('--bit', help=help_bit)
parser.add_argument('--conf', help=help_conf)
return parser.parse_args(argv)
def send(p):
url = server_info['url']
key = p['id']
log_info('Request: {}'.format(p))
start = time()
try:
resp = requests.post(url, data=json.dumps(p), headers=headers)
durations[key] = time() - start
if resp.status_code == 200:
json_resp = resp.json()
log_info('Response: {}'.format(json_resp))
json_responses[key] = json_resp
else:
log_info('Status Code: {}'.format(resp.status_code))
log_info('Body: {}'.format([resp.text]))
json_responses[key] = dict(fatal=resp.text)
except requests.exceptions.ConnectionError as e:
durations[key] = time() - start
log_info('Response: {}'.format(e))
json_responses[key] = dict(fatal=e)
except json.decoder.JSONDecodeError as e:
durations[key] = time() - start
log_info('Body: {}'.format([resp.text]))
log_info('Response: {}'.format(e))
json_responses[key] = dict(fatal=e)
finally:
end_threads.append(key)
def stan_from_result(result):
if result['code'] == 0:
return result['data']['stan']
return '-'
def show_errors(errors):
if errors:
for err, count in errors.items():
log_info('{} {}'.format(err, count))
else:
log_info('Tidak ada yang gagal')
def show_durations():
key_fastest = None
key_slowest = None
total_duration = 0
messages = dict()
errors = dict()
for key in durations:
duration = durations[key]
msg = 'thread {} {} detik'.format(key, duration)
resp = json_responses[key]
if 'fatal' in resp:
errors['fatal'] = resp['fatal']
elif 'error' in resp:
result = resp['error']
msg = '{} {}'.format(msg, result['message'])
err = result['message']
if err in errors:
errors[err] += 1
else:
errors[err] = 1
else:
result = resp['result']
stan = stan_from_result(result)
msg = '{} stan {}'.format(msg, stan)
messages[key] = msg
if key_fastest:
if duration < durations[key_fastest]:
key_fastest = key
else:
key_fastest = key
if key_slowest:
if duration > durations[key_slowest]:
key_slowest = key
else:
key_slowest = key
total_duration += duration
log_info(msg)
if key_fastest != key_slowest:
log_info('Tercepat {}'.format(messages[key_fastest]))
log_info('Terlama {}'.format(messages[key_slowest]))
log_info('Rerata {} detik / request'.format(
total_duration/len(durations)))
show_errors(errors)
def create_ntb():
i = randrange(99999999)
return str(i).zfill(8)
class App:
def __init__(self, argv):
self.option = get_option(argv)
server_info['url'] = self.option.url
if self.option.stan:
self.stan = self.option.stan
else:
self.stan = int(datetime.now().strftime('%H%M%S'))
def create_thread(self, data):
thread = Thread(target=send, args=[data])
# Exit the server thread when the main thread terminates
thread.daemon = True
thread_id = data['id']
threads[thread_id] = thread
thread.start()
def get_invoice_ids(self):
if not os.path.exists(self.option.invoice_id):
return [self.option.invoice_id]
r = []
with open(self.option.invoice_id) as f:
for line in f.readlines():
invoice_id = line.rstrip()
r += [invoice_id]
return r
def get_method(self):
if self.option.payment:
return 'payment'
if self.option.reversal:
return 'reversal'
return 'inquiry'
def get_transaction(self, invoice_id):
def required(name, default=None):
value = getattr(self.option, name)
if not value and not default:
error('--{} harus diisi'.format(name))
p[name] = value or default
p = dict(invoice_id=invoice_id)
if self.option.payment or self.option.reversal:
required('amount')
if self.option.payment:
ntb = create_ntb()
required('ntb', ntb)
else:
required('ntb')
required('stan')
if 'stan' not in p:
stan = str(self.stan).zfill(6)
p['stan'] = stan
self.stan += 1
if self.option.bit:
p['bits'] = dict()
for t in self.option.bit.split(','):
bit, value = t.split(':')
p['bits'][bit] = value
if self.option.conf:
conf = dict()
for t in self.option.conf.split(','):
key, val = t.split(':')
conf[key] = val
p['conf'] = conf
return p
def run_transaction(self):
method = self.get_method()
thread_id = 0
for i in range(self.option.count):
for invoice_id in self.get_invoice_ids():
thread_id += 1
p = self.get_transaction(invoice_id)
data = dict(
id=thread_id, method=method, params=[p],
jsonrpc='2.0')
self.create_thread(data)
sleep(self.option.delay)
def run_echo(self):
for thread_id in range(1, self.option.count+1):
p = dict(id=thread_id)
data = dict(id=thread_id, method='echo', params=[p], jsonrpc='2.0')
self.create_thread(dict(data))
def run(self):
p = dict()
if self.option.invoice_id:
self.run_transaction()
else:
self.run_echo()
while threads:
if not end_threads:
continue
i = end_threads[0]
if i in threads:
thread = threads[i]
thread.join()
del threads[i]
index = end_threads.index(i)
del end_threads[index]
show_durations()
def main(argv=sys.argv[1:]):
app = App(argv)
app.run()
from ISO8583.ISOErrors import BitNotSet
import transaction
from pyramid_rpc.jsonrpc import jsonrpc_method
# from linkaja.models import Rpc
from opensipkd.waktu import create_now
from opensipkd.string import FixLength
from opensipkd.jsonrpc.exc import (
JsonRpcInvalidParams,
JsonRpcBankNotFound,
JsonRpcBillerNetwork,
JsonRpcBillNotFound,
JsonRpcBillAlreadyPaid,
)
from ...iso8583.tools import iso_to_dict
from ...common import BaseIsoView
from ...models.conf import QConf
from ...models.log_iso import Log
# from opensipkd.iso8583.bjb.pbb.structure import INVOICE_PROFILE
# from opensipkd.iso8583.bjb.pbb.agratek.models import Log
ROUTE = 'rpc'
conf = dict()
TANGGAL = [
('Tahun', 4, 'N'),
('Bulan', 2, 'N'),
('Tanggal', 2, 'N')]
def pbb_response(iso):
d = iso_to_dict(iso)
if d[39] == '00':
pass
elif d[39] == '55':
raise JsonRpcBillNotFound()
elif d[39] == '54':
raise JsonRpcBillAlreadyPaid()
else:
raise JsonRpcBillerNetwork()
profile = FixLength(INVOICE_PROFILE)
profile.set_raw(d[62])
tgl = FixLength(TANGGAL)
tgl.set_raw(profile['Jatuh Tempo'])
jatuh_tempo = '-'.join([tgl['Tanggal'], tgl['Bulan'], tgl['Tahun']])
return dict(
stan=d[11],
invoice_id=d[61],
amount=int(d[4]),
nama_wp=profile['Nama'].strip(),
alamat_op=profile['Lokasi'].strip(),
kelurahan_op=profile['Nama Kelurahan'].strip(),
kecamatan_op=profile['Nama Kecamatan'].strip(),
provinsi_op=profile['Nama Propinsi'].strip(),
luas_tanah=int(profile['Luas Tanah']),
luas_bangunan=int(profile['Luas Bangunan']),
jatuh_tempo=jatuh_tempo,
tagihan=int(profile['Tagihan']),
denda=int(profile['Denda']),
total=int(profile['Total Bayar']),
discount=int(profile['Discount'] or 0))
def webr_response(iso):
d = iso_to_dict(iso)
if d[39] == '00':
pass
elif d[39] == '55':
raise JsonRpcBillNotFound()
elif d[39] == '54':
raise JsonRpcBillAlreadyPaid()
else:
raise JsonRpcBillerNetwork()
profile = FixLength(INVOICE_PROFILE)
profile.set_raw(d[62])
# tgl = FixLength(TANGGAL)
# tgl.set_raw(profile['Jatuh Tempo'])
# jatuh_tempo = '-'.join([tgl['Tanggal'], tgl['Bulan'], tgl['Tahun']])
result = dict(
stan=d[11],
invoice_id=d[61],
amount=int(d[4]),
)
result.update(profile.to_dict())
return result
def default_response(iso):
d = iso_to_dict(iso)
if d[39] == '00':
pass
elif d[39] == '55':
raise JsonRpcBillNotFound()
elif d[39] == '54':
raise JsonRpcBillAlreadyPaid()
else:
raise JsonRpcBillerNetwork()
profile = FixLength(INVOICE_PROFILE)
profile.set_raw(d[62])
tgl = FixLength(TANGGAL)
tgl.set_raw(profile['Jatuh Tempo'])
jatuh_tempo = '-'.join([tgl['Tanggal'], tgl['Bulan'], tgl['Tahun']])
return dict(
stan=d[11],
invoice_id=d[61],
amount=int(d[4]),
# nama_wp=profile['Nama'].strip(),
# alamat_op=profile['Lokasi'].strip(),
# kelurahan_op=profile['Nama Kelurahan'].strip(),
# kecamatan_op=profile['Nama Kecamatan'].strip(),
# provinsi_op=profile['Nama Propinsi'].strip(),
# luas_tanah=int(profile['Luas Tanah']),
# luas_bangunan=int(profile['Luas Bangunan']),
# jatuh_tempo=jatuh_tempo,
# tagihan=int(profile['Tagihan']),
# denda=int(profile['Denda']),
# total=int(profile['Total Bayar']),
# discount=int(profile['Discount'] or 0)
)
class View(BaseIsoView):
def __init__(self, request):
super().__init__(request)
self.qconf = QConf(request.dbsession)
def get_name(self): # Override
return 'rpc'
def get_allowed_ip(self): # Override
row = self.qconf.get('rpc allowed ip')
return row.as_list()
def get_iso_conf_name(self): # Override
return self.qconf.get_value('rpc to iso8583')
def create_web_log(self, method, p):
# todo ini hanya untuk linkaja sepertinya gak perlu disini
conn = self.get_connection()
iso_func = getattr(conn.job, method)
p['db_session'] = self.request.dbsession
iso_req = p and iso_func(p) or iso_func()
stan = iso_req.get_stan()
web_log = Rpc(
ip=self.request.client_addr, conf_name=self.get_name(),
merchant='-', terminal='-', trx_type=method[:3],
msisdn='-', acc_no=p['invoice_id'], stan=stan,
trx_date=create_now())
with transaction.manager:
self.request.dbsession.add(web_log)
self.request.dbsession.flush()
self.request.dbsession.expunge_all()
return web_log, iso_req, conn
def create_iso_log(self, iso, web_log=None):
conf = self.get_iso_conf()
iso_log = Log(
mti=iso.getMTI(),
rpc_id=web_log and web_log.id or None,
ip=conf['ip'],
conf_name=conf['name'])
for bit in iso.get_bit_definition():
try:
value = iso.getBit(bit)
except BitNotSet:
continue
bit_str = str(bit).zfill(3)
field = 'bit_' + bit_str
setattr(iso_log, field, value)
with transaction.manager:
self.request.dbsession.add(iso_log)
self.request.dbsession.flush()
self.request.dbsession.expunge_all()
return iso_log
def get_response(self, rpc_method, p=dict(), iso_method=None):
self.log_receive(rpc_method, p)
method = iso_method or rpc_method
conn = self.get_connection()
print(conn.job)
iso_func = getattr(conn.job, method)
p['db_session'] = self.request.dbsession
#On get request
# web_log, iso_req, conn = self.create_web_log(method, p) # ini hanya untuk linkaja????
iso_req = p and iso_func(p) or iso_func()
stan = iso_req.get_stan()
# before send request
self.create_iso_log(iso_req) #web_log
iso_resp = self.send_iso(conn, iso_req)
#after get
self.create_iso_log(iso_resp) # web_log
# xxxx
# from opensipkd.iso8583.bjb.pbb.structure import INQUIRY_CODE as pbb_inquiry
# from opensipkd.iso8583.bjb.pbb.structure import PAYMENT_CODE as pbb_payment
# from opensipkd.iso8583.bjb.webr.structure import INQUIRY_CODE as webr_inquiry
# from opensipkd.iso8583.bjb.webr.structure import PAYMENT_CODE as webr_payment
# if iso_resp.getBit(3) in [pbb_inquiry, pbb_payment]:
# data = pbb_response(iso_resp)
# elif iso_resp.getBit(3) in [webr_inquiry, webr_payment]:
# data = webr_response(iso_resp)
# else:
# data = default_response(iso_resp)
data={"stan": stan}
r = dict(code=0, message='OK', data=data)
self.log_send(r)
return r
def not_found_error(self, hostname): # Override
msg = f'Host {hostname} tidak ditemukan di konfigurasi'
return JsonRpcBankNotFound(message=msg)
def not_running_error(self, hostname): # Override
msg = f'Host {hostname} belum terhubung'
return JsonRpcBankNotFound(message=msg)
def timeout_error(self): # override
return JsonRpcBillerNetwork(message='Timeout')
def log_receive(self, method, p):
msg = f'{method} {p}'
super().log_receive(msg)
@jsonrpc_method(endpoint=ROUTE)
def echo(self, p):
self.validate()
return self.get_response('echo', iso_method='echo_request')
@jsonrpc_method(endpoint=ROUTE)
def inquiry(self, p):
self.validate()
return self.get_response('inquiry', p)
@jsonrpc_method(endpoint=ROUTE)
def payment(self, p):
self.validate()
return self.get_response('payment', p)
@jsonrpc_method(endpoint=ROUTE)
def reversal(self, p):
self.validate()
return self.get_response('reversal', p)
# Dipanggil read_conf.py
def init(cfg):
conf.update(cfg)
def includeme(config):
config.add_jsonrpc_endpoint('rpc', '/rpc')
config.scan('.')
......@@ -50,7 +50,7 @@ def login_validator(form, value):
raise colander.Invalid(form, _('Login failed'))
def login_ok(request, user, route='log'):
def login_ok(request, user, route='user'):
headers = remember(request, user.id)
user.last_login_date = create_now()
request.dbsession.add(user)
......@@ -89,7 +89,7 @@ def login_default_response():
@view_config(route_name='login', renderer='templates/login.pt')
def view_login(request):
if request.authenticated_userid():
if request and request.authenticated_userid:
return HTTPFound(location=request.route_url('home'))
if '_LOCALE_' in request.GET:
resp = Response()
......
......@@ -3,7 +3,6 @@ import sys
import subprocess
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.rst')) as f:
README = f.read()
......@@ -33,10 +32,10 @@ requires = [
'pyramid_mailer',
'requests',
'pyramid_rpc',
'opensipkd-iso8583 @ '
'git+https://git.opensipkd.com/sugiana/opensipkd-iso8583.git',
'opensipkd-hitung @ '
'git+https://git.opensipkd.com/sugiana/opensipkd-hitung.git',
# 'opensipkd-iso8583 @ '
# 'git+https://git.opensipkd.com/sugiana/opensipkd-iso8583.git',
# 'opensipkd-hitung @ '
# 'git+https://git.opensipkd.com/sugiana/opensipkd-hitung.git',
]
......@@ -75,8 +74,15 @@ setup(
],
'console_scripts': [
'iso8583 = iso8583_web.iso8583:main',
'initialize_iso8583_web_db = '
'iso8583_web.scripts.initialize_db:main',
'iso8583_set_conf = iso8583_web.scripts.set_conf:main',
'iso8583_web_client = '
'iso8583_web.scripts.web_client:main',
]
},
)
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!