From 437fbad5e6e0bc600ce0d61b0343e94678473703 Mon Sep 17 00:00:00 2001 From: Frank Harris Date: Thu, 23 Oct 2025 18:19:06 -0400 Subject: [PATCH] Website is completed working, moved into billing module --- .github/copilot-instructions.md | 4 +- _website/cart.php | 329 ------- _website/includes/login_required.php | 19 - _website/includes/top.php | 18 - _website/logout.php | 57 -- _website/payments/api/README.md | 1 - _website/payments/config.php | 4 - _website/payments/pay.php | 4 - _website/payments/return.php | 4 - _website/payments/webhook.php | 159 ---- _website/paypal/api/README.md | 1 - _website/paypal/config.php | 7 - _website/paypal/pay.php | 103 --- _website/paypal/return.php | 4 - _website/paypal/webhook.php | 161 ---- .../billing/_archived}/CONFIGURATION.md | 0 .../billing/_archived}/FEATURES.md | 0 .../_archived}/IMPLEMENTATION_SUMMARY.md | 0 .../billing/_archived}/README_LOGIN.md | 0 .../billing/_archived}/VISUAL_GUIDE.md | 0 .../ARCHIVE_README.txt | 16 + .../removed-20251023-142000/MOVED_DOCS.md | 3 + .../removed-20251023-202500/MOVED_FILES.json | 75 ++ .../_archived/removed-20251023-202500/ai.php | 325 +++++++ .../data/FREE-548-1761171178.json | 12 + .../data/FREE-549-1761246925.json | 12 + .../data/INV-20250825-170438-e37518.json | 11 + .../data/INV-20250825-174311-0a7993.json | 11 + .../data/NO-INVOICE.json | 10 + .../SIMULATED-WEBHOOK-20251022-101500.json | 0 modules/billing/add_to_cart.php | 305 +++--- {_website => modules/billing}/admin.php | 4 +- .../billing}/admin_config.php | 0 .../billing}/admin_payments.php | 0 .../billing}/adminserverlist.php | 0 {_website => modules/billing}/ai.php | 0 .../billing}/api/capture_order.php | 0 .../billing}/api/create_order.php | 0 modules/billing/bak/add_to_cart.php | 142 --- modules/billing/bak/bill.php | 177 ---- modules/billing/bak/bounce.php | 41 - modules/billing/bak/cart.php | 658 ------------- modules/billing/bak/coupon.php | 103 --- modules/billing/bak/coupons.php | 103 --- modules/billing/bak/create_servers.php | 378 -------- modules/billing/bak/create_servers.php.save | 375 -------- modules/billing/bak/cron-shop.php | 217 ----- modules/billing/bak/ipn.php | 116 --- modules/billing/bak/ipnlog.txt | 1 - modules/billing/bak/module.php | 137 --- modules/billing/bak/navigation.xml | 20 - modules/billing/bak/orders.php | 257 ------ modules/billing/bak/paid.php | 58 -- modules/billing/bak/paypal.php | 119 --- modules/billing/bak/services.php | 355 ------- modules/billing/bak/settings.php | 137 --- modules/billing/bak/shop.php | 325 ------- modules/billing/bak/test-email.php | 66 -- modules/billing/bill.php | 177 ---- modules/billing/bounce.php | 41 - modules/billing/cart.php | 872 ++++++++++-------- modules/billing/create_servers.php.save | 375 -------- modules/billing/cron-shop.php.bak | 217 ----- {_website => modules/billing}/css/header.css | 12 +- .../billing}/data/FREE-548-1761171178.json | 0 modules/billing/data/FREE-549-1761246925.json | 12 + .../data/INV-20250825-170438-e37518.json | 0 .../data/INV-20250825-174311-0a7993.json | 0 .../billing}/data/NO-INVOICE.json | 0 .../SIMULATED-WEBHOOK-20251022-101500.json | 10 + .../billing}/data/webhook.log | 0 modules/billing/diag_remote.php | 72 ++ {_website => modules/billing}/docs/docs.php | 0 .../billing}/docs/games/7-days-to-die.md | 0 .../billing}/docs/games/Parsedown.php | 0 .../billing}/docs/games/_TEMPLATE.MD | 0 .../billing}/docs/games/abiotic-factor.md | 0 .../billing}/docs/games/action-half-life.md | 0 .../billing}/docs/games/action-source.md | 0 .../billing}/docs/games/aliens-vs-predator.md | 0 .../docs/games/all_hostable_games_union.csv | 0 .../billing}/docs/games/aloft.md | 0 .../docs/games/american-truck-simulator.md | 0 .../docs/games/ark-survival-ascended.md | 0 .../docs/games/ark-survival-evolved.md | 0 .../docs/games/arma-2-combined-operations.md | 0 .../docs/games/arma-2-operation-arrowhead.md | 0 .../billing}/docs/games/arma-2.md | 0 .../billing}/docs/games/arma-3.md | 0 .../billing}/docs/games/arma-reforger.md | 0 .../docs/games/assetto-corsa-competizione.md | 0 .../billing}/docs/games/assetto-corsa.md | 0 .../billing}/docs/games/astroneer.md | 0 .../billing}/docs/games/atlas.md | 0 .../billing}/docs/games/avorion.md | 0 .../billing}/docs/games/ballistic-overkill.md | 0 .../billing}/docs/games/barotrauma.md | 0 .../billing}/docs/games/base-defense.md | 0 .../billing}/docs/games/battalion-legacy.md | 0 .../billing}/docs/games/battlefield-1942.md | 0 .../billing}/docs/games/battlefield-3.md | 0 .../billing}/docs/games/battlefield-4.md | 0 .../docs/games/battlefield-vietnam.md | 0 .../docs/games/black-mesa-deathmatch.md | 0 .../billing}/docs/games/blade-symphony.md | 0 .../billing}/docs/games/brainbread-2.md | 0 .../billing}/docs/games/brainbread.md | 0 .../billing}/docs/games/call-of-duty-2.md | 0 .../billing}/docs/games/call-of-duty-4.md | 0 .../games/call-of-duty-united-offensive.md | 0 .../docs/games/call-of-duty-world-at-war.md | 0 .../billing}/docs/games/call-of-duty.md | 0 .../docs/games/chivalry-medieval-warfare.md | 0 .../docs/games/citadel-forged-with-fire.md | 0 .../billing}/docs/games/codename-cure.md | 0 .../billing}/docs/games/colony-survival.md | 0 .../billing}/docs/games/conan-exiles.md | 0 .../billing}/docs/games/core-keeper.md | 0 .../billing}/docs/games/counter-strike-1-6.md | 0 .../billing}/docs/games/counter-strike-2.md | 0 .../games/counter-strike-condition-zero.md | 0 .../games/counter-strike-global-offensive.md | 0 .../docs/games/counter-strike-source.md | 0 .../billing}/docs/games/counter-strike.md | 0 .../billing}/docs/games/craftopia.md | 0 .../billing}/docs/games/cryofall.md | 0 .../docs/games/day-of-defeat-source.md | 0 .../billing}/docs/games/day-of-defeat.md | 0 .../billing}/docs/games/day-of-dragons.md | 0 .../billing}/docs/games/day-of-infamy.md | 0 .../docs/games/dayz-mod-for-arma2co.md | 0 .../billing}/docs/games/dayz-mod.md | 0 .../billing}/docs/games/dayz.md | 0 .../billing}/docs/games/dead-matter.md | 0 .../billing}/docs/games/deadside-console.md | 0 .../billing}/docs/games/deadside.md | 0 .../billing}/docs/games/deathmatch-classic.md | 0 .../docs/games/don-t-starve-together.md | 0 .../docs/games/double-action-boogaloo.md | 0 .../billing}/docs/games/dune-awakening.md | 0 .../billing}/docs/games/dystopia.md | 0 .../billing}/docs/games/eco.md | 0 .../billing}/docs/games/empires-mod.md | 0 .../docs/games/empyrion-galactic-survival.md | 0 .../billing}/docs/games/enshrouded.md | 0 .../billing}/docs/games/et-legacy.md | 0 ...ck-simulator-2-american-truck-simulator.md | 0 .../docs/games/euro-truck-simulator-2.md | 0 .../billing}/docs/games/factorio.md | 0 .../docs/games/farming-simulator-2019.md | 0 .../docs/games/farming-simulator-2022.md | 0 .../docs/games/farming-simulator-2025.md | 0 .../docs/games/farming-simulator-25.md | 0 .../billing}/docs/games/farming-simulator.md | 0 .../billing}/docs/games/fistful-of-frags.md | 0 .../billing}/docs/games/fivem.md | 0 .../billing}/docs/games/game.php | 0 .../billing}/docs/games/garry-s-mod.md | 0 .../billing}/docs/games/ground-branch.md | 0 .../docs/games/half-life-2-deathmatch.md | 0 .../docs/games/half-life-deathmatch-source.md | 0 .../docs/games/half-life-deathmatch.md | 0 .../billing}/docs/games/heat.md | 0 .../billing}/docs/games/hell-let-loose.md | 0 .../billing}/docs/games/humanitz.md | 0 .../billing}/docs/games/hurtworld.md | 0 .../docs/games/hypercharge-unboxed.md | 0 .../billing}/docs/games/icarus.md | 0 .../billing}/docs/games/index.php | 0 .../docs/games/insurgency-sandstorm.md | 0 .../billing}/docs/games/insurgency.md | 0 .../billing}/docs/games/iosoccer.md | 0 .../docs/games/jedi-knight-ii-jedi-outcast.md | 0 .../billing}/docs/games/just-cause-2.md | 0 .../billing}/docs/games/just-cause-3.md | 0 .../billing}/docs/games/killing-floor-2.md | 0 .../billing}/docs/games/killing-floor.md | 0 .../billing}/docs/games/last-oasis.md | 0 .../billing}/docs/games/leap.md | 0 .../billing}/docs/games/left-4-dead-2.md | 0 .../billing}/docs/games/left-4-dead.md | 0 .../billing}/docs/games/life-is-feudal.md | 0 .../billing}/docs/games/longvinter.md | 0 .../games/medal-of-honor-allied-assault.md | 0 .../billing}/docs/games/memories-of-mars.md | 0 .../docs/games/minecraft-bedrock-edition.md | 0 .../docs/games/minecraft-java-edition.md | 0 .../billing}/docs/games/minecraft.md | 0 .../billing}/docs/games/mordhau.md | 0 .../billing}/docs/games/multi-theft-auto.md | 0 .../billing}/docs/games/mumble.md | 0 .../billing}/docs/games/myth-of-empires.md | 0 .../docs/games/natural-selection-2.md | 0 .../billing}/docs/games/natural-selection.md | 0 .../billing}/docs/games/necesse.md | 0 .../docs/games/no-more-room-in-hell.md | 0 .../billing}/docs/games/ns2-combat.md | 0 .../billing}/docs/games/nuclear-dawn.md | 0 .../billing}/docs/games/onset.md | 0 .../docs/games/operation-harsh-doorstop.md | 0 .../billing}/docs/games/opposing-force.md | 0 .../docs/games/outlaws-of-the-old-west.md | 0 .../billing}/docs/games/outpost-zero.md | 0 .../billing}/docs/games/palworld-xbox.md | 0 .../billing}/docs/games/palworld.md | 0 .../billing}/docs/games/papermc.md | 0 .../billing}/docs/games/path-of-titans.md | 0 .../billing}/docs/games/pavlov-vr.md | 0 .../docs/games/pirates-vikings-knights-ii.md | 0 .../billing}/docs/games/pixark.md | 0 .../billing}/docs/games/project-cars-2.md | 0 .../billing}/docs/games/project-cars.md | 0 .../billing}/docs/games/project-zomboid.md | 0 .../billing}/docs/games/quake-2.md | 0 .../billing}/docs/games/quake-3-arena.md | 0 .../billing}/docs/games/quake-4.md | 0 .../billing}/docs/games/quake-live.md | 0 .../billing}/docs/games/quake-world.md | 0 .../games/red-orchestra-ostfront-41-45.md | 0 .../billing}/docs/games/redm.md | 0 .../billing}/docs/games/reign-of-kings.md | 0 .../billing}/docs/games/rem-survival.md | 0 .../games/return-to-castle-wolfenstein.md | 0 .../billing}/docs/games/ricochet.md | 0 .../billing}/docs/games/rising-world.md | 0 .../billing}/docs/games/risk-of-rain-2.md | 0 .../billing}/docs/games/rust.md | 0 .../docs/games/san-andreas-multiplayer.md | 0 .../billing}/docs/games/satisfactory.md | 0 .../games/scp-secret-laboratory-servermod.md | 0 .../docs/games/scp-secret-laboratory.md | 0 .../billing}/docs/games/scum.md | 0 .../docs/games/skyrim-together-reborn-mod.md | 0 .../billing}/docs/games/soldat.md | 0 .../soldier-of-fortune-2-double-helix-gold.md | 0 .../billing}/docs/games/sons-of-the-forest.md | 0 .../billing}/docs/games/soulmask.md | 0 .../docs/games/source-forts-classic.md | 0 .../billing}/docs/games/space-engineers.md | 0 .../billing}/docs/games/squad-44.md | 0 .../billing}/docs/games/squad.md | 0 .../billing}/docs/games/starbound.md | 0 .../billing}/docs/games/stationeers.md | 0 .../billing}/docs/games/staxel.md | 0 .../billing}/docs/games/stickybots.md | 0 .../billing}/docs/games/subsistence.md | 0 .../billing}/docs/games/survive-the-nights.md | 0 .../billing}/docs/games/sven-co-op.md | 0 .../billing}/docs/games/team-fortress-2.md | 0 .../docs/games/team-fortress-classic.md | 0 .../billing}/docs/games/teamspeak-3.md | 0 .../billing}/docs/games/tebex.md | 0 .../billing}/docs/games/teeworlds.md | 0 .../billing}/docs/games/terraria.md | 0 .../billing}/docs/games/terratech-worlds.md | 0 .../billing}/docs/games/the-forest.md | 0 .../billing}/docs/games/the-front.md | 0 .../billing}/docs/games/the-isle.md | 0 .../the-lord-of-the-rings-return-to-moria.md | 0 .../billing}/docs/games/the-specialists.md | 0 .../billing}/docs/games/tinkertown.md | 0 .../billing}/docs/games/tower-unite.md | 0 .../docs/games/unreal-tournament-2004.md | 0 .../docs/games/unreal-tournament-3.md | 0 .../docs/games/unreal-tournament-99.md | 0 .../billing}/docs/games/unreal-tournament.md | 0 .../billing}/docs/games/unturned.md | 0 .../billing}/docs/games/urban-terror-4.md | 0 .../billing}/docs/games/v-rising.md | 0 .../billing}/docs/games/valheim.md | 0 .../billing}/docs/games/vampire-slayer.md | 0 .../billing}/docs/games/velocity-proxy.md | 0 .../billing}/docs/games/vintage-story.md | 0 .../billing}/docs/games/warfork.md | 0 .../billing}/docs/games/warsow.md | 0 .../billing}/docs/games/waterfallmc.md | 0 .../docs/games/wolfenstein-enemy-territory.md | 0 .../billing}/docs/games/world-titans-war.md | 0 .../billing}/docs/games/wreckfest-2.md | 0 .../billing}/docs/games/wreckfest.md | 0 .../billing}/docs/games/wurm-unlimited.md | 0 .../billing}/docs/games/xonotic.md | 0 .../docs/games/zombie-master-reborn.md | 0 .../docs/games/zombie-panic-source.md | 0 {_website => modules/billing}/docs/server.php | 0 .../billing}/forgot_password.php | 0 .../billing}/images/banner.png | Bin .../billing}/images/bf3_the_russian.jpg | Bin {_website => modules/billing}/images/dark.jpg | Bin modules/billing/images/featured/7dtd.jpg | Bin 0 -> 40986 bytes modules/billing/images/featured/arkse.jpg | Bin 0 -> 133659 bytes .../featured/arma2_operation_arrowhead.jpg | Bin 0 -> 52834 bytes modules/billing/images/featured/arma_3.jpg | Bin 0 -> 33192 bytes modules/billing/images/featured/cs_go.jpg | Bin 0 -> 23981 bytes modules/billing/images/featured/day_z.jpg | Bin 0 -> 28105 bytes .../billing/images/featured/dayz_epochmod.jpg | Bin 0 -> 6556 bytes modules/billing/images/featured/dayz_mod.jpg | Bin 0 -> 37224 bytes .../billing/images/featured/eurotruck2.jpg | Bin 0 -> 41153 bytes .../images/featured/fistful_of_frags.jpg | Bin 0 -> 52615 bytes .../billing/images/featured/insurgency.jpg | Bin 0 -> 34600 bytes .../images/featured/insurgency_sandstorm.jpg | Bin 0 -> 39090 bytes modules/billing/images/featured/minecraft.jpg | Bin 0 -> 53010 bytes modules/billing/images/games/7dtd.jpg | Bin 0 -> 40986 bytes modules/billing/images/games/arkse.jpg | Bin 0 -> 133659 bytes modules/billing/images/games/arma2.jpg | Bin 0 -> 40955 bytes .../games/arma2_operation_arrowhead.jpg | Bin 0 -> 52834 bytes modules/billing/images/games/arma_3.jpg | Bin 0 -> 33192 bytes modules/billing/images/games/asseto.jpg | Bin 0 -> 50562 bytes modules/billing/images/games/avorion.jpg | Bin 0 -> 32342 bytes modules/billing/images/games/brainbread_2.jpg | Bin 0 -> 175402 bytes modules/billing/images/games/chivalry.jpg | Bin 0 -> 115345 bytes modules/billing/images/games/citadel.jpg | Bin 0 -> 59357 bytes .../billing/images/games/colonysurvival.jpg | Bin 0 -> 47610 bytes modules/billing/images/games/conanexiles.jpg | Bin 0 -> 37168 bytes modules/billing/images/games/cs_go.jpg | Bin 0 -> 23981 bytes modules/billing/images/games/cstrike.jpg | Bin 0 -> 28138 bytes .../billing/images/games/cstrikesource.jpg | Bin 0 -> 18125 bytes .../images/games/day_of_defeat_source.jpg | Bin 0 -> 25387 bytes modules/billing/images/games/day_z.jpg | Bin 0 -> 28105 bytes .../billing/images/games/dayz_epochmod.jpg | Bin 0 -> 6556 bytes modules/billing/images/games/dayz_mod.jpg | Bin 0 -> 37224 bytes .../images/games/deathmatch_classic.jpg | Bin 0 -> 32228 bytes modules/billing/images/games/dst.jpg | Bin 0 -> 56997 bytes modules/billing/images/games/eco.jpg | Bin 0 -> 44396 bytes modules/billing/images/games/eurotruck2.jpg | Bin 0 -> 41153 bytes .../billing/images/games/fistful_of_frags.jpg | Bin 0 -> 52615 bytes modules/billing/images/games/garrys_mod.jpg | Bin 0 -> 20463 bytes .../images/games/half-life2_deathmatch.jpg | Bin 0 -> 25477 bytes modules/billing/images/games/harsh.jpg | Bin 0 -> 39125 bytes modules/billing/images/games/hurt_world.jpg | Bin 0 -> 35501 bytes modules/billing/images/games/insurgency.jpg | Bin 0 -> 34600 bytes .../images/games/insurgency_sandstorm.jpg | Bin 0 -> 39090 bytes .../billing/images/games/killing_floor.jpg | Bin 0 -> 28352 bytes .../billing/images/games/killing_floor_2.jpg | Bin 0 -> 57305 bytes modules/billing/images/games/left_4_dead.jpg | Bin 0 -> 57336 bytes .../billing/images/games/left_4_dead_2.jpg | Bin 0 -> 39230 bytes modules/billing/images/games/minecraft.jpg | Bin 0 -> 53010 bytes .../images/games/miscreated_server.jpg | Bin 0 -> 35617 bytes modules/billing/images/games/mordhau.jpg | Bin 0 -> 51057 bytes .../billing/images/games/nomoreroominhell.jpg | Bin 0 -> 41993 bytes modules/billing/images/games/ootow.jpg | Bin 0 -> 43686 bytes modules/billing/images/games/rust_header.jpg | Bin 0 -> 15212 bytes modules/billing/images/games/scp.jpg | Bin 0 -> 29895 bytes modules/billing/images/games/squad.jpg | Bin 0 -> 60703 bytes modules/billing/images/games/starbound.jpg | Bin 0 -> 25697 bytes modules/billing/images/games/stationeers.jpg | Bin 0 -> 44708 bytes .../billing/images/games/team_fortress_2.jpg | Bin 0 -> 55689 bytes modules/billing/images/games/terraria.jpg | Bin 0 -> 62177 bytes modules/billing/images/games/urt.jpg | Bin 0 -> 26474 bytes modules/billing/images/games/valheim.jpg | Bin 0 -> 50327 bytes modules/billing/images/games/wurmu.jpg | Bin 0 -> 47158 bytes .../billing}/images/logo-sm.png | Bin modules/billing/images/logo.jpg | Bin 0 -> 54815 bytes modules/billing/images/logo.png | Bin 0 -> 302940 bytes .../billing}/includes/README.md | 0 .../billing}/includes/admin_auth.php | 0 .../billing}/includes/cart_helper.php | 0 .../billing}/includes/config.inc.php | 0 .../billing}/includes/footer.php | 0 modules/billing/includes/log.php | 33 + modules/billing/includes/login_required.php | 11 + .../billing}/includes/menu.php | 40 +- modules/billing/includes/top.php | 6 + {_website => modules/billing}/index.php | 0 {_website => modules/billing}/invoices.php | 0 modules/billing/ipn.php | 116 --- modules/billing/ipnlog.txt | 1 - {_website => modules/billing}/logfile.txt | 1 + {_website => modules/billing}/login.php | 121 +-- modules/billing/logout.php | 32 + modules/billing/logs/2025-10-23.log | 1 + modules/billing/logs/add_to_cart.log | 21 + modules/billing/logs/add_to_cart_requests.log | 12 + modules/billing/logs/free_create_audit.log | 1 + {_website => modules/billing}/my_servers.php | 0 modules/billing/navigation.xml | 20 - {_website => modules/billing}/order.php | 19 +- modules/billing/orders.php | 265 ------ modules/billing/paid.php | 58 -- modules/billing/payment_success.php | 151 +++ modules/billing/paypal.php | 119 --- {_website => modules/billing}/privacy.php | 0 {_website => modules/billing}/register.php | 0 .../billing}/renew_server.php | 0 .../billing}/reset_password.php | 0 {_website => modules/billing}/return.php | 0 .../billing}/server_status.php | 0 {_website => modules/billing}/serverlist.php | 0 modules/billing/services.php | 355 ------- modules/billing/settings.php | 137 --- modules/billing/shop.php | 325 ------- modules/billing/test-email.php | 66 -- .../billing}/test_db_connection.php | 0 .../billing}/tools/check_db_user.php | 0 .../tools/check_invoices_redirect.php | 0 .../billing}/tools/check_logout_redirect.php | 0 .../tools/debug_invoices_redirect.php | 0 .../billing}/tools/simulate_webhook.php | 0 {_website => modules/billing}/tos.php | 0 {_website => modules/billing}/webhook.php | 30 +- modules/dashboard/dashboard.php | 480 +++++----- 401 files changed, 1822 insertions(+), 7831 deletions(-) delete mode 100644 _website/cart.php delete mode 100644 _website/includes/login_required.php delete mode 100644 _website/includes/top.php delete mode 100644 _website/logout.php delete mode 100644 _website/payments/api/README.md delete mode 100644 _website/payments/config.php delete mode 100644 _website/payments/pay.php delete mode 100644 _website/payments/return.php delete mode 100644 _website/payments/webhook.php delete mode 100644 _website/paypal/api/README.md delete mode 100644 _website/paypal/config.php delete mode 100644 _website/paypal/pay.php delete mode 100644 _website/paypal/return.php delete mode 100644 _website/paypal/webhook.php rename {_website => modules/billing/_archived}/CONFIGURATION.md (100%) rename {_website => modules/billing/_archived}/FEATURES.md (100%) rename {_website => modules/billing/_archived}/IMPLEMENTATION_SUMMARY.md (100%) rename {_website => modules/billing/_archived}/README_LOGIN.md (100%) rename {_website => modules/billing/_archived}/VISUAL_GUIDE.md (100%) create mode 100644 modules/billing/_archived/removed-20251023-142000/ARCHIVE_README.txt create mode 100644 modules/billing/_archived/removed-20251023-142000/MOVED_DOCS.md create mode 100644 modules/billing/_archived/removed-20251023-202500/MOVED_FILES.json create mode 100644 modules/billing/_archived/removed-20251023-202500/ai.php create mode 100644 modules/billing/_archived/removed-20251023-202500/data/FREE-548-1761171178.json create mode 100644 modules/billing/_archived/removed-20251023-202500/data/FREE-549-1761246925.json create mode 100644 modules/billing/_archived/removed-20251023-202500/data/INV-20250825-170438-e37518.json create mode 100644 modules/billing/_archived/removed-20251023-202500/data/INV-20250825-174311-0a7993.json create mode 100644 modules/billing/_archived/removed-20251023-202500/data/NO-INVOICE.json rename {_website => modules/billing/_archived/removed-20251023-202500}/data/SIMULATED-WEBHOOK-20251022-101500.json (100%) rename {_website => modules/billing}/admin.php (91%) rename {_website => modules/billing}/admin_config.php (100%) rename {_website => modules/billing}/admin_payments.php (100%) rename {_website => modules/billing}/adminserverlist.php (100%) rename {_website => modules/billing}/ai.php (100%) rename {_website => modules/billing}/api/capture_order.php (100%) rename {_website => modules/billing}/api/create_order.php (100%) delete mode 100644 modules/billing/bak/add_to_cart.php delete mode 100644 modules/billing/bak/bill.php delete mode 100644 modules/billing/bak/bounce.php delete mode 100644 modules/billing/bak/cart.php delete mode 100644 modules/billing/bak/coupon.php delete mode 100644 modules/billing/bak/coupons.php delete mode 100644 modules/billing/bak/create_servers.php delete mode 100644 modules/billing/bak/create_servers.php.save delete mode 100644 modules/billing/bak/cron-shop.php delete mode 100644 modules/billing/bak/ipn.php delete mode 100644 modules/billing/bak/ipnlog.txt delete mode 100644 modules/billing/bak/module.php delete mode 100644 modules/billing/bak/navigation.xml delete mode 100644 modules/billing/bak/orders.php delete mode 100644 modules/billing/bak/paid.php delete mode 100644 modules/billing/bak/paypal.php delete mode 100644 modules/billing/bak/services.php delete mode 100644 modules/billing/bak/settings.php delete mode 100644 modules/billing/bak/shop.php delete mode 100644 modules/billing/bak/test-email.php delete mode 100644 modules/billing/bill.php delete mode 100644 modules/billing/bounce.php delete mode 100644 modules/billing/create_servers.php.save delete mode 100644 modules/billing/cron-shop.php.bak rename {_website => modules/billing}/css/header.css (90%) rename {_website => modules/billing}/data/FREE-548-1761171178.json (100%) create mode 100644 modules/billing/data/FREE-549-1761246925.json rename {_website => modules/billing}/data/INV-20250825-170438-e37518.json (100%) rename {_website => modules/billing}/data/INV-20250825-174311-0a7993.json (100%) rename {_website => modules/billing}/data/NO-INVOICE.json (100%) create mode 100644 modules/billing/data/SIMULATED-WEBHOOK-20251022-101500.json rename {_website => modules/billing}/data/webhook.log (100%) create mode 100644 modules/billing/diag_remote.php rename {_website => modules/billing}/docs/docs.php (100%) rename {_website => modules/billing}/docs/games/7-days-to-die.md (100%) rename {_website => modules/billing}/docs/games/Parsedown.php (100%) rename {_website => modules/billing}/docs/games/_TEMPLATE.MD (100%) rename {_website => modules/billing}/docs/games/abiotic-factor.md (100%) rename {_website => modules/billing}/docs/games/action-half-life.md (100%) rename {_website => modules/billing}/docs/games/action-source.md (100%) rename {_website => modules/billing}/docs/games/aliens-vs-predator.md (100%) rename {_website => modules/billing}/docs/games/all_hostable_games_union.csv (100%) rename {_website => modules/billing}/docs/games/aloft.md (100%) rename {_website => modules/billing}/docs/games/american-truck-simulator.md (100%) rename {_website => modules/billing}/docs/games/ark-survival-ascended.md (100%) rename {_website => modules/billing}/docs/games/ark-survival-evolved.md (100%) rename {_website => modules/billing}/docs/games/arma-2-combined-operations.md (100%) rename {_website => modules/billing}/docs/games/arma-2-operation-arrowhead.md (100%) rename {_website => modules/billing}/docs/games/arma-2.md (100%) rename {_website => modules/billing}/docs/games/arma-3.md (100%) rename {_website => modules/billing}/docs/games/arma-reforger.md (100%) rename {_website => modules/billing}/docs/games/assetto-corsa-competizione.md (100%) rename {_website => modules/billing}/docs/games/assetto-corsa.md (100%) rename {_website => modules/billing}/docs/games/astroneer.md (100%) rename {_website => modules/billing}/docs/games/atlas.md (100%) rename {_website => modules/billing}/docs/games/avorion.md (100%) rename {_website => modules/billing}/docs/games/ballistic-overkill.md (100%) rename {_website => modules/billing}/docs/games/barotrauma.md (100%) rename {_website => modules/billing}/docs/games/base-defense.md (100%) rename {_website => modules/billing}/docs/games/battalion-legacy.md (100%) rename {_website => modules/billing}/docs/games/battlefield-1942.md (100%) rename {_website => modules/billing}/docs/games/battlefield-3.md (100%) rename {_website => modules/billing}/docs/games/battlefield-4.md (100%) rename {_website => modules/billing}/docs/games/battlefield-vietnam.md (100%) rename {_website => modules/billing}/docs/games/black-mesa-deathmatch.md (100%) rename {_website => modules/billing}/docs/games/blade-symphony.md (100%) rename {_website => modules/billing}/docs/games/brainbread-2.md (100%) rename {_website => modules/billing}/docs/games/brainbread.md (100%) rename {_website => modules/billing}/docs/games/call-of-duty-2.md (100%) rename {_website => modules/billing}/docs/games/call-of-duty-4.md (100%) rename {_website => modules/billing}/docs/games/call-of-duty-united-offensive.md (100%) rename {_website => modules/billing}/docs/games/call-of-duty-world-at-war.md (100%) rename {_website => modules/billing}/docs/games/call-of-duty.md (100%) rename {_website => modules/billing}/docs/games/chivalry-medieval-warfare.md (100%) rename {_website => modules/billing}/docs/games/citadel-forged-with-fire.md (100%) rename {_website => modules/billing}/docs/games/codename-cure.md (100%) rename {_website => modules/billing}/docs/games/colony-survival.md (100%) rename {_website => modules/billing}/docs/games/conan-exiles.md (100%) rename {_website => modules/billing}/docs/games/core-keeper.md (100%) rename {_website => modules/billing}/docs/games/counter-strike-1-6.md (100%) rename {_website => modules/billing}/docs/games/counter-strike-2.md (100%) rename {_website => modules/billing}/docs/games/counter-strike-condition-zero.md (100%) rename {_website => modules/billing}/docs/games/counter-strike-global-offensive.md (100%) rename {_website => modules/billing}/docs/games/counter-strike-source.md (100%) rename {_website => modules/billing}/docs/games/counter-strike.md (100%) rename {_website => modules/billing}/docs/games/craftopia.md (100%) rename {_website => modules/billing}/docs/games/cryofall.md (100%) rename {_website => modules/billing}/docs/games/day-of-defeat-source.md (100%) rename {_website => modules/billing}/docs/games/day-of-defeat.md (100%) rename {_website => modules/billing}/docs/games/day-of-dragons.md (100%) rename {_website => modules/billing}/docs/games/day-of-infamy.md (100%) rename {_website => modules/billing}/docs/games/dayz-mod-for-arma2co.md (100%) rename {_website => modules/billing}/docs/games/dayz-mod.md (100%) rename {_website => modules/billing}/docs/games/dayz.md (100%) rename {_website => modules/billing}/docs/games/dead-matter.md (100%) rename {_website => modules/billing}/docs/games/deadside-console.md (100%) rename {_website => modules/billing}/docs/games/deadside.md (100%) rename {_website => modules/billing}/docs/games/deathmatch-classic.md (100%) rename {_website => modules/billing}/docs/games/don-t-starve-together.md (100%) rename {_website => modules/billing}/docs/games/double-action-boogaloo.md (100%) rename {_website => modules/billing}/docs/games/dune-awakening.md (100%) rename {_website => modules/billing}/docs/games/dystopia.md (100%) rename {_website => modules/billing}/docs/games/eco.md (100%) rename {_website => modules/billing}/docs/games/empires-mod.md (100%) rename {_website => modules/billing}/docs/games/empyrion-galactic-survival.md (100%) rename {_website => modules/billing}/docs/games/enshrouded.md (100%) rename {_website => modules/billing}/docs/games/et-legacy.md (100%) rename {_website => modules/billing}/docs/games/euro-truck-simulator-2-american-truck-simulator.md (100%) rename {_website => modules/billing}/docs/games/euro-truck-simulator-2.md (100%) rename {_website => modules/billing}/docs/games/factorio.md (100%) rename {_website => modules/billing}/docs/games/farming-simulator-2019.md (100%) rename {_website => modules/billing}/docs/games/farming-simulator-2022.md (100%) rename {_website => modules/billing}/docs/games/farming-simulator-2025.md (100%) rename {_website => modules/billing}/docs/games/farming-simulator-25.md (100%) rename {_website => modules/billing}/docs/games/farming-simulator.md (100%) rename {_website => modules/billing}/docs/games/fistful-of-frags.md (100%) rename {_website => modules/billing}/docs/games/fivem.md (100%) rename {_website => modules/billing}/docs/games/game.php (100%) rename {_website => modules/billing}/docs/games/garry-s-mod.md (100%) rename {_website => modules/billing}/docs/games/ground-branch.md (100%) rename {_website => modules/billing}/docs/games/half-life-2-deathmatch.md (100%) rename {_website => modules/billing}/docs/games/half-life-deathmatch-source.md (100%) rename {_website => modules/billing}/docs/games/half-life-deathmatch.md (100%) rename {_website => modules/billing}/docs/games/heat.md (100%) rename {_website => modules/billing}/docs/games/hell-let-loose.md (100%) rename {_website => modules/billing}/docs/games/humanitz.md (100%) rename {_website => modules/billing}/docs/games/hurtworld.md (100%) rename {_website => modules/billing}/docs/games/hypercharge-unboxed.md (100%) rename {_website => modules/billing}/docs/games/icarus.md (100%) rename {_website => modules/billing}/docs/games/index.php (100%) rename {_website => modules/billing}/docs/games/insurgency-sandstorm.md (100%) rename {_website => modules/billing}/docs/games/insurgency.md (100%) rename {_website => modules/billing}/docs/games/iosoccer.md (100%) rename {_website => modules/billing}/docs/games/jedi-knight-ii-jedi-outcast.md (100%) rename {_website => modules/billing}/docs/games/just-cause-2.md (100%) rename {_website => modules/billing}/docs/games/just-cause-3.md (100%) rename {_website => modules/billing}/docs/games/killing-floor-2.md (100%) rename {_website => modules/billing}/docs/games/killing-floor.md (100%) rename {_website => modules/billing}/docs/games/last-oasis.md (100%) rename {_website => modules/billing}/docs/games/leap.md (100%) rename {_website => modules/billing}/docs/games/left-4-dead-2.md (100%) rename {_website => modules/billing}/docs/games/left-4-dead.md (100%) rename {_website => modules/billing}/docs/games/life-is-feudal.md (100%) rename {_website => modules/billing}/docs/games/longvinter.md (100%) rename {_website => modules/billing}/docs/games/medal-of-honor-allied-assault.md (100%) rename {_website => modules/billing}/docs/games/memories-of-mars.md (100%) rename {_website => modules/billing}/docs/games/minecraft-bedrock-edition.md (100%) rename {_website => modules/billing}/docs/games/minecraft-java-edition.md (100%) rename {_website => modules/billing}/docs/games/minecraft.md (100%) rename {_website => modules/billing}/docs/games/mordhau.md (100%) rename {_website => modules/billing}/docs/games/multi-theft-auto.md (100%) rename {_website => modules/billing}/docs/games/mumble.md (100%) rename {_website => modules/billing}/docs/games/myth-of-empires.md (100%) rename {_website => modules/billing}/docs/games/natural-selection-2.md (100%) rename {_website => modules/billing}/docs/games/natural-selection.md (100%) rename {_website => modules/billing}/docs/games/necesse.md (100%) rename {_website => modules/billing}/docs/games/no-more-room-in-hell.md (100%) rename {_website => modules/billing}/docs/games/ns2-combat.md (100%) rename {_website => modules/billing}/docs/games/nuclear-dawn.md (100%) rename {_website => modules/billing}/docs/games/onset.md (100%) rename {_website => modules/billing}/docs/games/operation-harsh-doorstop.md (100%) rename {_website => modules/billing}/docs/games/opposing-force.md (100%) rename {_website => modules/billing}/docs/games/outlaws-of-the-old-west.md (100%) rename {_website => modules/billing}/docs/games/outpost-zero.md (100%) rename {_website => modules/billing}/docs/games/palworld-xbox.md (100%) rename {_website => modules/billing}/docs/games/palworld.md (100%) rename {_website => modules/billing}/docs/games/papermc.md (100%) rename {_website => modules/billing}/docs/games/path-of-titans.md (100%) rename {_website => modules/billing}/docs/games/pavlov-vr.md (100%) rename {_website => modules/billing}/docs/games/pirates-vikings-knights-ii.md (100%) rename {_website => modules/billing}/docs/games/pixark.md (100%) rename {_website => modules/billing}/docs/games/project-cars-2.md (100%) rename {_website => modules/billing}/docs/games/project-cars.md (100%) rename {_website => modules/billing}/docs/games/project-zomboid.md (100%) rename {_website => modules/billing}/docs/games/quake-2.md (100%) rename {_website => modules/billing}/docs/games/quake-3-arena.md (100%) rename {_website => modules/billing}/docs/games/quake-4.md (100%) rename {_website => modules/billing}/docs/games/quake-live.md (100%) rename {_website => modules/billing}/docs/games/quake-world.md (100%) rename {_website => modules/billing}/docs/games/red-orchestra-ostfront-41-45.md (100%) rename {_website => modules/billing}/docs/games/redm.md (100%) rename {_website => modules/billing}/docs/games/reign-of-kings.md (100%) rename {_website => modules/billing}/docs/games/rem-survival.md (100%) rename {_website => modules/billing}/docs/games/return-to-castle-wolfenstein.md (100%) rename {_website => modules/billing}/docs/games/ricochet.md (100%) rename {_website => modules/billing}/docs/games/rising-world.md (100%) rename {_website => modules/billing}/docs/games/risk-of-rain-2.md (100%) rename {_website => modules/billing}/docs/games/rust.md (100%) rename {_website => modules/billing}/docs/games/san-andreas-multiplayer.md (100%) rename {_website => modules/billing}/docs/games/satisfactory.md (100%) rename {_website => modules/billing}/docs/games/scp-secret-laboratory-servermod.md (100%) rename {_website => modules/billing}/docs/games/scp-secret-laboratory.md (100%) rename {_website => modules/billing}/docs/games/scum.md (100%) rename {_website => modules/billing}/docs/games/skyrim-together-reborn-mod.md (100%) rename {_website => modules/billing}/docs/games/soldat.md (100%) rename {_website => modules/billing}/docs/games/soldier-of-fortune-2-double-helix-gold.md (100%) rename {_website => modules/billing}/docs/games/sons-of-the-forest.md (100%) rename {_website => modules/billing}/docs/games/soulmask.md (100%) rename {_website => modules/billing}/docs/games/source-forts-classic.md (100%) rename {_website => modules/billing}/docs/games/space-engineers.md (100%) rename {_website => modules/billing}/docs/games/squad-44.md (100%) rename {_website => modules/billing}/docs/games/squad.md (100%) rename {_website => modules/billing}/docs/games/starbound.md (100%) rename {_website => modules/billing}/docs/games/stationeers.md (100%) rename {_website => modules/billing}/docs/games/staxel.md (100%) rename {_website => modules/billing}/docs/games/stickybots.md (100%) rename {_website => modules/billing}/docs/games/subsistence.md (100%) rename {_website => modules/billing}/docs/games/survive-the-nights.md (100%) rename {_website => modules/billing}/docs/games/sven-co-op.md (100%) rename {_website => modules/billing}/docs/games/team-fortress-2.md (100%) rename {_website => modules/billing}/docs/games/team-fortress-classic.md (100%) rename {_website => modules/billing}/docs/games/teamspeak-3.md (100%) rename {_website => modules/billing}/docs/games/tebex.md (100%) rename {_website => modules/billing}/docs/games/teeworlds.md (100%) rename {_website => modules/billing}/docs/games/terraria.md (100%) rename {_website => modules/billing}/docs/games/terratech-worlds.md (100%) rename {_website => modules/billing}/docs/games/the-forest.md (100%) rename {_website => modules/billing}/docs/games/the-front.md (100%) rename {_website => modules/billing}/docs/games/the-isle.md (100%) rename {_website => modules/billing}/docs/games/the-lord-of-the-rings-return-to-moria.md (100%) rename {_website => modules/billing}/docs/games/the-specialists.md (100%) rename {_website => modules/billing}/docs/games/tinkertown.md (100%) rename {_website => modules/billing}/docs/games/tower-unite.md (100%) rename {_website => modules/billing}/docs/games/unreal-tournament-2004.md (100%) rename {_website => modules/billing}/docs/games/unreal-tournament-3.md (100%) rename {_website => modules/billing}/docs/games/unreal-tournament-99.md (100%) rename {_website => modules/billing}/docs/games/unreal-tournament.md (100%) rename {_website => modules/billing}/docs/games/unturned.md (100%) rename {_website => modules/billing}/docs/games/urban-terror-4.md (100%) rename {_website => modules/billing}/docs/games/v-rising.md (100%) rename {_website => modules/billing}/docs/games/valheim.md (100%) rename {_website => modules/billing}/docs/games/vampire-slayer.md (100%) rename {_website => modules/billing}/docs/games/velocity-proxy.md (100%) rename {_website => modules/billing}/docs/games/vintage-story.md (100%) rename {_website => modules/billing}/docs/games/warfork.md (100%) rename {_website => modules/billing}/docs/games/warsow.md (100%) rename {_website => modules/billing}/docs/games/waterfallmc.md (100%) rename {_website => modules/billing}/docs/games/wolfenstein-enemy-territory.md (100%) rename {_website => modules/billing}/docs/games/world-titans-war.md (100%) rename {_website => modules/billing}/docs/games/wreckfest-2.md (100%) rename {_website => modules/billing}/docs/games/wreckfest.md (100%) rename {_website => modules/billing}/docs/games/wurm-unlimited.md (100%) rename {_website => modules/billing}/docs/games/xonotic.md (100%) rename {_website => modules/billing}/docs/games/zombie-master-reborn.md (100%) rename {_website => modules/billing}/docs/games/zombie-panic-source.md (100%) rename {_website => modules/billing}/docs/server.php (100%) rename {_website => modules/billing}/forgot_password.php (100%) rename {_website => modules/billing}/images/banner.png (100%) rename {_website => modules/billing}/images/bf3_the_russian.jpg (100%) rename {_website => modules/billing}/images/dark.jpg (100%) create mode 100644 modules/billing/images/featured/7dtd.jpg create mode 100644 modules/billing/images/featured/arkse.jpg create mode 100644 modules/billing/images/featured/arma2_operation_arrowhead.jpg create mode 100644 modules/billing/images/featured/arma_3.jpg create mode 100644 modules/billing/images/featured/cs_go.jpg create mode 100644 modules/billing/images/featured/day_z.jpg create mode 100644 modules/billing/images/featured/dayz_epochmod.jpg create mode 100644 modules/billing/images/featured/dayz_mod.jpg create mode 100644 modules/billing/images/featured/eurotruck2.jpg create mode 100644 modules/billing/images/featured/fistful_of_frags.jpg create mode 100644 modules/billing/images/featured/insurgency.jpg create mode 100644 modules/billing/images/featured/insurgency_sandstorm.jpg create mode 100644 modules/billing/images/featured/minecraft.jpg create mode 100644 modules/billing/images/games/7dtd.jpg create mode 100644 modules/billing/images/games/arkse.jpg create mode 100644 modules/billing/images/games/arma2.jpg create mode 100644 modules/billing/images/games/arma2_operation_arrowhead.jpg create mode 100644 modules/billing/images/games/arma_3.jpg create mode 100644 modules/billing/images/games/asseto.jpg create mode 100644 modules/billing/images/games/avorion.jpg create mode 100644 modules/billing/images/games/brainbread_2.jpg create mode 100644 modules/billing/images/games/chivalry.jpg create mode 100644 modules/billing/images/games/citadel.jpg create mode 100644 modules/billing/images/games/colonysurvival.jpg create mode 100644 modules/billing/images/games/conanexiles.jpg create mode 100644 modules/billing/images/games/cs_go.jpg create mode 100644 modules/billing/images/games/cstrike.jpg create mode 100644 modules/billing/images/games/cstrikesource.jpg create mode 100644 modules/billing/images/games/day_of_defeat_source.jpg create mode 100644 modules/billing/images/games/day_z.jpg create mode 100644 modules/billing/images/games/dayz_epochmod.jpg create mode 100644 modules/billing/images/games/dayz_mod.jpg create mode 100644 modules/billing/images/games/deathmatch_classic.jpg create mode 100644 modules/billing/images/games/dst.jpg create mode 100644 modules/billing/images/games/eco.jpg create mode 100644 modules/billing/images/games/eurotruck2.jpg create mode 100644 modules/billing/images/games/fistful_of_frags.jpg create mode 100644 modules/billing/images/games/garrys_mod.jpg create mode 100644 modules/billing/images/games/half-life2_deathmatch.jpg create mode 100644 modules/billing/images/games/harsh.jpg create mode 100644 modules/billing/images/games/hurt_world.jpg create mode 100644 modules/billing/images/games/insurgency.jpg create mode 100644 modules/billing/images/games/insurgency_sandstorm.jpg create mode 100644 modules/billing/images/games/killing_floor.jpg create mode 100644 modules/billing/images/games/killing_floor_2.jpg create mode 100644 modules/billing/images/games/left_4_dead.jpg create mode 100644 modules/billing/images/games/left_4_dead_2.jpg create mode 100644 modules/billing/images/games/minecraft.jpg create mode 100644 modules/billing/images/games/miscreated_server.jpg create mode 100644 modules/billing/images/games/mordhau.jpg create mode 100644 modules/billing/images/games/nomoreroominhell.jpg create mode 100644 modules/billing/images/games/ootow.jpg create mode 100644 modules/billing/images/games/rust_header.jpg create mode 100644 modules/billing/images/games/scp.jpg create mode 100644 modules/billing/images/games/squad.jpg create mode 100644 modules/billing/images/games/starbound.jpg create mode 100644 modules/billing/images/games/stationeers.jpg create mode 100644 modules/billing/images/games/team_fortress_2.jpg create mode 100644 modules/billing/images/games/terraria.jpg create mode 100644 modules/billing/images/games/urt.jpg create mode 100644 modules/billing/images/games/valheim.jpg create mode 100644 modules/billing/images/games/wurmu.jpg rename {_website => modules/billing}/images/logo-sm.png (100%) create mode 100644 modules/billing/images/logo.jpg create mode 100644 modules/billing/images/logo.png rename {_website => modules/billing}/includes/README.md (100%) rename {_website => modules/billing}/includes/admin_auth.php (100%) rename {_website => modules/billing}/includes/cart_helper.php (100%) rename {_website => modules/billing}/includes/config.inc.php (100%) rename {_website => modules/billing}/includes/footer.php (100%) create mode 100644 modules/billing/includes/log.php create mode 100644 modules/billing/includes/login_required.php rename {_website => modules/billing}/includes/menu.php (66%) create mode 100644 modules/billing/includes/top.php rename {_website => modules/billing}/index.php (100%) rename {_website => modules/billing}/invoices.php (100%) delete mode 100644 modules/billing/ipn.php delete mode 100644 modules/billing/ipnlog.txt rename {_website => modules/billing}/logfile.txt (94%) rename {_website => modules/billing}/login.php (64%) create mode 100644 modules/billing/logout.php create mode 100644 modules/billing/logs/2025-10-23.log create mode 100644 modules/billing/logs/add_to_cart.log create mode 100644 modules/billing/logs/add_to_cart_requests.log create mode 100644 modules/billing/logs/free_create_audit.log rename {_website => modules/billing}/my_servers.php (100%) delete mode 100644 modules/billing/navigation.xml rename {_website => modules/billing}/order.php (96%) delete mode 100644 modules/billing/orders.php delete mode 100644 modules/billing/paid.php create mode 100644 modules/billing/payment_success.php delete mode 100644 modules/billing/paypal.php rename {_website => modules/billing}/privacy.php (100%) rename {_website => modules/billing}/register.php (100%) rename {_website => modules/billing}/renew_server.php (100%) rename {_website => modules/billing}/reset_password.php (100%) rename {_website => modules/billing}/return.php (100%) rename {_website => modules/billing}/server_status.php (100%) rename {_website => modules/billing}/serverlist.php (100%) delete mode 100644 modules/billing/services.php delete mode 100644 modules/billing/settings.php delete mode 100644 modules/billing/shop.php delete mode 100644 modules/billing/test-email.php rename {_website => modules/billing}/test_db_connection.php (100%) rename {_website => modules/billing}/tools/check_db_user.php (100%) rename {_website => modules/billing}/tools/check_invoices_redirect.php (100%) rename {_website => modules/billing}/tools/check_logout_redirect.php (100%) rename {_website => modules/billing}/tools/debug_invoices_redirect.php (100%) rename {_website => modules/billing}/tools/simulate_webhook.php (100%) rename {_website => modules/billing}/tos.php (100%) rename {_website => modules/billing}/webhook.php (74%) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 57cd0554..b97ca1fd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,7 +14,7 @@ - `modules/` — panel modules (legacy `billing/` exists; its **schema** is authoritative for multi-remote, but the **pages** are deprecated). - `includes/` — panel configuration and DB connectors. - `ogp_api.php` — internal API entry point for panel-side actions. -- `paypal/` — PayPal code if present in this branch. +- `api/` — Payment-related API code if present in this branch (previously under `paypal/` or `payments/`). ## 2) No-Code Planning Mode (default) - Do **not** emit PHP, SQL, XML, or shell commands unless a maintainer explicitly asks: **“Generate code now.”** @@ -72,7 +72,7 @@ - **Licensing:** Preserve upstream notices and ensure our additions stay license-compatible. ## 7) Validation checklist (pre-PR / pre-merge) -- Read `_website/`, `modules/config_games/server_configs/`, `modules/`, `includes/`, `paypal/` (if present), and `ogp_api.php` to anchor proposals to actual code. +- Read `_website/`, `modules/config_games/server_configs/`, `modules/`, `includes/`, `api/` (if present), and `ogp_api.php` to anchor proposals to actual code. - Catalog uses only the XML metadata; no hardcoded ports/params. - Regions/nodes are read live from the panel DB; no duplicates on the website. - Auth plan preserves panel compatibility and modernizes website hashing; **sessions remain separate**. diff --git a/_website/cart.php b/_website/cart.php deleted file mode 100644 index 0f46e69f..00000000 --- a/_website/cart.php +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - Shopping Cart - GameServers.World - - - 0) { - $stmt = $db->prepare("UPDATE ogp_billing_orders SET status = 'paid' WHERE order_id = ? LIMIT 1"); - if ($stmt) { $stmt->bind_param('i', $orderId); $stmt->execute(); $stmt->close(); } - - // write a simulated webhook file - require_once(__DIR__ . '/includes/config.inc.php'); - $dataDir = (isset($SITE_DATA_DIR) && $SITE_DATA_DIR) ? $SITE_DATA_DIR : realpath(__DIR__ . '/') . DIRECTORY_SEPARATOR . 'data'; - @mkdir($dataDir, 0775, true); - $rec = [ - 'event_type' => 'PAYMENT.CAPTURE.COMPLETED', - 'status' => 'PAID', - 'amount' => 0.00, - 'currency' => 'USD', - 'payer' => $_SESSION['website_user_email'] ?? ($_SESSION['website_username'] ?? ''), - 'invoice' => 'FREE-' . $orderId . '-' . time(), - 'custom' => 'admin_free_create_order_' . $orderId, - 'resource_id' => 'FREE-' . bin2hex(random_bytes(6)), - 'items' => [], - 'ts' => date('c'), - ]; - $fname = $dataDir . DIRECTORY_SEPARATOR . $rec['invoice'] . '.json'; - file_put_contents($fname, json_encode($rec, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); - header('Location: return.php?invoice=' . urlencode($rec['invoice'])); - exit; - } - } -} - -// Include top bar and menu -include(__DIR__ . '/includes/top.php'); -include(__DIR__ . '/includes/menu.php'); - -$user_id=$_SESSION['user_id'] ?? 0; -$user_id = 186; // For testing purposes, set a default user ID - -if ($user_id <= 0) { - echo "

