fsfe-website/fsfe.org/cgi-bin/weborder.pl
Tobias Diekershoff 68775f6f09
All checks were successful
continuous-integration/drone/push Build is passing
added field for notes (#4513)
Reviewed-on: #4513
Co-authored-by: Tobias Diekershoff <tobiasd@fsfe.org>
Co-committed-by: Tobias Diekershoff <tobiasd@fsfe.org>
2024-10-15 12:48:41 +00:00

425 lines
13 KiB
Perl
Executable File

#!/usr/bin/perl
# -----------------------------------------------------------------------------
# Process merchandise order
# -----------------------------------------------------------------------------
# Copyright (C) 2008-2022 Free Software Foundation Europe <contact@fsfe.org>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -----------------------------------------------------------------------------
use CGI;
use CGI::Carp 'fatalsToBrowser';
use Encode qw(decode encode);
use POSIX qw(strftime);
use Digest::SHA qw(sha1_hex);
use MIME::Lite;
use MIME::Base64;
use XML::LibXML;
use utf8;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);
use JSON;
use strict;
use warnings;
use diagnostics;
# -----------------------------------------------------------------------------
# Get parameters
# -----------------------------------------------------------------------------
my $query = new CGI;
if (index($query->param("email"), "example.com") != -1) {
print "Content-type: text/html\n\n";
print "<p>Invalid input!</p>\n";
exit;
}
if ( $query->param("url") ) {
print "Content-type: text/html\n\n";
print "<p>Invalid input!</p>\n";
exit;
}
my $name = decode( "utf-8", $query->param("name") );
my $address = decode( "utf-8", $query->param("address") );
my $zip = decode( "utf-8", $query->param("zip") );
my $city = decode( "utf-8", $query->param("city") );
my $country = decode( "utf-8", $query->param("country") );
my ( $country_code, $country_name ) = split( /\|/, $country );
my $email = decode( "utf-8", $query->param("email") );
my $phone = decode( "utf-8", $query->param("phone") );
my $language = $query->param("language");
my $notes = decode( "utf-8", $query->param("notes") );
# Remove all parameters except for items and prices.
$query->delete(
"url", "name", "address", "zip", "city", "country",
"email", "phone", "language", "notes"
);
my $lang = substr $language, 0, 2;
# -----------------------------------------------------------------------------
# Calculate total amount and do some sanity checks
# -----------------------------------------------------------------------------
if ( !$name ) {
print "Content-type: text/html\n\n";
print "<p>Please enter your name!</p>\n";
exit;
}
if ( !$address or !$zip or !$city or !$country ) {
print "Content-type: text/html\n\n";
print "<p>Please enter your complete address!</p>\n";
exit;
}
if ( !$email ) {
print "Content-type: text/html\n\n";
print "<p>Please enter your email address!</p>\n";
exit;
}
my $items_file = $ENV{"DOCUMENT_ROOT"} . "order/data/items.en.xml";
my $items = XML::LibXML->load_xml(location => $items_file);
my $count = 0;
my $amount = 0;
my $pickup = 0;
foreach my $item ( $query->param ) {
my $value = $query->param($item);
if ( not $item =~ /^_/ and $value ) {
# Remove size from item info so price is found properly
my $origitem = $item;
$item =~ s/_.*//;
my $price = $items->findvalue("/itemset/item[\@id=\"$item\"]/\@price");
$count += 1;
$amount += $value * $price;
if ( !$pickup ) {
$pickup = "hoodie-fourfreedoms" eq substr($origitem,0,length("hoodie-fourfreedoms"));
}
}
}
# Determine shipping fees based on country code from drop-down list
my $shipping;
if ( $country_code eq 'DE' ) {
$shipping = 5;
} else {
$shipping = 8;
}
# if one item was for pick up only, then shipping cost are 0
if ( $pickup ) {
$shipping = 0;
}
$amount += $shipping;
if ( $count < 1 ) {
print "Content-type: text/html\n\n";
print "<p>No items selected!</p>\n";
exit;
}
if ( $amount > 999 ) {
print "Content-type: text/html\n\n";
print "<p>Sorry, total amount too large.</p>\n";
exit;
}
my $amount_f = sprintf "%.2f", $amount;
my $amount100 = $amount * 100;
my $vat = sprintf "%.2f", ( $amount_f / 1.19 ) * 0.19;
my $net = sprintf "%.2f", $amount_f - $vat;
# -----------------------------------------------------------------------------
# Create payment reference for this order
# -----------------------------------------------------------------------------
my $date = strftime( "%j", localtime );
my $time = strftime( "%s", localtime );
my $reference =
"MP" . $date . ( substr $time, -4 ) . ( sprintf "%03u", $amount );
# -----------------------------------------------------------------------------
# Compile ticket body
# -----------------------------------------------------------------------------
my $body = <<"HTML";
<html>
<body>
<p>
Dear $name,
</p>
<p>
thank you so much for your recent order to the Free Software Foundation
Europe. This is an automated email to confirm your order and give you
some additional information about it.
</p>
<p>
Once we receive payment for your order, you will get a second mail
notifying you that the payment has been received. If you have paid
online this will be slightly quicker than if you pay by bank
transfer.
</p>
<p>
If you have yet to pay your order, you may now do so by following this
link:
<a href=https://fsfe.org/order/payonline.$language/$reference>
https://fsfe.org/order/payonline.$language/$reference
</a>
</p>
<p>
In case you prefer to pay by bank transfer, please use the following data:
</p>
Recipient: Free Software Foundation Europe e.V.<br>
Address: Schoenhauser Allee 6/7, 10119 Berlin, Germany<br>
IBAN: DE47 4306 0967 2059 7908 01<br>
Bank: GLS Gemeinschaftsbank eG, 44774 Bochum, Germany<br>
BIC: GENODEM1GLS<br>
Payment reference: $reference<br>
Payment amount: $amount Euro
</p>
<p>
Notes: $notes
</p>
<p>
The following order was just received by our merchandise team. We will
ship the order as soon as we have received your payment. The invoice
will be included in the delivery of your order.
</p>
<h2>Address</h2>
<p>
$name<br>
$address<br>
$zip $city<br>
$country_name<br>
Phone: $phone
</p>
<h2>Order Details</h2>
<pre>
HTML
foreach my $item ( $query->param ) {
my $value = $query->param($item);
if ( not $item =~ /^_/ and $value ) {
# Remove size from item info so price is found properly
my $orig_item = $item;
$item =~ s/_.*//;
my $price = $items->findvalue("/itemset/item[\@id=\"$item\"]/\@price");
my $subtotal = $value * $price;
$body .= <<"HTML";
$value x $orig_item: € $subtotal
HTML
}
}
if ( $pickup ) {
$body .= <<"HTML";
Self pick-up (Shipping): € 0<br>
<strong>Total amount: € $amount</strong>
</pre>
<p>
Best regards,
</p>
</body>
</html>
HTML
} else {
$body .= <<"HTML";
Shipping to $country_name: € $shipping<br>
<strong>Total amount: € $amount</strong>
</pre>
<p>
Best regards,
</p>
</body>
</html>
HTML
}
# -----------------------------------------------------------------------------
# Generate invoice
# -----------------------------------------------------------------------------
my @odtfill = qw();
# odtfill script
push @odtfill, $ENV{"DOCUMENT_ROOT"} . "/cgi-bin/odtfill";
# template file
if ( !$pickup ) {
push @odtfill, $ENV{"DOCUMENT_ROOT"} . "/templates/invoice.odt";
} else {
push @odtfill, $ENV{"DOCUMENT_ROOT"} . "/templates/invoicepu.odt";
}
# output file
push @odtfill, "/tmp/invoice.odt";
# placeholder replacements
push @odtfill, "repeat=" . $count;
push @odtfill, "Name=" . $name;
push @odtfill, "Address=" . $address =~ s/\n/\\n/gr;
push @odtfill, "ZipCity=" . $zip . " " . $city;
push @odtfill, "Country=" . $country_name;
foreach my $item ( $query->param ) {
my $value = $query->param($item);
if ( not $item =~ /^_/ and $value ) {
my $orig_item = $item;
# Remove size from item info so price is found properly
$item =~ s/_.*//;
my $price = $items->findvalue("/itemset/item[\@id=\"$item\"]/\@price");
push @odtfill, "Count=" . $value;
push @odtfill, "Item=" . $orig_item;
push @odtfill, "Amount=" . sprintf "%.2f", $value * $price;
}
}
if ( !$pickup ) {
push @odtfill, "Country=" . $country_name; # 2nd Country placeholder
push @odtfill, "Shipping=" . sprintf "%.2f", $shipping;
}
push @odtfill, "Total=" . $amount_f;
push @odtfill, "Net=" . $net;
push @odtfill, "Vat=" . $vat;
push @odtfill, "Code=" . $reference;
# run the script
system @odtfill;
# Transform invoice.odt to base64 encoded string
my $invoice_path = "/tmp/invoice.odt";
open( my $fh, '<', $invoice_path ) or die "Cannot open file: $!";
binmode($fh);
my $file_contents = do { local $/; <$fh> };
close($fh);
my $base64_encoded_invoice = encode_base64($file_contents);
# -----------------------------------------------------------------------------
# Send API request to Helpdesk to create a new conversation
# -----------------------------------------------------------------------------
my $api_key = $ENV{'FREESCOUT_API_KEY'};
my $api_url = 'https://helpdesk.fsfe.org/api/conversations';
my $data = {
"type" => "email",
"mailboxId" => 7, # This is the Merchandise Mailbox
"subject" => "New merchandise order: $reference", # This is the Order ID
"customer" => {
"email" => "$email"
},
"threads" => [
{
"text" => "A new order came in.",
"type" => "customer",
"customer" => {
"email" => "$email",
},
"attachments" => [
{
"fileName" => "invoice.odt",
"mimeType" => "application/vnd.oasis.opendocument.text",
"data" => "$base64_encoded_invoice"
}
]
},
{
"text" => "$body",
"type" => "message",
"user" => 6530,
}
],
"imported" => JSON::false,
"assignTo" => 6584,
"status" => "pending",
"customFields" => [
{
"id" => 4, # Order ID Custom Field
"value" => "$reference" # Actual Order ID
}
],
};
# Encode the JSON
my $json_data = encode_json($data);
# Create a new HTTP::Request object
my $request = HTTP::Request->new( POST => $api_url );
$request->header( 'Content-Type' => 'application/json' );
$request->header( 'X-FreeScout-API-Key' => $api_key );
$request->content($json_data);
# Send the request using LWP::UserAgent
my $ua = LWP::UserAgent->new;
my $response = $ua->request($request);
# -----------------------------------------------------------------------------
# Generate form for ConCardis payment
# -----------------------------------------------------------------------------
my $passphrase = "Only4TestingPurposes";
my $shastring =
"ACCEPTURL=https://fsfe.org/order/thankyou.$lang.html$passphrase"
. "AMOUNT=$amount100$passphrase"
. "CANCELURL=https://fsfe.org/order/cancel.$lang.html$passphrase"
. "CN=$name$passphrase"
. "CURRENCY=EUR$passphrase"
. "EMAIL=$email$passphrase"
. "LANGUAGE=$language$passphrase"
. "ORDERID=$reference$passphrase"
. "PMLISTTYPE=2$passphrase"
. "PSPID=40F00871$passphrase"
. "TP=https://fsfe.org/order/tmpl-concardis.$lang.html$passphrase";
my $shasum = uc sha1_hex($shastring);
my $form =
" <!-- payment parameters -->\n"
. " <input type=\"hidden\" name=\"PSPID\" value=\"40F00871\"/>\n"
. " <input type=\"hidden\" name=\"orderID\" value=\"$reference\"/>\n"
. " <input type=\"hidden\" name=\"amount\" value=\"$amount100\"/>\n"
. " <input type=\"hidden\" name=\"currency\" value=\"EUR\"/>\n"
. " <input type=\"hidden\" name=\"language\" value=\"$language\"/>\n"
. " <input type=\"hidden\" name=\"CN\" value=\"$name\"/>\n"
. " <input type=\"hidden\" name=\"EMAIL\" value=\"$email\"/>\n"
. " <!-- interface template -->\n"
. " <input type=\"hidden\" name=\"TP\" value=\"https://fsfe.org/order/tmpl-concardis.$lang.html\"/>\n"
. " <input type=\"hidden\" name=\"PMListType\" value=\"2\"/>\n"
. " <!-- post-payment redirection -->\n"
. " <input type=\"hidden\" name=\"accepturl\" value=\"https://fsfe.org/order/thankyou.$lang.html\"/>\n"
. " <input type=\"hidden\" name=\"cancelurl\" value=\"https://fsfe.org/order/cancel.$lang.html\"/>\n"
. " <!-- SHA1 signature -->\n"
. " <input type=\"hidden\" name=\"SHASign\" value=\"$shasum\"/>";
# -----------------------------------------------------------------------------
# Lead user to "thankyou" page
# -----------------------------------------------------------------------------
print "Content-type: text/html\n\n";
open TEMPLATE,
$ENV{"DOCUMENT_ROOT"} . "/order/tmpl-thankyou." . $lang . ".html";
while (<TEMPLATE>) {
s/:AMOUNT:/$amount_f/g;
s/:REFERENCE:/$reference/g;
s/:FORM:/$form/g;
print;
}
close TEMPLATE;