function PsychPaidSupportAndServices(mininag)
%
% If you require paid support regarding Psychtoolbox, or custom feature
% development (or if you want to buy a Psychtoolbox themed coffee mug, bag or shirt)
% go to the following website which provides such services:
%
% https://www.psychtoolbox.net
%
% For paid support on the user forum or GitHub issue tracker, buy a
% "Psychtoolbox support membership" under the "become a member" section:
%
% https://www.psychtoolbox.net/#service
%
% 1. As part of your purchase, you will receive a document - an invoice - with a
%    printed "Order Id" or "Order no.", and a printed "License key".
%
% 2. Next you can run *this* function PsychPaidSupportAndServices in Octave or
%    Matlab. The function will ask you if you need paid support and do have
%    an active license key to prove your community membership. Answer "y" for yes.
%
% 3. Next the function will ask you about your "Order Id" and "License key".
%    Please enter that info that you got from step 1.
%
% 4. The function will print out an "authentication token", a string of letters and
%    numbers. Now you can post your question on the Psychtoolbox user forum at
%    https://psychtoolbox.discourse.group or open a new issue for bugs and feature
%    requests on our GitHub issue tracker at https://github.com/Psychtoolbox-3/Psychtoolbox-3/issues
%
%    Please add the "authentication token" to your forum question or GitHub issue.
%    This will prove to us that you deserve paid support for your question or
%    issue. Please note that initial activation of your license key may take 5-10
%    days after date of receipt of payment, so do not buy the paid support last
%    minute, when the "house is already burning", but ahead of time, like you
%    would do with a fire insurance.
%
%
% For more detailed background information about Psychtoolbox's need for funding, read on:
%
% Continued development, improvement, maintenance and support of
% Psychtoolbox itself, and also improvement of the various open-source
% software components on which Psychtoolbox and other neuroscience toolkits
% (e.g., PsychoPy) critically depend, including improvements to the Linux
% operating system as the strongest and most high quality foundation for
% demanding stimulation and data collection paradigms, requires a lot of
% highly qualified and highly focused work.
%
% This work is currently (year 2006 - present) mostly carried out by Mario
% Kleiner, who is now employed to do this work by the Medical Innovations
% Incubator GmbH (MII) in Tuebingen, Germany ( https://www.mi-incubator.com ).
% The MII belongs by 100% to the non-profit foundation for medical
% innovations ( http://www.mi-foundation.org ) in Tuebingen.
%
% In order to fund Mario's work and provide him with the resources to do
% this job, the MII offers different types of paid services around
% Psychtoolbox and the use of Linux for neuroscience, among them a
% "Psychtoolbox community membership with paid support".
%
% Your lab can now financially contribute to Psychtoolbox sustainability,
% upkeep and continued improvement, by once a year buying such a community
% membership for a modest fee. This membership entitles you to some
% paid support for questions regarding efficient use of Psychtoolbox,
% questions regarding the resolution of issues you may have with it, fixing
% of bugs you may encounter, and feature requests. MII also offers paid
% feature development and other commercial services.
%
% Please visit the following website, operated by MII, for our offering of
% commercial development services, and for the community membership with
% paid support:
%
%
% https://www.psychtoolbox.net
%
%
% The membership allows you to get your voice heard by the developers
% regarding future feature development, as well as preferential treatment
% on the public Psychtoolbox user forum and GitHub issue tracker. Spare
% income generated by your membership fee, that does not have to be used
% to process paid requests made individually by you, will be used to
% fund general development and upkeep of Psychtoolbox and its ecosystem. In
% other words, it contributes to / acts as an insurance that allows
% Psychtoolbox to be around and in good shape years into the future.
%
% To clarify: Psychtoolbox itself will stay open-source software. Anybody
% able and willing to contribute code and ideas of sufficiently high quality
% is invited to contribute to the open-source code, the documentation on our
% public community website and Wiki
% http://psychtoolbox.org, and to participate and help each other on the
% public community forum https://psychtoolbox.discourse.group.
%
% Support by our developers will be reserved to paying community members.
%
% Thanks for your participation and support!
%

% Mini advert requested by some caller, e.g., PsychtoolboxPostInstallRoutine?
if exist('mininag', 'var') && (mininag > 0)
    fprintf('\n');

    % mininag 2 if called from an error handler, mininag 1 if called from
    % general setup code:
    if mininag == 2
        fprintf('NOTE: You may want to acquire paid support for future issues like this.\n');
    else
        fprintf('IMPORTANT NEWS:\n\n');
        fprintf('You can now financially contribute to Psychtoolbox sustainability, upkeep and continued\n');
        fprintf('improvement by buying a paid support membership, which provides some paid support\n');
        fprintf('for questions regarding its use, or issues you may have with it. We also offer paid\n');
        fprintf('feature development and other useful commercial services.\n');
    end

    fprintf('Please type ''PsychPaidSupportAndServices'' to learn more.\n');
    fprintf('\n\n');

    if mininag == 1
    end

    return;
end

% Nope, full request by user for information:
try
    fid = fopen([PsychtoolboxConfigDir 'welcomemsgdone'], 'w');
    fclose(fid);
catch
end

more on;
help PsychPaidSupportAndServices;
more off;

% Check if user wants to file a support request:
fprintf('\n\n');

answer = '';
while length(answer)~=1 || ~ismember(answer, ['y', 'n'])
    answer = strtrim(input('Do you need paid support now and have an active license key [y/n]? ', 's'));
end

if ~strcmpi(answer, 'y')
    fprintf('A paid support membership can be bought as described above. Bye.\n');
    return;
end

% Ok, our valued community member wants support now. Guide them through the process:
orderId = '';
while isempty(orderId)
    fprintf('\nThe Order Id is on the invoice you received, as "Order no." between the invoice\n');
    fprintf('number and the customer number.\n\n');

    orderId = strtrim(upper(input('Please enter the Order Id of your license, or just ENTER if you can not find it: ', 's')));
    if length(orderId) ~= 8 || ~all(isstrprop(orderId, 'alphanum'))
        fprintf('\nOrder Id seems to be invalid: It must be 8 letters or numbers, e.g., XS92UVY3\n');
        orderId = '';
    end

    if isempty(orderId)
        fprintf('\nIf you can not find the Order Id, i can also just use the first 8 characters of\n');
        fprintf('your license key. This poses a mildly higher risk of somebody guessing your key\n');
        fprintf('and abusing your community membership to their advantage, or of other mixups.\n\n');
        if lower(input('Do you want me to use the license key instead [y/n]? ', 's')) == 'y'
            break;
        end
    end
end

% Get the date and time string as 2nd component:
requestTimeDate = sprintf('%i', round(clock));

licenseKey = '';
while isempty(licenseKey)
    licenseKey = strtrim(upper(input('Please enter the license key: ', 's')));
    if length(licenseKey) ~= 35 || ~all(isstrprop(licenseKey([1:5, 7:11, 13:17, 19:23, 25:29, 31:35]), 'alphanum')) || ~strcmp(licenseKey([6, 12, 18, 24, 30]), '-----')
        fprintf('The license key seems to be invalid: It must be 30 letters or numbers in dash separated groups of five, e.g.,\n');
        fprintf('2BQR7-R5ZQP-SA36G-RAVDJ-ABZBM-PKKFJ\n\n');
        licenseKey = '';
    end
end

% User wants us to use first eight chars of license key as orderId?
if isempty(orderId)
    orderId = licenseKey(1:8);
end

% Assemble the to-be-hashed string:
publicString = [orderId '-' requestTimeDate];
hashSrcString = [publicString '-' licenseKey];

% Hash it with SHA-256:
% fprintf('\nThe following string will be SHA-256 hashed: %s\n\n', hashSrcString);
hashString = computeSHA256(hashSrcString);

% Assemble final token for public posting at user forum, issue tracker etc.:
authToken = [publicString ':' hashString];
fprintf('\n\nPlease add the following string to your request when\n');
fprintf('asking for support on the Psychtoolbox user forum\n');
fprintf('[ https://psychtoolbox.discourse.group ]\n');
fprintf('or on the Psychtoolbox-3 GitHub issue tracker\n');
fprintf('[ https://github.com/Psychtoolbox-3/Psychtoolbox-3/issues ]\n');
fprintf('to prove that you are eligible for paid support:\n');
fprintf('========================================================================================\n\n');
fprintf('%s\n\n', authToken);
fprintf('========================================================================================\n\n');

% Validation code for the above:
if 0
    % Disassemble into pieces:
    eOrderId = authToken(1:8)
    epublicStringHashSep = strfind(authToken, ':');
    epublicString = authToken(1:epublicStringHashSep-1)
    ehashString = authToken(epublicStringHashSep+1:end)
    % Here we'd use eOrderId to lookup licenseKey in our DB.
    % Then reassemble source string for hashing:
    ehashSrcString = [epublicString '-' licenseKey]
    % Hash it, and compare our local hash against the one provided in public authToken:
    bingo = strcmp(ehashString, computeSHA256(ehashSrcString))
end

return;

% Embedded SHA-256 implementation. This is the same as PsychComputeSHA(msg, '256'),
% but embedded here to make PsychPaidSupportAndServices() self-contained.
function rethash = computeSHA256(msg)
    shaId = '256';
    if exist('hash', 'builtin')
        % Octave builtin
        rethash = hash(['SHA' shaId], msg);
    else
        md = javaMethod('getInstance', 'java.security.MessageDigest', ['SHA-' shaId]);
        rethash = sprintf('%2.2x', typecast(md.digest(uint8(msg)), 'uint8')');
    end
return;
