Skip to content

Commit f0149bb

Browse files
authored
Add UI polling logic for simulated confirmations
2 parents 4e4c28f + 4a6b34e commit f0149bb

3 files changed

Lines changed: 80 additions & 5 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ dotnet build
1717
## PrivateCircle demo UI
1818
- Run `Gateway.Api` and browse to [http://localhost:5023](http://localhost:5023) to use the built-in PrivateCircle-branded page. It can derive idempotency keys for fiat and crypto payloads, submit payments, and show canonical payloads used by the middleware.
1919

20+
### Online demo: worker updates for fiat + crypto
21+
Use the built-in UI at http://localhost:5023 to show the background worker updating payment records after the simulated PSP/chain responds.
22+
23+
1. Start **PspStub.Api**, **Gateway.Api**, and **Gateway.Worker** (see steps below). Keep the demo UI open in a browser.
24+
2. In the **Card / Fiat** card, click **Derive key** to populate the derived key and canonical payload, then click **Submit payment**. The UI displays the initial `Pending` response and fills the **Payment ID** field.
25+
3. Click **Fetch /payments/{paymentId}** to query the API from inside the demo. As the worker authorizes the payment, repeat the fetch to watch the status move to `Authorized` with an `authCode` from the PSP stub.
26+
4. Repeat the flow in the **Crypto** card. After **Submit crypto**, use **Fetch /payments/{paymentId}** and **Fetch /payments/{paymentId}/crypto** to watch the worker mark the transaction with confirmations and authorize the payment using the simulated `txHash`.
27+
5. Call either submit button with the same derived key to highlight that the stored response is replayed (idempotent behavior still works after the worker updates records).
28+
2029
## Let's test it
2130

2231
### Services and ports used

src/Gateway.Api/wwwroot/index.html

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
<small>Fiat + Crypto • Derived idempotency keys • Worker updates</small>
4242
</div>
4343
</div>
44-
<div class="badge">Ready to demo</div>
4544
</header>
4645
<main class="main">
4746
<div class="card">
@@ -69,7 +68,7 @@ <h2>Environment</h2>
6968
<div class="card">
7069
<div class="section-title">
7170
<h3>Card / Fiat</h3>
72-
<span class="badge">Idempotent</span>
71+
<span class="badge">Simulated card authorization</span>
7372
</div>
7473
<label for="fiatAmount">Amount (minor units)</label>
7574
<input id="fiatAmount" type="number" value="4200" />
@@ -92,7 +91,16 @@ <h3>Card / Fiat</h3>
9291
<label for="fiatCanonical">Canonical payload</label>
9392
<textarea id="fiatCanonical" readonly></textarea>
9493

95-
<div class="status" id="fiatStatus">Awaiting request...</div>
94+
<div class="status" id="fiatStatus">Make a request above...</div>
95+
96+
<div class="section-title">
97+
<h4>Track worker updates</h4>
98+
<small>Use the last response or paste a paymentId to query the API from here.</small>
99+
</div>
100+
<label for="fiatPaymentId">Payment ID</label>
101+
<input id="fiatPaymentId" placeholder="Filled after Submit payment" />
102+
<button class="secondary" id="fiatFetch">Fetch /payments/{paymentId}</button>
103+
<div class="status" id="fiatFetchStatus">No fetch yet.</div>
96104
</div>
97105

98106
<div class="card">
@@ -124,7 +132,19 @@ <h3>Crypto</h3>
124132
<label for="cryptoCanonical">Canonical payload</label>
125133
<textarea id="cryptoCanonical" readonly></textarea>
126134

127-
<div class="status" id="cryptoStatus">Awaiting request...</div>
135+
<div class="status" id="cryptoStatus">Make a request above...</div>
136+
137+
<div class="section-title">
138+
<h4>Track worker updates</h4>
139+
<small>Call the payment and crypto endpoints without leaving the demo.</small>
140+
</div>
141+
<label for="cryptoPaymentId">Payment ID</label>
142+
<input id="cryptoPaymentId" placeholder="Filled after Submit crypto" />
143+
<div class="grid">
144+
<button class="secondary" id="cryptoFetchPayment">Fetch /payments/{paymentId}</button>
145+
<button class="secondary" id="cryptoFetchTx">Fetch /payments/{paymentId}/crypto</button>
146+
</div>
147+
<div class="status" id="cryptoFetchStatus">No fetch yet.</div>
128148
</div>
129149
</div>
130150
</main>
@@ -147,6 +167,17 @@ <h3>Crypto</h3>
147167
return { status: response.status, body };
148168
}
149169

