rpc.py 11.9 KB
import sys
import json
import requests
import hmac
import hashlib
import base64
from datetime import datetime, time, date
from sqlalchemy import not_, func, between, and_
from pyramid_rpc.jsonrpc import jsonrpc_method
from pyramid_rpc.jsonrpc import JsonRpcError
from ..tools import (date_from_str, dict_to_str, to_str, get_settings)
##MODEL
from ..models.isipkd import (DBSession, ARInvoice, ARSspd, User, Unit,
                             ObjekPajak, SubjekPajak, Rekening, Wilayah)
from .arinvoice import save as save_invoice
from ..models.rpc import DepartemenRoute

now = datetime.now()
datenow = now.date()
lima_menit = 600

####################### API TOOL #######################################
def auth_from_rpc(request):
    settings = get_settings()
    env = request.environ
    if not ('HTTP_USERID' in env and 'HTTP_SIGNATURE' in env and
            'HTTP_KEY' in env):
        raise JsonRpcInvalidLoginError
    
    http_userid = env['HTTP_USERID']
    q = DBSession.query(User).filter_by(user_name=http_userid)
    user = q.first()
    
    if not user or user.status == 0:
        raise JsonRpcInvalidLoginError
        
    # bypass cek authentication for development
    if http_userid=='admin' and 'devel' in settings and settings['devel']:
        return user
        
    time_stamp = int(env['HTTP_KEY'])
    now = get_seconds()
    if (not 'devel' in settings or not settings['devel']) and abs(now - time_stamp) > lima_menit:
        raise JsonRpcInvalidTimeError
        
    header = json_rpc_header(http_userid, user.api_key, time_stamp)
    if header['signature'] != env['HTTP_SIGNATURE']:
        raise JsonRpcInvalidLoginError
        
    return user
    
def json_rpc_header(userid, password, time_stamp=None):
    if not time_stamp:
        time_stamp = str(get_seconds())
    value = '&'.join([str(userid), str(time_stamp)])
    password = str(password)
    signature = hmac.new(key=str.encode(password), msg=str.encode(value),
                         digestmod=hashlib.sha256).digest()
    if sys.version < '3':
        encoded_signature = base64.encodestring(signature).replace('\n', '')
    else:
        encoded_signature = base64.encodebytes(signature).decode().replace('\n', '')
    
    return dict(userid=userid, signature=encoded_signature, key=time_stamp)
    
def get_jsonrpc(method, params):
    return dict(jsonrpc='2.0', method=method, params=params, id=get_random_number(6))

class JsonRpcInvalidTimeError(JsonRpcError):
    code = -41014
    message = 'Periksa Date Time Server'
    
class JsonRpcInvalidLoginError(JsonRpcError):
    code = -41001
    message = "Invalid RPC User/Password"
    
class JsonRpcInvalidDataError(JsonRpcError):
    code = -41005
    message = 'Data Tidak Valid'
    
class JsonRpcBillNotFoundError(JsonRpcError):
    code = -52001
    message = 'Bill Not Found'

class JsonRpcBillAllreadyPaidError(JsonRpcError):
    code = -52002
    message = 'Bill Allready Paid'

class JsonRpcPaymentNotFoundError(JsonRpcError):
    code = -54001
    message = 'Payment Not Found'
    
def custom_error(code, message):
    cls = JsonRpcError()
    cls.code = code
    cls.message = message
    cls.message = message
    raise cls
    
def get_mandatory(data, values):
    for value in values:
        if not value in data or not data[value]:
            raise JsonRpcInvalidDataError(message="{} Not Found".format(value))

def get_seconds():
    begin_unix_time = datetime(1970, 1, 1)
    durasi = datetime.utcnow() - begin_unix_time
    return int(durasi.total_seconds())
    
def get_random_number(width=12):
    return ''.join(choice(digits) \
                   for _ in range(width))
    
def clear_null_value(values):
    # digunakan untuk menghapus dictionary yang value nya null
    tobe_del = []
    for value in values:
        if not values[value]:
            tobe_del.append(value)
    for value in tobe_del:
        del values[value]
    return values
    
def ymd(tgl):
    return tgl.strftime('%Y-%m-%d')
    
###########################################################################
    
