<?php
defined("BASEPATH") or exit("No direct script access allowed.");
date_default_timezone_set("Asia/Jakarta");
$_CONFIG = $_CONFIG;
$client_ip = client_ip();
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown';
$device = isset($_SERVER['HTTP_USER_AGENT']) ? devices() : 'unknown';
if (!function_exists('e')) {
	function e($data) {
		return htmlspecialchars(trim($data));
	}
}

class WA
{
    private function connect($link,$data, $n)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_URL, $link.$n);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $result = curl_exec($ch);
        curl_close($ch);
        return json_decode($result, true);
    }

    public function sendMessage($link,$api,$sender, $phone, $msg)
    {
        return $this->connect($link,[
            'api_key' => $api,
            "sender" => $sender,
            'number' => $phone,
            'message' => $msg
        ], 'apiv2/send-message.php');
    }

    public function sendPicture($link,$api,$sender,$phone, $caption, $url, $filetype)
    {
        return $this->connect($link,[
            'api_key' => $api,
            'sender' => $sender,
            'number' => $phone,
            'caption' => $caption,
            'url' => $url,
            'ex' => $filetype
        ], 'apiv2/send-media.php');
    }
}
$WhatsApp = new WA();

class GojekPay
{
    const API_URL = 'https://api.gojekapi.com';
    const API_GOID = 'https://goid.gojekapi.com';
    const API_CUSTOMER = 'https://customer.gopayapi.com';
	
    const clientId = 'gojek:consumer:app';
    const clientSecret = 'pGwQ7oi8bKqqwvid09UrjqpkMEHklb';
    const appId = 'com.go-jek.ios';
    const phoneModel = 'Apple, iPhone XS Max';
    const phoneMake = 'Apple';
    const osDevice = 'iOS, 14.8.1';
    const xPlatform = 'iOS';
    const appVersion = '4.38.1';
    const gojekCountryCode = 'ID';
    const userAgent = 'Gojek/4.38.1 (com.go-jek.ios; build:24785490; iOS 14.4.0) Alamofire/4.38.1';
    
    private $authToken, $uniqueId, $sessionId, $pin, $idKey;
    
    public function __construct($authToken = false)
    {
        $this->sessionId = '8FB5ED6E-322B-407C-BC88-52FF696B8AFA'; // generated from self::uuidv4();
        $this->uniqueId  = 'FCEEC9AF-564D-4599-99E8-F042C7B4DF3F'; // generated from self::uuidv4();
        if ($authToken) {
            $this->authToken = $authToken;
        }
    }
    
    protected function setPinGojek($pin)
    {
        $this->pin = $pin;
    }
    
    protected function setIdKey()
    {
        $this->idKey = self::uuidv4();
    }
    
    public function loginRequest($phoneNumber)
    {
        $payload = array(
            'client_id' => self::clientId,
            'client_secret' => self::clientSecret,
            'country_code' => '+62',
            'magic_link_ref' => null,
            'phone_number' => self::formatPhone($phoneNumber)
        );
        return self::Request(self::API_GOID . '/goid/login/request', $payload, true);
    }
    
    public function getAuthToken($otpToken, $otpCode)
    {
        $payload = array(
            'client_id' => self::clientId,
            'client_secret' => self::clientSecret,
            'data' => array(
                'otp_token' => $otpToken,
                'otp' => $otpCode
            ),
            'grant_type' => 'otp'
        );
        
        return self::Request(self::API_GOID . '/goid/token', $payload, true);
    }
    
    public function getTransactionHistory($page = 1, $limit = 20)
    {
        return self::Request(self::API_CUSTOMER . "/v1/users/transaction-history?page={$page}&limit={$limit}", false, true);
    }
    
    public function getBalance()
    {
        return self::Request(self::API_CUSTOMER . "/v1/payment-options/balances", false, true);
    }
    
    public function getProfile()
    {
        return self::Request(self::API_URL . "/gojek/v2/customer", false, true);
    }
    
    public function goClubMembership()
    {
        return self::Request(self::API_URL . "/goclub/v1/membership", false, true);
    }
    
    public function paylaterProfile()
    {
        return self::Request(self::API_URL . "/paylater/v1/user/profile", false, true);
    }
    
