turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

NTF-002

Transactional email templates worth sending

A tiny template layer with placeholder substitution, stored in the database so an operator can rewrite the wording without touching code.

Transactional mail is a handful of messages: verify this address, reset this password, your payout was sent, your payout failed. Hardcoding them means an operator who wants to change a sentence has to edit PHP.

Storing them as rows with {placeholder} tokens makes them editable, translatable, and testable. The substitution is one strtr call.

Escape placeholder values in the HTML version. A username is user-supplied text and it is going into markup.

PHP
function render_template(PDO $pdo, string $key, array $vars): ?array
{
    $st = $pdo->prepare('SELECT subject, body_text, body_html FROM mail_templates WHERE tpl_key = ? AND active = 1');
    $st->execute([$key]);
    $tpl = $st->fetch(PDO::FETCH_ASSOC);
    if (!$tpl) {
        return null;
    }

    $plain = [];
    $html  = [];
    foreach ($vars as $k => $v) {
        $plain['{' . $k . '}'] = (string) $v;
        $html['{' . $k . '}']  = htmlspecialchars((string) $v, ENT_QUOTES, 'UTF-8');
    }

    return [
        'subject' => strtr((string) $tpl['subject'], $plain),
        'text'    => strtr((string) $tpl['body_text'], $plain),
        'html'    => $tpl['body_html'] ? strtr((string) $tpl['body_html'], $html) : '',
    ];
}

/*
INSERT INTO mail_templates (tpl_key, subject, body_text, active) VALUES
('verify', 'Confirm your {site} account',
 "Hello {username},\n\nConfirm your address to finish signing up:\n{link}\n\n"
 . "The link works for 24 hours. If you did not sign up, ignore this.\n\n{site}", 1),
('payout_sent', 'Your {site} payout is on its way',
 "Hello {username},\n\n{amount} {currency} has been sent to {address}.\n"
 . "Transaction: {txid}\n\n{site}", 1),
('payout_held', 'Your {site} payout needs a look',
 "Hello {username},\n\nYour withdrawal of {amount} {currency} is on hold while we check it.\n"
 . "Nothing has been lost — your balance is safe and we will finish it shortly.\n\n{site}", 1);
*/

Using it

Always include the site name and a plain explanation of why the message was sent. Transactional mail that does not say what triggered it gets reported as spam.

Send the held-payout message. Silence on a delayed withdrawal is what turns a technical hiccup into an accusation.

Keep a preview in admin that renders a template with sample values, so wording changes can be checked without sending.

What bites people

Escape into HTML, not into text. Running htmlspecialchars over the plain text version puts & in front of users.

A missing placeholder leaves {username} in the message. Validate that every token in a template has a value before sending.

Do not put balances or wallet addresses in mail unless necessary. Mail is not private and often forwarded.

Also in Notifications