Let people buy mPAKT Compute credits with a card, through Stripe Checkout. Nobody enters a card number on this site; Stripe's page takes it, then tells the server, and the server adds the credits. About twenty minutes, most of it in Stripe's dashboard.
Someone picks an amount on /compute and presses Pay with card. The server asks Stripe for a Checkout page priced from the number of credits (1,000 credits = 1.00; the client never sends a price), and sends the browser there. When the payment succeeds Stripe calls POST /api/stripe/webhook, the server checks the signature and adds the credits, once, however many times Stripe repeats the call. A refund or a dispute takes the matching share of the credits back.
Bonus credits. People who volunteer their devices earn a contributor tier (Bronze, Silver or Gold; see compute/rewards.js), and a tier adds bonus credits to a card purchase: 5%, 10% or 20% on top. The price never changes: they pay for the credits they asked for and the bonus is extra. The bonus is worked out by the server when the Checkout Session is created, shown on Stripe's page as part of the product name (“… + 50 contributor bonus”), and carried in the session's metadata together with a signature only this server can make (an HMAC keyed with STRIPE_WEBHOOK_SECRET). The webhook credits a bonus only if the signature checks out and the amount is within the largest tier's rate; anything else is credited as an ordinary purchase without a bonus. You set nothing up for this, and it needs no extra Stripe permission or event. One consequence: if you change STRIPE_WEBHOOK_SECRET (for instance when going live) while a customer is paying, that one purchase is credited without its bonus; add the bonus by hand.
With the two secrets below unset nothing changes: the button is hidden and people ask you for credits by hand, as before.
Sign up at dashboard.stripe.com/register. You can do everything up to step 9 before Stripe has verified your business. Stay in test mode (the switch at the top of the dashboard; every key and webhook below exists once for test and once for live) until step 9.
Developers → API keys → Create restricted key. Name it mpakt-grid. Leave every permission at None except:
Refunds and disputes need no extra permission: the webhook event already carries everything the server needs. If a test purchase fails with a permissions error, open the key's View request logs (the ⋯ menu next to it): Stripe names the exact permission the failed request needed — add that one and nothing else. Copy the key (rk_test_…) — it is shown once. A full secret key (sk_test_…) also works but can do far more if it ever leaks; prefer the restricted one.
Developers → Webhooks → Add endpoint.
https://YOUR-PUBLIC-HOST/api/stripe/webhook (the address the world reaches this server at, over https)checkout.session.completedcheckout.session.async_payment_succeeded (bank debits and other delayed methods)charge.refundedcharge.dispute.createdAfter saving, reveal the Signing secret (whsec_…) and copy it. If you use Cloudflare, make sure no WAF or bot rule blocks /api/stripe/webhook; Stripe's calls are plain POSTs from the internet.
On the server, from /srv/mpakt_vtmu, as the service user. Each command asks for the value without echoing it and writes it to .env (mode 600) encrypted with AES-256-GCM; the first run creates the master key:
npm run admin -- env set STRIPE_SECRET_KEY npm run admin -- env set STRIPE_WEBHOOK_SECRET npm run admin -- env list # names and "encrypted", never values
Plain settings go in the same .env (PUBLIC_URL=https://YOUR-PUBLIC-HOST: where people are sent back to after paying; set it if it is not already). Back up ~/.config/mpakt-grid/env.key, or use a systemd credential to bind the key to the machine; see the README, "Secrets". No systemd drop-in is needed.
Optional, in .env:
STRIPE_CURRENCY | default usd. Any two-decimal currency Stripe supports; 1,000 credits cost 1.00 of it, so only change it if you want that. |
STRIPE_MIN_USD | default 5: the smallest purchase (Stripe has its own minimum around 0.50; fees make tiny sales a loss). |
STRIPE_MAX_USD | default 500: the largest purchase. |
(The names say USD; the amounts are in STRIPE_CURRENCY's units.)
sudo systemctl restart mpakt_vtmu.service journalctl -u mpakt_vtmu.service -n 20 --no-pager | grep stripe
You should see [stripe] card payments on. Restarting drops volunteers' connections for a moment; they reconnect by themselves. Open /compute: the Credits box now has Buy credits, and the Owner box says card payments are on.
Buy the smallest amount. On Stripe's page use card number 4242 4242 4242 4242, any future expiry, any CVC and postcode. You land back on /compute, the credits appear within seconds, and the purchase is listed under Purchases (with its bonus, if the account has a tier). In the dashboard, Developers → Webhooks → your endpoint → Event deliveries should show the four event types answered with 200. Other cards worth trying: 4000 0000 0000 9995 (declined) and 4000 0025 0000 3155 (asks for 3-D Secure).
Without a public address you can still test locally with the Stripe CLI: stripe listen --forward-to localhost:8008/api/stripe/webhook prints a temporary whsec_… to use as STRIPE_WEBHOOK_SECRET.
In the dashboard open the test payment and press Refund. The buyer's credits drop by the same share (a half refund takes back half of the credits, bonus included) and the Purchases list shows the amount refunded. A balance can go below zero; a box with no credits answers 402 until the buyer tops up. If a refund arrives for credits already spent, that is the cost of refunding.
The price of a credit is fixed at 1.00 per 1,000 credits; what credits buy is the price table on the Credits box (requests, GB-seconds, traffic), set in compute/service.js (PRICES). Stripe takes a fee from every payment (in the US around 2.9% + 30 cents), which is why the minimum purchase defaults to 5. Check the figures for your country on stripe.com/pricing. You may also owe tax or need terms of sale; that is outside this software.
Complete Stripe's business activation (Settings → Business). Then switch the dashboard to live mode and repeat steps 2 and 3 there: a new restricted key (rk_live_…) and a new webhook endpoint with the same URL and the same four events, which has a different signing secret. Put both into /etc/mpakt_vtmu/stripe.env, then sudo systemctl restart mpakt_vtmu.service. Make one small real purchase yourself and refund it.
stripe.env, restart, then delete the old one in Stripe..env and restart. Credits already bought stay.data/compute.json; Stripe is your system of record.Back to mPAKT Compute. Environment variables are also listed in the README.