sahab-utils 1.7.9 → 1.8.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/dist/constants/types.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/ts/cash.d.ts +15 -0
- package/dist/ts/cash.js +34 -0
- package/dist/ts/iban.d.ts +15 -0
- package/dist/ts/iban.js +38 -0
- package/package.json +2 -2
|
@@ -949,6 +949,8 @@ export type WeatherData = {
|
|
|
949
949
|
};
|
|
950
950
|
export type PaymentConfigs = {
|
|
951
951
|
isCashAllowed: boolean;
|
|
952
|
+
/** Sahab's surcharge on a cash order, in SAR. Missing, invalid or above MAX_CASH_FEE means no fee. */
|
|
953
|
+
cashFee?: number;
|
|
952
954
|
isGooglePayAllowed?: boolean;
|
|
953
955
|
/** Tap GoSell merchant id for card / Apple Pay SDK config (optional; falls back in app). */
|
|
954
956
|
tapMerchantId?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from './constants/types';
|
|
2
2
|
export * from './ts/broadcast';
|
|
3
3
|
export * from './ts/calcs';
|
|
4
|
+
export * from './ts/cash';
|
|
4
5
|
export * from './ts/cashback';
|
|
5
6
|
export * from './ts/gifts';
|
|
7
|
+
export * from './ts/iban';
|
|
6
8
|
export * from './ts/loyalty';
|
|
7
9
|
export * from './ts/orderActivity';
|
|
8
10
|
export * from './ts/push';
|
package/dist/index.js
CHANGED
|
@@ -17,8 +17,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./constants/types"), exports);
|
|
18
18
|
__exportStar(require("./ts/broadcast"), exports);
|
|
19
19
|
__exportStar(require("./ts/calcs"), exports);
|
|
20
|
+
__exportStar(require("./ts/cash"), exports);
|
|
20
21
|
__exportStar(require("./ts/cashback"), exports);
|
|
21
22
|
__exportStar(require("./ts/gifts"), exports);
|
|
23
|
+
__exportStar(require("./ts/iban"), exports);
|
|
22
24
|
__exportStar(require("./ts/loyalty"), exports);
|
|
23
25
|
__exportStar(require("./ts/orderActivity"), exports);
|
|
24
26
|
__exportStar(require("./ts/push"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Order, PaymentConfigs } from '../constants/types';
|
|
2
|
+
/** Cash payment — the shared rules. The fee is set from `payment_methods/configs`; the app only shows it
|
|
3
|
+
* and cloudFun is what charges it, so both must reach the same number from the same config. */
|
|
4
|
+
/** A fee above this is read as a typo (5000 for 5.00) and ignored rather than charged to customers. */
|
|
5
|
+
export declare const MAX_CASH_FEE = 50;
|
|
6
|
+
/** What the customer owes ignoring the cash fee, which comes from the payment method and would make a fully discounted order look unpaid. */
|
|
7
|
+
export declare const getOrderTotalWithoutCashFee: (order?: Partial<Order>) => number;
|
|
8
|
+
/** Nothing left to collect: a 100% promo, balance covering everything, or free services. */
|
|
9
|
+
export declare const isFreeOrder: (order?: Partial<Order>) => boolean;
|
|
10
|
+
/** The configured fee in SAR, or 0 when it is missing, not a number, negative or above `MAX_CASH_FEE`. */
|
|
11
|
+
export declare function resolveCashFee(configs?: Pick<PaymentConfigs, 'cashFee'> | null): number;
|
|
12
|
+
/** The fee this order would carry if paid in cash: none when there is nothing to collect. */
|
|
13
|
+
export declare function cashFeeForOrder(order: Partial<Order> | undefined, configs?: Pick<PaymentConfigs, 'cashFee'> | null): number;
|
|
14
|
+
/** A redeemed gift tops up its extras online only; cash is fine once the gift covers everything. */
|
|
15
|
+
export declare function isCashAllowedForOrder(order?: Partial<Order>): boolean;
|
package/dist/ts/cash.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFreeOrder = exports.getOrderTotalWithoutCashFee = exports.MAX_CASH_FEE = void 0;
|
|
4
|
+
exports.resolveCashFee = resolveCashFee;
|
|
5
|
+
exports.cashFeeForOrder = cashFeeForOrder;
|
|
6
|
+
exports.isCashAllowedForOrder = isCashAllowedForOrder;
|
|
7
|
+
const types_1 = require("../constants/types");
|
|
8
|
+
const calcs_1 = require("./calcs");
|
|
9
|
+
const gifts_1 = require("./gifts");
|
|
10
|
+
/** Cash payment — the shared rules. The fee is set from `payment_methods/configs`; the app only shows it
|
|
11
|
+
* and cloudFun is what charges it, so both must reach the same number from the same config. */
|
|
12
|
+
/** A fee above this is read as a typo (5000 for 5.00) and ignored rather than charged to customers. */
|
|
13
|
+
exports.MAX_CASH_FEE = 50;
|
|
14
|
+
/** What the customer owes ignoring the cash fee, which comes from the payment method and would make a fully discounted order look unpaid. */
|
|
15
|
+
const getOrderTotalWithoutCashFee = (order) => (0, calcs_1.getCustomerTotalToPay)(Object.assign(Object.assign({}, (order || {})), { paymentType: types_1.OrderPaymentType.CREDIT, sahabExtraPaymentAmount: 0 }));
|
|
16
|
+
exports.getOrderTotalWithoutCashFee = getOrderTotalWithoutCashFee;
|
|
17
|
+
/** Nothing left to collect: a 100% promo, balance covering everything, or free services. */
|
|
18
|
+
const isFreeOrder = (order) => (0, exports.getOrderTotalWithoutCashFee)(order) <= 0;
|
|
19
|
+
exports.isFreeOrder = isFreeOrder;
|
|
20
|
+
/** The configured fee in SAR, or 0 when it is missing, not a number, negative or above `MAX_CASH_FEE`. */
|
|
21
|
+
function resolveCashFee(configs) {
|
|
22
|
+
const fee = Number(configs === null || configs === void 0 ? void 0 : configs.cashFee);
|
|
23
|
+
if (!Number.isFinite(fee) || fee <= 0 || fee > exports.MAX_CASH_FEE)
|
|
24
|
+
return 0;
|
|
25
|
+
return Math.round(fee * 100) / 100;
|
|
26
|
+
}
|
|
27
|
+
/** The fee this order would carry if paid in cash: none when there is nothing to collect. */
|
|
28
|
+
function cashFeeForOrder(order, configs) {
|
|
29
|
+
return (0, exports.isFreeOrder)(order) ? 0 : resolveCashFee(configs);
|
|
30
|
+
}
|
|
31
|
+
/** A redeemed gift tops up its extras online only; cash is fine once the gift covers everything. */
|
|
32
|
+
function isCashAllowedForOrder(order) {
|
|
33
|
+
return !((0, gifts_1.isGiftRedemption)(order) && !(0, exports.isFreeOrder)(order));
|
|
34
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The only transfer percentages a provider can be on. */
|
|
2
|
+
export declare const PROVIDER_PERCENTAGES: readonly ["7%", "10%"];
|
|
3
|
+
export type ProviderPercentage = (typeof PROVIDER_PERCENTAGES)[number];
|
|
4
|
+
export declare const isProviderPercentage: (value: unknown) => value is ProviderPercentage;
|
|
5
|
+
/** Pasted IBANs carry spaces and lower case; every check below expects this form. */
|
|
6
|
+
export declare const normalizeBankAccount: (raw: string) => string;
|
|
7
|
+
export declare const isValidSaudiIban: (value: string) => boolean;
|
|
8
|
+
export declare const isSabAccountNumber: (value: string) => boolean;
|
|
9
|
+
/** What a provider's payout can go to: a Saudi IBAN, or a SAB in-bank account number. */
|
|
10
|
+
export declare const isValidProviderAccount: (value: string) => boolean;
|
|
11
|
+
/** Whether the payout is an in-bank SAB transfer. Decided from the account itself, never from a form flag. */
|
|
12
|
+
export declare const isSabBankAccount: (value: string) => boolean;
|
|
13
|
+
/** The two-digit bank code inside a Saudi IBAN, for showing which bank an account is with. */
|
|
14
|
+
export declare const saudiIbanBankCode: (value: string) => string | undefined;
|
|
15
|
+
export declare const PROVIDER_ACCOUNT_FORMAT_HINT = "A Saudi IBAN (SA + 22 characters) or a SAB account number (SASABB + 12 digits)";
|
package/dist/ts/iban.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROVIDER_ACCOUNT_FORMAT_HINT = exports.saudiIbanBankCode = exports.isSabBankAccount = exports.isValidProviderAccount = exports.isSabAccountNumber = exports.isValidSaudiIban = exports.normalizeBankAccount = exports.isProviderPercentage = exports.PROVIDER_PERCENTAGES = void 0;
|
|
4
|
+
/** The only transfer percentages a provider can be on. */
|
|
5
|
+
exports.PROVIDER_PERCENTAGES = ['7%', '10%'];
|
|
6
|
+
const isProviderPercentage = (value) => exports.PROVIDER_PERCENTAGES.includes(value);
|
|
7
|
+
exports.isProviderPercentage = isProviderPercentage;
|
|
8
|
+
/** Pasted IBANs carry spaces and lower case; every check below expects this form. */
|
|
9
|
+
const normalizeBankAccount = (raw) => raw.replace(/\s+/g, '').toUpperCase();
|
|
10
|
+
exports.normalizeBankAccount = normalizeBankAccount;
|
|
11
|
+
// SA + 2 check digits + 2-digit bank code + 18 account characters, which may be letters.
|
|
12
|
+
const SAUDI_IBAN_SHAPE = /^SA\d{4}[0-9A-Z]{18}$/;
|
|
13
|
+
// The in-bank account form SAB transfers use instead of an IBAN.
|
|
14
|
+
const SAB_ACCOUNT_NUMBER = /^SASABB\d{12}$/;
|
|
15
|
+
/** ISO 13616 checksum: the rearranged IBAN, letters as 10-35, must leave remainder 1 modulo 97. */
|
|
16
|
+
const hasValidChecksum = (iban) => {
|
|
17
|
+
const rearranged = iban.slice(4) + iban.slice(0, 4);
|
|
18
|
+
let remainder = 0;
|
|
19
|
+
for (const char of rearranged) {
|
|
20
|
+
const value = parseInt(char, 36);
|
|
21
|
+
remainder = (value < 10 ? remainder * 10 + value : remainder * 100 + value) % 97;
|
|
22
|
+
}
|
|
23
|
+
return remainder === 1;
|
|
24
|
+
};
|
|
25
|
+
const isValidSaudiIban = (value) => SAUDI_IBAN_SHAPE.test(value) && hasValidChecksum(value);
|
|
26
|
+
exports.isValidSaudiIban = isValidSaudiIban;
|
|
27
|
+
const isSabAccountNumber = (value) => SAB_ACCOUNT_NUMBER.test(value);
|
|
28
|
+
exports.isSabAccountNumber = isSabAccountNumber;
|
|
29
|
+
/** What a provider's payout can go to: a Saudi IBAN, or a SAB in-bank account number. */
|
|
30
|
+
const isValidProviderAccount = (value) => (0, exports.isValidSaudiIban)(value) || (0, exports.isSabAccountNumber)(value);
|
|
31
|
+
exports.isValidProviderAccount = isValidProviderAccount;
|
|
32
|
+
/** Whether the payout is an in-bank SAB transfer. Decided from the account itself, never from a form flag. */
|
|
33
|
+
const isSabBankAccount = (value) => (0, exports.isSabAccountNumber)(value);
|
|
34
|
+
exports.isSabBankAccount = isSabBankAccount;
|
|
35
|
+
/** The two-digit bank code inside a Saudi IBAN, for showing which bank an account is with. */
|
|
36
|
+
const saudiIbanBankCode = (value) => ((0, exports.isValidSaudiIban)(value) ? value.slice(4, 6) : undefined);
|
|
37
|
+
exports.saudiIbanBankCode = saudiIbanBankCode;
|
|
38
|
+
exports.PROVIDER_ACCOUNT_FORMAT_HINT = 'A Saudi IBAN (SA + 22 characters) or a SAB account number (SASABB + 12 digits)';
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sahab-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "This package contains types and utils functions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"deploy": "npm run build && npm publish",
|
|
10
|
-
"test": "npm run build && node tests/loyalty.test.js && node tests/gifts.test.js && node tests/cashback.test.js && node tests/utils.test.js && node tests/wheel.test.js && node tests/calcs.test.js && node tests/orderActivity.test.js && node tests/push.test.js && node tests/broadcast.test.js"
|
|
10
|
+
"test": "npm run build && node tests/loyalty.test.js && node tests/gifts.test.js && node tests/cashback.test.js && node tests/utils.test.js && node tests/wheel.test.js && node tests/calcs.test.js && node tests/orderActivity.test.js && node tests/push.test.js && node tests/broadcast.test.js && node tests/iban.test.js && node tests/cash.test.js"
|
|
11
11
|
},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|