@laranex/myanmar-payments 0.0.0-stage → 4.0.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/LICENSE.md +21 -0
- package/README.md +10 -2
- package/dist/cjs/aya-pay/aya-pay.d.ts +122 -0
- package/dist/cjs/aya-pay/aya-pay.js +301 -0
- package/dist/cjs/aya-pay/index.d.ts +1 -0
- package/dist/cjs/aya-pay/index.js +8 -0
- package/dist/cjs/core/amount.d.ts +57 -0
- package/dist/cjs/core/amount.js +107 -0
- package/dist/cjs/core/cache.d.ts +26 -0
- package/dist/cjs/core/cache.js +35 -0
- package/dist/cjs/core/callback.d.ts +180 -0
- package/dist/cjs/core/callback.js +304 -0
- package/dist/cjs/core/crypto.d.ts +19 -0
- package/dist/cjs/core/crypto.js +50 -0
- package/dist/cjs/core/env.d.ts +21 -0
- package/dist/cjs/core/env.js +54 -0
- package/dist/cjs/core/errors.d.ts +56 -0
- package/dist/cjs/core/errors.js +78 -0
- package/dist/cjs/core/http.d.ts +81 -0
- package/dist/cjs/core/http.js +128 -0
- package/dist/cjs/core/index.d.ts +8 -0
- package/dist/cjs/core/index.js +30 -0
- package/dist/cjs/core/json.d.ts +44 -0
- package/dist/cjs/core/json.js +254 -0
- package/dist/cjs/core/results.d.ts +122 -0
- package/dist/cjs/core/results.js +147 -0
- package/dist/cjs/core/status.d.ts +50 -0
- package/dist/cjs/core/status.js +60 -0
- package/dist/cjs/core/validate.d.ts +46 -0
- package/dist/cjs/core/validate.js +148 -0
- package/dist/cjs/core/values.d.ts +16 -0
- package/dist/cjs/core/values.js +45 -0
- package/dist/cjs/cyber-source/cyber-source.d.ts +95 -0
- package/dist/cjs/cyber-source/cyber-source.js +188 -0
- package/dist/cjs/cyber-source/index.d.ts +1 -0
- package/dist/cjs/cyber-source/index.js +7 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.js +25 -0
- package/dist/cjs/kbz-pay/index.d.ts +2 -0
- package/dist/cjs/kbz-pay/index.js +8 -0
- package/dist/cjs/kbz-pay/kbz-pay.d.ts +102 -0
- package/dist/cjs/kbz-pay/kbz-pay.js +266 -0
- package/dist/cjs/kbz-pay/signer.d.ts +15 -0
- package/dist/cjs/kbz-pay/signer.js +36 -0
- package/dist/cjs/myanmar-payments.d.ts +50 -0
- package/dist/cjs/myanmar-payments.js +73 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/wave-money/index.d.ts +1 -0
- package/dist/cjs/wave-money/index.js +6 -0
- package/dist/cjs/wave-money/wave-money.d.ts +111 -0
- package/dist/cjs/wave-money/wave-money.js +251 -0
- package/dist/cjs/yoma-mmqr/index.d.ts +1 -0
- package/dist/cjs/yoma-mmqr/index.js +6 -0
- package/dist/cjs/yoma-mmqr/yoma-mmqr.d.ts +105 -0
- package/dist/cjs/yoma-mmqr/yoma-mmqr.js +233 -0
- package/dist/esm/aya-pay/aya-pay.d.ts +122 -0
- package/dist/esm/aya-pay/aya-pay.js +296 -0
- package/dist/esm/aya-pay/index.d.ts +1 -0
- package/dist/esm/aya-pay/index.js +1 -0
- package/dist/esm/core/amount.d.ts +57 -0
- package/dist/esm/core/amount.js +103 -0
- package/dist/esm/core/cache.d.ts +26 -0
- package/dist/esm/core/cache.js +31 -0
- package/dist/esm/core/callback.d.ts +180 -0
- package/dist/esm/core/callback.js +294 -0
- package/dist/esm/core/crypto.d.ts +19 -0
- package/dist/esm/core/crypto.js +41 -0
- package/dist/esm/core/env.d.ts +21 -0
- package/dist/esm/core/env.js +45 -0
- package/dist/esm/core/errors.d.ts +56 -0
- package/dist/esm/core/errors.js +70 -0
- package/dist/esm/core/http.d.ts +81 -0
- package/dist/esm/core/http.js +121 -0
- package/dist/esm/core/index.d.ts +8 -0
- package/dist/esm/core/index.js +7 -0
- package/dist/esm/core/json.d.ts +44 -0
- package/dist/esm/core/json.js +244 -0
- package/dist/esm/core/results.d.ts +122 -0
- package/dist/esm/core/results.js +139 -0
- package/dist/esm/core/status.d.ts +50 -0
- package/dist/esm/core/status.js +56 -0
- package/dist/esm/core/validate.d.ts +46 -0
- package/dist/esm/core/validate.js +142 -0
- package/dist/esm/core/values.d.ts +16 -0
- package/dist/esm/core/values.js +38 -0
- package/dist/esm/cyber-source/cyber-source.d.ts +95 -0
- package/dist/esm/cyber-source/cyber-source.js +184 -0
- package/dist/esm/cyber-source/index.d.ts +1 -0
- package/dist/esm/cyber-source/index.js +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/kbz-pay/index.d.ts +2 -0
- package/dist/esm/kbz-pay/index.js +2 -0
- package/dist/esm/kbz-pay/kbz-pay.d.ts +102 -0
- package/dist/esm/kbz-pay/kbz-pay.js +262 -0
- package/dist/esm/kbz-pay/signer.d.ts +15 -0
- package/dist/esm/kbz-pay/signer.js +32 -0
- package/dist/esm/myanmar-payments.d.ts +50 -0
- package/dist/esm/myanmar-payments.js +69 -0
- package/dist/esm/wave-money/index.d.ts +1 -0
- package/dist/esm/wave-money/index.js +1 -0
- package/dist/esm/wave-money/wave-money.d.ts +111 -0
- package/dist/esm/wave-money/wave-money.js +246 -0
- package/dist/esm/yoma-mmqr/index.d.ts +1 -0
- package/dist/esm/yoma-mmqr/index.js +1 -0
- package/dist/esm/yoma-mmqr/yoma-mmqr.d.ts +105 -0
- package/dist/esm/yoma-mmqr/yoma-mmqr.js +229 -0
- package/package.json +151 -4
- package/skills/node-myanmar-payments/SKILL.md +109 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KBZ Pay's signature: every non-empty scalar field except `sign` and `sign_type`, sorted by key,
|
|
3
|
+
* joined as raw `key=value` pairs (not URL-encoded), with `&key=<app key>` appended, hashed with
|
|
4
|
+
* SHA-256 and uppercased.
|
|
5
|
+
*/
|
|
6
|
+
export declare class KbzPaySigner {
|
|
7
|
+
private readonly appKey;
|
|
8
|
+
constructor(appKey: string);
|
|
9
|
+
/** The sorted `key=value` string, without the app key. */
|
|
10
|
+
signString(fields: Readonly<Record<string, unknown>>): string;
|
|
11
|
+
/** The uppercase SHA-256 signature of `fields`. */
|
|
12
|
+
sign(fields: Readonly<Record<string, unknown>>): string;
|
|
13
|
+
/** Whether `fields.sign` matches, compared in constant time. */
|
|
14
|
+
verify(fields: Readonly<Record<string, unknown>>): boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { safeEqual, sha256Hex } from '../core/crypto.js';
|
|
2
|
+
import { scalarString } from '../core/values.js';
|
|
3
|
+
/**
|
|
4
|
+
* KBZ Pay's signature: every non-empty scalar field except `sign` and `sign_type`, sorted by key,
|
|
5
|
+
* joined as raw `key=value` pairs (not URL-encoded), with `&key=<app key>` appended, hashed with
|
|
6
|
+
* SHA-256 and uppercased.
|
|
7
|
+
*/
|
|
8
|
+
export class KbzPaySigner {
|
|
9
|
+
appKey;
|
|
10
|
+
constructor(appKey) {
|
|
11
|
+
this.appKey = appKey;
|
|
12
|
+
}
|
|
13
|
+
/** The sorted `key=value` string, without the app key. */
|
|
14
|
+
signString(fields) {
|
|
15
|
+
return Object.keys(fields)
|
|
16
|
+
.filter((key) => key !== 'sign' && key !== 'sign_type')
|
|
17
|
+
.map((key) => [key, scalarString(fields[key])])
|
|
18
|
+
.filter((pair) => pair[1] !== undefined && pair[1] !== '')
|
|
19
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
20
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
21
|
+
.join('&');
|
|
22
|
+
}
|
|
23
|
+
/** The uppercase SHA-256 signature of `fields`. */
|
|
24
|
+
sign(fields) {
|
|
25
|
+
return sha256Hex(`${this.signString(fields)}&key=${this.appKey}`).toUpperCase();
|
|
26
|
+
}
|
|
27
|
+
/** Whether `fields.sign` matches, compared in constant time. */
|
|
28
|
+
verify(fields) {
|
|
29
|
+
const sign = fields.sign;
|
|
30
|
+
return typeof sign === 'string' && safeEqual(this.sign(fields), sign.toUpperCase());
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AyaPay, AyaPayConfig, type AyaPayConfigOptions } from './aya-pay/aya-pay.js';
|
|
2
|
+
import type { TokenCache } from './core/cache.js';
|
|
3
|
+
import { type EnvSource } from './core/env.js';
|
|
4
|
+
import type { GatewayOptions } from './core/http.js';
|
|
5
|
+
import { CyberSource, CyberSourceConfig, type CyberSourceConfigOptions } from './cyber-source/cyber-source.js';
|
|
6
|
+
import { KbzPay, KbzPayConfig, type KbzPayConfigOptions } from './kbz-pay/kbz-pay.js';
|
|
7
|
+
import { WaveMoney, WaveMoneyConfig, type WaveMoneyConfigOptions } from './wave-money/wave-money.js';
|
|
8
|
+
import { YomaMmqr, YomaMmqrConfig, type YomaMmqrConfigOptions } from './yoma-mmqr/yoma-mmqr.js';
|
|
9
|
+
/** The configuration of every gateway; configure only the ones you use. */
|
|
10
|
+
export interface MyanmarPaymentsConfig {
|
|
11
|
+
kbzPay?: KbzPayConfig | KbzPayConfigOptions | undefined;
|
|
12
|
+
waveMoney?: WaveMoneyConfig | WaveMoneyConfigOptions | undefined;
|
|
13
|
+
ayaPay?: AyaPayConfig | AyaPayConfigOptions | undefined;
|
|
14
|
+
yomaMmqr?: YomaMmqrConfig | YomaMmqrConfigOptions | undefined;
|
|
15
|
+
cyberSource?: CyberSourceConfig | CyberSourceConfigOptions | undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Options shared by every gateway the facade builds. */
|
|
18
|
+
export interface MyanmarPaymentsOptions extends GatewayOptions {
|
|
19
|
+
/** Keeps Yoma MMQR's access token; defaults to a {@link MemoryTokenCache}. */
|
|
20
|
+
tokenCache?: TokenCache | undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Builds each gateway from one configuration object (or the environment). Gateways are created
|
|
24
|
+
* on first use and reused, so only the gateways you call need to be configured.
|
|
25
|
+
*/
|
|
26
|
+
export declare class MyanmarPayments {
|
|
27
|
+
private kbzPayGateway;
|
|
28
|
+
private waveMoneyGateway;
|
|
29
|
+
private ayaPayGateway;
|
|
30
|
+
private yomaMmqrGateway;
|
|
31
|
+
private cyberSourceGateway;
|
|
32
|
+
private readonly options;
|
|
33
|
+
private readonly sources;
|
|
34
|
+
constructor(config?: MyanmarPaymentsConfig, options?: MyanmarPaymentsOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Reads every gateway's configuration from environment variables (`KBZ_PAY_*`, `WAVE_MONEY_*`,
|
|
37
|
+
* `AYA_PAY_*`, `YOMA_MMQR_*`, `CYBER_SOURCE_*`) when the gateway is first used.
|
|
38
|
+
*/
|
|
39
|
+
static fromEnv(env?: EnvSource, options?: MyanmarPaymentsOptions): MyanmarPayments;
|
|
40
|
+
/** The KBZ Pay gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
41
|
+
kbzPay(): KbzPay;
|
|
42
|
+
/** The Wave Money gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
43
|
+
waveMoney(): WaveMoney;
|
|
44
|
+
/** The AYA Payment Gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
45
|
+
ayaPay(): AyaPay;
|
|
46
|
+
/** The Yoma MMQR gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
47
|
+
yomaMmqr(): YomaMmqr;
|
|
48
|
+
/** The CyberSource gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
49
|
+
cyberSource(): CyberSource;
|
|
50
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AyaPay, AyaPayConfig } from './aya-pay/aya-pay.js';
|
|
2
|
+
import { MemoryTokenCache } from './core/cache.js';
|
|
3
|
+
import { defaultEnv } from './core/env.js';
|
|
4
|
+
import { ConfigurationError } from './core/errors.js';
|
|
5
|
+
import { CyberSource, CyberSourceConfig, } from './cyber-source/cyber-source.js';
|
|
6
|
+
import { KbzPay, KbzPayConfig } from './kbz-pay/kbz-pay.js';
|
|
7
|
+
import { WaveMoney, WaveMoneyConfig, } from './wave-money/wave-money.js';
|
|
8
|
+
import { YomaMmqr, YomaMmqrConfig } from './yoma-mmqr/yoma-mmqr.js';
|
|
9
|
+
/**
|
|
10
|
+
* Builds each gateway from one configuration object (or the environment). Gateways are created
|
|
11
|
+
* on first use and reused, so only the gateways you call need to be configured.
|
|
12
|
+
*/
|
|
13
|
+
export class MyanmarPayments {
|
|
14
|
+
kbzPayGateway;
|
|
15
|
+
waveMoneyGateway;
|
|
16
|
+
ayaPayGateway;
|
|
17
|
+
yomaMmqrGateway;
|
|
18
|
+
cyberSourceGateway;
|
|
19
|
+
options;
|
|
20
|
+
sources;
|
|
21
|
+
constructor(config = {}, options = {}) {
|
|
22
|
+
this.options = { ...options, tokenCache: options.tokenCache ?? new MemoryTokenCache() };
|
|
23
|
+
this.sources = { ...config };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reads every gateway's configuration from environment variables (`KBZ_PAY_*`, `WAVE_MONEY_*`,
|
|
27
|
+
* `AYA_PAY_*`, `YOMA_MMQR_*`, `CYBER_SOURCE_*`) when the gateway is first used.
|
|
28
|
+
*/
|
|
29
|
+
static fromEnv(env = defaultEnv(), options = {}) {
|
|
30
|
+
const payments = new MyanmarPayments({}, options);
|
|
31
|
+
Object.assign(payments.sources, {
|
|
32
|
+
kbzPay: () => KbzPayConfig.fromEnv(env),
|
|
33
|
+
waveMoney: () => WaveMoneyConfig.fromEnv(env),
|
|
34
|
+
ayaPay: () => AyaPayConfig.fromEnv(env),
|
|
35
|
+
yomaMmqr: () => YomaMmqrConfig.fromEnv(env),
|
|
36
|
+
cyberSource: () => CyberSourceConfig.fromEnv(env),
|
|
37
|
+
});
|
|
38
|
+
return payments;
|
|
39
|
+
}
|
|
40
|
+
/** The KBZ Pay gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
41
|
+
kbzPay() {
|
|
42
|
+
return (this.kbzPayGateway ??= new KbzPay(resolve(this.sources.kbzPay, KbzPayConfig, 'kbz_pay', 'app_id'), this.options));
|
|
43
|
+
}
|
|
44
|
+
/** The Wave Money gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
45
|
+
waveMoney() {
|
|
46
|
+
return (this.waveMoneyGateway ??= new WaveMoney(resolve(this.sources.waveMoney, WaveMoneyConfig, 'wave_money', 'merchant_id'), this.options));
|
|
47
|
+
}
|
|
48
|
+
/** The AYA Payment Gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
49
|
+
ayaPay() {
|
|
50
|
+
return (this.ayaPayGateway ??= new AyaPay(resolve(this.sources.ayaPay, AyaPayConfig, 'aya_pay', 'app_key'), this.options));
|
|
51
|
+
}
|
|
52
|
+
/** The Yoma MMQR gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
53
|
+
yomaMmqr() {
|
|
54
|
+
return (this.yomaMmqrGateway ??= new YomaMmqr(resolve(this.sources.yomaMmqr, YomaMmqrConfig, 'yoma_mmqr', 'merchant_id'), this.options));
|
|
55
|
+
}
|
|
56
|
+
/** The CyberSource gateway. Throws a `ConfigurationError` when it is not configured. */
|
|
57
|
+
cyberSource() {
|
|
58
|
+
return (this.cyberSourceGateway ??= new CyberSource(resolve(this.sources.cyberSource, CyberSourceConfig, 'cyber_source', 'profile_id')));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function resolve(source, type, gateway, firstKey) {
|
|
62
|
+
if (source === undefined || source === null) {
|
|
63
|
+
throw new ConfigurationError(gateway, firstKey);
|
|
64
|
+
}
|
|
65
|
+
if (typeof source === 'function') {
|
|
66
|
+
return source();
|
|
67
|
+
}
|
|
68
|
+
return source instanceof type ? source : new type(source);
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WaveMoney, WaveMoneyConfig, type WaveMoneyConfigOptions, type WaveMoneyItem, type WaveMoneyPaymentData, } from './wave-money.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WaveMoney, WaveMoneyConfig, } from './wave-money.js';
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Amount, type AmountInput } from '../core/amount.js';
|
|
2
|
+
import { CallbackRequest, PaymentCallback } from '../core/callback.js';
|
|
3
|
+
import { type EnvSource } from '../core/env.js';
|
|
4
|
+
import { type GatewayOptions, type RequestOptions } from '../core/http.js';
|
|
5
|
+
import { RedirectPayment } from '../core/results.js';
|
|
6
|
+
/** The settings of {@link WaveMoneyConfig}. */
|
|
7
|
+
export interface WaveMoneyConfigOptions {
|
|
8
|
+
/** The merchant id Wave issued. */
|
|
9
|
+
merchantId: string;
|
|
10
|
+
/** The hash secret key Wave issued. */
|
|
11
|
+
secretKey: string;
|
|
12
|
+
/** Your business name, shown on Wave's payment page. */
|
|
13
|
+
merchantName: string;
|
|
14
|
+
/** How long the customer has to pay, in seconds (default 300). */
|
|
15
|
+
timeToLiveSeconds?: number | undefined;
|
|
16
|
+
/** Use the test environment (default `true`). */
|
|
17
|
+
sandbox?: boolean | undefined;
|
|
18
|
+
/** Overrides the API base URL. */
|
|
19
|
+
baseUrl?: string | undefined;
|
|
20
|
+
/** Overrides the host the customer is redirected to. Wave serves it without the API port. */
|
|
21
|
+
authenticateUrl?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Wave Money credentials and endpoints. A missing credential throws a `ConfigurationError`.
|
|
25
|
+
*/
|
|
26
|
+
export declare class WaveMoneyConfig {
|
|
27
|
+
static readonly SANDBOX_URL = "https://preprodpayments.wavemoney.io:8107";
|
|
28
|
+
static readonly PRODUCTION_URL = "https://payments.wavemoney.io";
|
|
29
|
+
static readonly SANDBOX_AUTHENTICATE_URL = "https://preprodpayments.wavemoney.io";
|
|
30
|
+
static readonly PRODUCTION_AUTHENTICATE_URL = "https://payments.wavemoney.io";
|
|
31
|
+
static readonly DEFAULT_TIME_TO_LIVE_SECONDS = 300;
|
|
32
|
+
readonly merchantId: string;
|
|
33
|
+
readonly secretKey: string;
|
|
34
|
+
readonly merchantName: string;
|
|
35
|
+
/** Seconds the customer has to pay. */
|
|
36
|
+
readonly timeToLiveSeconds: number;
|
|
37
|
+
readonly sandbox: boolean;
|
|
38
|
+
/** The API base URL in use. */
|
|
39
|
+
readonly baseUrl: string;
|
|
40
|
+
/** The host the customer is redirected to. */
|
|
41
|
+
readonly authenticateUrl: string;
|
|
42
|
+
constructor(options: WaveMoneyConfigOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Reads `WAVE_MONEY_MERCHANT_ID`, `WAVE_MONEY_SECRET_KEY`, `WAVE_MONEY_MERCHANT_NAME` (falling back
|
|
45
|
+
* to `APP_NAME`), `WAVE_MONEY_TIME_TO_LIVE_IN_SECONDS`, `WAVE_MONEY_SANDBOX`, `WAVE_MONEY_BASE_URL`
|
|
46
|
+
* and `WAVE_MONEY_AUTHENTICATE_URL`.
|
|
47
|
+
*/
|
|
48
|
+
static fromEnv(env?: EnvSource): WaveMoneyConfig;
|
|
49
|
+
}
|
|
50
|
+
/** A line item shown on Wave's payment page. */
|
|
51
|
+
export interface WaveMoneyItem {
|
|
52
|
+
/** The item name. */
|
|
53
|
+
name: string;
|
|
54
|
+
/** The item amount in whole kyat, e.g. `Amount.kyat(1000)` or `1000`. */
|
|
55
|
+
amount: AmountInput;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A Wave Money payment request. Wave only accepts whole kyat (MMK).
|
|
59
|
+
*/
|
|
60
|
+
export interface WaveMoneyPaymentData {
|
|
61
|
+
/** Your order id. One order can have several payment attempts. */
|
|
62
|
+
orderId: string;
|
|
63
|
+
/** The URL Wave posts the result to (`backend_result_url`). */
|
|
64
|
+
callbackUrl: string;
|
|
65
|
+
/** Where Wave sends the customer back (`frontend_result_url`). Not proof of payment. */
|
|
66
|
+
returnUrl: string;
|
|
67
|
+
/** Shown to the customer. */
|
|
68
|
+
description: string;
|
|
69
|
+
/** The line items shown on Wave's page; at least one. */
|
|
70
|
+
items: readonly WaveMoneyItem[];
|
|
71
|
+
/** The total in whole kyat. Leave it unset to charge the sum of the items. */
|
|
72
|
+
amount?: AmountInput | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* The unique id of this attempt; Wave rejects a reused one. `initiate()` fills it with a random
|
|
75
|
+
* id when empty, so store it afterwards: Wave's callback may omit `orderId` but always carries
|
|
76
|
+
* this.
|
|
77
|
+
*/
|
|
78
|
+
merchantReferenceId?: string | undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Wave Money (WavePay payment gateway): redirect payments and callbacks. Wave has no status API,
|
|
82
|
+
* so the callback is the only result.
|
|
83
|
+
*/
|
|
84
|
+
export declare class WaveMoney {
|
|
85
|
+
/** The configuration in use. */
|
|
86
|
+
readonly config: WaveMoneyConfig;
|
|
87
|
+
private readonly transport;
|
|
88
|
+
constructor(config: WaveMoneyConfig | WaveMoneyConfigOptions, options?: GatewayOptions);
|
|
89
|
+
/** A gateway configured from the `WAVE_MONEY_*` environment variables. */
|
|
90
|
+
static fromEnv(env?: EnvSource, options?: GatewayOptions): WaveMoney;
|
|
91
|
+
/**
|
|
92
|
+
* The total that will be charged: `amount`, or the exact sum of the items when it is unset.
|
|
93
|
+
* `undefined` when an item amount is missing, malformed or has decimals.
|
|
94
|
+
*/
|
|
95
|
+
static resolvedAmount(data: Pick<WaveMoneyPaymentData, 'amount' | 'items'>): Amount | undefined;
|
|
96
|
+
/** Checks the request against Wave's documented rules; throws an `InvalidPaymentDataError`. */
|
|
97
|
+
static validate(data: WaveMoneyPaymentData): void;
|
|
98
|
+
/**
|
|
99
|
+
* Creates a payment request and returns Wave's page to redirect the customer to. Once `data`
|
|
100
|
+
* passes validation it fills `data.merchantReferenceId` with a random id when empty, so store it
|
|
101
|
+
* afterwards. Invalid data is left untouched.
|
|
102
|
+
*/
|
|
103
|
+
initiate(data: WaveMoneyPaymentData, options?: RequestOptions): Promise<RedirectPayment>;
|
|
104
|
+
/**
|
|
105
|
+
* Verifies Wave's callback. Only `PAYMENT_CONFIRMED` means the customer paid. `orderId` falls
|
|
106
|
+
* back to `merchantReferenceId` when it is missing, null or empty, because Wave marks `orderId` as
|
|
107
|
+
* optional.
|
|
108
|
+
*/
|
|
109
|
+
handleCallback(request: CallbackRequest): PaymentCallback;
|
|
110
|
+
private hash;
|
|
111
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { Amount } from '../core/amount.js';
|
|
2
|
+
import { CallbackRequest, losslessBody, PaymentCallback } from '../core/callback.js';
|
|
3
|
+
import { hmacSha256Hex, queryEscape, randomHex, safeEqual } from '../core/crypto.js';
|
|
4
|
+
import { defaultEnv, envFirst, envInt, envSandbox, optionalSetting, requireSetting, trimUrl, } from '../core/env.js';
|
|
5
|
+
import { ApiError, SignatureVerificationError } from '../core/errors.js';
|
|
6
|
+
import { httpClientFrom, Transport, } from '../core/http.js';
|
|
7
|
+
import { isLosslessObject, toPlainObject } from '../core/json.js';
|
|
8
|
+
import { RedirectPayment } from '../core/results.js';
|
|
9
|
+
import { resolveStatus } from '../core/status.js';
|
|
10
|
+
import { toAmount, Validator } from '../core/validate.js';
|
|
11
|
+
import { get, optional, scalarString, trimmed } from '../core/values.js';
|
|
12
|
+
/**
|
|
13
|
+
* Wave Money credentials and endpoints. A missing credential throws a `ConfigurationError`.
|
|
14
|
+
*/
|
|
15
|
+
export class WaveMoneyConfig {
|
|
16
|
+
static SANDBOX_URL = 'https://preprodpayments.wavemoney.io:8107';
|
|
17
|
+
static PRODUCTION_URL = 'https://payments.wavemoney.io';
|
|
18
|
+
static SANDBOX_AUTHENTICATE_URL = 'https://preprodpayments.wavemoney.io';
|
|
19
|
+
static PRODUCTION_AUTHENTICATE_URL = 'https://payments.wavemoney.io';
|
|
20
|
+
static DEFAULT_TIME_TO_LIVE_SECONDS = 300;
|
|
21
|
+
merchantId;
|
|
22
|
+
secretKey;
|
|
23
|
+
merchantName;
|
|
24
|
+
/** Seconds the customer has to pay. */
|
|
25
|
+
timeToLiveSeconds;
|
|
26
|
+
sandbox;
|
|
27
|
+
/** The API base URL in use. */
|
|
28
|
+
baseUrl;
|
|
29
|
+
/** The host the customer is redirected to. */
|
|
30
|
+
authenticateUrl;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.merchantId = requireSetting('wave_money', 'merchant_id', options.merchantId);
|
|
33
|
+
this.secretKey = requireSetting('wave_money', 'secret_key', options.secretKey);
|
|
34
|
+
this.merchantName = requireSetting('wave_money', 'merchant_name', options.merchantName);
|
|
35
|
+
const ttl = options.timeToLiveSeconds;
|
|
36
|
+
this.timeToLiveSeconds =
|
|
37
|
+
ttl !== undefined && Number.isInteger(ttl) && ttl > 0
|
|
38
|
+
? ttl
|
|
39
|
+
: WaveMoneyConfig.DEFAULT_TIME_TO_LIVE_SECONDS;
|
|
40
|
+
this.sandbox = options.sandbox ?? true;
|
|
41
|
+
this.baseUrl = trimUrl(optionalSetting(options.baseUrl) ??
|
|
42
|
+
(this.sandbox ? WaveMoneyConfig.SANDBOX_URL : WaveMoneyConfig.PRODUCTION_URL));
|
|
43
|
+
this.authenticateUrl = trimUrl(optionalSetting(options.authenticateUrl) ??
|
|
44
|
+
(this.sandbox
|
|
45
|
+
? WaveMoneyConfig.SANDBOX_AUTHENTICATE_URL
|
|
46
|
+
: WaveMoneyConfig.PRODUCTION_AUTHENTICATE_URL));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Reads `WAVE_MONEY_MERCHANT_ID`, `WAVE_MONEY_SECRET_KEY`, `WAVE_MONEY_MERCHANT_NAME` (falling back
|
|
50
|
+
* to `APP_NAME`), `WAVE_MONEY_TIME_TO_LIVE_IN_SECONDS`, `WAVE_MONEY_SANDBOX`, `WAVE_MONEY_BASE_URL`
|
|
51
|
+
* and `WAVE_MONEY_AUTHENTICATE_URL`.
|
|
52
|
+
*/
|
|
53
|
+
static fromEnv(env = defaultEnv()) {
|
|
54
|
+
return new WaveMoneyConfig({
|
|
55
|
+
merchantId: envFirst(env, 'WAVE_MONEY_MERCHANT_ID'),
|
|
56
|
+
secretKey: envFirst(env, 'WAVE_MONEY_SECRET_KEY'),
|
|
57
|
+
merchantName: envFirst(env, 'WAVE_MONEY_MERCHANT_NAME', 'APP_NAME'),
|
|
58
|
+
timeToLiveSeconds: envInt(env, 'WAVE_MONEY_TIME_TO_LIVE_IN_SECONDS'),
|
|
59
|
+
sandbox: envSandbox(env, 'WAVE_MONEY_SANDBOX'),
|
|
60
|
+
baseUrl: envFirst(env, 'WAVE_MONEY_BASE_URL'),
|
|
61
|
+
authenticateUrl: envFirst(env, 'WAVE_MONEY_AUTHENTICATE_URL'),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const STATUSES = {
|
|
66
|
+
PAYMENT_CONFIRMED: 'successful',
|
|
67
|
+
INSUFFICIENT_BALANCE: 'pending',
|
|
68
|
+
ACCOUNT_LOCKED: 'failed',
|
|
69
|
+
BILL_COLLECTION_FAILED: 'failed',
|
|
70
|
+
PAYMENT_REQUEST_CANCELLED: 'cancelled',
|
|
71
|
+
TRANSACTION_TIMED_OUT: 'expired',
|
|
72
|
+
SCHEDULER_TRANSACTION_TIMED_OUT: 'expired',
|
|
73
|
+
};
|
|
74
|
+
const CALLBACK_FIELDS = [
|
|
75
|
+
'status',
|
|
76
|
+
'timeToLiveSeconds',
|
|
77
|
+
'merchantId',
|
|
78
|
+
'orderId',
|
|
79
|
+
'amount',
|
|
80
|
+
'backendResultUrl',
|
|
81
|
+
'merchantReferenceId',
|
|
82
|
+
'initiatorMsisdn',
|
|
83
|
+
'transactionId',
|
|
84
|
+
'paymentRequestId',
|
|
85
|
+
'requestTime',
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* Wave Money (WavePay payment gateway): redirect payments and callbacks. Wave has no status API,
|
|
89
|
+
* so the callback is the only result.
|
|
90
|
+
*/
|
|
91
|
+
export class WaveMoney {
|
|
92
|
+
/** The configuration in use. */
|
|
93
|
+
config;
|
|
94
|
+
transport;
|
|
95
|
+
constructor(config, options = {}) {
|
|
96
|
+
this.config = config instanceof WaveMoneyConfig ? config : new WaveMoneyConfig(config);
|
|
97
|
+
this.transport = new Transport(httpClientFrom(options));
|
|
98
|
+
}
|
|
99
|
+
/** A gateway configured from the `WAVE_MONEY_*` environment variables. */
|
|
100
|
+
static fromEnv(env = defaultEnv(), options = {}) {
|
|
101
|
+
return new WaveMoney(WaveMoneyConfig.fromEnv(env), options);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The total that will be charged: `amount`, or the exact sum of the items when it is unset.
|
|
105
|
+
* `undefined` when an item amount is missing, malformed or has decimals.
|
|
106
|
+
*/
|
|
107
|
+
static resolvedAmount(data) {
|
|
108
|
+
if (data.amount !== undefined) {
|
|
109
|
+
return toAmount(data.amount);
|
|
110
|
+
}
|
|
111
|
+
if (!Array.isArray(data.items) || data.items.length === 0) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
let total = 0n;
|
|
115
|
+
for (const item of data.items) {
|
|
116
|
+
const amount = toAmount(item?.amount);
|
|
117
|
+
if (amount === undefined || amount.decimalPlaces() > 0) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
total += BigInt(amount.toString());
|
|
121
|
+
}
|
|
122
|
+
return Amount.kyat(total);
|
|
123
|
+
}
|
|
124
|
+
/** Checks the request against Wave's documented rules; throws an `InvalidPaymentDataError`. */
|
|
125
|
+
static validate(data) {
|
|
126
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
127
|
+
const validator = new Validator()
|
|
128
|
+
.required('orderId', data.orderId)
|
|
129
|
+
.required('callbackUrl', data.callbackUrl)
|
|
130
|
+
.url('callbackUrl', data.callbackUrl)
|
|
131
|
+
.required('returnUrl', data.returnUrl)
|
|
132
|
+
.url('returnUrl', data.returnUrl)
|
|
133
|
+
.required('description', data.description)
|
|
134
|
+
.when(items.length === 0, 'items', 'The items field must have at least one item.')
|
|
135
|
+
.string('merchantReferenceId', data.merchantReferenceId);
|
|
136
|
+
items.forEach((item, index) => {
|
|
137
|
+
validator
|
|
138
|
+
.required(`items.${index}.name`, item?.name)
|
|
139
|
+
.amount(`items.${index}.amount`, item?.amount, { gateway: 'Wave Money', maxDecimals: 0 });
|
|
140
|
+
});
|
|
141
|
+
const resolved = WaveMoney.resolvedAmount(data);
|
|
142
|
+
if (!(items.length > 0 && data.amount === undefined && resolved === undefined)) {
|
|
143
|
+
validator.amount('amount', data.amount ?? resolved, {
|
|
144
|
+
gateway: 'Wave Money',
|
|
145
|
+
maxDecimals: 0,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
validator.validate();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Creates a payment request and returns Wave's page to redirect the customer to. Once `data`
|
|
152
|
+
* passes validation it fills `data.merchantReferenceId` with a random id when empty, so store it
|
|
153
|
+
* afterwards. Invalid data is left untouched.
|
|
154
|
+
*/
|
|
155
|
+
async initiate(data, options = {}) {
|
|
156
|
+
WaveMoney.validate(data);
|
|
157
|
+
if (!data.merchantReferenceId) {
|
|
158
|
+
data.merchantReferenceId = randomHex();
|
|
159
|
+
}
|
|
160
|
+
const amount = WaveMoney.resolvedAmount(data).toString();
|
|
161
|
+
const ttl = String(this.config.timeToLiveSeconds);
|
|
162
|
+
// Wave documents items as [{"name": "...", "amount": 1000}] with a numeric amount; the amount
|
|
163
|
+
// text is written as a JSON number without passing through a float.
|
|
164
|
+
const items = `[${data.items
|
|
165
|
+
.map((item) => `{"name":${JSON.stringify(item.name)},"amount":${String(toAmount(item.amount))}}`)
|
|
166
|
+
.join(',')}]`;
|
|
167
|
+
const response = await this.transport.postForm(`${this.config.baseUrl}/payment`, {
|
|
168
|
+
time_to_live_in_seconds: ttl,
|
|
169
|
+
merchant_id: this.config.merchantId,
|
|
170
|
+
order_id: data.orderId,
|
|
171
|
+
merchant_reference_id: data.merchantReferenceId,
|
|
172
|
+
frontend_result_url: data.returnUrl,
|
|
173
|
+
backend_result_url: data.callbackUrl,
|
|
174
|
+
amount,
|
|
175
|
+
payment_description: data.description,
|
|
176
|
+
merchant_name: this.config.merchantName,
|
|
177
|
+
items,
|
|
178
|
+
hash: this.hash([
|
|
179
|
+
ttl,
|
|
180
|
+
this.config.merchantId,
|
|
181
|
+
data.orderId,
|
|
182
|
+
amount,
|
|
183
|
+
data.callbackUrl,
|
|
184
|
+
data.merchantReferenceId,
|
|
185
|
+
]),
|
|
186
|
+
}, {}, options.signal);
|
|
187
|
+
const body = response.json();
|
|
188
|
+
const transactionId = get(body, 'transaction_id');
|
|
189
|
+
if (!response.successful() || get(body, 'message') !== 'success' || transactionId === '') {
|
|
190
|
+
const message = errorMessage(body);
|
|
191
|
+
throw new ApiError(`Wave Money payment request failed with HTTP ${response.status}: ${message}`, {
|
|
192
|
+
gatewayCode: 'errors' in body ? 'VALIDATION_ERROR' : optional(body, 'message'),
|
|
193
|
+
gatewayMessage: message,
|
|
194
|
+
httpStatus: response.status,
|
|
195
|
+
raw: toPlainObject(body),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return new RedirectPayment({
|
|
199
|
+
orderId: data.orderId,
|
|
200
|
+
url: `${this.config.authenticateUrl}/authenticate?transaction_id=${queryEscape(transactionId)}`,
|
|
201
|
+
gatewayReference: transactionId,
|
|
202
|
+
raw: toPlainObject(body),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Verifies Wave's callback. Only `PAYMENT_CONFIRMED` means the customer paid. `orderId` falls
|
|
207
|
+
* back to `merchantReferenceId` when it is missing, null or empty, because Wave marks `orderId` as
|
|
208
|
+
* optional.
|
|
209
|
+
*/
|
|
210
|
+
handleCallback(request) {
|
|
211
|
+
const payload = losslessBody(request);
|
|
212
|
+
const expected = this.hash(CALLBACK_FIELDS.map((field) => scalarString(payload[field]) ?? 'null'));
|
|
213
|
+
const hashValue = payload.hashValue;
|
|
214
|
+
if (typeof hashValue !== 'string' || !safeEqual(expected, hashValue.toLowerCase())) {
|
|
215
|
+
throw new SignatureVerificationError('Wave Money callback hash verification failed.', toPlainObject(payload));
|
|
216
|
+
}
|
|
217
|
+
const gatewayStatus = trimmed(payload, 'status');
|
|
218
|
+
return new PaymentCallback({
|
|
219
|
+
orderId: get(payload, 'orderId') || get(payload, 'merchantReferenceId'),
|
|
220
|
+
status: resolveStatus(STATUSES, gatewayStatus),
|
|
221
|
+
gatewayStatus,
|
|
222
|
+
gatewayReference: optional(payload, 'transactionId'),
|
|
223
|
+
amount: optional(payload, 'amount'),
|
|
224
|
+
raw: toPlainObject(payload),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
hash(parts) {
|
|
228
|
+
return hmacSha256Hex(this.config.secretKey, parts.join(''));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function errorMessage(body) {
|
|
232
|
+
const errors = body.errors;
|
|
233
|
+
if (isLosslessObject(errors)) {
|
|
234
|
+
return Object.keys(errors)
|
|
235
|
+
.sort()
|
|
236
|
+
.map((field) => {
|
|
237
|
+
const list = errors[field];
|
|
238
|
+
const texts = (Array.isArray(list) ? list : [list])
|
|
239
|
+
.map((item) => scalarString(item))
|
|
240
|
+
.filter((text) => text !== undefined);
|
|
241
|
+
return `${field}: ${texts.join(' ')}`;
|
|
242
|
+
})
|
|
243
|
+
.join('; ');
|
|
244
|
+
}
|
|
245
|
+
return get(body, 'message') || 'unexpected response';
|
|
246
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { YomaMmqr, YomaMmqrConfig, type YomaMmqrConfigOptions, type YomaMmqrOptions, type YomaMmqrPaymentData, } from './yoma-mmqr.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { YomaMmqr, YomaMmqrConfig, } from './yoma-mmqr.js';
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type AmountInput } from '../core/amount.js';
|
|
2
|
+
import { type TokenCache } from '../core/cache.js';
|
|
3
|
+
import { CallbackRequest, PaymentCallback, PaymentStatusResult } from '../core/callback.js';
|
|
4
|
+
import { type EnvSource } from '../core/env.js';
|
|
5
|
+
import { type GatewayOptions, type RequestOptions } from '../core/http.js';
|
|
6
|
+
import { QrPayment } from '../core/results.js';
|
|
7
|
+
/** The settings of {@link YomaMmqrConfig}. */
|
|
8
|
+
export interface YomaMmqrConfigOptions {
|
|
9
|
+
/** The merchant id Yoma issued. */
|
|
10
|
+
merchantId: string;
|
|
11
|
+
/** The OAuth client id. */
|
|
12
|
+
clientId: string;
|
|
13
|
+
/** The OAuth client secret. */
|
|
14
|
+
clientSecret: string;
|
|
15
|
+
/** The hash key Yoma issued for verifying callbacks. */
|
|
16
|
+
webhookHashKey: string;
|
|
17
|
+
/** The secret you shared with Yoma; when set, callbacks must carry it in `X-Webhook-Secret`. */
|
|
18
|
+
webhookSecret?: string | undefined;
|
|
19
|
+
/** Use the UAT payment hub (default `true`). */
|
|
20
|
+
sandbox?: boolean | undefined;
|
|
21
|
+
/** Overrides the API base URL. */
|
|
22
|
+
baseUrl?: string | undefined;
|
|
23
|
+
/** The `{version}` segment of the API paths (default `v1rc`). */
|
|
24
|
+
apiVersion?: string | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Yoma MMQR credentials and endpoints. A missing credential throws a `ConfigurationError`.
|
|
28
|
+
*/
|
|
29
|
+
export declare class YomaMmqrConfig {
|
|
30
|
+
static readonly SANDBOX_URL = "https://devapi.yomabank.net";
|
|
31
|
+
static readonly PRODUCTION_URL = "https://paymenthubapi.yomabank.com";
|
|
32
|
+
static readonly DEFAULT_API_VERSION = "v1rc";
|
|
33
|
+
readonly merchantId: string;
|
|
34
|
+
readonly clientId: string;
|
|
35
|
+
readonly clientSecret: string;
|
|
36
|
+
readonly webhookHashKey: string;
|
|
37
|
+
readonly webhookSecret: string | undefined;
|
|
38
|
+
readonly sandbox: boolean;
|
|
39
|
+
/** The base URL in use. */
|
|
40
|
+
readonly baseUrl: string;
|
|
41
|
+
/** The API version in use. */
|
|
42
|
+
readonly apiVersion: string;
|
|
43
|
+
constructor(options: YomaMmqrConfigOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Reads `YOMA_MMQR_MERCHANT_ID`, `YOMA_MMQR_CLIENT_ID`, `YOMA_MMQR_CLIENT_SECRET`,
|
|
46
|
+
* `YOMA_MMQR_WEBHOOK_HASHKEY`, `YOMA_MMQR_WEBHOOK_SECRET`, `YOMA_MMQR_SANDBOX`,
|
|
47
|
+
* `YOMA_MMQR_BASE_URL` and `YOMA_MMQR_API_VERSION`.
|
|
48
|
+
*/
|
|
49
|
+
static fromEnv(env?: EnvSource): YomaMmqrConfig;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A Yoma MMQR order. Yoma accepts each order number once; renew an expired QR with `renewQr()`.
|
|
53
|
+
* Yoma documents no decimals, so amounts are whole kyat.
|
|
54
|
+
*/
|
|
55
|
+
export interface YomaMmqrPaymentData {
|
|
56
|
+
/** Your unique order number, at most 20 characters. */
|
|
57
|
+
orderId: string;
|
|
58
|
+
/** The amount in whole kyat, e.g. `Amount.kyat(1000)` or `1000`. */
|
|
59
|
+
amount: AmountInput;
|
|
60
|
+
/** Shown on the payment slip, at most 50 characters. */
|
|
61
|
+
description: string;
|
|
62
|
+
}
|
|
63
|
+
/** Options of {@link YomaMmqr}: the HTTP options plus the access token cache. */
|
|
64
|
+
export interface YomaMmqrOptions extends GatewayOptions {
|
|
65
|
+
/** Keeps the access token between calls; defaults to a {@link MemoryTokenCache}. */
|
|
66
|
+
tokenCache?: TokenCache | undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Yoma Bank MMQR: QR payments, status checks and callbacks. Access tokens are cached in a
|
|
70
|
+
* {@link TokenCache}; share the gateway (or pass a shared cache) so they survive between calls.
|
|
71
|
+
*/
|
|
72
|
+
export declare class YomaMmqr {
|
|
73
|
+
/** How long a generated QR stays payable, in seconds. */
|
|
74
|
+
static readonly QR_LIFETIME_SECONDS = 120;
|
|
75
|
+
/** The configuration in use. */
|
|
76
|
+
readonly config: YomaMmqrConfig;
|
|
77
|
+
private readonly transport;
|
|
78
|
+
private readonly cache;
|
|
79
|
+
private pendingToken;
|
|
80
|
+
constructor(config: YomaMmqrConfig | YomaMmqrConfigOptions, options?: YomaMmqrOptions);
|
|
81
|
+
/** A gateway configured from the `YOMA_MMQR_*` environment variables. */
|
|
82
|
+
static fromEnv(env?: EnvSource, options?: YomaMmqrOptions): YomaMmqr;
|
|
83
|
+
/** Checks the order against Yoma's documented limits; throws an `InvalidPaymentDataError`. */
|
|
84
|
+
static validate(data: YomaMmqrPaymentData): void;
|
|
85
|
+
/** Checks the order out with Yoma and generates its first QR. Call it once per order. */
|
|
86
|
+
initiate(data: YomaMmqrPaymentData, options?: RequestOptions): Promise<QrPayment>;
|
|
87
|
+
/**
|
|
88
|
+
* Generates a new QR for an order that is already checked out, e.g. after the previous one
|
|
89
|
+
* expired. The previous QR's reference stops working.
|
|
90
|
+
*/
|
|
91
|
+
renewQr(orderId: string, options?: RequestOptions): Promise<QrPayment>;
|
|
92
|
+
/** Checks a QR's payment status by its reference (`QrPayment.reference`). An expired QR reports `expired`. */
|
|
93
|
+
status(reference: string, options?: RequestOptions): Promise<PaymentStatusResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Verifies Yoma's payment callback: HMAC-SHA256 of `orderNumber=..&status=..` keyed with the
|
|
96
|
+
* order number followed by the webhook hash key.
|
|
97
|
+
*/
|
|
98
|
+
handleCallback(request: CallbackRequest): PaymentCallback;
|
|
99
|
+
/** Drops the cached access token, e.g. after rotating the client secret. */
|
|
100
|
+
forgetToken(): Promise<void>;
|
|
101
|
+
private call;
|
|
102
|
+
private token;
|
|
103
|
+
private fetchToken;
|
|
104
|
+
private tokenCacheKey;
|
|
105
|
+
}
|