Diff -- /var/www/vhosts/3dshawn.com/repricer.3dshawn.com/tax_settings.cgi

O Operator
Diff

/var/www/vhosts/3dshawn.com/repricer.3dshawn.com/tax_settings.cgi

added on local at 2026-07-01 21:47:20

Added
+120
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to ed986962df87
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1#!/usr/bin/perl
2#======================================================================
3# RePricer -- per-seller tax / VAT / GST settings.
4#
5# Stripe applies tax server-side via Tax IDs on the customer object, but we
6# also need a local record so we can:
7# - show the right tax-inclusive vs tax-exclusive price on the pricing page,
8# - print the tax-id on PDF invoices,
9# - filter out tax-id holders from the consumer dunning email copy.
10#
11# Columns used (added by _features_migration.sql):
12# users.tax_mode ENUM('none','consumer','business') default 'none'
13# users.tax_country CHAR(2) ISO-3166-1 alpha-2
14# users.tax_id_number VARCHAR(40) VAT/GST/etc.
15#======================================================================
16use strict;
17use warnings;
18use lib '/var/www/vhosts/3dshawn.com/repricer.3dshawn.com';
19use CGI;
20use MODS::DBConnect;
21use MODS::Login;
22use MODS::RePricer::Config;
23use MODS::RePricer::Wrapper;
24
25$|=1;
26my $q = CGI->new;
27my $form = $q->Vars;
28my $auth = MODS::Login->new;
29my $wrap = MODS::RePricer::Wrapper->new;
30my $db = MODS::DBConnect->new;
31my $cfg = MODS::RePricer::Config->new;
32my $DB = $cfg->settings('database_name');
33
34my $userinfo = $auth->login_verify();
35unless ($userinfo) { print "Status: 302 Found\nLocation: /login.cgi\n\n"; exit; }
36my $uid = $userinfo->{user_id}; $uid =~ s/[^0-9]//g;
37
38sub _h { my $s = shift // ''; $s =~ s/&/&amp;/g; $s =~ s/</&lt;/g; $s =~ s/>/&gt;/g; $s =~ s/"/&quot;/g; $s }
39sub _q { my $s = shift // ''; $s =~ s/'/''/g; $s }
40
41my $dbh = $db->db_connect();
42my $flash = ''; my $kind = '';
43
44if (($ENV{REQUEST_METHOD} || '') eq 'POST') {
45 my $mode = $form->{tax_mode} || 'none';
46 $mode = 'none' unless $mode =~ /^(none|consumer|business)$/;
47 my $country = uc($form->{tax_country} // ''); $country =~ s/[^A-Z]//g; $country = substr($country, 0, 2);
48 my $tax_id = $form->{tax_id_number} // ''; $tax_id =~ s/\s+//g; $tax_id = substr($tax_id, 0, 40);
49
50 if ($mode eq 'business') {
51 if (!$country || length($country) != 2) {
52 $flash = 'Business tax mode requires a country code (e.g. GB, DE, US).'; $kind = 'error';
53 } elsif (!$tax_id) {
54 $flash = 'Business tax mode requires your VAT / GST / sales-tax ID.'; $kind = 'error';
55 }
56 }
57
58 if (!$flash) {
59 my $qc = _q($country); my $qi = _q($tax_id);
60 $db->db_readwrite($dbh, qq~
61 UPDATE `${DB}`.users SET
62 tax_mode='$mode',
63 tax_country='$qc',
64 tax_id_number='$qi'
65 WHERE id='$uid'
66 ~, $ENV{SCRIPT_NAME}, __LINE__);
67 $flash = 'Tax settings saved.'; $kind = 'success';
68 }
69}
70
71my $u = $db->db_readwrite($dbh, qq~
72 SELECT tax_mode, tax_country, tax_id_number FROM `${DB}`.users WHERE id='$uid'
73~, $ENV{SCRIPT_NAME}, __LINE__);
74$db->db_disconnect($dbh);
75
76my $mode = _h($u->{tax_mode} // 'none');
77my $country = _h($u->{tax_country} // '');
78my $tax_id = _h($u->{tax_id_number} // '');
79
80my $sel_none = $mode eq 'none' ? 'selected' : '';
81my $sel_consumer = $mode eq 'consumer' ? 'selected' : '';
82my $sel_business = $mode eq 'business' ? 'selected' : '';
83
84my $body = '<div style="max-width:680px;margin:0 auto;padding:24px 28px;color:#e6ecf6">';
85$body .= '<div style="margin-bottom:18px"><div style="font-size:11px;letter-spacing:2px;color:#14b8a6;text-transform:uppercase">Billing</div><h1 style="font-size:26px;color:#fff;margin:6px 0 4px">Tax settings</h1><p style="color:#7e92b6;margin:0">For invoices and tax-inclusive pricing displays. Stripe Tax handles the actual collection.</p></div>';
86
87if (length $flash) {
88 my $color = $kind eq 'success' ? '#10b981' : ($kind eq 'error' ? '#fca5a5' : '#10b981');
89 $body .= qq~<div style="background:${color}1a;border-left:3px solid $color;color:$color;padding:12px 16px;border-radius:8px;margin-bottom:16px;font-size:14px">~ . _h($flash) . qq~</div>~;
90}
91
92$body .= '<form method="POST" style="background:#0b1226;border:1px solid #1f2a4a;border-radius:12px;padding:18px 22px;display:flex;flex-direction:column;gap:14px">';
93$body .= qq~ <label style="display:flex;flex-direction:column;gap:4px">
94 <span style="color:#7e92b6;font-size:12px">Tax mode</span>
95 <select name="tax_mode" style="padding:8px 10px;background:#0a0f1f;border:1px solid #1f2a4a;color:#e6ecf6;border-radius:6px">
96 <option value="none" $sel_none>None &mdash; pay tax-exclusive</option>
97 <option value="consumer" $sel_consumer>Consumer &mdash; tax-inclusive pricing</option>
98 <option value="business" $sel_business>Business &mdash; reverse-charge with my tax ID</option>
99 </select>
100 </label>
101 <label style="display:flex;flex-direction:column;gap:4px">
102 <span style="color:#7e92b6;font-size:12px">Country (ISO 3166-1 alpha-2, e.g. GB, US, DE, AU)</span>
103 <input type="text" name="tax_country" value="$country" maxlength="2" style="padding:8px 10px;background:#0a0f1f;border:1px solid #1f2a4a;color:#e6ecf6;border-radius:6px;text-transform:uppercase;width:80px">
104 </label>
105 <label style="display:flex;flex-direction:column;gap:4px">
106 <span style="color:#7e92b6;font-size:12px">Tax ID number (VAT / GST / state sales-tax ID)</span>
107 <input type="text" name="tax_id_number" value="$tax_id" maxlength="40" placeholder="e.g. GB123456789 or DE123456789" style="padding:8px 10px;background:#0a0f1f;border:1px solid #1f2a4a;color:#e6ecf6;border-radius:6px">
108 </label>
109 <div style="text-align:right">
110 <button type="submit" style="background:#064e3b;color:#a7f3d0;border:0;padding:8px 18px;border-radius:6px;font-weight:600;cursor:pointer">Save tax settings</button>
111 </div>
112~;
113$body .= '</form>';
114
115$body .= '<div style="margin-top:18px;color:#7e92b6;font-size:12px">EU/UK customers with valid VAT IDs are zero-rated under reverse-charge. US customers see tax-exclusive prices and are charged sales tax in nexus states only. Need a different setup? <a href="/support.cgi" style="color:#14b8a6">Contact support</a>.</div>';
116$body .= '</div>';
117
118print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n";
119$wrap->render({ userinfo => $userinfo, title => 'Tax settings', body => $body });
120exit;