adapt to merchant API breaking changes

This commit is contained in:
Florian Dold 2023-05-05 14:56:28 +02:00
parent 990b056071
commit 9a412260f3
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
6 changed files with 130 additions and 47 deletions

View File

@ -28,6 +28,7 @@ import {
AmountJson, AmountJson,
Amounts, Amounts,
AmountString, AmountString,
codecForMerchantReserveCreateConfirmation,
Configuration, Configuration,
CoreApiResponse, CoreApiResponse,
createEddsaKeyPair, createEddsaKeyPair,
@ -38,6 +39,7 @@ import {
hash, hash,
j2s, j2s,
Logger, Logger,
MerchantReserveCreateConfirmation,
MerchantTemplateAddDetails, MerchantTemplateAddDetails,
parsePaytoUri, parsePaytoUri,
stringToBytes, stringToBytes,
@ -81,7 +83,11 @@ import {
RemoteWallet, RemoteWallet,
WalletNotificationWaiter, WalletNotificationWaiter,
} from "@gnu-taler/taler-wallet-core/remote"; } from "@gnu-taler/taler-wallet-core/remote";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; import {
createPlatformHttpLib,
readSuccessResponseJsonOrErrorCode,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
const logger = new Logger("harness.ts"); const logger = new Logger("harness.ts");
@ -1568,13 +1574,18 @@ export class MerchantApiClient {
async createTippingReserve( async createTippingReserve(
req: CreateMerchantTippingReserveRequest, req: CreateMerchantTippingReserveRequest,
): Promise<CreateMerchantTippingReserveConfirmation> { ): Promise<MerchantReserveCreateConfirmation> {
const url = new URL("private/reserves", this.baseUrl); const url = new URL("private/reserves", this.baseUrl);
const resp = await axios.post(url.href, req, { const resp = await this.http.fetch(url.href, {
method: "POST",
body: req,
headers: this.makeAuthHeader(), headers: this.makeAuthHeader(),
}); });
// FIXME: validate const respData = readSuccessResponseJsonOrThrow(
return resp.data; resp,
codecForMerchantReserveCreateConfirmation(),
);
return respData;
} }
async getPrivateInstanceInfo(): Promise<any> { async getPrivateInstanceInfo(): Promise<any> {
@ -1719,21 +1730,6 @@ export namespace MerchantPrivateApi {
}; };
} }
export async function createTippingReserve(
merchantService: MerchantServiceInterface,
instance: string,
req: CreateMerchantTippingReserveRequest,
): Promise<CreateMerchantTippingReserveConfirmation> {
const reqUrl = new URL(
`private/reserves`,
merchantService.makeInstanceBaseUrl(instance),
);
// FIXME: Don't use axios!
const resp = await axios.post(reqUrl.href, req);
// FIXME: validate
return resp.data;
}
export async function queryTippingReserves( export async function queryTippingReserves(
merchantService: MerchantServiceInterface, merchantService: MerchantServiceInterface,
instance: string, instance: string,
@ -1773,14 +1769,6 @@ export interface CreateMerchantTippingReserveRequest {
wire_method: string; wire_method: string;
} }
export interface CreateMerchantTippingReserveConfirmation {
// Public key identifying the reserve
reserve_pub: string;
// Wire account of the exchange where to transfer the funds
payto_uri: string;
}
export class MerchantService implements MerchantServiceInterface { export class MerchantService implements MerchantServiceInterface {
static fromExistingConfig(gc: GlobalTestState, name: string) { static fromExistingConfig(gc: GlobalTestState, name: string) {
const cfgFilename = gc.testDir + `/merchant-${name}.conf`; const cfgFilename = gc.testDir + `/merchant-${name}.conf`;

View File

@ -213,7 +213,7 @@ deploymentCli
allowHttp: true, allowHttp: true,
}); });
const paytoUri = addPaytoQueryParams(tipReserveResp.payto_uri, { const paytoUri = addPaytoQueryParams(tipReserveResp.accounts[0].payto_uri, {
message: `tip-reserve ${tipReserveResp.reserve_pub}`, message: `tip-reserve ${tipReserveResp.reserve_pub}`,
}); });

View File

@ -26,6 +26,7 @@ import { defaultCoinConfig } from "../harness/denomStructures.js";
import { import {
getWireMethodForTest, getWireMethodForTest,
GlobalTestState, GlobalTestState,
MerchantApiClient,
MerchantPrivateApi, MerchantPrivateApi,
WalletCli, WalletCli,
} from "../harness/harness.js"; } from "../harness/harness.js";
@ -55,6 +56,13 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
}, },
); );
const merchantClient = new MerchantApiClient(
merchant.makeInstanceBaseUrl("default"),
{
method: "external",
},
);
const walletTwo = new WalletCli(t, "walletTwo"); const walletTwo = new WalletCli(t, "walletTwo");
const walletThree = new WalletCli(t, "walletThree"); const walletThree = new WalletCli(t, "walletThree");
@ -147,9 +155,7 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
// Pay with coin from tipping // Pay with coin from tipping
{ {
const mbu = await BankApi.createRandomBankUser(bank); const mbu = await BankApi.createRandomBankUser(bank);
const tipReserveResp = await MerchantPrivateApi.createTippingReserve( const tipReserveResp = await merchantClient.createTippingReserve(
merchant,
"default",
{ {
exchange_url: exchange.baseUrl, exchange_url: exchange.baseUrl,
initial_balance: "TESTKUDOS:10", initial_balance: "TESTKUDOS:10",
@ -158,7 +164,7 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
); );
t.assertDeepEqual( t.assertDeepEqual(
tipReserveResp.payto_uri, tipReserveResp.accounts[0].payto_uri,
exchangeBankAccount.accountPaytoUri, exchangeBankAccount.accountPaytoUri,
); );

View File

@ -24,6 +24,7 @@ import {
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { import {
GlobalTestState, GlobalTestState,
MerchantApiClient,
MerchantPrivateApi, MerchantPrivateApi,
getWireMethodForTest, getWireMethodForTest,
} from "../harness/harness.js"; } from "../harness/harness.js";
@ -40,20 +41,23 @@ export async function runTippingTest(t: GlobalTestState) {
const mbu = await BankApi.createRandomBankUser(bank); const mbu = await BankApi.createRandomBankUser(bank);
const tipReserveResp = await MerchantPrivateApi.createTippingReserve( const merchantClient = new MerchantApiClient(
merchant, merchant.makeInstanceBaseUrl("default"),
"default",
{ {
exchange_url: exchange.baseUrl, method: "external",
initial_balance: "TESTKUDOS:10",
wire_method: getWireMethodForTest(),
}, },
); );
const tipReserveResp = await merchantClient.createTippingReserve({
exchange_url: exchange.baseUrl,
initial_balance: "TESTKUDOS:10",
wire_method: getWireMethodForTest(),
});
console.log("tipReserveResp:", tipReserveResp); console.log("tipReserveResp:", tipReserveResp);
t.assertDeepEqual( t.assertDeepEqual(
tipReserveResp.payto_uri, tipReserveResp.accounts[0].payto_uri,
exchangeBankAccount.accountPaytoUri, exchangeBankAccount.accountPaytoUri,
); );

View File

@ -44,6 +44,9 @@ import {
TalerProtocolDuration, TalerProtocolDuration,
codecForTimestamp, codecForTimestamp,
TalerProtocolTimestamp, TalerProtocolTimestamp,
WireAccount,
codecForWireAccount,
codecForList,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
export interface MerchantPostOrderRequest { export interface MerchantPostOrderRequest {
@ -75,11 +78,12 @@ export interface MerchantPostOrderResponse {
token?: ClaimToken; token?: ClaimToken;
} }
export const codecForMerchantPostOrderResponse = (): Codec<MerchantPostOrderResponse> => export const codecForMerchantPostOrderResponse =
buildCodecForObject<MerchantPostOrderResponse>() (): Codec<MerchantPostOrderResponse> =>
.property("order_id", codecForString()) buildCodecForObject<MerchantPostOrderResponse>()
.property("token", codecOptional(codecForString())) .property("order_id", codecForString())
.build("PostOrderResponse"); .property("token", codecOptional(codecForString()))
.build("PostOrderResponse");
export const codecForMerchantRefundDetails = (): Codec<RefundDetails> => export const codecForMerchantRefundDetails = (): Codec<RefundDetails> =>
buildCodecForObject<RefundDetails>() buildCodecForObject<RefundDetails>()
@ -351,7 +355,6 @@ export interface MerchantTemplateContractDetails {
} }
export interface MerchantTemplateAddDetails { export interface MerchantTemplateAddDetails {
// Template ID to use. // Template ID to use.
template_id: string; template_id: string;
@ -365,4 +368,19 @@ export interface MerchantTemplateAddDetails {
// Additional information in a separate template. // Additional information in a separate template.
template_contract: MerchantTemplateContractDetails; template_contract: MerchantTemplateContractDetails;
} }
export interface MerchantReserveCreateConfirmation {
// Public key identifying the reserve.
reserve_pub: EddsaPublicKeyString;
// Wire accounts of the exchange where to transfer the funds.
accounts: WireAccount[];
}
export const codecForMerchantReserveCreateConfirmation =
(): Codec<MerchantReserveCreateConfirmation> =>
buildCodecForObject<MerchantReserveCreateConfirmation>()
.property("accounts", codecForList(codecForWireAccount()))
.property("reserve_pub", codecForString())
.build("MerchantReserveCreateConfirmation");

View File

@ -2165,3 +2165,70 @@ export const codecForExchangeRefundSuccessResponse =
.property("exchange_pub", codecForString()) .property("exchange_pub", codecForString())
.property("exchange_sig", codecForString()) .property("exchange_sig", codecForString())
.build("ExchangeRefundSuccessResponse"); .build("ExchangeRefundSuccessResponse");
export type AccountRestriction =
| RegexAccountRestriction
| DenyAllAccountRestriction;
export interface DenyAllAccountRestriction {
type: "deny";
}
// Accounts interacting with this type of account
// restriction must have a payto://-URI matching
// the given regex.
export interface RegexAccountRestriction {
type: "regex";
// Regular expression that the payto://-URI of the
// partner account must follow. The regular expression
// should follow posix-egrep, but without support for character
// classes, GNU extensions, back-references or intervals. See
// https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
// for a description of the posix-egrep syntax. Applications
// may support regexes with additional features, but exchanges
// must not use such regexes.
payto_regex: string;
// Hint for a human to understand the restriction
// (that is hopefully easier to comprehend than the regex itself).
human_hint: string;
// Map from IETF BCP 47 language tags to localized
// human hints.
human_hint_i18n?: InternationalizedString;
}
export interface WireAccount {
// payto:// URI identifying the account and wire method
payto_uri: string;
// URI to convert amounts from or to the currency used by
// this wire account of the exchange. Missing if no
// conversion is applicable.
conversion_url?: string;
// Restrictions that apply to bank accounts that would send
// funds to the exchange (crediting this exchange bank account).
// Optional, empty array for unrestricted.
credit_restrictions: AccountRestriction[];
// Restrictions that apply to bank accounts that would receive
// funds from the exchange (debiting this exchange bank account).
// Optional, empty array for unrestricted.
debit_restrictions: AccountRestriction[];
// Signature using the exchange's offline key over
// a TALER_MasterWireDetailsPS
// with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
master_sig: EddsaSignatureString;
}
export const codecForWireAccount = (): Codec<WireAccount> =>
buildCodecForObject<WireAccount>()
.property("conversion_url", codecOptional(codecForString()))
.property("credit_restrictions", codecForList(codecForAny()))
.property("debit_restrictions", codecForList(codecForAny()))
.property("master_sig", codecForString())
.property("payto_uri", codecForString())
.build("WireAccount");