Repository navigation
feat: add the lender application flow and its API - #5
Merged
Merged
Conversation
added 8 commits
November 3, 2026 21:07
A demo that makes its own vendor calls should record every one without opening a port that could clash with LocalStack's. The client hands each request straight to the proxy and gets the answer back, so the inspector sees the same events as with a listener.
The lender measures the last 30 days from the moment the seeded history ends, not from the wall clock, because seeding moves the app emulators' clocks ahead. Keeping that moment in the facts makes the windows the same on every run.
The lender connects to the merchant's three accounts and reads sales, payouts, the bank account and receivables. Every call goes through a wire proxy so it is recorded, and the windows are measured from the moment the seeded history ends. The Stripe key is exported so the seeder and the lender use the same one.
The advance is the last 30 days of card sales rounded down to $500, for a flat 8% fee. Every number the offer rests on names the vendor call it came from, so a presenter can show where it comes from. Plaid's balance is shown but not used, since the app emulator's fixture is too thin to price against.
An application moves through apply, connect, review and offer, and each step is refused out of order. A step that fails saves nothing and can be tried again. State lives in the store, so the command line and the server see the same applications.
The API tests need the same three-vendor fake the CLI tests use. Moving it to one package keeps a single copy, with its strictness about the Stripe key.
The UI and the inspector need the flow over HTTP: status, reset, apply, connect, review, offer and the recorded vendor calls. A reset waits for in-flight calls and they wait for it, so nothing reads half a world. The server mounts the API under /api/ beside the UI.
Until the UI exists, the API is how the flow is shown. List the routes and the one offer the seeded merchant gets, so a presenter can run it from a terminal and know what to expect.
This was referenced Nov 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #4. Merge #4 first. The base branch is
feat/seeded-world-and-reset, and GitHub retargets this tomainonce that branch is merged and deleted.Motivation
The seeded world needs a lender to act on it. This adds ACME Commerce Lenders' own logic: a merchant applies, the lender connects to the merchant's Stripe, Plaid and Xero accounts, reads what they hold, and makes a working-capital offer. It is served over HTTP so the UI and the inspector have something to call.
Changes
feat(shared): let the recording proxy serve calls in process: the lender makes its own vendor calls, so each one is recorded without opening a port that could clash with LocalStack's.feat(acme): record when the world's history ends: "the last 30 days" is measured from that moment and not from the wall clock, because seeding moves the app emulators' clocks ahead.feat(acme): read a merchant's data from Stripe, Plaid and Xero: connects to each account, pages through all 941 charges, and reads payouts, the bank account and the invoices. Every call is recorded and credentials are masked in the recording.feat(acme): price a traceable working-capital offer: the advance is the last 30 days of card sales rounded down to $500, for a flat 8% fee. Each number names the vendor call it came from. Plaid's balance is shown but does not change the offer, because the app emulator's fixture is too thin to price against.feat(acme): add the application flow from apply to offer: apply, connect, review, offer, each refused out of order. A failed step saves nothing and can be retried.test(acme): share the fake vendors between test packagesfeat(acme): serve the application flow as an HTTP API: status, reset, the steps, and the recorded vendor calls. A reset waits for in-flight calls and they wait for it.docs(acme): document the application API and the seeded offerFor the seeded merchant the offer is a $30,000 advance for a $2,400 fee ($32,400 to repay), on sales growth of 14.8%.
Tests
On the final tip:
The seeded merchant's offer is pinned by a test. Its numbers were checked against a separate Python implementation of the world's order generator, which agrees on 941 orders, a gross of $81,083.84, last-30-day sales of $30,423.72, and an advance of $30,000. Each of the eight commits was also checked out on its own and passes gofmt,
go vet,go test -raceandmake check-kits.Run for real, with Plaid, Xero and Stripe built from
WonderTwin-AI/wondertwinorigin/main(9a92fbdf), and then again with Stripe served by a running LocalStack and Plaid and Xero run locally. In both, over HTTP:Both runs gave identical numbers, and each recorded call says whether LocalStack or a local process served it.
Not tested: running with the network off.
Related
DOC-581