Inspiration

A link should lead to a story, but too often it leads to another subscription. Readers may want one article from a publication without committing to a recurring plan, while publishers still deserve to be paid for their work.

Passage Pay explores a middle ground: a shared wallet that lets readers buy individual articles across participating publications. The goal is to make supporting good writing as convenient as discovering it.

What it does

Readers add credit through PayPal Sandbox, review an article’s price, and purchase access through the Passage browser extension or web flow. Purchased articles appear in their library and can be reopened without another charge.

Publishers can register their publication, verify their website, create article offers, track earnings, and request PayPal Sandbox payouts. They retain control of their content: Passage handles payment and access authorization, while the publisher’s server delivers the article.

I also built an article chat companion. On eligible purchased articles, readers can ask questions or highlight a passage for discussion. Each reply shows its usage and exact charge, paid from the same wallet.

To demonstrate the experience, I created The Common Hour, a separate publication with 30 original, source-linked essays, public introductions, and protected full text.

How I built it

Passage and the demo publisher are independent systems, each with a Go backend and Vue/TypeScript frontend. A Manifest V3 browser extension connects the reading experience to Passage. PostgreSQL stores accounts, purchases, entitlements, and financial records.

PayPal Sandbox powers wallet funding and publisher payouts. The integration uses the PayPal JavaScript SDK v6 with a redirect fallback, server-side Orders v2 creation and capture, verified webhooks, and Standard Payouts with status reconciliation. Wallet credit is recorded only after the server verifies a completed capture.

Purchasing creates a durable entitlement and a temporary access grant. The publisher redeems that grant through its own backend before serving the protected article. It never needs the reader’s Passage credentials or direct access to Passage’s database.

For article chat, the extension sends article context through Passage to OpenRouter after an explicit reader request. The documented default model is OpenAI GPT-4o mini. Passage checks article ownership and AI-analysis permission, reserves up to $0.02 for a reply, then settles verified provider usage and releases the remaining hold. The reader sees the reply’s token usage, exact charge, and running thread total. Conversation text stays in extension-local storage rather than Passage’s database.

Challenges I faced

The hardest work was making a small purchase behave correctly when requests fail, repeat, or arrive simultaneously. A double click, delayed response, or payment notification should never create a second charge. I used idempotent operations, database transactions, and balanced ledger entries to keep those outcomes consistent.

Small prices also require precise accounting. I stored money as integer millionths of a dollar, avoiding floating-point errors and preserving fractional earnings until payout.

Another challenge was keeping the purchase experience convenient while maintaining clear boundaries between the reader, Passage, and the publisher. Reopening an article, recovering an interrupted redemption, and supporting a web fallback all had to respect those boundaries.

Accomplishments I’m proud of

I built both sides of the experience: a reader wallet and library, a browser extension, publisher onboarding and earnings tools, and an independent publication that demonstrates protected content delivery.

I’m especially proud that reopening a purchased article uses a fresh access grant without charging again. That small interaction captures the experience I wanted: payment happens once, and returning to the story feels straightforward.

What I learned

I learned that payment integration extends well beyond checkout. Captures, retries, reservations, reconciliation, and payouts all affect whether a balance can be trusted.

I also learned that access and content delivery can remain separate. A payment platform can authorize a purchase while publishers keep control of their articles.

What’s next

I want to test Passage with independent publishers, refine the mobile reading experience, and learn which article prices readers find worthwhile. The current payment integration uses PayPal Sandbox; moving toward real payments would require further validation of the complete funding and payout lifecycle.

Deployment and demo

Render hosts the Passage web/API service and the independent Common Hour publisher web/API service. A managed PostgreSQL instance holds separate logical databases and application roles. The two services communicate through explicit purchase and grant-redemption APIs.

The web purchase flow works without the extension. Article chat is an extension feature and requires the matching API plus a configured server-side OpenRouter key. All payments are Sandbox; the annual membership shown on the publisher site is display-only. PayPal must enable Payouts on the Sandbox sender account before provider payouts can be exercised.

Development tools

Codex assisted development, debugging and testing. The reader and publisher interfaces use Vue, TypeScript, Vite and Tailwind CSS; the servers use Go and explicit SQL. The Common Hour demo essays disclose AI-assisted preparation and link to their public sources. Contributor names are fictional.

Built With

Share this project:

Updates