shabaaspay-mcp-server 1.0.2 → 1.0.4

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.
Files changed (70) hide show
  1. package/LICENSE +21 -21
  2. package/dist/agent-toolkit/ai-sdk.d.ts +5 -0
  3. package/dist/agent-toolkit/ai-sdk.js +34 -0
  4. package/dist/agent-toolkit/index.d.ts +16 -0
  5. package/dist/agent-toolkit/index.js +46 -0
  6. package/dist/agent-toolkit/langchain.d.ts +5 -0
  7. package/dist/agent-toolkit/langchain.js +30 -0
  8. package/dist/agent-toolkit/modelcontextprotocol.d.ts +2 -0
  9. package/dist/agent-toolkit/modelcontextprotocol.js +7 -0
  10. package/dist/api/client.d.ts +37 -26
  11. package/dist/api/client.js +119 -84
  12. package/dist/api/mcp-auth.d.ts +18 -0
  13. package/dist/api/mcp-auth.js +64 -0
  14. package/dist/config/index.d.ts +8 -8
  15. package/dist/config/index.js +15 -18
  16. package/dist/constants/backend-urls.d.ts +8 -0
  17. package/dist/constants/backend-urls.js +11 -0
  18. package/dist/enricher/index.d.ts +2 -0
  19. package/dist/enricher/index.js +67 -0
  20. package/dist/index.js +12 -0
  21. package/dist/mcp-handler/auth.d.ts +15 -0
  22. package/dist/mcp-handler/auth.js +33 -0
  23. package/dist/mcp-handler/discovery.d.ts +19 -0
  24. package/dist/mcp-handler/discovery.js +400 -0
  25. package/dist/mcp-handler/mcp.d.ts +13 -0
  26. package/dist/mcp-handler/mcp.js +151 -0
  27. package/dist/mcp-handler/oauth-origin.d.ts +8 -0
  28. package/dist/mcp-handler/oauth-origin.js +24 -0
  29. package/dist/mcp-handler/oauth-page-assets.d.ts +3 -0
  30. package/dist/mcp-handler/oauth-page-assets.js +163 -0
  31. package/dist/mcp-handler/proxy.d.ts +9 -0
  32. package/dist/mcp-handler/proxy.js +28 -0
  33. package/dist/mcp-handler/schema-helpers.d.ts +8 -0
  34. package/dist/mcp-handler/schema-helpers.js +26 -0
  35. package/dist/mcp-handler/server-card.d.ts +2 -0
  36. package/dist/mcp-handler/server-card.js +219 -0
  37. package/dist/mcp-handler/shabaas-api-client.d.ts +28 -0
  38. package/dist/mcp-handler/shabaas-api-client.js +109 -0
  39. package/dist/mcp-handler/tools/implementations.d.ts +17 -0
  40. package/dist/mcp-handler/tools/implementations.js +306 -0
  41. package/dist/mcp-handler/tools/index.d.ts +20 -0
  42. package/dist/mcp-handler/tools/index.js +124 -0
  43. package/dist/mcp-handler/types.d.ts +15 -0
  44. package/dist/mcp-handler/types.js +8 -0
  45. package/dist/mcp-handler/utils.d.ts +48 -0
  46. package/dist/mcp-handler/utils.js +126 -0
  47. package/dist/mcp-handler/worker.d.ts +11 -0
  48. package/dist/mcp-handler/worker.js +583 -0
  49. package/dist/security/auth.js +5 -4
  50. package/dist/security/policy.d.ts +0 -10
  51. package/dist/security/policy.js +0 -29
  52. package/dist/server/http-server.js +43 -65
  53. package/dist/server/stdio-server.d.ts +0 -2
  54. package/dist/server/stdio-server.js +10 -28
  55. package/dist/server-http.js +0 -0
  56. package/dist/tools/auth.d.ts +2 -1
  57. package/dist/tools/auth.js +3 -3
  58. package/dist/tools/index.d.ts +82 -15
  59. package/dist/tools/payment-agreements.d.ts +70 -9
  60. package/dist/tools/payment-agreements.js +10 -5
  61. package/dist/tools/payment-initiations.d.ts +12 -6
  62. package/dist/tools/payment-initiations.js +15 -74
  63. package/dist/tools/response-helpers.d.ts +4 -0
  64. package/dist/types/index.d.ts +78 -10
  65. package/dist/types/index.js +48 -7
  66. package/package.json +19 -7
  67. package/readme.md +132 -43
  68. package/server.json +31 -0
  69. package/dist/worker.d.ts +0 -15
  70. package/dist/worker.js +0 -767
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 ShaBaas
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShaBaas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ import { ShabaasAgentToolkit, type ShabaasAgentToolkitOptions } from './index.js';
2
+ export declare class ShabaasAgentToolkitAiSdk extends ShabaasAgentToolkit {
3
+ constructor(options: ShabaasAgentToolkitOptions);
4
+ getAiSdkTools(): Promise<Record<string, any>>;
5
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ShabaasAgentToolkitAiSdk = void 0;
4
+ const zod_to_json_schema_1 = require("zod-to-json-schema");
5
+ const index_js_1 = require("./index.js");
6
+ class ShabaasAgentToolkitAiSdk extends index_js_1.ShabaasAgentToolkit {
7
+ constructor(options) {
8
+ super(options);
9
+ }
10
+ async getAiSdkTools() {
11
+ let toolFactory;
12
+ try {
13
+ const aiModuleName = 'ai';
14
+ const aiModule = await import(aiModuleName);
15
+ toolFactory = aiModule.tool;
16
+ }
17
+ catch {
18
+ throw new Error('AI SDK adapter requires the "ai" package. Install it with: npm install ai');
19
+ }
20
+ const entries = this.getTools().map((toolDef) => {
21
+ const parameters = (0, zod_to_json_schema_1.zodToJsonSchema)(toolDef.inputSchema);
22
+ return [
23
+ toolDef.name,
24
+ toolFactory({
25
+ description: toolDef.description,
26
+ parameters,
27
+ execute: async (args) => toolDef.execute(args ?? {}),
28
+ }),
29
+ ];
30
+ });
31
+ return Object.fromEntries(entries);
32
+ }
33
+ }
34
+ exports.ShabaasAgentToolkitAiSdk = ShabaasAgentToolkitAiSdk;
@@ -0,0 +1,16 @@
1
+ export type ShabaasAgentToolkitOptions = {
2
+ apiKey: string;
3
+ environment?: 'sandbox' | 'production';
4
+ };
5
+ export type ShabaasFunctionTool = {
6
+ name: string;
7
+ description: string;
8
+ inputSchema: unknown;
9
+ execute: (args?: Record<string, unknown>) => Promise<unknown>;
10
+ };
11
+ export declare class ShabaasAgentToolkit {
12
+ private readonly apiKey;
13
+ private readonly tools;
14
+ constructor(options: ShabaasAgentToolkitOptions);
15
+ getTools(): ShabaasFunctionTool[];
16
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ShabaasAgentToolkit = void 0;
4
+ const client_js_1 = require("../api/client.js");
5
+ const backend_urls_js_1 = require("../constants/backend-urls.js");
6
+ const index_js_1 = require("../tools/index.js");
7
+ function toToolkitConfig(options) {
8
+ const environment = options.environment ?? 'sandbox';
9
+ return {
10
+ environment,
11
+ shabaasAuthUuid: options.apiKey,
12
+ sandboxUrl: backend_urls_js_1.CANONICAL_SANDBOX_BASE_URL,
13
+ productionUrl: backend_urls_js_1.CANONICAL_PRODUCTION_BASE_URL,
14
+ httpPort: 3001,
15
+ httpHost: '0.0.0.0',
16
+ mcpHttpApiKey: '',
17
+ mcpStdioApiKey: '',
18
+ allowedOrigins: ['*'],
19
+ rateLimitPerMinute: 60,
20
+ rateLimitPerHour: 1000,
21
+ authTokenMaxAgeMinutes: 50,
22
+ policyCacheTtlMs: 300_000,
23
+ };
24
+ }
25
+ class ShabaasAgentToolkit {
26
+ apiKey;
27
+ tools;
28
+ constructor(options) {
29
+ if (!options.apiKey) {
30
+ throw new Error('ShabaasAgentToolkit requires apiKey');
31
+ }
32
+ this.apiKey = options.apiKey;
33
+ const config = toToolkitConfig(options);
34
+ const apiClient = new client_js_1.ShabaasApiClient(config);
35
+ this.tools = (0, index_js_1.createAllTools)(apiClient, config);
36
+ }
37
+ getTools() {
38
+ return Object.values(this.tools).map((tool) => ({
39
+ name: tool.name,
40
+ description: tool.description,
41
+ inputSchema: tool.inputSchema,
42
+ execute: async (args) => tool.execute(args ?? {}, { requestUuid: this.apiKey }),
43
+ }));
44
+ }
45
+ }
46
+ exports.ShabaasAgentToolkit = ShabaasAgentToolkit;
@@ -0,0 +1,5 @@
1
+ import { ShabaasAgentToolkit, type ShabaasAgentToolkitOptions } from './index.js';
2
+ export declare class ShabaasAgentToolkitLangChain extends ShabaasAgentToolkit {
3
+ constructor(options: ShabaasAgentToolkitOptions);
4
+ getLangChainTools(): Promise<any[]>;
5
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ShabaasAgentToolkitLangChain = void 0;
4
+ const zod_1 = require("zod");
5
+ const index_js_1 = require("./index.js");
6
+ class ShabaasAgentToolkitLangChain extends index_js_1.ShabaasAgentToolkit {
7
+ constructor(options) {
8
+ super(options);
9
+ }
10
+ async getLangChainTools() {
11
+ let DynamicStructuredTool;
12
+ try {
13
+ const langchainToolsModule = '@langchain/core/tools';
14
+ ({ DynamicStructuredTool } = await import(langchainToolsModule));
15
+ }
16
+ catch {
17
+ throw new Error('LangChain adapter requires @langchain/core. Install it with: npm install @langchain/core');
18
+ }
19
+ return this.getTools().map((tool) => new DynamicStructuredTool({
20
+ name: tool.name,
21
+ description: tool.description,
22
+ schema: tool.inputSchema ?? zod_1.z.object({}),
23
+ func: async (args) => {
24
+ const result = await tool.execute(args);
25
+ return JSON.stringify(result);
26
+ },
27
+ }));
28
+ }
29
+ }
30
+ exports.ShabaasAgentToolkitLangChain = ShabaasAgentToolkitLangChain;
@@ -0,0 +1,2 @@
1
+ export { StdioMcpServer } from '../server/stdio-server.js';
2
+ export { HttpMcpServer } from '../server/http-server.js';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpMcpServer = exports.StdioMcpServer = void 0;
4
+ var stdio_server_js_1 = require("../server/stdio-server.js");
5
+ Object.defineProperty(exports, "StdioMcpServer", { enumerable: true, get: function () { return stdio_server_js_1.StdioMcpServer; } });
6
+ var http_server_js_1 = require("../server/http-server.js");
7
+ Object.defineProperty(exports, "HttpMcpServer", { enumerable: true, get: function () { return http_server_js_1.HttpMcpServer; } });
@@ -1,60 +1,71 @@
1
1
  import { Config } from '../config/index.js';
2
2
  import { ApiResponse } from '../types/index.js';
3
+ /** Options for API calls that need backend auth; requestApiKey comes from the request (no .env). */
4
+ export type RequestAuthOptions = {
5
+ requestUuid?: string;
6
+ };
7
+ /**
8
+ * Discoverable auth behavior metadata used by docs/discovery surfaces.
9
+ * Additive-only metadata: does not alter runtime request handling.
10
+ */
11
+ export declare const AUTH_BEHAVIOR: {
12
+ readonly token_cache_ttl_ms: number;
13
+ readonly accepts_authorization_formats: readonly ["sbp_live_xxx", "Bearer sbp_live_xxx"];
14
+ readonly upstream_token_exchange: true;
15
+ readonly cache_scope: "per_api_key";
16
+ };
3
17
  export declare class ShabaasApiClient {
4
18
  private client;
5
19
  private config;
6
- private bearerToken;
7
- private bearerTokenFetchedAtMs;
8
20
  constructor(config: Config);
9
21
  private normalizeBearer;
10
22
  /**
11
- * Exchanges the configured UUID for a Bearer token.
12
- * UUID must be sent only to /api/public/authorization in the Authorization header.
13
- * The returned token is used for all subsequent API calls.
23
+ * Returns a bearer token for backend API calls. Use the API key from the request (Authorization header).
24
+ * No .env or global API key; requestUuid must be provided by the caller (value is the API key from the authenticated request).
25
+ * The backend accepts API key only.
14
26
  */
15
- authorize(): Promise<{
16
- token: string;
17
- raw: any;
18
- }>;
19
- private tokenLooksFresh;
20
- ensureAuthenticated(): Promise<void>;
27
+ getTokenForRequest(requestUuid?: string): Promise<string>;
21
28
  private withAuthRetry;
22
- getAuthToken(): Promise<{
29
+ getAuthToken(options?: RequestAuthOptions): Promise<{
23
30
  token: string;
24
31
  fetchedAt: string;
25
32
  }>;
26
- getPaymentAgreement(id: string): Promise<ApiResponse>;
33
+ getPaymentAgreement(id: string, options?: RequestAuthOptions): Promise<ApiResponse>;
27
34
  createPaymentAgreement(data: {
28
35
  name: string;
29
36
  type: string;
30
37
  maximum_amount: string;
31
38
  frequency: string;
32
39
  number_of_transactions_permitted: number;
33
- pay_id: string;
34
- idempotency_key: string;
35
- }): Promise<ApiResponse>;
36
- cancelPaymentAgreement(id: string): Promise<ApiResponse>;
37
- resendPaymentAgreement(id: string): Promise<ApiResponse>;
40
+ pay_id?: string;
41
+ phone_number?: string;
42
+ bsb?: string | number;
43
+ account_number?: string | number;
44
+ agreement_type?: string;
45
+ start_date?: string;
46
+ end_date?: string;
47
+ }, options?: RequestAuthOptions): Promise<ApiResponse>;
48
+ cancelPaymentAgreement(id: string, options?: RequestAuthOptions): Promise<ApiResponse>;
49
+ resendPaymentAgreement(id: string, options?: RequestAuthOptions): Promise<ApiResponse>;
38
50
  initiatePayment(data: {
39
51
  payment_agreement_id: string;
40
52
  amount: string | number;
41
53
  description?: string;
42
- idempotency_key: string;
43
- }): Promise<ApiResponse>;
44
- getPaymentInitiation(id: string): Promise<ApiResponse>;
54
+ notes?: string;
55
+ }, options?: RequestAuthOptions): Promise<ApiResponse>;
56
+ getPaymentInitiation(id: string, options?: RequestAuthOptions): Promise<ApiResponse>;
45
57
  initiateDirectDebit(data: {
46
58
  payment_agreement_id: string;
47
59
  amount: string | number;
48
60
  description?: string;
49
- idempotency_key: string;
50
- }): Promise<ApiResponse>;
61
+ }, options?: RequestAuthOptions): Promise<ApiResponse>;
51
62
  getPaymentLink(data: {
52
63
  amount: string;
53
64
  reference?: string;
54
- }): Promise<ApiResponse>;
65
+ }, options?: RequestAuthOptions): Promise<ApiResponse>;
55
66
  registerWebhook(data: {
56
67
  url: string;
57
68
  events: string[];
58
- }): Promise<ApiResponse>;
59
- listWebhooks(): Promise<ApiResponse>;
69
+ }, options?: RequestAuthOptions): Promise<ApiResponse>;
70
+ listWebhooks(options?: RequestAuthOptions): Promise<ApiResponse>;
60
71
  }
