Marketing
Newsletter signups in Go
A Go package for the signup box: who asked to hear from the store, and who asked to stop. It keeps each address with where it came from and when, lists and exports them for an operator, and unsubscribes by token. It sends nothing; in its own words, that is a marketing tool’s job.
- Marketing module
- 3 settings
- 4 tests
- 7 API operations
- ext/newsletter
What it does
In the package’s words, “a subscription is an email address and a consent.” The storefront posts the address to POST /x/newsletter/subscribe, with an optional name and a source such as footer. The store keeps it lower-cased, with the date, and answers with the status alone: “an address and a token are not a storefront’s to show.”
An operator reads the list on the Newsletter screen, types in an address for a customer who asked at the counter, removes one on request, and exports the list as CSV. The package says it plainly: “Nothing here sends a newsletter: that is a marketing tool’s job, and the export is how the list reaches one.”
Each address gets a random token, and the unsubscribe route takes that token rather than the address, so a forwarded link cannot be edited into somebody else’s. Signing up again after leaving puts the same row back to subscribed.
Configuration
Nothing to configure in Go: Config has no fields. The Plugins screen holds the three lines of copy a storefront’s signup box can show.
| Setting | Environment variable | Required | What it does |
|---|---|---|---|
headingSignup heading | — | No | “Get the news” by default. Public, for the storefront to read. Panel only. |
blurbSignup text | — | No | “New arrivals and offers, now and then. Unsubscribe any time.” by default. Public. Panel only. |
thanksAfter signing up | — | No | “Thanks — you’re on the list.” by default. Public. Panel only. |
The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own main() you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.
Setting it up
- Install the modulePass newsletter.New to gocommerce.New, as below, or start the reference binary with -newsletter. It adds one table, and the plugin starts switched on.
- Post the signupThe storefront posts email, name and source to POST /x/newsletter/subscribe, with a hidden website field that people leave empty.
- Export for the mailerExport CSV on the Newsletter screen, or GET /api/admin/x/newsletter/subscriptions.csv, downloads the subscribed addresses with name, source and dates.
- Unsubscribe by tokenGET /x/newsletter/unsubscribe?token=…, or a POST with the token in the body, marks the address unsubscribed. The token lives in the table only; see the limits.
main.go
import (
"github.com/itswadesh/gocommerce/core"
"github.com/itswadesh/gocommerce/ext/newsletter"
)
app, err := gocommerce.New(cfg, newsletter.New(newsletter.Config{})) The package doc has no example of its own; this is how the reference binary’s -newsletter flag and examples/store/main.go install it. cfg is your gocommerce.Config. Import path github.com/itswadesh/gocommerce/ext/newsletter.
How it works
-
Twice is once
An address is unique per store. The same address posted again changes nothing and gets the same answer; a name given the second time replaces the first. After an unsubscribe, a new signup re-subscribes the row and records its new source.
-
A honeypot for bots
A hidden website field. A request that fills it is answered 201 subscribed, and nothing is kept.
-
A quiet unsubscribe
A token nobody has, or a link clicked twice, gets the same 200. In the code’s words, nothing about the list is learnable from this route.
-
An export a mailer can read
email, name, source, status, subscribed_at and unsubscribed_at, one address per row, in a file named newsletter-<date>.csv. Subscribed addresses only, unless ?status=unsubscribed asks for the others.
-
Rights of its own
newsletter.read, which managers and staff hold by default, shows the list. newsletter.write, given to no role by default, adds and removes. The export needs data.export.
-
Masked where it must be
On a demo store or for an agent key, addresses in the list and the export are masked, and search matches the name only, so an address cannot be read back one character at a time.
What it does not do
Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.
- It sends no mailNo newsletter, no welcome and no confirmation. The list reaches a mailing tool only through the CSV export or the admin list.
- No double opt-inAn address is subscribed on the first POST, with no confirming mail, so an address typed by somebody else is on the list. What the row records is the source and the date.
- The token is not handed outNo route returns an address’s unsubscribe token, and the export has no token column. A mailer working from the export cannot build the unsubscribe link the plugin’s description says every mail should carry; the token has to be read from the newsletter_subscriptions table.
- Opening the link is enoughGET /x/newsletter/unsubscribe changes the status. There is no confirmation step.
- Nothing comes back from the mailerThere is no import and no route for a mailing tool to call. An address that unsubscribes inside the mailing tool stays subscribed here until an operator removes it or the store’s own link is used.
- Removing is deletingRemoving an address deletes its row, and the record of when it signed up and left goes with it. Switching the plugin off closes the signup only: unsubscribing and the admin routes keep working.
FAQ
Questions about the Newsletter module
Does it send newsletters?
No. It collects signups and unsubscribes and keeps the list. Sending is left to a mailing tool, which gets the list from the CSV export.
What is in the export?
One row per address, with email, name, source, status, subscribed_at and unsubscribed_at, the dates in RFC 3339 and UTC. By default only subscribed addresses; ?status=unsubscribed gives the ones who left.
How does a shopper unsubscribe?
Through a link to /x/newsletter/unsubscribe carrying their token, opened or posted. The token is created at signup and kept in the store’s table; no route returns it, so building the link means reading it from the database.
Are bot signups kept?
Not those that fill the hidden website field: they are told they are subscribed and nothing is stored. Other bots are not stopped by the module.
Source
Everything on this page is read from ext/newsletter in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.
Try it against a store of your own
The backend + admin quick start runs GoCommerce’s API and admin on your own machine, without a storefront. Add this module to it and try it on test orders before a real one depends on it.