/* This file is part of GNU Taler (C) 2020 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ /** * Imports. */ import { ConfirmPayResultType, MerchantApiClient, PreparePayResultType, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV2, createWalletDaemonWithClient, withdrawViaBankV2, } from "../harness/helpers.js"; /** * Run test for basic, bank-integrated withdrawal and payment. */ export async function runPaymentShareTest(t: GlobalTestState) { // Set up test environment const { walletClient: firstWallet, bank, exchange, merchant, } = await createSimpleTestkudosEnvironmentV2(t); const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl()); // Withdraw digital cash into the wallet. await withdrawViaBankV2(t, { walletClient: firstWallet, bank, exchange, amount: "TESTKUDOS:20", }); await firstWallet.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); const { walletClient: secondWallet } = await createWalletDaemonWithClient(t, { name: "wallet2", }); await withdrawViaBankV2(t, { walletClient: secondWallet, bank, exchange, amount: "TESTKUDOS:20", }); await secondWallet.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); { const first = await firstWallet.call(WalletApiOperation.GetBalances, {}); const second = await secondWallet.call(WalletApiOperation.GetBalances, {}); t.assertAmountEquals(first.balances[0].available, "TESTKUDOS:19.53"); t.assertAmountEquals(second.balances[0].available, "TESTKUDOS:19.53"); } t.logStep("setup-done"); // create two orders to pay async function createOrder(amount: string) { const order = { summary: "Buy me!", amount, fulfillment_url: "taler://fulfillment-success/thx", }; const args = { order }; const orderResp = await merchantClient.createOrder({ order: args.order, }); const orderStatus = await merchantClient.queryPrivateOrderStatus({ orderId: orderResp.order_id, }); t.assertTrue(orderStatus.order_status === "unpaid"); return { id: orderResp.order_id, uri: orderStatus.taler_pay_uri }; } t.logStep("orders-created"); /** * FIRST CASE, create in first wallet and pay in the second wallet * first wallet should not be able to continue */ { const order = await createOrder("TESTKUDOS:5"); // Claim the order with the first wallet const claimFirstWallet = await firstWallet.call( WalletApiOperation.PreparePayForUri, { talerPayUri: order.uri }, ); t.assertTrue( claimFirstWallet.status === PreparePayResultType.PaymentPossible, ); t.logStep("w1-payment-possible"); // share order from the first wallet const { privatePayUri } = await firstWallet.call( WalletApiOperation.SharePayment, { merchantBaseUrl: merchant.makeInstanceBaseUrl(), orderId: order.id, }, ); t.logStep("w1-payment-shared"); // claim from the second wallet const claimSecondWallet = await secondWallet.call( WalletApiOperation.PreparePayForUri, { talerPayUri: privatePayUri }, ); t.assertTrue( claimSecondWallet.status === PreparePayResultType.PaymentPossible, ); t.logStep("w2-claimed"); // pay from the second wallet const r2 = await secondWallet.call(WalletApiOperation.ConfirmPay, { transactionId: claimSecondWallet.transactionId, }); t.assertTrue(r2.type === ConfirmPayResultType.Done); t.logStep("w2-confirmed"); // Wait for refresh to settle before we do checks await secondWallet.call( WalletApiOperation.TestingWaitTransactionsFinal, {}, ); t.logStep("w2-refresh-settled"); { const first = await firstWallet.call(WalletApiOperation.GetBalances, {}); const second = await secondWallet.call( WalletApiOperation.GetBalances, {}, ); t.assertAmountEquals(first.balances[0].available, "TESTKUDOS:19.53"); t.assertAmountEquals(second.balances[0].available, "TESTKUDOS:14.23"); } // Claim the order with the first wallet const claimFirstWalletAgain = await firstWallet.call( WalletApiOperation.PreparePayForUri, { talerPayUri: order.uri }, ); t.assertTrue( claimFirstWalletAgain.status === PreparePayResultType.AlreadyConfirmed, ); t.logStep("w1-prepared-again"); const r1 = await firstWallet.call(WalletApiOperation.ConfirmPay, { transactionId: claimFirstWallet.transactionId, }); t.assertTrue(r1.type === ConfirmPayResultType.Done); t.logStep("w1-confirmed-shared"); /** * only the second wallet balance was affected */ { const first = await firstWallet.call(WalletApiOperation.GetBalances, {}); const second = await secondWallet.call( WalletApiOperation.GetBalances, {}, ); t.assertAmountEquals(first.balances[0].available, "TESTKUDOS:19.53"); t.assertAmountEquals(second.balances[0].available, "TESTKUDOS:14.23"); } } t.logStep("first-case-done"); /** * SECOND CASE, create in first wallet and share to the second wallet * pay with the first wallet, second wallet should not be able to continue */ { const order = await createOrder("TESTKUDOS:3"); // Claim the order with the first wallet const claimFirstWallet = await firstWallet.call( WalletApiOperation.PreparePayForUri, { talerPayUri: order.uri }, ); t.assertTrue( claimFirstWallet.status === PreparePayResultType.PaymentPossible, ); // share order from the first wallet const { privatePayUri } = await firstWallet.call( WalletApiOperation.SharePayment, { merchantBaseUrl: merchant.makeInstanceBaseUrl(), orderId: order.id, }, ); // claim from the second wallet const claimSecondWallet = await secondWallet.call( WalletApiOperation.PreparePayForUri, { talerPayUri: privatePayUri }, ); t.assertTrue( claimSecondWallet.status === PreparePayResultType.PaymentPossible, ); // pay from the second wallet const r2 = await firstWallet.call(WalletApiOperation.ConfirmPay, { transactionId: claimFirstWallet.transactionId, }); t.assertTrue(r2.type === ConfirmPayResultType.Done); // Wait for refreshes to settle before doing checks await firstWallet.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); /** * only the first wallet balance was affected */ const bal1 = await firstWallet.call(WalletApiOperation.GetBalances, {}); const bal2 = await secondWallet.call(WalletApiOperation.GetBalances, {}); t.assertAmountEquals(bal1.balances[0].available, "TESTKUDOS:16.18"); t.assertAmountEquals(bal2.balances[0].available, "TESTKUDOS:14.23"); // Claim the order with the first wallet const claimSecondWalletAgain = await secondWallet.call( WalletApiOperation.PreparePayForUri, { talerPayUri: order.uri }, ); t.assertTrue( claimSecondWalletAgain.status === PreparePayResultType.AlreadyConfirmed, ); } t.logStep("second-case-done"); } runPaymentShareTest.suites = ["wallet"];