-
-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Paydisini Integration #309
Changes from all commits
dc13870
7b4fbf0
25fb44d
d7d7271
4f0138b
53a1e1d
92e2ed6
a20c3b3
15aa45a
d4fd8a0
9eac66b
97eabb5
26e074c
b48847a
cded4f3
31bbbcf
4f07175
99559bd
abe904a
63fa8e0
6434ae1
5443334
e9b0117
18ab296
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from database import dbname | ||
from typing import Optional | ||
|
||
autopay = dbname["autpay"] | ||
|
||
|
||
async def delete_autopay(uniqueCode: str): | ||
await autopay.delete_one({"_id": uniqueCode}) | ||
|
||
async def get_autopay(self, uniqueCode: str): | ||
exists = await autopay.find_one({"_id": uniqueCode}) | ||
return exists | ||
|
||
async def autopay_update(self, msg_id: Optional[int] = "", note: Optional[str] = "", user_id: Optional[int] = "", amount: Optional[int] = "", status: Optional[str] = "", uniqueCode: Optional[str] = "", createdAt: Optional[str] = ""): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 suggestion (security): Improve type hints and parameter handling The type hints for integer fields (msg_id, user_id, amount) default to empty strings, which is inconsistent. Consider using
|
||
data = {"msg_id": msg_id, "note": note, "user_id": user_id, "amount": amount, "status": status, "createdAt": createdAt} | ||
await autopay.update_one({"_id": uniqueCode}, {"$set": data}, upsert=True) |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,10 +4,12 @@ | |||||
import io | ||||||
import json | ||||||
import os | ||||||
import hashlib | ||||||
import pickle | ||||||
import platform | ||||||
import privatebinapi | ||||||
import re | ||||||
import secrets | ||||||
import sys | ||||||
import traceback | ||||||
from datetime import datetime | ||||||
|
@@ -22,6 +24,7 @@ | |||||
import requests | ||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler | ||||||
from bs4 import BeautifulSoup | ||||||
from urllib.parse import quote | ||||||
from PIL import Image, ImageDraw, ImageFont | ||||||
from psutil import Process, boot_time, cpu_count, cpu_percent | ||||||
from psutil import disk_usage as disk_usage_percent | ||||||
|
@@ -44,6 +47,7 @@ | |||||
LabeledPrice, | ||||||
Message, | ||||||
PreCheckoutQuery, | ||||||
WebAppInfo, | ||||||
) | ||||||
|
||||||
from database.gban_db import add_gban_user, is_gbanned_user, remove_gban_user | ||||||
|
@@ -55,7 +59,8 @@ | |||||
from misskaty.helper.http import fetch | ||||||
from misskaty.helper.human_read import get_readable_file_size, get_readable_time | ||||||
from misskaty.helper.localization import use_chat_lang | ||||||
from misskaty.vars import AUTO_RESTART, COMMAND_HANDLER, LOG_CHANNEL, SUDO | ||||||
from database.payment_db import autopay_update | ||||||
from misskaty.vars import AUTO_RESTART, COMMAND_HANDLER, LOG_CHANNEL, SUDO, PAYDISINI_CHANNEL_ID, PAYDISINI_KEY | ||||||
|
||||||
__MODULE__ = "DevCommand" | ||||||
__HELP__ = """ | ||||||
|
@@ -179,6 +184,45 @@ async def log_file(_, ctx: Message, strings): | |||||
else: | ||||||
await msg.edit_msg("Unsupported parameter") | ||||||
|
||||||
@app.on_message(filters.command(["payment"], COMMAND_HANDLER)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (complexity): Consider refactoring the 'payment' function into smaller, focused helper functions. The
async def create_payment(api_url, api_key, unique_id, service_id, amount, valid_time):
params = {
'key': api_key,
'request': 'new',
'unique_code': unique_id,
'service': service_id,
'amount': amount,
'note': 'MissKaty Support by YS Dev',
'valid_time': valid_time,
'type_fee': '1',
'payment_guide': True,
'signature': hashlib.md5((api_key + unique_id + service_id + amount + valid_time + 'NewTransaction').encode()).hexdigest(),
'return_url': f'https://t.me/{client.me.username}?start'
}
return await fetch.post(api_url, data=params)
def generate_qr_code(data):
return f"https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={quote(data)}"
async def update_payment_db(msg_id, note, user_id, amount, status, unique_code, created_at):
await autopay_update(msg_id, note, user_id, amount, status, unique_code, created_at) By organizing the code in this way, the main |
||||||
async def payment(client: Client, message: Message): | ||||||
api_url = 'https://api.paydisini.co.id/v1/' | ||||||
api_key = PAYDISINI_KEY | ||||||
unique_id = f"VIP-{secrets.token_hex(5)}" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 suggestion (security): Consider using a longer token for the unique ID While using
Suggested change
|
||||||
amount = "10000" if len(message.command) == 1 else str(message.command[1]) | ||||||
id_ = message.from_user.id if message.chat.id != message.from_user.id else message.chat.id | ||||||
valid_time = str(5*60) | ||||||
service_id = PAYDISINI_CHANNEL_ID | ||||||
|
||||||
params = { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (code-quality): Replace f-string with no interpolated values with string ( |
||||||
'key': api_key, | ||||||
'request': 'new', | ||||||
'unique_code': unique_id, | ||||||
'service': service_id, | ||||||
'amount': amount, | ||||||
'note': f'MissKaty Support by YS Dev', | ||||||
'valid_time': valid_time, | ||||||
'type_fee': '1', | ||||||
'payment_guide': True, | ||||||
'signature': hashlib.md5((api_key + unique_id + service_id + amount + valid_time + 'NewTransaction').encode()).hexdigest(), | ||||||
'return_url': f'https://t.me/{client.me.username}?start' | ||||||
} | ||||||
# if id_ in user_data and user_data[id_].get("is_auth"): | ||||||
# return await message.reply("Already Authorized!") | ||||||
rget = await fetch.post(api_url, data=params) | ||||||
if rget.status_code != 200: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Enhance error handling and logging The current error handling is quite basic. Consider implementing more robust error handling and logging. For example, log the specific error message and status code, and provide more informative error messages to the user based on different error scenarios.
|
||||||
return await message.reply("ERROR: Maybe your IP is not whitelisted or have another error from api.") | ||||||
res = rget.json() | ||||||
if not res.get("success"): | ||||||
return await message.reply(res["msg"]) | ||||||
qr_photo = f"https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={quote(res['data']['qr_content'])}" | ||||||
capt = f"𝗠𝗲𝗻𝘂𝗻𝗴𝗴𝘂 𝗽𝗲𝗺𝗯𝗮𝘆𝗮𝗿𝗮𝗻\nKode: {res['data']['unique_code']}\nNote: {res['data']['note']}\nHarga: {res['data']['amount']}\nFee: {res['data']['fee']}\nExpired: {res['data']['expired']}\n\n" | ||||||
payment_guide = f"<b>{res['payment_guide'][0]['title']}:</b>\n" + "\n".join(f"{i+1}. {step}" for i, step in enumerate(res["payment_guide"][0]['content'])) | ||||||
if message.chat.type.value != "private": | ||||||
msg = await message.reply_photo(qr_photo, caption=capt+payment_guide, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(text="Payment Web", url=res["data"]["checkout_url_v2"])]]), quote=True) | ||||||
else: | ||||||
msg = await message.reply_photo(qr_photo, caption=capt+payment_guide, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(text="Payment Web", web_app=WebAppInfo(url=res["data"]["checkout_url_v2"]))]]), quote=True) | ||||||
await autopay_update(msg.id, res["data"]["note"], id_, res['data']['amount'], res['data']['status'], res['data']['unique_code'], res['data']['created_at']) | ||||||
|
||||||
@app.on_message(filters.command(["donate"], COMMAND_HANDLER)) | ||||||
async def donate(self: Client, ctx: Message): | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,3 +34,6 @@ GitPython | |
aiofiles | ||
uvloop==0.19.0 | ||
lxml_html_clean | ||
fastapi | ||
uvicorn | ||
python-multipart |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
from logging import INFO, StreamHandler, basicConfig, getLogger, ERROR, handlers | ||
from os import path | ||
from time import time | ||
from datetime import datetime, timedelta | ||
|
||
from fastapi import FastAPI, Request | ||
from fastapi.responses import HTMLResponse, JSONResponse | ||
from starlette.exceptions import HTTPException | ||
from psutil import boot_time, disk_usage, net_io_counters | ||
from contextlib import suppress | ||
from asyncio import to_thread, subprocess, create_subprocess_shell | ||
from apscheduler.triggers.date import DateTrigger | ||
import hashlib | ||
|
||
api = FastAPI() | ||
|
||
basicConfig( | ||
level=INFO, | ||
format="[%(levelname)s] - [%(asctime)s - %(name)s - %(message)s] -> [%(module)s:%(lineno)d]", | ||
datefmt="%d-%b-%y %H:%M:%S", | ||
handlers=[ | ||
handlers.RotatingFileHandler( | ||
"MissKatyLogs.txt", mode="w+", maxBytes=5242880, backupCount=1 | ||
), | ||
StreamHandler(), | ||
], | ||
) | ||
botStartTime = time() | ||
|
||
LOGGER = getLogger(__name__) | ||
getLogger("fastapi").setLevel(ERROR) | ||
|
||
@api.post("/callback") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 suggestion (security): Enhance security measures for payment callback While you're verifying the IP and signature, consider adding additional security measures such as rate limiting, HTTPS enforcement, and possibly a webhook secret. Also, ensure that all sensitive data is properly sanitized before use.
|
||
async def autopay(request: Request): | ||
from misskaty import app | ||
from database.payment_db import delete_autopay, get_autopay | ||
from misskaty.vars import PAYDISINI_KEY, OWNER_ID | ||
data = await request.form() | ||
client_ip = request.client.host | ||
if PAYDISINI_KEY != data["key"] and client_ip != "84.247.150.90": | ||
raise HTTPException(status_code=403, detail="Access forbidden") | ||
signature_data = f"{PAYDISINI_KEY}{data['unique_code']}CallbackStatus" | ||
gen_signature = hashlib.md5(signature_data.encode()).hexdigest() | ||
if gen_signature != data["signature"]: | ||
raise HTTPException(status_code=403, detail="Invalid Signature") | ||
unique_code = data['unique_code'] | ||
status = data['status'] | ||
exp_date = (datetime.now(jkt) + timedelta(days=30)).strftime("%Y-%m-%d %H:%M:%S") | ||
r = await get_autopay(unique_code) | ||
msg = f"╭────〔 <b>TRANSAKSI SUKSES🎉</b> 〕──\n│・ <b>Transaksi ID :</b> {unique_code}\n│・ <b>Product :</b> MissKaty Support by YS Dev\n│・ <b>Durasi :</b> 30 hari\n│・ <b>Total Dibayar :</b> {r.get('amount')}\n│・ Langganan Berakhir: {exp_date}\n╰─────────" | ||
if not r: | ||
return JSONResponse({"status": false, "data": "Data not found on DB"}, 404) | ||
if status == "Success": | ||
with suppress(Exception): | ||
await bot.send_message(r.get("user_id"), f"{msg}\n\nJika ada pertanyaan silahkan hubungi pemilik bot ini.") | ||
await bot.delete_messages(r.get("user_id"), r.get("msg_id")) | ||
await bot.send_message(OWNER_ID, msg) | ||
await delete_autopay(unique_code) | ||
return JSONResponse({"status": status, "msg": "Pesanan berhasil dibayar oleh customer."}, 200) | ||
else: | ||
with suppress(Exception): | ||
await bot.send_message(r.get("user_id"), "QRIS Telah Expired, Silahkan Buat Transaksi Baru.") | ||
await bot.delete_messages(r.get("user_id"), r.get("msg_id")) | ||
await delete_autopay(unique_code) | ||
return JSONResponse({"status": status, "msg": "Pesanan telah dibatalkan/gagal dibayar."}, 403) | ||
|
||
@api.get("/status") | ||
async def status(): | ||
from misskaty.helper.human_read import get_readable_file_size, get_readable_time | ||
bot_uptime = get_readable_time(time() - botStartTime) | ||
uptime = get_readable_time(time() - boot_time()) | ||
sent = get_readable_file_size(net_io_counters().bytes_sent) | ||
recv = get_readable_file_size(net_io_counters().bytes_recv) | ||
if path.exists(".git"): | ||
commit_date = (await (await create_subprocess_shell("git log -1 --date=format:'%y/%m/%d %H:%M' --pretty=format:'%cd'", stdout=subprocess.PIPE, stderr=subprocess.STDOUT)).communicate())[0].decode() | ||
else: | ||
commit_date = "No UPSTREAM_REPO" | ||
return { | ||
"commit_date": commit_date, | ||
"uptime": uptime, | ||
"on_time": bot_uptime, | ||
"free_disk": get_readable_file_size(disk_usage(".").free), | ||
"total_disk": get_readable_file_size(disk_usage(".").total), | ||
"network": { | ||
"sent": sent, | ||
"recv": recv, | ||
}, | ||
} | ||
|
||
|
||
@api.api_route("/") | ||
async def homepage(): | ||
return "Hello World" | ||
|
||
|
||
@api.exception_handler(HTTPException) | ||
async def page_not_found(request: Request, exc: HTTPException): | ||
return HTMLResponse(content=f"<h1>Error: {exc}</h1>", status_code=exc.status_code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (code-quality): Inline variable that is immediately returned (
inline-immediately-returned-variable
)