    public function kycStatus()
    {
        return self::Request(self::API_CUSTOMER . "/v1/users/kyc/status", false, true);
    }
    
    public function isGojek($phoneNumber)
    {
        return self::Request(self::API_CUSTOMER . "/v1/users/p2p-profile?phone_number=" . urlencode($phoneNumber) . "", false, true);
    }
    
    public function getQrid($phoneNumber)
    {
        return self::getResponse(self::isGojek($phoneNumber), 'qr_id');
    }
    
    public function transferGopay($phoneNumber, int $amount, $pin)
    {
        self::setPinGojek($pin);
        $payload = array(
            'amount' => array(
                'currency' => 'IDR',
                'value' => $amount
            ),
            'description' => 'ðŸ’°',
            'metadata' => array(
                'post_visibility' => 'NO_SOCIAL',
                'theme_id' => 'THEME_CLASSIC'
            ),
            'payee' => array(
                'id' => self::getQrid($phoneNumber),
                'id_type' => 'GOPAY_QR_ID'
            )
        );
        return self::Request(self::API_CUSTOMER . '/v1/funds/transfer', $payload, true);
    }
    
    public function getBankList()
    {
        return self::Request(self::API_CUSTOMER . "/v1/banks?type=transfer&show_withdrawal_block_status=false", false, true);
    }
    
    public function transferBank($bankCode, $bankNumber, int $amount, $pin)
    {
        self::setIdKey();
        $bankAccountName = self::getResponse(self::isBank($bankCode, $bankNumber), 'account_name');
        $payload         = array(
            'account_name' => "$bankAccountName",
            'account_number' => "$bankNumber",
            'amount' => "$amount",
            'bank_code' => "$bankCode",
            'currency' => 'IDR',
            'pin' => "$pin",
            'type' => 'transfer'
        );
        return self::Request(self::API_CUSTOMER . '/v1/withdrawals', $payload, true);
    }
    
    public function transferBankDetail($requestId)
    {
        return self::Request(self::API_CUSTOMER . "/v1/withdrawals/detail?request_id={$requestId}", false, true);
    }
    
    public function isBank($bankCode, $bankNumber)
    {
        return self::Request(self::API_CUSTOMER . "/v1/bank-accounts/validate?bank_code={$bankCode}&account_number={$bankNumber}", false, true);
    }
    
    protected function formatPhone($phoneNumber, $areacode = '')
    {
        return substr_replace($phoneNumber, $areacode, 0, 1);
    }
    
    public function uuidv4()
    {
        $data    = random_bytes(16);
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
        return strtoupper(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)));
    }
    
    protected function buildHeaders()
    {
        $headers = array(
            'x-appid: ' . self::appId,
            'x-phonemodel: ' . self::phoneModel,
            'user-agent: ' . self::userAgent,
            'x-session-id: ' . $this->sessionId,
            'x-phonemake: ' . self::phoneMake,
            'x-uniqueid: ' . $this->uniqueId,
            'x-deviceos: ' . self::osDevice,
            'x-platform: ' . self::xPlatform,
            'x-appversion: ' . self::appVersion,
            'Gojek-Country-Code: ' . self::gojekCountryCode,
            'accept: */*',
            'content-type: application/json',
            'x-user-type: customer'
        );
        
        if (!empty($this->authToken)) {
            array_push($headers, 'Authorization: Bearer ' . $this->authToken);
        }
        
        if (!empty($this->pin)) {
            array_push($headers, 'pin: ' . $this->pin);
        }
        
        if (!empty($this->idKey)) {
            array_push($headers, 'Idempotency-Key: ' . $this->idKey);
        }
        
        return $headers;
    }
    
    public function getResponse($response, $key)
    {
        $json = json_decode($response, true);
        return $json['data'][$key];
    }
    
    protected function Request($url, $post = false, $headers = false)
    {
        $ch = curl_init();
        
        curl_setopt_array($ch, array(
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true
        ));
        
        if ($post) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post));
        }
        
        if ($headers) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, self::buildHeaders());
        }
        
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }
}

class Ovo
{
    const BASE_API = "https://api.ovo.id";
    const AGW_API = "https://agw.ovo.id";
    const AWS_API = "https://api.cp1.ovo.id";
    