def save_departemen_route(request, values, row=None):
    if not row:
        row = DepartemenRoute()
    
    if not 'kode' in values or not values['kode']:
       return 0
    if not 'no_skrd' in values or not values['no_skrd']:
       return 0
    if not 'tgl_skrd' in values or not values['tgl_skrd']:
       return 0
    if not 'ar_invoice_id' in values or not values['ar_invoice_id']:
       return 0
    
    if not 'status' in values:
        values['status'] = 0
    row.from_dict(values)
    DBSession.add(row)
    DBSession.flush()
    return row

@jsonrpc_method(method='set_invoice', endpoint='api-webr')
def set_invoice(request, data):
    """
    Digunakan untuk menerima data tagihan
    :param request:
    :param data:
        - op_kode
        - rek_kode
        - dasar
        - tarif
        - pokok
        - denda
        - bunga
        - jumlah
        - periode_1
        - periode_2
        - jatuh_tempo
        - no_skrd
        - tgl_skrd
    :return:
        - kd_bayar
    """
    
    auth_from_rpc(request)
    param = ['op_kode','rek_kode','dasar','tarif',
             'pokok','denda','bunga','jumlah','periode_1',
             'periode_2','jatuh_tempo','tgl_tetap','no_skrd']
    get_mandatory(data, param)
    values = clear_null_value(data)
    if 'jatuh_tempo' in values and values['jatuh_tempo']:
       values['jatuh_tempo'] = date_from_str(values['jatuh_tempo'])
    if 'periode_1' in values and values['periode_1']:
       values['periode_1'] = date_from_str(values['periode_1'])
    if 'periode_2' in values and values['periode_2']:
       values['periode_2'] = date_from_str(values['periode_2'])
    values['status_bayar'] = 0
    op = ObjekPajak.get_by_kode(values['op_kode'])
    if not op:
        return custom_error(-1,'Objek Pajak kode {} not found'.format(values['op_kode']))
    values['objek_pajak_id'] = op.id
    values['op_kode'] = op.kode
    values['op_nama'] = op.nama
    values['op_alamat_1'] = op.alamat_1
    values['op_alamat_2'] = op.alamat_2
    values['wilayah_id'] = op.wilayah_id
    values['subjek_pajak_id'] = op.subjekpajak_id
    values['unit_id'] = op.unit_id
    subjek = SubjekPajak.get_by_id(values['subjek_pajak_id'])
    if not subjek:
        return custom_error(-1,'Subjek Pajak id {} not found, contact admin'.format(values['subjek_pajak_id']))
    values['wp_kode'] = subjek.kode
    values['wp_nama'] = subjek.nama
    values['wp_alamat_1'] = subjek.alamat_1
    values['wp_alamat_2'] = subjek.alamat_2
    unit = Unit.get_by_id(values['unit_id'])
    if not unit:
        return custom_error(-1,'Unit id {} not found, contact admin'.format(values['unit_id']))
    values['unit_kode'] = unit.id
    values['unit_nama'] = unit.nama
    rek = Rekening.get_by_kode(values['rek_kode'])
    if not rek:
        return custom_error(-1,'Rekening kode {} not found, contact admin'.format(values['rek_kode']))
    values['rekening_id'] = rek.id
    values['rek_nama'] = rek.nama
    rdata = {}
    for v in values:
        v = v.strip()
    row_invoice = DBSession.query(ARInvoice).\
                  filter(ARInvoice.no_skrd == values['no_skrd']).first()
    if row_invoice:
        if row_invoice.status_bayar == 1:
            raise JsonRpcBillAllreadyPaidError
        return custom_error(-1,'Bill already exist')
    else:
        invoice = save_invoice(request, values)
        
        # Save Departemen_Route
        values['kode'] = invoice.kode
        values['no_skrd'] = invoice.no_skrd
        values['tgl_skrd'] = invoice.tgl_tetap
        values['ar_invoice_id'] = invoice.id
        save_departemen_route(request, values)
    
        if not save_departemen_route:
            return dict(code=-32603,
                    message='Gagal menambahkan Invoice',
                    data=rdata)
        
    rdata['kd_bayar'] = invoice.kode
    return dict(code=0,
                message='Sukses menambahkan Invoice',
                data=rdata)
                
