From af266ac928aa9473a8366c5baac8b2f4752b5f39 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 6 Jan 2020 15:38:09 +0100 Subject: [PATCH] move ParsedPaymentOptions to own file also smaller code updates --- src/lib/PublicRequestTypes.ts | 8 +-- src/lib/RequestParser.ts | 20 +----- src/lib/RequestTypes.ts | 64 +---------------- .../paymentOptions/BitcoinPaymentOptions.ts | 2 +- src/lib/paymentOptions/EtherPaymentOptions.ts | 10 +-- src/lib/paymentOptions/NimiqPaymentOptions.ts | 20 +++--- .../paymentOptions/ParsedPaymentOptions.ts | 71 +++++++++++++++++++ src/views/SignTransactionLedger.vue | 7 +- 8 files changed, 98 insertions(+), 104 deletions(-) create mode 100644 src/lib/paymentOptions/ParsedPaymentOptions.ts diff --git a/src/lib/PublicRequestTypes.ts b/src/lib/PublicRequestTypes.ts index ec26d94ce..842d2e404 100644 --- a/src/lib/PublicRequestTypes.ts +++ b/src/lib/PublicRequestTypes.ts @@ -83,7 +83,7 @@ export interface PaymentOptions { currency: C; expires?: number; /** - * Amount in the smallest unit of the currency specified in `currency`. + * Amount in the smallest unit of the currency specified as `currency`. * i.e Luna for Currency.NIM and Satoshi for Currency.BTC */ amount: string; @@ -109,13 +109,13 @@ export interface MultiCurrencyCheckoutRequest extends BasicRequest { */ shopLogoUrl: string; /** - * TODO + * TODO description of the api the callback needs to provide. * Input is {currency, type} alongside the order identifying parameters in the url. * the called url must return a PaymentOptions object */ callbackUrl?: string; /** - * The csrf token, that will be transmitted for future requests to the callback url + * A CSRF token, that will be transmitted in all requests to the callback url. */ csrf?: string; /** @@ -127,7 +127,7 @@ export interface MultiCurrencyCheckoutRequest extends BasicRequest { */ time: number; /** - * ISO 4217 Code of the fiat currency used on the calling site. + * ISO 4217 Code (three letters) of the fiat currency used on the calling site. */ fiatCurrency: string; /** diff --git a/src/lib/RequestParser.ts b/src/lib/RequestParser.ts index 38b936b87..a227a1dc4 100644 --- a/src/lib/RequestParser.ts +++ b/src/lib/RequestParser.ts @@ -199,7 +199,7 @@ export class RequestParser { fiatAmount: checkoutRequest.fiatAmount, paymentOptions: checkoutRequest.paymentOptions.map((option) => { if (currencies.has(option.currency)) { - throw new Error('Each Currency can only have one paymentOption'); + throw new Error('Only one paymentOption can be provided per cryptocurrency'); } else { currencies.add(option.currency); } @@ -331,23 +331,9 @@ export class RequestParser { } as NimiqCheckoutRequest; case 2: return { - appName: checkoutRequest.appName, - version: 2, - shopLogoUrl: checkoutRequest.shopLogoUrl, + ...checkoutRequest, extraData: checkoutRequest.data, - callbackUrl: checkoutRequest.callbackUrl, - csrf: checkoutRequest.csrf, - time: checkoutRequest.time, - fiatAmount: checkoutRequest.fiatAmount || undefined, - fiatCurrency: checkoutRequest.fiatCurrency || undefined, - paymentOptions: checkoutRequest.paymentOptions.map((option) => { - switch (option.type) { - case PaymentType.DIRECT: - return option.raw(); - default: - throw new Error('paymentOption.type not supported'); - } - }), + paymentOptions: checkoutRequest.paymentOptions.map((option) => option.raw()), } as MultiCurrencyCheckoutRequest; } case RequestType.ONBOARD: diff --git a/src/lib/RequestTypes.ts b/src/lib/RequestTypes.ts index d0e3901ad..1c13f747e 100644 --- a/src/lib/RequestTypes.ts +++ b/src/lib/RequestTypes.ts @@ -1,7 +1,5 @@ -type BigInteger = import('big-integer').BigInteger; // imports only the type without bundling -import { FormattableNumber, toNonScientificNumberString } from '@nimiq/utils'; -import { isMilliseconds } from './Helpers'; -import { Currency, PaymentType, PaymentOptionsForCurrencyAndType, RequestType } from './PublicRequestTypes'; +import { Currency, PaymentType, RequestType } from './PublicRequestTypes'; +import { ParsedPaymentOptions } from './paymentOptions/ParsedPaymentOptions'; import { ParsedNimiqSpecifics, ParsedNimiqDirectPaymentOptions } from './paymentOptions/NimiqPaymentOptions'; import { ParsedEtherSpecifics, ParsedEtherDirectPaymentOptions } from './paymentOptions/EtherPaymentOptions'; import { ParsedBitcoinSpecifics, ParsedBitcoinDirectPaymentOptions } from './paymentOptions/BitcoinPaymentOptions'; @@ -36,64 +34,6 @@ export type ParsedProtocolSpecificsForCurrency = : C extends Currency.ETH ? ParsedEtherSpecifics : undefined; -export interface ParsedPaymentOptions { - readonly currency: C; - readonly type: T; - readonly decimals: number; - protocolSpecific: ParsedProtocolSpecificsForCurrency; - amount: number | BigInteger; - expires?: number; - constructor: ParsedPaymentOptionsForCurrencyAndType; - new(options: PaymentOptionsForCurrencyAndType, ...optionalArgs: any[]): - ParsedPaymentOptionsForCurrencyAndType; - raw(): PaymentOptionsForCurrencyAndType; -} - -export abstract class ParsedPaymentOptions - implements ParsedPaymentOptions { - - protected constructor(options: PaymentOptionsForCurrencyAndType) { - if (options.currency !== this.currency || options.type !== this.type) { - throw new Error(`Cannot parse given options as ${this.constructor.name}.`); - } - if (!this.isNonNegativeInteger(options.amount)) { - throw new Error('amount must be a non-negative integer'); - } - this.expires = typeof options.expires === 'number' - ? isMilliseconds(options.expires) - ? options.expires - : options.expires * 1000 - : undefined; - } - - public get baseUnitAmount(): string { - return new FormattableNumber(this.amount).moveDecimalSeparator(-this.decimals).toString(); - } - - public update

>( - this: P, - options: PaymentOptionsForCurrencyAndType, - ...additionalArgs: any[] - ) { - const parsedOptions = new this.constructor(options as any, ...additionalArgs); // parse to check validity - this.amount = parsedOptions.amount; // amount must exist on all parsed options - this.expires = parsedOptions.expires || this.expires; - for (const key of - Object.keys(parsedOptions.protocolSpecific) as Array) { - if (parsedOptions.protocolSpecific[key] === undefined) continue; - this.protocolSpecific[key] = parsedOptions.protocolSpecific[key]; - } - } - - protected isNonNegativeInteger(value: string | number | bigint | BigInteger) { - try { - return /^\d+$/.test(toNonScientificNumberString(value)); - } catch (e) { - return false; - } - } -} - export type AvailableParsedPaymentOptions = ParsedNimiqDirectPaymentOptions | ParsedEtherDirectPaymentOptions | ParsedBitcoinDirectPaymentOptions; diff --git a/src/lib/paymentOptions/BitcoinPaymentOptions.ts b/src/lib/paymentOptions/BitcoinPaymentOptions.ts index faf12eb88..4033773ce 100644 --- a/src/lib/paymentOptions/BitcoinPaymentOptions.ts +++ b/src/lib/paymentOptions/BitcoinPaymentOptions.ts @@ -1,5 +1,5 @@ import { Currency, PaymentType, PaymentOptions } from '../PublicRequestTypes'; -import { ParsedPaymentOptions } from '../RequestTypes'; +import { ParsedPaymentOptions } from './ParsedPaymentOptions'; import { toNonScientificNumberString } from '@nimiq/utils'; export interface BitcoinSpecifics { diff --git a/src/lib/paymentOptions/EtherPaymentOptions.ts b/src/lib/paymentOptions/EtherPaymentOptions.ts index e664d6fa5..dd6fbb30e 100644 --- a/src/lib/paymentOptions/EtherPaymentOptions.ts +++ b/src/lib/paymentOptions/EtherPaymentOptions.ts @@ -1,6 +1,6 @@ import bigInt from 'big-integer'; import { Currency, PaymentType, PaymentOptions } from '../PublicRequestTypes'; -import { ParsedPaymentOptions } from '../RequestTypes'; +import { ParsedPaymentOptions } from './ParsedPaymentOptions'; import { toNonScientificNumberString } from '@nimiq/utils'; export interface EtherSpecifics { @@ -72,14 +72,14 @@ export class ParsedEtherDirectPaymentOptions extends ParsedPaymentOptions 0 - || recipientType !== undefined && recipientType !== Nimiq.Account.Type.BASIC - || flags !== undefined && flags !== Nimiq.Transaction.Flag.NONE; + || (recipientType !== undefined && recipientType !== Nimiq.Account.Type.BASIC) + || (flags !== undefined && flags !== Nimiq.Transaction.Flag.NONE); // Note that the transaction size can be bigger than this, for example if the sender type the user wants to use // requires an extended transaction or if an extended transaction includes a multi signature proof. The size // is therefore just an estimate. In the majority of cases the estimate will be accurate though and a fee that @@ -102,8 +102,8 @@ export class ParsedNimiqDirectPaymentOptions extends ParsedPaymentOptions { + protocolSpecific: ParsedProtocolSpecificsForCurrency; + amount: number | BigInteger; + expires?: number; + constructor: ParsedPaymentOptionsForCurrencyAndType; + new(options: PaymentOptionsForCurrencyAndType, ...optionalArgs: any[]): + ParsedPaymentOptionsForCurrencyAndType; +} + +export abstract class ParsedPaymentOptions +implements ParsedPaymentOptions { + protected constructor(options: PaymentOptionsForCurrencyAndType) { + if (options.currency !== this.constructor.currency || options.type !== this.constructor.type) { + throw new Error(`Cannot parse given options as ${this.constructor.name}.`); + } + if (!this.isNonNegativeInteger(options.amount)) { + throw new Error('Amount must be a non-negative integer'); + } + this.expires = typeof options.expires === 'number' + ? isMilliseconds(options.expires) + ? options.expires + : options.expires * 1000 + : undefined; + } + + public abstract get currency(): C; + public abstract get type(): T; + public abstract get decimals(): number; + + public get baseUnitAmount(): string { + return new FormattableNumber(this.amount).moveDecimalSeparator(-this.decimals).toString(); + } + + public update

>( + this: P, + options: PaymentOptionsForCurrencyAndType, + ...additionalArgs: any[] + ) { + const parsedOptions = new this.constructor(options as any, ...additionalArgs); // parse to check validity + this.amount = parsedOptions.amount; // amount must exist on all parsed options + this.expires = parsedOptions.expires || this.expires; + for (const key of + Object.keys(parsedOptions.protocolSpecific) as Array) { + if (parsedOptions.protocolSpecific[key] === undefined) continue; + this.protocolSpecific[key] = parsedOptions.protocolSpecific[key]; + } + } + + public abstract raw(): PaymentOptionsForCurrencyAndType; + + protected isNonNegativeInteger(value: string | number | bigint | BigInteger) { + try { + return /^\d+$/.test(toNonScientificNumberString(value)); + } catch (e) { + return false; + } + } +} diff --git a/src/views/SignTransactionLedger.vue b/src/views/SignTransactionLedger.vue index 054919382..d9e6f5de2 100644 --- a/src/views/SignTransactionLedger.vue +++ b/src/views/SignTransactionLedger.vue @@ -85,10 +85,7 @@ import StatusScreen from '../components/StatusScreen.vue'; import { Static } from '../lib/StaticStore'; import { Getter } from 'vuex-class'; import { State as RpcState } from '@nimiq/rpc'; -import { - ParsedCheckoutRequest, - ParsedSignTransactionRequest, -} from '../lib/RequestTypes'; +import { ParsedCheckoutRequest, ParsedSignTransactionRequest } from '../lib/RequestTypes'; import { Currency, RequestType } from '../lib/PublicRequestTypes'; import { WalletInfo } from '../lib/WalletInfo'; import { ERROR_CANCELED, TX_VALIDITY_WINDOW, CASHLINK_FUNDING_DATA } from '../lib/Constants'; @@ -218,7 +215,7 @@ export default class SignTransactionLedger extends Vue { // The next block is the earliest for which tx are accepted by standard miners validityStartHeight = blockchainHeight + 1 - TX_VALIDITY_WINDOW - + nimiqPaymentOption.protocolSpecific.validityDuration!; + + nimiqPaymentOption.protocolSpecific.validityDuration; } else { // this case get's rejected in created return;