Files
payment-search/search_engine.py
2026-10-06 23:43:40 -07:00

200 lines
13 KiB
Python

"""Search Engine — with geo-filtering for US-relevant results."""
import json, re, time, urllib.parse, urllib.request
from pathlib import Path
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
PROCESSORS = json.load(open(Path(__file__).parent / 'processors.json'))
SITE_CACHE = {
"Helcim": [
{"domain":"redbarnpetproducts.com","industry":"ecommerce","country":"US","notes":"Pet products retailer — Helcim checkout"},
{"domain":"vitalproteins.com","industry":"nutra","country":"US","notes":"Collagen supplements — Helcim gateway"},
{"domain":"grizzlycoolers.com","industry":"ecommerce","country":"US","notes":"Premium outdoor coolers — Helcim"},
{"domain":"woodlanddirect.com","industry":"ecommerce","country":"US","notes":"Fireplaces and grills retailer — Helcim"},
{"domain":"atomicdefense.com","industry":"firearms","country":"US","notes":"Tactical gear and body armor — Helcim"},
{"domain":"thegunstorelasvegas.com","industry":"firearms","country":"US","notes":"Las Vegas firearms dealer — Helcim"},
{"domain":"bishopscustombutchering.com","industry":"ecommerce","country":"US","notes":"Custom meat processing — Helcim"},
{"domain":"fireplacedoorsonline.com","industry":"ecommerce","country":"US","notes":"Custom fireplace doors — Helcim"},
],
"Stripe": [
{"domain":"notion.so","industry":"subscription","country":"US","notes":"All-in-one workspace — Stripe billing"},
{"domain":"linear.app","industry":"subscription","country":"US","notes":"Project management for developers — Stripe"},
{"domain":"vercel.com","industry":"subscription","country":"US","notes":"Frontend cloud platform — Stripe"},
{"domain":"figma.com","industry":"subscription","country":"US","notes":"Collaborative design tool — Stripe"},
{"domain":"substack.com","industry":"subscription","country":"US","notes":"Newsletter platform — Stripe"},
{"domain":"patreon.com","industry":"subscription","country":"US","notes":"Creator membership platform — Stripe"},
{"domain":"replit.com","industry":"subscription","country":"US","notes":"Browser-based IDE — Stripe"},
{"domain":"dub.co","industry":"subscription","country":"US","notes":"Link management — Stripe"},
{"domain":"cal.com","industry":"subscription","country":"US","notes":"Open-source scheduling — Stripe"},
{"domain":"raycast.com","industry":"subscription","country":"US","notes":"Mac productivity launcher — Stripe"},
{"domain":"shopfiy.com","industry":"ecommerce","country":"US","notes":"Ecommerce platform — Stripe payments"},
],
"Adyen": [
{"domain":"spotify.com","industry":"subscription","country":"US","notes":"Music streaming — Adyen payments"},
{"domain":"uber.com","industry":"travel","country":"US","notes":"Ride & food delivery — Adyen"},
{"domain":"ebay.com","industry":"ecommerce","country":"US","notes":"Global marketplace — Adyen"},
{"domain":"etsy.com","industry":"ecommerce","country":"US","notes":"Handmade & vintage marketplace — Adyen"},
{"domain":"mcdonalds.com","industry":"retail","country":"US","notes":"Fast food — Adyen US"},
{"domain":"booking.com","industry":"travel","country":"EU","notes":"Hotel & travel booking — Adyen (EU HQ)"},
{"domain":"zalando.com","industry":"ecommerce","country":"EU","notes":"European fashion platform — Adyen"},
{"domain":"deliveroo.com","industry":"travel","country":"EU","notes":"Food delivery UK — Adyen"},
],
"PaymentCloud": [
{"domain":"kratomspot.com","industry":"cbd","country":"US","notes":"Kratom products — high-risk PaymentCloud"},
{"domain":"purecbdvapors.com","industry":"cbd","country":"US","notes":"CBD vape products — PaymentCloud"},
{"domain":"d8gas.com","industry":"cbd","country":"US","notes":"Delta-8 THC products — PaymentCloud"},
{"domain":"tfcannabis.com","industry":"cbd","country":"US","notes":"Cannabis accessories — PaymentCloud"},
{"domain":"bwsofficefurniture.com","industry":"ecommerce","country":"US","notes":"Office furniture — PaymentCloud"},
{"domain":"elementvape.com","industry":"cbd","country":"US","notes":"Vape products — high-risk processor"},
],
"eMerchantBroker": [
{"domain":"jackpotcitycasino.com","industry":"gambling","country":"INTL","notes":"Online casino — EMB gateway"},
{"domain":"spinpalacecasino.com","industry":"gambling","country":"INTL","notes":"Online casino — EMB processing"},
{"domain":"rubyfortune.com","industry":"gambling","country":"INTL","notes":"Casino — EMB payment gateway"},
{"domain":"gamingclub.com","industry":"gambling","country":"INTL","notes":"Online casino — EMB"},
{"domain":"luxurycasino.com","industry":"gambling","country":"INTL","notes":"High-limit casino — EMB processing"},
],
"MuchBetter": [
{"domain":"stake.com","industry":"gambling","country":"INTL","notes":"Largest crypto casino — MuchBetter"},
{"domain":"bc.game","industry":"gambling","country":"INTL","notes":"Crypto casino — MuchBetter option"},
{"domain":"cloudbet.com","industry":"gambling","country":"INTL","notes":"Bitcoin sportsbook — MuchBetter"},
{"domain":"bitstarz.com","industry":"gambling","country":"INTL","notes":"Award-winning casino — MuchBetter"},
{"domain":"fortunejack.com","industry":"gambling","country":"INTL","notes":"Crypto casino — MuchBetter payments"},
],
"AstroPay": [
{"domain":"bet365.com","industry":"gambling","country":"EU","notes":"World's largest sportsbook — AstroPay"},
{"domain":"1xbet.com","industry":"gambling","country":"INTL","notes":"International sportsbook — AstroPay"},
{"domain":"betway.com","industry":"gambling","country":"EU","notes":"Sports & casino — AstroPay option"},
{"domain":"22bet.com","industry":"gambling","country":"INTL","notes":"Sportsbook — AstroPay deposits"},
],
"Trustly": [
{"domain":"leovegas.com","industry":"gambling","country":"EU","notes":"Mobile casino Sweden — Trustly Pay N Play"},
{"domain":"comeon.com","industry":"gambling","country":"EU","notes":"Sportsbook — Trustly deposits"},
{"domain":"unibet.com","industry":"gambling","country":"EU","notes":"Major European bookmaker — Trustly"},
{"domain":"betsson.com","industry":"gambling","country":"EU","notes":"Nordic casino group — Trustly"},
{"domain":"mrgreen.com","industry":"gambling","country":"EU","notes":"Award-winning casino — Trustly"},
{"domain":"ninjacasino.com","industry":"gambling","country":"EU","notes":"Casino — Trustly payments"},
],
"CardX": [
{"domain":"chicago.gov","industry":"government","country":"US","notes":"City of Chicago payments — CardX"},
{"domain":"harvard.edu","industry":"education","country":"US","notes":"Harvard University — CardX tuition"},
{"domain":"floridarevenue.com","industry":"government","country":"US","notes":"Florida tax payments — CardX"},
{"domain":"laccd.edu","industry":"education","country":"US","notes":"LA Community College District — CardX"},
],
"CDGcommerce": [
{"domain":"bluedogink.com","industry":"ecommerce","country":"US","notes":"Custom printing — CDGcommerce"},
{"domain":"shoprattan.com","industry":"ecommerce","country":"US","notes":"Rattan furniture — CDGcommerce"},
{"domain":"sportsmemorabilia.com","industry":"ecommerce","country":"US","notes":"Sports collectibles — CDGcommerce"},
{"domain":"thebouqs.com","industry":"ecommerce","country":"US","notes":"Farm-fresh flowers — CDGcommerce"},
],
"PayKings": [
{"domain":"guns.com","industry":"firearms","country":"US","notes":"Firearms marketplace — PayKings"},
{"domain":"palmettostatearmory.com","industry":"firearms","country":"US","notes":"AR-15 & firearms retailer — PayKings"},
{"domain":"cheaperthandirt.com","industry":"firearms","country":"US","notes":"Discount ammunition — PayKings"},
],
"Zimpler": [
{"domain":"leovegas.com","industry":"gambling","country":"EU","notes":"Mobile casino — Zimpler payments"},
{"domain":"casumo.com","industry":"gambling","country":"EU","notes":"Adventure casino — Zimpler"},
],
"Interac": [
{"domain":"bet365.com","industry":"gambling","country":"EU","notes":"Sportsbook — Interac for Canada"},
{"domain":"betway.com","industry":"gambling","country":"EU","notes":"Casino — Interac e-Transfer"},
],
"Soar Payments": [
{"domain":"cbdistillery.com","industry":"cbd","country":"US","notes":"Major CBD brand — Soar gateway"},
{"domain":"medterracbd.com","industry":"cbd","country":"US","notes":"CBD products — Soar processing"},
{"domain":"katsbotanicals.com","industry":"cbd","country":"US","notes":"Kratom & CBD — Soar Payments"},
],
"Durango Merchant": [
{"domain":"adultempire.com","industry":"adult","country":"US","notes":"Adult retailer — Durango processing"},
],
"Forte": [
{"domain":"plannedparenthood.org","industry":"healthcare","country":"US","notes":"Healthcare — Forte gateway"},
{"domain":"habitat.org","industry":"nonprofit","country":"US","notes":"Habitat for Humanity — Forte"},
{"domain":"ymca.net","industry":"nonprofit","country":"US","notes":"YMCA — Forte processing"},
],
}
COUNTRIES = {"US": "🇺🇸 United States", "EU": "🇪🇺 Europe", "INTL": "🌍 International"}
def search_duckduckgo(query, max_results=6):
url = f"https://html.duckduckgo.com/html/?q={urllib.parse.quote(query)}"
req = urllib.request.Request(url, headers={'User-Agent': USER_AGENT})
try:
resp = urllib.request.urlopen(req, timeout=12)
html = resp.read().decode('utf-8', errors='ignore')
except:
return []
if 'captcha' in html.lower() or 'sorry' in html.lower():
return []
links = re.findall(r'<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([^<]+)</a>', html)
snippets = re.findall(r'<a[^>]*class="result__snippet"[^>]*>([^<]+(?:<[^>]+>[^<]*)*)</a>', html)
results = []
for i, (url, title) in enumerate(links[:max_results]):
url = urllib.parse.unquote(url)
if 'duckduckgo.com' in url or not url.startswith('http'): continue
domain = urllib.parse.urlparse(url).netloc.replace('www.', '')
snippet = re.sub(r'<[^>]+>', '', snippets[i]) if i < len(snippets) else ''
results.append({'url': url, 'domain': domain, 'title': title.strip(), 'snippet': snippet.strip()})
return results
def get_from_cache(processor_name, industry=None, country=None):
cache = SITE_CACHE.get(processor_name, [])
if industry: cache = [s for s in cache if s['industry'] == industry]
if country: cache = [s for s in cache if s.get('country') == country]
proc = PROCESSORS['processors'].get(processor_name, {})
return [{
'url': f"https://{s['domain']}", 'domain': s['domain'],
'title': s['domain'], 'snippet': s['notes'],
'industry': s['industry'], 'country': s.get('country', 'US'),
'processor': processor_name, 'processor_type': proc.get('type', ''), 'cached': True
} for s in cache]
def search_processor(processor_name, industry=None, country=None, max_results=12):
proc = PROCESSORS['processors'].get(processor_name)
if not proc: return {'error': f'Unknown: {processor_name}', 'results': []}
results, seen = [], set()
# Try live DDG
for dork in proc['dorks'][:2]:
query = dork
if industry: query += f' {industry}'
if country == 'US': query += ' site:*.com'
try:
for r in search_duckduckgo(query, max_results=3):
if r['domain'] not in seen:
seen.add(r['domain'])
r['processor'] = processor_name
r['processor_type'] = proc['type']
r['industry'] = industry or proc['industries'][0]
r['country'] = country or 'US'
r['cached'] = False
results.append(r)
except: pass
time.sleep(0.5)
# Cache fallback
for c in get_from_cache(processor_name, industry, country):
if c['domain'] not in seen and len(results) < max_results:
seen.add(c['domain'])
results.append(c)
return {
'processor': processor_name, 'type': proc['type'],
'industries': proc['industries'], 'dorks': proc['dorks'],
'result_count': len(results),
'live_results': sum(1 for r in results if not r.get('cached')),
'cached_results': sum(1 for r in results if r.get('cached')),
'results': results[:max_results]
}
def search_all(industry=None, country=None, max_per_processor=6):
all_results = {}
for name in PROCESSORS['processors']:
if industry and industry not in PROCESSORS['processors'][name]['industries']: continue
result = search_processor(name, industry, country, max_per_processor)
all_results[name] = result
time.sleep(0.2)
return all_results