@jsonrpc_method(method='get_payment', endpoint='api-webr')
def get_payment(request, data):
    """
    Digunakan untuk informasi pembayaran
    :param request:
    :param data:
        - kd_bayar
    :return:
        - ntp
        - ntb
        - pembayaran_ke
        - bunga
        - bayar
        - tgl_bayar
        - jatuh_tempo
        - bank_id
        - channel_id
    """
    auth_from_rpc(request)
    get_mandatory(data, ['kd_bayar'])
    row_invoice = DBSession.query(ARInvoice).filter_by(kode=data['kd_bayar']).first()
    if not row_invoice:
        raise JsonRpcBillNotFoundError
    row_payment = DBSession.query(ARSspd.ntp, ARSspd.pembayaran_ke, ARSspd.bunga,
                        ARSspd.bayar, ARSspd.tgl_bayar, ARInvoice.jatuh_tempo, 
                        ARSspd.ntb, ARSspd.bank_id, ARSspd.channel_id).\
                   join(ARInvoice, ARInvoice.id == ARSspd.arinvoice_id).\
                   filter(ARInvoice.kode==data['kd_bayar']).\
                   order_by(ARSspd.pembayaran_ke).all()
    if not row_payment:
        raise JsonRpcPaymentNotFoundError
    
    rdata = []
    for k in row_payment:
        d = dict(
            kd_bayar = data['kd_bayar'],
            ntp = k.ntp.strip(),
            pembayaran_ke = k.pembayaran_ke,
            bunga = k.bunga,
            bayar = k.bayar,
            tgl_bayar = ymd(k.tgl_bayar),
            jatuh_tempo = ymd(k.jatuh_tempo),
            ntb = k.ntb.strip(),
            bank_id = k.bank_id,
            channel_id = k.channel_id,
        )
        rdata.append(d)

    message = ""
    return dict(code=0,
                message=message,
                data=rdata)
                
@jsonrpc_method(method='send_payment', endpoint='api-webr')
def send_payment(request, data):
    """
    Digunakan untuk Scheduler pengiriman data pembayaran
    :param request:
    :param data:
        - url
        - token
    :return:
        -
    """
    auth_from_rpc(request)
    get_mandatory(data, ['url','token'])
    ws_url = data['url']
    token = data['token']
    datar = []
    row_payment = DBSession.query(
                       ARSspd.ntp, ARSspd.pembayaran_ke,
                       ARSspd.bunga, ARSspd.bayar, ARSspd.tgl_bayar,
                       ARSspd.ntb, ARSspd.bank_id, ARSspd.channel_id,
                       ARInvoice.kode.label("kd_bayar"), ARInvoice.jatuh_tempo,
                       DepartemenRoute.id).\
                  join(DepartemenRoute, DepartemenRoute.ar_invoice_id == ARSspd.arinvoice_id).\
                  join(ARInvoice, ARInvoice.id == ARSspd.arinvoice_id).\
                  filter(DepartemenRoute.status==0,
                         ARInvoice.status_bayar==1).\
                  order_by(ARSspd.pembayaran_ke).all()
    if row_payment:
        for k in row_payment:
            responsetxt = None
            d = dict(
                kd_bayar = k.kd_bayar,
                ntp = k.ntp,
                pembayaran_ke = k.pembayaran_ke,
                bunga = k.bunga,
                bayar = k.bayar,
                tgl_bayar = ymd(k.tgl_bayar),
                jatuh_tempo = ymd(k.jatuh_tempo),
                ntb = k.ntb,
                bank_id = k.bank_id,
                channel_id = k.channel_id,
                token = token
            )
            # print('>>>>>>>>>>> DEBUG DATA PAYMENT',d)
            
            response = requests.post(ws_url, data=json.dumps(d), verify=False)
            if response.status_code != 200:
                continue
            # try:
                # responsetxt = json.loads(response.text)
                # if 'result' in responsetxt and responsetxt['result'] != 'success':
                    # continue
            # except:
                # continue
                        
            # Save Departemen_Route
            values = dict(id=k.id,status=1)
            row = DepartemenRoute.get_by_id(data['id'])
            save_departemen_route(request, values, row)
            del d['token']
            datar.append(d)
    
        return dict(code=0,
                    message="Send Payment Success",
                    data=datar,
                    )