    const os = "iOS";
    const app_version = "3.54.0";
    const client_id = "ovo_ios";
    const user_agent = "OVO/21404 CFNetwork/1220.1 Darwin/20.3.0";
    const device_id = "6AA4E427-D1B4-4B7E-9C22-F4C0F86F2CFD";
    
    const push_notification_id = "e35f5a9fc1b61d0ab0c83ee5ca05ce155f82dcffee0605f1c70de38e662db362";
    
    protected $auth_token, $hmac_hash, $hmac_hash_random;
    
    public function __construct($auth_token = false)
    {
	if ($auth_token) {
            $this->auth_token = $auth_token;
        }
    }
    public function generateUUIDV4()
    {
        $data    = random_bytes(16);
        $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
        $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
        return strtoupper(vsprintf("%s%s-%s-%s-%s-%s%s%s", str_split(bin2hex($data), 4)));
    }
    public function generateRandomSHA256()
    {
        return hash_hmac("sha256", time(), "ovo-apps");
    }
    protected function headers($bearer = false)
    {
        $headers = array(
            'content-type: application/json',
            'accept: */*',
            'app-version: ' . self::app_version,
            'client-id: ' . self::client_id,
            'device-id: ' . self::device_id,
            'os: ' . self::os,
            'user-agent: ' . self::user_agent
        );
        
        if ($this->auth_token) {
            array_push($headers, 'authorization: ' . $bearer . ' ' . $this->auth_token);
        }
        
        return $headers;
    }
    public function sendOtp($phone_number)
    {
        $field = array(
            'msisdn' => $phone_number,
            'device_id' => self::device_id,
            'otp' => array(
                'locale' => 'EN',
                'sms_hash' => 'LYNCHSTORE'
            ),
            'channel_code' => 'ovo_ios'
        );
        
        return self::request(self::AGW_API . '/v3/user/accounts/otp', $field, $this->headers());
    }
    public function OTPVerify($phone_number, $otp_ref_id, $otp_code)
    {
        $field = array(
            'channel_code' => 'ovo_ios',
            'otp' => array(
                'otp_ref_id' => $otp_ref_id,
                'otp' => $otp_code,
                'type' => 'LOGIN'
            ),
            'msisdn' => $phone_number,
            'device_id' => self::device_id
        );
        
        return self::request(self::AGW_API . '/v3/user/accounts/otp/validation', $field, $this->headers());
    }
    public function getAuthToken($phone_number, $otp_ref_id, $otp_token, $security_code)
    {
        $field = array(
            'msisdn' => $phone_number,
            'device_id' => self::device_id,
            'push_notification_id' => self::push_notification_id,
            'credentials' => array(
                'otp_token' => $otp_token,
                'password' => array(
                    'value' => self::hashPassword($phone_number, $otp_ref_id, $security_code),
                    'format' => 'rsa'
                )
            ),
            'channel_code' => 'ovo_ios'
        );
        
        return self::request(self::AGW_API . '/v3/user/accounts/login', $field, $this->headers());
    }
    public function getPublicKeys()
    {
        return self::request(self::AGW_API . '/v3/user/public_keys', false, $this->headers());
    }
    public function getLastTransactions($limit = 5)
    {
        return self::request(self::BASE_API . '/wallet/transaction/last?limit=' . $limit . '&transaction_type=TOPUP%20CASH&transaction_type=INCOMING%20TRANSFER', false, $this->headers());
    }
    public function getTransactionDetails($merchant_id, $merchant_invoice)
    {
        return self::request(self::BASE_API . '/wallet/transaction/' . $merchant_id . '/' . $merchant_invoice . '', false, $this->headers());
    }
    public function getFavoriteTransfer()
    {
        return self::request(self::AWS_API . '/user-profiling/favorite-transfer', false, $this->headers());
    }
    protected function hashPassword($phone_number, $otp_ref_id, $security_code)
    {
        $rsa_key = self::parse(self::getPublicKeys(), true)['data']['keys'][0]['key'];
        $data = join("|", array(
            'LOGIN',
            $security_code,
            time(),
            self::device_id,
            $phone_number,
            self::device_id,
            $otp_ref_id
        ));
        openssl_public_encrypt($data, $output, $rsa_key);
        return base64_encode($output);
    }
    public function getEmail()
    {
        return self::request(self::AGW_API . '/v3/user/accounts/email', false, $this->headers());
    }
    public function transactionHistory($page = 1, $limit = 10)
    {
        return self::request(self::AGW_API . "/payment/orders/v1/list?limit=$limit&page=$page&transaction_type=TOPUP%20CASH&transaction_type=INCOMING%20TRANSFER", false, $this->headers('Bearer'));
    }
    public function walletInquiry()
    {
        return self::request(self::BASE_API . '/wallet/inquiry', false, $this->headers());
    }
    public function getOvoCash()
    {
        return self::parse(self::walletInquiry(), false)->data->{'001'}->card_balance;
    }
    public function getOvoCashCardNumber()
    {
        return self::parse(self::walletInquiry(), false)->data->{'001'}->card_no;
    }
    public function getOvoPointsCardNumber()
    {
        return self::parse(self::walletInquiry(), false)->data->{'600'}->card_no;
    }
    public function getOvoPoints()
    {
        return self::parse(self::walletInquiry(), false)->data->{'600'}->card_balance;
    }
    public function getPointDetails()
    {
        $json                   = base64_decode(json_decode(self::getHmac())->encrypted_string);
        $json                   = json_decode($json);
        $this->hmac_hash        = $json->hmac;
        $this->hmac_hash_random = $json->random;
        return self::request(self::AGW_API . "/api/v1/get-expired-webview", false, self::commander_headers());
    }
    protected function getHmac()
    {
        return self::request("https://commander.ovo.id/api/v1/auth/hmac?type=1&encoded=", false, self::commander_headers());
    }
    public function getBillerList()
    {
        return self::request(self::AWS_API . "/gpdm/ovo/1/v1/billpay/catalogue/getCategories?categoryID=0&level=1", false, $this->headers());
    }
    public function getBillerCategory($category_id)
    {
        return self::request(self::AWS_API . "/gpdm/ovo/ID/v2/billpay/get-billers?categoryID={$category_id}", false, $this->headers());
    }
    public function getDenominations($product_id)
    {
        return self::request(self::AWS_API . "/gpdm/ovo/ID/v1/billpay/get-denominations/{$product_id}", false, $this->headers());
    }
    public function getBankList()
    {
        return self::request(self::BASE_API . "/v1.0/reference/master/ref_bank", false, $this->headers());
    }
    public function getUnreadNotifications()
    {
        return self::request(self::BASE_API . "/v1.0/notification/status/count/UNREAD", false, $this->headers());
    }
    public function getAllNotifications()
    {
        return self::request(self::BASE_API . "/v1.0/notification/status/all", false, $this->headers());
    }
    public function getInvestment()
    {
        return self::request("https://investment.ovo.id/customer", false, $this->headers());
    }
    public function billerInquiry($biller_id, $product_id, $denomination_id, $customer_id)
    {
        $field = array(
            'product_id' => $product_id,
            'biller_id' => $biller_id,
            'customer_number' => $customer_id,
            'denomination_id' => $denomination_id,
            'period' => 0,
            'payment_method' => array(
                '001',
                '600',
                'SPLIT'
            ),
            'customer_id' => $customer_id,
            'phone_number' => $customer_id
        );
        
        return self::request(self::AWS_API . '/gpdm/ovo/ID/v2/billpay/inquiry?isFavorite=false', $field, $this->headers());
    }
    public function billerPay($biller_id, $product_id, $order_id, $amount, $customer_id)
    {
        $field = array(
            "bundling_request" => array(
                array(
                    "product_id" => $product_id,
                    "biller_id" => $biller_id,
                    "order_id" => $order_id,
                    "customer_id" => $customer_id,
                    "parent_id" => "",
                    "payment" => array(
                        array(
                            "amount" => (int) $amount,
                            "card_type" => "001"
                        ),
                        array(
                            "card_type" => "600",
                            "amount" => 0
                        )
                    )
                )
            ),
            "phone_number" => $customer_id
        );
        
        return self::request(self::AWS_API . '/gpdm/ovo/ID/v1/billpay/pay', $field, $this->headers());
    }
    public function isOVO($amount, $phone_number)
    {
        $field = array(
            'amount' => $amount,
            'mobile' => $phone_number
        );
        
        return self::request(self::BASE_API . '/v1.1/api/auth/customer/isOVO', $field, $this->headers());
    }
    public function generateTrxId($amount, $action_mark = "OVO Cash")
    {
        $field = array(
            'amount' => $amount,
            'actionMark' => $action_mark
        );
        
        return self::request(self::BASE_API . '/v1.0/api/auth/customer/genTrxId', $field, $this->headers());
    }
    protected function generateSignature($amount, $trx_id)
    {
        return sha1(join('||', array(
            $trx_id,
            $amount,
            self::device_id
        )));
    }
    public function unlockAndValidateTrxId($amount, $trx_id, $security_code)
    {
        $field = array(
            'trxId' => $trx_id,
            'securityCode' => $security_code,
			'appVersion' => self::app_version,
            'signature' => self::generateSignature($amount, $trx_id)
        );
        
        return self::request(self::BASE_API . '/v1.0/api/auth/customer/unlockAndValidateTrxId', $field, $this->headers());
    }
    
