Running it
The checks that run every night from outside, the failure cases you can run now, every test suite, and how it holds under load.
Checked every night, from outside
A Render Workflow opens every page on the five-minute path, calls the public API, pays a gift split across its payees with a sandbox test card, and runs each failure case against the PayPal sandbox. Each check is its own task, retried if PayPal’s sandbox hiccups, and the results land here.
Reading the latest run…
Failure cases, run live
Payments fail in ordinary ways: a declined card, a double click, a browser that sends the wrong amount. Each check below runs the real code against the PayPal sandbox.
All eight, with what should happen
The donor's card is declined
Nothing is charged, nothing is marked paid, and the donor sees a plain next step.
Not run yet
The donor clicks Pay twice
PayPal charges once. Both clicks get the same capture.
Not run yet
Someone tries to complete a payment PayPal has not approved
PayPal refuses, nothing is charged, and the message is plain.
Not run yet
A browser tries to change the amount or the payee
The server ignores both. Amount and payee come only from the need record.
Not run yet
A gift would lower her check
The rules engine skips that route and sends the money a benefit-safe way.
Not run yet
An AI reviewer gets the rule wrong
Its quote is not in its rulebook, so the referee sets it aside and her check stays whole.
Not run yet
Someone edits a past ledger entry
Recomputing the chain finds the exact entry that changed.
Not run yet
Only the shop can send money back
Giftwise cannot pull money from a shop. PayPal allows the refund only from the account that received it.
Not run yet
Test results
Latest run November 10, 2026; each suite’s own date is with its command. Production build on localhost, PayPal sandbox, Postgres 17. The counts are written by scripts/test-results.mjs after each run, and the README and TESTING.md are checked against the same file.
- Rules engine, routing, the SSA report, the checks workflow and the cited answers469/469
- API end to end130/130
- Browser end to end71/71
- Accessibility and layout144/144
- No keys, every page72/72
- Real PayPal checkout, logged in as the sandbox buyer14/14
- An AI agent starts a checkout over MCP5/5
- A monthly gift, two months, as the sandbox buyer8/8
- Outside checks, as Render Workflows runs them25/25
- The organizer's side, end to end21/21
- The Postman collection, with Newman20/20
- Random gifts, needs and people100,000/100,000
- Failure cases, live8/8
What each checks, and its command
Rules engine, routing, the SSA report, the checks workflow and the cited answers 469/469
Every rule row, the 2026 values and the 2027 values for January and February checks, 1,000 random gifts held to all thirteen promises, the report code that lets anyone check a report, the Spanish rule sentences, the $60 quarterly exclusion in the order gifts arrive, shelter and the one-third reduction's meals test, prepaid housing bills, ABLE, gift routing, monthly gifts month by month, the Easy Read report in English and Spanish (every sentence 15 words or fewer) and its calendar reminder, pronouns, the reviewers' referee and what it does when one reviewer is wrong, the monthly report to Social Security, the checks workflow's fan-out and retries, the AG Studio to Claude translation, a known source behind every real-world answer, Social Security's own worked examples, and the benefit calendar, the logger's masking, the Postgres rate-limit table, the operations page's id shortening, the For agents recording against the gift page, the outside checks against the MCP server and render.yaml, and every test number in the README and TESTING.md against this file
npm test, November 10, 2026
API end to end 130/130
Gift checks, presets, bill and fundraiser reading, the two reviewers, gift pages, the monthly SSA report and its CSV, the MCP server and every tool it lists, a monthly gift started by an agent, the rules watch asked live, the report check page, the MCP Server Card and AI Catalog, llms.txt, security headers, share images, the outside checks and their shared secret, PayPal orders, routed gifts, fund payouts and their reconciliation, receipts, webhook signature checks, the ledger, developer keys and every error path
npm run e2e:api, November 10, 2026
Browser end to end 71/71
The Gift Checker, Try it, the two reviewers, a fundraiser turned into a gift page, For agents, the AG Studio dashboard and its assistant, For organizations, the gift page and its real-world answers, the benefit calendar, the gift page in Spanish, the guided tour, the calendar planner, keyboard use, and the developer flow
npm run e2e:ui, November 10, 2026
Accessibility and layout 144/144
axe WCAG 2.2 AA on every page, at desktop and phone widths, light and dark, with no sideways scroll: the gift page and receipt in English and Spanish, the organizer's manage page, the page that checks a report, the SSA report as given and as cash, its Easy Read version in English and Spanish, a giver's monthly gift page, the benefit calendar, a generated gift page, the Evidence page and its three case lists, For agents, the AG Studio dashboard, For organizations and the operations page
npm run e2e:a11y, November 10, 2026
No keys, every page 72/72
Every page and public endpoint answers with only a database: PayPal, Claude, Channel3 and Elastic each on its fallback. It also checks the request id in the header, the envelope and every error message, traced on the operations page, and that the recorded assistant on For agents names the same checks as Maya's gift page. CI runs it on every push against a fresh build and an empty Postgres, after npm audit (0 advisories at every severity), types, lint and the unit tests.
npm run e2e:smoke, November 10, 2026
Real PayPal checkout, logged in as the sandbox buyer 14/14
Both PayPal buttons: the buyer logs in and approves, PayPal captures every payee, the page updates, and the public receipt matches
npm run e2e:paypal, November 9, 2026
An AI agent starts a checkout over MCP 5/5
The agent calls start_gift_checkout, the sandbox buyer approves the PayPal link, and Giftwise captures on return and shows the receipt
npm run e2e:agent, November 8, 2026
A monthly gift, two months, as the sandbox buyer 8/8
Start $300 a month, approve the first month in PayPal, plan the next, check the rules routed it again with what changed and why, approve it, and confirm each order paid every purchase unit to its own payee
npm run e2e:monthly, November 9, 2026
Outside checks, as Render Workflows runs them 25/25
Every failure case against the PayPal sandbox, every page on the product path (now with the Easy Read report in Spanish) and the public API promises, including the Federal Register watch, all at once, the same list the nightly Render Workflow runs
npm run checks:local, November 8, 2026
The organizer's side, end to end 21/21
The sample's manage link; a need added, edited and removed with the rules choosing its route; the gift finder with a gift card held back and a product added with its link; closing and reopening the page; a webhook that refuses private addresses, receives a signed test event, then a signed gift.received from a real sandbox capture; a new page's own manage link
npm run e2e:manage, November 8, 2026
The Postman collection, with Newman 20/20
16 requests in five folders: gift checks, a quarter's exclusions, a bad request, the report and the page that checks it, the ledger, the rulebook, the rules watch, the MCP server and its discovery documents, OpenAPI and health
npm run test:postman, November 8, 2026
Random gifts, needs and people 100,000/100,000
100,000 seeded random gifts from one cent to $20,000, each held to thirteen promises: every cent placed, her check exactly as with no gift, never below cash, need caps, the ABLE yearly limit, balance and eligibility, at most 10 PayPal recipients, whole cents, cited rules, checks within the federal rate, recomputation and the same answer every time
npx tsx scripts/run-invariants.ts, November 9, 2026
Failure cases, live 8/8
Run the eight failure cases on this page yourself
POST /api/cases/run, November 6, 2026
The PayPal suites, step by step
The two suites that move sandbox money write a redacted transcript as they run, in content/eval/e2e-transcripts.json: each step with its PayPal order and capture ids, how many payees, the statuses PayPal reported and the timings. No address, key or token is kept. A GitHub Actions workflow (PayPal checkout, sandbox) runs the checkout suite on request when the sandbox secrets are set on the repository. A check named in a step is at its year’s rate: the full amount is $994 in 2026 and $1,029 from January 2027.
Real PayPal checkout, logged in as the sandbox buyer: 14 of 14, November 9, 2026, 18 steps in 41.2 s
Run against a local production build, PayPal sandbox. Steps that name a PayPal order or capture are shown with their ids; the rest are the suite’s own checks.
| Step | PayPal | At |
|---|---|---|
| need button creates an order | 4.8 s | |
| the need order, as created | order 8XL1701886187253P; 1 payee; PAYER_ACTION_REQUIRED | 4.8 s |
| PayPal checkout opens for that order | 6.5 s | |
| need checkout asks the donor for no shipping address | 10.8 s | |
| card shows it paid, with the buyer's name | 13.7 s | |
| PayPal says the need order is COMPLETED | 14.5 s | |
| PayPal's record of the need order | order 8XL1701886187253P; 1 payee; COMPLETED; captures 22K29357G96713608 | 15.0 s |
| public receipt for the need order | 15.0 s | |
| Give any amount creates one routed order | 17.6 s | |
| the routed order, as created | order 0HR89776MY849324R; 4 payees; PAYER_ACTION_REQUIRED | 17.6 s |
| PayPal checkout opens for the routed order | 20.9 s | |
| routed checkout asks the donor for no shipping address | 31.0 s | |
| thank-you shows every payee captured, each once | 38.1 s | |
| thank-you lists the same lines, in the same order, as the preview | 38.1 s | |
| needs the gift covered show as paid without a reload | 38.1 s | |
| PayPal says the routed order is COMPLETED | 39.1 s | |
| PayPal's record of the routed order | order 0HR89776MY849324R; 4 payees; COMPLETED; captures 4VE01614J5227092P, 68552514CD769774K, 2PJ75858779200138, 0HD57516W75436645 | 40.0 s |
| public receipt lists every capture, one line per purchase unit | 41.0 s |
API end to end: 130 of 130, November 10, 2026, 133 steps in 43.2 s
Run against a local production build, PayPal sandbox. Steps that name a PayPal order or capture are shown with their ids; the rest are the suite’s own checks.
| Step | PayPal | At |
|---|---|---|
| check-gift cash $1,000 -> $74 | 0.1 s | |
| check-gift vendor $420 -> $994 | 0.1 s | |
| check-gift rent $800 monthly -> $662.67 | 0.1 s | |
| check-gift ABLE rent -> $994 | 0.1 s | |
| check-gift a home given as itself counts at the presumed maximum value -> $662.67 (POMS SI 00835.400 F) | 0.2 s | |
| check-gift a co-owner's $1,600 for eight months counts $200 in February -> $814 (POMS SI 00835.360 D.3) | 0.2 s | |
| check-gift rent $130 below market from a child, $280 paid, counts $71.33 (POMS SI 00835.380 D) | 0.2 s | |
| plan-month values a co-owner's payment in each of the eight months it covers | 0.2 s | |
| check-gift covers that end before they start -> 400 | 0.2 s | |
| check-gift envelope has meta.request_id, rules_version, citations with urls | 0.2 s | |
| check-gift default date (today) works | 0.2 s | |
| check-gift bad JSON -> 400 invalid_request | 0.3 s | |
| check-gift negative amount -> 400 | 0.3 s | |
| check-gift unknown delivery -> 400 | 0.4 s | |
| plan-month shares the $60 exclusion across the quarter (50 then 10) | 0.4 s | |
| plan-month empty list -> 400 | 0.4 s | |
| preset wheelchair-repair download + expected result | 0.4 s | |
| preset birthday-cash download + expected result | 0.4 s | |
| preset electric-bill download + expected result | 0.4 s | |
| preset rent-help download + expected result | 0.4 s | |
| preset laptop-college download + expected result | 0.4 s | |
| preset groceries download + expected result | 0.4 s | |
| preset van-fundraiser download + expected result | 0.4 s | |
| preset march-month download + expected result | 0.4 s | |
| preset CSV download has header + 6 rows | 0.4 s | |
| unknown preset -> 404 | 0.4 s | |
| read-bill with a broken image -> friendly 422 (or 503 without AI) | 0.4 s | |
| read-bill replayed reads the electric bill as a $138 utility and routes to ABLE | 0.5 s | |
| analyze-fundraiser replayed plans every use so her check stays $1029 | 0.5 s | |
| analyze-fundraiser rejects non-http url | 0.5 s | |
| review-bill without a description -> 400 | 0.5 s | |
| review-bill replayed: both reviewers quote their own rulebook, agree it is housing, and the engine routes to ABLE at $994 | 0.5 s | |
| openapi lists review-bill with its request schema | 0.5 s | |
| openapi lists the letter reader and the manage link's profile | 0.5 s | |
| gift-pages: the lift is paid straight to the shop, rent goes to the ABLE account, check stays $1029 | 0.6 s | |
| the new page reads Help Jordan and uses their, never her or she | 0.6 s | |
| gift-pages: a need toward a home goes to the ABLE account | 0.7 s | |
| gift-pages with his own situation: his check is $106, not $1029, and both needs keep it | 0.7 s | |
| his gift page shows his check, $106, from his own situation | 0.7 s | |
| manage link reads his situation: own, from his answers, with the fields given | 0.8 s | |
| manage link puts the page back on the sample situation: $1029 | 0.8 s | |
| manage link sets his facts again, read from his letter | 0.8 s | |
| manage link refuses a negative amount | 0.8 s | |
| read-letter replayed: the sample letter's $106 for January 2027 matches the engine, every quote is in its text, and nothing private comes back | 0.8 s | |
| read-letter with no file -> 400 (or 503 without AI) | 0.8 s | |
| gift-pages rejects a name that is not letters | 0.8 s | |
| paypal order for need 4 created (sandbox) | 1.9 s | |
| the need order, as created (never approved: the API suite moves no money) | order 26F53216UM334992W; 1 payee; PAYER_ACTION_REQUIRED | 1.9 s |
| paypal order for unknown need -> 404 | 1.9 s | |
| paypal order with bad id -> 400 (or 403 from the host firewall) | 1.9 s | |
| capture unknown order -> 404 | 1.9 s | |
| capture path traversal -> not 200 | 1.9 s | |
| capture before buyer approval -> PayPal refuses, app returns plain error | 2.6 s | |
| gift-orders $1,500 -> one order, every open need plus ABLE | 3.8 s | |
| gift-orders plan keeps her full check, cash would not | 3.8 s | |
| gift-orders lines add up to the gift | 3.8 s | |
| live order check returns each payee unit from PayPal | 4.2 s | |
| the routed $1,500 order, read back from PayPal | order 1J410122JJ374220G; 5 payees; PAYER_ACTION_REQUIRED | 4.2 s |
| receipt page renders for a started order | 4.2 s | |
| gift-orders $0 -> 400 | 4.2 s | |
| gift-orders unknown page -> 404 | 4.2 s | |
| live order check with bad id -> 400 | 4.2 s | |
| live order check on the recorded multi-payee order -> 200 COMPLETED, every capture | 5.4 s | |
| receipt for unknown order -> not-found page, noindex | 5.5 s | |
| ledger verifies end to end | 5.5 s | |
| key created, shown once, gw_test_ plus 43 base62 characters | 5.5 s | |
| key without accepting terms -> 400 | 5.5 s | |
| keyed call works and returns rate-limit headers | 5.6 s | |
| unknown key -> 401 unauthorized | 5.6 s | |
| usage lists the key and its call | 5.6 s | |
| revoke from another browser -> 404 | 5.6 s | |
| rotate: old key stops, new key works | 5.6 s | |
| openapi.json is 3.1 and lists every endpoint | 5.6 s | |
| unknown preset -> 404 in the standard envelope | 5.6 s | |
| ssa-report October as given: 5 gifts, nothing counts, check stays $994, due November 10 | 5.8 s | |
| ssa-report records every gift with a rule and a cited source | 5.8 s | |
| ssa-report the same October as cash: 5 gifts to tell SSA, $538 counted, check $456, a call script | 5.9 s | |
| ssa-report CSV download has a header and one row per gift | 6.2 s | |
| ssa-report bad month -> 400 | 6.2 s | |
| ssa-report unknown page -> 404 | 6.2 s | |
| report page renders the verdict and the PayPal proof column | 6.5 s | |
| checks: the workflow can list the eight failure cases | 6.5 s | |
| checks: running a case without the secret -> 401 (or 503 if the server has none) | 6.5 s | |
| checks: paying the card check without the secret -> 401 (or 503 if the server has none) | 6.6 s | |
| checks: filing a run with the wrong secret -> 401 (or 503 if the server has none) | 6.6 s | |
| checks: a malformed run with the right secret -> 400 with details | 6.6 s | |
| workflow jobs: the active monthly gifts are listed by id and one month is planned ahead, with its split | 6.6 s | |
| workflow jobs: listing monthly gifts without the secret -> 401 | 6.6 s | |
| workflow jobs: the rulebook index rebuilds on Elastic, or reports Elastic is not connected | 12.8 s | |
| checks: the latest run is public, with a pass count | 12.8 s | |
| checks: starting a run needs Render Workflows (503 here) or is rate limited (429) | 12.8 s | |
| MCP initialize names the server and agrees a protocol version | 12.8 s | |
| MCP notification -> 202 with no body | 12.8 s | |
| MCP lists the nine tools with input schemas | 12.9 s | |
| MCP start_monthly_gift returns the giver's page, nothing charged, check stays $1029 | 12.9 s | |
| MCP plan_gift splits $500 so her check stays $1029 (cash would be $609) | 13.0 s | |
| MCP check_gift counts a home given as itself at the presumed maximum value -> $662.67 (POMS SI 00815.550 C.2) | 13.0 s | |
| MCP check_gift values rent below market from a child as $351.33 less the $280 paid -> $942.67 | 13.0 s | |
| MCP plan_gift puts $1,500 toward her home through the ABLE account, check stays $1029 | 13.1 s | |
| MCP get_gift_page reads his own situation and his $106 check | 13.1 s | |
| MCP plan_gift keeps his own check whole at $106; as cash it would be lower | 13.1 s | |
| MCP get_ssa_report: October needs no income report, due November 10 | 13.3 s | |
| MCP bad arguments come back as a tool error the agent can read | 13.3 s | |
| MCP unknown method -> JSON-RPC -32601 | 13.3 s | |
| check-gift a $25 weekly rent counts $108.33 a month -> $905.67 (POMS SI 00835.474 B.3) | 13.3 s | |
| plan-month divides a $1,200 tax bill between two in the household: $351.33, then $50 and $50 (POMS SI 00835.474 B.2) | 13.4 s | |
| check-gift with $101,000 in ABLE and $1,500 saved pays no SSI, ABLE the reason (POMS SI 01130.740 D.1.c) | 13.4 s | |
| check-gift an ABLE gift past $100,000 says the check stops and routes it elsewhere | 13.4 s | |
| MCP check_gift takes the ABLE balance: past $100,000 and over the limit, $0 and the item route instead | 13.4 s | |
| check-gift paying $100 of a $150 share says the household already takes a third: $662.67 (POMS SI 00835.160 G) | 13.4 s | |
| check-gift a utility bill paid as a loan she repays in full counts nothing (POMS SI 00835.482 G.4.c) | 13.4 s | |
| MCP check_gift: paying $175 toward a $150 share is sharing, so the check is the full $994 | 13.5 s | |
| unsigned PayPal webhook is refused | 13.5 s | |
| org payouts over the per-household sandbox limit -> 400 | 13.5 s | |
| org payouts to an unknown page -> 422 | 13.5 s | |
| org payouts preview routes a $10 grant and keeps the check at $1029 | 13.5 s | |
| org payouts sends one PayPal Payouts batch | 14.5 s | |
| the fund's $10 grant as one PayPal Payouts batch | batch QEEN7WYZXGGXJ; 1 payee; PENDING | 14.5 s |
| PayPal reports the batch sent, with a transaction id for every item | 40.4 s | |
| each paid item is checked against PayPal's transaction report | 40.4 s | |
| unknown payout batch -> 404 | 40.4 s | |
| rules-watch: the Federal Register answers live, every SSI income rule and amounts notice is reviewed, 2027 amounts in use | 40.4 s | |
| report code: October's report names its check page, and the page says it matches | 40.7 s | |
| report code: October is all sample year, and its check page says PayPal was not asked | 40.7 s | |
| report code: the recorded order's month asks PayPal for every capture, and every one is completed | 42.1 s | |
| report code: a code that does not match shows the current report's code | 42.4 s | |
| ask-rules: the phone bill question finds SSA's own passage on bills paid by someone else, and every quote it shows is checked | 42.9 s | |
| ask-rules: a question that is too short gets a plain 400 | 43.0 s | |
| discovery: the AI Catalog lists the MCP Server Card, and the card names /api/mcp with its protocol versions | 43.0 s | |
| discovery: the Server Card answers 304 to a matching ETag | 43.0 s | |
| llms.txt names the MCP server and every tool | 43.0 s | |
| security headers: no framing by other sites, nosniff, a referrer policy, and HSTS | 43.0 s | |
| the gift page has its own share image | 43.2 s |
Under load
Visitors at once, each checking a gift and opening Maya’s page, measured twice: gently on the deployed site, and harder on a laptop running the production build. Beside them, the ledger race test fires 60 appends at once and the hash chain verifies.
On the deployed site: 10 visitors at once, 3 rounds, 60 requests in 3.4 seconds, November 8, 2026
- POST /api/v1/check-giftp50 176 ms, p95 508 ms, p99 735 ms, 0 of 30 failed
- GET /g/mayap50 827 ms, p95 1113 ms, p99 1141 ms, 0 of 30 failed
On a laptop: 100 visitors at once, 3 rounds, 600 requests in 18.9 seconds, November 7, 2026
- POST /api/v1/check-giftp50 1514 ms, p95 3296 ms, p99 3915 ms, 0 of 300 failed
- GET /g/mayap50 3680 ms, p95 5616 ms, p99 5739 ms, 0 of 300 failed
The exact method and machines
On the deployed site. 10 concurrent runs, 3 rounds, each run a gift check (POST /api/v1/check-gift, $1,000 as cash) then the gift page (GET /g/maya), after one warm-up request per path. Each run arrives as its own visitor (its own x-forwarded-for in 198.51.100.0/24), so the per-visitor demo limit sees 10 visitors at once. Latency is measured by the client from request start to the full body, with node's fetch on a laptop, over the internet.
Machine: The deployed site: one Render Starter web service (0.5 CPU, 512 MB) and its Render Postgres, in Render's Oregon region; the client is a laptop (Intel(R) Core(TM) Ultra 9 185H, Node v24.13.1) on a home internet connection, so each time includes the network.
On a laptop. 100 concurrent runs, 3 rounds, each run a gift check (POST /api/v1/check-gift, $1,000 as cash) then the gift page (GET /g/maya), after one warm-up request per path. Each run arrives as its own visitor (its own x-forwarded-for in 198.51.100.0/24), so the per-visitor demo limit sees 100 visitors at once. Latency is measured by the client from request start to the full body, with node's fetch on the same machine as the server.
Machine: Intel(R) Core(TM) Ultra 9 185H, 22 threads, 32 GB, win32 10.0.26300, Node v24.13.1; the server is the production build (next start) with Postgres 17 in Docker on the same machine.
On the deployed site, one visitor at a time, Maya’s page answers in 178 ms and a gift check in 120 ms (p50, network included); with 10 at once, requests wait their turn for its half CPU, which is the difference above. Several instances behind Render’s load balancer take that wait away (the table below).
npm run test:load; BASE_URL=https://giftwise-405i.onrender.com LOAD_WHERE=hosted npm run test:load -- 10 3
Running it for real
What the demo runs on today, and what a real deployment changes. Each row is an operating range with a known next step.
| The demo runs on | A real deployment changes it to |
|---|---|
| One Render Starter web instance (0.5 CPU, 512 MB), always on | Several instances with autoscaling; rate limits and the request log already live in Postgres, so nothing is per instance |
| One 256 MB Postgres with a 1 GB disk, no pooler | A larger Postgres with a connection pooler, point-in-time recovery, and a restore tested on a schedule |
| Share images rendered on request; no uploads kept | Object storage with signed URLs for anything a family uploads, never the database |
| About $2 of Claude a day, capped and shown on the Operations page | A budget per organization, with the same per-call accounting |
| No login: the organizer's page is a private link shown once and stored as a hash; the organizations page is a sample fund | Login and roles for organizations, with the manage link kept for families |
| One PayPal sandbox app and one webhook; PAYPAL_ENV=sandbox (src/lib/paypal/env.ts) | PAYPAL_ENV=live with the live app's keys and a webhook per environment, each with its own webhook id |
| Shops paid by payee.email_address; Partner Referrals answers this sandbox app 403 NOT_AUTHORIZED | Each shop onboarded through Partner Referrals once PayPal approves the partner account, and paid by payee.merchant_id (src/lib/paypal/orders.ts, src/lib/households.ts) |
| The ABLE share paid to a sandbox business account standing in for the plan | The ABLE share sent to the plan's own gift page, the shops still paid through PayPal |
| Nightly outside checks on Render Workflows; errors as JSON log lines | The same checks on every deploy, plus Sentry for server, edge and browser errors with personal data masked |
What stays the same: JSON logs with a request id on every line, limits and logs in Postgres, the audit ledger, the Operations page, and the Render Workflows fan-out for anything that runs in the background: one task per unit of work, in parallel, with retries.
Monthly gifts: the giver approves each month in PayPal. Measured in PayPal's sandbox on November 4, 2026: a charge with a saved PayPal wallet (a vault_id) to another payee returns BILLING_AGREEMENT_NOT_FOUND, because a saved wallet pays only the platform's own account. So each month is a fresh Orders v2 order the giver approves, paying every recipient directly, and Giftwise never holds the money.