@laranex/myanmar-payments 4.0.0-dev.0 → 4.0.0-dev.10
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 +19 -4
- package/README.md +3 -3
- package/dist/cjs/aya-pay/aya-pay.d.ts +13 -9
- package/dist/cjs/aya-pay/aya-pay.js +16 -12
- package/dist/cjs/core/amount.d.ts +4 -3
- package/dist/cjs/core/amount.js +13 -4
- package/dist/cjs/core/callback.d.ts +14 -8
- package/dist/cjs/core/callback.js +19 -11
- package/dist/cjs/core/crypto.d.ts +6 -1
- package/dist/cjs/core/crypto.js +17 -3
- package/dist/cjs/core/env.d.ts +14 -5
- package/dist/cjs/core/env.js +35 -11
- package/dist/cjs/core/errors.d.ts +9 -3
- package/dist/cjs/core/errors.js +12 -4
- package/dist/cjs/core/http.d.ts +10 -11
- package/dist/cjs/core/http.js +7 -9
- package/dist/cjs/core/index.d.ts +2 -2
- package/dist/cjs/core/index.js +3 -2
- package/dist/cjs/core/json.d.ts +2 -2
- package/dist/cjs/core/json.js +3 -3
- package/dist/cjs/core/status.d.ts +3 -3
- package/dist/cjs/core/status.js +2 -2
- package/dist/cjs/core/values.d.ts +2 -0
- package/dist/cjs/core/values.js +5 -0
- package/dist/cjs/cyber-source/cyber-source.d.ts +12 -14
- package/dist/cjs/cyber-source/cyber-source.js +39 -26
- package/dist/cjs/kbz-pay/kbz-pay.d.ts +27 -11
- package/dist/cjs/kbz-pay/kbz-pay.js +43 -20
- package/dist/cjs/kbz-pay/signer.d.ts +4 -1
- package/dist/cjs/kbz-pay/signer.js +7 -2
- package/dist/cjs/wave-money/wave-money.d.ts +26 -13
- package/dist/cjs/wave-money/wave-money.js +29 -29
- package/dist/cjs/yoma-mmqr/yoma-mmqr.d.ts +17 -13
- package/dist/cjs/yoma-mmqr/yoma-mmqr.js +21 -19
- package/dist/esm/aya-pay/aya-pay.d.ts +13 -9
- package/dist/esm/aya-pay/aya-pay.js +18 -14
- package/dist/esm/core/amount.d.ts +4 -3
- package/dist/esm/core/amount.js +13 -4
- package/dist/esm/core/callback.d.ts +14 -8
- package/dist/esm/core/callback.js +19 -11
- package/dist/esm/core/crypto.d.ts +6 -1
- package/dist/esm/core/crypto.js +17 -3
- package/dist/esm/core/env.d.ts +14 -5
- package/dist/esm/core/env.js +32 -9
- package/dist/esm/core/errors.d.ts +9 -3
- package/dist/esm/core/errors.js +12 -4
- package/dist/esm/core/http.d.ts +10 -11
- package/dist/esm/core/http.js +6 -8
- package/dist/esm/core/index.d.ts +2 -2
- package/dist/esm/core/index.js +2 -1
- package/dist/esm/core/json.d.ts +2 -2
- package/dist/esm/core/json.js +3 -3
- package/dist/esm/core/status.d.ts +3 -3
- package/dist/esm/core/status.js +2 -2
- package/dist/esm/core/values.d.ts +2 -0
- package/dist/esm/core/values.js +4 -0
- package/dist/esm/cyber-source/cyber-source.d.ts +12 -14
- package/dist/esm/cyber-source/cyber-source.js +41 -28
- package/dist/esm/kbz-pay/kbz-pay.d.ts +27 -11
- package/dist/esm/kbz-pay/kbz-pay.js +45 -22
- package/dist/esm/kbz-pay/signer.d.ts +4 -1
- package/dist/esm/kbz-pay/signer.js +8 -3
- package/dist/esm/wave-money/wave-money.d.ts +26 -13
- package/dist/esm/wave-money/wave-money.js +31 -31
- package/dist/esm/yoma-mmqr/yoma-mmqr.d.ts +17 -13
- package/dist/esm/yoma-mmqr/yoma-mmqr.js +23 -21
- package/package.json +2 -2
- package/skills/node-myanmar-payments/SKILL.md +26 -22
package/dist/cjs/core/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { Amount, type AmountInput } from './amount.js';
|
|
2
2
|
export { MemoryTokenCache, type TokenCache } from './cache.js';
|
|
3
3
|
export { Acknowledgement, CallbackRequest, PaymentCallback, PaymentStatusResult, type AcknowledgementInit, type BodyInput, type CallbackRequestInit, type HeadersInput, type NodeRequestLike, type PaymentCallbackInit, type PaymentStatusResultInit, type QueryInput, type ServerResponseLike, type WebRequestLike, } from './callback.js';
|
|
4
|
-
export type
|
|
4
|
+
export { DEFAULT_TIMEOUT_SECONDS, type EnvSource } from './env.js';
|
|
5
5
|
export { ApiError, ConfigurationError, InvalidPaymentDataError, PaymentError, SignatureVerificationError, } from './errors.js';
|
|
6
|
-
export {
|
|
6
|
+
export { FetchHttpClient, type FetchFunction, type FetchHttpClientOptions, type GatewayOptions, type HttpClient, type HttpRequest, type HttpResponse, type RequestOptions, } from './http.js';
|
|
7
7
|
export { AppPayment, FormPayment, QrPayment, RedirectPayment, type FormField, type PaymentResult, } from './results.js';
|
|
8
8
|
export { PaymentFlow, PaymentStatus, resolveStatus } from './status.js';
|
package/dist/cjs/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveStatus = exports.PaymentStatus = exports.PaymentFlow = exports.RedirectPayment = exports.QrPayment = exports.FormPayment = exports.AppPayment = exports.FetchHttpClient = exports.
|
|
3
|
+
exports.resolveStatus = exports.PaymentStatus = exports.PaymentFlow = exports.RedirectPayment = exports.QrPayment = exports.FormPayment = exports.AppPayment = exports.FetchHttpClient = exports.SignatureVerificationError = exports.PaymentError = exports.InvalidPaymentDataError = exports.ConfigurationError = exports.ApiError = exports.DEFAULT_TIMEOUT_SECONDS = exports.PaymentStatusResult = exports.PaymentCallback = exports.CallbackRequest = exports.Acknowledgement = exports.MemoryTokenCache = exports.Amount = void 0;
|
|
4
4
|
var amount_js_1 = require("./amount.js");
|
|
5
5
|
Object.defineProperty(exports, "Amount", { enumerable: true, get: function () { return amount_js_1.Amount; } });
|
|
6
6
|
var cache_js_1 = require("./cache.js");
|
|
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "Acknowledgement", { enumerable: true, get: funct
|
|
|
10
10
|
Object.defineProperty(exports, "CallbackRequest", { enumerable: true, get: function () { return callback_js_1.CallbackRequest; } });
|
|
11
11
|
Object.defineProperty(exports, "PaymentCallback", { enumerable: true, get: function () { return callback_js_1.PaymentCallback; } });
|
|
12
12
|
Object.defineProperty(exports, "PaymentStatusResult", { enumerable: true, get: function () { return callback_js_1.PaymentStatusResult; } });
|
|
13
|
+
var env_js_1 = require("./env.js");
|
|
14
|
+
Object.defineProperty(exports, "DEFAULT_TIMEOUT_SECONDS", { enumerable: true, get: function () { return env_js_1.DEFAULT_TIMEOUT_SECONDS; } });
|
|
13
15
|
var errors_js_1 = require("./errors.js");
|
|
14
16
|
Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return errors_js_1.ApiError; } });
|
|
15
17
|
Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_js_1.ConfigurationError; } });
|
|
@@ -17,7 +19,6 @@ Object.defineProperty(exports, "InvalidPaymentDataError", { enumerable: true, ge
|
|
|
17
19
|
Object.defineProperty(exports, "PaymentError", { enumerable: true, get: function () { return errors_js_1.PaymentError; } });
|
|
18
20
|
Object.defineProperty(exports, "SignatureVerificationError", { enumerable: true, get: function () { return errors_js_1.SignatureVerificationError; } });
|
|
19
21
|
var http_js_1 = require("./http.js");
|
|
20
|
-
Object.defineProperty(exports, "DEFAULT_TIMEOUT_MS", { enumerable: true, get: function () { return http_js_1.DEFAULT_TIMEOUT_MS; } });
|
|
21
22
|
Object.defineProperty(exports, "FetchHttpClient", { enumerable: true, get: function () { return http_js_1.FetchHttpClient; } });
|
|
22
23
|
var results_js_1 = require("./results.js");
|
|
23
24
|
Object.defineProperty(exports, "AppPayment", { enumerable: true, get: function () { return results_js_1.AppPayment; } });
|
package/dist/cjs/core/json.d.ts
CHANGED
|
@@ -32,8 +32,8 @@ export declare function parseJsonObject(text: string): LosslessObject | undefine
|
|
|
32
32
|
/** @internal */
|
|
33
33
|
export declare function isLosslessObject(value: unknown): value is LosslessObject;
|
|
34
34
|
/**
|
|
35
|
-
* Converts a lossless value to plain JavaScript values
|
|
36
|
-
*
|
|
35
|
+
* Converts a lossless value to plain JavaScript values for the `raw` fields of results. Numbers
|
|
36
|
+
* become their exact text as a string (`1000.50` stays `"1000.50"`), so no precision is lost.
|
|
37
37
|
*
|
|
38
38
|
* @internal
|
|
39
39
|
*/
|
package/dist/cjs/core/json.js
CHANGED
|
@@ -56,14 +56,14 @@ function isLosslessObject(value) {
|
|
|
56
56
|
!(value instanceof JsonNumber));
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* Converts a lossless value to plain JavaScript values
|
|
60
|
-
*
|
|
59
|
+
* Converts a lossless value to plain JavaScript values for the `raw` fields of results. Numbers
|
|
60
|
+
* become their exact text as a string (`1000.50` stays `"1000.50"`), so no precision is lost.
|
|
61
61
|
*
|
|
62
62
|
* @internal
|
|
63
63
|
*/
|
|
64
64
|
function toPlain(value) {
|
|
65
65
|
if (value instanceof JsonNumber) {
|
|
66
|
-
return
|
|
66
|
+
return value.text;
|
|
67
67
|
}
|
|
68
68
|
if (Array.isArray(value)) {
|
|
69
69
|
return value.map(toPlain);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A gateway-independent payment status. Every gateway's own status values are mapped onto these.
|
|
3
3
|
*/
|
|
4
|
-
export type PaymentStatus = 'successful' | 'pending' | 'failed' | '
|
|
4
|
+
export type PaymentStatus = 'successful' | 'pending' | 'failed' | 'canceled' | 'expired' | 'unknown';
|
|
5
5
|
/**
|
|
6
6
|
* The payment statuses, plus helpers.
|
|
7
7
|
*
|
|
@@ -18,13 +18,13 @@ export declare const PaymentStatus: Readonly<{
|
|
|
18
18
|
/** The payment was attempted and failed or was rejected. */
|
|
19
19
|
Failed: "failed";
|
|
20
20
|
/** The payment or order was canceled or closed before completing. */
|
|
21
|
-
|
|
21
|
+
Canceled: "canceled";
|
|
22
22
|
/** The payment window ran out before the customer paid. */
|
|
23
23
|
Expired: "expired";
|
|
24
24
|
/** The gateway sent a status this package does not recognize. Inspect `gatewayStatus`. */
|
|
25
25
|
Unknown: "unknown";
|
|
26
26
|
/** Every status, in the order above. */
|
|
27
|
-
values: readonly ["successful", "pending", "failed", "
|
|
27
|
+
values: readonly ["successful", "pending", "failed", "canceled", "expired", "unknown"];
|
|
28
28
|
/** Whether the status will not change any more: `false` for `pending` and `unknown`. */
|
|
29
29
|
isFinal(status: PaymentStatus): boolean;
|
|
30
30
|
}>;
|
package/dist/cjs/core/status.js
CHANGED
|
@@ -18,7 +18,7 @@ exports.PaymentStatus = Object.freeze({
|
|
|
18
18
|
/** The payment was attempted and failed or was rejected. */
|
|
19
19
|
Failed: 'failed',
|
|
20
20
|
/** The payment or order was canceled or closed before completing. */
|
|
21
|
-
|
|
21
|
+
Canceled: 'canceled',
|
|
22
22
|
/** The payment window ran out before the customer paid. */
|
|
23
23
|
Expired: 'expired',
|
|
24
24
|
/** The gateway sent a status this package does not recognize. Inspect `gatewayStatus`. */
|
|
@@ -28,7 +28,7 @@ exports.PaymentStatus = Object.freeze({
|
|
|
28
28
|
'successful',
|
|
29
29
|
'pending',
|
|
30
30
|
'failed',
|
|
31
|
-
'
|
|
31
|
+
'canceled',
|
|
32
32
|
'expired',
|
|
33
33
|
'unknown',
|
|
34
34
|
]),
|
|
@@ -14,3 +14,5 @@ export declare function optional(source: Record<string, unknown>, key: string):
|
|
|
14
14
|
export declare function trimmed(source: Record<string, unknown>, key: string): string;
|
|
15
15
|
/** The value at `key` when it is an object (not an array). @internal */
|
|
16
16
|
export declare function object(source: Record<string, unknown>, key: string): LosslessObject | undefined;
|
|
17
|
+
/** Whether `value` is an object or array, which a gateway never signs. @internal */
|
|
18
|
+
export declare function isNested(value: unknown): boolean;
|
package/dist/cjs/core/values.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.get = get;
|
|
|
5
5
|
exports.optional = optional;
|
|
6
6
|
exports.trimmed = trimmed;
|
|
7
7
|
exports.object = object;
|
|
8
|
+
exports.isNested = isNested;
|
|
8
9
|
const json_js_1 = require("./json.js");
|
|
9
10
|
/**
|
|
10
11
|
* Returns a scalar as the text a gateway signs: strings as is, numbers exactly as sent, booleans as
|
|
@@ -43,3 +44,7 @@ function object(source, key) {
|
|
|
43
44
|
const value = source[key];
|
|
44
45
|
return (0, json_js_1.isLosslessObject)(value) ? value : undefined;
|
|
45
46
|
}
|
|
47
|
+
/** Whether `value` is an object or array, which a gateway never signs. @internal */
|
|
48
|
+
function isNested(value) {
|
|
49
|
+
return typeof value === 'object' && value !== null && !(value instanceof json_js_1.JsonNumber);
|
|
50
|
+
}
|
|
@@ -10,27 +10,24 @@ export interface CyberSourceConfigOptions {
|
|
|
10
10
|
accessKey: string;
|
|
11
11
|
/** The profile's secret key, which signs the fields. */
|
|
12
12
|
secretKey: string;
|
|
13
|
-
/**
|
|
14
|
-
sandbox?: boolean | undefined;
|
|
15
|
-
/** Overrides the Secure Acceptance base URL. */
|
|
13
|
+
/** Overrides the Secure Acceptance base URL, e.g. to point at the test environment. */
|
|
16
14
|
baseUrl?: string | undefined;
|
|
17
15
|
}
|
|
18
16
|
/**
|
|
19
|
-
* A CyberSource Secure Acceptance profile. A missing credential throws a `ConfigurationError`.
|
|
17
|
+
* A CyberSource Secure Acceptance profile. A missing credential throws a `ConfigurationError`. The
|
|
18
|
+
* URL defaults to production.
|
|
20
19
|
*/
|
|
21
20
|
export declare class CyberSourceConfig {
|
|
22
|
-
static readonly SANDBOX_URL = "https://testsecureacceptance.cybersource.com";
|
|
23
21
|
static readonly PRODUCTION_URL = "https://secureacceptance.cybersource.com";
|
|
24
22
|
readonly profileId: string;
|
|
25
23
|
readonly accessKey: string;
|
|
26
24
|
readonly secretKey: string;
|
|
27
|
-
readonly sandbox: boolean;
|
|
28
25
|
/** The base URL in use. */
|
|
29
26
|
readonly baseUrl: string;
|
|
30
27
|
constructor(options: CyberSourceConfigOptions);
|
|
31
28
|
/**
|
|
32
29
|
* Reads `CYBER_SOURCE_PROFILE_ID`, `CYBER_SOURCE_ACCESS_KEY`, `CYBER_SOURCE_SECRET_KEY`,
|
|
33
|
-
*
|
|
30
|
+
* and `CYBER_SOURCE_BASE_URL`.
|
|
34
31
|
*/
|
|
35
32
|
static fromEnv(env?: EnvSource): CyberSourceConfig;
|
|
36
33
|
}
|
|
@@ -61,12 +58,12 @@ export interface CyberSourcePaymentData {
|
|
|
61
58
|
returnUrl?: string | undefined;
|
|
62
59
|
/** The page shown on cancel (`override_custom_cancel_page`), at most 255 characters. */
|
|
63
60
|
cancelUrl?: string | undefined;
|
|
64
|
-
/** An ISO 4217 code
|
|
65
|
-
currency
|
|
66
|
-
/** What to do with the card
|
|
67
|
-
transactionType
|
|
68
|
-
/** The hosted page language, e.g. `en-us
|
|
69
|
-
locale
|
|
61
|
+
/** An ISO 4217 code, e.g. `MMK`. */
|
|
62
|
+
currency: string;
|
|
63
|
+
/** What to do with the card, e.g. `sale`. */
|
|
64
|
+
transactionType: CyberSourceTransactionType;
|
|
65
|
+
/** The hosted page language, e.g. `en-us`. */
|
|
66
|
+
locale: string;
|
|
70
67
|
}
|
|
71
68
|
/**
|
|
72
69
|
* CyberSource Secure Acceptance hosted checkout for card payments: signs the form and verifies the
|
|
@@ -87,7 +84,8 @@ export declare class CyberSource {
|
|
|
87
84
|
initiate(data: CyberSourcePaymentData): FormPayment;
|
|
88
85
|
/**
|
|
89
86
|
* Verifies CyberSource's result post. The same check works for the browser post to your receipt
|
|
90
|
-
* page.
|
|
87
|
+
* page. Only the fields listed in `signed_field_names` are trusted: `decision` and
|
|
88
|
+
* `req_reference_number` must be signed, and unsigned fields are left out of the result.
|
|
91
89
|
*/
|
|
92
90
|
handleCallback(request: CallbackRequest): PaymentCallback;
|
|
93
91
|
/** Signs the fields listed in `signed_field_names`; `undefined` when a listed field is missing. */
|
|
@@ -11,35 +11,31 @@ const status_js_1 = require("../core/status.js");
|
|
|
11
11
|
const validate_js_1 = require("../core/validate.js");
|
|
12
12
|
const values_js_1 = require("../core/values.js");
|
|
13
13
|
/**
|
|
14
|
-
* A CyberSource Secure Acceptance profile. A missing credential throws a `ConfigurationError`.
|
|
14
|
+
* A CyberSource Secure Acceptance profile. A missing credential throws a `ConfigurationError`. The
|
|
15
|
+
* URL defaults to production.
|
|
15
16
|
*/
|
|
16
17
|
class CyberSourceConfig {
|
|
17
|
-
static SANDBOX_URL = 'https://testsecureacceptance.cybersource.com';
|
|
18
18
|
static PRODUCTION_URL = 'https://secureacceptance.cybersource.com';
|
|
19
19
|
profileId;
|
|
20
20
|
accessKey;
|
|
21
21
|
secretKey;
|
|
22
|
-
sandbox;
|
|
23
22
|
/** The base URL in use. */
|
|
24
23
|
baseUrl;
|
|
25
24
|
constructor(options) {
|
|
26
25
|
this.profileId = (0, env_js_1.requireSetting)('cyber_source', 'profile_id', options.profileId);
|
|
27
26
|
this.accessKey = (0, env_js_1.requireSetting)('cyber_source', 'access_key', options.accessKey);
|
|
28
27
|
this.secretKey = (0, env_js_1.requireSetting)('cyber_source', 'secret_key', options.secretKey);
|
|
29
|
-
this.
|
|
30
|
-
this.baseUrl = (0, env_js_1.trimUrl)((0, env_js_1.optionalSetting)(options.baseUrl) ??
|
|
31
|
-
(this.sandbox ? CyberSourceConfig.SANDBOX_URL : CyberSourceConfig.PRODUCTION_URL));
|
|
28
|
+
this.baseUrl = (0, env_js_1.trimUrl)((0, env_js_1.optionalSetting)(options.baseUrl) ?? CyberSourceConfig.PRODUCTION_URL);
|
|
32
29
|
}
|
|
33
30
|
/**
|
|
34
31
|
* Reads `CYBER_SOURCE_PROFILE_ID`, `CYBER_SOURCE_ACCESS_KEY`, `CYBER_SOURCE_SECRET_KEY`,
|
|
35
|
-
*
|
|
32
|
+
* and `CYBER_SOURCE_BASE_URL`.
|
|
36
33
|
*/
|
|
37
34
|
static fromEnv(env = (0, env_js_1.defaultEnv)()) {
|
|
38
35
|
return new CyberSourceConfig({
|
|
39
36
|
profileId: (0, env_js_1.envFirst)(env, 'CYBER_SOURCE_PROFILE_ID'),
|
|
40
37
|
accessKey: (0, env_js_1.envFirst)(env, 'CYBER_SOURCE_ACCESS_KEY'),
|
|
41
38
|
secretKey: (0, env_js_1.envFirst)(env, 'CYBER_SOURCE_SECRET_KEY'),
|
|
42
|
-
sandbox: (0, env_js_1.envSandbox)(env, 'CYBER_SOURCE_SANDBOX'),
|
|
43
39
|
baseUrl: (0, env_js_1.envFirst)(env, 'CYBER_SOURCE_BASE_URL'),
|
|
44
40
|
});
|
|
45
41
|
}
|
|
@@ -77,7 +73,7 @@ const STATUSES = {
|
|
|
77
73
|
REVIEW: 'pending',
|
|
78
74
|
DECLINE: 'failed',
|
|
79
75
|
ERROR: 'failed',
|
|
80
|
-
CANCEL: '
|
|
76
|
+
CANCEL: 'canceled',
|
|
81
77
|
};
|
|
82
78
|
/**
|
|
83
79
|
* CyberSource Secure Acceptance hosted checkout for card payments: signs the form and verifies the
|
|
@@ -113,9 +109,12 @@ class CyberSource {
|
|
|
113
109
|
.string('cancelUrl', data.cancelUrl)
|
|
114
110
|
.url('cancelUrl', data.cancelUrl)
|
|
115
111
|
.max('cancelUrl', data.cancelUrl, 255)
|
|
116
|
-
.
|
|
117
|
-
.pattern('
|
|
118
|
-
.
|
|
112
|
+
.required('currency', data.currency)
|
|
113
|
+
.pattern('currency', data.currency, /^[A-Z]{3}$/, 'a three letter ISO 4217 code')
|
|
114
|
+
.required('transactionType', data.transactionType)
|
|
115
|
+
.when(!TRANSACTION_TYPES.includes(String(data.transactionType)), 'transactionType', 'The transactionType field is not a supported transaction type.')
|
|
116
|
+
.required('locale', data.locale)
|
|
117
|
+
.pattern('locale', data.locale, /^[a-z]{2}-[a-z]{2}$/, 'a locale code such as en-us')
|
|
119
118
|
.validate();
|
|
120
119
|
}
|
|
121
120
|
/**
|
|
@@ -130,11 +129,11 @@ class CyberSource {
|
|
|
130
129
|
transaction_uuid: (0, crypto_js_1.randomHex)(),
|
|
131
130
|
signed_field_names: SIGNED_FIELDS.join(','),
|
|
132
131
|
signed_date_time: new Date().toISOString().replace(/\.\d{3}Z$/, 'Z'),
|
|
133
|
-
locale: data.locale
|
|
134
|
-
transaction_type: data.transactionType
|
|
132
|
+
locale: data.locale,
|
|
133
|
+
transaction_type: data.transactionType,
|
|
135
134
|
reference_number: data.orderId,
|
|
136
135
|
amount: String((0, validate_js_1.toAmount)(data.amount)),
|
|
137
|
-
currency: data.currency
|
|
136
|
+
currency: data.currency,
|
|
138
137
|
override_custom_receipt_page: data.returnUrl ?? '',
|
|
139
138
|
override_backoffice_post_url: data.callbackUrl,
|
|
140
139
|
override_custom_cancel_page: data.cancelUrl ?? '',
|
|
@@ -149,31 +148,39 @@ class CyberSource {
|
|
|
149
148
|
}
|
|
150
149
|
/**
|
|
151
150
|
* Verifies CyberSource's result post. The same check works for the browser post to your receipt
|
|
152
|
-
* page.
|
|
151
|
+
* page. Only the fields listed in `signed_field_names` are trusted: `decision` and
|
|
152
|
+
* `req_reference_number` must be signed, and unsigned fields are left out of the result.
|
|
153
153
|
*/
|
|
154
154
|
handleCallback(request) {
|
|
155
155
|
const payload = (0, callback_js_1.losslessInput)(request);
|
|
156
156
|
const expected = this.sign(payload);
|
|
157
|
-
|
|
157
|
+
const names = signedNames(payload);
|
|
158
|
+
if (expected === undefined ||
|
|
159
|
+
!(0, crypto_js_1.safeEqual)(expected, (0, values_js_1.get)(payload, 'signature')) ||
|
|
160
|
+
!names.includes('decision') ||
|
|
161
|
+
!names.includes('req_reference_number')) {
|
|
158
162
|
throw new errors_js_1.SignatureVerificationError('CyberSource callback signature verification failed.', (0, json_js_1.toPlainObject)(payload));
|
|
159
163
|
}
|
|
160
|
-
|
|
164
|
+
// A signature only covers the listed fields, so anything else in the post could have been
|
|
165
|
+
// added by the sender (e.g. a `decision` next to the signed request fields).
|
|
166
|
+
const signed = {};
|
|
167
|
+
for (const name of [...names, 'signature']) {
|
|
168
|
+
(0, json_js_1.setKey)(signed, name, payload[name]);
|
|
169
|
+
}
|
|
170
|
+
const decision = (0, values_js_1.trimmed)(signed, 'decision').toUpperCase();
|
|
161
171
|
return new callback_js_1.PaymentCallback({
|
|
162
|
-
orderId: (0, values_js_1.get)(
|
|
172
|
+
orderId: (0, values_js_1.get)(signed, 'req_reference_number'),
|
|
163
173
|
status: (0, status_js_1.resolveStatus)(STATUSES, decision),
|
|
164
174
|
gatewayStatus: decision,
|
|
165
|
-
gatewayReference: (0, values_js_1.optional)(
|
|
166
|
-
amount: (0, values_js_1.get)(
|
|
167
|
-
raw: (0, json_js_1.toPlainObject)(
|
|
175
|
+
gatewayReference: (0, values_js_1.optional)(signed, 'transaction_id'),
|
|
176
|
+
amount: (0, values_js_1.get)(signed, 'auth_amount') || (0, values_js_1.optional)(signed, 'req_amount'),
|
|
177
|
+
raw: (0, json_js_1.toPlainObject)(signed),
|
|
168
178
|
});
|
|
169
179
|
}
|
|
170
180
|
/** Signs the fields listed in `signed_field_names`; `undefined` when a listed field is missing. */
|
|
171
181
|
sign(fields) {
|
|
172
182
|
const pairs = [];
|
|
173
|
-
for (const name of (
|
|
174
|
-
if (name === '') {
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
183
|
+
for (const name of signedNames(fields)) {
|
|
177
184
|
const value = (0, values_js_1.scalarString)(fields[name]);
|
|
178
185
|
if (value === undefined) {
|
|
179
186
|
return undefined;
|
|
@@ -186,3 +193,9 @@ class CyberSource {
|
|
|
186
193
|
}
|
|
187
194
|
}
|
|
188
195
|
exports.CyberSource = CyberSource;
|
|
196
|
+
/** The names listed in `signed_field_names`, in order. */
|
|
197
|
+
function signedNames(fields) {
|
|
198
|
+
return (0, values_js_1.get)(fields, 'signed_field_names')
|
|
199
|
+
.split(',')
|
|
200
|
+
.filter((name) => name !== '');
|
|
201
|
+
}
|
|
@@ -12,33 +12,45 @@ export interface KbzPayConfigOptions {
|
|
|
12
12
|
appKey: string;
|
|
13
13
|
/** The `merch_code` KBZ issued. */
|
|
14
14
|
merchantCode: string;
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Seconds before the default HTTP client gives up on a request, a whole number greater than 0
|
|
17
|
+
* (or its text). Defaults to 30 (`DEFAULT_TIMEOUT_SECONDS`) when unset or blank. A
|
|
18
|
+
* client you pass keeps its own timeout.
|
|
19
|
+
*/
|
|
20
|
+
timeoutSeconds?: number | string | null | undefined;
|
|
21
|
+
/** Overrides the API base URL, e.g. to point at UAT or go through a proxy. */
|
|
18
22
|
apiUrl?: string | undefined;
|
|
19
23
|
/** Overrides the PWA checkout URL. A trailing `#` or `#/` is normalized to `#/`. */
|
|
20
24
|
pwaUrl?: string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Seconds the customer has to pay each order, a multiple of 60 from 60 to 7200 (or its text).
|
|
27
|
+
* A payment's own `timeToLiveSeconds` overrides it; unset or blank leaves it to KBZ (7200).
|
|
28
|
+
*/
|
|
29
|
+
timeToLiveSeconds?: number | string | null | undefined;
|
|
21
30
|
}
|
|
22
31
|
/**
|
|
23
|
-
* KBZ Pay credentials and endpoints. A missing
|
|
32
|
+
* KBZ Pay credentials and endpoints. A missing setting throws a `ConfigurationError`. The URLs
|
|
33
|
+
* default to production; UAT and production issue separate credentials.
|
|
24
34
|
*/
|
|
25
35
|
export declare class KbzPayConfig {
|
|
26
|
-
static readonly SANDBOX_API_URL = "http://api-uat.kbzpay.com/payment/gateway/uat";
|
|
27
36
|
static readonly PRODUCTION_API_URL = "https://api.kbzpay.com/payment/gateway";
|
|
28
|
-
static readonly SANDBOX_PWA_URL = "https://static.kbzpay.com/pgw/uat/pwa/#/";
|
|
29
37
|
static readonly PRODUCTION_PWA_URL = "https://wap.kbzpay.com/pgw/pwa/#/";
|
|
30
38
|
readonly appId: string;
|
|
31
39
|
readonly appKey: string;
|
|
32
40
|
readonly merchantCode: string;
|
|
33
|
-
|
|
41
|
+
/** Seconds before the default HTTP client gives up on a request. */
|
|
42
|
+
readonly timeoutSeconds: number;
|
|
34
43
|
/** The API base URL in use. */
|
|
35
44
|
readonly apiUrl: string;
|
|
36
45
|
/** The PWA checkout URL in use, always ending in `/`. */
|
|
37
46
|
readonly pwaUrl: string;
|
|
47
|
+
/** Seconds the customer has to pay each order unless the payment sets its own; `undefined` leaves it to KBZ. */
|
|
48
|
+
readonly timeToLiveSeconds: number | undefined;
|
|
38
49
|
constructor(options: KbzPayConfigOptions);
|
|
39
50
|
/**
|
|
40
|
-
* Reads `KBZ_PAY_APP_ID`, `KBZ_PAY_APP_KEY`, `KBZ_PAY_MERCHANT_CODE`,
|
|
41
|
-
* `KBZ_PAY_BASE_URL` and
|
|
51
|
+
* Reads `KBZ_PAY_APP_ID`, `KBZ_PAY_APP_KEY`, `KBZ_PAY_MERCHANT_CODE`,
|
|
52
|
+
* `MYANMAR_PAYMENTS_HTTP_TIMEOUT`, `KBZ_PAY_BASE_URL`, `KBZ_PAY_PWA_BASE_REDIRECT_URL` and
|
|
53
|
+
* `KBZ_PAY_TIME_TO_LIVE_IN_SECONDS`.
|
|
42
54
|
*/
|
|
43
55
|
static fromEnv(env?: EnvSource): KbzPayConfig;
|
|
44
56
|
}
|
|
@@ -54,8 +66,12 @@ export interface KbzPayPaymentData {
|
|
|
54
66
|
callbackUrl: string;
|
|
55
67
|
/** The product name shown to the customer. */
|
|
56
68
|
title?: string | undefined;
|
|
57
|
-
/**
|
|
58
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Seconds the customer has to pay before the order expires, 60 to 7200 in whole minutes (a multiple
|
|
71
|
+
* of 60). KBZ counts minutes, so 1800 is sent as `30m`. Overrides the config's `timeToLiveSeconds`;
|
|
72
|
+
* when both are unset KBZ allows 7200 (120 minutes).
|
|
73
|
+
*/
|
|
74
|
+
timeToLiveSeconds?: number | undefined;
|
|
59
75
|
/** Free text KBZ sends back unchanged in the callback, at most 512 characters once URL-encoded. */
|
|
60
76
|
callbackInfo?: string | undefined;
|
|
61
77
|
}
|
|
@@ -12,44 +12,65 @@ const status_js_1 = require("../core/status.js");
|
|
|
12
12
|
const validate_js_1 = require("../core/validate.js");
|
|
13
13
|
const values_js_1 = require("../core/values.js");
|
|
14
14
|
const signer_js_1 = require("./signer.js");
|
|
15
|
+
/** What a valid KBZ Pay `time_to_live_in_seconds` is: KBZ counts whole minutes, up to 120. */
|
|
16
|
+
const TIME_TO_LIVE_REQUIREMENT = 'a multiple of 60 from 60 to 7200';
|
|
17
|
+
function resolveTimeToLive(value) {
|
|
18
|
+
if (value === undefined || value === null || (typeof value === 'string' && value.trim() === '')) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
let seconds;
|
|
22
|
+
if (typeof value === 'number' && Number.isSafeInteger(value)) {
|
|
23
|
+
seconds = value;
|
|
24
|
+
}
|
|
25
|
+
else if (typeof value === 'string' && /^[+-]?\d+$/.test(value.trim())) {
|
|
26
|
+
seconds = Number.parseInt(value.trim(), 10);
|
|
27
|
+
}
|
|
28
|
+
if (seconds === undefined || seconds < 60 || seconds > 7200 || seconds % 60 !== 0) {
|
|
29
|
+
throw new errors_js_1.ConfigurationError('kbz_pay', 'time_to_live_in_seconds', true, TIME_TO_LIVE_REQUIREMENT);
|
|
30
|
+
}
|
|
31
|
+
return seconds;
|
|
32
|
+
}
|
|
15
33
|
/**
|
|
16
|
-
* KBZ Pay credentials and endpoints. A missing
|
|
34
|
+
* KBZ Pay credentials and endpoints. A missing setting throws a `ConfigurationError`. The URLs
|
|
35
|
+
* default to production; UAT and production issue separate credentials.
|
|
17
36
|
*/
|
|
18
37
|
class KbzPayConfig {
|
|
19
|
-
static SANDBOX_API_URL = 'http://api-uat.kbzpay.com/payment/gateway/uat';
|
|
20
38
|
static PRODUCTION_API_URL = 'https://api.kbzpay.com/payment/gateway';
|
|
21
|
-
static SANDBOX_PWA_URL = 'https://static.kbzpay.com/pgw/uat/pwa/#/';
|
|
22
39
|
static PRODUCTION_PWA_URL = 'https://wap.kbzpay.com/pgw/pwa/#/';
|
|
23
40
|
appId;
|
|
24
41
|
appKey;
|
|
25
42
|
merchantCode;
|
|
26
|
-
|
|
43
|
+
/** Seconds before the default HTTP client gives up on a request. */
|
|
44
|
+
timeoutSeconds;
|
|
27
45
|
/** The API base URL in use. */
|
|
28
46
|
apiUrl;
|
|
29
47
|
/** The PWA checkout URL in use, always ending in `/`. */
|
|
30
48
|
pwaUrl;
|
|
49
|
+
/** Seconds the customer has to pay each order unless the payment sets its own; `undefined` leaves it to KBZ. */
|
|
50
|
+
timeToLiveSeconds;
|
|
31
51
|
constructor(options) {
|
|
32
52
|
this.appId = (0, env_js_1.requireSetting)('kbz_pay', 'app_id', options.appId);
|
|
33
53
|
this.appKey = (0, env_js_1.requireSetting)('kbz_pay', 'app_key', options.appKey);
|
|
34
54
|
this.merchantCode = (0, env_js_1.requireSetting)('kbz_pay', 'merchant_code', options.merchantCode);
|
|
35
|
-
this.
|
|
36
|
-
this.apiUrl = (0, env_js_1.trimUrl)((0, env_js_1.optionalSetting)(options.apiUrl) ??
|
|
37
|
-
|
|
38
|
-
this.
|
|
39
|
-
(this.sandbox ? KbzPayConfig.SANDBOX_PWA_URL : KbzPayConfig.PRODUCTION_PWA_URL))}/`;
|
|
55
|
+
this.timeoutSeconds = (0, env_js_1.resolveTimeout)('kbz_pay', options.timeoutSeconds);
|
|
56
|
+
this.apiUrl = (0, env_js_1.trimUrl)((0, env_js_1.optionalSetting)(options.apiUrl) ?? KbzPayConfig.PRODUCTION_API_URL);
|
|
57
|
+
this.pwaUrl = `${(0, env_js_1.trimUrl)((0, env_js_1.optionalSetting)(options.pwaUrl) ?? KbzPayConfig.PRODUCTION_PWA_URL)}/`;
|
|
58
|
+
this.timeToLiveSeconds = resolveTimeToLive(options.timeToLiveSeconds);
|
|
40
59
|
}
|
|
41
60
|
/**
|
|
42
|
-
* Reads `KBZ_PAY_APP_ID`, `KBZ_PAY_APP_KEY`, `KBZ_PAY_MERCHANT_CODE`,
|
|
43
|
-
* `KBZ_PAY_BASE_URL` and
|
|
61
|
+
* Reads `KBZ_PAY_APP_ID`, `KBZ_PAY_APP_KEY`, `KBZ_PAY_MERCHANT_CODE`,
|
|
62
|
+
* `MYANMAR_PAYMENTS_HTTP_TIMEOUT`, `KBZ_PAY_BASE_URL`, `KBZ_PAY_PWA_BASE_REDIRECT_URL` and
|
|
63
|
+
* `KBZ_PAY_TIME_TO_LIVE_IN_SECONDS`.
|
|
44
64
|
*/
|
|
45
65
|
static fromEnv(env = (0, env_js_1.defaultEnv)()) {
|
|
46
66
|
return new KbzPayConfig({
|
|
47
67
|
appId: (0, env_js_1.envFirst)(env, 'KBZ_PAY_APP_ID'),
|
|
48
68
|
appKey: (0, env_js_1.envFirst)(env, 'KBZ_PAY_APP_KEY'),
|
|
49
69
|
merchantCode: (0, env_js_1.envFirst)(env, 'KBZ_PAY_MERCHANT_CODE'),
|
|
50
|
-
|
|
70
|
+
timeoutSeconds: (0, env_js_1.envFirst)(env, env_js_1.HTTP_TIMEOUT_VARIABLE),
|
|
51
71
|
apiUrl: (0, env_js_1.envFirst)(env, 'KBZ_PAY_BASE_URL'),
|
|
52
72
|
pwaUrl: (0, env_js_1.envFirst)(env, 'KBZ_PAY_PWA_BASE_REDIRECT_URL'),
|
|
73
|
+
timeToLiveSeconds: (0, env_js_1.envFirst)(env, 'KBZ_PAY_TIME_TO_LIVE_IN_SECONDS'),
|
|
53
74
|
});
|
|
54
75
|
}
|
|
55
76
|
}
|
|
@@ -60,7 +81,7 @@ const STATUSES = {
|
|
|
60
81
|
WAIT_PAY: 'pending',
|
|
61
82
|
PAYING: 'pending',
|
|
62
83
|
PAY_FAILED: 'failed',
|
|
63
|
-
ORDER_CLOSED: '
|
|
84
|
+
ORDER_CLOSED: 'canceled',
|
|
64
85
|
ORDER_EXPIRED: 'expired',
|
|
65
86
|
};
|
|
66
87
|
/**
|
|
@@ -76,7 +97,7 @@ class KbzPay {
|
|
|
76
97
|
constructor(config, options = {}) {
|
|
77
98
|
this.config = config instanceof KbzPayConfig ? config : new KbzPayConfig(config);
|
|
78
99
|
this.signer = new signer_js_1.KbzPaySigner(this.config.appKey);
|
|
79
|
-
this.transport = new http_js_1.Transport((0, http_js_1.httpClientFrom)(options));
|
|
100
|
+
this.transport = new http_js_1.Transport((0, http_js_1.httpClientFrom)(options, this.config.timeoutSeconds));
|
|
80
101
|
}
|
|
81
102
|
/** A gateway configured from the `KBZ_PAY_*` environment variables. */
|
|
82
103
|
static fromEnv(env = (0, env_js_1.defaultEnv)(), options = {}) {
|
|
@@ -95,7 +116,8 @@ class KbzPay {
|
|
|
95
116
|
.max('callbackUrl', callbackUrl, 512)
|
|
96
117
|
.when(callbackUrl.includes('?'), 'callbackUrl', 'The callbackUrl field must not contain a query string.')
|
|
97
118
|
.string('title', data.title)
|
|
98
|
-
.between('
|
|
119
|
+
.between('timeToLiveSeconds', data.timeToLiveSeconds, 60, 7200)
|
|
120
|
+
.when(Number.isInteger(data.timeToLiveSeconds) && data.timeToLiveSeconds % 60 !== 0, 'timeToLiveSeconds', 'The timeToLiveSeconds field must be a multiple of 60.')
|
|
99
121
|
.string('callbackInfo', data.callbackInfo)
|
|
100
122
|
.max('callbackInfo', typeof data.callbackInfo === 'string' ? (0, crypto_js_1.queryEscape)(data.callbackInfo) : undefined, 512)
|
|
101
123
|
.validate();
|
|
@@ -128,9 +150,9 @@ class KbzPay {
|
|
|
128
150
|
return new results_js_1.QrPayment({
|
|
129
151
|
orderId: data.orderId,
|
|
130
152
|
qrString: qrCode,
|
|
131
|
-
expiresAt:
|
|
153
|
+
expiresAt: order.timeToLiveSeconds === undefined
|
|
132
154
|
? undefined
|
|
133
|
-
: new Date(Date.now() +
|
|
155
|
+
: new Date(Date.now() + order.timeToLiveSeconds * 1000),
|
|
134
156
|
reference: order.prepayId,
|
|
135
157
|
raw: (0, json_js_1.toPlainObject)(order.response),
|
|
136
158
|
});
|
|
@@ -201,8 +223,9 @@ class KbzPay {
|
|
|
201
223
|
if (data.title) {
|
|
202
224
|
biz.title = data.title;
|
|
203
225
|
}
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
const timeToLiveSeconds = data.timeToLiveSeconds ?? this.config.timeToLiveSeconds;
|
|
227
|
+
if (timeToLiveSeconds !== undefined) {
|
|
228
|
+
biz.timeout_express = `${timeToLiveSeconds / 60}m`;
|
|
206
229
|
}
|
|
207
230
|
if (data.callbackInfo) {
|
|
208
231
|
biz.callback_info = (0, crypto_js_1.queryEscape)(data.callbackInfo);
|
|
@@ -214,7 +237,7 @@ class KbzPay {
|
|
|
214
237
|
if (prepayId === '') {
|
|
215
238
|
throw new errors_js_1.ApiError('KBZ Pay did not return a prepay_id.', { raw: (0, json_js_1.toPlainObject)(response) });
|
|
216
239
|
}
|
|
217
|
-
return { prepayId, nonce, timestamp: time, response };
|
|
240
|
+
return { prepayId, nonce, timestamp: time, timeToLiveSeconds, response };
|
|
218
241
|
}
|
|
219
242
|
/** Sends a signed request and returns the `Response` object, throwing when KBZ reports an error. */
|
|
220
243
|
async call(endpoint, method, version, biz, extra, nonce, time, signal) {
|
|
@@ -10,6 +10,9 @@ export declare class KbzPaySigner {
|
|
|
10
10
|
signString(fields: Readonly<Record<string, unknown>>): string;
|
|
11
11
|
/** The uppercase SHA-256 signature of `fields`. */
|
|
12
12
|
sign(fields: Readonly<Record<string, unknown>>): string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Whether `fields.sign` matches, compared in constant time. Nested values are never signed, so
|
|
15
|
+
* fields holding an object or array fail rather than being partly trusted.
|
|
16
|
+
*/
|
|
14
17
|
verify(fields: Readonly<Record<string, unknown>>): boolean;
|
|
15
18
|
}
|
|
@@ -27,10 +27,15 @@ class KbzPaySigner {
|
|
|
27
27
|
sign(fields) {
|
|
28
28
|
return (0, crypto_js_1.sha256Hex)(`${this.signString(fields)}&key=${this.appKey}`).toUpperCase();
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Whether `fields.sign` matches, compared in constant time. Nested values are never signed, so
|
|
32
|
+
* fields holding an object or array fail rather than being partly trusted.
|
|
33
|
+
*/
|
|
31
34
|
verify(fields) {
|
|
32
35
|
const sign = fields.sign;
|
|
33
|
-
return typeof sign === 'string' &&
|
|
36
|
+
return (typeof sign === 'string' &&
|
|
37
|
+
!Object.values(fields).some(values_js_1.isNested) &&
|
|
38
|
+
(0, crypto_js_1.safeEqual)(this.sign(fields), sign.toUpperCase()));
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
exports.KbzPaySigner = KbzPaySigner;
|