Please login to view your cart

"; - mysqli_close($db); - echo ""; - return; -} - - - -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_single'])) { - $order_id = intval($_POST['delete_single']); - if ($order_id > 0) { - // First, check if the status is 'renew' - $stmt = $db->prepare("SELECT status FROM ogp_billing_orders WHERE order_id = ? AND user_id = ?"); - $stmt->bind_param("ii", $order_id, $user_id); - $stmt->execute(); - $stmt->bind_result($status); - if ($stmt->fetch() && strtolower($status) === 'renew') { - $stmt->close(); - // Set status to 'expired' if currently 'renew' - $update = $db->prepare("UPDATE ogp_billing_orders SET status = 'expired' WHERE order_id = ? AND user_id = ?"); - $update->bind_param("ii", $order_id, $user_id); - $update->execute(); - $update->close(); - } else { - $stmt->close(); - // Otherwise, delete the order - $delete = $db->prepare("DELETE FROM ogp_billing_orders WHERE order_id = ? AND user_id = ?"); - $delete->bind_param("ii", $order_id, $user_id); - $delete->execute(); - $delete->close(); - } - } -} - -if ($db){ - $carts = $db->query("SELECT * FROM ogp_billing_orders AS cart - WHERE (status = 'in-cart' OR status = 'renew') AND user_id = " . $user_id . " ORDER BY order_id ASC"); - - - -} - -?> - -
-

