Merge branch 'master' into age-withdraw
This commit is contained in:
commit
77ea209ddb
@ -14,7 +14,7 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
|
import { Amounts, HttpStatusCode, parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
ErrorType,
|
ErrorType,
|
||||||
HttpResponsePaginated,
|
HttpResponsePaginated,
|
||||||
@ -27,6 +27,7 @@ import { useAccountDetails } from "../hooks/access.js";
|
|||||||
import { LoginForm } from "./LoginForm.js";
|
import { LoginForm } from "./LoginForm.js";
|
||||||
import { PaymentOptions } from "./PaymentOptions.js";
|
import { PaymentOptions } from "./PaymentOptions.js";
|
||||||
import { notifyError } from "../hooks/notification.js";
|
import { notifyError } from "../hooks/notification.js";
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
account: string;
|
account: string;
|
||||||
@ -34,6 +35,60 @@ interface Props {
|
|||||||
error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
|
error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
|
||||||
) => VNode;
|
) => VNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const CopyIcon = (): VNode => (
|
||||||
|
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CopiedIcon = (): VNode => (
|
||||||
|
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
function CopyButton({ getContent }: { getContent: () => string }): VNode {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
function copyText(): void {
|
||||||
|
navigator.clipboard.writeText(getContent() || "");
|
||||||
|
setCopied(true);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
if (copied) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setCopied(false);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}, [copied]);
|
||||||
|
|
||||||
|
if (!copied) {
|
||||||
|
return (
|
||||||
|
<button onClick={copyText} style={{width:32, height:32, fontSize: "initial"}}>
|
||||||
|
<CopyIcon />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div content="Copied" style={{display:"inline-block"}}>
|
||||||
|
<button disabled style={{width:32, height:32 , fontSize: "initial"}}>
|
||||||
|
<CopiedIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query account information and show QR code if there is pending withdrawal
|
* Query account information and show QR code if there is pending withdrawal
|
||||||
*/
|
*/
|
||||||
@ -66,7 +121,6 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode {
|
|||||||
<div>Payto from server is not valid "{data.paytoUri}"</div>
|
<div>Payto from server is not valid "{data.paytoUri}"</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const accountNumber = payto.iban;
|
|
||||||
const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
|
const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
|
||||||
const limit = balanceIsDebit
|
const limit = balanceIsDebit
|
||||||
? Amounts.sub(debitThreshold, balance).amount
|
? Amounts.sub(debitThreshold, balance).amount
|
||||||
@ -76,8 +130,7 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode {
|
|||||||
<div>
|
<div>
|
||||||
<h1 class="nav welcome-text">
|
<h1 class="nav welcome-text">
|
||||||
<i18n.Translate>
|
<i18n.Translate>
|
||||||
Welcome, {accountNumber ? `${account} (${accountNumber})` : account}
|
Welcome, {account} (<a href={stringifyPaytoUri(payto)}>{payto.iban}</a>)! <CopyButton getContent={() => stringifyPaytoUri(payto)} />
|
||||||
!
|
|
||||||
</i18n.Translate>
|
</i18n.Translate>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +44,7 @@ export function InputSelector<T>({
|
|||||||
fromStr = defaultFromString,
|
fromStr = defaultFromString,
|
||||||
toStr = defaultToString,
|
toStr = defaultToString,
|
||||||
}: Props<keyof T>): VNode {
|
}: Props<keyof T>): VNode {
|
||||||
const { error, value, onChange } = useField<T>(name);
|
const { error, value, onChange, required } = useField<T>(name);
|
||||||
return (
|
return (
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label is-normal">
|
<div class="field-label is-normal">
|
||||||
@ -58,7 +58,7 @@ export function InputSelector<T>({
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body is-flex-grow-3">
|
<div class="field-body is-flex-grow-3">
|
||||||
<div class="field">
|
<div class="field has-icons-right">
|
||||||
<p class={expand ? "control is-expanded select" : "control select "}>
|
<p class={expand ? "control is-expanded select" : "control select "}>
|
||||||
<select
|
<select
|
||||||
class={error ? "select is-danger" : "select"}
|
class={error ? "select is-danger" : "select"}
|
||||||
@ -78,8 +78,14 @@ export function InputSelector<T>({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{help}
|
{help}
|
||||||
</p>
|
</p>
|
||||||
|
{required && (
|
||||||
|
<span class="icon has-text-danger is-right" style={{height: "2.5em"}}>
|
||||||
|
<i class="mdi mdi-alert" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{error && <p class="help is-danger">{error}</p>}
|
{error && <p class="help is-danger">{error}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1331,12 +1331,13 @@ export namespace MerchantBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace Webhooks {
|
namespace Webhooks {
|
||||||
|
type MerchantWebhookType = "pay" | "refund";
|
||||||
interface WebhookAddDetails {
|
interface WebhookAddDetails {
|
||||||
// Webhook ID to use.
|
// Webhook ID to use.
|
||||||
webhook_id: string;
|
webhook_id: string;
|
||||||
|
|
||||||
// The event of the webhook: why the webhook is used.
|
// The event of the webhook: why the webhook is used.
|
||||||
event_type: string;
|
event_type: MerchantWebhookType;
|
||||||
|
|
||||||
// URL of the webhook where the customer will be redirected.
|
// URL of the webhook where the customer will be redirected.
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -33,6 +33,7 @@ import { InputDuration } from "../../../../components/form/InputDuration.js";
|
|||||||
import { InputNumber } from "../../../../components/form/InputNumber.js";
|
import { InputNumber } from "../../../../components/form/InputNumber.js";
|
||||||
import { useBackendContext } from "../../../../context/backend.js";
|
import { useBackendContext } from "../../../../context/backend.js";
|
||||||
import { MerchantBackend } from "../../../../declaration.js";
|
import { MerchantBackend } from "../../../../declaration.js";
|
||||||
|
import { InputSelector } from "../../../../components/form/InputSelector.js";
|
||||||
|
|
||||||
type Entity = MerchantBackend.Webhooks.WebhookAddDetails;
|
type Entity = MerchantBackend.Webhooks.WebhookAddDetails;
|
||||||
|
|
||||||
@ -50,7 +51,9 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
|
|||||||
|
|
||||||
const errors: FormErrors<Entity> = {
|
const errors: FormErrors<Entity> = {
|
||||||
webhook_id: !state.webhook_id ? i18n.str`required` : undefined,
|
webhook_id: !state.webhook_id ? i18n.str`required` : undefined,
|
||||||
event_type: !state.event_type ? i18n.str`required` : undefined,
|
event_type: !state.event_type ? i18n.str`required`
|
||||||
|
: state.event_type !== "pay" && state.event_type !== "refund" ? i18n.str`it should be "pay" or "refund"`
|
||||||
|
: undefined,
|
||||||
http_method: !state.http_method
|
http_method: !state.http_method
|
||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: !validMethod.includes(state.http_method)
|
: !validMethod.includes(state.http_method)
|
||||||
@ -84,16 +87,30 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
|
|||||||
label={i18n.str`ID`}
|
label={i18n.str`ID`}
|
||||||
tooltip={i18n.str`Webhook ID to use`}
|
tooltip={i18n.str`Webhook ID to use`}
|
||||||
/>
|
/>
|
||||||
<Input<Entity>
|
<InputSelector
|
||||||
name="event_type"
|
name="event_type"
|
||||||
label={i18n.str`Event`}
|
label={i18n.str`Event`}
|
||||||
|
values={[
|
||||||
|
i18n.str`Choose one...`,
|
||||||
|
i18n.str`pay`,
|
||||||
|
i18n.str`refund`,
|
||||||
|
]}
|
||||||
tooltip={i18n.str`The event of the webhook: why the webhook is used`}
|
tooltip={i18n.str`The event of the webhook: why the webhook is used`}
|
||||||
/>
|
/>
|
||||||
<Input<Entity>
|
<InputSelector
|
||||||
name="http_method"
|
name="http_method"
|
||||||
label={i18n.str`Method`}
|
label={i18n.str`Method`}
|
||||||
|
values={[
|
||||||
|
i18n.str`Choose one...`,
|
||||||
|
i18n.str`GET`,
|
||||||
|
i18n.str`POST`,
|
||||||
|
i18n.str`PUT`,
|
||||||
|
i18n.str`PATCH`,
|
||||||
|
i18n.str`HEAD`,
|
||||||
|
]}
|
||||||
tooltip={i18n.str`Method used by the webhook`}
|
tooltip={i18n.str`Method used by the webhook`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input<Entity>
|
<Input<Entity>
|
||||||
name="url"
|
name="url"
|
||||||
label={i18n.str`URL`}
|
label={i18n.str`URL`}
|
||||||
|
@ -51,8 +51,8 @@ import {
|
|||||||
stringToBytes,
|
stringToBytes,
|
||||||
TalerError,
|
TalerError,
|
||||||
TalerProtocolDuration,
|
TalerProtocolDuration,
|
||||||
TipCreateConfirmation,
|
RewardCreateConfirmation,
|
||||||
TipCreateRequest,
|
RewardCreateRequest,
|
||||||
TippingReserveStatus,
|
TippingReserveStatus,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
@ -1751,8 +1751,8 @@ export namespace MerchantPrivateApi {
|
|||||||
export async function giveTip(
|
export async function giveTip(
|
||||||
merchantService: MerchantServiceInterface,
|
merchantService: MerchantServiceInterface,
|
||||||
instance: string,
|
instance: string,
|
||||||
req: TipCreateRequest,
|
req: RewardCreateRequest,
|
||||||
): Promise<TipCreateConfirmation> {
|
): Promise<RewardCreateConfirmation> {
|
||||||
const reqUrl = new URL(
|
const reqUrl = new URL(
|
||||||
`private/tips`,
|
`private/tips`,
|
||||||
merchantService.makeInstanceBaseUrl(instance),
|
merchantService.makeInstanceBaseUrl(instance),
|
||||||
|
@ -191,12 +191,12 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
const walletTipping = new WalletCli(t, "age-tipping");
|
const walletTipping = new WalletCli(t, "age-tipping");
|
||||||
|
|
||||||
const ptr = await walletTipping.client.call(WalletApiOperation.PrepareTip, {
|
const ptr = await walletTipping.client.call(WalletApiOperation.PrepareReward, {
|
||||||
talerTipUri: tip.taler_tip_uri,
|
talerRewardUri: tip.taler_reward_uri,
|
||||||
});
|
});
|
||||||
|
|
||||||
await walletTipping.client.call(WalletApiOperation.AcceptTip, {
|
await walletTipping.client.call(WalletApiOperation.AcceptReward, {
|
||||||
walletTipId: ptr.walletTipId,
|
walletRewardId: ptr.walletRewardId,
|
||||||
});
|
});
|
||||||
|
|
||||||
await walletTipping.runUntilDone();
|
await walletTipping.runUntilDone();
|
||||||
|
@ -17,13 +17,20 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { AbsoluteTime, Duration } from "@gnu-taler/taler-util";
|
import {
|
||||||
|
AbsoluteTime,
|
||||||
|
Duration,
|
||||||
|
NotificationType,
|
||||||
|
TransactionMajorState,
|
||||||
|
TransactionMinorState,
|
||||||
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
import { defaultCoinConfig } from "../harness/denomStructures.js";
|
import { defaultCoinConfig } from "../harness/denomStructures.js";
|
||||||
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
import { GlobalTestState, WalletCli } from "../harness/harness.js";
|
||||||
import {
|
import {
|
||||||
createSimpleTestkudosEnvironment,
|
createSimpleTestkudosEnvironmentV2,
|
||||||
withdrawViaBank,
|
createWalletDaemonWithClient,
|
||||||
|
withdrawViaBankV2,
|
||||||
} from "../harness/helpers.js";
|
} from "../harness/helpers.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,12 +39,7 @@ import {
|
|||||||
export async function runAgeRestrictionsPeerTest(t: GlobalTestState) {
|
export async function runAgeRestrictionsPeerTest(t: GlobalTestState) {
|
||||||
// Set up test environment
|
// Set up test environment
|
||||||
|
|
||||||
const {
|
const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(
|
||||||
wallet: walletOne,
|
|
||||||
bank,
|
|
||||||
exchange,
|
|
||||||
merchant,
|
|
||||||
} = await createSimpleTestkudosEnvironment(
|
|
||||||
t,
|
t,
|
||||||
defaultCoinConfig.map((x) => x("TESTKUDOS")),
|
defaultCoinConfig.map((x) => x("TESTKUDOS")),
|
||||||
{
|
{
|
||||||
@ -45,20 +47,29 @@ export async function runAgeRestrictionsPeerTest(t: GlobalTestState) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const walletTwo = new WalletCli(t, "walletTwo");
|
const w1 = await createWalletDaemonWithClient(t, {
|
||||||
const walletThree = new WalletCli(t, "walletThree");
|
name: "w1",
|
||||||
|
persistent: true,
|
||||||
|
});
|
||||||
|
const w2 = await createWalletDaemonWithClient(t, {
|
||||||
|
name: "w2",
|
||||||
|
persistent: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallet1 = w1.walletClient;
|
||||||
|
const wallet2 = w2.walletClient;
|
||||||
|
|
||||||
{
|
{
|
||||||
const wallet = walletOne;
|
const withdrawalRes = await withdrawViaBankV2(t, {
|
||||||
|
walletClient: wallet1,
|
||||||
await withdrawViaBank(t, {
|
|
||||||
wallet,
|
|
||||||
bank,
|
bank,
|
||||||
exchange,
|
exchange,
|
||||||
amount: "TESTKUDOS:20",
|
amount: "TESTKUDOS:20",
|
||||||
restrictAge: 13,
|
restrictAge: 13,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await withdrawalRes.withdrawalFinishedCond;
|
||||||
|
|
||||||
const purse_expiration = AbsoluteTime.toProtocolTimestamp(
|
const purse_expiration = AbsoluteTime.toProtocolTimestamp(
|
||||||
AbsoluteTime.addDuration(
|
AbsoluteTime.addDuration(
|
||||||
AbsoluteTime.now(),
|
AbsoluteTime.now(),
|
||||||
@ -66,7 +77,7 @@ export async function runAgeRestrictionsPeerTest(t: GlobalTestState) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const initResp = await wallet.client.call(
|
const initResp = await wallet1.client.call(
|
||||||
WalletApiOperation.InitiatePeerPushDebit,
|
WalletApiOperation.InitiatePeerPushDebit,
|
||||||
{
|
{
|
||||||
partialContractTerms: {
|
partialContractTerms: {
|
||||||
@ -77,20 +88,35 @@ export async function runAgeRestrictionsPeerTest(t: GlobalTestState) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await wallet.runUntilDone();
|
const peerPushReadyCond = wallet1.waitForNotificationCond(
|
||||||
|
(x) =>
|
||||||
|
x.type === NotificationType.TransactionStateTransition &&
|
||||||
|
x.newTxState.major === TransactionMajorState.Pending &&
|
||||||
|
x.newTxState.minor === TransactionMinorState.Ready &&
|
||||||
|
x.transactionId === initResp.transactionId,
|
||||||
|
);
|
||||||
|
|
||||||
const checkResp = await walletTwo.client.call(
|
await peerPushReadyCond;
|
||||||
|
|
||||||
|
const checkResp = await wallet2.call(
|
||||||
WalletApiOperation.PreparePeerPushCredit,
|
WalletApiOperation.PreparePeerPushCredit,
|
||||||
{
|
{
|
||||||
talerUri: initResp.talerUri,
|
talerUri: initResp.talerUri,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await walletTwo.client.call(WalletApiOperation.ConfirmPeerPushCredit, {
|
await wallet2.call(WalletApiOperation.ConfirmPeerPushCredit, {
|
||||||
peerPushPaymentIncomingId: checkResp.peerPushPaymentIncomingId,
|
peerPushPaymentIncomingId: checkResp.peerPushPaymentIncomingId,
|
||||||
});
|
});
|
||||||
|
|
||||||
await walletTwo.runUntilDone();
|
const peerPullCreditDoneCond = wallet2.waitForNotificationCond(
|
||||||
|
(x) =>
|
||||||
|
x.type === NotificationType.TransactionStateTransition &&
|
||||||
|
x.newTxState.major === TransactionMajorState.Done &&
|
||||||
|
x.transactionId === checkResp.transactionId,
|
||||||
|
);
|
||||||
|
|
||||||
|
await peerPullCreditDoneCond;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
j2s,
|
j2s,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
TransactionMajorState,
|
TransactionMajorState,
|
||||||
|
TransactionMinorState,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
|
||||||
@ -46,12 +47,14 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
const w1 = await createWalletDaemonWithClient(t, {
|
const w1 = await createWalletDaemonWithClient(t, {
|
||||||
name: "w1",
|
name: "w1",
|
||||||
|
persistent: true,
|
||||||
handleNotification(wn) {
|
handleNotification(wn) {
|
||||||
allW1Notifications.push(wn);
|
allW1Notifications.push(wn);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const w2 = await createWalletDaemonWithClient(t, {
|
const w2 = await createWalletDaemonWithClient(t, {
|
||||||
name: "w2",
|
name: "w2",
|
||||||
|
persistent: true,
|
||||||
handleNotification(wn) {
|
handleNotification(wn) {
|
||||||
allW2Notifications.push(wn);
|
allW2Notifications.push(wn);
|
||||||
},
|
},
|
||||||
@ -89,6 +92,15 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const peerPullCreditReadyCond = wallet1.waitForNotificationCond(
|
||||||
|
(x) => x.type === NotificationType.TransactionStateTransition &&
|
||||||
|
x.transactionId === resp.transactionId &&
|
||||||
|
x.newTxState.major === TransactionMajorState.Pending &&
|
||||||
|
x.newTxState.minor === TransactionMinorState.Ready,
|
||||||
|
);
|
||||||
|
|
||||||
|
await peerPullCreditReadyCond;
|
||||||
|
|
||||||
const checkResp = await wallet2.client.call(
|
const checkResp = await wallet2.client.call(
|
||||||
WalletApiOperation.PreparePeerPullDebit,
|
WalletApiOperation.PreparePeerPullDebit,
|
||||||
{
|
{
|
||||||
@ -98,8 +110,6 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
console.log(`checkResp: ${j2s(checkResp)}`);
|
console.log(`checkResp: ${j2s(checkResp)}`);
|
||||||
|
|
||||||
// FIXME: The wallet should emit a more appropriate notification here.
|
|
||||||
// Yes, it's technically a withdrawal.
|
|
||||||
const peerPullCreditDoneCond = wallet1.waitForNotificationCond(
|
const peerPullCreditDoneCond = wallet1.waitForNotificationCond(
|
||||||
(x) => x.type === NotificationType.TransactionStateTransition &&
|
(x) => x.type === NotificationType.TransactionStateTransition &&
|
||||||
x.transactionId === resp.transactionId &&
|
x.transactionId === resp.transactionId &&
|
||||||
|
@ -99,17 +99,17 @@ export async function runTippingTest(t: GlobalTestState) {
|
|||||||
console.log("created tip", tip);
|
console.log("created tip", tip);
|
||||||
|
|
||||||
const doTip = async (): Promise<void> => {
|
const doTip = async (): Promise<void> => {
|
||||||
const ptr = await wallet.client.call(WalletApiOperation.PrepareTip, {
|
const ptr = await wallet.client.call(WalletApiOperation.PrepareReward, {
|
||||||
talerTipUri: tip.taler_tip_uri,
|
talerRewardUri: tip.taler_reward_uri,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(ptr);
|
console.log(ptr);
|
||||||
|
|
||||||
t.assertAmountEquals(ptr.tipAmountRaw, "TESTKUDOS:5");
|
t.assertAmountEquals(ptr.rewardAmountRaw, "TESTKUDOS:5");
|
||||||
t.assertAmountEquals(ptr.tipAmountEffective, "TESTKUDOS:4.85");
|
t.assertAmountEquals(ptr.rewardAmountEffective, "TESTKUDOS:4.85");
|
||||||
|
|
||||||
await wallet.client.call(WalletApiOperation.AcceptTip, {
|
await wallet.client.call(WalletApiOperation.AcceptReward, {
|
||||||
walletTipId: ptr.walletTipId,
|
walletRewardId: ptr.walletRewardId,
|
||||||
});
|
});
|
||||||
|
|
||||||
await wallet.runUntilDone();
|
await wallet.runUntilDone();
|
||||||
@ -127,7 +127,7 @@ export async function runTippingTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
console.log("Transactions:", JSON.stringify(txns, undefined, 2));
|
console.log("Transactions:", JSON.stringify(txns, undefined, 2));
|
||||||
|
|
||||||
t.assertDeepEqual(txns.transactions[0].type, "tip");
|
t.assertDeepEqual(txns.transactions[0].type, "reward");
|
||||||
t.assertDeepEqual(txns.transactions[0].txState.major, TransactionMajorState.Done);
|
t.assertDeepEqual(txns.transactions[0].txState.major, TransactionMajorState.Done);
|
||||||
t.assertAmountEquals(
|
t.assertAmountEquals(
|
||||||
txns.transactions[0].amountEffective,
|
txns.transactions[0].amountEffective,
|
||||||
|
@ -499,7 +499,7 @@ export interface BackupRecoupGroup {
|
|||||||
export enum BackupCoinSourceType {
|
export enum BackupCoinSourceType {
|
||||||
Withdraw = "withdraw",
|
Withdraw = "withdraw",
|
||||||
Refresh = "refresh",
|
Refresh = "refresh",
|
||||||
Tip = "tip",
|
Reward = "reward",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -546,7 +546,7 @@ export interface BackupRefreshCoinSource {
|
|||||||
* Metadata about a coin obtained from a tip.
|
* Metadata about a coin obtained from a tip.
|
||||||
*/
|
*/
|
||||||
export interface BackupTipCoinSource {
|
export interface BackupTipCoinSource {
|
||||||
type: BackupCoinSourceType.Tip;
|
type: BackupCoinSourceType.Reward;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wallet's identifier for the tip that this coin
|
* Wallet's identifier for the tip that this coin
|
||||||
|
@ -183,7 +183,16 @@ export class HttpLibImpl implements HttpRequestLibrary {
|
|||||||
resolve(resp);
|
resolve(resp);
|
||||||
});
|
});
|
||||||
res.on("error", (e) => {
|
res.on("error", (e) => {
|
||||||
reject(e);
|
const err = TalerError.fromDetail(
|
||||||
|
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
|
||||||
|
{
|
||||||
|
requestUrl: url,
|
||||||
|
requestMethod: method,
|
||||||
|
httpStatusCode: 0,
|
||||||
|
},
|
||||||
|
`Error in HTTP response handler: ${e.message}`,
|
||||||
|
);
|
||||||
|
reject(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,7 +206,16 @@ export class HttpLibImpl implements HttpRequestLibrary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.on("error", (e: Error) => {
|
req.on("error", (e: Error) => {
|
||||||
reject(e);
|
const err = TalerError.fromDetail(
|
||||||
|
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
|
||||||
|
{
|
||||||
|
requestUrl: url,
|
||||||
|
requestMethod: method,
|
||||||
|
httpStatusCode: 0,
|
||||||
|
},
|
||||||
|
`Error in HTTP request: ${e.message}`,
|
||||||
|
);
|
||||||
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (reqBody) {
|
if (reqBody) {
|
||||||
|
@ -290,22 +290,22 @@ export interface ReserveStatusEntry {
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TipCreateConfirmation {
|
export interface RewardCreateConfirmation {
|
||||||
// Unique tip identifier for the tip that was created.
|
// Unique tip identifier for the tip that was created.
|
||||||
tip_id: string;
|
reward_id: string;
|
||||||
|
|
||||||
// taler://tip URI for the tip
|
// taler://tip URI for the tip
|
||||||
taler_tip_uri: string;
|
taler_reward_uri: string;
|
||||||
|
|
||||||
// URL that will directly trigger processing
|
// URL that will directly trigger processing
|
||||||
// the tip when the browser is redirected to it
|
// the tip when the browser is redirected to it
|
||||||
tip_status_url: string;
|
reward_status_url: string;
|
||||||
|
|
||||||
// when does the tip expire
|
// when does the reward expire
|
||||||
tip_expiration: AbsoluteTime;
|
reward_expiration: AbsoluteTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TipCreateRequest {
|
export interface RewardCreateRequest {
|
||||||
// Amount that the customer should be tipped
|
// Amount that the customer should be tipped
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
parsePayUri,
|
parsePayUri,
|
||||||
parseRefundUri,
|
parseRefundUri,
|
||||||
parseRestoreUri,
|
parseRestoreUri,
|
||||||
parseTipUri,
|
parseRewardUri,
|
||||||
parseWithdrawExchangeUri,
|
parseWithdrawExchangeUri,
|
||||||
parseWithdrawUri,
|
parseWithdrawUri,
|
||||||
stringifyPayPushUri,
|
stringifyPayPushUri,
|
||||||
@ -161,7 +161,7 @@ test("taler refund uri parsing with instance", (t) => {
|
|||||||
|
|
||||||
test("taler tip pickup uri", (t) => {
|
test("taler tip pickup uri", (t) => {
|
||||||
const url1 = "taler://tip/merchant.example.com/tipid";
|
const url1 = "taler://tip/merchant.example.com/tipid";
|
||||||
const r1 = parseTipUri(url1);
|
const r1 = parseRewardUri(url1);
|
||||||
if (!r1) {
|
if (!r1) {
|
||||||
t.fail();
|
t.fail();
|
||||||
return;
|
return;
|
||||||
@ -171,7 +171,7 @@ test("taler tip pickup uri", (t) => {
|
|||||||
|
|
||||||
test("taler tip pickup uri with instance", (t) => {
|
test("taler tip pickup uri with instance", (t) => {
|
||||||
const url1 = "taler://tip/merchant.example.com/instances/tipm/tipid";
|
const url1 = "taler://tip/merchant.example.com/instances/tipm/tipid";
|
||||||
const r1 = parseTipUri(url1);
|
const r1 = parseRewardUri(url1);
|
||||||
if (!r1) {
|
if (!r1) {
|
||||||
t.fail();
|
t.fail();
|
||||||
return;
|
return;
|
||||||
@ -182,7 +182,7 @@ test("taler tip pickup uri with instance", (t) => {
|
|||||||
|
|
||||||
test("taler tip pickup uri with instance and prefix", (t) => {
|
test("taler tip pickup uri with instance and prefix", (t) => {
|
||||||
const url1 = "taler://tip/merchant.example.com/my/pfx/tipm/tipid";
|
const url1 = "taler://tip/merchant.example.com/my/pfx/tipm/tipid";
|
||||||
const r1 = parseTipUri(url1);
|
const r1 = parseRewardUri(url1);
|
||||||
if (!r1) {
|
if (!r1) {
|
||||||
t.fail();
|
t.fail();
|
||||||
return;
|
return;
|
||||||
@ -367,6 +367,6 @@ test("taler withdraw exchange URI with amount (stringify)", (t) => {
|
|||||||
});
|
});
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
url,
|
url,
|
||||||
"taler://withdraw-exchange/exchange.demo.taler.net/JFX1NE38C65A5XT8VSNQXX7R7BBG4GNZ63F5T7Y6859V4J8KBKF0?a=KUDOS%3A19",
|
"taler://withdraw-exchange/exchange.demo.taler.net/GJKG23V4ZBHEH45YRK7TWQE8ZTY7JWTY5094TQJSRZN5DSDBX8E0?a=KUDOS%3A19",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ export type TalerUri =
|
|||||||
| PayPushUriResult
|
| PayPushUriResult
|
||||||
| BackupRestoreUri
|
| BackupRestoreUri
|
||||||
| RefundUriResult
|
| RefundUriResult
|
||||||
| TipUriResult
|
| RewardUriResult
|
||||||
| WithdrawUriResult
|
| WithdrawUriResult
|
||||||
| ExchangeUri
|
| ExchangeUri
|
||||||
| WithdrawExchangeUri
|
| WithdrawExchangeUri
|
||||||
@ -60,8 +60,8 @@ export interface RefundUriResult {
|
|||||||
orderId: string;
|
orderId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TipUriResult {
|
export interface RewardUriResult {
|
||||||
type: TalerUriAction.Tip;
|
type: TalerUriAction.Reward;
|
||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
merchantTipId: string;
|
merchantTipId: string;
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ export enum TalerUriAction {
|
|||||||
Pay = "pay",
|
Pay = "pay",
|
||||||
Withdraw = "withdraw",
|
Withdraw = "withdraw",
|
||||||
Refund = "refund",
|
Refund = "refund",
|
||||||
Tip = "tip",
|
Reward = "reward",
|
||||||
PayPull = "pay-pull",
|
PayPull = "pay-pull",
|
||||||
PayPush = "pay-push",
|
PayPush = "pay-push",
|
||||||
PayTemplate = "pay-template",
|
PayTemplate = "pay-template",
|
||||||
@ -212,7 +212,7 @@ const parsers: { [A in TalerUriAction]: Parser } = {
|
|||||||
[TalerUriAction.PayTemplate]: parsePayTemplateUri,
|
[TalerUriAction.PayTemplate]: parsePayTemplateUri,
|
||||||
[TalerUriAction.Restore]: parseRestoreUri,
|
[TalerUriAction.Restore]: parseRestoreUri,
|
||||||
[TalerUriAction.Refund]: parseRefundUri,
|
[TalerUriAction.Refund]: parseRefundUri,
|
||||||
[TalerUriAction.Tip]: parseTipUri,
|
[TalerUriAction.Reward]: parseRewardUri,
|
||||||
[TalerUriAction.Withdraw]: parseWithdrawUri,
|
[TalerUriAction.Withdraw]: parseWithdrawUri,
|
||||||
[TalerUriAction.DevExperiment]: parseDevExperimentUri,
|
[TalerUriAction.DevExperiment]: parseDevExperimentUri,
|
||||||
[TalerUriAction.Exchange]: parseExchangeUri,
|
[TalerUriAction.Exchange]: parseExchangeUri,
|
||||||
@ -255,8 +255,8 @@ export function stringifyTalerUri(uri: TalerUri): string {
|
|||||||
case TalerUriAction.Refund: {
|
case TalerUriAction.Refund: {
|
||||||
return stringifyRefundUri(uri);
|
return stringifyRefundUri(uri);
|
||||||
}
|
}
|
||||||
case TalerUriAction.Tip: {
|
case TalerUriAction.Reward: {
|
||||||
return stringifyTipUri(uri);
|
return stringifyRewardUri(uri);
|
||||||
}
|
}
|
||||||
case TalerUriAction.Withdraw: {
|
case TalerUriAction.Withdraw: {
|
||||||
return stringifyWithdrawUri(uri);
|
return stringifyWithdrawUri(uri);
|
||||||
@ -394,11 +394,11 @@ export function parsePayPullUri(s: string): PayPullUriResult | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a taler[+http]://tip URI.
|
* Parse a taler[+http]://reward URI.
|
||||||
* Return undefined if not passed a valid URI.
|
* Return undefined if not passed a valid URI.
|
||||||
*/
|
*/
|
||||||
export function parseTipUri(s: string): TipUriResult | undefined {
|
export function parseRewardUri(s: string): RewardUriResult | undefined {
|
||||||
const pi = parseProtoInfo(s, "tip");
|
const pi = parseProtoInfo(s, "reward");
|
||||||
if (!pi) {
|
if (!pi) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -416,7 +416,7 @@ export function parseTipUri(s: string): TipUriResult | undefined {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: TalerUriAction.Tip,
|
type: TalerUriAction.Reward,
|
||||||
merchantBaseUrl,
|
merchantBaseUrl,
|
||||||
merchantTipId: tipId,
|
merchantTipId: tipId,
|
||||||
};
|
};
|
||||||
@ -699,12 +699,12 @@ export function stringifyRefundUri({
|
|||||||
const { proto, path } = getUrlInfo(merchantBaseUrl);
|
const { proto, path } = getUrlInfo(merchantBaseUrl);
|
||||||
return `${proto}://refund/${path}${orderId}`;
|
return `${proto}://refund/${path}${orderId}`;
|
||||||
}
|
}
|
||||||
export function stringifyTipUri({
|
export function stringifyRewardUri({
|
||||||
merchantBaseUrl,
|
merchantBaseUrl,
|
||||||
merchantTipId,
|
merchantTipId,
|
||||||
}: Omit<TipUriResult, "type">): string {
|
}: Omit<RewardUriResult, "type">): string {
|
||||||
const { proto, path } = getUrlInfo(merchantBaseUrl);
|
const { proto, path } = getUrlInfo(merchantBaseUrl);
|
||||||
return `${proto}://tip/${path}${merchantTipId}`;
|
return `${proto}://reward/${path}${merchantTipId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringifyExchangeUri({
|
export function stringifyExchangeUri({
|
||||||
|
@ -186,7 +186,7 @@ export type Transaction =
|
|||||||
| TransactionWithdrawal
|
| TransactionWithdrawal
|
||||||
| TransactionPayment
|
| TransactionPayment
|
||||||
| TransactionRefund
|
| TransactionRefund
|
||||||
| TransactionTip
|
| TransactionReward
|
||||||
| TransactionRefresh
|
| TransactionRefresh
|
||||||
| TransactionDeposit
|
| TransactionDeposit
|
||||||
| TransactionPeerPullCredit
|
| TransactionPeerPullCredit
|
||||||
@ -201,7 +201,7 @@ export enum TransactionType {
|
|||||||
Payment = "payment",
|
Payment = "payment",
|
||||||
Refund = "refund",
|
Refund = "refund",
|
||||||
Refresh = "refresh",
|
Refresh = "refresh",
|
||||||
Tip = "tip",
|
Reward = "reward",
|
||||||
Deposit = "deposit",
|
Deposit = "deposit",
|
||||||
PeerPushDebit = "peer-push-debit",
|
PeerPushDebit = "peer-push-debit",
|
||||||
PeerPushCredit = "peer-push-credit",
|
PeerPushCredit = "peer-push-credit",
|
||||||
@ -591,8 +591,8 @@ export interface TransactionRefund extends TransactionCommon {
|
|||||||
paymentInfo: RefundPaymentInfo | undefined;
|
paymentInfo: RefundPaymentInfo | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionTip extends TransactionCommon {
|
export interface TransactionReward extends TransactionCommon {
|
||||||
type: TransactionType.Tip;
|
type: TransactionType.Reward;
|
||||||
|
|
||||||
// Raw amount of the tip, without extra fees that apply
|
// Raw amount of the tip, without extra fees that apply
|
||||||
amountRaw: AmountString;
|
amountRaw: AmountString;
|
||||||
|
@ -605,7 +605,7 @@ export interface PrepareTipResult {
|
|||||||
*
|
*
|
||||||
* @deprecated use transactionId instead
|
* @deprecated use transactionId instead
|
||||||
*/
|
*/
|
||||||
walletTipId: string;
|
walletRewardId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tip transaction ID.
|
* Tip transaction ID.
|
||||||
@ -620,13 +620,13 @@ export interface PrepareTipResult {
|
|||||||
/**
|
/**
|
||||||
* Amount that the merchant gave.
|
* Amount that the merchant gave.
|
||||||
*/
|
*/
|
||||||
tipAmountRaw: AmountString;
|
rewardAmountRaw: AmountString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount that arrived at the wallet.
|
* Amount that arrived at the wallet.
|
||||||
* Might be lower than the raw amount due to fees.
|
* Might be lower than the raw amount due to fees.
|
||||||
*/
|
*/
|
||||||
tipAmountEffective: AmountString;
|
rewardAmountEffective: AmountString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base URL of the merchant backend giving then tip.
|
* Base URL of the merchant backend giving then tip.
|
||||||
@ -654,14 +654,14 @@ export interface AcceptTipResponse {
|
|||||||
export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
|
export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
|
||||||
buildCodecForObject<PrepareTipResult>()
|
buildCodecForObject<PrepareTipResult>()
|
||||||
.property("accepted", codecForBoolean())
|
.property("accepted", codecForBoolean())
|
||||||
.property("tipAmountRaw", codecForAmountString())
|
.property("rewardAmountRaw", codecForAmountString())
|
||||||
.property("tipAmountEffective", codecForAmountString())
|
.property("rewardAmountEffective", codecForAmountString())
|
||||||
.property("exchangeBaseUrl", codecForString())
|
.property("exchangeBaseUrl", codecForString())
|
||||||
.property("merchantBaseUrl", codecForString())
|
.property("merchantBaseUrl", codecForString())
|
||||||
.property("expirationTimestamp", codecForTimestamp)
|
.property("expirationTimestamp", codecForTimestamp)
|
||||||
.property("walletTipId", codecForString())
|
.property("walletRewardId", codecForString())
|
||||||
.property("transactionId", codecForString())
|
.property("transactionId", codecForString())
|
||||||
.build("PrepareTipResult");
|
.build("PrepareRewardResult");
|
||||||
|
|
||||||
export interface BenchmarkResult {
|
export interface BenchmarkResult {
|
||||||
time: { [s: string]: number };
|
time: { [s: string]: number };
|
||||||
@ -994,6 +994,9 @@ export interface ExchangeDetailedResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WalletCoreVersion {
|
export interface WalletCoreVersion {
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
hash: string | undefined;
|
hash: string | undefined;
|
||||||
version: string;
|
version: string;
|
||||||
exchange: string;
|
exchange: string;
|
||||||
@ -1930,23 +1933,23 @@ export const codecForStartRefundQueryRequest =
|
|||||||
.property("transactionId", codecForTransactionIdStr())
|
.property("transactionId", codecForTransactionIdStr())
|
||||||
.build("StartRefundQueryRequest");
|
.build("StartRefundQueryRequest");
|
||||||
|
|
||||||
export interface PrepareTipRequest {
|
export interface PrepareRewardRequest {
|
||||||
talerTipUri: string;
|
talerRewardUri: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codecForPrepareTipRequest = (): Codec<PrepareTipRequest> =>
|
export const codecForPrepareRewardRequest = (): Codec<PrepareRewardRequest> =>
|
||||||
buildCodecForObject<PrepareTipRequest>()
|
buildCodecForObject<PrepareRewardRequest>()
|
||||||
.property("talerTipUri", codecForString())
|
.property("talerRewardUri", codecForString())
|
||||||
.build("PrepareTipRequest");
|
.build("PrepareRewardRequest");
|
||||||
|
|
||||||
export interface AcceptTipRequest {
|
export interface AcceptRewardRequest {
|
||||||
walletTipId: string;
|
walletRewardId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codecForAcceptTipRequest = (): Codec<AcceptTipRequest> =>
|
export const codecForAcceptTipRequest = (): Codec<AcceptRewardRequest> =>
|
||||||
buildCodecForObject<AcceptTipRequest>()
|
buildCodecForObject<AcceptRewardRequest>()
|
||||||
.property("walletTipId", codecForString())
|
.property("walletRewardId", codecForString())
|
||||||
.build("AcceptTipRequest");
|
.build("AcceptRewardRequest");
|
||||||
|
|
||||||
export interface FailTransactionRequest {
|
export interface FailTransactionRequest {
|
||||||
transactionId: TransactionIdStr;
|
transactionId: TransactionIdStr;
|
||||||
|
@ -651,13 +651,13 @@ walletCli
|
|||||||
alwaysYes: args.handleUri.autoYes,
|
alwaysYes: args.handleUri.autoYes,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case TalerUriAction.Tip: {
|
case TalerUriAction.Reward: {
|
||||||
const res = await wallet.client.call(WalletApiOperation.PrepareTip, {
|
const res = await wallet.client.call(WalletApiOperation.PrepareReward, {
|
||||||
talerTipUri: uri,
|
talerRewardUri: uri,
|
||||||
});
|
});
|
||||||
console.log("tip status", res);
|
console.log("tip status", res);
|
||||||
await wallet.client.call(WalletApiOperation.AcceptTip, {
|
await wallet.client.call(WalletApiOperation.AcceptReward, {
|
||||||
walletTipId: res.walletTipId,
|
walletRewardId: res.walletRewardId,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
1
packages/taler-wallet-core/.gitignore
vendored
1
packages/taler-wallet-core/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/lib
|
/lib
|
||||||
/coverage
|
/coverage
|
||||||
|
/src/version.json
|
||||||
|
@ -28,7 +28,6 @@ import {
|
|||||||
AgeCommitmentProof,
|
AgeCommitmentProof,
|
||||||
AgeRestriction,
|
AgeRestriction,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
AmountLike,
|
|
||||||
Amounts,
|
Amounts,
|
||||||
AmountString,
|
AmountString,
|
||||||
amountToBuffer,
|
amountToBuffer,
|
||||||
@ -64,7 +63,6 @@ import {
|
|||||||
hashCoinPub,
|
hashCoinPub,
|
||||||
hashDenomPub,
|
hashDenomPub,
|
||||||
hashTruncate32,
|
hashTruncate32,
|
||||||
j2s,
|
|
||||||
kdf,
|
kdf,
|
||||||
kdfKw,
|
kdfKw,
|
||||||
keyExchangeEcdhEddsa,
|
keyExchangeEcdhEddsa,
|
||||||
@ -81,16 +79,13 @@ import {
|
|||||||
rsaVerify,
|
rsaVerify,
|
||||||
setupTipPlanchet,
|
setupTipPlanchet,
|
||||||
stringToBytes,
|
stringToBytes,
|
||||||
TalerProtocolDuration,
|
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
TalerSignaturePurpose,
|
TalerSignaturePurpose,
|
||||||
timestampRoundedToBuffer,
|
timestampRoundedToBuffer,
|
||||||
UnblindedSignature,
|
UnblindedSignature,
|
||||||
validateIban,
|
|
||||||
WireFee,
|
WireFee,
|
||||||
WithdrawalPlanchet,
|
WithdrawalPlanchet,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import bigint from "big-integer";
|
|
||||||
// FIXME: Crypto should not use DB Types!
|
// FIXME: Crypto should not use DB Types!
|
||||||
import { DenominationRecord } from "../db.js";
|
import { DenominationRecord } from "../db.js";
|
||||||
import {
|
import {
|
||||||
@ -108,7 +103,6 @@ import {
|
|||||||
EncryptContractForDepositResponse,
|
EncryptContractForDepositResponse,
|
||||||
EncryptContractRequest,
|
EncryptContractRequest,
|
||||||
EncryptContractResponse,
|
EncryptContractResponse,
|
||||||
EncryptedContract,
|
|
||||||
SignDeletePurseRequest,
|
SignDeletePurseRequest,
|
||||||
SignDeletePurseResponse,
|
SignDeletePurseResponse,
|
||||||
SignPurseMergeRequest,
|
SignPurseMergeRequest,
|
||||||
|
@ -677,7 +677,7 @@ export interface PlanchetRecord {
|
|||||||
export enum CoinSourceType {
|
export enum CoinSourceType {
|
||||||
Withdraw = "withdraw",
|
Withdraw = "withdraw",
|
||||||
Refresh = "refresh",
|
Refresh = "refresh",
|
||||||
Tip = "tip",
|
Reward = "reward",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WithdrawCoinSource {
|
export interface WithdrawCoinSource {
|
||||||
@ -705,13 +705,13 @@ export interface RefreshCoinSource {
|
|||||||
oldCoinPub: string;
|
oldCoinPub: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TipCoinSource {
|
export interface RewardCoinSource {
|
||||||
type: CoinSourceType.Tip;
|
type: CoinSourceType.Reward;
|
||||||
walletTipId: string;
|
walletRewardId: string;
|
||||||
coinIndex: number;
|
coinIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CoinSource = WithdrawCoinSource | RefreshCoinSource | TipCoinSource;
|
export type CoinSource = WithdrawCoinSource | RefreshCoinSource | RewardCoinSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CoinRecord as stored in the "coins" data store
|
* CoinRecord as stored in the "coins" data store
|
||||||
@ -815,9 +815,9 @@ export interface CoinAllocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Status of a tip we got from a merchant.
|
* Status of a reward we got from a merchant.
|
||||||
*/
|
*/
|
||||||
export interface TipRecord {
|
export interface RewardRecord {
|
||||||
/**
|
/**
|
||||||
* Has the user accepted the tip? Only after the tip has been accepted coins
|
* Has the user accepted the tip? Only after the tip has been accepted coins
|
||||||
* withdrawn from the tip may be used.
|
* withdrawn from the tip may be used.
|
||||||
@ -827,17 +827,17 @@ export interface TipRecord {
|
|||||||
/**
|
/**
|
||||||
* The tipped amount.
|
* The tipped amount.
|
||||||
*/
|
*/
|
||||||
tipAmountRaw: AmountString;
|
rewardAmountRaw: AmountString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Effect on the balance (including fees etc).
|
* Effect on the balance (including fees etc).
|
||||||
*/
|
*/
|
||||||
tipAmountEffective: AmountString;
|
rewardAmountEffective: AmountString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp, the tip can't be picked up anymore after this deadline.
|
* Timestamp, the tip can't be picked up anymore after this deadline.
|
||||||
*/
|
*/
|
||||||
tipExpiration: TalerProtocolTimestamp;
|
rewardExpiration: TalerProtocolTimestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exchange that will sign our coins, chosen by the merchant.
|
* The exchange that will sign our coins, chosen by the merchant.
|
||||||
@ -863,7 +863,7 @@ export interface TipRecord {
|
|||||||
/**
|
/**
|
||||||
* Tip ID chosen by the wallet.
|
* Tip ID chosen by the wallet.
|
||||||
*/
|
*/
|
||||||
walletTipId: string;
|
walletRewardId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Secret seed used to derive planchets for this tip.
|
* Secret seed used to derive planchets for this tip.
|
||||||
@ -871,9 +871,9 @@ export interface TipRecord {
|
|||||||
secretSeed: string;
|
secretSeed: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The merchant's identifier for this tip.
|
* The merchant's identifier for this reward.
|
||||||
*/
|
*/
|
||||||
merchantTipId: string;
|
merchantRewardId: string;
|
||||||
|
|
||||||
createdTimestamp: TalerPreciseTimestamp;
|
createdTimestamp: TalerPreciseTimestamp;
|
||||||
|
|
||||||
@ -888,10 +888,10 @@ export interface TipRecord {
|
|||||||
*/
|
*/
|
||||||
pickedUpTimestamp: TalerPreciseTimestamp | undefined;
|
pickedUpTimestamp: TalerPreciseTimestamp | undefined;
|
||||||
|
|
||||||
status: TipRecordStatus;
|
status: RewardRecordStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TipRecordStatus {
|
export enum RewardRecordStatus {
|
||||||
PendingPickup = 10,
|
PendingPickup = 10,
|
||||||
|
|
||||||
SuspendidPickup = 20,
|
SuspendidPickup = 20,
|
||||||
@ -1420,7 +1420,7 @@ export interface KycPendingInfo {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Group of withdrawal operations that need to be executed.
|
* Group of withdrawal operations that need to be executed.
|
||||||
* (Either for a normal withdrawal or from a tip.)
|
* (Either for a normal withdrawal or from a reward.)
|
||||||
*
|
*
|
||||||
* The withdrawal group record is only created after we know
|
* The withdrawal group record is only created after we know
|
||||||
* the coin selection we want to withdraw.
|
* the coin selection we want to withdraw.
|
||||||
@ -2480,12 +2480,12 @@ export const WalletStoresV1 = {
|
|||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tips: describeStore(
|
rewards: describeStore(
|
||||||
"tips",
|
"rewards",
|
||||||
describeContents<TipRecord>({ keyPath: "walletTipId" }),
|
describeContents<RewardRecord>({ keyPath: "walletRewardId" }),
|
||||||
{
|
{
|
||||||
byMerchantTipIdAndBaseUrl: describeIndex("byMerchantTipIdAndBaseUrl", [
|
byMerchantTipIdAndBaseUrl: describeIndex("byMerchantRewardIdAndBaseUrl", [
|
||||||
"merchantTipId",
|
"merchantRewardId",
|
||||||
"merchantBaseUrl",
|
"merchantBaseUrl",
|
||||||
]),
|
]),
|
||||||
byStatus: describeIndex("byStatus", "status", {
|
byStatus: describeIndex("byStatus", "status", {
|
||||||
@ -2935,22 +2935,6 @@ export const walletDbFixups: FixupDescription[] = [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "TipRecordRecord_status_add",
|
|
||||||
async fn(tx): Promise<void> {
|
|
||||||
await tx.tips.iter().forEachAsync(async (r) => {
|
|
||||||
// Remove legacy transactions that don't have the totalCost field yet.
|
|
||||||
if (r.status == null) {
|
|
||||||
if (r.pickedUpTimestamp) {
|
|
||||||
r.status = TipRecordStatus.Done;
|
|
||||||
} else {
|
|
||||||
r.status = TipRecordStatus.PendingPickup;
|
|
||||||
}
|
|
||||||
await tx.tips.put(r);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "CoinAvailabilityRecord_visibleCoinCount_add",
|
name: "CoinAvailabilityRecord_visibleCoinCount_add",
|
||||||
async fn(tx): Promise<void> {
|
async fn(tx): Promise<void> {
|
||||||
|
@ -96,7 +96,7 @@ export async function exportBackup(
|
|||||||
x.purchases,
|
x.purchases,
|
||||||
x.refreshGroups,
|
x.refreshGroups,
|
||||||
x.backupProviders,
|
x.backupProviders,
|
||||||
x.tips,
|
x.rewards,
|
||||||
x.recoupGroups,
|
x.recoupGroups,
|
||||||
x.withdrawalGroups,
|
x.withdrawalGroups,
|
||||||
])
|
])
|
||||||
@ -184,12 +184,12 @@ export async function exportBackup(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.tips.iter().forEach((tip) => {
|
await tx.rewards.iter().forEach((tip) => {
|
||||||
backupTips.push({
|
backupTips.push({
|
||||||
exchange_base_url: tip.exchangeBaseUrl,
|
exchange_base_url: tip.exchangeBaseUrl,
|
||||||
merchant_base_url: tip.merchantBaseUrl,
|
merchant_base_url: tip.merchantBaseUrl,
|
||||||
merchant_tip_id: tip.merchantTipId,
|
merchant_tip_id: tip.merchantRewardId,
|
||||||
wallet_tip_id: tip.walletTipId,
|
wallet_tip_id: tip.walletRewardId,
|
||||||
next_url: tip.next_url,
|
next_url: tip.next_url,
|
||||||
secret_seed: tip.secretSeed,
|
secret_seed: tip.secretSeed,
|
||||||
selected_denoms: tip.denomsSel.selectedDenoms.map((x) => ({
|
selected_denoms: tip.denomsSel.selectedDenoms.map((x) => ({
|
||||||
@ -199,8 +199,8 @@ export async function exportBackup(
|
|||||||
timestamp_finished: tip.pickedUpTimestamp,
|
timestamp_finished: tip.pickedUpTimestamp,
|
||||||
timestamp_accepted: tip.acceptedTimestamp,
|
timestamp_accepted: tip.acceptedTimestamp,
|
||||||
timestamp_created: tip.createdTimestamp,
|
timestamp_created: tip.createdTimestamp,
|
||||||
timestamp_expiration: tip.tipExpiration,
|
timestamp_expiration: tip.rewardExpiration,
|
||||||
tip_amount_raw: Amounts.stringify(tip.tipAmountRaw),
|
tip_amount_raw: Amounts.stringify(tip.rewardAmountRaw),
|
||||||
selected_denoms_uid: tip.denomSelUid,
|
selected_denoms_uid: tip.denomSelUid,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -244,11 +244,11 @@ export async function exportBackup(
|
|||||||
refresh_group_id: coin.coinSource.refreshGroupId,
|
refresh_group_id: coin.coinSource.refreshGroupId,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case CoinSourceType.Tip:
|
case CoinSourceType.Reward:
|
||||||
bcs = {
|
bcs = {
|
||||||
type: BackupCoinSourceType.Tip,
|
type: BackupCoinSourceType.Reward,
|
||||||
coin_index: coin.coinSource.coinIndex,
|
coin_index: coin.coinSource.coinIndex,
|
||||||
wallet_tip_id: coin.coinSource.walletTipId,
|
wallet_tip_id: coin.coinSource.walletRewardId,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case CoinSourceType.Withdraw:
|
case CoinSourceType.Withdraw:
|
||||||
|
@ -56,7 +56,7 @@ import {
|
|||||||
WithdrawalGroupStatus,
|
WithdrawalGroupStatus,
|
||||||
WithdrawalRecordType,
|
WithdrawalRecordType,
|
||||||
RefreshOperationStatus,
|
RefreshOperationStatus,
|
||||||
TipRecordStatus,
|
RewardRecordStatus,
|
||||||
} from "../../db.js";
|
} from "../../db.js";
|
||||||
import { InternalWalletState } from "../../internal-wallet-state.js";
|
import { InternalWalletState } from "../../internal-wallet-state.js";
|
||||||
import { assertUnreachable } from "../../util/assertUnreachable.js";
|
import { assertUnreachable } from "../../util/assertUnreachable.js";
|
||||||
@ -250,11 +250,11 @@ export async function importCoin(
|
|||||||
refreshGroupId: backupCoin.coin_source.refresh_group_id,
|
refreshGroupId: backupCoin.coin_source.refresh_group_id,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case BackupCoinSourceType.Tip:
|
case BackupCoinSourceType.Reward:
|
||||||
coinSource = {
|
coinSource = {
|
||||||
type: CoinSourceType.Tip,
|
type: CoinSourceType.Reward,
|
||||||
coinIndex: backupCoin.coin_source.coin_index,
|
coinIndex: backupCoin.coin_source.coin_index,
|
||||||
walletTipId: backupCoin.coin_source.wallet_tip_id,
|
walletRewardId: backupCoin.coin_source.wallet_tip_id,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case BackupCoinSourceType.Withdraw:
|
case BackupCoinSourceType.Withdraw:
|
||||||
@ -311,7 +311,7 @@ export async function importBackup(
|
|||||||
x.purchases,
|
x.purchases,
|
||||||
x.refreshGroups,
|
x.refreshGroups,
|
||||||
x.backupProviders,
|
x.backupProviders,
|
||||||
x.tips,
|
x.rewards,
|
||||||
x.recoupGroups,
|
x.recoupGroups,
|
||||||
x.withdrawalGroups,
|
x.withdrawalGroups,
|
||||||
x.tombstones,
|
x.tombstones,
|
||||||
@ -812,13 +812,13 @@ export async function importBackup(
|
|||||||
|
|
||||||
for (const backupTip of backupBlob.tips) {
|
for (const backupTip of backupBlob.tips) {
|
||||||
const ts = constructTombstone({
|
const ts = constructTombstone({
|
||||||
tag: TombstoneTag.DeleteTip,
|
tag: TombstoneTag.DeleteReward,
|
||||||
walletTipId: backupTip.wallet_tip_id,
|
walletTipId: backupTip.wallet_tip_id,
|
||||||
});
|
});
|
||||||
if (tombstoneSet.has(ts)) {
|
if (tombstoneSet.has(ts)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const existingTip = await tx.tips.get(backupTip.wallet_tip_id);
|
const existingTip = await tx.rewards.get(backupTip.wallet_tip_id);
|
||||||
if (!existingTip) {
|
if (!existingTip) {
|
||||||
const tipAmountRaw = Amounts.parseOrThrow(backupTip.tip_amount_raw);
|
const tipAmountRaw = Amounts.parseOrThrow(backupTip.tip_amount_raw);
|
||||||
const denomsSel = await getDenomSelStateFromBackup(
|
const denomsSel = await getDenomSelStateFromBackup(
|
||||||
@ -827,22 +827,22 @@ export async function importBackup(
|
|||||||
backupTip.exchange_base_url,
|
backupTip.exchange_base_url,
|
||||||
backupTip.selected_denoms,
|
backupTip.selected_denoms,
|
||||||
);
|
);
|
||||||
await tx.tips.put({
|
await tx.rewards.put({
|
||||||
acceptedTimestamp: backupTip.timestamp_accepted,
|
acceptedTimestamp: backupTip.timestamp_accepted,
|
||||||
createdTimestamp: backupTip.timestamp_created,
|
createdTimestamp: backupTip.timestamp_created,
|
||||||
denomsSel,
|
denomsSel,
|
||||||
next_url: backupTip.next_url,
|
next_url: backupTip.next_url,
|
||||||
exchangeBaseUrl: backupTip.exchange_base_url,
|
exchangeBaseUrl: backupTip.exchange_base_url,
|
||||||
merchantBaseUrl: backupTip.exchange_base_url,
|
merchantBaseUrl: backupTip.exchange_base_url,
|
||||||
merchantTipId: backupTip.merchant_tip_id,
|
merchantRewardId: backupTip.merchant_tip_id,
|
||||||
pickedUpTimestamp: backupTip.timestamp_finished,
|
pickedUpTimestamp: backupTip.timestamp_finished,
|
||||||
secretSeed: backupTip.secret_seed,
|
secretSeed: backupTip.secret_seed,
|
||||||
tipAmountEffective: Amounts.stringify(denomsSel.totalCoinValue),
|
rewardAmountEffective: Amounts.stringify(denomsSel.totalCoinValue),
|
||||||
tipAmountRaw: Amounts.stringify(tipAmountRaw),
|
rewardAmountRaw: Amounts.stringify(tipAmountRaw),
|
||||||
tipExpiration: backupTip.timestamp_expiration,
|
rewardExpiration: backupTip.timestamp_expiration,
|
||||||
walletTipId: backupTip.wallet_tip_id,
|
walletRewardId: backupTip.wallet_tip_id,
|
||||||
denomSelUid: backupTip.selected_denoms_uid,
|
denomSelUid: backupTip.selected_denoms_uid,
|
||||||
status: TipRecordStatus.Done, // FIXME!
|
status: RewardRecordStatus.Done, // FIXME!
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -863,8 +863,8 @@ export async function importBackup(
|
|||||||
} else if (type === TombstoneTag.DeleteRefund) {
|
} else if (type === TombstoneTag.DeleteRefund) {
|
||||||
// Nothing required, will just prevent display
|
// Nothing required, will just prevent display
|
||||||
// in the transactions list
|
// in the transactions list
|
||||||
} else if (type === TombstoneTag.DeleteTip) {
|
} else if (type === TombstoneTag.DeleteReward) {
|
||||||
await tx.tips.delete(rest[0]);
|
await tx.rewards.delete(rest[0]);
|
||||||
} else if (type === TombstoneTag.DeleteWithdrawalGroup) {
|
} else if (type === TombstoneTag.DeleteWithdrawalGroup) {
|
||||||
await tx.withdrawalGroups.delete(rest[0]);
|
await tx.withdrawalGroups.delete(rest[0]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -57,7 +57,7 @@ import {
|
|||||||
PurchaseRecord,
|
PurchaseRecord,
|
||||||
RecoupGroupRecord,
|
RecoupGroupRecord,
|
||||||
RefreshGroupRecord,
|
RefreshGroupRecord,
|
||||||
TipRecord,
|
RewardRecord,
|
||||||
WithdrawalGroupRecord,
|
WithdrawalGroupRecord,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util";
|
import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util";
|
||||||
@ -293,10 +293,10 @@ function convertTaskToTransactionId(
|
|||||||
tag: TransactionType.Refresh,
|
tag: TransactionType.Refresh,
|
||||||
refreshGroupId: parsedTaskId.refreshGroupId,
|
refreshGroupId: parsedTaskId.refreshGroupId,
|
||||||
});
|
});
|
||||||
case PendingTaskType.TipPickup:
|
case PendingTaskType.RewardPickup:
|
||||||
return constructTransactionIdentifier({
|
return constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId: parsedTaskId.walletTipId,
|
walletRewardId: parsedTaskId.walletRewardId,
|
||||||
});
|
});
|
||||||
case PendingTaskType.PeerPushDebit:
|
case PendingTaskType.PeerPushDebit:
|
||||||
return constructTransactionIdentifier({
|
return constructTransactionIdentifier({
|
||||||
@ -515,7 +515,7 @@ export enum TombstoneTag {
|
|||||||
DeleteWithdrawalGroup = "delete-withdrawal-group",
|
DeleteWithdrawalGroup = "delete-withdrawal-group",
|
||||||
DeleteReserve = "delete-reserve",
|
DeleteReserve = "delete-reserve",
|
||||||
DeletePayment = "delete-payment",
|
DeletePayment = "delete-payment",
|
||||||
DeleteTip = "delete-tip",
|
DeleteReward = "delete-reward",
|
||||||
DeleteRefreshGroup = "delete-refresh-group",
|
DeleteRefreshGroup = "delete-refresh-group",
|
||||||
DeleteDepositGroup = "delete-deposit-group",
|
DeleteDepositGroup = "delete-deposit-group",
|
||||||
DeleteRefund = "delete-refund",
|
DeleteRefund = "delete-refund",
|
||||||
@ -601,7 +601,9 @@ export function runLongpollAsync(
|
|||||||
};
|
};
|
||||||
res = await reqFn(cts.token);
|
res = await reqFn(cts.token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await storePendingTaskError(ws, retryTag, getErrorDetailFromException(e));
|
const errDetail = getErrorDetailFromException(e);
|
||||||
|
logger.warn(`got error during long-polling: ${j2s(errDetail)}`);
|
||||||
|
await storePendingTaskError(ws, retryTag, errDetail);
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
delete ws.activeLongpoll[retryTag];
|
delete ws.activeLongpoll[retryTag];
|
||||||
@ -622,7 +624,7 @@ export type ParsedTombstone =
|
|||||||
| { tag: TombstoneTag.DeleteRefund; refundGroupId: string }
|
| { tag: TombstoneTag.DeleteRefund; refundGroupId: string }
|
||||||
| { tag: TombstoneTag.DeleteReserve; reservePub: string }
|
| { tag: TombstoneTag.DeleteReserve; reservePub: string }
|
||||||
| { tag: TombstoneTag.DeleteRefreshGroup; refreshGroupId: string }
|
| { tag: TombstoneTag.DeleteRefreshGroup; refreshGroupId: string }
|
||||||
| { tag: TombstoneTag.DeleteTip; walletTipId: string }
|
| { tag: TombstoneTag.DeleteReward; walletTipId: string }
|
||||||
| { tag: TombstoneTag.DeletePayment; proposalId: string };
|
| { tag: TombstoneTag.DeletePayment; proposalId: string };
|
||||||
|
|
||||||
export function constructTombstone(p: ParsedTombstone): TombstoneIdStr {
|
export function constructTombstone(p: ParsedTombstone): TombstoneIdStr {
|
||||||
@ -637,7 +639,7 @@ export function constructTombstone(p: ParsedTombstone): TombstoneIdStr {
|
|||||||
return `tmb:${p.tag}:${p.proposalId}` as TombstoneIdStr;
|
return `tmb:${p.tag}:${p.proposalId}` as TombstoneIdStr;
|
||||||
case TombstoneTag.DeleteRefreshGroup:
|
case TombstoneTag.DeleteRefreshGroup:
|
||||||
return `tmb:${p.tag}:${p.refreshGroupId}` as TombstoneIdStr;
|
return `tmb:${p.tag}:${p.refreshGroupId}` as TombstoneIdStr;
|
||||||
case TombstoneTag.DeleteTip:
|
case TombstoneTag.DeleteReward:
|
||||||
return `tmb:${p.tag}:${p.walletTipId}` as TombstoneIdStr;
|
return `tmb:${p.tag}:${p.walletTipId}` as TombstoneIdStr;
|
||||||
default:
|
default:
|
||||||
assertUnreachable(p);
|
assertUnreachable(p);
|
||||||
@ -810,7 +812,7 @@ export type ParsedTaskIdentifier =
|
|||||||
| { tag: PendingTaskType.PeerPushDebit; pursePub: string }
|
| { tag: PendingTaskType.PeerPushDebit; pursePub: string }
|
||||||
| { tag: PendingTaskType.Purchase; proposalId: string }
|
| { tag: PendingTaskType.Purchase; proposalId: string }
|
||||||
| { tag: PendingTaskType.Recoup; recoupGroupId: string }
|
| { tag: PendingTaskType.Recoup; recoupGroupId: string }
|
||||||
| { tag: PendingTaskType.TipPickup; walletTipId: string }
|
| { tag: PendingTaskType.RewardPickup; walletRewardId: string }
|
||||||
| { tag: PendingTaskType.Refresh; refreshGroupId: string };
|
| { tag: PendingTaskType.Refresh; refreshGroupId: string };
|
||||||
|
|
||||||
export function parseTaskIdentifier(x: string): ParsedTaskIdentifier {
|
export function parseTaskIdentifier(x: string): ParsedTaskIdentifier {
|
||||||
@ -844,8 +846,8 @@ export function parseTaskIdentifier(x: string): ParsedTaskIdentifier {
|
|||||||
return { tag: type, recoupGroupId: rest[0] };
|
return { tag: type, recoupGroupId: rest[0] };
|
||||||
case PendingTaskType.Refresh:
|
case PendingTaskType.Refresh:
|
||||||
return { tag: type, refreshGroupId: rest[0] };
|
return { tag: type, refreshGroupId: rest[0] };
|
||||||
case PendingTaskType.TipPickup:
|
case PendingTaskType.RewardPickup:
|
||||||
return { tag: type, walletTipId: rest[0] };
|
return { tag: type, walletRewardId: rest[0] };
|
||||||
case PendingTaskType.Withdraw:
|
case PendingTaskType.Withdraw:
|
||||||
return { tag: type, withdrawalGroupId: rest[0] };
|
return { tag: type, withdrawalGroupId: rest[0] };
|
||||||
default:
|
default:
|
||||||
@ -877,8 +879,8 @@ export function constructTaskIdentifier(p: ParsedTaskIdentifier): TaskId {
|
|||||||
return `${p.tag}:${p.recoupGroupId}` as TaskId;
|
return `${p.tag}:${p.recoupGroupId}` as TaskId;
|
||||||
case PendingTaskType.Refresh:
|
case PendingTaskType.Refresh:
|
||||||
return `${p.tag}:${p.refreshGroupId}` as TaskId;
|
return `${p.tag}:${p.refreshGroupId}` as TaskId;
|
||||||
case PendingTaskType.TipPickup:
|
case PendingTaskType.RewardPickup:
|
||||||
return `${p.tag}:${p.walletTipId}` as TaskId;
|
return `${p.tag}:${p.walletRewardId}` as TaskId;
|
||||||
case PendingTaskType.Withdraw:
|
case PendingTaskType.Withdraw:
|
||||||
return `${p.tag}:${p.withdrawalGroupId}` as TaskId;
|
return `${p.tag}:${p.withdrawalGroupId}` as TaskId;
|
||||||
default:
|
default:
|
||||||
@ -899,8 +901,8 @@ export namespace TaskIdentifiers {
|
|||||||
export function forExchangeCheckRefresh(exch: ExchangeRecord): TaskId {
|
export function forExchangeCheckRefresh(exch: ExchangeRecord): TaskId {
|
||||||
return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId;
|
return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId;
|
||||||
}
|
}
|
||||||
export function forTipPickup(tipRecord: TipRecord): TaskId {
|
export function forTipPickup(tipRecord: RewardRecord): TaskId {
|
||||||
return `${PendingTaskType.TipPickup}:${tipRecord.walletTipId}` as TaskId;
|
return `${PendingTaskType.RewardPickup}:${tipRecord.walletRewardId}` as TaskId;
|
||||||
}
|
}
|
||||||
export function forRefresh(refreshGroupRecord: RefreshGroupRecord): TaskId {
|
export function forRefresh(refreshGroupRecord: RefreshGroupRecord): TaskId {
|
||||||
return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}` as TaskId;
|
return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}` as TaskId;
|
||||||
|
@ -436,16 +436,25 @@ async function handlePeerPullCreditCreatePurse(
|
|||||||
|
|
||||||
logger.info(`reserve merge response: ${j2s(resp)}`);
|
logger.info(`reserve merge response: ${j2s(resp)}`);
|
||||||
|
|
||||||
await ws.db
|
const transactionId = constructTransactionIdentifier({
|
||||||
|
tag: TransactionType.PeerPullCredit,
|
||||||
|
pursePub: pullIni.pursePub,
|
||||||
|
});
|
||||||
|
|
||||||
|
const transitionInfo = await ws.db
|
||||||
.mktx((x) => [x.peerPullPaymentInitiations])
|
.mktx((x) => [x.peerPullPaymentInitiations])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const pi2 = await tx.peerPullPaymentInitiations.get(pursePub);
|
const pi2 = await tx.peerPullPaymentInitiations.get(pursePub);
|
||||||
if (!pi2) {
|
if (!pi2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const oldTxState = computePeerPullCreditTransactionState(pi2);
|
||||||
pi2.status = PeerPullPaymentInitiationStatus.PendingReady;
|
pi2.status = PeerPullPaymentInitiationStatus.PendingReady;
|
||||||
await tx.peerPullPaymentInitiations.put(pi2);
|
await tx.peerPullPaymentInitiations.put(pi2);
|
||||||
|
const newTxState = computePeerPullCreditTransactionState(pi2);
|
||||||
|
return { oldTxState, newTxState };
|
||||||
});
|
});
|
||||||
|
notifyTransition(ws, transactionId, transitionInfo);
|
||||||
|
|
||||||
return TaskRunResult.finished();
|
return TaskRunResult.finished();
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ import {
|
|||||||
PeerPushPaymentIncomingStatus,
|
PeerPushPaymentIncomingStatus,
|
||||||
PeerPullPaymentInitiationStatus,
|
PeerPullPaymentInitiationStatus,
|
||||||
WithdrawalGroupStatus,
|
WithdrawalGroupStatus,
|
||||||
TipRecordStatus,
|
RewardRecordStatus,
|
||||||
DepositOperationStatus,
|
DepositOperationStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import {
|
import {
|
||||||
@ -232,17 +232,17 @@ async function gatherDepositPending(
|
|||||||
async function gatherTipPending(
|
async function gatherTipPending(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
tx: GetReadOnlyAccess<{
|
tx: GetReadOnlyAccess<{
|
||||||
tips: typeof WalletStoresV1.tips;
|
rewards: typeof WalletStoresV1.rewards;
|
||||||
operationRetries: typeof WalletStoresV1.operationRetries;
|
operationRetries: typeof WalletStoresV1.operationRetries;
|
||||||
}>,
|
}>,
|
||||||
now: AbsoluteTime,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const range = GlobalIDB.KeyRange.bound(
|
const range = GlobalIDB.KeyRange.bound(
|
||||||
TipRecordStatus.PendingPickup,
|
RewardRecordStatus.PendingPickup,
|
||||||
TipRecordStatus.PendingPickup,
|
RewardRecordStatus.PendingPickup,
|
||||||
);
|
);
|
||||||
await tx.tips.indexes.byStatus.iter(range).forEachAsync(async (tip) => {
|
await tx.rewards.indexes.byStatus.iter(range).forEachAsync(async (tip) => {
|
||||||
// FIXME: The tip record needs a proper status field!
|
// FIXME: The tip record needs a proper status field!
|
||||||
if (tip.pickedUpTimestamp) {
|
if (tip.pickedUpTimestamp) {
|
||||||
return;
|
return;
|
||||||
@ -252,13 +252,13 @@ async function gatherTipPending(
|
|||||||
const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
|
const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
|
||||||
if (tip.acceptedTimestamp) {
|
if (tip.acceptedTimestamp) {
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.TipPickup,
|
type: PendingTaskType.RewardPickup,
|
||||||
...getPendingCommon(ws, opId, timestampDue),
|
...getPendingCommon(ws, opId, timestampDue),
|
||||||
givesLifeness: true,
|
givesLifeness: true,
|
||||||
timestampDue: retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(),
|
timestampDue: retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(),
|
||||||
merchantBaseUrl: tip.merchantBaseUrl,
|
merchantBaseUrl: tip.merchantBaseUrl,
|
||||||
tipId: tip.walletTipId,
|
tipId: tip.walletRewardId,
|
||||||
merchantTipId: tip.merchantTipId,
|
merchantTipId: tip.merchantRewardId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -494,7 +494,7 @@ export async function getPendingOperations(
|
|||||||
x.refreshGroups,
|
x.refreshGroups,
|
||||||
x.coins,
|
x.coins,
|
||||||
x.withdrawalGroups,
|
x.withdrawalGroups,
|
||||||
x.tips,
|
x.rewards,
|
||||||
x.purchases,
|
x.purchases,
|
||||||
x.planchets,
|
x.planchets,
|
||||||
x.depositGroups,
|
x.depositGroups,
|
||||||
|
@ -82,7 +82,7 @@ async function putGroupAsFinished(
|
|||||||
await tx.recoupGroups.put(recoupGroup);
|
await tx.recoupGroups.put(recoupGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function recoupTipCoin(
|
async function recoupRewardCoin(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
recoupGroupId: string,
|
recoupGroupId: string,
|
||||||
coinIdx: number,
|
coinIdx: number,
|
||||||
@ -482,8 +482,8 @@ async function processRecoup(
|
|||||||
const cs = coin.coinSource;
|
const cs = coin.coinSource;
|
||||||
|
|
||||||
switch (cs.type) {
|
switch (cs.type) {
|
||||||
case CoinSourceType.Tip:
|
case CoinSourceType.Reward:
|
||||||
return recoupTipCoin(ws, recoupGroupId, coinIdx, coin);
|
return recoupRewardCoin(ws, recoupGroupId, coinIdx, coin);
|
||||||
case CoinSourceType.Refresh:
|
case CoinSourceType.Refresh:
|
||||||
return recoupRefreshCoin(ws, recoupGroupId, coinIdx, coin, cs);
|
return recoupRefreshCoin(ws, recoupGroupId, coinIdx, coin, cs);
|
||||||
case CoinSourceType.Withdraw:
|
case CoinSourceType.Withdraw:
|
||||||
|
@ -31,7 +31,7 @@ import {
|
|||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
parseTipUri,
|
parseRewardUri,
|
||||||
PrepareTipResult,
|
PrepareTipResult,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerPreciseTimestamp,
|
TalerPreciseTimestamp,
|
||||||
@ -48,8 +48,8 @@ import {
|
|||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
TipRecord,
|
RewardRecord,
|
||||||
TipRecordStatus,
|
RewardRecordStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { makeErrorDetail } from "@gnu-taler/taler-util";
|
import { makeErrorDetail } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
@ -84,31 +84,31 @@ const logger = new Logger("operations/tip.ts");
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the (DD37-style) transaction status based on the
|
* Get the (DD37-style) transaction status based on the
|
||||||
* database record of a tip.
|
* database record of a reward.
|
||||||
*/
|
*/
|
||||||
export function computeTipTransactionStatus(
|
export function computeRewardTransactionStatus(
|
||||||
tipRecord: TipRecord,
|
tipRecord: RewardRecord,
|
||||||
): TransactionState {
|
): TransactionState {
|
||||||
switch (tipRecord.status) {
|
switch (tipRecord.status) {
|
||||||
case TipRecordStatus.Done:
|
case RewardRecordStatus.Done:
|
||||||
return {
|
return {
|
||||||
major: TransactionMajorState.Done,
|
major: TransactionMajorState.Done,
|
||||||
};
|
};
|
||||||
case TipRecordStatus.Aborted:
|
case RewardRecordStatus.Aborted:
|
||||||
return {
|
return {
|
||||||
major: TransactionMajorState.Aborted,
|
major: TransactionMajorState.Aborted,
|
||||||
};
|
};
|
||||||
case TipRecordStatus.PendingPickup:
|
case RewardRecordStatus.PendingPickup:
|
||||||
return {
|
return {
|
||||||
major: TransactionMajorState.Pending,
|
major: TransactionMajorState.Pending,
|
||||||
minor: TransactionMinorState.Pickup,
|
minor: TransactionMinorState.Pickup,
|
||||||
};
|
};
|
||||||
case TipRecordStatus.DialogAccept:
|
case RewardRecordStatus.DialogAccept:
|
||||||
return {
|
return {
|
||||||
major: TransactionMajorState.Dialog,
|
major: TransactionMajorState.Dialog,
|
||||||
minor: TransactionMinorState.Proposed,
|
minor: TransactionMinorState.Proposed,
|
||||||
};
|
};
|
||||||
case TipRecordStatus.SuspendidPickup:
|
case RewardRecordStatus.SuspendidPickup:
|
||||||
return {
|
return {
|
||||||
major: TransactionMajorState.Pending,
|
major: TransactionMajorState.Pending,
|
||||||
minor: TransactionMinorState.Pickup,
|
minor: TransactionMinorState.Pickup,
|
||||||
@ -119,18 +119,18 @@ export function computeTipTransactionStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function computeTipTransactionActions(
|
export function computeTipTransactionActions(
|
||||||
tipRecord: TipRecord,
|
tipRecord: RewardRecord,
|
||||||
): TransactionAction[] {
|
): TransactionAction[] {
|
||||||
switch (tipRecord.status) {
|
switch (tipRecord.status) {
|
||||||
case TipRecordStatus.Done:
|
case RewardRecordStatus.Done:
|
||||||
return [TransactionAction.Delete];
|
return [TransactionAction.Delete];
|
||||||
case TipRecordStatus.Aborted:
|
case RewardRecordStatus.Aborted:
|
||||||
return [TransactionAction.Delete];
|
return [TransactionAction.Delete];
|
||||||
case TipRecordStatus.PendingPickup:
|
case RewardRecordStatus.PendingPickup:
|
||||||
return [TransactionAction.Suspend, TransactionAction.Fail];
|
return [TransactionAction.Suspend, TransactionAction.Fail];
|
||||||
case TipRecordStatus.SuspendidPickup:
|
case RewardRecordStatus.SuspendidPickup:
|
||||||
return [TransactionAction.Resume, TransactionAction.Fail];
|
return [TransactionAction.Resume, TransactionAction.Fail];
|
||||||
case TipRecordStatus.DialogAccept:
|
case RewardRecordStatus.DialogAccept:
|
||||||
return [TransactionAction.Abort];
|
return [TransactionAction.Abort];
|
||||||
default:
|
default:
|
||||||
assertUnreachable(tipRecord.status);
|
assertUnreachable(tipRecord.status);
|
||||||
@ -141,15 +141,15 @@ export async function prepareTip(
|
|||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
talerTipUri: string,
|
talerTipUri: string,
|
||||||
): Promise<PrepareTipResult> {
|
): Promise<PrepareTipResult> {
|
||||||
const res = parseTipUri(talerTipUri);
|
const res = parseRewardUri(talerTipUri);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
throw Error("invalid taler://tip URI");
|
throw Error("invalid taler://tip URI");
|
||||||
}
|
}
|
||||||
|
|
||||||
let tipRecord = await ws.db
|
let tipRecord = await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
return tx.tips.indexes.byMerchantTipIdAndBaseUrl.get([
|
return tx.rewards.indexes.byMerchantTipIdAndBaseUrl.get([
|
||||||
res.merchantTipId,
|
res.merchantTipId,
|
||||||
res.merchantBaseUrl,
|
res.merchantBaseUrl,
|
||||||
]);
|
]);
|
||||||
@ -194,44 +194,44 @@ export async function prepareTip(
|
|||||||
const secretSeed = encodeCrock(getRandomBytes(64));
|
const secretSeed = encodeCrock(getRandomBytes(64));
|
||||||
const denomSelUid = encodeCrock(getRandomBytes(32));
|
const denomSelUid = encodeCrock(getRandomBytes(32));
|
||||||
|
|
||||||
const newTipRecord: TipRecord = {
|
const newTipRecord: RewardRecord = {
|
||||||
walletTipId: walletTipId,
|
walletRewardId: walletTipId,
|
||||||
acceptedTimestamp: undefined,
|
acceptedTimestamp: undefined,
|
||||||
status: TipRecordStatus.DialogAccept,
|
status: RewardRecordStatus.DialogAccept,
|
||||||
tipAmountRaw: Amounts.stringify(amount),
|
rewardAmountRaw: Amounts.stringify(amount),
|
||||||
tipExpiration: tipPickupStatus.expiration,
|
rewardExpiration: tipPickupStatus.expiration,
|
||||||
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
||||||
next_url: tipPickupStatus.next_url,
|
next_url: tipPickupStatus.next_url,
|
||||||
merchantBaseUrl: res.merchantBaseUrl,
|
merchantBaseUrl: res.merchantBaseUrl,
|
||||||
createdTimestamp: TalerPreciseTimestamp.now(),
|
createdTimestamp: TalerPreciseTimestamp.now(),
|
||||||
merchantTipId: res.merchantTipId,
|
merchantRewardId: res.merchantTipId,
|
||||||
tipAmountEffective: Amounts.stringify(selectedDenoms.totalCoinValue),
|
rewardAmountEffective: Amounts.stringify(selectedDenoms.totalCoinValue),
|
||||||
denomsSel: selectedDenoms,
|
denomsSel: selectedDenoms,
|
||||||
pickedUpTimestamp: undefined,
|
pickedUpTimestamp: undefined,
|
||||||
secretSeed,
|
secretSeed,
|
||||||
denomSelUid,
|
denomSelUid,
|
||||||
};
|
};
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
await tx.tips.put(newTipRecord);
|
await tx.rewards.put(newTipRecord);
|
||||||
});
|
});
|
||||||
tipRecord = newTipRecord;
|
tipRecord = newTipRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactionId = constructTransactionIdentifier({
|
const transactionId = constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId: tipRecord.walletTipId,
|
walletRewardId: tipRecord.walletRewardId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tipStatus: PrepareTipResult = {
|
const tipStatus: PrepareTipResult = {
|
||||||
accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
|
accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
|
||||||
tipAmountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
|
rewardAmountRaw: Amounts.stringify(tipRecord.rewardAmountRaw),
|
||||||
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
exchangeBaseUrl: tipRecord.exchangeBaseUrl,
|
||||||
merchantBaseUrl: tipRecord.merchantBaseUrl,
|
merchantBaseUrl: tipRecord.merchantBaseUrl,
|
||||||
expirationTimestamp: tipRecord.tipExpiration,
|
expirationTimestamp: tipRecord.rewardExpiration,
|
||||||
tipAmountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
|
rewardAmountEffective: Amounts.stringify(tipRecord.rewardAmountEffective),
|
||||||
walletTipId: tipRecord.walletTipId,
|
walletRewardId: tipRecord.walletRewardId,
|
||||||
transactionId,
|
transactionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -243,25 +243,25 @@ export async function processTip(
|
|||||||
walletTipId: string,
|
walletTipId: string,
|
||||||
): Promise<TaskRunResult> {
|
): Promise<TaskRunResult> {
|
||||||
const tipRecord = await ws.db
|
const tipRecord = await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
return tx.tips.get(walletTipId);
|
return tx.rewards.get(walletTipId);
|
||||||
});
|
});
|
||||||
if (!tipRecord) {
|
if (!tipRecord) {
|
||||||
return TaskRunResult.finished();
|
return TaskRunResult.finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (tipRecord.status) {
|
switch (tipRecord.status) {
|
||||||
case TipRecordStatus.Aborted:
|
case RewardRecordStatus.Aborted:
|
||||||
case TipRecordStatus.DialogAccept:
|
case RewardRecordStatus.DialogAccept:
|
||||||
case TipRecordStatus.Done:
|
case RewardRecordStatus.Done:
|
||||||
case TipRecordStatus.SuspendidPickup:
|
case RewardRecordStatus.SuspendidPickup:
|
||||||
return TaskRunResult.finished();
|
return TaskRunResult.finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactionId = constructTransactionIdentifier({
|
const transactionId = constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId,
|
walletRewardId: walletTipId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const denomsForWithdraw = tipRecord.denomsSel;
|
const denomsForWithdraw = tipRecord.denomsSel;
|
||||||
@ -300,7 +300,7 @@ export async function processTip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tipStatusUrl = new URL(
|
const tipStatusUrl = new URL(
|
||||||
`tips/${tipRecord.merchantTipId}/pickup`,
|
`tips/${tipRecord.merchantRewardId}/pickup`,
|
||||||
tipRecord.merchantBaseUrl,
|
tipRecord.merchantBaseUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -384,9 +384,9 @@ export async function processTip(
|
|||||||
coinPriv: planchet.coinPriv,
|
coinPriv: planchet.coinPriv,
|
||||||
coinPub: planchet.coinPub,
|
coinPub: planchet.coinPub,
|
||||||
coinSource: {
|
coinSource: {
|
||||||
type: CoinSourceType.Tip,
|
type: CoinSourceType.Reward,
|
||||||
coinIndex: i,
|
coinIndex: i,
|
||||||
walletTipId: walletTipId,
|
walletRewardId: walletTipId,
|
||||||
},
|
},
|
||||||
sourceTransactionId: transactionId,
|
sourceTransactionId: transactionId,
|
||||||
denomPubHash: denom.denomPubHash,
|
denomPubHash: denom.denomPubHash,
|
||||||
@ -401,20 +401,20 @@ export async function processTip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const transitionInfo = await ws.db
|
const transitionInfo = await ws.db
|
||||||
.mktx((x) => [x.coins, x.coinAvailability, x.denominations, x.tips])
|
.mktx((x) => [x.coins, x.coinAvailability, x.denominations, x.rewards])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tr = await tx.tips.get(walletTipId);
|
const tr = await tx.rewards.get(walletTipId);
|
||||||
if (!tr) {
|
if (!tr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tr.status !== TipRecordStatus.PendingPickup) {
|
if (tr.status !== RewardRecordStatus.PendingPickup) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const oldTxState = computeTipTransactionStatus(tr);
|
const oldTxState = computeRewardTransactionStatus(tr);
|
||||||
tr.pickedUpTimestamp = TalerPreciseTimestamp.now();
|
tr.pickedUpTimestamp = TalerPreciseTimestamp.now();
|
||||||
tr.status = TipRecordStatus.Done;
|
tr.status = RewardRecordStatus.Done;
|
||||||
await tx.tips.put(tr);
|
await tx.rewards.put(tr);
|
||||||
const newTxState = computeTipTransactionStatus(tr);
|
const newTxState = computeRewardTransactionStatus(tr);
|
||||||
for (const cr of newCoinRecords) {
|
for (const cr of newCoinRecords) {
|
||||||
await makeCoinAvailable(ws, tx, cr);
|
await makeCoinAvailable(ws, tx, cr);
|
||||||
}
|
}
|
||||||
@ -432,26 +432,26 @@ export async function acceptTip(
|
|||||||
walletTipId: string,
|
walletTipId: string,
|
||||||
): Promise<AcceptTipResponse> {
|
): Promise<AcceptTipResponse> {
|
||||||
const transactionId = constructTransactionIdentifier({
|
const transactionId = constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId,
|
walletRewardId: walletTipId,
|
||||||
});
|
});
|
||||||
const dbRes = await ws.db
|
const dbRes = await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tipRecord = await tx.tips.get(walletTipId);
|
const tipRecord = await tx.rewards.get(walletTipId);
|
||||||
if (!tipRecord) {
|
if (!tipRecord) {
|
||||||
logger.error("tip not found");
|
logger.error("tip not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tipRecord.status != TipRecordStatus.DialogAccept) {
|
if (tipRecord.status != RewardRecordStatus.DialogAccept) {
|
||||||
logger.warn("Unable to accept tip in the current state");
|
logger.warn("Unable to accept tip in the current state");
|
||||||
return { tipRecord };
|
return { tipRecord };
|
||||||
}
|
}
|
||||||
const oldTxState = computeTipTransactionStatus(tipRecord);
|
const oldTxState = computeRewardTransactionStatus(tipRecord);
|
||||||
tipRecord.acceptedTimestamp = TalerPreciseTimestamp.now();
|
tipRecord.acceptedTimestamp = TalerPreciseTimestamp.now();
|
||||||
tipRecord.status = TipRecordStatus.PendingPickup;
|
tipRecord.status = RewardRecordStatus.PendingPickup;
|
||||||
await tx.tips.put(tipRecord);
|
await tx.rewards.put(tipRecord);
|
||||||
const newTxState = computeTipTransactionStatus(tipRecord);
|
const newTxState = computeRewardTransactionStatus(tipRecord);
|
||||||
return { tipRecord, transitionInfo: { oldTxState, newTxState } };
|
return { tipRecord, transitionInfo: { oldTxState, newTxState } };
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -465,53 +465,53 @@ export async function acceptTip(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
transactionId: constructTransactionIdentifier({
|
transactionId: constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId: walletTipId,
|
walletRewardId: walletTipId,
|
||||||
}),
|
}),
|
||||||
next_url: tipRecord.next_url,
|
next_url: tipRecord.next_url,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function suspendTipTransaction(
|
export async function suspendRewardTransaction(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
walletTipId: string,
|
walletRewardId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const taskId = constructTaskIdentifier({
|
const taskId = constructTaskIdentifier({
|
||||||
tag: PendingTaskType.TipPickup,
|
tag: PendingTaskType.RewardPickup,
|
||||||
walletTipId,
|
walletRewardId: walletRewardId,
|
||||||
});
|
});
|
||||||
const transactionId = constructTransactionIdentifier({
|
const transactionId = constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId,
|
walletRewardId: walletRewardId,
|
||||||
});
|
});
|
||||||
stopLongpolling(ws, taskId);
|
stopLongpolling(ws, taskId);
|
||||||
const transitionInfo = await ws.db
|
const transitionInfo = await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tipRec = await tx.tips.get(walletTipId);
|
const tipRec = await tx.rewards.get(walletRewardId);
|
||||||
if (!tipRec) {
|
if (!tipRec) {
|
||||||
logger.warn(`transaction tip ${walletTipId} not found`);
|
logger.warn(`transaction tip ${walletRewardId} not found`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newStatus: TipRecordStatus | undefined = undefined;
|
let newStatus: RewardRecordStatus | undefined = undefined;
|
||||||
switch (tipRec.status) {
|
switch (tipRec.status) {
|
||||||
case TipRecordStatus.Done:
|
case RewardRecordStatus.Done:
|
||||||
case TipRecordStatus.SuspendidPickup:
|
case RewardRecordStatus.SuspendidPickup:
|
||||||
case TipRecordStatus.Aborted:
|
case RewardRecordStatus.Aborted:
|
||||||
case TipRecordStatus.DialogAccept:
|
case RewardRecordStatus.DialogAccept:
|
||||||
break;
|
break;
|
||||||
case TipRecordStatus.PendingPickup:
|
case RewardRecordStatus.PendingPickup:
|
||||||
newStatus = TipRecordStatus.SuspendidPickup;
|
newStatus = RewardRecordStatus.SuspendidPickup;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assertUnreachable(tipRec.status);
|
assertUnreachable(tipRec.status);
|
||||||
}
|
}
|
||||||
if (newStatus != null) {
|
if (newStatus != null) {
|
||||||
const oldTxState = computeTipTransactionStatus(tipRec);
|
const oldTxState = computeRewardTransactionStatus(tipRec);
|
||||||
tipRec.status = newStatus;
|
tipRec.status = newStatus;
|
||||||
const newTxState = computeTipTransactionStatus(tipRec);
|
const newTxState = computeRewardTransactionStatus(tipRec);
|
||||||
await tx.tips.put(tipRec);
|
await tx.rewards.put(tipRec);
|
||||||
return {
|
return {
|
||||||
oldTxState,
|
oldTxState,
|
||||||
newTxState,
|
newTxState,
|
||||||
@ -525,43 +525,43 @@ export async function suspendTipTransaction(
|
|||||||
|
|
||||||
export async function resumeTipTransaction(
|
export async function resumeTipTransaction(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
walletTipId: string,
|
walletRewardId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const taskId = constructTaskIdentifier({
|
const taskId = constructTaskIdentifier({
|
||||||
tag: PendingTaskType.TipPickup,
|
tag: PendingTaskType.RewardPickup,
|
||||||
walletTipId,
|
walletRewardId: walletRewardId,
|
||||||
});
|
});
|
||||||
const transactionId = constructTransactionIdentifier({
|
const transactionId = constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId,
|
walletRewardId: walletRewardId,
|
||||||
});
|
});
|
||||||
stopLongpolling(ws, taskId);
|
stopLongpolling(ws, taskId);
|
||||||
const transitionInfo = await ws.db
|
const transitionInfo = await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tipRec = await tx.tips.get(walletTipId);
|
const rewardRec = await tx.rewards.get(walletRewardId);
|
||||||
if (!tipRec) {
|
if (!rewardRec) {
|
||||||
logger.warn(`transaction tip ${walletTipId} not found`);
|
logger.warn(`transaction reward ${walletRewardId} not found`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newStatus: TipRecordStatus | undefined = undefined;
|
let newStatus: RewardRecordStatus | undefined = undefined;
|
||||||
switch (tipRec.status) {
|
switch (rewardRec.status) {
|
||||||
case TipRecordStatus.Done:
|
case RewardRecordStatus.Done:
|
||||||
case TipRecordStatus.PendingPickup:
|
case RewardRecordStatus.PendingPickup:
|
||||||
case TipRecordStatus.Aborted:
|
case RewardRecordStatus.Aborted:
|
||||||
case TipRecordStatus.DialogAccept:
|
case RewardRecordStatus.DialogAccept:
|
||||||
break;
|
break;
|
||||||
case TipRecordStatus.SuspendidPickup:
|
case RewardRecordStatus.SuspendidPickup:
|
||||||
newStatus = TipRecordStatus.PendingPickup;
|
newStatus = RewardRecordStatus.PendingPickup;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertUnreachable(tipRec.status);
|
assertUnreachable(rewardRec.status);
|
||||||
}
|
}
|
||||||
if (newStatus != null) {
|
if (newStatus != null) {
|
||||||
const oldTxState = computeTipTransactionStatus(tipRec);
|
const oldTxState = computeRewardTransactionStatus(rewardRec);
|
||||||
tipRec.status = newStatus;
|
rewardRec.status = newStatus;
|
||||||
const newTxState = computeTipTransactionStatus(tipRec);
|
const newTxState = computeRewardTransactionStatus(rewardRec);
|
||||||
await tx.tips.put(tipRec);
|
await tx.rewards.put(rewardRec);
|
||||||
return {
|
return {
|
||||||
oldTxState,
|
oldTxState,
|
||||||
newTxState,
|
newTxState,
|
||||||
@ -582,43 +582,43 @@ export async function failTipTransaction(
|
|||||||
|
|
||||||
export async function abortTipTransaction(
|
export async function abortTipTransaction(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
walletTipId: string,
|
walletRewardId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const taskId = constructTaskIdentifier({
|
const taskId = constructTaskIdentifier({
|
||||||
tag: PendingTaskType.TipPickup,
|
tag: PendingTaskType.RewardPickup,
|
||||||
walletTipId,
|
walletRewardId: walletRewardId,
|
||||||
});
|
});
|
||||||
const transactionId = constructTransactionIdentifier({
|
const transactionId = constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId,
|
walletRewardId: walletRewardId,
|
||||||
});
|
});
|
||||||
stopLongpolling(ws, taskId);
|
stopLongpolling(ws, taskId);
|
||||||
const transitionInfo = await ws.db
|
const transitionInfo = await ws.db
|
||||||
.mktx((x) => [x.tips])
|
.mktx((x) => [x.rewards])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tipRec = await tx.tips.get(walletTipId);
|
const tipRec = await tx.rewards.get(walletRewardId);
|
||||||
if (!tipRec) {
|
if (!tipRec) {
|
||||||
logger.warn(`transaction tip ${walletTipId} not found`);
|
logger.warn(`transaction tip ${walletRewardId} not found`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newStatus: TipRecordStatus | undefined = undefined;
|
let newStatus: RewardRecordStatus | undefined = undefined;
|
||||||
switch (tipRec.status) {
|
switch (tipRec.status) {
|
||||||
case TipRecordStatus.Done:
|
case RewardRecordStatus.Done:
|
||||||
case TipRecordStatus.Aborted:
|
case RewardRecordStatus.Aborted:
|
||||||
case TipRecordStatus.PendingPickup:
|
case RewardRecordStatus.PendingPickup:
|
||||||
case TipRecordStatus.DialogAccept:
|
case RewardRecordStatus.DialogAccept:
|
||||||
break;
|
break;
|
||||||
case TipRecordStatus.SuspendidPickup:
|
case RewardRecordStatus.SuspendidPickup:
|
||||||
newStatus = TipRecordStatus.Aborted;
|
newStatus = RewardRecordStatus.Aborted;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertUnreachable(tipRec.status);
|
assertUnreachable(tipRec.status);
|
||||||
}
|
}
|
||||||
if (newStatus != null) {
|
if (newStatus != null) {
|
||||||
const oldTxState = computeTipTransactionStatus(tipRec);
|
const oldTxState = computeRewardTransactionStatus(tipRec);
|
||||||
tipRec.status = newStatus;
|
tipRec.status = newStatus;
|
||||||
const newTxState = computeTipTransactionStatus(tipRec);
|
const newTxState = computeRewardTransactionStatus(tipRec);
|
||||||
await tx.tips.put(tipRec);
|
await tx.rewards.put(tipRec);
|
||||||
return {
|
return {
|
||||||
oldTxState,
|
oldTxState,
|
||||||
newTxState,
|
newTxState,
|
@ -58,7 +58,7 @@ import {
|
|||||||
RefreshGroupRecord,
|
RefreshGroupRecord,
|
||||||
RefreshOperationStatus,
|
RefreshOperationStatus,
|
||||||
RefundGroupRecord,
|
RefundGroupRecord,
|
||||||
TipRecord,
|
RewardRecord,
|
||||||
WalletContractData,
|
WalletContractData,
|
||||||
WithdrawalGroupRecord,
|
WithdrawalGroupRecord,
|
||||||
WithdrawalGroupStatus,
|
WithdrawalGroupStatus,
|
||||||
@ -107,11 +107,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
abortTipTransaction,
|
abortTipTransaction,
|
||||||
failTipTransaction,
|
failTipTransaction,
|
||||||
computeTipTransactionStatus,
|
computeRewardTransactionStatus,
|
||||||
resumeTipTransaction,
|
resumeTipTransaction,
|
||||||
suspendTipTransaction,
|
suspendRewardTransaction,
|
||||||
computeTipTransactionActions,
|
computeTipTransactionActions,
|
||||||
} from "./tip.js";
|
} from "./reward.js";
|
||||||
import {
|
import {
|
||||||
abortWithdrawalTransaction,
|
abortWithdrawalTransaction,
|
||||||
augmentPaytoUrisForWithdrawal,
|
augmentPaytoUrisForWithdrawal,
|
||||||
@ -187,7 +187,7 @@ function shouldSkipSearch(
|
|||||||
*/
|
*/
|
||||||
const txOrder: { [t in TransactionType]: number } = {
|
const txOrder: { [t in TransactionType]: number } = {
|
||||||
[TransactionType.Withdrawal]: 1,
|
[TransactionType.Withdrawal]: 1,
|
||||||
[TransactionType.Tip]: 2,
|
[TransactionType.Reward]: 2,
|
||||||
[TransactionType.Payment]: 3,
|
[TransactionType.Payment]: 3,
|
||||||
[TransactionType.PeerPullCredit]: 4,
|
[TransactionType.PeerPullCredit]: 4,
|
||||||
[TransactionType.PeerPullDebit]: 5,
|
[TransactionType.PeerPullDebit]: 5,
|
||||||
@ -284,12 +284,12 @@ export async function getTransactionById(
|
|||||||
throw Error(`no tx for refresh`);
|
throw Error(`no tx for refresh`);
|
||||||
}
|
}
|
||||||
|
|
||||||
case TransactionType.Tip: {
|
case TransactionType.Reward: {
|
||||||
const tipId = parsedTx.walletTipId;
|
const tipId = parsedTx.walletRewardId;
|
||||||
return await ws.db
|
return await ws.db
|
||||||
.mktx((x) => [x.tips, x.operationRetries])
|
.mktx((x) => [x.rewards, x.operationRetries])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tipRecord = await tx.tips.get(tipId);
|
const tipRecord = await tx.rewards.get(tipId);
|
||||||
if (!tipRecord) throw Error("not found");
|
if (!tipRecord) throw Error("not found");
|
||||||
|
|
||||||
const retries = await tx.operationRetries.get(
|
const retries = await tx.operationRetries.get(
|
||||||
@ -818,21 +818,21 @@ function buildTransactionForDeposit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildTransactionForTip(
|
function buildTransactionForTip(
|
||||||
tipRecord: TipRecord,
|
tipRecord: RewardRecord,
|
||||||
ort?: OperationRetryRecord,
|
ort?: OperationRetryRecord,
|
||||||
): Transaction {
|
): Transaction {
|
||||||
checkLogicInvariant(!!tipRecord.acceptedTimestamp);
|
checkLogicInvariant(!!tipRecord.acceptedTimestamp);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: TransactionType.Tip,
|
type: TransactionType.Reward,
|
||||||
txState: computeTipTransactionStatus(tipRecord),
|
txState: computeRewardTransactionStatus(tipRecord),
|
||||||
txActions: computeTipTransactionActions(tipRecord),
|
txActions: computeTipTransactionActions(tipRecord),
|
||||||
amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
|
amountEffective: Amounts.stringify(tipRecord.rewardAmountEffective),
|
||||||
amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
|
amountRaw: Amounts.stringify(tipRecord.rewardAmountRaw),
|
||||||
timestamp: tipRecord.acceptedTimestamp,
|
timestamp: tipRecord.acceptedTimestamp,
|
||||||
transactionId: constructTransactionIdentifier({
|
transactionId: constructTransactionIdentifier({
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId: tipRecord.walletTipId,
|
walletRewardId: tipRecord.walletRewardId,
|
||||||
}),
|
}),
|
||||||
merchantBaseUrl: tipRecord.merchantBaseUrl,
|
merchantBaseUrl: tipRecord.merchantBaseUrl,
|
||||||
...(ort?.lastError ? { error: ort.lastError } : {}),
|
...(ort?.lastError ? { error: ort.lastError } : {}),
|
||||||
@ -945,7 +945,7 @@ export async function getTransactions(
|
|||||||
x.purchases,
|
x.purchases,
|
||||||
x.contractTerms,
|
x.contractTerms,
|
||||||
x.recoupGroups,
|
x.recoupGroups,
|
||||||
x.tips,
|
x.rewards,
|
||||||
x.tombstones,
|
x.tombstones,
|
||||||
x.withdrawalGroups,
|
x.withdrawalGroups,
|
||||||
x.refreshGroups,
|
x.refreshGroups,
|
||||||
@ -1200,11 +1200,11 @@ export async function getTransactions(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
tx.tips.iter().forEachAsync(async (tipRecord) => {
|
tx.rewards.iter().forEachAsync(async (tipRecord) => {
|
||||||
if (
|
if (
|
||||||
shouldSkipCurrency(
|
shouldSkipCurrency(
|
||||||
transactionsRequest,
|
transactionsRequest,
|
||||||
Amounts.parseOrThrow(tipRecord.tipAmountRaw).currency,
|
Amounts.parseOrThrow(tipRecord.rewardAmountRaw).currency,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@ -1267,7 +1267,7 @@ export type ParsedTransactionIdentifier =
|
|||||||
| { tag: TransactionType.PeerPushDebit; pursePub: string }
|
| { tag: TransactionType.PeerPushDebit; pursePub: string }
|
||||||
| { tag: TransactionType.Refresh; refreshGroupId: string }
|
| { tag: TransactionType.Refresh; refreshGroupId: string }
|
||||||
| { tag: TransactionType.Refund; refundGroupId: string }
|
| { tag: TransactionType.Refund; refundGroupId: string }
|
||||||
| { tag: TransactionType.Tip; walletTipId: string }
|
| { tag: TransactionType.Reward; walletRewardId: string }
|
||||||
| { tag: TransactionType.Withdrawal; withdrawalGroupId: string }
|
| { tag: TransactionType.Withdrawal; withdrawalGroupId: string }
|
||||||
| { tag: TransactionType.InternalWithdrawal; withdrawalGroupId: string };
|
| { tag: TransactionType.InternalWithdrawal; withdrawalGroupId: string };
|
||||||
|
|
||||||
@ -1291,8 +1291,8 @@ export function constructTransactionIdentifier(
|
|||||||
return `txn:${pTxId.tag}:${pTxId.refreshGroupId}` as TransactionIdStr;
|
return `txn:${pTxId.tag}:${pTxId.refreshGroupId}` as TransactionIdStr;
|
||||||
case TransactionType.Refund:
|
case TransactionType.Refund:
|
||||||
return `txn:${pTxId.tag}:${pTxId.refundGroupId}` as TransactionIdStr;
|
return `txn:${pTxId.tag}:${pTxId.refundGroupId}` as TransactionIdStr;
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
return `txn:${pTxId.tag}:${pTxId.walletTipId}` as TransactionIdStr;
|
return `txn:${pTxId.tag}:${pTxId.walletRewardId}` as TransactionIdStr;
|
||||||
case TransactionType.Withdrawal:
|
case TransactionType.Withdrawal:
|
||||||
return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
|
return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
|
||||||
case TransactionType.InternalWithdrawal:
|
case TransactionType.InternalWithdrawal:
|
||||||
@ -1346,10 +1346,10 @@ export function parseTransactionIdentifier(
|
|||||||
tag: TransactionType.Refund,
|
tag: TransactionType.Refund,
|
||||||
refundGroupId: rest[0],
|
refundGroupId: rest[0],
|
||||||
};
|
};
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
return {
|
return {
|
||||||
tag: TransactionType.Tip,
|
tag: TransactionType.Reward,
|
||||||
walletTipId: rest[0],
|
walletRewardId: rest[0],
|
||||||
};
|
};
|
||||||
case TransactionType.Withdrawal:
|
case TransactionType.Withdrawal:
|
||||||
return {
|
return {
|
||||||
@ -1427,10 +1427,10 @@ export async function retryTransaction(
|
|||||||
stopLongpolling(ws, taskId);
|
stopLongpolling(ws, taskId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TransactionType.Tip: {
|
case TransactionType.Reward: {
|
||||||
const taskId = constructTaskIdentifier({
|
const taskId = constructTaskIdentifier({
|
||||||
tag: PendingTaskType.TipPickup,
|
tag: PendingTaskType.RewardPickup,
|
||||||
walletTipId: parsedTx.walletTipId,
|
walletRewardId: parsedTx.walletRewardId,
|
||||||
});
|
});
|
||||||
await resetPendingTaskTimeout(ws, taskId);
|
await resetPendingTaskTimeout(ws, taskId);
|
||||||
stopLongpolling(ws, taskId);
|
stopLongpolling(ws, taskId);
|
||||||
@ -1522,8 +1522,8 @@ export async function suspendTransaction(
|
|||||||
break;
|
break;
|
||||||
case TransactionType.Refund:
|
case TransactionType.Refund:
|
||||||
throw Error("refund transactions can't be suspended or resumed");
|
throw Error("refund transactions can't be suspended or resumed");
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
await suspendTipTransaction(ws, tx.walletTipId);
|
await suspendRewardTransaction(ws, tx.walletRewardId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertUnreachable(tx);
|
assertUnreachable(tx);
|
||||||
@ -1551,8 +1551,8 @@ export async function failTransaction(
|
|||||||
return;
|
return;
|
||||||
case TransactionType.Refund:
|
case TransactionType.Refund:
|
||||||
throw Error("can't do cancel-aborting on refund transaction");
|
throw Error("can't do cancel-aborting on refund transaction");
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
await failTipTransaction(ws, tx.walletTipId);
|
await failTipTransaction(ws, tx.walletRewardId);
|
||||||
return;
|
return;
|
||||||
case TransactionType.Refresh:
|
case TransactionType.Refresh:
|
||||||
await failRefreshGroup(ws, tx.refreshGroupId);
|
await failRefreshGroup(ws, tx.refreshGroupId);
|
||||||
@ -1613,8 +1613,8 @@ export async function resumeTransaction(
|
|||||||
break;
|
break;
|
||||||
case TransactionType.Refund:
|
case TransactionType.Refund:
|
||||||
throw Error("refund transactions can't be suspended or resumed");
|
throw Error("refund transactions can't be suspended or resumed");
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
await resumeTipTransaction(ws, tx.walletTipId);
|
await resumeTipTransaction(ws, tx.walletRewardId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1763,16 +1763,16 @@ export async function deleteTransaction(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TransactionType.Tip: {
|
case TransactionType.Reward: {
|
||||||
const tipId = parsedTx.walletTipId;
|
const tipId = parsedTx.walletRewardId;
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => [x.tips, x.tombstones])
|
.mktx((x) => [x.rewards, x.tombstones])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const tipRecord = await tx.tips.get(tipId);
|
const tipRecord = await tx.rewards.get(tipId);
|
||||||
if (tipRecord) {
|
if (tipRecord) {
|
||||||
await tx.tips.delete(tipId);
|
await tx.rewards.delete(tipId);
|
||||||
await tx.tombstones.put({
|
await tx.tombstones.put({
|
||||||
id: TombstoneTag.DeleteTip + ":" + tipId,
|
id: TombstoneTag.DeleteReward + ":" + tipId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1856,8 +1856,8 @@ export async function abortTransaction(
|
|||||||
case TransactionType.Deposit:
|
case TransactionType.Deposit:
|
||||||
await abortDepositGroup(ws, txId.depositGroupId);
|
await abortDepositGroup(ws, txId.depositGroupId);
|
||||||
break;
|
break;
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
await abortTipTransaction(ws, txId.walletTipId);
|
await abortTipTransaction(ws, txId.walletRewardId);
|
||||||
break;
|
break;
|
||||||
case TransactionType.Refund:
|
case TransactionType.Refund:
|
||||||
throw Error("can't abort refund transactions");
|
throw Error("can't abort refund transactions");
|
||||||
|
@ -33,7 +33,7 @@ export enum PendingTaskType {
|
|||||||
Purchase = "purchase",
|
Purchase = "purchase",
|
||||||
Refresh = "refresh",
|
Refresh = "refresh",
|
||||||
Recoup = "recoup",
|
Recoup = "recoup",
|
||||||
TipPickup = "tip-pickup",
|
RewardPickup = "reward-pickup",
|
||||||
Withdraw = "withdraw",
|
Withdraw = "withdraw",
|
||||||
Deposit = "deposit",
|
Deposit = "deposit",
|
||||||
Backup = "backup",
|
Backup = "backup",
|
||||||
@ -144,7 +144,7 @@ export interface PendingRefreshTask {
|
|||||||
* The wallet is picking up a tip that the user has accepted.
|
* The wallet is picking up a tip that the user has accepted.
|
||||||
*/
|
*/
|
||||||
export interface PendingTipPickupTask {
|
export interface PendingTipPickupTask {
|
||||||
type: PendingTaskType.TipPickup;
|
type: PendingTaskType.RewardPickup;
|
||||||
tipId: string;
|
tipId: string;
|
||||||
merchantBaseUrl: string;
|
merchantBaseUrl: string;
|
||||||
merchantTipId: string;
|
merchantTipId: string;
|
||||||
|
@ -34,3 +34,11 @@ export const WALLET_MERCHANT_PROTOCOL_VERSION = "2:0:1";
|
|||||||
* Uses libtool's current:revision:age versioning.
|
* Uses libtool's current:revision:age versioning.
|
||||||
*/
|
*/
|
||||||
export const WALLET_BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
|
export const WALLET_BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semver of the wallet-core implementation.
|
||||||
|
* Will be replaced with the value from package.json in a
|
||||||
|
* post-compilation step (inside lib/).
|
||||||
|
*/
|
||||||
|
export const WALLET_CORE_IMPLEMENTATION_VERSION =
|
||||||
|
"__WALLET_CORE_IMPLEMENTATION_VERSION__";
|
||||||
|
@ -29,7 +29,7 @@ import {
|
|||||||
AcceptExchangeTosRequest,
|
AcceptExchangeTosRequest,
|
||||||
AcceptManualWithdrawalRequest,
|
AcceptManualWithdrawalRequest,
|
||||||
AcceptManualWithdrawalResult,
|
AcceptManualWithdrawalResult,
|
||||||
AcceptTipRequest,
|
AcceptRewardRequest,
|
||||||
AcceptTipResponse,
|
AcceptTipResponse,
|
||||||
AcceptWithdrawalResponse,
|
AcceptWithdrawalResponse,
|
||||||
AddExchangeRequest,
|
AddExchangeRequest,
|
||||||
@ -85,8 +85,8 @@ import {
|
|||||||
PreparePeerPushCreditRequest,
|
PreparePeerPushCreditRequest,
|
||||||
PreparePeerPushCreditResponse,
|
PreparePeerPushCreditResponse,
|
||||||
PrepareRefundRequest,
|
PrepareRefundRequest,
|
||||||
PrepareTipRequest,
|
PrepareRewardRequest as PrepareRewardRequest,
|
||||||
PrepareTipResult,
|
PrepareTipResult as PrepareRewardResult,
|
||||||
RecoveryLoadRequest,
|
RecoveryLoadRequest,
|
||||||
RetryTransactionRequest,
|
RetryTransactionRequest,
|
||||||
SetCoinSuspendedRequest,
|
SetCoinSuspendedRequest,
|
||||||
@ -178,8 +178,8 @@ export enum WalletApiOperation {
|
|||||||
DumpCoins = "dumpCoins",
|
DumpCoins = "dumpCoins",
|
||||||
SetCoinSuspended = "setCoinSuspended",
|
SetCoinSuspended = "setCoinSuspended",
|
||||||
ForceRefresh = "forceRefresh",
|
ForceRefresh = "forceRefresh",
|
||||||
PrepareTip = "prepareTip",
|
PrepareReward = "prepareReward",
|
||||||
AcceptTip = "acceptTip",
|
AcceptReward = "acceptReward",
|
||||||
ExportBackup = "exportBackup",
|
ExportBackup = "exportBackup",
|
||||||
AddBackupProvider = "addBackupProvider",
|
AddBackupProvider = "addBackupProvider",
|
||||||
RemoveBackupProvider = "removeBackupProvider",
|
RemoveBackupProvider = "removeBackupProvider",
|
||||||
@ -507,23 +507,23 @@ export type StartRefundQueryOp = {
|
|||||||
response: EmptyObject;
|
response: EmptyObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
// group: Tipping
|
// group: Rewards
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query and store information about a tip.
|
* Query and store information about a reward.
|
||||||
*/
|
*/
|
||||||
export type PrepareTipOp = {
|
export type PrepareTipOp = {
|
||||||
op: WalletApiOperation.PrepareTip;
|
op: WalletApiOperation.PrepareReward;
|
||||||
request: PrepareTipRequest;
|
request: PrepareRewardRequest;
|
||||||
response: PrepareTipResult;
|
response: PrepareRewardResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept a tip.
|
* Accept a reward.
|
||||||
*/
|
*/
|
||||||
export type AcceptTipOp = {
|
export type AcceptTipOp = {
|
||||||
op: WalletApiOperation.AcceptTip;
|
op: WalletApiOperation.AcceptReward;
|
||||||
request: AcceptTipRequest;
|
request: AcceptRewardRequest;
|
||||||
response: AcceptTipResponse;
|
response: AcceptTipResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1023,8 +1023,8 @@ export type WalletOperations = {
|
|||||||
[WalletApiOperation.ForceRefresh]: ForceRefreshOp;
|
[WalletApiOperation.ForceRefresh]: ForceRefreshOp;
|
||||||
[WalletApiOperation.DeleteTransaction]: DeleteTransactionOp;
|
[WalletApiOperation.DeleteTransaction]: DeleteTransactionOp;
|
||||||
[WalletApiOperation.RetryTransaction]: RetryTransactionOp;
|
[WalletApiOperation.RetryTransaction]: RetryTransactionOp;
|
||||||
[WalletApiOperation.PrepareTip]: PrepareTipOp;
|
[WalletApiOperation.PrepareReward]: PrepareTipOp;
|
||||||
[WalletApiOperation.AcceptTip]: AcceptTipOp;
|
[WalletApiOperation.AcceptReward]: AcceptTipOp;
|
||||||
[WalletApiOperation.StartRefundQueryForUri]: StartRefundQueryForUriOp;
|
[WalletApiOperation.StartRefundQueryForUri]: StartRefundQueryForUriOp;
|
||||||
[WalletApiOperation.StartRefundQuery]: StartRefundQueryOp;
|
[WalletApiOperation.StartRefundQuery]: StartRefundQueryOp;
|
||||||
[WalletApiOperation.ListCurrencies]: ListCurrenciesOp;
|
[WalletApiOperation.ListCurrencies]: ListCurrenciesOp;
|
||||||
|
@ -93,7 +93,7 @@ import {
|
|||||||
codecForPreparePeerPullPaymentRequest,
|
codecForPreparePeerPullPaymentRequest,
|
||||||
codecForPreparePeerPushCreditRequest,
|
codecForPreparePeerPushCreditRequest,
|
||||||
codecForPrepareRefundRequest,
|
codecForPrepareRefundRequest,
|
||||||
codecForPrepareTipRequest,
|
codecForPrepareRewardRequest,
|
||||||
codecForResumeTransaction,
|
codecForResumeTransaction,
|
||||||
codecForRetryTransactionRequest,
|
codecForRetryTransactionRequest,
|
||||||
codecForSetCoinSuspendedRequest,
|
codecForSetCoinSuspendedRequest,
|
||||||
@ -249,10 +249,10 @@ import {
|
|||||||
} from "./operations/testing.js";
|
} from "./operations/testing.js";
|
||||||
import {
|
import {
|
||||||
acceptTip,
|
acceptTip,
|
||||||
computeTipTransactionStatus,
|
computeRewardTransactionStatus,
|
||||||
prepareTip,
|
prepareTip,
|
||||||
processTip,
|
processTip,
|
||||||
} from "./operations/tip.js";
|
} from "./operations/reward.js";
|
||||||
import {
|
import {
|
||||||
abortTransaction,
|
abortTransaction,
|
||||||
deleteTransaction,
|
deleteTransaction,
|
||||||
@ -300,6 +300,7 @@ import {
|
|||||||
import { TimerAPI, TimerGroup } from "./util/timer.js";
|
import { TimerAPI, TimerGroup } from "./util/timer.js";
|
||||||
import {
|
import {
|
||||||
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
|
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
|
||||||
|
WALLET_CORE_IMPLEMENTATION_VERSION,
|
||||||
WALLET_EXCHANGE_PROTOCOL_VERSION,
|
WALLET_EXCHANGE_PROTOCOL_VERSION,
|
||||||
WALLET_MERCHANT_PROTOCOL_VERSION,
|
WALLET_MERCHANT_PROTOCOL_VERSION,
|
||||||
} from "./versions.js";
|
} from "./versions.js";
|
||||||
@ -328,7 +329,7 @@ async function callOperationHandler(
|
|||||||
return await processRefreshGroup(ws, pending.refreshGroupId);
|
return await processRefreshGroup(ws, pending.refreshGroupId);
|
||||||
case PendingTaskType.Withdraw:
|
case PendingTaskType.Withdraw:
|
||||||
return await processWithdrawalGroup(ws, pending.withdrawalGroupId);
|
return await processWithdrawalGroup(ws, pending.withdrawalGroupId);
|
||||||
case PendingTaskType.TipPickup:
|
case PendingTaskType.RewardPickup:
|
||||||
return await processTip(ws, pending.tipId);
|
return await processTip(ws, pending.tipId);
|
||||||
case PendingTaskType.Purchase:
|
case PendingTaskType.Purchase:
|
||||||
return await processPurchase(ws, pending.proposalId);
|
return await processPurchase(ws, pending.proposalId);
|
||||||
@ -1016,12 +1017,6 @@ export async function getClientFromWalletState(
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const __VERSION__: string;
|
|
||||||
declare const __GIT_HASH__: string;
|
|
||||||
|
|
||||||
const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "dev";
|
|
||||||
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the "wallet-core" API.
|
* Implementation of the "wallet-core" API.
|
||||||
*/
|
*/
|
||||||
@ -1355,9 +1350,9 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
|||||||
refreshGroupId,
|
refreshGroupId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case WalletApiOperation.PrepareTip: {
|
case WalletApiOperation.PrepareReward: {
|
||||||
const req = codecForPrepareTipRequest().decode(payload);
|
const req = codecForPrepareRewardRequest().decode(payload);
|
||||||
return await prepareTip(ws, req.talerTipUri);
|
return await prepareTip(ws, req.talerRewardUri);
|
||||||
}
|
}
|
||||||
case WalletApiOperation.StartRefundQueryForUri: {
|
case WalletApiOperation.StartRefundQueryForUri: {
|
||||||
const req = codecForPrepareRefundRequest().decode(payload);
|
const req = codecForPrepareRefundRequest().decode(payload);
|
||||||
@ -1375,9 +1370,9 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
|||||||
await startQueryRefund(ws, txIdParsed.proposalId);
|
await startQueryRefund(ws, txIdParsed.proposalId);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case WalletApiOperation.AcceptTip: {
|
case WalletApiOperation.AcceptReward: {
|
||||||
const req = codecForAcceptTipRequest().decode(payload);
|
const req = codecForAcceptTipRequest().decode(payload);
|
||||||
return await acceptTip(ws, req.walletTipId);
|
return await acceptTip(ws, req.walletRewardId);
|
||||||
}
|
}
|
||||||
case WalletApiOperation.ExportBackupPlain: {
|
case WalletApiOperation.ExportBackupPlain: {
|
||||||
return exportBackup(ws);
|
return exportBackup(ws);
|
||||||
@ -1590,15 +1585,15 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getVersion(ws: InternalWalletState): WalletCoreVersion {
|
export function getVersion(ws: InternalWalletState): WalletCoreVersion {
|
||||||
const version: WalletCoreVersion = {
|
const result: WalletCoreVersion = {
|
||||||
hash: GIT_HASH,
|
hash: undefined,
|
||||||
version: VERSION,
|
version: WALLET_CORE_IMPLEMENTATION_VERSION,
|
||||||
exchange: WALLET_EXCHANGE_PROTOCOL_VERSION,
|
exchange: WALLET_EXCHANGE_PROTOCOL_VERSION,
|
||||||
merchant: WALLET_MERCHANT_PROTOCOL_VERSION,
|
merchant: WALLET_MERCHANT_PROTOCOL_VERSION,
|
||||||
bank: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
|
bank: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
|
||||||
devMode: false,
|
devMode: false,
|
||||||
};
|
};
|
||||||
return version;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1889,12 +1884,12 @@ class InternalWalletStateImpl implements InternalWalletState {
|
|||||||
}
|
}
|
||||||
return computeRefreshTransactionState(rec);
|
return computeRefreshTransactionState(rec);
|
||||||
}
|
}
|
||||||
case TransactionType.Tip: {
|
case TransactionType.Reward: {
|
||||||
const rec = await tx.tips.get(parsedTxId.walletTipId);
|
const rec = await tx.rewards.get(parsedTxId.walletRewardId);
|
||||||
if (!rec) {
|
if (!rec) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return computeTipTransactionStatus(rec);
|
return computeRewardTransactionStatus(rec);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
assertUnreachable(parsedTxId);
|
assertUnreachable(parsedTxId);
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
"target": "ES2017",
|
"target": "ES2017",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node16",
|
"moduleResolution": "Node16",
|
||||||
|
"resolveJsonModule": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"lib": ["es6"],
|
"lib": ["es6"],
|
||||||
|
"resolvePackageJsonImports": true,
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
@ -31,5 +33,8 @@
|
|||||||
"path": "../taler-util/"
|
"path": "../taler-util/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": ["src/**/*"]
|
"include": [
|
||||||
|
"src/**/*",
|
||||||
|
"src/*.json"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ const talerUriActionToPageName: {
|
|||||||
} = {
|
} = {
|
||||||
[TalerUriAction.Withdraw]: "ctaWithdraw",
|
[TalerUriAction.Withdraw]: "ctaWithdraw",
|
||||||
[TalerUriAction.Pay]: "ctaPay",
|
[TalerUriAction.Pay]: "ctaPay",
|
||||||
[TalerUriAction.Tip]: "ctaTips",
|
[TalerUriAction.Reward]: "ctaTips",
|
||||||
[TalerUriAction.Refund]: "ctaRefund",
|
[TalerUriAction.Refund]: "ctaRefund",
|
||||||
[TalerUriAction.PayPull]: "ctaInvoicePay",
|
[TalerUriAction.PayPull]: "ctaInvoicePay",
|
||||||
[TalerUriAction.PayPush]: "ctaTransferPickup",
|
[TalerUriAction.PayPush]: "ctaTransferPickup",
|
||||||
|
@ -134,7 +134,7 @@ export function HistoryItem(props: { tx: Transaction }): VNode {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case TransactionType.Tip:
|
case TransactionType.Reward:
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
id={tx.transactionId}
|
id={tx.transactionId}
|
||||||
|
@ -23,7 +23,7 @@ import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
|||||||
import { Props, State } from "./index.js";
|
import { Props, State } from "./index.js";
|
||||||
|
|
||||||
export function useComponentState({
|
export function useComponentState({
|
||||||
talerTipUri,
|
talerTipUri: talerRewardUri,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}: Props): State {
|
}: Props): State {
|
||||||
@ -31,9 +31,9 @@ export function useComponentState({
|
|||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
const { pushAlertOnError } = useAlertContext();
|
const { pushAlertOnError } = useAlertContext();
|
||||||
const tipInfo = useAsyncAsHook(async () => {
|
const tipInfo = useAsyncAsHook(async () => {
|
||||||
if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
|
if (!talerRewardUri) throw Error("ERROR_NO-URI-FOR-TIP");
|
||||||
const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {
|
const tip = await api.wallet.call(WalletApiOperation.PrepareReward, {
|
||||||
talerTipUri,
|
talerRewardUri,
|
||||||
});
|
});
|
||||||
return { tip };
|
return { tip };
|
||||||
});
|
});
|
||||||
@ -63,8 +63,8 @@ export function useComponentState({
|
|||||||
const { tip } = tipInfo.response;
|
const { tip } = tipInfo.response;
|
||||||
|
|
||||||
const doAccept = async (): Promise<void> => {
|
const doAccept = async (): Promise<void> => {
|
||||||
const res = await api.wallet.call(WalletApiOperation.AcceptTip, {
|
const res = await api.wallet.call(WalletApiOperation.AcceptReward, {
|
||||||
walletTipId: tip.walletTipId,
|
walletRewardId: tip.walletRewardId,
|
||||||
});
|
});
|
||||||
|
|
||||||
//FIX: this may not be seen since we are moving to the success also
|
//FIX: this may not be seen since we are moving to the success also
|
||||||
@ -75,7 +75,7 @@ export function useComponentState({
|
|||||||
const baseInfo = {
|
const baseInfo = {
|
||||||
merchantBaseUrl: tip.merchantBaseUrl,
|
merchantBaseUrl: tip.merchantBaseUrl,
|
||||||
exchangeBaseUrl: tip.exchangeBaseUrl,
|
exchangeBaseUrl: tip.exchangeBaseUrl,
|
||||||
amount: Amounts.parseOrThrow(tip.tipAmountEffective),
|
amount: Amounts.parseOrThrow(tip.rewardAmountEffective),
|
||||||
error: undefined,
|
error: undefined,
|
||||||
cancel: {
|
cancel: {
|
||||||
onClick: pushAlertOnError(onCancel),
|
onClick: pushAlertOnError(onCancel),
|
@ -62,17 +62,17 @@ describe("Tip CTA states", () => {
|
|||||||
it("should be ready for accepting the tip", async () => {
|
it("should be ready for accepting the tip", async () => {
|
||||||
const { handler, TestingContext } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareReward, undefined, {
|
||||||
accepted: false,
|
accepted: false,
|
||||||
exchangeBaseUrl: "exchange url",
|
exchangeBaseUrl: "exchange url",
|
||||||
merchantBaseUrl: "merchant url",
|
merchantBaseUrl: "merchant url",
|
||||||
tipAmountEffective: "EUR:1",
|
rewardAmountEffective: "EUR:1",
|
||||||
walletTipId: "tip_id",
|
walletRewardId: "tip_id",
|
||||||
transactionId: "txn:tip:ABC1234",
|
transactionId: "txn:tip:ABC1234",
|
||||||
expirationTimestamp: {
|
expirationTimestamp: {
|
||||||
t_s: 1,
|
t_s: 1,
|
||||||
},
|
},
|
||||||
tipAmountRaw: "",
|
rewardAmountRaw: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
@ -100,23 +100,23 @@ describe("Tip CTA states", () => {
|
|||||||
expect(state.exchangeBaseUrl).eq("exchange url");
|
expect(state.exchangeBaseUrl).eq("exchange url");
|
||||||
if (state.accept.onClick === undefined) expect.fail();
|
if (state.accept.onClick === undefined) expect.fail();
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.AcceptTip);
|
handler.addWalletCallResponse(WalletApiOperation.AcceptReward);
|
||||||
state.accept.onClick();
|
state.accept.onClick();
|
||||||
|
|
||||||
handler.addWalletCallResponse(
|
handler.addWalletCallResponse(
|
||||||
WalletApiOperation.PrepareTip,
|
WalletApiOperation.PrepareReward,
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
accepted: true,
|
accepted: true,
|
||||||
exchangeBaseUrl: "exchange url",
|
exchangeBaseUrl: "exchange url",
|
||||||
merchantBaseUrl: "merchant url",
|
merchantBaseUrl: "merchant url",
|
||||||
tipAmountEffective: "EUR:1",
|
rewardAmountEffective: "EUR:1",
|
||||||
walletTipId: "tip_id",
|
walletRewardId: "tip_id",
|
||||||
transactionId: "txn:tip:ABC1234",
|
transactionId: "txn:tip:ABC1234",
|
||||||
expirationTimestamp: {
|
expirationTimestamp: {
|
||||||
t_s: 1,
|
t_s: 1,
|
||||||
},
|
},
|
||||||
tipAmountRaw: "",
|
rewardAmountRaw: "",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -137,17 +137,17 @@ describe("Tip CTA states", () => {
|
|||||||
|
|
||||||
it.skip("should be ignored after clicking the ignore button", async () => {
|
it.skip("should be ignored after clicking the ignore button", async () => {
|
||||||
const { handler, TestingContext } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareReward, undefined, {
|
||||||
exchangeBaseUrl: "exchange url",
|
exchangeBaseUrl: "exchange url",
|
||||||
merchantBaseUrl: "merchant url",
|
merchantBaseUrl: "merchant url",
|
||||||
tipAmountEffective: "EUR:1",
|
rewardAmountEffective: "EUR:1",
|
||||||
walletTipId: "tip_id",
|
walletRewardId: "tip_id",
|
||||||
transactionId: "txn:tip:ABC1234",
|
transactionId: "txn:tip:ABC1234",
|
||||||
accepted: false,
|
accepted: false,
|
||||||
expirationTimestamp: {
|
expirationTimestamp: {
|
||||||
t_s: 1,
|
t_s: 1,
|
||||||
},
|
},
|
||||||
tipAmountRaw: "",
|
rewardAmountRaw: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
@ -184,17 +184,17 @@ describe("Tip CTA states", () => {
|
|||||||
it("should render accepted if the tip has been used previously", async () => {
|
it("should render accepted if the tip has been used previously", async () => {
|
||||||
const { handler, TestingContext } = createWalletApiMock();
|
const { handler, TestingContext } = createWalletApiMock();
|
||||||
|
|
||||||
handler.addWalletCallResponse(WalletApiOperation.PrepareTip, undefined, {
|
handler.addWalletCallResponse(WalletApiOperation.PrepareReward, undefined, {
|
||||||
accepted: true,
|
accepted: true,
|
||||||
exchangeBaseUrl: "exchange url",
|
exchangeBaseUrl: "exchange url",
|
||||||
merchantBaseUrl: "merchant url",
|
merchantBaseUrl: "merchant url",
|
||||||
tipAmountEffective: "EUR:1",
|
rewardAmountEffective: "EUR:1",
|
||||||
walletTipId: "tip_id",
|
walletRewardId: "tip_id",
|
||||||
transactionId: "txn:tip:ABC1234",
|
transactionId: "txn:tip:ABC1234",
|
||||||
expirationTimestamp: {
|
expirationTimestamp: {
|
||||||
t_s: 1,
|
t_s: 1,
|
||||||
},
|
},
|
||||||
tipAmountRaw: "",
|
rewardAmountRaw: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const props: Props = {
|
const props: Props = {
|
@ -22,7 +22,7 @@
|
|||||||
export * as a1 from "./Deposit/stories.jsx";
|
export * as a1 from "./Deposit/stories.jsx";
|
||||||
export * as a3 from "./Payment/stories.jsx";
|
export * as a3 from "./Payment/stories.jsx";
|
||||||
export * as a4 from "./Refund/stories.jsx";
|
export * as a4 from "./Refund/stories.jsx";
|
||||||
export * as a5 from "./Tip/stories.jsx";
|
export * as a5 from "./Reward/stories.js";
|
||||||
export * as a6 from "./Withdraw/stories.jsx";
|
export * as a6 from "./Withdraw/stories.jsx";
|
||||||
export * as a8 from "./InvoiceCreate/stories.js";
|
export * as a8 from "./InvoiceCreate/stories.js";
|
||||||
export * as a9 from "./InvoicePay/stories.js";
|
export * as a9 from "./InvoicePay/stories.js";
|
||||||
|
@ -279,7 +279,7 @@ function openWalletURIFromPopup(uri: TalerUri): void {
|
|||||||
`static/wallet.html#/cta/pay?talerUri=${encodeURIComponent(talerUri)}`,
|
`static/wallet.html#/cta/pay?talerUri=${encodeURIComponent(talerUri)}`,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case TalerUriAction.Tip:
|
case TalerUriAction.Reward:
|
||||||
url = chrome.runtime.getURL(
|
url = chrome.runtime.getURL(
|
||||||
`static/wallet.html#/cta/tip?talerUri=${encodeURIComponent(talerUri)}`,
|
`static/wallet.html#/cta/tip?talerUri=${encodeURIComponent(talerUri)}`,
|
||||||
);
|
);
|
||||||
|
@ -65,7 +65,7 @@ function ContentByUriType({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case TalerUriAction.Tip:
|
case TalerUriAction.Reward:
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -66,7 +66,7 @@ export function AddNewActionView({ onCancel }: Props): VNode {
|
|||||||
return <i18n.Translate>Open pay page</i18n.Translate>;
|
return <i18n.Translate>Open pay page</i18n.Translate>;
|
||||||
case TalerUriAction.Refund:
|
case TalerUriAction.Refund:
|
||||||
return <i18n.Translate>Open refund page</i18n.Translate>;
|
return <i18n.Translate>Open refund page</i18n.Translate>;
|
||||||
case TalerUriAction.Tip:
|
case TalerUriAction.Reward:
|
||||||
return <i18n.Translate>Open tip page</i18n.Translate>;
|
return <i18n.Translate>Open tip page</i18n.Translate>;
|
||||||
case TalerUriAction.Withdraw:
|
case TalerUriAction.Withdraw:
|
||||||
return <i18n.Translate>Open withdraw page</i18n.Translate>;
|
return <i18n.Translate>Open withdraw page</i18n.Translate>;
|
||||||
|
@ -58,7 +58,7 @@ import { PaymentPage } from "../cta/Payment/index.js";
|
|||||||
import { PaymentTemplatePage } from "../cta/PaymentTemplate/index.js";
|
import { PaymentTemplatePage } from "../cta/PaymentTemplate/index.js";
|
||||||
import { RecoveryPage } from "../cta/Recovery/index.js";
|
import { RecoveryPage } from "../cta/Recovery/index.js";
|
||||||
import { RefundPage } from "../cta/Refund/index.js";
|
import { RefundPage } from "../cta/Refund/index.js";
|
||||||
import { TipPage } from "../cta/Tip/index.js";
|
import { TipPage } from "../cta/Reward/index.js";
|
||||||
import { TransferCreatePage } from "../cta/TransferCreate/index.js";
|
import { TransferCreatePage } from "../cta/TransferCreate/index.js";
|
||||||
import { TransferPickupPage } from "../cta/TransferPickup/index.js";
|
import { TransferPickupPage } from "../cta/TransferPickup/index.js";
|
||||||
import {
|
import {
|
||||||
|
@ -34,7 +34,7 @@ import {
|
|||||||
TransactionPeerPushDebit,
|
TransactionPeerPushDebit,
|
||||||
TransactionRefresh,
|
TransactionRefresh,
|
||||||
TransactionRefund,
|
TransactionRefund,
|
||||||
TransactionTip,
|
TransactionReward,
|
||||||
TransactionType,
|
TransactionType,
|
||||||
TransactionWithdrawal,
|
TransactionWithdrawal,
|
||||||
WithdrawalType,
|
WithdrawalType,
|
||||||
@ -113,9 +113,9 @@ const exampleData = {
|
|||||||
} as TransactionRefresh,
|
} as TransactionRefresh,
|
||||||
tip: {
|
tip: {
|
||||||
...commonTransaction(),
|
...commonTransaction(),
|
||||||
type: TransactionType.Tip,
|
type: TransactionType.Reward,
|
||||||
merchantBaseUrl: "http://ads.merchant.taler.net/",
|
merchantBaseUrl: "http://ads.merchant.taler.net/",
|
||||||
} as TransactionTip,
|
} as TransactionReward,
|
||||||
refund: {
|
refund: {
|
||||||
...commonTransaction(),
|
...commonTransaction(),
|
||||||
type: TransactionType.Refund,
|
type: TransactionType.Refund,
|
||||||
|
@ -37,7 +37,7 @@ import {
|
|||||||
TransactionPeerPushDebit,
|
TransactionPeerPushDebit,
|
||||||
TransactionRefresh,
|
TransactionRefresh,
|
||||||
TransactionRefund,
|
TransactionRefund,
|
||||||
TransactionTip,
|
TransactionReward,
|
||||||
TransactionType,
|
TransactionType,
|
||||||
TransactionWithdrawal,
|
TransactionWithdrawal,
|
||||||
WithdrawalDetails,
|
WithdrawalDetails,
|
||||||
@ -138,7 +138,7 @@ const exampleData = {
|
|||||||
} as TransactionRefresh,
|
} as TransactionRefresh,
|
||||||
tip: {
|
tip: {
|
||||||
...commonTransaction,
|
...commonTransaction,
|
||||||
type: TransactionType.Tip,
|
type: TransactionType.Reward,
|
||||||
// merchant: {
|
// merchant: {
|
||||||
// name: "the merchant",
|
// name: "the merchant",
|
||||||
// logo: merchantIcon,
|
// logo: merchantIcon,
|
||||||
@ -146,7 +146,7 @@ const exampleData = {
|
|||||||
// email: "contact@merchant.taler",
|
// email: "contact@merchant.taler",
|
||||||
// },
|
// },
|
||||||
merchantBaseUrl: "http://merchant.taler",
|
merchantBaseUrl: "http://merchant.taler",
|
||||||
} as TransactionTip,
|
} as TransactionReward,
|
||||||
refund: {
|
refund: {
|
||||||
...commonTransaction,
|
...commonTransaction,
|
||||||
type: TransactionType.Refund,
|
type: TransactionType.Refund,
|
||||||
|
@ -745,7 +745,7 @@ export function TransactionView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transaction.type === TransactionType.Tip) {
|
if (transaction.type === TransactionType.Reward) {
|
||||||
return (
|
return (
|
||||||
<TransactionTemplate
|
<TransactionTemplate
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
function setupLiveReload(): void {
|
function setupLiveReload(): void {
|
||||||
const protocol = window.location.protocol === "http:" ? "ws:" : "wss:";
|
const protocol = window.location.protocol === "http:" ? "ws:" : "wss:";
|
||||||
const port = window.location.protocol === "http:" ? "8080" : "8081";
|
const ws = new WebSocket(`${protocol}//${window.location.hostname}:${window.location.port}/ws`);
|
||||||
const ws = new WebSocket(`${protocol}//localhost:${port}/ws`);
|
|
||||||
|
|
||||||
ws.addEventListener("message", (message) => {
|
ws.addEventListener("message", (message) => {
|
||||||
try {
|
try {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
lockfileVersion: '6.0'
|
lockfileVersion: '6.1'
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
|
Loading…
Reference in New Issue
Block a user