Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { verifyCallback } from './callback'
import { Currency, Language } from './enums'
import { TransportError } from './errors'
import type { EpointCallback, EpointResponse } from './response'
import { buildBody, type Payload, parse } from './transport'
Expand Down Expand Up @@ -39,8 +40,8 @@ export class EpointClient {
this.publicKey = options.publicKey
this.privateKey = options.privateKey
this.baseUrl = (options.baseUrl ?? PRODUCTION_URL).replace(/\/+$/, '')
this.language = options.language ?? 'az'
this.currency = options.currency ?? 'AZN'
this.language = options.language ?? Language.AZ
this.currency = options.currency ?? Currency.AZN
this.successRedirectUrl = options.successRedirectUrl
this.errorRedirectUrl = options.errorRedirectUrl
this.timeoutMs = options.timeoutMs ?? 30_000
Expand All @@ -58,7 +59,7 @@ export class EpointClient {
publicKey,
privateKey,
baseUrl: env.EPOINT_BASE_URL ?? PRODUCTION_URL,
language: env.EPOINT_LANGUAGE ?? 'az',
language: env.EPOINT_LANGUAGE ?? Language.AZ,
successRedirectUrl: env.EPOINT_SUCCESS_REDIRECT_URL,
errorRedirectUrl: env.EPOINT_FAILED_REDIRECT_URL,
...overrides,
Expand Down
75 changes: 75 additions & 0 deletions src/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/** The status on a response or a callback. */
export const Status = {
NEW: 'new',
SUCCESS: 'success',
FAILED: 'failed',
ERROR: 'error',
RETURNED: 'returned',
SERVER_ERROR: 'server_error',
} as const

export type Status = (typeof Status)[keyof typeof Status]

export const SETTLED_STATUSES: ReadonlySet<string> = new Set([Status.SUCCESS])

export const CardStatus = {
NEW: 'new',
ACTIVE: 'active',
PENDING: 'pending',
REJECTED: 'rejected',
EXPIRED: 'expired',
SESSION_EXPIRED: 'session_expired',
} as const

export type CardStatus = (typeof CardStatus)[keyof typeof CardStatus]

export const USABLE_CARD_STATUSES: ReadonlySet<string> = new Set([CardStatus.ACTIVE])

export const InvoiceStatus = {
WAITING: 'waiting_for_payment',
PAID: 'paid',
CANCELED: 'canceled',
} as const

export type InvoiceStatus = (typeof InvoiceStatus)[keyof typeof InvoiceStatus]

export const B2BStatus = {
PENDING: 'PENDING',
PROCESSING: 'PROCESSING',
SUCCESS: 'SUCCESS',
FAILED: 'FAILED',
} as const

export type B2BStatus = (typeof B2BStatus)[keyof typeof B2BStatus]

/** What the transaction did, as it comes back on a callback. */
export const OperationCode = {
CARD_REGISTRATION: '001',
PAYMENT: '100',
REGISTRATION_WITH_PAYMENT: '200',
} as const

export type OperationCode = (typeof OperationCode)[keyof typeof OperationCode]

export const Language = {
AZ: 'az',
EN: 'en',
RU: 'ru',
} as const

export type Language = (typeof Language)[keyof typeof Language]

export const Currency = {
AZN: 'AZN',
USD: 'USD',
EUR: 'EUR',
RUB: 'RUB',
} as const

export type Currency = (typeof Currency)[keyof typeof Currency]

/** Checkout takes any of these. */
export const SUPPORTED_CURRENCIES: ReadonlySet<string> = new Set(Object.values(Currency))

/** Split, pre-auth, refund, reverse, payout and wallet take AZN and nothing else. */
export const AZN_ONLY: ReadonlySet<string> = new Set([Currency.AZN])
13 changes: 13 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
export { verifyCallback } from './callback'
export { EpointClient } from './client'
export {
AZN_ONLY,
B2BStatus,
CardStatus,
Currency,
InvoiceStatus,
Language,
OperationCode,
SETTLED_STATUSES,
Status,
SUPPORTED_CURRENCIES,
USABLE_CARD_STATUSES,
} from './enums'
export { EpointError, GatewayError, SignatureError, TransportError } from './errors'
export { type EpointCallback, EpointResponse } from './response'
export type {
Expand Down
6 changes: 4 additions & 2 deletions src/response.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { SETTLED_STATUSES } from './enums'

function asString(value: unknown): string | undefined {
return value === undefined || value === null ? undefined : String(value)
}
Expand All @@ -10,7 +12,7 @@ export class EpointResponse {
}

get ok(): boolean {
return this.raw.status === 'success'
return SETTLED_STATUSES.has(String(this.raw.status))
}

get transaction(): string | undefined {
Expand Down Expand Up @@ -62,7 +64,7 @@ export function toCallback(raw: Record<string, unknown>): EpointCallback {
cardName: (raw.card_name as string | null) ?? null,
amount: raw.amount == null ? 0 : Number(raw.amount),
otherAttr: raw.other_attr,
ok: status === 'success',
ok: SETTLED_STATUSES.has(status),
raw,
}
}
3 changes: 2 additions & 1 deletion src/transport.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Status } from './enums'
import { GatewayError, TransportError } from './errors'
import { EpointResponse } from './response'
import { encodePayload, sign } from './signing'
Expand Down Expand Up @@ -27,7 +28,7 @@ export function parse(statusCode: number, body: unknown): EpointResponse {
}

const record = body as Payload
if (record.status === 'error') {
if (record.status === Status.ERROR) {
throw new GatewayError(String(record.message ?? 'request refused'), {
code: record.code == null ? undefined : String(record.code),
status: String(record.status),
Expand Down
17 changes: 17 additions & 0 deletions test/enums.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, expect, it } from 'vitest'
import { Currency, EpointResponse, Language, OperationCode, Status } from '../src/index'

describe('enums', () => {
it('are plain strings on the wire', () => {
expect(JSON.stringify({ language: Language.AZ, currency: Currency.AZN })).toBe(
'{"language":"az","currency":"AZN"}',
)
expect(OperationCode.PAYMENT).toBe('100')
expect(Status.SERVER_ERROR).toBe('server_error')
})

it('still reads a raw status string as ok', () => {
expect(new EpointResponse({ status: 'success' }).ok).toBe(true)
expect(new EpointResponse({ status: 'failed' }).ok).toBe(false)
})
})
Loading