Your Cart

- - - - - - - - - - - - - - - - - - num_rows > 0) { - while ($row = $carts->fetch_assoc()) { - ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Server IDGame NameLocationMax PlayersPrice per PlayerMonthsTotal
-
- -
-
$ -
- - -
-
 $
- Cart Total: - - $ -
No items in your cart.
- - -'srv123','amount'=>9.99], ['serverID'=>'srv999','amount'=>14.50]] - -// --- Sanity + normalization --- -if (!isset($grandTotal) || !is_numeric($grandTotal)) { - $grandTotal = 0.00; -} -if (!isset($invoice) || !is_array($invoice)) { - $invoice = []; -} -$currency = 'USD'; -$amount = number_format((float)$grandTotal, 2, '.', ''); -$lineItems = []; - -// Build PayPal-friendly items array (name, unit_amount, quantity, sku) -foreach ($invoice as $i) { - $sid = isset($i['serverID']) ? (string)$i['serverID'] : 'unknown'; - $amt = isset($i['amount']) && is_numeric($i['amount']) ? number_format((float)$i['amount'], 2, '.', '') : '0.00'; - $lineItems[] = [ - 'name' => "Server $sid", - 'quantity' => '1', - 'unit_amount' => ['currency_code' => $currency, 'value' => $amt], - 'sku' => $sid - ]; -} - -// Single overall invoice id for the order -$invoiceId = 'INV-' . date('Ymd-His') . '-' . bin2hex(random_bytes(3)); - -// A short custom reference derived from your line items (<= 127 chars for PayPal) -$customHash = substr(strtoupper(sha1(json_encode($invoice))), 0, 16); -$customId = "INVREF-$customHash"; - -// Text on the PayPal side -$description = 'Game server order (' . count($lineItems) . ' item' . (count($lineItems)===1?'': 's') . ')'; - -// URLs -$siteBase = 'https://panel.iaregamer.com'; -$returnUrl = $siteBase . '/_website/return.php?invoice=' . urlencode($invoiceId); -$cancelUrl = $siteBase . '/_website/return.php?invoice=' . urlencode($invoiceId) . '&cancel=1'; - -// API base (relative) -$apiBase = '/_website/api'; -?> - - - -
-
- - - - -
- - - - - diff --git a/_website/includes/login_required.php b/_website/includes/login_required.php deleted file mode 100644 index 17745da1..00000000 --- a/_website/includes/login_required.php +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/_website/includes/top.php b/_website/includes/top.php deleted file mode 100644 index 75dd7d2d..00000000 --- a/_website/includes/top.php +++ /dev/null @@ -1,18 +0,0 @@ - - -body{background-image:url('". $bg ."');background-size:cover;background-position:center fixed;}\n"; -} -?> - -
-
- Gameservers World logo -
-
Gameservers World
-
diff --git a/_website/logout.php b/_website/logout.php deleted file mode 100644 index 7ba8429e..00000000 --- a/_website/logout.php +++ /dev/null @@ -1,57 +0,0 @@ - diff --git a/_website/payments/api/README.md b/_website/payments/api/README.md deleted file mode 100644 index fd1ff4db..00000000 --- a/_website/payments/api/README.md +++ /dev/null @@ -1 +0,0 @@ -Compatibility wrappers for payments API endpoints. Canonical implementations are under /_website/api/. diff --git a/_website/payments/config.php b/_website/payments/config.php deleted file mode 100644 index 2cb8ba34..00000000 --- a/_website/payments/config.php +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/_website/payments/pay.php b/_website/payments/pay.php deleted file mode 100644 index 6bf4793c..00000000 --- a/_website/payments/pay.php +++ /dev/null @@ -1,4 +0,0 @@ - true, - 'client_id' => '', - 'client_secret' => '', - 'webhook_id' => '', - 'data_dir' => realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data', - 'log_file' => realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'webhook.log', -]; - -if (defined('SITE_DATA_DIR') && SITE_DATA_DIR) { - $config['data_dir'] = rtrim(SITE_DATA_DIR, "\\/") . DIRECTORY_SEPARATOR; -} - -@mkdir($config['data_dir'], 0775, true); - -function log_line($m){global $config; @file_put_contents($config['log_file'],'['.date('c')."] $m\n",FILE_APPEND);} -function api_base(){global $config; return $config['sandbox'] ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';} - -http_response_code(200); - -$raw = file_get_contents('php://input'); -$headers = array_change_key_case(getallheaders() ?: [], CASE_UPPER); -log_line("HIT ip=".($_SERVER['REMOTE_ADDR']??'') ." bytes=".strlen($raw)); -if (!$raw) { log_line("NO_BODY"); exit; } - -// 1) OAuth2 -$ch = curl_init(api_base().'/v1/oauth2/token'); -curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER=>true, - CURLOPT_POST=>true, - CURLOPT_POSTFIELDS=>'grant_type=client_credentials', - CURLOPT_HTTPHEADER=>['Accept: application/json'], - CURLOPT_USERPWD=>$config['client_id'].':'.$config['client_secret'], -]); -$tokenResp = curl_exec($ch); -$http = curl_getinfo($ch, CURLINFO_HTTP_CODE); -curl_close($ch); -if ($http!==200){ log_line("OAUTH_FAIL http=$http resp=$tokenResp"); exit; } -$access_token = json_decode($tokenResp, true)['access_token'] ?? null; -if (!$access_token){ log_line("OAUTH_NO_TOKEN"); exit; } - -// 2) Verify webhook signature -$verifyPayload = [ - 'transmission_id' => $headers['PAYPAL-TRANSMISSION-ID'] ?? '', - 'transmission_time' => $headers['PAYPAL-TRANSMISSION-TIME'] ?? '', - 'cert_url' => $headers['PAYPAL-CERT-URL'] ?? '', - 'auth_algo' => $headers['PAYPAL-AUTH-ALGO'] ?? '', - 'transmission_sig' => $headers['PAYPAL-TRANSMISSION-SIG'] ?? '', - 'webhook_id' => $config['webhook_id'], - 'webhook_event' => json_decode($raw, true), -]; -$ch = curl_init(api_base().'/v1/notifications/verify-webhook-signature'); -curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER=>true, - CURLOPT_POST=>true, - CURLOPT_POSTFIELDS=>json_encode($verifyPayload), - CURLOPT_HTTPHEADER=>[ - 'Content-Type: application/json', - 'Authorization: Bearer '.$access_token - ], -]); -$verifyResp = curl_exec($ch); -$http = curl_getinfo($ch, CURLINFO_HTTP_CODE); -curl_close($ch); -$verifyJson = json_decode($verifyResp, true); -if ($http!==200 || ($verifyJson['verification_status'] ?? '') !== 'SUCCESS'){ - log_line("VERIFY_FAIL http=$http status=".($verifyJson['verification_status']??'NONE')); - exit; -} -log_line("VERIFY_OK"); - -// 3) Parse and persist (now with items) -$evt = json_decode($raw, true); -$type = $evt['event_type'] ?? ''; -$res = $evt['resource'] ?? []; - -// Extract common fields -$invoice = $res['invoice_id'] ?? ($res['invoice_number'] ?? null); -$custom = $res['custom_id'] ?? ($res['custom'] ?? null); - -// Amounts/payer -$amount = $res['amount']['value'] ?? ($res['amount']['total'] ?? null); -$currency = $res['amount']['currency_code'] ?? ($res['amount']['currency'] ?? null); -$payer = $res['payer']['email_address'] ?? ($res['payer']['payer_info']['email'] ?? null); - -// Try to capture line items if present directly in this event: -$items = []; -if (isset($res['purchase_units'][0]['items']) && is_array($res['purchase_units'][0]['items'])) { - $items = $res['purchase_units'][0]['items']; -} - -// If capture event, try to fetch the parent ORDER to get items -if (!$items && $type === 'PAYMENT.CAPTURE.COMPLETED') { - $orderId = - $res['supplementary_data']['related_ids']['order_id'] // preferred - ?? null; - - if (!$orderId && isset($res['links']) && is_array($res['links'])) { - // Fallback: look for a link to the parent order - foreach ($res['links'] as $lnk) { - if (!empty($lnk['href']) && !empty($lnk['rel']) && stripos($lnk['href'], '/v2/checkout/orders/') !== false) { - $orderId = basename(parse_url($lnk['href'], PHP_URL_PATH)); - break; - } - } - } - - if ($orderId) { - $ch = curl_init(api_base()."/v2/checkout/orders/".urlencode($orderId)); - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_HTTPHEADER => [ - 'Authorization: Bearer '.$access_token, - 'Content-Type: application/json' - ], - ]); - $orderJson = curl_exec($ch); - $httpOrder = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - if ($httpOrder === 200) { - $order = json_decode($orderJson, true); - if (isset($order['purchase_units'][0]['items']) && is_array($order['purchase_units'][0]['items'])) { - $items = $order['purchase_units'][0]['items']; - } - // If the order has invoice/custom (sometimes more reliable), prefer those: - if (!$invoice) { $invoice = $order['purchase_units'][0]['invoice_id'] ?? $invoice; } - if (!$custom) { $custom = $order['purchase_units'][0]['custom_id'] ?? $custom; } - } else { - log_line("ORDER_FETCH_FAIL id=$orderId http=$httpOrder"); - } - } -} - -$status = 'IGNORED'; - -// We persist on payment completed events -if (in_array($type, ['PAYMENT.CAPTURE.COMPLETED','PAYMENT.SALE.COMPLETED'], true)) { - $record = [ - 'event_type' => $type, - 'status' => 'PAID', - 'amount' => $amount, - 'currency' => $currency, - 'payer' => $payer, - 'invoice' => $invoice, - 'custom' => $custom, - 'resource_id' => $res['id'] ?? null, - 'items' => $items, // Persist line items for your return.php/UI - 'ts' => date('c'), - ]; - $name = $invoice ?: 'NO-INVOICE'; - @file_put_contents($config['data_dir']."/$name.json", json_encode($record, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); - $status = 'WROTE_FILE'; -} - -log_line("EVENT $type invoice=".($invoice ?: 'none')." items_count=".count($items)." status=$status"); diff --git a/_website/paypal/api/README.md b/_website/paypal/api/README.md deleted file mode 100644 index 3977577d..00000000 --- a/_website/paypal/api/README.md +++ /dev/null @@ -1 +0,0 @@ -This folder contains compatibility wrappers for PayPal API endpoints. The canonical implementations live in /_website/api/. diff --git a/_website/paypal/config.php b/_website/paypal/config.php deleted file mode 100644 index 3d7d035b..00000000 --- a/_website/paypal/config.php +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/_website/paypal/pay.php b/_website/paypal/pay.php deleted file mode 100644 index 364632b1..00000000 --- a/_website/paypal/pay.php +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - Checkout - - - - - - -