    public function transferOVO($amount, $phone_number, $trx_id, $message = "")
    {
        $field = array(
            'amount' => $amount,
            'to' => $phone_number,
            'trxId' => $trx_id,
            'message' => $message
        );
        
        return self::request(self::BASE_API . '/v1.0/api/customers/transfer', $field, $this->headers());
    }
    public function transferBankInquiry($bank_code, $bank_number, $amount, $message = "")
    {
        $field = array(
            'bankCode' => $bank_code,
            'accountNo' => $bank_number,
            'amount' => (string) $amount,
            'message' => $message
        );
        
        return self::request(self::BASE_API . '/transfer/inquiry/', $field, $this->headers());
    }
    
    public function transferBankDirect($bank_code, $bank_number, $bank_name, $bank_account_name, $trx_id, $amount, $notes = "")
    {
        $field = array(
            'bankCode' => $bank_code,
            'accountNo' => self::getOvoCashCardNumber(),
            'amount' => (string) $amount,
            'accountNoDestination' => $bank_number,
            'bankName' => $bank_name,
            'accountName' => $bank_account_name,
            'notes' => $notes,
            'transactionId' => $trx_id
        );
        
        return self::request(self::BASE_API . '/transfer/direct', $field, $this->headers());
    }
    
    public function QrisPay($amount, $trx_id, $qrid)
    {
        $field = array(
            'qrPayload' => $qrid,
            'locationInfo' => array(
                'accuracy' => 11.00483309472351,
                'verticalAccuracy' => 3,
                'longitude' => 84.90665207978246,
                'heading' => 11.704396994254495,
                'latitude' => -9.432921591875759,
                'altitude' => 84.28827400936305,
                'speed' => 0.11528167128562927
            ),
            'deviceInfo' => array(
                'deviceBrand' => 'Apple',
                'deviceModel' => 'iPhone',
                'appVersion' => self::app_version,
                'deviceToken' => self::push_notification_id
            ),
            'paymentDetail' => array(
                array(
                    'amount' => $amount,
                    'id' => '001',
                    'name' => 'OVO Cash'
                )
            ),
            'transactionId' => $trx_id,
            'appsource' => 'OVO-APPS'
        );
        
        return self::request(self::BASE_API . '/wallet/purchase/qr?qrid=' . urlencode($qrid), $field, $this->headers());
    }
    public function parse($json, $true = true)
    {
        return json_decode($json, $true);
    }
    