170+
async function getJson(url, apiKey) {
171+
const response = await fetch(url, {
172+
method: 'GET',
173+
headers: { 'x-api-key': apiKey }
174+
});
175+
const contentType = response.headers.get('content-type') || '';
176+
const isJson = contentType.includes('application/json');
177+
const body = isJson ? await response.json() : await response.text();
178+
return { status: response.status, body };
179+
}
180+
150181
function buildFiatPayload() {
151182
return {
152183
amount: Number(document.getElementById('fiatAmount').value),
@@ -197,15 +228,50 @@ <h3>Crypto</h3>
197228
const result = await postJson(url, payload, apiKey, derived);
198229
if (flow === 'fiat') {
199230
writeStatus('fiatStatus', result);
231+
if (result.body && result.body.paymentId) {
232+
document.getElementById('fiatPaymentId').value = result.body.paymentId;
233+
}
200234
} else {
201235
writeStatus('cryptoStatus', result);
236+
if (result.body && result.body.paymentId) {
237+
document.getElementById('cryptoPaymentId').value = result.body.paymentId;
238+
}
202239
}
203240
}
204241

242+
async function fetchPaymentStatus(flow) {
243+
const baseUrl = document.getElementById('baseUrl').value;
244+
const apiKey = document.getElementById('apiKey').value;
245+
const paymentId = (flow === 'fiat' ? document.getElementById('fiatPaymentId') : document.getElementById('cryptoPaymentId')).value.trim();
246+
if (!paymentId) {
247+
writeStatus(flow === 'fiat' ? 'fiatFetchStatus' : 'cryptoFetchStatus', 'Enter or submit a payment to get an id.');
248+
return;
249+
}
250+
251+
const result = await getJson(`${baseUrl}/payments/${paymentId}`, apiKey);
252+
writeStatus(flow === 'fiat' ? 'fiatFetchStatus' : 'cryptoFetchStatus', result);
253+
}
254+
255+
async function fetchCryptoTx() {
256+
const baseUrl = document.getElementById('baseUrl').value;
257+
const apiKey = document.getElementById('apiKey').value;
258+
const paymentId = document.getElementById('cryptoPaymentId').value.trim();
259+
if (!paymentId) {
260+
writeStatus('cryptoFetchStatus', 'Enter or submit a crypto payment to get an id.');
261+
return;
262+
}
263+
264+
const result = await getJson(`${baseUrl}/payments/${paymentId}/crypto`, apiKey);
265+
writeStatus('cryptoFetchStatus', result);
266+
}
267+
205268
document.getElementById('fiatDerive').addEventListener('click', () => derive('fiat'));
206269
document.getElementById('cryptoDerive').addEventListener('click', () => derive('crypto'));
207270
document.getElementById('fiatSubmit').addEventListener('click', () => submit('fiat'));
208271
document.getElementById('cryptoSubmit').addEventListener('click', () => submit('crypto'));
272+
document.getElementById('fiatFetch').addEventListener('click', () => fetchPaymentStatus('fiat'));
273+
document.getElementById('cryptoFetchPayment').addEventListener('click', () => fetchPaymentStatus('crypto'));
274+
document.getElementById('cryptoFetchTx').addEventListener('click', fetchCryptoTx);
209275
</script>
210276
</body>
211277
</html>

src/Gateway.Api/wwwroot/styles.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
:root {
22
--pc-primary: #243b5a;
3-
--pc-accent: #21b6a8;
3+
--pc-accent: #e53935;
44
--pc-bg: #f5f7fb;
55
--pc-card: #ffffff;
66
--pc-border: #dfe6f2;

0 commit comments

Comments
 (0)