@@ -3,14 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ShabaasApiClient = void 0;
6
+ exports.ShabaasApiClient = exports.AUTH_BEHAVIOR = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const index_js_1 = require("../config/index.js");
9
+ /** Cache bearer token per API key so we don't refetch every request. */
10
+ const tokenCache = new Map();
11
+ const CACHE_TTL_MS = 50 * 60 * 1000; // 50 min
12
+ /**
13
+ * Discoverable auth behavior metadata used by docs/discovery surfaces.
14
+ * Additive-only metadata: does not alter runtime request handling.
15
+ */
16
+ exports.AUTH_BEHAVIOR = {
17
+ token_cache_ttl_ms: CACHE_TTL_MS,
18
+ accepts_authorization_formats: ['sbp_live_xxx', 'Bearer sbp_live_xxx'],
19
+ upstream_token_exchange: true,
20
+ cache_scope: 'per_api_key',
21
+ };
9
22
  class ShabaasApiClient {
10
23
  client;
11
24
  config;
12
- bearerToken = null;
13
- bearerTokenFetchedAtMs = null;
14
25
  constructor(config) {
15
26
  this.config = config;
16
27
  const baseURL = (0, index_js_1.getApiUrl)(config);
@@ -18,12 +29,17 @@ class ShabaasApiClient {
18
29
  baseURL,
19
30
  headers: {
20
31
  'Content-Type': 'application/json',
32
+ // Identify requests as coming from the MCP server so the backend
33
+ 'X-Shabaas-Client': 'mcp',
21
34
  },
22
35
  timeout: 30000,
23
36
  });
24
37
  this.client.interceptors.request.use((cfg) => {
25
38
  // Log to stderr to keep STDIO transport stdout clean for MCP JSON
26
39
  console.error(`[API Request] ${cfg.method?.toUpperCase()} ${cfg.url}`);
40
+ if (process.env.MCP_DEBUG === '1' || process.env.SHABAAS_DEBUG_HEADERS === '1') {
41
+ console.error(`[API Request] X-Shabaas-Client: ${cfg.headers['X-Shabaas-Client'] ?? '(not set)'}`);
42
+ }
27
43
  return cfg;
28
44
  }, (error) => {
29
45
  console.error('[API Request Error]', error);
@@ -49,14 +65,24 @@ class ShabaasApiClient {
49
65
  : `Bearer ${trimmed}`;
50
66
  }
51
67
  /**
52
- * Exchanges the configured UUID for a Bearer token.
53
- * UUID must be sent only to /api/public/authorization in the Authorization header.
54
- * The returned token is used for all subsequent API calls.
68
+ * Returns a bearer token for backend API calls. Use the API key from the request (Authorization header).
69
+ * No .env or global API key; requestUuid must be provided by the caller (value is the API key from the authenticated request).
70
+ * The backend accepts API key only.
55
71
  */
56
- async authorize() {
72
+ async getTokenForRequest(requestUuid) {
73
+ const apiKey = (requestUuid ?? this.config.shabaasAuthUuid ?? '').trim();
74
+ if (!apiKey) {
75
+ throw new Error('Backend API auth requires the API key. Send it in the Authorization header (Bearer <your_api_key>) when calling the MCP server.');
76
+ }
77
+ const cacheKey = apiKey;
78
+ const cached = tokenCache.get(cacheKey);
79
+ if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
80
+ return cached.token;
81
+ }
82
+ const authHeader = apiKey.toLowerCase().startsWith('bearer ') ? apiKey : `Bearer ${apiKey}`;
57
83
  const response = await this.client.post('/api/public/authorization', null, {
58
84
  headers: {
59
- Authorization: this.config.shabaasAuthUuid,
85
+ Authorization: authHeader,
60
86
  accept: 'application/json',
61
87
  },
62
88
  });
@@ -71,104 +97,107 @@ class ShabaasApiClient {
71
97
  throw new Error('Authorization succeeded but no token was returned. Expected token in response at data.token.');
72
98
  }
73
99
  const token = this.normalizeBearer(rawToken);
74
- this.bearerToken = token;
75
- this.bearerTokenFetchedAtMs = Date.now();
76
- this.client.defaults.headers.common['Authorization'] = token;
77
- return { token, raw: payload };
78
- }
79
- tokenLooksFresh() {
80
- if (!this.bearerToken || !this.bearerTokenFetchedAtMs)
81
- return false;
82
- const ageMs = Date.now() - this.bearerTokenFetchedAtMs;
83
- const maxAgeMs = this.config.authTokenMaxAgeMinutes * 60 * 1000;
84
- return ageMs < maxAgeMs;
85
- }
86
- async ensureAuthenticated() {
87
- if (this.tokenLooksFresh())
88
- return;
89
- await this.authorize();
90
- }
91
- async withAuthRetry(fn) {
100
+ tokenCache.set(cacheKey, { token, fetchedAt: Date.now() });
101
+ return token;
102
+ }
103
+ async withAuthRetry(fn, requestUuid) {
92
104
  try {
93
- await this.ensureAuthenticated();
94
- return await fn();
105
+ const token = await this.getTokenForRequest(requestUuid);
106
+ return await fn(token);
95
107
  }
96
108
  catch (err) {
97
109
  const status = err?.response?.status;
98
- if (status === 401 || status === 403) {
99
- await this.authorize();
100
- return await fn();
110
+ if ((status === 401 || status === 403) && requestUuid) {
111
+ tokenCache.delete((requestUuid ?? '').trim());
112
+ const token = await this.getTokenForRequest(requestUuid);
113
+ return await fn(token);
101
114
  }
102
115
  throw err;
103
116
  }
104
117
  }
105
- async getAuthToken() {
106
- const { token } = await this.authorize();
118
+ async getAuthToken(options) {
119
+ const token = await this.getTokenForRequest(options?.requestUuid);
107
120
  return { token, fetchedAt: new Date().toISOString() };
108
121
  }
109
- async getPaymentAgreement(id) {
110
- return await this.withAuthRetry(async () => {
111
- const response = await this.client.get(`/api/public/payment_agreement?id=${encodeURIComponent(id)}`);
122
+ async getPaymentAgreement(id, options) {
123
+ return await this.withAuthRetry(async (token) => {
124
+ const response = await this.client.get(`/api/public/payment_agreement?id=${encodeURIComponent(id)}`, { headers: { Authorization: token } });
112
125
  return response.data;
113
- });
114
- }
115
- async createPaymentAgreement(data) {
116
- return await this.withAuthRetry(async () => {
117
- const body = {
118
- payment_agreement: {
119
- name: data.name,
120
- type: data.type,
121
- maximum_amount: data.maximum_amount,
122
- frequency: data.frequency,
123
- number_of_transactions_permitted: data.number_of_transactions_permitted,
124
- pay_id: data.pay_id,
125
- },
126
+ }, options?.requestUuid);
127
+ }
128
+ async createPaymentAgreement(data, options) {
129
+ return await this.withAuthRetry(async (token) => {
130
+ const pa = {
131
+ name: data.name,
132
+ type: data.type,
133
+ maximum_amount: data.maximum_amount,
134
+ frequency: data.frequency,
135
+ number_of_transactions_permitted: data.number_of_transactions_permitted,
126
136
  };
137
+ if (data.pay_id != null)
138
+ pa.pay_id = data.pay_id;
139
+ if (data.phone_number != null)
140
+ pa.phone_number = data.phone_number;
141
+ if (data.bsb != null)
142
+ pa.bsb = data.bsb;
143
+ if (data.account_number != null)
144
+ pa.account_number = data.account_number;
145
+ if (data.agreement_type != null)
146
+ pa.agreement_type = data.agreement_type;
147
+ if (data.start_date != null)
148
+ pa.start_date = data.start_date;
149
+ if (data.end_date != null)
150
+ pa.end_date = data.end_date;
151
+ const body = { payment_agreement: pa };
127
152
  const response = await this.client.post('/api/public/payment_agreement', body, {
128
153
  headers: {
129
- 'Idempotency-Key': data.idempotency_key,
154
+ Authorization: token,
130
155
  },
131
156
  });
132
157
  return response.data;
133
- });
158
+ }, options?.requestUuid);
134
159
  }
135
- async cancelPaymentAgreement(id) {
136
- return await this.withAuthRetry(async () => {
137
- const response = await this.client.delete(`/api/public/payment_agreement/cancel?id=${encodeURIComponent(id)}`);
160
+ async cancelPaymentAgreement(id, options) {
161
+ return await this.withAuthRetry(async (token) => {
162
+ const response = await this.client.delete(`/api/public/payment_agreement/cancel?id=${encodeURIComponent(id)}`, { headers: { Authorization: token } });
138
163
  return response.data;
139
- });
164
+ }, options?.requestUuid);
140
165
  }
141
- async resendPaymentAgreement(id) {
142
- return await this.withAuthRetry(async () => {
143
- const response = await this.client.patch('/api/public/payment_agreement/resend', { id });
166
+ async resendPaymentAgreement(id, options) {
167
+ return await this.withAuthRetry(async (token) => {
168
+ const response = await this.client.patch('/api/public/payment_agreement/resend', { id }, {
169
+ headers: { Authorization: token },
170
+ });
144
171
  return response.data;
145
- });
172
+ }, options?.requestUuid);
146
173
  }
147
- async initiatePayment(data) {
148
- return await this.withAuthRetry(async () => {
174
+ async initiatePayment(data, options) {
175
+ return await this.withAuthRetry(async (token) => {
149
176
  const amount = normalizeAmount(data.amount);
150
177
  const response = await this.client.post('/api/public/payment_initiation', {
151
178
  payment_initiation: {
152
179
  payment_agreement_id: data.payment_agreement_id,
153
180
  amount,
154
- description: data.description,
181
+ notes: data.notes ?? data.description,
155
182
  },
156
183
  }, {
157
184
  headers: {
158
- 'Idempotency-Key': data.idempotency_key,
185
+ Authorization: token,
159
186
  },
187
+ // Backend may call Monoova and can take up to Rack timeout (60s); use 65s so we get Rails response
188
+ timeout: 65000,
160
189
  });
161
190
  return response.data;
162
- });
191
+ }, options?.requestUuid);
163
192
  }
164
- async getPaymentInitiation(id) {
165
- return await this.withAuthRetry(async () => {
166
- const response = await this.client.get(`/api/public/payment_initiation?id=${encodeURIComponent(id)}`);
193
+ async getPaymentInitiation(id, options) {
194
+ return await this.withAuthRetry(async (token) => {
195
+ const response = await this.client.get(`/api/public/payment_initiation?id=${encodeURIComponent(id)}`, { headers: { Authorization: token } });
167
196
  return response.data;
168
- });
197
+ }, options?.requestUuid);
169
198
  }
170
- async initiateDirectDebit(data) {
171
- return await this.withAuthRetry(async () => {
199
+ async initiateDirectDebit(data, options) {
200
+ return await this.withAuthRetry(async (token) => {
172
201
  const amount = normalizeAmount(data.amount);
173
202
  const response = await this.client.post('/api/public/payment_initiation/direct_debit', {
174
203
  payment_initiation: {
@@ -178,29 +207,35 @@ class ShabaasApiClient {
178
207
  },
179
208
  }, {
180
209
  headers: {
181
- 'Idempotency-Key': data.idempotency_key,
210
+ Authorization: token,
182
211
  },
183
212
  });
184
213
  return response.data;
185
- });
214
+ }, options?.requestUuid);
186
215
  }
187
- async getPaymentLink(data) {
188
- return await this.withAuthRetry(async () => {
189
- const response = await this.client.post('/api/get_url', data);
216
+ async getPaymentLink(data, options) {
217
+ return await this.withAuthRetry(async (token) => {
218
+ const response = await this.client.post('/api/get_url', data, {
219
+ headers: { Authorization: token },
220
+ });
190
221
  return response.data;
191
- });
222
+ }, options?.requestUuid);
192
223
  }
193
- async registerWebhook(data) {
194
- return await this.withAuthRetry(async () => {
195
- const response = await this.client.post('/api/public/webhooks', data);
224
+ async registerWebhook(data, options) {
225
+ return await this.withAuthRetry(async (token) => {
226
+ const response = await this.client.post('/api/public/webhooks', data, {
227
+ headers: { Authorization: token },
228
+ });
196
229
  return response.data;
197
- });
230
+ }, options?.requestUuid);
198
231
  }
199
- async listWebhooks() {
200
- return await this.withAuthRetry(async () => {
201
- const response = await this.client.get('/api/public/webhooks');
232
+ async listWebhooks(options) {
233
+ return await this.withAuthRetry(async (token) => {
234
+ const response = await this.client.get('/api/public/webhooks', {
235
+ headers: { Authorization: token },
236
+ });
202
237
  return response.data;
203
- });
238
+ }, options?.requestUuid);
204
239
  }
205
240
  }
206
241
  exports.ShabaasApiClient = ShabaasApiClient;
@@ -0,0 +1,18 @@
1
+ import type { ClientPolicy } from '../security/policy.js';
2
+ export type FetchPolicyResult = {
3
+ policy: ClientPolicy;
4
+ rejection?: undefined;
5
+ } | {
6
+ policy?: undefined;
7
+ rejection: string;
8
+ };
9
+ /**
10
+ * Returns policy for the given token, using in-memory cache when ttlMs > 0.
11
+ * On cache miss or when ttlMs <= 0, calls the backend. Only successful results are cached.
12
+ */
13
+ export declare function getPolicyWithCache(token: string, baseUrl: string, environment: 'sandbox' | 'production', ttlMs: number): Promise<FetchPolicyResult>;
14
+ /**
15
+ * Calls backend GET /api/mcp/auth with Bearer token (API key); returns policy or rejection.
16
+ * The backend looks up the user by API key only.
17
+ */
18
+ export declare function fetchPolicyFromBackend(token: string, baseUrl: string, environment: 'sandbox' | 'production'): Promise<FetchPolicyResult>;