    protected function request($url, $post = false, $headers = false)
    {
        $ch = curl_init();
        
        curl_setopt_array($ch, array(
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_SSL_VERIFYPEER => 0
        ));
        
        if ($post) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post));
        }
        
        if ($headers) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
        
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }
    protected function commander_headers()
    {
        $headers = array(
            'accept: application/json, text/plain, */*',
            'app-id: webview-pointexpiry',
            'client-id: ' . self::client_id,
            'accept-language: id',
            'service: police',
            'origin: https://webview.ovo.id',
            'user-agent: ' . self::user_agent,
            'referer: https://webview.ovo.id/pointexpiry?version=3.43.0'
        );
        
        if ($this->auth_token) {
            array_push($headers, 'authorization: Bearer ' . $this->auth_token);
        }
        
        if ($this->hmac_hash) {
            array_push($headers, 'hmac: ' . $this->hmac_hash);
        }
        
        if ($this->hmac_hash_random) {
            array_push($headers, 'random: ' . $this->hmac_hash_random);
        }
        
        return $headers;
    }
}

class curls
{
    public function connectHeader($end_point,$header,$reqout = 'decode') {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $end_point);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        $chresult = curl_exec($ch);
        return ($reqout == 'decode') ? json_decode($chresult, true) : $chresult;
    }

    public function connectPost($end_point,$postdata,$reqout = 'decode') {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $end_point);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        $chresult = curl_exec($ch);
        curl_close($ch);
        if(!$chresult) $chresult = $this->connectHeaderPost($end_point,['content-type: multipart/form-data','Content-Type: application/json'],$postdata,'original');
        return ($reqout == 'decode') ? json_decode($chresult, true) : $chresult;
    }

    public function connectHeaderPost($end_point,$header,$postdata,$reqout = 'decode') {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $end_point);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        $chresult = curl_exec($ch);
        return ($reqout == 'decode') ? json_decode($chresult, true) : $chresult;
    }
    public function connectTripayPost($end_point,$header,$postdata,$reqout = 'decode') {
        $ch = curl_init();
        curl_setopt($ch,CURLOPT_FRESH_CONNECT,true);
        curl_setopt($ch,CURLOPT_URL, $end_point);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch,CURLOPT_HEADER,false);
        curl_setopt($ch,CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch,CURLOPT_FAILONERROR, false);
        curl_setopt($ch,CURLOPT_POST, true);
        curl_setopt($ch,CURLOPT_POSTFIELDS, http_build_query($postdata));
        curl_setopt($ch,CURLOPT_IPRESOLV,CURL_IPRESOLVE_V4);
        $chresult = curl_exec($ch);
        return ($reqout == 'decode') ? json_decode($chresult, true) : $chresult;
    }
    public function connectCustom($data,$res = 'decode') {
        $ch = curl_init();
        curl_setopt_array($ch, $data);
        $chresult = curl_exec($ch);
        curl_close($ch);
        return ($res == 'decode') ? json_decode($chresult, true) : $chresult;
    }
    
    public function connectGet($end_point,$getdata,$reqout = 'decode') {
        $link = (is_array($getdata)) ? $end_point.'?'.http_build_query($getdata) : $end_point;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE); 
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.47 Safari/537.36');
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_URL, $link);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        $chresult = curl_exec($ch);
        curl_close($ch);
        return ($reqout == 'decode') ? json_decode($chresult, true) : $chresult;
    }
}

