From 630f53f8a5721b2f5f2d43772aa4de7146df58fc Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 19 Mar 2024 19:57:50 -0300 Subject: wip --- packages/taler-util/src/http-client/merchant.ts | 77 +++++++- packages/taler-util/src/http-client/types.ts | 242 ++++++++++++++++++++++-- packages/taler-util/src/taler-types.ts | 8 +- 3 files changed, 300 insertions(+), 27 deletions(-) (limited to 'packages/taler-util/src') diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts index b054a04eb..e9fda5075 100644 --- a/packages/taler-util/src/http-client/merchant.ts +++ b/packages/taler-util/src/http-client/merchant.ts @@ -20,7 +20,6 @@ import { PaginationParams, TalerMerchantApi, codecForAbortResponse, - codecForAccountAddDetails, codecForAccountAddResponse, codecForAccountKycRedirects, codecForAccountsSummaryResponse, @@ -28,22 +27,28 @@ import { codecForClaimResponse, codecForInventorySummaryResponse, codecForMerchantConfig, + codecForMerchantOrderPrivateStatusResponse, + codecForOrderHistory, + codecForOutOfStockResponse, codecForPaidRefundStatusResponse, codecForPaymentResponse, + codecForPostOrderResponse, + codecForProductDetail, codecForQueryInstancesResponse, codecForStatusGoto, codecForStatusPaid, codecForStatusStatusUnpaid, codecForWalletRefundResponse, opEmptySuccess, - opKnownHttpFailure, + opKnownAlternativeFailure, + opKnownHttpFailure } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, createPlatformHttpLib, } from "@gnu-taler/taler-util/http"; import { opSuccessFromHttp, opUnknownFailure } from "../operation.js"; -import { CacheEvictor, addMerchantPaginationParams, addPaginationParams, nullEvictor } from "./utils.js"; +import { CacheEvictor, addMerchantPaginationParams, nullEvictor } from "./utils.js"; export enum TalerMerchantCacheEviction { CREATE_ORDER, @@ -640,6 +645,15 @@ export class TalerMerchantInstanceHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", }); + + switch (resp.status) { + case HttpStatusCode.Ok: + return opSuccessFromHttp(resp, codecForProductDetail()); + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); + } } /** @@ -651,6 +665,17 @@ export class TalerMerchantInstanceHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "POST", }); + + switch (resp.status) { + case HttpStatusCode.NoContent: + return opEmptySuccess(resp) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Gone: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); + } } /** @@ -662,6 +687,17 @@ export class TalerMerchantInstanceHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "DELETE", }); + + switch (resp.status) { + case HttpStatusCode.NoContent: + return opEmptySuccess(resp) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + default: + return opUnknownFailure(resp, await resp.text()); + } } // @@ -678,6 +714,19 @@ export class TalerMerchantInstanceHttpClient { method: "POST", body, }); + + switch (resp.status) { + case HttpStatusCode.Ok: + return opSuccessFromHttp(resp, codecForPostOrderResponse()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Conflict: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.Gone: + return opKnownAlternativeFailure(resp, resp.status, codecForOutOfStockResponse()); + default: + return opUnknownFailure(resp, await resp.text()); + } } /** @@ -717,6 +766,13 @@ export class TalerMerchantInstanceHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", }); + + switch (resp.status) { + case HttpStatusCode.Ok: + return opSuccessFromHttp(resp, codecForOrderHistory()) + default: + return opUnknownFailure(resp, await resp.text()); + } } /** @@ -741,6 +797,20 @@ export class TalerMerchantInstanceHttpClient { const resp = await this.httpLib.fetch(url.href, { method: "GET", }); + + switch (resp.status) { + case HttpStatusCode.Ok: + return opSuccessFromHttp(resp, codecForMerchantOrderPrivateStatusResponse()) + case HttpStatusCode.NotFound: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.BadGateway: + return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.GatewayTimeout: + return opKnownAlternativeFailure(resp, resp.status, codecForOutOfStockResponse()); + default: + return opUnknownFailure(resp, await resp.text()); + } + } /** @@ -753,6 +823,7 @@ export class TalerMerchantInstanceHttpClient { method: "PATCH", body, }); + } /** diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index 7e4e2f84c..b53566304 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -18,15 +18,15 @@ import { import { PaytoString, codecForPaytoString } from "../payto.js"; import { AmountString, - MerchantContractTerms, + codecForInternationalizedString, codecForLocation, - codecForMerchantContractTerms, } from "../taler-types.js"; import { TalerActionString, codecForTalerActionString } from "../taleruri.js"; import { AbsoluteTime, TalerProtocolDuration, TalerProtocolTimestamp, + codecForAbsoluteTime, codecForDuration, codecForTimestamp, } from "../time.js"; @@ -322,7 +322,7 @@ export const codecForMerchantConfig = export const codecForClaimResponse = (): Codec => buildCodecForObject() - .property("contract_terms", codecForMerchantContractTerms()) + .property("contract_terms", codecForContractTerms()) .property("sig", codecForString()) .build("TalerMerchantApi.ClaimResponse"); @@ -533,6 +533,208 @@ export const codecForInventoryEntry = .property("product_serial", codecForNumber()) .build("TalerMerchantApi.InventoryEntry"); +export const codecForProductDetail = + (): Codec => + buildCodecForObject() + .property("description", codecForString()) + .property("description_i18n", codecForInternationalizedString()) + .property("unit", codecForString()) + .property("price", codecForAmountString()) + .property("image", codecForString()) + .property("taxes", codecForList(codecForTax())) + .property("address", codecForLocation()) + .property("next_restock", codecForTimestamp) + .property("total_stock", codecForNumber()) + .property("total_sold", codecForNumber()) + .property("total_lost", codecForNumber()) + .property("minimum_age", codecOptional(codecForNumber())) + .build("TalerMerchantApi.ProductDetail"); + +export const codecForTax = (): Codec => + buildCodecForObject() + .property("name", codecForString()) + .property("tax", codecForAmountString()) + .build("TalerMerchantApi.Tax"); + +export const codecForPostOrderResponse = + (): Codec => + buildCodecForObject() + .property("order_id", codecForString()) + .property("token", codecOptional(codecForString())) + .build("TalerMerchantApi.PostOrderResponse"); + +export const codecForOutOfStockResponse = + (): Codec => + buildCodecForObject() + .property("product_id", codecForString()) + .property("available_quantity", codecForNumber()) + .property("requested_quantity", codecForNumber()) + .property("restock_expected", codecForTimestamp) + .build("TalerMerchantApi.OutOfStockResponse"); + +export const codecForOrderHistory = + (): Codec => + buildCodecForObject() + .property("orders", codecForList(codecForOrderHistoryEntry())) + .build("TalerMerchantApi.OrderHistory"); + +export const codecForOrderHistoryEntry = + (): Codec => + buildCodecForObject() + .property("order_id", codecForString()) + .property("row_id", codecForNumber()) + .property("timestamp", codecForTimestamp) + .property("amount", codecForAmountString()) + .property("summary", codecForString()) + .property("refundable", codecForBoolean()) + .property("paid", codecForBoolean()) + .build("TalerMerchantApi.OrderHistoryEntry"); + + +export const codecForMerchant = (): Codec => + buildCodecForObject() + .property("name", codecForString()) + .property("email", codecOptional(codecForString())) + .property("logo", codecOptional(codecForString())) + .property("website", codecOptional(codecForString())) + .property("address", codecOptional(codecForLocation())) + .property("jurisdiction", codecOptional(codecForLocation())) + .build("TalerMerchantApi.MerchantInfo"); + +export const codecForExchange = (): Codec => + buildCodecForObject() + .property("master_pub", codecForString()) + .property("priority", codecForNumber()) + .property("url", codecForString()) + .build("ExchangeHandle"); + +export const codecForContractTerms = (): Codec => + buildCodecForObject() + .property("order_id", codecForString()) + .property("fulfillment_url", codecOptional(codecForString())) + .property("fulfillment_message", codecOptional(codecForString())) + .property( + "fulfillment_message_i18n", + codecOptional(codecForInternationalizedString()), + ) + .property("merchant_base_url", codecForString()) + .property("h_wire", codecForString()) + .property("auto_refund", codecOptional(codecForDuration)) + .property("wire_method", codecForString()) + .property("summary", codecForString()) + .property("summary_i18n", codecOptional(codecForInternationalizedString())) + .property("nonce", codecForString()) + .property("amount", codecForAmountString()) + .property("pay_deadline", codecForTimestamp) + .property("refund_deadline", codecForTimestamp) + .property("wire_transfer_deadline", codecForTimestamp) + .property("timestamp", codecForTimestamp) + .property("delivery_location", codecOptional(codecForLocation())) + .property("delivery_date", codecOptional(codecForTimestamp)) + .property("max_fee", codecForAmountString()) + .property("merchant", codecForMerchant()) + .property("merchant_pub", codecForString()) + .property("exchanges", codecForList(codecForExchange())) + .property("products", codecForList(codecForProduct())) + .property("extra", codecForAny()) + .build("TalerMerchantApi.ContractTerms"); + +export const codecForProduct = (): Codec => + buildCodecForObject() + .property("product_id", codecOptional(codecForString())) + .property("description", codecForString()) + .property( + "description_i18n", + codecOptional(codecForInternationalizedString()), + ) + .property("quantity", codecOptional(codecForNumber())) + .property("unit", codecOptional(codecForString())) + .property("price", codecOptional(codecForAmountString())) + .property("image", codecOptional(codecForString())) + .property("taxes", codecOptional(codecForList(codecForTax()))) + .property("delivery_date", codecOptional(codecForTimestamp)) + .build("TalerMerchantApi.Product"); + + + +export const codecForCheckPaymentPaidResponse = + (): Codec => + buildCodecForObject() + .property("order_status", codecForConstString("paid")) + .property("refunded", codecForBoolean()) + .property("refund_pending", codecForBoolean()) + .property("wired", codecForBoolean()) + .property("deposit_total", codecForAmountString()) + .property("exchange_code", codecForNumber()) + .property("exchange_http_status", codecForNumber()) + .property("refund_amount", codecForAmountString()) + .property("contract_terms", codecForContractTerms()) + .property("wire_reports", codecForList(codecForTransactionWireReport())) + .property("wire_details", codecForList(codecForTransactionWireTransfer())) + .property("refund_details", codecForList(codecForRefundDetails())) + .property("order_status_url", codecForURL()) + .build("TalerMerchantApi.CheckPaymentPaidResponse"); + +export const codecForCheckPaymentUnpaidResponse = + (): Codec => + buildCodecForObject() + .property("order_status", codecForConstString("unpaid")) + .property("taler_pay_uri", codecForString()) + .property("creation_time", codecForTimestamp) + .property("summary", codecForString()) + .property("total_amount", codecForAmountString()) + .property("already_paid_order_id", codecOptional(codecForString())) + .property("already_paid_fulfillment_url", codecOptional(codecForString())) + .property("order_status_url", codecForString()) + .build("TalerMerchantApi.CheckPaymentPaidResponse"); + +export const codecForCheckPaymentClaimedResponse = + (): Codec => + buildCodecForObject() + .property("order_status", codecForConstString("claimed")) + .property("contract_terms", codecForContractTerms()) + .build("TalerMerchantApi.CheckPaymentClaimedResponse"); + +export const codecForMerchantOrderPrivateStatusResponse = + (): Codec => + buildCodecForUnion() + .discriminateOn("order_status") + .alternative("paid", codecForCheckPaymentPaidResponse()) + .alternative("unpaid", codecForCheckPaymentUnpaidResponse()) + .alternative("claimed", codecForCheckPaymentClaimedResponse()) + .build("TalerMerchantApi.MerchantOrderStatusResponse"); + + + +export const codecForRefundDetails = + (): Codec => + buildCodecForObject() + .property("reason", codecForString()) + .property("pending", codecForBoolean()) + .property("timestamp", codecForTimestamp) + .property("amount", codecForAmountString()) + .build("TalerMerchantApi.RefundDetails"); + +export const codecForTransactionWireTransfer = + (): Codec => + buildCodecForObject() + .property("exchange_url", codecForURL()) + .property("wtid", codecForString()) + .property("execution_time", codecForTimestamp) + .property("amount", codecForAmountString()) + .property("confirmed", codecForBoolean()) + .build("TalerMerchantApi.TransactionWireTransfer"); + +export const codecForTransactionWireReport = + (): Codec => + buildCodecForObject() + .property("code", codecForNumber()) + .property("hint", codecForString()) + .property("exchange_code", codecForNumber()) + .property("exchange_http_status", codecForNumber()) + .property("coin_pub", codecForString()) + .build("TalerMerchantApi.TransactionWireReport"); + export const codecForExchangeConfig = (): Codec => buildCodecForObject() @@ -2690,7 +2892,7 @@ export namespace TalerMerchantApi { export interface ClaimResponse { // Contract terms of the claimed order - contract_terms: MerchantContractTerms; + contract_terms: ContractTerms; // Signature by the merchant over the contract terms. sig: EddsaSignature; @@ -3635,7 +3837,7 @@ export namespace TalerMerchantApi { quantity: Integer; } - interface PostOrderResponse { + export interface PostOrderResponse { // Order ID of the response that was just created. order_id: string; @@ -3644,7 +3846,7 @@ export namespace TalerMerchantApi { // in the request. token?: ClaimToken; } - interface OutOfStockResponse { + export interface OutOfStockResponse { // Product ID of an out-of-stock item. product_id: string; @@ -3659,12 +3861,12 @@ export namespace TalerMerchantApi { restock_expected?: Timestamp; } - interface OrderHistory { + export interface OrderHistory { // Timestamp-sorted array of all orders matching the query. // The order of the sorting depends on the sign of delta. orders: OrderHistoryEntry[]; } - interface OrderHistoryEntry { + export interface OrderHistoryEntry { // Order ID of the transaction related to this entry. order_id: string; @@ -3690,11 +3892,11 @@ export namespace TalerMerchantApi { paid: boolean; } - type MerchantOrderStatusResponse = + export type MerchantOrderStatusResponse = | CheckPaymentPaidResponse | CheckPaymentClaimedResponse | CheckPaymentUnpaidResponse; - interface CheckPaymentPaidResponse { + export interface CheckPaymentPaidResponse { // The customer paid for this contract. order_status: "paid"; @@ -3745,14 +3947,14 @@ export namespace TalerMerchantApi { // to show the order QR code / trigger the wallet. order_status_url: string; } - interface CheckPaymentClaimedResponse { + export interface CheckPaymentClaimedResponse { // A wallet claimed the order, but did not yet pay for the contract. order_status: "claimed"; // Contract terms. contract_terms: ContractTerms; } - interface CheckPaymentUnpaidResponse { + export interface CheckPaymentUnpaidResponse { // The order was neither claimed nor paid. order_status: "unpaid"; @@ -3783,7 +3985,7 @@ export namespace TalerMerchantApi { // We do we NOT return the contract terms here because they may not // exist in case the wallet did not yet claim them. } - interface RefundDetails { + export interface RefundDetails { // Reason given for the refund. reason: string; @@ -3796,7 +3998,7 @@ export namespace TalerMerchantApi { // Total amount that was refunded (minus a refund fee). amount: AmountString; } - interface TransactionWireTransfer { + export interface TransactionWireTransfer { // Responsible exchange. exchange_url: string; @@ -3814,7 +4016,7 @@ export namespace TalerMerchantApi { // POST /transfers API, or is it merely claimed by the exchange? confirmed: boolean; } - interface TransactionWireReport { + export interface TransactionWireReport { // Numerical error code. code: number; @@ -4430,7 +4632,7 @@ export namespace TalerMerchantApi { // How many tokens have been redeemed for this family. redeemed: Integer; } - interface ContractTerms { + export interface ContractTerms { // Human-readable description of the whole purchase. summary: string; @@ -4553,7 +4755,7 @@ export namespace TalerMerchantApi { extra?: any; } - interface Product { + export interface Product { // Merchant-internal identifier for the product. product_id?: string; @@ -4582,14 +4784,14 @@ export namespace TalerMerchantApi { delivery_date?: Timestamp; } - interface Tax { + export interface Tax { // The name of the tax. name: string; // Amount paid in tax. tax: AmountString; } - interface Merchant { + export interface Merchant { // The merchant's legal name of business. name: string; @@ -4653,7 +4855,7 @@ export namespace TalerMerchantApi { // Base URL of the auditor. url: string; } - interface Exchange { + export interface Exchange { // The exchange's base URL. url: string; diff --git a/packages/taler-util/src/taler-types.ts b/packages/taler-util/src/taler-types.ts index a0db25182..e8a6fca7a 100644 --- a/packages/taler-util/src/taler-types.ts +++ b/packages/taler-util/src/taler-types.ts @@ -486,7 +486,7 @@ export interface MerchantContractTerms { // Total price for the transaction. // The exchange will subtract deposit fees from that amount // before transferring it to the merchant. - amount: string; + amount: AmountString; // Nonce generated by the wallet and echoed by the merchant // in this field when the proposal is generated. @@ -568,7 +568,7 @@ export interface MerchantContractTerms { // Maximum total deposit fee accepted by the merchant for this contract. // Overrides defaults of the merchant instance. - max_fee: string; + max_fee: AmountString; // Extra data that is only interpreted by the merchant frontend. // Useful when the merchant needs to store extra information on a @@ -1464,14 +1464,14 @@ export const codecForMerchantContractTerms = (): Codec => .property("summary", codecForString()) .property("summary_i18n", codecOptional(codecForInternationalizedString())) .property("nonce", codecForString()) - .property("amount", codecForString()) + .property("amount", codecForAmountString()) .property("pay_deadline", codecForTimestamp) .property("refund_deadline", codecForTimestamp) .property("wire_transfer_deadline", codecForTimestamp) .property("timestamp", codecForTimestamp) .property("delivery_location", codecOptional(codecForLocation())) .property("delivery_date", codecOptional(codecForTimestamp)) - .property("max_fee", codecForString()) + .property("max_fee", codecForAmountString()) .property("merchant", codecForMerchantInfo()) .property("merchant_pub", codecForString()) .property("exchanges", codecForList(codecForExchangeHandle())) -- cgit v1.2.3