From f43580e1b097e1c31a315be1a7335c886f06229c Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 3 Jun 2025 00:05:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D1=87=D0=B8=D0=BA=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=86=D0=B5=D0=BD=20=D0=BD=D0=B0=20=D1=83=D1=81=D0=BB?= =?UTF-8?q?=D1=83=D0=B3=D0=B8=20=D0=B2=20main.py.=20=D0=A0=D0=B5=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B8=D0=BA=D0=B0=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D1=80=D0=B8=20=D0=BF=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D0=BC=D0=BE=D1=82=D1=80=D0=B5=20=D1=86=D0=B5=D0=BD=20=D0=B8=20?= =?UTF-8?q?=D1=84=D0=BE=D1=80=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=81=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B5=D0=B9=20=D0=BE=20=D1=86=D0=B5=D0=BD=D0=B0?= =?UTF-8?q?=D1=85=20=D0=BD=D0=B0=20=D1=83=D1=81=D0=BB=D1=83=D0=B3=D0=B8.?= =?UTF-8?q?=20=D0=AD=D1=82=D0=B8=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B0=D1=8E?= =?UTF-8?q?=D1=82=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D0=B2=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=B8=20=D0=B2=D0=B7?= =?UTF-8?q?=D0=B0=D0=B8=D0=BC=D0=BE=D0=B4=D0=B5=D0=B9=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=D0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db_sql/log_payment.sql | 100 +++++++++++++++++++++++++++++++++++++++++ main.py | 56 +++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 db_sql/log_payment.sql diff --git a/db_sql/log_payment.sql b/db_sql/log_payment.sql new file mode 100644 index 0000000..41da81a --- /dev/null +++ b/db_sql/log_payment.sql @@ -0,0 +1,100 @@ +-- +-- , , VIN + +-- ID +CREATE SEQUENCE payment_logs_seq + START WITH 1 + INCREMENT BY 1 + CACHE 100; + +-- +CREATE TABLE payment_logs ( + log_id NUMBER PRIMARY KEY, + + -- + user_id NUMBER NOT NULL, + user_first_name VARCHAR2(255), + user_last_name VARCHAR2(255), + user_username VARCHAR2(255), + user_language_code VARCHAR2(10), + user_is_premium NUMBER(1) DEFAULT 0, + + -- + service_type VARCHAR2(50) NOT NULL, -- 'decode_vin', 'check_salvage', 'get_photos' + vin_number VARCHAR2(17) NOT NULL, + + -- + payment_amount NUMBER(10,2) NOT NULL, + transaction_id VARCHAR2(255), + payment_status VARCHAR2(50) DEFAULT 'completed', -- 'completed', 'pending', 'failed' + payment_currency VARCHAR2(10) DEFAULT 'XTR', + + -- + service_status VARCHAR2(50), -- 'success', 'failed', 'no_data', 'error' + data_found_count NUMBER DEFAULT 0, -- / + refund_status VARCHAR2(50) DEFAULT 'no_refund', -- 'no_refund', 'auto_refund', 'manual_refund', 'admin_refund' + refund_reason VARCHAR2(500), + + -- + vehicle_make VARCHAR2(100), + vehicle_model VARCHAR2(100), + vehicle_year VARCHAR2(10), + + -- + error_message CLOB, + created_date DATE DEFAULT SYSDATE, + ip_address VARCHAR2(45), -- + + -- + CONSTRAINT chk_service_type CHECK (service_type IN ('decode_vin', 'check_salvage', 'get_photos')), + CONSTRAINT chk_payment_status CHECK (payment_status IN ('completed', 'pending', 'failed')), + CONSTRAINT chk_service_status CHECK (service_status IN ('success', 'failed', 'no_data', 'error', 'pending')), + CONSTRAINT chk_refund_status CHECK (refund_status IN ('no_refund', 'auto_refund', 'manual_refund', 'admin_refund')) +); + +-- +CREATE INDEX idx_payment_logs_user_id ON payment_logs(user_id); +CREATE INDEX idx_payment_logs_vin ON payment_logs(vin_number); +CREATE INDEX idx_payment_logs_service_type ON payment_logs(service_type); +CREATE INDEX idx_payment_logs_date ON payment_logs(created_date); +CREATE INDEX idx_payment_logs_transaction_id ON payment_logs(transaction_id); +CREATE INDEX idx_payment_logs_status ON payment_logs(payment_status, service_status); + +-- +COMMENT ON TABLE payment_logs IS ' '; +COMMENT ON COLUMN payment_logs.log_id IS ' '; +COMMENT ON COLUMN payment_logs.user_id IS 'ID Telegram'; +COMMENT ON COLUMN payment_logs.service_type IS ' : decode_vin, check_salvage, get_photos'; +COMMENT ON COLUMN payment_logs.vin_number IS 'VIN '; +COMMENT ON COLUMN payment_logs.payment_amount IS ' Telegram Stars'; +COMMENT ON COLUMN payment_logs.transaction_id IS 'ID Telegram'; +COMMENT ON COLUMN payment_logs.data_found_count IS ' (, ..)'; +COMMENT ON COLUMN payment_logs.refund_status IS ' '; +COMMENT ON COLUMN payment_logs.created_date IS ' '; + +-- +CREATE OR REPLACE VIEW payment_analytics AS +SELECT + service_type, + COUNT(*) as total_transactions, + SUM(payment_amount) as total_revenue, + AVG(payment_amount) as avg_payment, + COUNT(CASE WHEN refund_status != 'no_refund' THEN 1 END) as refunds_count, + COUNT(CASE WHEN service_status = 'success' THEN 1 END) as successful_services, + ROUND(COUNT(CASE WHEN service_status = 'success' THEN 1 END) * 100.0 / COUNT(*), 2) as success_rate, + TRUNC(created_date) as date_created +FROM payment_logs +GROUP BY service_type, TRUNC(created_date) +ORDER BY date_created DESC, service_type; + + +-- created_date +CREATE OR REPLACE TRIGGER trg_payment_logs_created_date + BEFORE INSERT ON payment_logs + FOR EACH ROW +BEGIN + IF :NEW.created_date IS NULL THEN + :NEW.created_date := SYSDATE; + END IF; +END; +/ diff --git a/main.py b/main.py index d754ee2..b86c41c 100644 --- a/main.py +++ b/main.py @@ -473,6 +473,62 @@ async def help_callback(callback: CallbackQuery, db: OracleDatabase = None): await callback.answer() +@dp.callback_query(lambda c: c.data == "prices") +async def prices_callback(callback: CallbackQuery, db: OracleDatabase = None): + # Используем переданный db или глобальный oracle_db + database = db or oracle_db + + # Сохраняем данные пользователя при просмотре цен + await database.save_user(callback.from_user, "prices_button") + + prices_text = ( + "💰 **Our Service Prices**\n\n" + + "🔍 **VIN Decoding Service:**\n" + f"• Basic info (make, model, year): **FREE** 🆓\n" + f"• Detailed specifications: **{DECODE_PRICE} ⭐**\n" + f" └ Engine details, transmission, safety features\n" + f" └ Dimensions, construction, brake system\n" + f" └ Lighting, additional features, NCSA data\n\n" + + "🚨 **Salvage Check Service:**\n" + f"• Records count check: **FREE** 🆓\n" + f"• Detailed damage history: **{CHECK_PRICE} ⭐**\n" + f" └ Primary and secondary damage types\n" + f" └ Sale dates and auction locations\n" + f" └ Odometer readings and repair costs\n" + f" └ Engine/drive status information\n\n" + + "📸 **Vehicle Photos Service:**\n" + f"• Photo availability check: **FREE** 🆓\n" + f"• Access to damage photos: **{IMG_PRICE} ⭐**\n" + f" └ High-quality auction images\n" + f" └ Multiple angles of vehicle damage\n" + f" └ Before and after condition photos\n\n" + + "⭐ **Payment Information:**\n" + "• All payments made with **Telegram Stars**\n" + "• Instant delivery after successful payment\n" + "• Automatic refund if no data found\n" + "• Admin users get automatic refunds\n\n" + + "💡 **Money-back guarantee:** If we can't provide the requested data, " + "your payment will be automatically refunded!" + ) + + builder = InlineKeyboardBuilder() + builder.button(text=f"🔍 Decode for {DECODE_PRICE} ⭐", callback_data="decode_vin") + builder.button(text=f"🚨 Check for {CHECK_PRICE} ⭐", callback_data="check_vin") + builder.button(text=f"📸 Photos for {IMG_PRICE} ⭐", callback_data="search_car_photo") + builder.adjust(3) + builder.button(text="ℹ️ Help", callback_data="help") + builder.button(text="🏠 Back to Main Menu", callback_data="main_menu") + builder.adjust(2, 1) + + await callback.message.answer(prices_text, reply_markup=builder.as_markup(), parse_mode="Markdown") + await callback.answer() + + @dp.callback_query(lambda c: c.data and c.data.startswith("pay_detailed_info:")) async def pay_detailed_info_callback(callback: CallbackQuery, db: OracleDatabase = None): # Используем переданный db или глобальный oracle_db