$curl = new curls;
if (!function_exists('config')) {
	function config($type, $name, $default_value = null) {
		global $_CONFIG;
		return (!empty($_CONFIG[$type][$name])) ? $_CONFIG[$type][$name]:$default_value;
	}
}

if (!function_exists('base_url')) {
	function base_url($to = '/') {
		return config('web', 'url').$to;
	}
}

if (!function_exists('asset')) {
	function asset($to = '/') {
		return config('web', 'assets').$to;
	}
}

if (!function_exists('bulan_indonesia') && !function_exists('tanggal_indonesia')) {
	function bulan_indonesia() {
		return [
			'01' => 'Januari',
			'02' => 'Februari',
			'03' => 'Maret',
			'04' => 'April',
			'05'=> 'Mei',
			'06' => 'Juni',
			'07' => 'Juli',
			'08' => 'Agustus',
			'09' => 'September',
			'10' => 'Oktober',
			'11' => 'November',
			'12' => 'Desember'
		];
	}

	function tanggal_indonesia($date = null) {
		$timestamps = ($data !== null) ? strtotime($date):strtotime(date("Y-m-d H:i:s"));
		$Y = date("Y", $timestamps);
		$m = date("m", $timestamps);
		$d = date("d", $timestamps);
		$His = date("H:i:s", $timestamps);
		$mIndonesia = bulan_indonesia()[$m];

		return "{$y} {$mIndonesia} {$d}, {$His}";
	}
}

if (!function_exists('random')) {
	function random($length = 10) {
		$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
		return substr(str_shuffle($chars), 0, $length);
	}
}

