@atbas/m-mcp 0.1.0
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/LICENSE +21 -0
- package/README.md +364 -0
- package/dist/builder/transaction-builder.d.ts +53 -0
- package/dist/builder/transaction-builder.d.ts.map +1 -0
- package/dist/builder/transaction-builder.js +159 -0
- package/dist/builder/transaction-builder.js.map +1 -0
- package/dist/catalogue/default-catalogue.d.ts +18 -0
- package/dist/catalogue/default-catalogue.d.ts.map +1 -0
- package/dist/catalogue/default-catalogue.js +21 -0
- package/dist/catalogue/default-catalogue.js.map +1 -0
- package/dist/catalogue/endpoints.d.ts +22 -0
- package/dist/catalogue/endpoints.d.ts.map +1 -0
- package/dist/catalogue/endpoints.js +24 -0
- package/dist/catalogue/endpoints.js.map +1 -0
- package/dist/catalogue/flows.d.ts +32 -0
- package/dist/catalogue/flows.d.ts.map +1 -0
- package/dist/catalogue/flows.js +64 -0
- package/dist/catalogue/flows.js.map +1 -0
- package/dist/catalogue/hash-options.d.ts +112 -0
- package/dist/catalogue/hash-options.d.ts.map +1 -0
- package/dist/catalogue/hash-options.js +151 -0
- package/dist/catalogue/hash-options.js.map +1 -0
- package/dist/catalogue/types.d.ts +279 -0
- package/dist/catalogue/types.d.ts.map +1 -0
- package/dist/catalogue/types.js +360 -0
- package/dist/catalogue/types.js.map +1 -0
- package/dist/codegen/generate.d.ts +14 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/generate.js +490 -0
- package/dist/codegen/generate.js.map +1 -0
- package/dist/codegen/languages/node.d.ts +3 -0
- package/dist/codegen/languages/node.d.ts.map +1 -0
- package/dist/codegen/languages/node.js +32 -0
- package/dist/codegen/languages/node.js.map +1 -0
- package/dist/codegen/languages/php.d.ts +3 -0
- package/dist/codegen/languages/php.d.ts.map +1 -0
- package/dist/codegen/languages/php.js +27 -0
- package/dist/codegen/languages/php.js.map +1 -0
- package/dist/codegen/languages/python.d.ts +3 -0
- package/dist/codegen/languages/python.d.ts.map +1 -0
- package/dist/codegen/languages/python.js +27 -0
- package/dist/codegen/languages/python.js.map +1 -0
- package/dist/codegen/renderer.d.ts +24 -0
- package/dist/codegen/renderer.d.ts.map +1 -0
- package/dist/codegen/renderer.js +59 -0
- package/dist/codegen/renderer.js.map +1 -0
- package/dist/codegen/types.d.ts +50 -0
- package/dist/codegen/types.d.ts.map +1 -0
- package/dist/codegen/types.js +5 -0
- package/dist/codegen/types.js.map +1 -0
- package/dist/config/credentials.d.ts +64 -0
- package/dist/config/credentials.d.ts.map +1 -0
- package/dist/config/credentials.js +216 -0
- package/dist/config/credentials.js.map +1 -0
- package/dist/config/environment.d.ts +23 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +54 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/core/dependencies.d.ts +43 -0
- package/dist/core/dependencies.d.ts.map +1 -0
- package/dist/core/dependencies.js +2 -0
- package/dist/core/dependencies.js.map +1 -0
- package/dist/core/registry.d.ts +33 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +13 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/server.d.ts +61 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/server.js +90 -0
- package/dist/core/server.js.map +1 -0
- package/dist/dependencies.d.ts +28 -0
- package/dist/dependencies.d.ts.map +1 -0
- package/dist/dependencies.js +2 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/diagnostics/diagnose.d.ts +43 -0
- package/dist/diagnostics/diagnose.d.ts.map +1 -0
- package/dist/diagnostics/diagnose.js +121 -0
- package/dist/diagnostics/diagnose.js.map +1 -0
- package/dist/domain/amount.d.ts +157 -0
- package/dist/domain/amount.d.ts.map +1 -0
- package/dist/domain/amount.js +275 -0
- package/dist/domain/amount.js.map +1 -0
- package/dist/domain/datetime.d.ts +95 -0
- package/dist/domain/datetime.d.ts.map +1 -0
- package/dist/domain/datetime.js +211 -0
- package/dist/domain/datetime.js.map +1 -0
- package/dist/domain/response-codes.d.ts +116 -0
- package/dist/domain/response-codes.d.ts.map +1 -0
- package/dist/domain/response-codes.js +380 -0
- package/dist/domain/response-codes.js.map +1 -0
- package/dist/domain/secure-hash.d.ts +78 -0
- package/dist/domain/secure-hash.d.ts.map +1 -0
- package/dist/domain/secure-hash.js +69 -0
- package/dist/domain/secure-hash.js.map +1 -0
- package/dist/domain/signature/jazzcash.d.ts +56 -0
- package/dist/domain/signature/jazzcash.d.ts.map +1 -0
- package/dist/domain/signature/jazzcash.js +214 -0
- package/dist/domain/signature/jazzcash.js.map +1 -0
- package/dist/domain/signature/registry.d.ts +69 -0
- package/dist/domain/signature/registry.d.ts.map +1 -0
- package/dist/domain/signature/registry.js +92 -0
- package/dist/domain/signature/registry.js.map +1 -0
- package/dist/domain/signature/scheme.d.ts +349 -0
- package/dist/domain/signature/scheme.d.ts.map +1 -0
- package/dist/domain/signature/scheme.js +398 -0
- package/dist/domain/signature/scheme.js.map +1 -0
- package/dist/domain/txn-ref.d.ts +76 -0
- package/dist/domain/txn-ref.d.ts.map +1 -0
- package/dist/domain/txn-ref.js +126 -0
- package/dist/domain/txn-ref.js.map +1 -0
- package/dist/domain/types.d.ts +45 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +70 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/errors.d.ts +84 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +87 -0
- package/dist/errors.js.map +1 -0
- package/dist/gateway/checkout-form.d.ts +16 -0
- package/dist/gateway/checkout-form.d.ts.map +1 -0
- package/dist/gateway/checkout-form.js +103 -0
- package/dist/gateway/checkout-form.js.map +1 -0
- package/dist/gateway/checkout-output.d.ts +70 -0
- package/dist/gateway/checkout-output.d.ts.map +1 -0
- package/dist/gateway/checkout-output.js +199 -0
- package/dist/gateway/checkout-output.js.map +1 -0
- package/dist/gateway/guards.d.ts +37 -0
- package/dist/gateway/guards.d.ts.map +1 -0
- package/dist/gateway/guards.js +44 -0
- package/dist/gateway/guards.js.map +1 -0
- package/dist/gateway/http-client.d.ts +42 -0
- package/dist/gateway/http-client.d.ts.map +1 -0
- package/dist/gateway/http-client.js +153 -0
- package/dist/gateway/http-client.js.map +1 -0
- package/dist/gateway/rest-client.d.ts +42 -0
- package/dist/gateway/rest-client.d.ts.map +1 -0
- package/dist/gateway/rest-client.js +241 -0
- package/dist/gateway/rest-client.js.map +1 -0
- package/dist/gateway/transport.d.ts +31 -0
- package/dist/gateway/transport.d.ts.map +1 -0
- package/dist/gateway/transport.js +73 -0
- package/dist/gateway/transport.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/install/cli.d.ts +38 -0
- package/dist/install/cli.d.ts.map +1 -0
- package/dist/install/cli.js +274 -0
- package/dist/install/cli.js.map +1 -0
- package/dist/install/harnesses.d.ts +74 -0
- package/dist/install/harnesses.d.ts.map +1 -0
- package/dist/install/harnesses.js +263 -0
- package/dist/install/harnesses.js.map +1 -0
- package/dist/knowledge/corpus.d.ts +15 -0
- package/dist/knowledge/corpus.d.ts.map +1 -0
- package/dist/knowledge/corpus.js +129 -0
- package/dist/knowledge/corpus.js.map +1 -0
- package/dist/knowledge/search.d.ts +29 -0
- package/dist/knowledge/search.d.ts.map +1 -0
- package/dist/knowledge/search.js +181 -0
- package/dist/knowledge/search.js.map +1 -0
- package/dist/knowledge/source.d.ts +60 -0
- package/dist/knowledge/source.d.ts.map +1 -0
- package/dist/knowledge/source.js +91 -0
- package/dist/knowledge/source.js.map +1 -0
- package/dist/knowledge/types.d.ts +40 -0
- package/dist/knowledge/types.d.ts.map +1 -0
- package/dist/knowledge/types.js +19 -0
- package/dist/knowledge/types.js.map +1 -0
- package/dist/paths.d.ts +26 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +106 -0
- package/dist/paths.js.map +1 -0
- package/dist/prompts/integration-walkthrough.d.ts +11 -0
- package/dist/prompts/integration-walkthrough.d.ts.map +1 -0
- package/dist/prompts/integration-walkthrough.js +52 -0
- package/dist/prompts/integration-walkthrough.js.map +1 -0
- package/dist/prompts/register.d.ts +4 -0
- package/dist/prompts/register.d.ts.map +1 -0
- package/dist/prompts/register.js +4 -0
- package/dist/prompts/register.js.map +1 -0
- package/dist/registry.d.ts +14 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +2 -0
- package/dist/registry.js.map +1 -0
- package/dist/resources/knowledge-resources.d.ts +5 -0
- package/dist/resources/knowledge-resources.d.ts.map +1 -0
- package/dist/resources/knowledge-resources.js +121 -0
- package/dist/resources/knowledge-resources.js.map +1 -0
- package/dist/resources/register.d.ts +4 -0
- package/dist/resources/register.d.ts.map +1 -0
- package/dist/resources/register.js +8 -0
- package/dist/resources/register.js.map +1 -0
- package/dist/server.d.ts +40 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +56 -0
- package/dist/server.js.map +1 -0
- package/dist/telemetry/logger.d.ts +31 -0
- package/dist/telemetry/logger.d.ts.map +1 -0
- package/dist/telemetry/logger.js +67 -0
- package/dist/telemetry/logger.js.map +1 -0
- package/dist/telemetry/redaction.d.ts +88 -0
- package/dist/telemetry/redaction.d.ts.map +1 -0
- package/dist/telemetry/redaction.js +238 -0
- package/dist/telemetry/redaction.js.map +1 -0
- package/dist/tools/build-transaction-parameters.d.ts +10 -0
- package/dist/tools/build-transaction-parameters.d.ts.map +1 -0
- package/dist/tools/build-transaction-parameters.js +154 -0
- package/dist/tools/build-transaction-parameters.js.map +1 -0
- package/dist/tools/call-context.d.ts +47 -0
- package/dist/tools/call-context.d.ts.map +1 -0
- package/dist/tools/call-context.js +15 -0
- package/dist/tools/call-context.js.map +1 -0
- package/dist/tools/codegen-tools.d.ts +4 -0
- package/dist/tools/codegen-tools.d.ts.map +1 -0
- package/dist/tools/codegen-tools.js +131 -0
- package/dist/tools/codegen-tools.js.map +1 -0
- package/dist/tools/credential-tools.d.ts +32 -0
- package/dist/tools/credential-tools.d.ts.map +1 -0
- package/dist/tools/credential-tools.js +65 -0
- package/dist/tools/credential-tools.js.map +1 -0
- package/dist/tools/descriptor.d.ts +52 -0
- package/dist/tools/descriptor.d.ts.map +1 -0
- package/dist/tools/descriptor.js +2 -0
- package/dist/tools/descriptor.js.map +1 -0
- package/dist/tools/diagnostic-tools.d.ts +3 -0
- package/dist/tools/diagnostic-tools.d.ts.map +1 -0
- package/dist/tools/diagnostic-tools.js +128 -0
- package/dist/tools/diagnostic-tools.js.map +1 -0
- package/dist/tools/gateway-tools.d.ts +6 -0
- package/dist/tools/gateway-tools.d.ts.map +1 -0
- package/dist/tools/gateway-tools.js +381 -0
- package/dist/tools/gateway-tools.js.map +1 -0
- package/dist/tools/hash-tools.d.ts +5 -0
- package/dist/tools/hash-tools.d.ts.map +1 -0
- package/dist/tools/hash-tools.js +238 -0
- package/dist/tools/hash-tools.js.map +1 -0
- package/dist/tools/knowledge-tools.d.ts +7 -0
- package/dist/tools/knowledge-tools.d.ts.map +1 -0
- package/dist/tools/knowledge-tools.js +247 -0
- package/dist/tools/knowledge-tools.js.map +1 -0
- package/dist/tools/onboarding-checklist.d.ts +3 -0
- package/dist/tools/onboarding-checklist.d.ts.map +1 -0
- package/dist/tools/onboarding-checklist.js +141 -0
- package/dist/tools/onboarding-checklist.js.map +1 -0
- package/dist/tools/register.d.ts +60 -0
- package/dist/tools/register.d.ts.map +1 -0
- package/dist/tools/register.js +145 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/tool-kit.d.ts +88 -0
- package/dist/tools/tool-kit.d.ts.map +1 -0
- package/dist/tools/tool-kit.js +297 -0
- package/dist/tools/tool-kit.js.map +1 -0
- package/dist/validation/payload-validator.d.ts +29 -0
- package/dist/validation/payload-validator.d.ts.map +1 -0
- package/dist/validation/payload-validator.js +79 -0
- package/dist/validation/payload-validator.js.map +1 -0
- package/dist/validation/rules/amount.d.ts +3 -0
- package/dist/validation/rules/amount.d.ts.map +1 -0
- package/dist/validation/rules/amount.js +77 -0
- package/dist/validation/rules/amount.js.map +1 -0
- package/dist/validation/rules/bill-reference.d.ts +10 -0
- package/dist/validation/rules/bill-reference.d.ts.map +1 -0
- package/dist/validation/rules/bill-reference.js +34 -0
- package/dist/validation/rules/bill-reference.js.map +1 -0
- package/dist/validation/rules/cnic.d.ts +3 -0
- package/dist/validation/rules/cnic.d.ts.map +1 -0
- package/dist/validation/rules/cnic.js +42 -0
- package/dist/validation/rules/cnic.js.map +1 -0
- package/dist/validation/rules/constants.d.ts +9 -0
- package/dist/validation/rules/constants.d.ts.map +1 -0
- package/dist/validation/rules/constants.js +28 -0
- package/dist/validation/rules/constants.js.map +1 -0
- package/dist/validation/rules/constraints.d.ts +3 -0
- package/dist/validation/rules/constraints.d.ts.map +1 -0
- package/dist/validation/rules/constraints.js +113 -0
- package/dist/validation/rules/constraints.js.map +1 -0
- package/dist/validation/rules/datetime.d.ts +3 -0
- package/dist/validation/rules/datetime.d.ts.map +1 -0
- package/dist/validation/rules/datetime.js +49 -0
- package/dist/validation/rules/datetime.js.map +1 -0
- package/dist/validation/rules/empty-string.d.ts +13 -0
- package/dist/validation/rules/empty-string.d.ts.map +1 -0
- package/dist/validation/rules/empty-string.js +32 -0
- package/dist/validation/rules/empty-string.js.map +1 -0
- package/dist/validation/rules/expiry.d.ts +3 -0
- package/dist/validation/rules/expiry.d.ts.map +1 -0
- package/dist/validation/rules/expiry.js +52 -0
- package/dist/validation/rules/expiry.js.map +1 -0
- package/dist/validation/rules/msisdn.d.ts +3 -0
- package/dist/validation/rules/msisdn.d.ts.map +1 -0
- package/dist/validation/rules/msisdn.js +45 -0
- package/dist/validation/rules/msisdn.js.map +1 -0
- package/dist/validation/rules/presence.d.ts +3 -0
- package/dist/validation/rules/presence.d.ts.map +1 -0
- package/dist/validation/rules/presence.js +90 -0
- package/dist/validation/rules/presence.js.map +1 -0
- package/dist/validation/rules/return-url.d.ts +14 -0
- package/dist/validation/rules/return-url.d.ts.map +1 -0
- package/dist/validation/rules/return-url.js +58 -0
- package/dist/validation/rules/return-url.js.map +1 -0
- package/dist/validation/rules/tool-argument.d.ts +16 -0
- package/dist/validation/rules/tool-argument.d.ts.map +1 -0
- package/dist/validation/rules/tool-argument.js +38 -0
- package/dist/validation/rules/tool-argument.js.map +1 -0
- package/dist/validation/rules/txn-ref.d.ts +3 -0
- package/dist/validation/rules/txn-ref.d.ts.map +1 -0
- package/dist/validation/rules/txn-ref.js +47 -0
- package/dist/validation/rules/txn-ref.js.map +1 -0
- package/dist/validation/rules/unknown-fields.d.ts +10 -0
- package/dist/validation/rules/unknown-fields.d.ts.map +1 -0
- package/dist/validation/rules/unknown-fields.js +29 -0
- package/dist/validation/rules/unknown-fields.js.map +1 -0
- package/dist/validation/types.d.ts +36 -0
- package/dist/validation/types.d.ts.map +1 -0
- package/dist/validation/types.js +12 -0
- package/dist/validation/types.js.map +1 -0
- package/dist/version.d.ts +23 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/knowledge/diagnostics/symptoms.json +321 -0
- package/knowledge/docs/bnpl-guide.md +77 -0
- package/knowledge/docs/card-page-redirection.md +67 -0
- package/knowledge/docs/card-refund.md +91 -0
- package/knowledge/docs/hmac-sha256-calculation.md +72 -0
- package/knowledge/docs/index.json +189 -0
- package/knowledge/docs/ipn-implementation.md +89 -0
- package/knowledge/docs/mwallet-recurring.md +117 -0
- package/knowledge/docs/mwallet-refund.md +65 -0
- package/knowledge/docs/mwallet-rest-v1-1.md +126 -0
- package/knowledge/docs/mwallet-rest-v2-0-cnic.md +113 -0
- package/knowledge/docs/portal-guide.md +45 -0
- package/knowledge/docs/shopify-guide.md +74 -0
- package/knowledge/docs/status-inquiry-2025-26.md +32 -0
- package/knowledge/docs/status-inquiry.md +111 -0
- package/knowledge/docs/wordpress-guide.md +49 -0
- package/knowledge/flow.schema.json +390 -0
- package/knowledge/flows/bnpl-page-redirection-v1.1.json +211 -0
- package/knowledge/flows/card-page-redirection-v1.1.json +211 -0
- package/knowledge/flows/mwallet-rest-v1.1.json +187 -0
- package/knowledge/flows/mwallet-rest-v2.0-cnic.json +197 -0
- package/knowledge/flows/mwallet-token-pay-v4.0.json +130 -0
- package/knowledge/flows/refund-bnpl.json +105 -0
- package/knowledge/flows/refund-card-v1.1.json +112 -0
- package/knowledge/flows/refund-card-v2.0.json +121 -0
- package/knowledge/flows/refund-mwallet-v1.1.json +106 -0
- package/knowledge/flows/status-inquiry-v1.1.json +102 -0
- package/knowledge/flows/status-inquiry-v2.0.json +103 -0
- package/knowledge/flows/token-delete-v1.json +85 -0
- package/knowledge/flows/token-inquiry-v1.json +85 -0
- package/knowledge/flows/wallet-linking-portal.json +103 -0
- package/knowledge/templates/node/checkout-redirect.tmpl +166 -0
- package/knowledge/templates/node/ipn-handler.tmpl +108 -0
- package/knowledge/templates/node/rest-client.tmpl +197 -0
- package/knowledge/templates/node/wallet-linking.tmpl +147 -0
- package/knowledge/templates/php/checkout-redirect.tmpl +149 -0
- package/knowledge/templates/php/ipn-handler.tmpl +105 -0
- package/knowledge/templates/php/rest-client.tmpl +191 -0
- package/knowledge/templates/php/wallet-linking.tmpl +137 -0
- package/knowledge/templates/python/checkout-redirect.tmpl +148 -0
- package/knowledge/templates/python/ipn-handler.tmpl +110 -0
- package/knowledge/templates/python/rest-client.tmpl +179 -0
- package/knowledge/templates/python/wallet-linking.tmpl +133 -0
- package/package.json +84 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { describeError, JazzCashError } from '../errors.js';
|
|
2
|
+
import { maskSecretsDeep, scrubTextForTranscript } from '../telemetry/redaction.js';
|
|
3
|
+
import { createCallContext } from './call-context.js';
|
|
4
|
+
/** Build a tool result carrying both human-readable text and structured data. */
|
|
5
|
+
export function toolResult(text, options = {}) {
|
|
6
|
+
return {
|
|
7
|
+
content: [{ type: 'text', text }],
|
|
8
|
+
...(options.structuredContent === undefined ? {} : { structuredContent: options.structuredContent }),
|
|
9
|
+
...(options.isError === true ? { isError: true } : {}),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Build a failure result, naming the stable error code when there is one. */
|
|
13
|
+
export function toolError(error, secrets = []) {
|
|
14
|
+
const code = error instanceof JazzCashError ? `[${error.code}] ` : '';
|
|
15
|
+
const docRefs = error instanceof JazzCashError && error.docRefs.length > 0
|
|
16
|
+
? `\n\nSee: ${error.docRefs.map((ref) => `jazzcash://docs/${ref.docId}${ref.sectionId === undefined ? '' : `#${ref.sectionId}`}`).join(', ')}`
|
|
17
|
+
: '';
|
|
18
|
+
return toolResult(scrubTextForTranscript(`${code}${describeError(error)}${docRefs}`, secrets), { isError: true });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wrap a handler so failures surface as tool errors and secrets never leak.
|
|
22
|
+
*
|
|
23
|
+
* Redaction covers every configured profile, not only the one in play, because a
|
|
24
|
+
* caller can pass an explicit salt for one environment while another is active.
|
|
25
|
+
*
|
|
26
|
+
* The handler receives a {@link CallContext} built for this invocation. It is
|
|
27
|
+
* built from `deps` rather than from the SDK's `extra` argument, and the second
|
|
28
|
+
* parameter below is the reason that distinction has to be written down.
|
|
29
|
+
*
|
|
30
|
+
* A `declaration` makes the wrapper refuse forbidden arguments before the
|
|
31
|
+
* handler runs at all, so the flagged value is never read, built into a payload
|
|
32
|
+
* or written to a log. The refusal is returned through the same scrubbing path
|
|
33
|
+
* as any other result.
|
|
34
|
+
*/
|
|
35
|
+
export function safeHandler(deps, toolName, handler, declaration) {
|
|
36
|
+
// `_extra` is where the SDK passes its `RequestHandlerExtra`. It is accepted
|
|
37
|
+
// and deliberately ignored: `extra.authInfo` is populated only by the HTTP
|
|
38
|
+
// transports' auth middleware, and this server connects a
|
|
39
|
+
// `StdioServerTransport` (src/index.ts), so `extra.authInfo` is permanently
|
|
40
|
+
// empty here and must never be read as an authorisation source — a check
|
|
41
|
+
// written against it would pass for every caller. `CoreDependencies` is the
|
|
42
|
+
// same object on every transport, so the call identity is derived from there.
|
|
43
|
+
return async (args, _extra) => {
|
|
44
|
+
// Declared out here and assigned inside the try, so that building the
|
|
45
|
+
// context or reading the credentials is covered by the wrapper as well.
|
|
46
|
+
// Neither throws today, but the promise this module makes is unconditional:
|
|
47
|
+
// a statement above the `try` is one whose failure crosses the protocol
|
|
48
|
+
// boundary raw, and the scrub list would not exist to redact it.
|
|
49
|
+
let context;
|
|
50
|
+
let secrets = [];
|
|
51
|
+
try {
|
|
52
|
+
context = createCallContext(deps, toolName);
|
|
53
|
+
secrets = deps.credentials.allSecrets();
|
|
54
|
+
// Refused ahead of the handler, so a flagged value never reaches the
|
|
55
|
+
// builder, the signer or the transport. `??` rather than an early return
|
|
56
|
+
// because a refusal is transcript content like any other result and must
|
|
57
|
+
// go through the same scrubbing below.
|
|
58
|
+
const refusal = declaration === undefined ? undefined : refuseDeclaredArguments(deps, context, args, declaration);
|
|
59
|
+
const result = refusal ?? (await handler(args, context));
|
|
60
|
+
return {
|
|
61
|
+
...result,
|
|
62
|
+
// A text block often embeds a payload as JSON, so it needs the same
|
|
63
|
+
// name-based masking as structuredContent: literal masking alone cannot
|
|
64
|
+
// remove a four-digit MPIN without corrupting unrelated values.
|
|
65
|
+
content: result.content.map((block) => block.type === 'text' ? { ...block, text: scrubTextForTranscript(block.text, secrets) } : block),
|
|
66
|
+
// structuredContent is transcript content too. A payload echoed back to
|
|
67
|
+
// the caller carries pp_Password, so it must be scrubbed as well.
|
|
68
|
+
...(result.structuredContent === undefined
|
|
69
|
+
? {}
|
|
70
|
+
: { structuredContent: maskSecretsDeep(result.structuredContent, secrets) }),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logCallFailure(context, error);
|
|
75
|
+
return toolError(error, secrets);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Record a failed call, without the record becoming the failure.
|
|
81
|
+
*
|
|
82
|
+
* Logged through the context, so the record carries the `requestId` as well as
|
|
83
|
+
* the `tool` binding it already had. Without it, two concurrent calls to the
|
|
84
|
+
* same tool produce indistinguishable failure records.
|
|
85
|
+
*
|
|
86
|
+
* Both defences here exist because this runs on the failure path, where the
|
|
87
|
+
* caller is owed the scrubbed tool error and nothing else. The context is
|
|
88
|
+
* absent only when its own construction threw, and the logger writes to stderr,
|
|
89
|
+
* which can fail — EPIPE once the client has gone. Either would otherwise
|
|
90
|
+
* replace an explained payment failure with a raw logging one.
|
|
91
|
+
*/
|
|
92
|
+
function logCallFailure(context, error) {
|
|
93
|
+
try {
|
|
94
|
+
context?.logger.warn('Tool call failed', { error: describeError(error) });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Deliberately swallowed: the scrubbed error below is the result that matters.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Parameter names one flow refuses to accept as a tool argument. */
|
|
101
|
+
function flowForbiddenNames(flow) {
|
|
102
|
+
return flow.parameters.filter((parameter) => parameter.neverAcceptAsToolArgument).map((parameter) => parameter.name);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Every flagged name in the catalogue, whatever flow the call names.
|
|
106
|
+
*
|
|
107
|
+
* Deliberately the whole union rather than one flow's list: a name one flow
|
|
108
|
+
* calls a PAN is a PAN wherever it appears, and naming a flow must not be a way
|
|
109
|
+
* to *narrow* what this server refuses — that would make the refusal weakest for
|
|
110
|
+
* the caller who told it the most. The tools that take a payload without a
|
|
111
|
+
* `flowId` — hash debugging, diagnosis, IPN verification — have nothing else to
|
|
112
|
+
* go on in any case. The stricter reading costs a caller one refusal on a field
|
|
113
|
+
* named after card data; the looser one accepts a card number that no later
|
|
114
|
+
* redaction can recall.
|
|
115
|
+
*/
|
|
116
|
+
function catalogueForbiddenNames(catalogue) {
|
|
117
|
+
return [...new Set(catalogue.list().flatMap(flowForbiddenNames))];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Fold a field name to the form the refusal compares on.
|
|
121
|
+
*
|
|
122
|
+
* Three foldings, each closing a way of writing the same field that this server
|
|
123
|
+
* would otherwise read as a different one:
|
|
124
|
+
*
|
|
125
|
+
* - NFKC, because `vp_Pan` in fullwidth forms is `vp_Pan` to every reader
|
|
126
|
+
* and to the gateway, and matching raw code points accepts a PAN from anyone
|
|
127
|
+
* who typed it that way.
|
|
128
|
+
* - Format characters removed, because NFKC leaves them in place: a single
|
|
129
|
+
* zero-width space suffixed to `vp_Pan` survives normalisation and the key
|
|
130
|
+
* still looks identical in the transcript.
|
|
131
|
+
* - Case folded and surrounding whitespace trimmed last, because the point is
|
|
132
|
+
* to stop the value being processed, not to grade the caller's spelling.
|
|
133
|
+
*/
|
|
134
|
+
function foldForMatching(name) {
|
|
135
|
+
// Built per call rather than held at module scope: a `g`-flagged pattern is
|
|
136
|
+
// mutable state, and this module has no reason to carry any.
|
|
137
|
+
return name
|
|
138
|
+
.normalize('NFKC')
|
|
139
|
+
.replace(/\p{Cf}/gu, '')
|
|
140
|
+
.trim()
|
|
141
|
+
.toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* True when `supplied` carries a usable value for `name`, however it is written.
|
|
145
|
+
*
|
|
146
|
+
* Absent, null and empty are all treated as nothing supplied, which is the same
|
|
147
|
+
* line the `tool-argument.forbidden` validation rule draws.
|
|
148
|
+
*/
|
|
149
|
+
function carriesValue(supplied, name) {
|
|
150
|
+
if (supplied === undefined) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const wanted = foldForMatching(name);
|
|
154
|
+
return Object.entries(supplied).some(([key, value]) => foldForMatching(key) === wanted && value !== undefined && value !== null && value !== '');
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Say whose flow flagged the refused names.
|
|
158
|
+
*
|
|
159
|
+
* A refusal can name a field the flow in play never declares, because the list
|
|
160
|
+
* is catalogue-wide. Saying so is the difference between "you sent card data"
|
|
161
|
+
* and an apparently arbitrary rejection of a field the merchant's own flow
|
|
162
|
+
* specification does not mention.
|
|
163
|
+
*/
|
|
164
|
+
function attributionSentences(names, flow) {
|
|
165
|
+
const declared = flowForbiddenNames(flow);
|
|
166
|
+
const own = names.filter((name) => declared.includes(name));
|
|
167
|
+
const foreign = names.filter((name) => !declared.includes(name));
|
|
168
|
+
const sentences = [];
|
|
169
|
+
if (own.length > 0) {
|
|
170
|
+
sentences.push(foreign.length === 0
|
|
171
|
+
? `${flow.title} declares ${own.length === 1 ? 'this field' : 'these fields'} as data that cannot cross the tool boundary.`
|
|
172
|
+
: `${flow.title} declares ${own.join(', ')} as data that cannot cross the tool boundary.`);
|
|
173
|
+
}
|
|
174
|
+
if (foreign.length > 0) {
|
|
175
|
+
sentences.push(`${foreign.join(', ')} ${foreign.length === 1 ? 'is' : 'are'} flagged by another flow in this catalogue rather than by ${flow.title}, and a name one flow uses for card data is card data wherever it appears, so it is refused here too.`);
|
|
176
|
+
}
|
|
177
|
+
return sentences.join(' ');
|
|
178
|
+
}
|
|
179
|
+
/** Explain the refusal, naming the fields and nothing else. */
|
|
180
|
+
function forbiddenArgumentText(names, flow) {
|
|
181
|
+
const single = names.length === 1;
|
|
182
|
+
return [
|
|
183
|
+
`Refused: this call carries ${single ? 'a field' : 'fields'} that must never reach this server as a tool argument. Nothing was built, signed, sent or verified.`,
|
|
184
|
+
'',
|
|
185
|
+
'## Refused',
|
|
186
|
+
bulletList(names),
|
|
187
|
+
'',
|
|
188
|
+
'Only the names are listed above. The values are repeated nowhere in this result, and this server has neither processed nor stored them.',
|
|
189
|
+
'',
|
|
190
|
+
flow === undefined
|
|
191
|
+
? // Both ways of arriving here, because the wrapper's lookup is tolerant:
|
|
192
|
+
// a mistyped id must not turn a call carrying card data into "no such
|
|
193
|
+
// flow", which would report nothing about the field that was sent.
|
|
194
|
+
'No flowId was supplied, or the one supplied names no flow in this catalogue, so every field any flow in the catalogue marks as unacceptable is refused.'
|
|
195
|
+
: attributionSentences(names, flow),
|
|
196
|
+
'',
|
|
197
|
+
'## What to do instead',
|
|
198
|
+
`${single ? 'This field belongs' : 'These fields belong'} in the integration code that runs on your own server, never in a tool call. Generate that code with jazzcash_generate_integration_code — it emits every field, including ${single ? 'this one' : 'these'} — and supply the value there, or use the provider's hosted page or hosted fields so it travels from the customer's browser to the gateway without passing through you.`,
|
|
199
|
+
'',
|
|
200
|
+
'A tool argument is written to the conversation before this server runs, so refusing it here cannot recall it. Treat the value already sent as compromised.',
|
|
201
|
+
].join('\n');
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Refuse a call carrying a parameter a flow forbids as a tool argument.
|
|
205
|
+
*
|
|
206
|
+
* Module-private: every tool reaches it through a
|
|
207
|
+
* {@link PayloadArgumentDeclaration}, so the check is a property of the
|
|
208
|
+
* registration rather than a line each handler has to remember to write. A
|
|
209
|
+
* handler that called it directly could also decide not to.
|
|
210
|
+
*
|
|
211
|
+
* `flow` chooses the wording only. The names refused are the catalogue-wide
|
|
212
|
+
* union in every case, so naming a flow can never narrow what is refused; see
|
|
213
|
+
* {@link catalogueForbiddenNames}.
|
|
214
|
+
*
|
|
215
|
+
* The `tool-argument.forbidden` validation rule still stands behind this and is
|
|
216
|
+
* not made redundant by it: the rule also fires for a payload assembled inside
|
|
217
|
+
* the server, and it is the last line for any caller path added later.
|
|
218
|
+
*
|
|
219
|
+
* Returns `undefined` when there is nothing to refuse, so the handler proceeds
|
|
220
|
+
* unchanged for every payload that carries no flagged field.
|
|
221
|
+
*/
|
|
222
|
+
function refuseForbiddenArguments(deps, context, flow, ...supplied) {
|
|
223
|
+
const forbidden = catalogueForbiddenNames(deps.catalogue);
|
|
224
|
+
const refused = forbidden.filter((name) => supplied.some((object) => carriesValue(object, name)));
|
|
225
|
+
if (refused.length === 0) {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
// The catalogue's spelling of each name is recorded, never the caller's key
|
|
229
|
+
// and never the value: a log record is exactly the durable artefact this
|
|
230
|
+
// refusal exists to keep card data out of.
|
|
231
|
+
context.logger.warn('Refused a tool argument the flow forbids', { fields: refused });
|
|
232
|
+
return toolResult(forbiddenArgumentText(refused, flow), {
|
|
233
|
+
isError: true,
|
|
234
|
+
structuredContent: {
|
|
235
|
+
refused: true,
|
|
236
|
+
flowId: flow?.id ?? null,
|
|
237
|
+
refusedFields: refused,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Read an argument as a payload map, or as nothing at all.
|
|
243
|
+
*
|
|
244
|
+
* An argument the caller omitted, or sent as an array or a scalar, addresses no
|
|
245
|
+
* field by name and so can carry no flagged one. Narrowed rather than cast: a
|
|
246
|
+
* declaration naming an argument that is not a map must degrade to "nothing
|
|
247
|
+
* supplied" instead of throwing inside the refusal check.
|
|
248
|
+
*/
|
|
249
|
+
function payloadRecordOf(value) {
|
|
250
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
251
|
+
? value
|
|
252
|
+
: undefined;
|
|
253
|
+
}
|
|
254
|
+
/** Apply a registered {@link PayloadArgumentDeclaration} to one call's arguments. */
|
|
255
|
+
function refuseDeclaredArguments(deps, context, args, declaration) {
|
|
256
|
+
const supplied = payloadRecordOf(args) ?? {};
|
|
257
|
+
const flowId = declaration.flowFrom === undefined ? undefined : supplied[declaration.flowFrom];
|
|
258
|
+
// `find` rather than `get`: the flow chooses the wording of the refusal and
|
|
259
|
+
// nothing else, so a lookup that threw would make the wrapper decide what an
|
|
260
|
+
// unknown flow id means — a decision each tool already makes for itself, and
|
|
261
|
+
// differently. It would also answer a call carrying a PAN with "no such flow",
|
|
262
|
+
// leaving the field neither refused nor reported.
|
|
263
|
+
const flow = typeof flowId === 'string' ? deps.catalogue.find(flowId) : undefined;
|
|
264
|
+
return refuseForbiddenArguments(deps, context, flow, ...declaration.payloadArguments.map((name) => payloadRecordOf(supplied[name])));
|
|
265
|
+
}
|
|
266
|
+
/** Resolve the environment a tool should act in, defaulting to the configured one. */
|
|
267
|
+
export function resolveEnvironment(deps, requested) {
|
|
268
|
+
return requested ?? deps.config.defaultEnvironment;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Resolve the Integrity Salt for a hash operation.
|
|
272
|
+
*
|
|
273
|
+
* An explicit salt is accepted because a merchant debugging a mismatch usually
|
|
274
|
+
* has the value in front of them and may not have configured the server yet.
|
|
275
|
+
*/
|
|
276
|
+
export function resolveIntegritySalt(deps, explicitSalt, environment) {
|
|
277
|
+
if (explicitSalt !== undefined && explicitSalt !== '') {
|
|
278
|
+
return { salt: explicitSalt, source: 'supplied in the tool call' };
|
|
279
|
+
}
|
|
280
|
+
const profile = deps.credentials.get(environment);
|
|
281
|
+
return { salt: profile.integritySalt, source: `the configured ${environment} credentials` };
|
|
282
|
+
}
|
|
283
|
+
/** Render a bullet list, or a placeholder when there is nothing to list. */
|
|
284
|
+
export function bulletList(items, empty = '(none)') {
|
|
285
|
+
return items.length === 0 ? empty : items.map((item) => `- ${item}`).join('\n');
|
|
286
|
+
}
|
|
287
|
+
/** Render a citation line for a set of documentation references. */
|
|
288
|
+
export function citations(refs) {
|
|
289
|
+
if (refs.length === 0) {
|
|
290
|
+
return '';
|
|
291
|
+
}
|
|
292
|
+
const unique = [
|
|
293
|
+
...new Set(refs.map((ref) => `jazzcash://docs/${ref.docId}${ref.sectionId === undefined ? '' : `#${ref.sectionId}`}`)),
|
|
294
|
+
];
|
|
295
|
+
return `\n\nSources: ${unique.join(', ')}`;
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=tool-kit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-kit.js","sourceRoot":"","sources":["../../src/tools/tool-kit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IntegrationFlow } from '../catalogue/types.js';
|
|
2
|
+
import { type NormalisedParameters, type RawParameters } from '../domain/types.js';
|
|
3
|
+
import type { ValidationIssue, ValidationRule } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Payload validation.
|
|
6
|
+
*
|
|
7
|
+
* Composes the independent rule modules. Each rule owns exactly one concern and
|
|
8
|
+
* is responsible for not duplicating another rule's diagnosis, so a single
|
|
9
|
+
* mistake yields a single actionable issue rather than a wall of noise.
|
|
10
|
+
*/
|
|
11
|
+
export declare const VALIDATION_RULES: readonly ValidationRule[];
|
|
12
|
+
export interface ValidationOptions {
|
|
13
|
+
/** The `pp_ReturnURL` registered against the merchant profile, when known. */
|
|
14
|
+
readonly registeredReturnUrl?: string | undefined;
|
|
15
|
+
readonly now?: Date | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface ValidationResult {
|
|
18
|
+
readonly valid: boolean;
|
|
19
|
+
readonly errors: readonly ValidationIssue[];
|
|
20
|
+
readonly warnings: readonly ValidationIssue[];
|
|
21
|
+
/** Every value coerced to its wire representation, ready to hash and send. */
|
|
22
|
+
readonly normalisedPayload: NormalisedParameters;
|
|
23
|
+
readonly flowId: string;
|
|
24
|
+
}
|
|
25
|
+
/** Validate a payload against its flow specification. */
|
|
26
|
+
export declare function validatePayload(flow: IntegrationFlow, parameters: RawParameters, options?: ValidationOptions): ValidationResult;
|
|
27
|
+
/** Render a validation result as text for a merchant or an AI harness. */
|
|
28
|
+
export declare function formatValidationResult(result: ValidationResult): string;
|
|
29
|
+
//# sourceMappingURL=payload-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-validator.d.ts","sourceRoot":"","sources":["../../src/validation/payload-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAuB,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAexG,OAAO,KAAK,EAAe,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/E;;;;;;GAMG;AAEH,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAoBrD,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,8EAA8E;IAC9E,QAAQ,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,aAAa,EACzB,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAkBlB;AAED,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAqBvE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { normaliseParameters } from '../domain/types.js';
|
|
2
|
+
import { amountRule } from './rules/amount.js';
|
|
3
|
+
import { billReferenceRule } from './rules/bill-reference.js';
|
|
4
|
+
import { cnicRule } from './rules/cnic.js';
|
|
5
|
+
import { constantsRule } from './rules/constants.js';
|
|
6
|
+
import { constraintsRule } from './rules/constraints.js';
|
|
7
|
+
import { dateTimeRule } from './rules/datetime.js';
|
|
8
|
+
import { emptyStringRule } from './rules/empty-string.js';
|
|
9
|
+
import { expiryRule } from './rules/expiry.js';
|
|
10
|
+
import { msisdnRule } from './rules/msisdn.js';
|
|
11
|
+
import { presenceRule } from './rules/presence.js';
|
|
12
|
+
import { returnUrlRule } from './rules/return-url.js';
|
|
13
|
+
import { toolArgumentRule } from './rules/tool-argument.js';
|
|
14
|
+
import { txnRefRule } from './rules/txn-ref.js';
|
|
15
|
+
import { unknownFieldsRule } from './rules/unknown-fields.js';
|
|
16
|
+
/**
|
|
17
|
+
* Payload validation.
|
|
18
|
+
*
|
|
19
|
+
* Composes the independent rule modules. Each rule owns exactly one concern and
|
|
20
|
+
* is responsible for not duplicating another rule's diagnosis, so a single
|
|
21
|
+
* mistake yields a single actionable issue rather than a wall of noise.
|
|
22
|
+
*/
|
|
23
|
+
export const VALIDATION_RULES = [
|
|
24
|
+
// First: every other rule describes a payload the merchant is expected to
|
|
25
|
+
// correct, whereas this one reports data that should never have reached the
|
|
26
|
+
// server at all. Reported ahead of them so it cannot be lost in the list.
|
|
27
|
+
toolArgumentRule,
|
|
28
|
+
presenceRule,
|
|
29
|
+
unknownFieldsRule,
|
|
30
|
+
emptyStringRule,
|
|
31
|
+
constantsRule,
|
|
32
|
+
amountRule,
|
|
33
|
+
dateTimeRule,
|
|
34
|
+
expiryRule,
|
|
35
|
+
txnRefRule,
|
|
36
|
+
billReferenceRule,
|
|
37
|
+
returnUrlRule,
|
|
38
|
+
msisdnRule,
|
|
39
|
+
cnicRule,
|
|
40
|
+
// Last: the generic constraint check reports what the specific rules above do
|
|
41
|
+
// not express, so their more precise diagnosis comes first.
|
|
42
|
+
constraintsRule,
|
|
43
|
+
];
|
|
44
|
+
/** Validate a payload against its flow specification. */
|
|
45
|
+
export function validatePayload(flow, parameters, options = {}) {
|
|
46
|
+
const context = {
|
|
47
|
+
flow,
|
|
48
|
+
parameters: normaliseParameters(parameters),
|
|
49
|
+
registeredReturnUrl: options.registeredReturnUrl,
|
|
50
|
+
now: options.now ?? new Date(),
|
|
51
|
+
};
|
|
52
|
+
const issues = VALIDATION_RULES.flatMap((rule) => rule(context));
|
|
53
|
+
const errors = issues.filter((entry) => entry.severity === 'error');
|
|
54
|
+
return {
|
|
55
|
+
valid: errors.length === 0,
|
|
56
|
+
errors,
|
|
57
|
+
warnings: issues.filter((entry) => entry.severity === 'warning'),
|
|
58
|
+
normalisedPayload: context.parameters,
|
|
59
|
+
flowId: flow.id,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** Render a validation result as text for a merchant or an AI harness. */
|
|
63
|
+
export function formatValidationResult(result) {
|
|
64
|
+
if (result.valid && result.warnings.length === 0) {
|
|
65
|
+
return `The payload is valid for ${result.flowId}. No issues found.`;
|
|
66
|
+
}
|
|
67
|
+
const lines = [];
|
|
68
|
+
lines.push(result.valid
|
|
69
|
+
? `The payload is valid for ${result.flowId}, with ${String(result.warnings.length)} warning(s).`
|
|
70
|
+
: `The payload is NOT valid for ${result.flowId}: ${String(result.errors.length)} error(s), ${String(result.warnings.length)} warning(s).`);
|
|
71
|
+
for (const entry of [...result.errors, ...result.warnings]) {
|
|
72
|
+
const label = entry.severity === 'error' ? 'ERROR' : 'WARN ';
|
|
73
|
+
lines.push('', `${label} [${entry.code}]${entry.field === undefined ? '' : ` ${entry.field}`}`);
|
|
74
|
+
lines.push(` ${entry.message}`);
|
|
75
|
+
lines.push(` Fix: ${entry.fix}`);
|
|
76
|
+
}
|
|
77
|
+
return lines.join('\n');
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=payload-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-validator.js","sourceRoot":"","sources":["../../src/validation/payload-validator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAiD,MAAM,oBAAoB,CAAC;AACxG,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAA8B;IACzD,0EAA0E;IAC1E,4EAA4E;IAC5E,0EAA0E;IAC1E,gBAAgB;IAChB,YAAY;IACZ,iBAAiB;IACjB,eAAe;IACf,aAAa;IACb,UAAU;IACV,YAAY;IACZ,UAAU;IACV,UAAU;IACV,iBAAiB;IACjB,aAAa;IACb,UAAU;IACV,QAAQ;IACR,8EAA8E;IAC9E,4DAA4D;IAC5D,eAAe;CAChB,CAAC;AAiBF,yDAAyD;AACzD,MAAM,UAAU,eAAe,CAC7B,IAAqB,EACrB,UAAyB,EACzB,UAA6B,EAAE;IAE/B,MAAM,OAAO,GAAgB;QAC3B,IAAI;QACJ,UAAU,EAAE,mBAAmB,CAAC,UAAU,CAAC;QAC3C,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;QAChD,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE;KAC/B,CAAC;IAEF,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAEpE,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;QACN,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC;QAChE,iBAAiB,EAAE,OAAO,CAAC,UAAU;QACrC,MAAM,EAAE,IAAI,CAAC,EAAE;KAChB,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,sBAAsB,CAAC,MAAwB;IAC7D,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,4BAA4B,MAAM,CAAC,MAAM,oBAAoB,CAAC;IACvE,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CACR,MAAM,CAAC,KAAK;QACV,CAAC,CAAC,4BAA4B,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc;QACjG,CAAC,CAAC,gCAAgC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAC7I,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,KAAK,KAAK,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChG,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/amount.ts"],"names":[],"mappings":"AAEA,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AA2B/E,eAAO,MAAM,UAAU,EAAE,cA0DxB,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { assertMinorUnits, fromMinorUnits, looksLikeMajorUnits, toMinorUnits } from '../../domain/amount.js';
|
|
2
|
+
import { describeError } from '../../errors.js';
|
|
3
|
+
import { issue } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* `pp_Amount` must be a positive whole number of minor units.
|
|
6
|
+
*
|
|
7
|
+
* Forgetting the ×100 conversion is the most common integration error after the
|
|
8
|
+
* secure hash, so the failure message names the conversion explicitly rather
|
|
9
|
+
* than reporting a generic format problem.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Suggest the exact minor-unit value, or no value at all.
|
|
13
|
+
*
|
|
14
|
+
* `fix` is documented as the machine-actionable remedy, so an agent may apply it
|
|
15
|
+
* verbatim to a payment: a number that is approximately right is worse than no
|
|
16
|
+
* number. The exact converter is reused rather than the digits rearranged by
|
|
17
|
+
* hand, and when it refuses the value the advice stays deliberately general.
|
|
18
|
+
*/
|
|
19
|
+
function conversionAdvice(value) {
|
|
20
|
+
let minorUnits;
|
|
21
|
+
try {
|
|
22
|
+
minorUnits = toMinorUnits(value);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'Multiply by 100 and send a whole number of minor units, with no decimal point. PKR 100.00 is sent as "10000".';
|
|
26
|
+
}
|
|
27
|
+
return `Multiply by 100 and send a whole number: PKR ${value} becomes "${minorUnits}". PKR 100.00 is sent as "10000".`;
|
|
28
|
+
}
|
|
29
|
+
export const amountRule = (context) => {
|
|
30
|
+
const parameter = context.flow.parameters.find((entry) => entry.valueType === 'minor-units');
|
|
31
|
+
if (parameter === undefined) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const value = context.parameters[parameter.name];
|
|
35
|
+
if (value === undefined || value === '') {
|
|
36
|
+
// Absence and emptiness are both reported by the presence rule; do not
|
|
37
|
+
// report either twice.
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const issues = [];
|
|
41
|
+
if (looksLikeMajorUnits(value)) {
|
|
42
|
+
issues.push(issue(context, {
|
|
43
|
+
code: 'amount.not-minor-units',
|
|
44
|
+
severity: 'error',
|
|
45
|
+
field: parameter.name,
|
|
46
|
+
message: `${parameter.name} is "${value}", which contains a decimal point. The gateway reads the last two digits as decimals, so amounts are transmitted as whole minor units.`,
|
|
47
|
+
fix: conversionAdvice(value),
|
|
48
|
+
}));
|
|
49
|
+
return issues;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
assertMinorUnits(value);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
issues.push(issue(context, {
|
|
56
|
+
code: 'amount.invalid',
|
|
57
|
+
severity: 'error',
|
|
58
|
+
field: parameter.name,
|
|
59
|
+
message: describeError(error),
|
|
60
|
+
fix: 'Send pp_Amount as a positive whole number of minor units, for example "100" for PKR 1.00.',
|
|
61
|
+
}));
|
|
62
|
+
return issues;
|
|
63
|
+
}
|
|
64
|
+
// A PKR 1.00 test transaction is "100" and entirely legitimate, so a small
|
|
65
|
+
// value is only worth a note, never an error.
|
|
66
|
+
if (Number(value) < 100) {
|
|
67
|
+
issues.push(issue(context, {
|
|
68
|
+
code: 'amount.suspiciously-small',
|
|
69
|
+
severity: 'warning',
|
|
70
|
+
field: parameter.name,
|
|
71
|
+
message: `${parameter.name} is "${value}", which the gateway reads as PKR ${fromMinorUnits(value)}.`,
|
|
72
|
+
fix: `Confirm this is intended. If you meant PKR ${value}.00, send "${value}00" instead.`,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
return issues;
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=amount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.js","sourceRoot":"","sources":["../../../src/validation/rules/amount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC7G,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,EAA6C,MAAM,aAAa,CAAC;AAE/E;;;;;;GAMG;AAEH;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,+GAA+G,CAAC;IACzH,CAAC;IACD,OAAO,gDAAgD,KAAK,aAAa,UAAU,mCAAmC,CAAC;AACzH,CAAC;AACD,MAAM,CAAC,MAAM,UAAU,GAAmB,CAAC,OAAO,EAA8B,EAAE;IAChF,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,KAAK,aAAa,CAAC,CAAC;IAC7F,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,uEAAuE;QACvE,uBAAuB;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAsB,EAAE,CAAC;IAErC,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CACT,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,wBAAwB;YAC9B,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,QAAQ,KAAK,wIAAwI;YAC/K,GAAG,EAAE,gBAAgB,CAAC,KAAK,CAAC;SAC7B,CAAC,CACH,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,CAAC;QACH,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CACT,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,gBAAgB;YACtB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC;YAC7B,GAAG,EAAE,2FAA2F;SACjG,CAAC,CACH,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,2EAA2E;IAC3E,8CAA8C;IAC9C,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;QACxB,MAAM,CAAC,IAAI,CACT,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,2BAA2B;YACjC,QAAQ,EAAE,SAAS;YACnB,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,QAAQ,KAAK,qCAAqC,cAAc,CAAC,KAAK,CAAC,GAAG;YACpG,GAAG,EAAE,8CAA8C,KAAK,cAAc,KAAK,cAAc;SAC1F,CAAC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ValidationRule } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `pp_BillReference` must be alphanumeric.
|
|
4
|
+
*
|
|
5
|
+
* "Must contain only alphanumeric characters. Spaces and special characters are
|
|
6
|
+
* not allowed." Order numbers such as `ORD-1024` and `INV/2026/01` are the usual
|
|
7
|
+
* offenders, so the fix suggests the stripped form rather than restating the rule.
|
|
8
|
+
*/
|
|
9
|
+
export declare const billReferenceRule: ValidationRule;
|
|
10
|
+
//# sourceMappingURL=bill-reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bill-reference.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/bill-reference.ts"],"names":[],"mappings":"AACA,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,cA8B/B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { validateBillReference } from '../../domain/txn-ref.js';
|
|
2
|
+
import { issue } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* `pp_BillReference` must be alphanumeric.
|
|
5
|
+
*
|
|
6
|
+
* "Must contain only alphanumeric characters. Spaces and special characters are
|
|
7
|
+
* not allowed." Order numbers such as `ORD-1024` and `INV/2026/01` are the usual
|
|
8
|
+
* offenders, so the fix suggests the stripped form rather than restating the rule.
|
|
9
|
+
*/
|
|
10
|
+
export const billReferenceRule = (context) => {
|
|
11
|
+
const parameter = context.flow.parameters.find((entry) => entry.valueType === 'bill-reference');
|
|
12
|
+
if (parameter === undefined) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const value = context.parameters[parameter.name];
|
|
16
|
+
if (value === undefined || value === '') {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const check = validateBillReference(value);
|
|
20
|
+
if (check.valid) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const suggestion = value.replace(/[^A-Za-z0-9]/g, '');
|
|
24
|
+
return check.issues.map((problem) => issue(context, {
|
|
25
|
+
code: 'bill-reference.invalid',
|
|
26
|
+
severity: 'error',
|
|
27
|
+
field: parameter.name,
|
|
28
|
+
message: problem.replace(/^pp_\w+/, parameter.name),
|
|
29
|
+
fix: suggestion === ''
|
|
30
|
+
? `Supply an alphanumeric ${parameter.name}, for example "billRef185".`
|
|
31
|
+
: `Strip the punctuation, for example "${suggestion}". The value may repeat across transactions, so it does not need to be unique.`,
|
|
32
|
+
}));
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=bill-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bill-reference.js","sourceRoot":"","sources":["../../../src/validation/rules/bill-reference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,KAAK,EAA6C,MAAM,aAAa,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAmB,CAAC,OAAO,EAA8B,EAAE;IACvF,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,KAAK,gBAAgB,CAAC,CAAC;IAChG,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAEtD,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAmB,EAAE,CACnD,KAAK,CAAC,OAAO,EAAE;QACb,IAAI,EAAE,wBAAwB;QAC9B,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,SAAS,CAAC,IAAI;QACrB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC;QACnD,GAAG,EACD,UAAU,KAAK,EAAE;YACf,CAAC,CAAC,0BAA0B,SAAS,CAAC,IAAI,6BAA6B;YACvE,CAAC,CAAC,uCAAuC,UAAU,gFAAgF;KACxI,CAAC,CACH,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cnic.d.ts","sourceRoot":"","sources":["../../../src/validation/rules/cnic.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAa/E,eAAO,MAAM,QAAQ,EAAE,cAkCtB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { issue } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `pp_CNIC` carries the LAST SIX DIGITS of the customer's CNIC, not the whole number.
|
|
4
|
+
*
|
|
5
|
+
* The MWallet v2.0 guide annotates the field as `{{Last 6-digits}}`. Sending the
|
|
6
|
+
* full 13-digit CNIC is both a validation failure and an unnecessary disclosure
|
|
7
|
+
* of personal data, so it is called out specifically.
|
|
8
|
+
*/
|
|
9
|
+
const LAST_SIX_DIGITS = /^\d{6}$/;
|
|
10
|
+
const FULL_CNIC = /^\d{5}-?\d{7}-?\d$/;
|
|
11
|
+
export const cnicRule = (context) => {
|
|
12
|
+
const parameter = context.flow.parameters.find((entry) => entry.valueType === 'cnic');
|
|
13
|
+
if (parameter === undefined) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const value = context.parameters[parameter.name];
|
|
17
|
+
if (value === undefined || value === '' || LAST_SIX_DIGITS.test(value)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const digits = value.replace(/\D/g, '');
|
|
21
|
+
if (FULL_CNIC.test(value)) {
|
|
22
|
+
return [
|
|
23
|
+
issue(context, {
|
|
24
|
+
code: 'cnic.full-number-supplied',
|
|
25
|
+
severity: 'error',
|
|
26
|
+
field: parameter.name,
|
|
27
|
+
message: `${parameter.name} appears to be a full CNIC. The gateway expects only the last six digits, and sending the whole number discloses more personal data than the integration requires.`,
|
|
28
|
+
fix: `Send only the last six digits: "${digits.slice(-6)}".`,
|
|
29
|
+
}),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
return [
|
|
33
|
+
issue(context, {
|
|
34
|
+
code: 'cnic.invalid',
|
|
35
|
+
severity: 'error',
|
|
36
|
+
field: parameter.name,
|
|
37
|
+
message: `${parameter.name} is not six digits.`,
|
|
38
|
+
fix: 'Send exactly the last six digits of the customer CNIC, with no dashes or spaces.',
|
|
39
|
+
}),
|
|
40
|
+
];
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=cnic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cnic.js","sourceRoot":"","sources":["../../../src/validation/rules/cnic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA6C,MAAM,aAAa,CAAC;AAE/E;;;;;;GAMG;AAEH,MAAM,eAAe,GAAG,SAAS,CAAC;AAClC,MAAM,SAAS,GAAG,oBAAoB,CAAC;AAEvC,MAAM,CAAC,MAAM,QAAQ,GAAmB,CAAC,OAAO,EAA8B,EAAE;IAC9E,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC;IACtF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAExC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,KAAK,CAAC,OAAO,EAAE;gBACb,IAAI,EAAE,2BAA2B;gBACjC,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,SAAS,CAAC,IAAI;gBACrB,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,oKAAoK;gBAC9L,GAAG,EAAE,mCAAmC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;aAC7D,CAAC;SACH,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,SAAS,CAAC,IAAI;YACrB,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,qBAAqB;YAC/C,GAAG,EAAE,kFAAkF;SACxF,CAAC;KACH,CAAC;AACJ,CAAC,CAAC"}
|