implement fulfillment_message and make fulfillment_url optional
This commit is contained in:
parent
69c4950762
commit
0e88ef9bd2
@ -349,7 +349,7 @@ export class GlobalTestState {
|
|||||||
args: string[],
|
args: string[],
|
||||||
logName: string,
|
logName: string,
|
||||||
): ProcessWrapper {
|
): ProcessWrapper {
|
||||||
console.log(`spawning process (${command})`);
|
console.log(`spawning process ${command} with arguments ${args})`);
|
||||||
const proc = spawn(command, args, {
|
const proc = spawn(command, args, {
|
||||||
stdio: ["inherit", "pipe", "pipe"],
|
stdio: ["inherit", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
|
@ -79,3 +79,12 @@ export function str(stringSeq: TemplateStringsArray, ...values: any[]): string {
|
|||||||
.fetch(...values);
|
.fetch(...values);
|
||||||
return tr;
|
return tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an internationalized string (based on the globally set, current language)
|
||||||
|
* from a JSON object. Fall back to the default language of the JSON object
|
||||||
|
* if no match exists.
|
||||||
|
*/
|
||||||
|
export function getJsonI18n<K extends string>(obj: Record<K, string>, key: K): string {
|
||||||
|
return obj[key];
|
||||||
|
}
|
@ -513,17 +513,6 @@ async function recordConfirmPay(
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextUrl(contractData: WalletContractData): string {
|
|
||||||
const f = contractData.fulfillmentUrl;
|
|
||||||
if (f.startsWith("http://") || f.startsWith("https://")) {
|
|
||||||
const fu = new URL(contractData.fulfillmentUrl);
|
|
||||||
fu.searchParams.set("order_id", contractData.orderId);
|
|
||||||
return fu.href;
|
|
||||||
} else {
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function incrementProposalRetry(
|
async function incrementProposalRetry(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
proposalId: string,
|
proposalId: string,
|
||||||
@ -642,7 +631,10 @@ async function processDownloadProposalImpl(
|
|||||||
const httpResponse = await ws.http.postJson(orderClaimUrl, requestBody, {
|
const httpResponse = await ws.http.postJson(orderClaimUrl, requestBody, {
|
||||||
timeout: getProposalRequestTimeout(proposal),
|
timeout: getProposalRequestTimeout(proposal),
|
||||||
});
|
});
|
||||||
const r = await readSuccessResponseJsonOrErrorCode(httpResponse, codecForProposal());
|
const r = await readSuccessResponseJsonOrErrorCode(
|
||||||
|
httpResponse,
|
||||||
|
codecForProposal(),
|
||||||
|
);
|
||||||
if (r.isError) {
|
if (r.isError) {
|
||||||
switch (r.talerErrorResponse.code) {
|
switch (r.talerErrorResponse.code) {
|
||||||
case TalerErrorCode.ORDERS_ALREADY_CLAIMED:
|
case TalerErrorCode.ORDERS_ALREADY_CLAIMED:
|
||||||
@ -652,7 +644,8 @@ async function processDownloadProposalImpl(
|
|||||||
{
|
{
|
||||||
orderId: proposal.orderId,
|
orderId: proposal.orderId,
|
||||||
claimUrl: orderClaimUrl,
|
claimUrl: orderClaimUrl,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
throwUnexpectedRequestError(httpResponse, r.talerErrorResponse);
|
throwUnexpectedRequestError(httpResponse, r.talerErrorResponse);
|
||||||
}
|
}
|
||||||
@ -723,8 +716,9 @@ async function processDownloadProposalImpl(
|
|||||||
contractTermsRaw: JSON.stringify(proposalResp.contract_terms),
|
contractTermsRaw: JSON.stringify(proposalResp.contract_terms),
|
||||||
};
|
};
|
||||||
if (
|
if (
|
||||||
fulfillmentUrl.startsWith("http://") ||
|
fulfillmentUrl &&
|
||||||
fulfillmentUrl.startsWith("https://")
|
(fulfillmentUrl.startsWith("http://") ||
|
||||||
|
fulfillmentUrl.startsWith("https://"))
|
||||||
) {
|
) {
|
||||||
const differentPurchase = await tx.getIndexed(
|
const differentPurchase = await tx.getIndexed(
|
||||||
Stores.purchases.fulfillmentUrlIndex,
|
Stores.purchases.fulfillmentUrlIndex,
|
||||||
@ -968,15 +962,9 @@ export async function submitPay(
|
|||||||
await storePayReplaySuccess(ws, proposalId, sessionId);
|
await storePayReplaySuccess(ws, proposalId, sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextUrl = getNextUrl(purchase.contractData);
|
|
||||||
ws.cachedNextUrl[purchase.contractData.fulfillmentUrl] = {
|
|
||||||
nextUrl,
|
|
||||||
lastSessionId: sessionId,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: ConfirmPayResultType.Done,
|
type: ConfirmPayResultType.Done,
|
||||||
nextUrl,
|
contractTerms: JSON.parse(purchase.contractTermsRaw),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1089,7 +1077,6 @@ export async function preparePayForUri(
|
|||||||
contractTerms: JSON.parse(purchase.contractTermsRaw),
|
contractTerms: JSON.parse(purchase.contractTermsRaw),
|
||||||
contractTermsHash: purchase.contractData.contractTermsHash,
|
contractTermsHash: purchase.contractData.contractTermsHash,
|
||||||
paid: true,
|
paid: true,
|
||||||
nextUrl: r.nextUrl,
|
|
||||||
amountRaw: Amounts.stringify(purchase.contractData.amount),
|
amountRaw: Amounts.stringify(purchase.contractData.amount),
|
||||||
amountEffective: Amounts.stringify(purchase.payCostInfo.totalCost),
|
amountEffective: Amounts.stringify(purchase.payCostInfo.totalCost),
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { HttpRequestLibrary } from "../util/http";
|
import { HttpRequestLibrary } from "../util/http";
|
||||||
import { NextUrlResult, BalancesResponse } from "../types/walletTypes";
|
import { BalancesResponse } from "../types/walletTypes";
|
||||||
import { CryptoApi, CryptoWorkerFactory } from "../crypto/workers/cryptoApi";
|
import { CryptoApi, CryptoWorkerFactory } from "../crypto/workers/cryptoApi";
|
||||||
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "../util/asyncMemo";
|
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "../util/asyncMemo";
|
||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
@ -32,7 +32,6 @@ export const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
|
|||||||
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
|
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
|
||||||
|
|
||||||
export class InternalWalletState {
|
export class InternalWalletState {
|
||||||
cachedNextUrl: { [fulfillmentUrl: string]: NextUrlResult } = {};
|
|
||||||
memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
|
||||||
memoGetPending: AsyncOpMemoSingle<
|
memoGetPending: AsyncOpMemoSingle<
|
||||||
|
@ -35,7 +35,7 @@ import {
|
|||||||
PaymentStatus,
|
PaymentStatus,
|
||||||
WithdrawalType,
|
WithdrawalType,
|
||||||
WithdrawalDetails,
|
WithdrawalDetails,
|
||||||
PaymentShortInfo,
|
OrderShortInfo,
|
||||||
} from "../types/transactions";
|
} from "../types/transactions";
|
||||||
import { getFundingPaytoUris } from "./reserves";
|
import { getFundingPaytoUris } from "./reserves";
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ export async function getTransactions(
|
|||||||
if (!proposal) {
|
if (!proposal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const info: PaymentShortInfo = {
|
const info: OrderShortInfo = {
|
||||||
fulfillmentUrl: pr.contractData.fulfillmentUrl,
|
fulfillmentUrl: pr.contractData.fulfillmentUrl,
|
||||||
merchant: pr.contractData.merchant,
|
merchant: pr.contractData.merchant,
|
||||||
orderId: pr.contractData.orderId,
|
orderId: pr.contractData.orderId,
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
ExchangeSignKeyJson,
|
ExchangeSignKeyJson,
|
||||||
MerchantInfo,
|
MerchantInfo,
|
||||||
Product,
|
Product,
|
||||||
|
InternationalizedString,
|
||||||
} from "./talerTypes";
|
} from "./talerTypes";
|
||||||
|
|
||||||
import { Index, Store } from "../util/query";
|
import { Index, Store } from "../util/query";
|
||||||
@ -1270,8 +1271,10 @@ export interface AllowedExchangeInfo {
|
|||||||
export interface WalletContractData {
|
export interface WalletContractData {
|
||||||
products?: Product[];
|
products?: Product[];
|
||||||
summaryI18n: { [lang_tag: string]: string } | undefined;
|
summaryI18n: { [lang_tag: string]: string } | undefined;
|
||||||
fulfillmentUrl: string;
|
fulfillmentUrl?: string;
|
||||||
contractTermsHash: string;
|
contractTermsHash: string;
|
||||||
|
fulfillmentMessage?: string;
|
||||||
|
fulfillmentMessageI18n?: InternationalizedString;
|
||||||
merchantSig: string;
|
merchantSig: string;
|
||||||
merchantPub: string;
|
merchantPub: string;
|
||||||
merchant: MerchantInfo;
|
merchant: MerchantInfo;
|
||||||
|
@ -314,6 +314,10 @@ export interface Product {
|
|||||||
delivery_location?: string;
|
delivery_location?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InternationalizedString {
|
||||||
|
[lang_tag: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract terms from a merchant.
|
* Contract terms from a merchant.
|
||||||
*/
|
*/
|
||||||
@ -338,7 +342,7 @@ export class ContractTerms {
|
|||||||
*/
|
*/
|
||||||
summary: string;
|
summary: string;
|
||||||
|
|
||||||
summary_i18n?: { [lang_tag: string]: string };
|
summary_i18n?: InternationalizedString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nonce used to ensure freshness.
|
* Nonce used to ensure freshness.
|
||||||
@ -420,7 +424,17 @@ export class ContractTerms {
|
|||||||
* Fulfillment URL to view the product or
|
* Fulfillment URL to view the product or
|
||||||
* delivery status.
|
* delivery status.
|
||||||
*/
|
*/
|
||||||
fulfillment_url: string;
|
fulfillment_url?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plain text fulfillment message in the merchant's default language.
|
||||||
|
*/
|
||||||
|
fulfillment_message?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internationalized fulfillment messages.
|
||||||
|
*/
|
||||||
|
fulfillment_message_i18n?: InternationalizedString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Share of the wire fee that must be settled with one payment.
|
* Share of the wire fee that must be settled with one payment.
|
||||||
@ -1032,14 +1046,14 @@ export const codecForTax = (): Codec<Tax> =>
|
|||||||
.property("tax", codecForString())
|
.property("tax", codecForString())
|
||||||
.build("Tax");
|
.build("Tax");
|
||||||
|
|
||||||
export const codecForI18n = (): Codec<{ [lang_tag: string]: string }> =>
|
export const codecForInternationalizedString = (): Codec<InternationalizedString> =>
|
||||||
codecForMap(codecForString());
|
codecForMap(codecForString());
|
||||||
|
|
||||||
export const codecForProduct = (): Codec<Product> =>
|
export const codecForProduct = (): Codec<Product> =>
|
||||||
buildCodecForObject<Product>()
|
buildCodecForObject<Product>()
|
||||||
.property("product_id", codecOptional(codecForString()))
|
.property("product_id", codecOptional(codecForString()))
|
||||||
.property("description", codecForString())
|
.property("description", codecForString())
|
||||||
.property("description_i18n", codecOptional(codecForI18n()))
|
.property("description_i18n", codecOptional(codecForInternationalizedString()))
|
||||||
.property("quantity", codecOptional(codecForNumber()))
|
.property("quantity", codecOptional(codecForNumber()))
|
||||||
.property("unit", codecOptional(codecForString()))
|
.property("unit", codecOptional(codecForString()))
|
||||||
.property("price", codecOptional(codecForString()))
|
.property("price", codecOptional(codecForString()))
|
||||||
@ -1050,13 +1064,15 @@ export const codecForProduct = (): Codec<Product> =>
|
|||||||
export const codecForContractTerms = (): Codec<ContractTerms> =>
|
export const codecForContractTerms = (): Codec<ContractTerms> =>
|
||||||
buildCodecForObject<ContractTerms>()
|
buildCodecForObject<ContractTerms>()
|
||||||
.property("order_id", codecForString())
|
.property("order_id", codecForString())
|
||||||
.property("fulfillment_url", codecForString())
|
.property("fulfillment_url", codecOptional(codecForString()))
|
||||||
|
.property("fulfillment_message", codecOptional(codecForString()))
|
||||||
|
.property("fulfillment_message_i18n", codecOptional(codecForInternationalizedString()))
|
||||||
.property("merchant_base_url", codecForString())
|
.property("merchant_base_url", codecForString())
|
||||||
.property("h_wire", codecForString())
|
.property("h_wire", codecForString())
|
||||||
.property("auto_refund", codecOptional(codecForDuration))
|
.property("auto_refund", codecOptional(codecForDuration))
|
||||||
.property("wire_method", codecForString())
|
.property("wire_method", codecForString())
|
||||||
.property("summary", codecForString())
|
.property("summary", codecForString())
|
||||||
.property("summary_i18n", codecOptional(codecForI18n()))
|
.property("summary_i18n", codecOptional(codecForInternationalizedString()))
|
||||||
.property("nonce", codecForString())
|
.property("nonce", codecForString())
|
||||||
.property("amount", codecForString())
|
.property("amount", codecForString())
|
||||||
.property("auditors", codecForList(codecForAuditorHandle()))
|
.property("auditors", codecForList(codecForAuditorHandle()))
|
||||||
|
@ -25,7 +25,15 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { Timestamp } from "../util/time";
|
import { Timestamp } from "../util/time";
|
||||||
import { AmountString, Product } from "./talerTypes";
|
import {
|
||||||
|
AmountString,
|
||||||
|
Product,
|
||||||
|
InternationalizedString,
|
||||||
|
MerchantInfo,
|
||||||
|
codecForInternationalizedString,
|
||||||
|
codecForMerchantInfo,
|
||||||
|
codecForProduct,
|
||||||
|
} from "./talerTypes";
|
||||||
import {
|
import {
|
||||||
Codec,
|
Codec,
|
||||||
buildCodecForObject,
|
buildCodecForObject,
|
||||||
@ -202,7 +210,7 @@ export interface TransactionPayment extends TransactionCommon {
|
|||||||
/**
|
/**
|
||||||
* Additional information about the payment.
|
* Additional information about the payment.
|
||||||
*/
|
*/
|
||||||
info: PaymentShortInfo;
|
info: OrderShortInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How far did the wallet get with processing the payment?
|
* How far did the wallet get with processing the payment?
|
||||||
@ -220,7 +228,7 @@ export interface TransactionPayment extends TransactionCommon {
|
|||||||
amountEffective: AmountString;
|
amountEffective: AmountString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaymentShortInfo {
|
export interface OrderShortInfo {
|
||||||
/**
|
/**
|
||||||
* Order ID, uniquely identifies the order within a merchant instance
|
* Order ID, uniquely identifies the order within a merchant instance
|
||||||
*/
|
*/
|
||||||
@ -234,7 +242,7 @@ export interface PaymentShortInfo {
|
|||||||
/**
|
/**
|
||||||
* More information about the merchant
|
* More information about the merchant
|
||||||
*/
|
*/
|
||||||
merchant: any;
|
merchant: MerchantInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summary of the order, given by the merchant
|
* Summary of the order, given by the merchant
|
||||||
@ -244,7 +252,7 @@ export interface PaymentShortInfo {
|
|||||||
/**
|
/**
|
||||||
* Map from IETF BCP 47 language tags to localized summaries
|
* Map from IETF BCP 47 language tags to localized summaries
|
||||||
*/
|
*/
|
||||||
summary_i18n?: { [lang_tag: string]: string };
|
summary_i18n?: InternationalizedString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of products that are part of the order
|
* List of products that are part of the order
|
||||||
@ -254,7 +262,18 @@ export interface PaymentShortInfo {
|
|||||||
/**
|
/**
|
||||||
* URL of the fulfillment, given by the merchant
|
* URL of the fulfillment, given by the merchant
|
||||||
*/
|
*/
|
||||||
fulfillmentUrl: string;
|
fulfillmentUrl?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plain text message that should be shown to the user
|
||||||
|
* when the payment is complete.
|
||||||
|
*/
|
||||||
|
fulfillmentMessage?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translations of fulfillmentMessage.
|
||||||
|
*/
|
||||||
|
fulfillmentMessage_i18n?: InternationalizedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransactionRefund extends TransactionCommon {
|
interface TransactionRefund extends TransactionCommon {
|
||||||
@ -264,7 +283,7 @@ interface TransactionRefund extends TransactionCommon {
|
|||||||
refundedTransactionId: string;
|
refundedTransactionId: string;
|
||||||
|
|
||||||
// Additional information about the refunded payment
|
// Additional information about the refunded payment
|
||||||
info: PaymentShortInfo;
|
info: OrderShortInfo;
|
||||||
|
|
||||||
// Amount that has been refunded by the merchant
|
// Amount that has been refunded by the merchant
|
||||||
amountRaw: AmountString;
|
amountRaw: AmountString;
|
||||||
@ -321,4 +340,20 @@ export const codecForTransactionsRequest = (): Codec<TransactionsRequest> =>
|
|||||||
export const codecForTransactionsResponse = (): Codec<TransactionsResponse> =>
|
export const codecForTransactionsResponse = (): Codec<TransactionsResponse> =>
|
||||||
buildCodecForObject<TransactionsResponse>()
|
buildCodecForObject<TransactionsResponse>()
|
||||||
.property("transactions", codecForList(codecForAny()))
|
.property("transactions", codecForList(codecForAny()))
|
||||||
.build("TransactionsResponse");
|
.build("TransactionsResponse");
|
||||||
|
|
||||||
|
export const codecForOrderShortInfo = (): Codec<OrderShortInfo> =>
|
||||||
|
buildCodecForObject<OrderShortInfo>()
|
||||||
|
.property("contractTermsHash", codecForString())
|
||||||
|
.property("fulfillmentMessage", codecOptional(codecForString()))
|
||||||
|
.property(
|
||||||
|
"fulfillmentMessage_i18n",
|
||||||
|
codecOptional(codecForInternationalizedString()),
|
||||||
|
)
|
||||||
|
.property("fulfillmentUrl", codecOptional(codecForString()))
|
||||||
|
.property("merchant", codecForMerchantInfo())
|
||||||
|
.property("orderId", codecForString())
|
||||||
|
.property("products", codecOptional(codecForList(codecForProduct())))
|
||||||
|
.property("summary", codecForString())
|
||||||
|
.property("summary_i18n", codecOptional(codecForInternationalizedString()))
|
||||||
|
.build("OrderShortInfo");
|
||||||
|
@ -50,8 +50,8 @@ import {
|
|||||||
codecForAny,
|
codecForAny,
|
||||||
buildCodecForUnion,
|
buildCodecForUnion,
|
||||||
} from "../util/codec";
|
} from "../util/codec";
|
||||||
import { AmountString, codecForContractTerms } from "./talerTypes";
|
import { AmountString, codecForContractTerms, ContractTerms } from "./talerTypes";
|
||||||
import { TransactionError } from "./transactions";
|
import { TransactionError, OrderShortInfo, codecForOrderShortInfo } from "./transactions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response for the create reserve request to the wallet.
|
* Response for the create reserve request to the wallet.
|
||||||
@ -209,8 +209,7 @@ export const enum ConfirmPayResultType {
|
|||||||
*/
|
*/
|
||||||
export interface ConfirmPayResultDone {
|
export interface ConfirmPayResultDone {
|
||||||
type: ConfirmPayResultType.Done;
|
type: ConfirmPayResultType.Done;
|
||||||
|
contractTerms: ContractTerms;
|
||||||
nextUrl: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfirmPayResultPending {
|
export interface ConfirmPayResultPending {
|
||||||
@ -232,7 +231,7 @@ export const codecForConfirmPayResultPending = (): Codec<
|
|||||||
export const codecForConfirmPayResultDone = (): Codec<ConfirmPayResultDone> =>
|
export const codecForConfirmPayResultDone = (): Codec<ConfirmPayResultDone> =>
|
||||||
buildCodecForObject<ConfirmPayResultDone>()
|
buildCodecForObject<ConfirmPayResultDone>()
|
||||||
.property("type", codecForConstString(ConfirmPayResultType.Done))
|
.property("type", codecForConstString(ConfirmPayResultType.Done))
|
||||||
.property("nextUrl", codecForString())
|
.property("contractTerms", codecForContractTerms())
|
||||||
.build("ConfirmPayResultDone");
|
.build("ConfirmPayResultDone");
|
||||||
|
|
||||||
export const codecForConfirmPayResult = (): Codec<ConfirmPayResult> =>
|
export const codecForConfirmPayResult = (): Codec<ConfirmPayResult> =>
|
||||||
@ -368,14 +367,6 @@ export interface BenchmarkResult {
|
|||||||
repetitions: number;
|
repetitions: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cached next URL for a particular session id.
|
|
||||||
*/
|
|
||||||
export interface NextUrlResult {
|
|
||||||
nextUrl: string;
|
|
||||||
lastSessionId: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const enum PreparePayResultType {
|
export const enum PreparePayResultType {
|
||||||
PaymentPossible = "payment-possible",
|
PaymentPossible = "payment-possible",
|
||||||
InsufficientBalance = "insufficient-balance",
|
InsufficientBalance = "insufficient-balance",
|
||||||
@ -388,7 +379,7 @@ export const codecForPreparePayResultPaymentPossible = (): Codec<
|
|||||||
buildCodecForObject<PreparePayResultPaymentPossible>()
|
buildCodecForObject<PreparePayResultPaymentPossible>()
|
||||||
.property("amountEffective", codecForAmountString())
|
.property("amountEffective", codecForAmountString())
|
||||||
.property("amountRaw", codecForAmountString())
|
.property("amountRaw", codecForAmountString())
|
||||||
.property("contractTerms", codecForAny())
|
.property("contractTerms", codecForContractTerms())
|
||||||
.property("proposalId", codecForString())
|
.property("proposalId", codecForString())
|
||||||
.property(
|
.property(
|
||||||
"status",
|
"status",
|
||||||
@ -419,7 +410,6 @@ export const codecForPreparePayResultAlreadyConfirmed = (): Codec<
|
|||||||
)
|
)
|
||||||
.property("amountEffective", codecForAmountString())
|
.property("amountEffective", codecForAmountString())
|
||||||
.property("amountRaw", codecForAmountString())
|
.property("amountRaw", codecForAmountString())
|
||||||
.property("nextUrl", codecForString())
|
|
||||||
.property("paid", codecForBoolean)
|
.property("paid", codecForBoolean)
|
||||||
.property("contractTerms", codecForAny())
|
.property("contractTerms", codecForAny())
|
||||||
.property("contractTermsHash", codecForString())
|
.property("contractTermsHash", codecForString())
|
||||||
@ -450,7 +440,7 @@ export type PreparePayResult =
|
|||||||
export interface PreparePayResultPaymentPossible {
|
export interface PreparePayResultPaymentPossible {
|
||||||
status: PreparePayResultType.PaymentPossible;
|
status: PreparePayResultType.PaymentPossible;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
contractTerms: Record<string, unknown>;
|
contractTerms: ContractTerms;
|
||||||
amountRaw: string;
|
amountRaw: string;
|
||||||
amountEffective: string;
|
amountEffective: string;
|
||||||
}
|
}
|
||||||
@ -458,19 +448,16 @@ export interface PreparePayResultPaymentPossible {
|
|||||||
export interface PreparePayResultInsufficientBalance {
|
export interface PreparePayResultInsufficientBalance {
|
||||||
status: PreparePayResultType.InsufficientBalance;
|
status: PreparePayResultType.InsufficientBalance;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
contractTerms: Record<string, unknown>;
|
contractTerms: ContractTerms;
|
||||||
amountRaw: string;
|
amountRaw: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreparePayResultAlreadyConfirmed {
|
export interface PreparePayResultAlreadyConfirmed {
|
||||||
status: PreparePayResultType.AlreadyConfirmed;
|
status: PreparePayResultType.AlreadyConfirmed;
|
||||||
contractTerms: Record<string, unknown>;
|
contractTerms: ContractTerms;
|
||||||
paid: boolean;
|
paid: boolean;
|
||||||
amountRaw: string;
|
amountRaw: string;
|
||||||
amountEffective: string;
|
amountEffective: string;
|
||||||
// Only specified if paid.
|
|
||||||
nextUrl?: string;
|
|
||||||
|
|
||||||
contractTermsHash: string;
|
contractTermsHash: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +37,13 @@ import {
|
|||||||
ContractTerms,
|
ContractTerms,
|
||||||
codecForContractTerms,
|
codecForContractTerms,
|
||||||
ConfirmPayResultType,
|
ConfirmPayResultType,
|
||||||
|
ConfirmPayResult,
|
||||||
|
getJsonI18n,
|
||||||
} from "taler-wallet-core";
|
} from "taler-wallet-core";
|
||||||
|
|
||||||
function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
||||||
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>();
|
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>();
|
||||||
|
const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>();
|
||||||
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
|
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
|
||||||
const [numTries, setNumTries] = useState(0);
|
const [numTries, setNumTries] = useState(0);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -71,25 +74,25 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
|||||||
payStatus.status === PreparePayResultType.AlreadyConfirmed &&
|
payStatus.status === PreparePayResultType.AlreadyConfirmed &&
|
||||||
numTries === 0
|
numTries === 0
|
||||||
) {
|
) {
|
||||||
return (
|
const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
|
||||||
|
if (fulfillmentUrl) {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
You have already paid for this article. Click{" "}
|
||||||
|
<a href={fulfillmentUrl}>here</a> to view it again.
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
<span>
|
<span>
|
||||||
You have already paid for this article. Click{" "}
|
You have already paid for this article:{" "}
|
||||||
<a href={payStatus.nextUrl}>here</a> to view it again.
|
<em>
|
||||||
</span>
|
{payStatus.contractTerms.fulfillment_message ?? "no message given"}
|
||||||
);
|
</em>
|
||||||
|
</span>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let contractTerms: ContractTerms;
|
let contractTerms: ContractTerms = payStatus.contractTerms;
|
||||||
|
|
||||||
try {
|
|
||||||
contractTerms = codecForContractTerms().decode(payStatus.contractTerms);
|
|
||||||
} catch (e) {
|
|
||||||
// This should never happen, as the wallet is supposed to check the contract terms
|
|
||||||
// before storing them.
|
|
||||||
console.error(e);
|
|
||||||
console.log("raw contract terms were", payStatus.contractTerms);
|
|
||||||
return <span>Invalid contract terms.</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!contractTerms) {
|
if (!contractTerms) {
|
||||||
return (
|
return (
|
||||||
@ -122,13 +125,33 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
|||||||
if (res.type !== ConfirmPayResultType.Done) {
|
if (res.type !== ConfirmPayResultType.Done) {
|
||||||
throw Error("payment pending");
|
throw Error("payment pending");
|
||||||
}
|
}
|
||||||
document.location.href = res.nextUrl;
|
const fu = res.contractTerms.fulfillment_url;
|
||||||
|
if (fu) {
|
||||||
|
document.location.href = fu;
|
||||||
|
}
|
||||||
|
setPayResult(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setPayErrMsg(e.message);
|
setPayErrMsg(e.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (payResult && payResult.type === ConfirmPayResultType.Done) {
|
||||||
|
if (payResult.contractTerms.fulfillment_message) {
|
||||||
|
const obj = {
|
||||||
|
fulfillment_message: payResult.contractTerms.fulfillment_message,
|
||||||
|
fulfillment_message_i18n: payResult.contractTerms.fulfillment_message_i18n,
|
||||||
|
};
|
||||||
|
const msg = getJsonI18n(obj, "fulfillment_message")
|
||||||
|
return <div>
|
||||||
|
<p>Payment succeeded.</p>
|
||||||
|
<p>{msg}</p>
|
||||||
|
</div>;
|
||||||
|
} else {
|
||||||
|
return <span>Redirecting ...</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
|
Loading…
Reference in New Issue
Block a user