Complete your purchase

-

Amount:

-

Invoice:

-
-
- - - - - diff --git a/_website/paypal/return.php b/_website/paypal/return.php deleted file mode 100644 index 4243a345..00000000 --- a/_website/paypal/return.php +++ /dev/null @@ -1,4 +0,0 @@ - true, - 'client_id' => '', - 'client_secret' => '', - 'webhook_id' => '', - 'data_dir' => realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data', - 'log_file' => realpath(__DIR__ . '/..') . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'webhook.log', -]; - -// Allow includes/config.inc.php to override SITE_DATA_DIR if set -if (defined('SITE_DATA_DIR') && SITE_DATA_DIR) { - $config['data_dir'] = rtrim(SITE_DATA_DIR, "\\/") . DIRECTORY_SEPARATOR; -} - -@mkdir($config['data_dir'], 0775, true); - -function log_line($m){global $config; @file_put_contents($config['log_file'],'['.date('c')."] $m\n",FILE_APPEND);} -function api_base(){global $config; return $config['sandbox'] ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';} - -http_response_code(200); - -$raw = file_get_contents('php://input'); -$headers = array_change_key_case(getallheaders() ?: [], CASE_UPPER); -log_line("HIT ip=".($_SERVER['REMOTE_ADDR']??'') ." bytes=".strlen($raw)); -if (!$raw) { log_line("NO_BODY"); exit; } - -// 1) OAuth2 -$ch = curl_init(api_base().'/v1/oauth2/token'); -curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER=>true, - CURLOPT_POST=>true, - CURLOPT_POSTFIELDS=>'grant_type=client_credentials', - CURLOPT_HTTPHEADER=>['Accept: application/json'], - CURLOPT_USERPWD=>$config['client_id'].':'.$config['client_secret'], -]); -$tokenResp = curl_exec($ch); -$http = curl_getinfo($ch, CURLINFO_HTTP_CODE); -curl_close($ch); -if ($http!==200){ log_line("OAUTH_FAIL http=$http resp=$tokenResp"); exit; } -$access_token = json_decode($tokenResp, true)['access_token'] ?? null; -if (!$access_token){ log_line("OAUTH_NO_TOKEN"); exit; } - -// 2) Verify webhook signature -$verifyPayload = [ - 'transmission_id' => $headers['PAYPAL-TRANSMISSION-ID'] ?? '', - 'transmission_time' => $headers['PAYPAL-TRANSMISSION-TIME'] ?? '', - 'cert_url' => $headers['PAYPAL-CERT-URL'] ?? '', - 'auth_algo' => $headers['PAYPAL-AUTH-ALGO'] ?? '', - 'transmission_sig' => $headers['PAYPAL-TRANSMISSION-SIG'] ?? '', - 'webhook_id' => $config['webhook_id'], - 'webhook_event' => json_decode($raw, true), -]; -$ch = curl_init(api_base().'/v1/notifications/verify-webhook-signature'); -curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER=>true, - CURLOPT_POST=>true, - CURLOPT_POSTFIELDS=>json_encode($verifyPayload), - CURLOPT_HTTPHEADER=>[ - 'Content-Type: application/json', - 'Authorization: Bearer '.$access_token - ], -]); -$verifyResp = curl_exec($ch); -$http = curl_getinfo($ch, CURLINFO_HTTP_CODE); -curl_close($ch); -$verifyJson = json_decode($verifyResp, true); -if ($http!==200 || ($verifyJson['verification_status'] ?? '') !== 'SUCCESS'){ - log_line("VERIFY_FAIL http=$http status=".($verifyJson['verification_status']??'NONE')); - exit; -} -log_line("VERIFY_OK"); - -// 3) Parse and persist (now with items) -$evt = json_decode($raw, true); -$type = $evt['event_type'] ?? ''; -$res = $evt['resource'] ?? []; - -// Extract common fields -$invoice = $res['invoice_id'] ?? ($res['invoice_number'] ?? null); -$custom = $res['custom_id'] ?? ($res['custom'] ?? null); - -// Amounts/payer -$amount = $res['amount']['value'] ?? ($res['amount']['total'] ?? null); -$currency = $res['amount']['currency_code'] ?? ($res['amount']['currency'] ?? null); -$payer = $res['payer']['email_address'] ?? ($res['payer']['payer_info']['email'] ?? null); - -// Try to capture line items if present directly in this event: -$items = []; -if (isset($res['purchase_units'][0]['items']) && is_array($res['purchase_units'][0]['items'])) { - $items = $res['purchase_units'][0]['items']; -} - -// If capture event, try to fetch the parent ORDER to get items -if (!$items && $type === 'PAYMENT.CAPTURE.COMPLETED') { - $orderId = - $res['supplementary_data']['related_ids']['order_id'] // preferred - ?? null; - - if (!$orderId && isset($res['links']) && is_array($res['links'])) { - // Fallback: look for a link to the parent order - foreach ($res['links'] as $lnk) { - if (!empty($lnk['href']) && !empty($lnk['rel']) && stripos($lnk['href'], '/v2/checkout/orders/') !== false) { - $orderId = basename(parse_url($lnk['href'], PHP_URL_PATH)); - break; - } - } - } - - if ($orderId) { - $ch = curl_init(api_base()."/v2/checkout/orders/".urlencode($orderId)); - curl_setopt_array($ch, [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_HTTPHEADER => [ - 'Authorization: Bearer '.$access_token, - 'Content-Type: application/json' - ], - ]); - $orderJson = curl_exec($ch); - $httpOrder = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - if ($httpOrder === 200) { - $order = json_decode($orderJson, true); - if (isset($order['purchase_units'][0]['items']) && is_array($order['purchase_units'][0]['items'])) { - $items = $order['purchase_units'][0]['items']; - } - // If the order has invoice/custom (sometimes more reliable), prefer those: - if (!$invoice) { $invoice = $order['purchase_units'][0]['invoice_id'] ?? $invoice; } - if (!$custom) { $custom = $order['purchase_units'][0]['custom_id'] ?? $custom; } - } else { - log_line("ORDER_FETCH_FAIL id=$orderId http=$httpOrder"); - } - } -} - -$status = 'IGNORED'; - -// We persist on payment completed events -if (in_array($type, ['PAYMENT.CAPTURE.COMPLETED','PAYMENT.SALE.COMPLETED'], true)) { - $record = [ - 'event_type' => $type, - 'status' => 'PAID', - 'amount' => $amount, - 'currency' => $currency, - 'payer' => $payer, - 'invoice' => $invoice, - 'custom' => $custom, - 'resource_id' => $res['id'] ?? null, - 'items' => $items, // Persist line items for your return.php/UI - 'ts' => date('c'), - ]; - $name = $invoice ?: 'NO-INVOICE'; - @file_put_contents($config['data_dir']."/$name.json", json_encode($record, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); - $status = 'WROTE_FILE'; -} - -log_line("EVENT $type invoice=".($invoice ?: 'none')." items_count=".count($items)." status=$status"); diff --git a/_website/CONFIGURATION.md b/modules/billing/_archived/CONFIGURATION.md similarity index 100% rename from _website/CONFIGURATION.md rename to modules/billing/_archived/CONFIGURATION.md diff --git a/_website/FEATURES.md b/modules/billing/_archived/FEATURES.md similarity index 100% rename from _website/FEATURES.md rename to modules/billing/_archived/FEATURES.md diff --git a/_website/IMPLEMENTATION_SUMMARY.md b/modules/billing/_archived/IMPLEMENTATION_SUMMARY.md similarity index 100% rename from _website/IMPLEMENTATION_SUMMARY.md rename to modules/billing/_archived/IMPLEMENTATION_SUMMARY.md diff --git a/_website/README_LOGIN.md b/modules/billing/_archived/README_LOGIN.md similarity index 100% rename from _website/README_LOGIN.md rename to modules/billing/_archived/README_LOGIN.md diff --git a/_website/VISUAL_GUIDE.md b/modules/billing/_archived/VISUAL_GUIDE.md similarity index 100% rename from _website/VISUAL_GUIDE.md rename to modules/billing/_archived/VISUAL_GUIDE.md diff --git a/modules/billing/_archived/removed-20251023-142000/ARCHIVE_README.txt b/modules/billing/_archived/removed-20251023-142000/ARCHIVE_README.txt new file mode 100644 index 00000000..4e128727 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-142000/ARCHIVE_README.txt @@ -0,0 +1,16 @@ +Archived files from _website on 2025-10-23 14:20:00 + +This folder contains a snapshot of removed documentation and test artifacts moved from the active `_website/` tree. + +Files moved here (original paths): +- VISUAL_GUIDE.md +- README_LOGIN.md +- FEATURES.md +- IMPLEMENTATION_SUMMARY.md +- CONFIGURATION.md +- test_db_connection.php +- tools/simulate_webhook.php +- ai.php +- data/SIMULATED-WEBHOOK-20251022-101500.json + +If you need to restore any of these, copy them back to the original paths. diff --git a/modules/billing/_archived/removed-20251023-142000/MOVED_DOCS.md b/modules/billing/_archived/removed-20251023-142000/MOVED_DOCS.md new file mode 100644 index 00000000..6c2c5ff2 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-142000/MOVED_DOCS.md @@ -0,0 +1,3 @@ +The detailed game docs under `_website/docs/games/` were intentionally left in place (they are product-facing). + +Top-level documentation (VISUAL_GUIDE.md, FEATURES.md, IMPLEMENTATION_SUMMARY.md, CONFIGURATION.md, README_LOGIN.md) were archived here and removed from the active site to reduce clutter. diff --git a/modules/billing/_archived/removed-20251023-202500/MOVED_FILES.json b/modules/billing/_archived/removed-20251023-202500/MOVED_FILES.json new file mode 100644 index 00000000..ea411e67 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/MOVED_FILES.json @@ -0,0 +1,75 @@ +{ + "moved_at": "2025-10-23T20:25:00Z", + "kept": { + "logs": "_website/logs/", + "docs": "_website/docs/" + }, + "files": [ + { + "original": "_website/ai.php", + "archived": "_website/_archived/removed-20251023-202500/ai.php", + "size_bytes": null, + "note": "archived sample and tools; size omitted" + }, + { + "original": "_website/test_db_connection.php", + "archived": "_website/_archived/removed-20251023-202500/test_db_connection.php", + "size_bytes": null + }, + { + "original": "_website/tools/simulate_webhook.php", + "archived": "_website/_archived/removed-20251023-202500/tools/simulate_webhook.php", + "size_bytes": null + }, + { + "original": "_website/tools/check_db_user.php", + "archived": "_website/_archived/removed-20251023-202500/tools/check_db_user.php", + "size_bytes": null + }, + { + "original": "_website/tools/check_invoices_redirect.php", + "archived": "_website/_archived/removed-20251023-202500/tools/check_invoices_redirect.php", + "size_bytes": null + }, + { + "original": "_website/tools/debug_invoices_redirect.php", + "archived": "_website/_archived/removed-20251023-202500/tools/debug_invoices_redirect.php", + "size_bytes": null + }, + { + "original": "_website/tools/check_logout_redirect.php", + "archived": "_website/_archived/removed-20251023-202500/tools/check_logout_redirect.php", + "size_bytes": null + }, + { + "original": "_website/data/SIMULATED-WEBHOOK-20251022-101500.json", + "archived": "_website/_archived/removed-20251023-202500/data/SIMULATED-WEBHOOK-20251022-101500.json", + "size_bytes": null + }, + { + "original": "_website/data/NO-INVOICE.json", + "archived": "_website/_archived/removed-20251023-202500/data/NO-INVOICE.json", + "size_bytes": null + }, + { + "original": "_website/data/INV-20250825-174311-0a7993.json", + "archived": "_website/_archived/removed-20251023-202500/data/INV-20250825-174311-0a7993.json", + "size_bytes": null + }, + { + "original": "_website/data/INV-20250825-170438-e37518.json", + "archived": "_website/_archived/removed-20251023-202500/data/INV-20250825-170438-e37518.json", + "size_bytes": null + }, + { + "original": "_website/data/FREE-549-1761246925.json", + "archived": "_website/_archived/removed-20251023-202500/data/FREE-549-1761246925.json", + "size_bytes": null + }, + { + "original": "_website/data/FREE-548-1761171178.json", + "archived": "_website/_archived/removed-20251023-202500/data/FREE-548-1761171178.json", + "size_bytes": null + } + ] +} diff --git a/modules/billing/_archived/removed-20251023-202500/ai.php b/modules/billing/_archived/removed-20251023-202500/ai.php new file mode 100644 index 00000000..3b62d1d0 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/ai.php @@ -0,0 +1,325 @@ += 400) { + $msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown API error'; + throw new RuntimeException("OpenAI API error ({$code}): {$msg}"); + } + return is_array($data) ? $data : []; +} + +/** Create or reuse a per-visitor thread */ +function ensure_thread_id() { + if (!empty($_SESSION['thread_id'])) return $_SESSION['thread_id']; + $created = openai_request('POST', '/threads', ['metadata' => ['site' => $_SERVER['HTTP_HOST'] ?? 'unknown']]); + $tid = $created['id'] ?? null; + if (!$tid) throw new RuntimeException('Failed to create thread.'); + $_SESSION['thread_id'] = $tid; + return $tid; +} + +/** Add a user message */ +function add_user_message($thread_id, $text) { + openai_request('POST', "/threads/{$thread_id}/messages", [ + 'role' => 'user', + 'content' => $text, + ]); +} + +/** Start a run */ +function start_run($thread_id, $assistant_id) { + $run = openai_request('POST', "/threads/{$thread_id}/runs", [ + 'assistant_id' => $assistant_id, + ]); + $run_id = $run['id'] ?? null; + if (!$run_id) throw new RuntimeException('Failed to start run.'); + return $run_id; +} + +/** Wait for completion (or fail/timeout) */ +function wait_for_run($thread_id, $run_id, $max_tries, $delay_us) { + $terminal = ['completed', 'failed', 'requires_action', 'cancelled', 'expired']; + for ($i = 0; $i < $max_tries; $i++) { + usleep($delay_us); + $run = openai_request('GET', "/threads/{$thread_id}/runs/{$run_id}"); + $status = $run['status'] ?? ''; + if (in_array($status, $terminal, true)) return $run; + } + return ['status' => 'timeout']; +} + +/** Cache of file_id => filename (per request) */ +$_FILE_NAME_CACHE = []; + +/** Resolve file name from file_id (API returns "filename" or sometimes "display_name") */ +function get_file_name_by_id($file_id) { + global $_FILE_NAME_CACHE; + if (isset($_FILE_NAME_CACHE[$file_id])) return $_FILE_NAME_CACHE[$file_id]; + $file = openai_request('GET', "/files/{$file_id}"); + $name = $file['filename'] ?? ($file['display_name'] ?? ($file['name'] ?? $file_id)); + $_FILE_NAME_CACHE[$file_id] = $name; + return $name; +} + +/** + * Extract message text + citations (filename + page if available). + * Returns an array of entries: ['role' => 'user|assistant', 'text' => '...', 'refs' => [['filename'=>'','page'=>'','file_id'=>'']]] + */ +function normalize_messages($messages) { + $out = []; + if (empty($messages['data']) || !is_array($messages['data'])) return $out; + + // The API returns newest first by default if not specifying; we request 'asc' in fetch. + foreach ($messages['data'] as $m) { + $role = $m['role'] ?? ''; + if (!in_array($role, ['user', 'assistant', 'system'], true)) continue; + + if (empty($m['content']) || !is_array($m['content'])) continue; + + $all_text = []; + $refs = []; + foreach ($m['content'] as $part) { + if (($part['type'] ?? '') === 'text' && !empty($part['text']['value'])) { + $all_text[] = $part['text']['value']; + + // Parse annotations for citations (file_citation) + $anns = $part['text']['annotations'] ?? []; + if (is_array($anns)) { + foreach ($anns as $ann) { + if (($ann['type'] ?? '') === 'file_citation' && !empty($ann['file_citation']['file_id'])) { + $fid = $ann['file_citation']['file_id']; + $page = null; + + // Page can appear under different shapes depending on backend. Try common keys: + if (isset($ann['file_citation']['page'])) { + $page = $ann['file_citation']['page']; + } elseif (isset($ann['file_citation']['page_range']) && is_array($ann['file_citation']['page_range'])) { + // Example: ['start' => 5, 'end' => 6] + $start = $ann['file_citation']['page_range']['start'] ?? null; + $end = $ann['file_citation']['page_range']['end'] ?? null; + if ($start && $end && $start !== $end) $page = "{$start}-{$end}"; + elseif ($start) $page = (string)$start; + } + // Fetch filename + try { + $filename = get_file_name_by_id($fid); + } catch (Throwable $e) { + $filename = $fid; + } + $refs[] = [ + 'file_id' => $fid, + 'filename' => $filename, + 'page' => $page ?? 'n/a', + ]; + } + } + } + } + } + + if (!empty($all_text)) { + $out[] = [ + 'role' => $role, + 'text' => implode("\n", $all_text), + 'refs' => $refs, + ]; + } + } + return $out; +} + +/** Fetch conversation (ascending) */ +function fetch_history($thread_id) { + $messages = openai_request('GET', "/threads/{$thread_id}/messages", null, ['order' => 'asc', 'limit' => 50]); + return normalize_messages($messages); +} + +/* ------------------- HANDLE POST ------------------- */ +$error = null; +$history = []; + +try { + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!empty($_POST['reset_thread'])) { + $_SESSION['thread_id'] = null; + } elseif (isset($_POST['user_input'])) { + $user_text = trim((string)$_POST['user_input']); + if ($user_text !== '') { + $thread_id = ensure_thread_id(); + add_user_message($thread_id, $user_text); + $run_id = start_run($thread_id, $ASSISTANT_ID); + $run = wait_for_run($thread_id, $run_id, $POLL_MAX_TRIES, $RUN_POLL_DELAY); + + if (($run['status'] ?? '') === 'failed') { + $error = 'Assistant run failed.'; + } elseif (($run['status'] ?? '') === 'requires_action') { + // If you later support tool calls, handle them here then submit outputs. + } elseif (($run['status'] ?? '') === 'timeout') { + $error = 'Assistant timed out. Please try again.'; + } + } + } + } + + if (!empty($_SESSION['thread_id'])) { + $history = fetch_history($_SESSION['thread_id']); + } +} catch (Throwable $e) { + $error = $e->getMessage(); +} +?> + + +
+