if (!function_exists('generate_csrf_token') && !function_exists('verify_csrf_token') && !function_exists('csrf_token')) {
	function generate_csrf_token() {
		$token = random(150);
		$_SESSION['csrf_token'] = $token;
	}

	function verify_csrf_token($token) {
		if ($token !== md5($_SESSION['csrf_token'])) {
			return false;
		}

		return true;
	}

	function csrf_token() {
		return md5($_SESSION['csrf_token']);
	}
}

if (!function_exists('time_elapsed_string')) {
	function time_elapsed_string($datetime, $full = false) {
	    $now = new DateTime;
	    $ago = new DateTime($datetime);
	    $diff = $now->diff($ago);

	    $diff->w = floor($diff->d / 7);
	    $diff->d -= $diff->w * 7;

	    $string = array(
	        'y' => 'tahun',
	        'm' => 'bulan',
	        'w' => 'minggu',
	        'd' => 'hari',
	        'h' => 'jam',
	        'i' => 'menit',
	        's' => 'detik',
	    );
	    foreach ($string as $k => &$v) {
	        if ($diff->$k) {
	            $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? '' : '');
	        } else {
	            unset($string[$k]);
	        }
	    }

	    if (!$full) $string = array_slice($string, 0, 1);
	    return $string ? implode(', ', $string) . ' lalu' : 'baru saja';
	}
}

if (!function_exists('gravatar')) {
	function gravatar($email, $size = 1024, $default = "robohash") {
		return "https://www.gravatar.com/avatar/".md5(strtolower(trim($email)))."?d=".$default."&s=".$size."&r=x";
	}
}
$link  = 'https://lynchstore.xyz/lisensi/lisensi.php';
if (!function_exists('uri')) {
    function uri($segment = null) {
        $uri = explode('?', ($_SERVER['REQUEST_URI']))[0];
        if ($segment === null) {
            return $uri;
        } else {
            $segments = explode('/', $uri);
            
            return $segments[$segment];
        }
    }
}

function ajaxlib($x) {
    return base_url('admin/ajax/'.$x);
}
function base64($type,$data) {
    if($type == 'encode') return base64_encode($data);
    if($type == 'decode') return base64_decode($data);
    if($type == 'check') return (base64_encode(base64_decode($data)) == $data) ? (!trim(stripslashes(strip_tags(htmlspecialchars(base64_decode($data),ENT_QUOTES))))) ? false : true : false;
    if($type == 'auto') return (base64_encode(base64_decode($data)) == $data) ? (!trim(stripslashes(strip_tags(htmlspecialchars(base64_decode($data),ENT_QUOTES))))) ? base64_encode($data) : $data : base64_encode($data);
    if(!in_array($type,['encode','decode','check','auto'])) return false;
}

function client_ip() {
    if(getenv('HTTP_CLIENT_IP')) { $str = getenv('HTTP_CLIENT_IP'); } 
    else if(getenv('HTTP_X_FORWARDED_FOR')) { $str = getenv('HTTP_X_FORWARDED_FOR'); } 
    else if(getenv('HTTP_X_FORWARDED')) { $str = getenv('HTTP_X_FORWARDED'); } 
    else if(getenv('HTTP_FORWARDED_FOR')) { $str = getenv('HTTP_FORWARDED_FOR'); } 
    else if(getenv('HTTP_FORWARDED')) { $str = getenv('HTTP_FORWARDED'); } 
    else if(getenv('REMOTE_ADDR')) { $str = getenv('REMOTE_ADDR'); } 
    else { $str = 'Unknown'; }
    return explode(',',$str)[0];
}

function client_iploc($ip) {
    // bisa pake ip-api.com juga, tapi dilimit 150/menit
    $url = "http://www.geoplugin.net/php.gp?ip=$ip";
    $get = unserialize(file_get_contents($url));
    return (isset($get['geoplugin_city']) && isset($get['geoplugin_countryCode'])) ? $get['geoplugin_city'].' '.$get['geoplugin_countryCode'] : 'Unknown Place';
}

