merchant-backoffice

ZZZ: Inactive/Deprecated
Log | Files | Refs | Submodules | README

commit ee6e662d4838f5d0c48d7de589bb1f287ef99807
parent 726c0a03611ca71e4911106dc34e4f8d641f9301
Author: ms <ms@taler.net>
Date:   Mon, 20 Dec 2021 10:35:36 +0100

bank: implement withdrawal abort

Diffstat:
Mpackages/bank/src/pages/home/index.tsx | 139++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mpackages/bank/tests/__tests__/homepage.js | 4++++
2 files changed, 110 insertions(+), 33 deletions(-)

diff --git a/packages/bank/src/pages/home/index.tsx b/packages/bank/src/pages/home/index.tsx @@ -55,6 +55,22 @@ interface AccountStateType { * Helpers. * ***********/ +/** + * Craft headers with Authorization and Content-Type. + */ +function prepareHeaders(username: string, password: string) { + let headers = new Headers(); + headers.append( + "Authorization", + `Basic ${Buffer.from(username + ":" + password).toString("base64")}` + ); + headers.append( + "Content-Type", + "application/json" + ) + return headers; +} + const getRootPath = () => { return typeof window !== undefined ? window.location.origin + window.location.pathname @@ -123,6 +139,71 @@ function usePageState( * a helper function. */ +/** + * Abort a withdrawal operation via the Access API's /abort. + */ +async function abortWithdrawalCall( + backendState: BackendStateTypeOpt, + withdrawalId: string | undefined, + pageStateSetter: StateUpdater<PageStateType> +) { + if (typeof backendState === "undefined") { + console.log("No credentials found."); + pageStateSetter((prevState) => ({...prevState, hasError: true, error: "No credentials found."})) + return; + } + if (typeof withdrawalId === "undefined") { + console.log("No withdrawal ID found."); + pageStateSetter((prevState) => ({...prevState, hasError: true, error: "No withdrawal ID found."})) + return; + } + + try { + const { username, password } = backendState; + let headers = prepareHeaders(username, password); + /** + * NOTE: tests show that when a same object is being + * POSTed, caching might prevent same requests from being + * made. Hence, trying to POST twice the same amount might + * get silently ignored. + * + * headers.append("cache-control", "no-store"); + * headers.append("cache-control", "no-cache"); + * headers.append("pragma", "no-cache"); + * */ + + // Backend URL must have been stored _with_ a final slash. + const url = new URL( + `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/abort`, + backendState.url + ) + var res = await fetch(url.href, {method: 'POST', headers: headers}) + } catch (error) { + console.log("Could not abort the withdrawal", error); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Could not abort the withdrawal: ${error}`})) + return; + } + if (!res.ok) { + console.log(`Withdrawal abort gave response error (${res.status})`, res.statusText); + pageStateSetter((prevState) => ({ + ...prevState, + hasError: true, + error: `Withdrawal abortion gave response error (${res.status})`})) + return; + } else { + console.log("Withdrawal operation aborted!"); + pageStateSetter((prevState) => { + delete prevState.talerWithdrawUri; + const { talerWithdrawUri, ...rest } = prevState; + return { + ...rest, + withdrawalOutcome: "Withdrawal aborted!" + }}) + } +} /** * This function confirms a withdrawal operation AFTER @@ -139,23 +220,21 @@ async function confirmWithdrawalCall( withdrawalId: string | undefined, pageStateSetter: StateUpdater<PageStateType> ) { + if (typeof backendState === "undefined") { - console.log("Page has a problem: no credentials found in the state."); - pageStateSetter((prevState) => ({ - ...prevState, - hasError: true, - error: "No credentials found in the state"})) + console.log("No credentials found."); + pageStateSetter((prevState) => ({...prevState, hasError: true, error: "No credentials found."})) return; } if (typeof withdrawalId === "undefined") { - pageStateSetter((prevState) => ({ - ...prevState, - hasError: true, - error: "Withdrawal ID wasn't found in the state; cannot confirm it."})) + console.log("No withdrawal ID found."); + pageStateSetter((prevState) => ({...prevState, hasError: true, error: "No withdrawal ID found."})) return; } + try { - let headers = new Headers(); + const { username, password } = backendState; + let headers = prepareHeaders(username, password); /** * NOTE: tests show that when a same object is being * POSTed, caching might prevent same requests from being @@ -166,14 +245,7 @@ async function confirmWithdrawalCall( * headers.append("cache-control", "no-cache"); * headers.append("pragma", "no-cache"); * */ - headers.append( - "Authorization", - `Basic ${Buffer.from(backendState.username + ":" + backendState.password).toString("base64")}` - ); - headers.append( - "Content-Type", - "application/json" - ) + // Backend URL must have been stored _with_ a final slash. const url = new URL( `access-api/accounts/${backendState.username}/withdrawals/${withdrawalId}/confirm`, @@ -227,14 +299,12 @@ async function createWithdrawalCall( ) { if (typeof backendState === "undefined") { console.log("Page has a problem: no credentials found in the state."); - pageStateSetter((prevState) => ({ - ...prevState, - hasError: true, - error: "No credentials found in the state"})) + pageStateSetter((prevState) => ({...prevState, hasError: true, error: "No credentials given."})) return; } try { - let headers = new Headers(); + const { username, password } = backendState; + let headers = prepareHeaders(username, password); /** * NOTE: tests show that when a same object is being * POSTed, caching might prevent same requests from being @@ -245,14 +315,7 @@ async function createWithdrawalCall( * headers.append("cache-control", "no-cache"); * headers.append("pragma", "no-cache"); * */ - headers.append( - "Authorization", - `Basic ${Buffer.from(backendState.username + ":" + backendState.password).toString("base64")}` - ); - headers.append( - "Content-Type", - "application/json" - ) + // Backend URL must have been stored _with_ a final slash. const url = new URL( `access-api/accounts/${backendState.username}/withdrawals`, @@ -501,6 +564,10 @@ export function BankHome(): VNode { talerWithdrawUri={pageState.talerWithdrawUri} accountLabel={backendState.username}> + <button onClick={() => { + pageStateSetter({...pageState, isLoggedIn: false}) + }}>Sign out</button> + {!pageState.withdrawalInProgress && <button onClick={() => { createWithdrawalCall( "EUR:5", @@ -515,12 +582,18 @@ export function BankHome(): VNode { return {...rest, withdrawalInProgress: false};})}}>Close</button> } - {pageState.talerWithdrawUri && <button onClick={() => { + {pageState.talerWithdrawUri && <div><button onClick={() => { confirmWithdrawalCall( backendState, pageState.withdrawalId, pageStateSetter);}}>Confirm withdrawal</button> - } + <button onClick={() => { + abortWithdrawalCall( + backendState, + pageState.withdrawalId, + pageStateSetter);}}>Abort withdrawal</button> + </div>} + </Account> </SWRWithCredentials> ); diff --git a/packages/bank/tests/__tests__/homepage.js b/packages/bank/tests/__tests__/homepage.js @@ -206,6 +206,10 @@ describe("home page", () => { paytoUri: "payto://iban/123/ABC" })) fireEvent.click(signinButton); + expect(fetch).toHaveBeenCalledWith( + `http://localhost/demobanks/default/access-api/accounts/${username}`, + expect.anything() + ) await screen.findByText("balance is EUR:10", {exact: false}) })