Site Assistant

+

Type a question below. Press Enter to send, Shift+Enter for a new line.

+ + +
+ Error: +
+ + + +
Thread:
+ + +
+ +
+ + +
+
+ + +
+ Question, assistant => Answer, system => (optional) + $role = $msg['role'] ?? 'assistant'; + if ($role === 'user') $label = 'Question'; + elseif ($role === 'assistant') $label = 'Answer'; + else $label = ucfirst($role); // e.g., System + $text = str_replace("\r\n", "\n", $msg['text'] ?? ''); + $refs = $msg['refs'] ?? []; + ?> +
+
+
+ + +
+ References: + +
+ +
+ +
+ +
No messages yet.
+ + +
+ Conversation persists until you click “Reset Conversation”. +
+
+ + + diff --git a/modules/billing/_archived/removed-20251023-202500/data/FREE-548-1761171178.json b/modules/billing/_archived/removed-20251023-202500/data/FREE-548-1761171178.json new file mode 100644 index 00000000..12661d0a --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/data/FREE-548-1761171178.json @@ -0,0 +1,12 @@ +{ + "event_type": "PAYMENT.CAPTURE.COMPLETED", + "status": "PAID", + "amount": 0, + "currency": "USD", + "payer": "iaretechnician@gmail.com", + "invoice": "FREE-548-1761171178", + "custom": "admin_free_create_order_548", + "resource_id": "FREE-439c594e1e65", + "items": [], + "ts": "2025-10-23T00:12:58+02:00" +} diff --git a/modules/billing/_archived/removed-20251023-202500/data/FREE-549-1761246925.json b/modules/billing/_archived/removed-20251023-202500/data/FREE-549-1761246925.json new file mode 100644 index 00000000..764d6832 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/data/FREE-549-1761246925.json @@ -0,0 +1,12 @@ +{ + "event_type": "PAYMENT.CAPTURE.COMPLETED", + "status": "PAID", + "amount": 0, + "currency": "USD", + "payer": "iaretechnician@gmail.com", + "invoice": "FREE-549-1761246925", + "custom": "admin_free_create_order_549", + "resource_id": "FREE-439c594e1e65", + "items": [], + "ts": "2025-10-23T00:12:58+02:00" +} diff --git a/modules/billing/_archived/removed-20251023-202500/data/INV-20250825-170438-e37518.json b/modules/billing/_archived/removed-20251023-202500/data/INV-20250825-170438-e37518.json new file mode 100644 index 00000000..071c7e79 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/data/INV-20250825-170438-e37518.json @@ -0,0 +1,11 @@ +{ + "event_type": "PAYMENT.CAPTURE.COMPLETED", + "status": "PAID", + "amount": "19.99", + "currency": "USD", + "payer": null, + "invoice": "INV-20250825-170438-e37518", + "custom": "user_1234_order_5678", + "resource_id": "2V315801FX904340P", + "ts": "2025-08-25T17:05:27-04:00" +} diff --git a/modules/billing/_archived/removed-20251023-202500/data/INV-20250825-174311-0a7993.json b/modules/billing/_archived/removed-20251023-202500/data/INV-20250825-174311-0a7993.json new file mode 100644 index 00000000..f14c95a7 --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/data/INV-20250825-174311-0a7993.json @@ -0,0 +1,11 @@ +{ + "event_type": "PAYMENT.CAPTURE.COMPLETED", + "status": "PAID", + "amount": "19.99", + "currency": "USD", + "payer": null, + "invoice": "INV-20250825-174311-0a7993", + "custom": "user_1234_order_5678", + "resource_id": "2V315801FX904340P", + "ts": "2025-08-25T17:05:27-04:00" +} diff --git a/modules/billing/_archived/removed-20251023-202500/data/NO-INVOICE.json b/modules/billing/_archived/removed-20251023-202500/data/NO-INVOICE.json new file mode 100644 index 00000000..338554da --- /dev/null +++ b/modules/billing/_archived/removed-20251023-202500/data/NO-INVOICE.json @@ -0,0 +1,10 @@ +{ + "event_type": "PAYMENT.SALE.COMPLETED", + "status": "PAID", + "amount": "0.48", + "currency": "USD", + "payer": null, + "invoice": null, + "custom": null, + "ts": "2025-08-25T16:46:11-04:00" +} diff --git a/_website/data/SIMULATED-WEBHOOK-20251022-101500.json b/modules/billing/_archived/removed-20251023-202500/data/SIMULATED-WEBHOOK-20251022-101500.json similarity index 100% rename from _website/data/SIMULATED-WEBHOOK-20251022-101500.json rename to modules/billing/_archived/removed-20251023-202500/data/SIMULATED-WEBHOOK-20251022-101500.json diff --git a/modules/billing/add_to_cart.php b/modules/billing/add_to_cart.php index 4b834545..0a2dc514 100644 --- a/modules/billing/add_to_cart.php +++ b/modules/billing/add_to_cart.php @@ -1,142 +1,175 @@ getSettings(); +// Start session if not already +if (session_status() === PHP_SESSION_NONE) session_start(); - //The service id should also be cast to an int. - $service_id = intval($_REQUEST['service_id']); +// Immediate request tracing log (helps confirm the script is hit) +@mkdir(__DIR__ . '/logs', 0775, true); +$trace_file = __DIR__ . '/logs/add_to_cart_requests.log'; +file_put_contents($trace_file, date('c') . " - REQUEST_METHOD=" . ($_SERVER['REQUEST_METHOD'] ?? '') . " URI=" . ($_SERVER['REQUEST_URI'] ?? '') . "\n", FILE_APPEND); - // Query for Selected service info. - $qry_service = "SELECT DISTINCT service_id, home_cfg_id, mod_cfg_id, service_name, remote_server_id, slot_max_qty, slot_min_qty, price_daily, price_monthly, price_year, description, img_url FROM OGP_DB_PREFIXbilling_services WHERE service_id=".$db->realEscapeSingle($service_id); - $result_service = $db->resultQuery($qry_service); - $row_service = $result_service[0]; - //Compiling info about invoice to create an invoice order. - - /* - Check if it's numeric before used in the WHERE clause... otherwise an SQL error is possible currently. - If it's not an int (or if it's 0 after casting and or not vaild service) redirect to the shop page. - */ - if ($service_id <= 0 || $result_service === false){ - $view->refresh("home.php?m=billing&p=shop"); - return; - } - - // remote server value - //is now held in the the IP_ID value - //$remote_server_id = $row_service['remote_server_id']; - $remote_server_id = $_POST['ip_id']; - - // request ogp user to create a home path. - $r_server = $db->getRemoteServer($remote_server_id); - $ogp_user = $r_server['ogp_user']; - - // request the user name and the game name to generate a game home name. - $home_name = $_POST['home_name']; - - //Calculating Price - if ($_POST['invoice_duration'] == "day") - { - $price_slot=$row_service['price_daily']; - } - elseif ($_POST['invoice_duration'] == "month") - { - $price_slot=$row_service['price_monthly']; - } - elseif ($_POST['invoice_duration'] == "year") - { - $price_slot=$row_service['price_year']*12; - } - else - { - $price_slot=$row_service['price_monthly']; - } - - - //Game Server Values - $ip_id = $_POST['ip_id']; - $ip = $db->getIpById($ip_id); - $max_players = $_POST['max_players']; - $qty = $_POST['qty']; - $invoice_duration = $_POST['invoice_duration']; - $user_id = $_SESSION['user_id']; - $remote_control_password = $_POST['remote_control_password']; - $ftp_password = $_POST['ftp_password']; - $tax_amount = $settings['tax_amount']; - $currency = $settings['currency']; - - /* - Cast $_REQUEST['service_id'] to an int and then check if its value is higher than 0 before using it in the WHERE clause. - Checking if it's higher than 0 because if it's a non-numeric value, after casting it to an int it'll be 0. - */ - if($service_id !== 0) $where_service_id = " WHERE service_id=".$db->realEscapeSingle($service_id); else $where_service_id = ""; - $qry_services = "SELECT * FROM OGP_DB_PREFIXbilling_services".$where_service_id; - $services = $db->resultQuery($qry_services); - foreach ($services as $key => $row) { - if($max_players < $row['slot_min_qty'] || $qty < 1){ - $max_players = $row['slot_min_qty']; - $qty = 1; - } - /* - An extra check added for the inverse: check max_players against slot_max_qty. - It would be good to do in the event someone is only selling a max of 16 slots per server. - */ - elseif ($max_players > $row['slot_max_qty']) - { - $max_players = $row['slot_max_qty']; - } - } - - - if( isset( $_POST["add_to_cart"] ) ) - { - if( isset( $_SESSION['CART'] ) ) - { - $i = count( $_SESSION['CART'] ); - $i++; - } - else - { - $i = 0; - } - - $_SESSION['CART'][$i] = array( "cart_id" => $i, - "service_id" => $service_id, - "home_name" => $home_name, - "ip" => $ip_id, - "max_players" => $max_players, - "qty" => $qty, - "invoice_duration" => $invoice_duration, - "price" => $price_slot, - "remote_control_password" => $remote_control_password, - "ftp_password" => $ftp_password, - "tax_amount" => $tax_amount, - "currency" => $currency, - "paid" => 0); - echo ''; - } +// Prefer website session id if set (login.php sets website_user_id in debug mode) +$user_id = 0; +if (isset($_SESSION['website_user_id']) && !empty($_SESSION['website_user_id'])) { + $user_id = intval($_SESSION['website_user_id']); +} elseif (isset($_SESSION['user_id']) && !empty($_SESSION['user_id'])) { + $user_id = intval($_SESSION['user_id']); } +// If we don't have a numeric user_id but have a username, try to resolve it from the panel DB +if ($user_id <= 0 && isset($_SESSION['website_username']) && !empty($_SESSION['website_username'])) { + $uname = trim((string)$_SESSION['website_username']); + // attempt to lookup in DB (if connection available later we will set session after connecting) + // We'll set a temporary flag to resolve after DB connection is established below + $resolve_username_for_user_id = $uname; +} else { + $resolve_username_for_user_id = null; +} +/* +if ($user_id <= 0) { + // Not logged in - redirect to login with return + $return = urlencode('/' . trim(str_replace('\\', '/', $_SERVER['REQUEST_URI']), '/')); + header('Location: ' . (isset($SITE_BASE_URL) ? $SITE_BASE_URL : '') . '/_website/login.php?return_to=' . $return); + exit; +}*/ + +// Basic validation and normalization +$service_id = isset($_POST['service_id']) ? intval($_POST['service_id']) : 0; +$home_name = isset($_POST['home_name']) ? trim($_POST['home_name']) : ''; +$ip_id = isset($_POST['ip_id']) ? intval($_POST['ip_id']) : 0; +$max_players = isset($_POST['max_players']) ? intval($_POST['max_players']) : 0; +$qty = isset($_POST['qty']) ? intval($_POST['qty']) : 1; +$invoice_duration = isset($_POST['invoice_duration']) ? $_POST['invoice_duration'] : 'month'; +$remote_control_password = isset($_POST['remote_control_password']) ? $_POST['remote_control_password'] : ''; +$ftp_password = isset($_POST['ftp_password']) ? $_POST['ftp_password'] : ''; + +// Price lookup: try to find service price_monthly +$db = mysqli_connect($db_host, $db_user, $db_pass, $db_name); +if (!$db) { + // Log connection error and exit + @mkdir(__DIR__ . '/logs', 0775, true); + $trace = __DIR__ . '/logs/add_to_cart.log'; + file_put_contents($trace, date('c') . " - mysqli_connect failed: " . mysqli_connect_error() . "\n", FILE_APPEND); + die('DB connection failed'); +} else { + // Log that config was loaded (mask password) + @mkdir(__DIR__ . '/logs', 0775, true); + $trace = __DIR__ . '/logs/add_to_cart.log'; + $masked_pass = strlen($db_pass) ? '***' : ''; + file_put_contents($trace, date('c') . " - DB connected host={$db_host} user={$db_user} pass={$masked_pass} db={$db_name}\n", FILE_APPEND); +} + +// If we deferred resolving username to user_id, do it now with the DB connection +if (!empty($resolve_username_for_user_id) && $db) { + $safe_uname = mysqli_real_escape_string($db, $resolve_username_for_user_id); + // users_login is the correct column name in this schema + $q = mysqli_query($db, "SELECT user_id FROM ogp_users WHERE users_login = '$safe_uname' LIMIT 1"); + if ($q && mysqli_num_rows($q) === 1) { + $r = mysqli_fetch_assoc($q); + $user_id = intval($r['user_id'] ?? 0); + // persist into session for subsequent requests + if ($user_id > 0) { + $_SESSION['website_user_id'] = $user_id; + site_log_info('resolved_user_id_from_username', ['username'=>$resolve_username_for_user_id,'user_id'=>$user_id]); + // Also resolve and persist the user's role so menus and admin checks are consistent + $role_q = mysqli_query($db, "SELECT users_role FROM ogp_users WHERE user_id = " . intval($user_id) . " LIMIT 1"); + if ($role_q && mysqli_num_rows($role_q) === 1) { + $role_row = mysqli_fetch_assoc($role_q); + $_SESSION['website_user_role'] = $role_row['users_role'] ?? ''; + } + } + } else { + site_log_warn('resolve_user_failed', ['username'=>$resolve_username_for_user_id]); + } +} + +$price = 0.0; +if ($service_id > 0) { + $stmt = $db->prepare('SELECT price_monthly, slot_min_qty, slot_max_qty FROM ogp_billing_services WHERE service_id = ? LIMIT 1'); + if ($stmt) { + $stmt->bind_param('i', $service_id); + $stmt->execute(); + $stmt->bind_result($price_monthly, $slot_min_qty, $slot_max_qty); + if ($stmt->fetch()) { + $price = floatval($price_monthly); + // constrain slots + if ($max_players < $slot_min_qty) $max_players = $slot_min_qty; + if ($max_players > $slot_max_qty) $max_players = $slot_max_qty; + } + $stmt->close(); + } +} + +// Insert into ogp_billing_orders +$now = date('Y-m-d H:i:s'); +$status = 'in-cart'; + +// Normal flow: process POST immediately. If debug=1 is passed, we'll still log SQL and show results in logs. +$debug = (isset($_GET['debug']) && $_GET['debug'] == '1') || (isset($_POST['debug']) && $_POST['debug'] == '1'); + +// Build and execute a simple INSERT using mysqli_query for debugging clarity +@mkdir(__DIR__ . '/logs', 0775, true); +$logfile = __DIR__ . '/logs/add_to_cart.log'; +site_log_info('add_to_cart_invoked', ['user_id'=>$user_id, 'service_id'=>$service_id]); + +// Escape values +$esc_user_id = intval($user_id); +$esc_service_id = intval($service_id); +$esc_home_name = mysqli_real_escape_string($db, $home_name); +$esc_ip_id = intval($ip_id); +$esc_max_players = intval($max_players); +$esc_qty = intval($qty); +$esc_invoice_duration = mysqli_real_escape_string($db, $invoice_duration); +$esc_price = number_format((float)$price, 2, '.', ''); +$esc_remote_control_password = mysqli_real_escape_string($db, $remote_control_password); +$esc_ftp_password = mysqli_real_escape_string($db, $ftp_password); +$esc_status = mysqli_real_escape_string($db, $status); + +$sql = "INSERT INTO ogp_billing_orders (user_id, service_id, home_name, ip, max_players, qty, invoice_duration, price, remote_control_password, ftp_password, status) VALUES ({$esc_user_id}, {$esc_service_id}, '{$esc_home_name}', {$esc_ip_id}, {$esc_max_players}, {$esc_qty}, '{$esc_invoice_duration}', {$esc_price}, '{$esc_remote_control_password}', '{$esc_ftp_password}', '{$esc_status}')"; + +// Compute finish_date = now + 3 days +$finish_dt = new DateTime('now'); +$finish_dt->modify('+3 days'); +$finish_date = $finish_dt->format('Y-m-d H:i:s'); + +// Check if the ogp_billing_orders table has a finish_date column; if so include it in the INSERT +$has_finish = false; +$col_check_q = mysqli_query($db, "SHOW COLUMNS FROM ogp_billing_orders LIKE 'finish_date'"); +if ($col_check_q && mysqli_num_rows($col_check_q) > 0) { + $has_finish = true; +} + +if ($has_finish) { + $esc_finish_date = mysqli_real_escape_string($db, $finish_date); + $sql = "INSERT INTO ogp_billing_orders (user_id, service_id, home_name, ip, max_players, qty, invoice_duration, price, remote_control_password, ftp_password, status, finish_date) VALUES ({$esc_user_id}, {$esc_service_id}, '{$esc_home_name}', {$esc_ip_id}, {$esc_max_players}, {$esc_qty}, '{$esc_invoice_duration}', {$esc_price}, '{$esc_remote_control_password}', '{$esc_ftp_password}', '{$esc_status}', '{$esc_finish_date}')"; + file_put_contents($logfile, date('c') . " - finish_date included: {$esc_finish_date}\n", FILE_APPEND); +} else { + file_put_contents($logfile, date('c') . " - finish_date column not present, skipping finish_date. computed_finish_date={$finish_date}\n", FILE_APPEND); +} + +site_log_info('add_to_cart_sql', ['sql'=>$sql]); + +$res = mysqli_query($db, $sql); +if (!$res) { + $err_no = mysqli_errno($db); + $err = mysqli_error($db); + site_log_error('mysqli_query_failed', ['errno'=>$err_no, 'error'=>$err, 'sql'=>$sql]); + // Log table existence check + $tbl_check = mysqli_query($db, "SHOW TABLES LIKE 'ogp_billing_orders'"); + $tbl_exists = ($tbl_check && mysqli_num_rows($tbl_check) > 0) ? 'yes' : 'no'; + site_log_warn('ogp_billing_orders_exists', ['exists'=>$tbl_exists]); +} else { + $insert_id = mysqli_insert_id($db); + $affected = mysqli_affected_rows($db); + site_log_info('add_to_cart_insert', ['insert_id'=>$insert_id, 'affected_rows'=>$affected]); +} + +// Redirect to cart page +header('Location: cart.php'); +exit; + ?> diff --git a/_website/admin.php b/modules/billing/admin.php similarity index 91% rename from _website/admin.php rename to modules/billing/admin.php index 738dc177..529fa897 100644 --- a/_website/admin.php +++ b/modules/billing/admin.php @@ -36,7 +36,7 @@ function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }

Sandbox account (testing)

-

Use PayPal sandbox credentials when testing payments. Set your sandbox client_id and client_secret in the runtime config that the payment handlers use (for this site those are in the respective files under _website/paypal/ and _website/payments/ or in a central config if you moved credentials).

+

Use PayPal sandbox credentials when testing payments. Set your sandbox client_id and client_secret in the runtime config that the payment handlers use (for this site those are in the respective files under _website/api/ or in a central config if you moved credentials).