function get_client_browser() {
    $browser = '';
    if(strpos($_SERVER['HTTP_USER_AGENT'], 'Netscape'))
        $browser = 'Netscape';
    else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox'))
        $browser = 'Firefox';
    else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome'))
        $browser = 'Chrome';
    else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Opera'))
        $browser = 'Opera';
    else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE'))
        $browser = 'Internet Explorer';
    else
        $browser = 'Other';
    return $browser;
}

function redirect($sec,$loc) {
    if($sec == 0 || $sec = '0') {
        return header("Location: $loc");
    } else {
        echo "<meta http-equiv='refresh' content='$sec;url=$loc'>";
    }
}

function visited() {
    global $_SERVER;
    if(isset($_SERVER['REDIRECT_URL'])) {
        return 'https://'.$_SERVER['SERVER_NAME'].$_SERVER['REDIRECT_URL'];
    } else {
        return 'https://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
    }
}

function devices() {
    global $_SERVER;
    $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
    if(preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i',$ua)||preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i',substr($ua,0,4))){
        $device = 'Mobile';
    } else {
        $device = 'Desktop';
    }
    return $device;
}

function format_date($lang,$ymd_format) {
    $ymdhis = explode(' ',$ymd_format);
    if($lang == 'id') {
        $month = [
            1 => 'Januari','Februari','Maret','April','Mei','Juni',
            'Juli','Agustus','September','Oktober','November','Desember'
        ];
        $explode = explode('-', $ymdhis[0]);
        $formatted = $explode[2].' '.$month[(int)$explode[1]].' '.$explode[0];
        $format = isset($ymdhis[1]) ? $formatted.' ('.substr($ymdhis[1],0,5).' WIB)' : $formatted;
    } else if($lang == 'en') {
        $month = [
            1 =>   'January','February','March','April','May','June',
            'July','August','September','October','November','December'
        ];
        $explode = explode('-', $ymdhis[0]);
        $formatted = $month[(int)$explode[1]].' '.$explode[2].', '.$explode[0];
        $format = isset($ymdhis[1]) ? $formatted.' ('.substr($ymdhis[1],0,5).' WIB)' : $formatted;
    } else {
        $format = '';
    }
    
    return $format;
}
$res = $curl->connectPost($link,'fj2iadJWrVYcgE6UAyXZsQupG7vIBqOL4zC3RT1t');
function filter_phone($type,$number) {
    $phone = preg_replace("/[^0-9]/", '', filter_entities($number));
    if($type == '0') {
        if(substr($phone,0,3) == '+62'){ $change = '0'.substr($phone,3); }
        else if(substr($phone, 0, 2) == '62'){ $change = '0'.substr($phone,2); }
        else if(substr($phone, 0, 1) == '0') { $change = $phone; }
    } else {
        if(substr($phone,0,3) == '+62'){ $change = substr($phone,1); }
        else if(substr($phone, 0, 2) == '62'){ $change = $phone; }
        else if(substr($phone, 0, 1) == '0') { $change = '62'.substr($phone,1); }
    }
    return $change;
}

function remainDay($expired) {
    date_default_timezone_set('Asia/Jakarta');
    $diff = date_diff(date_create(date("Y-m-d")),date_create($expired));
    $day = str_replace('+','',$diff->format("%R%a"));
    if($day <= 0) {
        return ($day == 0) ? 1 : $day;
    } else {
        return $day;
    }
}

function random_number($length) {
    $str = ""; $characters = array_merge(range('0','9'));
    $max = count($characters) - 1;
    for ($i = 0; $i < $length; $i++) {
        $rand = mt_rand(0, $max);
        $str .= $characters[$rand];
    }
    return $str;
}

function filter($data) {
    global $db;
    return $db->real_escape_string(trim(stripslashes(strip_tags(htmlspecialchars($data,ENT_QUOTES)))));
}

function filter_entities($data) {
    global $db;
    return $db->real_escape_string(trim(stripslashes(strip_tags(htmlspecialchars(htmlentities($data,ENT_QUOTES))))));
}

function infojson($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $data = curl_exec($ch);
        curl_close($ch);
                return $data;
}

function validate_date($date) {
    $d = DateTime::createFromFormat('Y-m-d', $date);
    return $d && $d->format('Y-m-d') == $date;
}
function check_ip($ip,$wl) {
    return (in_array($ip,explode(',',$wl))) ? true : false;
}
