From ef8389730ab75336e64a9e315d7edc2423081345 Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Fri, 20 Sep 2024 13:50:13 +0200 Subject: [PATCH 1/9] Proposal. --- Card/Operation/Authorization.ts | 17 ++++++++++------- Card/Operation/Card.ts | 2 +- Card/Operation/Clearing.ts | 22 ++++++++++++++++++++++ Card/Operation/index.ts | 6 +++--- Card/index.ts | 8 +------- 5 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 Card/Operation/Clearing.ts diff --git a/Card/Operation/Authorization.ts b/Card/Operation/Authorization.ts index 338d3e1d..875826e3 100644 --- a/Card/Operation/Authorization.ts +++ b/Card/Operation/Authorization.ts @@ -1,22 +1,25 @@ import { isoly } from "isoly" import { isly } from "isly" +import { Amount } from "../../Amount" + export interface Authorization { type: "authorization" id: string - status: Authorization.Status - reason?: string + event: Authorization.Event created: isoly.DateTime + amount: Amount + reason?: string } - export namespace Authorization { - export const statuses = ["created", "confirmed", "refunded", "captured", "cancelled"] as const - export type Status = typeof statuses[number] + export const events = ["created", "rejected", "refunded", "captured", "cancelled"] as const + export type Event = typeof events[number] export const type = isly.object({ type: isly.string("authorization"), id: isly.string(), - status: isly.string(statuses), - reason: isly.string().optional(), + event: isly.string(events), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + amount: Amount.type, + reason: isly.string().optional(), }) export const is = type.is } diff --git a/Card/Operation/Card.ts b/Card/Operation/Card.ts index f46d8dcc..622a8e67 100644 --- a/Card/Operation/Card.ts +++ b/Card/Operation/Card.ts @@ -5,8 +5,8 @@ import { Changeable } from "../Changeable" export interface Card { type: "card" status: Card.Status - from?: Changeable created: isoly.DateTime + from?: Changeable } export namespace Card { diff --git a/Card/Operation/Clearing.ts b/Card/Operation/Clearing.ts new file mode 100644 index 00000000..1f655b68 --- /dev/null +++ b/Card/Operation/Clearing.ts @@ -0,0 +1,22 @@ +import { isoly } from "isoly" +import { isly } from "isly" +import { Amount } from "../../Amount" + +export interface Clearing { + type: "clearing" + event: Clearing.Event + created: isoly.DateTime + amount: Amount + charge: Amount +} +export namespace Clearing { + export const events = ["captured", "refunded"] as const + export type Event = typeof events[number] + export const type = isly.object({ + type: isly.string("clearing"), + event: isly.string(events), + created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + amount: Amount.type, + charge: Amount.type, + }) +} diff --git a/Card/Operation/index.ts b/Card/Operation/index.ts index 1f11e3d6..de0a9a68 100644 --- a/Card/Operation/index.ts +++ b/Card/Operation/index.ts @@ -10,7 +10,7 @@ export type Operation = Card | OperationAuthorization export namespace Operation { export function fromAuthorization( authorization: Authorization, - status: OperationAuthorization.Status + status: OperationAuthorization.Event ): Operation | undefined { return { type: "authorization", @@ -29,8 +29,8 @@ export namespace Operation { created: isoly.DateTime.now(), } } - export function fromEntryStatus(status: Exclude): OperationAuthorization.Status { - const statusConverter: Record, OperationAuthorization.Status> = { + export function fromEntryStatus(status: Exclude): OperationAuthorization.Event { + const statusConverter: Record, OperationAuthorization.Event> = { capture: "captured", cancel: "cancelled", refund: "refunded", diff --git a/Card/index.ts b/Card/index.ts index f78f8ecf..465fbc8b 100644 --- a/Card/index.ts +++ b/Card/index.ts @@ -23,13 +23,7 @@ export interface Card { preset: CardPreset scheme: CardScheme reference?: string - details: { - iin: string - last4: string - expiry: CardExpiry - holder: string - token?: string - } + details: { iin: string; last4: string; expiry: CardExpiry; holder: string; token?: string } limit: Amount spent: Amount status: "active" | "cancelled" From 1396b3815819408e575cc0cd1cb0fe5ddef96e66 Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Fri, 20 Sep 2024 14:59:51 +0200 Subject: [PATCH 2/9] Meeting. --- Card/{Operation => Event}/Authorization.ts | 9 ++++----- Card/Event/Cancel.ts | 13 +++++++++++++ Card/Event/Capture.ts | 20 ++++++++++++++++++++ Card/{Operation/Card.ts => Event/Change.ts} | 8 ++++---- Card/{Operation => Event}/Clearing.ts | 0 Card/Event/Create.ts | 14 ++++++++++++++ Card/{Operation => Event}/Operation.spec.ts | 0 Card/Event/Refund.ts | 21 +++++++++++++++++++++ Card/{Operation => Event}/index.ts | 18 +++++++++--------- Card/index.ts | 2 +- 10 files changed, 86 insertions(+), 19 deletions(-) rename Card/{Operation => Event}/Authorization.ts (69%) create mode 100644 Card/Event/Cancel.ts create mode 100644 Card/Event/Capture.ts rename Card/{Operation/Card.ts => Event/Change.ts} (82%) rename Card/{Operation => Event}/Clearing.ts (100%) create mode 100644 Card/Event/Create.ts rename Card/{Operation => Event}/Operation.spec.ts (100%) create mode 100644 Card/Event/Refund.ts rename Card/{Operation => Event}/index.ts (68%) diff --git a/Card/Operation/Authorization.ts b/Card/Event/Authorization.ts similarity index 69% rename from Card/Operation/Authorization.ts rename to Card/Event/Authorization.ts index 875826e3..1f7ff242 100644 --- a/Card/Operation/Authorization.ts +++ b/Card/Event/Authorization.ts @@ -5,21 +5,20 @@ import { Amount } from "../../Amount" export interface Authorization { type: "authorization" id: string - event: Authorization.Event + outcome: Authorization.Outcome created: isoly.DateTime amount: Amount reason?: string } export namespace Authorization { - export const events = ["created", "rejected", "refunded", "captured", "cancelled"] as const - export type Event = typeof events[number] + export const outcomes = ["created", "rejected", "cancelled"] as const + export type Outcome = typeof outcomes[number] export const type = isly.object({ type: isly.string("authorization"), id: isly.string(), - event: isly.string(events), + outcome: isly.string(outcomes), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), amount: Amount.type, reason: isly.string().optional(), }) - export const is = type.is } diff --git a/Card/Event/Cancel.ts b/Card/Event/Cancel.ts new file mode 100644 index 00000000..01bc457e --- /dev/null +++ b/Card/Event/Cancel.ts @@ -0,0 +1,13 @@ +import { isoly } from "isoly" +import { isly } from "isly" + +export interface Cancel { + type: "cancel" + created: isoly.DateTime +} +export namespace Cancel { + export const type = isly.object({ + type: isly.string(), + created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + }) +} diff --git a/Card/Event/Capture.ts b/Card/Event/Capture.ts new file mode 100644 index 00000000..36f0150b --- /dev/null +++ b/Card/Event/Capture.ts @@ -0,0 +1,20 @@ +import { isoly } from "isoly" +import { isly } from "isly" +import { Amount } from "../../Amount" + +export interface Capture { + type: "clearing" + created: isoly.DateTime + total: Amount + net: Amount + fee: Amount + charge?: Amount +} +export namespace Capture { + export const type = isly.object({ + type: isly.string("clearing"), + created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + amount: Amount.type, + charge: Amount.type, + }) +} diff --git a/Card/Operation/Card.ts b/Card/Event/Change.ts similarity index 82% rename from Card/Operation/Card.ts rename to Card/Event/Change.ts index 622a8e67..6a940513 100644 --- a/Card/Operation/Card.ts +++ b/Card/Event/Change.ts @@ -2,17 +2,17 @@ import { isoly } from "isoly" import { isly } from "isly" import { Changeable } from "../Changeable" -export interface Card { - type: "card" - status: Card.Status +export interface Change { + type: "change" created: isoly.DateTime from?: Changeable + to?: Changeable } export namespace Card { export const statuses = ["created", "changed", "cancelled"] as const export type Status = typeof statuses[number] - export const type = isly.object({ + export const type = isly.object({ type: isly.string("card"), status: isly.string(statuses), from: Changeable.type.optional(), diff --git a/Card/Operation/Clearing.ts b/Card/Event/Clearing.ts similarity index 100% rename from Card/Operation/Clearing.ts rename to Card/Event/Clearing.ts diff --git a/Card/Event/Create.ts b/Card/Event/Create.ts new file mode 100644 index 00000000..5e924c2c --- /dev/null +++ b/Card/Event/Create.ts @@ -0,0 +1,14 @@ +import { isoly } from "isoly" +import { isly } from "isly" +import { Changeable } from "../Changeable" + +export interface Create { + type: "create" + created: isoly.DateTime +} +export namespace Card { + export const type = isly.object({ + type: isly.string("create"), + created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + }) +} diff --git a/Card/Operation/Operation.spec.ts b/Card/Event/Operation.spec.ts similarity index 100% rename from Card/Operation/Operation.spec.ts rename to Card/Event/Operation.spec.ts diff --git a/Card/Event/Refund.ts b/Card/Event/Refund.ts new file mode 100644 index 00000000..6cd12bff --- /dev/null +++ b/Card/Event/Refund.ts @@ -0,0 +1,21 @@ +import { isoly } from "isoly" +import { isly } from "isly" +import { Amount } from "../../Amount" + +export interface Refund { + type: "refund" + created: isoly.DateTime + total: Amount + net: Amount + fee: Amount + charge?: Amount +} +export namespace Refund { + export const type = isly.object({ + type: isly.string("refund"), + event: isly.string(events), + created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + amount: Amount.type, + charge: Amount.type, + }) +} diff --git a/Card/Operation/index.ts b/Card/Event/index.ts similarity index 68% rename from Card/Operation/index.ts rename to Card/Event/index.ts index de0a9a68..03b8022b 100644 --- a/Card/Operation/index.ts +++ b/Card/Event/index.ts @@ -2,16 +2,16 @@ import { isoly } from "isoly" import { isly } from "isly" import { Authorization } from "../../Authorization" import { Entry } from "../../Settlement/Entry" -import { Authorization as OperationAuthorization } from "./Authorization" -import { Card } from "./Card" +import { Authorization as EventAuthorization } from "./Authorization" +import { Card } from "./Cancel" -export type Operation = Card | OperationAuthorization +export type Event = Card | EventAuthorization export namespace Operation { export function fromAuthorization( authorization: Authorization, - status: OperationAuthorization.Event - ): Operation | undefined { + status: EventAuthorization.Outcome + ): Event | undefined { return { type: "authorization", id: authorization?.id ?? authorization.transaction?.id ?? "unknown", @@ -19,7 +19,7 @@ export namespace Operation { created: isoly.DateTime.now(), } } - export function fromEntry(entry: Entry): Operation | undefined { + export function fromEntry(entry: Entry): Event | undefined { return entry.type == "unknown" ? undefined : { @@ -29,14 +29,14 @@ export namespace Operation { created: isoly.DateTime.now(), } } - export function fromEntryStatus(status: Exclude): OperationAuthorization.Event { - const statusConverter: Record, OperationAuthorization.Event> = { + export function fromEntryStatus(status: Exclude): EventAuthorization.Outcome { + const statusConverter: Record, EventAuthorization.Outcome> = { capture: "captured", cancel: "cancelled", refund: "refunded", } return statusConverter[status] } - export const type = isly.union(Card.type, OperationAuthorization.type) + export const type = isly.union(Card.type, EventAuthorization.type) export const is = type.is } diff --git a/Card/index.ts b/Card/index.ts index 465fbc8b..c1b9b2a6 100644 --- a/Card/index.ts +++ b/Card/index.ts @@ -6,9 +6,9 @@ import { Report } from "../Report" import { Rule, type as ruleType } from "../Rule/Rule" import { Changeable as CardChangeable } from "./Changeable" import { Creatable as CardCreatable } from "./Creatable" +import { Event as CardOperation } from "./Event" import { Expiry as CardExpiry } from "./Expiry" import { Meta as CardMeta } from "./Meta" -import { Operation as CardOperation } from "./Operation" import { Preset as CardPreset } from "./Preset" import { Scheme as CardScheme } from "./Scheme" import { Stack as CardStack } from "./Stack" From f583e278064487daf7d4e58f5f7c9946d84c6d5b Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Mon, 23 Sep 2024 10:12:50 +0200 Subject: [PATCH 3/9] wip --- Card/Event/Capture.ts | 4 ++-- Card/Event/Clearing.ts | 22 ---------------------- 2 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 Card/Event/Clearing.ts diff --git a/Card/Event/Capture.ts b/Card/Event/Capture.ts index 36f0150b..543c4118 100644 --- a/Card/Event/Capture.ts +++ b/Card/Event/Capture.ts @@ -3,7 +3,7 @@ import { isly } from "isly" import { Amount } from "../../Amount" export interface Capture { - type: "clearing" + type: "capture" created: isoly.DateTime total: Amount net: Amount @@ -12,7 +12,7 @@ export interface Capture { } export namespace Capture { export const type = isly.object({ - type: isly.string("clearing"), + type: isly.string("capture"), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), amount: Amount.type, charge: Amount.type, diff --git a/Card/Event/Clearing.ts b/Card/Event/Clearing.ts deleted file mode 100644 index 1f655b68..00000000 --- a/Card/Event/Clearing.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { isoly } from "isoly" -import { isly } from "isly" -import { Amount } from "../../Amount" - -export interface Clearing { - type: "clearing" - event: Clearing.Event - created: isoly.DateTime - amount: Amount - charge: Amount -} -export namespace Clearing { - export const events = ["captured", "refunded"] as const - export type Event = typeof events[number] - export const type = isly.object({ - type: isly.string("clearing"), - event: isly.string(events), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), - amount: Amount.type, - charge: Amount.type, - }) -} From 07acc225c1e2c130c4bd667292dc5dbaa1c073e6 Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Wed, 25 Sep 2024 11:18:58 +0200 Subject: [PATCH 4/9] Finished event types. Added mild backwards compatability. --- Card/Event/Capture.ts | 20 -------------------- Card/Event/Change.ts | 12 ++++-------- Card/Event/Clearing.ts | 24 ++++++++++++++++++++++++ Card/Event/Create.ts | 3 +-- Card/Event/Index.spec.ts | 14 ++++++++++++++ Card/Event/Operation.spec.ts | 13 ------------- Card/Event/Refund.ts | 21 --------------------- Card/Event/index.ts | 19 +++++++++++++------ Card/index.ts | 29 ++++++++++++++++++++++------- 9 files changed, 78 insertions(+), 77 deletions(-) delete mode 100644 Card/Event/Capture.ts create mode 100644 Card/Event/Clearing.ts create mode 100644 Card/Event/Index.spec.ts delete mode 100644 Card/Event/Operation.spec.ts delete mode 100644 Card/Event/Refund.ts diff --git a/Card/Event/Capture.ts b/Card/Event/Capture.ts deleted file mode 100644 index 543c4118..00000000 --- a/Card/Event/Capture.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { isoly } from "isoly" -import { isly } from "isly" -import { Amount } from "../../Amount" - -export interface Capture { - type: "capture" - created: isoly.DateTime - total: Amount - net: Amount - fee: Amount - charge?: Amount -} -export namespace Capture { - export const type = isly.object({ - type: isly.string("capture"), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), - amount: Amount.type, - charge: Amount.type, - }) -} diff --git a/Card/Event/Change.ts b/Card/Event/Change.ts index 6a940513..f47143de 100644 --- a/Card/Event/Change.ts +++ b/Card/Event/Change.ts @@ -8,15 +8,11 @@ export interface Change { from?: Changeable to?: Changeable } - -export namespace Card { - export const statuses = ["created", "changed", "cancelled"] as const - export type Status = typeof statuses[number] +export namespace Change { export const type = isly.object({ - type: isly.string("card"), - status: isly.string(statuses), - from: Changeable.type.optional(), + type: isly.string("change"), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + from: Changeable.type.optional(), + to: Changeable.type.optional(), }) - export const is = type.is } diff --git a/Card/Event/Clearing.ts b/Card/Event/Clearing.ts new file mode 100644 index 00000000..fa2472eb --- /dev/null +++ b/Card/Event/Clearing.ts @@ -0,0 +1,24 @@ +import { isoly } from "isoly" +import { isly } from "isly" +import { Amount } from "../../Amount" + +export interface Clearing { + type: Clearing.Type + created: isoly.DateTime + total: Amount + net: Amount + fee: Amount + charge?: Amount +} +export namespace Clearing { + export const types = ["capture", "refund"] as const + export type Type = typeof types[number] + export const type = isly.object({ + type: isly.string(types), + created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + total: Amount.type, + net: Amount.type, + fee: Amount.type, + charge: Amount.type.optional(), + }) +} diff --git a/Card/Event/Create.ts b/Card/Event/Create.ts index 5e924c2c..a5ace315 100644 --- a/Card/Event/Create.ts +++ b/Card/Event/Create.ts @@ -1,12 +1,11 @@ import { isoly } from "isoly" import { isly } from "isly" -import { Changeable } from "../Changeable" export interface Create { type: "create" created: isoly.DateTime } -export namespace Card { +export namespace Create { export const type = isly.object({ type: isly.string("create"), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), diff --git a/Card/Event/Index.spec.ts b/Card/Event/Index.spec.ts new file mode 100644 index 00000000..76b589cd --- /dev/null +++ b/Card/Event/Index.spec.ts @@ -0,0 +1,14 @@ +import { pax2pay } from "../../index" + +describe("Card.Event", () => { + const event: pax2pay.Card.Event = { + type: "authorization", + id: "19236hf", + outcome: "created", + created: "2023-12-13T12:11:00.000Z", + amount: ["UAH", 9999], + } + it("authorization", () => { + expect(pax2pay.Card.Event.type.is(event)).toBeTruthy() + }) +}) diff --git a/Card/Event/Operation.spec.ts b/Card/Event/Operation.spec.ts deleted file mode 100644 index 58fdf7f3..00000000 --- a/Card/Event/Operation.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { pax2pay } from "../../index" - -describe("Card.Operation", () => { - const operation: pax2pay.Card.Operation = { - type: "authorization", - id: "19236hf", - status: pax2pay.Card.Operation.fromEntryStatus("capture"), - created: "2023-12-13T12:11:00.000Z", - } - it("authorization", () => { - expect(pax2pay.Card.Operation.is(operation)).toBeTruthy() - }) -}) diff --git a/Card/Event/Refund.ts b/Card/Event/Refund.ts deleted file mode 100644 index 6cd12bff..00000000 --- a/Card/Event/Refund.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { isoly } from "isoly" -import { isly } from "isly" -import { Amount } from "../../Amount" - -export interface Refund { - type: "refund" - created: isoly.DateTime - total: Amount - net: Amount - fee: Amount - charge?: Amount -} -export namespace Refund { - export const type = isly.object({ - type: isly.string("refund"), - event: isly.string(events), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), - amount: Amount.type, - charge: Amount.type, - }) -} diff --git a/Card/Event/index.ts b/Card/Event/index.ts index 03b8022b..44eb46f4 100644 --- a/Card/Event/index.ts +++ b/Card/Event/index.ts @@ -3,11 +3,20 @@ import { isly } from "isly" import { Authorization } from "../../Authorization" import { Entry } from "../../Settlement/Entry" import { Authorization as EventAuthorization } from "./Authorization" -import { Card } from "./Cancel" +import { Cancel as EventCancel } from "./Cancel" +import { Change as EventChange } from "./Change" +import { Clearing as EventClearing } from "./Clearing" +import { Create as EventCreate } from "./Create" -export type Event = Card | EventAuthorization +export type Event = Event.Create | Event.Cancel | Event.Change | Event.Authorization | Event.Clearing -export namespace Operation { +export namespace Event { + export import Create = EventCreate + export import Cancel = EventCancel + export import Change = EventChange + export import Authorization = EventAuthorization + export import Clearing = EventClearing + export const type = isly.union(Create.type, Cancel.type, Change.type, Authorization.type, Clearing.type) export function fromAuthorization( authorization: Authorization, status: EventAuthorization.Outcome @@ -25,7 +34,7 @@ export namespace Operation { : { type: "authorization", id: (entry.type != "refund" ? entry.authorization?.id : entry.transaction?.id) ?? "unknown", - status: Operation.fromEntryStatus(entry.type), + status: Event.fromEntryStatus(entry.type), created: isoly.DateTime.now(), } } @@ -37,6 +46,4 @@ export namespace Operation { } return statusConverter[status] } - export const type = isly.union(Card.type, EventAuthorization.type) - export const is = type.is } diff --git a/Card/index.ts b/Card/index.ts index c1b9b2a6..59350a01 100644 --- a/Card/index.ts +++ b/Card/index.ts @@ -6,7 +6,7 @@ import { Report } from "../Report" import { Rule, type as ruleType } from "../Rule/Rule" import { Changeable as CardChangeable } from "./Changeable" import { Creatable as CardCreatable } from "./Creatable" -import { Event as CardOperation } from "./Event" +import { Event as CardEvent } from "./Event" import { Expiry as CardExpiry } from "./Expiry" import { Meta as CardMeta } from "./Meta" import { Preset as CardPreset } from "./Preset" @@ -27,7 +27,7 @@ export interface Card { limit: Amount spent: Amount status: "active" | "cancelled" - history: CardOperation[] + history: CardEvent[] rules: Rule[] meta?: CardMeta } @@ -52,7 +52,7 @@ export namespace Card { limit: isly.tuple(isly.fromIs("isoly.Currency", isoly.Currency.is), isly.number()), spent: isly.tuple(isly.fromIs("isoly.Currency", isoly.Currency.is), isly.number()), status: isly.union(isly.string("active"), isly.string("cancelled")), - history: isly.array(CardOperation.type), + history: isly.union(CardEvent.type, isly.any()).array(), rules: ruleType.array(), meta: isly.fromIs("Card.Meta", CardMeta.is).optional(), }) @@ -62,13 +62,18 @@ export namespace Card { export import Meta = CardMeta export import Expiry = CardExpiry export import Changeable = CardChangeable - export import Operation = CardOperation + export import Event = CardEvent export import Scheme = CardScheme export import Stack = CardStack const csvMap: Record string | number | undefined> = { id: card => card.id, created: card => readableDate(card.created), - cancelled: card => readableDate(card.history.find(o => o.type == "card" && o.status == "cancelled")?.created), + cancelled: card => + readableDate( + card.history.find( + o => o.type == "create" || ((o as any).type == "card" && "status" in o && o.status == "cancelled") // as any for legacy reasons + )?.created + ), "organization.code": card => card.organization, realm: card => card.realm, account: card => card.account, @@ -82,8 +87,18 @@ export namespace Card { expiry: card => readableDate(Expiry.toDateTime(card.details.expiry)), iin: card => card.details.iin, holder: card => card.details.holder, - "authorization.count": card => card.history.filter(o => o.type == "authorization" && o.status == "created").length, - "capture.count": card => card.history.filter(o => o.type == "authorization" && o.status == "captured").length, + "authorization.count": card => + card.history.filter( + o => + (o.type == "authorization" && "status" in o && o.status == "created") || // for legacy reasons + (o.type == "authorization" && o.outcome == "created") + ).length, + "capture.count": card => + card.history.filter( + o => + (o.type == "authorization" && "status" in o && o.status == "created") || // for legacy reasons + o.type == "capture" + ).length, } function readableDate(date: isoly.DateTime | undefined): string | undefined { return date && date.slice(0, 10) + " " + (date.endsWith("Z") ? date.slice(11, -1) : date.slice(11)) From 16f388e1be96637bc8e8d5f268546ef53fb27242 Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Wed, 25 Sep 2024 12:48:08 +0200 Subject: [PATCH 5/9] Fixed card even from functions. --- Card/Event/index.ts | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/Card/Event/index.ts b/Card/Event/index.ts index 44eb46f4..08383099 100644 --- a/Card/Event/index.ts +++ b/Card/Event/index.ts @@ -1,7 +1,7 @@ import { isoly } from "isoly" import { isly } from "isly" -import { Authorization } from "../../Authorization" -import { Entry } from "../../Settlement/Entry" +import type { Authorization as ModelAuthorization } from "../../Authorization" +import type { Entry } from "../../Settlement/Entry" import { Authorization as EventAuthorization } from "./Authorization" import { Cancel as EventCancel } from "./Cancel" import { Change as EventChange } from "./Change" @@ -16,34 +16,30 @@ export namespace Event { export import Change = EventChange export import Authorization = EventAuthorization export import Clearing = EventClearing - export const type = isly.union(Create.type, Cancel.type, Change.type, Authorization.type, Clearing.type) - export function fromAuthorization( - authorization: Authorization, - status: EventAuthorization.Outcome - ): Event | undefined { + export const type = isly.union(Create.type, Cancel.type, Change.type, Event.Authorization.type, Clearing.type) + export function fromAuthorization(authorization: ModelAuthorization): Event { return { type: "authorization", - id: authorization?.id ?? authorization.transaction?.id ?? "unknown", - status, - created: isoly.DateTime.now(), + id: authorization.id, + outcome: authorization.status != "approved" ? "rejected" : "created", + created: authorization.created, + reason: authorization.status == "approved" ? undefined : authorization.status, + amount: authorization.amount, // FIXME: we need the total transaction amount on auth } } export function fromEntry(entry: Entry): Event | undefined { - return entry.type == "unknown" + return entry.type == "unknown" || entry.type == "cancel" ? undefined : { - type: "authorization", - id: (entry.type != "refund" ? entry.authorization?.id : entry.transaction?.id) ?? "unknown", - status: Event.fromEntryStatus(entry.type), + type: entry.type, created: isoly.DateTime.now(), + net: entry.amount, + fee: [entry.amount[0], entry.fee.other[entry.amount[0]] ?? 0], + total: [ + entry.amount[0], + isoly.Currency.add(entry.amount[0], entry.amount[1], entry.fee.other[entry.amount[0]] ?? 0), // FIXME: this computation should probably be done in entry + ], + // FIXME: charge: entry.charge, } } - export function fromEntryStatus(status: Exclude): EventAuthorization.Outcome { - const statusConverter: Record, EventAuthorization.Outcome> = { - capture: "captured", - cancel: "cancelled", - refund: "refunded", - } - return statusConverter[status] - } } From 0e7ecc2caa9cde196386203221edb1f17c709f64 Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Thu, 26 Sep 2024 17:23:53 +0200 Subject: [PATCH 6/9] Add events array to card so history can be kept for legacy reasons. --- Card/index.ts | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/Card/index.ts b/Card/index.ts index 59350a01..5002fe69 100644 --- a/Card/index.ts +++ b/Card/index.ts @@ -27,7 +27,8 @@ export interface Card { limit: Amount spent: Amount status: "active" | "cancelled" - history: CardEvent[] + history: any[] + events?: Card.Event[] rules: Rule[] meta?: CardMeta } @@ -52,7 +53,8 @@ export namespace Card { limit: isly.tuple(isly.fromIs("isoly.Currency", isoly.Currency.is), isly.number()), spent: isly.tuple(isly.fromIs("isoly.Currency", isoly.Currency.is), isly.number()), status: isly.union(isly.string("active"), isly.string("cancelled")), - history: isly.union(CardEvent.type, isly.any()).array(), + history: isly.any(), + events: CardEvent.type.array().optional(), rules: ruleType.array(), meta: isly.fromIs("Card.Meta", CardMeta.is).optional(), }) @@ -68,12 +70,16 @@ export namespace Card { const csvMap: Record string | number | undefined> = { id: card => card.id, created: card => readableDate(card.created), - cancelled: card => - readableDate( - card.history.find( - o => o.type == "create" || ((o as any).type == "card" && "status" in o && o.status == "cancelled") // as any for legacy reasons - )?.created - ), + cancelled: card => { + if (card.events) { + return readableDate(card.events.find(o => o.type == "cancel")?.created) + } else { + // for legacy reasons + return readableDate( + card.history?.find(o => o.type == "card" && "status" in o && o.status == "cancelled")?.created + ) + } + }, "organization.code": card => card.organization, realm: card => card.realm, account: card => card.account, @@ -87,18 +93,18 @@ export namespace Card { expiry: card => readableDate(Expiry.toDateTime(card.details.expiry)), iin: card => card.details.iin, holder: card => card.details.holder, - "authorization.count": card => - card.history.filter( - o => - (o.type == "authorization" && "status" in o && o.status == "created") || // for legacy reasons - (o.type == "authorization" && o.outcome == "created") - ).length, - "capture.count": card => - card.history.filter( - o => - (o.type == "authorization" && "status" in o && o.status == "created") || // for legacy reasons - o.type == "capture" - ).length, + "authorization.count": card => { + const authorizations = card.events?.filter(o => o.type == "authorization" && o.outcome == "created").length ?? 0 + const legacy = + card.history?.filter(o => o.type == "authorization" && "status" in o && o.status == "created").length ?? 0 + return authorizations + legacy + }, + "capture.count": card => { + const captures = card.events?.filter(o => o.type == "capture").length ?? 0 + const legacy = + card.history?.filter(o => o.type == "authorization" && "status" in o && o.status == "captured").length ?? 0 + return captures + legacy + }, } function readableDate(date: isoly.DateTime | undefined): string | undefined { return date && date.slice(0, 10) + " " + (date.endsWith("Z") ? date.slice(11, -1) : date.slice(11)) From 9e634fc226cec810793471ea7b50140992e97ce7 Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Thu, 26 Sep 2024 18:08:04 +0200 Subject: [PATCH 7/9] Some requested changes. --- Card/Event/Change.ts | 8 ++++---- Card/Event/Create.ts | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Card/Event/Change.ts b/Card/Event/Change.ts index f47143de..4fcf7f0c 100644 --- a/Card/Event/Change.ts +++ b/Card/Event/Change.ts @@ -5,14 +5,14 @@ import { Changeable } from "../Changeable" export interface Change { type: "change" created: isoly.DateTime - from?: Changeable - to?: Changeable + from: Changeable + to: Changeable } export namespace Change { export const type = isly.object({ type: isly.string("change"), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), - from: Changeable.type.optional(), - to: Changeable.type.optional(), + from: Changeable.type, + to: Changeable.type, }) } diff --git a/Card/Event/Create.ts b/Card/Event/Create.ts index a5ace315..557851eb 100644 --- a/Card/Event/Create.ts +++ b/Card/Event/Create.ts @@ -1,13 +1,16 @@ import { isoly } from "isoly" import { isly } from "isly" +import { Amount } from "../../Amount" export interface Create { type: "create" created: isoly.DateTime + limit: Amount } export namespace Create { export const type = isly.object({ type: isly.string("create"), created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + limit: Amount.type, }) } From 6719656aef436f0cd53be46e430de5831a78510c Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Tue, 1 Oct 2024 17:15:22 +0200 Subject: [PATCH 8/9] Event.created -> at. --- Card/Event/Authorization.ts | 4 ++-- Card/Event/Cancel.ts | 4 ++-- Card/Event/Change.ts | 4 ++-- Card/Event/Clearing.ts | 4 ++-- Card/Event/Create.ts | 4 ++-- Card/Event/index.ts | 4 ++-- Card/index.ts | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Card/Event/Authorization.ts b/Card/Event/Authorization.ts index 1f7ff242..d6f8e372 100644 --- a/Card/Event/Authorization.ts +++ b/Card/Event/Authorization.ts @@ -6,7 +6,7 @@ export interface Authorization { type: "authorization" id: string outcome: Authorization.Outcome - created: isoly.DateTime + at: isoly.DateTime amount: Amount reason?: string } @@ -17,7 +17,7 @@ export namespace Authorization { type: isly.string("authorization"), id: isly.string(), outcome: isly.string(outcomes), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + at: isly.fromIs("isoly.DateTime", isoly.DateTime.is), amount: Amount.type, reason: isly.string().optional(), }) diff --git a/Card/Event/Cancel.ts b/Card/Event/Cancel.ts index 01bc457e..f1038dca 100644 --- a/Card/Event/Cancel.ts +++ b/Card/Event/Cancel.ts @@ -3,11 +3,11 @@ import { isly } from "isly" export interface Cancel { type: "cancel" - created: isoly.DateTime + at: isoly.DateTime } export namespace Cancel { export const type = isly.object({ type: isly.string(), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + at: isly.fromIs("isoly.DateTime", isoly.DateTime.is), }) } diff --git a/Card/Event/Change.ts b/Card/Event/Change.ts index 4fcf7f0c..6494f2ee 100644 --- a/Card/Event/Change.ts +++ b/Card/Event/Change.ts @@ -4,14 +4,14 @@ import { Changeable } from "../Changeable" export interface Change { type: "change" - created: isoly.DateTime + at: isoly.DateTime from: Changeable to: Changeable } export namespace Change { export const type = isly.object({ type: isly.string("change"), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + at: isly.fromIs("isoly.DateTime", isoly.DateTime.is), from: Changeable.type, to: Changeable.type, }) diff --git a/Card/Event/Clearing.ts b/Card/Event/Clearing.ts index fa2472eb..01f7a3c9 100644 --- a/Card/Event/Clearing.ts +++ b/Card/Event/Clearing.ts @@ -4,7 +4,7 @@ import { Amount } from "../../Amount" export interface Clearing { type: Clearing.Type - created: isoly.DateTime + at: isoly.DateTime total: Amount net: Amount fee: Amount @@ -15,7 +15,7 @@ export namespace Clearing { export type Type = typeof types[number] export const type = isly.object({ type: isly.string(types), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + at: isly.fromIs("isoly.DateTime", isoly.DateTime.is), total: Amount.type, net: Amount.type, fee: Amount.type, diff --git a/Card/Event/Create.ts b/Card/Event/Create.ts index 557851eb..3e947d2c 100644 --- a/Card/Event/Create.ts +++ b/Card/Event/Create.ts @@ -4,13 +4,13 @@ import { Amount } from "../../Amount" export interface Create { type: "create" - created: isoly.DateTime + at: isoly.DateTime limit: Amount } export namespace Create { export const type = isly.object({ type: isly.string("create"), - created: isly.fromIs("isoly.DateTime", isoly.DateTime.is), + at: isly.fromIs("isoly.DateTime", isoly.DateTime.is), limit: Amount.type, }) } diff --git a/Card/Event/index.ts b/Card/Event/index.ts index 08383099..9a593dfe 100644 --- a/Card/Event/index.ts +++ b/Card/Event/index.ts @@ -22,7 +22,7 @@ export namespace Event { type: "authorization", id: authorization.id, outcome: authorization.status != "approved" ? "rejected" : "created", - created: authorization.created, + at: authorization.created, reason: authorization.status == "approved" ? undefined : authorization.status, amount: authorization.amount, // FIXME: we need the total transaction amount on auth } @@ -32,7 +32,7 @@ export namespace Event { ? undefined : { type: entry.type, - created: isoly.DateTime.now(), + at: isoly.DateTime.now(), net: entry.amount, fee: [entry.amount[0], entry.fee.other[entry.amount[0]] ?? 0], total: [ diff --git a/Card/index.ts b/Card/index.ts index 1f056bee..5799780f 100644 --- a/Card/index.ts +++ b/Card/index.ts @@ -73,7 +73,7 @@ export namespace Card { created: card => readableDate(card.created), cancelled: card => { if (card.events) { - return readableDate(card.events.find(o => o.type == "cancel")?.created) + return readableDate(card.events.find(o => o.type == "cancel")?.at) } else { // for legacy reasons return readableDate( From 73706359a3bc39e4c5c4cc58d38710d43879e7ba Mon Sep 17 00:00:00 2001 From: Anton Kroner Date: Tue, 1 Oct 2024 18:09:54 +0200 Subject: [PATCH 9/9] Amount changes. --- Card/Event/Clearing.ts | 19 ++++++++++--------- Card/Event/index.ts | 10 ++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Card/Event/Clearing.ts b/Card/Event/Clearing.ts index 01f7a3c9..5769e395 100644 --- a/Card/Event/Clearing.ts +++ b/Card/Event/Clearing.ts @@ -1,14 +1,14 @@ import { isoly } from "isoly" import { isly } from "isly" -import { Amount } from "../../Amount" export interface Clearing { type: Clearing.Type at: isoly.DateTime - total: Amount - net: Amount - fee: Amount - charge?: Amount + currency: isoly.Currency + total: number + net: number + fee: number + charge?: number } export namespace Clearing { export const types = ["capture", "refund"] as const @@ -16,9 +16,10 @@ export namespace Clearing { export const type = isly.object({ type: isly.string(types), at: isly.fromIs("isoly.DateTime", isoly.DateTime.is), - total: Amount.type, - net: Amount.type, - fee: Amount.type, - charge: Amount.type.optional(), + currency: isly.string(), + total: isly.number(), + net: isly.number(), + fee: isly.number(), + charge: isly.number().optional(), }) } diff --git a/Card/Event/index.ts b/Card/Event/index.ts index 9a593dfe..f2d7813b 100644 --- a/Card/Event/index.ts +++ b/Card/Event/index.ts @@ -33,12 +33,10 @@ export namespace Event { : { type: entry.type, at: isoly.DateTime.now(), - net: entry.amount, - fee: [entry.amount[0], entry.fee.other[entry.amount[0]] ?? 0], - total: [ - entry.amount[0], - isoly.Currency.add(entry.amount[0], entry.amount[1], entry.fee.other[entry.amount[0]] ?? 0), // FIXME: this computation should probably be done in entry - ], + currency: entry.amount[0], + net: entry.amount[1], + fee: entry.fee.other[entry.amount[0]] ?? 0, + total: isoly.Currency.add(entry.amount[0], entry.amount[1], entry.fee.other[entry.amount[0]] ?? 0), // FIXME: this computation should probably be done in entry // FIXME: charge: entry.charge, } }