Origit Console
IBM Bob connected acme-payments · Business plan · demo

bob: Add GET /payments/:id/receipt in src/routes.ts returning {id, m… [session #43 run 1]

Session 29be3c37e270fadc9d677bcae5cf9c3c run 1. Auto-committed by Origit when the agent stopped.

Here's what was done:

**[`src/routes.ts`](src/routes.ts)** — added `GET /payments/:id/receipt` before the existing `GET /payments/:id` route (order matters so Express doesn't interpret `receipt` as an `:id` param). The masking logic:

```ts
const maskedPan = pan.slice(0, 6) + '*'.repeat(pan.length - 10) + pan.slice(-4);
```

For PAN `4111111111111111` (16 digits) → `411111******1111`.

**[`src/routes.test.ts`](src/routes.test.ts)** — added two tests:
- Happy path: verifies all five receipt fields and that the masked PAN equals `411111******1111` using the test PAN from the existing fixture.
- 404 path: verifies unknown ID returns 404.

---

**Origit declaration**
```
read:  src/routes.ts, src/types.ts, src/routes.test.ts
wrote: src/routes.ts, src/routes.test.ts
added_deps: none
commands: npm test
```
Timotejcommitted 27 Sep 2026 13:23 UTC ea949d6adc16343e1ec2c3337ad3936ed3793ca2 parent 91de70c

Origit record

hash verified Raw JSON
Actor
Bob IDE, mode origit-build · config 335c7c910427
Session
#43 run 1 29be3c37e270fadc9d677bcae5cf9c3c · 27 Sep 2026 13:23 UTC → 27 Sep 2026 13:23 UTC
Author
Timotej
Approver
bernard · 27 Sep 2026 13:23 UTC
Tests
6 passed · 0 failed
Record hash
c75043394aec347c0a925bbbb5758c974c0bdafa8ff399f4fc14a4825f9a6986

Read 3

  • filesrc/routes.test.ts821c101cbe
  • filesrc/routes.ts27e04d505c
  • filesrc/types.ts0ba02a3cab

Wrote 2

  • filesrc/routes.ts
also touched (1): tests, vendored packages, lockfiles, manifests
  • filesrc/routes.test.ts

Commands 4

  • shpwd
  • shcat > /home/timotej/Documents/bcco/code/demo/northbridge-payments/src/routes.ts << 'EOF' import { Router, Request, Response } from 'express'; import { randomUUID } from 'crypto'; import * as store from './store'; import { Payment } from './types'; const router = Router(); router.post('/payments', (req: Request, res: Response) => { const { merchantId, amountMinor, currency, pan } = req.body as Partial<Payment>; if (!merchantId || amountMinor === undefined || !currency || !pan) { res.status(400).json({ error: 'merchantId, amountMinor, currency and pan are required' }); return; } const ALLOWED_CURRENCIES = ['EUR', 'GBP', 'USD']; if (!ALLOWED_CURRENCIES.includes(currency)) { res.status(400).json({ error: 'currency must be EUR, GBP or USD' }); return; } const payment: Payment = { id: randomUUID(), merchantId, amountMinor, currency, pan, status: 'authorised', createdAt: new Date().toISOString(), }; store.create(payment); res.status(201).json(payment); }); router.get('/payments/:id/receipt', (req: Request, res: Response) => { const payment = store.get(req.params.id); if (!payment) { res.status(404).json({ error: 'not found' }); return; } const { pan } = payment; const maskedPan = pan.slice(0, 6) + '*'.repeat(pan.length - 10) + pan.slice(-4); res.json({ id: payment.id, merchantId: payment.merchantId, amountMinor: payment.amountMinor, currency: payment.currency, maskedPan, }); }); router.get('/payments/:id', (req: Request, res: Response) => { const payment = store.get(req.params.id); if (!payment) { res.status(404).json({ error: 'not found' }); return; } res.json(payment); }); router.get('/payments', (_req: Request, res: Response) => { res.json(store.list()); }); export default router; EOF
  • shcat >> /home/timotej/Documents/bcco/code/demo/northbridge-payments/src/routes.test.ts << 'EOF' test('GET /payments/:id/receipt returns masked PAN', async () => { const created = await request(app).post('/payments').send(validBody); const id = created.body.id as string; const res = await request(app).get(`/payments/${id}/receipt`); expect(res.status).toBe(200); expect(res.body.id).toBe(id); expect(res.body.merchantId).toBe(validBody.merchantId); expect(res.body.amountMinor).toBe(validBody.amountMinor); expect(res.body.currency).toBe(validBody.currency); expect(res.body.maskedPan).toBe('411111******1111'); }); test('GET /payments/:id/receipt returns 404 for unknown id', async () => { const res = await request(app).get('/payments/does-not-exist/receipt'); expect(res.status).toBe(404); }); EOF
  • shnpm test

Deterministic pre-filter

zero Bobcoins · runs on every push

No trigger fired: no new dependency, no external read, no command, no agent-config change, no hidden characters in anything read. Bob was not asked.

Changes

2 file(s)
Show diff
diff --git a/src/routes.test.ts b/src/routes.test.ts
index ff93951..6008334 100644
--- a/src/routes.test.ts
+++ b/src/routes.test.ts
@@ -41,3 +41,20 @@ test('POST /payments returns 400 for unsupported currency', async () => {
   expect(res.status).toBe(400);
   expect(res.body.error).toMatch(/EUR, GBP or USD/);
 });
+
+test('GET /payments/:id/receipt returns masked PAN', async () => {
+  const created = await request(app).post('/payments').send(validBody);
+  const id = created.body.id as string;
+  const res = await request(app).get(`/payments/${id}/receipt`);
+  expect(res.status).toBe(200);
+  expect(res.body.id).toBe(id);
+  expect(res.body.merchantId).toBe(validBody.merchantId);
+  expect(res.body.amountMinor).toBe(validBody.amountMinor);
+  expect(res.body.currency).toBe(validBody.currency);
+  expect(res.body.maskedPan).toBe('411111******1111');
+});
+
+test('GET /payments/:id/receipt returns 404 for unknown id', async () => {
+  const res = await request(app).get('/payments/does-not-exist/receipt');
+  expect(res.status).toBe(404);
+});
diff --git a/src/routes.ts b/src/routes.ts
index cc4637c..8522764 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -29,6 +29,23 @@ router.post('/payments', (req: Request, res: Response) => {
   res.status(201).json(payment);
 });
 
+router.get('/payments/:id/receipt', (req: Request, res: Response) => {
+  const payment = store.get(req.params.id);
+  if (!payment) {
+    res.status(404).json({ error: 'not found' });
+    return;
+  }
+  const { pan } = payment;
+  const maskedPan = pan.slice(0, 6) + '*'.repeat(pan.length - 10) + pan.slice(-4);
+  res.json({
+    id: payment.id,
+    merchantId: payment.merchantId,
+    amountMinor: payment.amountMinor,
+    currency: payment.currency,
+    maskedPan,
+  });
+});
+
 router.get('/payments/:id', (req: Request, res: Response) => {
   const payment = store.get(req.params.id);
   if (!payment) {
Powered by IBM Bob 2.0